xref: /freebsd/contrib/ldns-host/ldns-host.c (revision 3f68b24e10aeb1a1cd85f2d349da44138d52c501)
13fc9e2c3SDag-Erling Smørgrav /*-
23fc9e2c3SDag-Erling Smørgrav  * (c) Magerya Vitaly
33fc9e2c3SDag-Erling Smørgrav  *
43fc9e2c3SDag-Erling Smørgrav  * Copying and distribution of this file, with or without modification,
53fc9e2c3SDag-Erling Smørgrav  * are permitted in any medium without royalty provided the copyright
63fc9e2c3SDag-Erling Smørgrav  * notice and this notice are preserved. This file is offered as-is,
73fc9e2c3SDag-Erling Smørgrav  * without any warranty.
83fc9e2c3SDag-Erling Smørgrav  */
93fc9e2c3SDag-Erling Smørgrav 
10*3f68b24eSDag-Erling Smørgrav #include <ldns/ldns.h>
113fc9e2c3SDag-Erling Smørgrav #include <limits.h>
123fc9e2c3SDag-Erling Smørgrav #include <netdb.h>
13*3f68b24eSDag-Erling Smørgrav #include <netinet/in.h>
143fc9e2c3SDag-Erling Smørgrav #include <stdio.h>
153fc9e2c3SDag-Erling Smørgrav #include <stdlib.h>
163fc9e2c3SDag-Erling Smørgrav #include <unistd.h>
173fc9e2c3SDag-Erling Smørgrav 
183fc9e2c3SDag-Erling Smørgrav /* General utilities.
193fc9e2c3SDag-Erling Smørgrav  */
203fc9e2c3SDag-Erling Smørgrav 
213fc9e2c3SDag-Erling Smørgrav static char *progname;
223fc9e2c3SDag-Erling Smørgrav 
233fc9e2c3SDag-Erling Smørgrav #define countof(array) (sizeof(array)/sizeof(*(array)))
243fc9e2c3SDag-Erling Smørgrav 
253fc9e2c3SDag-Erling Smørgrav static void
263fc9e2c3SDag-Erling Smørgrav die(int code, const char *fmt, ...) {
273fc9e2c3SDag-Erling Smørgrav     va_list args;
283fc9e2c3SDag-Erling Smørgrav 
293fc9e2c3SDag-Erling Smørgrav     va_start(args, fmt);
303fc9e2c3SDag-Erling Smørgrav     fprintf(stderr, "%s: ", progname);
313fc9e2c3SDag-Erling Smørgrav     vfprintf(stderr, fmt, args);
323fc9e2c3SDag-Erling Smørgrav     fprintf(stderr, "\n");
333fc9e2c3SDag-Erling Smørgrav     va_end(args);
343fc9e2c3SDag-Erling Smørgrav     exit(code);
353fc9e2c3SDag-Erling Smørgrav }
363fc9e2c3SDag-Erling Smørgrav 
373fc9e2c3SDag-Erling Smørgrav static int
383fc9e2c3SDag-Erling Smørgrav ndots(const char *name) {
393fc9e2c3SDag-Erling Smørgrav     int n;
403fc9e2c3SDag-Erling Smørgrav 
413fc9e2c3SDag-Erling Smørgrav     for (n = 0; (name = strchr(name, '.')); n++, name++);
423fc9e2c3SDag-Erling Smørgrav     return n;
433fc9e2c3SDag-Erling Smørgrav }
443fc9e2c3SDag-Erling Smørgrav 
453fc9e2c3SDag-Erling Smørgrav /* General LDNS-specific utilities.
463fc9e2c3SDag-Erling Smørgrav  */
473fc9e2c3SDag-Erling Smørgrav 
483fc9e2c3SDag-Erling Smørgrav static ldns_status
493fc9e2c3SDag-Erling Smørgrav ldns_resolver_new_default(ldns_resolver **res) {
503fc9e2c3SDag-Erling Smørgrav     if (ldns_resolver_new_frm_file(res, NULL) == LDNS_STATUS_OK ||
513fc9e2c3SDag-Erling Smørgrav         (*res = ldns_resolver_new()) != NULL)
523fc9e2c3SDag-Erling Smørgrav         return LDNS_STATUS_OK;
533fc9e2c3SDag-Erling Smørgrav     return LDNS_STATUS_MEM_ERR;
543fc9e2c3SDag-Erling Smørgrav }
553fc9e2c3SDag-Erling Smørgrav 
563fc9e2c3SDag-Erling Smørgrav static ldns_status
573fc9e2c3SDag-Erling Smørgrav ldns_resolver_push_default_servers(ldns_resolver *res) {
583fc9e2c3SDag-Erling Smørgrav     ldns_status status;
593fc9e2c3SDag-Erling Smørgrav     ldns_rdf *addr;
603fc9e2c3SDag-Erling Smørgrav 
613fc9e2c3SDag-Erling Smørgrav     if ((status = ldns_str2rdf_a(&addr, "127.0.0.1")) != LDNS_STATUS_OK ||
623fc9e2c3SDag-Erling Smørgrav         (status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK)
633fc9e2c3SDag-Erling Smørgrav         return ldns_rdf_deep_free(addr), status;
643fc9e2c3SDag-Erling Smørgrav     ldns_rdf_deep_free(addr);
653fc9e2c3SDag-Erling Smørgrav     if ((status = ldns_str2rdf_aaaa(&addr, "::1")) != LDNS_STATUS_OK ||
663fc9e2c3SDag-Erling Smørgrav         (status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK)
673fc9e2c3SDag-Erling Smørgrav         return ldns_rdf_deep_free(addr), status;
683fc9e2c3SDag-Erling Smørgrav     ldns_rdf_deep_free(addr);
693fc9e2c3SDag-Erling Smørgrav     return LDNS_STATUS_OK;
703fc9e2c3SDag-Erling Smørgrav }
713fc9e2c3SDag-Erling Smørgrav 
723fc9e2c3SDag-Erling Smørgrav static ldns_rdf *
733fc9e2c3SDag-Erling Smørgrav ldns_rdf_new_addr_frm_str(const char *str) {
743fc9e2c3SDag-Erling Smørgrav     ldns_rdf *addr;
753fc9e2c3SDag-Erling Smørgrav 
763fc9e2c3SDag-Erling Smørgrav     if ((addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, str)) == NULL)
773fc9e2c3SDag-Erling Smørgrav         addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, str);
783fc9e2c3SDag-Erling Smørgrav     return addr;
793fc9e2c3SDag-Erling Smørgrav }
803fc9e2c3SDag-Erling Smørgrav 
813fc9e2c3SDag-Erling Smørgrav static void
823fc9e2c3SDag-Erling Smørgrav ldns_resolver_remove_nameservers(ldns_resolver *res) {
833fc9e2c3SDag-Erling Smørgrav     while (ldns_resolver_nameserver_count(res) > 0)
843fc9e2c3SDag-Erling Smørgrav         ldns_rdf_deep_free(ldns_resolver_pop_nameserver(res));
853fc9e2c3SDag-Erling Smørgrav }
863fc9e2c3SDag-Erling Smørgrav 
873fc9e2c3SDag-Erling Smørgrav static ldns_rdf *
883fc9e2c3SDag-Erling Smørgrav ldns_rdf_reverse_a(ldns_rdf *addr, const char *base) {
893fc9e2c3SDag-Erling Smørgrav     char *buf;
903fc9e2c3SDag-Erling Smørgrav     int i, len;
913fc9e2c3SDag-Erling Smørgrav 
923fc9e2c3SDag-Erling Smørgrav     len = strlen(base);
933fc9e2c3SDag-Erling Smørgrav     buf = alloca(LDNS_IP4ADDRLEN*4 + len + 1);
943fc9e2c3SDag-Erling Smørgrav     for (len = i = 0; i < LDNS_IP4ADDRLEN; i++)
953fc9e2c3SDag-Erling Smørgrav         len += sprintf(&buf[len], "%d.",
963fc9e2c3SDag-Erling Smørgrav             (int)ldns_rdf_data(addr)[LDNS_IP4ADDRLEN - i - 1]);
973fc9e2c3SDag-Erling Smørgrav     sprintf(&buf[len], "%s", base);
983fc9e2c3SDag-Erling Smørgrav     return ldns_dname_new_frm_str(buf);
993fc9e2c3SDag-Erling Smørgrav }
1003fc9e2c3SDag-Erling Smørgrav 
1013fc9e2c3SDag-Erling Smørgrav static ldns_rdf *
1023fc9e2c3SDag-Erling Smørgrav ldns_rdf_reverse_aaaa(ldns_rdf *addr, const char *base) {
1033fc9e2c3SDag-Erling Smørgrav     char *buf;
1043fc9e2c3SDag-Erling Smørgrav     int i, len;
1053fc9e2c3SDag-Erling Smørgrav 
1063fc9e2c3SDag-Erling Smørgrav     len = strlen(base);
1073fc9e2c3SDag-Erling Smørgrav     buf = alloca(LDNS_IP6ADDRLEN*4 + len + 1);
1083fc9e2c3SDag-Erling Smørgrav     for (i = 0; i < LDNS_IP6ADDRLEN; i++) {
1093fc9e2c3SDag-Erling Smørgrav         uint8_t byte = ldns_rdf_data(addr)[LDNS_IP6ADDRLEN - i - 1];
1103fc9e2c3SDag-Erling Smørgrav         sprintf(&buf[i*4], "%x.%x.", byte & 0x0F, byte >> 4);
1113fc9e2c3SDag-Erling Smørgrav     }
1123fc9e2c3SDag-Erling Smørgrav     sprintf(&buf[LDNS_IP6ADDRLEN*4], "%s", base);
1133fc9e2c3SDag-Erling Smørgrav     return ldns_dname_new_frm_str(buf);
1143fc9e2c3SDag-Erling Smørgrav }
1153fc9e2c3SDag-Erling Smørgrav 
1163fc9e2c3SDag-Erling Smørgrav static ldns_status
1173fc9e2c3SDag-Erling Smørgrav ldns_pkt_push_rr_soa(ldns_pkt *pkt, ldns_pkt_section sec,
1183fc9e2c3SDag-Erling Smørgrav     const ldns_rdf *name, ldns_rr_class c, uint32_t serial) {
1193fc9e2c3SDag-Erling Smørgrav     ldns_rdf *rdf;
1203fc9e2c3SDag-Erling Smørgrav     ldns_rr *rr;
1213fc9e2c3SDag-Erling Smørgrav     uint32_t n;
1223fc9e2c3SDag-Erling Smørgrav 
1233fc9e2c3SDag-Erling Smørgrav     if ((rr = ldns_rr_new_frm_type(LDNS_RR_TYPE_SOA)) == NULL)
1243fc9e2c3SDag-Erling Smørgrav         return LDNS_STATUS_MEM_ERR;
1253fc9e2c3SDag-Erling Smørgrav     ldns_rr_set_class(rr, c);
1263fc9e2c3SDag-Erling Smørgrav     ldns_rr_set_owner(rr, ldns_rdf_clone(name));
1273fc9e2c3SDag-Erling Smørgrav     ldns_rr_set_ttl(rr, 0);
1283fc9e2c3SDag-Erling Smørgrav 
1293fc9e2c3SDag-Erling Smørgrav     n = 0;
1303fc9e2c3SDag-Erling Smørgrav     if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, 1, &n)) == NULL)
1313fc9e2c3SDag-Erling Smørgrav         goto memerr;
1323fc9e2c3SDag-Erling Smørgrav     ldns_rr_set_rdf(rr, rdf, 0);
1333fc9e2c3SDag-Erling Smørgrav     ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 1);
1343fc9e2c3SDag-Erling Smørgrav 
1353fc9e2c3SDag-Erling Smørgrav     n = htonl(serial);
1363fc9e2c3SDag-Erling Smørgrav     if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT32, 4, &n)) == NULL)
1373fc9e2c3SDag-Erling Smørgrav         goto memerr;
1383fc9e2c3SDag-Erling Smørgrav     ldns_rr_set_rdf(rr, rdf, 2);
1393fc9e2c3SDag-Erling Smørgrav 
1403fc9e2c3SDag-Erling Smørgrav     n = 0;
1413fc9e2c3SDag-Erling Smørgrav     if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_PERIOD, 4, &n)) == NULL)
1423fc9e2c3SDag-Erling Smørgrav         goto memerr;
1433fc9e2c3SDag-Erling Smørgrav     ldns_rr_set_rdf(rr, rdf, 3);
1443fc9e2c3SDag-Erling Smørgrav     ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 4);
1453fc9e2c3SDag-Erling Smørgrav     ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 5);
1463fc9e2c3SDag-Erling Smørgrav     ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 6);
1473fc9e2c3SDag-Erling Smørgrav 
1483fc9e2c3SDag-Erling Smørgrav     if (ldns_rr_rdf(rr, 1) == NULL || ldns_rr_rdf(rr, 4) == NULL ||
1493fc9e2c3SDag-Erling Smørgrav         ldns_rr_rdf(rr, 5) == NULL || ldns_rr_rdf(rr, 6) == NULL ||
1503fc9e2c3SDag-Erling Smørgrav         !ldns_pkt_push_rr(pkt, sec, rr))
1513fc9e2c3SDag-Erling Smørgrav         goto memerr;
1523fc9e2c3SDag-Erling Smørgrav     return LDNS_STATUS_OK;
1533fc9e2c3SDag-Erling Smørgrav 
1543fc9e2c3SDag-Erling Smørgrav memerr:
1553fc9e2c3SDag-Erling Smørgrav     ldns_rr_free(rr);
1563fc9e2c3SDag-Erling Smørgrav     return LDNS_STATUS_MEM_ERR;
1573fc9e2c3SDag-Erling Smørgrav }
1583fc9e2c3SDag-Erling Smørgrav 
1593fc9e2c3SDag-Erling Smørgrav static ldns_status
160*3f68b24eSDag-Erling Smørgrav ldns_tcp_start(ldns_resolver *res, ldns_pkt *qpkt, int nameserver) {
161*3f68b24eSDag-Erling Smørgrav     /* This routine is based on ldns_axfr_start, with the major
162*3f68b24eSDag-Erling Smørgrav      * difference in that it takes a query packet explicitly.
163*3f68b24eSDag-Erling Smørgrav      */
164*3f68b24eSDag-Erling Smørgrav     struct sockaddr_storage *ns = NULL;
165*3f68b24eSDag-Erling Smørgrav     size_t ns_len = 0;
166*3f68b24eSDag-Erling Smørgrav     ldns_buffer *qbuf = NULL;
167*3f68b24eSDag-Erling Smørgrav     ldns_status status;
168*3f68b24eSDag-Erling Smørgrav 
169*3f68b24eSDag-Erling Smørgrav     ns = ldns_rdf2native_sockaddr_storage(
170*3f68b24eSDag-Erling Smørgrav             res->_nameservers[nameserver], ldns_resolver_port(res), &ns_len);
171*3f68b24eSDag-Erling Smørgrav     if (ns == NULL) {
172*3f68b24eSDag-Erling Smørgrav         status = LDNS_STATUS_MEM_ERR;
173*3f68b24eSDag-Erling Smørgrav         goto error;
174*3f68b24eSDag-Erling Smørgrav     }
175*3f68b24eSDag-Erling Smørgrav 
176*3f68b24eSDag-Erling Smørgrav     res->_socket = ldns_tcp_connect(
177*3f68b24eSDag-Erling Smørgrav             ns, (socklen_t)ns_len, ldns_resolver_timeout(res));
178*3f68b24eSDag-Erling Smørgrav     if (res->_socket <= 0) {
179*3f68b24eSDag-Erling Smørgrav         status = LDNS_STATUS_ADDRESS_ERR;
180*3f68b24eSDag-Erling Smørgrav         goto error;
181*3f68b24eSDag-Erling Smørgrav     }
182*3f68b24eSDag-Erling Smørgrav 
183*3f68b24eSDag-Erling Smørgrav     qbuf = ldns_buffer_new(LDNS_MAX_PACKETLEN);
184*3f68b24eSDag-Erling Smørgrav     if (qbuf == NULL) {
185*3f68b24eSDag-Erling Smørgrav         status = LDNS_STATUS_MEM_ERR;
186*3f68b24eSDag-Erling Smørgrav         goto error;
187*3f68b24eSDag-Erling Smørgrav     }
188*3f68b24eSDag-Erling Smørgrav 
189*3f68b24eSDag-Erling Smørgrav     status = ldns_pkt2buffer_wire(qbuf, qpkt);
190*3f68b24eSDag-Erling Smørgrav     if (status != LDNS_STATUS_OK)
191*3f68b24eSDag-Erling Smørgrav         goto error;
192*3f68b24eSDag-Erling Smørgrav 
193*3f68b24eSDag-Erling Smørgrav     if (ldns_tcp_send_query(qbuf, res->_socket, ns, (socklen_t)ns_len) == 0) {
194*3f68b24eSDag-Erling Smørgrav         status = LDNS_STATUS_NETWORK_ERR;
195*3f68b24eSDag-Erling Smørgrav         goto error;
196*3f68b24eSDag-Erling Smørgrav     }
197*3f68b24eSDag-Erling Smørgrav 
198*3f68b24eSDag-Erling Smørgrav     ldns_buffer_free(qbuf);
199*3f68b24eSDag-Erling Smørgrav     free(ns);
200*3f68b24eSDag-Erling Smørgrav     return LDNS_STATUS_OK;
201*3f68b24eSDag-Erling Smørgrav 
202*3f68b24eSDag-Erling Smørgrav error:
203*3f68b24eSDag-Erling Smørgrav     ldns_buffer_free(qbuf);
204*3f68b24eSDag-Erling Smørgrav     free(ns);
205*3f68b24eSDag-Erling Smørgrav     if (res->_socket > 0) {
206*3f68b24eSDag-Erling Smørgrav         close(res->_socket);
207*3f68b24eSDag-Erling Smørgrav         res->_socket = 0;
208*3f68b24eSDag-Erling Smørgrav     }
209*3f68b24eSDag-Erling Smørgrav     return status;
210*3f68b24eSDag-Erling Smørgrav }
211*3f68b24eSDag-Erling Smørgrav 
212*3f68b24eSDag-Erling Smørgrav static ldns_status
213*3f68b24eSDag-Erling Smørgrav ldns_tcp_read(ldns_pkt **answer, ldns_resolver *res) {
214*3f68b24eSDag-Erling Smørgrav     ldns_status status;
215*3f68b24eSDag-Erling Smørgrav     struct timeval t1, t2;
216*3f68b24eSDag-Erling Smørgrav     uint8_t *data;
217*3f68b24eSDag-Erling Smørgrav     size_t size;
218*3f68b24eSDag-Erling Smørgrav 
219*3f68b24eSDag-Erling Smørgrav     if (res->_socket <= 0)
220*3f68b24eSDag-Erling Smørgrav         return LDNS_STATUS_ERR;
221*3f68b24eSDag-Erling Smørgrav 
222*3f68b24eSDag-Erling Smørgrav     gettimeofday(&t1, NULL);
223*3f68b24eSDag-Erling Smørgrav     data = ldns_tcp_read_wire_timeout(
224*3f68b24eSDag-Erling Smørgrav             res->_socket, &size, ldns_resolver_timeout(res));
225*3f68b24eSDag-Erling Smørgrav     if (data == NULL)
226*3f68b24eSDag-Erling Smørgrav         goto error;
227*3f68b24eSDag-Erling Smørgrav 
228*3f68b24eSDag-Erling Smørgrav     status = ldns_wire2pkt(answer, data, size);
229*3f68b24eSDag-Erling Smørgrav     free(data);
230*3f68b24eSDag-Erling Smørgrav     if (status != LDNS_STATUS_OK)
231*3f68b24eSDag-Erling Smørgrav         goto error;
232*3f68b24eSDag-Erling Smørgrav 
233*3f68b24eSDag-Erling Smørgrav     gettimeofday(&t2, NULL);
234*3f68b24eSDag-Erling Smørgrav     ldns_pkt_set_querytime(*answer,
235*3f68b24eSDag-Erling Smørgrav             (uint32_t)((t2.tv_sec - t1.tv_sec)*1000) +
236*3f68b24eSDag-Erling Smørgrav                 (t2.tv_usec - t1.tv_usec)/1000);
237*3f68b24eSDag-Erling Smørgrav     ldns_pkt_set_timestamp(*answer, t2);
238*3f68b24eSDag-Erling Smørgrav     return status;
239*3f68b24eSDag-Erling Smørgrav 
240*3f68b24eSDag-Erling Smørgrav error:
241*3f68b24eSDag-Erling Smørgrav     close(res->_socket);
242*3f68b24eSDag-Erling Smørgrav     res->_socket = 0;
243*3f68b24eSDag-Erling Smørgrav     return LDNS_STATUS_ERR;
244*3f68b24eSDag-Erling Smørgrav }
245*3f68b24eSDag-Erling Smørgrav 
246*3f68b24eSDag-Erling Smørgrav static void
247*3f68b24eSDag-Erling Smørgrav ldns_tcp_close(ldns_resolver *res) {
248*3f68b24eSDag-Erling Smørgrav     if (res->_socket > 0) {
249*3f68b24eSDag-Erling Smørgrav         close(res->_socket);
250*3f68b24eSDag-Erling Smørgrav         res->_socket = 0;
251*3f68b24eSDag-Erling Smørgrav     }
252*3f68b24eSDag-Erling Smørgrav }
253*3f68b24eSDag-Erling Smørgrav 
254*3f68b24eSDag-Erling Smørgrav static ldns_status
2553fc9e2c3SDag-Erling Smørgrav ldns_resolver_send_to(ldns_pkt **answer, ldns_resolver *res,
2563fc9e2c3SDag-Erling Smørgrav     const ldns_rdf *name, ldns_rr_type t, ldns_rr_class c,
257*3f68b24eSDag-Erling Smørgrav     uint16_t flags, uint32_t ixfr_serial, int nameserver,
258*3f68b24eSDag-Erling Smørgrav     bool close_tcp) {
259*3f68b24eSDag-Erling Smørgrav     ldns_status status = LDNS_STATUS_OK;
2603fc9e2c3SDag-Erling Smørgrav     ldns_pkt *qpkt;
261*3f68b24eSDag-Erling Smørgrav     struct timeval now;
2623fc9e2c3SDag-Erling Smørgrav 
2633fc9e2c3SDag-Erling Smørgrav     int nscnt = ldns_resolver_nameserver_count(res);
2643fc9e2c3SDag-Erling Smørgrav     ldns_rdf **ns = ldns_resolver_nameservers(res);
2653fc9e2c3SDag-Erling Smørgrav     size_t *rtt = ldns_resolver_rtt(res);
2663fc9e2c3SDag-Erling Smørgrav 
2673fc9e2c3SDag-Erling Smørgrav     ldns_resolver_set_nameservers(res, &ns[nameserver]);
2683fc9e2c3SDag-Erling Smørgrav     ldns_resolver_set_rtt(res, &rtt[nameserver]);
2693fc9e2c3SDag-Erling Smørgrav     ldns_resolver_set_nameserver_count(res, 1);
2703fc9e2c3SDag-Erling Smørgrav 
271*3f68b24eSDag-Erling Smørgrav     /* The next fragment should have been a call to
272*3f68b24eSDag-Erling Smørgrav      * ldns_resolver_prepare_query_pkt(), but starting with ldns
273*3f68b24eSDag-Erling Smørgrav      * version 1.6.17 that function tries to add it's own SOA
274*3f68b24eSDag-Erling Smørgrav      * records when rr_type is LDNS_RR_TYPE_IXFR, and we don't
275*3f68b24eSDag-Erling Smørgrav      * want that.
276*3f68b24eSDag-Erling Smørgrav      */
277*3f68b24eSDag-Erling Smørgrav     qpkt = ldns_pkt_query_new(ldns_rdf_clone(name), t, c, flags);
278*3f68b24eSDag-Erling Smørgrav     if (qpkt == NULL) {
279*3f68b24eSDag-Erling Smørgrav         status = LDNS_STATUS_ERR;
280*3f68b24eSDag-Erling Smørgrav         goto done;
281*3f68b24eSDag-Erling Smørgrav     }
282*3f68b24eSDag-Erling Smørgrav     now.tv_sec = time(NULL);
283*3f68b24eSDag-Erling Smørgrav     now.tv_usec = 0;
284*3f68b24eSDag-Erling Smørgrav     ldns_pkt_set_timestamp(qpkt, now);
285*3f68b24eSDag-Erling Smørgrav     ldns_pkt_set_random_id(qpkt);
286*3f68b24eSDag-Erling Smørgrav 
287*3f68b24eSDag-Erling Smørgrav     if (t == LDNS_RR_TYPE_IXFR) {
2883fc9e2c3SDag-Erling Smørgrav         status = ldns_pkt_push_rr_soa(qpkt,
2893fc9e2c3SDag-Erling Smørgrav             LDNS_SECTION_AUTHORITY, name, c, ixfr_serial);
290*3f68b24eSDag-Erling Smørgrav         if (status != LDNS_STATUS_OK) goto done;
291*3f68b24eSDag-Erling Smørgrav     }
292*3f68b24eSDag-Erling Smørgrav     if (close_tcp) {
2933fc9e2c3SDag-Erling Smørgrav         status = ldns_resolver_send_pkt(answer, res, qpkt);
294*3f68b24eSDag-Erling Smørgrav     } else {
295*3f68b24eSDag-Erling Smørgrav         status = ldns_tcp_start(res, qpkt, 0);
296*3f68b24eSDag-Erling Smørgrav         if (status != LDNS_STATUS_OK) goto done;
297*3f68b24eSDag-Erling Smørgrav         status = ldns_tcp_read(answer, res);
298*3f68b24eSDag-Erling Smørgrav         if (status != LDNS_STATUS_OK) goto done;
299*3f68b24eSDag-Erling Smørgrav         ldns_pkt_set_answerfrom(*answer, ldns_rdf_clone(ns[0]));
300*3f68b24eSDag-Erling Smørgrav     }
301*3f68b24eSDag-Erling Smørgrav 
302*3f68b24eSDag-Erling Smørgrav done:
3033fc9e2c3SDag-Erling Smørgrav     ldns_pkt_free(qpkt);
3043fc9e2c3SDag-Erling Smørgrav 
3053fc9e2c3SDag-Erling Smørgrav     ldns_resolver_set_nameservers(res, ns);
3063fc9e2c3SDag-Erling Smørgrav     ldns_resolver_set_rtt(res, rtt);
3073fc9e2c3SDag-Erling Smørgrav     ldns_resolver_set_nameserver_count(res, nscnt);
3083fc9e2c3SDag-Erling Smørgrav     return status;
3093fc9e2c3SDag-Erling Smørgrav }
3103fc9e2c3SDag-Erling Smørgrav 
3113fc9e2c3SDag-Erling Smørgrav static void
3123fc9e2c3SDag-Erling Smørgrav ldns_pkt_filter_answer(ldns_pkt *pkt, ldns_rr_type type) {
3133fc9e2c3SDag-Erling Smørgrav     int i, j, cnt;
3143fc9e2c3SDag-Erling Smørgrav     ldns_rr_list *rrlist;
3153fc9e2c3SDag-Erling Smørgrav     ldns_rr *rr;
3163fc9e2c3SDag-Erling Smørgrav     ldns_rr_type rrtype;
3173fc9e2c3SDag-Erling Smørgrav 
3183fc9e2c3SDag-Erling Smørgrav     rrlist = ldns_pkt_answer(pkt);
3193fc9e2c3SDag-Erling Smørgrav     cnt = ldns_rr_list_rr_count(rrlist);
3203fc9e2c3SDag-Erling Smørgrav     for (i = j = 0; i < cnt; i++) {
3213fc9e2c3SDag-Erling Smørgrav         rr = ldns_rr_list_rr(rrlist, i);
3223fc9e2c3SDag-Erling Smørgrav         rrtype = ldns_rr_get_type(rr);
3233fc9e2c3SDag-Erling Smørgrav         if (type == LDNS_RR_TYPE_ANY ||
3243fc9e2c3SDag-Erling Smørgrav             type == rrtype ||
3253fc9e2c3SDag-Erling Smørgrav             (type == LDNS_RR_TYPE_AXFR &&
3263fc9e2c3SDag-Erling Smørgrav                 (rrtype == LDNS_RR_TYPE_A ||
3273fc9e2c3SDag-Erling Smørgrav                 rrtype == LDNS_RR_TYPE_AAAA ||
3283fc9e2c3SDag-Erling Smørgrav                 rrtype == LDNS_RR_TYPE_NS ||
3293fc9e2c3SDag-Erling Smørgrav                 rrtype == LDNS_RR_TYPE_PTR)))
3303fc9e2c3SDag-Erling Smørgrav             ldns_rr_list_set_rr(rrlist, rr, j++);
3313fc9e2c3SDag-Erling Smørgrav     }
3323fc9e2c3SDag-Erling Smørgrav     ldns_rr_list_set_rr_count(rrlist, j);
3333fc9e2c3SDag-Erling Smørgrav }
3343fc9e2c3SDag-Erling Smørgrav 
3353fc9e2c3SDag-Erling Smørgrav /* Packet content printing.
3363fc9e2c3SDag-Erling Smørgrav  */
3373fc9e2c3SDag-Erling Smørgrav 
3383fc9e2c3SDag-Erling Smørgrav static struct {
3393fc9e2c3SDag-Erling Smørgrav     ldns_rr_type type;
3403fc9e2c3SDag-Erling Smørgrav     const char *text;
3413fc9e2c3SDag-Erling Smørgrav } rr_types[] = {
3423fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_A,        "has address"},
3433fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_NS,       "name server"},
3443fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_CNAME,    "is an alias for"},
3453fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_WKS,      "has well known services"},
3463fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_PTR,      "domain name pointer"},
3473fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_HINFO,    "host information"},
3483fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_MX,       "mail is handled by"},
3493fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_TXT,      "descriptive text"},
3503fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_X25,      "x25 address"},
3513fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_ISDN,     "ISDN address"},
3523fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_SIG,      "has signature"},
3533fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_KEY,      "has key"},
3543fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_AAAA,     "has IPv6 address"},
3553fc9e2c3SDag-Erling Smørgrav     {LDNS_RR_TYPE_LOC,      "location"},
3563fc9e2c3SDag-Erling Smørgrav };
3573fc9e2c3SDag-Erling Smørgrav 
3583fc9e2c3SDag-Erling Smørgrav static void
3593fc9e2c3SDag-Erling Smørgrav print_opcode(ldns_pkt_opcode opcode) {
3603fc9e2c3SDag-Erling Smørgrav     ldns_lookup_table *lt = ldns_lookup_by_id(ldns_opcodes, opcode);
3613fc9e2c3SDag-Erling Smørgrav 
3623fc9e2c3SDag-Erling Smørgrav     if (lt && lt->name)
3633fc9e2c3SDag-Erling Smørgrav         printf("%s", lt->name);
3643fc9e2c3SDag-Erling Smørgrav     else
3653fc9e2c3SDag-Erling Smørgrav         printf("RESERVED%d", opcode);
3663fc9e2c3SDag-Erling Smørgrav }
3673fc9e2c3SDag-Erling Smørgrav 
3683fc9e2c3SDag-Erling Smørgrav static void
3693fc9e2c3SDag-Erling Smørgrav print_rcode(uint8_t rcode) {
3703fc9e2c3SDag-Erling Smørgrav     ldns_lookup_table *lt = ldns_lookup_by_id(ldns_rcodes, rcode);
3713fc9e2c3SDag-Erling Smørgrav 
3723fc9e2c3SDag-Erling Smørgrav     if (lt && lt->name)
3733fc9e2c3SDag-Erling Smørgrav         printf("%s", lt->name);
3743fc9e2c3SDag-Erling Smørgrav     else
3753fc9e2c3SDag-Erling Smørgrav         printf("RESERVED%d", rcode);
3763fc9e2c3SDag-Erling Smørgrav }
3773fc9e2c3SDag-Erling Smørgrav 
3783fc9e2c3SDag-Erling Smørgrav static int
3793fc9e2c3SDag-Erling Smørgrav print_rr_type(ldns_rr_type type) {
3803fc9e2c3SDag-Erling Smørgrav     char *str;
3813fc9e2c3SDag-Erling Smørgrav     int n;
3823fc9e2c3SDag-Erling Smørgrav 
3833fc9e2c3SDag-Erling Smørgrav     str = ldns_rr_type2str(type);
3843fc9e2c3SDag-Erling Smørgrav     n = printf("%s", str);
3853fc9e2c3SDag-Erling Smørgrav     free(str);
3863fc9e2c3SDag-Erling Smørgrav     return n;
3873fc9e2c3SDag-Erling Smørgrav }
3883fc9e2c3SDag-Erling Smørgrav 
3893fc9e2c3SDag-Erling Smørgrav static int
3903fc9e2c3SDag-Erling Smørgrav print_rr_class(ldns_rr_class cls) {
3913fc9e2c3SDag-Erling Smørgrav     char *str;
3923fc9e2c3SDag-Erling Smørgrav     int n;
3933fc9e2c3SDag-Erling Smørgrav 
3943fc9e2c3SDag-Erling Smørgrav     str = ldns_rr_class2str(cls);
3953fc9e2c3SDag-Erling Smørgrav     n = printf("%s", str);
3963fc9e2c3SDag-Erling Smørgrav     free(str);
3973fc9e2c3SDag-Erling Smørgrav     return n;
3983fc9e2c3SDag-Erling Smørgrav }
3993fc9e2c3SDag-Erling Smørgrav 
4003fc9e2c3SDag-Erling Smørgrav static int
4013fc9e2c3SDag-Erling Smørgrav print_rdf(ldns_rdf *rdf) {
4023fc9e2c3SDag-Erling Smørgrav     char *str;
4033fc9e2c3SDag-Erling Smørgrav     int n;
4043fc9e2c3SDag-Erling Smørgrav 
4053fc9e2c3SDag-Erling Smørgrav     str = ldns_rdf2str(rdf);
4063fc9e2c3SDag-Erling Smørgrav     n = printf("%s", str);
4073fc9e2c3SDag-Erling Smørgrav     free(str);
4083fc9e2c3SDag-Erling Smørgrav     return n;
4093fc9e2c3SDag-Erling Smørgrav }
4103fc9e2c3SDag-Erling Smørgrav 
4113fc9e2c3SDag-Erling Smørgrav static int
4123fc9e2c3SDag-Erling Smørgrav print_rdf_nodot(ldns_rdf *rdf) {
4133fc9e2c3SDag-Erling Smørgrav     char *str;
4143fc9e2c3SDag-Erling Smørgrav     int len, n;
4153fc9e2c3SDag-Erling Smørgrav 
4163fc9e2c3SDag-Erling Smørgrav     str = ldns_rdf2str(rdf);
4173fc9e2c3SDag-Erling Smørgrav     len = strlen(str);
4183fc9e2c3SDag-Erling Smørgrav     n = printf("%.*s", str[len-1] == '.' ? len-1 : len, str);
4193fc9e2c3SDag-Erling Smørgrav     free(str);
4203fc9e2c3SDag-Erling Smørgrav     return n;
4213fc9e2c3SDag-Erling Smørgrav }
4223fc9e2c3SDag-Erling Smørgrav 
4233fc9e2c3SDag-Erling Smørgrav static int
4243fc9e2c3SDag-Erling Smørgrav print_padding(int fromcol, int tocol) {
4253fc9e2c3SDag-Erling Smørgrav     int col = fromcol, nextcol = fromcol + 8 - fromcol%8;
4263fc9e2c3SDag-Erling Smørgrav 
4273fc9e2c3SDag-Erling Smørgrav     if (fromcol + 1 > tocol) tocol = fromcol + 1;
4283fc9e2c3SDag-Erling Smørgrav     for (; nextcol <= tocol; col = nextcol, nextcol += 8)
4293fc9e2c3SDag-Erling Smørgrav         printf("\t");
4303fc9e2c3SDag-Erling Smørgrav     for (; col < tocol; col++)
4313fc9e2c3SDag-Erling Smørgrav         printf(" ");
4323fc9e2c3SDag-Erling Smørgrav     return col - fromcol;
4333fc9e2c3SDag-Erling Smørgrav }
4343fc9e2c3SDag-Erling Smørgrav 
4353fc9e2c3SDag-Erling Smørgrav static void
4363fc9e2c3SDag-Erling Smørgrav print_rr_verbose(ldns_rr *rr) {
4373fc9e2c3SDag-Erling Smørgrav     bool isq = ldns_rr_is_question(rr);
4383fc9e2c3SDag-Erling Smørgrav     int rdcnt = ldns_rr_rd_count(rr);
4393fc9e2c3SDag-Erling Smørgrav     int i, n;
4403fc9e2c3SDag-Erling Smørgrav 
4413fc9e2c3SDag-Erling Smørgrav     /* bind9-host does not count the initial ';' here */
4423fc9e2c3SDag-Erling Smørgrav     n = isq ? printf(";") : 0;
4433fc9e2c3SDag-Erling Smørgrav     n = print_rdf(ldns_rr_owner(rr));
4443fc9e2c3SDag-Erling Smørgrav     if (!isq) {
4453fc9e2c3SDag-Erling Smørgrav         n += print_padding(n, 24);
4463fc9e2c3SDag-Erling Smørgrav         n += printf("%d", ldns_rr_ttl(rr));
4473fc9e2c3SDag-Erling Smørgrav     }
4483fc9e2c3SDag-Erling Smørgrav     n += print_padding(n, 32);
4493fc9e2c3SDag-Erling Smørgrav     n += print_rr_class(ldns_rr_get_class(rr));
4503fc9e2c3SDag-Erling Smørgrav     n += print_padding(n, 40);
4513fc9e2c3SDag-Erling Smørgrav     n += print_rr_type(ldns_rr_get_type(rr));
4523fc9e2c3SDag-Erling Smørgrav     for (i = 0; i < rdcnt; i++) {
4533fc9e2c3SDag-Erling Smørgrav         if (i == 0) print_padding(n, 48);
4543fc9e2c3SDag-Erling Smørgrav         else printf(" ");
4553fc9e2c3SDag-Erling Smørgrav         print_rdf(ldns_rr_rdf(rr, i));
4563fc9e2c3SDag-Erling Smørgrav     }
4573fc9e2c3SDag-Erling Smørgrav     printf("\n");
4583fc9e2c3SDag-Erling Smørgrav }
4593fc9e2c3SDag-Erling Smørgrav 
4603fc9e2c3SDag-Erling Smørgrav static void
4613fc9e2c3SDag-Erling Smørgrav print_pkt_section_verbose(const char *name, ldns_rr_list *rrlist) {
4623fc9e2c3SDag-Erling Smørgrav     int i, cnt = ldns_rr_list_rr_count(rrlist);
4633fc9e2c3SDag-Erling Smørgrav 
4643fc9e2c3SDag-Erling Smørgrav     if (cnt == 0)
4653fc9e2c3SDag-Erling Smørgrav         return;
4663fc9e2c3SDag-Erling Smørgrav     printf(";; %s SECTION:\n", name);
4673fc9e2c3SDag-Erling Smørgrav     for (i = 0; i < cnt; i++)
4683fc9e2c3SDag-Erling Smørgrav         print_rr_verbose(ldns_rr_list_rr(rrlist, i));
4693fc9e2c3SDag-Erling Smørgrav     printf("\n");
4703fc9e2c3SDag-Erling Smørgrav }
4713fc9e2c3SDag-Erling Smørgrav 
4723fc9e2c3SDag-Erling Smørgrav static void
4733fc9e2c3SDag-Erling Smørgrav print_pkt_verbose(ldns_pkt *pkt) {
4743fc9e2c3SDag-Erling Smørgrav     int got_flags = 0;
4753fc9e2c3SDag-Erling Smørgrav 
4763fc9e2c3SDag-Erling Smørgrav     printf(";; ->>HEADER<<- opcode: ");
4773fc9e2c3SDag-Erling Smørgrav     print_opcode(ldns_pkt_get_opcode(pkt));
4783fc9e2c3SDag-Erling Smørgrav     printf(", status: ");
4793fc9e2c3SDag-Erling Smørgrav     print_rcode(ldns_pkt_get_rcode(pkt));
4803fc9e2c3SDag-Erling Smørgrav     printf(", id: %u\n", ldns_pkt_id(pkt));
4813fc9e2c3SDag-Erling Smørgrav     printf(";; flags:");
4823fc9e2c3SDag-Erling Smørgrav     if (ldns_pkt_qr(pkt)) printf(" qr"), got_flags = 1;
4833fc9e2c3SDag-Erling Smørgrav     if (ldns_pkt_aa(pkt)) printf(" aa"), got_flags = 1;
4843fc9e2c3SDag-Erling Smørgrav     if (ldns_pkt_tc(pkt)) printf(" tc"), got_flags = 1;
4853fc9e2c3SDag-Erling Smørgrav     if (ldns_pkt_rd(pkt)) printf(" rd"), got_flags = 1;
4863fc9e2c3SDag-Erling Smørgrav     if (ldns_pkt_ra(pkt)) printf(" ra"), got_flags = 1;
4873fc9e2c3SDag-Erling Smørgrav     if (ldns_pkt_ad(pkt)) printf(" ad"), got_flags = 1;
4883fc9e2c3SDag-Erling Smørgrav     if (ldns_pkt_cd(pkt)) printf(" cd"), got_flags = 1;
4893fc9e2c3SDag-Erling Smørgrav     if (!got_flags) printf(" ");
4903fc9e2c3SDag-Erling Smørgrav     printf("; QUERY: %u, ANSWER: %u, AUTHORITY: %u, ADDITIONAL: %u\n",
4913fc9e2c3SDag-Erling Smørgrav         ldns_pkt_qdcount(pkt), ldns_pkt_ancount(pkt),
4923fc9e2c3SDag-Erling Smørgrav         ldns_pkt_nscount(pkt), ldns_pkt_arcount(pkt));
4933fc9e2c3SDag-Erling Smørgrav     if (ldns_pkt_edns(pkt))
4943fc9e2c3SDag-Erling Smørgrav         printf(";; EDNS: version: %u, udp=%u\n",
4953fc9e2c3SDag-Erling Smørgrav             ldns_pkt_edns_version(pkt), ldns_pkt_edns_udp_size(pkt));
4963fc9e2c3SDag-Erling Smørgrav     printf("\n");
4973fc9e2c3SDag-Erling Smørgrav     print_pkt_section_verbose("QUESTION", ldns_pkt_question(pkt));
4983fc9e2c3SDag-Erling Smørgrav     print_pkt_section_verbose("ANSWER", ldns_pkt_answer(pkt));
4993fc9e2c3SDag-Erling Smørgrav     print_pkt_section_verbose("AUTHORITY", ldns_pkt_authority(pkt));
5003fc9e2c3SDag-Erling Smørgrav     print_pkt_section_verbose("ADDITIONAL", ldns_pkt_additional(pkt));
5013fc9e2c3SDag-Erling Smørgrav }
5023fc9e2c3SDag-Erling Smørgrav 
5033fc9e2c3SDag-Erling Smørgrav static void
5043fc9e2c3SDag-Erling Smørgrav print_rr_short(ldns_rr *rr) {
5053fc9e2c3SDag-Erling Smørgrav     ldns_rr_type type = ldns_rr_get_type(rr);
5063fc9e2c3SDag-Erling Smørgrav     size_t i, rdcnt = ldns_rr_rd_count(rr);
5073fc9e2c3SDag-Erling Smørgrav 
5083fc9e2c3SDag-Erling Smørgrav     print_rdf_nodot(ldns_rr_owner(rr));
5093fc9e2c3SDag-Erling Smørgrav     printf(" ");
5103fc9e2c3SDag-Erling Smørgrav     for (i = 0; i < countof(rr_types); i++) {
5113fc9e2c3SDag-Erling Smørgrav         if (rr_types[i].type == type) {
5123fc9e2c3SDag-Erling Smørgrav             printf("%s", rr_types[i].text);
5133fc9e2c3SDag-Erling Smørgrav             goto found;
5143fc9e2c3SDag-Erling Smørgrav         }
5153fc9e2c3SDag-Erling Smørgrav     }
5163fc9e2c3SDag-Erling Smørgrav 
5173fc9e2c3SDag-Erling Smørgrav     printf("has ");
5183fc9e2c3SDag-Erling Smørgrav     print_rr_type(type);
5193fc9e2c3SDag-Erling Smørgrav     printf(" record");
5203fc9e2c3SDag-Erling Smørgrav 
5213fc9e2c3SDag-Erling Smørgrav found:
5223fc9e2c3SDag-Erling Smørgrav     for (i = 0; i < rdcnt; i++) {
5233fc9e2c3SDag-Erling Smørgrav         printf(" ");
5243fc9e2c3SDag-Erling Smørgrav         print_rdf(ldns_rr_rdf(rr, i));
5253fc9e2c3SDag-Erling Smørgrav     }
5263fc9e2c3SDag-Erling Smørgrav     printf("\n");
5273fc9e2c3SDag-Erling Smørgrav }
5283fc9e2c3SDag-Erling Smørgrav 
5293fc9e2c3SDag-Erling Smørgrav static void
5303fc9e2c3SDag-Erling Smørgrav print_pkt_short(ldns_pkt *pkt, bool print_rr_server) {
5313fc9e2c3SDag-Erling Smørgrav     ldns_rr_list *rrlist = ldns_pkt_answer(pkt);
5323fc9e2c3SDag-Erling Smørgrav     size_t i;
5333fc9e2c3SDag-Erling Smørgrav 
5343fc9e2c3SDag-Erling Smørgrav     for (i = 0; i < ldns_rr_list_rr_count(rrlist); i++) {
5353fc9e2c3SDag-Erling Smørgrav         if (print_rr_server) {
5363fc9e2c3SDag-Erling Smørgrav             printf("Nameserver ");
5373fc9e2c3SDag-Erling Smørgrav             print_rdf(ldns_pkt_answerfrom(pkt));
5383fc9e2c3SDag-Erling Smørgrav             printf(":\n\t");
5393fc9e2c3SDag-Erling Smørgrav         }
5403fc9e2c3SDag-Erling Smørgrav         print_rr_short(ldns_rr_list_rr(rrlist, i));
5413fc9e2c3SDag-Erling Smørgrav     }
5423fc9e2c3SDag-Erling Smørgrav }
5433fc9e2c3SDag-Erling Smørgrav 
5443fc9e2c3SDag-Erling Smørgrav static void
5453fc9e2c3SDag-Erling Smørgrav print_received_line(ldns_resolver *res, ldns_pkt *pkt) {
5463fc9e2c3SDag-Erling Smørgrav     char *from = ldns_rdf2str(ldns_pkt_answerfrom(pkt));
5473fc9e2c3SDag-Erling Smørgrav 
5483fc9e2c3SDag-Erling Smørgrav     printf("Received %zu bytes from %s#%d in %d ms\n",
5493fc9e2c3SDag-Erling Smørgrav             ldns_pkt_size(pkt), from, ldns_resolver_port(res),
5503fc9e2c3SDag-Erling Smørgrav             ldns_pkt_querytime(pkt));
5513fc9e2c3SDag-Erling Smørgrav     free(from);
5523fc9e2c3SDag-Erling Smørgrav }
5533fc9e2c3SDag-Erling Smørgrav 
5543fc9e2c3SDag-Erling Smørgrav /* Main program.
5553fc9e2c3SDag-Erling Smørgrav  *
5563fc9e2c3SDag-Erling Smørgrav  * Note that no memory is freed below this line by intention.
5573fc9e2c3SDag-Erling Smørgrav  */
5583fc9e2c3SDag-Erling Smørgrav 
5593fc9e2c3SDag-Erling Smørgrav #define DEFAULT_TCP_TIMEOUT 10
5603fc9e2c3SDag-Erling Smørgrav #define DEFAULT_UDP_TIMEOUT 5
5613fc9e2c3SDag-Erling Smørgrav 
562*3f68b24eSDag-Erling Smørgrav enum operation_mode { M_AXFR, M_IXFR, M_DEFAULT_Q, M_SINGLE_Q, M_SOA };
5633fc9e2c3SDag-Erling Smørgrav 
5643fc9e2c3SDag-Erling Smørgrav static enum operation_mode o_mode = M_DEFAULT_Q;
5653fc9e2c3SDag-Erling Smørgrav static bool o_ignore_servfail = true;
5663fc9e2c3SDag-Erling Smørgrav static bool o_ip6_int = false;
5673fc9e2c3SDag-Erling Smørgrav static bool o_print_pkt_server = false;
5683fc9e2c3SDag-Erling Smørgrav static bool o_print_rr_server = false;
5693fc9e2c3SDag-Erling Smørgrav static bool o_recursive = true;
5703fc9e2c3SDag-Erling Smørgrav static bool o_tcp = false;
5713fc9e2c3SDag-Erling Smørgrav static bool o_verbose = false;
5723fc9e2c3SDag-Erling Smørgrav static char *o_name = NULL;
5733fc9e2c3SDag-Erling Smørgrav static char *o_server = NULL;
5743fc9e2c3SDag-Erling Smørgrav static int o_ipversion = LDNS_RESOLV_INETANY;
5753fc9e2c3SDag-Erling Smørgrav static int o_ndots = 1;
5763fc9e2c3SDag-Erling Smørgrav static int o_retries = 1;
5773fc9e2c3SDag-Erling Smørgrav static ldns_rr_class o_rrclass = LDNS_RR_CLASS_IN;
578*3f68b24eSDag-Erling Smørgrav static ldns_rr_type o_rrtype = (ldns_rr_type)-1;
5793fc9e2c3SDag-Erling Smørgrav static time_t o_timeout = 0;
5803fc9e2c3SDag-Erling Smørgrav static uint32_t o_ixfr_serial = 0;
5813fc9e2c3SDag-Erling Smørgrav 
5823fc9e2c3SDag-Erling Smørgrav static void
5833fc9e2c3SDag-Erling Smørgrav usage(void) {
584*3f68b24eSDag-Erling Smørgrav     fprintf(stderr,
585*3f68b24eSDag-Erling Smørgrav     "Usage: %s [-aCdilrsTvw46] [-c class] [-N ndots] [-R number]\n"
586*3f68b24eSDag-Erling Smørgrav     "       %*c [-t type] [-W wait] name [server]\n"
5873fc9e2c3SDag-Erling Smørgrav     "\t-a same as -v -t ANY\n"
5883fc9e2c3SDag-Erling Smørgrav     "\t-C query SOA records from all authoritative name servers\n"
5893fc9e2c3SDag-Erling Smørgrav     "\t-c use this query class (IN, CH, HS, etc)\n"
5903fc9e2c3SDag-Erling Smørgrav     "\t-d produce verbose output, same as -v\n"
5913fc9e2c3SDag-Erling Smørgrav     "\t-i use IP6.INT for IPv6 reverse lookups\n"
5923fc9e2c3SDag-Erling Smørgrav     "\t-l list records in a zone via AXFR\n"
5933fc9e2c3SDag-Erling Smørgrav     "\t-N consider names with at least this many dots as absolute\n"
5943fc9e2c3SDag-Erling Smørgrav     "\t-R retry UDP queries this many times\n"
5953fc9e2c3SDag-Erling Smørgrav     "\t-r disable recursive query\n"
5963fc9e2c3SDag-Erling Smørgrav     "\t-s do not ignore SERVFAIL responses\n"
5973fc9e2c3SDag-Erling Smørgrav     "\t-T send query via TCP\n"
5983fc9e2c3SDag-Erling Smørgrav     "\t-t use this query type (A, AAAA, MX, etc)\n"
5993fc9e2c3SDag-Erling Smørgrav     "\t-v produce verbose output\n"
6003fc9e2c3SDag-Erling Smørgrav     "\t-w wait forever for a server reply\n"
6013fc9e2c3SDag-Erling Smørgrav     "\t-W wait this many seconds for a reply\n"
6023fc9e2c3SDag-Erling Smørgrav     "\t-4 use IPv4 only\n"
6033fc9e2c3SDag-Erling Smørgrav     "\t-6 use IPv6 only\n",
604*3f68b24eSDag-Erling Smørgrav     progname, (int)strlen(progname), ' ');
6053fc9e2c3SDag-Erling Smørgrav     exit(1);
6063fc9e2c3SDag-Erling Smørgrav }
6073fc9e2c3SDag-Erling Smørgrav 
6083fc9e2c3SDag-Erling Smørgrav static void
6093fc9e2c3SDag-Erling Smørgrav parse_args(int argc, char *argv[]) {
6103fc9e2c3SDag-Erling Smørgrav     int ch;
6113fc9e2c3SDag-Erling Smørgrav 
6123fc9e2c3SDag-Erling Smørgrav     progname = argv[0];
6133fc9e2c3SDag-Erling Smørgrav     while ((ch = getopt(argc, argv, "aCdilrsTvw46c:N:R:t:W:")) != -1) {
6143fc9e2c3SDag-Erling Smørgrav         switch (ch) {
6153fc9e2c3SDag-Erling Smørgrav         case 'a':
6163fc9e2c3SDag-Erling Smørgrav             if (o_mode != M_AXFR)
6173fc9e2c3SDag-Erling Smørgrav                 o_mode = M_SINGLE_Q;
6183fc9e2c3SDag-Erling Smørgrav             o_rrtype = LDNS_RR_TYPE_ANY;
6193fc9e2c3SDag-Erling Smørgrav             o_verbose = true;
6203fc9e2c3SDag-Erling Smørgrav             break;
6213fc9e2c3SDag-Erling Smørgrav         case 'C':
6223fc9e2c3SDag-Erling Smørgrav             o_mode = M_SOA;
6233fc9e2c3SDag-Erling Smørgrav             o_print_rr_server = true;
6243fc9e2c3SDag-Erling Smørgrav             o_rrclass = LDNS_RR_CLASS_IN;
6253fc9e2c3SDag-Erling Smørgrav             o_rrtype = LDNS_RR_TYPE_NS;
6263fc9e2c3SDag-Erling Smørgrav             break;
6273fc9e2c3SDag-Erling Smørgrav         case 'c':
6283fc9e2c3SDag-Erling Smørgrav             /* bind9-host sets o_mode to M_SINGLE_Q here */
6293fc9e2c3SDag-Erling Smørgrav             o_rrclass = ldns_get_rr_class_by_name(optarg);
6303fc9e2c3SDag-Erling Smørgrav             if (o_rrclass <= 0)
6313fc9e2c3SDag-Erling Smørgrav                 die(2, "invalid class: %s\n", optarg);
6323fc9e2c3SDag-Erling Smørgrav             break;
6333fc9e2c3SDag-Erling Smørgrav         case 'd': o_verbose = true; break;
6343fc9e2c3SDag-Erling Smørgrav         case 'i': o_ip6_int = true; break;
6353fc9e2c3SDag-Erling Smørgrav         case 'l':
6363fc9e2c3SDag-Erling Smørgrav             o_mode = M_AXFR;
637*3f68b24eSDag-Erling Smørgrav             if (o_rrtype == (ldns_rr_type)-1)
6383fc9e2c3SDag-Erling Smørgrav                 o_rrtype = LDNS_RR_TYPE_AXFR;
6393fc9e2c3SDag-Erling Smørgrav             o_tcp = true;
6403fc9e2c3SDag-Erling Smørgrav             break;
6413fc9e2c3SDag-Erling Smørgrav         case 'N':
6423fc9e2c3SDag-Erling Smørgrav             o_ndots = atoi(optarg);
6433fc9e2c3SDag-Erling Smørgrav             if (o_ndots < 0) o_ndots = 0;
6443fc9e2c3SDag-Erling Smørgrav             break;
6453fc9e2c3SDag-Erling Smørgrav         case 'n':
6463fc9e2c3SDag-Erling Smørgrav             /* bind9-host accepts and ignores this option */
6473fc9e2c3SDag-Erling Smørgrav             break;
6483fc9e2c3SDag-Erling Smørgrav         case 'r': o_recursive = 0; break;
6493fc9e2c3SDag-Erling Smørgrav         case 'R':
6503fc9e2c3SDag-Erling Smørgrav             o_retries = atoi(optarg);
6513fc9e2c3SDag-Erling Smørgrav             if (o_retries <= 0) o_retries = 1;
6523fc9e2c3SDag-Erling Smørgrav             if (o_retries > 255) o_retries = 255;
6533fc9e2c3SDag-Erling Smørgrav             break;
6543fc9e2c3SDag-Erling Smørgrav         case 's': o_ignore_servfail = false; break;
6553fc9e2c3SDag-Erling Smørgrav         case 'T': o_tcp = true; break;
6563fc9e2c3SDag-Erling Smørgrav         case 't':
6573fc9e2c3SDag-Erling Smørgrav             if (o_mode != M_AXFR)
6583fc9e2c3SDag-Erling Smørgrav                 o_mode = M_SINGLE_Q;
6593fc9e2c3SDag-Erling Smørgrav             if (strncasecmp(optarg, "ixfr=", 5) == 0) {
6603fc9e2c3SDag-Erling Smørgrav                 o_rrtype = LDNS_RR_TYPE_IXFR;
6613fc9e2c3SDag-Erling Smørgrav                 o_ixfr_serial = atol(optarg + 5);
6623fc9e2c3SDag-Erling Smørgrav             } else {
6633fc9e2c3SDag-Erling Smørgrav                 o_rrtype = ldns_get_rr_type_by_name(optarg);
6643fc9e2c3SDag-Erling Smørgrav                 if (o_rrtype <= 0)
6653fc9e2c3SDag-Erling Smørgrav                     die(2, "invalid type: %s\n", optarg);
6663fc9e2c3SDag-Erling Smørgrav             }
6673fc9e2c3SDag-Erling Smørgrav             if (o_rrtype == LDNS_RR_TYPE_AXFR) {
6683fc9e2c3SDag-Erling Smørgrav                 o_mode = M_AXFR;
6693fc9e2c3SDag-Erling Smørgrav                 o_rrtype = LDNS_RR_TYPE_ANY;
6703fc9e2c3SDag-Erling Smørgrav                 o_verbose = true;
6713fc9e2c3SDag-Erling Smørgrav             }
672*3f68b24eSDag-Erling Smørgrav             if (o_rrtype == LDNS_RR_TYPE_IXFR) {
673*3f68b24eSDag-Erling Smørgrav                 o_mode = M_IXFR;
674*3f68b24eSDag-Erling Smørgrav                 o_rrtype = LDNS_RR_TYPE_ANY;
675*3f68b24eSDag-Erling Smørgrav             }
6763fc9e2c3SDag-Erling Smørgrav             break;
6773fc9e2c3SDag-Erling Smørgrav         case 'v': o_verbose = true; break;
6783fc9e2c3SDag-Erling Smørgrav         case 'w':
6793fc9e2c3SDag-Erling Smørgrav               o_timeout = (time_t)INT_MAX;
6803fc9e2c3SDag-Erling Smørgrav               break;
6813fc9e2c3SDag-Erling Smørgrav         case 'W':
6823fc9e2c3SDag-Erling Smørgrav             o_timeout = atol(optarg);
6833fc9e2c3SDag-Erling Smørgrav             if (o_timeout <= 0) o_timeout = 1;
6843fc9e2c3SDag-Erling Smørgrav             break;
6853fc9e2c3SDag-Erling Smørgrav         case '4': o_ipversion = LDNS_RESOLV_INET; break;
6863fc9e2c3SDag-Erling Smørgrav         case '6': o_ipversion = LDNS_RESOLV_INET6; break;
6873fc9e2c3SDag-Erling Smørgrav         default:
6883fc9e2c3SDag-Erling Smørgrav             usage();
6893fc9e2c3SDag-Erling Smørgrav         }
6903fc9e2c3SDag-Erling Smørgrav     }
6913fc9e2c3SDag-Erling Smørgrav     argc -= optind;
6923fc9e2c3SDag-Erling Smørgrav     argv += optind;
6933fc9e2c3SDag-Erling Smørgrav     /* bind9-host ignores arguments after the 2-nd one */
6943fc9e2c3SDag-Erling Smørgrav     if (argc < 1)
6953fc9e2c3SDag-Erling Smørgrav         usage();
6963fc9e2c3SDag-Erling Smørgrav     o_name = argv[0];
6973fc9e2c3SDag-Erling Smørgrav     if (argc > 1) {
6983fc9e2c3SDag-Erling Smørgrav         o_server = argv[1];
6993fc9e2c3SDag-Erling Smørgrav         o_print_pkt_server = true;
7003fc9e2c3SDag-Erling Smørgrav     }
701*3f68b24eSDag-Erling Smørgrav     if (o_rrtype == (ldns_rr_type)-1)
702*3f68b24eSDag-Erling Smørgrav         o_rrtype = LDNS_RR_TYPE_A;
7033fc9e2c3SDag-Erling Smørgrav }
7043fc9e2c3SDag-Erling Smørgrav 
7053fc9e2c3SDag-Erling Smørgrav static ldns_rdf*
7063fc9e2c3SDag-Erling Smørgrav safe_str2rdf_dname(const char *name) {
7073fc9e2c3SDag-Erling Smørgrav     ldns_rdf *dname;
7083fc9e2c3SDag-Erling Smørgrav     ldns_status status;
7093fc9e2c3SDag-Erling Smørgrav 
7103fc9e2c3SDag-Erling Smørgrav     if ((status = ldns_str2rdf_dname(&dname, name)) != LDNS_STATUS_OK) {
7113fc9e2c3SDag-Erling Smørgrav         die(1, "'%s' is not a legal name (%s)",
7123fc9e2c3SDag-Erling Smørgrav             name, ldns_get_errorstr_by_id(status));
7133fc9e2c3SDag-Erling Smørgrav     }
7143fc9e2c3SDag-Erling Smørgrav     return dname;
7153fc9e2c3SDag-Erling Smørgrav }
7163fc9e2c3SDag-Erling Smørgrav 
7173fc9e2c3SDag-Erling Smørgrav static ldns_rdf*
7183fc9e2c3SDag-Erling Smørgrav safe_dname_cat_clone(const ldns_rdf *rd1, const ldns_rdf *rd2) {
7193fc9e2c3SDag-Erling Smørgrav     ldns_rdf *result = ldns_dname_cat_clone(rd1, rd2);
7203fc9e2c3SDag-Erling Smørgrav 
7213fc9e2c3SDag-Erling Smørgrav     if (!result)
7223fc9e2c3SDag-Erling Smørgrav         die(1, "not enought memory for a domain name");
7233fc9e2c3SDag-Erling Smørgrav     /* Why doesn't ldns_dname_cat_clone check this condition? */
7243fc9e2c3SDag-Erling Smørgrav     if (ldns_rdf_size(result) > LDNS_MAX_DOMAINLEN)
7253fc9e2c3SDag-Erling Smørgrav         die(1, "'%s' is not a legal name (%s)\n", ldns_rdf2str(result),
7263fc9e2c3SDag-Erling Smørgrav             ldns_get_errorstr_by_id(LDNS_STATUS_DOMAINNAME_OVERFLOW));
7273fc9e2c3SDag-Erling Smørgrav     return result;
7283fc9e2c3SDag-Erling Smørgrav }
7293fc9e2c3SDag-Erling Smørgrav 
7303fc9e2c3SDag-Erling Smørgrav static bool
731*3f68b24eSDag-Erling Smørgrav query(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt, bool close_tcp) {
7323fc9e2c3SDag-Erling Smørgrav     ldns_status status;
7333fc9e2c3SDag-Erling Smørgrav     ldns_pkt_rcode rcode;
7343fc9e2c3SDag-Erling Smørgrav     int i, cnt;
7353fc9e2c3SDag-Erling Smørgrav 
7363fc9e2c3SDag-Erling Smørgrav     if (o_verbose) {
7373fc9e2c3SDag-Erling Smørgrav         printf("Trying \"");
7383fc9e2c3SDag-Erling Smørgrav         print_rdf_nodot(domain);
7393fc9e2c3SDag-Erling Smørgrav         printf("\"\n");
7403fc9e2c3SDag-Erling Smørgrav     }
7413fc9e2c3SDag-Erling Smørgrav     for (cnt = ldns_resolver_nameserver_count(res), i = 0; i < cnt; i++) {
7423fc9e2c3SDag-Erling Smørgrav         status = ldns_resolver_send_to(pkt, res, domain, o_rrtype,
743*3f68b24eSDag-Erling Smørgrav             o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i,
744*3f68b24eSDag-Erling Smørgrav             close_tcp);
7453fc9e2c3SDag-Erling Smørgrav         if (status != LDNS_STATUS_OK) {
7463fc9e2c3SDag-Erling Smørgrav             *pkt = NULL;
7473fc9e2c3SDag-Erling Smørgrav             continue;
7483fc9e2c3SDag-Erling Smørgrav         }
7493fc9e2c3SDag-Erling Smørgrav         if (ldns_pkt_tc(*pkt) && !ldns_resolver_usevc(res)) {
7503fc9e2c3SDag-Erling Smørgrav             if (o_verbose)
7513fc9e2c3SDag-Erling Smørgrav                 printf(";; Truncated, retrying in TCP mode.\n");
7523fc9e2c3SDag-Erling Smørgrav             ldns_resolver_set_usevc(res, true);
7533fc9e2c3SDag-Erling Smørgrav             status = ldns_resolver_send_to(pkt, res, domain, o_rrtype,
754*3f68b24eSDag-Erling Smørgrav                 o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i,
755*3f68b24eSDag-Erling Smørgrav                 close_tcp);
7563fc9e2c3SDag-Erling Smørgrav             ldns_resolver_set_usevc(res, false);
7573fc9e2c3SDag-Erling Smørgrav             if (status != LDNS_STATUS_OK)
7583fc9e2c3SDag-Erling Smørgrav                 continue;
7593fc9e2c3SDag-Erling Smørgrav         }
7603fc9e2c3SDag-Erling Smørgrav         rcode = ldns_pkt_get_rcode(*pkt);
7613fc9e2c3SDag-Erling Smørgrav         if (o_ignore_servfail && rcode == LDNS_RCODE_SERVFAIL && cnt > 1)
7623fc9e2c3SDag-Erling Smørgrav             continue;
7633fc9e2c3SDag-Erling Smørgrav         return rcode == LDNS_RCODE_NOERROR;
7643fc9e2c3SDag-Erling Smørgrav     }
7653fc9e2c3SDag-Erling Smørgrav     if (*pkt == NULL) {
7663fc9e2c3SDag-Erling Smørgrav         printf(";; connection timed out; no servers could be reached\n");
7673fc9e2c3SDag-Erling Smørgrav         exit(1);
7683fc9e2c3SDag-Erling Smørgrav     }
7693fc9e2c3SDag-Erling Smørgrav     return false;
7703fc9e2c3SDag-Erling Smørgrav }
7713fc9e2c3SDag-Erling Smørgrav 
7723fc9e2c3SDag-Erling Smørgrav static ldns_rdf *
773*3f68b24eSDag-Erling Smørgrav search(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt,
774*3f68b24eSDag-Erling Smørgrav     bool absolute, bool close_tcp) {
7753fc9e2c3SDag-Erling Smørgrav     ldns_rdf *dname, **searchlist;
7763fc9e2c3SDag-Erling Smørgrav     int i, n;
7773fc9e2c3SDag-Erling Smørgrav 
778*3f68b24eSDag-Erling Smørgrav     if (absolute && query(res, domain, pkt, close_tcp))
7793fc9e2c3SDag-Erling Smørgrav         return domain;
7803fc9e2c3SDag-Erling Smørgrav 
7813fc9e2c3SDag-Erling Smørgrav     if ((dname = ldns_resolver_domain(res)) != NULL) {
7823fc9e2c3SDag-Erling Smørgrav         dname = safe_dname_cat_clone(domain, dname);
783*3f68b24eSDag-Erling Smørgrav         if (query(res, dname, pkt, close_tcp))
7843fc9e2c3SDag-Erling Smørgrav             return dname;
7853fc9e2c3SDag-Erling Smørgrav     }
7863fc9e2c3SDag-Erling Smørgrav 
7873fc9e2c3SDag-Erling Smørgrav     searchlist = ldns_resolver_searchlist(res);
7883fc9e2c3SDag-Erling Smørgrav     n = ldns_resolver_searchlist_count(res);
7893fc9e2c3SDag-Erling Smørgrav     for (i = 0; i < n; i++) {
7903fc9e2c3SDag-Erling Smørgrav         dname = safe_dname_cat_clone(domain, searchlist[i]);
791*3f68b24eSDag-Erling Smørgrav         if (query(res, dname, pkt, close_tcp))
7923fc9e2c3SDag-Erling Smørgrav             return dname;
7933fc9e2c3SDag-Erling Smørgrav     }
7943fc9e2c3SDag-Erling Smørgrav 
795*3f68b24eSDag-Erling Smørgrav     if (!absolute && query(res, domain, pkt, close_tcp))
7963fc9e2c3SDag-Erling Smørgrav         return domain;
7973fc9e2c3SDag-Erling Smørgrav 
7983fc9e2c3SDag-Erling Smørgrav     return NULL;
7993fc9e2c3SDag-Erling Smørgrav }
8003fc9e2c3SDag-Erling Smørgrav 
8013fc9e2c3SDag-Erling Smørgrav static void
8023fc9e2c3SDag-Erling Smørgrav report(ldns_resolver *res, ldns_rdf *domain, ldns_pkt *pkt) {
8033fc9e2c3SDag-Erling Smørgrav     ldns_pkt_rcode rcode;
8043fc9e2c3SDag-Erling Smørgrav 
8053fc9e2c3SDag-Erling Smørgrav     if (o_print_pkt_server) {
8063fc9e2c3SDag-Erling Smørgrav         printf("Using domain server:\nName: %s\nAddress: ", o_server);
8073fc9e2c3SDag-Erling Smørgrav         print_rdf(ldns_pkt_answerfrom(pkt));
8083fc9e2c3SDag-Erling Smørgrav         printf("#%d\nAliases: \n\n", ldns_resolver_port(res));
8093fc9e2c3SDag-Erling Smørgrav         o_print_pkt_server = false;
8103fc9e2c3SDag-Erling Smørgrav     }
8113fc9e2c3SDag-Erling Smørgrav     rcode = ldns_pkt_get_rcode(pkt);
8123fc9e2c3SDag-Erling Smørgrav     if (rcode != LDNS_RCODE_NOERROR) {
8133fc9e2c3SDag-Erling Smørgrav         printf("Host ");
8143fc9e2c3SDag-Erling Smørgrav         print_rdf_nodot(domain);
8153fc9e2c3SDag-Erling Smørgrav         printf(" not found: %d(", rcode);
8163fc9e2c3SDag-Erling Smørgrav         print_rcode(rcode);
8173fc9e2c3SDag-Erling Smørgrav         printf(")\n");
8183fc9e2c3SDag-Erling Smørgrav     } else {
8193fc9e2c3SDag-Erling Smørgrav         if (o_verbose) {
8203fc9e2c3SDag-Erling Smørgrav             print_pkt_verbose(pkt);
8213fc9e2c3SDag-Erling Smørgrav         } else {
8223fc9e2c3SDag-Erling Smørgrav             print_pkt_short(pkt, o_print_rr_server);
823*3f68b24eSDag-Erling Smørgrav             if (o_mode == M_SINGLE_Q &&
8243fc9e2c3SDag-Erling Smørgrav                 ldns_rr_list_rr_count(ldns_pkt_answer(pkt)) == 0) {
8253fc9e2c3SDag-Erling Smørgrav                 print_rdf_nodot(domain);
8263fc9e2c3SDag-Erling Smørgrav                 printf(" has no ");
8273fc9e2c3SDag-Erling Smørgrav                 print_rr_type(o_rrtype);
8283fc9e2c3SDag-Erling Smørgrav                 printf(" record\n");
8293fc9e2c3SDag-Erling Smørgrav             }
8303fc9e2c3SDag-Erling Smørgrav         }
8313fc9e2c3SDag-Erling Smørgrav     }
8323fc9e2c3SDag-Erling Smørgrav     if (o_verbose)
8333fc9e2c3SDag-Erling Smørgrav         print_received_line(res, pkt);
8343fc9e2c3SDag-Erling Smørgrav }
8353fc9e2c3SDag-Erling Smørgrav 
8363fc9e2c3SDag-Erling Smørgrav static bool
8373fc9e2c3SDag-Erling Smørgrav doquery(ldns_resolver *res, ldns_rdf *domain) {
8383fc9e2c3SDag-Erling Smørgrav     ldns_pkt *pkt;
8393fc9e2c3SDag-Erling Smørgrav     bool q;
8403fc9e2c3SDag-Erling Smørgrav 
841*3f68b24eSDag-Erling Smørgrav     q = query(res, domain, &pkt, true);
8423fc9e2c3SDag-Erling Smørgrav     report(res, domain, pkt);
8433fc9e2c3SDag-Erling Smørgrav     return q;
8443fc9e2c3SDag-Erling Smørgrav }
8453fc9e2c3SDag-Erling Smørgrav 
8463fc9e2c3SDag-Erling Smørgrav static bool
8473fc9e2c3SDag-Erling Smørgrav doquery_filtered(ldns_resolver *res, ldns_rdf *domain) {
8483fc9e2c3SDag-Erling Smørgrav     ldns_pkt *pkt;
8493fc9e2c3SDag-Erling Smørgrav     bool q;
8503fc9e2c3SDag-Erling Smørgrav 
851*3f68b24eSDag-Erling Smørgrav     q = query(res, domain, &pkt, true);
8523fc9e2c3SDag-Erling Smørgrav     ldns_pkt_filter_answer(pkt, o_rrtype);
8533fc9e2c3SDag-Erling Smørgrav     report(res, domain, pkt);
8543fc9e2c3SDag-Erling Smørgrav     return q;
8553fc9e2c3SDag-Erling Smørgrav }
8563fc9e2c3SDag-Erling Smørgrav 
8573fc9e2c3SDag-Erling Smørgrav static bool
8583fc9e2c3SDag-Erling Smørgrav dosearch(ldns_resolver *res, ldns_rdf *domain, bool absolute) {
8593fc9e2c3SDag-Erling Smørgrav     ldns_pkt *pkt;
8603fc9e2c3SDag-Erling Smørgrav     ldns_rdf *dname;
8613fc9e2c3SDag-Erling Smørgrav 
862*3f68b24eSDag-Erling Smørgrav     dname = search(res, domain, &pkt, absolute, true);
8633fc9e2c3SDag-Erling Smørgrav     report(res, dname != NULL ? dname : domain, pkt);
8643fc9e2c3SDag-Erling Smørgrav     return o_mode != M_DEFAULT_Q ? (dname != NULL) :
8653fc9e2c3SDag-Erling Smørgrav         (dname != NULL) &&
8663fc9e2c3SDag-Erling Smørgrav         (o_rrtype = LDNS_RR_TYPE_AAAA, doquery_filtered(res, dname)) &&
8673fc9e2c3SDag-Erling Smørgrav         (o_rrtype = LDNS_RR_TYPE_MX, doquery_filtered(res, dname));
8683fc9e2c3SDag-Erling Smørgrav }
8693fc9e2c3SDag-Erling Smørgrav 
8703fc9e2c3SDag-Erling Smørgrav static bool
871*3f68b24eSDag-Erling Smørgrav dozonetransfer(ldns_resolver *res, ldns_rdf *domain, bool absolute) {
872*3f68b24eSDag-Erling Smørgrav     ldns_pkt *pkt, *nextpkt;
8733fc9e2c3SDag-Erling Smørgrav     ldns_rdf *dname;
8743fc9e2c3SDag-Erling Smørgrav     ldns_rr_type rrtype;
875*3f68b24eSDag-Erling Smørgrav     ldns_rr_list *rrl;
876*3f68b24eSDag-Erling Smørgrav     int i, nsoa = 0;
8773fc9e2c3SDag-Erling Smørgrav 
8783fc9e2c3SDag-Erling Smørgrav     rrtype = o_rrtype;
879*3f68b24eSDag-Erling Smørgrav     o_rrtype = (o_mode == M_AXFR) ? LDNS_RR_TYPE_AXFR : LDNS_RR_TYPE_IXFR;
880*3f68b24eSDag-Erling Smørgrav     dname = search(res, domain, &pkt, absolute, false);
881*3f68b24eSDag-Erling Smørgrav 
882*3f68b24eSDag-Erling Smørgrav     for (;;) {
883*3f68b24eSDag-Erling Smørgrav         rrl = ldns_pkt_answer(pkt);
884*3f68b24eSDag-Erling Smørgrav         for (i = ldns_rr_list_rr_count(rrl) - 1; i >= 0; i--) {
885*3f68b24eSDag-Erling Smørgrav             if (ldns_rr_get_type(ldns_rr_list_rr(rrl, i)) == LDNS_RR_TYPE_SOA)
886*3f68b24eSDag-Erling Smørgrav                 nsoa++;
887*3f68b24eSDag-Erling Smørgrav         }
8883fc9e2c3SDag-Erling Smørgrav         ldns_pkt_filter_answer(pkt, rrtype);
8893fc9e2c3SDag-Erling Smørgrav         report(res, dname != NULL ? dname : domain, pkt);
890*3f68b24eSDag-Erling Smørgrav         if ((dname == NULL) ||
891*3f68b24eSDag-Erling Smørgrav                 (ldns_pkt_get_rcode(pkt) != LDNS_RCODE_NOERROR)) {
892*3f68b24eSDag-Erling Smørgrav             printf("; Transfer failed.\n");
893*3f68b24eSDag-Erling Smørgrav             ldns_tcp_close(res);
894*3f68b24eSDag-Erling Smørgrav             return false;
895*3f68b24eSDag-Erling Smørgrav         }
896*3f68b24eSDag-Erling Smørgrav         if (nsoa >= 2) {
897*3f68b24eSDag-Erling Smørgrav             ldns_tcp_close(res);
898*3f68b24eSDag-Erling Smørgrav             return true;
899*3f68b24eSDag-Erling Smørgrav         }
900*3f68b24eSDag-Erling Smørgrav         if (ldns_tcp_read(&nextpkt, res) != LDNS_STATUS_OK) {
901*3f68b24eSDag-Erling Smørgrav             printf("; Transfer failed.\n");
902*3f68b24eSDag-Erling Smørgrav             return false;
903*3f68b24eSDag-Erling Smørgrav         }
904*3f68b24eSDag-Erling Smørgrav         ldns_pkt_set_answerfrom(nextpkt,
905*3f68b24eSDag-Erling Smørgrav                 ldns_rdf_clone(ldns_pkt_answerfrom(pkt)));
906*3f68b24eSDag-Erling Smørgrav         ldns_pkt_free(pkt);
907*3f68b24eSDag-Erling Smørgrav         pkt = nextpkt;
908*3f68b24eSDag-Erling Smørgrav     }
9093fc9e2c3SDag-Erling Smørgrav }
9103fc9e2c3SDag-Erling Smørgrav 
9113fc9e2c3SDag-Erling Smørgrav static bool
9123fc9e2c3SDag-Erling Smørgrav dosoa(ldns_resolver *res, ldns_rdf *domain, bool absolute) {
9133fc9e2c3SDag-Erling Smørgrav     ldns_rr_list *answer, **nsaddrs;
9143fc9e2c3SDag-Erling Smørgrav     ldns_rdf *dname, *addr;
9153fc9e2c3SDag-Erling Smørgrav     ldns_pkt *pkt;
9163fc9e2c3SDag-Erling Smørgrav     ldns_rr *rr;
9173fc9e2c3SDag-Erling Smørgrav     size_t i, j, n, cnt;
9183fc9e2c3SDag-Erling Smørgrav 
919*3f68b24eSDag-Erling Smørgrav     if ((dname = search(res, domain, &pkt, absolute, true)) == NULL)
9203fc9e2c3SDag-Erling Smørgrav         return false;
9213fc9e2c3SDag-Erling Smørgrav 
9223fc9e2c3SDag-Erling Smørgrav     answer = ldns_pkt_answer(pkt);
9233fc9e2c3SDag-Erling Smørgrav     cnt = ldns_rr_list_rr_count(answer);
9243fc9e2c3SDag-Erling Smørgrav     nsaddrs = alloca(cnt*sizeof(*nsaddrs));
9253fc9e2c3SDag-Erling Smørgrav     for (n = 0, i = 0; i < cnt; i++)
9263fc9e2c3SDag-Erling Smørgrav         if ((addr = ldns_rr_ns_nsdname(ldns_rr_list_rr(answer, i))) != NULL)
9273fc9e2c3SDag-Erling Smørgrav             nsaddrs[n++] = ldns_get_rr_list_addr_by_name(res,
9283fc9e2c3SDag-Erling Smørgrav                 addr, LDNS_RR_CLASS_IN, 0);
9293fc9e2c3SDag-Erling Smørgrav 
9303fc9e2c3SDag-Erling Smørgrav     o_print_pkt_server = false;
9313fc9e2c3SDag-Erling Smørgrav     o_recursive = false;
9323fc9e2c3SDag-Erling Smørgrav     o_rrtype = LDNS_RR_TYPE_SOA;
9333fc9e2c3SDag-Erling Smørgrav     for (i = 0; i < n; i++) {
9343fc9e2c3SDag-Erling Smørgrav         cnt = ldns_rr_list_rr_count(nsaddrs[i]);
9353fc9e2c3SDag-Erling Smørgrav         for (j = 0; j < cnt; j++) {
9363fc9e2c3SDag-Erling Smørgrav             ldns_resolver_remove_nameservers(res);
9373fc9e2c3SDag-Erling Smørgrav             rr = ldns_rr_list_rr(nsaddrs[i], j);
9383fc9e2c3SDag-Erling Smørgrav             if ((ldns_resolver_ip6(res) == LDNS_RESOLV_INET &&
9393fc9e2c3SDag-Erling Smørgrav                 ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA) ||
9403fc9e2c3SDag-Erling Smørgrav                 (ldns_resolver_ip6(res) == LDNS_RESOLV_INET6 &&
9413fc9e2c3SDag-Erling Smørgrav                 ldns_rr_get_type(rr) == LDNS_RR_TYPE_A))
9423fc9e2c3SDag-Erling Smørgrav                 continue;
9433fc9e2c3SDag-Erling Smørgrav             if (ldns_resolver_push_nameserver_rr(res, rr) == LDNS_STATUS_OK)
9443fc9e2c3SDag-Erling Smørgrav                 /* bind9-host queries for domain, not dname here */
9453fc9e2c3SDag-Erling Smørgrav                 doquery(res, dname);
9463fc9e2c3SDag-Erling Smørgrav         }
9473fc9e2c3SDag-Erling Smørgrav     }
9483fc9e2c3SDag-Erling Smørgrav     return 0;
9493fc9e2c3SDag-Erling Smørgrav }
9503fc9e2c3SDag-Erling Smørgrav 
9513fc9e2c3SDag-Erling Smørgrav static void
9523fc9e2c3SDag-Erling Smørgrav resolver_set_nameserver_hostname(ldns_resolver *res, const char *server) {
9533fc9e2c3SDag-Erling Smørgrav     struct addrinfo hints, *ailist, *ai;
9543fc9e2c3SDag-Erling Smørgrav     ldns_status status;
9553fc9e2c3SDag-Erling Smørgrav     ldns_rdf *rdf;
9563fc9e2c3SDag-Erling Smørgrav     int err;
9573fc9e2c3SDag-Erling Smørgrav 
9583fc9e2c3SDag-Erling Smørgrav     memset(&hints, 0, sizeof hints);
9593fc9e2c3SDag-Erling Smørgrav     switch (ldns_resolver_ip6(res)) {
9603fc9e2c3SDag-Erling Smørgrav     case LDNS_RESOLV_INET: hints.ai_family = PF_INET; break;
9613fc9e2c3SDag-Erling Smørgrav     case LDNS_RESOLV_INET6: hints.ai_family = PF_INET6; break;
9623fc9e2c3SDag-Erling Smørgrav     default: hints.ai_family = PF_UNSPEC; break;
9633fc9e2c3SDag-Erling Smørgrav     }
9643fc9e2c3SDag-Erling Smørgrav     hints.ai_socktype = SOCK_STREAM;
9653fc9e2c3SDag-Erling Smørgrav     do err = getaddrinfo(server, NULL, &hints, &ailist);
9663fc9e2c3SDag-Erling Smørgrav     while (err == EAI_AGAIN);
9673fc9e2c3SDag-Erling Smørgrav     if (err != 0)
9683fc9e2c3SDag-Erling Smørgrav         die(1, "couldn't get address for '%s': %s", server, gai_strerror(err));
9693fc9e2c3SDag-Erling Smørgrav     for (ai = ailist; ai != NULL; ai = ai->ai_next) {
9703fc9e2c3SDag-Erling Smørgrav         if ((rdf = ldns_sockaddr_storage2rdf((void*)ai->ai_addr, NULL)) == NULL)
9713fc9e2c3SDag-Erling Smørgrav             die(1, "couldn't allocate an rdf: %s",
9723fc9e2c3SDag-Erling Smørgrav                 ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR));
9733fc9e2c3SDag-Erling Smørgrav         status = ldns_resolver_push_nameserver(res, rdf);
9743fc9e2c3SDag-Erling Smørgrav         if (status != LDNS_STATUS_OK)
9753fc9e2c3SDag-Erling Smørgrav             die(1, "couldn't push a nameserver address: %s",
9763fc9e2c3SDag-Erling Smørgrav                 ldns_get_errorstr_by_id(status));
9773fc9e2c3SDag-Erling Smørgrav     }
9783fc9e2c3SDag-Erling Smørgrav }
9793fc9e2c3SDag-Erling Smørgrav 
9803fc9e2c3SDag-Erling Smørgrav static void
9813fc9e2c3SDag-Erling Smørgrav resolver_set_nameserver_str(ldns_resolver *res, const char *server) {
9823fc9e2c3SDag-Erling Smørgrav     ldns_rdf *addr;
9833fc9e2c3SDag-Erling Smørgrav 
9843fc9e2c3SDag-Erling Smørgrav     ldns_resolver_remove_nameservers(res);
9853fc9e2c3SDag-Erling Smørgrav     addr = ldns_rdf_new_addr_frm_str(server);
9863fc9e2c3SDag-Erling Smørgrav     if (addr) {
9873fc9e2c3SDag-Erling Smørgrav         if (ldns_resolver_push_nameserver(res, addr) != LDNS_STATUS_OK)
9883fc9e2c3SDag-Erling Smørgrav             die(1, "couldn't push a nameserver address");
9893fc9e2c3SDag-Erling Smørgrav     } else
9903fc9e2c3SDag-Erling Smørgrav         resolver_set_nameserver_hostname(res, server);
9913fc9e2c3SDag-Erling Smørgrav }
9923fc9e2c3SDag-Erling Smørgrav 
9933fc9e2c3SDag-Erling Smørgrav int
9943fc9e2c3SDag-Erling Smørgrav main(int argc, char *argv[]) {
9953fc9e2c3SDag-Erling Smørgrav     ldns_rdf *addr, *dname;
9963fc9e2c3SDag-Erling Smørgrav     ldns_resolver *res;
9973fc9e2c3SDag-Erling Smørgrav     ldns_status status;
9983fc9e2c3SDag-Erling Smørgrav     struct timeval restimeout;
9993fc9e2c3SDag-Erling Smørgrav 
10003fc9e2c3SDag-Erling Smørgrav     parse_args(argc, argv);
10013fc9e2c3SDag-Erling Smørgrav 
10023fc9e2c3SDag-Erling Smørgrav     status = ldns_resolver_new_default(&res);
10033fc9e2c3SDag-Erling Smørgrav     if (status != LDNS_STATUS_OK)
10043fc9e2c3SDag-Erling Smørgrav         die(1, "error creating resolver: %s", ldns_get_errorstr_by_id(status));
10053fc9e2c3SDag-Erling Smørgrav     if (ldns_resolver_nameserver_count(res) == 0)
10063fc9e2c3SDag-Erling Smørgrav         ldns_resolver_push_default_servers(res);
10073fc9e2c3SDag-Erling Smørgrav 
10083fc9e2c3SDag-Erling Smørgrav     ldns_resolver_set_usevc(res, o_tcp);
10093fc9e2c3SDag-Erling Smørgrav     restimeout.tv_sec = o_timeout > 0 ? o_timeout :
10103fc9e2c3SDag-Erling Smørgrav         o_tcp ? DEFAULT_TCP_TIMEOUT : DEFAULT_UDP_TIMEOUT;
10113fc9e2c3SDag-Erling Smørgrav     restimeout.tv_usec = 0;
10123fc9e2c3SDag-Erling Smørgrav     ldns_resolver_set_timeout(res, restimeout);
10133fc9e2c3SDag-Erling Smørgrav     ldns_resolver_set_retry(res, o_retries+1);
10143fc9e2c3SDag-Erling Smørgrav     ldns_resolver_set_ip6(res, o_ipversion);
10153fc9e2c3SDag-Erling Smørgrav     ldns_resolver_set_defnames(res, false);
10163fc9e2c3SDag-Erling Smørgrav     ldns_resolver_set_fallback(res, false);
10173fc9e2c3SDag-Erling Smørgrav 
10183fc9e2c3SDag-Erling Smørgrav     if (o_server)
10193fc9e2c3SDag-Erling Smørgrav         resolver_set_nameserver_str(res, o_server);
10203fc9e2c3SDag-Erling Smørgrav 
10213fc9e2c3SDag-Erling Smørgrav     if (ldns_str2rdf_a(&addr, o_name) == LDNS_STATUS_OK) {
10223fc9e2c3SDag-Erling Smørgrav         dname = ldns_rdf_reverse_a(addr, "in-addr.arpa");
10233fc9e2c3SDag-Erling Smørgrav         if (dname == NULL)
10243fc9e2c3SDag-Erling Smørgrav             die(1, "can't reverse '%s': %s", o_name,
10253fc9e2c3SDag-Erling Smørgrav                 ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR));
10263fc9e2c3SDag-Erling Smørgrav         o_mode = M_SINGLE_Q;
10273fc9e2c3SDag-Erling Smørgrav         o_rrtype = LDNS_RR_TYPE_PTR;
10283fc9e2c3SDag-Erling Smørgrav         return !doquery(res, dname);
10293fc9e2c3SDag-Erling Smørgrav     } else if (ldns_str2rdf_aaaa(&addr, o_name) == LDNS_STATUS_OK) {
10303fc9e2c3SDag-Erling Smørgrav         dname = ldns_rdf_reverse_aaaa(addr, o_ip6_int ? "ip6.int" : "ip6.arpa");
10313fc9e2c3SDag-Erling Smørgrav         if (dname == NULL)
10323fc9e2c3SDag-Erling Smørgrav             die(1, "can't reverse '%s': %s", o_name,
10333fc9e2c3SDag-Erling Smørgrav                 ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR));
10343fc9e2c3SDag-Erling Smørgrav         o_mode = M_SINGLE_Q;
10353fc9e2c3SDag-Erling Smørgrav         o_rrtype = LDNS_RR_TYPE_PTR;
10363fc9e2c3SDag-Erling Smørgrav         return !doquery(res, dname);
10373fc9e2c3SDag-Erling Smørgrav     }
1038*3f68b24eSDag-Erling Smørgrav     return !(o_mode == M_SOA ? dosoa :
1039*3f68b24eSDag-Erling Smørgrav              o_mode == M_AXFR ? dozonetransfer :
1040*3f68b24eSDag-Erling Smørgrav              o_mode == M_IXFR ? dozonetransfer :
1041*3f68b24eSDag-Erling Smørgrav              dosearch)
10423fc9e2c3SDag-Erling Smørgrav         (res, safe_str2rdf_dname(o_name), ndots(o_name) >= o_ndots);
10433fc9e2c3SDag-Erling Smørgrav }
1044