/* darkstat 3
- * copyright (c) 2001-2008 Emil Mikulic.
+ * copyright (c) 2001-2011 Emil Mikulic.
*
* dns.c: synchronous DNS in a child process.
*
* GNU General Public License version 2. (see COPYING.GPL)
*/
-#include "darkstat.h"
+#include "cdefs.h"
#include "conv.h"
#include "decode.h"
#include "dns.h"
#include "err.h"
#include "hosts_db.h"
#include "queue.h"
+#include "str.h"
#include "tree.h"
+#include "bsd.h" /* for setproctitle, strlcpy */
#include <sys/param.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
-static void dns_main(void); /* this is what the child process runs */
+#ifdef __NetBSD__
+# define gethostbyaddr(addr, len, type) \
+ gethostbyaddr((const char *)(addr), len, type)
+#endif
+
+static void dns_main(void) _noreturn_; /* the child process runs this */
#define CHILD 0 /* child process uses this socket */
#define PARENT 1
static pid_t pid = -1;
struct dns_reply {
- struct addr46 addr;
+ struct addr addr;
int error; /* for gai_strerror(), or 0 if no error */
- char name[MAXHOSTNAMELEN];
+ char name[256]; /* http://tools.ietf.org/html/rfc1034#section-3.1 */
};
void
struct tree_rec {
RB_ENTRY(tree_rec) ptree;
- struct addr46 ip;
+ struct addr ip;
};
static int
tree_cmp(struct tree_rec *a, struct tree_rec *b)
{
- if (a->ip.af != b->ip.af)
+ if (a->ip.family != b->ip.family)
/* Sort IPv4 to the left of IPv6. */
- return (a->ip.af == AF_INET ? -1 : +1);
+ return ((a->ip.family == IPv4) ? -1 : +1);
- if (a->ip.af == AF_INET) {
- return (memcmp(&a->ip.addr.ip, &b->ip.addr.ip, sizeof(a->ip.addr.ip)));
+ if (a->ip.family == IPv4)
+ return (memcmp(&a->ip.ip.v4, &b->ip.ip.v4, sizeof(a->ip.ip.v4)));
+ else {
+ assert(a->ip.family == IPv6);
+ return (memcmp(&a->ip.ip.v6, &b->ip.ip.v6, sizeof(a->ip.ip.v6)));
}
-
- /* AF_INET6 should remain. */
- if (a->ip.af == AF_INET6)
- return (memcmp(&a->ip.addr.ip6, &b->ip.addr.ip6, sizeof(a->ip.addr.ip6)));
-
- /* Last resort. */
- return -1;
}
static RB_HEAD(tree_t, tree_rec) ip_tree = RB_INITIALIZER(&tree_rec);
RB_GENERATE(tree_t, tree_rec, ptree, tree_cmp)
void
-dns_queue(const struct addr46 *const ipaddr)
+dns_queue(const struct addr *const ipaddr)
{
struct tree_rec *rec;
ssize_t num_w;
if (pid == -1)
return; /* no child was started - we're not doing any DNS */
- if (ipaddr->af != AF_INET && ipaddr->af != AF_INET6) {
- verbosef("dns_queue() for unknown family %d.\n", ipaddr->af);
+ if ((ipaddr->family != IPv4) && (ipaddr->family != IPv6)) {
+ verbosef("dns_queue() for unknown family %d", ipaddr->family);
return;
}
if (RB_INSERT(tree_t, &ip_tree, rec) != NULL) {
/* Already queued - this happens seldom enough that we don't care about
* the performance hit of needlessly malloc()ing. */
- verbosef("already queued %s", ip_to_str(ipaddr));
+ verbosef("already queued %s", addr_to_str(ipaddr));
free(rec);
return;
}
else if (num_w == -1)
warn("dns_queue: ignoring write error");
else if (num_w != sizeof(*ipaddr))
- err(1, "dns_queue: wrote %z instead of %z", num_w, sizeof(*ipaddr));
+ err(1, "dns_queue: wrote %zu instead of %zu", num_w, sizeof(*ipaddr));
}
static void
-dns_unqueue(const struct addr46 *const ipaddr)
+dns_unqueue(const struct addr *const ipaddr)
{
struct tree_rec tmp, *rec;
free(rec);
}
else
- verbosef("couldn't unqueue %s - not in queue!", ip_to_str(ipaddr));
+ verbosef("couldn't unqueue %s - not in queue!", addr_to_str(ipaddr));
}
/*
* (name buffer is allocated by dns_poll)
*/
static int
-dns_get_result(struct addr46 *ipaddr, char **name)
+dns_get_result(struct addr *ipaddr, char **name)
{
struct dns_reply reply;
ssize_t numread;
if (numread == 0)
goto error; /* EOF */
if (numread != sizeof(reply))
- errx(1, "dns_get_result read got %z, expected %z", numread, sizeof(reply));
+ errx(1, "dns_get_result read got %zu, expected %zu",
+ numread, sizeof(reply));
/* Return successful reply. */
memcpy(ipaddr, &reply.addr, sizeof(*ipaddr));
-#if DARKSTAT_USES_HOSTENT
- if (reply.error != 0)
- xasprintf(name, "(%s)", hstrerror(reply.error));
- else
- *name = xstrdup(reply.name);
-#else /* !DARKSTAT_USES_HOSTENT */
if (reply.error != 0) {
/* Identify common special cases. */
- char *type = "none";
+ const char *type = "none";
- if (reply.addr.af == AF_INET6) {
- if (IN6_IS_ADDR_LINKLOCAL(&reply.addr.addr.ip6))
+ if (reply.addr.family == IPv6) {
+ if (IN6_IS_ADDR_LINKLOCAL(&reply.addr.ip.v6))
type = "link-local";
- else if (IN6_IS_ADDR_SITELOCAL(&reply.addr.addr.ip6))
+ else if (IN6_IS_ADDR_SITELOCAL(&reply.addr.ip.v6))
type = "site-local";
- else if (IN6_IS_ADDR_MULTICAST(&reply.addr.addr.ip6))
+ else if (IN6_IS_ADDR_MULTICAST(&reply.addr.ip.v6))
type = "multicast";
- } else { /* AF_INET */
- if (IN_MULTICAST(reply.addr.addr.ip.s_addr))
+ } else {
+ assert(reply.addr.family == IPv4);
+ if (IN_MULTICAST(htonl(reply.addr.ip.v4)))
type = "multicast";
}
xasprintf(name, "(%s)", type);
}
else /* Correctly resolved name. */
*name = xstrdup(reply.name);
-#endif /* !DARKSTAT_USES_HOSTENT */
dns_unqueue(&reply.addr);
return (1);
void
dns_poll(void)
{
- struct addr46 ip;
+ struct addr ip;
char *name;
if (pid == -1)
if (b == NULL) {
verbosef("resolved %s to %s but it's not in the DB!",
- ip_to_str(&ip), name);
+ addr_to_str(&ip), name);
return;
}
if (b->u.host.dns != NULL) {
verbosef("resolved %s to %s but it's already in the DB!",
- ip_to_str(&ip), name);
+ addr_to_str(&ip), name);
return;
}
b->u.host.dns = name;
struct qitem {
STAILQ_ENTRY(qitem) entries;
- struct addr46 ip;
+ struct addr ip;
};
STAILQ_HEAD(qhead, qitem) queue = STAILQ_HEAD_INITIALIZER(queue);
static void
-enqueue(const struct addr46 *const ip)
+enqueue(const struct addr *const ip)
{
struct qitem *i;
i = xmalloc(sizeof(*i));
memcpy(&i->ip, ip, sizeof(i->ip));
STAILQ_INSERT_TAIL(&queue, i, entries);
- verbosef("DNS: enqueued %s", ip_to_str(ip));
+ verbosef("DNS: enqueued %s", addr_to_str(ip));
}
/* Return non-zero and populate <ip> pointer if queue isn't empty. */
static int
-dequeue(struct addr46 *ip)
+dequeue(struct addr *ip)
{
struct qitem *i;
STAILQ_REMOVE_HEAD(&queue, entries);
memcpy(ip, &i->ip, sizeof(*ip));
free(i);
- verbosef("DNS: dequeued %s", ip_to_str(ip));
+ verbosef("DNS: dequeued %s", addr_to_str(ip));
return 1;
}
static void
dns_main(void)
{
- struct addr46 ip;
+ struct addr ip;
-#ifdef HAVE_SETPROCTITLE
setproctitle("DNS child");
-#endif
fd_set_nonblock(sock[CHILD]);
verbosef("DNS child entering main DNS loop");
for (;;) {
err(1, "DNS: read failed");
}
if (numread != sizeof(ip))
- err(1, "DNS: read got %z bytes, expecting %z", numread, sizeof(ip));
+ err(1, "DNS: read got %zu bytes, expecting %zu",
+ numread, sizeof(ip));
enqueue(&ip);
if (blocking) {
/* After one blocking read, become non-blocking so that when we
/* Process queue. */
if (dequeue(&ip)) {
struct dns_reply reply;
-#if DARKSTAT_USES_HOSTENT
- struct hostent *he;
-
- memcpy(&reply.addr, &ip, sizeof(reply.addr));
- he = gethostbyaddr((char *)&ip.addr.ip, sizeof(ip.addr.ip), ip.af); /* TODO MEA */
-
- /* On some platforms (for example Linux with GLIBC 2.3.3), h_errno
- * will be non-zero here even though the lookup succeeded.
- */
- if (he == NULL) {
- reply.name[0] = '\0';
- reply.error = h_errno;
- } else {
- assert(sizeof(reply.name) > sizeof(char *)); /* not just a ptr */
- strlcpy(reply.name, he->h_name, sizeof(reply.name));
- reply.error = 0;
- }
- fd_set_block(sock[CHILD]);
- xwrite(sock[CHILD], &reply, sizeof(reply));
- verbosef("DNS: %s is %s", ip_to_str(&reply.addr),
- (h_errno == 0)?reply.name:hstrerror(h_errno));
-#else /* !DARKSTAT_USES_HOSTENT */
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
+ struct hostent *he;
char host[NI_MAXHOST];
int ret, flags;
- reply.addr.af = ip.af;
+ reply.addr = ip;
flags = NI_NAMEREQD;
# ifdef NI_IDN
flags |= NI_IDN;
# endif
- switch (ip.af) {
- case AF_INET:
- sin.sin_family = ip.af;
- memcpy(&reply.addr.addr.ip, &ip.addr.ip, sizeof(reply.addr.addr.ip));
- memcpy(&sin.sin_addr, &ip.addr.ip, sizeof(sin.sin_addr));
+ switch (ip.family) {
+ case IPv4:
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = ip.ip.v4;
ret = getnameinfo((struct sockaddr *) &sin, sizeof(sin),
host, sizeof(host), NULL, 0, flags);
+ if (ret == EAI_FAMILY) {
+ verbosef("getnameinfo error %s, trying gethostbyname",
+ gai_strerror(ret));
+ he = gethostbyaddr(&sin.sin_addr.s_addr,
+ sizeof(sin.sin_addr.s_addr), sin.sin_family);
+ if (he == NULL) {
+ ret = EAI_FAIL;
+ verbosef("gethostbyname error %s", hstrerror(h_errno));
+ } else {
+ ret = 0;
+ strlcpy(host, he->h_name, sizeof(host));
+ }
+ }
break;
- case AF_INET6:
- sin6.sin6_family = ip.af;
- memcpy(&reply.addr.addr.ip6, &ip.addr.ip6, sizeof(reply.addr.addr.ip6));
- memcpy(&sin6.sin6_addr, &ip.addr.ip6, sizeof(sin6.sin6_addr));
+ case IPv6:
+ sin6.sin6_family = AF_INET6;
+ memcpy(&sin6.sin6_addr, &ip.ip.v6, sizeof(sin6.sin6_addr));
ret = getnameinfo((struct sockaddr *) &sin6, sizeof(sin6),
host, sizeof(host), NULL, 0, flags);
break;
default:
ret = EAI_FAMILY;
-
}
if (ret != 0) {
}
fd_set_block(sock[CHILD]);
xwrite(sock[CHILD], &reply, sizeof(reply));
- verbosef("DNS: %s is \"%s\".", ip_to_str(&reply.addr),
+ verbosef("DNS: %s is \"%s\".", addr_to_str(&reply.addr),
(ret == 0) ? reply.name : gai_strerror(ret));
-#endif /* !DARKSTAT_USES_HOSTENT */
}
}
}