Skip to content

Commit

Permalink
refactor: reformat output system
Browse files Browse the repository at this point in the history
  • Loading branch information
natesales committed Sep 7, 2023
1 parent 96c59bb commit ae2f695
Show file tree
Hide file tree
Showing 10 changed files with 456 additions and 379 deletions.
69 changes: 69 additions & 0 deletions cli/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package cli

import "time"

type Flags struct {
Name string `short:"q" long:"qname" description:"Query name"`
Server string `short:"s" long:"server" description:"DNS server"`
Types []string `short:"t" long:"type" description:"RR type (e.g. A, AAAA, MX, etc.) or type integer"`
Reverse bool `short:"x" long:"reverse" description:"Reverse lookup"`
DNSSEC bool `short:"d" long:"dnssec" description:"Set the DO (DNSSEC OK) bit in the OPT record"`
NSID bool `short:"n" long:"nsid" description:"Set EDNS0 NSID opt"`
ClientSubnet string `long:"subnet" description:"Set EDNS0 client subnet"`
Chaos bool `short:"c" long:"chaos" description:"Use CHAOS query class"`
Class uint16 `short:"C" description:"Set query class (default: IN 0x01)" default:"1"`
ODoHProxy string `short:"p" long:"odoh-proxy" description:"ODoH proxy"`
Timeout time.Duration `long:"timeout" description:"Query timeout" default:"10s"`
Pad bool `long:"pad" description:"Set EDNS0 padding"`
HTTP3 bool `long:"http3" description:"Use HTTP/3 for DoH"`
NoIDCheck bool `long:"no-id-check" description:"Disable checking of DNS response ID"`
NoReuseConn bool `long:"no-reuse-conn" description:"Use a new connection for each query"`

RecAXFR bool `long:"recaxfr" description:"Perform recursive AXFR"`

// Output
Format string `short:"f" long:"format" description:"Output format (pretty, json, yaml, raw)" default:"pretty"`
PrettyTTLs bool `long:"pretty-ttls" description:"Format TTLs in human readable format (default: true)"`
Color bool `long:"color" description:"Enable color output"`
ShowQuestion bool `long:"question" description:"Show question section"`
ShowAnswer bool `long:"answer" description:"Show answer section (default: true)"`
ShowAuthority bool `long:"authority" description:"Show authority section"`
ShowAdditional bool `long:"additional" description:"Show additional section"`
ShowStats bool `short:"S" long:"stats" description:"Show time statistics"`
ShowAll bool `long:"all" description:"Show all sections and statistics"`
Whois bool `short:"w" description:"Resolve ASN/ASName for A and AAAA records"`
ValueOnly bool `short:"r" long:"value" description:"Show record values only"`

// Header flags
AuthoritativeAnswer bool `long:"aa" description:"Set AA (Authoritative Answer) flag in query"`
AuthenticData bool `long:"ad" description:"Set AD (Authentic Data) flag in query"`
CheckingDisabled bool `long:"cd" description:"Set CD (Checking Disabled) flag in query"`
RecursionDesired bool `long:"rd" description:"Set RD (Recursion Desired) flag in query (default: true)"`
RecursionAvailable bool `long:"ra" description:"Set RA (Recursion Available) flag in query"`
Zero bool `long:"z" description:"Set Z (Zero) flag in query"`
Truncated bool `long:"t" description:"Set TC (Truncated) flag in query"`

// TCP parameters
TLSNoVerify bool `short:"i" long:"tls-no-verify" description:"Disable TLS certificate verification"`
TLSServerName string `long:"tls-server-name" description:"TLS server name for host verification"`
TLSMinVersion string `long:"tls-min-version" description:"Minimum TLS version to use" default:"1.0"`
TLSMaxVersion string `long:"tls-max-version" description:"Maximum TLS version to use" default:"1.3"`
TLSNextProtos []string `long:"tls-next-protos" description:"TLS next protocols for ALPN"`
TLSCipherSuites []string `long:"tls-cipher-suites" description:"TLS cipher suites"`

// HTTP
HTTPUserAgent string `long:"http-user-agent" description:"HTTP user agent" default:""`
HTTPMethod string `long:"http-method" description:"HTTP method" default:"GET"`

// QUIC
QUICALPNTokens []string `long:"quic-alpn-tokens" description:"QUIC ALPN tokens" default:"doq" default:"doq-i11"`
QUICNoPMTUD bool `long:"quic-no-pmtud" description:"Disable QUIC PMTU discovery"`
QUICNoLengthPrefix bool `long:"quic-no-length-prefix" description:"Don't add RFC 9250 compliant length prefix"`

DefaultRRTypes []string `long:"default-rr-types" description:"Default record types" default:"A" default:"AAAA" default:"NS" default:"MX" default:"TXT" default:"CNAME"`

UDPBuffer uint16 `long:"udp-buffer" description:"Set EDNS0 UDP size in query" default:"1232"`
Verbose bool `short:"v" long:"verbose" description:"Show verbose log messages"`
Trace bool `long:"trace" description:"Show trace log messages"`
ShowVersion bool `short:"V" long:"version" description:"Show version and exit"`
}
109 changes: 37 additions & 72 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,82 +11,21 @@ import (
"strings"
"time"

"github.com/natesales/q/output"

"github.com/natesales/q/util"

"github.com/jedisct1/go-dnsstamps"
"github.com/jessevdk/go-flags"
"github.com/miekg/dns"
log "github.com/sirupsen/logrus"

"github.com/natesales/q/cli"
)

const defaultServerVar = "Q_DEFAULT_SERVER"

// CLI flags
type optsTemplate struct {
Name string `short:"q" long:"qname" description:"Query name"`
Server string `short:"s" long:"server" description:"DNS server"`
Types []string `short:"t" long:"type" description:"RR type (e.g. A, AAAA, MX, etc.) or type integer"`
Reverse bool `short:"x" long:"reverse" description:"Reverse lookup"`
DNSSEC bool `short:"d" long:"dnssec" description:"Set the DO (DNSSEC OK) bit in the OPT record"`
NSID bool `short:"n" long:"nsid" description:"Set EDNS0 NSID opt"`
ClientSubnet string `long:"subnet" description:"Set EDNS0 client subnet"`
Chaos bool `short:"c" long:"chaos" description:"Use CHAOS query class"`
Class uint16 `short:"C" description:"Set query class (default: IN 0x01)" default:"1"`
ODoHProxy string `short:"p" long:"odoh-proxy" description:"ODoH proxy"`
Timeout time.Duration `long:"timeout" description:"Query timeout" default:"10s"`
Pad bool `long:"pad" description:"Set EDNS0 padding"`
HTTP3 bool `long:"http3" description:"Use HTTP/3 for DoH"`
NoIDCheck bool `long:"no-id-check" description:"Disable checking of DNS response ID"`
NoReuseConn bool `long:"no-reuse-conn" description:"Use a new connection for each query"`

RecAXFR bool `long:"recaxfr" description:"Perform recursive AXFR"`

// Output
Format string `short:"f" long:"format" description:"Output format (pretty, json, yaml, raw)" default:"pretty"`
PrettyTTLs bool `long:"pretty-ttls" description:"Format TTLs in human readable format (default: true)"`
Color bool `long:"color" description:"Enable color output"`
ShowQuestion bool `long:"question" description:"Show question section"`
ShowAnswer bool `long:"answer" description:"Show answer section (default: true)"`
ShowAuthority bool `long:"authority" description:"Show authority section"`
ShowAdditional bool `long:"additional" description:"Show additional section"`
ShowStats bool `short:"S" long:"stats" description:"Show time statistics"`
ShowAll bool `long:"all" description:"Show all sections and statistics"`
Whois bool `short:"w" description:"Resolve ASN/ASName for A and AAAA records"`
ValueOnly bool `short:"r" long:"value" description:"Show record values only"`

// Header flags
AuthoritativeAnswer bool `long:"aa" description:"Set AA (Authoritative Answer) flag in query"`
AuthenticData bool `long:"ad" description:"Set AD (Authentic Data) flag in query"`
CheckingDisabled bool `long:"cd" description:"Set CD (Checking Disabled) flag in query"`
RecursionDesired bool `long:"rd" description:"Set RD (Recursion Desired) flag in query (default: true)"`
RecursionAvailable bool `long:"ra" description:"Set RA (Recursion Available) flag in query"`
Zero bool `long:"z" description:"Set Z (Zero) flag in query"`
Truncated bool `long:"t" description:"Set TC (Truncated) flag in query"`

// TCP parameters
TLSNoVerify bool `short:"i" long:"tls-no-verify" description:"Disable TLS certificate verification"`
TLSServerName string `long:"tls-server-name" description:"TLS server name for host verification"`
TLSMinVersion string `long:"tls-min-version" description:"Minimum TLS version to use" default:"1.0"`
TLSMaxVersion string `long:"tls-max-version" description:"Maximum TLS version to use" default:"1.3"`
TLSNextProtos []string `long:"tls-next-protos" description:"TLS next protocols for ALPN"`
TLSCipherSuites []string `long:"tls-cipher-suites" description:"TLS cipher suites"`

// HTTP
HTTPUserAgent string `long:"http-user-agent" description:"HTTP user agent" default:""`
HTTPMethod string `long:"http-method" description:"HTTP method" default:"GET"`

// QUIC
QUICALPNTokens []string `long:"quic-alpn-tokens" description:"QUIC ALPN tokens" default:"doq" default:"doq-i11"`
QUICNoPMTUD bool `long:"quic-no-pmtud" description:"Disable QUIC PMTU discovery"`
QUICNoLengthPrefix bool `long:"quic-no-length-prefix" description:"Don't add RFC 9250 compliant length prefix"`

DefaultRRTypes []string `long:"default-rr-types" description:"Default record types" default:"A" default:"AAAA" default:"NS" default:"MX" default:"TXT" default:"CNAME"`

UDPBuffer uint16 `long:"udp-buffer" description:"Set EDNS0 UDP size in query" default:"1232"`
Verbose bool `short:"v" long:"verbose" description:"Show verbose log messages"`
Trace bool `long:"trace" description:"Show trace log messages"`
ShowVersion bool `short:"V" long:"version" description:"Show version and exit"`
}

var opts = optsTemplate{}
var opts = cli.Flags{}

// Build process flags
var (
Expand Down Expand Up @@ -141,7 +80,7 @@ func parseTLSCipherSuites(cipherSuites []string) []uint16 {

// clearOpts sets the default values for the CLI options
func clearOpts() {
opts = optsTemplate{}
opts = cli.Flags{}
opts.RecursionDesired = true
opts.ShowAnswer = true
opts.PrettyTTLs = true
Expand All @@ -156,6 +95,7 @@ func clearOpts() {
log.Debug("NO_COLOR set")
opts.Color = false
}
util.UseColor = opts.Color
}

// tlsVersion returns a TLS version number by given protocol string
Expand Down Expand Up @@ -345,6 +285,7 @@ All long form (--) flags can be toggled with the dig-standard +[no]flag notation
os.Exit(1)
}
parsePlusFlags(args)
util.UseColor = opts.Color

if opts.Verbose {
log.SetLevel(log.DebugLevel)
Expand All @@ -354,7 +295,7 @@ All long form (--) flags can be toggled with the dig-standard +[no]flag notation
}

if opts.ShowVersion {
mustWritef(out, "https://github.com/natesales/q version %s (%s %s)\n", version, commit, date)
util.MustWritef(out, "https://github.com/natesales/q version %s (%s %s)\n", version, commit, date)
return nil
}

Expand Down Expand Up @@ -402,7 +343,7 @@ All long form (--) flags can be toggled with the dig-standard +[no]flag notation

// Set qname if not set by flag
if opts.Name == "" &&
!containsAny(arg, []string{"@", "/", "\\", "+"}) && // Not a server, path, or flag
!util.ContainsAny(arg, []string{"@", "/", "\\", "+"}) && // Not a server, path, or flag
!typeFound && // Not a RR type
!strings.HasSuffix(arg, ".exe") && // Not an executable
!strings.HasPrefix(arg, "-") { // Not a flag
Expand Down Expand Up @@ -541,7 +482,31 @@ All long form (--) flags can be toggled with the dig-standard +[no]flag notation
}
queryTime := time.Since(startTime)

return display(replies, server, queryTime, out)
if opts.NSID && opts.Format == "pretty" {
output.PrettyPrintNSID(replies, out)
}

printer := output.Printer{
Server: server,
Out: out,
Opts: &opts,
QueryTime: queryTime,
NumReplies: len(replies),
}
for i, reply := range replies {
switch opts.Format {
case "pretty":
printer.PrintPretty(i, reply)
case "raw":
printer.PrintRaw(i, reply)
case "json", "yml", "yaml":
printer.PrintStructured(i, reply)
default:
return fmt.Errorf("invalid output format")
}
}

return nil
}

func main() {
Expand Down
Loading

0 comments on commit ae2f695

Please sign in to comment.