diff options
| -rw-r--r-- | _doc/resolver.1.gz | bin | 1460 -> 1941 bytes | |||
| -rw-r--r-- | _doc/resolver.adoc | 113 | ||||
| -rw-r--r-- | client.go | 60 | ||||
| -rw-r--r-- | cmd/resolver/main.go | 68 | ||||
| -rw-r--r-- | cmd/resolver/resolver.go | 73 |
5 files changed, 252 insertions, 62 deletions
diff --git a/_doc/resolver.1.gz b/_doc/resolver.1.gz Binary files differindex d495fa6..ca2af09 100644 --- a/_doc/resolver.1.gz +++ b/_doc/resolver.1.gz diff --git a/_doc/resolver.adoc b/_doc/resolver.adoc index 0aa3a70..e71b406 100644 --- a/_doc/resolver.adoc +++ b/_doc/resolver.adoc @@ -9,45 +9,72 @@ == NAME -resolver - a tool to query DNS record, specifically build for *rescached*(1) -and developer. +resolver - command line interface for DNS and rescached server. == SYNOPSIS -+resolver+ [-ns nameserver] [-t type] [-c class] hostname +resolver [-insecure] [-ns nameserver] [-server] <command> [args...] == DESCRIPTION -+resolver+ is a tool to resolve hostname to address, or to query services -on hostname by type (MX, SOA, TXT, etc.). -It will return and print the DNS response. +resolver is a tool to resolve hostname to IP address or to query services +on hostname by type (MX, SOA, TXT, etc.) using standard DNS protocol with UDP, +DNS over TLS (DoT), or DNS over HTTPS (DoH). == OPTIONS -[[nameserver]] -=== +nameserver+ +`-insecure`:: ++ +-- +Ignore invalid server certificate when querying DoT, DoH, or rescached server. +-- + +`-ns <nameserver>`:: ++ +-- +This option define the parent DNS server where the resolver send the query. +Default to one of "nameserver" in `/etc/resolv.conf`. + +The nameserver is defined in the following format, + + ("udp"/"tcp"/"https") "://" (domain / ip-address) [":" port] + +Examples, + +* udp://194.233.68.184:53 for querying with UDP, +* tcp://194.233.68.184:53 for querying with TCP, +* https://194.233.68.184:853 for querying with DNS over TLS (DoT), and +* https://kilabit.info/dns-query for querying with DNS over HTTPS (DoH). +-- + +`-server <rescached-URL>`:: ++ +-- +Set the rescached HTTP server where commands will send. +The rescached-URL use HTTP scheme: -Value:: Internet address -Format:: xxx.xxx.xxx.xxx[:port] -Default:: One of +nameserver+ in +/etc/resolv.conf+ -Description:: This option define the DNS server where the resolver will -send the query. + ("http" / "https") "://" (domain / ip-address) [":" port] -[[type]] -=== +type+ +Default to "https://127.0.0.1:5380" if its empty. +-- -Value:: DNS record type -Format:: String -Default:: A -Description:: List of supported DNS record type, +== COMMANDS + +query <domain / ip-address> [type] [class]:: + ----- -TYPE (ID) - Description +-- +Query the domain or IP address with optional type and/or class. + +Unless the option "-ns" is given, the query command will use the +nameserver defined in the system resolv.conf file. + +The "type" parameter define DNS record type to be queried. +List of valid types, -* A (1) - a host Address +* A (1) - a host Address (default) * NS (2) - an authoritative Name Server * CNAME (5) - the Canonical NAME for an alias * SOA (6) - marks the Start of a zone of Authority @@ -63,15 +90,15 @@ TYPE (ID) - Description * TXT (16) - TeXT strings * AAAA (28) - a host address in IPv6 * SRV (33) - a SerViCe record ----- -[[hostname]] -=== +hostname+ +The "class" parameter is optional, its either IN (default), CS, or HS. +-- -Value:: Internet hostname -Format:: [subdomain].[domain][.TLD] -Default:: - -Description:: Hostname that will be queried to name server. +caches:: ++ +-- +Fetch and print all caches from rescached server. +-- == EXIT STATUS @@ -79,22 +106,34 @@ Description:: Hostname that will be queried to name server. Upon exit and success +resolver+ will return 0, or 1 otherwise. -== EXAMPLE +== EXAMPLES + +Query the IPv4 address for kilabit.info, -Resolve the IPv4 address for kilabit.info using one of nameserver in -`/etc/resolv.conf`, + $ resolver query kilabit.info - $ resolver kilabit.info +Query the mail exchange (MX) for domain kilabit.info, -Resolve the IPv4 address for kilabit.info using 127.0.0.1 at port 54 as + $ resolver query kilabit.info MX + +Query the IPv4 address for kilabit.info using 127.0.0.1 at port 53 as name server, - $ resolver -ns 127.0.0.1:54 kilabit.info + $ resolver -ns=udp://127.0.0.1:53 query kilabit.info + +Query the IPv4 address of domain name "kilabit.info" using DNS over TLS at +name server 194.233.68.184, + + $ resolver -insecure -ns=https://194.233.68.184 query kilabit.info + +Query the IPv4 records of domain name "kilabit.info" using DNS over HTTPS on +name server kilabit.info, -Resolve the mail exchange (MX) for kilabit.info, + $ resolver -ns=https://kilabit.info/dns-query query kilabit.info - $ resolver -t MX kilabit.info +Inspect the rescached's caches on server at http://127.0.0.1:5380 + $ resolver -server=http://127.0.0.1:5380 caches == AUTHOR diff --git a/client.go b/client.go new file mode 100644 index 0000000..0d626f1 --- /dev/null +++ b/client.go @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2022 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later + +package rescached + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/shuLhan/share/lib/dns" + libhttp "github.com/shuLhan/share/lib/http" +) + +// Client for rescached. +type Client struct { + *libhttp.Client +} + +// NewClient create new HTTP client that connect to rescached HTTP server. +func NewClient(serverUrl string, insecure bool) (cl *Client) { + var ( + httpcOpts = libhttp.ClientOptions{ + ServerUrl: serverUrl, + AllowInsecure: insecure, + } + ) + cl = &Client{ + Client: libhttp.NewClient(&httpcOpts), + } + return cl +} + +// Caches fetch all of non-local caches from server. +func (cl *Client) Caches() (answers []*dns.Answer, err error) { + var ( + logp = "Caches" + res = libhttp.EndpointResponse{ + Data: &answers, + } + + resb []byte + ) + + _, resb, err = cl.Get(apiCaches, nil, nil) + if err != nil { + return nil, fmt.Errorf("%s: %w", logp, err) + } + + err = json.Unmarshal(resb, &res) + if err != nil { + return nil, fmt.Errorf("%s: %w", logp, err) + } + + if res.Code != http.StatusOK { + return nil, fmt.Errorf("%s: %s", logp, res.Code, res.Message) + } + + return answers, nil +} diff --git a/cmd/resolver/main.go b/cmd/resolver/main.go index 49ba6a6..98e448d 100644 --- a/cmd/resolver/main.go +++ b/cmd/resolver/main.go @@ -15,7 +15,8 @@ import ( // List of valid commands. const ( - cmdQuery = "query" + cmdCaches = "caches" + cmdQuery = "query" ) func main() { @@ -28,8 +29,9 @@ func main() { log.SetFlags(0) + flag.BoolVar(&rsol.insecure, "insecure", false, "Ignore invalid server certificate.") flag.StringVar(&rsol.nameserver, "ns", "", "Parent name server address using scheme based.") - flag.BoolVar(&rsol.insecure, "insecure", false, "Ignore invalid server certificate") + flag.StringVar(&rsol.rescachedUrl, "server", defRescachedUrl, "Set the rescached HTTP server.") flag.BoolVar(&optHelp, "h", false, "") flag.Parse() @@ -49,6 +51,9 @@ func main() { rsol.cmd = strings.ToLower(args[0]) switch rsol.cmd { + case cmdCaches: + rsol.doCmdCaches() + case cmdQuery: args = args[1:] if len(args) == 0 { @@ -69,25 +74,36 @@ func help() { == Usage - resolver [-ns nameserver] [-insecure] <command> <args> + resolver [-insecure] [-ns nameserver] [-server] <command> [args...] == Options -Accepted command is query. +The following options affect the commands operation. + +-insecure + + Ignore invalid server certificate when querying DoT, DoH, or rescached + server. -ns nameserver 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). --insecure + * 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). - Ignore invalid server certificate when querying DoT, DoH, or rescached - server. +-server <rescached-URL> + + Set the location of rescached HTTP server where commands will send. + The rescached-URL use HTTP scheme: + + ("http" / "https") "://" (domain / ip-address) [":" port] + + Default to "https://127.0.0.1:5380" if its empty. == Commands @@ -105,19 +121,37 @@ query <domain / ip-address> [type] [class] Valid class are either IN, CS, HS. Default value is IN. +caches + + Fetch and print all caches from rescached server. + == Examples -Query the MX records using UDP on name server 35.240.172.103, +Query the IPv4 address for kilabit.info, + + $ resolver query kilabit.info - $ resolver -ns udp://35.240.172.103 query kilabit.info MX +Query the mail exchange (MX) for domain kilabit.info, -Query the IPv4 records of domain name "kilabit.info" using DNS over TLS on -name server 35.240.172.103, + $ resolver query kilabit.info MX - $ resolver -ns https://35.240.172.103 -insecure query kilabit.info +Query the IPv4 address for kilabit.info using 127.0.0.1 at port 53 as +name server, + + $ resolver -ns=udp://127.0.0.1:53 query kilabit.info + +Query the IPv4 address of domain name "kilabit.info" using DNS over TLS at +name server 194.233.68.184, + + $ resolver -insecure -ns=https://194.233.68.184 query kilabit.info 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`) + $ resolver -insecure -ns=https://kilabit.info/dns-query query kilabit.info + +Inspect the rescached's caches on server at http://127.0.0.1:5380 + + $ resolver -server=http://127.0.0.1:5380 caches +`) } diff --git a/cmd/resolver/resolver.go b/cmd/resolver/resolver.go index 19cebaf..c9f172e 100644 --- a/cmd/resolver/resolver.go +++ b/cmd/resolver/resolver.go @@ -10,16 +10,18 @@ import ( "strings" "time" + "github.com/shuLhan/rescached-go/v4" "github.com/shuLhan/share/lib/dns" libnet "github.com/shuLhan/share/lib/net" ) const ( - defAttempts = 1 - defQueryType = "A" - defQueryClass = "IN" - defResolvConf = "/etc/resolv.conf" - defTimeout = 5 * time.Second + defAttempts = 1 + defQueryType = "A" + defQueryClass = "IN" + defRescachedUrl = "http://127.0.0.1:5380" + defResolvConf = "/etc/resolv.conf" + defTimeout = 5 * time.Second ) type resolver struct { @@ -31,13 +33,68 @@ type resolver struct { sqtype string sqclass string - nameserver string - qtype dns.RecordType - qclass dns.RecordClass + nameserver string + rescachedUrl string + + qtype dns.RecordType + qclass dns.RecordClass insecure bool } +func (rsol *resolver) doCmdCaches() { + var ( + resc *rescached.Client + answer *dns.Answer + answers []*dns.Answer + format string + header string + line strings.Builder + err error + x int + maxNameLen int + ) + + if len(rsol.rescachedUrl) == 0 { + rsol.rescachedUrl = defRescachedUrl + } + + resc = rescached.NewClient(rsol.rescachedUrl, rsol.insecure) + + answers, err = resc.Caches() + if err != nil { + log.Printf("resolver: caches: %s", err) + return + } + + fmt.Printf("Total caches: %d\n", len(answers)) + + for _, answer = range answers { + if len(answer.QName) > maxNameLen { + maxNameLen = len(answer.QName) + } + } + + format = fmt.Sprintf("%%4s | %%%ds | %%4s | %%5s | %%30s | %%30s", maxNameLen) + header = fmt.Sprintf(format, "#", "Name", "Type", "Class", "Received at", "Accessed at") + for x = 0; x < len(header); x++ { + line.WriteString("-") + } + fmt.Println(line.String()) + fmt.Println(header) + fmt.Println(line.String()) + + format = fmt.Sprintf("%%4d | %%%ds | %%4s | %%5s | %%30s | %%30s\n", maxNameLen) + for x, answer = range answers { + fmt.Printf(format, x, answer.QName, + dns.RecordTypeNames[answer.RType], + dns.RecordClassName[answer.RClass], + time.Unix(answer.ReceivedAt, 0), + time.Unix(answer.AccessedAt, 0), + ) + } +} + func (rsol *resolver) doCmdQuery(args []string) { var ( maxAttempts = defAttempts |
