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
10301759Sdes#include <ldns/ldns.h>
11254939Sdes#include <limits.h>
12254939Sdes#include <netdb.h>
13301759Sdes#include <netinet/in.h>
14254939Sdes#include <stdio.h>
15254939Sdes#include <stdlib.h>
16254939Sdes#include <unistd.h>
17254939Sdes
18254939Sdes/* General utilities.
19254939Sdes */
20254939Sdes
21254939Sdesstatic char *progname;
22254939Sdes
23254939Sdes#define countof(array) (sizeof(array)/sizeof(*(array)))
24254939Sdes
25254939Sdesstatic void
26254939Sdesdie(int code, const char *fmt, ...) {
27254939Sdes    va_list args;
28254939Sdes
29254939Sdes    va_start(args, fmt);
30254939Sdes    fprintf(stderr, "%s: ", progname);
31254939Sdes    vfprintf(stderr, fmt, args);
32254939Sdes    fprintf(stderr, "\n");
33254939Sdes    va_end(args);
34254939Sdes    exit(code);
35254939Sdes}
36254939Sdes
37254939Sdesstatic int
38254939Sdesndots(const char *name) {
39254939Sdes    int n;
40254939Sdes
41254939Sdes    for (n = 0; (name = strchr(name, '.')); n++, name++);
42254939Sdes    return n;
43254939Sdes}
44254939Sdes
45254939Sdes/* General LDNS-specific utilities.
46254939Sdes */
47254939Sdes
48254939Sdesstatic ldns_status
49254939Sdesldns_resolver_new_default(ldns_resolver **res) {
50254939Sdes    if (ldns_resolver_new_frm_file(res, NULL) == LDNS_STATUS_OK ||
51254939Sdes        (*res = ldns_resolver_new()) != NULL)
52254939Sdes        return LDNS_STATUS_OK;
53254939Sdes    return LDNS_STATUS_MEM_ERR;
54254939Sdes}
55254939Sdes
56254939Sdesstatic ldns_status
57254939Sdesldns_resolver_push_default_servers(ldns_resolver *res) {
58254939Sdes    ldns_status status;
59254939Sdes    ldns_rdf *addr;
60254939Sdes
61254939Sdes    if ((status = ldns_str2rdf_a(&addr, "127.0.0.1")) != LDNS_STATUS_OK ||
62254939Sdes        (status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK)
63254939Sdes        return ldns_rdf_deep_free(addr), status;
64254939Sdes    ldns_rdf_deep_free(addr);
65254939Sdes    if ((status = ldns_str2rdf_aaaa(&addr, "::1")) != LDNS_STATUS_OK ||
66254939Sdes        (status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK)
67254939Sdes        return ldns_rdf_deep_free(addr), status;
68254939Sdes    ldns_rdf_deep_free(addr);
69254939Sdes    return LDNS_STATUS_OK;
70254939Sdes}
71254939Sdes
72254939Sdesstatic ldns_rdf *
73254939Sdesldns_rdf_new_addr_frm_str(const char *str) {
74254939Sdes    ldns_rdf *addr;
75254939Sdes
76254939Sdes    if ((addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, str)) == NULL)
77254939Sdes        addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, str);
78254939Sdes    return addr;
79254939Sdes}
80254939Sdes
81254939Sdesstatic void
82254939Sdesldns_resolver_remove_nameservers(ldns_resolver *res) {
83254939Sdes    while (ldns_resolver_nameserver_count(res) > 0)
84254939Sdes        ldns_rdf_deep_free(ldns_resolver_pop_nameserver(res));
85254939Sdes}
86254939Sdes
87254939Sdesstatic ldns_rdf *
88254939Sdesldns_rdf_reverse_a(ldns_rdf *addr, const char *base) {
89254939Sdes    char *buf;
90254939Sdes    int i, len;
91254939Sdes
92254939Sdes    len = strlen(base);
93254939Sdes    buf = alloca(LDNS_IP4ADDRLEN*4 + len + 1);
94254939Sdes    for (len = i = 0; i < LDNS_IP4ADDRLEN; i++)
95254939Sdes        len += sprintf(&buf[len], "%d.",
96254939Sdes            (int)ldns_rdf_data(addr)[LDNS_IP4ADDRLEN - i - 1]);
97254939Sdes    sprintf(&buf[len], "%s", base);
98254939Sdes    return ldns_dname_new_frm_str(buf);
99254939Sdes}
100254939Sdes
101254939Sdesstatic ldns_rdf *
102254939Sdesldns_rdf_reverse_aaaa(ldns_rdf *addr, const char *base) {
103254939Sdes    char *buf;
104254939Sdes    int i, len;
105254939Sdes
106254939Sdes    len = strlen(base);
107254939Sdes    buf = alloca(LDNS_IP6ADDRLEN*4 + len + 1);
108254939Sdes    for (i = 0; i < LDNS_IP6ADDRLEN; i++) {
109254939Sdes        uint8_t byte = ldns_rdf_data(addr)[LDNS_IP6ADDRLEN - i - 1];
110254939Sdes        sprintf(&buf[i*4], "%x.%x.", byte & 0x0F, byte >> 4);
111254939Sdes    }
112254939Sdes    sprintf(&buf[LDNS_IP6ADDRLEN*4], "%s", base);
113254939Sdes    return ldns_dname_new_frm_str(buf);
114254939Sdes}
115254939Sdes
116254939Sdesstatic ldns_status
117254939Sdesldns_pkt_push_rr_soa(ldns_pkt *pkt, ldns_pkt_section sec,
118254939Sdes    const ldns_rdf *name, ldns_rr_class c, uint32_t serial) {
119254939Sdes    ldns_rdf *rdf;
120254939Sdes    ldns_rr *rr;
121254939Sdes    uint32_t n;
122254939Sdes
123254939Sdes    if ((rr = ldns_rr_new_frm_type(LDNS_RR_TYPE_SOA)) == NULL)
124254939Sdes        return LDNS_STATUS_MEM_ERR;
125254939Sdes    ldns_rr_set_class(rr, c);
126254939Sdes    ldns_rr_set_owner(rr, ldns_rdf_clone(name));
127254939Sdes    ldns_rr_set_ttl(rr, 0);
128254939Sdes
129254939Sdes    n = 0;
130254939Sdes    if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, 1, &n)) == NULL)
131254939Sdes        goto memerr;
132254939Sdes    ldns_rr_set_rdf(rr, rdf, 0);
133254939Sdes    ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 1);
134254939Sdes
135254939Sdes    n = htonl(serial);
136254939Sdes    if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT32, 4, &n)) == NULL)
137254939Sdes        goto memerr;
138254939Sdes    ldns_rr_set_rdf(rr, rdf, 2);
139254939Sdes
140254939Sdes    n = 0;
141254939Sdes    if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_PERIOD, 4, &n)) == NULL)
142254939Sdes        goto memerr;
143254939Sdes    ldns_rr_set_rdf(rr, rdf, 3);
144254939Sdes    ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 4);
145254939Sdes    ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 5);
146254939Sdes    ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 6);
147254939Sdes
148254939Sdes    if (ldns_rr_rdf(rr, 1) == NULL || ldns_rr_rdf(rr, 4) == NULL ||
149254939Sdes        ldns_rr_rdf(rr, 5) == NULL || ldns_rr_rdf(rr, 6) == NULL ||
150254939Sdes        !ldns_pkt_push_rr(pkt, sec, rr))
151254939Sdes        goto memerr;
152254939Sdes    return LDNS_STATUS_OK;
153254939Sdes
154254939Sdesmemerr:
155254939Sdes    ldns_rr_free(rr);
156254939Sdes    return LDNS_STATUS_MEM_ERR;
157254939Sdes}
158254939Sdes
159254939Sdesstatic ldns_status
160301759Sdesldns_tcp_start(ldns_resolver *res, ldns_pkt *qpkt, int nameserver) {
161301759Sdes    /* This routine is based on ldns_axfr_start, with the major
162301759Sdes     * difference in that it takes a query packet explicitly.
163301759Sdes     */
164301759Sdes    struct sockaddr_storage *ns = NULL;
165301759Sdes    size_t ns_len = 0;
166301759Sdes    ldns_buffer *qbuf = NULL;
167301759Sdes    ldns_status status;
168301759Sdes
169301759Sdes    ns = ldns_rdf2native_sockaddr_storage(
170301759Sdes            res->_nameservers[nameserver], ldns_resolver_port(res), &ns_len);
171301759Sdes    if (ns == NULL) {
172301759Sdes        status = LDNS_STATUS_MEM_ERR;
173301759Sdes        goto error;
174301759Sdes    }
175301759Sdes
176301759Sdes    res->_socket = ldns_tcp_connect(
177301759Sdes            ns, (socklen_t)ns_len, ldns_resolver_timeout(res));
178301759Sdes    if (res->_socket <= 0) {
179301759Sdes        status = LDNS_STATUS_ADDRESS_ERR;
180301759Sdes        goto error;
181301759Sdes    }
182301759Sdes
183301759Sdes    qbuf = ldns_buffer_new(LDNS_MAX_PACKETLEN);
184301759Sdes    if (qbuf == NULL) {
185301759Sdes        status = LDNS_STATUS_MEM_ERR;
186301759Sdes        goto error;
187301759Sdes    }
188301759Sdes
189301759Sdes    status = ldns_pkt2buffer_wire(qbuf, qpkt);
190301759Sdes    if (status != LDNS_STATUS_OK)
191301759Sdes        goto error;
192301759Sdes
193301759Sdes    if (ldns_tcp_send_query(qbuf, res->_socket, ns, (socklen_t)ns_len) == 0) {
194301759Sdes        status = LDNS_STATUS_NETWORK_ERR;
195301759Sdes        goto error;
196301759Sdes    }
197301759Sdes
198301759Sdes    ldns_buffer_free(qbuf);
199301759Sdes    free(ns);
200301759Sdes    return LDNS_STATUS_OK;
201301759Sdes
202301759Sdeserror:
203301759Sdes    ldns_buffer_free(qbuf);
204301759Sdes    free(ns);
205301759Sdes    if (res->_socket > 0) {
206301759Sdes        close(res->_socket);
207301759Sdes        res->_socket = 0;
208301759Sdes    }
209301759Sdes    return status;
210301759Sdes}
211301759Sdes
212301759Sdesstatic ldns_status
213301759Sdesldns_tcp_read(ldns_pkt **answer, ldns_resolver *res) {
214301759Sdes    ldns_status status;
215301759Sdes    struct timeval t1, t2;
216301759Sdes    uint8_t *data;
217301759Sdes    size_t size;
218301759Sdes
219301759Sdes    if (res->_socket <= 0)
220301759Sdes        return LDNS_STATUS_ERR;
221301759Sdes
222301759Sdes    gettimeofday(&t1, NULL);
223301759Sdes    data = ldns_tcp_read_wire_timeout(
224301759Sdes            res->_socket, &size, ldns_resolver_timeout(res));
225301759Sdes    if (data == NULL)
226301759Sdes        goto error;
227301759Sdes
228301759Sdes    status = ldns_wire2pkt(answer, data, size);
229301759Sdes    free(data);
230301759Sdes    if (status != LDNS_STATUS_OK)
231301759Sdes        goto error;
232301759Sdes
233301759Sdes    gettimeofday(&t2, NULL);
234301759Sdes    ldns_pkt_set_querytime(*answer,
235301759Sdes            (uint32_t)((t2.tv_sec - t1.tv_sec)*1000) +
236301759Sdes                (t2.tv_usec - t1.tv_usec)/1000);
237301759Sdes    ldns_pkt_set_timestamp(*answer, t2);
238301759Sdes    return status;
239301759Sdes
240301759Sdeserror:
241301759Sdes    close(res->_socket);
242301759Sdes    res->_socket = 0;
243301759Sdes    return LDNS_STATUS_ERR;
244301759Sdes}
245301759Sdes
246301759Sdesstatic void
247301759Sdesldns_tcp_close(ldns_resolver *res) {
248301759Sdes    if (res->_socket > 0) {
249301759Sdes        close(res->_socket);
250301759Sdes        res->_socket = 0;
251301759Sdes    }
252301759Sdes}
253301759Sdes
254301759Sdesstatic ldns_status
255254939Sdesldns_resolver_send_to(ldns_pkt **answer, ldns_resolver *res,
256254939Sdes    const ldns_rdf *name, ldns_rr_type t, ldns_rr_class c,
257301759Sdes    uint16_t flags, uint32_t ixfr_serial, int nameserver,
258301759Sdes    bool close_tcp) {
259301759Sdes    ldns_status status = LDNS_STATUS_OK;
260254939Sdes    ldns_pkt *qpkt;
261301759Sdes    struct timeval now;
262254939Sdes
263254939Sdes    int nscnt = ldns_resolver_nameserver_count(res);
264254939Sdes    ldns_rdf **ns = ldns_resolver_nameservers(res);
265254939Sdes    size_t *rtt = ldns_resolver_rtt(res);
266254939Sdes
267254939Sdes    ldns_resolver_set_nameservers(res, &ns[nameserver]);
268254939Sdes    ldns_resolver_set_rtt(res, &rtt[nameserver]);
269254939Sdes    ldns_resolver_set_nameserver_count(res, 1);
270254939Sdes
271301759Sdes    /* The next fragment should have been a call to
272301759Sdes     * ldns_resolver_prepare_query_pkt(), but starting with ldns
273301759Sdes     * version 1.6.17 that function tries to add it's own SOA
274301759Sdes     * records when rr_type is LDNS_RR_TYPE_IXFR, and we don't
275301759Sdes     * want that.
276301759Sdes     */
277301759Sdes    qpkt = ldns_pkt_query_new(ldns_rdf_clone(name), t, c, flags);
278301759Sdes    if (qpkt == NULL) {
279301759Sdes        status = LDNS_STATUS_ERR;
280301759Sdes        goto done;
281301759Sdes    }
282301759Sdes    now.tv_sec = time(NULL);
283301759Sdes    now.tv_usec = 0;
284301759Sdes    ldns_pkt_set_timestamp(qpkt, now);
285301759Sdes    ldns_pkt_set_random_id(qpkt);
286301759Sdes
287301759Sdes    if (t == LDNS_RR_TYPE_IXFR) {
288254939Sdes        status = ldns_pkt_push_rr_soa(qpkt,
289254939Sdes            LDNS_SECTION_AUTHORITY, name, c, ixfr_serial);
290301759Sdes        if (status != LDNS_STATUS_OK) goto done;
291301759Sdes    }
292301759Sdes    if (close_tcp) {
293254939Sdes        status = ldns_resolver_send_pkt(answer, res, qpkt);
294301759Sdes    } else {
295301759Sdes        status = ldns_tcp_start(res, qpkt, 0);
296301759Sdes        if (status != LDNS_STATUS_OK) goto done;
297301759Sdes        status = ldns_tcp_read(answer, res);
298301759Sdes        if (status != LDNS_STATUS_OK) goto done;
299301759Sdes        ldns_pkt_set_answerfrom(*answer, ldns_rdf_clone(ns[0]));
300301759Sdes    }
301301759Sdes
302301759Sdesdone:
303254939Sdes    ldns_pkt_free(qpkt);
304254939Sdes
305254939Sdes    ldns_resolver_set_nameservers(res, ns);
306254939Sdes    ldns_resolver_set_rtt(res, rtt);
307254939Sdes    ldns_resolver_set_nameserver_count(res, nscnt);
308254939Sdes    return status;
309254939Sdes}
310254939Sdes
311254939Sdesstatic void
312254939Sdesldns_pkt_filter_answer(ldns_pkt *pkt, ldns_rr_type type) {
313254939Sdes    int i, j, cnt;
314254939Sdes    ldns_rr_list *rrlist;
315254939Sdes    ldns_rr *rr;
316254939Sdes    ldns_rr_type rrtype;
317254939Sdes
318254939Sdes    rrlist = ldns_pkt_answer(pkt);
319254939Sdes    cnt = ldns_rr_list_rr_count(rrlist);
320254939Sdes    for (i = j = 0; i < cnt; i++) {
321254939Sdes        rr = ldns_rr_list_rr(rrlist, i);
322254939Sdes        rrtype = ldns_rr_get_type(rr);
323254939Sdes        if (type == LDNS_RR_TYPE_ANY ||
324254939Sdes            type == rrtype ||
325255403Sdes            (type == LDNS_RR_TYPE_AXFR &&
326254939Sdes                (rrtype == LDNS_RR_TYPE_A ||
327301759Sdes                rrtype == LDNS_RR_TYPE_AAAA ||
328301759Sdes                rrtype == LDNS_RR_TYPE_NS ||
329301759Sdes                rrtype == LDNS_RR_TYPE_PTR)))
330254939Sdes            ldns_rr_list_set_rr(rrlist, rr, j++);
331254939Sdes    }
332254939Sdes    ldns_rr_list_set_rr_count(rrlist, j);
333254939Sdes}
334254939Sdes
335254939Sdes/* Packet content printing.
336254939Sdes */
337254939Sdes
338254939Sdesstatic struct {
339254939Sdes    ldns_rr_type type;
340254939Sdes    const char *text;
341254939Sdes} rr_types[] = {
342254939Sdes    {LDNS_RR_TYPE_A,        "has address"},
343254939Sdes    {LDNS_RR_TYPE_NS,       "name server"},
344254939Sdes    {LDNS_RR_TYPE_CNAME,    "is an alias for"},
345254939Sdes    {LDNS_RR_TYPE_WKS,      "has well known services"},
346254939Sdes    {LDNS_RR_TYPE_PTR,      "domain name pointer"},
347254939Sdes    {LDNS_RR_TYPE_HINFO,    "host information"},
348254939Sdes    {LDNS_RR_TYPE_MX,       "mail is handled by"},
349254939Sdes    {LDNS_RR_TYPE_TXT,      "descriptive text"},
350254939Sdes    {LDNS_RR_TYPE_X25,      "x25 address"},
351254939Sdes    {LDNS_RR_TYPE_ISDN,     "ISDN address"},
352254939Sdes    {LDNS_RR_TYPE_SIG,      "has signature"},
353254939Sdes    {LDNS_RR_TYPE_KEY,      "has key"},
354254939Sdes    {LDNS_RR_TYPE_AAAA,     "has IPv6 address"},
355254939Sdes    {LDNS_RR_TYPE_LOC,      "location"},
356254939Sdes};
357254939Sdes
358254939Sdesstatic void
359254939Sdesprint_opcode(ldns_pkt_opcode opcode) {
360254939Sdes    ldns_lookup_table *lt = ldns_lookup_by_id(ldns_opcodes, opcode);
361254939Sdes
362254939Sdes    if (lt && lt->name)
363254939Sdes        printf("%s", lt->name);
364254939Sdes    else
365254939Sdes        printf("RESERVED%d", opcode);
366254939Sdes}
367254939Sdes
368254939Sdesstatic void
369254939Sdesprint_rcode(uint8_t rcode) {
370254939Sdes    ldns_lookup_table *lt = ldns_lookup_by_id(ldns_rcodes, rcode);
371254939Sdes
372254939Sdes    if (lt && lt->name)
373254939Sdes        printf("%s", lt->name);
374254939Sdes    else
375254939Sdes        printf("RESERVED%d", rcode);
376254939Sdes}
377254939Sdes
378254939Sdesstatic int
379254939Sdesprint_rr_type(ldns_rr_type type) {
380254939Sdes    char *str;
381254939Sdes    int n;
382254939Sdes
383254939Sdes    str = ldns_rr_type2str(type);
384254939Sdes    n = printf("%s", str);
385254939Sdes    free(str);
386254939Sdes    return n;
387254939Sdes}
388254939Sdes
389254939Sdesstatic int
390254939Sdesprint_rr_class(ldns_rr_class cls) {
391254939Sdes    char *str;
392254939Sdes    int n;
393254939Sdes
394254939Sdes    str = ldns_rr_class2str(cls);
395254939Sdes    n = printf("%s", str);
396254939Sdes    free(str);
397254939Sdes    return n;
398254939Sdes}
399254939Sdes
400254939Sdesstatic int
401254939Sdesprint_rdf(ldns_rdf *rdf) {
402254939Sdes    char *str;
403254939Sdes    int n;
404254939Sdes
405254939Sdes    str = ldns_rdf2str(rdf);
406254939Sdes    n = printf("%s", str);
407254939Sdes    free(str);
408254939Sdes    return n;
409254939Sdes}
410254939Sdes
411254939Sdesstatic int
412254939Sdesprint_rdf_nodot(ldns_rdf *rdf) {
413254939Sdes    char *str;
414254939Sdes    int len, n;
415254939Sdes
416254939Sdes    str = ldns_rdf2str(rdf);
417254939Sdes    len = strlen(str);
418254939Sdes    n = printf("%.*s", str[len-1] == '.' ? len-1 : len, str);
419254939Sdes    free(str);
420254939Sdes    return n;
421254939Sdes}
422254939Sdes
423254939Sdesstatic int
424254939Sdesprint_padding(int fromcol, int tocol) {
425254939Sdes    int col = fromcol, nextcol = fromcol + 8 - fromcol%8;
426254939Sdes
427254939Sdes    if (fromcol + 1 > tocol) tocol = fromcol + 1;
428254939Sdes    for (; nextcol <= tocol; col = nextcol, nextcol += 8)
429254939Sdes        printf("\t");
430254939Sdes    for (; col < tocol; col++)
431254939Sdes        printf(" ");
432254939Sdes    return col - fromcol;
433254939Sdes}
434254939Sdes
435254939Sdesstatic void
436254939Sdesprint_rr_verbose(ldns_rr *rr) {
437254939Sdes    bool isq = ldns_rr_is_question(rr);
438254939Sdes    int rdcnt = ldns_rr_rd_count(rr);
439254939Sdes    int i, n;
440254939Sdes
441254939Sdes    /* bind9-host does not count the initial ';' here */
442254939Sdes    n = isq ? printf(";") : 0;
443254939Sdes    n = print_rdf(ldns_rr_owner(rr));
444254939Sdes    if (!isq) {
445254939Sdes        n += print_padding(n, 24);
446254939Sdes        n += printf("%d", ldns_rr_ttl(rr));
447254939Sdes    }
448254939Sdes    n += print_padding(n, 32);
449254939Sdes    n += print_rr_class(ldns_rr_get_class(rr));
450254939Sdes    n += print_padding(n, 40);
451254939Sdes    n += print_rr_type(ldns_rr_get_type(rr));
452254939Sdes    for (i = 0; i < rdcnt; i++) {
453254939Sdes        if (i == 0) print_padding(n, 48);
454254939Sdes        else printf(" ");
455254939Sdes        print_rdf(ldns_rr_rdf(rr, i));
456254939Sdes    }
457254939Sdes    printf("\n");
458254939Sdes}
459254939Sdes
460254939Sdesstatic void
461254939Sdesprint_pkt_section_verbose(const char *name, ldns_rr_list *rrlist) {
462254939Sdes    int i, cnt = ldns_rr_list_rr_count(rrlist);
463254939Sdes
464254939Sdes    if (cnt == 0)
465254939Sdes        return;
466254939Sdes    printf(";; %s SECTION:\n", name);
467254939Sdes    for (i = 0; i < cnt; i++)
468254939Sdes        print_rr_verbose(ldns_rr_list_rr(rrlist, i));
469254939Sdes    printf("\n");
470254939Sdes}
471254939Sdes
472254939Sdesstatic void
473254939Sdesprint_pkt_verbose(ldns_pkt *pkt) {
474254939Sdes    int got_flags = 0;
475254939Sdes
476254939Sdes    printf(";; ->>HEADER<<- opcode: ");
477254939Sdes    print_opcode(ldns_pkt_get_opcode(pkt));
478254939Sdes    printf(", status: ");
479254939Sdes    print_rcode(ldns_pkt_get_rcode(pkt));
480254939Sdes    printf(", id: %u\n", ldns_pkt_id(pkt));
481254939Sdes    printf(";; flags:");
482254939Sdes    if (ldns_pkt_qr(pkt)) printf(" qr"), got_flags = 1;
483254939Sdes    if (ldns_pkt_aa(pkt)) printf(" aa"), got_flags = 1;
484254939Sdes    if (ldns_pkt_tc(pkt)) printf(" tc"), got_flags = 1;
485254939Sdes    if (ldns_pkt_rd(pkt)) printf(" rd"), got_flags = 1;
486254939Sdes    if (ldns_pkt_ra(pkt)) printf(" ra"), got_flags = 1;
487254939Sdes    if (ldns_pkt_ad(pkt)) printf(" ad"), got_flags = 1;
488254939Sdes    if (ldns_pkt_cd(pkt)) printf(" cd"), got_flags = 1;
489254939Sdes    if (!got_flags) printf(" ");
490254939Sdes    printf("; QUERY: %u, ANSWER: %u, AUTHORITY: %u, ADDITIONAL: %u\n",
491254939Sdes        ldns_pkt_qdcount(pkt), ldns_pkt_ancount(pkt),
492254939Sdes        ldns_pkt_nscount(pkt), ldns_pkt_arcount(pkt));
493254939Sdes    if (ldns_pkt_edns(pkt))
494254939Sdes        printf(";; EDNS: version: %u, udp=%u\n",
495254939Sdes            ldns_pkt_edns_version(pkt), ldns_pkt_edns_udp_size(pkt));
496254939Sdes    printf("\n");
497254939Sdes    print_pkt_section_verbose("QUESTION", ldns_pkt_question(pkt));
498254939Sdes    print_pkt_section_verbose("ANSWER", ldns_pkt_answer(pkt));
499254939Sdes    print_pkt_section_verbose("AUTHORITY", ldns_pkt_authority(pkt));
500254939Sdes    print_pkt_section_verbose("ADDITIONAL", ldns_pkt_additional(pkt));
501254939Sdes}
502254939Sdes
503254939Sdesstatic void
504254939Sdesprint_rr_short(ldns_rr *rr) {
505254939Sdes    ldns_rr_type type = ldns_rr_get_type(rr);
506254939Sdes    size_t i, rdcnt = ldns_rr_rd_count(rr);
507254939Sdes
508254939Sdes    print_rdf_nodot(ldns_rr_owner(rr));
509254939Sdes    printf(" ");
510254939Sdes    for (i = 0; i < countof(rr_types); i++) {
511254939Sdes        if (rr_types[i].type == type) {
512254939Sdes            printf("%s", rr_types[i].text);
513254939Sdes            goto found;
514254939Sdes        }
515254939Sdes    }
516254939Sdes
517254939Sdes    printf("has ");
518254939Sdes    print_rr_type(type);
519254939Sdes    printf(" record");
520254939Sdes
521254939Sdesfound:
522254939Sdes    for (i = 0; i < rdcnt; i++) {
523254939Sdes        printf(" ");
524254939Sdes        print_rdf(ldns_rr_rdf(rr, i));
525254939Sdes    }
526254939Sdes    printf("\n");
527254939Sdes}
528254939Sdes
529254939Sdesstatic void
530254939Sdesprint_pkt_short(ldns_pkt *pkt, bool print_rr_server) {
531254939Sdes    ldns_rr_list *rrlist = ldns_pkt_answer(pkt);
532254939Sdes    size_t i;
533254939Sdes
534254939Sdes    for (i = 0; i < ldns_rr_list_rr_count(rrlist); i++) {
535254939Sdes        if (print_rr_server) {
536254939Sdes            printf("Nameserver ");
537254939Sdes            print_rdf(ldns_pkt_answerfrom(pkt));
538254939Sdes            printf(":\n\t");
539254939Sdes        }
540254939Sdes        print_rr_short(ldns_rr_list_rr(rrlist, i));
541254939Sdes    }
542254939Sdes}
543254939Sdes
544254939Sdesstatic void
545254939Sdesprint_received_line(ldns_resolver *res, ldns_pkt *pkt) {
546254939Sdes    char *from = ldns_rdf2str(ldns_pkt_answerfrom(pkt));
547254939Sdes
548254939Sdes    printf("Received %zu bytes from %s#%d in %d ms\n",
549254939Sdes            ldns_pkt_size(pkt), from, ldns_resolver_port(res),
550254939Sdes            ldns_pkt_querytime(pkt));
551254939Sdes    free(from);
552254939Sdes}
553254939Sdes
554254939Sdes/* Main program.
555254939Sdes *
556254939Sdes * Note that no memory is freed below this line by intention.
557254939Sdes */
558254939Sdes
559254939Sdes#define DEFAULT_TCP_TIMEOUT 10
560254939Sdes#define DEFAULT_UDP_TIMEOUT 5
561254939Sdes
562301759Sdesenum operation_mode { M_AXFR, M_IXFR, M_DEFAULT_Q, M_SINGLE_Q, M_SOA };
563254939Sdes
564254939Sdesstatic enum operation_mode o_mode = M_DEFAULT_Q;
565254939Sdesstatic bool o_ignore_servfail = true;
566254939Sdesstatic bool o_ip6_int = false;
567254939Sdesstatic bool o_print_pkt_server = false;
568254939Sdesstatic bool o_print_rr_server = false;
569254939Sdesstatic bool o_recursive = true;
570254939Sdesstatic bool o_tcp = false;
571254939Sdesstatic bool o_verbose = false;
572254939Sdesstatic char *o_name = NULL;
573254939Sdesstatic char *o_server = NULL;
574254939Sdesstatic int o_ipversion = LDNS_RESOLV_INETANY;
575254939Sdesstatic int o_ndots = 1;
576254939Sdesstatic int o_retries = 1;
577254939Sdesstatic ldns_rr_class o_rrclass = LDNS_RR_CLASS_IN;
578301759Sdesstatic ldns_rr_type o_rrtype = (ldns_rr_type)-1;
579254939Sdesstatic time_t o_timeout = 0;
580254939Sdesstatic uint32_t o_ixfr_serial = 0;
581254939Sdes
582254939Sdesstatic void
583254939Sdesusage(void) {
584301759Sdes    fprintf(stderr,
585301759Sdes    "Usage: %s [-aCdilrsTvw46] [-c class] [-N ndots] [-R number]\n"
586301759Sdes    "       %*c [-t type] [-W wait] name [server]\n"
587254939Sdes    "\t-a same as -v -t ANY\n"
588254939Sdes    "\t-C query SOA records from all authoritative name servers\n"
589254939Sdes    "\t-c use this query class (IN, CH, HS, etc)\n"
590254939Sdes    "\t-d produce verbose output, same as -v\n"
591254939Sdes    "\t-i use IP6.INT for IPv6 reverse lookups\n"
592254939Sdes    "\t-l list records in a zone via AXFR\n"
593254939Sdes    "\t-N consider names with at least this many dots as absolute\n"
594254939Sdes    "\t-R retry UDP queries this many times\n"
595254939Sdes    "\t-r disable recursive query\n"
596254939Sdes    "\t-s do not ignore SERVFAIL responses\n"
597254939Sdes    "\t-T send query via TCP\n"
598254939Sdes    "\t-t use this query type (A, AAAA, MX, etc)\n"
599254939Sdes    "\t-v produce verbose output\n"
600254939Sdes    "\t-w wait forever for a server reply\n"
601254939Sdes    "\t-W wait this many seconds for a reply\n"
602254939Sdes    "\t-4 use IPv4 only\n"
603254939Sdes    "\t-6 use IPv6 only\n",
604301759Sdes    progname, (int)strlen(progname), ' ');
605254939Sdes    exit(1);
606254939Sdes}
607254939Sdes
608254939Sdesstatic void
609254939Sdesparse_args(int argc, char *argv[]) {
610254939Sdes    int ch;
611254939Sdes
612254939Sdes    progname = argv[0];
613254939Sdes    while ((ch = getopt(argc, argv, "aCdilrsTvw46c:N:R:t:W:")) != -1) {
614254939Sdes        switch (ch) {
615254939Sdes        case 'a':
616254939Sdes            if (o_mode != M_AXFR)
617254939Sdes                o_mode = M_SINGLE_Q;
618254939Sdes            o_rrtype = LDNS_RR_TYPE_ANY;
619254939Sdes            o_verbose = true;
620254939Sdes            break;
621254939Sdes        case 'C':
622254939Sdes            o_mode = M_SOA;
623254939Sdes            o_print_rr_server = true;
624254939Sdes            o_rrclass = LDNS_RR_CLASS_IN;
625254939Sdes            o_rrtype = LDNS_RR_TYPE_NS;
626254939Sdes            break;
627254939Sdes        case 'c':
628254939Sdes            /* bind9-host sets o_mode to M_SINGLE_Q here */
629254939Sdes            o_rrclass = ldns_get_rr_class_by_name(optarg);
630254939Sdes            if (o_rrclass <= 0)
631254939Sdes                die(2, "invalid class: %s\n", optarg);
632254939Sdes            break;
633254939Sdes        case 'd': o_verbose = true; break;
634254939Sdes        case 'i': o_ip6_int = true; break;
635254939Sdes        case 'l':
636254939Sdes            o_mode = M_AXFR;
637301759Sdes            if (o_rrtype == (ldns_rr_type)-1)
638301759Sdes                o_rrtype = LDNS_RR_TYPE_AXFR;
639254939Sdes            o_tcp = true;
640254939Sdes            break;
641254939Sdes        case 'N':
642254939Sdes            o_ndots = atoi(optarg);
643254939Sdes            if (o_ndots < 0) o_ndots = 0;
644254939Sdes            break;
645254939Sdes        case 'n':
646254939Sdes            /* bind9-host accepts and ignores this option */
647254939Sdes            break;
648254939Sdes        case 'r': o_recursive = 0; break;
649254939Sdes        case 'R':
650254939Sdes            o_retries = atoi(optarg);
651254939Sdes            if (o_retries <= 0) o_retries = 1;
652254939Sdes            if (o_retries > 255) o_retries = 255;
653254939Sdes            break;
654254939Sdes        case 's': o_ignore_servfail = false; break;
655254939Sdes        case 'T': o_tcp = true; break;
656254939Sdes        case 't':
657254939Sdes            if (o_mode != M_AXFR)
658254939Sdes                o_mode = M_SINGLE_Q;
659254939Sdes            if (strncasecmp(optarg, "ixfr=", 5) == 0) {
660254939Sdes                o_rrtype = LDNS_RR_TYPE_IXFR;
661254939Sdes                o_ixfr_serial = atol(optarg + 5);
662254939Sdes            } else {
663254939Sdes                o_rrtype = ldns_get_rr_type_by_name(optarg);
664254939Sdes                if (o_rrtype <= 0)
665254939Sdes                    die(2, "invalid type: %s\n", optarg);
666254939Sdes            }
667254939Sdes            if (o_rrtype == LDNS_RR_TYPE_AXFR) {
668254939Sdes                o_mode = M_AXFR;
669254939Sdes                o_rrtype = LDNS_RR_TYPE_ANY;
670254939Sdes                o_verbose = true;
671254939Sdes            }
672301759Sdes            if (o_rrtype == LDNS_RR_TYPE_IXFR) {
673301759Sdes                o_mode = M_IXFR;
674301759Sdes                o_rrtype = LDNS_RR_TYPE_ANY;
675301759Sdes            }
676254939Sdes            break;
677254939Sdes        case 'v': o_verbose = true; break;
678254939Sdes        case 'w':
679254939Sdes              o_timeout = (time_t)INT_MAX;
680254939Sdes              break;
681254939Sdes        case 'W':
682254939Sdes            o_timeout = atol(optarg);
683254939Sdes            if (o_timeout <= 0) o_timeout = 1;
684254939Sdes            break;
685254939Sdes        case '4': o_ipversion = LDNS_RESOLV_INET; break;
686254939Sdes        case '6': o_ipversion = LDNS_RESOLV_INET6; break;
687254939Sdes        default:
688254939Sdes            usage();
689254939Sdes        }
690254939Sdes    }
691254939Sdes    argc -= optind;
692254939Sdes    argv += optind;
693254939Sdes    /* bind9-host ignores arguments after the 2-nd one */
694254939Sdes    if (argc < 1)
695254939Sdes        usage();
696254939Sdes    o_name = argv[0];
697254939Sdes    if (argc > 1) {
698254939Sdes        o_server = argv[1];
699254939Sdes        o_print_pkt_server = true;
700254939Sdes    }
701301759Sdes    if (o_rrtype == (ldns_rr_type)-1)
702301759Sdes        o_rrtype = LDNS_RR_TYPE_A;
703254939Sdes}
704254939Sdes
705254939Sdesstatic ldns_rdf*
706254939Sdessafe_str2rdf_dname(const char *name) {
707254939Sdes    ldns_rdf *dname;
708254939Sdes    ldns_status status;
709254939Sdes
710254939Sdes    if ((status = ldns_str2rdf_dname(&dname, name)) != LDNS_STATUS_OK) {
711254939Sdes        die(1, "'%s' is not a legal name (%s)",
712254939Sdes            name, ldns_get_errorstr_by_id(status));
713254939Sdes    }
714254939Sdes    return dname;
715254939Sdes}
716254939Sdes
717254939Sdesstatic ldns_rdf*
718254939Sdessafe_dname_cat_clone(const ldns_rdf *rd1, const ldns_rdf *rd2) {
719254939Sdes    ldns_rdf *result = ldns_dname_cat_clone(rd1, rd2);
720254939Sdes
721254939Sdes    if (!result)
722254939Sdes        die(1, "not enought memory for a domain name");
723254939Sdes    /* Why doesn't ldns_dname_cat_clone check this condition? */
724254939Sdes    if (ldns_rdf_size(result) > LDNS_MAX_DOMAINLEN)
725254939Sdes        die(1, "'%s' is not a legal name (%s)\n", ldns_rdf2str(result),
726254939Sdes            ldns_get_errorstr_by_id(LDNS_STATUS_DOMAINNAME_OVERFLOW));
727254939Sdes    return result;
728254939Sdes}
729254939Sdes
730254939Sdesstatic bool
731301759Sdesquery(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt, bool close_tcp) {
732254939Sdes    ldns_status status;
733254939Sdes    ldns_pkt_rcode rcode;
734254939Sdes    int i, cnt;
735254939Sdes
736254939Sdes    if (o_verbose) {
737254939Sdes        printf("Trying \"");
738254939Sdes        print_rdf_nodot(domain);
739254939Sdes        printf("\"\n");
740254939Sdes    }
741254939Sdes    for (cnt = ldns_resolver_nameserver_count(res), i = 0; i < cnt; i++) {
742254939Sdes        status = ldns_resolver_send_to(pkt, res, domain, o_rrtype,
743301759Sdes            o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i,
744301759Sdes            close_tcp);
745254939Sdes        if (status != LDNS_STATUS_OK) {
746254939Sdes            *pkt = NULL;
747254939Sdes            continue;
748254939Sdes        }
749254939Sdes        if (ldns_pkt_tc(*pkt) && !ldns_resolver_usevc(res)) {
750254939Sdes            if (o_verbose)
751254939Sdes                printf(";; Truncated, retrying in TCP mode.\n");
752254939Sdes            ldns_resolver_set_usevc(res, true);
753254939Sdes            status = ldns_resolver_send_to(pkt, res, domain, o_rrtype,
754301759Sdes                o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i,
755301759Sdes                close_tcp);
756254939Sdes            ldns_resolver_set_usevc(res, false);
757254939Sdes            if (status != LDNS_STATUS_OK)
758254939Sdes                continue;
759254939Sdes        }
760254939Sdes        rcode = ldns_pkt_get_rcode(*pkt);
761254939Sdes        if (o_ignore_servfail && rcode == LDNS_RCODE_SERVFAIL && cnt > 1)
762254939Sdes            continue;
763254939Sdes        return rcode == LDNS_RCODE_NOERROR;
764254939Sdes    }
765254939Sdes    if (*pkt == NULL) {
766254939Sdes        printf(";; connection timed out; no servers could be reached\n");
767254939Sdes        exit(1);
768254939Sdes    }
769254939Sdes    return false;
770254939Sdes}
771254939Sdes
772254939Sdesstatic ldns_rdf *
773301759Sdessearch(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt,
774301759Sdes    bool absolute, bool close_tcp) {
775254939Sdes    ldns_rdf *dname, **searchlist;
776254939Sdes    int i, n;
777254939Sdes
778301759Sdes    if (absolute && query(res, domain, pkt, close_tcp))
779254939Sdes        return domain;
780254939Sdes
781254939Sdes    if ((dname = ldns_resolver_domain(res)) != NULL) {
782254939Sdes        dname = safe_dname_cat_clone(domain, dname);
783301759Sdes        if (query(res, dname, pkt, close_tcp))
784254939Sdes            return dname;
785254939Sdes    }
786254939Sdes
787254939Sdes    searchlist = ldns_resolver_searchlist(res);
788254939Sdes    n = ldns_resolver_searchlist_count(res);
789254939Sdes    for (i = 0; i < n; i++) {
790254939Sdes        dname = safe_dname_cat_clone(domain, searchlist[i]);
791301759Sdes        if (query(res, dname, pkt, close_tcp))
792254939Sdes            return dname;
793254939Sdes    }
794254939Sdes
795301759Sdes    if (!absolute && query(res, domain, pkt, close_tcp))
796254939Sdes        return domain;
797254939Sdes
798254939Sdes    return NULL;
799254939Sdes}
800254939Sdes
801254939Sdesstatic void
802254939Sdesreport(ldns_resolver *res, ldns_rdf *domain, ldns_pkt *pkt) {
803254939Sdes    ldns_pkt_rcode rcode;
804254939Sdes
805254939Sdes    if (o_print_pkt_server) {
806254939Sdes        printf("Using domain server:\nName: %s\nAddress: ", o_server);
807254939Sdes        print_rdf(ldns_pkt_answerfrom(pkt));
808254939Sdes        printf("#%d\nAliases: \n\n", ldns_resolver_port(res));
809254939Sdes        o_print_pkt_server = false;
810254939Sdes    }
811254939Sdes    rcode = ldns_pkt_get_rcode(pkt);
812254939Sdes    if (rcode != LDNS_RCODE_NOERROR) {
813254939Sdes        printf("Host ");
814254939Sdes        print_rdf_nodot(domain);
815254939Sdes        printf(" not found: %d(", rcode);
816254939Sdes        print_rcode(rcode);
817254939Sdes        printf(")\n");
818254939Sdes    } else {
819254939Sdes        if (o_verbose) {
820254939Sdes            print_pkt_verbose(pkt);
821254939Sdes        } else {
822254939Sdes            print_pkt_short(pkt, o_print_rr_server);
823301759Sdes            if (o_mode == M_SINGLE_Q &&
824254939Sdes                ldns_rr_list_rr_count(ldns_pkt_answer(pkt)) == 0) {
825254939Sdes                print_rdf_nodot(domain);
826254939Sdes                printf(" has no ");
827254939Sdes                print_rr_type(o_rrtype);
828254939Sdes                printf(" record\n");
829254939Sdes            }
830254939Sdes        }
831254939Sdes    }
832254939Sdes    if (o_verbose)
833254939Sdes        print_received_line(res, pkt);
834254939Sdes}
835254939Sdes
836254939Sdesstatic bool
837254939Sdesdoquery(ldns_resolver *res, ldns_rdf *domain) {
838254939Sdes    ldns_pkt *pkt;
839254939Sdes    bool q;
840254939Sdes
841301759Sdes    q = query(res, domain, &pkt, true);
842254939Sdes    report(res, domain, pkt);
843254939Sdes    return q;
844254939Sdes}
845254939Sdes
846254939Sdesstatic bool
847254939Sdesdoquery_filtered(ldns_resolver *res, ldns_rdf *domain) {
848254939Sdes    ldns_pkt *pkt;
849254939Sdes    bool q;
850254939Sdes
851301759Sdes    q = query(res, domain, &pkt, true);
852254939Sdes    ldns_pkt_filter_answer(pkt, o_rrtype);
853254939Sdes    report(res, domain, pkt);
854254939Sdes    return q;
855254939Sdes}
856254939Sdes
857254939Sdesstatic bool
858254939Sdesdosearch(ldns_resolver *res, ldns_rdf *domain, bool absolute) {
859254939Sdes    ldns_pkt *pkt;
860254939Sdes    ldns_rdf *dname;
861254939Sdes
862301759Sdes    dname = search(res, domain, &pkt, absolute, true);
863254939Sdes    report(res, dname != NULL ? dname : domain, pkt);
864254939Sdes    return o_mode != M_DEFAULT_Q ? (dname != NULL) :
865254939Sdes        (dname != NULL) &&
866254939Sdes        (o_rrtype = LDNS_RR_TYPE_AAAA, doquery_filtered(res, dname)) &&
867254939Sdes        (o_rrtype = LDNS_RR_TYPE_MX, doquery_filtered(res, dname));
868254939Sdes}
869254939Sdes
870254939Sdesstatic bool
871301759Sdesdozonetransfer(ldns_resolver *res, ldns_rdf *domain, bool absolute) {
872301759Sdes    ldns_pkt *pkt, *nextpkt;
873254939Sdes    ldns_rdf *dname;
874254939Sdes    ldns_rr_type rrtype;
875301759Sdes    ldns_rr_list *rrl;
876301759Sdes    int i, nsoa = 0;
877254939Sdes
878254939Sdes    rrtype = o_rrtype;
879301759Sdes    o_rrtype = (o_mode == M_AXFR) ? LDNS_RR_TYPE_AXFR : LDNS_RR_TYPE_IXFR;
880301759Sdes    dname = search(res, domain, &pkt, absolute, false);
881301759Sdes
882301759Sdes    for (;;) {
883301759Sdes        rrl = ldns_pkt_answer(pkt);
884301759Sdes        for (i = ldns_rr_list_rr_count(rrl) - 1; i >= 0; i--) {
885301759Sdes            if (ldns_rr_get_type(ldns_rr_list_rr(rrl, i)) == LDNS_RR_TYPE_SOA)
886301759Sdes                nsoa++;
887301759Sdes        }
888301759Sdes        ldns_pkt_filter_answer(pkt, rrtype);
889301759Sdes        report(res, dname != NULL ? dname : domain, pkt);
890301759Sdes        if ((dname == NULL) ||
891301759Sdes                (ldns_pkt_get_rcode(pkt) != LDNS_RCODE_NOERROR)) {
892301759Sdes            printf("; Transfer failed.\n");
893301759Sdes            ldns_tcp_close(res);
894301759Sdes            return false;
895301759Sdes        }
896301759Sdes        if (nsoa >= 2) {
897301759Sdes            ldns_tcp_close(res);
898301759Sdes            return true;
899301759Sdes        }
900301759Sdes        if (ldns_tcp_read(&nextpkt, res) != LDNS_STATUS_OK) {
901301759Sdes            printf("; Transfer failed.\n");
902301759Sdes            return false;
903301759Sdes        }
904301759Sdes        ldns_pkt_set_answerfrom(nextpkt,
905301759Sdes                ldns_rdf_clone(ldns_pkt_answerfrom(pkt)));
906301759Sdes        ldns_pkt_free(pkt);
907301759Sdes        pkt = nextpkt;
908301759Sdes    }
909254939Sdes}
910254939Sdes
911254939Sdesstatic bool
912254939Sdesdosoa(ldns_resolver *res, ldns_rdf *domain, bool absolute) {
913254939Sdes    ldns_rr_list *answer, **nsaddrs;
914254939Sdes    ldns_rdf *dname, *addr;
915254939Sdes    ldns_pkt *pkt;
916254939Sdes    ldns_rr *rr;
917254939Sdes    size_t i, j, n, cnt;
918254939Sdes
919301759Sdes    if ((dname = search(res, domain, &pkt, absolute, true)) == NULL)
920254939Sdes        return false;
921254939Sdes
922254939Sdes    answer = ldns_pkt_answer(pkt);
923254939Sdes    cnt = ldns_rr_list_rr_count(answer);
924254939Sdes    nsaddrs = alloca(cnt*sizeof(*nsaddrs));
925254939Sdes    for (n = 0, i = 0; i < cnt; i++)
926254939Sdes        if ((addr = ldns_rr_ns_nsdname(ldns_rr_list_rr(answer, i))) != NULL)
927254939Sdes            nsaddrs[n++] = ldns_get_rr_list_addr_by_name(res,
928254939Sdes                addr, LDNS_RR_CLASS_IN, 0);
929254939Sdes
930254939Sdes    o_print_pkt_server = false;
931254939Sdes    o_recursive = false;
932254939Sdes    o_rrtype = LDNS_RR_TYPE_SOA;
933254939Sdes    for (i = 0; i < n; i++) {
934254939Sdes        cnt = ldns_rr_list_rr_count(nsaddrs[i]);
935254939Sdes        for (j = 0; j < cnt; j++) {
936254939Sdes            ldns_resolver_remove_nameservers(res);
937254939Sdes            rr = ldns_rr_list_rr(nsaddrs[i], j);
938255403Sdes            if ((ldns_resolver_ip6(res) == LDNS_RESOLV_INET &&
939301759Sdes                ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA) ||
940255403Sdes                (ldns_resolver_ip6(res) == LDNS_RESOLV_INET6 &&
941301759Sdes                ldns_rr_get_type(rr) == LDNS_RR_TYPE_A))
942254939Sdes                continue;
943254939Sdes            if (ldns_resolver_push_nameserver_rr(res, rr) == LDNS_STATUS_OK)
944254939Sdes                /* bind9-host queries for domain, not dname here */
945254939Sdes                doquery(res, dname);
946254939Sdes        }
947254939Sdes    }
948254939Sdes    return 0;
949254939Sdes}
950254939Sdes
951254939Sdesstatic void
952254939Sdesresolver_set_nameserver_hostname(ldns_resolver *res, const char *server) {
953254939Sdes    struct addrinfo hints, *ailist, *ai;
954254939Sdes    ldns_status status;
955254939Sdes    ldns_rdf *rdf;
956254939Sdes    int err;
957254939Sdes
958254939Sdes    memset(&hints, 0, sizeof hints);
959254939Sdes    switch (ldns_resolver_ip6(res)) {
960254939Sdes    case LDNS_RESOLV_INET: hints.ai_family = PF_INET; break;
961254939Sdes    case LDNS_RESOLV_INET6: hints.ai_family = PF_INET6; break;
962254939Sdes    default: hints.ai_family = PF_UNSPEC; break;
963254939Sdes    }
964254939Sdes    hints.ai_socktype = SOCK_STREAM;
965254939Sdes    do err = getaddrinfo(server, NULL, &hints, &ailist);
966254939Sdes    while (err == EAI_AGAIN);
967254939Sdes    if (err != 0)
968254939Sdes        die(1, "couldn't get address for '%s': %s", server, gai_strerror(err));
969254939Sdes    for (ai = ailist; ai != NULL; ai = ai->ai_next) {
970254939Sdes        if ((rdf = ldns_sockaddr_storage2rdf((void*)ai->ai_addr, NULL)) == NULL)
971254939Sdes            die(1, "couldn't allocate an rdf: %s",
972254939Sdes                ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR));
973254939Sdes        status = ldns_resolver_push_nameserver(res, rdf);
974254939Sdes        if (status != LDNS_STATUS_OK)
975254939Sdes            die(1, "couldn't push a nameserver address: %s",
976254939Sdes                ldns_get_errorstr_by_id(status));
977254939Sdes    }
978254939Sdes}
979254939Sdes
980254939Sdesstatic void
981254939Sdesresolver_set_nameserver_str(ldns_resolver *res, const char *server) {
982254939Sdes    ldns_rdf *addr;
983254939Sdes
984254939Sdes    ldns_resolver_remove_nameservers(res);
985254939Sdes    addr = ldns_rdf_new_addr_frm_str(server);
986254939Sdes    if (addr) {
987254939Sdes        if (ldns_resolver_push_nameserver(res, addr) != LDNS_STATUS_OK)
988254939Sdes            die(1, "couldn't push a nameserver address");
989254939Sdes    } else
990254939Sdes        resolver_set_nameserver_hostname(res, server);
991254939Sdes}
992254939Sdes
993254939Sdesint
994254939Sdesmain(int argc, char *argv[]) {
995254939Sdes    ldns_rdf *addr, *dname;
996254939Sdes    ldns_resolver *res;
997254939Sdes    ldns_status status;
998254939Sdes    struct timeval restimeout;
999254939Sdes
1000254939Sdes    parse_args(argc, argv);
1001254939Sdes
1002254939Sdes    status = ldns_resolver_new_default(&res);
1003254939Sdes    if (status != LDNS_STATUS_OK)
1004254939Sdes        die(1, "error creating resolver: %s", ldns_get_errorstr_by_id(status));
1005254939Sdes    if (ldns_resolver_nameserver_count(res) == 0)
1006254939Sdes        ldns_resolver_push_default_servers(res);
1007254939Sdes
1008254939Sdes    ldns_resolver_set_usevc(res, o_tcp);
1009254939Sdes    restimeout.tv_sec = o_timeout > 0 ? o_timeout :
1010254939Sdes        o_tcp ? DEFAULT_TCP_TIMEOUT : DEFAULT_UDP_TIMEOUT;
1011254939Sdes    restimeout.tv_usec = 0;
1012254939Sdes    ldns_resolver_set_timeout(res, restimeout);
1013254939Sdes    ldns_resolver_set_retry(res, o_retries+1);
1014254939Sdes    ldns_resolver_set_ip6(res, o_ipversion);
1015254939Sdes    ldns_resolver_set_defnames(res, false);
1016254939Sdes    ldns_resolver_set_fallback(res, false);
1017254939Sdes
1018254939Sdes    if (o_server)
1019254939Sdes        resolver_set_nameserver_str(res, o_server);
1020254939Sdes
1021254939Sdes    if (ldns_str2rdf_a(&addr, o_name) == LDNS_STATUS_OK) {
1022254939Sdes        dname = ldns_rdf_reverse_a(addr, "in-addr.arpa");
1023254939Sdes        if (dname == NULL)
1024254939Sdes            die(1, "can't reverse '%s': %s", o_name,
1025254939Sdes                ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR));
1026254939Sdes        o_mode = M_SINGLE_Q;
1027254939Sdes        o_rrtype = LDNS_RR_TYPE_PTR;
1028254939Sdes        return !doquery(res, dname);
1029254939Sdes    } else if (ldns_str2rdf_aaaa(&addr, o_name) == LDNS_STATUS_OK) {
1030254939Sdes        dname = ldns_rdf_reverse_aaaa(addr, o_ip6_int ? "ip6.int" : "ip6.arpa");
1031254939Sdes        if (dname == NULL)
1032254939Sdes            die(1, "can't reverse '%s': %s", o_name,
1033254939Sdes                ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR));
1034254939Sdes        o_mode = M_SINGLE_Q;
1035254939Sdes        o_rrtype = LDNS_RR_TYPE_PTR;
1036254939Sdes        return !doquery(res, dname);
1037254939Sdes    }
1038301759Sdes    return !(o_mode == M_SOA ? dosoa :
1039301759Sdes             o_mode == M_AXFR ? dozonetransfer :
1040301759Sdes             o_mode == M_IXFR ? dozonetransfer :
1041301759Sdes             dosearch)
1042254939Sdes        (res, safe_str2rdf_dname(o_name), ndots(o_name) >= o_ndots);
1043254939Sdes}
1044