master #2

Merged
sam merged 3 commits from :master into master 2022-06-22 11:55:39 +00:00
4 changed files with 133 additions and 126 deletions

146
awl.go
View File

@ -1,32 +1,21 @@
package main
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
"runtime"
"strconv"
"strings"
"time"
"github.com/c-robinson/iplib"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"github.com/urfave/cli/v2"
"golang.org/x/net/idna"
)
// The basic structure of a DNS request
type request struct {
server string // The server to make the DNS request from
request uint16 // The type of request
name string // The domain name to make a DNS request for
}
func main() {
// Custom version string
cli.VersionPrinter = func(c *cli.Context) {
@ -126,8 +115,8 @@ func main() {
},
Action: func(c *cli.Context) error {
var err error
req := parseArgs(c.Args().Slice())
var resp util.Response
resp.Answers = parseArgs(c.Args().Slice())
// Set DNS-over-TLS, if enabled
port := c.Int("port")
@ -141,29 +130,26 @@ func main() {
}
if !c.Bool("https") {
req.server = net.JoinHostPort(req.server, strconv.Itoa(port))
resp.Answers.Server = net.JoinHostPort(resp.Answers.Server, strconv.Itoa(port))
} else {
req.server = "https://" + req.server
resp.Answers.Server = "https://" + resp.Answers.Server
}
// Process the IP/Phone number so a PTR/NAPTR can be done
if c.Bool("reverse") {
if dns.TypeToString[req.request] == "A" {
req.request = dns.StringToType["PTR"]
}
req.name, err = reverseDNS(req.name, dns.TypeToString[req.request])
if err != nil {
return err
if dns.TypeToString[resp.Answers.Request] == "A" {
resp.Answers.Request = dns.StringToType["PTR"]
}
resp.Answers.Name, err = util.ReverseDNS(resp.Answers.Name, dns.TypeToString[resp.Answers.Request])
}
// if the domain is not canonical, make it canonical
if !strings.HasSuffix(req.name, ".") {
req.name = fmt.Sprintf("%s.", req.name)
if !strings.HasSuffix(resp.Answers.Name, ".") {
resp.Answers.Name = fmt.Sprintf("%s.", resp.Answers.Name)
}
msg := new(dns.Msg)
msg.SetQuestion(req.name, req.request)
msg.SetQuestion(resp.Answers.Name, resp.Answers.Request)
// Set DNSSEC if requested
if c.Bool("dnssec") {
@ -171,13 +157,12 @@ func main() {
}
var (
in *dns.Msg
rtt time.Duration
in *dns.Msg
)
// Make the DNS request
if c.Bool("https") {
in, err = resolveHTTPS(msg, req.server)
in, err = util.ResolveHTTPS(msg, resp.Answers.Server)
} else if c.Bool("quic") {
return fmt.Errorf("quic: not yet implemented")
} else {
@ -208,7 +193,7 @@ func main() {
if c.Bool("tls") {
d.Net = "tcp-tls"
}
in, rtt, err = d.Exchange(msg, req.server)
in, resp.Answers.RTT, err = d.Exchange(msg, resp.Answers.Server)
// If UDP truncates, use TCP instead
if !c.Bool("no-truncate") {
@ -221,7 +206,7 @@ func main() {
if c.Bool("6") {
d.Net = "tcp6"
}
in, rtt, err = d.Exchange(msg, req.server)
in, resp.Answers.RTT, err = d.Exchange(msg, resp.Answers.Server)
}
}
}
@ -231,14 +216,14 @@ func main() {
}
if c.Bool("json") {
json, _ := json.Marshal(in)
json, _ := json.MarshalIndent(in, "", " ")
fmt.Println(string(json))
} else {
if !c.Bool("short") {
// Print everything
fmt.Println(in)
fmt.Println(";; Query time:", rtt)
fmt.Println(";; SERVER:", req.server)
fmt.Println(";; Query time:", resp.Answers.RTT)
fmt.Println(";; SERVER:", resp.Answers.Server)
fmt.Println(";; WHEN:", time.Now().Format(time.RFC1123))
fmt.Println(";; MSG SIZE rcvd:", in.Len())
} else {
@ -261,114 +246,51 @@ func main() {
}
}
func parseArgs(args []string) request {
func parseArgs(args []string) util.Answers {
var (
server string
req uint16
name string
resp util.Response
)
for _, arg := range args {
// If it starts with @, it's a DNS server
if strings.HasPrefix(arg, "@") {
server = strings.Split(arg, "@")[1]
resp.Answers.Server = strings.Split(arg, "@")[1]
continue
}
// If there's a dot, it's a name
if strings.Contains(arg, ".") {
name, _ = idna.ToUnicode(arg)
resp.Answers.Name, _ = idna.ToUnicode(arg)
continue
}
// If it's a request, it's a request (duh)
if r, ok := dns.StringToType[strings.ToUpper(arg)]; ok {
req = r
resp.Answers.Request = r
continue
}
//else, assume it's a name
name, _ = idna.ToUnicode(arg)
resp.Answers.Name, _ = idna.ToUnicode(arg)
}
// If nothing was set, set a default
if name == "" {
name = "."
if req == 0 {
req = dns.StringToType["NS"]
if resp.Answers.Name == "" {
resp.Answers.Name = "."
if resp.Answers.Request == 0 {
resp.Answers.Request = dns.StringToType["NS"]
}
} else {
if req == 0 {
req = dns.StringToType["A"]
if resp.Answers.Request == 0 {
resp.Answers.Request = dns.StringToType["A"]
}
}
if server == "" {
if resp.Answers.Server == "" {
resolv, err := dns.ClientConfigFromFile("/etc/resolv.conf")
if err != nil { // Query Google by default, needed for Windows since the DNS library doesn't support Windows
// TODO: Actually find where windows stuffs its dns resolvers
server = "8.8.4.4"
resp.Answers.Server = "8.8.4.4"
} else {
server = resolv.Servers[0]
resp.Answers.Server = resolv.Servers[0]
}
}
return request{server: server, request: req, name: name}
}
func reverseDNS(dom string, q string) (string, error) {
if q == "PTR" {
if strings.Contains(dom, ".") {
// It's an IPv4 address
ip := net.ParseIP(dom)
if ip != nil {
return iplib.IP4ToARPA(ip), nil
} else {
return "", errors.New("error: Could not parse IPv4 address")
}
} else if strings.Contains(dom, ":") {
// It's an IPv6 address
ip := net.ParseIP(dom)
if ip != nil {
return iplib.IP6ToARPA(ip), nil
} else {
return "", errors.New("error: Could not parse IPv6 address")
}
}
}
return "", errors.New("error: -x flag given but no IP found")
}
func resolveHTTPS(msg *dns.Msg, server string) (*dns.Msg, error) {
httpR := &http.Client{}
buf, err := msg.Pack()
if err != nil {
return nil, err
}
query := server + "?dns=" + base64.RawURLEncoding.EncodeToString(buf)
req, err := http.NewRequest("GET", query, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/dns-message")
res, err := httpR.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("bad HTTP Request: %d", res.StatusCode)
}
fullRes, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}
response := dns.Msg{}
err = response.Unpack(fullRes)
if err != nil {
return nil, err
}
return &response, nil
return util.Answers{Server: resp.Answers.Server, Request: resp.Answers.Request, Name: resp.Answers.Name}
}

9
go.mod
View File

@ -1,4 +1,4 @@
module example.com/awl
module git.froth.zone/sam/awl
go 1.18
@ -13,8 +13,9 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.11 // indirect
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)

15
go.sum
View File

@ -2,8 +2,6 @@ github.com/c-robinson/iplib v1.0.3 h1:NG0UF0GoEsrC1/vyfX1Lx2Ss7CySWl3KqqXh3q4DdP
github.com/c-robinson/iplib v1.0.3/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8=
github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@ -15,15 +13,12 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsr
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc=
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220621193019-9d032be2e588 h1:9ubFuySsnAJYGyJrZ3koiEv8FyqofCBdz3G9Mbf2YFc=
golang.org/x/net v0.0.0-20220621193019-9d032be2e588/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -36,8 +31,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU=
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -46,9 +41,9 @@ golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

89
util/dns.go Normal file
View File

@ -0,0 +1,89 @@
package util
import (
"encoding/base64"
"errors"
"fmt"
"io"
"net"
"net/http"
"strings"
"time"
"github.com/c-robinson/iplib"
"github.com/miekg/dns"
)
type Response struct {
Answers Answers `json:"Response"` //
}
// The Answers struct is the basic structure of a DNS request
// to be returned to the user upon making a request
type Answers struct {
Server string `json:"Server"` // The server to make the DNS request from
Request uint16 `json:"Request"` // The type of request
Name string `json:"Name"` // The domain name to make a DNS request for
RTT time.Duration `json:"RTT"` // When AWL was ran
}
func ReverseDNS(dom string, q string) (string, error) {
if q == "PTR" {
if strings.Contains(dom, ".") {
// It's an IPv4 address
ip := net.ParseIP(dom)
if ip != nil {
return iplib.IP4ToARPA(ip), nil
} else {
return "", errors.New("error: Could not parse IPv4 address")
}
} else if strings.Contains(dom, ":") {
// It's an IPv6 address
ip := net.ParseIP(dom)
if ip != nil {
return iplib.IP6ToARPA(ip), nil
} else {
return "", errors.New("error: Could not parse IPv6 address")
}
}
}
return "", errors.New("error: -x flag given but no IP found")
}
func ResolveHTTPS(msg *dns.Msg, server string) (*dns.Msg, error) {
httpR := &http.Client{}
buf, err := msg.Pack()
if err != nil {
return nil, err
}
query := server + "?dns=" + base64.RawURLEncoding.EncodeToString(buf)
req, err := http.NewRequest("GET", query, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/dns-message")
res, err := httpR.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("bad HTTP Request: %d", res.StatusCode)
}
fullRes, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}
response := dns.Msg{}
err = response.Unpack(fullRes)
if err != nil {
return nil, err
}
return &response, nil
}