From: sthen Date: Wed, 4 Sep 2024 09:35:34 +0000 (+0000) Subject: import unbound 1.21.0, ok phessler florian X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=a43524d9cc222a049058246319ec6a29f2d9ca78;p=openbsd import unbound 1.21.0, ok phessler florian --- diff --git a/usr.sbin/unbound/dnstap/dnstap.h b/usr.sbin/unbound/dnstap/dnstap.h index 77914c20c9c..21c033697da 100644 --- a/usr.sbin/unbound/dnstap/dnstap.h +++ b/usr.sbin/unbound/dnstap/dnstap.h @@ -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; }; /** diff --git a/usr.sbin/unbound/dnstap/dnstap.m4 b/usr.sbin/unbound/dnstap/dnstap.m4 index 80b7bc92c92..78d0dd68b76 100644 --- a/usr.sbin/unbound/dnstap/dnstap.m4 +++ b/usr.sbin/unbound/dnstap/dnstap.m4 @@ -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], [], diff --git a/usr.sbin/unbound/dnstap/dtstream.c b/usr.sbin/unbound/dnstap/dtstream.c index 69c951276ff..2d5ab20f0c8 100644 --- a/usr.sbin/unbound/dnstap/dtstream.c +++ b/usr.sbin/unbound/dnstap/dtstream.c @@ -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 */ } } diff --git a/usr.sbin/unbound/dnstap/unbound-dnstap-socket.c b/usr.sbin/unbound/dnstap/unbound-dnstap-socket.c index 04fda74b80e..f203aa7d73a 100644 --- a/usr.sbin/unbound/dnstap/unbound-dnstap-socket.c +++ b/usr.sbin/unbound/dnstap/unbound-dnstap-socket.c @@ -75,17 +75,18 @@ 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 listen to unix socket with this file name\n"); - printf("-s listen for TCP on the IP and port\n"); - printf("-t listen for TLS on IP and port\n"); - printf("-x server key file for TLS service\n"); - printf("-y server cert file for TLS service\n"); - printf("-z 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 listen to unix socket with this file name\n"); + printf("-s listen for TCP on the IP and port\n"); + printf("-t listen for TLS on IP and port\n"); + printf("-x server key file for TLS service\n"); + printf("-y server cert file for TLS service\n"); + printf("-z 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; idata_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; inext; + 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; inext; + 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(); diff --git a/usr.sbin/unbound/dynlibmod/dynlibmod.c b/usr.sbin/unbound/dynlibmod/dynlibmod.c index 1e040a30e74..c941154927e 100644 --- a/usr.sbin/unbound/dynlibmod/dynlibmod.c +++ b/usr.sbin/unbound/dynlibmod/dynlibmod.c @@ -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) diff --git a/usr.sbin/unbound/ipset/ipset.c b/usr.sbin/unbound/ipset/ipset.c index af55de8d6fc..1ad2c09f46d 100644 --- a/usr.sbin/unbound/ipset/ipset.c +++ b/usr.sbin/unbound/ipset/ipset.c @@ -17,9 +17,19 @@ #include "sldns/wire2str.h" #include "sldns/parseutil.h" +#ifdef HAVE_NET_PFVAR_H +#include +#include +#include +#include +#include +typedef intptr_t filter_dev; +#else #include #include #include +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) { diff --git a/usr.sbin/unbound/ipset/ipset.h b/usr.sbin/unbound/ipset/ipset.h index f60a8be8c83..195c7db93cd 100644 --- a/usr.sbin/unbound/ipset/ipset.h +++ b/usr.sbin/unbound/ipset/ipset.h @@ -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 */ diff --git a/usr.sbin/unbound/services/rpz.c b/usr.sbin/unbound/services/rpz.c index f036cc5fd64..d8999a8a55e 100644 --- a/usr.sbin/unbound/services/rpz.c +++ b/usr.sbin/unbound/services/rpz.c @@ -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) { diff --git a/usr.sbin/unbound/systemd.m4 b/usr.sbin/unbound/systemd.m4 index e5d4e27f6de..154f4ffabc3 100644 --- a/usr.sbin/unbound/systemd.m4 +++ b/usr.sbin/unbound/systemd.m4 @@ -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]) diff --git a/usr.sbin/unbound/testcode/fake_event.c b/usr.sbin/unbound/testcode/fake_event.c index 09269289dd4..a517fa5f373 100644 --- a/usr.sbin/unbound/testcode/fake_event.c +++ b/usr.sbin/unbound/testcode/fake_event.c @@ -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 ***********/ diff --git a/usr.sbin/unbound/testcode/mini_tdir.sh b/usr.sbin/unbound/testcode/mini_tdir.sh index 624ecdf7fe5..d1f7bfce94a 100755 --- a/usr.sbin/unbound/testcode/mini_tdir.sh +++ b/usr.sbin/unbound/testcode/mini_tdir.sh @@ -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 .. diff --git a/usr.sbin/unbound/testcode/petal.c b/usr.sbin/unbound/testcode/petal.c index 63d3d452ee3..6d825f1e0ca 100644 --- a/usr.sbin/unbound/testcode/petal.c +++ b/usr.sbin/unbound/testcode/petal.c @@ -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) { diff --git a/usr.sbin/unbound/testcode/readzone.c b/usr.sbin/unbound/testcode/readzone.c index 94511e5771f..f50eea31f2e 100644 --- a/usr.sbin/unbound/testcode/readzone.c +++ b/usr.sbin/unbound/testcode/readzone.c @@ -45,7 +45,6 @@ #include #include -#include #include "sldns/str2wire.h" #include "sldns/wire2str.h" diff --git a/usr.sbin/unbound/testcode/streamtcp.c b/usr.sbin/unbound/testcode/streamtcp.c index 037bcfd8b6a..60122c4ddfd 100644 --- a/usr.sbin/unbound/testcode/streamtcp.c +++ b/usr.sbin/unbound/testcode/streamtcp.c @@ -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); diff --git a/usr.sbin/unbound/testcode/testbound.c b/usr.sbin/unbound/testcode/testbound.c index f023860e086..123fe0d4e46 100644 --- a/usr.sbin/unbound/testcode/testbound.c +++ b/usr.sbin/unbound/testcode/testbound.c @@ -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) diff --git a/usr.sbin/unbound/testcode/unitmain.c b/usr.sbin/unbound/testcode/unitmain.c index 647cbca3b05..084c12b93b4 100644 --- a/usr.sbin/unbound/testcode/unitmain.c +++ b/usr.sbin/unbound/testcode/unitmain.c @@ -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; ilock); + 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 */ diff --git a/usr.sbin/unbound/testcode/unitzonemd.c b/usr.sbin/unbound/testcode/unitzonemd.c index 23c9f701064..bf130df5a25 100644 --- a/usr.sbin/unbound/testcode/unitzonemd.c +++ b/usr.sbin/unbound/testcode/unitzonemd.c @@ -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); diff --git a/usr.sbin/unbound/util/edns.c b/usr.sbin/unbound/util/edns.c index 2b4047f0b60..ee95a691220 100644 --- a/usr.sbin/unbound/util/edns.c +++ b/usr.sbin/unbound/util/edns.c @@ -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; icookie_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; +} diff --git a/usr.sbin/unbound/util/edns.h b/usr.sbin/unbound/util/edns.h index 5da0ecb290a..47ccb1ad2cd 100644 --- a/usr.sbin/unbound/util/edns.h +++ b/usr.sbin/unbound/util/edns.h @@ -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 diff --git a/usr.sbin/unbound/util/proxy_protocol.c b/usr.sbin/unbound/util/proxy_protocol.c index a1880497404..235538b6251 100644 --- a/usr.sbin/unbound/util/proxy_protocol.c +++ b/usr.sbin/unbound/util/proxy_protocol.c @@ -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; diff --git a/usr.sbin/unbound/util/siphash.c b/usr.sbin/unbound/util/siphash.c index 32797dff60e..a13657ccffe 100644 --- a/usr.sbin/unbound/util/siphash.c +++ b/usr.sbin/unbound/util/siphash.c @@ -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]);