]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
unbound-dnstap-socket debug test program.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Mon, 20 Jan 2020 14:45:31 +0000 (15:45 +0100)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Mon, 20 Jan 2020 14:45:31 +0000 (15:45 +0100)
.gitignore
Makefile.in
dnstap/dtstream.h
dnstap/unbound-dnstap-socket.c [new file with mode: 0644]

index 22fedf0d76152c127c7ebb91421399c88fd13651..6f26d217c585d3c0bd3619313e3a27aa9148a09e 100644 (file)
@@ -37,6 +37,7 @@
 /petal
 /pktview
 /streamtcp
+/unbound-dnstap-socket
 /testbound
 /unittest
 /contrib/libunbound.pc
index b7961b2b93de8e8e86c3398a85ae0f89ac795e1e..65dc206635dbf94ff15992dd0af26353c3fb2f04 100644 (file)
@@ -232,6 +232,10 @@ DELAYER_OBJ_LINK=$(DELAYER_OBJ) worker_cb.lo $(COMMON_OBJ) $(COMPAT_OBJ) \
 $(SLDNS_OBJ)
 IPSET_SRC=@IPSET_SRC@
 IPSET_OBJ=@IPSET_OBJ@
+DNSTAP_SOCKET_SRC=dnstap/unbound-dnstap-socket.c
+DNSTAP_SOCKET_OBJ=unbound-dnstap-socket.lo
+DNSTAP_SOCKET_OBJ_LINK=$(DNSTAP_SOCKET_OBJ) worker_cb.lo $(COMMON_OBJ) \
+$(COMPAT_OBJ) $(SLDNS_OBJ)
 LIBUNBOUND_SRC=libunbound/context.c libunbound/libunbound.c \
 libunbound/libworker.c
 LIBUNBOUND_OBJ=context.lo libunbound.lo libworker.lo ub_event_pluggable.lo
@@ -258,7 +262,7 @@ ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \
        $(TESTBOUND_SRC) $(LOCKVERIFY_SRC) $(PKTVIEW_SRC) \
        $(MEMSTATS_SRC) $(CHECKCONF_SRC) $(LIBUNBOUND_SRC) $(HOST_SRC) \
        $(ASYNCLOOK_SRC) $(STREAMTCP_SRC) $(PERF_SRC) $(DELAYER_SRC) \
-       $(CONTROL_SRC) $(UBANCHOR_SRC) $(PETAL_SRC) \
+       $(CONTROL_SRC) $(UBANCHOR_SRC) $(PETAL_SRC) $(DNSTAP_SOCKET_SRC)\
        $(PYTHONMOD_SRC) $(PYUNBOUND_SRC) $(WIN_DAEMON_THE_SRC) \
        $(SVCINST_SRC) $(SVCUNINST_SRC) $(ANCHORUPD_SRC) $(SLDNS_SRC)
 
@@ -266,7 +270,7 @@ ALL_OBJ=$(COMMON_OBJ) $(UNITTEST_OBJ) $(DAEMON_OBJ) \
        $(TESTBOUND_OBJ) $(LOCKVERIFY_OBJ) $(PKTVIEW_OBJ) \
        $(MEMSTATS_OBJ) $(CHECKCONF_OBJ) $(LIBUNBOUND_OBJ) $(HOST_OBJ) \
        $(ASYNCLOOK_OBJ) $(STREAMTCP_OBJ) $(PERF_OBJ) $(DELAYER_OBJ) \
-       $(CONTROL_OBJ) $(UBANCHOR_OBJ) $(PETAL_OBJ) \
+       $(CONTROL_OBJ) $(UBANCHOR_OBJ) $(PETAL_OBJ) $(DNSTAP_SOCKET_OBJ)\
        $(COMPAT_OBJ) $(PYUNBOUND_OBJ) \
        $(SVCINST_OBJ) $(SVCUNINST_OBJ) $(ANCHORUPD_OBJ) $(SLDNS_OBJ)
 
@@ -305,7 +309,7 @@ rsrc_unbound_checkconf.o:   $(srcdir)/winrc/rsrc_unbound_checkconf.rc config.h
 TEST_BIN=asynclook$(EXEEXT) delayer$(EXEEXT) \
        lock-verify$(EXEEXT) memstats$(EXEEXT) perf$(EXEEXT) \
        petal$(EXEEXT) pktview$(EXEEXT) streamtcp$(EXEEXT) \
-       testbound$(EXEEXT) unittest$(EXEEXT)
+       testbound$(EXEEXT) unittest$(EXEEXT) unbound-dnstap-socket$(EXEEXT)
 tests: all $(TEST_BIN)
 
 check: test
@@ -400,7 +404,12 @@ dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.h: $(srcdir)/dnstap/dnstap.proto
        @-if test ! -d dnstap; then $(INSTALL) -d dnstap; fi
        $(PROTOC_C) --c_out=. --proto_path=$(srcdir) $(srcdir)/dnstap/dnstap.proto
 
+unbound-dnstap-socket$(EXEEXT):        $(DNSTAP_SOCKET_OBJ_LINK)
+       $(LINK) -o $@ $(DNSTAP_SOCKET_OBJ_LINK) $(SSLLIB) $(LIBS)
+
 dnstap.pb-c.lo dnstap.pb-c.o: dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.h
+dtstream.lo dtstream.o: $(srcdir)/dnstap/dtstream.c config.h $(srcdir)/dnstap/dtstream.h
+unbound-dnstap-socket.lo unbound-dnstap-socket.o: $(srcdir)/dnstap/unbound-dnstap-socket.c config.h $(srcdir)/dnstap/dtstream.h
 
 # dnscrypt
 dnscrypt.lo dnscrypt.o: $(srcdir)/dnscrypt/dnscrypt.c config.h \
@@ -1059,7 +1068,6 @@ respip.lo respip.o: $(srcdir)/respip/respip.c config.h $(srcdir)/services/localz
  $(srcdir)/services/modstack.h $(srcdir)/util/net_help.h $(srcdir)/util/regional.h $(srcdir)/respip/respip.h
 checklocks.lo checklocks.o: $(srcdir)/testcode/checklocks.c config.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
  $(srcdir)/testcode/checklocks.h
-dtstream.lo dtstream.o: $(srcdir)/dnstap/dtstream.c config.h $(srcdir)/dnstap/dtstream.h
 dnstap.lo dnstap.o: $(srcdir)/dnstap/dnstap.c  config.h $(srcdir)/sldns/sbuffer.h \
  $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/util/log.h $(srcdir)/util/netevent.h \
  $(srcdir)/dnscrypt/dnscrypt.h  $(srcdir)/dnscrypt/cert.h \
index 41114c836dbc599680fc0ed4a07a8d24096e4e9e..e76264f50f02a2237d1b996b752591d714feb35a 100644 (file)
@@ -178,6 +178,15 @@ struct dt_io_list_item {
  * The START type can have only one field.  Field max len 256.
  * control frame max frame length 512 (excludes the 0-escape and control
  * frame length bytes).
+ *
+ * the bidirectional type of transmission is like this:
+ * client sends READY (with content type included),
+ * client waits for ACCEPT (with content type included),
+ * client sends START (with matched content type from ACCEPT)
+ * .. data frames
+ * client sends STOP.
+ * client waits for FINISH frame.
+ *
  */
 
 /** max length of Frame Streams content type field string */
diff --git a/dnstap/unbound-dnstap-socket.c b/dnstap/unbound-dnstap-socket.c
new file mode 100644 (file)
index 0000000..30412fb
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ * dnstap/unbound-dnstap-socket.c - debug program that listens for DNSTAP logs.
+ *
+ * Copyright (c) 2020, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This program listens on a DNSTAP socket for logged messages.
+ */
+#include "config.h"
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#include <openssl/ssl.h>
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#include "dnstap/dtstream.h"
+#include "util/log.h"
+#include "util/ub_event.h"
+#include "util/net_help.h"
+#include "services/listen_dnsport.h"
+
+/** usage information for streamtcp */
+static void usage(char* argv[])
+{
+       printf("usage: %s [options]\n", argv[0]);
+       printf("        Listen to dnstap messages\n");
+       printf("-u <socketpath> use unix socket with this file name\n");
+       printf("-l              long format for DNS printout\n");
+       printf("-v              more verbose log output\n");
+       printf("-h              this help text\n");
+       exit(1);
+}
+
+/** tap callback variables */
+struct tap_data {
+       /** the fd */
+       int fd;
+       /** the ub event */
+       struct ub_event* ev;
+       /** have we read the length, and how many bytes of it */
+       int len_done;
+       /** have we read the data, and how many bytes of it */
+       size_t data_done;
+       /** are we bi-directional (if false, uni-directional) */
+       int is_bidirectional;
+       /** are we reading a control frame */
+       int control_frame;
+       /** data of the frame */
+       uint8_t* frame;
+       /** length of this frame */
+       size_t len;
+};
+
+/** receive bytes from fd, prints errors if bad,
+ * returns 0: closed/error, -1: continue, >0 number of bytes */
+static ssize_t receive_bytes(int fd, void* buf, size_t len)
+{
+       ssize_t ret = recv(fd, buf, len, 0);
+       if(ret == 0) {
+               /* closed */
+               if(verbosity) log_info("dnstap client stream closed");
+               return 0;
+       } else if(ret == -1) {
+               /* error */
+#ifndef USE_WINSOCK
+               if(errno == EINTR || errno == EAGAIN)
+                       return -1;
+               log_err("could not recv: %s", strerror(errno));
+#else /* USE_WINSOCK */
+               if(WSAGetLastError() == WSAEINPROGRESS)
+                       return -1;
+               if(WSAGetLastError() == WSAEWOULDBLOCK) {
+                       ub_winsock_tcp_wouldblock(data->ev,
+                               UB_EV_READ);
+                       return -1;
+               }
+               log_err("could not recv: %s",
+                       wsa_strerror(WSAGetLastError()));
+#endif
+               if(verbosity) log_info("dnstap client stream closed");
+               return 0;
+       }
+       return ret;
+}
+
+/** delete the tap structure */
+void tap_data_free(struct tap_data* data)
+{
+       ub_event_del(data->ev);
+       ub_event_free(data->ev);
+       close(data->fd);
+       free(data->frame);
+       free(data);
+}
+
+/** callback for dnstap listener */
+static void tap_callback(int fd, short ATTR_UNUSED(bits), void* arg)
+{
+       struct tap_data* data = (struct tap_data*)arg;
+       if(verbosity) log_info("tap callback");
+       while(data->len_done < 4) {
+               uint32_t l = (uint32_t)data->len;
+               ssize_t ret = receive_bytes(fd,
+                       ((uint8_t*)&l)+data->len_done, 4-data->len_done);
+               log_info("s recv %d got %d %d", (int)ret, (int)l, (int)ntohl(l));
+               if(ret == 0) {
+                       /* closed or error */
+                       tap_data_free(data);
+                       return;
+               } else if(ret == -1) {
+                       /* continue later */
+                       return;
+               }
+               data->len_done += ret;
+               data->len = (size_t)l;
+               if(data->len_done < 4)
+                       return; /* continue later */
+               data->len = (size_t)(ntohl(l));
+               if(verbosity) log_info("length is %d", (int)data->len);
+               if(data->len == 0) {
+                       /* it is a control frame */
+                       data->control_frame = 1;
+                       /* read controlframelen */
+                       data->len_done = 0;
+               } else {
+                       /* allocate frame size */
+                       data->frame = calloc(1, data->len);
+                       if(!data->frame) {
+                               log_err("out of memory");
+                               tap_data_free(data);
+                               return;
+                       }
+               }
+       }
+       /* we want to read the full length now */
+       if(data->data_done < data->len) {
+               ssize_t r = receive_bytes(fd, data->frame + data->data_done,
+                       data->len - data->data_done);
+               if(r == 0) {
+                       /* closed or error */
+                       tap_data_free(data);
+                       return;
+               } else if(r == -1) {
+                       /* continue later */
+                       return;
+               }
+               data->data_done += r;
+               if(data->data_done < data->len)
+                       return; /* continue later */
+       }
+       /* we are done with a frame */
+       if(verbosity) log_info("received %sframe len %d",
+               (data->control_frame?"control ":""), (int)data->len);
+       if(data->len >= 4 && ntohl(*(uint32_t*)data->frame) ==
+               FSTRM_CONTROL_FRAME_READY) {
+               data->is_bidirectional = 1;
+               if(verbosity) log_info("bidirectional stream");
+       }
+
+       /* prepare for next frame */
+       free(data->frame);
+       data->frame = NULL;
+       data->control_frame = 0;
+       data->len = 0;
+       data->len_done = 0;
+       data->data_done = 0;
+}
+
+/** callback for main listening file descriptor */
+void mainfdcallback(int fd, short ATTR_UNUSED(bits), void* arg)
+{
+       struct tap_data* data;
+       struct sockaddr_storage addr;
+       socklen_t addrlen = (socklen_t)sizeof(addr);
+       struct ub_event_base* base = (struct ub_event_base*)arg;
+       int s = accept(fd, (struct sockaddr*)&addr, &addrlen);
+       if(s == -1) {
+#ifndef USE_WINSOCK
+                /* EINTR is signal interrupt. others are closed connection. */
+                if(     errno == EINTR || errno == EAGAIN
+#ifdef EWOULDBLOCK
+                        || errno == EWOULDBLOCK
+#endif
+#ifdef ECONNABORTED
+                        || errno == ECONNABORTED
+#endif
+#ifdef EPROTO
+                        || errno == EPROTO
+#endif /* EPROTO */
+                        )
+                        return;
+               log_err_addr("accept failed", strerror(errno), &addr, addrlen);
+#else /* USE_WINSOCK */
+                if(WSAGetLastError() == WSAEINPROGRESS ||
+                        WSAGetLastError() == WSAECONNRESET)
+                        return;
+                if(WSAGetLastError() == WSAEWOULDBLOCK) {
+                        ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ);
+                        return;
+                }
+                log_err_addr("accept failed", wsa_strerror(WSAGetLastError()),
+                        addr, *addrlen);
+#endif
+                return;
+       }
+       fd_set_nonblock(s);
+       if(verbosity) {
+               if(addr.ss_family == AF_LOCAL) {
+                       struct sockaddr_un* usock = calloc(1, sizeof(struct sockaddr_un) + 1);
+                       if(usock) {
+                               socklen_t ulen = sizeof(struct sockaddr_un);
+                               if(getsockname(fd, (struct sockaddr*)usock, &ulen) != -1) {
+                                       log_info("accepted new dnstap client from %s", usock->sun_path);
+                               } else {
+                                       log_info("accepted new dnstap client");
+                               }
+                               free(usock);
+                       } else {
+                               log_info("accepted new dnstap client");
+                       }
+               } else {
+                       log_info("accepted new dnstap client");
+               }
+       }
+       
+       data = calloc(1, sizeof(*data));
+       if(!data) fatal_exit("out of memory");
+       data->fd = s;
+       data->ev = ub_event_new(base, s, UB_EV_READ | UB_EV_PERSIST,
+               &tap_callback, data);
+       if(!data->ev) fatal_exit("could not ub_event_new");
+       if(ub_event_add(data->ev, NULL) != 0) fatal_exit("could not ub_event_add");
+}
+
+/** create file descriptor to listen on */
+static int
+setup_fd(char* socketpath)
+{
+       return create_local_accept_sock(socketpath, NULL, 0);
+}
+
+/** setup and run the server to listen to DNSTAP messages */
+static void
+setup_and_run(char* socketpath, int longformat)
+{
+       int fd;
+       time_t secs = 0;
+       struct timeval now;
+       struct ub_event_base* base;
+       const char *evnm="event", *evsys="", *evmethod="";
+       struct ub_event *ev;
+
+       memset(&now, 0, sizeof(now));
+       base = ub_default_event_base(1, &secs, &now);
+       if(!base) fatal_exit("could not create ub_event base");
+       fd = setup_fd(socketpath);
+       ub_get_event_sys(base, &evnm, &evsys, &evmethod);
+       if(verbosity) log_info("%s %s uses %s method", evnm, evsys, evmethod);
+       ev = ub_event_new(base, fd, UB_EV_READ | UB_EV_PERSIST,
+               &mainfdcallback, base);
+       if(!ev) fatal_exit("could not ub_event_new");
+       if(ub_event_add(ev, NULL) != 0) fatal_exit("could not ub_event_add");
+
+       ub_event_base_dispatch(base);
+
+       ub_event_del(ev);
+       ub_event_free(ev);
+       ub_event_base_free(base);
+       close(fd);
+}
+
+/** getopt global, in case header files fail to declare it. */
+extern int optind;
+/** getopt global, in case header files fail to declare it. */
+extern char* optarg;
+
+/** main program for streamtcp */
+int main(int argc, char** argv) 
+{
+       int c;
+       int usessl = 0, longformat = 0;
+       char* socketpath = NULL;
+#ifdef USE_WINSOCK
+       WSADATA wsa_data;
+       if(WSAStartup(MAKEWORD(2,2), &wsa_data) != 0) {
+               printf("WSAStartup failed\n");
+               return 1;
+       }
+#endif
+
+       /* lock debug start (if any) */
+       log_ident_set("unbound-dnstap-socket");
+       log_init(0, 0, 0);
+       checklock_start();
+
+#ifdef SIGPIPE
+       if(signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
+               perror("could not install signal handler for SIGPIPE");
+               return 1;
+       }
+#endif
+
+       /* command line options */
+       while( (c=getopt(argc, argv, "hlu:v")) != -1) {
+               switch(c) {
+                       case 'u':
+                               socketpath = optarg;
+                               break;
+                       case 'l':
+                               longformat = 1;
+                               break;
+                       case 'v':
+                               verbosity++;
+                               break;
+                       case 'h':
+                       case '?':
+                       default:
+                               usage(argv);
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if(usessl) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
+               ERR_load_SSL_strings();
+#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO)
+#  ifndef S_SPLINT_S
+               OpenSSL_add_all_algorithms();
+#  endif
+#else
+               OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
+                       | OPENSSL_INIT_ADD_ALL_DIGESTS
+                       | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
+#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
+               (void)SSL_library_init();
+#else
+               (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
+#endif
+       }
+       setup_and_run(socketpath, longformat);
+       checklock_stop();
+#ifdef USE_WINSOCK
+       WSACleanup();
+#endif
+       return 0;
+}