diff options
| author | Shulhan <ms@kilabit.info> | 2022-04-14 23:34:09 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2022-04-15 00:25:05 +0700 |
| commit | 4d3ab7f49410eb2b774a7c9e25ba10a1efbd1413 (patch) | |
| tree | eae4f143752ff1f6b48f1763399ff6a7f4215f53 | |
| parent | 358c5ee72207c37712e44edcdb479c97f1266c8a (diff) | |
| download | rescached-4d3ab7f49410eb2b774a7c9e25ba10a1efbd1413.tar.xz | |
cmd/resolver: implement command to search the caches
The "caches" command accept sub command "search" with a string
argument.
Given the following command "caches search bit", the resolver will
call HTTP API to rescached server to search for caches that have name
contains bit.
| -rw-r--r-- | _doc/resolver.1.gz | bin | 1941 -> 2104 bytes | |||
| -rw-r--r-- | _doc/resolver.adoc | 15 | ||||
| -rw-r--r-- | client.go | 38 | ||||
| -rw-r--r-- | cmd/resolver/main.go | 33 | ||||
| -rw-r--r-- | cmd/resolver/resolver.go | 165 |
5 files changed, 191 insertions, 60 deletions
diff --git a/_doc/resolver.1.gz b/_doc/resolver.1.gz Binary files differindex ca2af09..29fe89c 100644 --- a/_doc/resolver.1.gz +++ b/_doc/resolver.1.gz diff --git a/_doc/resolver.adoc b/_doc/resolver.adoc index e71b406..d529394 100644 --- a/_doc/resolver.adoc +++ b/_doc/resolver.adoc @@ -101,6 +101,14 @@ Fetch and print all caches from rescached server. -- +caches search <string>:: ++ +-- +Search the domain name in rescached caches. +This command can also be used to inspect each DNS message on the caches. +-- + + == EXIT STATUS Upon exit and success +resolver+ will return 0, or 1 otherwise. @@ -131,10 +139,15 @@ name server kilabit.info, $ resolver -ns=https://kilabit.info/dns-query query kilabit.info -Inspect the rescached's caches on server at http://127.0.0.1:5380 +Inspect the rescached's caches on server at http://127.0.0.1:5380, $ resolver -server=http://127.0.0.1:5380 caches +Search caches that contains "bit" on the domain name, + + $ resolver -server=http://127.0.0.1:5380 caches search bit + + == AUTHOR This software is developed by M. Shulhan (ms@kilabit.info). @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "net/http" + "net/url" "github.com/shuLhan/share/lib/dns" libhttp "github.com/shuLhan/share/lib/http" @@ -53,8 +54,43 @@ func (cl *Client) Caches() (answers []*dns.Answer, err error) { } if res.Code != http.StatusOK { - return nil, fmt.Errorf("%s: %s", logp, res.Code, res.Message) + return nil, fmt.Errorf("%s: %d %s", logp, res.Code, res.Message) } return answers, nil } + +// CachesSearch search the answer in caches by its domain name and return it +// as DNS message. +func (cl *Client) CachesSearch(q string) (listMsg []*dns.Message, err error) { + var ( + logp = "CachesSearch" + params = url.Values{} + res = libhttp.EndpointResponse{ + Data: &listMsg, + } + + path string + resb []byte + ) + + params.Set(paramNameQuery, q) + + path = apiCachesSearch + "?" + params.Encode() + fmt.Printf("%s: path: %s\n", logp, path) + _, resb, err = cl.Get(path, 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: %d %s", logp, res.Code, res.Message) + } + + return listMsg, nil +} diff --git a/cmd/resolver/main.go b/cmd/resolver/main.go index 98e448d..322ef85 100644 --- a/cmd/resolver/main.go +++ b/cmd/resolver/main.go @@ -17,12 +17,15 @@ import ( const ( cmdCaches = "caches" cmdQuery = "query" + + subCmdSearch = "search" ) func main() { var ( rsol = new(resolver) + subCmd string args []string optHelp bool ) @@ -52,7 +55,21 @@ func main() { switch rsol.cmd { case cmdCaches: - rsol.doCmdCaches() + args = args[1:] + if len(args) == 0 { + rsol.doCmdCaches() + return + } + + subCmd = strings.ToLower(args[0]) + switch subCmd { + case subCmdSearch: + args = args[1:] + if len(args) == 0 { + log.Fatalf("resolver: %s %s: missing argument", rsol.cmd, subCmd) + } + rsol.doCmdCachesSearch(args[0]) + } case cmdQuery: args = args[1:] @@ -125,6 +142,13 @@ caches Fetch and print all caches from rescached server. +caches search <string> + + Search the domain name in rescached caches. + This command can also be used to inspect each DNS message on the + caches. + + == Examples Query the IPv4 address for kilabit.info, @@ -150,8 +174,11 @@ name server 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 +Inspect the rescached's caches on server at http://127.0.0.1:5380, $ resolver -server=http://127.0.0.1:5380 caches -`) + +Search caches that contains "bit" on the domain name, + + $ resolver -server=http://127.0.0.1:5380 caches search bit`) } diff --git a/cmd/resolver/resolver.go b/cmd/resolver/resolver.go index c9f172e..5ba632f 100644 --- a/cmd/resolver/resolver.go +++ b/cmd/resolver/resolver.go @@ -42,24 +42,14 @@ type resolver struct { insecure bool } +// doCmdCaches call the rescached HTTP API to fetch all caches. 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 - ) + resc = rsol.newRescachedClient() - if len(rsol.rescachedUrl) == 0 { - rsol.rescachedUrl = defRescachedUrl - } - - resc = rescached.NewClient(rsol.rescachedUrl, rsol.insecure) + answers []*dns.Answer + err error + ) answers, err = resc.Caches() if err != nil { @@ -68,31 +58,27 @@ func (rsol *resolver) doCmdCaches() { } fmt.Printf("Total caches: %d\n", len(answers)) + printAnswers(answers) +} - for _, answer = range answers { - if len(answer.QName) > maxNameLen { - maxNameLen = len(answer.QName) - } - } +// doCmdCachesSearch call the rescached HTTP API to search the caches by +// domain name. +func (rsol *resolver) doCmdCachesSearch(q string) { + var ( + resc = rsol.newRescachedClient() - 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()) + listMsg []*dns.Message + err error + ) - 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), - ) + listMsg, err = resc.CachesSearch(q) + if err != nil { + log.Printf("resolver: caches: %s", err) + return } + + fmt.Printf("Total search: %d\n", len(listMsg)) + printMessages(listMsg) } func (rsol *resolver) doCmdQuery(args []string) { @@ -204,6 +190,15 @@ func (rsol *resolver) initSystemResolver() (err error) { return nil } +// newRescachedClient create new rescached Client. +func (rsol *resolver) newRescachedClient() (resc *rescached.Client) { + if len(rsol.rescachedUrl) == 0 { + rsol.rescachedUrl = defRescachedUrl + } + resc = rescached.NewClient(rsol.rescachedUrl, rsol.insecure) + return resc +} + func (rsol *resolver) query(timeout time.Duration, qname string) (res *dns.Message, err error) { var ( logp = "query" @@ -255,12 +250,87 @@ func populateQueries(cr *libnet.ResolvConf, qname string) (queries []string) { return } +// printAnswers print list of DNS Answer to stdout. +func printAnswers(answers []*dns.Answer) { + var ( + answer *dns.Answer + format string + header string + line strings.Builder + x int + maxNameLen int + ) + + 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), + ) + } +} + +// printMessages print list of DNS Message to standard output. +func printMessages(listMsg []*dns.Message) { + var ( + msg *dns.Message + ) + + for _, msg = range listMsg { + printMessage(msg) + } +} + +// printMessage print a DNS Message to standard output. +func printMessage(msg *dns.Message) { + var ( + rr dns.ResourceRecord + x int + ) + + fmt.Printf("\nQuestion: %+v\n", msg.Question) + fmt.Printf(" Header: %+v\n", msg.Header) + + for x, rr = range msg.Answer { + fmt.Printf(" Answer #%d\n", x) + fmt.Printf(" Resource record: %s\n", rr.String()) + fmt.Printf(" RDATA: %+v\n", rr.Value) + } + for x, rr = range msg.Authority { + fmt.Printf(" Authority #%d\n", x) + fmt.Printf(" Resource record: %s\n", rr.String()) + fmt.Printf(" RDATA: %+v\n", rr.Value) + } + for x, rr = range msg.Additional { + fmt.Printf(" Additional #%d\n", x) + fmt.Printf(" Resource record: %s\n", rr.String()) + fmt.Printf(" RDATA: %+v\n", rr.Value) + } +} + +// printQueryResponse print query response from nameserver including its DNS +// message as answer to stdout. func printQueryResponse(nameserver string, msg *dns.Message) { var b strings.Builder fmt.Fprintf(&b, "> From: %s", nameserver) - fmt.Fprintf(&b, "\n> Header: %+v", msg.Header) - fmt.Fprintf(&b, "\n> Question: %s", msg.Question.String()) b.WriteString("\n> Status: ") switch msg.Header.RCode { @@ -279,22 +349,7 @@ func printQueryResponse(nameserver string, msg *dns.Message) { case dns.RCodeRefused: b.WriteString(" Server refused the request") } - - 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: %+v", 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: %+v", 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: %+v", rr.Value) - } - + b.WriteString("\n> Response: ") fmt.Println(b.String()) + printMessage(msg) } |
