ldns-host.c revision 254939
1170613Sbms/*- 2189592Sbms * (c) Magerya Vitaly 3170613Sbms * 4170613Sbms * Copying and distribution of this file, with or without modification, 5170613Sbms * are permitted in any medium without royalty provided the copyright 6170613Sbms * notice and this notice are preserved. This file is offered as-is, 7170613Sbms * without any warranty. 8170613Sbms */ 9170613Sbms 10170613Sbms#include <ldns/ldns.h> 11170613Sbms#include <limits.h> 12170613Sbms#include <netdb.h> 13170613Sbms#include <stdio.h> 14170613Sbms#include <stdlib.h> 15170613Sbms#include <unistd.h> 16170613Sbms 17170613Sbms/* General utilities. 18170613Sbms */ 19170613Sbms 20170613Sbmsstatic char *progname; 21170613Sbms 22170613Sbms#define countof(array) (sizeof(array)/sizeof(*(array))) 23170613Sbms 24170613Sbmsstatic void 25170613Sbmsdie(int code, const char *fmt, ...) { 26170613Sbms va_list args; 27170613Sbms 28170613Sbms va_start(args, fmt); 29170613Sbms fprintf(stderr, "%s: ", progname); 30170613Sbms vfprintf(stderr, fmt, args); 31170613Sbms fprintf(stderr, "\n"); 32170613Sbms va_end(args); 33170613Sbms exit(code); 34170613Sbms} 35170613Sbms 36170613Sbmsstatic int 37170613Sbmsndots(const char *name) { 38170613Sbms int n; 39170613Sbms 40170613Sbms for (n = 0; (name = strchr(name, '.')); n++, name++); 41170613Sbms return n; 42170613Sbms} 43171746Scsjp 44170613Sbms/* General LDNS-specific utilities. 45170613Sbms */ 46189592Sbms 47170613Sbmsstatic ldns_status 48189592Sbmsldns_resolver_new_default(ldns_resolver **res) { 49228969Sjhb if (ldns_resolver_new_frm_file(res, NULL) == LDNS_STATUS_OK || 50189592Sbms (*res = ldns_resolver_new()) != NULL) 51170613Sbms return LDNS_STATUS_OK; 52170613Sbms return LDNS_STATUS_MEM_ERR; 53170613Sbms} 54170613Sbms 55185571Sbzstatic ldns_status 56170613Sbmsldns_resolver_push_default_servers(ldns_resolver *res) { 57170613Sbms ldns_status status; 58170613Sbms ldns_rdf *addr; 59170613Sbms 60170613Sbms if ((status = ldns_str2rdf_a(&addr, "127.0.0.1")) != LDNS_STATUS_OK || 61170613Sbms (status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK) 62170613Sbms return ldns_rdf_deep_free(addr), status; 63170613Sbms ldns_rdf_deep_free(addr); 64189592Sbms if ((status = ldns_str2rdf_aaaa(&addr, "::1")) != LDNS_STATUS_OK || 65191659Sbms (status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK) 66189592Sbms return ldns_rdf_deep_free(addr), status; 67189592Sbms ldns_rdf_deep_free(addr); 68170613Sbms return LDNS_STATUS_OK; 69170613Sbms} 70170613Sbms 71170613Sbmsstatic ldns_rdf * 72170613Sbmsldns_rdf_new_addr_frm_str(const char *str) { 73170613Sbms ldns_rdf *addr; 74170613Sbms 75170613Sbms if ((addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, str)) == NULL) 76170613Sbms addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, str); 77170613Sbms return addr; 78170613Sbms} 79189592Sbms 80189592Sbmsstatic void 81170613Sbmsldns_resolver_remove_nameservers(ldns_resolver *res) { 82170613Sbms while (ldns_resolver_nameserver_count(res) > 0) 83189592Sbms ldns_rdf_deep_free(ldns_resolver_pop_nameserver(res)); 84189592Sbms} 85170613Sbms 86170613Sbmsstatic ldns_rdf * 87189592Sbmsldns_rdf_reverse_a(ldns_rdf *addr, const char *base) { 88189592Sbms char *buf; 89189592Sbms int i, len; 90189592Sbms 91189592Sbms len = strlen(base); 92189592Sbms buf = alloca(LDNS_IP4ADDRLEN*4 + len + 1); 93189592Sbms for (len = i = 0; i < LDNS_IP4ADDRLEN; i++) 94189592Sbms len += sprintf(&buf[len], "%d.", 95189592Sbms (int)ldns_rdf_data(addr)[LDNS_IP4ADDRLEN - i - 1]); 96170613Sbms sprintf(&buf[len], "%s", base); 97170613Sbms return ldns_dname_new_frm_str(buf); 98189592Sbms} 99170613Sbms 100170613Sbmsstatic ldns_rdf * 101170613Sbmsldns_rdf_reverse_aaaa(ldns_rdf *addr, const char *base) { 102170613Sbms char *buf; 103189592Sbms int i, len; 104170613Sbms 105170613Sbms len = strlen(base); 106189592Sbms buf = alloca(LDNS_IP6ADDRLEN*4 + len + 1); 107189592Sbms for (i = 0; i < LDNS_IP6ADDRLEN; i++) { 108189592Sbms uint8_t byte = ldns_rdf_data(addr)[LDNS_IP6ADDRLEN - i - 1]; 109189592Sbms sprintf(&buf[i*4], "%x.%x.", byte & 0x0F, byte >> 4); 110170613Sbms } 111170613Sbms sprintf(&buf[LDNS_IP6ADDRLEN*4], "%s", base); 112170613Sbms return ldns_dname_new_frm_str(buf); 113170613Sbms} 114189592Sbms 115189592Sbmsstatic ldns_status 116189592Sbmsldns_pkt_push_rr_soa(ldns_pkt *pkt, ldns_pkt_section sec, 117170613Sbms const ldns_rdf *name, ldns_rr_class c, uint32_t serial) { 118189592Sbms ldns_rdf *rdf; 119189592Sbms ldns_rr *rr; 120189592Sbms uint32_t n; 121189592Sbms 122189592Sbms if ((rr = ldns_rr_new_frm_type(LDNS_RR_TYPE_SOA)) == NULL) 123189592Sbms return LDNS_STATUS_MEM_ERR; 124189592Sbms ldns_rr_set_class(rr, c); 125189592Sbms ldns_rr_set_owner(rr, ldns_rdf_clone(name)); 126189592Sbms ldns_rr_set_ttl(rr, 0); 127189592Sbms 128189592Sbms n = 0; 129189592Sbms if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, 1, &n)) == NULL) 130170613Sbms goto memerr; 131189592Sbms ldns_rr_set_rdf(rr, rdf, 0); 132189592Sbms ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 1); 133189592Sbms 134189592Sbms n = htonl(serial); 135189592Sbms if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT32, 4, &n)) == NULL) 136189592Sbms goto memerr; 137189592Sbms ldns_rr_set_rdf(rr, rdf, 2); 138189592Sbms 139189592Sbms n = 0; 140189592Sbms if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_PERIOD, 4, &n)) == NULL) 141189592Sbms goto memerr; 142189592Sbms ldns_rr_set_rdf(rr, rdf, 3); 143189592Sbms ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 4); 144189592Sbms ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 5); 145189592Sbms ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 6); 146170613Sbms 147170613Sbms if (ldns_rr_rdf(rr, 1) == NULL || ldns_rr_rdf(rr, 4) == NULL || 148228969Sjhb ldns_rr_rdf(rr, 5) == NULL || ldns_rr_rdf(rr, 6) == NULL || 149228969Sjhb !ldns_pkt_push_rr(pkt, sec, rr)) 150170613Sbms goto memerr; 151170613Sbms return LDNS_STATUS_OK; 152170613Sbms 153189592Sbmsmemerr: 154189592Sbms ldns_rr_free(rr); 155189592Sbms return LDNS_STATUS_MEM_ERR; 156189592Sbms} 157170613Sbms 158170613Sbmsstatic ldns_status 159189592Sbmsldns_resolver_send_to(ldns_pkt **answer, ldns_resolver *res, 160170613Sbms const ldns_rdf *name, ldns_rr_type t, ldns_rr_class c, 161227309Sed uint16_t flags, uint32_t ixfr_serial, int nameserver) { 162227309Sed ldns_status status; 163189357Sbms ldns_pkt *qpkt; 164189592Sbms 165189592Sbms int nscnt = ldns_resolver_nameserver_count(res); 166189592Sbms ldns_rdf **ns = ldns_resolver_nameservers(res); 167189592Sbms size_t *rtt = ldns_resolver_rtt(res); 168189592Sbms 169189592Sbms ldns_resolver_set_nameservers(res, &ns[nameserver]); 170189592Sbms ldns_resolver_set_rtt(res, &rtt[nameserver]); 171189592Sbms ldns_resolver_set_nameserver_count(res, 1); 172189592Sbms 173189592Sbms status = ldns_resolver_prepare_query_pkt(&qpkt, res, name, t, c, flags); 174189592Sbms if (status == LDNS_STATUS_OK && t == LDNS_RR_TYPE_IXFR) 175189592Sbms status = ldns_pkt_push_rr_soa(qpkt, 176189357Sbms LDNS_SECTION_AUTHORITY, name, c, ixfr_serial); 177189357Sbms if (status == LDNS_STATUS_OK) 178189357Sbms status = ldns_resolver_send_pkt(answer, res, qpkt); 179189357Sbms ldns_pkt_free(qpkt); 180189357Sbms 181227309Sed ldns_resolver_set_nameservers(res, ns); 182189592Sbms ldns_resolver_set_rtt(res, rtt); 183189592Sbms ldns_resolver_set_nameserver_count(res, nscnt); 184189592Sbms return status; 185228969Sjhb} 186228969Sjhb 187228969Sjhbstatic void 188228969Sjhbldns_pkt_filter_answer(ldns_pkt *pkt, ldns_rr_type type) { 189170613Sbms int i, j, cnt; 190189592Sbms ldns_rr_list *rrlist; 191189592Sbms ldns_rr *rr; 192189592Sbms ldns_rr_type rrtype; 193189592Sbms 194189592Sbms rrlist = ldns_pkt_answer(pkt); 195189592Sbms cnt = ldns_rr_list_rr_count(rrlist); 196189592Sbms for (i = j = 0; i < cnt; i++) { 197189592Sbms rr = ldns_rr_list_rr(rrlist, i); 198189592Sbms rrtype = ldns_rr_get_type(rr); 199189592Sbms if (type == LDNS_RR_TYPE_ANY || 200189592Sbms type == rrtype || 201189592Sbms type == LDNS_RR_TYPE_AXFR && 202189592Sbms (rrtype == LDNS_RR_TYPE_A || 203189592Sbms rrtype == LDNS_RR_TYPE_AAAA || 204189592Sbms rrtype == LDNS_RR_TYPE_NS || 205189592Sbms rrtype == LDNS_RR_TYPE_PTR)) 206189592Sbms ldns_rr_list_set_rr(rrlist, rr, j++); 207189592Sbms } 208189592Sbms ldns_rr_list_set_rr_count(rrlist, j); 209189592Sbms} 210189592Sbms 211189592Sbms/* Packet content printing. 212189592Sbms */ 213189592Sbms 214189592Sbmsstatic struct { 215189592Sbms ldns_rr_type type; 216189592Sbms const char *text; 217189592Sbms} rr_types[] = { 218189592Sbms {LDNS_RR_TYPE_A, "has address"}, 219189592Sbms {LDNS_RR_TYPE_NS, "name server"}, 220189592Sbms {LDNS_RR_TYPE_CNAME, "is an alias for"}, 221189592Sbms {LDNS_RR_TYPE_WKS, "has well known services"}, 222189592Sbms {LDNS_RR_TYPE_PTR, "domain name pointer"}, 223189592Sbms {LDNS_RR_TYPE_HINFO, "host information"}, 224189592Sbms {LDNS_RR_TYPE_MX, "mail is handled by"}, 225189592Sbms {LDNS_RR_TYPE_TXT, "descriptive text"}, 226170613Sbms {LDNS_RR_TYPE_X25, "x25 address"}, 227170613Sbms {LDNS_RR_TYPE_ISDN, "ISDN address"}, 228170613Sbms {LDNS_RR_TYPE_SIG, "has signature"}, 229170613Sbms {LDNS_RR_TYPE_KEY, "has key"}, 230170613Sbms {LDNS_RR_TYPE_AAAA, "has IPv6 address"}, 231170613Sbms {LDNS_RR_TYPE_LOC, "location"}, 232170613Sbms}; 233170613Sbms 234170613Sbmsstatic void 235170613Sbmsprint_opcode(ldns_pkt_opcode opcode) { 236170613Sbms ldns_lookup_table *lt = ldns_lookup_by_id(ldns_opcodes, opcode); 237170613Sbms 238170613Sbms if (lt && lt->name) 239170613Sbms printf("%s", lt->name); 240170613Sbms else 241170613Sbms printf("RESERVED%d", opcode); 242170613Sbms} 243170613Sbms 244170613Sbmsstatic void 245170613Sbmsprint_rcode(uint8_t rcode) { 246170613Sbms ldns_lookup_table *lt = ldns_lookup_by_id(ldns_rcodes, rcode); 247170613Sbms 248170613Sbms if (lt && lt->name) 249170613Sbms printf("%s", lt->name); 250170613Sbms else 251189592Sbms printf("RESERVED%d", rcode); 252170613Sbms} 253170613Sbms 254170613Sbmsstatic int 255189592Sbmsprint_rr_type(ldns_rr_type type) { 256189592Sbms char *str; 257170613Sbms int n; 258170613Sbms 259170613Sbms str = ldns_rr_type2str(type); 260170613Sbms n = printf("%s", str); 261170613Sbms free(str); 262170613Sbms return n; 263170613Sbms} 264170613Sbms 265170613Sbmsstatic int 266170613Sbmsprint_rr_class(ldns_rr_class cls) { 267170613Sbms char *str; 268189592Sbms int n; 269170613Sbms 270170613Sbms str = ldns_rr_class2str(cls); 271170613Sbms n = printf("%s", str); 272170613Sbms free(str); 273170613Sbms return n; 274170613Sbms} 275170613Sbms 276170613Sbmsstatic int 277170613Sbmsprint_rdf(ldns_rdf *rdf) { 278170613Sbms char *str; 279170613Sbms int n; 280189592Sbms 281189592Sbms str = ldns_rdf2str(rdf); 282189592Sbms n = printf("%s", str); 283170613Sbms free(str); 284189592Sbms return n; 285170613Sbms} 286170613Sbms 287170613Sbmsstatic int 288170613Sbmsprint_rdf_nodot(ldns_rdf *rdf) { 289189592Sbms char *str; 290170613Sbms int len, n; 291170613Sbms 292170613Sbms str = ldns_rdf2str(rdf); 293170613Sbms len = strlen(str); 294170613Sbms n = printf("%.*s", str[len-1] == '.' ? len-1 : len, str); 295170613Sbms free(str); 296170613Sbms return n; 297170613Sbms} 298170613Sbms 299170613Sbmsstatic int 300170613Sbmsprint_padding(int fromcol, int tocol) { 301189592Sbms int col = fromcol, nextcol = fromcol + 8 - fromcol%8; 302170613Sbms 303170613Sbms if (fromcol + 1 > tocol) tocol = fromcol + 1; 304170613Sbms for (; nextcol <= tocol; col = nextcol, nextcol += 8) 305170613Sbms printf("\t"); 306170613Sbms for (; col < tocol; col++) 307170613Sbms printf(" "); 308170613Sbms return col - fromcol; 309170613Sbms} 310170613Sbms 311170613Sbmsstatic void 312189592Sbmsprint_rr_verbose(ldns_rr *rr) { 313170613Sbms bool isq = ldns_rr_is_question(rr); 314189592Sbms int rdcnt = ldns_rr_rd_count(rr); 315189592Sbms int i, n; 316189592Sbms 317170613Sbms /* bind9-host does not count the initial ';' here */ 318189592Sbms n = isq ? printf(";") : 0; 319189592Sbms n = print_rdf(ldns_rr_owner(rr)); 320189592Sbms if (!isq) { 321170613Sbms n += print_padding(n, 24); 322189592Sbms n += printf("%d", ldns_rr_ttl(rr)); 323170613Sbms } 324189592Sbms n += print_padding(n, 32); 325189592Sbms n += print_rr_class(ldns_rr_get_class(rr)); 326170613Sbms n += print_padding(n, 40); 327170613Sbms n += print_rr_type(ldns_rr_get_type(rr)); 328170613Sbms for (i = 0; i < rdcnt; i++) { 329170613Sbms if (i == 0) print_padding(n, 48); 330170613Sbms else printf(" "); 331170613Sbms print_rdf(ldns_rr_rdf(rr, i)); 332170613Sbms } 333170613Sbms printf("\n"); 334170613Sbms} 335170613Sbms 336189592Sbmsstatic void 337189592Sbmsprint_pkt_section_verbose(const char *name, ldns_rr_list *rrlist) { 338189592Sbms int i, cnt = ldns_rr_list_rr_count(rrlist); 339189592Sbms 340189592Sbms if (cnt == 0) 341189592Sbms return; 342170613Sbms printf(";; %s SECTION:\n", name); 343170613Sbms for (i = 0; i < cnt; i++) 344170613Sbms print_rr_verbose(ldns_rr_list_rr(rrlist, i)); 345189592Sbms printf("\n"); 346189592Sbms} 347189592Sbms 348189592Sbmsstatic void 349170613Sbmsprint_pkt_verbose(ldns_pkt *pkt) { 350189592Sbms int got_flags = 0; 351189592Sbms 352189592Sbms printf(";; ->>HEADER<<- opcode: "); 353170613Sbms print_opcode(ldns_pkt_get_opcode(pkt)); 354189592Sbms printf(", status: "); 355189592Sbms print_rcode(ldns_pkt_get_rcode(pkt)); 356189592Sbms printf(", id: %u\n", ldns_pkt_id(pkt)); 357189592Sbms printf(";; flags:"); 358189592Sbms if (ldns_pkt_qr(pkt)) printf(" qr"), got_flags = 1; 359189592Sbms if (ldns_pkt_aa(pkt)) printf(" aa"), got_flags = 1; 360189592Sbms if (ldns_pkt_tc(pkt)) printf(" tc"), got_flags = 1; 361189592Sbms if (ldns_pkt_rd(pkt)) printf(" rd"), got_flags = 1; 362189592Sbms if (ldns_pkt_ra(pkt)) printf(" ra"), got_flags = 1; 363189592Sbms if (ldns_pkt_ad(pkt)) printf(" ad"), got_flags = 1; 364189592Sbms if (ldns_pkt_cd(pkt)) printf(" cd"), got_flags = 1; 365189592Sbms if (!got_flags) printf(" "); 366189592Sbms printf("; QUERY: %u, ANSWER: %u, AUTHORITY: %u, ADDITIONAL: %u\n", 367189592Sbms ldns_pkt_qdcount(pkt), ldns_pkt_ancount(pkt), 368189592Sbms ldns_pkt_nscount(pkt), ldns_pkt_arcount(pkt)); 369189592Sbms if (ldns_pkt_edns(pkt)) 370189592Sbms printf(";; EDNS: version: %u, udp=%u\n", 371189592Sbms ldns_pkt_edns_version(pkt), ldns_pkt_edns_udp_size(pkt)); 372189592Sbms printf("\n"); 373189592Sbms print_pkt_section_verbose("QUESTION", ldns_pkt_question(pkt)); 374189592Sbms print_pkt_section_verbose("ANSWER", ldns_pkt_answer(pkt)); 375189592Sbms print_pkt_section_verbose("AUTHORITY", ldns_pkt_authority(pkt)); 376189592Sbms print_pkt_section_verbose("ADDITIONAL", ldns_pkt_additional(pkt)); 377189592Sbms} 378189592Sbms 379189592Sbmsstatic void 380189592Sbmsprint_rr_short(ldns_rr *rr) { 381189592Sbms ldns_rr_type type = ldns_rr_get_type(rr); 382189592Sbms size_t i, rdcnt = ldns_rr_rd_count(rr); 383189592Sbms 384189592Sbms print_rdf_nodot(ldns_rr_owner(rr)); 385189592Sbms printf(" "); 386189592Sbms for (i = 0; i < countof(rr_types); i++) { 387189592Sbms if (rr_types[i].type == type) { 388189592Sbms printf("%s", rr_types[i].text); 389189592Sbms goto found; 390189592Sbms } 391189592Sbms } 392189592Sbms 393189592Sbms printf("has "); 394189592Sbms print_rr_type(type); 395189592Sbms printf(" record"); 396189592Sbms 397189592Sbmsfound: 398189592Sbms for (i = 0; i < rdcnt; i++) { 399189592Sbms printf(" "); 400170613Sbms print_rdf(ldns_rr_rdf(rr, i)); 401189592Sbms } 402170613Sbms printf("\n"); 403189592Sbms} 404170613Sbms 405189592Sbmsstatic void 406170613Sbmsprint_pkt_short(ldns_pkt *pkt, bool print_rr_server) { 407170613Sbms ldns_rr_list *rrlist = ldns_pkt_answer(pkt); 408170613Sbms size_t i; 409170613Sbms 410170613Sbms for (i = 0; i < ldns_rr_list_rr_count(rrlist); i++) { 411170613Sbms if (print_rr_server) { 412170613Sbms printf("Nameserver "); 413170613Sbms print_rdf(ldns_pkt_answerfrom(pkt)); 414189592Sbms printf(":\n\t"); 415189592Sbms } 416189592Sbms print_rr_short(ldns_rr_list_rr(rrlist, i)); 417170613Sbms } 418189592Sbms} 419189592Sbms 420189592Sbmsstatic void 421189592Sbmsprint_received_line(ldns_resolver *res, ldns_pkt *pkt) { 422170613Sbms char *from = ldns_rdf2str(ldns_pkt_answerfrom(pkt)); 423189592Sbms 424189592Sbms printf("Received %zu bytes from %s#%d in %d ms\n", 425189592Sbms ldns_pkt_size(pkt), from, ldns_resolver_port(res), 426189592Sbms ldns_pkt_querytime(pkt)); 427189592Sbms free(from); 428189592Sbms} 429189592Sbms 430189592Sbms/* Main program. 431189931Sbms * 432229621Sjhb * Note that no memory is freed below this line by intention. 433189931Sbms */ 434189592Sbms 435189592Sbms#define DEFAULT_TCP_TIMEOUT 10 436189592Sbms#define DEFAULT_UDP_TIMEOUT 5 437189592Sbms 438189592Sbmsenum operation_mode { M_AXFR, M_DEFAULT_Q, M_SINGLE_Q, M_SOA }; 439189592Sbms 440189592Sbmsstatic enum operation_mode o_mode = M_DEFAULT_Q; 441189592Sbmsstatic bool o_ignore_servfail = true; 442189592Sbmsstatic bool o_ip6_int = false; 443170613Sbmsstatic bool o_print_pkt_server = false; 444189592Sbmsstatic bool o_print_rr_server = false; 445189592Sbmsstatic bool o_recursive = true; 446189592Sbmsstatic bool o_tcp = false; 447189592Sbmsstatic bool o_verbose = false; 448189592Sbmsstatic char *o_name = NULL; 449189592Sbmsstatic char *o_server = NULL; 450189592Sbmsstatic int o_ipversion = LDNS_RESOLV_INETANY; 451189592Sbmsstatic int o_ndots = 1; 452189592Sbmsstatic int o_retries = 1; 453170613Sbmsstatic ldns_rr_class o_rrclass = LDNS_RR_CLASS_IN; 454189592Sbmsstatic ldns_rr_type o_rrtype = LDNS_RR_TYPE_A; 455189592Sbmsstatic time_t o_timeout = 0; 456229621Sjhbstatic uint32_t o_ixfr_serial = 0; 457189592Sbms 458189592Sbmsstatic void 459189592Sbmsusage(void) { 460229621Sjhb fputs( 461189931Sbms "Usage: ldns-host [-aCdilrsTvw46] [-c class] [-N ndots] [-R number]\n" 462189592Sbms " [-t type] [-W wait] name [server]\n" 463189592Sbms "\t-a same as -v -t ANY\n" 464189592Sbms "\t-C query SOA records from all authoritative name servers\n" 465189592Sbms "\t-c use this query class (IN, CH, HS, etc)\n" 466189592Sbms "\t-d produce verbose output, same as -v\n" 467189592Sbms "\t-i use IP6.INT for IPv6 reverse lookups\n" 468189592Sbms "\t-l list records in a zone via AXFR\n" 469189592Sbms "\t-N consider names with at least this many dots as absolute\n" 470189592Sbms "\t-R retry UDP queries this many times\n" 471189592Sbms "\t-r disable recursive query\n" 472229621Sjhb "\t-s do not ignore SERVFAIL responses\n" 473189592Sbms "\t-T send query via TCP\n" 474189592Sbms "\t-t use this query type (A, AAAA, MX, etc)\n" 475189592Sbms "\t-v produce verbose output\n" 476189592Sbms "\t-w wait forever for a server reply\n" 477189592Sbms "\t-W wait this many seconds for a reply\n" 478189592Sbms "\t-4 use IPv4 only\n" 479189592Sbms "\t-6 use IPv6 only\n", 480189592Sbms stderr); 481189592Sbms exit(1); 482189592Sbms} 483189592Sbms 484189592Sbmsstatic void 485189592Sbmsparse_args(int argc, char *argv[]) { 486189592Sbms int ch; 487189592Sbms 488189592Sbms progname = argv[0]; 489189592Sbms while ((ch = getopt(argc, argv, "aCdilrsTvw46c:N:R:t:W:")) != -1) { 490189592Sbms switch (ch) { 491189592Sbms case 'a': 492189592Sbms if (o_mode != M_AXFR) 493189592Sbms o_mode = M_SINGLE_Q; 494189592Sbms o_rrtype = LDNS_RR_TYPE_ANY; 495229621Sjhb o_verbose = true; 496189592Sbms break; 497189592Sbms case 'C': 498189592Sbms o_mode = M_SOA; 499189592Sbms o_print_rr_server = true; 500189592Sbms o_rrclass = LDNS_RR_CLASS_IN; 501189592Sbms o_rrtype = LDNS_RR_TYPE_NS; 502189592Sbms break; 503189592Sbms case 'c': 504189592Sbms /* bind9-host sets o_mode to M_SINGLE_Q here */ 505189592Sbms o_rrclass = ldns_get_rr_class_by_name(optarg); 506189592Sbms if (o_rrclass <= 0) 507189592Sbms die(2, "invalid class: %s\n", optarg); 508189592Sbms break; 509189592Sbms case 'd': o_verbose = true; break; 510189592Sbms case 'i': o_ip6_int = true; break; 511189592Sbms case 'l': 512189592Sbms o_mode = M_AXFR; 513189592Sbms o_rrtype = LDNS_RR_TYPE_AXFR; 514189592Sbms o_tcp = true; 515189592Sbms break; 516189592Sbms case 'N': 517189592Sbms o_ndots = atoi(optarg); 518189592Sbms if (o_ndots < 0) o_ndots = 0; 519189592Sbms break; 520189592Sbms case 'n': 521189592Sbms /* bind9-host accepts and ignores this option */ 522189592Sbms break; 523189592Sbms case 'r': o_recursive = 0; break; 524189931Sbms case 'R': 525189592Sbms o_retries = atoi(optarg); 526189592Sbms if (o_retries <= 0) o_retries = 1; 527189592Sbms if (o_retries > 255) o_retries = 255; 528189592Sbms break; 529189592Sbms case 's': o_ignore_servfail = false; break; 530189592Sbms case 'T': o_tcp = true; break; 531189592Sbms case 't': 532189592Sbms if (o_mode != M_AXFR) 533189592Sbms o_mode = M_SINGLE_Q; 534189592Sbms if (strncasecmp(optarg, "ixfr=", 5) == 0) { 535189592Sbms o_rrtype = LDNS_RR_TYPE_IXFR; 536189592Sbms o_ixfr_serial = atol(optarg + 5); 537189592Sbms } else { 538189592Sbms o_rrtype = ldns_get_rr_type_by_name(optarg); 539189592Sbms if (o_rrtype <= 0) 540189592Sbms die(2, "invalid type: %s\n", optarg); 541189592Sbms } 542189592Sbms if (o_rrtype == LDNS_RR_TYPE_AXFR || o_rrtype == LDNS_RR_TYPE_IXFR) 543189592Sbms o_tcp = true; 544189592Sbms if (o_rrtype == LDNS_RR_TYPE_AXFR) { 545189592Sbms o_mode = M_AXFR; 546189592Sbms o_rrtype = LDNS_RR_TYPE_ANY; 547189592Sbms o_verbose = true; 548189592Sbms } 549189592Sbms break; 550189592Sbms case 'v': o_verbose = true; break; 551189592Sbms case 'w': 552189592Sbms o_timeout = (time_t)INT_MAX; 553170613Sbms break; 554189592Sbms case 'W': 555189592Sbms o_timeout = atol(optarg); 556189592Sbms if (o_timeout <= 0) o_timeout = 1; 557189592Sbms break; 558170613Sbms case '4': o_ipversion = LDNS_RESOLV_INET; break; 559189592Sbms case '6': o_ipversion = LDNS_RESOLV_INET6; break; 560189592Sbms default: 561189592Sbms usage(); 562189592Sbms } 563189592Sbms } 564189592Sbms argc -= optind; 565189592Sbms argv += optind; 566189592Sbms /* bind9-host ignores arguments after the 2-nd one */ 567189592Sbms if (argc < 1) 568189592Sbms usage(); 569189592Sbms o_name = argv[0]; 570189592Sbms if (argc > 1) { 571189592Sbms o_server = argv[1]; 572189592Sbms o_print_pkt_server = true; 573189592Sbms } 574189592Sbms} 575189592Sbms 576189592Sbmsstatic ldns_rdf* 577189592Sbmssafe_str2rdf_dname(const char *name) { 578189592Sbms ldns_rdf *dname; 579189592Sbms ldns_status status; 580189592Sbms 581189592Sbms if ((status = ldns_str2rdf_dname(&dname, name)) != LDNS_STATUS_OK) { 582189592Sbms die(1, "'%s' is not a legal name (%s)", 583189592Sbms name, ldns_get_errorstr_by_id(status)); 584189592Sbms } 585189592Sbms return dname; 586189592Sbms} 587189592Sbms 588189592Sbmsstatic ldns_rdf* 589189592Sbmssafe_dname_cat_clone(const ldns_rdf *rd1, const ldns_rdf *rd2) { 590189592Sbms ldns_rdf *result = ldns_dname_cat_clone(rd1, rd2); 591189592Sbms 592189592Sbms if (!result) 593189592Sbms die(1, "not enought memory for a domain name"); 594189592Sbms /* Why doesn't ldns_dname_cat_clone check this condition? */ 595189592Sbms if (ldns_rdf_size(result) > LDNS_MAX_DOMAINLEN) 596189592Sbms die(1, "'%s' is not a legal name (%s)\n", ldns_rdf2str(result), 597189592Sbms ldns_get_errorstr_by_id(LDNS_STATUS_DOMAINNAME_OVERFLOW)); 598189592Sbms return result; 599189592Sbms} 600189592Sbms 601189592Sbmsstatic bool 602189592Sbmsquery(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt) { 603189592Sbms ldns_status status; 604189592Sbms ldns_pkt_rcode rcode; 605189592Sbms int i, cnt; 606189592Sbms 607189592Sbms if (o_verbose) { 608189592Sbms printf("Trying \""); 609189592Sbms print_rdf_nodot(domain); 610189592Sbms printf("\"\n"); 611189592Sbms } 612189592Sbms for (cnt = ldns_resolver_nameserver_count(res), i = 0; i < cnt; i++) { 613189592Sbms status = ldns_resolver_send_to(pkt, res, domain, o_rrtype, 614189592Sbms o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i); 615189592Sbms if (status != LDNS_STATUS_OK) { 616189592Sbms *pkt = NULL; 617189592Sbms continue; 618189592Sbms } 619189592Sbms if (ldns_pkt_tc(*pkt) && !ldns_resolver_usevc(res)) { 620189592Sbms if (o_verbose) 621189592Sbms printf(";; Truncated, retrying in TCP mode.\n"); 622189592Sbms ldns_resolver_set_usevc(res, true); 623189592Sbms status = ldns_resolver_send_to(pkt, res, domain, o_rrtype, 624189592Sbms o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i); 625189592Sbms ldns_resolver_set_usevc(res, false); 626189592Sbms if (status != LDNS_STATUS_OK) 627189592Sbms continue; 628189592Sbms } 629189592Sbms rcode = ldns_pkt_get_rcode(*pkt); 630189592Sbms if (o_ignore_servfail && rcode == LDNS_RCODE_SERVFAIL && cnt > 1) 631189592Sbms continue; 632189592Sbms return rcode == LDNS_RCODE_NOERROR; 633189592Sbms } 634189592Sbms if (*pkt == NULL) { 635189592Sbms printf(";; connection timed out; no servers could be reached\n"); 636189592Sbms exit(1); 637189592Sbms } 638189592Sbms return false; 639189592Sbms} 640189592Sbms 641189592Sbmsstatic ldns_rdf * 642189592Sbmssearch(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt, bool absolute) { 643189592Sbms ldns_rdf *dname, **searchlist; 644189592Sbms int i, n; 645189592Sbms 646189592Sbms if (absolute && query(res, domain, pkt)) 647189592Sbms return domain; 648189592Sbms 649189592Sbms if ((dname = ldns_resolver_domain(res)) != NULL) { 650189592Sbms dname = safe_dname_cat_clone(domain, dname); 651189592Sbms if (query(res, dname, pkt)) 652189592Sbms return dname; 653189592Sbms } 654189592Sbms 655189592Sbms searchlist = ldns_resolver_searchlist(res); 656189592Sbms n = ldns_resolver_searchlist_count(res); 657189592Sbms for (i = 0; i < n; i++) { 658189592Sbms dname = safe_dname_cat_clone(domain, searchlist[i]); 659189592Sbms if (query(res, dname, pkt)) 660189592Sbms return dname; 661189592Sbms } 662189592Sbms 663189592Sbms if (!absolute && query(res, domain, pkt)) 664189592Sbms return domain; 665189592Sbms 666189592Sbms return NULL; 667189592Sbms} 668189592Sbms 669189592Sbmsstatic void 670189592Sbmsreport(ldns_resolver *res, ldns_rdf *domain, ldns_pkt *pkt) { 671189592Sbms ldns_pkt_rcode rcode; 672189592Sbms 673189592Sbms if (o_print_pkt_server) { 674189592Sbms printf("Using domain server:\nName: %s\nAddress: ", o_server); 675189592Sbms print_rdf(ldns_pkt_answerfrom(pkt)); 676189592Sbms printf("#%d\nAliases: \n\n", ldns_resolver_port(res)); 677189592Sbms o_print_pkt_server = false; 678189592Sbms } 679189592Sbms rcode = ldns_pkt_get_rcode(pkt); 680189592Sbms if (rcode != LDNS_RCODE_NOERROR) { 681189592Sbms printf("Host "); 682189592Sbms print_rdf_nodot(domain); 683189592Sbms printf(" not found: %d(", rcode); 684189592Sbms print_rcode(rcode); 685189592Sbms printf(")\n"); 686189592Sbms } else { 687189592Sbms if (o_verbose) { 688189592Sbms print_pkt_verbose(pkt); 689189592Sbms } else { 690189592Sbms print_pkt_short(pkt, o_print_rr_server); 691189592Sbms if (o_mode != M_DEFAULT_Q && 692189592Sbms ldns_rr_list_rr_count(ldns_pkt_answer(pkt)) == 0) { 693189592Sbms print_rdf_nodot(domain); 694189592Sbms printf(" has no "); 695189592Sbms print_rr_type(o_rrtype); 696189592Sbms printf(" record\n"); 697189592Sbms } 698189592Sbms } 699189592Sbms } 700189592Sbms if (o_verbose) 701189592Sbms print_received_line(res, pkt); 702189592Sbms} 703189592Sbms 704189592Sbmsstatic bool 705189592Sbmsdoquery(ldns_resolver *res, ldns_rdf *domain) { 706189592Sbms ldns_pkt *pkt; 707189592Sbms bool q; 708189592Sbms 709189592Sbms q = query(res, domain, &pkt); 710189592Sbms report(res, domain, pkt); 711189592Sbms return q; 712189592Sbms} 713189592Sbms 714189592Sbmsstatic bool 715189592Sbmsdoquery_filtered(ldns_resolver *res, ldns_rdf *domain) { 716189592Sbms ldns_pkt *pkt; 717189592Sbms bool q; 718189592Sbms 719189592Sbms q = query(res, domain, &pkt); 720189592Sbms ldns_pkt_filter_answer(pkt, o_rrtype); 721189592Sbms report(res, domain, pkt); 722189592Sbms return q; 723189592Sbms} 724189592Sbms 725189592Sbmsstatic bool 726189592Sbmsdosearch(ldns_resolver *res, ldns_rdf *domain, bool absolute) { 727189592Sbms ldns_pkt *pkt; 728189592Sbms ldns_rdf *dname; 729189592Sbms 730189592Sbms dname = search(res, domain, &pkt, absolute); 731189592Sbms report(res, dname != NULL ? dname : domain, pkt); 732189592Sbms return o_mode != M_DEFAULT_Q ? (dname != NULL) : 733189592Sbms (dname != NULL) && 734189592Sbms (o_rrtype = LDNS_RR_TYPE_AAAA, doquery_filtered(res, dname)) && 735189592Sbms (o_rrtype = LDNS_RR_TYPE_MX, doquery_filtered(res, dname)); 736189592Sbms} 737189592Sbms 738189592Sbmsstatic bool 739189592Sbmsdoaxfr(ldns_resolver *res, ldns_rdf *domain, bool absolute) { 740189592Sbms ldns_pkt *pkt; 741189592Sbms ldns_rdf *dname; 742189592Sbms ldns_rr_type rrtype; 743189592Sbms 744189592Sbms rrtype = o_rrtype; 745189592Sbms o_rrtype = LDNS_RR_TYPE_AXFR; 746189592Sbms dname = search(res, domain, &pkt, absolute); 747189592Sbms ldns_pkt_filter_answer(pkt, rrtype); 748189592Sbms report(res, dname != NULL ? dname : domain, pkt); 749189592Sbms return dname != NULL; 750189592Sbms} 751189592Sbms 752189592Sbmsstatic bool 753189592Sbmsdosoa(ldns_resolver *res, ldns_rdf *domain, bool absolute) { 754189592Sbms ldns_rr_list *answer, **nsaddrs; 755189592Sbms ldns_rdf *dname, *addr; 756189592Sbms ldns_pkt *pkt; 757189592Sbms ldns_rr *rr; 758189592Sbms size_t i, j, n, cnt; 759189592Sbms 760189592Sbms if ((dname = search(res, domain, &pkt, absolute)) == NULL) 761189592Sbms return false; 762189592Sbms 763189592Sbms answer = ldns_pkt_answer(pkt); 764189592Sbms cnt = ldns_rr_list_rr_count(answer); 765189592Sbms nsaddrs = alloca(cnt*sizeof(*nsaddrs)); 766189592Sbms for (n = 0, i = 0; i < cnt; i++) 767189592Sbms if ((addr = ldns_rr_ns_nsdname(ldns_rr_list_rr(answer, i))) != NULL) 768189592Sbms nsaddrs[n++] = ldns_get_rr_list_addr_by_name(res, 769189592Sbms addr, LDNS_RR_CLASS_IN, 0); 770189592Sbms 771189592Sbms o_print_pkt_server = false; 772189592Sbms o_recursive = false; 773189592Sbms o_rrtype = LDNS_RR_TYPE_SOA; 774189592Sbms for (i = 0; i < n; i++) { 775189592Sbms cnt = ldns_rr_list_rr_count(nsaddrs[i]); 776189592Sbms for (j = 0; j < cnt; j++) { 777189592Sbms ldns_resolver_remove_nameservers(res); 778189592Sbms rr = ldns_rr_list_rr(nsaddrs[i], j); 779189592Sbms if (ldns_resolver_ip6(res) == LDNS_RESOLV_INET && 780189592Sbms ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA || 781189592Sbms ldns_resolver_ip6(res) == LDNS_RESOLV_INET6 && 782189592Sbms ldns_rr_get_type(rr) == LDNS_RR_TYPE_A) 783189592Sbms continue; 784189592Sbms if (ldns_resolver_push_nameserver_rr(res, rr) == LDNS_STATUS_OK) 785189592Sbms /* bind9-host queries for domain, not dname here */ 786189592Sbms doquery(res, dname); 787189592Sbms } 788189592Sbms } 789189592Sbms return 0; 790189592Sbms} 791189592Sbms 792189592Sbmsstatic void 793189592Sbmsresolver_set_nameserver_hostname(ldns_resolver *res, const char *server) { 794189592Sbms struct addrinfo hints, *ailist, *ai; 795189592Sbms ldns_status status; 796189592Sbms ldns_rdf *rdf; 797189592Sbms int err; 798189592Sbms 799189592Sbms memset(&hints, 0, sizeof hints); 800189592Sbms switch (ldns_resolver_ip6(res)) { 801189592Sbms case LDNS_RESOLV_INET: hints.ai_family = PF_INET; break; 802189592Sbms case LDNS_RESOLV_INET6: hints.ai_family = PF_INET6; break; 803189592Sbms default: hints.ai_family = PF_UNSPEC; break; 804189592Sbms } 805189592Sbms hints.ai_socktype = SOCK_STREAM; 806189592Sbms do err = getaddrinfo(server, NULL, &hints, &ailist); 807189592Sbms while (err == EAI_AGAIN); 808189592Sbms if (err != 0) 809189592Sbms die(1, "couldn't get address for '%s': %s", server, gai_strerror(err)); 810189592Sbms for (ai = ailist; ai != NULL; ai = ai->ai_next) { 811189592Sbms if ((rdf = ldns_sockaddr_storage2rdf((void*)ai->ai_addr, NULL)) == NULL) 812189592Sbms die(1, "couldn't allocate an rdf: %s", 813189592Sbms ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); 814189592Sbms status = ldns_resolver_push_nameserver(res, rdf); 815189592Sbms if (status != LDNS_STATUS_OK) 816189592Sbms die(1, "couldn't push a nameserver address: %s", 817189592Sbms ldns_get_errorstr_by_id(status)); 818189592Sbms } 819189592Sbms} 820189592Sbms 821189592Sbmsstatic void 822189592Sbmsresolver_set_nameserver_str(ldns_resolver *res, const char *server) { 823189592Sbms ldns_rdf *addr; 824189592Sbms 825189592Sbms ldns_resolver_remove_nameservers(res); 826189592Sbms addr = ldns_rdf_new_addr_frm_str(server); 827189592Sbms if (addr) { 828189592Sbms if (ldns_resolver_push_nameserver(res, addr) != LDNS_STATUS_OK) 829189592Sbms die(1, "couldn't push a nameserver address"); 830189592Sbms } else 831189592Sbms resolver_set_nameserver_hostname(res, server); 832189592Sbms} 833189592Sbms 834189592Sbmsint 835189592Sbmsmain(int argc, char *argv[]) { 836189592Sbms ldns_rdf *addr, *dname; 837189592Sbms ldns_resolver *res; 838189592Sbms ldns_status status; 839189592Sbms struct timeval restimeout; 840189592Sbms 841189592Sbms parse_args(argc, argv); 842189592Sbms 843189592Sbms status = ldns_resolver_new_default(&res); 844189592Sbms if (status != LDNS_STATUS_OK) 845189592Sbms die(1, "error creating resolver: %s", ldns_get_errorstr_by_id(status)); 846189592Sbms if (ldns_resolver_nameserver_count(res) == 0) 847189592Sbms ldns_resolver_push_default_servers(res); 848189592Sbms 849189592Sbms ldns_resolver_set_usevc(res, o_tcp); 850189592Sbms restimeout.tv_sec = o_timeout > 0 ? o_timeout : 851189592Sbms o_tcp ? DEFAULT_TCP_TIMEOUT : DEFAULT_UDP_TIMEOUT; 852189592Sbms restimeout.tv_usec = 0; 853189592Sbms ldns_resolver_set_timeout(res, restimeout); 854189592Sbms ldns_resolver_set_retry(res, o_retries+1); 855189592Sbms ldns_resolver_set_ip6(res, o_ipversion); 856189592Sbms ldns_resolver_set_defnames(res, false); 857189592Sbms ldns_resolver_set_fallback(res, false); 858189592Sbms 859189592Sbms if (o_server) 860189592Sbms resolver_set_nameserver_str(res, o_server); 861189592Sbms 862189592Sbms if (ldns_str2rdf_a(&addr, o_name) == LDNS_STATUS_OK) { 863189592Sbms dname = ldns_rdf_reverse_a(addr, "in-addr.arpa"); 864189592Sbms if (dname == NULL) 865189592Sbms die(1, "can't reverse '%s': %s", o_name, 866189592Sbms ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); 867189592Sbms o_mode = M_SINGLE_Q; 868189592Sbms o_rrtype = LDNS_RR_TYPE_PTR; 869189592Sbms return !doquery(res, dname); 870189592Sbms } else if (ldns_str2rdf_aaaa(&addr, o_name) == LDNS_STATUS_OK) { 871189592Sbms dname = ldns_rdf_reverse_aaaa(addr, o_ip6_int ? "ip6.int" : "ip6.arpa"); 872189592Sbms if (dname == NULL) 873189592Sbms die(1, "can't reverse '%s': %s", o_name, 874189592Sbms ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); 875189592Sbms o_mode = M_SINGLE_Q; 876189592Sbms o_rrtype = LDNS_RR_TYPE_PTR; 877189592Sbms return !doquery(res, dname); 878189592Sbms } 879189592Sbms return !(o_mode == M_SOA ? dosoa : o_mode == M_AXFR ? doaxfr : dosearch) 880189592Sbms (res, safe_str2rdf_dname(o_name), ndots(o_name) >= o_ndots); 881189592Sbms} 882189592Sbms