import unbound 1.21.0, ok phessler florian
authorsthen <sthen@openbsd.org>
Wed, 4 Sep 2024 09:35:34 +0000 (09:35 +0000)
committersthen <sthen@openbsd.org>
Wed, 4 Sep 2024 09:35:34 +0000 (09:35 +0000)
21 files changed:
usr.sbin/unbound/dnstap/dnstap.h
usr.sbin/unbound/dnstap/dnstap.m4
usr.sbin/unbound/dnstap/dtstream.c
usr.sbin/unbound/dnstap/unbound-dnstap-socket.c
usr.sbin/unbound/dynlibmod/dynlibmod.c
usr.sbin/unbound/ipset/ipset.c
usr.sbin/unbound/ipset/ipset.h
usr.sbin/unbound/services/rpz.c
usr.sbin/unbound/systemd.m4
usr.sbin/unbound/testcode/fake_event.c
usr.sbin/unbound/testcode/mini_tdir.sh
usr.sbin/unbound/testcode/petal.c
usr.sbin/unbound/testcode/readzone.c
usr.sbin/unbound/testcode/streamtcp.c
usr.sbin/unbound/testcode/testbound.c
usr.sbin/unbound/testcode/unitmain.c
usr.sbin/unbound/testcode/unitzonemd.c
usr.sbin/unbound/util/edns.c
usr.sbin/unbound/util/edns.h
usr.sbin/unbound/util/proxy_protocol.c
usr.sbin/unbound/util/siphash.c

index 77914c2..21c0336 100644 (file)
@@ -39,6 +39,7 @@
 
 #ifdef USE_DNSTAP
 
+#include "util/locks.h"
 struct config_file;
 struct sldns_buffer;
 struct dt_msg_queue;
@@ -75,6 +76,13 @@ struct dt_env {
        unsigned log_forwarder_query_messages : 1;
        /** whether to log Message/FORWARDER_RESPONSE */
        unsigned log_forwarder_response_messages : 1;
+
+       /** lock on sample count */
+       lock_basic_type sample_lock;
+       /** rate limit value from config, samples 1/N messages */
+       unsigned int sample_rate;
+       /** rate limit counter */
+       unsigned int sample_rate_count;
 };
 
 /**
index 80b7bc9..78d0dd6 100644 (file)
@@ -34,41 +34,38 @@ AC_DEFUN([dt_DNSTAP],
                 LDFLAGS="$LDFLAGS -L$withval/lib"
             ],
             [
-                ifdef([PKG_CHECK_MODULES],
-                    [
-                        PKG_CHECK_MODULES([PROTOBUFC], [libprotobuf-c],
-                            [
-                                CFLAGS="$CFLAGS $PROTOBUFC_CFLAGS"
-                                LIBS="$LIBS $PROTOBUFC_LIBS"
-                            ],
-                            [
-                                # pkg-config failed; try falling back to known values
-                                # workaround for protobuf-c includes at old dir before protobuf-c-1.0.0
-                                if test -f /usr/include/google/protobuf-c/protobuf-c.h; then
-                                    CFLAGS="$CFLAGS -I/usr/include/google"
+                if test -n "$PKG_CONFIG"; then
+                    PKG_CHECK_MODULES([PROTOBUFC], [libprotobuf-c],
+                        [
+                            CFLAGS="$CFLAGS $PROTOBUFC_CFLAGS"
+                            LIBS="$LIBS $PROTOBUFC_LIBS"
+                        ],
+                        [
+                            # pkg-config failed; try falling back to known values
+                            # workaround for protobuf-c includes at old dir before protobuf-c-1.0.0
+                            if test -f /usr/include/google/protobuf-c/protobuf-c.h; then
+                                CFLAGS="$CFLAGS -I/usr/include/google"
+                            else
+                                if test -f /usr/local/include/google/protobuf-c/protobuf-c.h; then
+                                    CFLAGS="$CFLAGS -I/usr/local/include/google"
+                                    LDFLAGS="$LDFLAGS -L/usr/local/lib"
                                 else
-                                    if test -f /usr/local/include/google/protobuf-c/protobuf-c.h; then
-                                        CFLAGS="$CFLAGS -I/usr/local/include/google"
-                                        LDFLAGS="$LDFLAGS -L/usr/local/lib"
-                                    else
-                                        AC_MSG_ERROR([The protobuf-c package was not found with pkg-config. Please install protobuf-c!])
-                                    fi
+                                    AC_MSG_ERROR([The protobuf-c package was not found with pkg-config. Please install protobuf-c!])
                                 fi
-                            ]
-                        )
-                    ],
-                    [
-                        # workaround for protobuf-c includes at old dir before protobuf-c-1.0.0
-                        if test -f /usr/include/google/protobuf-c/protobuf-c.h; then
-                            CFLAGS="$CFLAGS -I/usr/include/google"
-                        else
-                            if test -f /usr/local/include/google/protobuf-c/protobuf-c.h; then
-                                CFLAGS="$CFLAGS -I/usr/local/include/google"
-                                LDFLAGS="$LDFLAGS -L/usr/local/lib"
                             fi
+                        ]
+                    )
+                else
+                    # workaround for protobuf-c includes at old dir before protobuf-c-1.0.0
+                    if test -f /usr/include/google/protobuf-c/protobuf-c.h; then
+                        CFLAGS="$CFLAGS -I/usr/include/google"
+                    else
+                        if test -f /usr/local/include/google/protobuf-c/protobuf-c.h; then
+                            CFLAGS="$CFLAGS -I/usr/local/include/google"
+                            LDFLAGS="$LDFLAGS -L/usr/local/lib"
                         fi
-                    ]
-                )
+                    fi
+                fi
             ]
         )
         AC_SEARCH_LIBS([protobuf_c_message_pack], [protobuf-c], [],
index 69c9512..2d5ab20 100644 (file)
@@ -176,10 +176,7 @@ void
 mq_wakeup_cb(void* arg)
 {
        struct dt_msg_queue* mq = (struct dt_msg_queue*)arg;
-       /* even if the dtio is already active, because perhaps much
-        * traffic suddenly, we leave the timer running to save on
-        * managing it, the once a second timer is less work then
-        * starting and stopping the timer frequently */
+
        lock_basic_lock(&mq->dtio->wakeup_timer_lock);
        mq->dtio->wakeup_timer_enabled = 0;
        lock_basic_unlock(&mq->dtio->wakeup_timer_lock);
@@ -210,6 +207,8 @@ dt_msg_queue_start_timer(struct dt_msg_queue* mq, int wakeupnow)
        lock_basic_lock(&mq->dtio->wakeup_timer_lock);
        if(mq->dtio->wakeup_timer_enabled) {
                if(wakeupnow) {
+                       tv.tv_sec = 0;
+                       tv.tv_usec = 0;
                        comm_timer_set(mq->wakeup_timer, &tv);
                }
                lock_basic_unlock(&mq->dtio->wakeup_timer_lock);
@@ -221,8 +220,14 @@ dt_msg_queue_start_timer(struct dt_msg_queue* mq, int wakeupnow)
        if(!wakeupnow) {
                tv.tv_sec = 1;
                tv.tv_usec = 0;
+               /* If it is already set, keep it running. */
+               if(!comm_timer_is_set(mq->wakeup_timer))
+                       comm_timer_set(mq->wakeup_timer, &tv);
+       } else {
+               tv.tv_sec = 0;
+               tv.tv_usec = 0;
+               comm_timer_set(mq->wakeup_timer, &tv);
        }
-       comm_timer_set(mq->wakeup_timer, &tv);
        lock_basic_unlock(&mq->dtio->wakeup_timer_lock);
 }
 
@@ -260,8 +265,9 @@ dt_msg_queue_submit(struct dt_msg_queue* mq, void* buf, size_t len)
 
        /* acquire lock */
        lock_basic_lock(&mq->lock);
-       /* if list was empty, start timer for (eventual) wakeup */
-       if(mq->first == NULL)
+       /* if list was empty, start timer for (eventual) wakeup,
+        * or if dtio is not writing now an eventual wakeup is needed. */
+       if(mq->first == NULL || !mq->dtio->event_added_is_write)
                wakeupstarttimer = 1;
        /* if list contains more than wakeupnum elements, wakeup now,
         * or if list is (going to be) almost full */
@@ -1259,6 +1265,13 @@ static void dtio_sleep(struct dt_io_thread* dtio)
        /* unregister the event polling for write, because there is
         * nothing to be written */
        (void)dtio_add_output_event_read(dtio);
+
+       /* Set wakeuptimer enabled off; so that the next worker thread that
+        * wants to log starts a timer if needed, since the writer thread
+        * has gone to sleep. */
+       lock_basic_lock(&dtio->wakeup_timer_lock);
+       dtio->wakeup_timer_enabled = 0;
+       lock_basic_unlock(&dtio->wakeup_timer_lock);
 }
 
 #ifdef HAVE_SSL
@@ -1322,7 +1335,11 @@ static int dtio_ssl_check_peer(struct dt_io_thread* dtio)
        if((SSL_get_verify_mode(dtio->ssl)&SSL_VERIFY_PEER)) {
                /* verification */
                if(SSL_get_verify_result(dtio->ssl) == X509_V_OK) {
+#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
+                       X509* x = SSL_get1_peer_certificate(dtio->ssl);
+#else
                        X509* x = SSL_get_peer_certificate(dtio->ssl);
+#endif
                        if(!x) {
                                verbose(VERB_ALGO, "dnstap io, %s, SSL "
                                        "connection failed no certificate",
@@ -1347,7 +1364,11 @@ static int dtio_ssl_check_peer(struct dt_io_thread* dtio)
 #endif
                        X509_free(x);
                } else {
+#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
+                       X509* x = SSL_get1_peer_certificate(dtio->ssl);
+#else
                        X509* x = SSL_get_peer_certificate(dtio->ssl);
+#endif
                        if(x) {
                                log_cert(VERB_ALGO, "dnstap io, peer "
                                        "certificate", x);
@@ -1489,8 +1510,10 @@ void dtio_output_cb(int ATTR_UNUSED(fd), short bits, void* arg)
 #endif
 
        if((bits&UB_EV_READ || dtio->ssl_brief_write)) {
+#ifdef HAVE_SSL
                if(dtio->ssl_brief_write)
                        (void)dtio_disable_brief_write(dtio);
+#endif
                if(dtio->ready_frame_sent && !dtio->accept_frame_received) {
                        if(dtio_read_accept_frame(dtio) <= 0)
                                return;
@@ -1513,8 +1536,22 @@ void dtio_output_cb(int ATTR_UNUSED(fd), short bits, void* arg)
                                        /* no messages on the first iteration,
                                         * the queues are all empty */
                                        dtio_sleep(dtio);
+                                       /* After putting to sleep, see if
+                                        * a message is in a message queue,
+                                        * if so, resume service. Stops a
+                                        * race condition where a thread could
+                                        * have one message but the dtio
+                                        * also just went to sleep. With the
+                                        * message queued between the
+                                        * dtio_find_msg and dtio_sleep
+                                        * calls. */
+                                       if(dtio_find_msg(dtio)) {
+                                               if(!dtio_add_output_event_write(dtio))
+                                                       return;
+                                       }
                                }
-                               return; /* nothing to do */
+                               if(!dtio->cur_msg)
+                                       return; /* nothing to do */
                        }
                }
 
index 04fda74..f203aa7 100644 (file)
 static void usage(char* argv[])
 {
        printf("usage: %s [options]\n", argv[0]);
-       printf("        Listen to dnstap messages\n");
+       printf("        Listen to dnstap messages\n");
        printf("stdout has dnstap log, stderr has verbose server log\n");
-       printf("-u <socketpath> listen to unix socket with this file name\n");
-       printf("-s <serverip[@port]> listen for TCP on the IP and port\n");
-       printf("-t <serverip[@port]> listen for TLS on IP and port\n");
-       printf("-x <server.key> server key file for TLS service\n");
-       printf("-y <server.pem> server cert file for TLS service\n");
-       printf("-z <verify.pem> cert file to verify client connections\n");
-       printf("-l              long format for DNS printout\n");
-       printf("-v              more verbose log output\n");
-       printf("-h              this help text\n");
+       printf("-u <socketpath>         listen to unix socket with this file name\n");
+       printf("-s <serverip[@port]>    listen for TCP on the IP and port\n");
+       printf("-t <serverip[@port]>    listen for TLS on IP and port\n");
+       printf("-x <server.key>         server key file for TLS service\n");
+       printf("-y <server.pem>         server cert file for TLS service\n");
+       printf("-z <verify.pem>         cert file to verify client connections\n");
+       printf("-l                      long format for DNS printout\n");
+       printf("-v                      more verbose log output\n");
+       printf("-c                      internal unit test and exit\n");
+       printf("-h                      this help text\n");
        exit(1);
 }
 
@@ -102,6 +103,14 @@ struct main_tap_data {
        struct tap_socket_list* acceptlist;
 };
 
+/* list of data */
+struct tap_data_list {
+       /** next in list */
+       struct tap_data_list* next;
+       /** the data */
+       struct tap_data* d;
+};
+
 /** tap callback variables */
 struct tap_data {
        /** the fd */
@@ -128,6 +137,10 @@ struct tap_data {
        uint8_t* frame;
        /** length of this frame */
        size_t len;
+       /** back pointer to the tap_data_list entry;
+        * used to NULL the forward pointer to this data
+        * when this data is freed. */
+       struct tap_data_list* data_list;
 };
 
 /** list of sockets */
@@ -156,8 +169,89 @@ struct tap_socket {
        char* ip;
        /** for a TLS socket, the tls context */
        SSL_CTX* sslctx;
+       /** dumb way to deal with memory leaks:
+        * tap_data was only freed on errors and not during exit leading to
+        * false positives when testing for memory leaks. */
+       struct tap_data_list* data_list;
 };
 
+/** try to delete tail entries from the list if all of them have no data */
+static void tap_data_list_try_to_free_tail(struct tap_data_list* list)
+{
+       struct tap_data_list* current = list;
+       log_assert(!list->d);
+       if(!list->next) /* we are the last, we can't remove ourselves */
+               return;
+       list = list->next;
+       while(list) {
+               if(list->d) /* a tail entry still has data; return */
+                       return;
+               list = list->next;
+       }
+       /* keep the next */
+       list = current->next;
+       /* the tail will be removed; but not ourselves */
+       current->next = NULL;
+       while(list) {
+               current = list;
+               list = list->next;
+               free(current);
+       }
+}
+
+/** delete the tap structure */
+static void tap_data_free(struct tap_data* data, int free_tail)
+{
+       if(!data)
+               return;
+       if(data->ev) {
+               ub_event_del(data->ev);
+               ub_event_free(data->ev);
+       }
+#ifdef HAVE_SSL
+       SSL_free(data->ssl);
+#endif
+       sock_close(data->fd);
+       free(data->id);
+       free(data->frame);
+       if(data->data_list) {
+               data->data_list->d = NULL;
+               if(free_tail)
+                       tap_data_list_try_to_free_tail(data->data_list);
+       }
+       free(data);
+}
+
+/** insert tap_data in the tap_data_list */
+static int tap_data_list_insert(struct tap_data_list** liststart,
+       struct tap_data* d)
+{
+       struct tap_data_list* entry = (struct tap_data_list*)
+               malloc(sizeof(*entry));
+       if(!entry)
+               return 0;
+       entry->next = *liststart;
+       entry->d = d;
+       d->data_list = entry;
+       *liststart = entry;
+       return 1;
+}
+
+/** delete the tap_data_list and free any remaining tap_data */
+static void tap_data_list_delete(struct tap_data_list* list)
+{
+       struct tap_data_list* e = list, *next;
+       while(e) {
+               next = e->next;
+               if(e->d) {
+                       tap_data_free(e->d, 0);
+                       e->d = NULL;
+               }
+               free(e);
+               e = next;
+       }
+}
+
 /** del the tap event */
 static void tap_socket_delev(struct tap_socket* s)
 {
@@ -173,7 +267,7 @@ static void tap_socket_close(struct tap_socket* s)
 {
        if(!s) return;
        if(s->fd == -1) return;
-       close(s->fd);
+       sock_close(s->fd);
        s->fd = -1;
 }
 
@@ -184,6 +278,7 @@ static void tap_socket_delete(struct tap_socket* s)
 #ifdef HAVE_SSL
        SSL_CTX_free(s->sslctx);
 #endif
+       tap_data_list_delete(s->data_list);
        ub_event_free(s->ev);
        free(s->socketpath);
        free(s->ip);
@@ -728,27 +823,12 @@ static ssize_t tap_receive(struct tap_data* data, void* buf, size_t len)
        return receive_bytes(data, data->fd, buf, len);
 }
 
-/** delete the tap structure */
-static void tap_data_free(struct tap_data* data)
-{
-       ub_event_del(data->ev);
-       ub_event_free(data->ev);
-#ifdef HAVE_SSL
-       SSL_free(data->ssl);
-#endif
-       close(data->fd);
-       free(data->id);
-       free(data->frame);
-       free(data);
-}
-
 /** reply with ACCEPT control frame to bidirectional client,
  * returns 0 on error */
 static int reply_with_accept(struct tap_data* data)
 {
 #ifdef USE_DNSTAP
        /* len includes the escape and framelength */
-       int r;
        size_t len = 0;
        void* acceptframe = fstrm_create_control_frame_accept(
                DNSTAP_CONTENT_TYPE, &len);
@@ -759,6 +839,8 @@ static int reply_with_accept(struct tap_data* data)
 
        fd_set_block(data->fd);
        if(data->ssl) {
+#ifdef HAVE_SSL
+               int r;
                if((r=SSL_write(data->ssl, acceptframe, len)) <= 0) {
                        int r2;
                        if((r2=SSL_get_error(data->ssl, r)) == SSL_ERROR_ZERO_RETURN)
@@ -769,6 +851,7 @@ static int reply_with_accept(struct tap_data* data)
                        free(acceptframe);
                        return 0;
                }
+#endif
        } else {
                if(send(data->fd, acceptframe, len, 0) == -1) {
                        log_err("send failed: %s", sock_strerror(errno));
@@ -804,6 +887,7 @@ static int reply_with_finish(struct tap_data* data)
 
        fd_set_block(data->fd);
        if(data->ssl) {
+#ifdef HAVE_SSL
                int r;
                if((r=SSL_write(data->ssl, finishframe, len)) <= 0) {
                        int r2;
@@ -815,6 +899,7 @@ static int reply_with_finish(struct tap_data* data)
                        free(finishframe);
                        return 0;
                }
+#endif
        } else {
                if(send(data->fd, finishframe, len, 0) == -1) {
                        log_err("send failed: %s", sock_strerror(errno));
@@ -842,7 +927,11 @@ static int tap_check_peer(struct tap_data* data)
        if((SSL_get_verify_mode(data->ssl)&SSL_VERIFY_PEER)) {
                /* verification */
                if(SSL_get_verify_result(data->ssl) == X509_V_OK) {
+#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
+                       X509* x = SSL_get1_peer_certificate(data->ssl);
+#else
                        X509* x = SSL_get_peer_certificate(data->ssl);
+#endif
                        if(!x) {
                                if(verbosity) log_info("SSL connection %s"
                                        " failed no certificate", data->id);
@@ -864,7 +953,11 @@ static int tap_check_peer(struct tap_data* data)
 #endif
                        X509_free(x);
                } else {
+#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
+                       X509* x = SSL_get1_peer_certificate(data->ssl);
+#else
                        X509* x = SSL_get_peer_certificate(data->ssl);
+#endif
                        if(x) {
                                if(verbosity)
                                        log_cert(VERB_ALGO, "peer certificate", x);
@@ -906,7 +999,7 @@ static int tap_handshake(struct tap_data* data)
                        return 0;
                } else if(r == 0) {
                        /* closed */
-                       tap_data_free(data);
+                       tap_data_free(data, 1);
                        return 0;
                } else if(want == SSL_ERROR_SYSCALL) {
                        /* SYSCALL and errno==0 means closed uncleanly */
@@ -924,7 +1017,7 @@ static int tap_handshake(struct tap_data* data)
                        if(!silent)
                                log_err("SSL_handshake syscall: %s",
                                        strerror(errno));
-                       tap_data_free(data);
+                       tap_data_free(data, 1);
                        return 0;
                } else {
                        unsigned long err = ERR_get_error();
@@ -934,7 +1027,7 @@ static int tap_handshake(struct tap_data* data)
                                verbose(VERB_OPS, "ssl handshake failed "
                                        "from %s", data->id);
                        }
-                       tap_data_free(data);
+                       tap_data_free(data, 1);
                        return 0;
                }
        }
@@ -942,7 +1035,7 @@ static int tap_handshake(struct tap_data* data)
        data->ssl_handshake_done = 1;
        if(!tap_check_peer(data)) {
                /* closed */
-               tap_data_free(data);
+               tap_data_free(data, 1);
                return 0;
        }
        return 1;
@@ -968,7 +1061,7 @@ void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg)
                if(verbosity>=4) log_info("s recv %d", (int)ret);
                if(ret == 0) {
                        /* closed or error */
-                       tap_data_free(data);
+                       tap_data_free(data, 1);
                        return;
                } else if(ret == -1) {
                        /* continue later */
@@ -990,7 +1083,7 @@ void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg)
                        data->frame = calloc(1, data->len);
                        if(!data->frame) {
                                log_err("out of memory");
-                               tap_data_free(data);
+                               tap_data_free(data, 1);
                                return;
                        }
                }
@@ -1003,7 +1096,7 @@ void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg)
                if(verbosity>=4) log_info("f recv %d", (int)r);
                if(r == 0) {
                        /* closed or error */
-                       tap_data_free(data);
+                       tap_data_free(data, 1);
                        return;
                } else if(r == -1) {
                        /* continue later */
@@ -1028,13 +1121,13 @@ void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg)
                data->is_bidirectional = 1;
                if(verbosity) log_info("bidirectional stream");
                if(!reply_with_accept(data)) {
-                       tap_data_free(data);
+                       tap_data_free(data, 1);
                        return;
                }
        } else if(data->len >= 4 && sldns_read_uint32(data->frame) ==
                FSTRM_CONTROL_FRAME_STOP && data->is_bidirectional) {
                if(!reply_with_finish(data)) {
-                       tap_data_free(data);
+                       tap_data_free(data, 1);
                        return;
                }
        }
@@ -1046,7 +1139,6 @@ void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg)
        data->len = 0;
        data->len_done = 0;
        data->data_done = 0;
-
 }
 
 /** callback for main listening file descriptor */
@@ -1129,6 +1221,8 @@ void dtio_mainfdcallback(int fd, short ATTR_UNUSED(bits), void* arg)
                &dtio_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");
+       if(!tap_data_list_insert(&tap_sock->data_list, data))
+               fatal_exit("could not tap_data_list_insert");
 }
 
 /** setup local accept sockets */
@@ -1243,6 +1337,114 @@ setup_and_run(struct config_strlist_head* local_list,
        free(maindata);
 }
 
+/* internal unit tests */
+static int internal_unittest()
+{
+       /* unit test tap_data_list_try_to_free_tail() */
+#define unit_tap_datas_max 5
+       struct tap_data* datas[unit_tap_datas_max];
+       struct tap_data_list* list;
+       struct tap_socket* socket = calloc(1, sizeof(*socket));
+       size_t i = 0;
+       log_assert(socket);
+       log_assert(unit_tap_datas_max>2); /* needed for the test */
+       for(i=0; i<unit_tap_datas_max; i++) {
+               datas[i] = calloc(1, sizeof(struct tap_data));
+               log_assert(datas[i]);
+               log_assert(tap_data_list_insert(&socket->data_list, datas[i]));
+       }
+       /* sanity base check */
+       list = socket->data_list;
+       for(i=0; list; i++) list = list->next;
+       log_assert(i==unit_tap_datas_max);
+
+       /* Free the last data, tail cannot be erased */
+       list = socket->data_list;
+       while(list->next) list = list->next;
+       free(list->d);
+       list->d = NULL;
+       tap_data_list_try_to_free_tail(list);
+       list = socket->data_list;
+       for(i=0; list; i++) list = list->next;
+       log_assert(i==unit_tap_datas_max);
+
+       /* Free the third to last data, tail cannot be erased */
+       list = socket->data_list;
+       for(i=0; i<unit_tap_datas_max-3; i++) list = list->next;
+       free(list->d);
+       list->d = NULL;
+       tap_data_list_try_to_free_tail(list);
+       list = socket->data_list;
+       for(i=0; list; i++) list = list->next;
+       log_assert(i==unit_tap_datas_max);
+
+       /* Free the second to last data, try to remove tail from the third
+        * again, tail (last 2) should be removed */
+       list = socket->data_list;
+       for(i=0; i<unit_tap_datas_max-2; i++) list = list->next;
+       free(list->d);
+       list->d = NULL;
+       list = socket->data_list;
+       while(list->d) list = list->next;
+       tap_data_list_try_to_free_tail(list);
+       list = socket->data_list;
+       for(i=0; list; i++) list = list->next;
+       log_assert(i==unit_tap_datas_max-2);
+
+       /* Free all the remaining data, try to remove tail from the start,
+        * only the start should remain */
+       list = socket->data_list;
+       while(list) {
+               free(list->d);
+               list->d = NULL;
+               list = list->next;
+       }
+       tap_data_list_try_to_free_tail(socket->data_list);
+       list = socket->data_list;
+       for(i=0; list; i++) list = list->next;
+       log_assert(i==1);
+
+       /* clean up */
+       tap_data_list_delete(socket->data_list);
+       free(socket);
+
+       /* Start again. Add two elements */
+       socket = calloc(1, sizeof(*socket));
+       log_assert(socket);
+       for(i=0; i<2; i++) {
+               datas[i] = calloc(1, sizeof(struct tap_data));
+               log_assert(datas[i]);
+               log_assert(tap_data_list_insert(&socket->data_list, datas[i]));
+       }
+       /* sanity base check */
+       list = socket->data_list;
+       for(i=0; list; i++) list = list->next;
+       log_assert(i==2);
+
+       /* Free the last data, tail cannot be erased */
+       list = socket->data_list;
+       while(list->next) list = list->next;
+       free(list->d);
+       list->d = NULL;
+       tap_data_list_try_to_free_tail(list);
+       list = socket->data_list;
+       for(i=0; list; i++) list = list->next;
+       log_assert(i==2);
+
+       /* clean up */
+       tap_data_list_delete(socket->data_list);
+       free(socket);
+
+       if(log_get_lock()) {
+               lock_basic_destroy((lock_basic_type*)log_get_lock());
+       }
+       checklock_stop();
+#ifdef USE_WINSOCK
+       WSACleanup();
+#endif
+       return 0;
+}
+
 /** getopt global, in case header files fail to declare it. */
 extern int optind;
 /** getopt global, in case header files fail to declare it. */
@@ -1293,7 +1495,7 @@ int main(int argc, char** argv)
 #endif
 
        /* command line options */
-       while( (c=getopt(argc, argv, "hls:t:u:vx:y:z:")) != -1) {
+       while( (c=getopt(argc, argv, "hcls:t:u:vx:y:z:")) != -1) {
                switch(c) {
                        case 'u':
                                if(!cfg_strlist_append(&local_list,
@@ -1329,6 +1531,12 @@ int main(int argc, char** argv)
                        case 'v':
                                verbosity++;
                                break;
+                       case 'c':
+#ifndef UNBOUND_DEBUG
+                               fatal_exit("-c option needs compilation with "
+                                       "--enable-debug");
+#endif
+                               return internal_unittest();
                        case 'h':
                        case '?':
                        default:
@@ -1365,6 +1573,9 @@ int main(int argc, char** argv)
        config_delstrlist(tcp_list.first);
        config_delstrlist(tls_list.first);
 
+       if(log_get_lock()) {
+               lock_basic_destroy((lock_basic_type*)log_get_lock());
+       }
        checklock_stop();
 #ifdef USE_WINSOCK
        WSACleanup();
index 1e040a3..c941154 100644 (file)
@@ -297,8 +297,8 @@ inplace_cb_delete_wrapped(struct module_env* env, enum inplace_cb_list_type type
  */
 static struct module_func_block dynlibmod_block = {
    "dynlib",
-   &dynlibmod_init, &dynlibmod_deinit, &dynlibmod_operate, &dynlibmod_inform_super,
-   &dynlibmod_clear, &dynlibmod_get_mem
+   NULL, NULL, &dynlibmod_init, &dynlibmod_deinit, &dynlibmod_operate,
+   &dynlibmod_inform_super, &dynlibmod_clear, &dynlibmod_get_mem
 };
 
 struct module_func_block* dynlibmod_get_funcblock(void)
index af55de8..1ad2c09 100644 (file)
 #include "sldns/wire2str.h"
 #include "sldns/parseutil.h"
 
+#ifdef HAVE_NET_PFVAR_H
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/pfvar.h>
+typedef intptr_t filter_dev;
+#else
 #include <libmnl/libmnl.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/ipset/ip_set.h>
+typedef struct mnl_socket * filter_dev;
+#endif
 
 #define BUFF_LEN 256
 
@@ -41,24 +51,95 @@ static int error_response(struct module_qstate* qstate, int id, int rcode) {
        return 0;
 }
 
-static struct mnl_socket * open_mnl_socket() {
-       struct mnl_socket *mnl;
+#ifdef HAVE_NET_PFVAR_H
+static void * open_filter() {
+       filter_dev dev;
 
-       mnl = mnl_socket_open(NETLINK_NETFILTER);
-       if (!mnl) {
+       dev = open("/dev/pf", O_RDWR);
+       if (dev == -1) {
+               log_err("open(\"/dev/pf\") failed: %s", strerror(errno));
+               return NULL;
+       }
+       else
+               return (void *)dev;
+}
+#else
+static void * open_filter() {
+       filter_dev dev;
+
+       dev = mnl_socket_open(NETLINK_NETFILTER);
+       if (!dev) {
                log_err("ipset: could not open netfilter.");
                return NULL;
        }
 
-       if (mnl_socket_bind(mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
-               mnl_socket_close(mnl);
+       if (mnl_socket_bind(dev, 0, MNL_SOCKET_AUTOPID) < 0) {
+               mnl_socket_close(dev);
                log_err("ipset: could not bind netfilter.");
                return NULL;
        }
-       return mnl;
+       return (void *)dev;
 }
+#endif
+
+#ifdef HAVE_NET_PFVAR_H
+static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, int af) {
+       struct pfioc_table io;
+       struct pfr_addr addr;
+       const char *p;
+       int i;
+
+       bzero(&io, sizeof(io));
+       bzero(&addr, sizeof(addr));
+
+       p = strrchr(setname, '/');
+       if (p) {
+               i = p - setname;
+               if (i >= PATH_MAX) {
+                       errno = ENAMETOOLONG;
+                       return -1;
+               }
+               memcpy(io.pfrio_table.pfrt_anchor, setname, i);
+               if (i < PATH_MAX)
+                       io.pfrio_table.pfrt_anchor[i] = '\0';
+               p++;
+       }
+       else
+               p = setname;
 
-static int add_to_ipset(struct mnl_socket *mnl, const char *setname, const void *ipaddr, int af) {
+       if (strlen(p) >= PF_TABLE_NAME_SIZE) {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+       strlcpy(io.pfrio_table.pfrt_name, p, PF_TABLE_NAME_SIZE);
+
+       io.pfrio_buffer = &addr;
+       io.pfrio_size = 1;
+       io.pfrio_esize = sizeof(addr);
+
+       switch (af) {
+               case AF_INET:
+                       addr.pfra_ip4addr = *(struct in_addr *)ipaddr;
+                       addr.pfra_net = 32;
+                       break;
+               case AF_INET6:
+                       addr.pfra_ip6addr = *(struct in6_addr *)ipaddr;
+                       addr.pfra_net = 128;
+                       break;
+               default:
+               errno = EAFNOSUPPORT;
+               return -1;
+       }
+       addr.pfra_af = af;
+
+       if (ioctl(dev, DIOCRADDADDRS, &io) == -1) {
+               log_err("ioctl failed: %s", strerror(errno));
+               return -1;
+       }
+       return 0;
+}
+#else
+static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, int af) {
        struct nlmsghdr *nlh;
        struct nfgenmsg *nfg;
        struct nlattr *nested[2];
@@ -91,14 +172,15 @@ static int add_to_ipset(struct mnl_socket *mnl, const char *setname, const void
        mnl_attr_nest_end(nlh, nested[1]);
        mnl_attr_nest_end(nlh, nested[0]);
 
-       if (mnl_socket_sendto(mnl, nlh, nlh->nlmsg_len) < 0) {
+       if (mnl_socket_sendto(dev, nlh, nlh->nlmsg_len) < 0) {
                return -1;
        }
        return 0;
 }
+#endif
 
 static void
-ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl,
+ipset_add_rrset_data(struct ipset_env *ie,
        struct packed_rrset_data *d, const char* setname, int af,
        const char* dname)
 {
@@ -123,12 +205,16 @@ ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl,
                                        snprintf(ip, sizeof(ip), "(inet_ntop_error)");
                                verbose(VERB_QUERY, "ipset: add %s to %s for %s", ip, setname, dname);
                        }
-                       ret = add_to_ipset(mnl, setname, rr_data + 2, af);
+                       ret = add_to_ipset((filter_dev)ie->dev, setname, rr_data + 2, af);
                        if (ret < 0) {
                                log_err("ipset: could not add %s into %s", dname, setname);
 
-                               mnl_socket_close(mnl);
-                               ie->mnl = NULL;
+#if HAVE_NET_PFVAR_H
+                               /* don't close as we might not be able to open again due to dropped privs */
+#else
+                               mnl_socket_close((filter_dev)ie->dev);
+                               ie->dev = NULL;
+#endif
                                break;
                        }
                }
@@ -137,8 +223,8 @@ ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl,
 
 static int
 ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie,
-       struct mnl_socket *mnl, struct ub_packed_rrset_key *rrset,
-       const char *qname, const int qlen, const char *setname, int af)
+       struct ub_packed_rrset_key *rrset, const char *qname, int qlen,
+       const char *setname, int af)
 {
        static char dname[BUFF_LEN];
        const char *ds, *qs;
@@ -152,11 +238,20 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie,
                log_err("bad domain name");
                return -1;
        }
+       if (dname[dlen - 1] == '.') {
+               dlen--;
+       }
+       if (qname[qlen - 1] == '.') {
+               qlen--;
+       }
 
        for (p = env->cfg->local_zones_ipset; p; p = p->next) {
                ds = NULL;
                qs = NULL;
                plen = strlen(p->str);
+               if (p->str[plen - 1] == '.') {
+                       plen--;
+               }
 
                if (dlen == plen || (dlen > plen && dname[dlen - plen - 1] == '.' )) {
                        ds = dname + (dlen - plen);
@@ -167,8 +262,7 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie,
                if ((ds && strncasecmp(p->str, ds, plen) == 0)
                        || (qs && strncasecmp(p->str, qs, plen) == 0)) {
                        d = (struct packed_rrset_data*)rrset->entry.data;
-                       ipset_add_rrset_data(ie, mnl, d, setname,
-                               af, dname);
+                       ipset_add_rrset_data(ie, d, setname, af, dname);
                        break;
                }
        }
@@ -178,7 +272,6 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie,
 static int ipset_update(struct module_env *env, struct dns_msg *return_msg,
        struct query_info qinfo, struct ipset_env *ie)
 {
-       struct mnl_socket *mnl;
        size_t i;
        const char *setname;
        struct ub_packed_rrset_key *rrset;
@@ -186,15 +279,17 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg,
        static char qname[BUFF_LEN];
        int qlen;
 
-       mnl = (struct mnl_socket *)ie->mnl;
-       if (!mnl) {
+#ifdef HAVE_NET_PFVAR_H
+#else
+       if (!ie->dev) {
                /* retry to create mnl socket */
-               mnl = open_mnl_socket();
-               if (!mnl) {
+               ie->dev = open_filter();
+               if (!ie->dev) {
+                       log_warn("ipset open_filter failed");
                        return -1;
                }
-               ie->mnl = mnl;
        }
+#endif
 
        qlen = sldns_wire2str_dname_buf(qinfo.qname, qinfo.qname_len,
                qname, BUFF_LEN);
@@ -217,8 +312,8 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg,
                }
 
                if (setname) {
-                       if(ipset_check_zones_for_rrset(env, ie, mnl, rrset,
-                               qname, qlen, setname, af) == -1)
+                       if(ipset_check_zones_for_rrset(env, ie, rrset, qname,
+                               qlen, setname, af) == -1)
                                return -1;
                }
        }
@@ -226,7 +321,7 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg,
        return 0;
 }
 
-int ipset_init(struct module_env* env, int id) {
+int ipset_startup(struct module_env* env, int id) {
        struct ipset_env *ipset_env;
 
        ipset_env = (struct ipset_env *)calloc(1, sizeof(struct ipset_env));
@@ -237,7 +332,43 @@ int ipset_init(struct module_env* env, int id) {
 
        env->modinfo[id] = (void *)ipset_env;
 
-       ipset_env->mnl = NULL;
+#ifdef HAVE_NET_PFVAR_H
+       ipset_env->dev = open_filter();
+       if (!ipset_env->dev) {
+               log_err("ipset open_filter failed");
+               return 0;
+       }
+#else
+       ipset_env->dev = NULL;
+#endif
+       return 1;
+}
+
+void ipset_destartup(struct module_env* env, int id) {
+       filter_dev dev;
+       struct ipset_env *ipset_env;
+
+       if (!env || !env->modinfo[id]) {
+               return;
+       }
+       ipset_env = (struct ipset_env*)env->modinfo[id];
+
+       dev = (filter_dev)ipset_env->dev;
+       if (dev) {
+#if HAVE_NET_PFVAR_H
+               close(dev);
+#else
+               mnl_socket_close(dev);
+#endif
+               ipset_env->dev = NULL;
+       }
+
+       free(ipset_env);
+       env->modinfo[id] = NULL;
+}
+
+int ipset_init(struct module_env* env, int id) {
+       struct ipset_env *ipset_env = env->modinfo[id];
 
        ipset_env->name_v4 = env->cfg->ipset_name_v4;
        ipset_env->name_v6 = env->cfg->ipset_name_v6;
@@ -253,24 +384,8 @@ int ipset_init(struct module_env* env, int id) {
        return 1;
 }
 
-void ipset_deinit(struct module_env *env, int id) {
-       struct mnl_socket *mnl;
-       struct ipset_env *ipset_env;
-
-       if (!env || !env->modinfo[id]) {
-               return;
-       }
-
-       ipset_env = (struct ipset_env *)env->modinfo[id];
-
-       mnl = (struct mnl_socket *)ipset_env->mnl;
-       if (mnl) {
-               mnl_socket_close(mnl);
-               ipset_env->mnl = NULL;
-       }
-
-       free(ipset_env);
-       env->modinfo[id] = NULL;
+void ipset_deinit(struct module_env *ATTR_UNUSED(env), int ATTR_UNUSED(id)) {
+       /* nothing */
 }
 
 static int ipset_new(struct module_qstate* qstate, int id) {
@@ -376,8 +491,8 @@ size_t ipset_get_mem(struct module_env *env, int id) {
  */
 static struct module_func_block ipset_block = {
        "ipset",
-       &ipset_init, &ipset_deinit, &ipset_operate,
-       &ipset_inform_super, &ipset_clear, &ipset_get_mem
+       &ipset_startup, &ipset_destartup, &ipset_init, &ipset_deinit,
+       &ipset_operate, &ipset_inform_super, &ipset_clear, &ipset_get_mem
 };
 
 struct module_func_block * ipset_get_funcblock(void) {
index f60a8be..195c7db 100644 (file)
@@ -37,7 +37,7 @@ extern "C" {
 #endif
 
 struct ipset_env {
-    void* mnl;
+       void* dev;
 
        int v4_enabled;
        int v6_enabled;
@@ -50,6 +50,10 @@ struct ipset_qstate {
        int dummy;
 };
 
+/** Startup the ipset module */
+int ipset_startup(struct module_env* env, int id);
+/** Destartup the ipset module */
+void ipset_destartup(struct module_env* env, int id);
 /** Init the ipset module */
 int ipset_init(struct module_env* env, int id);
 /** Deinit the ipset module */
index f036cc5..d8999a8 100644 (file)
@@ -242,10 +242,14 @@ rpz_action_to_localzone_type(enum rpz_action a)
        case RPZ_NODATA_ACTION: return local_zone_always_nodata;
        case RPZ_DROP_ACTION: return local_zone_always_deny;
        case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent;
-       case RPZ_LOCAL_DATA_ACTION:     /* fallthrough */
+       case RPZ_LOCAL_DATA_ACTION:
+               ATTR_FALLTHROUGH
+               /* fallthrough */
        case RPZ_CNAME_OVERRIDE_ACTION: return local_zone_redirect;
        case RPZ_TCP_ONLY_ACTION: return local_zone_truncate;
-       case RPZ_INVALID_ACTION: /* fallthrough */
+       case RPZ_INVALID_ACTION:
+               ATTR_FALLTHROUGH
+               /* fallthrough */
        default: return local_zone_invalid;
        }
 }
@@ -258,10 +262,14 @@ rpz_action_to_respip_action(enum rpz_action a)
        case RPZ_NODATA_ACTION: return respip_always_nodata;
        case RPZ_DROP_ACTION: return respip_always_deny;
        case RPZ_PASSTHRU_ACTION: return respip_always_transparent;
-       case RPZ_LOCAL_DATA_ACTION: /* fallthrough */
+       case RPZ_LOCAL_DATA_ACTION:
+               ATTR_FALLTHROUGH
+               /* fallthrough */
        case RPZ_CNAME_OVERRIDE_ACTION: return respip_redirect;
        case RPZ_TCP_ONLY_ACTION: return respip_truncate;
-       case RPZ_INVALID_ACTION: /* fallthrough */
+       case RPZ_INVALID_ACTION:
+               ATTR_FALLTHROUGH
+               /* fallthrough */
        default: return respip_invalid;
        }
 }
@@ -276,7 +284,9 @@ localzone_type_to_rpz_action(enum localzone_type lzt)
        case local_zone_always_transparent: return RPZ_PASSTHRU_ACTION;
        case local_zone_redirect: return RPZ_LOCAL_DATA_ACTION;
        case local_zone_truncate: return RPZ_TCP_ONLY_ACTION;
-       case local_zone_invalid: /* fallthrough */
+       case local_zone_invalid:
+               ATTR_FALLTHROUGH
+               /* fallthrough */
        default: return RPZ_INVALID_ACTION;
        }
 }
@@ -291,7 +301,9 @@ respip_action_to_rpz_action(enum respip_action a)
        case respip_always_transparent: return RPZ_PASSTHRU_ACTION;
        case respip_redirect: return RPZ_LOCAL_DATA_ACTION;
        case respip_truncate: return RPZ_TCP_ONLY_ACTION;
-       case respip_invalid: /* fallthrough */
+       case respip_invalid:
+               ATTR_FALLTHROUGH
+               /* fallthrough */
        default: return RPZ_INVALID_ACTION;
        }
 }
@@ -2435,11 +2447,10 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate*
        if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; }
 
        az = ms->env->auth_zones;
+       lock_rw_rdlock(&az->rpz_lock);
 
        verbose(VERB_ALGO, "rpz: iterator module callback: have_rpz=%d", az->rpz_first != NULL);
 
-       lock_rw_rdlock(&az->rpz_lock);
-
        /* precedence of RPZ works, loosely, like this:
         * CNAMEs in order of the CNAME chain. rpzs in the order they are
         * configured. In an RPZ: first client-IP addr, then QNAME, then
@@ -2454,6 +2465,13 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate*
                        lock_rw_unlock(&a->lock);
                        continue;
                }
+               if(r->taglist && (!ms->client_info ||
+                       !taglist_intersect(r->taglist, r->taglistlen,
+                               ms->client_info->taglist,
+                               ms->client_info->taglen))) {
+                       lock_rw_unlock(&a->lock);
+                       continue;
+               }
 
                /* the nsdname has precedence over the nsip triggers */
                z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones,
@@ -2512,6 +2530,13 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms,
                        lock_rw_unlock(&a->lock);
                        continue;
                }
+               if(r->taglist && (!ms->client_info ||
+                       !taglist_intersect(r->taglist, r->taglistlen,
+                               ms->client_info->taglist,
+                               ms->client_info->taglen))) {
+                       lock_rw_unlock(&a->lock);
+                       continue;
+               }
                z = rpz_find_zone(r->local_zones, is->qchase.qname,
                        is->qchase.qname_len, is->qchase.qclass, 0, 0, 0);
                if(z && r->action_override == RPZ_DISABLED_ACTION) {
index e5d4e27..154f4ff 100644 (file)
@@ -2,17 +2,19 @@
 #   Copyright 2015, Sami Kerola, CloudFlare.
 #   BSD licensed.
 AC_ARG_ENABLE([systemd],
-       [AS_HELP_STRING([--enable-systemd], [compile with systemd support])],
+       [AS_HELP_STRING([--enable-systemd], [compile with systemd support (requires libsystemd, pkg-config)])],
        [], [enable_systemd=no])
 have_systemd=no
 AS_IF([test "x$enable_systemd" != xno], [
-    ifdef([PKG_CHECK_MODULES], [
+    if test -n "$PKG_CONFIG"; then
        dnl systemd v209 or newer
-       PKG_CHECK_MODULES([SYSTEMD], [libsystemd], [have_systemd=yes], [have_systemd=no])
+       have_systemd=no
+       PKG_CHECK_MODULES([SYSTEMD], [libsystemd], [have_systemd=yes], [])
        dnl old systemd library
        AS_IF([test "x$have_systemd" != "xyes"], [
+               have_systemd_daemon=no
                PKG_CHECK_MODULES([SYSTEMD_DAEMON], [libsystemd-daemon],
-                       [have_systemd_daemon=yes], [have_systemd_daemon=no])
+                       [have_systemd_daemon=yes], [])
                AS_IF([test "x$have_systemd_daemon" = "xyes"],
                        [have_systemd=yes])
        ])
@@ -24,8 +26,8 @@ AS_IF([test "x$enable_systemd" != xno], [
                LIBS="$LIBS $SYSTEMD_LIBS"
                ]
        )
-    ], [
+    else
        AC_MSG_ERROR([systemd enabled but need pkg-config to configure for it])
-    ])
+    fi
 ])
 AM_CONDITIONAL([USE_SYSTEMD], [test "x$have_systemd" = xyes])
index 0926928..a517fa5 100644 (file)
@@ -1655,6 +1655,12 @@ void comm_timer_set(struct comm_timer* timer, struct timeval* tv)
        timeval_add(&t->tv, &t->runtime->now_tv);
 }
 
+int comm_timer_is_set(struct comm_timer* timer)
+{
+       struct fake_timer* t = (struct fake_timer*)timer;
+       return t->enabled;
+}
+
 void comm_timer_delete(struct comm_timer* timer)
 {
        struct fake_timer* t = (struct fake_timer*)timer;
@@ -1978,4 +1984,8 @@ void http2_stream_add_meshstate(struct http2_stream* ATTR_UNUSED(h2_stream),
 {
 }
 
+void http2_stream_remove_mesh_state(struct http2_stream* ATTR_UNUSED(h2_stream))
+{
+}
+
 /*********** End of Dummy routines ***********/
index 624ecdf..d1f7bfc 100755 (executable)
@@ -127,6 +127,7 @@ dir=$name.$$
 result=result.$name
 done=.done-$name
 skip=.skip-$name
+asan_text="SUMMARY: AddressSanitizer"
 success="no"
 if test -x "`which bash`"; then
        shell="bash"
@@ -200,6 +201,16 @@ if test -f $name.post -a ! -f ../$skip; then
                echo "Warning: $name.post did not exit successfully"
        fi
 fi
+# Check if there were any AddressSanitizer errors
+# if compiled with -fsanitize=address
+if grep "$asan_text" $result >/dev/null 2>&1; then
+       if test -f ../$done; then
+               rm ../$done
+       fi
+       echo "$name: FAILED (AddressSanitizer)" >> $result
+       echo "$name: FAILED (AddressSanitizer)"
+       success="no"
+fi
 echo "DateRunEnd: "`date "+%s" 2>/dev/null` >> $result
 
 mv $result ..
index 63d3d45..6d825f1 100644 (file)
@@ -256,7 +256,7 @@ setup_ctx(char* key, char* cert)
 #if HAVE_DECL_SSL_CTX_SET_ECDH_AUTO
        if (!SSL_CTX_set_ecdh_auto(ctx,1))
                if(verb>=1) printf("failed to set_ecdh_auto, not enabling ECDHE\n");
-#elif defined(USE_ECDSA)
+#elif defined(USE_ECDSA) && defined(HAVE_SSL_CTX_SET_TMP_ECDH)
        if(1) {
                EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1);
                if (!ecdh) {
index 94511e5..f50eea3 100644 (file)
@@ -45,7 +45,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <stdint.h>
 #include "sldns/str2wire.h"
 #include "sldns/wire2str.h"
 
index 037bcfd..60122c4 100644 (file)
@@ -471,7 +471,11 @@ send_em(const char* svr, const char* pp2_client, int udp, int usessl,
                        }
                }
                if(1) {
+#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
+                       X509* x = SSL_get1_peer_certificate(ssl);
+#else
                        X509* x = SSL_get_peer_certificate(ssl);
+#endif
                        if(!x) printf("SSL: no peer certificate\n");
                        else {
                                X509_print_fp(stdout, x);
index f023860..123fe0d 100644 (file)
@@ -72,23 +72,6 @@ int daemon_main(int argc, char* argv[]);
 /** config files (removed at exit) */
 static struct config_strlist* cfgfiles = NULL;
 
-#ifdef UNBOUND_ALLOC_STATS
-#  define strdup(s) unbound_stat_strdup_log(s, __FILE__, __LINE__, __func__)
-char* unbound_stat_strdup_log(char* s, const char* file, int line,
-       const char* func);
-char* unbound_stat_strdup_log(char* s, const char* file, int line,
-        const char* func) {
-       char* result;
-       size_t len;
-       if(!s) return NULL;
-       len = strlen(s);
-       log_info("%s:%d %s strdup(%u)", file, line, func, (unsigned)len+1);
-       result = unbound_stat_malloc(len+1);
-       memmove(result, s, len+1);
-       return result;
-}
-#endif /* UNBOUND_ALLOC_STATS */
-
 /** give commandline usage for testbound. */
 static void
 testbound_usage(void)
index 647cbca..084c12b 100644 (file)
@@ -1117,7 +1117,7 @@ static void edns_ede_encode_encodedecode(struct query_info* qinfo,
        sldns_buffer_skip(pkt, 2 + 2);
        /* decode */
        unit_assert(parse_edns_from_query_pkt(pkt, edns, NULL, NULL, NULL, 0,
-               region) == 0);
+               region, NULL) == 0);
 }
 
 static void edns_ede_encode_check(struct edns_data* edns, int* found_ede,
@@ -1252,6 +1252,109 @@ static void edns_ede_answer_encode_test(void)
        regional_destroy(region);
 }
 
+#include "services/localzone.h"
+/* Utility function that compares two localzone trees */
+static void compare_localzone_trees(struct local_zones* z1,
+       struct local_zones* z2)
+{
+       struct local_zone *node1, *node2;
+       lock_rw_rdlock(&z1->lock);
+       lock_rw_rdlock(&z2->lock);
+       /* size should be the same */
+       unit_assert(z1->ztree.count == z2->ztree.count);
+       for(node1=(struct local_zone*)rbtree_first(&z1->ztree),
+               node2=(struct local_zone*)rbtree_first(&z2->ztree);
+               (rbnode_type*)node1 != RBTREE_NULL &&
+               (rbnode_type*)node2 != RBTREE_NULL;
+               node1=(struct local_zone*)rbtree_next((rbnode_type*)node1),
+               node2=(struct local_zone*)rbtree_next((rbnode_type*)node2)) {
+               int labs;
+               /* the same zone should be at the same nodes */
+               unit_assert(!dname_lab_cmp(
+                       node1->name, node1->namelabs,
+                       node2->name, node2->namelabs,
+                       &labs));
+               /* the zone's parent should be the same on both nodes */
+               unit_assert(
+                       (node1->parent == NULL && node2->parent == NULL) ||
+                       (node1->parent != NULL && node2->parent != NULL));
+               if(node1->parent) {
+                       unit_assert(!dname_lab_cmp(
+                               node1->parent->name, node1->parent->namelabs,
+                               node2->parent->name, node2->parent->namelabs,
+                               &labs));
+               }
+       }
+       lock_rw_unlock(&z1->lock);
+       lock_rw_unlock(&z2->lock);
+}
+
+/* test that zone addition results in the same tree from both the configuration
+ * file and the unbound-control commands */
+static void localzone_parents_test(void)
+{
+       struct local_zones *z1, *z2;
+       size_t i;
+       char* zone_data[] = {
+               "one",
+               "a.b.c.one",
+               "b.c.one",
+               "c.one",
+               "two",
+               "c.two",
+               "b.c.two",
+               "a.b.c.two",
+               "a.b.c.three",
+               "b.c.three",
+               "c.three",
+               "three",
+               "c.four",
+               "b.c.four",
+               "a.b.c.four",
+               "four",
+               "."
+       };
+       unit_show_feature("localzones parent calculation");
+       z1 = local_zones_create();
+       z2 = local_zones_create();
+       /* parse test data */
+       for(i=0; i<sizeof(zone_data)/sizeof(zone_data[0]); i++) {
+               uint8_t* nm;
+               int nmlabs;
+               size_t nmlen;
+               struct local_zone* z;
+
+               /* This is the config way */
+               z = lz_enter_zone(z1, zone_data[i], "always_nxdomain",
+                       LDNS_RR_CLASS_IN);
+               (void)z;  /* please compiler when no threading and no lock
+               code; the following line disappears and z stays unused */
+               lock_rw_unlock(&z->lock);
+               lz_init_parents(z1);
+
+               /* This is the unbound-control way */
+               nm = sldns_str2wire_dname(zone_data[i], &nmlen);
+               if(!nm) unit_assert(0);
+               nmlabs = dname_count_size_labels(nm, &nmlen);
+               lock_rw_wrlock(&z2->lock);
+               local_zones_add_zone(z2, nm, nmlen, nmlabs, LDNS_RR_CLASS_IN,
+                       local_zone_always_nxdomain);
+               lock_rw_unlock(&z2->lock);
+       }
+       /* The trees should be the same, iterate and check the nodes */
+       compare_localzone_trees(z1, z2);
+
+       /* cleanup */
+       local_zones_delete(z1);
+       local_zones_delete(z2);
+}
+
+/** localzone unit tests */
+static void localzone_test(void)
+{
+       localzone_parents_test();
+}
+
 void unit_show_func(const char* file, const char* func)
 {
        printf("test %s:%s\n", file, func);
@@ -1325,6 +1428,7 @@ main(int argc, char* argv[])
        tcpreuse_test();
        msgparse_test();
        edns_ede_answer_encode_test();
+       localzone_test();
 #ifdef CLIENT_SUBNET
        ecs_test();
 #endif /* CLIENT_SUBNET */
index 23c9f70..bf130df 100644 (file)
@@ -256,7 +256,6 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr,
        struct auth_zone* z;
 
        /* setup test harness */
-       memset(&mods, 0, sizeof(mods));
        memset(&env, 0, sizeof(env));
        env.scratch = regional_create();
        if(!env.scratch)
@@ -288,8 +287,10 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr,
        if(!env.auth_zones)
                fatal_exit("out of memory");
        modstack_init(&mods);
-       if(!modstack_setup(&mods, env.cfg->module_conf, &env))
-               fatal_exit("could not modstack_setup");
+       if(!modstack_call_startup(&mods, env.cfg->module_conf, &env))
+               fatal_exit("could not modstack_startup");
+       if(!modstack_call_init(&mods, env.cfg->module_conf, &env))
+               fatal_exit("could not modstack_call_init");
        env.mesh = mesh_create(&mods, &env);
        if(!env.mesh)
                fatal_exit("out of memory");
@@ -327,7 +328,9 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr,
 
        /* desetup test harness */
        mesh_delete(env.mesh);
-       modstack_desetup(&mods, &env);
+       modstack_call_deinit(&mods, &env);
+       modstack_call_destartup(&mods, &env);
+       modstack_free(&mods);
        auth_zones_delete(env.auth_zones);
        anchors_delete(env.anchors);
        config_delete(env.cfg);
index 2b4047f..ee95a69 100644 (file)
@@ -187,3 +187,189 @@ edns_cookie_server_validate(const uint8_t* cookie, size_t cookie_len,
                return COOKIE_STATUS_VALID_RENEW;
        return COOKIE_STATUS_VALID;
 }
+
+struct cookie_secrets*
+cookie_secrets_create(void)
+{
+       struct cookie_secrets* cookie_secrets = calloc(1,
+               sizeof(*cookie_secrets));
+       if(!cookie_secrets)
+               return NULL;
+       lock_basic_init(&cookie_secrets->lock);
+       lock_protect(&cookie_secrets->lock, &cookie_secrets->cookie_count,
+               sizeof(cookie_secrets->cookie_count));
+       lock_protect(&cookie_secrets->lock, cookie_secrets->cookie_secrets,
+               sizeof(cookie_secret_type)*UNBOUND_COOKIE_HISTORY_SIZE);
+       return cookie_secrets;
+}
+
+void
+cookie_secrets_delete(struct cookie_secrets* cookie_secrets)
+{
+       if(!cookie_secrets)
+               return;
+       lock_basic_destroy(&cookie_secrets->lock);
+       explicit_bzero(cookie_secrets->cookie_secrets,
+               sizeof(cookie_secret_type)*UNBOUND_COOKIE_HISTORY_SIZE);
+       free(cookie_secrets);
+}
+
+/** Read the cookie secret file */
+static int
+cookie_secret_file_read(struct cookie_secrets* cookie_secrets,
+       char* cookie_secret_file)
+{
+       char secret[UNBOUND_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/];
+       FILE* f;
+       int corrupt = 0;
+       size_t count;
+
+       log_assert(cookie_secret_file != NULL);
+       cookie_secrets->cookie_count = 0;
+       f = fopen(cookie_secret_file, "r");
+       /* a non-existing cookie file is not an error */
+       if( f == NULL ) {
+               if(errno != EPERM) {
+                       log_err("Could not read cookie-secret-file '%s': %s",
+                               cookie_secret_file, strerror(errno));
+                       return 0;
+               }
+               return 1;
+       }
+       /* cookie secret file exists and is readable */
+       for( count = 0; count < UNBOUND_COOKIE_HISTORY_SIZE; count++ ) {
+               size_t secret_len = 0;
+               ssize_t decoded_len = 0;
+               if( fgets(secret, sizeof(secret), f) == NULL ) { break; }
+               secret_len = strlen(secret);
+               if( secret_len == 0 ) { break; }
+               log_assert( secret_len <= sizeof(secret) );
+               secret_len = secret[secret_len - 1] == '\n' ? secret_len - 1 : secret_len;
+               if( secret_len != UNBOUND_COOKIE_SECRET_SIZE * 2 ) { corrupt++; break; }
+               /* needed for `hex_pton`; stripping potential `\n` */
+               secret[secret_len] = '\0';
+               decoded_len = hex_pton(secret, cookie_secrets->cookie_secrets[count].cookie_secret,
+                                      UNBOUND_COOKIE_SECRET_SIZE);
+               if( decoded_len != UNBOUND_COOKIE_SECRET_SIZE ) { corrupt++; break; }
+               cookie_secrets->cookie_count++;
+       }
+       fclose(f);
+       return corrupt == 0;
+}
+
+int
+cookie_secrets_apply_cfg(struct cookie_secrets* cookie_secrets,
+       char* cookie_secret_file)
+{
+       if(!cookie_secrets) {
+               if(!cookie_secret_file || !cookie_secret_file[0])
+                       return 1; /* There is nothing to read anyway */
+               log_err("Could not read cookie secrets, no structure alloced");
+               return 0;
+       }
+       if(!cookie_secret_file_read(cookie_secrets, cookie_secret_file))
+               return 0;
+       return 1;
+}
+
+enum edns_cookie_val_status
+cookie_secrets_server_validate(const uint8_t* cookie, size_t cookie_len,
+       struct cookie_secrets* cookie_secrets, int v4,
+       const uint8_t* hash_input, uint32_t now)
+{
+       size_t i;
+       enum edns_cookie_val_status cookie_val_status,
+               last = COOKIE_STATUS_INVALID;
+       if(!cookie_secrets)
+               return COOKIE_STATUS_INVALID; /* There are no cookie secrets.*/
+       lock_basic_lock(&cookie_secrets->lock);
+       if(cookie_secrets->cookie_count == 0) {
+               lock_basic_unlock(&cookie_secrets->lock);
+               return COOKIE_STATUS_INVALID; /* There are no cookie secrets.*/
+       }
+       for(i=0; i<cookie_secrets->cookie_count; i++) {
+               cookie_val_status = edns_cookie_server_validate(cookie,
+                       cookie_len,
+                       cookie_secrets->cookie_secrets[i].cookie_secret,
+                       UNBOUND_COOKIE_SECRET_SIZE, v4, hash_input, now);
+               if(cookie_val_status == COOKIE_STATUS_VALID ||
+                       cookie_val_status == COOKIE_STATUS_VALID_RENEW) {
+                       lock_basic_unlock(&cookie_secrets->lock);
+                       /* For staging cookies, write a fresh cookie. */
+                       if(i != 0)
+                               return COOKIE_STATUS_VALID_RENEW;
+                       return cookie_val_status;
+               }
+               if(last == COOKIE_STATUS_INVALID)
+                       last = cookie_val_status; /* Store more interesting
+                               failure to return. */
+       }
+       lock_basic_unlock(&cookie_secrets->lock);
+       return last;
+}
+
+void add_cookie_secret(struct cookie_secrets* cookie_secrets,
+       uint8_t* secret, size_t secret_len)
+{
+       log_assert(secret_len == UNBOUND_COOKIE_SECRET_SIZE);
+       (void)secret_len;
+       if(!cookie_secrets)
+               return;
+
+       /* New cookie secret becomes the staging secret (position 1)
+        * unless there is no active cookie yet, then it becomes the active
+        * secret.  If the UNBOUND_COOKIE_HISTORY_SIZE > 2 then all staging cookies
+        * are moved one position down.
+        */
+       if(cookie_secrets->cookie_count == 0) {
+               memcpy( cookie_secrets->cookie_secrets->cookie_secret
+                      , secret, UNBOUND_COOKIE_SECRET_SIZE);
+               cookie_secrets->cookie_count = 1;
+               explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
+               return;
+       }
+#if UNBOUND_COOKIE_HISTORY_SIZE > 2
+       memmove( &cookie_secrets->cookie_secrets[2], &cookie_secrets->cookie_secrets[1]
+              , sizeof(struct cookie_secret) * (UNBOUND_COOKIE_HISTORY_SIZE - 2));
+#endif
+       memcpy( cookie_secrets->cookie_secrets[1].cookie_secret
+             , secret, UNBOUND_COOKIE_SECRET_SIZE);
+       cookie_secrets->cookie_count = cookie_secrets->cookie_count     < UNBOUND_COOKIE_HISTORY_SIZE
+                         ? cookie_secrets->cookie_count + 1 : UNBOUND_COOKIE_HISTORY_SIZE;
+       explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
+}
+
+void activate_cookie_secret(struct cookie_secrets* cookie_secrets)
+{
+       uint8_t active_secret[UNBOUND_COOKIE_SECRET_SIZE];
+       if(!cookie_secrets)
+               return;
+       /* The staging secret becomes the active secret.
+        * The active secret becomes a staging secret.
+        * If the UNBOUND_COOKIE_HISTORY_SIZE > 2 then all staging secrets are moved
+        * one position up and the previously active secret becomes the last
+        * staging secret.
+        */
+       if(cookie_secrets->cookie_count < 2)
+               return;
+       memcpy( active_secret, cookie_secrets->cookie_secrets[0].cookie_secret
+             , UNBOUND_COOKIE_SECRET_SIZE);
+       memmove( &cookie_secrets->cookie_secrets[0], &cookie_secrets->cookie_secrets[1]
+              , sizeof(struct cookie_secret) * (UNBOUND_COOKIE_HISTORY_SIZE - 1));
+       memcpy( cookie_secrets->cookie_secrets[cookie_secrets->cookie_count - 1].cookie_secret
+             , active_secret, UNBOUND_COOKIE_SECRET_SIZE);
+       explicit_bzero(active_secret, UNBOUND_COOKIE_SECRET_SIZE);
+}
+
+void drop_cookie_secret(struct cookie_secrets* cookie_secrets)
+{
+       if(!cookie_secrets)
+               return;
+       /* Drops a staging cookie secret. If there are more than one, it will
+        * drop the last staging secret. */
+       if(cookie_secrets->cookie_count < 2)
+               return;
+       explicit_bzero( cookie_secrets->cookie_secrets[cookie_secrets->cookie_count - 1].cookie_secret
+                     , UNBOUND_COOKIE_SECRET_SIZE);
+       cookie_secrets->cookie_count -= 1;
+}
index 5da0ecb..47ccb1a 100644 (file)
@@ -43,6 +43,7 @@
 #define UTIL_EDNS_H
 
 #include "util/storage/dnstree.h"
+#include "util/locks.h"
 
 struct edns_data;
 struct config_file;
@@ -75,6 +76,31 @@ struct edns_string_addr {
        size_t string_len;
 };
 
+#define UNBOUND_COOKIE_HISTORY_SIZE 2
+#define UNBOUND_COOKIE_SECRET_SIZE 16
+
+typedef struct cookie_secret cookie_secret_type;
+struct cookie_secret {
+       /** cookie secret */
+       uint8_t cookie_secret[UNBOUND_COOKIE_SECRET_SIZE];
+};
+
+/**
+ * The cookie secrets from the cookie-secret-file.
+ */
+struct cookie_secrets {
+       /** lock on the structure, in case there are modifications
+        * from remote control, this avoids race conditions. */
+       lock_basic_type lock;
+
+       /** how many cookies are there in the cookies array */
+       size_t cookie_count;
+
+       /* keep track of the last `UNBOUND_COOKIE_HISTORY_SIZE`
+        * cookies as per rfc requirement .*/
+       cookie_secret_type cookie_secrets[UNBOUND_COOKIE_HISTORY_SIZE];
+};
+
 enum edns_cookie_val_status {
        COOKIE_STATUS_CLIENT_ONLY = -3,
        COOKIE_STATUS_FUTURE = -2,
@@ -165,4 +191,63 @@ enum edns_cookie_val_status edns_cookie_server_validate(const uint8_t* cookie,
        size_t cookie_len, const uint8_t* secret, size_t secret_len, int v4,
        const uint8_t* hash_input, uint32_t now);
 
+/**
+ * Create the cookie secrets structure.
+ * @return the structure or NULL on failure.
+ */
+struct cookie_secrets* cookie_secrets_create(void);
+
+/**
+ * Delete the cookie secrets.
+ * @param cookie_secrets: the cookie secrets.
+ */
+void cookie_secrets_delete(struct cookie_secrets* cookie_secrets);
+
+/**
+ * Apply configuration to cookie secrets, read them from file.
+ * @param cookie_secrets: the cookie secrets structure.
+ * @param cookie_secret_file: the file name, it is read.
+ * @return false on failure.
+ */
+int cookie_secrets_apply_cfg(struct cookie_secrets* cookie_secrets,
+       char* cookie_secret_file);
+
+/**
+ * Validate the cookie secrets, try all of them.
+ * @param cookie: pointer to the cookie data.
+ * @param cookie_len: the length of the cookie data.
+ * @param cookie_secrets: struct of cookie secrets.
+ * @param v4: if the client IP is v4 or v6.
+ * @param hash_input: pointer to the hash input for validation. It needs to be:
+ *     Client Cookie | Version | Reserved | Timestamp | Client-IP
+ * @param now: the current time.
+ * return edns_cookie_val_status with the cookie validation status i.e.,
+ *     <=0 for invalid, else valid.
+ */
+enum edns_cookie_val_status cookie_secrets_server_validate(
+       const uint8_t* cookie, size_t cookie_len,
+       struct cookie_secrets* cookie_secrets, int v4,
+       const uint8_t* hash_input, uint32_t now);
+
+/**
+ * Add a cookie secret. If there are no secrets yet, the secret will become
+ * the active secret. Otherwise it will become the staging secret.
+ * Active secrets are used to both verify and create new DNS Cookies.
+ * Staging secrets are only used to verify DNS Cookies. Caller has to lock.
+ */
+void add_cookie_secret(struct cookie_secrets* cookie_secrets, uint8_t* secret,
+       size_t secret_len);
+
+/**
+ * Makes the staging cookie secret active and the active secret staging.
+ * Caller has to lock.
+ */
+void activate_cookie_secret(struct cookie_secrets* cookie_secrets);
+
+/**
+ * Drop a cookie secret. Drops the staging secret. An active secret will not
+ * be dropped. Caller has to lock.
+ */
+void drop_cookie_secret(struct cookie_secrets* cookie_secrets);
+
 #endif
index a188049..235538b 100644 (file)
@@ -153,6 +153,7 @@ pp2_write_to_buf(uint8_t* buf, size_t buflen,
                break;
 #endif /* INET6 */
        case AF_UNIX:
+               ATTR_FALLTHROUGH
                /* fallthrough */
        default:
                return 0;
index 32797df..a13657c 100644 (file)
@@ -128,26 +128,32 @@ int siphash(const uint8_t *in, const size_t inlen, const uint8_t *k,
     case 7:
         b |= ((uint64_t)in[6]) << 48;
         /** EDIT annotate case statement fallthrough for gcc */
+       ATTR_FALLTHROUGH
         /* fallthrough */
     case 6:
         b |= ((uint64_t)in[5]) << 40;
         /** EDIT annotate case statement fallthrough for gcc */
+       ATTR_FALLTHROUGH
         /* fallthrough */
     case 5:
         b |= ((uint64_t)in[4]) << 32;
         /** EDIT annotate case statement fallthrough for gcc */
+       ATTR_FALLTHROUGH
         /* fallthrough */
     case 4:
         b |= ((uint64_t)in[3]) << 24;
         /** EDIT annotate case statement fallthrough for gcc */
+       ATTR_FALLTHROUGH
         /* fallthrough */
     case 3:
         b |= ((uint64_t)in[2]) << 16;
         /** EDIT annotate case statement fallthrough for gcc */
+       ATTR_FALLTHROUGH
         /* fallthrough */
     case 2:
         b |= ((uint64_t)in[1]) << 8;
         /** EDIT annotate case statement fallthrough for gcc */
+       ATTR_FALLTHROUGH
         /* fallthrough */
     case 1:
         b |= ((uint64_t)in[0]);