feat: RFC-8427 #171
3 changed files with 352 additions and 313 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
282
pkg/query/util.go
Normal file
282
pkg/query/util.go
Normal file
|
@ -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
|
||||
}
|
Loading…
Reference in a new issue