Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
name: Find Authoritative Nameserver
description: How to find and query the authoritative nameserver for a record.
---
How to find and query the authoritative nameserver for a record.

1. Query SOA for the target_domain.
Expand Down
11 changes: 10 additions & 1 deletion GEMINI.md → AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,16 @@ The project is composed of two main Go-based command-line tools:
## Workspace

* Use `./workspace` as a place to install tools and output binaries and intermediate results.
* Use `./workspace/.venv` for Python installs. Do not install anything globally.
* Use `./.venv` for Python installs. Do not install anything globally.

### Python Environment Setup

To set up the Python environment using `uv`, run the following commands from the project root:

```bash
uv venv
uv pip install -r requirements.txt
```

---
*The domain list used for the analysis is the [Tranco list](https://tranco-list.eu/).*
9,955 changes: 9,955 additions & 0 deletions dnsreport/domains-top10000.csv

Large diffs are not rendered by default.

30,100 changes: 30,100 additions & 0 deletions dnsreport/log.txt

Large diffs are not rendered by default.

62 changes: 33 additions & 29 deletions dnsreport/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,44 +230,48 @@ func annotateDomains(sem *semaphore.Weighted, client *dns.Client, domainsFilenam
go func(td tranco.Domain) {
defer sem.Release(1)
defer domainsWg.Done()
msg := new(dns.Msg)
msg.SetQuestion(dns.Fqdn(td.Name), dns.TypeSOA)
msg.RecursionDesired = true

// Get SOA and CNAME for the domain. We need the SOA to get the authoritative nameservers, and we want to know the CNAME if it exists.
cname := td.Name
soa := ""
slog.Info("Collecting SOA for domain", "rank", td.Rank, "name", td.Name)
r, _, err := client.Exchange(msg, resolverAddress)
soaRequest := new(dns.Msg)
soaRequest.SetQuestion(dns.Fqdn(td.Name), dns.TypeSOA)
soaRequest.RecursionDesired = true
soaResponse, _, err := client.Exchange(soaRequest, resolverAddress)
if err != nil {
slog.Error("SOA query failed", "domain", td.Name, "type", dns.TypeToString[dns.TypeSOA], "error", err)
return
}
cname := td.Name
soa := ""
for _, answer := range r.Answer {
if cnameRR, ok := answer.(*dns.CNAME); ok {
cname = cnameRR.Target
}
if soaRR, ok := answer.(*dns.SOA); ok {
soa = soaRR.Hdr.Name
}
}
if soa == "" {
for _, ns := range r.Ns {
if soaRR, ok := ns.(*dns.SOA); ok {
} else {
for _, answer := range soaResponse.Answer {
if cnameRR, ok := answer.(*dns.CNAME); ok {
cname = cnameRR.Target
}
if soaRR, ok := answer.(*dns.SOA); ok {
soa = soaRR.Hdr.Name
break
}
}
if soa == "" {
for _, ns := range soaResponse.Ns {
if soaRR, ok := ns.(*dns.SOA); ok {
soa = soaRR.Hdr.Name
break
}
}
}
}
nsList, err := net.LookupNS(soa)
if err != nil {
slog.Error("NS lookup failed", "domain", td.Name, "soa", soa, "error", err)
return
}

// Get authoritative nameservers for the domain using the SOA record. If we couldn't get an SOA, we'll just end up with an empty list of nameservers.
var nameservers []string
for _, ns := range nsList {
nameservers = append(nameservers, ns.Host)
if soa != "" {
nsList, err := net.LookupNS(soa)
if err != nil {
slog.Error("NS lookup failed", "domain", td.Name, "soa", soa, "error", err)
return
}
for _, ns := range nsList {
nameservers = append(nameservers, ns.Host)
}
sort.Strings(nameservers)
}
sort.Strings(nameservers)
domain := Domain{
Name: td.Name,
Rank: td.Rank,
Expand Down
Loading