diff --git a/pkg/query/print.go b/pkg/query/print.go index 33734d6..1737525 100644 --- a/pkg/query/print.go +++ b/pkg/query/print.go @@ -3,7 +3,6 @@ package query import ( - "encoding/hex" "encoding/json" "encoding/xml" "errors" @@ -253,297 +252,60 @@ func MakePrintable(res util.Response, opts *util.Options) (*Message, error) { msg = res.DNS ) // The things I do for compatibility - ret := Message{ - Header: Header{ - ID: msg.Id, - Response: msg.Response, - Opcode: dns.OpcodeToString[msg.Opcode], - Authoritative: msg.Authoritative, - Truncated: msg.Truncated, - RecursionDesired: msg.RecursionDesired, - RecursionAvailable: msg.RecursionAvailable, - Zero: msg.Zero, - AuthenticatedData: msg.AuthenticatedData, - CheckingDisabled: msg.CheckingDisabled, - Status: dns.RcodeToString[msg.Rcode], - }, + ret := &Message{ + RTT: res.RTT.String(), + Server: opts.Request.Server + serverExtra(opts), + When: time.Now().Format(time.RFC3339), + DateString: time.Now().Format(time.RFC3339), + DateSeconds: time.Now().Format(time.UnixDate), + MsgSize: res.DNS.Len(), + ID: msg.Id, + Opcode: dns.OpcodeToString[msg.Opcode], + Status: dns.RcodeToString[msg.Rcode], + Response: msg.Response, + + Authoritative: msg.Authoritative, + Truncated: msg.Truncated, + RecursionDesired: msg.RecursionDesired, + RecursionAvailable: msg.RecursionAvailable, + Zero: msg.Zero, + AuthenticatedData: msg.AuthenticatedData, + CheckingDisabled: msg.CheckingDisabled, } opt := msg.IsEdns0() if opt != nil && opts.Display.Opt { - ret.Opt, err = parseOpt(*opt) + ret.Opt, err = ret.ParseOpt(*opt) if err != nil { return nil, fmt.Errorf("edns print: %w", err) } } if opts.Display.Question { - for _, question := range msg.Question { - var name string - if opts.Display.UcodeTranslate { - name, err = idna.ToUnicode(question.Name) - if err != nil { - return nil, fmt.Errorf("punycode to unicode: %w", err) - } - } else { - name = question.Name - } - - ret.Question = append(ret.Question, Question{ - Name: name, - Type: dns.TypeToString[question.Qtype], - Class: dns.ClassToString[question.Qclass], - }) + err = ret.displayQuestion(msg, opts, opt) + if err != nil { + return nil, fmt.Errorf("unable to display questions: %w", err) } } if opts.Display.Answer { - for _, answer := range msg.Answer { - temp := strings.Split(answer.String(), "\t") - - var ( - ttl string - name string - ) - - if opts.Display.TTL { - if opts.Display.HumanTTL { - ttl = (time.Duration(answer.Header().Ttl) * time.Second).String() - } else { - ttl = strconv.Itoa(int(answer.Header().Ttl)) - } - } - - if opts.Display.UcodeTranslate { - name, err = idna.ToUnicode(answer.Header().Name) - if err != nil { - return nil, fmt.Errorf("punycode to unicode: %w", err) - } - } else { - name = answer.Header().Name - } - - ret.Answer = append(ret.Answer, Answer{ - RRHeader: RRHeader{ - Name: name, - Type: dns.TypeToString[answer.Header().Rrtype], - Class: dns.ClassToString[answer.Header().Class], - Rdlength: answer.Header().Rdlength, - TTL: ttl, - }, - Value: temp[len(temp)-1], - }) + err = ret.displayAnswers(msg, opts, opt) + if err != nil { + return nil, fmt.Errorf("unable to display answers: %w", err) } } if opts.Display.Authority { - for _, ns := range msg.Ns { - temp := strings.Split(ns.String(), "\t") - - var ( - ttl string - name string - ) - - if opts.Display.TTL { - if opts.Display.HumanTTL { - ttl = (time.Duration(ns.Header().Ttl) * time.Second).String() - } else { - ttl = strconv.Itoa(int(ns.Header().Ttl)) - } - } - - if opts.Display.UcodeTranslate { - name, err = idna.ToUnicode(ns.Header().Name) - if err != nil { - return nil, fmt.Errorf("punycode to unicode: %w", err) - } - } else { - name = ns.Header().Name - } - - ret.Authority = append(ret.Authority, Answer{ - RRHeader: RRHeader{ - Name: name, - Type: dns.TypeToString[ns.Header().Rrtype], - Class: dns.ClassToString[ns.Header().Class], - Rdlength: ns.Header().Rdlength, - TTL: ttl, - }, - Value: temp[len(temp)-1], - }) + err = ret.displayAuthority(msg, opts, opt) + if err != nil { + return nil, fmt.Errorf("unable to display authority: %w", err) } } if opts.Display.Additional { - for _, additional := range msg.Extra { - if additional.Header().Rrtype == dns.StringToType["OPT"] { - continue - } else { - temp := strings.Split(additional.String(), "\t") - - var ( - ttl string - name string - ) - - if opts.Display.TTL { - if opts.Display.HumanTTL { - ttl = (time.Duration(additional.Header().Ttl) * time.Second).String() - } else { - ttl = strconv.Itoa(int(additional.Header().Ttl)) - } - } - - if opts.Display.UcodeTranslate { - name, err = idna.ToUnicode(additional.Header().Name) - if err != nil { - return nil, fmt.Errorf("punycode to unicode: %w", err) - } - } else { - name = additional.Header().Name - } - - ret.Additional = append(ret.Additional, Answer{ - RRHeader: RRHeader{ - Name: name, - Type: dns.TypeToString[additional.Header().Rrtype], - Class: dns.ClassToString[additional.Header().Class], - Rdlength: additional.Header().Rdlength, - TTL: ttl, - }, - Value: temp[len(temp)-1], - }) - } - } - } - - if opts.Display.Statistics { - ret.Statistics = Statistics{ - RTT: res.RTT.String(), - Server: opts.Request.Server + serverExtra(opts), - When: time.Now().Format(time.RFC3339), - MsgSize: res.DNS.Len(), - } - } else { - ret.Statistics = Statistics{} - } - - 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(), - }) + ret.displayAdditional(msg, opts, opt) + if err != nil { + return nil, fmt.Errorf("unable to display additional: %w", err) } } diff --git a/pkg/query/struct.go b/pkg/query/struct.go index 3ad2159..d3a1119 100644 --- a/pkg/query/struct.go +++ b/pkg/query/struct.go @@ -10,51 +10,60 @@ import ( // //nolint:govet // Better looking output is worth a few bytes. type Message struct { - // Header section - Header `json:"header,omitempty" xml:"header,omitempty" yaml:"header,omitempty"` - // Opt Pseudosection - Opt []Opts `json:"opt,omitempty" xml:"opt,omitempty" yaml:"opt,omitempty"` - // Question Section - Question []Question `json:"question,omitempty" xml:"question,omitempty" yaml:"question,omitempty"` - // Answer Section - Answer []Answer `json:"answer,omitempty" xml:"answer,omitempty" yaml:"answer,omitempty"` - // Authority Section - Authority []Answer `json:"authority,omitempty" xml:"authority,omitempty" yaml:"authority,omitempty"` - // Additional Section - Additional []Answer `json:"additional,omitempty" xml:"additional,omitempty" yaml:"additional,omitempty"` - // Statistics :) - Statistics `json:"statistics,omitempty" xml:"statistics,omitempty" yaml:"statistics,omitempty"` -} + DateString string `json:"dateString,omitempty" xml:"dateString,omitempty" yaml:"dateString,omitempty"` + DateSeconds string `json:"dateSeconds,omitempty" xml:"dateSeconds,omitempty" yaml:"dateSeconds,omitempty"` + MsgSize int `json:"msgLength,omitempty" xml:"msgSize,omitempty" yaml:"msgSize,omitempty"` + ID uint16 `json:"id," xml:"id," yaml:"id" example:"12"` -// Header is the header. -type Header struct { Opcode string `json:"opcode," xml:"opcode," yaml:"opcode" example:"QUERY"` Status string `json:"status," xml:"status," yaml:"status" example:"NOERR"` - ID uint16 `json:"id," xml:"id," yaml:"id" example:"12"` Response bool `json:"response," xml:"response," yaml:"response" example:"true"` Authoritative bool `json:"authoritative," xml:"authoritative," yaml:"authoritative" example:"false"` Truncated bool `json:"truncated," xml:"truncated," yaml:"truncated" example:"false"` - RecursionDesired bool `json:"recursionDesired," xml:"recursionDesired," yaml:"recursionDesired" example:"true"` - RecursionAvailable bool `json:"recursionAvailable," xml:"recursionAvailable," yaml:"recursionAvailable" example:"true"` + RecursionDesired bool `json:"RD," xml:"recursionDesired," yaml:"recursionDesired" example:"true"` + RecursionAvailable bool `json:"RA," xml:"recursionAvailable," yaml:"recursionAvailable" example:"true"` Zero bool `json:"zero," xml:"zero," yaml:"zero" example:"false"` AuthenticatedData bool `json:"authenticatedData," xml:"authenticatedData," yaml:"authenticatedData" example:"false"` CheckingDisabled bool `json:"checkingDisabled," xml:"checkingDisabled," yaml:"checkingDisabled" example:"false"` + Name string `json:"QNAME,omitempty" xml:"QNAME,omitempty" yaml:"QNAME,omitempty" example:"localhost"` + Type uint16 `json:"QTYPE,omitempty" xml:"QTYPE,omitempty" yaml:"QTYPE,omitempty" example:"IN"` + TypeName string `json:"QTYPEname,omitempty" xml:"QTYPEname,omitempty" yaml:"QTYPEname,omitempty" example:"IN"` + Class string `json:"QCLASS,omitempty" xml:"QCLASS,omitempty" yaml:"QCLASS,omitempty" example:"A"` + ClassName string `json:"QCLASSname,omitempty" xml:"QCLASSname,omitempty" yaml:"QCLASSname,omitempty" example:"1"` + RTT string `json:"queryTime,omitempty" xml:"queryTime,omitempty" yaml:"queryTime,omitempty"` + Server string `json:"server,omitempty" xml:"server,omitempty" yaml:"server,omitempty"` + When string `json:"when,omitempty" xml:"when,omitempty" yaml:"when,omitempty"` + + Opt []Opts `json:"opt,omitempty" xml:"opt,omitempty" yaml:"opt,omitempty"` + // Question Section + Question []Question `json:"question,omitempty" xml:"question,omitempty" yaml:"question,omitempty"` + + // Answer Section + AnswerRRs []Answer `json:"answersRRs,omitempty" xml:"answersRRs,omitempty" yaml:"answersRRs,omitempty" example:"false"` + AuthoritativeRRs []Answer `json:"authorityRRs,omitempty" xml:"authorityRRs,omitempty" yaml:"authorityRRs,omitempty" example:"false"` + AdditionalRRs []Answer `json:"additionalRRs,omitempty" xml:"additionalRRs,omitempty" yaml:"additionalRRs,omitempty" example:"false"` } // Question is a DNS Query. type Question struct { - Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty" example:"localhost"` - Class string `json:"class,omitempty" xml:"class,omitempty" yaml:"class,omitempty" example:"A"` - Type string `json:"type,omitempty" xml:"type,omitempty" yaml:"type,omitempty" example:"IN"` + Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty" example:"localhost"` + Type uint16 `json:"type,omitempty" xml:"type,omitempty" yaml:"type,omitempty" example:"IN"` + TypeName string `json:"TYPEname,omitempty" xml:"TYPEname,omitempty" yaml:"TYPEname,omitempty" example:"IN"` + Class string `json:"class,omitempty" xml:"class,omitempty" yaml:"class,omitempty" example:"A"` + ClassName string `json:"CLASSname,omitempty" xml:"CLASSname,omitempty" yaml:"CLASSname,omitempty" example:"1"` } // RRHeader is for DNS Resource Headers. -type RRHeader struct { - Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty" example:"127.0.0.1"` - TTL string `json:"ttl,omitempty" xml:"ttl,omitempty" yaml:"ttl,omitempty" example:"0ms"` - Class string `json:"class,omitempty" xml:"class,omitempty" yaml:"class,omitempty" example:"A"` - Type string `json:"type,omitempty" xml:"type,omitempty" yaml:"type,omitempty" example:"IN"` - Rdlength uint16 `json:"-" xml:"-" yaml:"-"` +type Answer struct { + Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty" example:"127.0.0.1"` + Type uint16 `json:"type,omitempty" xml:"type,omitempty" yaml:"type,omitempty" example:"IN"` + TypeName string `json:"TYPEname,omitempty" xml:"TYPEname,omitempty" yaml:"TYPEname,omitempty" example:"IN"` + Class uint16 `json:"class,omitempty" xml:"class,omitempty" yaml:"class,omitempty" example:"A"` + ClassName string `json:"CLASSname,omitempty" xml:"CLASSname,omitempty" yaml:"CLASSname,omitempty" example:"A"` + TTL string `json:"ttl,omitempty" xml:"ttl,omitempty" yaml:"ttl,omitempty" example:"0ms"` + Value string `json:"response,omitempty" xml:"response,omitempty" yaml:"response,omitempty"` + Rdlength uint16 `json:"RDLENGTH,omitempty" xml:"RDLENGTH,omitempty" yaml:"RDLENGTH,omitempty"` + Rdhex string `json:"RDATAHEX,omitempty" xml:"RDATAHEX,omitempty" yaml:"RDATAHEX,omitempty"` } // Opts is for the OPT pseudosection, nearly exclusively for EDNS. @@ -63,18 +72,4 @@ type Opts struct { Value string `json:"value" xml:"value" yaml:"value"` } -// Answer is for a DNS Response. -type Answer struct { - Value string `json:"response,omitempty" xml:"response,omitempty" yaml:"response,omitempty"` - RRHeader `json:"header,omitempty" xml:"header,omitempty" yaml:"header,omitempty"` -} - -// Statistics is the little bit at the bottom :). -type Statistics struct { - RTT string `json:"queryTime,omitempty" xml:"queryTime,omitempty" yaml:"queryTime,omitempty"` - Server string `json:"server,omitempty" xml:"server,omitempty" yaml:"server,omitempty"` - When string `json:"when,omitempty" xml:"when,omitempty" yaml:"when,omitempty"` - MsgSize int `json:"msgSize,omitempty" xml:"msgSize,omitempty" yaml:"msgSize,omitempty"` -} - var errNoMessage = errors.New("no message") diff --git a/pkg/query/util.go b/pkg/query/util.go new file mode 100644 index 0000000..e543aba --- /dev/null +++ b/pkg/query/util.go @@ -0,0 +1,282 @@ +package query + +import ( + "encoding/hex" + "fmt" + "strconv" + "strings" + "time" + + "git.froth.zone/sam/awl/pkg/util" + "github.com/miekg/dns" + "golang.org/x/net/idna" +) + +func (message *Message) displayQuestion(msg *dns.Msg, opts *util.Options, opt *dns.OPT) error { + var ( + name string + err error + ) + for _, question := range msg.Question { + if opts.Display.UcodeTranslate { + name, err = idna.ToUnicode(question.Name) + if err != nil { + return fmt.Errorf("punycode to unicode: %w", err) + } + } else { + name = question.Name + } + + message.Name = name + message.Type = opt.Header().Rrtype + message.TypeName = dns.TypeToString[question.Qtype] + message.Class = dns.ClassToString[question.Qclass] + message.ClassName = dns.TypeToString[question.Qtype] + } + return nil +} + +func (message *Message) displayAnswers(msg *dns.Msg, opts *util.Options, opt *dns.OPT) error { + var ( + ttl string + name string + err error + ) + for _, answer := range msg.Answer { + temp := strings.Split(answer.String(), "\t") + + if opts.Display.TTL { + if opts.Display.HumanTTL { + ttl = (time.Duration(answer.Header().Ttl) * time.Second).String() + } else { + ttl = strconv.Itoa(int(answer.Header().Ttl)) + } + } + + if opts.Display.UcodeTranslate { + name, err = idna.ToUnicode(answer.Header().Name) + if err != nil { + return fmt.Errorf("punycode to unicode: %w", err) + } + } else { + name = answer.Header().Name + } + + message.AnswerRRs = append(message.AnswerRRs, Answer{ + + Name: name, + TypeName: dns.TypeToString[answer.Header().Rrtype], + Type: answer.Header().Rrtype, + Rdlength: answer.Header().Rdlength, + TTL: ttl, + + Value: temp[len(temp)-1], + }) + } + return err +} + +func (message *Message) displayAuthority(msg *dns.Msg, opts *util.Options, opt *dns.OPT) error { + var ( + ttl string + name string + err error + ) + + for _, ns := range msg.Ns { + temp := strings.Split(ns.String(), "\t") + + if opts.Display.TTL { + if opts.Display.HumanTTL { + ttl = (time.Duration(ns.Header().Ttl) * time.Second).String() + } else { + ttl = strconv.Itoa(int(ns.Header().Ttl)) + } + } + + if opts.Display.UcodeTranslate { + name, err = idna.ToUnicode(ns.Header().Name) + if err != nil { + return fmt.Errorf("punycode to unicode: %w", err) + } + } else { + name = ns.Header().Name + } + + message.AuthoritativeRRs = append(message.AuthoritativeRRs, Answer{ + + Name: name, + TypeName: dns.TypeToString[ns.Header().Rrtype], + Type: ns.Header().Rrtype, + Class: ns.Header().Rrtype, + ClassName: dns.ClassToString[ns.Header().Class], + Rdlength: ns.Header().Rdlength, + TTL: ttl, + + Value: temp[len(temp)-1], + }) + } + return err +} + +func (message *Message) displayAdditional(msg *dns.Msg, opts *util.Options, opt *dns.OPT) error { + var ( + ttl string + name string + err error + ) + for _, additional := range msg.Extra { + if additional.Header().Rrtype == dns.StringToType["OPT"] { + continue + } else { + temp := strings.Split(additional.String(), "\t") + + if opts.Display.TTL { + if opts.Display.HumanTTL { + ttl = (time.Duration(additional.Header().Ttl) * time.Second).String() + } else { + ttl = strconv.Itoa(int(additional.Header().Ttl)) + } + } + + if opts.Display.UcodeTranslate { + name, err = idna.ToUnicode(additional.Header().Name) + if err != nil { + return fmt.Errorf("punycode to unicode: %w", err) + } + } else { + name = additional.Header().Name + } + + message.AdditionalRRs = append(message.AdditionalRRs, Answer{ + Name: name, + TypeName: dns.TypeToString[additional.Header().Rrtype], + Type: additional.Header().Rrtype, + Class: additional.Header().Rrtype, + ClassName: dns.ClassToString[additional.Header().Class], + Rdlength: additional.Header().Rdlength, + TTL: ttl, + Value: temp[len(temp)-1], + }) + } + } + return err +} + +func (message *Message) 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 +}