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