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