github.com/phuslu/fastdns@v0.8.3-0.20240310041952-69506fc67dd1/cmd/fastdig/fastdig.go (about) 1 package main 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "net/netip" 7 "os" 8 "regexp" 9 "runtime" 10 "strings" 11 "time" 12 13 "github.com/phuslu/fastdns" 14 ) 15 16 func main() { 17 domain, qtype, server, options := parse(os.Args[1:]) 18 19 client := &fastdns.Client{ 20 AddrPort: netip.AddrPortFrom(netip.MustParseAddr(server), 53), 21 ReadTimeout: 2 * time.Second, 22 MaxConns: 1000, 23 } 24 25 req, resp := fastdns.AcquireMessage(), fastdns.AcquireMessage() 26 defer fastdns.ReleaseMessage(req) 27 defer fastdns.ReleaseMessage(resp) 28 29 req.SetRequestQuestion(domain, fastdns.ParseType(qtype), fastdns.ClassINET) 30 31 start := time.Now() 32 err := client.Exchange(req, resp) 33 if err != nil { 34 fmt.Fprintf(os.Stderr, "client=%+v exchange(\"%s\") error: %+v\n", client, domain, err) 35 os.Exit(1) 36 } 37 end := time.Now() 38 39 if opt("raw", options) { 40 fmt.Printf("%x\n", resp.Raw) 41 } 42 43 if opt("short", options) { 44 short(resp) 45 } else { 46 cmd(req, resp, server, start, end) 47 } 48 } 49 50 func parse(args []string) (domain, qtype, server string, options []string) { 51 for i := 0; i < len(args); i++ { 52 arg := args[i] 53 if arg == "" { 54 continue 55 } 56 switch arg[0] { 57 case '@': 58 server = arg[1:] 59 case '+': 60 options = append(options, arg[1:]) 61 default: 62 if domain == "" { 63 domain = arg 64 } else { 65 qtype = arg 66 } 67 } 68 } 69 if server == "" { 70 server = "8.8.8.8" 71 if data, err := os.ReadFile("/etc/resolv.conf"); err == nil { 72 if m := regexp.MustCompile(`(^|\n)\s*nameserver\s+(\S+)`).FindAllStringSubmatch(string(data), -1); len(m) != 0 { 73 server = m[0][2] 74 } 75 } 76 } 77 if qtype == "" { 78 qtype = "A" 79 } 80 if fastdns.ParseType(qtype) == 0 && fastdns.ParseType(domain) != 0 { 81 domain, qtype = qtype, domain 82 } 83 return 84 } 85 86 func opt(option string, options []string) bool { 87 for _, s := range options { 88 if s == option { 89 return true 90 } 91 } 92 return false 93 } 94 95 func short(resp *fastdns.Message) { 96 _ = resp.Walk(func(name []byte, typ fastdns.Type, class fastdns.Class, ttl uint32, data []byte) bool { 97 var v interface{} 98 switch typ { 99 case fastdns.TypeA, fastdns.TypeAAAA: 100 v, _ = netip.AddrFromSlice(data) 101 case fastdns.TypeCNAME, fastdns.TypeNS: 102 v = fmt.Sprintf("%s.", resp.DecodeName(nil, data)) 103 case fastdns.TypeMX: 104 v = fmt.Sprintf("%d %s.", binary.BigEndian.Uint16(data), resp.DecodeName(nil, data[2:])) 105 case fastdns.TypeTXT: 106 v = fmt.Sprintf("\"%s\"", data[1:]) 107 case fastdns.TypeSRV: 108 priority := binary.BigEndian.Uint16(data) 109 weight := binary.BigEndian.Uint16(data[2:]) 110 port := binary.BigEndian.Uint16(data[4:]) 111 target := resp.DecodeName(nil, data[6:]) 112 v = fmt.Sprintf("%d %d %d %s.", priority, weight, port, target) 113 case fastdns.TypeSOA: 114 var mname []byte 115 for i, b := range data { 116 if b == 0 { 117 mname = data[:i+1] 118 break 119 } else if b&0b11000000 == 0b11000000 { 120 mname = data[:i+2] 121 break 122 } 123 } 124 nname := resp.DecodeName(nil, data[len(mname):len(data)-20]) 125 mname = resp.DecodeName(nil, mname) 126 serial := binary.BigEndian.Uint32(data[len(data)-20:]) 127 refresh := binary.BigEndian.Uint32(data[len(data)-16:]) 128 retry := binary.BigEndian.Uint32(data[len(data)-12:]) 129 expire := binary.BigEndian.Uint32(data[len(data)-8:]) 130 minimum := binary.BigEndian.Uint32(data[len(data)-4:]) 131 v = fmt.Sprintf("%s. %s. %d %d %d %d %d", mname, nname, serial, refresh, retry, expire, minimum) 132 default: 133 v = fmt.Sprintf("%x", data) 134 } 135 fmt.Printf("%s\n", v) 136 return true 137 }) 138 } 139 140 func cmd(req, resp *fastdns.Message, server string, start, end time.Time) { 141 var flags string 142 for _, f := range []struct { 143 b byte 144 s string 145 }{ 146 {resp.Header.Flags.QR(), "qr"}, 147 {resp.Header.Flags.AA(), "aa"}, 148 {resp.Header.Flags.TC(), "tc"}, 149 {resp.Header.Flags.RD(), "rd"}, 150 {resp.Header.Flags.RA(), "ra"}, 151 } { 152 if f.b == 0 { 153 continue 154 } 155 flags += f.s + " " 156 } 157 flags = strings.TrimSpace(flags) 158 159 fmt.Printf("\n") 160 fmt.Printf("; <<>> DiG 0.0.1-fastdns-%s <<>> %s\n", runtime.Version(), req.Domain) 161 fmt.Printf(";; global options: +cmd +noedns\n") 162 fmt.Printf(";; Got answer:\n") 163 fmt.Printf(";; ->>HEADER<<- opcode: %s, status: %s, id: %d\n", 164 strings.ToUpper(resp.Header.Flags.Opcode().String()), strings.ToUpper(resp.Header.Flags.Rcode().String()), resp.Header.ID) 165 fmt.Printf(";; flags: %s; QUERY: %d, ANSWER: %d, AUTHORITY: %d, ADDITIONAL: %d\n", 166 flags, resp.Header.QDCount, resp.Header.ANCount, resp.Header.NSCount, resp.Header.ARCount) 167 168 fmt.Printf("\n") 169 // fmt.Printf(";; OPT PSEUDOSECTION:\n") 170 // fmt.Printf("; EDNS: version: 0, flags:; udp: 512\n") 171 fmt.Printf(";; QUESTION SECTION:\n") 172 fmt.Printf(";%s. %s %s\n", req.Domain, req.Question.Class, req.Question.Type) 173 174 fmt.Printf("\n") 175 if resp.Header.ANCount > 0 { 176 fmt.Printf(";; ANSWER SECTION:\n") 177 } else { 178 fmt.Printf(";; AUTHORITY SECTION:\n") 179 } 180 _ = resp.Walk(func(name []byte, typ fastdns.Type, class fastdns.Class, ttl uint32, data []byte) bool { 181 var v interface{} 182 switch typ { 183 case fastdns.TypeA, fastdns.TypeAAAA: 184 v, _ = netip.AddrFromSlice(data) 185 case fastdns.TypeCNAME, fastdns.TypeNS: 186 v = fmt.Sprintf("%s.", resp.DecodeName(nil, data)) 187 case fastdns.TypeMX: 188 v = fmt.Sprintf("%d %s.", binary.BigEndian.Uint16(data), resp.DecodeName(nil, data[2:])) 189 case fastdns.TypeTXT: 190 v = fmt.Sprintf("\"%s\"", data[1:]) 191 case fastdns.TypeSRV: 192 priority := binary.BigEndian.Uint16(data) 193 weight := binary.BigEndian.Uint16(data[2:]) 194 port := binary.BigEndian.Uint16(data[4:]) 195 target := resp.DecodeName(nil, data[6:]) 196 v = fmt.Sprintf("%d %d %d %s.", priority, weight, port, target) 197 case fastdns.TypeSOA: 198 var mname []byte 199 for i, b := range data { 200 if b == 0 { 201 mname = data[:i+1] 202 break 203 } else if b&0b11000000 == 0b11000000 { 204 mname = data[:i+2] 205 break 206 } 207 } 208 nname := resp.DecodeName(nil, data[len(mname):len(data)-20]) 209 mname = resp.DecodeName(nil, mname) 210 serial := binary.BigEndian.Uint32(data[len(data)-20:]) 211 refresh := binary.BigEndian.Uint32(data[len(data)-16:]) 212 retry := binary.BigEndian.Uint32(data[len(data)-12:]) 213 expire := binary.BigEndian.Uint32(data[len(data)-8:]) 214 minimum := binary.BigEndian.Uint32(data[len(data)-4:]) 215 v = fmt.Sprintf("%s. %s. %d %d %d %d %d", mname, nname, serial, refresh, retry, expire, minimum) 216 default: 217 v = fmt.Sprintf("%x", data) 218 } 219 fmt.Printf("%s. %d %s %s %s\n", resp.DecodeName(nil, name), ttl, class, typ, v) 220 return true 221 }) 222 223 fmt.Printf("\n") 224 fmt.Printf(";; Query time: %d msec\n", end.Sub(start)/time.Millisecond) 225 fmt.Printf(";; SERVER: %s#53(%s)\n", server, server) 226 fmt.Printf(";; WHEN: %s\n", start.Format("Mon Jan 02 15:04:05 MST 2006")) 227 fmt.Printf(";; MSG SIZE rcvd: %d\n", len(resp.Raw)) 228 fmt.Printf("\n") 229 }