diff --git a/conf/plan9.go b/conf/plan9.go index 350394c..0b19102 100644 --- a/conf/plan9.go +++ b/conf/plan9.go @@ -13,6 +13,7 @@ import ( ) // GetDNSConfig gets DNS information from Plan 9, because it's different from UNIX and Windows. +// // Plan 9 stores its network data in /net/ndb, which seems to be formatted a specific way // Yoink it and use it. // diff --git a/conf/wasm.go b/conf/wasm.go new file mode 100644 index 0000000..3deeef0 --- /dev/null +++ b/conf/wasm.go @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: BSD-3-Clause +//go:build js + +package conf + +import ( + "errors" + + "github.com/miekg/dns" +) + +// GetDNSConfig doesn't do anything, because it is impossible (and bad security) +// if it could, as that is the definition of a DNS leak. +func GetDNSConfig() (*dns.ClientConfig, error) { + return nil, errNotImplemented +} + +var errNotImplemented = errors.New("not implemented") diff --git a/go.mod b/go.mod index be3137f..9f7fcb1 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/google/go-cmp v0.5.8 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect - golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 // indirect ) require ( diff --git a/go.sum b/go.sum index 7944a46..f7afb59 100644 --- a/go.sum +++ b/go.sum @@ -81,6 +81,8 @@ golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5 golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= diff --git a/main.go b/main.go index bf737af..641deac 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,5 @@ // SPDX-License-Identifier: BSD-3-Clause +//go:build !js package main diff --git a/query/general_test.go b/query/general_test.go index 9243a7a..312cf60 100644 --- a/query/general_test.go +++ b/query/general_test.go @@ -36,10 +36,7 @@ func TestResolve(t *testing.T) { assert.ErrorContains(t, err, "timeout") } -// FIXME: See when this is fixed and fix it. - func TestTruncate(t *testing.T) { - t.Skip("Sadly this is broken.") t.Parallel() opts := util.Options{ diff --git a/query/print.go b/query/print.go index 0c81761..5eaa6bf 100644 --- a/query/print.go +++ b/query/print.go @@ -3,6 +3,7 @@ package query import ( + "encoding/hex" "encoding/json" "encoding/xml" "errors" @@ -178,7 +179,7 @@ func MakePrintable(msg *dns.Msg, opts util.Options) (*Message, error) { name = additional.Header().Name } - ret.Extra = append(ret.Extra, Answer{ + ret.Additional = append(ret.Additional, Answer{ RRHeader: RRHeader{ Name: name, Type: dns.TypeToString[additional.Header().Rrtype], @@ -191,7 +192,133 @@ func MakePrintable(msg *dns.Msg, opts util.Options) (*Message, error) { } } + opt := msg.IsEdns0() + if opt != nil && opts.Display.Opt { + ret.Opt, err = parseOpt(*opt) + if err != nil { + return nil, fmt.Errorf("edns print: %w", err) + } + } + return &ret, nil } +func parseOpt(rr dns.OPT) ([]Opts, error) { + ret := []Opts{} + // Most of this is taken from https://github.com/miekg/dns/blob/master/edns.go#L76 + + ret = append(ret, Opts{ + Name: "Version", + Value: strconv.Itoa(int(rr.Version())), + }) + + if rr.Do() { + ret = append(ret, Opts{ + Name: "Flags", + Value: "do", + }) + } else { + ret = append(ret, Opts{ + Name: "Flags", + Value: "", + }) + } + + if rr.Hdr.Ttl&0x7FFF != 0 { + ret = append(ret, Opts{ + Name: "MBZ", + Value: fmt.Sprintf("0x%04x", rr.Hdr.Ttl&0x7FFF), + }) + } + + ret = append(ret, Opts{ + Name: "UDP Buffer Size", + Value: strconv.Itoa(int(rr.UDPSize())), + }) + + for _, opt := range rr.Option { + switch opt.(type) { + case *dns.EDNS0_NSID: + str := opt.String() + + hex, err := hex.DecodeString(str) + if err != nil { + return nil, fmt.Errorf("%w", err) + } + + ret = append(ret, Opts{ + Name: "NSID", + Value: fmt.Sprintf("%s (%s)", str, string(hex)), + }) + case *dns.EDNS0_SUBNET: + ret = append(ret, Opts{ + Name: "Subnet", + Value: opt.String(), + }) + case *dns.EDNS0_COOKIE: + ret = append(ret, Opts{ + Name: "Cookie", + Value: opt.String(), + }) + case *dns.EDNS0_EXPIRE: + ret = append(ret, Opts{ + Name: "Expire", + Value: opt.String(), + }) + case *dns.EDNS0_TCP_KEEPALIVE: + ret = append(ret, Opts{ + Name: "TCP Keepalive", + Value: opt.String(), + }) + case *dns.EDNS0_UL: + ret = append(ret, Opts{ + Name: "Update Lease", + Value: opt.String(), + }) + case *dns.EDNS0_LLQ: + ret = append(ret, Opts{ + Name: "Long Lived Queries", + Value: opt.String(), + }) + case *dns.EDNS0_DAU: + ret = append(ret, Opts{ + Name: "DNSSEC Algorithm Understood", + Value: opt.String(), + }) + case *dns.EDNS0_DHU: + ret = append(ret, Opts{ + Name: "DS Hash Understood", + Value: opt.String(), + }) + case *dns.EDNS0_N3U: + ret = append(ret, Opts{ + Name: "NSEC3 Hash Understood", + Value: opt.String(), + }) + case *dns.EDNS0_LOCAL: + ret = append(ret, Opts{ + Name: "Local OPT", + Value: opt.String(), + }) + case *dns.EDNS0_PADDING: + ret = append(ret, Opts{ + Name: "Padding", + Value: opt.String(), + }) + case *dns.EDNS0_EDE: + ret = append(ret, Opts{ + Name: "EDE", + Value: opt.String(), + }) + case *dns.EDNS0_ESU: + ret = append(ret, Opts{ + Name: "ESU", + Value: opt.String(), + }) + } + } + + return ret, nil +} + var errInvalidFormat = errors.New("this should never happen") diff --git a/query/query_test.go b/query/query_test.go index b9c2be9..6b4d504 100644 --- a/query/query_test.go +++ b/query/query_test.go @@ -37,12 +37,16 @@ func TestCreateQ(t *testing.T) { Statistics: true, }, EDNS: util.EDNS{ + ZFlag: 1, + BufSize: 1500, EnableEDNS: true, - DNSSEC: true, Cookie: true, + DNSSEC: true, Expire: true, KeepOpen: true, Nsid: true, + Padding: true, + Version: 0, }, }, { diff --git a/query/struct.go b/query/struct.go index 908c10f..2617b4f 100644 --- a/query/struct.go +++ b/query/struct.go @@ -15,30 +15,39 @@ import ( ) // Message is for overall DNS responses. +// +//nolint:govet // Better output is worth 32 bytes. type Message struct { - Question []Question `json:"question,omitempty" xml:"question,omitempty" yaml:",omitempty"` - Answer []Answer `json:"answer,omitempty" xml:"answer,omitempty" yaml:",omitempty"` - Ns []Answer `json:"ns,omitempty" xml:"ns,omitempty" yaml:",omitempty"` - Extra []Answer `json:"extra,omitempty" xml:"extra,omitempty" yaml:",omitempty"` - Header dns.MsgHdr `json:"header,omitempty" xml:"header,omitempty" yaml:",omitempty"` + Header dns.MsgHdr `json:"header,omitempty" xml:"header,omitempty" yaml:",omitempty"` + Opt []Opts `json:"opt,omitempty" xml:"opt,omitempty" yaml:"opt,omitempty"` + Question []Question `json:"question,omitempty" xml:"question,omitempty" yaml:",omitempty"` + Answer []Answer `json:"answer,omitempty" xml:"answer,omitempty" yaml:",omitempty"` + Ns []Answer `json:"ns,omitempty" xml:"ns,omitempty" yaml:",omitempty"` + Additional []Answer `json:"additional,omitempty" xml:"additional,omitempty" yaml:",omitempty"` } // Question is a DNS Query. type Question struct { Name string `json:"name,omitempty" xml:"name,omitempty" yaml:",omitempty"` - Type string `json:"type,omitempty" xml:"type,omitempty" yaml:",omitempty"` Class string `json:"class,omitempty" xml:"class,omitempty" yaml:",omitempty"` + Type string `json:"type,omitempty" xml:"type,omitempty" yaml:",omitempty"` } // RRHeader is for DNS Resource Headers. type RRHeader struct { Name string `json:"name,omitempty" xml:"name,omitempty" yaml:",omitempty"` - Type string `json:"type,omitempty" xml:"type,omitempty" yaml:",omitempty"` - Class string `json:"class,omitempty" xml:"class,omitempty" yaml:",omitempty"` TTL string `json:"ttl,omitempty" xml:"ttl,omitempty" yaml:",omitempty"` + Class string `json:"class,omitempty" xml:"class,omitempty" yaml:",omitempty"` + Type string `json:"type,omitempty" xml:"type,omitempty" yaml:",omitempty"` Rdlength uint16 `json:"-" xml:"-" yaml:"-"` } +// Opts is for the OPT pseudosection, nearly exclusively for EDNS. +type Opts struct { + Name string `json:"name,omitempty" xml:"name,omitempty" yaml:",omitempty"` + Value string `json:"value" xml:"value" yaml:""` +} + // Answer is for a DNS Response. type Answer struct { Value string `json:"response,omitempty" xml:"response,omitempty" yaml:"response,omitempty"` @@ -212,7 +221,7 @@ func stringParse(str string, isAns bool, opts util.Options) (string, error) { } } - if opts.HumanTTL { + if opts.ShowTTL && opts.HumanTTL { ttl, _ := strconv.Atoi(split[1]) split[1] = (time.Duration(ttl) * time.Second).String() } diff --git a/util/options.go b/util/options.go index aaf1a74..91e5f14 100644 --- a/util/options.go +++ b/util/options.go @@ -10,7 +10,7 @@ import ( "github.com/miekg/dns" ) -// Options is the grand CLI options structure. +// Options is the grand structure for all query options. type Options struct { Logger *logawl.Logger TLSHost string