awl/pkg/query/util.go
2022-12-15 18:00:01 -05:00

334 lines
7.6 KiB
Go

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"
)
const aaaa = "AAAA"
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 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
}
switch dns.TypeToString[answer.Header().Rrtype] {
case "A":
message.AdditionalRRs = append(message.AdditionalRRs, 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,
ValueA: temp[len(temp)-1],
})
case aaaa:
message.AdditionalRRs = append(message.AdditionalRRs, 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,
ValueAAAA: temp[len(temp)-1],
})
}
}
return nil
}
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
}
switch dns.TypeToString[ns.Header().Rrtype] {
case "A":
message.AdditionalRRs = append(message.AdditionalRRs, 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,
ValueA: temp[len(temp)-1],
})
case aaaa:
message.AdditionalRRs = append(message.AdditionalRRs, 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,
ValueAAAA: temp[len(temp)-1],
})
}
}
return nil
}
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
}
switch dns.TypeToString[additional.Header().Rrtype] {
case "A":
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,
ValueA: temp[len(temp)-1],
})
case aaaa:
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,
ValueAAAA: 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
}