Skip to content

Commit 7f88a9f

Browse files
0.2.3; 2025-01-07
1 parent 2333c70 commit 7f88a9f

File tree

4 files changed

+88
-18
lines changed

4 files changed

+88
-18
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
### version 0.2.3; 2025-01-07
2+
```
3+
add sanity check for punycode domains, https://github.com/cyclone-github/ipscope/issues/1
4+
add -json output flag, https://github.com/cyclone-github/ipscope/issues/2
5+
fixed stdout to stderr, https://github.com/cyclone-github/ipscope/issues/3
6+
```
17
### version 0.2.2; 2025-01-04
28
```
39
Added geo printout

crt.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ func getSubdomainsFromCRT(domain string) ([]string, error) {
3939
names := strings.Split(entry.NameValue, "\n")
4040
for _, name := range names {
4141
name = strings.TrimSpace(name)
42-
if strings.HasSuffix(name, domain) {
42+
// tld sanity check
43+
if isValidDomain(name, domain) {
4344
subdomainsSet[name] = struct{}{}
4445
}
4546
}
@@ -52,3 +53,16 @@ func getSubdomainsFromCRT(domain string) ([]string, error) {
5253

5354
return subdomains, nil
5455
}
56+
57+
func isValidDomain(name, domain string) bool {
58+
// only process domains from the tld domain
59+
if !strings.HasSuffix(name, "."+domain) && name != domain {
60+
return false
61+
}
62+
/* // explicitly exclude punycode domains -- probably not necessary as this would cause false negatives
63+
if strings.HasPrefix(name, "xn--") {
64+
return false
65+
}
66+
*/
67+
return true
68+
}

main.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ Version History:
3636
0.2.2; 2025-01-04
3737
Added geo printout
3838
Updated Reverse Proxy / WAF list
39+
0.2.3; 2025-01-07
40+
add sanity check for punycode domains, https://github.com/cyclone-github/ipscope/issues/1
41+
add -json output flag, https://github.com/cyclone-github/ipscope/issues/2
42+
fixed stdout to stderr, https://github.com/cyclone-github/ipscope/issues/3
3943
*/
4044

4145
const cloudflareIPv4URL = "https://www.cloudflare.com/ips-v4/"
@@ -52,6 +56,7 @@ func main() {
5256
urlFlag := flag.String("url", "", "URL to process")
5357
subFlag := flag.String("sub", "", "File containing subdomains")
5458
dnsFlag := flag.String("dns", "1.1.1.1", "Custom DNS server (ex: 1.1.1.1)")
59+
jsonFlag := flag.Bool("json", false, "Output results in JSON format")
5560
cycloneFlag := flag.Bool("cyclone", false, "")
5661
versionFlag := flag.Bool("version", false, "Version info")
5762
helpFlag := flag.Bool("help", false, "Display help")
@@ -79,6 +84,8 @@ func main() {
7984
os.Exit(1)
8085
}
8186

87+
jsonOutput := *jsonFlag
88+
8289
domain := *urlFlag
8390

8491
// DNS resolver
@@ -99,8 +106,8 @@ func main() {
99106

100107
printCyclone()
101108

102-
fmt.Fprintf(writer, "Processing URL: %s using DNS: %s\n\n", domain, *dnsFlag)
103-
writer.Flush()
109+
fmt.Fprintf(os.Stderr, "Processing URL: %s using DNS: %s\n\n", domain, *dnsFlag)
110+
//writer.Flush()
104111

105112
// load Cloudflare IP ranges
106113
loadCloudflareIPs()
@@ -136,7 +143,7 @@ func main() {
136143
}
137144

138145
if ips, err := customResolver.LookupIP(context.Background(), "ip4", fullDomain); err == nil {
139-
printOutput(writer, label, fullDomain, ips)
146+
printOutput(writer, label, fullDomain, ips, jsonOutput)
140147
writer.Flush()
141148
}
142149
}
@@ -148,7 +155,7 @@ func main() {
148155
if err != nil {
149156
fmt.Fprintf(writer, "Error getting IP for TLD (%s): %v\n", domain, err)
150157
} else {
151-
printOutput(writer, "TLD", domain, tldIPs)
158+
printOutput(writer, "TLD", domain, tldIPs, jsonOutput)
152159
writer.Flush()
153160
processedSubdomains[domain] = true
154161
}
@@ -171,7 +178,7 @@ func main() {
171178
}
172179
processedSubdomains[fullDomain] = true
173180
if ips, err := customResolver.LookupIP(context.Background(), "ip4", fullDomain); err == nil {
174-
printOutput(writer, "SUB", fullDomain, ips)
181+
printOutput(writer, "SUB", fullDomain, ips, jsonOutput)
175182
writer.Flush()
176183
}
177184
}
@@ -187,7 +194,7 @@ func main() {
187194
}
188195
processedSubdomains[fullDomain] = true
189196
if ips, err := customResolver.LookupIP(context.Background(), "ip4", fullDomain); err == nil {
190-
printOutput(writer, "SUB", fullDomain, ips)
197+
printOutput(writer, "SUB", fullDomain, ips, jsonOutput)
191198
writer.Flush()
192199
}
193200
}

utils.go

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,60 @@ func init() {
4848
}
4949

5050
// print info
51-
func printOutput(writer *tabwriter.Writer, label, domain string, ips []net.IP) {
52-
for _, ip := range ips {
53-
if ipv4 := ip.To4(); ipv4 != nil {
54-
if !isValidPublicIPv4(ipv4) {
55-
continue
51+
func printOutput(writer *tabwriter.Writer, label, domain string, ips []net.IP, jsonOutput bool) {
52+
type JSONOutput struct {
53+
Label string `json:"label"`
54+
Domain string `json:"domain"`
55+
IP string `json:"ip"`
56+
Asn string `json:"asn"`
57+
City string `json:"city"`
58+
Region string `json:"region"`
59+
Country string `json:"country"`
60+
Proxy bool `json:"proxy"`
61+
}
62+
63+
if jsonOutput {
64+
// JSON output format
65+
for _, ip := range ips {
66+
if ipv4 := ip.To4(); ipv4 != nil {
67+
if !isValidPublicIPv4(ipv4) {
68+
continue
69+
}
70+
71+
ipInfo, err := getIPInfo(ipv4.String())
72+
if err != nil {
73+
continue
74+
}
75+
76+
output := JSONOutput{
77+
Label: label,
78+
Domain: domain,
79+
IP: ipv4.String(),
80+
Asn: ipInfo.Org,
81+
City: ipInfo.City,
82+
Region: ipInfo.Region,
83+
Country: ipInfo.Country,
84+
Proxy: checkCloudFlare(ipv4.String()) || checkKnownWAF(ipInfo.Org),
85+
}
86+
87+
jsonData, _ := json.Marshal(output)
88+
fmt.Println(string(jsonData))
5689
}
90+
}
91+
} else {
92+
// tabwriter "pretty" output
93+
for _, ip := range ips {
94+
if ipv4 := ip.To4(); ipv4 != nil {
95+
if !isValidPublicIPv4(ipv4) {
96+
continue
97+
}
98+
99+
ipInfo, err := getIPInfo(ipv4.String())
100+
if err != nil {
101+
fmt.Fprintf(writer, "Error fetching IP info: %v\n", err)
102+
continue
103+
}
57104

58-
ipInfo, err := getIPInfo(ipv4.String())
59-
if err != nil {
60-
fmt.Fprintf(writer, "Error fetching IP info: %v\n", err)
61-
} else {
62105
isReverseProxy := checkCloudFlare(ipv4.String()) || checkKnownWAF(ipInfo.Org)
63106
if isReverseProxy {
64107
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)
@@ -223,7 +266,7 @@ func isValidPublicIPv4(ip net.IP) bool {
223266

224267
// version info
225268
func versionFunc() {
226-
fmt.Fprintln(os.Stderr, "Cyclone's IPScope v0.2.2; 2025-01-04\nhttps://github.com/cyclone-github/ipscope\n")
269+
fmt.Fprintln(os.Stderr, "Cyclone's IPScope v0.2.3; 2025-01-07\nhttps://github.com/cyclone-github/ipscope\n")
227270
}
228271

229272
// cyclone
@@ -236,7 +279,7 @@ func printCyclone() {
236279
\____)\__ |\____)\_)___/|_| |_|_____)
237280
(____/
238281
`
239-
fmt.Println(cyclone)
282+
fmt.Fprintln(os.Stderr, cyclone)
240283
time.Sleep(250 * time.Millisecond)
241284
}
242285

0 commit comments

Comments
 (0)