fix dns lookup and asn prefixes

This commit is contained in:
2025-09-19 22:01:30 +02:00
parent 8ce0755d45
commit cf123d61c8
4 changed files with 97 additions and 61 deletions
+4
View File
@@ -6,6 +6,7 @@ require (
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/likexian/whois v1.15.6 github.com/likexian/whois v1.15.6
github.com/likexian/whois-parser v1.24.20 github.com/likexian/whois-parser v1.24.20
github.com/miekg/dns v1.1.68
github.com/oschwald/maxminddb-golang v1.13.1 github.com/oschwald/maxminddb-golang v1.13.1
golang.org/x/net v0.44.0 golang.org/x/net v0.44.0
) )
@@ -15,6 +16,9 @@ require (
github.com/likexian/gokit v0.25.15 // indirect github.com/likexian/gokit v0.25.15 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/stretchr/testify v1.10.0 // indirect github.com/stretchr/testify v1.10.0 // indirect
golang.org/x/mod v0.27.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.36.0 // indirect golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.29.0 // indirect golang.org/x/text v0.29.0 // indirect
golang.org/x/tools v0.36.0 // indirect
) )
+10
View File
@@ -1,5 +1,7 @@
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/likexian/gokit v0.25.15 h1:QjospM1eXhdMMHwZRpMKKAHY/Wig9wgcREmLtf9NslY= github.com/likexian/gokit v0.25.15 h1:QjospM1eXhdMMHwZRpMKKAHY/Wig9wgcREmLtf9NslY=
@@ -8,17 +10,25 @@ github.com/likexian/whois v1.15.6 h1:hizngFHJTNQDlhwhU+FEGyPGxy8bRnf25gHDNrSB4Ag
github.com/likexian/whois v1.15.6/go.mod h1:vx3kt3sZ4mx4XFgpaNp3GXQCZQIzAoyrUAkRtJwoM2I= github.com/likexian/whois v1.15.6/go.mod h1:vx3kt3sZ4mx4XFgpaNp3GXQCZQIzAoyrUAkRtJwoM2I=
github.com/likexian/whois-parser v1.24.20 h1:oxEkRi0GxgqWQRLDMJpXU1EhgWmLmkqEFZ2ChXTeQLE= github.com/likexian/whois-parser v1.24.20 h1:oxEkRi0GxgqWQRLDMJpXU1EhgWmLmkqEFZ2ChXTeQLE=
github.com/likexian/whois-parser v1.24.20/go.mod h1:rAtaofg2luol09H+ogDzGIfcG8ig1NtM5R16uQADDz4= github.com/likexian/whois-parser v1.24.20/go.mod h1:rAtaofg2luol09H+ogDzGIfcG8ig1NtM5R16uQADDz4=
github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE= github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+76 -56
View File
@@ -11,6 +11,7 @@ import (
"ipinfo/internal/db" "ipinfo/internal/db"
"github.com/likexian/whois-parser" "github.com/likexian/whois-parser"
"github.com/miekg/dns"
"golang.org/x/net/publicsuffix" "golang.org/x/net/publicsuffix"
) )
@@ -94,6 +95,8 @@ func LookupASNData(geoIP *db.GeoIPManager, targetASN uint) (*ASNDataResponse, er
var ipv4Prefixes, ipv6Prefixes []string var ipv4Prefixes, ipv6Prefixes []string
for _, prefix := range prefixes { for _, prefix := range prefixes {
// Filter out bogon prefixes before adding them to the list.
if !IsBogon(prefix.IP) {
prefixStr := prefix.String() prefixStr := prefix.String()
if strings.Contains(prefixStr, ":") { if strings.Contains(prefixStr, ":") {
ipv6Prefixes = append(ipv6Prefixes, prefixStr) ipv6Prefixes = append(ipv6Prefixes, prefixStr)
@@ -101,6 +104,7 @@ func LookupASNData(geoIP *db.GeoIPManager, targetASN uint) (*ASNDataResponse, er
ipv4Prefixes = append(ipv4Prefixes, prefixStr) ipv4Prefixes = append(ipv4Prefixes, prefixStr)
} }
} }
}
sort.Strings(ipv4Prefixes) sort.Strings(ipv4Prefixes)
sort.Strings(ipv6Prefixes) sort.Strings(ipv6Prefixes)
@@ -119,6 +123,25 @@ func LookupASNData(geoIP *db.GeoIPManager, targetASN uint) (*ASNDataResponse, er
return response, nil return response, nil
} }
// queryDns performs a DNS query for a specific type against a public resolver.
func queryDns(domain string, recordType uint16) ([]dns.RR, error) {
c := new(dns.Client)
m := new(dns.Msg)
m.SetQuestion(dns.Fqdn(domain), recordType)
m.RecursionDesired = true
r, _, err := c.Exchange(m, "1.1.1.1:53") // Using Cloudflare's public resolver
if err != nil {
return nil, err
}
if r.Rcode != dns.RcodeSuccess {
return nil, nil // No error, just no records found
}
return r.Answer, nil
}
// LookupDomainData looks up domain data with caching. // LookupDomainData looks up domain data with caching.
func LookupDomainData(domain string) (*DomainDataResponse, error) { func LookupDomainData(domain string) (*DomainDataResponse, error) {
if data, found := cache.Get(domain); found { if data, found := cache.Get(domain); found {
@@ -149,68 +172,65 @@ func LookupDomainData(domain string) (*DomainDataResponse, error) {
var wg sync.WaitGroup var wg sync.WaitGroup
var mu sync.Mutex var mu sync.Mutex
lookupTasks := []func(){ recordTypes := map[string]uint16{
func() { // A and AAAA records "A": dns.TypeA,
ips, err := net.LookupIP(domain) "AAAA": dns.TypeAAAA,
if err == nil { "CNAME": dns.TypeCNAME,
mu.Lock() "MX": dns.TypeMX,
defer mu.Unlock() "TXT": dns.TypeTXT,
for _, ip := range ips { "NS": dns.TypeNS,
if ip.To4() != nil { "SOA": dns.TypeSOA,
dnsData.A = append(dnsData.A, ip.String()) "CAA": dns.TypeCAA,
} else {
dnsData.AAAA = append(dnsData.AAAA, ip.String())
}
}
}
},
func() { // CNAME record
cname, err := net.LookupCNAME(domain)
if err == nil && cname != domain+"." && cname != "" {
mu.Lock()
defer mu.Unlock()
dnsData.CNAME = strings.TrimSuffix(cname, ".")
}
},
func() { // MX records
mxs, err := net.LookupMX(domain)
if err == nil {
mu.Lock()
defer mu.Unlock()
for _, mx := range mxs {
dnsData.MX = append(dnsData.MX, fmt.Sprintf("%d %s", mx.Pref, strings.TrimSuffix(mx.Host, ".")))
}
}
},
func() { // TXT records
txts, err := net.LookupTXT(domain)
if err == nil {
mu.Lock()
defer mu.Unlock()
dnsData.TXT = append(dnsData.TXT, txts...)
}
},
func() { // NS records
nss, err := net.LookupNS(eTLD)
if err == nil {
mu.Lock()
defer mu.Unlock()
for _, ns := range nss {
dnsData.NS = append(dnsData.NS, strings.TrimSuffix(ns.Host, "."))
}
}
},
} }
wg.Add(len(lookupTasks)) for key, rType := range recordTypes {
for _, task := range lookupTasks { wg.Add(1)
go func(t func()) { go func(name string, recordType uint16) {
defer wg.Done() defer wg.Done()
t() answers, err := queryDns(domain, recordType)
}(task) if err != nil {
slog.Debug("dns lookup failed for type", "type", name, "domain", domain, "err", err)
return
} }
mu.Lock()
defer mu.Unlock()
for _, ans := range answers {
switch rr := ans.(type) {
case *dns.A:
dnsData.A = append(dnsData.A, rr.A.String())
case *dns.AAAA:
dnsData.AAAA = append(dnsData.AAAA, rr.AAAA.String())
case *dns.CNAME:
dnsData.CNAME = strings.TrimSuffix(rr.Target, ".")
case *dns.MX:
dnsData.MX = append(dnsData.MX, fmt.Sprintf("%d %s", rr.Preference, strings.TrimSuffix(rr.Mx, ".")))
case *dns.TXT:
dnsData.TXT = append(dnsData.TXT, strings.Join(rr.Txt, " "))
case *dns.NS:
dnsData.NS = append(dnsData.NS, strings.TrimSuffix(rr.Ns, "."))
case *dns.SOA:
soaStr := fmt.Sprintf("%s %s %d %d %d %d %d",
strings.TrimSuffix(rr.Ns, "."), strings.TrimSuffix(rr.Mbox, "."),
rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl)
dnsData.SOA = append(dnsData.SOA, soaStr)
case *dns.CAA:
dnsData.CAA = append(dnsData.CAA, fmt.Sprintf(`%d %s "%s"`, rr.Flag, rr.Tag, rr.Value))
}
}
}(key, rType)
}
wg.Wait() wg.Wait()
// Sort MX records for consistent output
sort.Slice(dnsData.MX, func(i, j int) bool {
var prefI, prefJ int
_, _ = fmt.Sscanf(dnsData.MX[i], "%d", &prefI)
_, _ = fmt.Sscanf(dnsData.MX[j], "%d", &prefJ)
return prefI < prefJ
})
response := &DomainDataResponse{ response := &DomainDataResponse{
Whois: whoisResult, Whois: whoisResult,
DNS: dnsData, DNS: dnsData,
+2
View File
@@ -44,6 +44,8 @@ type DNSData struct {
MX []string `json:"MX,omitempty"` MX []string `json:"MX,omitempty"`
TXT []string `json:"TXT,omitempty"` TXT []string `json:"TXT,omitempty"`
NS []string `json:"NS,omitempty"` NS []string `json:"NS,omitempty"`
SOA []string `json:"SOA,omitempty"`
CAA []string `json:"CAA,omitempty"`
} }
// WhoisInfo is a sanitized version of the parsed whois data for the API response. // WhoisInfo is a sanitized version of the parsed whois data for the API response.