Import Magerya Vitaly's ldns-host, and build it instead of the BIND version

in the WITH_LDNS_UTILS case.

Approved by:	re (blanket)
This commit is contained in:
des 2013-09-08 19:40:32 +00:00
commit ff13bc56dc
4 changed files with 1179 additions and 0 deletions

View File

@ -0,0 +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
XLDFLAGS=${LDFLAGS} -L${LOCALBASE}/lib -lldns
${PROG}: ${SRC}
${CC} -o $@ ${XCFLAGS} ${XLDFLAGS} ${SRC}
clean:
rm -f ${PROG}
install: ${PROG}
cp ${PROG} ${PREFIX}/bin/
cp ${MAN} ${MANDIR}/man1/
deinstall:
rm -f ${PREFIX}/bin/${PROG} ${MANDIR}/man1/${MAN}

View File

@ -0,0 +1,246 @@
.\" (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

View File

@ -0,0 +1,884 @@
/*-
* (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 <netinet/in.h>
#include <limits.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ldns/ldns.h>
/* 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_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;
ldns_pkt *qpkt;
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)
status = ldns_pkt_push_rr_soa(qpkt,
LDNS_SECTION_AUTHORITY, name, c, ixfr_serial);
if (status == LDNS_STATUS_OK)
status = ldns_resolver_send_pkt(answer, res, qpkt);
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 &&
(rrtype == LDNS_RR_TYPE_A ||
rrtype == LDNS_RR_TYPE_AAAA ||
rrtype == LDNS_RR_TYPE_NS ||
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 };
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 time_t o_timeout = 0;
static uint32_t o_ixfr_serial = 0;
static void
usage(void) {
fputs(
"Usage: host [-aCdilrsTvw46] [-c class] [-N ndots] [-R number]\n"
" [-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);
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;
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;
}
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;
}
}
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) {
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);
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);
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) {
ldns_rdf *dname, **searchlist;
int i, n;
if (absolute && query(res, domain, pkt))
return domain;
if ((dname = ldns_resolver_domain(res)) != NULL) {
dname = safe_dname_cat_clone(domain, dname);
if (query(res, dname, pkt))
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))
return dname;
}
if (!absolute && query(res, domain, pkt))
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 &&
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);
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);
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);
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;
ldns_rdf *dname;
ldns_rr_type rrtype;
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;
}
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)
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))
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)
(res, safe_str2rdf_dname(o_name), ndots(o_name) >= o_ndots);
}

View File

@ -1,5 +1,29 @@
# $FreeBSD$
.include <bsd.own.mk>
.if ${MK_LDNS_UTILS} != "no"
LDNSDIR= ${.CURDIR}/../../contrib/ldns
LDNSHOSTDIR= ${.CURDIR}/../../contrib/ldns-host
.PATH: ${LDNSHOSTDIR}
PROG= host
SRCS= ldns-host.c
MAN= host.1
host.1: ldns-host.1
sed -e 's/ldns-//gI' <${.ALLSRC} >${.TARGET} || \
(rm -rf ${.TARGET} ; false)
CFLAGS+= -I${LDNSDIR}
DPADD+= ${LIBLDNS} ${LIBCRYPTO}
LDADD+= -lldns -lcrypto
USEPRIVATELIB= ldns
.else
BIND_DIR= ${.CURDIR}/../../contrib/bind9
LIB_BIND_REL= ../../lib/bind
LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL}
@ -20,4 +44,6 @@ WARNS?= 0
DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD}
LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD}
.endif
.include <bsd.prog.mk>