"Fossies" - the Fresh Open Source Software Archive

Member "nsd-4.3.6/dnstap/dnstap_collector.c" (6 Apr 2021, 15637 Bytes) of package /linux/misc/dns/nsd-4.3.6.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. For more information about "dnstap_collector.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.3.5_vs_4.3.6.

    1 /*
    2  * dnstap/dnstap_collector.c -- nsd collector process for dnstap information
    3  *
    4  * Copyright (c) 2018, NLnet Labs. All rights reserved.
    5  *
    6  * See LICENSE for the license.
    7  *
    8  */
    9 
   10 #include "config.h"
   11 #include <sys/types.h>
   12 #include <sys/socket.h>
   13 #include <errno.h>
   14 #include <fcntl.h>
   15 #include <unistd.h>
   16 #ifndef USE_MINI_EVENT
   17 #  ifdef HAVE_EVENT_H
   18 #    include <event.h>
   19 #  else
   20 #    include <event2/event.h>
   21 #    include "event2/event_struct.h"
   22 #    include "event2/event_compat.h"
   23 #  endif
   24 #else
   25 #  include "mini_event.h"
   26 #endif
   27 #include "dnstap/dnstap_collector.h"
   28 #include "dnstap/dnstap.h"
   29 #include "util.h"
   30 #include "nsd.h"
   31 #include "region-allocator.h"
   32 #include "buffer.h"
   33 #include "namedb.h"
   34 #include "options.h"
   35 
   36 struct dt_collector* dt_collector_create(struct nsd* nsd)
   37 {
   38     int i, sv[2];
   39     struct dt_collector* dt_col = (struct dt_collector*)xalloc_zero(
   40         sizeof(*dt_col));
   41     dt_col->count = nsd->child_count;
   42     dt_col->dt_env = NULL;
   43     dt_col->region = region_create(xalloc, free);
   44     dt_col->send_buffer = buffer_create(dt_col->region,
   45         /* msglen + is_response + addrlen + is_tcp + packetlen + packet + zonelen + zone + spare + local_addr + addr */
   46         4+1+4+1+4+TCP_MAX_MESSAGE_LEN+4+MAXHOSTNAMELEN + 32 +
   47 #ifdef INET6
   48         sizeof(struct sockaddr_storage) + sizeof(struct sockaddr_storage)
   49 #else
   50         sizeof(struct sockaddr_in) + sizeof(struct sockaddr_in)
   51 #endif
   52         );
   53 
   54     /* open pipes in struct nsd */
   55     nsd->dt_collector_fd_send = (int*)xalloc_array_zero(dt_col->count,
   56         sizeof(int));
   57     nsd->dt_collector_fd_recv = (int*)xalloc_array_zero(dt_col->count,
   58         sizeof(int));
   59     for(i=0; i<dt_col->count; i++) {
   60         int fd[2];
   61         fd[0] = -1;
   62         fd[1] = -1;
   63         if(pipe(fd) < 0) {
   64             error("dnstap_collector: cannot create pipe: %s",
   65                 strerror(errno));
   66         }
   67         if(fcntl(fd[0], F_SETFL, O_NONBLOCK) == -1) {
   68             log_msg(LOG_ERR, "fcntl failed: %s", strerror(errno));
   69         }
   70         if(fcntl(fd[1], F_SETFL, O_NONBLOCK) == -1) {
   71             log_msg(LOG_ERR, "fcntl failed: %s", strerror(errno));
   72         }
   73         nsd->dt_collector_fd_recv[i] = fd[0];
   74         nsd->dt_collector_fd_send[i] = fd[1];
   75     }
   76 
   77     /* open socketpair */
   78     if(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
   79         error("dnstap_collector: cannot create socketpair: %s",
   80             strerror(errno));
   81     }
   82     if(fcntl(sv[0], F_SETFL, O_NONBLOCK) == -1) {
   83         log_msg(LOG_ERR, "fcntl failed: %s", strerror(errno));
   84     }
   85     if(fcntl(sv[1], F_SETFL, O_NONBLOCK) == -1) {
   86         log_msg(LOG_ERR, "fcntl failed: %s", strerror(errno));
   87     }
   88     dt_col->cmd_socket_dt = sv[0];
   89     dt_col->cmd_socket_nsd = sv[1];
   90 
   91     return dt_col;
   92 }
   93 
   94 void dt_collector_destroy(struct dt_collector* dt_col, struct nsd* nsd)
   95 {
   96     if(!dt_col) return;
   97     free(nsd->dt_collector_fd_recv);
   98     nsd->dt_collector_fd_recv = NULL;
   99     free(nsd->dt_collector_fd_send);
  100     nsd->dt_collector_fd_send = NULL;
  101     region_destroy(dt_col->region);
  102     free(dt_col);
  103 }
  104 
  105 void dt_collector_close(struct dt_collector* dt_col, struct nsd* nsd)
  106 {
  107     int i;
  108     if(!dt_col) return;
  109     if(dt_col->cmd_socket_dt != -1) {
  110         close(dt_col->cmd_socket_dt);
  111         dt_col->cmd_socket_dt = -1;
  112     }
  113     if(dt_col->cmd_socket_nsd != -1) {
  114         close(dt_col->cmd_socket_nsd);
  115         dt_col->cmd_socket_nsd = -1;
  116     }
  117     for(i=0; i<dt_col->count; i++) {
  118         if(nsd->dt_collector_fd_recv[i] != -1) {
  119             close(nsd->dt_collector_fd_recv[i]);
  120             nsd->dt_collector_fd_recv[i] = -1;
  121         }
  122         if(nsd->dt_collector_fd_send[i] != -1) {
  123             close(nsd->dt_collector_fd_send[i]);
  124             nsd->dt_collector_fd_send[i] = -1;
  125         }
  126     }
  127 }
  128 
  129 /* handle command from nsd to dt collector.
  130  * mostly, check for fd closed, this means we have to exit */
  131 void
  132 dt_handle_cmd_from_nsd(int ATTR_UNUSED(fd), short event, void* arg)
  133 {
  134     struct dt_collector* dt_col = (struct dt_collector*)arg;
  135     if((event&EV_READ) != 0) {
  136         event_base_loopexit(dt_col->event_base, NULL);
  137     }
  138 }
  139 
  140 /* read data from fd into buffer, true when message is complete */
  141 static int read_into_buffer(int fd, struct buffer* buf)
  142 {
  143     size_t msglen;
  144     ssize_t r;
  145     if(buffer_position(buf) < 4) {
  146         /* read the length of the message */
  147         r = read(fd, buffer_current(buf), 4 - buffer_position(buf));
  148         if(r == -1) {
  149             if(errno == EAGAIN || errno == EINTR) {
  150                 /* continue to read later */
  151                 return 0;
  152             }
  153             log_msg(LOG_ERR, "dnstap collector: read failed: %s",
  154                 strerror(errno));
  155             return 0;
  156         }
  157         buffer_skip(buf, r);
  158         if(buffer_position(buf) < 4)
  159             return 0; /* continue to read more msglen later */
  160     }
  161 
  162     /* msglen complete */
  163     msglen = buffer_read_u32_at(buf, 0);
  164     /* assert we have enough space, if we don't and we wanted to continue,
  165      * we would have to skip the message somehow, but that should never
  166      * happen because send_buffer and receive_buffer have the same size */
  167     assert(buffer_capacity(buf) >= msglen + 4);
  168     r = read(fd, buffer_current(buf), msglen - (buffer_position(buf) - 4));
  169     if(r == -1) {
  170         if(errno == EAGAIN || errno == EINTR) {
  171             /* continue to read later */
  172             return 0;
  173         }
  174         log_msg(LOG_ERR, "dnstap collector: read failed: %s",
  175             strerror(errno));
  176         return 0;
  177     }
  178     buffer_skip(buf, r);
  179     if(buffer_position(buf) < 4 + msglen)
  180         return 0; /* read more msg later */
  181 
  182     /* msg complete */
  183     buffer_flip(buf);
  184     return 1;
  185 }
  186 
  187 /* submit the content of the buffer received to dnstap */
  188 static void
  189 dt_submit_content(struct dt_env* dt_env, struct buffer* buf)
  190 {
  191     uint8_t is_response, is_tcp;
  192 #ifdef INET6
  193     struct sockaddr_storage local_addr, addr;
  194 #else
  195     struct sockaddr_in local_addr, addr;
  196 #endif
  197     socklen_t addrlen;
  198     size_t pktlen;
  199     uint8_t* data;
  200     size_t zonelen;
  201     uint8_t* zone;
  202 
  203     /* parse content from buffer */
  204     if(!buffer_available(buf, 4+1+4)) return;
  205     buffer_skip(buf, 4); /* skip msglen */
  206     is_response = buffer_read_u8(buf);
  207     addrlen = buffer_read_u32(buf);
  208     if(addrlen > sizeof(local_addr) || addrlen > sizeof(addr)) return;
  209     if(!buffer_available(buf, 2*addrlen)) return;
  210     buffer_read(buf, &local_addr, addrlen);
  211     buffer_read(buf, &addr, addrlen);
  212     if(!buffer_available(buf, 1+4)) return;
  213     is_tcp = buffer_read_u8(buf);
  214     pktlen = buffer_read_u32(buf);
  215     if(!buffer_available(buf, pktlen)) return;
  216     data = buffer_current(buf);
  217     buffer_skip(buf, pktlen);
  218     if(!buffer_available(buf, 4)) return;
  219     zonelen = buffer_read_u32(buf);
  220     if(zonelen == 0) {
  221         zone = NULL;
  222     } else {
  223         if(zonelen > MAXDOMAINLEN) return;
  224         if(!buffer_available(buf, zonelen)) return;
  225         zone = buffer_current(buf);
  226         buffer_skip(buf, zonelen);
  227     }
  228 
  229     /* submit it */
  230     if(is_response) {
  231         dt_msg_send_auth_response(dt_env, &local_addr, &addr, is_tcp, zone,
  232             zonelen, data, pktlen);
  233     } else {
  234         dt_msg_send_auth_query(dt_env, &local_addr, &addr, is_tcp, zone,
  235             zonelen, data, pktlen);
  236     }
  237 }
  238 
  239 /* handle input from worker for dnstap */
  240 void
  241 dt_handle_input(int fd, short event, void* arg)
  242 {
  243     struct dt_collector_input* dt_input = (struct dt_collector_input*)arg;
  244     if((event&EV_READ) != 0) {
  245         /* read */
  246         if(!read_into_buffer(fd, dt_input->buffer))
  247             return;
  248 
  249         /* once data is complete, write it to dnstap */
  250         VERBOSITY(4, (LOG_INFO, "dnstap collector: received msg len %d",
  251             (int)buffer_remaining(dt_input->buffer)));
  252         if(dt_input->dt_collector->dt_env) {
  253             dt_submit_content(dt_input->dt_collector->dt_env,
  254                 dt_input->buffer);
  255         }
  256         
  257         /* clear buffer for next message */
  258         buffer_clear(dt_input->buffer);
  259     }
  260 }
  261 
  262 /* init dnstap */
  263 static void dt_init_dnstap(struct dt_collector* dt_col, struct nsd* nsd)
  264 {
  265     int num_workers = 1;
  266 #ifdef HAVE_CHROOT
  267     if(nsd->chrootdir && nsd->chrootdir[0]) {
  268         int l = strlen(nsd->chrootdir)-1; /* ends in trailing slash */
  269         if (nsd->options->dnstap_socket_path &&
  270             nsd->options->dnstap_socket_path[0] == '/' &&
  271             strncmp(nsd->options->dnstap_socket_path,
  272                 nsd->chrootdir, l) == 0)
  273             nsd->options->dnstap_socket_path += l;
  274     }
  275 #endif
  276     dt_col->dt_env = dt_create(nsd->options->dnstap_socket_path, num_workers);
  277     if(!dt_col->dt_env) {
  278         log_msg(LOG_ERR, "could not create dnstap env");
  279         return;
  280     }
  281     dt_apply_cfg(dt_col->dt_env, nsd->options);
  282     dt_init(dt_col->dt_env);
  283 }
  284 
  285 /* cleanup dt collector process for exit */
  286 static void dt_collector_cleanup(struct dt_collector* dt_col, struct nsd* nsd)
  287 {
  288     int i;
  289     dt_delete(dt_col->dt_env);
  290     event_del(dt_col->cmd_event);
  291     for(i=0; i<dt_col->count; i++) {
  292         event_del(dt_col->inputs[i].event);
  293     }
  294     dt_collector_close(dt_col, nsd);
  295     event_base_free(dt_col->event_base);
  296 #ifdef MEMCLEAN
  297     free(dt_col->cmd_event);
  298     if(dt_col->inputs) {
  299         for(i=0; i<dt_col->count; i++) {
  300             free(dt_col->inputs[i].event);
  301         }
  302         free(dt_col->inputs);
  303     }
  304     dt_collector_destroy(dt_col, nsd);
  305 #endif
  306 }
  307 
  308 /* attach events to the event base to listen to the workers and cmd channel */
  309 static void dt_attach_events(struct dt_collector* dt_col, struct nsd* nsd)
  310 {
  311     int i;
  312     /* create event base */
  313     dt_col->event_base = nsd_child_event_base();
  314     if(!dt_col->event_base) {
  315         error("dnstap collector: event_base create failed");
  316     }
  317 
  318     /* add command handler */
  319     dt_col->cmd_event = (struct event*)xalloc_zero(
  320         sizeof(*dt_col->cmd_event));
  321     event_set(dt_col->cmd_event, dt_col->cmd_socket_dt,
  322         EV_PERSIST|EV_READ, dt_handle_cmd_from_nsd, dt_col);
  323     if(event_base_set(dt_col->event_base, dt_col->cmd_event) != 0)
  324         log_msg(LOG_ERR, "dnstap collector: event_base_set failed");
  325     if(event_add(dt_col->cmd_event, NULL) != 0)
  326         log_msg(LOG_ERR, "dnstap collector: event_add failed");
  327     
  328     /* add worker input handlers */
  329     dt_col->inputs = xalloc_array_zero(dt_col->count,
  330         sizeof(*dt_col->inputs));
  331     for(i=0; i<dt_col->count; i++) {
  332         dt_col->inputs[i].dt_collector = dt_col;
  333         dt_col->inputs[i].event = (struct event*)xalloc_zero(
  334             sizeof(struct event));
  335         event_set(dt_col->inputs[i].event,
  336             nsd->dt_collector_fd_recv[i], EV_PERSIST|EV_READ,
  337             dt_handle_input, &dt_col->inputs[i]);
  338         if(event_base_set(dt_col->event_base,
  339             dt_col->inputs[i].event) != 0)
  340             log_msg(LOG_ERR, "dnstap collector: event_base_set failed");
  341         if(event_add(dt_col->inputs[i].event, NULL) != 0)
  342             log_msg(LOG_ERR, "dnstap collector: event_add failed");
  343         
  344         dt_col->inputs[i].buffer = buffer_create(dt_col->region,
  345             /* msglen + is_response + addrlen + is_tcp + packetlen + packet + zonelen + zone + spare + local_addr + addr */
  346             4+1+4+1+4+TCP_MAX_MESSAGE_LEN+4+MAXHOSTNAMELEN + 32 +
  347 #ifdef INET6
  348             sizeof(struct sockaddr_storage) + sizeof(struct sockaddr_storage)
  349 #else
  350             sizeof(struct sockaddr_in) + sizeof(struct sockaddr_in)
  351 #endif
  352         );
  353         assert(buffer_capacity(dt_col->inputs[i].buffer) ==
  354             buffer_capacity(dt_col->send_buffer));
  355     }
  356 }
  357 
  358 /* the dnstap collector process main routine */
  359 static void dt_collector_run(struct dt_collector* dt_col, struct nsd* nsd)
  360 {
  361     /* init dnstap */
  362     VERBOSITY(1, (LOG_INFO, "dnstap collector started"));
  363     dt_init_dnstap(dt_col, nsd);
  364     dt_attach_events(dt_col, nsd);
  365 
  366     /* run */
  367     if(event_base_loop(dt_col->event_base, 0) == -1) {
  368         error("dnstap collector: event_base_loop failed");
  369     }
  370 
  371     /* cleanup and done */
  372     VERBOSITY(1, (LOG_INFO, "dnstap collector stopped"));
  373     dt_collector_cleanup(dt_col, nsd);
  374     exit(0);
  375 }
  376 
  377 void dt_collector_start(struct dt_collector* dt_col, struct nsd* nsd)
  378 {
  379     /* fork */
  380     dt_col->dt_pid = fork();
  381     if(dt_col->dt_pid == -1) {
  382         error("dnstap_collector: fork failed: %s", strerror(errno));
  383     }
  384     if(dt_col->dt_pid == 0) {
  385         /* the dt collector process is this */
  386         /* close the nsd side of the command channel */
  387         close(dt_col->cmd_socket_nsd);
  388         dt_col->cmd_socket_nsd = -1;
  389         dt_collector_run(dt_col, nsd);
  390         /* NOTREACH */
  391         exit(0);
  392     } else {
  393         /* the parent continues on, with starting NSD */
  394         /* close the dt side of the command channel */
  395         close(dt_col->cmd_socket_dt);
  396         dt_col->cmd_socket_dt = -1;
  397     }
  398 }
  399 
  400 /* put data for sending to the collector process into the buffer */
  401 static int
  402 prep_send_data(struct buffer* buf, uint8_t is_response,
  403 #ifdef INET6
  404     struct sockaddr_storage* local_addr,
  405     struct sockaddr_storage* addr,
  406 #else
  407     struct sockaddr_in* local_addr,
  408     struct sockaddr_in* addr,
  409 #endif
  410     socklen_t addrlen, int is_tcp, struct buffer* packet,
  411     struct zone* zone)
  412 {
  413     buffer_clear(buf);
  414 #ifdef INET6
  415     if(local_addr->ss_family != addr->ss_family)
  416         return 0; /* must be same length to send */
  417 #else
  418     if(local_addr->sin_family != addr->sin_family)
  419         return 0; /* must be same length to send */
  420 #endif
  421     if(!buffer_available(buf, 4+1+4+2*addrlen+1+4+buffer_remaining(packet)))
  422         return 0; /* does not fit in send_buffer, log is dropped */
  423     buffer_skip(buf, 4); /* the length of the message goes here */
  424     buffer_write_u8(buf, is_response);
  425     buffer_write_u32(buf, addrlen);
  426     buffer_write(buf, local_addr, (size_t)addrlen);
  427     buffer_write(buf, addr, (size_t)addrlen);
  428     buffer_write_u8(buf, (is_tcp?1:0));
  429     buffer_write_u32(buf, buffer_remaining(packet));
  430     buffer_write(buf, buffer_begin(packet), buffer_remaining(packet));
  431     if(zone && zone->apex && domain_dname(zone->apex)) {
  432         if(!buffer_available(buf, 4 + domain_dname(zone->apex)->name_size))
  433             return 0;
  434         buffer_write_u32(buf, domain_dname(zone->apex)->name_size);
  435         buffer_write(buf, dname_name(domain_dname(zone->apex)),
  436             domain_dname(zone->apex)->name_size);
  437     } else {
  438         if(!buffer_available(buf, 4))
  439             return 0;
  440         buffer_write_u32(buf, 0);
  441     }
  442 
  443     buffer_flip(buf);
  444     /* write length of message */
  445     buffer_write_u32_at(buf, 0, buffer_remaining(buf)-4);
  446     return 1;
  447 }
  448 
  449 /* attempt to write buffer to socket, if it blocks do not write it. */
  450 static void attempt_to_write(int s, uint8_t* data, size_t len)
  451 {
  452     size_t total = 0;
  453     ssize_t r;
  454     while(total < len) {
  455         r = write(s, data+total, len-total);
  456         if(r == -1) {
  457             if(errno == EAGAIN && total == 0) {
  458                 /* on first write part, check if pipe is full,
  459                  * if the nonblocking fd blocks, then drop
  460                  * the message */
  461                 return;
  462             }
  463             if(errno != EAGAIN && errno != EINTR) {
  464                 /* some sort of error, print it and drop it */
  465                 log_msg(LOG_ERR,
  466                     "dnstap collector: write failed: %s",
  467                     strerror(errno));
  468                 return;
  469             }
  470             /* continue and write this again */
  471             /* for EINTR, we have to do this,
  472              * for EAGAIN, if the first part succeeded, we have
  473              * to continue to write the remainder of the message,
  474              * because otherwise partial messages confuse the
  475              * receiver. */
  476             continue;
  477         }
  478         total += r;
  479     }
  480 }
  481 
  482 void dt_collector_submit_auth_query(struct nsd* nsd,
  483 #ifdef INET6
  484     struct sockaddr_storage* local_addr,
  485     struct sockaddr_storage* addr,
  486 #else
  487     struct sockaddr_in* local_addr,
  488     struct sockaddr_in* addr,
  489 #endif
  490     socklen_t addrlen, int is_tcp, struct buffer* packet)
  491 {
  492     if(!nsd->dt_collector) return;
  493     if(!nsd->options->dnstap_log_auth_query_messages) return;
  494     VERBOSITY(4, (LOG_INFO, "dnstap submit auth query"));
  495 
  496     /* marshal data into send buffer */
  497     if(!prep_send_data(nsd->dt_collector->send_buffer, 0, local_addr, addr, addrlen,
  498         is_tcp, packet, NULL))
  499         return; /* probably did not fit in buffer */
  500 
  501     /* attempt to send data; do not block */
  502     attempt_to_write(nsd->dt_collector_fd_send[nsd->this_child->child_num],
  503         buffer_begin(nsd->dt_collector->send_buffer),
  504         buffer_remaining(nsd->dt_collector->send_buffer));
  505 }
  506 
  507 void dt_collector_submit_auth_response(struct nsd* nsd,
  508 #ifdef INET6
  509     struct sockaddr_storage* local_addr,
  510     struct sockaddr_storage* addr,
  511 #else
  512     struct sockaddr_in* local_addr,
  513     struct sockaddr_in* addr,
  514 #endif
  515     socklen_t addrlen, int is_tcp, struct buffer* packet,
  516     struct zone* zone)
  517 {
  518     if(!nsd->dt_collector) return;
  519     if(!nsd->options->dnstap_log_auth_response_messages) return;
  520     VERBOSITY(4, (LOG_INFO, "dnstap submit auth response"));
  521 
  522     /* marshal data into send buffer */
  523     if(!prep_send_data(nsd->dt_collector->send_buffer, 1, local_addr, addr, addrlen,
  524         is_tcp, packet, zone))
  525         return; /* probably did not fit in buffer */
  526 
  527     /* attempt to send data; do not block */
  528     attempt_to_write(nsd->dt_collector_fd_send[nsd->this_child->child_num],
  529         buffer_begin(nsd->dt_collector->send_buffer),
  530         buffer_remaining(nsd->dt_collector->send_buffer));
  531 }