X-Git-Url: https://unix4lyfe.org/gitweb/darkstat/blobdiff_plain/f2e0493cd79657110cf40fdcfb022e27444120d0..0736c704e88399324e1a3c5d484cb116458c23b1:/dns.c diff --git a/dns.c b/dns.c index 125ab62..163aa56 100644 --- a/dns.c +++ b/dns.c @@ -1,5 +1,5 @@ /* darkstat 3 - * copyright (c) 2001-2008 Emil Mikulic. + * copyright (c) 2001-2014 Emil Mikulic. * * dns.c: synchronous DNS in a child process. * @@ -7,14 +7,17 @@ * 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 "err.h" #include "hosts_db.h" #include "queue.h" +#include "str.h" #include "tree.h" +#include "bsd.h" /* for setproctitle, strlcpy */ #include #include @@ -27,23 +30,28 @@ #include #include -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 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 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(); @@ -53,19 +61,19 @@ dns_init(const char *privdrop_user) 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); } } @@ -75,7 +83,7 @@ dns_stop(void) { 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"); @@ -86,38 +94,29 @@ dns_stop(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); -/* 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 addr46 *const ipaddr) +dns_queue(const struct addr *const ipaddr) { struct tree_rec *rec; ssize_t num_w; @@ -125,8 +124,8 @@ dns_queue(const struct addr46 *const ipaddr) 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; } @@ -136,22 +135,22 @@ dns_queue(const struct addr46 *const ipaddr) 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; } - 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 -dns_unqueue(const struct addr46 *const ipaddr) +dns_unqueue(const struct addr *const ipaddr) { struct tree_rec tmp, *rec; @@ -161,7 +160,7 @@ dns_unqueue(const struct addr46 *const ipaddr) 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)); } /* @@ -169,12 +168,12 @@ dns_unqueue(const struct addr46 *const 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; - 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 */ @@ -184,36 +183,31 @@ dns_get_result(struct addr46 *ipaddr, char **name) 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); @@ -227,7 +221,7 @@ error: void dns_poll(void) { - struct addr46 ip; + struct addr ip; char *name; if (pid == -1) @@ -239,12 +233,12 @@ dns_poll(void) 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; @@ -255,25 +249,25 @@ dns_poll(void) struct qitem { STAILQ_ENTRY(qitem) entries; - struct addr46 ip; + 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 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 pointer if queue isn't empty. */ static int -dequeue(struct addr46 *ip) +dequeue(struct addr *ip) { struct qitem *i; @@ -283,7 +277,7 @@ dequeue(struct addr46 *ip) 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; } @@ -301,28 +295,26 @@ xwrite(const int d, const void *buf, const size_t nbytes) static void dns_main(void) { - struct addr46 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) { @@ -332,70 +324,60 @@ dns_main(void) 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", 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; - + errx(1, "unexpected ip.family = %d", ip.family); } if (ret != 0) { @@ -406,11 +388,10 @@ dns_main(void) strlcpy(reply.name, host, 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), + 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 */ } } }