diff options
Diffstat (limited to 'cmd/resolver/main.go')
| -rw-r--r-- | cmd/resolver/main.go | 238 |
1 files changed, 72 insertions, 166 deletions
diff --git a/cmd/resolver/main.go b/cmd/resolver/main.go index 2fdfe3b..49ba6a6 100644 --- a/cmd/resolver/main.go +++ b/cmd/resolver/main.go @@ -1,217 +1,123 @@ // SPDX-FileCopyrightText: 2018 M. Shulhan <ms@kilabit.info> // SPDX-License-Identifier: GPL-3.0-or-later +// Command resolver is client for DNS server to resolve query and client for +// rescached HTTP server. package main import ( + "flag" "fmt" "log" - "math/rand" + "os" "strings" - "time" - - "github.com/shuLhan/share/lib/dns" - libnet "github.com/shuLhan/share/lib/net" ) +// List of valid commands. const ( - defResolvConf = "/etc/resolv.conf" + cmdQuery = "query" ) -// -// initSystemResolver read the system resolv.conf to create fallback DNS -// resolver. -// -func initSystemResolver() (rc *libnet.ResolvConf, cl dns.Client) { +func main() { var ( - err error - ns string - ) + rsol = new(resolver) - rc, err = libnet.NewResolvConf(defResolvConf) - if err != nil { - log.Fatal("! ", err) - } + args []string + optHelp bool + ) - if len(rc.NameServers) == 0 { - ns = "127.0.0.1:53" - } else { - ns = rc.NameServers[0] - } + log.SetFlags(0) - cl, err = dns.NewUDPClient(ns) - if err != nil { - log.Fatal("! ", err) - } + flag.StringVar(&rsol.nameserver, "ns", "", "Parent name server address using scheme based.") + flag.BoolVar(&rsol.insecure, "insecure", false, "Ignore invalid server certificate") + flag.BoolVar(&optHelp, "h", false, "") - return -} + flag.Parse() -func populateQueries(cr *libnet.ResolvConf, qname string) (queries []string) { - ndots := 0 + args = flag.Args() - for _, c := range qname { - if c == '.' { - ndots++ - continue - } + if optHelp { + help() + os.Exit(1) } - if ndots >= cr.NDots { - queries = append(queries, qname) - } else { - if len(cr.Domain) > 0 { - queries = append(queries, qname+"."+cr.Domain) - } - for _, s := range cr.Search { - queries = append(queries, qname+"."+s) - } + if len(args) == 0 { + help() + os.Exit(1) } - return -} + rsol.cmd = strings.ToLower(args[0]) -func messagePrint(nameserver string, msg *dns.Message) string { - var b strings.Builder + switch rsol.cmd { + case cmdQuery: + args = args[1:] + if len(args) == 0 { + log.Fatalf("resolver: %s: missing argument", rsol.cmd) + } - fmt.Fprintf(&b, "< From: %s", nameserver) - fmt.Fprintf(&b, "\n> Header: %+v", msg.Header) - fmt.Fprintf(&b, "\n> Question: %s", msg.Question.String()) + rsol.doCmdQuery(args) - b.WriteString("\n> Status:") - switch msg.Header.RCode { - case dns.RCodeOK: - b.WriteString(" OK") - case dns.RCodeErrFormat: - b.WriteString(" Invalid request format") - case dns.RCodeErrServer: - b.WriteString(" Server internal failure") - case dns.RCodeErrName: - b.WriteString(" Domain name did not exist") - case dns.RCodeNotImplemented: - b.WriteString(" Unknown query") - case dns.RCodeRefused: - b.WriteString(" Server refused the request") + default: + log.Printf("resolver: unknown command: %s", rsol.cmd) + os.Exit(2) } +} - if msg.Header.RCode != dns.RCodeOK { - return b.String() - } +func help() { + fmt.Println(` += resolver: command line interface for DNS and rescached server - for x, rr := range msg.Answer { - fmt.Fprintf(&b, "\n> Answer #%d:", x+1) - fmt.Fprintf(&b, "\n>> Resource record: %s", rr.String()) - fmt.Fprintf(&b, "\n>> RDATA: %s", rr.Value) - } - for x, rr := range msg.Authority { - fmt.Fprintf(&b, "\n> Authority #%d:", x+1) - fmt.Fprintf(&b, "\n>> Resource record: %s", rr.String()) - fmt.Fprintf(&b, "\n>> RDATA: %s", rr.Value) - } - for x, rr := range msg.Additional { - fmt.Fprintf(&b, "\n> Additional #%d:", x+1) - fmt.Fprintf(&b, "\n>> Resource record: %s", rr.String()) - fmt.Fprintf(&b, "\n>> RDATA: %s", rr.Value) - } +== Usage - return b.String() -} + resolver [-ns nameserver] [-insecure] <command> <args> -func lookup(opts *options, cl dns.Client, timeout time.Duration, qname string, -) *dns.Message { - var ( - err error - ) +== Options - rand.Seed(time.Now().Unix()) +Accepted command is query. - cl.SetTimeout(timeout) +-ns nameserver - req := dns.NewMessage() - req.Header.ID = uint16(rand.Intn(65535)) - req.Question.Name = qname - req.Question.Type = opts.qtype - req.Question.Class = opts.qclass - _, err = req.Pack() - if err != nil { - log.Fatal("! Pack:", err) - } + Parent name server address using scheme based. + For example, + udp://35.240.172.103:53 for querying with UDP, + tcp://35.240.172.103:53 for querying with TCP, + https://35.240.172:103:853 for querying with DNS over TLS (DoT), and + https://kilabit.info/dns-query for querying with DNS over HTTPS (DoH). - res, err := cl.Query(req) - if err != nil { - log.Println("! Lookup: ", err) - return nil - } +-insecure - if res.Header.RCode == 0 { - return res - } + Ignore invalid server certificate when querying DoT, DoH, or rescached + server. - switch res.Header.RCode { - case dns.RCodeErrFormat: - log.Println("! ResponseCode: Format error") - case dns.RCodeErrServer: - log.Println("! ResponseCode: Server failure") - case dns.RCodeErrName: - log.Println("! ResponseCode: Domain not exist") - case dns.RCodeNotImplemented: - log.Println("! ResponseCode: Not implemented") - case dns.RCodeRefused: - log.Println("! ResponseCode: Refused") - } - return nil -} +== Commands -func main() { - var ( - cl dns.Client - rc *libnet.ResolvConf - res *dns.Message - err error - ) +query <domain / ip-address> [type] [class] - log.SetFlags(0) + Query the domain or IP address with optional type and/or class. - opts, err := newOptions() - if err != nil { - log.Fatal("! ", err) - } + Unless the option "-ns" is given, the query command will use the + nameserver defined in the system resolv.conf file. - fmt.Printf("= options: %+v\n", opts) + Valid type are either A, NS, CNAME, SOA, MB, MG, MR, NULL, + WKS, PTR, HINFO, MINFO, MX, TXT, AAAA, or SRV. + Default value is A." - rc, systemResolver := initSystemResolver() + Valid class are either IN, CS, HS. + Default value is IN. - fmt.Printf("= resolv.conf: %+v\n", rc) +== Examples - if len(opts.nameserver) == 0 { - cl = systemResolver - } else { - cl, err = dns.NewClient(opts.nameserver, opts.insecure) - if err != nil { - log.Fatal(err) - } - } +Query the MX records using UDP on name server 35.240.172.103, - queries := populateQueries(rc, opts.qname) - timeout := time.Duration(rc.Timeout) * time.Second + $ resolver -ns udp://35.240.172.103 query kilabit.info MX - // The algorithm used is to try a name server, and if the query - // times out, try the next, until out of name servers, then repeat - // trying all the name servers until a maximum number of retries are - // made.) - for _, qname := range queries { - for x := 0; x < rc.Attempts; x++ { - fmt.Printf("> Lookup %s at %s\n", qname, cl.RemoteAddr()) +Query the IPv4 records of domain name "kilabit.info" using DNS over TLS on +name server 35.240.172.103, - res = lookup(opts, cl, timeout, qname) - if res != nil { - goto out - } - } - } + $ resolver -ns https://35.240.172.103 -insecure query kilabit.info -out: - if res != nil { - println(messagePrint(cl.RemoteAddr(), res)) - } +Query the IPv4 records of domain name "kilabit.info" using DNS over HTTPS on +name server kilabit.info, + + $ resolver -ns https://kilabit.info/dns-query query kilabit.info`) } |
