1254939Sdes/*- 2254939Sdes * (c) Magerya Vitaly 3254939Sdes * 4254939Sdes * Copying and distribution of this file, with or without modification, 5254939Sdes * are permitted in any medium without royalty provided the copyright 6254939Sdes * notice and this notice are preserved. This file is offered as-is, 7254939Sdes * without any warranty. 8254939Sdes */ 9254939Sdes 10255403Sdes#include <netinet/in.h> 11255403Sdes 12254939Sdes#include <limits.h> 13254939Sdes#include <netdb.h> 14254939Sdes#include <stdio.h> 15254939Sdes#include <stdlib.h> 16254939Sdes#include <unistd.h> 17254939Sdes 18255403Sdes#include <ldns/ldns.h> 19255403Sdes 20254939Sdes/* General utilities. 21254939Sdes */ 22254939Sdes 23254939Sdesstatic char *progname; 24254939Sdes 25254939Sdes#define countof(array) (sizeof(array)/sizeof(*(array))) 26254939Sdes 27254939Sdesstatic void 28254939Sdesdie(int code, const char *fmt, ...) { 29254939Sdes va_list args; 30254939Sdes 31254939Sdes va_start(args, fmt); 32254939Sdes fprintf(stderr, "%s: ", progname); 33254939Sdes vfprintf(stderr, fmt, args); 34254939Sdes fprintf(stderr, "\n"); 35254939Sdes va_end(args); 36254939Sdes exit(code); 37254939Sdes} 38254939Sdes 39254939Sdesstatic int 40254939Sdesndots(const char *name) { 41254939Sdes int n; 42254939Sdes 43254939Sdes for (n = 0; (name = strchr(name, '.')); n++, name++); 44254939Sdes return n; 45254939Sdes} 46254939Sdes 47254939Sdes/* General LDNS-specific utilities. 48254939Sdes */ 49254939Sdes 50254939Sdesstatic ldns_status 51254939Sdesldns_resolver_new_default(ldns_resolver **res) { 52254939Sdes if (ldns_resolver_new_frm_file(res, NULL) == LDNS_STATUS_OK || 53254939Sdes (*res = ldns_resolver_new()) != NULL) 54254939Sdes return LDNS_STATUS_OK; 55254939Sdes return LDNS_STATUS_MEM_ERR; 56254939Sdes} 57254939Sdes 58254939Sdesstatic ldns_status 59254939Sdesldns_resolver_push_default_servers(ldns_resolver *res) { 60254939Sdes ldns_status status; 61254939Sdes ldns_rdf *addr; 62254939Sdes 63254939Sdes if ((status = ldns_str2rdf_a(&addr, "127.0.0.1")) != LDNS_STATUS_OK || 64254939Sdes (status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK) 65254939Sdes return ldns_rdf_deep_free(addr), status; 66254939Sdes ldns_rdf_deep_free(addr); 67254939Sdes if ((status = ldns_str2rdf_aaaa(&addr, "::1")) != LDNS_STATUS_OK || 68254939Sdes (status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK) 69254939Sdes return ldns_rdf_deep_free(addr), status; 70254939Sdes ldns_rdf_deep_free(addr); 71254939Sdes return LDNS_STATUS_OK; 72254939Sdes} 73254939Sdes 74254939Sdesstatic ldns_rdf * 75254939Sdesldns_rdf_new_addr_frm_str(const char *str) { 76254939Sdes ldns_rdf *addr; 77254939Sdes 78254939Sdes if ((addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, str)) == NULL) 79254939Sdes addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, str); 80254939Sdes return addr; 81254939Sdes} 82254939Sdes 83254939Sdesstatic void 84254939Sdesldns_resolver_remove_nameservers(ldns_resolver *res) { 85254939Sdes while (ldns_resolver_nameserver_count(res) > 0) 86254939Sdes ldns_rdf_deep_free(ldns_resolver_pop_nameserver(res)); 87254939Sdes} 88254939Sdes 89254939Sdesstatic ldns_rdf * 90254939Sdesldns_rdf_reverse_a(ldns_rdf *addr, const char *base) { 91254939Sdes char *buf; 92254939Sdes int i, len; 93254939Sdes 94254939Sdes len = strlen(base); 95254939Sdes buf = alloca(LDNS_IP4ADDRLEN*4 + len + 1); 96254939Sdes for (len = i = 0; i < LDNS_IP4ADDRLEN; i++) 97254939Sdes len += sprintf(&buf[len], "%d.", 98254939Sdes (int)ldns_rdf_data(addr)[LDNS_IP4ADDRLEN - i - 1]); 99254939Sdes sprintf(&buf[len], "%s", base); 100254939Sdes return ldns_dname_new_frm_str(buf); 101254939Sdes} 102254939Sdes 103254939Sdesstatic ldns_rdf * 104254939Sdesldns_rdf_reverse_aaaa(ldns_rdf *addr, const char *base) { 105254939Sdes char *buf; 106254939Sdes int i, len; 107254939Sdes 108254939Sdes len = strlen(base); 109254939Sdes buf = alloca(LDNS_IP6ADDRLEN*4 + len + 1); 110254939Sdes for (i = 0; i < LDNS_IP6ADDRLEN; i++) { 111254939Sdes uint8_t byte = ldns_rdf_data(addr)[LDNS_IP6ADDRLEN - i - 1]; 112254939Sdes sprintf(&buf[i*4], "%x.%x.", byte & 0x0F, byte >> 4); 113254939Sdes } 114254939Sdes sprintf(&buf[LDNS_IP6ADDRLEN*4], "%s", base); 115254939Sdes return ldns_dname_new_frm_str(buf); 116254939Sdes} 117254939Sdes 118254939Sdesstatic ldns_status 119254939Sdesldns_pkt_push_rr_soa(ldns_pkt *pkt, ldns_pkt_section sec, 120254939Sdes const ldns_rdf *name, ldns_rr_class c, uint32_t serial) { 121254939Sdes ldns_rdf *rdf; 122254939Sdes ldns_rr *rr; 123254939Sdes uint32_t n; 124254939Sdes 125254939Sdes if ((rr = ldns_rr_new_frm_type(LDNS_RR_TYPE_SOA)) == NULL) 126254939Sdes return LDNS_STATUS_MEM_ERR; 127254939Sdes ldns_rr_set_class(rr, c); 128254939Sdes ldns_rr_set_owner(rr, ldns_rdf_clone(name)); 129254939Sdes ldns_rr_set_ttl(rr, 0); 130254939Sdes 131254939Sdes n = 0; 132254939Sdes if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, 1, &n)) == NULL) 133254939Sdes goto memerr; 134254939Sdes ldns_rr_set_rdf(rr, rdf, 0); 135254939Sdes ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 1); 136254939Sdes 137254939Sdes n = htonl(serial); 138254939Sdes if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT32, 4, &n)) == NULL) 139254939Sdes goto memerr; 140254939Sdes ldns_rr_set_rdf(rr, rdf, 2); 141254939Sdes 142254939Sdes n = 0; 143254939Sdes if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_PERIOD, 4, &n)) == NULL) 144254939Sdes goto memerr; 145254939Sdes ldns_rr_set_rdf(rr, rdf, 3); 146254939Sdes ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 4); 147254939Sdes ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 5); 148254939Sdes ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 6); 149254939Sdes 150254939Sdes if (ldns_rr_rdf(rr, 1) == NULL || ldns_rr_rdf(rr, 4) == NULL || 151254939Sdes ldns_rr_rdf(rr, 5) == NULL || ldns_rr_rdf(rr, 6) == NULL || 152254939Sdes !ldns_pkt_push_rr(pkt, sec, rr)) 153254939Sdes goto memerr; 154254939Sdes return LDNS_STATUS_OK; 155254939Sdes 156254939Sdesmemerr: 157254939Sdes ldns_rr_free(rr); 158254939Sdes return LDNS_STATUS_MEM_ERR; 159254939Sdes} 160254939Sdes 161254939Sdesstatic ldns_status 162254939Sdesldns_resolver_send_to(ldns_pkt **answer, ldns_resolver *res, 163254939Sdes const ldns_rdf *name, ldns_rr_type t, ldns_rr_class c, 164254939Sdes uint16_t flags, uint32_t ixfr_serial, int nameserver) { 165254939Sdes ldns_status status; 166254939Sdes ldns_pkt *qpkt; 167254939Sdes 168254939Sdes int nscnt = ldns_resolver_nameserver_count(res); 169254939Sdes ldns_rdf **ns = ldns_resolver_nameservers(res); 170254939Sdes size_t *rtt = ldns_resolver_rtt(res); 171254939Sdes 172254939Sdes ldns_resolver_set_nameservers(res, &ns[nameserver]); 173254939Sdes ldns_resolver_set_rtt(res, &rtt[nameserver]); 174254939Sdes ldns_resolver_set_nameserver_count(res, 1); 175254939Sdes 176254939Sdes status = ldns_resolver_prepare_query_pkt(&qpkt, res, name, t, c, flags); 177254939Sdes if (status == LDNS_STATUS_OK && t == LDNS_RR_TYPE_IXFR) 178254939Sdes status = ldns_pkt_push_rr_soa(qpkt, 179254939Sdes LDNS_SECTION_AUTHORITY, name, c, ixfr_serial); 180254939Sdes if (status == LDNS_STATUS_OK) 181254939Sdes status = ldns_resolver_send_pkt(answer, res, qpkt); 182254939Sdes ldns_pkt_free(qpkt); 183254939Sdes 184254939Sdes ldns_resolver_set_nameservers(res, ns); 185254939Sdes ldns_resolver_set_rtt(res, rtt); 186254939Sdes ldns_resolver_set_nameserver_count(res, nscnt); 187254939Sdes return status; 188254939Sdes} 189254939Sdes 190254939Sdesstatic void 191254939Sdesldns_pkt_filter_answer(ldns_pkt *pkt, ldns_rr_type type) { 192254939Sdes int i, j, cnt; 193254939Sdes ldns_rr_list *rrlist; 194254939Sdes ldns_rr *rr; 195254939Sdes ldns_rr_type rrtype; 196254939Sdes 197254939Sdes rrlist = ldns_pkt_answer(pkt); 198254939Sdes cnt = ldns_rr_list_rr_count(rrlist); 199254939Sdes for (i = j = 0; i < cnt; i++) { 200254939Sdes rr = ldns_rr_list_rr(rrlist, i); 201254939Sdes rrtype = ldns_rr_get_type(rr); 202254939Sdes if (type == LDNS_RR_TYPE_ANY || 203254939Sdes type == rrtype || 204255403Sdes (type == LDNS_RR_TYPE_AXFR && 205254939Sdes (rrtype == LDNS_RR_TYPE_A || 206255403Sdes rrtype == LDNS_RR_TYPE_AAAA || 207255403Sdes rrtype == LDNS_RR_TYPE_NS || 208255403Sdes rrtype == LDNS_RR_TYPE_PTR))) 209254939Sdes ldns_rr_list_set_rr(rrlist, rr, j++); 210254939Sdes } 211254939Sdes ldns_rr_list_set_rr_count(rrlist, j); 212254939Sdes} 213254939Sdes 214254939Sdes/* Packet content printing. 215254939Sdes */ 216254939Sdes 217254939Sdesstatic struct { 218254939Sdes ldns_rr_type type; 219254939Sdes const char *text; 220254939Sdes} rr_types[] = { 221254939Sdes {LDNS_RR_TYPE_A, "has address"}, 222254939Sdes {LDNS_RR_TYPE_NS, "name server"}, 223254939Sdes {LDNS_RR_TYPE_CNAME, "is an alias for"}, 224254939Sdes {LDNS_RR_TYPE_WKS, "has well known services"}, 225254939Sdes {LDNS_RR_TYPE_PTR, "domain name pointer"}, 226254939Sdes {LDNS_RR_TYPE_HINFO, "host information"}, 227254939Sdes {LDNS_RR_TYPE_MX, "mail is handled by"}, 228254939Sdes {LDNS_RR_TYPE_TXT, "descriptive text"}, 229254939Sdes {LDNS_RR_TYPE_X25, "x25 address"}, 230254939Sdes {LDNS_RR_TYPE_ISDN, "ISDN address"}, 231254939Sdes {LDNS_RR_TYPE_SIG, "has signature"}, 232254939Sdes {LDNS_RR_TYPE_KEY, "has key"}, 233254939Sdes {LDNS_RR_TYPE_AAAA, "has IPv6 address"}, 234254939Sdes {LDNS_RR_TYPE_LOC, "location"}, 235254939Sdes}; 236254939Sdes 237254939Sdesstatic void 238254939Sdesprint_opcode(ldns_pkt_opcode opcode) { 239254939Sdes ldns_lookup_table *lt = ldns_lookup_by_id(ldns_opcodes, opcode); 240254939Sdes 241254939Sdes if (lt && lt->name) 242254939Sdes printf("%s", lt->name); 243254939Sdes else 244254939Sdes printf("RESERVED%d", opcode); 245254939Sdes} 246254939Sdes 247254939Sdesstatic void 248254939Sdesprint_rcode(uint8_t rcode) { 249254939Sdes ldns_lookup_table *lt = ldns_lookup_by_id(ldns_rcodes, rcode); 250254939Sdes 251254939Sdes if (lt && lt->name) 252254939Sdes printf("%s", lt->name); 253254939Sdes else 254254939Sdes printf("RESERVED%d", rcode); 255254939Sdes} 256254939Sdes 257254939Sdesstatic int 258254939Sdesprint_rr_type(ldns_rr_type type) { 259254939Sdes char *str; 260254939Sdes int n; 261254939Sdes 262254939Sdes str = ldns_rr_type2str(type); 263254939Sdes n = printf("%s", str); 264254939Sdes free(str); 265254939Sdes return n; 266254939Sdes} 267254939Sdes 268254939Sdesstatic int 269254939Sdesprint_rr_class(ldns_rr_class cls) { 270254939Sdes char *str; 271254939Sdes int n; 272254939Sdes 273254939Sdes str = ldns_rr_class2str(cls); 274254939Sdes n = printf("%s", str); 275254939Sdes free(str); 276254939Sdes return n; 277254939Sdes} 278254939Sdes 279254939Sdesstatic int 280254939Sdesprint_rdf(ldns_rdf *rdf) { 281254939Sdes char *str; 282254939Sdes int n; 283254939Sdes 284254939Sdes str = ldns_rdf2str(rdf); 285254939Sdes n = printf("%s", str); 286254939Sdes free(str); 287254939Sdes return n; 288254939Sdes} 289254939Sdes 290254939Sdesstatic int 291254939Sdesprint_rdf_nodot(ldns_rdf *rdf) { 292254939Sdes char *str; 293254939Sdes int len, n; 294254939Sdes 295254939Sdes str = ldns_rdf2str(rdf); 296254939Sdes len = strlen(str); 297254939Sdes n = printf("%.*s", str[len-1] == '.' ? len-1 : len, str); 298254939Sdes free(str); 299254939Sdes return n; 300254939Sdes} 301254939Sdes 302254939Sdesstatic int 303254939Sdesprint_padding(int fromcol, int tocol) { 304254939Sdes int col = fromcol, nextcol = fromcol + 8 - fromcol%8; 305254939Sdes 306254939Sdes if (fromcol + 1 > tocol) tocol = fromcol + 1; 307254939Sdes for (; nextcol <= tocol; col = nextcol, nextcol += 8) 308254939Sdes printf("\t"); 309254939Sdes for (; col < tocol; col++) 310254939Sdes printf(" "); 311254939Sdes return col - fromcol; 312254939Sdes} 313254939Sdes 314254939Sdesstatic void 315254939Sdesprint_rr_verbose(ldns_rr *rr) { 316254939Sdes bool isq = ldns_rr_is_question(rr); 317254939Sdes int rdcnt = ldns_rr_rd_count(rr); 318254939Sdes int i, n; 319254939Sdes 320254939Sdes /* bind9-host does not count the initial ';' here */ 321254939Sdes n = isq ? printf(";") : 0; 322254939Sdes n = print_rdf(ldns_rr_owner(rr)); 323254939Sdes if (!isq) { 324254939Sdes n += print_padding(n, 24); 325254939Sdes n += printf("%d", ldns_rr_ttl(rr)); 326254939Sdes } 327254939Sdes n += print_padding(n, 32); 328254939Sdes n += print_rr_class(ldns_rr_get_class(rr)); 329254939Sdes n += print_padding(n, 40); 330254939Sdes n += print_rr_type(ldns_rr_get_type(rr)); 331254939Sdes for (i = 0; i < rdcnt; i++) { 332254939Sdes if (i == 0) print_padding(n, 48); 333254939Sdes else printf(" "); 334254939Sdes print_rdf(ldns_rr_rdf(rr, i)); 335254939Sdes } 336254939Sdes printf("\n"); 337254939Sdes} 338254939Sdes 339254939Sdesstatic void 340254939Sdesprint_pkt_section_verbose(const char *name, ldns_rr_list *rrlist) { 341254939Sdes int i, cnt = ldns_rr_list_rr_count(rrlist); 342254939Sdes 343254939Sdes if (cnt == 0) 344254939Sdes return; 345254939Sdes printf(";; %s SECTION:\n", name); 346254939Sdes for (i = 0; i < cnt; i++) 347254939Sdes print_rr_verbose(ldns_rr_list_rr(rrlist, i)); 348254939Sdes printf("\n"); 349254939Sdes} 350254939Sdes 351254939Sdesstatic void 352254939Sdesprint_pkt_verbose(ldns_pkt *pkt) { 353254939Sdes int got_flags = 0; 354254939Sdes 355254939Sdes printf(";; ->>HEADER<<- opcode: "); 356254939Sdes print_opcode(ldns_pkt_get_opcode(pkt)); 357254939Sdes printf(", status: "); 358254939Sdes print_rcode(ldns_pkt_get_rcode(pkt)); 359254939Sdes printf(", id: %u\n", ldns_pkt_id(pkt)); 360254939Sdes printf(";; flags:"); 361254939Sdes if (ldns_pkt_qr(pkt)) printf(" qr"), got_flags = 1; 362254939Sdes if (ldns_pkt_aa(pkt)) printf(" aa"), got_flags = 1; 363254939Sdes if (ldns_pkt_tc(pkt)) printf(" tc"), got_flags = 1; 364254939Sdes if (ldns_pkt_rd(pkt)) printf(" rd"), got_flags = 1; 365254939Sdes if (ldns_pkt_ra(pkt)) printf(" ra"), got_flags = 1; 366254939Sdes if (ldns_pkt_ad(pkt)) printf(" ad"), got_flags = 1; 367254939Sdes if (ldns_pkt_cd(pkt)) printf(" cd"), got_flags = 1; 368254939Sdes if (!got_flags) printf(" "); 369254939Sdes printf("; QUERY: %u, ANSWER: %u, AUTHORITY: %u, ADDITIONAL: %u\n", 370254939Sdes ldns_pkt_qdcount(pkt), ldns_pkt_ancount(pkt), 371254939Sdes ldns_pkt_nscount(pkt), ldns_pkt_arcount(pkt)); 372254939Sdes if (ldns_pkt_edns(pkt)) 373254939Sdes printf(";; EDNS: version: %u, udp=%u\n", 374254939Sdes ldns_pkt_edns_version(pkt), ldns_pkt_edns_udp_size(pkt)); 375254939Sdes printf("\n"); 376254939Sdes print_pkt_section_verbose("QUESTION", ldns_pkt_question(pkt)); 377254939Sdes print_pkt_section_verbose("ANSWER", ldns_pkt_answer(pkt)); 378254939Sdes print_pkt_section_verbose("AUTHORITY", ldns_pkt_authority(pkt)); 379254939Sdes print_pkt_section_verbose("ADDITIONAL", ldns_pkt_additional(pkt)); 380254939Sdes} 381254939Sdes 382254939Sdesstatic void 383254939Sdesprint_rr_short(ldns_rr *rr) { 384254939Sdes ldns_rr_type type = ldns_rr_get_type(rr); 385254939Sdes size_t i, rdcnt = ldns_rr_rd_count(rr); 386254939Sdes 387254939Sdes print_rdf_nodot(ldns_rr_owner(rr)); 388254939Sdes printf(" "); 389254939Sdes for (i = 0; i < countof(rr_types); i++) { 390254939Sdes if (rr_types[i].type == type) { 391254939Sdes printf("%s", rr_types[i].text); 392254939Sdes goto found; 393254939Sdes } 394254939Sdes } 395254939Sdes 396254939Sdes printf("has "); 397254939Sdes print_rr_type(type); 398254939Sdes printf(" record"); 399254939Sdes 400254939Sdesfound: 401254939Sdes for (i = 0; i < rdcnt; i++) { 402254939Sdes printf(" "); 403254939Sdes print_rdf(ldns_rr_rdf(rr, i)); 404254939Sdes } 405254939Sdes printf("\n"); 406254939Sdes} 407254939Sdes 408254939Sdesstatic void 409254939Sdesprint_pkt_short(ldns_pkt *pkt, bool print_rr_server) { 410254939Sdes ldns_rr_list *rrlist = ldns_pkt_answer(pkt); 411254939Sdes size_t i; 412254939Sdes 413254939Sdes for (i = 0; i < ldns_rr_list_rr_count(rrlist); i++) { 414254939Sdes if (print_rr_server) { 415254939Sdes printf("Nameserver "); 416254939Sdes print_rdf(ldns_pkt_answerfrom(pkt)); 417254939Sdes printf(":\n\t"); 418254939Sdes } 419254939Sdes print_rr_short(ldns_rr_list_rr(rrlist, i)); 420254939Sdes } 421254939Sdes} 422254939Sdes 423254939Sdesstatic void 424254939Sdesprint_received_line(ldns_resolver *res, ldns_pkt *pkt) { 425254939Sdes char *from = ldns_rdf2str(ldns_pkt_answerfrom(pkt)); 426254939Sdes 427254939Sdes printf("Received %zu bytes from %s#%d in %d ms\n", 428254939Sdes ldns_pkt_size(pkt), from, ldns_resolver_port(res), 429254939Sdes ldns_pkt_querytime(pkt)); 430254939Sdes free(from); 431254939Sdes} 432254939Sdes 433254939Sdes/* Main program. 434254939Sdes * 435254939Sdes * Note that no memory is freed below this line by intention. 436254939Sdes */ 437254939Sdes 438254939Sdes#define DEFAULT_TCP_TIMEOUT 10 439254939Sdes#define DEFAULT_UDP_TIMEOUT 5 440254939Sdes 441254939Sdesenum operation_mode { M_AXFR, M_DEFAULT_Q, M_SINGLE_Q, M_SOA }; 442254939Sdes 443254939Sdesstatic enum operation_mode o_mode = M_DEFAULT_Q; 444254939Sdesstatic bool o_ignore_servfail = true; 445254939Sdesstatic bool o_ip6_int = false; 446254939Sdesstatic bool o_print_pkt_server = false; 447254939Sdesstatic bool o_print_rr_server = false; 448254939Sdesstatic bool o_recursive = true; 449254939Sdesstatic bool o_tcp = false; 450254939Sdesstatic bool o_verbose = false; 451254939Sdesstatic char *o_name = NULL; 452254939Sdesstatic char *o_server = NULL; 453254939Sdesstatic int o_ipversion = LDNS_RESOLV_INETANY; 454254939Sdesstatic int o_ndots = 1; 455254939Sdesstatic int o_retries = 1; 456254939Sdesstatic ldns_rr_class o_rrclass = LDNS_RR_CLASS_IN; 457254939Sdesstatic ldns_rr_type o_rrtype = LDNS_RR_TYPE_A; 458254939Sdesstatic time_t o_timeout = 0; 459254939Sdesstatic uint32_t o_ixfr_serial = 0; 460254939Sdes 461254939Sdesstatic void 462254939Sdesusage(void) { 463254939Sdes fputs( 464255403Sdes "Usage: host [-aCdilrsTvw46] [-c class] [-N ndots] [-R number]\n" 465255403Sdes " [-t type] [-W wait] name [server]\n" 466254939Sdes "\t-a same as -v -t ANY\n" 467254939Sdes "\t-C query SOA records from all authoritative name servers\n" 468254939Sdes "\t-c use this query class (IN, CH, HS, etc)\n" 469254939Sdes "\t-d produce verbose output, same as -v\n" 470254939Sdes "\t-i use IP6.INT for IPv6 reverse lookups\n" 471254939Sdes "\t-l list records in a zone via AXFR\n" 472254939Sdes "\t-N consider names with at least this many dots as absolute\n" 473254939Sdes "\t-R retry UDP queries this many times\n" 474254939Sdes "\t-r disable recursive query\n" 475254939Sdes "\t-s do not ignore SERVFAIL responses\n" 476254939Sdes "\t-T send query via TCP\n" 477254939Sdes "\t-t use this query type (A, AAAA, MX, etc)\n" 478254939Sdes "\t-v produce verbose output\n" 479254939Sdes "\t-w wait forever for a server reply\n" 480254939Sdes "\t-W wait this many seconds for a reply\n" 481254939Sdes "\t-4 use IPv4 only\n" 482254939Sdes "\t-6 use IPv6 only\n", 483254939Sdes stderr); 484254939Sdes exit(1); 485254939Sdes} 486254939Sdes 487254939Sdesstatic void 488254939Sdesparse_args(int argc, char *argv[]) { 489254939Sdes int ch; 490254939Sdes 491254939Sdes progname = argv[0]; 492254939Sdes while ((ch = getopt(argc, argv, "aCdilrsTvw46c:N:R:t:W:")) != -1) { 493254939Sdes switch (ch) { 494254939Sdes case 'a': 495254939Sdes if (o_mode != M_AXFR) 496254939Sdes o_mode = M_SINGLE_Q; 497254939Sdes o_rrtype = LDNS_RR_TYPE_ANY; 498254939Sdes o_verbose = true; 499254939Sdes break; 500254939Sdes case 'C': 501254939Sdes o_mode = M_SOA; 502254939Sdes o_print_rr_server = true; 503254939Sdes o_rrclass = LDNS_RR_CLASS_IN; 504254939Sdes o_rrtype = LDNS_RR_TYPE_NS; 505254939Sdes break; 506254939Sdes case 'c': 507254939Sdes /* bind9-host sets o_mode to M_SINGLE_Q here */ 508254939Sdes o_rrclass = ldns_get_rr_class_by_name(optarg); 509254939Sdes if (o_rrclass <= 0) 510254939Sdes die(2, "invalid class: %s\n", optarg); 511254939Sdes break; 512254939Sdes case 'd': o_verbose = true; break; 513254939Sdes case 'i': o_ip6_int = true; break; 514254939Sdes case 'l': 515254939Sdes o_mode = M_AXFR; 516254939Sdes o_rrtype = LDNS_RR_TYPE_AXFR; 517254939Sdes o_tcp = true; 518254939Sdes break; 519254939Sdes case 'N': 520254939Sdes o_ndots = atoi(optarg); 521254939Sdes if (o_ndots < 0) o_ndots = 0; 522254939Sdes break; 523254939Sdes case 'n': 524254939Sdes /* bind9-host accepts and ignores this option */ 525254939Sdes break; 526254939Sdes case 'r': o_recursive = 0; break; 527254939Sdes case 'R': 528254939Sdes o_retries = atoi(optarg); 529254939Sdes if (o_retries <= 0) o_retries = 1; 530254939Sdes if (o_retries > 255) o_retries = 255; 531254939Sdes break; 532254939Sdes case 's': o_ignore_servfail = false; break; 533254939Sdes case 'T': o_tcp = true; break; 534254939Sdes case 't': 535254939Sdes if (o_mode != M_AXFR) 536254939Sdes o_mode = M_SINGLE_Q; 537254939Sdes if (strncasecmp(optarg, "ixfr=", 5) == 0) { 538254939Sdes o_rrtype = LDNS_RR_TYPE_IXFR; 539254939Sdes o_ixfr_serial = atol(optarg + 5); 540254939Sdes } else { 541254939Sdes o_rrtype = ldns_get_rr_type_by_name(optarg); 542254939Sdes if (o_rrtype <= 0) 543254939Sdes die(2, "invalid type: %s\n", optarg); 544254939Sdes } 545254939Sdes if (o_rrtype == LDNS_RR_TYPE_AXFR || o_rrtype == LDNS_RR_TYPE_IXFR) 546254939Sdes o_tcp = true; 547254939Sdes if (o_rrtype == LDNS_RR_TYPE_AXFR) { 548254939Sdes o_mode = M_AXFR; 549254939Sdes o_rrtype = LDNS_RR_TYPE_ANY; 550254939Sdes o_verbose = true; 551254939Sdes } 552254939Sdes break; 553254939Sdes case 'v': o_verbose = true; break; 554254939Sdes case 'w': 555254939Sdes o_timeout = (time_t)INT_MAX; 556254939Sdes break; 557254939Sdes case 'W': 558254939Sdes o_timeout = atol(optarg); 559254939Sdes if (o_timeout <= 0) o_timeout = 1; 560254939Sdes break; 561254939Sdes case '4': o_ipversion = LDNS_RESOLV_INET; break; 562254939Sdes case '6': o_ipversion = LDNS_RESOLV_INET6; break; 563254939Sdes default: 564254939Sdes usage(); 565254939Sdes } 566254939Sdes } 567254939Sdes argc -= optind; 568254939Sdes argv += optind; 569254939Sdes /* bind9-host ignores arguments after the 2-nd one */ 570254939Sdes if (argc < 1) 571254939Sdes usage(); 572254939Sdes o_name = argv[0]; 573254939Sdes if (argc > 1) { 574254939Sdes o_server = argv[1]; 575254939Sdes o_print_pkt_server = true; 576254939Sdes } 577254939Sdes} 578254939Sdes 579254939Sdesstatic ldns_rdf* 580254939Sdessafe_str2rdf_dname(const char *name) { 581254939Sdes ldns_rdf *dname; 582254939Sdes ldns_status status; 583254939Sdes 584254939Sdes if ((status = ldns_str2rdf_dname(&dname, name)) != LDNS_STATUS_OK) { 585254939Sdes die(1, "'%s' is not a legal name (%s)", 586254939Sdes name, ldns_get_errorstr_by_id(status)); 587254939Sdes } 588254939Sdes return dname; 589254939Sdes} 590254939Sdes 591254939Sdesstatic ldns_rdf* 592254939Sdessafe_dname_cat_clone(const ldns_rdf *rd1, const ldns_rdf *rd2) { 593254939Sdes ldns_rdf *result = ldns_dname_cat_clone(rd1, rd2); 594254939Sdes 595254939Sdes if (!result) 596254939Sdes die(1, "not enought memory for a domain name"); 597254939Sdes /* Why doesn't ldns_dname_cat_clone check this condition? */ 598254939Sdes if (ldns_rdf_size(result) > LDNS_MAX_DOMAINLEN) 599254939Sdes die(1, "'%s' is not a legal name (%s)\n", ldns_rdf2str(result), 600254939Sdes ldns_get_errorstr_by_id(LDNS_STATUS_DOMAINNAME_OVERFLOW)); 601254939Sdes return result; 602254939Sdes} 603254939Sdes 604254939Sdesstatic bool 605254939Sdesquery(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt) { 606254939Sdes ldns_status status; 607254939Sdes ldns_pkt_rcode rcode; 608254939Sdes int i, cnt; 609254939Sdes 610254939Sdes if (o_verbose) { 611254939Sdes printf("Trying \""); 612254939Sdes print_rdf_nodot(domain); 613254939Sdes printf("\"\n"); 614254939Sdes } 615254939Sdes for (cnt = ldns_resolver_nameserver_count(res), i = 0; i < cnt; i++) { 616254939Sdes status = ldns_resolver_send_to(pkt, res, domain, o_rrtype, 617254939Sdes o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i); 618254939Sdes if (status != LDNS_STATUS_OK) { 619254939Sdes *pkt = NULL; 620254939Sdes continue; 621254939Sdes } 622254939Sdes if (ldns_pkt_tc(*pkt) && !ldns_resolver_usevc(res)) { 623254939Sdes if (o_verbose) 624254939Sdes printf(";; Truncated, retrying in TCP mode.\n"); 625254939Sdes ldns_resolver_set_usevc(res, true); 626254939Sdes status = ldns_resolver_send_to(pkt, res, domain, o_rrtype, 627254939Sdes o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i); 628254939Sdes ldns_resolver_set_usevc(res, false); 629254939Sdes if (status != LDNS_STATUS_OK) 630254939Sdes continue; 631254939Sdes } 632254939Sdes rcode = ldns_pkt_get_rcode(*pkt); 633254939Sdes if (o_ignore_servfail && rcode == LDNS_RCODE_SERVFAIL && cnt > 1) 634254939Sdes continue; 635254939Sdes return rcode == LDNS_RCODE_NOERROR; 636254939Sdes } 637254939Sdes if (*pkt == NULL) { 638254939Sdes printf(";; connection timed out; no servers could be reached\n"); 639254939Sdes exit(1); 640254939Sdes } 641254939Sdes return false; 642254939Sdes} 643254939Sdes 644254939Sdesstatic ldns_rdf * 645254939Sdessearch(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt, bool absolute) { 646254939Sdes ldns_rdf *dname, **searchlist; 647254939Sdes int i, n; 648254939Sdes 649254939Sdes if (absolute && query(res, domain, pkt)) 650254939Sdes return domain; 651254939Sdes 652254939Sdes if ((dname = ldns_resolver_domain(res)) != NULL) { 653254939Sdes dname = safe_dname_cat_clone(domain, dname); 654254939Sdes if (query(res, dname, pkt)) 655254939Sdes return dname; 656254939Sdes } 657254939Sdes 658254939Sdes searchlist = ldns_resolver_searchlist(res); 659254939Sdes n = ldns_resolver_searchlist_count(res); 660254939Sdes for (i = 0; i < n; i++) { 661254939Sdes dname = safe_dname_cat_clone(domain, searchlist[i]); 662254939Sdes if (query(res, dname, pkt)) 663254939Sdes return dname; 664254939Sdes } 665254939Sdes 666254939Sdes if (!absolute && query(res, domain, pkt)) 667254939Sdes return domain; 668254939Sdes 669254939Sdes return NULL; 670254939Sdes} 671254939Sdes 672254939Sdesstatic void 673254939Sdesreport(ldns_resolver *res, ldns_rdf *domain, ldns_pkt *pkt) { 674254939Sdes ldns_pkt_rcode rcode; 675254939Sdes 676254939Sdes if (o_print_pkt_server) { 677254939Sdes printf("Using domain server:\nName: %s\nAddress: ", o_server); 678254939Sdes print_rdf(ldns_pkt_answerfrom(pkt)); 679254939Sdes printf("#%d\nAliases: \n\n", ldns_resolver_port(res)); 680254939Sdes o_print_pkt_server = false; 681254939Sdes } 682254939Sdes rcode = ldns_pkt_get_rcode(pkt); 683254939Sdes if (rcode != LDNS_RCODE_NOERROR) { 684254939Sdes printf("Host "); 685254939Sdes print_rdf_nodot(domain); 686254939Sdes printf(" not found: %d(", rcode); 687254939Sdes print_rcode(rcode); 688254939Sdes printf(")\n"); 689254939Sdes } else { 690254939Sdes if (o_verbose) { 691254939Sdes print_pkt_verbose(pkt); 692254939Sdes } else { 693254939Sdes print_pkt_short(pkt, o_print_rr_server); 694254939Sdes if (o_mode != M_DEFAULT_Q && 695254939Sdes ldns_rr_list_rr_count(ldns_pkt_answer(pkt)) == 0) { 696254939Sdes print_rdf_nodot(domain); 697254939Sdes printf(" has no "); 698254939Sdes print_rr_type(o_rrtype); 699254939Sdes printf(" record\n"); 700254939Sdes } 701254939Sdes } 702254939Sdes } 703254939Sdes if (o_verbose) 704254939Sdes print_received_line(res, pkt); 705254939Sdes} 706254939Sdes 707254939Sdesstatic bool 708254939Sdesdoquery(ldns_resolver *res, ldns_rdf *domain) { 709254939Sdes ldns_pkt *pkt; 710254939Sdes bool q; 711254939Sdes 712254939Sdes q = query(res, domain, &pkt); 713254939Sdes report(res, domain, pkt); 714254939Sdes return q; 715254939Sdes} 716254939Sdes 717254939Sdesstatic bool 718254939Sdesdoquery_filtered(ldns_resolver *res, ldns_rdf *domain) { 719254939Sdes ldns_pkt *pkt; 720254939Sdes bool q; 721254939Sdes 722254939Sdes q = query(res, domain, &pkt); 723254939Sdes ldns_pkt_filter_answer(pkt, o_rrtype); 724254939Sdes report(res, domain, pkt); 725254939Sdes return q; 726254939Sdes} 727254939Sdes 728254939Sdesstatic bool 729254939Sdesdosearch(ldns_resolver *res, ldns_rdf *domain, bool absolute) { 730254939Sdes ldns_pkt *pkt; 731254939Sdes ldns_rdf *dname; 732254939Sdes 733254939Sdes dname = search(res, domain, &pkt, absolute); 734254939Sdes report(res, dname != NULL ? dname : domain, pkt); 735254939Sdes return o_mode != M_DEFAULT_Q ? (dname != NULL) : 736254939Sdes (dname != NULL) && 737254939Sdes (o_rrtype = LDNS_RR_TYPE_AAAA, doquery_filtered(res, dname)) && 738254939Sdes (o_rrtype = LDNS_RR_TYPE_MX, doquery_filtered(res, dname)); 739254939Sdes} 740254939Sdes 741254939Sdesstatic bool 742254939Sdesdoaxfr(ldns_resolver *res, ldns_rdf *domain, bool absolute) { 743254939Sdes ldns_pkt *pkt; 744254939Sdes ldns_rdf *dname; 745254939Sdes ldns_rr_type rrtype; 746254939Sdes 747254939Sdes rrtype = o_rrtype; 748254939Sdes o_rrtype = LDNS_RR_TYPE_AXFR; 749254939Sdes dname = search(res, domain, &pkt, absolute); 750254939Sdes ldns_pkt_filter_answer(pkt, rrtype); 751254939Sdes report(res, dname != NULL ? dname : domain, pkt); 752254939Sdes return dname != NULL; 753254939Sdes} 754254939Sdes 755254939Sdesstatic bool 756254939Sdesdosoa(ldns_resolver *res, ldns_rdf *domain, bool absolute) { 757254939Sdes ldns_rr_list *answer, **nsaddrs; 758254939Sdes ldns_rdf *dname, *addr; 759254939Sdes ldns_pkt *pkt; 760254939Sdes ldns_rr *rr; 761254939Sdes size_t i, j, n, cnt; 762254939Sdes 763254939Sdes if ((dname = search(res, domain, &pkt, absolute)) == NULL) 764254939Sdes return false; 765254939Sdes 766254939Sdes answer = ldns_pkt_answer(pkt); 767254939Sdes cnt = ldns_rr_list_rr_count(answer); 768254939Sdes nsaddrs = alloca(cnt*sizeof(*nsaddrs)); 769254939Sdes for (n = 0, i = 0; i < cnt; i++) 770254939Sdes if ((addr = ldns_rr_ns_nsdname(ldns_rr_list_rr(answer, i))) != NULL) 771254939Sdes nsaddrs[n++] = ldns_get_rr_list_addr_by_name(res, 772254939Sdes addr, LDNS_RR_CLASS_IN, 0); 773254939Sdes 774254939Sdes o_print_pkt_server = false; 775254939Sdes o_recursive = false; 776254939Sdes o_rrtype = LDNS_RR_TYPE_SOA; 777254939Sdes for (i = 0; i < n; i++) { 778254939Sdes cnt = ldns_rr_list_rr_count(nsaddrs[i]); 779254939Sdes for (j = 0; j < cnt; j++) { 780254939Sdes ldns_resolver_remove_nameservers(res); 781254939Sdes rr = ldns_rr_list_rr(nsaddrs[i], j); 782255403Sdes if ((ldns_resolver_ip6(res) == LDNS_RESOLV_INET && 783255403Sdes ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA) || 784255403Sdes (ldns_resolver_ip6(res) == LDNS_RESOLV_INET6 && 785255403Sdes ldns_rr_get_type(rr) == LDNS_RR_TYPE_A)) 786254939Sdes continue; 787254939Sdes if (ldns_resolver_push_nameserver_rr(res, rr) == LDNS_STATUS_OK) 788254939Sdes /* bind9-host queries for domain, not dname here */ 789254939Sdes doquery(res, dname); 790254939Sdes } 791254939Sdes } 792254939Sdes return 0; 793254939Sdes} 794254939Sdes 795254939Sdesstatic void 796254939Sdesresolver_set_nameserver_hostname(ldns_resolver *res, const char *server) { 797254939Sdes struct addrinfo hints, *ailist, *ai; 798254939Sdes ldns_status status; 799254939Sdes ldns_rdf *rdf; 800254939Sdes int err; 801254939Sdes 802254939Sdes memset(&hints, 0, sizeof hints); 803254939Sdes switch (ldns_resolver_ip6(res)) { 804254939Sdes case LDNS_RESOLV_INET: hints.ai_family = PF_INET; break; 805254939Sdes case LDNS_RESOLV_INET6: hints.ai_family = PF_INET6; break; 806254939Sdes default: hints.ai_family = PF_UNSPEC; break; 807254939Sdes } 808254939Sdes hints.ai_socktype = SOCK_STREAM; 809254939Sdes do err = getaddrinfo(server, NULL, &hints, &ailist); 810254939Sdes while (err == EAI_AGAIN); 811254939Sdes if (err != 0) 812254939Sdes die(1, "couldn't get address for '%s': %s", server, gai_strerror(err)); 813254939Sdes for (ai = ailist; ai != NULL; ai = ai->ai_next) { 814254939Sdes if ((rdf = ldns_sockaddr_storage2rdf((void*)ai->ai_addr, NULL)) == NULL) 815254939Sdes die(1, "couldn't allocate an rdf: %s", 816254939Sdes ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); 817254939Sdes status = ldns_resolver_push_nameserver(res, rdf); 818254939Sdes if (status != LDNS_STATUS_OK) 819254939Sdes die(1, "couldn't push a nameserver address: %s", 820254939Sdes ldns_get_errorstr_by_id(status)); 821254939Sdes } 822254939Sdes} 823254939Sdes 824254939Sdesstatic void 825254939Sdesresolver_set_nameserver_str(ldns_resolver *res, const char *server) { 826254939Sdes ldns_rdf *addr; 827254939Sdes 828254939Sdes ldns_resolver_remove_nameservers(res); 829254939Sdes addr = ldns_rdf_new_addr_frm_str(server); 830254939Sdes if (addr) { 831254939Sdes if (ldns_resolver_push_nameserver(res, addr) != LDNS_STATUS_OK) 832254939Sdes die(1, "couldn't push a nameserver address"); 833254939Sdes } else 834254939Sdes resolver_set_nameserver_hostname(res, server); 835254939Sdes} 836254939Sdes 837254939Sdesint 838254939Sdesmain(int argc, char *argv[]) { 839254939Sdes ldns_rdf *addr, *dname; 840254939Sdes ldns_resolver *res; 841254939Sdes ldns_status status; 842254939Sdes struct timeval restimeout; 843254939Sdes 844254939Sdes parse_args(argc, argv); 845254939Sdes 846254939Sdes status = ldns_resolver_new_default(&res); 847254939Sdes if (status != LDNS_STATUS_OK) 848254939Sdes die(1, "error creating resolver: %s", ldns_get_errorstr_by_id(status)); 849254939Sdes if (ldns_resolver_nameserver_count(res) == 0) 850254939Sdes ldns_resolver_push_default_servers(res); 851254939Sdes 852254939Sdes ldns_resolver_set_usevc(res, o_tcp); 853254939Sdes restimeout.tv_sec = o_timeout > 0 ? o_timeout : 854254939Sdes o_tcp ? DEFAULT_TCP_TIMEOUT : DEFAULT_UDP_TIMEOUT; 855254939Sdes restimeout.tv_usec = 0; 856254939Sdes ldns_resolver_set_timeout(res, restimeout); 857254939Sdes ldns_resolver_set_retry(res, o_retries+1); 858254939Sdes ldns_resolver_set_ip6(res, o_ipversion); 859254939Sdes ldns_resolver_set_defnames(res, false); 860254939Sdes ldns_resolver_set_fallback(res, false); 861254939Sdes 862254939Sdes if (o_server) 863254939Sdes resolver_set_nameserver_str(res, o_server); 864254939Sdes 865254939Sdes if (ldns_str2rdf_a(&addr, o_name) == LDNS_STATUS_OK) { 866254939Sdes dname = ldns_rdf_reverse_a(addr, "in-addr.arpa"); 867254939Sdes if (dname == NULL) 868254939Sdes die(1, "can't reverse '%s': %s", o_name, 869254939Sdes ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); 870254939Sdes o_mode = M_SINGLE_Q; 871254939Sdes o_rrtype = LDNS_RR_TYPE_PTR; 872254939Sdes return !doquery(res, dname); 873254939Sdes } else if (ldns_str2rdf_aaaa(&addr, o_name) == LDNS_STATUS_OK) { 874254939Sdes dname = ldns_rdf_reverse_aaaa(addr, o_ip6_int ? "ip6.int" : "ip6.arpa"); 875254939Sdes if (dname == NULL) 876254939Sdes die(1, "can't reverse '%s': %s", o_name, 877254939Sdes ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); 878254939Sdes o_mode = M_SINGLE_Q; 879254939Sdes o_rrtype = LDNS_RR_TYPE_PTR; 880254939Sdes return !doquery(res, dname); 881254939Sdes } 882254939Sdes return !(o_mode == M_SOA ? dosoa : o_mode == M_AXFR ? doaxfr : dosearch) 883254939Sdes (res, safe_str2rdf_dname(o_name), ndots(o_name) >= o_ndots); 884254939Sdes} 885