summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2022-04-14 23:34:09 +0700
committerShulhan <ms@kilabit.info>2022-04-15 00:25:05 +0700
commit4d3ab7f49410eb2b774a7c9e25ba10a1efbd1413 (patch)
treeeae4f143752ff1f6b48f1763399ff6a7f4215f53
parent358c5ee72207c37712e44edcdb479c97f1266c8a (diff)
downloadrescached-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.gzbin1941 -> 2104 bytes
-rw-r--r--_doc/resolver.adoc15
-rw-r--r--client.go38
-rw-r--r--cmd/resolver/main.go33
-rw-r--r--cmd/resolver/resolver.go165
5 files changed, 191 insertions, 60 deletions
diff --git a/_doc/resolver.1.gz b/_doc/resolver.1.gz
index ca2af09..29fe89c 100644
--- a/_doc/resolver.1.gz
+++ b/_doc/resolver.1.gz
Binary files differ
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).
diff --git a/client.go b/client.go
index 0d626f1..ecf6e62 100644
--- a/client.go
+++ b/client.go
@@ -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)
}