Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package doggo for openSUSE:Factory checked 
in at 2026-06-25 10:51:53
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/doggo (Old)
 and      /work/SRC/openSUSE:Factory/.doggo.new.2088 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "doggo"

Thu Jun 25 10:51:53 2026 rev:11 rq:1361467 version:1.2.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/doggo/doggo.changes      2026-05-28 
23:12:46.857476497 +0200
+++ /work/SRC/openSUSE:Factory/.doggo.new.2088/doggo.changes    2026-06-25 
10:56:03.595001983 +0200
@@ -1,0 +2,15 @@
+Tue Jun 23 08:02:44 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 1.2.0:
+  * New Features
+    - 58781f2: feat: add --authoritative/-A flag to query the
+      domain's authoritative nameserver (@nopolabs)
+  * Bug fixes
+    - 8a97e57: fix(app): use delegated NS RRset for authoritative
+      queries (@nopolabs)
+  * Others
+    - 4c3bf3d: chore(ci): use patched Go toolchain (@mr-karan)
+    - 86ff8f4: chore(release): prepare v1.2.0 artifacts (@mr-karan)
+    - 7dbf633: docs(README): change arch package name (@Souvlaki42)
+
+-------------------------------------------------------------------

Old:
----
  doggo-1.1.7.obscpio

New:
----
  doggo-1.2.0.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ doggo.spec ++++++
--- /var/tmp/diff_new_pack.VhCD20/_old  2026-06-25 10:56:05.907081714 +0200
+++ /var/tmp/diff_new_pack.VhCD20/_new  2026-06-25 10:56:05.947083093 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           doggo
-Version:        1.1.7
+Version:        1.2.0
 Release:        0
 Summary:        CLI tool and API server DNS client implemented in Go
 License:        GPL-3.0-only

++++++ _service ++++++
--- /var/tmp/diff_new_pack.VhCD20/_old  2026-06-25 10:56:06.267094129 +0200
+++ /var/tmp/diff_new_pack.VhCD20/_new  2026-06-25 10:56:06.299095232 +0200
@@ -2,7 +2,7 @@
   <service name="obs_scm" mode="manual">
     <param name="scm">git</param>
     <param name="url">https://github.com/mr-karan/doggo.git</param>
-    <param name="revision">v1.1.7</param>
+    <param name="revision">v1.2.0</param>
     <param name="match-tag">*</param>
     <param name="versionrewrite-pattern">v(\d+\.\d+\.\d+)</param>
     <param name="versionformat">@PARENT_TAG@</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.VhCD20/_old  2026-06-25 10:56:06.531103232 +0200
+++ /var/tmp/diff_new_pack.VhCD20/_new  2026-06-25 10:56:06.555104060 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param name="url">https://github.com/mr-karan/doggo.git</param>
-              <param 
name="changesrevision">b400c464b445c79805a9987475f862357ce7f49d</param></service></servicedata>
+              <param 
name="changesrevision">4c3bf3d68955e3988af287cfeb1b7aa41d787fb0</param></service></servicedata>
 (No newline at EOF)
 

++++++ doggo-1.1.7.obscpio -> doggo-1.2.0.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/doggo-1.1.7/.github/workflows/govulncheck.yml 
new/doggo-1.2.0/.github/workflows/govulncheck.yml
--- old/doggo-1.1.7/.github/workflows/govulncheck.yml   2026-05-22 
15:05:55.000000000 +0200
+++ new/doggo-1.2.0/.github/workflows/govulncheck.yml   2026-06-23 
09:00:28.000000000 +0200
@@ -21,4 +21,5 @@
         with:
           go-version-file: go.mod
       - run: |
-          go run golang.org/x/vuln/cmd/govulncheck@latest ./...
+          # v1.4.0 currently panics under Go 1.26 while building the SSA call 
graph.
+          go run golang.org/x/vuln/cmd/[email protected] ./...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/doggo-1.1.7/.github/workflows/release.yml 
new/doggo-1.2.0/.github/workflows/release.yml
--- old/doggo-1.1.7/.github/workflows/release.yml       2026-05-22 
15:05:55.000000000 +0200
+++ new/doggo-1.2.0/.github/workflows/release.yml       2026-06-23 
09:00:28.000000000 +0200
@@ -19,7 +19,7 @@
       - name: Set up Go
         uses: actions/setup-go@v6
         with:
-          go-version: "1.26"
+          go-version: "1.26.4"
       - name: Log in to the Container registry
         uses: docker/login-action@v3
         with:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/doggo-1.1.7/.github/workflows/test.yml 
new/doggo-1.2.0/.github/workflows/test.yml
--- old/doggo-1.1.7/.github/workflows/test.yml  2026-05-22 15:05:55.000000000 
+0200
+++ new/doggo-1.2.0/.github/workflows/test.yml  2026-06-23 09:00:28.000000000 
+0200
@@ -32,6 +32,13 @@
           go-version-file: ${{ matrix.go.go-version-file }}
       - run: |
           if [ "${{ matrix.deps }}" = "latest" ]; then
-            go get -u -t ./...
+            # globalping-cli v1.5.2 removed 
github.com/jsdelivr/globalping-cli/globalping,
+            # which doggo imports. Keep the rest of the dependency freshness 
check
+            # while holding that module at the last compatible release.
+            if ! go get -u -t ./... 2>go-get.err; then
+              cat go-get.err
+              grep -q "github.com/jsdelivr/globalping-cli/globalping" 
go-get.err
+            fi
+            go get github.com/jsdelivr/[email protected]
           fi
           go test -v ./...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/doggo-1.1.7/.goreleaser.yml 
new/doggo-1.2.0/.goreleaser.yml
--- old/doggo-1.1.7/.goreleaser.yml     2026-05-22 15:05:55.000000000 +0200
+++ new/doggo-1.2.0/.goreleaser.yml     2026-06-23 09:00:28.000000000 +0200
@@ -48,25 +48,38 @@
 
 archives:
   - id: cli
-    builds:
+    ids:
       - cli
     format_overrides:
       - goos: windows
-        format: zip
+        formats:
+          - zip
     name_template: >-
-      {{ .ProjectName }}_
-      {{- .Version }}_
-      {{- title .Os }}_
+      {{ .ProjectName }}-
+      {{- .Os }}-
       {{- if eq .Arch "amd64" }}x86_64
+      {{- else if eq .Arch "arm64" }}aarch64
       {{- else if eq .Arch "386" }}i386
       {{- else }}{{ .Arch }}{{ end }}
-    wrap_in_directory: true
+    wrap_in_directory: false
+    builds_info:
+      owner: root
+      group: root
+      mode: 0755
     files:
-      - README.md
-      - LICENSE
+      - src: README.md
+        info:
+          owner: root
+          group: root
+          mode: 0644
+      - src: LICENSE
+        info:
+          owner: root
+          group: root
+          mode: 0644
 
   - id: web
-    builds:
+    ids:
       - web
     wrap_in_directory: true
     files:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/doggo-1.1.7/README.md new/doggo-1.2.0/README.md
--- old/doggo-1.1.7/README.md   2026-05-22 15:05:55.000000000 +0200
+++ new/doggo-1.2.0/README.md   2026-06-23 09:00:28.000000000 +0200
@@ -32,7 +32,7 @@
 
 - Homebrew: `brew install doggo`
 - MacPorts (macOS): `port install doggo`
-- Arch Linux: `yay -S doggo-bin`
+- Arch Linux: `pacman -S doggo`
 - [Mise](https://github.com/jdx/mise): `mise use -g doggo@latest`
 - Nix: `nix profile install nixpkgs#doggo`
 - Scoop (Windows): `scoop install doggo`
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/doggo-1.1.7/cmd/doggo/cli.go 
new/doggo-1.2.0/cmd/doggo/cli.go
--- old/doggo-1.1.7/cmd/doggo/cli.go    2026-05-22 15:05:55.000000000 +0200
+++ new/doggo-1.2.0/cmd/doggo/cli.go    2026-06-23 09:00:28.000000000 +0200
@@ -182,6 +182,7 @@
        f.Bool("skip-hostname-verification", false, "Skip TLS Hostname 
Verification")
 
        f.Bool("any", false, "Query all supported DNS record types")
+       f.BoolP("authoritative", "A", false, "Automatically query the 
authoritative nameserver for the domain")
 
        f.BoolP("json", "J", false, "Set the output format as JSON")
        f.Bool("short", false, "Short output format")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/doggo-1.1.7/cmd/doggo/help.go 
new/doggo-1.2.0/cmd/doggo/help.go
--- old/doggo-1.1.7/cmd/doggo/help.go   2026-05-22 15:05:55.000000000 +0200
+++ new/doggo-1.2.0/cmd/doggo/help.go   2026-06-23 09:00:28.000000000 +0200
@@ -119,6 +119,7 @@
                        {"-c, --class=CLASS", "Network class of the DNS record 
(IN, CH, HS etc)."},
                        {"-x, --reverse", "Performs a DNS Lookup for an IPv4 or 
IPv6 address. Sets the query type and class to PTR and IN respectively."},
                        {"--any", "Query all supported DNS record types (A, 
AAAA, CNAME, MX, NS, PTR, SOA, SRV, TXT, CAA)."},
+                       {"-A, --authoritative", "Find the domain's zone via SOA 
and query its delegated authoritative nameservers (the NS RRset). Honours 
--strategy to narrow the set."},
                },
                "ResolverOptions": []Option{
                        {"--strategy=STRATEGY", "Specify strategy to query 
nameservers. Options: all, random, first, internal (RFC 1918/ULA private IPs 
only)."},
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/doggo-1.1.7/go.mod new/doggo-1.2.0/go.mod
--- old/doggo-1.1.7/go.mod      2026-05-22 15:05:55.000000000 +0200
+++ new/doggo-1.2.0/go.mod      2026-06-23 09:00:28.000000000 +0200
@@ -1,6 +1,6 @@
 module github.com/mr-karan/doggo
 
-go 1.26.3
+go 1.26.4
 
 require (
        github.com/ameshkov/dnscrypt/v2 v2.4.0
@@ -17,8 +17,8 @@
        github.com/olekukonko/tablewriter v1.1.4
        github.com/quic-go/quic-go v0.59.1
        github.com/spf13/pflag v1.0.10
-       golang.org/x/net v0.54.0
-       golang.org/x/sys v0.44.0
+       golang.org/x/net v0.55.0
+       golang.org/x/sys v0.45.0
 )
 
 require (
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/doggo-1.1.7/go.sum new/doggo-1.2.0/go.sum
--- old/doggo-1.1.7/go.sum      2026-05-22 15:05:55.000000000 +0200
+++ new/doggo-1.2.0/go.sum      2026-06-23 09:00:28.000000000 +0200
@@ -80,12 +80,12 @@
 golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a/go.mod 
h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw=
 golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
 golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
-golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
-golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
 golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
 golang.org/x/sync v0.20.0/go.mod 
h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
-golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
-golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
 golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
 golang.org/x/text v0.37.0/go.mod 
h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
 golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/doggo-1.1.7/install.sh new/doggo-1.2.0/install.sh
--- old/doggo-1.1.7/install.sh  2026-05-22 15:05:55.000000000 +0200
+++ new/doggo-1.2.0/install.sh  2026-06-23 09:00:28.000000000 +0200
@@ -31,7 +31,7 @@
   command -v "$1" 1>/dev/null 2>&1
 }
 
-SUPPORTED_TARGETS="Linux_x86_64 Linux_arm64 Windows_x86_64 Darwin_x86_64 
Darwin_arm64"
+SUPPORTED_TARGETS="linux_x86_64 linux_aarch64 windows_x86_64 darwin_x86_64 
darwin_aarch64"
 
 get_latest_release() {
   curl --silent "https://api.github.com/repos/mr-karan/doggo/releases/latest"; |
@@ -42,9 +42,9 @@
 detect_platform() {
   platform="$(uname -s)"
   case "${platform}" in
-    Linux*) platform="Linux" ;;
-    Darwin*) platform="Darwin" ;;
-    MINGW*|MSYS*|CYGWIN*) platform="Windows" ;;
+    Linux*) platform="linux" ;;
+    Darwin*) platform="darwin" ;;
+    MINGW*|MSYS*|CYGWIN*) platform="windows" ;;
     *)
       error "Unsupported platform: ${platform}"
       exit 1
@@ -57,7 +57,7 @@
   arch="$(uname -m)"
   case "${arch}" in
     x86_64) arch="x86_64" ;;
-    aarch64|arm64) arch="arm64" ;;
+    aarch64|arm64) arch="aarch64" ;;
     armv6l|armv7l|armv8l) arch="arm" ;;
     *)
       error "Unsupported architecture: ${arch}"
@@ -67,40 +67,69 @@
   printf '%s' "${arch}"
 }
 
+legacy_platform() {
+  case "$1" in
+    linux) printf 'Linux' ;;
+    darwin) printf 'Darwin' ;;
+    windows) printf 'Windows' ;;
+  esac
+}
+
+legacy_arch() {
+  case "$1" in
+    aarch64) printf 'arm64' ;;
+    *) printf '%s' "$1" ;;
+  esac
+}
+
+download_file() {
+  url="$1"
+  filename="$2"
+
+  if has curl; then
+    curl -fsSL "${url}" -o "${filename}"
+  elif has wget; then
+    wget -q "${url}" -O "${filename}"
+  else
+    error "Neither curl nor wget found. Please install one of them and try 
again."
+    exit 1
+  fi
+}
+
 download_and_install() {
   version="$1"
   platform="$2"
   arch="$3"
 
-  # Remove 'v' prefix from version for filename
-  version_no_v="${version#v}"
-  if [ "${platform}" = "Windows" ]; then
-    filename="doggo_${version_no_v}_${platform}_${arch}.zip"
+  if [ "${platform}" = "windows" ]; then
+    filename="doggo-${platform}-${arch}.zip"
   else
-    filename="doggo_${version_no_v}_${platform}_${arch}.tar.gz"
+    filename="doggo-${platform}-${arch}.tar.gz"
   fi
   
url="https://github.com/mr-karan/doggo/releases/download/${version}/${filename}";
 
   info "Downloading doggo ${version} for ${platform}_${arch}..."
   info "Download URL: ${url}"
 
-  if has curl; then
-    if ! curl -sSL "${url}" -o "${filename}"; then
-      error "Failed to download ${filename}"
-      error "Curl output:"
-      curl -SL "${url}"
-      exit 1
+  if ! download_file "${url}" "${filename}"; then
+    warn "Could not download ${filename}; trying legacy release asset name."
+    rm -f "${filename}"
+
+    version_no_v="${version#v}"
+    old_platform="$(legacy_platform "${platform}")"
+    old_arch="$(legacy_arch "${arch}")"
+    if [ "${platform}" = "windows" ]; then
+      filename="doggo_${version_no_v}_${old_platform}_${old_arch}.zip"
+    else
+      filename="doggo_${version_no_v}_${old_platform}_${old_arch}.tar.gz"
     fi
-  elif has wget; then
-    if ! wget -q "${url}" -O "${filename}"; then
+    
url="https://github.com/mr-karan/doggo/releases/download/${version}/${filename}";
+    info "Legacy download URL: ${url}"
+
+    if ! download_file "${url}" "${filename}"; then
       error "Failed to download ${filename}"
-      error "Wget output:"
-      wget "${url}"
       exit 1
     fi
-  else
-    error "Neither curl nor wget found. Please install one of them and try 
again."
-    exit 1
   fi
 
   info "Verifying if file command exists"
@@ -110,7 +139,7 @@
   fi
 
   info "Verifying downloaded file..."
-  if [ "${platform}" = "Windows" ]; then
+  if [ "${platform}" = "windows" ]; then
     if ! file "${filename}" | grep -q "Zip archive data"; then
       error "Downloaded file is not in zip format. Installation failed."
       error "File type:"
@@ -131,7 +160,7 @@
   info "Extracting ${filename}..."
   extract_dir="doggo_extract"
   mkdir -p "${extract_dir}"
-  if [ "${platform}" = "Windows" ]; then
+  if [ "${platform}" = "windows" ]; then
     if ! unzip -q "${filename}" -d "${extract_dir}"; then
       error "Failed to extract ${filename}"
       rm -rf "${filename}" "${extract_dir}"
@@ -147,7 +176,7 @@
 
   info "Installing doggo..."
   binary_name="doggo"
-  if [ "${platform}" = "Windows" ]; then
+  if [ "${platform}" = "windows" ]; then
     binary_name="doggo.exe"
   fi
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/doggo-1.1.7/internal/app/nameservers.go 
new/doggo-1.2.0/internal/app/nameservers.go
--- old/doggo-1.1.7/internal/app/nameservers.go 2026-05-22 15:05:55.000000000 
+0200
+++ new/doggo-1.2.0/internal/app/nameservers.go 2026-06-23 09:00:28.000000000 
+0200
@@ -5,10 +5,12 @@
        "math/rand"
        "net"
        "net/url"
+       "sort"
        "strings"
        "time"
 
        "github.com/ameshkov/dnsstamps"
+       "github.com/miekg/dns"
        "github.com/mr-karan/doggo/pkg/config"
        "github.com/mr-karan/doggo/pkg/models"
 )
@@ -39,8 +41,11 @@
                }
        }
 
-       // If no nameservers were successfully loaded, fall back to system 
nameservers
+       // If no nameservers were successfully loaded, check for authoritative 
flag
        if len(app.Nameservers) == 0 {
+               if app.QueryFlags.UseAuthoritative && 
len(app.QueryFlags.QNames) > 0 {
+                       return 
app.loadAuthoritativeNameserver(app.QueryFlags.QNames[0])
+               }
                return app.loadSystemNameservers()
        }
 
@@ -455,3 +460,123 @@
 
        return servers, ndots, search, nil
 }
+
+// loadAuthoritativeNameserver finds the closest enclosing zone for the domain
+// via SOA queries, then resolves that zone's delegated NS RRset and adds those
+// nameservers to app.Nameservers.
+//
+// SOA is used only to locate the zone cut. The actual query targets come from
+// the zone's NS records, which are the publicly delegated authoritative
+// servers. We deliberately avoid SOA.Ns (the zone primary/MNAME): it is often
+// an internal hostname that does not resolve publicly (e.g. amazon.com's MNAME
+// is dns-external-route53.us-east-1.amazonaws.com), whereas the delegated NS
+// set is what recursive resolvers actually query.
+func (app *App) loadAuthoritativeNameserver(domain string) error {
+       systemServers, _, _, err := config.GetDefaultServers()
+       if err != nil || len(systemServers) == 0 {
+               return fmt.Errorf("unable to load system nameservers for SOA 
lookup: %w", err)
+       }
+       resolver := net.JoinHostPort(systemServers[0], models.DefaultUDPPort)
+
+       c := &dns.Client{Timeout: 5 * time.Second}
+
+       // Step 1: use SOA to identify the closest enclosing zone (the zone 
cut).
+       zone, err := app.closestZone(c, resolver, dns.Fqdn(domain))
+       if err != nil {
+               return err
+       }
+
+       // Step 2: fetch that zone's delegated NS RRset — the public 
authoritative servers.
+       nsNames, err := app.zoneNameservers(c, resolver, zone)
+       if err != nil {
+               return err
+       }
+
+       servers := make([]models.Nameserver, 0, len(nsNames))
+       for _, name := range nsNames {
+               ns, err := initNameserver(strings.TrimSuffix(name, "."))
+               if err != nil {
+                       app.Logger.Debug("Skipping invalid authoritative 
nameserver", "ns", name, "error", err)
+                       continue
+               }
+               servers = append(servers, ns)
+       }
+       if len(servers) == 0 {
+               return fmt.Errorf("no usable authoritative nameservers found 
for zone %q", strings.TrimSuffix(zone, "."))
+       }
+
+       app.Logger.Debug("Resolved authoritative nameservers via NS RRset", 
"zone", zone, "nameservers", servers)
+
+       // Step 3: let the nameserver strategy select the query target(s).
+       servers, err = app.applyNameserverStrategy(servers, "authoritative")
+       if err != nil {
+               return err
+       }
+
+       app.Nameservers = append(app.Nameservers, servers...)
+       return nil
+}
+
+// closestZone walks up the domain hierarchy issuing SOA queries until it finds
+// the closest enclosing zone, returning that zone's apex as an FQDN.
+func (app *App) closestZone(c *dns.Client, resolver, candidate string) 
(string, error) {
+       domain := candidate
+       for {
+               m := new(dns.Msg)
+               m.SetQuestion(candidate, dns.TypeSOA)
+               m.RecursionDesired = true
+
+               r, _, err := c.Exchange(m, resolver)
+               if err == nil {
+                       if soa := firstSOA(r); soa != nil {
+                               // The SOA owner name is the zone apex 
regardless of the label
+                               // we queried: a recursive resolver returns the 
enclosing zone's
+                               // SOA in the authority section for sub-zone 
names.
+                               return soa.Hdr.Name, nil
+                       }
+               }
+
+               // No SOA found — walk up to the parent zone.
+               labels := dns.SplitDomainName(candidate)
+               if len(labels) <= 1 {
+                       return "", fmt.Errorf("no authoritative zone found for 
%q", strings.TrimSuffix(domain, "."))
+               }
+               candidate = dns.Fqdn(strings.Join(labels[1:], "."))
+       }
+}
+
+// zoneNameservers queries the NS RRset for the given zone and returns the
+// delegated nameserver hostnames, sorted for deterministic selection.
+func (app *App) zoneNameservers(c *dns.Client, resolver, zone string) 
([]string, error) {
+       m := new(dns.Msg)
+       m.SetQuestion(zone, dns.TypeNS)
+       m.RecursionDesired = true
+
+       r, _, err := c.Exchange(m, resolver)
+       if err != nil {
+               return nil, fmt.Errorf("failed to query NS records for zone %q: 
%w", strings.TrimSuffix(zone, "."), err)
+       }
+
+       var names []string
+       for _, rr := range append(r.Answer, r.Ns...) {
+               if ns, ok := rr.(*dns.NS); ok {
+                       names = append(names, ns.Ns)
+               }
+       }
+       if len(names) == 0 {
+               return nil, fmt.Errorf("no NS records found for zone %q", 
strings.TrimSuffix(zone, "."))
+       }
+       sort.Strings(names)
+
+       return names, nil
+}
+
+// firstSOA returns the first SOA record from the answer or authority section.
+func firstSOA(r *dns.Msg) *dns.SOA {
+       for _, rr := range append(r.Answer, r.Ns...) {
+               if soa, ok := rr.(*dns.SOA); ok {
+                       return soa
+               }
+       }
+       return nil
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/doggo-1.1.7/internal/app/nameservers_test.go 
new/doggo-1.2.0/internal/app/nameservers_test.go
--- old/doggo-1.1.7/internal/app/nameservers_test.go    2026-05-22 
15:05:55.000000000 +0200
+++ new/doggo-1.2.0/internal/app/nameservers_test.go    2026-06-23 
09:00:28.000000000 +0200
@@ -3,6 +3,7 @@
 import (
        "io"
        "log/slog"
+       "strings"
        "testing"
 
        "github.com/mr-karan/doggo/pkg/models"
@@ -59,6 +60,71 @@
        }
 }
 
+func TestLoadNameserversExplicitNameserverTakesPrecedenceOverAuthoritative(t 
*testing.T) {
+       app := newTestApp()
+       app.QueryFlags.Nameservers = []string{"1.1.1.1"}
+       app.QueryFlags.UseAuthoritative = true
+       app.QueryFlags.QNames = []string{"github.com"}
+
+       if err := app.LoadNameservers(); err != nil {
+               t.Fatalf("LoadNameservers() error = %v", err)
+       }
+
+       want := []models.Nameserver{
+               {Address: "1.1.1.1:53", Type: models.UDPResolver},
+       }
+       assertNameservers(t, app.Nameservers, want)
+}
+
+func TestLoadAuthoritativeNameserver(t *testing.T) {
+       if testing.Short() {
+               t.Skip("skipping integration test requiring network")
+       }
+       app := newTestApp()
+       app.QueryFlags.UseAuthoritative = true
+       app.QueryFlags.QNames = []string{"github.com"}
+
+       if err := app.LoadNameservers(); err != nil {
+               t.Fatalf("LoadNameservers() error = %v", err)
+       }
+
+       if len(app.Nameservers) == 0 {
+               t.Fatal("expected at least one authoritative nameserver, got 
none")
+       }
+       t.Logf("resolved authoritative NS for github.com: %v", 
app.Nameservers[0].Address)
+}
+
+// TestLoadAuthoritativeNameserverUsesDelegatedNS verifies the resolver targets
+// come from the zone's delegated NS RRset, not the SOA primary (MNAME). 
amazon.com
+// is the canonical case: its MNAME 
(dns-external-route53.us-east-1.amazonaws.com)
+// is not publicly queryable, while its delegated NS set lives under awsdns-*.
+func TestLoadAuthoritativeNameserverUsesDelegatedNS(t *testing.T) {
+       if testing.Short() {
+               t.Skip("skipping integration test requiring network")
+       }
+       app := newTestApp()
+       app.QueryFlags.UseAuthoritative = true
+       app.QueryFlags.QNames = []string{"amazon.com"}
+
+       if err := app.LoadNameservers(); err != nil {
+               t.Fatalf("LoadNameservers() error = %v", err)
+       }
+
+       if len(app.Nameservers) == 0 {
+               t.Fatal("expected at least one authoritative nameserver, got 
none")
+       }
+
+       for _, ns := range app.Nameservers {
+               if strings.Contains(ns.Address, "dns-external-route53") {
+                       t.Fatalf("selected SOA primary (MNAME) instead of 
delegated NS: %v", ns.Address)
+               }
+               if !strings.Contains(ns.Address, "awsdns") {
+                       t.Errorf("expected a delegated awsdns nameserver, got 
%v", ns.Address)
+               }
+       }
+       t.Logf("resolved authoritative NS for amazon.com: %v", app.Nameservers)
+}
+
 func assertNameservers(t *testing.T, got, want []models.Nameserver) {
        t.Helper()
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/doggo-1.1.7/pkg/models/models.go 
new/doggo-1.2.0/pkg/models/models.go
--- old/doggo-1.1.7/pkg/models/models.go        2026-05-22 15:05:55.000000000 
+0200
+++ new/doggo-1.2.0/pkg/models/models.go        2026-06-23 09:00:28.000000000 
+0200
@@ -45,6 +45,7 @@
        InsecureSkipVerify bool          `koanf:"skip-hostname-verification" 
skip-hostname-verification:"-"`
        TLSHostname        string        `koanf:"tls-hostname" tls-hostname:"-"`
        QueryAny           bool          `koanf:"any" json:"any"`
+       UseAuthoritative   bool          `koanf:"authoritative" 
json:"authoritative"`
 
        // DNS Query Flags
        AA bool `koanf:"aa" json:"aa"` // Authoritative Answer

++++++ doggo.obsinfo ++++++
--- /var/tmp/diff_new_pack.VhCD20/_old  2026-06-25 10:56:07.791146684 +0200
+++ /var/tmp/diff_new_pack.VhCD20/_new  2026-06-25 10:56:07.831148064 +0200
@@ -1,5 +1,5 @@
 name: doggo
-version: 1.1.7
-mtime: 1779455155
-commit: b400c464b445c79805a9987475f862357ce7f49d
+version: 1.2.0
+mtime: 1782198028
+commit: 4c3bf3d68955e3988af287cfeb1b7aa41d787fb0
 

++++++ vendor.tar.xz ++++++
++++ 4195 lines of diff (skipped)

Reply via email to