"Fossies" - the Fresh Open Source Software Archive

Member "darkstat-3.0.721/dns.c" (12 Jan 2022, 11015 Bytes) of package /linux/privat/darkstat-3.0.721.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /* darkstat 3
    2  * copyright (c) 2001-2014 Emil Mikulic.
    3  *
    4  * dns.c: synchronous DNS in a child process.
    5  *
    6  * You may use, modify and redistribute this file under the terms of the
    7  * GNU General Public License version 2. (see COPYING.GPL)
    8  */
    9 
   10 #include "cdefs.h"
   11 #include "cap.h"
   12 #include "conv.h"
   13 #include "decode.h"
   14 #include "dns.h"
   15 #include "err.h"
   16 #include "hosts_db.h"
   17 #include "queue.h"
   18 #include "str.h"
   19 #include "tree.h"
   20 #include "bsd.h" /* for setproctitle, strlcpy */
   21 
   22 #include <sys/param.h>
   23 #include <sys/socket.h>
   24 #include <sys/wait.h>
   25 #include <assert.h>
   26 #include <errno.h>
   27 #include <netdb.h>
   28 #include <signal.h>
   29 #include <stdlib.h>
   30 #include <string.h>
   31 #include <unistd.h>
   32 
   33 #ifdef __NetBSD__
   34 # define gethostbyaddr(addr, len, type) \
   35          gethostbyaddr((const char *)(addr), len, type)
   36 #endif
   37 
   38 static void dns_main(void) _noreturn_; /* the child process runs this */
   39 
   40 #define CHILD 0 /* child process uses this socket */
   41 #define PARENT 1
   42 static int dns_sock[2];
   43 static pid_t pid = -1;
   44 
   45 struct dns_reply {
   46    struct addr addr;
   47    int error; /* for gai_strerror(), or 0 if no error */
   48    char name[256]; /* http://tools.ietf.org/html/rfc1034#section-3.1 */
   49 };
   50 
   51 void
   52 dns_init(const char *privdrop_user)
   53 {
   54    if (socketpair(AF_UNIX, SOCK_STREAM, 0, dns_sock) == -1)
   55       err(1, "socketpair");
   56 
   57    pid = fork();
   58    if (pid == -1)
   59       err(1, "fork");
   60 
   61    if (pid == 0) {
   62       /* We are the child. */
   63       privdrop(NULL /* don't chroot */, privdrop_user);
   64       close(dns_sock[PARENT]);
   65       dns_sock[PARENT] = -1;
   66       daemonize_finish(); /* drop our copy of the lifeline! */
   67       if (signal(SIGUSR1, SIG_IGN) == SIG_ERR)
   68          errx(1, "signal(SIGUSR1, ignore) failed");
   69       cap_free_args();
   70       dns_main();
   71    } else {
   72       /* We are the parent. */
   73       close(dns_sock[CHILD]);
   74       dns_sock[CHILD] = -1;
   75       fd_set_nonblock(dns_sock[PARENT]);
   76       verbosef("DNS child has PID %d", pid);
   77    }
   78 }
   79 
   80 void
   81 dns_stop(void)
   82 {
   83    if (pid == -1)
   84       return; /* no child was started */
   85    close(dns_sock[PARENT]);
   86    if (kill(pid, SIGINT) == -1)
   87       err(1, "kill");
   88    verbosef("dns_stop() waiting for child");
   89    if (waitpid(pid, NULL, 0) == -1)
   90       err(1, "waitpid");
   91    verbosef("dns_stop() done waiting for child");
   92 }
   93 
   94 struct tree_rec {
   95    RB_ENTRY(tree_rec) ptree;
   96    struct addr ip;
   97 };
   98 
   99 static int
  100 tree_cmp(struct tree_rec *a, struct tree_rec *b)
  101 {
  102    if (a->ip.family != b->ip.family)
  103       /* Sort IPv4 to the left of IPv6.  */
  104       return ((a->ip.family == IPv4) ? -1 : +1);
  105 
  106    if (a->ip.family == IPv4)
  107       return (memcmp(&a->ip.ip.v4, &b->ip.ip.v4, sizeof(a->ip.ip.v4)));
  108    else {
  109       assert(a->ip.family == IPv6);
  110       return (memcmp(&a->ip.ip.v6, &b->ip.ip.v6, sizeof(a->ip.ip.v6)));
  111    }
  112 }
  113 
  114 static RB_HEAD(tree_t, tree_rec) ip_tree = RB_INITIALIZER(&tree_rec);
  115 RB_GENERATE_STATIC(tree_t, tree_rec, ptree, tree_cmp)
  116 
  117 void
  118 dns_queue(const struct addr *const ipaddr)
  119 {
  120    struct tree_rec *rec;
  121    ssize_t num_w;
  122 
  123    if (pid == -1)
  124       return; /* no child was started - we're not doing any DNS */
  125 
  126    if ((ipaddr->family != IPv4) && (ipaddr->family != IPv6)) {
  127       verbosef("dns_queue() for unknown family %d", ipaddr->family);
  128       return;
  129    }
  130 
  131    rec = xmalloc(sizeof(*rec));
  132    memcpy(&rec->ip, ipaddr, sizeof(rec->ip));
  133 
  134    if (RB_INSERT(tree_t, &ip_tree, rec) != NULL) {
  135       /* Already queued - this happens seldom enough that we don't care about
  136        * the performance hit of needlessly malloc()ing. */
  137       verbosef("already queued %s", addr_to_str(ipaddr));
  138       free(rec);
  139       return;
  140    }
  141 
  142    num_w = write(dns_sock[PARENT], ipaddr, sizeof(*ipaddr)); /* won't block */
  143    if (num_w == 0)
  144       warnx("dns_queue: write: ignoring end of file");
  145    else if (num_w == -1)
  146       warn("dns_queue: ignoring write error");
  147    else if (num_w != sizeof(*ipaddr))
  148       err(1, "dns_queue: wrote %zu instead of %zu", num_w, sizeof(*ipaddr));
  149 }
  150 
  151 static void
  152 dns_unqueue(const struct addr *const ipaddr)
  153 {
  154    struct tree_rec tmp, *rec;
  155 
  156    memcpy(&tmp.ip, ipaddr, sizeof(tmp.ip));
  157    if ((rec = RB_FIND(tree_t, &ip_tree, &tmp)) != NULL) {
  158       RB_REMOVE(tree_t, &ip_tree, rec);
  159       free(rec);
  160    }
  161    else
  162       verbosef("couldn't unqueue %s - not in queue!", addr_to_str(ipaddr));
  163 }
  164 
  165 /*
  166  * Returns non-zero if result waiting, stores IP and name into given pointers
  167  * (name buffer is allocated by dns_poll)
  168  */
  169 static int
  170 dns_get_result(struct addr *ipaddr, char **name)
  171 {
  172    struct dns_reply reply;
  173    ssize_t numread;
  174 
  175    numread = read(dns_sock[PARENT], &reply, sizeof(reply));
  176    if (numread == -1) {
  177       if (errno == EAGAIN)
  178          return (0); /* no input waiting */
  179       else
  180          goto error;
  181    }
  182    if (numread == 0)
  183       goto error; /* EOF */
  184    if (numread != sizeof(reply))
  185       errx(1, "dns_get_result read got %zu, expected %zu",
  186          numread, sizeof(reply));
  187 
  188    /* Return successful reply. */
  189    memcpy(ipaddr, &reply.addr, sizeof(*ipaddr));
  190    if (reply.error != 0) {
  191       /* Identify common special cases.  */
  192       const char *type = "none";
  193 
  194       if (reply.addr.family == IPv6) {
  195          if (IN6_IS_ADDR_LINKLOCAL(&reply.addr.ip.v6))
  196             type = "link-local";
  197          else if (IN6_IS_ADDR_SITELOCAL(&reply.addr.ip.v6))
  198             type = "site-local";
  199          else if (IN6_IS_ADDR_MULTICAST(&reply.addr.ip.v6))
  200             type = "multicast";
  201       } else {
  202          assert(reply.addr.family == IPv4);
  203          if (IN_MULTICAST(htonl(reply.addr.ip.v4)))
  204             type = "multicast";
  205       }
  206       xasprintf(name, "(%s)", type);
  207    }
  208    else  /* Correctly resolved name.  */
  209       *name = xstrdup(reply.name);
  210 
  211    dns_unqueue(&reply.addr);
  212    return (1);
  213 
  214 error:
  215    warn("dns_get_result: ignoring read error");
  216    /* FIXME: re-align to stream?  restart dns child? */
  217    return (0);
  218 }
  219 
  220 void
  221 dns_poll(void)
  222 {
  223    struct addr ip;
  224    char *name;
  225 
  226    if (pid == -1)
  227       return; /* no child was started - we're not doing any DNS */
  228 
  229    while (dns_get_result(&ip, &name)) {
  230       /* push into hosts_db */
  231       struct bucket *b = host_find(&ip);
  232 
  233       if (b == NULL) {
  234          verbosef("resolved %s to %s but it's not in the DB!",
  235             addr_to_str(&ip), name);
  236          return;
  237       }
  238       if (b->u.host.dns != NULL) {
  239          verbosef("resolved %s to %s but it's already in the DB!",
  240             addr_to_str(&ip), name);
  241          return;
  242       }
  243       b->u.host.dns = name;
  244    }
  245 }
  246 
  247 /* ------------------------------------------------------------------------ */
  248 
  249 struct qitem {
  250    STAILQ_ENTRY(qitem) entries;
  251    struct addr ip;
  252 };
  253 
  254 static STAILQ_HEAD(qhead, qitem) queue = STAILQ_HEAD_INITIALIZER(queue);
  255 
  256 static void
  257 enqueue(const struct addr *const ip)
  258 {
  259    struct qitem *i;
  260 
  261    i = xmalloc(sizeof(*i));
  262    memcpy(&i->ip, ip, sizeof(i->ip));
  263    STAILQ_INSERT_TAIL(&queue, i, entries);
  264    verbosef("DNS: enqueued %s", addr_to_str(ip));
  265 }
  266 
  267 /* Return non-zero and populate <ip> pointer if queue isn't empty. */
  268 static int
  269 dequeue(struct addr *ip)
  270 {
  271    struct qitem *i;
  272 
  273    i = STAILQ_FIRST(&queue);
  274    if (i == NULL)
  275       return (0);
  276    STAILQ_REMOVE_HEAD(&queue, entries);
  277    memcpy(ip, &i->ip, sizeof(*ip));
  278    free(i);
  279    verbosef("DNS: dequeued %s", addr_to_str(ip));
  280    return 1;
  281 }
  282 
  283 static void
  284 xwrite(const int d, const void *buf, const size_t nbytes)
  285 {
  286    ssize_t ret = write(d, buf, nbytes);
  287 
  288    if (ret == -1)
  289       err(1, "write");
  290    if (ret != (ssize_t)nbytes)
  291       err(1, "wrote %d bytes instead of all %d bytes", (int)ret, (int)nbytes);
  292 }
  293 
  294 static void
  295 dns_main(void)
  296 {
  297    struct addr ip;
  298 
  299    setproctitle("DNS child");
  300    fd_set_nonblock(dns_sock[CHILD]);
  301    verbosef("DNS child entering main DNS loop");
  302    for (;;) {
  303       int blocking;
  304 
  305       if (STAILQ_EMPTY(&queue)) {
  306          blocking = 1;
  307          fd_set_block(dns_sock[CHILD]);
  308          verbosef("entering blocking read loop");
  309       } else {
  310          blocking = 0;
  311          fd_set_nonblock(dns_sock[CHILD]);
  312          verbosef("non-blocking poll");
  313       }
  314       for (;;) {
  315          /* While we have input to process... */
  316          ssize_t numread = read(dns_sock[CHILD], &ip, sizeof(ip));
  317          if (numread == 0)
  318             exit(0); /* end of file, nothing more to do here. */
  319          if (numread == -1) {
  320             if (!blocking && (errno == EAGAIN))
  321                break; /* ran out of input */
  322             /* else error */
  323             err(1, "DNS: read failed");
  324          }
  325          if (numread != sizeof(ip))
  326             err(1, "DNS: read got %zu bytes, expecting %zu",
  327                numread, sizeof(ip));
  328          enqueue(&ip);
  329          if (blocking) {
  330             /* After one blocking read, become non-blocking so that when we
  331              * run out of input we fall through to queue processing.
  332              */
  333             blocking = 0;
  334             fd_set_nonblock(dns_sock[CHILD]);
  335          }
  336       }
  337 
  338       /* Process queue. */
  339       if (dequeue(&ip)) {
  340          struct dns_reply reply;
  341          struct sockaddr_in sin;
  342          struct sockaddr_in6 sin6;
  343          struct hostent *he;
  344          char host[NI_MAXHOST];
  345          int ret, flags;
  346 
  347          reply.addr = ip;
  348          flags = NI_NAMEREQD;
  349 #  ifdef NI_IDN
  350          flags |= NI_IDN;
  351 #  endif
  352          switch (ip.family) {
  353             case IPv4:
  354                sin.sin_family = AF_INET;
  355                sin.sin_addr.s_addr = ip.ip.v4;
  356                ret = getnameinfo((struct sockaddr *) &sin, sizeof(sin),
  357                                  host, sizeof(host), NULL, 0, flags);
  358                if (ret == EAI_FAMILY) {
  359                   verbosef("getnameinfo error %s, trying gethostbyname",
  360                      gai_strerror(ret));
  361                   he = gethostbyaddr(&sin.sin_addr.s_addr,
  362                      sizeof(sin.sin_addr.s_addr), sin.sin_family);
  363                   if (he == NULL) {
  364                      ret = EAI_FAIL;
  365                      verbosef("gethostbyname error %s", hstrerror(h_errno));
  366                   } else {
  367                      ret = 0;
  368                      strlcpy(host, he->h_name, sizeof(host));
  369                   }
  370                }
  371                break;
  372             case IPv6:
  373                sin6.sin6_family = AF_INET6;
  374                memcpy(&sin6.sin6_addr, &ip.ip.v6, sizeof(sin6.sin6_addr));
  375                ret = getnameinfo((struct sockaddr *) &sin6, sizeof(sin6),
  376                                  host, sizeof(host), NULL, 0, flags);
  377                break;
  378             default:
  379                errx(1, "unexpected ip.family = %d", ip.family);
  380          }
  381 
  382          if (ret != 0) {
  383             reply.name[0] = '\0';
  384             reply.error = ret;
  385          } else {
  386             assert(sizeof(reply.name) > sizeof(char *)); /* not just a ptr */
  387             strlcpy(reply.name, host, sizeof(reply.name));
  388             reply.error = 0;
  389          }
  390          fd_set_block(dns_sock[CHILD]);
  391          xwrite(dns_sock[CHILD], &reply, sizeof(reply));
  392          verbosef("DNS: %s is \"%s\".", addr_to_str(&reply.addr),
  393             (ret == 0) ? reply.name : gai_strerror(ret));
  394       }
  395    }
  396 }
  397 
  398 /* vim:set ts=3 sw=3 tw=78 expandtab: */