Skip to content

Commit

Permalink
0.2.4; 2025-01-08
Browse files Browse the repository at this point in the history
Addressed issues:
- #1
- #2
- #3
  • Loading branch information
cyclone-github authored Jan 8, 2025
1 parent 2b025b5 commit acbd6cd
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 64 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
### version 0.2.4; 2025-01-08
```
added -o {output_file} flag to redirect stdout to file
updated -help output
refactored code
```
### version 0.2.3; 2025-01-07
```
add sanity check for punycode domains, https://github.com/cyclone-github/ipscope/issues/1
Expand Down
29 changes: 23 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
)

/*
IPScope is a CLI tool for IP lookup and subdomain discovery.
IPScope is a CLI tool for subdomain discovery and IP lookup.
Designed for security researchers and network administrators to resolve IP addresses for TLDs and subdomains.
Includes support for some reverse proxy and WAF detection.
Expand All @@ -40,6 +40,10 @@ Version History:
add sanity check for punycode domains, https://github.com/cyclone-github/ipscope/issues/1
add -json output flag, https://github.com/cyclone-github/ipscope/issues/2
fixed stdout to stderr, https://github.com/cyclone-github/ipscope/issues/3
0.2.4; 2025-01-08
added -o {output_file} flag to redirect stdout to file
updated -help output
refactored code
*/

const cloudflareIPv4URL = "https://www.cloudflare.com/ips-v4/"
Expand All @@ -57,6 +61,7 @@ func main() {
subFlag := flag.String("sub", "", "File containing subdomains")
dnsFlag := flag.String("dns", "1.1.1.1", "Custom DNS server (ex: 1.1.1.1)")
jsonFlag := flag.Bool("json", false, "Output results in JSON format")
outputFlag := flag.String("o", "", "Output to file (defaults to stdout)")
cycloneFlag := flag.Bool("cyclone", false, "")
versionFlag := flag.Bool("version", false, "Version info")
helpFlag := flag.Bool("help", false, "Display help")
Expand Down Expand Up @@ -84,6 +89,16 @@ func main() {
os.Exit(1)
}

if *outputFlag != "" {
file, err := os.OpenFile(*outputFlag, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "Error opening output file: %v\n", err)
os.Exit(1)
}
defer file.Close()
os.Stdout = file
}

jsonOutput := *jsonFlag

domain := *urlFlag
Expand All @@ -107,7 +122,10 @@ func main() {
printCyclone()

fmt.Fprintf(os.Stderr, "Processing URL: %s using DNS: %s\n\n", domain, *dnsFlag)
//writer.Flush()

if *outputFlag != "" {
fmt.Fprintf(os.Stderr, "Output redirected to file: %s\n\n", *outputFlag)
}

// load Cloudflare IP ranges
loadCloudflareIPs()
Expand All @@ -121,7 +139,6 @@ func main() {
var err error

for i := 0; i < retries; i++ {
writer.Flush()
crtSubdomains, err = getSubdomainsFromCRT(domain)
if err == nil {
break
Expand Down Expand Up @@ -153,7 +170,7 @@ func main() {
if !processedSubdomains[domain] {
tldIPs, err := customResolver.LookupIP(context.Background(), "ip4", domain)
if err != nil {
fmt.Fprintf(writer, "Error getting IP for TLD (%s): %v\n", domain, err)
fmt.Fprintf(os.Stderr, "Error getting IP for TLD (%s): %v\n", domain, err)
} else {
printOutput(writer, "TLD", domain, tldIPs, jsonOutput)
writer.Flush()
Expand All @@ -164,7 +181,7 @@ func main() {
if *subFlag != "" {
file, err := os.Open(*subFlag)
if err != nil {
fmt.Fprintf(writer, "Error opening subdomains file (%s): %v\n", *subFlag, err)
fmt.Fprintf(os.Stderr, "Error opening subdomains file (%s): %v\n", *subFlag, err)
os.Exit(1)
}
defer file.Close()
Expand All @@ -184,7 +201,7 @@ func main() {
}

if err := scanner.Err(); err != nil {
fmt.Fprintf(writer, "Error reading subdomains file (%s): %v\n", *subFlag, err)
fmt.Fprintf(os.Stderr, "Error reading subdomains file (%s): %v\n", *subFlag, err)
}
} else {
for _, subdomain := range defaultSubdomains() {
Expand Down
104 changes: 46 additions & 58 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func init() {
for _, cidr := range staticIPs {
_, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
fmt.Printf("Failed to parse Cloudflare IP range %s: %v\n", cidr, err)
fmt.Fprintf(os.Stderr, "Failed to parse Cloudflare IP range %s: %v\n", cidr, err)
continue
}
cloudflareIPNets = append(cloudflareIPNets, ipnet)
Expand All @@ -60,54 +60,40 @@ func printOutput(writer *tabwriter.Writer, label, domain string, ips []net.IP, j
Proxy bool `json:"proxy"`
}

if jsonOutput {
// JSON output format
for _, ip := range ips {
if ipv4 := ip.To4(); ipv4 != nil {
if !isValidPublicIPv4(ipv4) {
continue
}

ipInfo, err := getIPInfo(ipv4.String())
if err != nil {
continue
}

output := JSONOutput{
Label: label,
Domain: domain,
IP: ipv4.String(),
Asn: ipInfo.Org,
City: ipInfo.City,
Region: ipInfo.Region,
Country: ipInfo.Country,
Proxy: checkCloudFlare(ipv4.String()) || checkKnownWAF(ipInfo.Org),
}

jsonData, _ := json.Marshal(output)
fmt.Println(string(jsonData))
}
for _, ip := range ips {
ipv4 := ip.To4()
if ipv4 == nil || !isValidPublicIPv4(ipv4) {
continue
}
} else {
// tabwriter "pretty" output
for _, ip := range ips {
if ipv4 := ip.To4(); ipv4 != nil {
if !isValidPublicIPv4(ipv4) {
continue
}

ipInfo, err := getIPInfo(ipv4.String())
if err != nil {
fmt.Fprintf(writer, "Error fetching IP info: %v\n", err)
continue
}
ipInfo, err := getIPInfo(ipv4.String())
if err != nil {
fmt.Fprintf(os.Stderr, "Error fetching IP info for %s: %v\n", ipv4.String(), err)
continue
}

isReverseProxy := checkCloudFlare(ipv4.String()) || checkKnownWAF(ipInfo.Org)
if isReverseProxy {
fmt.Fprintf(writer, "%-3s\t%-25s\t%-16s\t%-32s\t %s, %s, %s (Reverse Proxy or WAF Detected)\n", label, domain, ipv4, ipInfo.Org, ipInfo.City, ipInfo.Region, ipInfo.Country)
} else {
fmt.Fprintf(writer, "%-3s\t%-25s\t%-16s\t%-32s\t %s, %s, %s\n", label, domain, ipv4, ipInfo.Org, ipInfo.City, ipInfo.Region, ipInfo.Country)
}
isReverseProxy := checkCloudFlare(ipv4.String()) || checkKnownWAF(ipInfo.Org)

if jsonOutput {
// JSON output format
output := JSONOutput{
Label: label,
Domain: domain,
IP: ipv4.String(),
Asn: ipInfo.Org,
City: ipInfo.City,
Region: ipInfo.Region,
Country: ipInfo.Country,
Proxy: isReverseProxy,
}
jsonData, _ := json.Marshal(output)
fmt.Println(string(jsonData))
} else {
// tabwriter "pretty" output
if isReverseProxy {
fmt.Fprintf(writer, "%-3s\t%-25s\t%-16s\t%-32s\t %s, %s, %s (Reverse Proxy or WAF Detected)\n", label, domain, ipv4, ipInfo.Org, ipInfo.City, ipInfo.Region, ipInfo.Country)
} else {
fmt.Fprintf(writer, "%-3s\t%-25s\t%-16s\t%-32s\t %s, %s, %s\n", label, domain, ipv4, ipInfo.Org, ipInfo.City, ipInfo.Region, ipInfo.Country)
}
}
}
Expand Down Expand Up @@ -178,7 +164,7 @@ func getIPInfo(ip string) (*IPInfo, error) {
if retryAfter != "" {
if seconds, err := strconv.Atoi(retryAfter); err == nil {
waitTime = time.Duration(seconds) * time.Second
fmt.Printf("Rate-limited: Retrying after %s...\n", waitTime)
fmt.Fprintf(os.Stderr, "Rate-limited: Retrying after %s...\n", waitTime)
}
}

Expand Down Expand Up @@ -217,14 +203,14 @@ func checkCloudFlare(ipStr string) bool {
func loadCloudflareIPs() {
resp, err := http.Get(cloudflareIPv4URL)
if err != nil {
fmt.Println("Failed to download Cloudflare IPs, using static list.")
fmt.Fprintln(os.Stderr, "Failed to download Cloudflare IPs, using static list.")
return
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Failed to read Cloudflare IPs, using static list.")
fmt.Fprintln(os.Stderr, "Failed to read Cloudflare IPs, using static list.")
return
}

Expand All @@ -236,7 +222,7 @@ func loadCloudflareIPs() {
}
_, ipnet, err := net.ParseCIDR(ipStr)
if err != nil {
fmt.Printf("Failed to parse Cloudflare IP range %s: %v\n", ipStr, err)
fmt.Fprintf(os.Stderr, "Failed to parse Cloudflare IP range %s: %v\n", ipStr, err)
continue
}
cloudflareIPNets = append(cloudflareIPNets, ipnet)
Expand Down Expand Up @@ -266,7 +252,7 @@ func isValidPublicIPv4(ip net.IP) bool {

// version info
func versionFunc() {
fmt.Fprintln(os.Stderr, "Cyclone's IPScope v0.2.3; 2025-01-07\nhttps://github.com/cyclone-github/ipscope\n")
fmt.Fprint(os.Stderr, "Cyclone's IPScope v0.2.4; 2025-01-08\nhttps://github.com/cyclone-github/ipscope\n\n")
}

// cyclone
Expand All @@ -280,6 +266,7 @@ func printCyclone() {
(____/
`
fmt.Fprintln(os.Stderr, cyclone)
versionFunc()
time.Sleep(250 * time.Millisecond)
}

Expand All @@ -289,15 +276,16 @@ func helpFunc() {
str := `Example Usage:
./ipscope.bin -url example.com
./ipscope.bin -url example.com -sub subdomains.txt -dns 8.8.8.8
./ipscope.bin -url example.com -sub subdomains.txt -dns 8.8.8.8 -json -o output.txt
Supported flags:
-url example.com (required)
-sub subdomain.txt (optional, defaults to built-in list)
-dns 8.8.8.8 (optional, defaults to 1.1.1.1)
-help (usage instructions)
-version (version info)`
-url (url to scan)
-sub (defaults to built-in list)
-dns (defaults to 1.1.1.1)
-json (outputs stdout to json)
-o (redirects stdout to file)
-help (usage instructions)
-version (version info)`
fmt.Fprintln(os.Stderr, str)
}

0 comments on commit acbd6cd

Please sign in to comment.