1package main 2 3import ( 4 "errors" 5 "fmt" 6 "net" 7 "os" 8 "strings" 9 "time" 10 11 "github.com/miekg/dns" 12) 13 14const myResolverHost string = "resolver.dnscrypt.info." 15const nonexistentName string = "nonexistent-zone.dnscrypt-test." 16 17func resolveQuery(server string, qName string, qType uint16) (*dns.Msg, error) { 18 client := new(dns.Client) 19 client.ReadTimeout = 2 * time.Second 20 msg := &dns.Msg{ 21 MsgHdr: dns.MsgHdr{ 22 RecursionDesired: true, 23 Opcode: dns.OpcodeQuery, 24 }, 25 Question: make([]dns.Question, 1), 26 } 27 options := &dns.OPT{ 28 Hdr: dns.RR_Header{ 29 Name: ".", 30 Rrtype: dns.TypeOPT, 31 }, 32 } 33 msg.Extra = append(msg.Extra, options) 34 options.SetDo() 35 options.SetUDPSize(uint16(MaxDNSPacketSize)) 36 msg.Question[0] = dns.Question{Name: qName, Qtype: qType, Qclass: dns.ClassINET} 37 msg.Id = dns.Id() 38 for i := 0; i < 3; i++ { 39 response, rtt, err := client.Exchange(msg, server) 40 if neterr, ok := err.(net.Error); ok && neterr.Timeout() { 41 client.ReadTimeout *= 2 42 continue 43 } 44 _ = rtt 45 if err != nil { 46 return nil, err 47 } 48 return response, nil 49 } 50 return nil, errors.New("Timeout") 51} 52 53func Resolve(server string, name string, singleResolver bool) { 54 parts := strings.SplitN(name, ",", 2) 55 if len(parts) == 2 { 56 name, server = parts[0], parts[1] 57 singleResolver = true 58 } 59 60 host, port := ExtractHostAndPort(server, 53) 61 if host == "0.0.0.0" { 62 host = "127.0.0.1" 63 } else if host == "[::]" { 64 host = "[::1]" 65 } 66 server = fmt.Sprintf("%s:%d", host, port) 67 68 fmt.Printf("Resolving [%s] using %s port %d\n\n", name, host, port) 69 name = dns.Fqdn(name) 70 71 cname := name 72 73 for once := true; once; once = false { 74 response, err := resolveQuery(server, myResolverHost, dns.TypeA) 75 if err != nil { 76 fmt.Printf("Unable to resolve: [%s]\n", err) 77 os.Exit(1) 78 } 79 fmt.Printf("Resolver : ") 80 res := make([]string, 0) 81 for _, answer := range response.Answer { 82 if answer.Header().Class != dns.ClassINET { 83 continue 84 } 85 var ip string 86 if answer.Header().Rrtype == dns.TypeA { 87 ip = answer.(*dns.A).A.String() 88 } else if answer.Header().Rrtype == dns.TypeAAAA { 89 ip = answer.(*dns.AAAA).AAAA.String() 90 } 91 if rev, err := dns.ReverseAddr(ip); err == nil { 92 response, err = resolveQuery(server, rev, dns.TypePTR) 93 if err != nil { 94 break 95 } 96 for _, answer := range response.Answer { 97 if answer.Header().Rrtype != dns.TypePTR || answer.Header().Class != dns.ClassINET { 98 continue 99 } 100 ip = ip + " (" + answer.(*dns.PTR).Ptr + ")" 101 break 102 } 103 } 104 res = append(res, ip) 105 } 106 if len(res) == 0 { 107 fmt.Println("-") 108 } else { 109 fmt.Println(strings.Join(res, ", ")) 110 } 111 } 112 113 if singleResolver { 114 for once := true; once; once = false { 115 fmt.Printf("Lying : ") 116 response, err := resolveQuery(server, nonexistentName, dns.TypeA) 117 if err != nil { 118 break 119 } 120 if response.Rcode == dns.RcodeSuccess { 121 fmt.Println("yes. That resolver returns wrong responses") 122 } else if response.Rcode == dns.RcodeNameError { 123 fmt.Println("no") 124 } else { 125 fmt.Printf("unknown - query returned %s\n", dns.RcodeToString[response.Rcode]) 126 } 127 128 if response.Rcode == dns.RcodeNameError { 129 fmt.Printf("DNSSEC : ") 130 if response.AuthenticatedData { 131 fmt.Println("yes, the resolver supports DNSSEC") 132 } else { 133 fmt.Println("no, the resolver doesn't support DNSSEC") 134 } 135 } 136 } 137 } 138 139 fmt.Println("") 140 141cname: 142 for once := true; once; once = false { 143 fmt.Printf("Canonical name: ") 144 for i := 0; i < 100; i++ { 145 response, err := resolveQuery(server, cname, dns.TypeCNAME) 146 if err != nil { 147 break cname 148 } 149 found := false 150 for _, answer := range response.Answer { 151 if answer.Header().Rrtype != dns.TypeCNAME || answer.Header().Class != dns.ClassINET { 152 continue 153 } 154 cname = answer.(*dns.CNAME).Target 155 found = true 156 break 157 } 158 if !found { 159 break 160 } 161 } 162 fmt.Println(cname) 163 } 164 165 fmt.Println("") 166 167 for once := true; once; once = false { 168 fmt.Printf("IPv4 addresses: ") 169 response, err := resolveQuery(server, cname, dns.TypeA) 170 if err != nil { 171 break 172 } 173 ipv4 := make([]string, 0) 174 for _, answer := range response.Answer { 175 if answer.Header().Rrtype != dns.TypeA || answer.Header().Class != dns.ClassINET { 176 continue 177 } 178 ipv4 = append(ipv4, answer.(*dns.A).A.String()) 179 } 180 if len(ipv4) == 0 { 181 fmt.Println("-") 182 } else { 183 fmt.Println(strings.Join(ipv4, ", ")) 184 } 185 } 186 187 for once := true; once; once = false { 188 fmt.Printf("IPv6 addresses: ") 189 response, err := resolveQuery(server, cname, dns.TypeAAAA) 190 if err != nil { 191 break 192 } 193 ipv6 := make([]string, 0) 194 for _, answer := range response.Answer { 195 if answer.Header().Rrtype != dns.TypeAAAA || answer.Header().Class != dns.ClassINET { 196 continue 197 } 198 ipv6 = append(ipv6, answer.(*dns.AAAA).AAAA.String()) 199 } 200 if len(ipv6) == 0 { 201 fmt.Println("-") 202 } else { 203 fmt.Println(strings.Join(ipv6, ", ")) 204 } 205 } 206 207 fmt.Println("") 208 209 for once := true; once; once = false { 210 fmt.Printf("Name servers : ") 211 response, err := resolveQuery(server, cname, dns.TypeNS) 212 if err != nil { 213 break 214 } 215 nss := make([]string, 0) 216 for _, answer := range response.Answer { 217 if answer.Header().Rrtype != dns.TypeNS || answer.Header().Class != dns.ClassINET { 218 continue 219 } 220 nss = append(nss, answer.(*dns.NS).Ns) 221 } 222 if response.Rcode == dns.RcodeNameError { 223 fmt.Println("name does not exist") 224 } else if response.Rcode != dns.RcodeSuccess { 225 fmt.Printf("server returned %s", dns.RcodeToString[response.Rcode]) 226 } else if len(nss) == 0 { 227 fmt.Println("no name servers found") 228 } else { 229 fmt.Println(strings.Join(nss, ", ")) 230 } 231 fmt.Printf("DNSSEC signed : ") 232 if response.AuthenticatedData { 233 fmt.Println("yes") 234 } else { 235 fmt.Println("no") 236 } 237 } 238 239 for once := true; once; once = false { 240 fmt.Printf("Mail servers : ") 241 response, err := resolveQuery(server, cname, dns.TypeMX) 242 if err != nil { 243 break 244 } 245 mxs := make([]string, 0) 246 for _, answer := range response.Answer { 247 if answer.Header().Rrtype != dns.TypeMX || answer.Header().Class != dns.ClassINET { 248 continue 249 } 250 mxs = append(mxs, answer.(*dns.MX).Mx) 251 } 252 if len(mxs) == 0 { 253 fmt.Println("no mail servers found") 254 } else if len(mxs) > 1 { 255 fmt.Printf("%d mail servers found\n", len(mxs)) 256 } else { 257 fmt.Println("1 mail servers found") 258 } 259 } 260 261 fmt.Println("") 262 263 for once := true; once; once = false { 264 fmt.Printf("HTTPS alias : ") 265 response, err := resolveQuery(server, cname, dns.TypeHTTPS) 266 if err != nil { 267 break 268 } 269 aliases := make([]string, 0) 270 for _, answer := range response.Answer { 271 if answer.Header().Rrtype != dns.TypeHTTPS || answer.Header().Class != dns.ClassINET { 272 continue 273 } 274 https := answer.(*dns.HTTPS) 275 if https.Priority != 0 || len(https.Target) < 2 { 276 continue 277 } 278 aliases = append(aliases, https.Target) 279 } 280 if len(aliases) == 0 { 281 fmt.Println("-") 282 } else { 283 fmt.Println(strings.Join(aliases, ", ")) 284 } 285 286 fmt.Printf("HTTPS info : ") 287 info := make([]string, 0) 288 for _, answer := range response.Answer { 289 if answer.Header().Rrtype != dns.TypeHTTPS || answer.Header().Class != dns.ClassINET { 290 continue 291 } 292 https := answer.(*dns.HTTPS) 293 if https.Priority == 0 || len(https.Target) > 1 { 294 continue 295 } 296 for _, value := range https.Value { 297 info = append(info, fmt.Sprintf("[%s]=[%s]", value.Key(), value.String())) 298 } 299 } 300 if len(info) == 0 { 301 fmt.Println("-") 302 } else { 303 fmt.Println(strings.Join(info, ", ")) 304 } 305 } 306 307 fmt.Println("") 308 309 for once := true; once; once = false { 310 fmt.Printf("Host info : ") 311 response, err := resolveQuery(server, cname, dns.TypeHINFO) 312 if err != nil { 313 break 314 } 315 hinfo := make([]string, 0) 316 for _, answer := range response.Answer { 317 if answer.Header().Rrtype != dns.TypeHINFO || answer.Header().Class != dns.ClassINET { 318 continue 319 } 320 hinfo = append(hinfo, fmt.Sprintf("%s %s", answer.(*dns.HINFO).Cpu, answer.(*dns.HINFO).Os)) 321 } 322 if len(hinfo) == 0 { 323 fmt.Println("-") 324 } else { 325 fmt.Println(strings.Join(hinfo, ", ")) 326 } 327 } 328 329 for once := true; once; once = false { 330 fmt.Printf("TXT records : ") 331 response, err := resolveQuery(server, cname, dns.TypeTXT) 332 if err != nil { 333 break 334 } 335 txt := make([]string, 0) 336 for _, answer := range response.Answer { 337 if answer.Header().Rrtype != dns.TypeTXT || answer.Header().Class != dns.ClassINET { 338 continue 339 } 340 txt = append(txt, strings.Join(answer.(*dns.TXT).Txt, " ")) 341 } 342 if len(txt) == 0 { 343 fmt.Println("-") 344 } else { 345 fmt.Println(strings.Join(txt, ", ")) 346 } 347 } 348 349 fmt.Println("") 350} 351