#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <err.h>
#include <errno.h>
#include <inttypes.h>
#include <netdb.h>
#include <resolv.h>
#include <stdio.h>
#include <string.h>
#include "common.h"
int long_err;
int gai_errno;
int rrset_errno;
char *
gethostarg(char *n)
{
if (n == NULL)
return (n);
if (!strcmp(n, "NULL"))
return (NULL);
if (!strcmp(n, "EMPTY"))
return ("");
return (n);
}
const char *rrsetstrerror(int);
char * print_addr(const struct sockaddr *, char *, size_t);
struct kv { int code; const char *name; };
struct kv kv_family[] = {
{ AF_UNIX, "unix" },
{ AF_INET, "inet" },
{ AF_INET6, "inet6" },
{ 0, NULL, }
};
struct kv kv_socktype[] = {
{ SOCK_STREAM, "stream" },
{ SOCK_DGRAM, "dgram" },
{ SOCK_RAW, "raw" },
{ SOCK_SEQPACKET, "seqpacket" },
{ 0, NULL, }
};
struct kv kv_protocol[] = {
{ IPPROTO_UDP, "udp" },
{ IPPROTO_TCP, "tcp" },
{ IPPROTO_ICMP, "icmp" },
{ IPPROTO_ICMPV6, "icmpv6" },
{ 0, NULL, }
};
static const char *
kv_lookup_name(struct kv *kv, int code, char *buf, size_t sz)
{
while (kv->name) {
if (kv->code == code)
return (kv->name);
kv++;
}
snprintf(buf, sz, "%i", code);
return (buf);
}
struct keyval {
const char *key;
int value;
};
static struct keyval kv_class[] = {
{ "IN", C_IN },
{ "CHAOS", C_CHAOS },
{ "HS", C_HS },
{ "ANY", C_ANY },
{ NULL, 0 },
};
static struct keyval kv_type[] = {
{ "A", T_A },
{ "NS", T_NS },
{ "MD", T_MD },
{ "MF", T_MF },
{ "CNAME", T_CNAME },
{ "SOA", T_SOA },
{ "MB", T_MB },
{ "MG", T_MG },
{ "MR", T_MR },
{ "NULL", T_NULL },
{ "WKS", T_WKS },
{ "PTR", T_PTR },
{ "HINFO", T_HINFO },
{ "MINFO", T_MINFO },
{ "MX", T_MX },
{ "TXT", T_TXT },
{ "AAAA", T_AAAA },
{ "AXFR", T_AXFR },
{ "MAILB", T_MAILB },
{ "MAILA", T_MAILA },
{ "ANY", T_ANY },
{ NULL, 0 },
};
static struct keyval kv_rcode[] = {
{ "NOERROR", NOERROR },
{ "FORMERR", FORMERR },
{ "SERVFAIL", SERVFAIL },
{ "NXDOMAIN", NXDOMAIN },
{ "NOTIMP", NOTIMP },
{ "REFUSED", REFUSED },
{ NULL, 0 },
};
static struct keyval kv_resopt[] = {
{ "DEBUG", RES_DEBUG },
{ "AAONLY", RES_AAONLY },
{ "USEVC", RES_USEVC },
{ "PRIMARY", RES_PRIMARY },
{ "IGNTC", RES_IGNTC },
{ "RECURSE", RES_RECURSE },
{ "DEFNAMES", RES_DEFNAMES },
{ "STAYOPEN", RES_STAYOPEN },
{ "DNSRCH", RES_DNSRCH },
{ "INSECURE1", RES_INSECURE1 },
{ "INSECURE2", RES_INSECURE2 },
{ "NOALIASES", RES_NOALIASES },
{ "USE_INET6", RES_USE_INET6 },
{ "USE_EDNS0", RES_USE_EDNS0 },
{ "USE_DNSSEC", RES_USE_DNSSEC },
{ NULL, 0 },
};
const char *
rcodetostr(uint16_t v)
{
static char buf[16];
size_t i;
for(i = 0; kv_rcode[i].key; i++)
if (kv_rcode[i].value == v)
return (kv_rcode[i].key);
snprintf(buf, sizeof buf, "%"PRIu16"?", v);
return (buf);
}
const char *
typetostr(uint16_t v)
{
static char buf[16];
size_t i;
for(i = 0; kv_type[i].key; i++)
if (kv_type[i].value == v)
return (kv_type[i].key);
snprintf(buf, sizeof buf, "%"PRIu16"?", v);
return (buf);
}
const char *
classtostr(uint16_t v)
{
static char buf[16];
size_t i;
for(i = 0; kv_class[i].key; i++)
if (kv_class[i].value == v)
return (kv_class[i].key);
snprintf(buf, sizeof buf, "%"PRIu16"?", v);
return (buf);
}
uint16_t
strtotype(const char *name)
{
size_t i;
for(i = 0; kv_type[i].key; i++)
if (!strcasecmp(kv_type[i].key, name))
return (kv_type[i].value);
return (0);
}
uint16_t
strtoclass(const char *name)
{
size_t i;
for(i = 0; kv_class[i].key; i++)
if (!strcasecmp(kv_class[i].key, name))
return (kv_class[i].value);
return (0);
}
int
strtoresopt(const char *name)
{
size_t i;
for(i = 0; kv_resopt[i].key; i++)
if (!strcasecmp(kv_resopt[i].key, name))
return (kv_resopt[i].value);
return (0);
}
void
parseresopt(const char *name)
{
static int init = 0;
int flag, neg = 0;
if (init == 0) {
res_init();
init = 1;
}
if (name[0] == '-') {
neg = 1;
name++;
}
else if (name[0] == '+')
name++;
flag = strtoresopt(name);
if (flag == 0)
errx(1, "unknown reslover option %s", name);
if (neg)
_res.options &= ~flag;
else
_res.options |= flag;
}
void
print_hostent(struct hostent *e)
{
char buf[256], **c;
printf("name = \"%s\"\n", e->h_name);
printf("aliases =");
for(c = e->h_aliases; *c; c++)
printf(" \"%s\"", *c);
printf("\n");
printf("addrtype = %i\n", e->h_addrtype);
printf("addrlength = %i\n", e->h_length);
printf("addr_list =");
for(c = e->h_addr_list; *c; c++) {
printf(" %s", inet_ntop(e->h_addrtype, *c, buf, sizeof buf));
}
printf("\n");
}
void
print_netent(struct netent *e)
{
char buf[256], **c;
uint32_t addr;
addr = htonl(e->n_net);
printf("name = \"%s\"\n", e->n_name);
printf("aliases =");
for (c = e->n_aliases; *c; c++)
printf(" \"%s\"", *c);
printf("\n");
printf("addrtype = %i\n", e->n_addrtype);
printf("net = %s\n", inet_ntop(e->n_addrtype, &addr, buf, sizeof buf));
}
void
print_addrinfo(struct addrinfo *ai)
{
char buf[256], bf[64], bt[64], bp[64];
printf("family=%s socktype=%s protocol=%s addr=%s canonname=%s\n",
kv_lookup_name(kv_family, ai->ai_family, bf, sizeof bf),
kv_lookup_name(kv_socktype, ai->ai_socktype, bt, sizeof bt),
kv_lookup_name(kv_protocol, ai->ai_protocol, bp, sizeof bp),
print_addr(ai->ai_addr, buf, sizeof buf),
ai->ai_canonname);
}
const char *
rrsetstrerror(int e)
{
switch (e) {
case 0:
return "OK";
case ERRSET_NONAME:
return "ERRSET_NONAME";
case ERRSET_NODATA:
return "ERRSET_NODATA";
case ERRSET_NOMEMORY:
return "ERRSET_NOMEMORY";
case ERRSET_INVAL:
return "ERRSET_INVAL";
case ERRSET_FAIL:
return "ERRSET_FAIL";
default:
return "???";
}
}
void
print_rrsetinfo(struct rrsetinfo * rrset)
{
printf("rri_flags=%u\n", rrset->rri_flags);
printf("rri_rdclass=%u\n", rrset->rri_rdclass);
printf("rri_rdtype=%u\n", rrset->rri_rdtype);
printf("rri_ttl=%u\n", rrset->rri_ttl);
printf("rri_nrdatas=%u\n", rrset->rri_nrdatas);
printf("rri_nsigs=%u\n", rrset->rri_nsigs);
printf("rri_name=\"%s\"\n", rrset->rri_name);
}
void
print_errors(void)
{
switch (long_err) {
case 0:
return;
case 1:
printf(" => errno %i, h_errno %i", errno, h_errno);
printf(", rrset_errno %i", rrset_errno);
printf(", gai_errno %i", gai_errno);
printf ("\n");
return;
default:
printf(" => errno %i: %s\n => h_errno %i: %s\n => rrset_errno %i: %s\n",
errno, errno ? strerror(errno) : "ok",
h_errno, h_errno ? hstrerror(h_errno) : "ok",
rrset_errno, rrset_errno ? rrsetstrerror(rrset_errno) : "ok");
printf(" => gai_errno %i: %s\n",
gai_errno, gai_errno ? gai_strerror(gai_errno) : "ok");
}
}
static char *
print_host(const struct sockaddr *sa, char *buf, size_t len)
{
switch (sa->sa_family) {
case AF_INET:
inet_ntop(AF_INET, &((struct sockaddr_in*)sa)->sin_addr,
buf, len);
break;
case AF_INET6:
inet_ntop(AF_INET6, &((struct sockaddr_in6*)sa)->sin6_addr,
buf, len);
break;
default:
buf[0] = '\0';
}
return (buf);
}
char *
print_addr(const struct sockaddr *sa, char *buf, size_t len)
{
char h[256];
print_host(sa, h, sizeof h);
switch (sa->sa_family) {
case AF_INET:
snprintf(buf, len, "%s:%i", h,
ntohs(((struct sockaddr_in*)(sa))->sin_port));
break;
case AF_INET6:
snprintf(buf, len, "[%s]:%i", h,
ntohs(((struct sockaddr_in6*)(sa))->sin6_port));
break;
default:
snprintf(buf, len, "?");
break;
}
return (buf);
}
void
packed_init(struct packed *pack, char *data, size_t len)
{
pack->data = data;
pack->len = len;
pack->offset = 0;
pack->err = NULL;
}
static ssize_t
dname_expand(const unsigned char *data, size_t len, size_t offset,
size_t *newoffset, char *dst, size_t max)
{
size_t n, count, end, ptr, start;
ssize_t res;
if (offset >= len)
return (-1);
res = 0;
end = start = offset;
for(; (n = data[offset]); ) {
if ((n & 0xc0) == 0xc0) {
if (offset + 2 > len)
return (-1);
ptr = 256 * (n & ~0xc0) + data[offset + 1];
if (ptr >= start)
return (-1);
if (end < offset + 2)
end = offset + 2;
offset = ptr;
continue;
}
if (offset + n + 1 > len)
return (-1);
if (dst != NULL && max != 0) {
count = (max < n + 1) ? (max) : (n + 1);
memmove(dst, data + offset, count);
dst += count;
max -= count;
}
res += n + 1;
offset += n + 1;
if (end < offset)
end = offset;
}
if (end < offset + 1)
end = offset + 1;
if (dst != NULL && max != 0)
dst[0] = 0;
if (newoffset)
*newoffset = end;
return (res + 1);
}
static int
unpack_data(struct packed *p, void *data, size_t len)
{
if (p->err)
return (-1);
if (p->len - p->offset < len) {
p->err = "too short";
return (-1);
}
memmove(data, p->data + p->offset, len);
p->offset += len;
return (0);
}
static int
unpack_u16(struct packed *p, uint16_t *u16)
{
if (unpack_data(p, u16, 2) == -1)
return (-1);
*u16 = ntohs(*u16);
return (0);
}
static int
unpack_u32(struct packed *p, uint32_t *u32)
{
if (unpack_data(p, u32, 4) == -1)
return (-1);
*u32 = ntohl(*u32);
return (0);
}
static int
unpack_inaddr(struct packed *p, struct in_addr *a)
{
return (unpack_data(p, a, 4));
}
static int
unpack_in6addr(struct packed *p, struct in6_addr *a6)
{
return (unpack_data(p, a6, 16));
}
static int
unpack_dname(struct packed *p, char *dst, size_t max)
{
ssize_t e;
if (p->err)
return (-1);
e = dname_expand(p->data, p->len, p->offset, &p->offset, dst, max);
if (e == -1) {
p->err = "bad domain name";
return (-1);
}
if (e < 0 || e > MAXDNAME) {
p->err = "domain name too long";
return (-1);
}
return (0);
}
int
unpack_header(struct packed *p, struct header *h)
{
if (unpack_data(p, h, HFIXEDSZ) == -1)
return (-1);
h->flags = ntohs(h->flags);
h->qdcount = ntohs(h->qdcount);
h->ancount = ntohs(h->ancount);
h->nscount = ntohs(h->nscount);
h->arcount = ntohs(h->arcount);
return (0);
}
int
unpack_query(struct packed *p, struct query *q)
{
unpack_dname(p, q->q_dname, sizeof(q->q_dname));
unpack_u16(p, &q->q_type);
unpack_u16(p, &q->q_class);
return (p->err) ? (-1) : (0);
}
int
unpack_rr(struct packed *p, struct rr *rr)
{
uint16_t rdlen;
size_t save_offset;
unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname));
unpack_u16(p, &rr->rr_type);
unpack_u16(p, &rr->rr_class);
unpack_u32(p, &rr->rr_ttl);
unpack_u16(p, &rdlen);
if (p->err)
return (-1);
if (p->len - p->offset < rdlen) {
p->err = "too short";
return (-1);
}
save_offset = p->offset;
switch(rr->rr_type) {
case T_CNAME:
unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname));
break;
case T_MX:
unpack_u16(p, &rr->rr.mx.preference);
unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange));
break;
case T_NS:
unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname));
break;
case T_PTR:
unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname));
break;
case T_SOA:
unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname));
unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname));
unpack_u32(p, &rr->rr.soa.serial);
unpack_u32(p, &rr->rr.soa.refresh);
unpack_u32(p, &rr->rr.soa.retry);
unpack_u32(p, &rr->rr.soa.expire);
unpack_u32(p, &rr->rr.soa.minimum);
break;
case T_A:
if (rr->rr_class != C_IN)
goto other;
unpack_inaddr(p, &rr->rr.in_a.addr);
break;
case T_AAAA:
if (rr->rr_class != C_IN)
goto other;
unpack_in6addr(p, &rr->rr.in_aaaa.addr6);
break;
default:
other:
rr->rr.other.rdata = p->data + p->offset;
rr->rr.other.rdlen = rdlen;
p->offset += rdlen;
}
if (p->err)
return (-1);
if (p->offset - save_offset != rdlen)
p->err = "bad dlen";
return (p->err) ? (-1) : (0);
}
int
sockaddr_from_str(struct sockaddr *sa, int family, const char *str)
{
struct in_addr ina;
struct in6_addr in6a;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
switch (family) {
case PF_UNSPEC:
if (sockaddr_from_str(sa, PF_INET, str) == 0)
return (0);
return sockaddr_from_str(sa, PF_INET6, str);
case PF_INET:
if (inet_pton(PF_INET, str, &ina) != 1)
return (-1);
sin = (struct sockaddr_in *)sa;
memset(sin, 0, sizeof *sin);
sin->sin_len = sizeof(struct sockaddr_in);
sin->sin_family = PF_INET;
sin->sin_addr.s_addr = ina.s_addr;
return (0);
case PF_INET6:
if (inet_pton(PF_INET6, str, &in6a) != 1)
return (-1);
sin6 = (struct sockaddr_in6 *)sa;
memset(sin6, 0, sizeof *sin6);
sin6->sin6_len = sizeof(struct sockaddr_in6);
sin6->sin6_family = PF_INET6;
sin6->sin6_addr = in6a;
return (0);
default:
break;
}
return (-1);
}