dany

package module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 9, 2026 License: MIT Imports: 12 Imported by: 0

README

Overview

dany is a commandline DNS client that simulates (unreliable/semi-deprecated) dns ANY queries by doing individual typed DNS queries and aggregating the results. Queries are done concurrently for best performance.

Usage

dany [<types>] <hostname>

where <types> is a comma-separated list of DNS record types to query. If unspecified, the default types list is: SOA,NS,A,AAAA,MX,TXT.

In the output, fields are separated by tabs, so you can use cut (with no delimiter) to extract individual fields.

Examples:

$ dany cisco.com
A               72.163.4.185
AAAA            2001:420:1101:1::185
MX      10      alln-mx-01.cisco.com.
MX      20      rcdn-mx-01.cisco.com.
MX      30      aer-mx-01.cisco.com.
NS              ns1.cisco.com.
NS              ns2.cisco.com.
NS              ns3.cisco.com.
SOA             ns1.cisco.com.      postmaster.cisco.com.
TXT             926723159-3188410
TXT             MS=ms35724259
TXT             docusign=5e18de8e-36d0-4a8e-8e88-b7803423fa2f
TXT             docusign=95052c5f-a421-4594-9227-02ad2d86dfbe
TXT             facebook-domain-verification=qr2nigspzrpa96j1nd9criovuuwino
TXT             google-site-verification=K2w--6oeqrFjHfYtTsYyd2tFw7OQd6g5HJDC9UAI8Jk
TXT             google-site-verification=PdOwpBvoBbr90361WK-DzUDRAwNMWd2f4jqgvGKlpWg
TXT             google-site-verification=lW5eqPMJI4VrLc28YW-JBkqA-FDNVnhFCXQVDvFqZTo
TXT             v=spf1 redirect=spfa._spf.cisco.com
TXT             zpSH7Ye/seyY61hH8+Rq5Kb+ZJ9hDa+qeFBaD/6sPAAg+2POkGdP0byHb1pFVK9uZgYF2AIosUSZq4MB17oydQ==

$ dany mx,txt google.com
MX      10      aspmx.l.google.com.
MX      20      alt1.aspmx.l.google.com.
MX      30      alt2.aspmx.l.google.com.
MX      40      alt3.aspmx.l.google.com.
MX      50      alt4.aspmx.l.google.com.
TXT             docusign=05958488-4752-4ef2-95eb-aa7ba8a3bd0e
TXT             facebook-domain-verification=22rm551cu4k0ab0bxsw536tlds4h95
TXT             globalsign-smime-dv=CDYX+XFHUw2wml6/Gb8+59BsH31KzUr6c1l2BPvqKX8=
TXT             v=spf1 include:_spf.google.com ~all

The -w/--www flag additionally probes the www.<hostname> label. By default only A and AAAA are queried for the www label; if -t/--types (or -a/--all) is given explicitly, that set is used for the www probe too. With -T/--tag, apex and www results are emitted on separate lines tagged with their hostname; without it, they share the same output and identical rows are collapsed (so a www that resolves to the same IP as the apex won't appear twice).

$ dany -w -T google.com
google.com              A               172.217.25.110
google.com              AAAA            2404:6800:4006:80d::200e
google.com              MX      10      smtp.google.com.
google.com              NS              ns1.google.com.
google.com              NS              ns2.google.com.
google.com              NS              ns3.google.com.
google.com              NS              ns4.google.com.
google.com              SOA             ns1.google.com. dns-admin.google.com.
google.com              TXT             v=spf1 include:_spf.google.com ~all
www.google.com          A               142.251.150.119
www.google.com          AAAA            2404:6800:4006:80d::2004

The -f/--fmt flag selects the output format: text (default, the tab-separated form shown above), json, or yaml (yml as a convenience alias). In json and yaml modes:

  • Each record carries both rdata (the canonical DNS presentation form, always a string) and data (a typed per-RR-type payload — address for A/AAAA, preference+exchange for MX, full RFC field set for SOA, strings array for TXT, etc.).
  • A schema_version: 1 envelope wraps the per-hostname output. Multi- hostname runs emit NDJSON (one object per line) in json mode and multi-document YAML (----separated) in yaml mode.
  • Errors fold into the envelope's errors field with a stable machine- readable code (NXDOMAIN, SERVFAIL, EXCHANGE_ERROR, …) — nothing goes to stderr.
  • PTR records (when -p/--ptr is set) appear as standalone entries with the source IP carried in data.ip for easy joining (text mode folds them into the matching A/AAAA row instead).
$ dany -f json -t a,mx example.com
{"schema_version":1,"query":{"hostname":"example.com","types":["a","mx"],
"server":"8.8.8.8:53","options":{"www":false,"usd":false,"ptr":false}},
"answers":[{"type":"A","name":"example.com.","ttl":86400,"class":"IN",
"rdata":"96.7.128.198","data":{"address":"96.7.128.198"}}],"errors":[]}

$ dany -f yaml -t mx example.com
---
schema_version: 1
query:
  hostname: example.com
  types:
    - mx
  server: 8.8.8.8:53
  options:
    www: false
    usd: false
    ptr: false
answers:
  - type: MX
    name: example.com.
    ttl: 86400
    class: IN
    rdata: 10 mx.example.com.
    data:
      preference: 10
      exchange: mx.example.com.
errors: []

Author

Gavin Carr gavin@openfusion.net

Licence

MIT. See LICENCE.

Documentation

Index

Constants

View Source
const SchemaVersion = 1

SchemaVersion is the JSON output schema version, bumped only for backwards-incompatible changes. Additive changes (new optional fields, new RR types, new error codes) do not bump it.

Variables

View Source
var DefaultRRTypes = []string{"A", "AAAA", "MX", "NS", "SOA", "TXT"}
View Source
var ErrNXDomain = errors.New("NXDOMAIN")
View Source
var ErrServFail = errors.New("SERVFAIL")
View Source
var NXTypes = []string{
	"MX", "NS", "SOA",
}
View Source
var SupportedRRTypes = []string{
	"A", "AAAA", "CAA", "CNAME", "DNSKEY", "MX", "NS", "NSEC", "RRSIG", "SOA", "SRV", "TXT",
}
View Source
var SupportedUSDs = []string{
	"_dmarc", "_domainkey", "_mta-sts",
}

Functions

func Render

func Render(answers []Answer, tagHostname bool) string

Render turns a slice of Answers into the canonical dany text output: one tab-separated `\n`-terminated line per non-PTR Answer, with PTR records folded into their corresponding A/AAAA lines, sorted globally. Exact-duplicate lines are collapsed (relevant when --www queries surface IPs the apex already returned). If tagHostname is true each line is prefixed with the queried hostname and a tab — useful when caller is multiplexing multiple hostnames. Render does no I/O.

func RenderJSON

func RenderJSON(answers []Answer, q *Query, errs []error) string

RenderJSON serializes BuildOutput as a single JSON object terminated by a newline — i.e. ready to concatenate into NDJSON across multiple hostname invocations.

func RenderYAML

func RenderYAML(answers []Answer, q *Query, errs []error) string

RenderYAML serializes BuildOutput as one YAML document prefixed with `---\n`. Concatenating multiple RenderYAML outputs across hostnames yields a well-formed multi-document YAML stream.

Output shape is the same Output envelope used by RenderJSON; the yaml struct tags on the *Data types are kept in lockstep with the json tags (snake_case, same field names) so consumers can swap formats without schema surprises.

func RunNXQuery

func RunNXQuery(q *Query) int

RunNXQuery probes q.Hostname across multiple RR types and returns the count of probes that did NOT return NXDOMAIN — i.e. len(types) - nxcount, where types is q.Types if non-empty else NXTypes.

Interpretation:

  • 0 — every probe returned NXDOMAIN; hostname is fully NX.
  • len(types) — no probe returned NXDOMAIN (either real answers, other errors, or timeouts; we can't distinguish these here).
  • in between — partial NXDOMAIN; treated as not-NX by callers (dnx).

Probes that time out or error with anything other than ErrNXDomain are counted as non-NX, so transient failures bias toward "not NX" rather than false-positive NXDOMAIN reports.

NX probes intentionally stay on UDP regardless of q.Udp — responses are tiny and there is no benefit to TCP for this codepath.

Types

type AAAAData

type AAAAData struct {
	Address string `json:"address" yaml:"address"`
}

type AData

type AData struct {
	Address string `json:"address" yaml:"address"`
}

type Answer

type Answer struct {
	Type     string
	Hostname string
	RR       dns.RR
	Empty    bool // present-empty (NODATA); RR is nil when true
}

Answer is a single DNS resource record returned from a typed query. Type is the queried RR type, uppercase ("A", "MX", "TXT", ...) — plus "PTR" for reverse-lookups synthesised when q.Ptr is set. Hostname is the queried hostname (e.g. "example.com", or "_dmarc.example.com" for USD probes; for PTR Answers it is the IP whose PTR was looked up, in dotted/colon form). RR is the raw record from miekg/dns.

func RunQuery

func RunQuery(q *Query) ([]Answer, []error)

RunQuery fans out concurrent typed DNS lookups for q.Hostname across q.Types (plus USD TXT probes if q.Usd is set, plus A/AAAA probes against www.<q.Hostname> if q.Www is set, plus PTR follow-ups for A/AAAA if q.Ptr is set) and returns the collected Answers and per-query errors. The returned slice is unsorted; pass it through Render for the canonical tab-separated text representation.

On wall-clock timeout (timeoutSeconds), in-flight goroutines' results are silently dropped — they appear as neither Answers nor errors.

type CAAData

type CAAData struct {
	Flag  uint8  `json:"flag"  yaml:"flag"`
	Tag   string `json:"tag"   yaml:"tag"`
	Value string `json:"value" yaml:"value"`
}

type CNAMEData

type CNAMEData struct {
	Target string `json:"target" yaml:"target"`
}

type DNSKEYData

type DNSKEYData struct {
	Flags     uint16 `json:"flags"      yaml:"flags"`
	Protocol  uint8  `json:"protocol"   yaml:"protocol"`
	Algorithm uint8  `json:"algorithm"  yaml:"algorithm"`
	PublicKey string `json:"public_key" yaml:"public_key"`
}

type MXData

type MXData struct {
	Preference uint16 `json:"preference" yaml:"preference"`
	Exchange   string `json:"exchange"   yaml:"exchange"`
}

type NSData

type NSData struct {
	Target string `json:"target" yaml:"target"`
}

type NSECData

type NSECData struct {
	NextDomain string   `json:"next_domain" yaml:"next_domain"`
	Types      []string `json:"types"       yaml:"types"`
}

type Output

type Output struct {
	SchemaVersion int            `json:"schema_version" yaml:"schema_version"`
	Query         OutputQuery    `json:"query"          yaml:"query"`
	Answers       []OutputAnswer `json:"answers"        yaml:"answers"`
	Errors        []OutputError  `json:"errors"         yaml:"errors"`
}

Output is the top-level envelope returned per hostname. Used as the data model for every renderer (JSON, YAML, ...) — see render_json.go / render_yaml.go for the format-specific wrappers around BuildOutput. Stable shape; new fields may be added but existing fields must not be renamed or removed without bumping SchemaVersion.

func BuildOutput

func BuildOutput(answers []Answer, q *Query, errs []error) *Output

BuildOutput assembles the typed Output envelope from a RunQuery result. Answers and Errors are sorted into a stable total order (see below) so consecutive runs render identically despite RunQuery's nondeterministic concurrent arrival order. The marshaling step (json/yaml/...) is the caller's concern — see RenderJSON for the canonical NDJSON wrapper.

type OutputAnswer

type OutputAnswer struct {
	Type         string      `json:"type"          yaml:"type"`
	Name         string      `json:"name"          yaml:"name"`
	TTL          uint32      `json:"ttl"           yaml:"ttl"`
	Class        string      `json:"class"         yaml:"class"`
	Rdata        string      `json:"rdata"         yaml:"rdata"`
	Data         interface{} `json:"data"          yaml:"data"`
	PresentEmpty bool        `json:"present_empty,omitempty" yaml:"present_empty,omitempty"`
}

OutputAnswer is one DNS resource record. Rdata is the canonical DNS presentation form (always present); Data is the typed per-RR payload — see the per-type *Data structs for shapes.

type OutputError

type OutputError struct {
	Type     string `json:"type,omitempty"     yaml:"type,omitempty"`
	Hostname string `json:"hostname,omitempty" yaml:"hostname,omitempty"`
	Code     string `json:"code"               yaml:"code"`
	Message  string `json:"message"            yaml:"message"`
}

OutputError mirrors QueryError for the envelope. Code is a stable machine-readable identifier (e.g. "NXDOMAIN", "SERVFAIL", "EXCHANGE_ERROR", "UNSUPPORTED_TYPE", or "UNKNOWN" for errors lacking a QueryError wrapper). Message preserves the human-readable text.

type OutputOptions

type OutputOptions struct {
	Www bool `json:"www" yaml:"www"`
	Usd bool `json:"usd" yaml:"usd"`
	Ptr bool `json:"ptr" yaml:"ptr"`
}

type OutputQuery

type OutputQuery struct {
	Hostname string        `json:"hostname" yaml:"hostname"`
	Types    []string      `json:"types"    yaml:"types"`
	Server   string        `json:"server"   yaml:"server"`
	Options  OutputOptions `json:"options"  yaml:"options"`
}

type PTRData

type PTRData struct {
	Target string `json:"target"        yaml:"target"`
	IP     string `json:"ip,omitempty"  yaml:"ip,omitempty"`
}

PTRData carries Target (the resolved hostname) and IP (the original IP the PTR was queried for, recovered from the in-addr.arpa/ip6.arpa name). IP is empty for PTR records that aren't reverse lookups of an IP.

type Query

type Query struct {
	Hostname     string
	Types        []string
	Resolvers    *Resolvers
	Server       string
	IgnoreErrors bool
	Udp          bool
	Ptr          bool
	Usd          bool
	Www          bool
	// WwwTypes, when non-empty, overrides the default www-probe type set
	// (A, AAAA). Only consulted when Www is true. Callers that want the
	// www. probe to mirror the user's explicit -t/--types selection set
	// this; callers that want the address-only default leave it nil.
	WwwTypes []string
	Tag      bool
}

dany Query - lookup Types for Hostname using Server

type QueryError

type QueryError struct {
	Type     string
	Hostname string
	Code     string
	Err      error
}

QueryError is the structured error type emitted by RunQuery / RunNXQuery goroutines. It carries the RR type and hostname that were being queried plus a stable string Code (DNS rcode names like "NXDOMAIN"/"SERVFAIL", plus "EXCHANGE_ERROR" for transport-level failures and "UNSUPPORTED_TYPE" for unknown RR types) so structured consumers — notably the JSON renderer — can act on errors without parsing the message.

Error() preserves the historical `Error on <type> lookup for "<host>": <…>` format so legacy text consumers and existing substring-based tests keep working. Unwrap exposes the underlying error so errors.Is(err, ErrNXDomain) / ErrServFail continue to match.

func (*QueryError) Error

func (e *QueryError) Error() string

func (*QueryError) Unwrap

func (e *QueryError) Unwrap() error

type RRSIGData

type RRSIGData struct {
	TypeCovered string `json:"type_covered" yaml:"type_covered"`
	Algorithm   uint8  `json:"algorithm"    yaml:"algorithm"`
	Labels      uint8  `json:"labels"       yaml:"labels"`
	OriginalTTL uint32 `json:"original_ttl" yaml:"original_ttl"`
	Expiration  string `json:"expiration"   yaml:"expiration"`
	Inception   string `json:"inception"    yaml:"inception"`
	KeyTag      uint16 `json:"key_tag"      yaml:"key_tag"`
	SignerName  string `json:"signer_name"  yaml:"signer_name"`
	Signature   string `json:"signature"    yaml:"signature"`
}

type Resolvers

type Resolvers struct {
	List   []net.IP
	Length int
	Index  int
}

List of Resolver ips

func LoadResolvers

func LoadResolvers(filename string) (*Resolvers, error)

func NewResolvers

func NewResolvers(ip net.IP) *Resolvers

func (*Resolvers) Append

func (r *Resolvers) Append(ip net.IP)

func (*Resolvers) Next

func (r *Resolvers) Next() net.IP

type SOAData

type SOAData struct {
	MName   string `json:"mname"   yaml:"mname"`
	RName   string `json:"rname"   yaml:"rname"`
	Serial  uint32 `json:"serial"  yaml:"serial"`
	Refresh uint32 `json:"refresh" yaml:"refresh"`
	Retry   uint32 `json:"retry"   yaml:"retry"`
	Expire  uint32 `json:"expire"  yaml:"expire"`
	Minimum uint32 `json:"minimum" yaml:"minimum"`
}

type SRVData

type SRVData struct {
	Priority uint16 `json:"priority" yaml:"priority"`
	Weight   uint16 `json:"weight"   yaml:"weight"`
	Port     uint16 `json:"port"     yaml:"port"`
	Target   string `json:"target"   yaml:"target"`
}

type TXTData

type TXTData struct {
	Strings []string `json:"strings" yaml:"strings"`
}

TXTData exposes the raw multi-string form (TXT can legitimately carry multiple character-strings). Consumers wanting the concatenated text can join them; Rdata also carries the presentation form.

Directories

Path Synopsis
cmd
dany command
dnx command
internal
testdns
Package testdns is a tiny in-process DNS server for tests.
Package testdns is a tiny in-process DNS server for tests.
version
Package version exposes the current dany release version, embedded from VERSION at compile time.
Package version exposes the current dany release version, embedded from VERSION at compile time.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL