From 11e80ce3abdf4f1f55150173a1ce5c0c00d6b66b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 31 Jan 2020 09:53:49 +0100 Subject: [PATCH] dnstap unbound-dnstap-sock, can listen to multiple sockets, can listen to TCP sockets, cleans up on exit after signal. --- dnstap/unbound-dnstap-socket.c | 376 +++++++++++++++++++++++++++++++-- 1 file changed, 353 insertions(+), 23 deletions(-) diff --git a/dnstap/unbound-dnstap-socket.c b/dnstap/unbound-dnstap-socket.c index 8afb1c1fc..218d46d3c 100644 --- a/dnstap/unbound-dnstap-socket.c +++ b/dnstap/unbound-dnstap-socket.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #ifdef HAVE_SYS_UN_H #include @@ -62,8 +63,10 @@ #include "sldns/wire2str.h" #include #include "dnstap/dnstap.pb-c.h" +#include "util/config_file.h" #define DNSTAP_CONTENT_TYPE "protobuf:dnstap.Dnstap" +#define LISTEN_BACKLOG 16 /** usage information for streamtcp */ static void usage(char* argv[]) @@ -72,6 +75,7 @@ static void usage(char* argv[]) 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 on the IP and port\n"); printf("-l long format for DNS printout\n"); printf("-v more verbose log output\n"); printf("-h this help text\n"); @@ -81,12 +85,14 @@ static void usage(char* argv[]) /** long format option, for multiline printout per message */ static int longformat = 0; +struct tap_socket_list; +struct tap_socket; /** main tap callback data */ struct main_tap_data { /** the event base (to loopexit) */ struct ub_event_base* base; - /** the event (that is accept()ed) */ - struct ub_event* ev; + /** the list of accept sockets */ + struct tap_socket_list* acceptlist; }; /** tap callback variables */ @@ -109,6 +115,265 @@ struct tap_data { size_t len; }; +/** list of sockets */ +struct tap_socket_list { + /** next in list */ + struct tap_socket_list* next; + /** the socket */ + struct tap_socket* s; +}; + +/** tap socket */ +struct tap_socket { + /** fd of socket */ + int fd; + /** the event for it */ + struct ub_event *ev; + /** has the event been added */ + int ev_added; + /** the callback, for the event, ev_cb(fd, bits, arg) */ + void (*ev_cb)(int, short, void*); + /** data element, (arg for the tap_socket struct) */ + void* data; + /** socketpath, if this is an AF_LOCAL socket */ + char* socketpath; + /** IP, if this is a TCP socket */ + char* ip; +}; + +/** del the tap event */ +static void tap_socket_delev(struct tap_socket* s) +{ + if(!s) return; + if(!s->ev) return; + if(!s->ev_added) return; + ub_event_del(s->ev); + s->ev_added = 0; +} + +/** close the tap socket */ +static void tap_socket_close(struct tap_socket* s) +{ + if(!s) return; + if(s->fd == -1) return; + close(s->fd); + s->fd = -1; +} + +/** delete tap socket */ +static void tap_socket_delete(struct tap_socket* s) +{ + if(!s) return; + ub_event_free(s->ev); + free(s->socketpath); + free(s->ip); + free(s); +} + +/** create new socket (unconnected, not base-added), or NULL malloc fail */ +static struct tap_socket* tap_socket_new_local(char* socketpath, + void (*ev_cb)(int, short, void*), void* data) +{ + struct tap_socket* s = calloc(1, sizeof(*s)); + if(!s) { + log_err("malloc failure"); + return NULL; + } + s->socketpath = strdup(socketpath); + if(!s->socketpath) { + free(s); + log_err("malloc failure"); + return NULL; + } + s->fd = -1; + s->ev_cb = ev_cb; + s->data = data; + return s; +} + +/** create new socket (unconnected, not base-added), or NULL malloc fail */ +static struct tap_socket* tap_socket_new_tcpaccept(char* ip, + void (*ev_cb)(int, short, void*), void* data) +{ + struct tap_socket* s = calloc(1, sizeof(*s)); + if(!s) { + log_err("malloc failure"); + return NULL; + } + s->ip = strdup(ip); + if(!s->ip) { + free(s); + log_err("malloc failure"); + return NULL; + } + s->fd = -1; + s->ev_cb = ev_cb; + s->data = data; + return s; +} + +/** setup tcp accept socket on IP string */ +static int make_tcp_accept(char* ip) +{ +#ifdef SO_REUSEADDR + int on = 1; +#endif + struct sockaddr_storage addr; + socklen_t len; + int s; + + memset(&addr, 0, sizeof(addr)); + len = (socklen_t)sizeof(addr); + if(!extstrtoaddr(ip, &addr, &len)) { + log_err("could not parse IP '%s'", ip); + return -1; + } + + if((s = socket(addr.ss_family, SOCK_STREAM, 0)) == -1) { +#ifndef USE_WINSOCK + log_err("can't create socket: %s", strerror(errno)); +#else + log_err("can't create socket: %s", + wsa_strerror(WSAGetLastError())); +#endif + return -1; + } +#ifdef SO_REUSEADDR + if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on, + (socklen_t)sizeof(on)) < 0) { +#ifndef USE_WINSOCK + log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s", + strerror(errno)); + close(s); +#else + log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s", + wsa_strerror(WSAGetLastError())); + closesocket(s); +#endif + return -1; + } +#endif /* SO_REUSEADDR */ + if(bind(s, (struct sockaddr*)&addr, len) != 0) { +#ifndef USE_WINSOCK + log_err_addr("can't bind socket", strerror(errno), + &addr, len); + close(s); +#else + log_err_addr("can't bind socket", + wsa_strerror(WSAGetLastError()), &addr, len); + closesocket(s); +#endif + return -1; + } + if(!fd_set_nonblock(s)) { +#ifndef USE_WINSOCK + close(s); +#else + closesocket(s); +#endif + return -1; + } + if(listen(s, LISTEN_BACKLOG) == -1) { +#ifndef USE_WINSOCK + log_err("can't listen: %s", strerror(errno)); + close(s); +#else + log_err("can't listen: %s", wsa_strerror(WSAGetLastError())); + closesocket(s); +#endif + return -1; + } + return s; +} + +/** setup socket on event base */ +static int tap_socket_setup(struct tap_socket* s, struct ub_event_base* base) +{ + if(s->socketpath) { + /* AF_LOCAL accept socket */ + s->fd = create_local_accept_sock(s->socketpath, NULL, 0); + if(s->fd == -1) { + log_err("could not create local socket"); + return 0; + } + s->ev = ub_event_new(base, s->fd, UB_EV_READ | UB_EV_PERSIST, + s->ev_cb, s); + if(!s->ev) { + log_err("could not ub_event_new"); + return 0; + } + if(ub_event_add(s->ev, NULL) != 0) { + log_err("could not ub_event_add"); + return 0; + } + s->ev_added = 1; + return 1; + } + if(s->ip) { + /* TCP accept socket */ + s->fd = make_tcp_accept(s->ip); + if(s->fd == -1) { + log_err("could not create tcp socket"); + return 0; + } + s->ev = ub_event_new(base, s->fd, UB_EV_READ | UB_EV_PERSIST, + s->ev_cb, s); + if(!s->ev) { + log_err("could not ub_event_new"); + return 0; + } + if(ub_event_add(s->ev, NULL) != 0) { + log_err("could not ub_event_add"); + return 0; + } + s->ev_added = 1; + return 1; + } + return 0; +} + +/** add tap socket to list */ +static int tap_socket_list_insert(struct tap_socket_list** liststart, + struct tap_socket* s) +{ + struct tap_socket_list* entry = (struct tap_socket_list*) + malloc(sizeof(*entry)); + if(!entry) + return 0; + entry->next = *liststart; + entry->s = s; + *liststart = entry; + return 1; +} + +/** delete the list */ +static void tap_socket_list_delete(struct tap_socket_list* list) +{ + struct tap_socket_list* e = list, *next; + while(e) { + next = e->next; + tap_socket_delev(e->s); + tap_socket_close(e->s); + tap_socket_delete(e->s); + free(e); + e = next; + } +} + +/** setup accept events */ +static int tap_socket_list_addevs(struct tap_socket_list* list, + struct ub_event_base* base) +{ + struct tap_socket_list* entry; + for(entry = list; entry; entry = entry->next) { + if(!tap_socket_setup(entry->s, base)) { + log_err("could not setup socket"); + return 0; + } + } + return 1; +} + /** log control frame contents */ static void log_control_frame(uint8_t* pkt, size_t len) { @@ -588,7 +853,9 @@ static void tap_callback(int fd, short ATTR_UNUSED(bits), void* arg) /** callback for main listening file descriptor */ void mainfdcallback(int fd, short ATTR_UNUSED(bits), void* arg) { - struct main_tap_data* maindata = (struct main_tap_data*)arg; + struct tap_socket* tap_sock = (struct tap_socket*)arg; + struct main_tap_data* maindata = (struct main_tap_data*) + tap_sock->data; struct tap_data* data; struct sockaddr_storage addr; socklen_t addrlen = (socklen_t)sizeof(addr); @@ -653,24 +920,59 @@ void mainfdcallback(int fd, short ATTR_UNUSED(bits), void* arg) if(ub_event_add(data->ev, NULL) != 0) fatal_exit("could not ub_event_add"); } -/** create file descriptor to listen on */ -static int -setup_fd(char* socketpath) +/** setup local accept sockets */ +static void setup_local_list(struct main_tap_data* maindata, + struct config_strlist_head* local_list) +{ + struct config_strlist* item; + for(item = local_list->first; item; item = item->next) { + struct tap_socket* s; + s = tap_socket_new_local(item->str, &mainfdcallback, + maindata); + if(!s) fatal_exit("out of memory"); + if(!tap_socket_list_insert(&maindata->acceptlist, s)) + fatal_exit("out of memory"); + } +} + +/** setup tcp accept sockets */ +static void setup_tcp_list(struct main_tap_data* maindata, + struct config_strlist_head* tcp_list) +{ + struct config_strlist* item; + for(item = tcp_list->first; item; item = item->next) { + struct tap_socket* s; + s = tap_socket_new_tcpaccept(item->str, &mainfdcallback, + maindata); + if(!s) fatal_exit("out of memory"); + if(!tap_socket_list_insert(&maindata->acceptlist, s)) + fatal_exit("out of memory"); + } +} + +/** signal variable */ +static struct ub_event_base* sig_base = NULL; +/** do we have to quit */ +int sig_quit = 0; +/** signal handler for user quit */ +static RETSIGTYPE main_sigh(int sig) { - return create_local_accept_sock(socketpath, NULL, 0); + if(!sig_base) return; + verbose(VERB_ALGO, "exit on signal %d\n", sig); + ub_event_base_loopexit(sig_base); + sig_quit = 1; } /** setup and run the server to listen to DNSTAP messages */ static void -setup_and_run(char* socketpath) +setup_and_run(struct config_strlist_head* local_list, + struct config_strlist_head* tcp_list) { - int fd; time_t secs = 0; struct timeval now; struct main_tap_data* maindata; struct ub_event_base* base; const char *evnm="event", *evsys="", *evmethod=""; - struct ub_event *ev; maindata = calloc(1, sizeof(*maindata)); if(!maindata) fatal_exit("out of memory"); @@ -678,21 +980,24 @@ setup_and_run(char* socketpath) base = ub_default_event_base(1, &secs, &now); if(!base) fatal_exit("could not create ub_event base"); maindata->base = base; - fd = setup_fd(socketpath); + sig_base = base; + if(sig_quit) { + ub_event_base_free(base); + free(maindata); + return; + } ub_get_event_sys(base, &evnm, &evsys, &evmethod); if(verbosity) log_info("%s %s uses %s method", evnm, evsys, evmethod); - ev = ub_event_new(base, fd, UB_EV_READ | UB_EV_PERSIST, - &mainfdcallback, maindata); - if(!ev) fatal_exit("could not ub_event_new"); - if(ub_event_add(ev, NULL) != 0) fatal_exit("could not ub_event_add"); - maindata->ev = ev; + + setup_local_list(maindata, local_list); + setup_tcp_list(maindata, tcp_list); + if(!tap_socket_list_addevs(maindata->acceptlist, base)) + fatal_exit("could not setup accept events"); ub_event_base_dispatch(base); - ub_event_del(ev); - ub_event_free(ev); + tap_socket_list_delete(maindata->acceptlist); ub_event_base_free(base); - close(fd); free(maindata); } @@ -706,7 +1011,8 @@ int main(int argc, char** argv) { int c; int usessl = 0; - char* socketpath = NULL; + struct config_strlist_head local_list; + struct config_strlist_head tcp_list; #ifdef USE_WINSOCK WSADATA wsa_data; if(WSAStartup(MAKEWORD(2,2), &wsa_data) != 0) { @@ -714,6 +1020,20 @@ int main(int argc, char** argv) return 1; } #endif + if(signal(SIGINT, main_sigh) == SIG_ERR || +#ifdef SIGQUIT + signal(SIGQUIT, main_sigh) == SIG_ERR || +#endif +#ifdef SIGHUP + signal(SIGHUP, main_sigh) == SIG_ERR || +#endif +#ifdef SIGBREAK + signal(SIGBREAK, main_sigh) == SIG_ERR || +#endif + signal(SIGTERM, main_sigh) == SIG_ERR) + fatal_exit("could not bind to signal"); + memset(&local_list, 0, sizeof(local_list)); + memset(&tcp_list, 0, sizeof(tcp_list)); /* lock debug start (if any) */ log_ident_set("unbound-dnstap-socket"); @@ -728,10 +1048,17 @@ int main(int argc, char** argv) #endif /* command line options */ - while( (c=getopt(argc, argv, "hlu:v")) != -1) { + while( (c=getopt(argc, argv, "hls:u:v")) != -1) { switch(c) { case 'u': - socketpath = optarg; + if(!cfg_strlist_append(&local_list, + strdup(optarg))) + fatal_exit("out of memory"); + break; + case 's': + if(!cfg_strlist_append(&tcp_list, + strdup(optarg))) + fatal_exit("out of memory"); break; case 'l': longformat = 1; @@ -767,7 +1094,10 @@ int main(int argc, char** argv) (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); #endif } - setup_and_run(socketpath); + setup_and_run(&local_list, &tcp_list); + config_delstrlist(local_list.first); + config_delstrlist(tcp_list.first); + checklock_stop(); #ifdef USE_WINSOCK WSACleanup(); -- 2.47.3