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 = question.Qtype message.TypeName = dns.TypeToString[question.Qtype] message.Class = question.Qclass message.ClassName = dns.ClassToString[question.Qclass] } return nil } func (message *Message) displayAnswers(msg *dns.Msg, opts *util.Options, opt *dns.OPT) error { var ( ttl any 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, ClassName: dns.ClassToString[answer.Header().Class], Class: answer.Header().Class, TypeName: dns.TypeToString[answer.Header().Rrtype], Type: answer.Header().Rrtype, Rdlength: answer.Header().Rdlength, TTL: ttl, Value: temp[len(temp)-1], }) } return nil } func (message *Message) displayAuthority(msg *dns.Msg, opts *util.Options, opt *dns.OPT) error { var ( ttl any 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().Class, ClassName: dns.ClassToString[ns.Header().Class], Rdlength: ns.Header().Rdlength, TTL: ttl, Value: temp[len(temp)-1], }) } return nil } func (message *Message) displayAdditional(msg *dns.Msg, opts *util.Options, opt *dns.OPT) error { var ( ttl any 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().Class, ClassName: dns.ClassToString[additional.Header().Class], Rdlength: additional.Header().Rdlength, TTL: ttl, Value: temp[len(temp)-1], }) } } return nil } // ParseOpt parses opts. 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 }