mirror of
https://github.com/skidoodle/ipinfo.git
synced 2026-04-28 17:37:37 +02:00
fix dns lookup and asn prefixes
This commit is contained in:
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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=
|
||||||
|
|||||||
+81
-61
@@ -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,11 +95,14 @@ 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 {
|
||||||
prefixStr := prefix.String()
|
// Filter out bogon prefixes before adding them to the list.
|
||||||
if strings.Contains(prefixStr, ":") {
|
if !IsBogon(prefix.IP) {
|
||||||
ipv6Prefixes = append(ipv6Prefixes, prefixStr)
|
prefixStr := prefix.String()
|
||||||
} else {
|
if strings.Contains(prefixStr, ":") {
|
||||||
ipv4Prefixes = append(ipv4Prefixes, prefixStr)
|
ipv6Prefixes = append(ipv6Prefixes, prefixStr)
|
||||||
|
} else {
|
||||||
|
ipv4Prefixes = append(ipv4Prefixes, prefixStr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Strings(ipv4Prefixes)
|
sort.Strings(ipv4Prefixes)
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user