Skip to content
Open
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
8 changes: 8 additions & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ type Flags struct {
// Special query modes
RecAXFR bool `long:"recaxfr" description:"Perform recursive AXFR"`

// Registry lookup (RDAP / port-43 WHOIS) — distinct from -w (bgptools ASN enrichment)
Registry bool `short:"g" long:"registry" description:"Resolve target via RDAP with WHOIS fallback"`
RegistryRDAP bool `long:"registry-rdap" description:"Resolve target via RDAP only"`
RDAPServer string `long:"rdap-server" description:"RDAP base URL override (skips bootstrap)"`
RIR string `long:"rir" description:"Force RIR for registry lookups: iana, arin, ripe, apnic, lacnic, afrinic" default:"iana"`
RegistryWhois bool `long:"registry-whois" description:"Resolve target via port-43 WHOIS only"`
WhoisServer string `long:"whois-server" description:"Port-43 WHOIS server override (host or host:port)"`

// Output
Format string `short:"f" long:"format" description:"Output format (pretty, column, json, yaml, raw)" default:"pretty"`
PrettyTTLs bool `long:"pretty-ttls" description:"Format TTLs in human readable format (default: true)"`
Expand Down
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/json-iterator/go v1.1.12
github.com/miekg/dns v1.1.72
github.com/natesales/bgptools-go v0.0.0-20230212051756-2b519d61269c
github.com/openrdap/rdap v0.9.1
github.com/quic-go/quic-go v0.59.0
github.com/sthorne/odoh-go v1.0.4
github.com/stretchr/testify v1.11.1
Expand All @@ -19,6 +20,8 @@ require (

require (
github.com/AdguardTeam/golibs v0.35.9 // indirect
github.com/alecthomas/kingpin/v2 v2.3.2 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/ameshkov/dnsstamps v1.0.3 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/colorprofile v0.4.2 // indirect
Expand All @@ -37,12 +40,14 @@ require (
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.20 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.uber.org/mock v0.6.0 // indirect
golang.org/x/crypto v0.48.0 // indirect
Expand Down
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
git.schwanenlied.me/yawning/x448.git v0.0.0-20170617130356-01b048fb03d6/go.mod h1:wQaGCqEu44ykB17jZHCevrgSVl3KJnwQBObUtrKU4uU=
github.com/AdguardTeam/golibs v0.35.9 h1:KmmPBgE+PnXmxd+sp5fACRjU6oYT8r7okWEU3L9B6Qo=
github.com/AdguardTeam/golibs v0.35.9/go.mod h1:kuLQ0yNRTl0Em2FmmXtSri7ZdVT7p62oojyc51RvP38=
github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU=
github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/ameshkov/dnscrypt/v2 v2.4.0 h1:if6ZG2cuQmcP2TwSY+D0+8+xbPfoatufGlOQTMNkI9o=
github.com/ameshkov/dnscrypt/v2 v2.4.0/go.mod h1:WpEFV2uhebXb8Jhes/5/fSdpmhGV8TL22RDaeWwV6hI=
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
Expand Down Expand Up @@ -40,6 +44,8 @@ github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
github.com/jedisct1/go-dnsstamps v0.0.0-20251112173516-191fc465df31 h1:O2qUxpVUeJxoBU5FJ0vGD03g5bY9uRrouaLlJeVOHr8=
github.com/jedisct1/go-dnsstamps v0.0.0-20251112173516-191fc465df31/go.mod h1:mEGEFZsGe4sG5Mb3Xi89pmsy+TZ0946ArbYMGKAM5uA=
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
Expand All @@ -58,6 +64,8 @@ github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjc
github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
Expand All @@ -67,6 +75,8 @@ github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/natesales/bgptools-go v0.0.0-20230212051756-2b519d61269c h1:bblm7D7Ld1/zkWvMU1j60lMk5h/F4AiKW23j1yffM5Y=
github.com/natesales/bgptools-go v0.0.0-20230212051756-2b519d61269c/go.mod h1:jl8YnQACciyOXRgNIRhURrCF9FmRHjnfT8UCj3LBkyY=
github.com/openrdap/rdap v0.9.1 h1:Rv6YbanbiVPsKRvOLdUmlU1AL5+2OFuEFLjFN+mQsCM=
github.com/openrdap/rdap v0.9.1/go.mod h1:vKSiotbsENrjM/vaHXLddXbW8iQkBfa+ldEuYEjyLTQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
Expand All @@ -81,9 +91,12 @@ github.com/sthorne/odoh-go v1.0.4 h1:RPJceVs/dIpNE3gyzk6wdSay+nR+tI1YXBUwykvCEXI
github.com/sthorne/odoh-go v1.0.4/go.mod h1:KdB/NGiepr9bLVs3k26uWl4HHPHqa2DaoPUgUfKNmJU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
Expand Down Expand Up @@ -119,6 +132,7 @@ golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
28 changes: 28 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/natesales/q/cli"
"github.com/natesales/q/output"
"github.com/natesales/q/registry"
"github.com/natesales/q/transport"
"github.com/natesales/q/util"
tlsutil "github.com/natesales/q/util/tls"
Expand Down Expand Up @@ -347,6 +348,33 @@ All long form (--) flags can be toggled with the dig-standard +[no]flag notation
}
}

// Registry lookup (RDAP / port-43 WHOIS)
// rdap first, whois fallback, autoresolve LIR via -SUFFIX
if opts.Registry || opts.RegistryRDAP || opts.RegistryWhois {
if opts.Name == "" {
return fmt.Errorf("registry lookup: target required")
}
useRDAP := opts.RegistryRDAP || opts.Registry
useWHOIS := opts.RegistryWhois || opts.Registry
ctx, cancel := context.WithTimeout(context.Background(), opts.Timeout)
defer cancel()
text, err := registry.Query(ctx, registry.Options{
Target: opts.Name,
UseRDAP: useRDAP,
UseWHOIS: useWHOIS,
RDAPServer: opts.RDAPServer,
WhoisServer: opts.WhoisServer,
RIR: strings.ToLower(opts.RIR),
Format: opts.Format,
Timeout: opts.Timeout,
})
if err != nil {
return err
}
util.MustWritef(out, "%s\n", text)
return nil
}

// If no RR types are defined, set a list of default ones
if len(rrTypes) < 1 {
if opts.Name == "" {
Expand Down
84 changes: 84 additions & 0 deletions registry/rdap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package registry

import (
"context"
"encoding/json"
"fmt"
"net/url"
"strings"

"github.com/openrdap/rdap"
)

// queryRDAP performs an RDAP lookup using github.com/openrdap/rdap.
// When opts.RDAPServer or a non-IANA RIR is set, the server URL is supplied
// directly on the request, bypassing bootstrap.
func queryRDAP(ctx context.Context, opts Options) (string, error) {
kind := Classify(opts.Target)

req, err := buildRDAPRequest(kind, opts.Target)
if err != nil {
return "", err
}

base := opts.RDAPServer
if base == "" {
base = rirRDAPBase(opts.RIR)
}
if base != "" {
serverURL, err := url.Parse(base)
if err != nil {
return "", fmt.Errorf("registry: parse rdap server %q: %w", base, err)
}
req = req.WithServer(serverURL)
}
req = req.WithContext(ctx)
req.Timeout = opts.Timeout

client := &rdap.Client{}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("registry: rdap: %w", err)
}
if resp == nil || resp.Object == nil {
return "", fmt.Errorf("registry: rdap: empty response")
}

return formatRDAP(resp, opts.Format)
}

func buildRDAPRequest(kind Kind, target string) (*rdap.Request, error) {
switch kind {
case KindDomain:
return &rdap.Request{Type: rdap.DomainRequest, Query: target}, nil
case KindIPv4, KindIPv6:
return &rdap.Request{Type: rdap.IPRequest, Query: target}, nil
case KindASN:
num := strings.TrimPrefix(strings.ToUpper(target), "AS")
return &rdap.Request{Type: rdap.AutnumRequest, Query: num}, nil
case KindNICHandle:
return &rdap.Request{Type: rdap.EntityRequest, Query: target}, nil
}
return nil, fmt.Errorf("registry: cannot classify rdap target %q", target)
}

func formatRDAP(resp *rdap.Response, format string) (string, error) {
switch strings.ToLower(format) {
case "json", "raw":
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: RDAP formatter ignores yaml and returns pretty/JSON output despite CLI advertising YAML support.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At registry/rdap.go, line 67:

<comment>RDAP formatter ignores `yaml` and returns pretty/JSON output despite CLI advertising YAML support.</comment>

<file context>
@@ -0,0 +1,84 @@
+
+func formatRDAP(resp *rdap.Response, format string) (string, error) {
+	switch strings.ToLower(format) {
+	case "json", "raw":
+		b, err := json.MarshalIndent(resp.Object, "", "  ")
+		if err != nil {
</file context>
Fix with Cubic

b, err := json.MarshalIndent(resp.Object, "", " ")
if err != nil {
return "", fmt.Errorf("registry: marshal rdap: %w", err)
}
return string(b), nil
}

if pretty := formatRDAPPretty(resp); pretty != "" {
return pretty, nil
}

b, err := json.MarshalIndent(resp.Object, "", " ")
if err != nil {
return "", fmt.Errorf("registry: marshal rdap: %w", err)
}
return string(b), nil
}
Loading