/* darkstat 3
- * copyright (c) 2001-2008 Emil Mikulic.
+ * copyright (c) 2001-2014 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 "cap.h"
#include "conv.h"
#include "decode.h"
#include "dns.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 int sock[2];
+static int dns_sock[2];
static pid_t pid = -1;
struct dns_reply {
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
dns_init(const char *privdrop_user)
{
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) == -1)
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, dns_sock) == -1)
err(1, "socketpair");
pid = fork();
if (pid == 0) {
/* We are the child. */
privdrop(NULL /* don't chroot */, privdrop_user);
- close(sock[PARENT]);
- sock[PARENT] = -1;
+ close(dns_sock[PARENT]);
+ dns_sock[PARENT] = -1;
daemonize_finish(); /* drop our copy of the lifeline! */
if (signal(SIGUSR1, SIG_IGN) == SIG_ERR)
errx(1, "signal(SIGUSR1, ignore) failed");
+ cap_free_args();
dns_main();
- verbosef("fell out of dns_main()");
- exit(0);
+ errx(1, "DNS child fell out of dns_main()");
} else {
/* We are the parent. */
- close(sock[CHILD]);
- sock[CHILD] = -1;
- fd_set_nonblock(sock[PARENT]);
+ close(dns_sock[CHILD]);
+ dns_sock[CHILD] = -1;
+ fd_set_nonblock(dns_sock[PARENT]);
verbosef("DNS child has PID %d", pid);
}
}
{
if (pid == -1)
return; /* no child was started */
- close(sock[PARENT]);
+ close(dns_sock[PARENT]);
if (kill(pid, SIGINT) == -1)
err(1, "kill");
verbosef("dns_stop() waiting for child");
}
static RB_HEAD(tree_t, tree_rec) ip_tree = RB_INITIALIZER(&tree_rec);
-/* Quiet warnings. */
-static struct tree_rec * tree_t_RB_NEXT(struct tree_rec *elm)
- _unused_;
-static struct tree_rec * tree_t_RB_MINMAX(struct tree_t *head, int val)
- _unused_;
-RB_GENERATE(tree_t, tree_rec, ptree, tree_cmp)
+RB_GENERATE_STATIC(tree_t, tree_rec, ptree, tree_cmp)
void
dns_queue(const struct addr *const ipaddr)
return;
}
- num_w = write(sock[PARENT], ipaddr, sizeof(*ipaddr)); /* won't block */
+ num_w = write(dns_sock[PARENT], ipaddr, sizeof(*ipaddr)); /* won't block */
if (num_w == 0)
warnx("dns_queue: write: ignoring end of file");
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
struct dns_reply reply;
ssize_t numread;
- numread = read(sock[PARENT], &reply, sizeof(reply));
+ numread = read(dns_sock[PARENT], &reply, sizeof(reply));
if (numread == -1) {
if (errno == EAGAIN)
return (0); /* no input waiting */
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. */
const char *type = "none";
type = "site-local";
else if (IN6_IS_ADDR_MULTICAST(&reply.addr.ip.v6))
type = "multicast";
- } else { /* AF_INET */
- if (IN_MULTICAST(reply.addr.ip.v4))
+ } 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);
struct addr ip;
};
-STAILQ_HEAD(qhead, qitem) queue = STAILQ_HEAD_INITIALIZER(queue);
+static STAILQ_HEAD(qhead, qitem) queue = STAILQ_HEAD_INITIALIZER(queue);
static void
enqueue(const struct addr *const ip)
{
struct addr ip;
-#ifdef HAVE_SETPROCTITLE
setproctitle("DNS child");
-#endif
- fd_set_nonblock(sock[CHILD]);
+ fd_set_nonblock(dns_sock[CHILD]);
verbosef("DNS child entering main DNS loop");
for (;;) {
int blocking;
if (STAILQ_EMPTY(&queue)) {
blocking = 1;
- fd_set_block(sock[CHILD]);
+ fd_set_block(dns_sock[CHILD]);
verbosef("entering blocking read loop");
} else {
blocking = 0;
- fd_set_nonblock(sock[CHILD]);
+ fd_set_nonblock(dns_sock[CHILD]);
verbosef("non-blocking poll");
}
for (;;) {
/* While we have input to process... */
- ssize_t numread = read(sock[CHILD], &ip, sizeof(ip));
+ ssize_t numread = read(dns_sock[CHILD], &ip, sizeof(ip));
if (numread == 0)
exit(0); /* end of file, nothing more to do here. */
if (numread == -1) {
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
* run out of input we fall through to queue processing.
*/
blocking = 0;
- fd_set_nonblock(sock[CHILD]);
+ fd_set_nonblock(dns_sock[CHILD]);
}
}
/* 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", addr_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;
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 IPv6:
sin6.sin6_family = AF_INET6;
host, sizeof(host), NULL, 0, flags);
break;
default:
- ret = EAI_FAMILY;
+ errx(1, "unexpected ip.family = %d", ip.family);
}
if (ret != 0) {
strlcpy(reply.name, host, sizeof(reply.name));
reply.error = 0;
}
- fd_set_block(sock[CHILD]);
- xwrite(sock[CHILD], &reply, sizeof(reply));
+ fd_set_block(dns_sock[CHILD]);
+ xwrite(dns_sock[CHILD], &reply, sizeof(reply));
verbosef("DNS: %s is \"%s\".", addr_to_str(&reply.addr),
(ret == 0) ? reply.name : gai_strerror(ret));
-#endif /* !DARKSTAT_USES_HOSTENT */
}
}
}