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