Index: vendor/ldns-host/dist/Makefile =================================================================== --- vendor/ldns-host/dist/Makefile (revision 301754) +++ vendor/ldns-host/dist/Makefile (revision 301755) @@ -1,23 +1,23 @@ PROG=ldns-host SRC=ldns-host.c MAN=ldns-host.1 LOCALBASE?=/usr/local PREFIX?=${LOCALBASE} MANDIR?=${PREFIX}/man -XCFLAGS=${CFLAGS} -I${LOCALBASE}/include +XCFLAGS=${CFLAGS} -I${LOCALBASE}/include -std=c99 -Wall -Wextra -pedantic XLDFLAGS=${LDFLAGS} -L${LOCALBASE}/lib -lldns ${PROG}: ${SRC} - ${CC} -o $@ ${XCFLAGS} ${XLDFLAGS} ${SRC} + ${CC} -o $@ ${XCFLAGS} ${SRC} ${XLDFLAGS} clean: rm -f ${PROG} install: ${PROG} cp ${PROG} ${PREFIX}/bin/ cp ${MAN} ${MANDIR}/man1/ deinstall: rm -f ${PREFIX}/bin/${PROG} ${MANDIR}/man1/${MAN} Index: vendor/ldns-host/dist/ldns-host.1 =================================================================== --- vendor/ldns-host/dist/ldns-host.1 (revision 301754) +++ vendor/ldns-host/dist/ldns-host.1 (revision 301755) @@ -1,246 +1,243 @@ .\" (c) Magerya Vitaly .\" .\" Copying and distribution of this file, with or without modification, .\" are permitted in any medium without royalty provided the copyright .\" notice and this notice are preserved. This file is offered as-is, .\" without any warranty. .Dd Aug 27, 2012 .Dt LDNS-HOST 1 .Os .Sh NAME .Nm ldns-host .Nd DNS lookup utility .Sh SYNOPSIS .Nm .Op Fl aCdilrsTvw46 .Op Fl c Ar class .Op Fl N Ar ndots .Op Fl R Ar number .Op Fl t Ar type .Op Fl W Ar wait .Ar name .Op Ar server .Sh DESCRIPTION .Nm is a simple utility for performing DNS lookups. It is normally used to convert names to IP addresses and vice versa. .Pp .Ar name is the domain name that is to be looked up. It can also be a dotted-decimal IPv4 address or a colon-delimited IPv6 address, in which case .Nm will by default perform a reverse lookup for that address. .Pp When .Ar name is not provided, .Nm prints a short summary of it's usage. .Pp .Ar server is an optional argument which is either a domain name or an IP address of the name server that .Nm should query instead of the server or servers listed in .Pa /etc/resolv.conf . When .Ar server is a domain name, system resolver is used to obtain it's address. .Pp Supported options: .Bl -tag -width indent .It Fl a Make a verbose query of type .Cm ANY . Equivalent to .Fl v Fl t Cm ANY . .It Fl C Query for .Cm SOA records for zone .Ar name from all of it's authoritative name servers. The list of name servers is obtained via .Cm NS query for .Ar name . .It Fl c Ar class Perform DNS query of class .Ar class . Recognized classes are .Cm IN Pq Internet , .Cm CH Pq Chaosnet , .Cm HS Pq Hesiod , .Cm NONE , .Cm ANY and .Cm CLASS Ns Ar N (where .Ar N is a number from 1 to 255). Default is .Cm IN . .It Fl d Produce verbose output. This is a synonym for .Fl v , and is provided for backward compatibility. .It Fl i Use IP6.INT domain for reverse lookups of IPv6 addresses (as defined in RFC1886; note that RFC4159 deprecates IP6.INT). By default IP6.ARPA is used. .It Fl l List all .Cm NS, PTR, A and .Cm AAAA records in zone .Ar name by performing a zone transfer .Pq Cm AXFR . You can combine this option with .Fl a to print all records, or with .Fl t to only print specific ones. .It Fl N Ar ndots Consider names with at least this many dots as absolute. That is, try to resolve them directly before consulting .Ic domain or .Ic search options from .Pa /etc/resolv.conf . .It Fl r Perform non-recursive query to the name server by clearing RD .Pq Dq recursion desired bit of the query. .It Fl R Ar number Retry this many times when a query does not receive an answer in time. The default is 1 retry. If .Ar number is negative or zero, 1 is used instead. .It Fl s Report SERVFAIL responses as they are, do not ignore them. .It Fl T Query name server over TCP. By default UDP is used, except for .Cm AXFR and .Cm IXFR queries, which require TCP. .Nm will also retry UDP queries in TCP mode if the UDP response was truncated (i.e. had TC bit set). .It Fl t Ar type Perform DNS query of type .Ar type , which can be any standard query type name .Pq Cm A , CNAME , MX , TXT , No etc , a wildcard query .Pq Cm ANY , or .Cm TYPE Ns Ar N , where .Ar N is a number from 1 to 65535. For .Cm IXFR Pq incremental zone transfer queries the starting serial number can be specified by appending an equal sign followed by the number .Pq e.g. Fl t Cm IXFR Ns =12345678 . .Pp The default is to query for .Cm A , AAAA , No and Cm MX records, unless .Fl C or .Fl l options are given (in which case .Cm SOA or .Cm AXFR queries are made) or .Ar name is a valid IP address (in which case reverse lookup using .Cm PTR query is performed). .It Fl v Produce verbose output. .It Fl w Wait forever (or for a very long time) for response from the name server. .It Fl W Ar wait Wait this many seconds for a reply from name server before timing out. If .Ar wait is negative or zero, value of 1 is used. The default is to wait 10 seconds for TCP connections, and 5 seconds for UDP (both are subject to retries, see option .Fl R ) . .It Fl 4 Only use IPv4 transport. .It Fl 6 Only use IPv6 transport. .El .Sh FILES .Pa /etc/resolv.conf .Sh SEE ALSO .Xr drill 1 , .Xr resolv.conf 5 .Sh COMPATIBILITY .Nm aims to be reasonably compatible with .Sq host utility from BIND9 distribution, both in supported options and in produced output. Here is a list of known notable differences: .Bl -bullet .It Debugging options .Pq Fl D No and Fl m are not supported. .It Query class .Cm CLASS0 and type .Cm TYPE0 are not supported. .It Backslashes in domain names are treated especially. .It The maximum of 255 retries (option .Fl R ) are supported. .It Some resource records are formatted differently. For example, .Cm RRSIG and .Cm DNSKEY records are displayed without spaces in them. .It When parsing .Pa /etc/resolv.conf commands .Ic sortlist and .Ic options are ignored. When multiple .Ic search and/or .Ic domain commands are present, .Nm first uses the last .Ic domain command, and then all of .Ic search commands, while .Sq host from BIND9 uses whatever command was specified last. .It -Multi-packet zone transfers are not supported; only the first -response packet is printed. -.It .Sq Pseudosection TSIG is missing from verbose packet output. .El .Sh AUTHORS .An Vitaly Magerya Aq magv@tx97.net Index: vendor/ldns-host/dist/ldns-host.c =================================================================== --- vendor/ldns-host/dist/ldns-host.c (revision 301754) +++ vendor/ldns-host/dist/ldns-host.c (revision 301755) @@ -1,881 +1,1043 @@ /*- * (c) Magerya Vitaly * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. This file is offered as-is, * without any warranty. */ #include #include #include +#include #include #include #include /* General utilities. */ static char *progname; #define countof(array) (sizeof(array)/sizeof(*(array))) static void die(int code, const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "%s: ", progname); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); exit(code); } static int ndots(const char *name) { int n; for (n = 0; (name = strchr(name, '.')); n++, name++); return n; } /* General LDNS-specific utilities. */ static ldns_status ldns_resolver_new_default(ldns_resolver **res) { if (ldns_resolver_new_frm_file(res, NULL) == LDNS_STATUS_OK || (*res = ldns_resolver_new()) != NULL) return LDNS_STATUS_OK; return LDNS_STATUS_MEM_ERR; } static ldns_status ldns_resolver_push_default_servers(ldns_resolver *res) { ldns_status status; ldns_rdf *addr; if ((status = ldns_str2rdf_a(&addr, "127.0.0.1")) != LDNS_STATUS_OK || (status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK) return ldns_rdf_deep_free(addr), status; ldns_rdf_deep_free(addr); if ((status = ldns_str2rdf_aaaa(&addr, "::1")) != LDNS_STATUS_OK || (status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK) return ldns_rdf_deep_free(addr), status; ldns_rdf_deep_free(addr); return LDNS_STATUS_OK; } static ldns_rdf * ldns_rdf_new_addr_frm_str(const char *str) { ldns_rdf *addr; if ((addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, str)) == NULL) addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, str); return addr; } static void ldns_resolver_remove_nameservers(ldns_resolver *res) { while (ldns_resolver_nameserver_count(res) > 0) ldns_rdf_deep_free(ldns_resolver_pop_nameserver(res)); } static ldns_rdf * ldns_rdf_reverse_a(ldns_rdf *addr, const char *base) { char *buf; int i, len; len = strlen(base); buf = alloca(LDNS_IP4ADDRLEN*4 + len + 1); for (len = i = 0; i < LDNS_IP4ADDRLEN; i++) len += sprintf(&buf[len], "%d.", (int)ldns_rdf_data(addr)[LDNS_IP4ADDRLEN - i - 1]); sprintf(&buf[len], "%s", base); return ldns_dname_new_frm_str(buf); } static ldns_rdf * ldns_rdf_reverse_aaaa(ldns_rdf *addr, const char *base) { char *buf; int i, len; len = strlen(base); buf = alloca(LDNS_IP6ADDRLEN*4 + len + 1); for (i = 0; i < LDNS_IP6ADDRLEN; i++) { uint8_t byte = ldns_rdf_data(addr)[LDNS_IP6ADDRLEN - i - 1]; sprintf(&buf[i*4], "%x.%x.", byte & 0x0F, byte >> 4); } sprintf(&buf[LDNS_IP6ADDRLEN*4], "%s", base); return ldns_dname_new_frm_str(buf); } static ldns_status ldns_pkt_push_rr_soa(ldns_pkt *pkt, ldns_pkt_section sec, const ldns_rdf *name, ldns_rr_class c, uint32_t serial) { ldns_rdf *rdf; ldns_rr *rr; uint32_t n; if ((rr = ldns_rr_new_frm_type(LDNS_RR_TYPE_SOA)) == NULL) return LDNS_STATUS_MEM_ERR; ldns_rr_set_class(rr, c); ldns_rr_set_owner(rr, ldns_rdf_clone(name)); ldns_rr_set_ttl(rr, 0); n = 0; if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, 1, &n)) == NULL) goto memerr; ldns_rr_set_rdf(rr, rdf, 0); ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 1); n = htonl(serial); if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT32, 4, &n)) == NULL) goto memerr; ldns_rr_set_rdf(rr, rdf, 2); n = 0; if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_PERIOD, 4, &n)) == NULL) goto memerr; ldns_rr_set_rdf(rr, rdf, 3); ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 4); ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 5); ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 6); if (ldns_rr_rdf(rr, 1) == NULL || ldns_rr_rdf(rr, 4) == NULL || ldns_rr_rdf(rr, 5) == NULL || ldns_rr_rdf(rr, 6) == NULL || !ldns_pkt_push_rr(pkt, sec, rr)) goto memerr; return LDNS_STATUS_OK; memerr: ldns_rr_free(rr); return LDNS_STATUS_MEM_ERR; } static ldns_status +ldns_tcp_start(ldns_resolver *res, ldns_pkt *qpkt, int nameserver) { + /* This routine is based on ldns_axfr_start, with the major + * difference in that it takes a query packet explicitly. + */ + struct sockaddr_storage *ns = NULL; + size_t ns_len = 0; + ldns_buffer *qbuf = NULL; + ldns_status status; + + ns = ldns_rdf2native_sockaddr_storage( + res->_nameservers[nameserver], ldns_resolver_port(res), &ns_len); + if (ns == NULL) { + status = LDNS_STATUS_MEM_ERR; + goto error; + } + + res->_socket = ldns_tcp_connect( + ns, (socklen_t)ns_len, ldns_resolver_timeout(res)); + if (res->_socket <= 0) { + status = LDNS_STATUS_ADDRESS_ERR; + goto error; + } + + qbuf = ldns_buffer_new(LDNS_MAX_PACKETLEN); + if (qbuf == NULL) { + status = LDNS_STATUS_MEM_ERR; + goto error; + } + + status = ldns_pkt2buffer_wire(qbuf, qpkt); + if (status != LDNS_STATUS_OK) + goto error; + + if (ldns_tcp_send_query(qbuf, res->_socket, ns, (socklen_t)ns_len) == 0) { + status = LDNS_STATUS_NETWORK_ERR; + goto error; + } + + ldns_buffer_free(qbuf); + free(ns); + return LDNS_STATUS_OK; + +error: + ldns_buffer_free(qbuf); + free(ns); + if (res->_socket > 0) { + close(res->_socket); + res->_socket = 0; + } + return status; +} + +static ldns_status +ldns_tcp_read(ldns_pkt **answer, ldns_resolver *res) { + ldns_status status; + struct timeval t1, t2; + uint8_t *data; + size_t size; + + if (res->_socket <= 0) + return LDNS_STATUS_ERR; + + gettimeofday(&t1, NULL); + data = ldns_tcp_read_wire_timeout( + res->_socket, &size, ldns_resolver_timeout(res)); + if (data == NULL) + goto error; + + status = ldns_wire2pkt(answer, data, size); + free(data); + if (status != LDNS_STATUS_OK) + goto error; + + gettimeofday(&t2, NULL); + ldns_pkt_set_querytime(*answer, + (uint32_t)((t2.tv_sec - t1.tv_sec)*1000) + + (t2.tv_usec - t1.tv_usec)/1000); + ldns_pkt_set_timestamp(*answer, t2); + return status; + +error: + close(res->_socket); + res->_socket = 0; + return LDNS_STATUS_ERR; +} + +static void +ldns_tcp_close(ldns_resolver *res) { + if (res->_socket > 0) { + close(res->_socket); + res->_socket = 0; + } +} + +static ldns_status ldns_resolver_send_to(ldns_pkt **answer, ldns_resolver *res, const ldns_rdf *name, ldns_rr_type t, ldns_rr_class c, - uint16_t flags, uint32_t ixfr_serial, int nameserver) { - ldns_status status; + uint16_t flags, uint32_t ixfr_serial, int nameserver, + bool close_tcp) { + ldns_status status = LDNS_STATUS_OK; ldns_pkt *qpkt; + struct timeval now; int nscnt = ldns_resolver_nameserver_count(res); ldns_rdf **ns = ldns_resolver_nameservers(res); size_t *rtt = ldns_resolver_rtt(res); ldns_resolver_set_nameservers(res, &ns[nameserver]); ldns_resolver_set_rtt(res, &rtt[nameserver]); ldns_resolver_set_nameserver_count(res, 1); - status = ldns_resolver_prepare_query_pkt(&qpkt, res, name, t, c, flags); - if (status == LDNS_STATUS_OK && t == LDNS_RR_TYPE_IXFR) + /* The next fragment should have been a call to + * ldns_resolver_prepare_query_pkt(), but starting with ldns + * version 1.6.17 that function tries to add it's own SOA + * records when rr_type is LDNS_RR_TYPE_IXFR, and we don't + * want that. + */ + qpkt = ldns_pkt_query_new(ldns_rdf_clone(name), t, c, flags); + if (qpkt == NULL) { + status = LDNS_STATUS_ERR; + goto done; + } + now.tv_sec = time(NULL); + now.tv_usec = 0; + ldns_pkt_set_timestamp(qpkt, now); + ldns_pkt_set_random_id(qpkt); + + if (t == LDNS_RR_TYPE_IXFR) { status = ldns_pkt_push_rr_soa(qpkt, LDNS_SECTION_AUTHORITY, name, c, ixfr_serial); - if (status == LDNS_STATUS_OK) + if (status != LDNS_STATUS_OK) goto done; + } + if (close_tcp) { status = ldns_resolver_send_pkt(answer, res, qpkt); + } else { + status = ldns_tcp_start(res, qpkt, 0); + if (status != LDNS_STATUS_OK) goto done; + status = ldns_tcp_read(answer, res); + if (status != LDNS_STATUS_OK) goto done; + ldns_pkt_set_answerfrom(*answer, ldns_rdf_clone(ns[0])); + } + +done: ldns_pkt_free(qpkt); ldns_resolver_set_nameservers(res, ns); ldns_resolver_set_rtt(res, rtt); ldns_resolver_set_nameserver_count(res, nscnt); return status; } static void ldns_pkt_filter_answer(ldns_pkt *pkt, ldns_rr_type type) { int i, j, cnt; ldns_rr_list *rrlist; ldns_rr *rr; ldns_rr_type rrtype; rrlist = ldns_pkt_answer(pkt); cnt = ldns_rr_list_rr_count(rrlist); for (i = j = 0; i < cnt; i++) { rr = ldns_rr_list_rr(rrlist, i); rrtype = ldns_rr_get_type(rr); if (type == LDNS_RR_TYPE_ANY || type == rrtype || - type == LDNS_RR_TYPE_AXFR && + (type == LDNS_RR_TYPE_AXFR && (rrtype == LDNS_RR_TYPE_A || rrtype == LDNS_RR_TYPE_AAAA || rrtype == LDNS_RR_TYPE_NS || - rrtype == LDNS_RR_TYPE_PTR)) + rrtype == LDNS_RR_TYPE_PTR))) ldns_rr_list_set_rr(rrlist, rr, j++); } ldns_rr_list_set_rr_count(rrlist, j); } /* Packet content printing. */ static struct { ldns_rr_type type; const char *text; } rr_types[] = { {LDNS_RR_TYPE_A, "has address"}, {LDNS_RR_TYPE_NS, "name server"}, {LDNS_RR_TYPE_CNAME, "is an alias for"}, {LDNS_RR_TYPE_WKS, "has well known services"}, {LDNS_RR_TYPE_PTR, "domain name pointer"}, {LDNS_RR_TYPE_HINFO, "host information"}, {LDNS_RR_TYPE_MX, "mail is handled by"}, {LDNS_RR_TYPE_TXT, "descriptive text"}, {LDNS_RR_TYPE_X25, "x25 address"}, {LDNS_RR_TYPE_ISDN, "ISDN address"}, {LDNS_RR_TYPE_SIG, "has signature"}, {LDNS_RR_TYPE_KEY, "has key"}, {LDNS_RR_TYPE_AAAA, "has IPv6 address"}, {LDNS_RR_TYPE_LOC, "location"}, }; static void print_opcode(ldns_pkt_opcode opcode) { ldns_lookup_table *lt = ldns_lookup_by_id(ldns_opcodes, opcode); if (lt && lt->name) printf("%s", lt->name); else printf("RESERVED%d", opcode); } static void print_rcode(uint8_t rcode) { ldns_lookup_table *lt = ldns_lookup_by_id(ldns_rcodes, rcode); if (lt && lt->name) printf("%s", lt->name); else printf("RESERVED%d", rcode); } static int print_rr_type(ldns_rr_type type) { char *str; int n; str = ldns_rr_type2str(type); n = printf("%s", str); free(str); return n; } static int print_rr_class(ldns_rr_class cls) { char *str; int n; str = ldns_rr_class2str(cls); n = printf("%s", str); free(str); return n; } static int print_rdf(ldns_rdf *rdf) { char *str; int n; str = ldns_rdf2str(rdf); n = printf("%s", str); free(str); return n; } static int print_rdf_nodot(ldns_rdf *rdf) { char *str; int len, n; str = ldns_rdf2str(rdf); len = strlen(str); n = printf("%.*s", str[len-1] == '.' ? len-1 : len, str); free(str); return n; } static int print_padding(int fromcol, int tocol) { int col = fromcol, nextcol = fromcol + 8 - fromcol%8; if (fromcol + 1 > tocol) tocol = fromcol + 1; for (; nextcol <= tocol; col = nextcol, nextcol += 8) printf("\t"); for (; col < tocol; col++) printf(" "); return col - fromcol; } static void print_rr_verbose(ldns_rr *rr) { bool isq = ldns_rr_is_question(rr); int rdcnt = ldns_rr_rd_count(rr); int i, n; /* bind9-host does not count the initial ';' here */ n = isq ? printf(";") : 0; n = print_rdf(ldns_rr_owner(rr)); if (!isq) { n += print_padding(n, 24); n += printf("%d", ldns_rr_ttl(rr)); } n += print_padding(n, 32); n += print_rr_class(ldns_rr_get_class(rr)); n += print_padding(n, 40); n += print_rr_type(ldns_rr_get_type(rr)); for (i = 0; i < rdcnt; i++) { if (i == 0) print_padding(n, 48); else printf(" "); print_rdf(ldns_rr_rdf(rr, i)); } printf("\n"); } static void print_pkt_section_verbose(const char *name, ldns_rr_list *rrlist) { int i, cnt = ldns_rr_list_rr_count(rrlist); if (cnt == 0) return; printf(";; %s SECTION:\n", name); for (i = 0; i < cnt; i++) print_rr_verbose(ldns_rr_list_rr(rrlist, i)); printf("\n"); } static void print_pkt_verbose(ldns_pkt *pkt) { int got_flags = 0; printf(";; ->>HEADER<<- opcode: "); print_opcode(ldns_pkt_get_opcode(pkt)); printf(", status: "); print_rcode(ldns_pkt_get_rcode(pkt)); printf(", id: %u\n", ldns_pkt_id(pkt)); printf(";; flags:"); if (ldns_pkt_qr(pkt)) printf(" qr"), got_flags = 1; if (ldns_pkt_aa(pkt)) printf(" aa"), got_flags = 1; if (ldns_pkt_tc(pkt)) printf(" tc"), got_flags = 1; if (ldns_pkt_rd(pkt)) printf(" rd"), got_flags = 1; if (ldns_pkt_ra(pkt)) printf(" ra"), got_flags = 1; if (ldns_pkt_ad(pkt)) printf(" ad"), got_flags = 1; if (ldns_pkt_cd(pkt)) printf(" cd"), got_flags = 1; if (!got_flags) printf(" "); printf("; QUERY: %u, ANSWER: %u, AUTHORITY: %u, ADDITIONAL: %u\n", ldns_pkt_qdcount(pkt), ldns_pkt_ancount(pkt), ldns_pkt_nscount(pkt), ldns_pkt_arcount(pkt)); if (ldns_pkt_edns(pkt)) printf(";; EDNS: version: %u, udp=%u\n", ldns_pkt_edns_version(pkt), ldns_pkt_edns_udp_size(pkt)); printf("\n"); print_pkt_section_verbose("QUESTION", ldns_pkt_question(pkt)); print_pkt_section_verbose("ANSWER", ldns_pkt_answer(pkt)); print_pkt_section_verbose("AUTHORITY", ldns_pkt_authority(pkt)); print_pkt_section_verbose("ADDITIONAL", ldns_pkt_additional(pkt)); } static void print_rr_short(ldns_rr *rr) { ldns_rr_type type = ldns_rr_get_type(rr); size_t i, rdcnt = ldns_rr_rd_count(rr); print_rdf_nodot(ldns_rr_owner(rr)); printf(" "); for (i = 0; i < countof(rr_types); i++) { if (rr_types[i].type == type) { printf("%s", rr_types[i].text); goto found; } } printf("has "); print_rr_type(type); printf(" record"); found: for (i = 0; i < rdcnt; i++) { printf(" "); print_rdf(ldns_rr_rdf(rr, i)); } printf("\n"); } static void print_pkt_short(ldns_pkt *pkt, bool print_rr_server) { ldns_rr_list *rrlist = ldns_pkt_answer(pkt); size_t i; for (i = 0; i < ldns_rr_list_rr_count(rrlist); i++) { if (print_rr_server) { printf("Nameserver "); print_rdf(ldns_pkt_answerfrom(pkt)); printf(":\n\t"); } print_rr_short(ldns_rr_list_rr(rrlist, i)); } } static void print_received_line(ldns_resolver *res, ldns_pkt *pkt) { char *from = ldns_rdf2str(ldns_pkt_answerfrom(pkt)); printf("Received %zu bytes from %s#%d in %d ms\n", ldns_pkt_size(pkt), from, ldns_resolver_port(res), ldns_pkt_querytime(pkt)); free(from); } /* Main program. * * Note that no memory is freed below this line by intention. */ #define DEFAULT_TCP_TIMEOUT 10 #define DEFAULT_UDP_TIMEOUT 5 -enum operation_mode { M_AXFR, M_DEFAULT_Q, M_SINGLE_Q, M_SOA }; +enum operation_mode { M_AXFR, M_IXFR, M_DEFAULT_Q, M_SINGLE_Q, M_SOA }; static enum operation_mode o_mode = M_DEFAULT_Q; static bool o_ignore_servfail = true; static bool o_ip6_int = false; static bool o_print_pkt_server = false; static bool o_print_rr_server = false; static bool o_recursive = true; static bool o_tcp = false; static bool o_verbose = false; static char *o_name = NULL; static char *o_server = NULL; static int o_ipversion = LDNS_RESOLV_INETANY; static int o_ndots = 1; static int o_retries = 1; static ldns_rr_class o_rrclass = LDNS_RR_CLASS_IN; -static ldns_rr_type o_rrtype = LDNS_RR_TYPE_A; +static ldns_rr_type o_rrtype = (ldns_rr_type)-1; static time_t o_timeout = 0; static uint32_t o_ixfr_serial = 0; static void usage(void) { - fputs( - "Usage: ldns-host [-aCdilrsTvw46] [-c class] [-N ndots] [-R number]\n" - " [-t type] [-W wait] name [server]\n" + fprintf(stderr, + "Usage: %s [-aCdilrsTvw46] [-c class] [-N ndots] [-R number]\n" + " %*c [-t type] [-W wait] name [server]\n" "\t-a same as -v -t ANY\n" "\t-C query SOA records from all authoritative name servers\n" "\t-c use this query class (IN, CH, HS, etc)\n" "\t-d produce verbose output, same as -v\n" "\t-i use IP6.INT for IPv6 reverse lookups\n" "\t-l list records in a zone via AXFR\n" "\t-N consider names with at least this many dots as absolute\n" "\t-R retry UDP queries this many times\n" "\t-r disable recursive query\n" "\t-s do not ignore SERVFAIL responses\n" "\t-T send query via TCP\n" "\t-t use this query type (A, AAAA, MX, etc)\n" "\t-v produce verbose output\n" "\t-w wait forever for a server reply\n" "\t-W wait this many seconds for a reply\n" "\t-4 use IPv4 only\n" "\t-6 use IPv6 only\n", - stderr); + progname, (int)strlen(progname), ' '); exit(1); } static void parse_args(int argc, char *argv[]) { int ch; progname = argv[0]; while ((ch = getopt(argc, argv, "aCdilrsTvw46c:N:R:t:W:")) != -1) { switch (ch) { case 'a': if (o_mode != M_AXFR) o_mode = M_SINGLE_Q; o_rrtype = LDNS_RR_TYPE_ANY; o_verbose = true; break; case 'C': o_mode = M_SOA; o_print_rr_server = true; o_rrclass = LDNS_RR_CLASS_IN; o_rrtype = LDNS_RR_TYPE_NS; break; case 'c': /* bind9-host sets o_mode to M_SINGLE_Q here */ o_rrclass = ldns_get_rr_class_by_name(optarg); if (o_rrclass <= 0) die(2, "invalid class: %s\n", optarg); break; case 'd': o_verbose = true; break; case 'i': o_ip6_int = true; break; case 'l': o_mode = M_AXFR; - o_rrtype = LDNS_RR_TYPE_AXFR; + if (o_rrtype == (ldns_rr_type)-1) + o_rrtype = LDNS_RR_TYPE_AXFR; o_tcp = true; break; case 'N': o_ndots = atoi(optarg); if (o_ndots < 0) o_ndots = 0; break; case 'n': /* bind9-host accepts and ignores this option */ break; case 'r': o_recursive = 0; break; case 'R': o_retries = atoi(optarg); if (o_retries <= 0) o_retries = 1; if (o_retries > 255) o_retries = 255; break; case 's': o_ignore_servfail = false; break; case 'T': o_tcp = true; break; case 't': if (o_mode != M_AXFR) o_mode = M_SINGLE_Q; if (strncasecmp(optarg, "ixfr=", 5) == 0) { o_rrtype = LDNS_RR_TYPE_IXFR; o_ixfr_serial = atol(optarg + 5); } else { o_rrtype = ldns_get_rr_type_by_name(optarg); if (o_rrtype <= 0) die(2, "invalid type: %s\n", optarg); } - if (o_rrtype == LDNS_RR_TYPE_AXFR || o_rrtype == LDNS_RR_TYPE_IXFR) - o_tcp = true; if (o_rrtype == LDNS_RR_TYPE_AXFR) { o_mode = M_AXFR; o_rrtype = LDNS_RR_TYPE_ANY; o_verbose = true; } + if (o_rrtype == LDNS_RR_TYPE_IXFR) { + o_mode = M_IXFR; + o_rrtype = LDNS_RR_TYPE_ANY; + } break; case 'v': o_verbose = true; break; case 'w': o_timeout = (time_t)INT_MAX; break; case 'W': o_timeout = atol(optarg); if (o_timeout <= 0) o_timeout = 1; break; case '4': o_ipversion = LDNS_RESOLV_INET; break; case '6': o_ipversion = LDNS_RESOLV_INET6; break; default: usage(); } } argc -= optind; argv += optind; /* bind9-host ignores arguments after the 2-nd one */ if (argc < 1) usage(); o_name = argv[0]; if (argc > 1) { o_server = argv[1]; o_print_pkt_server = true; } + if (o_rrtype == (ldns_rr_type)-1) + o_rrtype = LDNS_RR_TYPE_A; } static ldns_rdf* safe_str2rdf_dname(const char *name) { ldns_rdf *dname; ldns_status status; if ((status = ldns_str2rdf_dname(&dname, name)) != LDNS_STATUS_OK) { die(1, "'%s' is not a legal name (%s)", name, ldns_get_errorstr_by_id(status)); } return dname; } static ldns_rdf* safe_dname_cat_clone(const ldns_rdf *rd1, const ldns_rdf *rd2) { ldns_rdf *result = ldns_dname_cat_clone(rd1, rd2); if (!result) die(1, "not enought memory for a domain name"); /* Why doesn't ldns_dname_cat_clone check this condition? */ if (ldns_rdf_size(result) > LDNS_MAX_DOMAINLEN) die(1, "'%s' is not a legal name (%s)\n", ldns_rdf2str(result), ldns_get_errorstr_by_id(LDNS_STATUS_DOMAINNAME_OVERFLOW)); return result; } static bool -query(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt) { +query(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt, bool close_tcp) { ldns_status status; ldns_pkt_rcode rcode; int i, cnt; if (o_verbose) { printf("Trying \""); print_rdf_nodot(domain); printf("\"\n"); } for (cnt = ldns_resolver_nameserver_count(res), i = 0; i < cnt; i++) { status = ldns_resolver_send_to(pkt, res, domain, o_rrtype, - o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i); + o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i, + close_tcp); if (status != LDNS_STATUS_OK) { *pkt = NULL; continue; } if (ldns_pkt_tc(*pkt) && !ldns_resolver_usevc(res)) { if (o_verbose) printf(";; Truncated, retrying in TCP mode.\n"); ldns_resolver_set_usevc(res, true); status = ldns_resolver_send_to(pkt, res, domain, o_rrtype, - o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i); + o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i, + close_tcp); ldns_resolver_set_usevc(res, false); if (status != LDNS_STATUS_OK) continue; } rcode = ldns_pkt_get_rcode(*pkt); if (o_ignore_servfail && rcode == LDNS_RCODE_SERVFAIL && cnt > 1) continue; return rcode == LDNS_RCODE_NOERROR; } if (*pkt == NULL) { printf(";; connection timed out; no servers could be reached\n"); exit(1); } return false; } static ldns_rdf * -search(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt, bool absolute) { +search(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt, + bool absolute, bool close_tcp) { ldns_rdf *dname, **searchlist; int i, n; - if (absolute && query(res, domain, pkt)) + if (absolute && query(res, domain, pkt, close_tcp)) return domain; if ((dname = ldns_resolver_domain(res)) != NULL) { dname = safe_dname_cat_clone(domain, dname); - if (query(res, dname, pkt)) + if (query(res, dname, pkt, close_tcp)) return dname; } searchlist = ldns_resolver_searchlist(res); n = ldns_resolver_searchlist_count(res); for (i = 0; i < n; i++) { dname = safe_dname_cat_clone(domain, searchlist[i]); - if (query(res, dname, pkt)) + if (query(res, dname, pkt, close_tcp)) return dname; } - if (!absolute && query(res, domain, pkt)) + if (!absolute && query(res, domain, pkt, close_tcp)) return domain; return NULL; } static void report(ldns_resolver *res, ldns_rdf *domain, ldns_pkt *pkt) { ldns_pkt_rcode rcode; if (o_print_pkt_server) { printf("Using domain server:\nName: %s\nAddress: ", o_server); print_rdf(ldns_pkt_answerfrom(pkt)); printf("#%d\nAliases: \n\n", ldns_resolver_port(res)); o_print_pkt_server = false; } rcode = ldns_pkt_get_rcode(pkt); if (rcode != LDNS_RCODE_NOERROR) { printf("Host "); print_rdf_nodot(domain); printf(" not found: %d(", rcode); print_rcode(rcode); printf(")\n"); } else { if (o_verbose) { print_pkt_verbose(pkt); } else { print_pkt_short(pkt, o_print_rr_server); - if (o_mode != M_DEFAULT_Q && + if (o_mode == M_SINGLE_Q && ldns_rr_list_rr_count(ldns_pkt_answer(pkt)) == 0) { print_rdf_nodot(domain); printf(" has no "); print_rr_type(o_rrtype); printf(" record\n"); } } } if (o_verbose) print_received_line(res, pkt); } static bool doquery(ldns_resolver *res, ldns_rdf *domain) { ldns_pkt *pkt; bool q; - q = query(res, domain, &pkt); + q = query(res, domain, &pkt, true); report(res, domain, pkt); return q; } static bool doquery_filtered(ldns_resolver *res, ldns_rdf *domain) { ldns_pkt *pkt; bool q; - q = query(res, domain, &pkt); + q = query(res, domain, &pkt, true); ldns_pkt_filter_answer(pkt, o_rrtype); report(res, domain, pkt); return q; } static bool dosearch(ldns_resolver *res, ldns_rdf *domain, bool absolute) { ldns_pkt *pkt; ldns_rdf *dname; - dname = search(res, domain, &pkt, absolute); + dname = search(res, domain, &pkt, absolute, true); report(res, dname != NULL ? dname : domain, pkt); return o_mode != M_DEFAULT_Q ? (dname != NULL) : (dname != NULL) && (o_rrtype = LDNS_RR_TYPE_AAAA, doquery_filtered(res, dname)) && (o_rrtype = LDNS_RR_TYPE_MX, doquery_filtered(res, dname)); } static bool -doaxfr(ldns_resolver *res, ldns_rdf *domain, bool absolute) { - ldns_pkt *pkt; +dozonetransfer(ldns_resolver *res, ldns_rdf *domain, bool absolute) { + ldns_pkt *pkt, *nextpkt; ldns_rdf *dname; ldns_rr_type rrtype; + ldns_rr_list *rrl; + int i, nsoa = 0; rrtype = o_rrtype; - o_rrtype = LDNS_RR_TYPE_AXFR; - dname = search(res, domain, &pkt, absolute); - ldns_pkt_filter_answer(pkt, rrtype); - report(res, dname != NULL ? dname : domain, pkt); - return dname != NULL; + o_rrtype = (o_mode == M_AXFR) ? LDNS_RR_TYPE_AXFR : LDNS_RR_TYPE_IXFR; + dname = search(res, domain, &pkt, absolute, false); + + for (;;) { + rrl = ldns_pkt_answer(pkt); + for (i = ldns_rr_list_rr_count(rrl) - 1; i >= 0; i--) { + if (ldns_rr_get_type(ldns_rr_list_rr(rrl, i)) == LDNS_RR_TYPE_SOA) + nsoa++; + } + ldns_pkt_filter_answer(pkt, rrtype); + report(res, dname != NULL ? dname : domain, pkt); + if ((dname == NULL) || + (ldns_pkt_get_rcode(pkt) != LDNS_RCODE_NOERROR)) { + printf("; Transfer failed.\n"); + ldns_tcp_close(res); + return false; + } + if (nsoa >= 2) { + ldns_tcp_close(res); + return true; + } + if (ldns_tcp_read(&nextpkt, res) != LDNS_STATUS_OK) { + printf("; Transfer failed.\n"); + return false; + } + ldns_pkt_set_answerfrom(nextpkt, + ldns_rdf_clone(ldns_pkt_answerfrom(pkt))); + ldns_pkt_free(pkt); + pkt = nextpkt; + } } static bool dosoa(ldns_resolver *res, ldns_rdf *domain, bool absolute) { ldns_rr_list *answer, **nsaddrs; ldns_rdf *dname, *addr; ldns_pkt *pkt; ldns_rr *rr; size_t i, j, n, cnt; - if ((dname = search(res, domain, &pkt, absolute)) == NULL) + if ((dname = search(res, domain, &pkt, absolute, true)) == NULL) return false; answer = ldns_pkt_answer(pkt); cnt = ldns_rr_list_rr_count(answer); nsaddrs = alloca(cnt*sizeof(*nsaddrs)); for (n = 0, i = 0; i < cnt; i++) if ((addr = ldns_rr_ns_nsdname(ldns_rr_list_rr(answer, i))) != NULL) nsaddrs[n++] = ldns_get_rr_list_addr_by_name(res, addr, LDNS_RR_CLASS_IN, 0); o_print_pkt_server = false; o_recursive = false; o_rrtype = LDNS_RR_TYPE_SOA; for (i = 0; i < n; i++) { cnt = ldns_rr_list_rr_count(nsaddrs[i]); for (j = 0; j < cnt; j++) { ldns_resolver_remove_nameservers(res); rr = ldns_rr_list_rr(nsaddrs[i], j); - if (ldns_resolver_ip6(res) == LDNS_RESOLV_INET && - ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA || - ldns_resolver_ip6(res) == LDNS_RESOLV_INET6 && - ldns_rr_get_type(rr) == LDNS_RR_TYPE_A) + if ((ldns_resolver_ip6(res) == LDNS_RESOLV_INET && + ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA) || + (ldns_resolver_ip6(res) == LDNS_RESOLV_INET6 && + ldns_rr_get_type(rr) == LDNS_RR_TYPE_A)) continue; if (ldns_resolver_push_nameserver_rr(res, rr) == LDNS_STATUS_OK) /* bind9-host queries for domain, not dname here */ doquery(res, dname); } } return 0; } static void resolver_set_nameserver_hostname(ldns_resolver *res, const char *server) { struct addrinfo hints, *ailist, *ai; ldns_status status; ldns_rdf *rdf; int err; memset(&hints, 0, sizeof hints); switch (ldns_resolver_ip6(res)) { case LDNS_RESOLV_INET: hints.ai_family = PF_INET; break; case LDNS_RESOLV_INET6: hints.ai_family = PF_INET6; break; default: hints.ai_family = PF_UNSPEC; break; } hints.ai_socktype = SOCK_STREAM; do err = getaddrinfo(server, NULL, &hints, &ailist); while (err == EAI_AGAIN); if (err != 0) die(1, "couldn't get address for '%s': %s", server, gai_strerror(err)); for (ai = ailist; ai != NULL; ai = ai->ai_next) { if ((rdf = ldns_sockaddr_storage2rdf((void*)ai->ai_addr, NULL)) == NULL) die(1, "couldn't allocate an rdf: %s", ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); status = ldns_resolver_push_nameserver(res, rdf); if (status != LDNS_STATUS_OK) die(1, "couldn't push a nameserver address: %s", ldns_get_errorstr_by_id(status)); } } static void resolver_set_nameserver_str(ldns_resolver *res, const char *server) { ldns_rdf *addr; ldns_resolver_remove_nameservers(res); addr = ldns_rdf_new_addr_frm_str(server); if (addr) { if (ldns_resolver_push_nameserver(res, addr) != LDNS_STATUS_OK) die(1, "couldn't push a nameserver address"); } else resolver_set_nameserver_hostname(res, server); } int main(int argc, char *argv[]) { ldns_rdf *addr, *dname; ldns_resolver *res; ldns_status status; struct timeval restimeout; parse_args(argc, argv); status = ldns_resolver_new_default(&res); if (status != LDNS_STATUS_OK) die(1, "error creating resolver: %s", ldns_get_errorstr_by_id(status)); if (ldns_resolver_nameserver_count(res) == 0) ldns_resolver_push_default_servers(res); ldns_resolver_set_usevc(res, o_tcp); restimeout.tv_sec = o_timeout > 0 ? o_timeout : o_tcp ? DEFAULT_TCP_TIMEOUT : DEFAULT_UDP_TIMEOUT; restimeout.tv_usec = 0; ldns_resolver_set_timeout(res, restimeout); ldns_resolver_set_retry(res, o_retries+1); ldns_resolver_set_ip6(res, o_ipversion); ldns_resolver_set_defnames(res, false); ldns_resolver_set_fallback(res, false); if (o_server) resolver_set_nameserver_str(res, o_server); if (ldns_str2rdf_a(&addr, o_name) == LDNS_STATUS_OK) { dname = ldns_rdf_reverse_a(addr, "in-addr.arpa"); if (dname == NULL) die(1, "can't reverse '%s': %s", o_name, ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); o_mode = M_SINGLE_Q; o_rrtype = LDNS_RR_TYPE_PTR; return !doquery(res, dname); } else if (ldns_str2rdf_aaaa(&addr, o_name) == LDNS_STATUS_OK) { dname = ldns_rdf_reverse_aaaa(addr, o_ip6_int ? "ip6.int" : "ip6.arpa"); if (dname == NULL) die(1, "can't reverse '%s': %s", o_name, ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); o_mode = M_SINGLE_Q; o_rrtype = LDNS_RR_TYPE_PTR; return !doquery(res, dname); } - return !(o_mode == M_SOA ? dosoa : o_mode == M_AXFR ? doaxfr : dosearch) + return !(o_mode == M_SOA ? dosoa : + o_mode == M_AXFR ? dozonetransfer : + o_mode == M_IXFR ? dozonetransfer : + dosearch) (res, safe_str2rdf_dname(o_name), ndots(o_name) >= o_ndots); }