aboutsummaryrefslogtreecommitdiff
path: root/cmd/resolver/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/resolver/main.go')
-rw-r--r--cmd/resolver/main.go238
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`)
}