#ifdef USE_DNSTAP
+#include "util/locks.h"
struct config_file;
struct sldns_buffer;
struct dt_msg_queue;
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;
};
/**
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], [],
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);
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);
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);
}
/* 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 */
/* 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
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",
#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);
#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;
/* 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 */
}
}
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);
}
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 */
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 */
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)
{
{
if(!s) return;
if(s->fd == -1) return;
- close(s->fd);
+ sock_close(s->fd);
s->fd = -1;
}
#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);
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);
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)
free(acceptframe);
return 0;
}
+#endif
} else {
if(send(data->fd, acceptframe, len, 0) == -1) {
log_err("send failed: %s", sock_strerror(errno));
fd_set_block(data->fd);
if(data->ssl) {
+#ifdef HAVE_SSL
int r;
if((r=SSL_write(data->ssl, finishframe, len)) <= 0) {
int r2;
free(finishframe);
return 0;
}
+#endif
} else {
if(send(data->fd, finishframe, len, 0) == -1) {
log_err("send failed: %s", sock_strerror(errno));
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);
#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);
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 */
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();
verbose(VERB_OPS, "ssl handshake failed "
"from %s", data->id);
}
- tap_data_free(data);
+ tap_data_free(data, 1);
return 0;
}
}
data->ssl_handshake_done = 1;
if(!tap_check_peer(data)) {
/* closed */
- tap_data_free(data);
+ tap_data_free(data, 1);
return 0;
}
return 1;
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 */
data->frame = calloc(1, data->len);
if(!data->frame) {
log_err("out of memory");
- tap_data_free(data);
+ tap_data_free(data, 1);
return;
}
}
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 */
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;
}
}
data->len = 0;
data->len_done = 0;
data->data_done = 0;
-
}
/** callback for main listening file descriptor */
&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 */
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. */
#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,
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:
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();
*/
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)
#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
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];
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)
{
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;
}
}
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;
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);
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;
}
}
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;
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);
}
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;
}
}
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));
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;
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) {
*/
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) {
#endif
struct ipset_env {
- void* mnl;
+ void* dev;
int v4_enabled;
int v6_enabled;
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 */
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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
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,
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) {
# 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])
])
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])
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;
{
}
+void http2_stream_remove_mesh_state(struct http2_stream* ATTR_UNUSED(h2_stream))
+{
+}
+
/*********** End of Dummy routines ***********/
result=result.$name
done=.done-$name
skip=.skip-$name
+asan_text="SUMMARY: AddressSanitizer"
success="no"
if test -x "`which bash`"; then
shell="bash"
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 ..
#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) {
#include <string.h>
#include <unistd.h>
-#include <stdint.h>
#include "sldns/str2wire.h"
#include "sldns/wire2str.h"
}
}
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);
/** 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)
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,
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);
tcpreuse_test();
msgparse_test();
edns_ede_answer_encode_test();
+ localzone_test();
#ifdef CLIENT_SUBNET
ecs_test();
#endif /* CLIENT_SUBNET */
struct auth_zone* z;
/* setup test harness */
- memset(&mods, 0, sizeof(mods));
memset(&env, 0, sizeof(env));
env.scratch = regional_create();
if(!env.scratch)
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");
/* 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);
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;
+}
#define UTIL_EDNS_H
#include "util/storage/dnstree.h"
+#include "util/locks.h"
struct edns_data;
struct config_file;
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,
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
break;
#endif /* INET6 */
case AF_UNIX:
+ ATTR_FALLTHROUGH
/* fallthrough */
default:
return 0;
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]);