Sam
4cf19ebf78
All checks were successful
continuous-integration/drone/push Build is passing
I need to make fewer of these :) Reviewed-on: #61
197 lines
4.2 KiB
Go
197 lines
4.2 KiB
Go
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package query
|
|
|
|
import (
|
|
"encoding/json"
|
|
"encoding/xml"
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.froth.zone/sam/awl/util"
|
|
"github.com/miekg/dns"
|
|
"golang.org/x/net/idna"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// PrintSpecial is for printing as JSON, XML or YAML.
|
|
// As of now JSON and XML use the stdlib version.
|
|
func PrintSpecial(msg *dns.Msg, opts util.Options) (string, error) {
|
|
formatted, err := MakePrintable(msg, opts)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
switch {
|
|
case opts.JSON:
|
|
opts.Logger.Info("Printing as JSON")
|
|
|
|
json, err := json.MarshalIndent(formatted, " ", " ")
|
|
|
|
return string(json), err
|
|
case opts.XML:
|
|
opts.Logger.Info("Printing as XML")
|
|
|
|
xml, err := xml.MarshalIndent(formatted, " ", " ")
|
|
|
|
return string(xml), err
|
|
case opts.YAML:
|
|
opts.Logger.Info("Printing as YAML")
|
|
|
|
yaml, err := yaml.Marshal(formatted)
|
|
|
|
return string(yaml), err
|
|
default:
|
|
return "", errInvalidFormat
|
|
}
|
|
}
|
|
|
|
// MakePrintable takes a DNS message and makes it nicer to be printed as JSON,YAML,
|
|
// and XML. Little is changed beyond naming.
|
|
func MakePrintable(msg *dns.Msg, opts util.Options) (*Message, error) {
|
|
var err error
|
|
|
|
ret := Message{
|
|
Header: msg.MsgHdr,
|
|
}
|
|
|
|
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],
|
|
})
|
|
}
|
|
|
|
for _, answer := range msg.Answer {
|
|
temp := strings.Split(answer.String(), "\t")
|
|
|
|
var (
|
|
ttl string
|
|
name string
|
|
)
|
|
|
|
if opts.ShowTTL {
|
|
if opts.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],
|
|
})
|
|
}
|
|
|
|
for _, ns := range msg.Ns {
|
|
temp := strings.Split(ns.String(), "\t")
|
|
|
|
var (
|
|
ttl string
|
|
name string
|
|
)
|
|
|
|
if opts.ShowTTL {
|
|
if opts.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.Ns = append(ret.Ns, 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],
|
|
})
|
|
}
|
|
|
|
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.ShowTTL {
|
|
if opts.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.Extra = append(ret.Extra, 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],
|
|
})
|
|
}
|
|
}
|
|
|
|
return &ret, nil
|
|
}
|
|
|
|
var errInvalidFormat = errors.New("this should never happen")
|