xref: /openbsd-src/usr.sbin/nsd/dnstap/dnstap_collector.c (revision b71395ea3d4830c6fd338870b804059761b8292d)
1e02bc0dfSflorian /*
2e02bc0dfSflorian  * dnstap/dnstap_collector.c -- nsd collector process for dnstap information
3e02bc0dfSflorian  *
4e02bc0dfSflorian  * Copyright (c) 2018, NLnet Labs. All rights reserved.
5e02bc0dfSflorian  *
6e02bc0dfSflorian  * See LICENSE for the license.
7e02bc0dfSflorian  *
8e02bc0dfSflorian  */
9e02bc0dfSflorian 
10e02bc0dfSflorian #include "config.h"
11e02bc0dfSflorian #include <sys/types.h>
12e02bc0dfSflorian #include <sys/socket.h>
13e02bc0dfSflorian #include <errno.h>
14e02bc0dfSflorian #include <fcntl.h>
15e02bc0dfSflorian #include <unistd.h>
16e02bc0dfSflorian #ifndef USE_MINI_EVENT
17e02bc0dfSflorian #  ifdef HAVE_EVENT_H
18e02bc0dfSflorian #    include <event.h>
19e02bc0dfSflorian #  else
20e02bc0dfSflorian #    include <event2/event.h>
21e02bc0dfSflorian #    include "event2/event_struct.h"
22e02bc0dfSflorian #    include "event2/event_compat.h"
23e02bc0dfSflorian #  endif
24e02bc0dfSflorian #else
25e02bc0dfSflorian #  include "mini_event.h"
26e02bc0dfSflorian #endif
27e02bc0dfSflorian #include "dnstap/dnstap_collector.h"
28e02bc0dfSflorian #include "dnstap/dnstap.h"
29e02bc0dfSflorian #include "util.h"
30e02bc0dfSflorian #include "nsd.h"
31e02bc0dfSflorian #include "region-allocator.h"
32e02bc0dfSflorian #include "buffer.h"
33e02bc0dfSflorian #include "namedb.h"
34e02bc0dfSflorian #include "options.h"
353efee2e1Sflorian #include "remote.h"
36e02bc0dfSflorian 
37063644e9Sflorian #include "udb.h"
38063644e9Sflorian #include "rrl.h"
39063644e9Sflorian 
dt_collector_create(struct nsd * nsd)40e02bc0dfSflorian struct dt_collector* dt_collector_create(struct nsd* nsd)
41e02bc0dfSflorian {
42e02bc0dfSflorian 	int i, sv[2];
43e02bc0dfSflorian 	struct dt_collector* dt_col = (struct dt_collector*)xalloc_zero(
44e02bc0dfSflorian 		sizeof(*dt_col));
45063644e9Sflorian 	dt_col->count = nsd->child_count * 2;
46e02bc0dfSflorian 	dt_col->dt_env = NULL;
47e02bc0dfSflorian 	dt_col->region = region_create(xalloc, free);
48e02bc0dfSflorian 	dt_col->send_buffer = buffer_create(dt_col->region,
493aefc4aaSsthen 		/* msglen + is_response + addrlen + is_tcp + packetlen + packet + zonelen + zone + spare + local_addr + addr */
50e02bc0dfSflorian 		4+1+4+1+4+TCP_MAX_MESSAGE_LEN+4+MAXHOSTNAMELEN + 32 +
51e02bc0dfSflorian #ifdef INET6
523aefc4aaSsthen 		sizeof(struct sockaddr_storage) + sizeof(struct sockaddr_storage)
53e02bc0dfSflorian #else
543aefc4aaSsthen 		sizeof(struct sockaddr_in) + sizeof(struct sockaddr_in)
55e02bc0dfSflorian #endif
56e02bc0dfSflorian 		);
57e02bc0dfSflorian 
58063644e9Sflorian 	/* open communication channels in struct nsd */
59e02bc0dfSflorian 	nsd->dt_collector_fd_send = (int*)xalloc_array_zero(dt_col->count,
60e02bc0dfSflorian 		sizeof(int));
61e02bc0dfSflorian 	nsd->dt_collector_fd_recv = (int*)xalloc_array_zero(dt_col->count,
62e02bc0dfSflorian 		sizeof(int));
63e02bc0dfSflorian 	for(i=0; i<dt_col->count; i++) {
64063644e9Sflorian 		int sv[2];
65063644e9Sflorian 		int bufsz = buffer_capacity(dt_col->send_buffer);
66063644e9Sflorian 		sv[0] = -1; /* For receiving by parent (dnstap-collector) */
67063644e9Sflorian 		sv[1] = -1; /* For sending   by child  (server childs) */
68*b71395eaSflorian 		if(socketpair(AF_UNIX, SOCK_DGRAM
69*b71395eaSflorian #ifdef SOCK_NONBLOCK
70*b71395eaSflorian 			| SOCK_NONBLOCK
71*b71395eaSflorian #endif
72*b71395eaSflorian 			, 0, sv) < 0) {
73063644e9Sflorian 			error("dnstap_collector: cannot create communication channel: %s",
74e02bc0dfSflorian 				strerror(errno));
75e02bc0dfSflorian 		}
76*b71395eaSflorian #ifndef SOCK_NONBLOCK
77*b71395eaSflorian 		if (fcntl(sv[0], F_SETFL, O_NONBLOCK) == -1) {
78*b71395eaSflorian 			log_msg(LOG_ERR, "dnstap_collector receive fd fcntl "
79*b71395eaSflorian 				"failed: %s", strerror(errno));
80*b71395eaSflorian 		}
81*b71395eaSflorian 		if (fcntl(sv[1], F_SETFL, O_NONBLOCK) == -1) {
82*b71395eaSflorian 			log_msg(LOG_ERR, "dnstap_collector send fd fcntl "
83*b71395eaSflorian 				"failed: %s", strerror(errno));
84*b71395eaSflorian 		}
85*b71395eaSflorian #endif
86063644e9Sflorian 		if(setsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &bufsz, sizeof(bufsz))) {
87063644e9Sflorian 			log_msg(LOG_ERR, "setting dnstap_collector "
88063644e9Sflorian 				"receive buffer size failed: %s", strerror(errno));
89e02bc0dfSflorian 		}
90063644e9Sflorian 		if(setsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &bufsz, sizeof(bufsz))) {
91063644e9Sflorian 			log_msg(LOG_ERR, "setting dnstap_collector "
92063644e9Sflorian 				"send buffer size failed: %s", strerror(errno));
93e02bc0dfSflorian 		}
94063644e9Sflorian 		nsd->dt_collector_fd_recv[i] = sv[0];
95063644e9Sflorian 		nsd->dt_collector_fd_send[i] = sv[1];
96e02bc0dfSflorian 	}
97063644e9Sflorian 	nsd->dt_collector_fd_swap = nsd->dt_collector_fd_send + nsd->child_count;
98e02bc0dfSflorian 
99e02bc0dfSflorian 	/* open socketpair */
100e02bc0dfSflorian 	if(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
101e02bc0dfSflorian 		error("dnstap_collector: cannot create socketpair: %s",
102e02bc0dfSflorian 			strerror(errno));
103e02bc0dfSflorian 	}
104e02bc0dfSflorian 	if(fcntl(sv[0], F_SETFL, O_NONBLOCK) == -1) {
105e02bc0dfSflorian 		log_msg(LOG_ERR, "fcntl failed: %s", strerror(errno));
106e02bc0dfSflorian 	}
107e02bc0dfSflorian 	if(fcntl(sv[1], F_SETFL, O_NONBLOCK) == -1) {
108e02bc0dfSflorian 		log_msg(LOG_ERR, "fcntl failed: %s", strerror(errno));
109e02bc0dfSflorian 	}
110e02bc0dfSflorian 	dt_col->cmd_socket_dt = sv[0];
111e02bc0dfSflorian 	dt_col->cmd_socket_nsd = sv[1];
112e02bc0dfSflorian 
113e02bc0dfSflorian 	return dt_col;
114e02bc0dfSflorian }
115e02bc0dfSflorian 
dt_collector_destroy(struct dt_collector * dt_col,struct nsd * nsd)116e02bc0dfSflorian void dt_collector_destroy(struct dt_collector* dt_col, struct nsd* nsd)
117e02bc0dfSflorian {
118e02bc0dfSflorian 	if(!dt_col) return;
119e02bc0dfSflorian 	free(nsd->dt_collector_fd_recv);
120e02bc0dfSflorian 	nsd->dt_collector_fd_recv = NULL;
121063644e9Sflorian 	if (nsd->dt_collector_fd_send < nsd->dt_collector_fd_swap)
122e02bc0dfSflorian 		free(nsd->dt_collector_fd_send);
123063644e9Sflorian 	else
124063644e9Sflorian 		free(nsd->dt_collector_fd_swap);
125e02bc0dfSflorian 	nsd->dt_collector_fd_send = NULL;
126063644e9Sflorian 	nsd->dt_collector_fd_swap = NULL;
127e02bc0dfSflorian 	region_destroy(dt_col->region);
128e02bc0dfSflorian 	free(dt_col);
129e02bc0dfSflorian }
130e02bc0dfSflorian 
dt_collector_close(struct dt_collector * dt_col,struct nsd * nsd)131e02bc0dfSflorian void dt_collector_close(struct dt_collector* dt_col, struct nsd* nsd)
132e02bc0dfSflorian {
133063644e9Sflorian 	int i, *fd_send;
134e02bc0dfSflorian 	if(!dt_col) return;
135e02bc0dfSflorian 	if(dt_col->cmd_socket_dt != -1) {
136e02bc0dfSflorian 		close(dt_col->cmd_socket_dt);
137e02bc0dfSflorian 		dt_col->cmd_socket_dt = -1;
138e02bc0dfSflorian 	}
139e02bc0dfSflorian 	if(dt_col->cmd_socket_nsd != -1) {
140e02bc0dfSflorian 		close(dt_col->cmd_socket_nsd);
141e02bc0dfSflorian 		dt_col->cmd_socket_nsd = -1;
142e02bc0dfSflorian 	}
143063644e9Sflorian 	fd_send = nsd->dt_collector_fd_send < nsd->dt_collector_fd_swap
144063644e9Sflorian 	        ? nsd->dt_collector_fd_send : nsd->dt_collector_fd_swap;
145e02bc0dfSflorian 	for(i=0; i<dt_col->count; i++) {
146e02bc0dfSflorian 		if(nsd->dt_collector_fd_recv[i] != -1) {
147e02bc0dfSflorian 			close(nsd->dt_collector_fd_recv[i]);
148e02bc0dfSflorian 			nsd->dt_collector_fd_recv[i] = -1;
149e02bc0dfSflorian 		}
150063644e9Sflorian 		if(fd_send[i] != -1) {
151063644e9Sflorian 			close(fd_send[i]);
152063644e9Sflorian 			fd_send[i] = -1;
153e02bc0dfSflorian 		}
154e02bc0dfSflorian 	}
155e02bc0dfSflorian }
156e02bc0dfSflorian 
157e02bc0dfSflorian /* handle command from nsd to dt collector.
158e02bc0dfSflorian  * mostly, check for fd closed, this means we have to exit */
159e02bc0dfSflorian void
dt_handle_cmd_from_nsd(int ATTR_UNUSED (fd),short event,void * arg)160e02bc0dfSflorian dt_handle_cmd_from_nsd(int ATTR_UNUSED(fd), short event, void* arg)
161e02bc0dfSflorian {
162e02bc0dfSflorian 	struct dt_collector* dt_col = (struct dt_collector*)arg;
163e02bc0dfSflorian 	if((event&EV_READ) != 0) {
164e02bc0dfSflorian 		event_base_loopexit(dt_col->event_base, NULL);
165e02bc0dfSflorian 	}
166e02bc0dfSflorian }
167e02bc0dfSflorian 
168063644e9Sflorian /* receive data from fd into buffer, 1 when message received, -1 on error */
recv_into_buffer(int fd,struct buffer * buf)169063644e9Sflorian static int recv_into_buffer(int fd, struct buffer* buf)
170e02bc0dfSflorian {
171e02bc0dfSflorian 	size_t msglen;
172e02bc0dfSflorian 	ssize_t r;
173e02bc0dfSflorian 
174063644e9Sflorian 	assert(buffer_position(buf) == 0);
175063644e9Sflorian 	r = recv(fd, buffer_current(buf), buffer_capacity(buf), MSG_DONTWAIT);
176063644e9Sflorian 	if(r == -1) {
177063644e9Sflorian 		if(errno == EAGAIN || errno == EINTR || errno == EMSGSIZE) {
178063644e9Sflorian 			/* continue to receive a message later */
179063644e9Sflorian 			return 0;
180063644e9Sflorian 		}
181063644e9Sflorian 		log_msg(LOG_ERR, "dnstap collector: receive failed: %s",
182063644e9Sflorian 			strerror(errno));
183063644e9Sflorian 		return -1;
184063644e9Sflorian 	}
185063644e9Sflorian 	if(r == 0) {
186063644e9Sflorian 		/* Remote end closed the connection? */
187063644e9Sflorian 		log_msg(LOG_ERR, "dnstap collector: remote closed connection");
188063644e9Sflorian 		return -1;
189063644e9Sflorian 	}
190063644e9Sflorian 	assert(r > 4);
191e02bc0dfSflorian 	msglen = buffer_read_u32_at(buf, 0);
192063644e9Sflorian 	if(msglen != (size_t)(r - 4)) {
193063644e9Sflorian 		/* Is this still possible now the communication channel is of
194063644e9Sflorian 		 * type SOCK_DGRAM? I think not, but better safe than sorry. */
195063644e9Sflorian 		log_msg(LOG_ERR, "dnstap collector: out of sync (msglen: %u)",
196063644e9Sflorian 			(unsigned int) msglen);
197e02bc0dfSflorian 		return 0;
198e02bc0dfSflorian 	}
199e02bc0dfSflorian 	buffer_skip(buf, r);
200e02bc0dfSflorian 	buffer_flip(buf);
201e02bc0dfSflorian 	return 1;
202e02bc0dfSflorian }
203e02bc0dfSflorian 
204e02bc0dfSflorian /* submit the content of the buffer received to dnstap */
205e02bc0dfSflorian static void
dt_submit_content(struct dt_env * dt_env,struct buffer * buf)206e02bc0dfSflorian dt_submit_content(struct dt_env* dt_env, struct buffer* buf)
207e02bc0dfSflorian {
208e02bc0dfSflorian 	uint8_t is_response, is_tcp;
209e02bc0dfSflorian #ifdef INET6
2103aefc4aaSsthen 	struct sockaddr_storage local_addr, addr;
211e02bc0dfSflorian #else
2123aefc4aaSsthen 	struct sockaddr_in local_addr, addr;
213e02bc0dfSflorian #endif
214e02bc0dfSflorian 	socklen_t addrlen;
215e02bc0dfSflorian 	size_t pktlen;
216e02bc0dfSflorian 	uint8_t* data;
217e02bc0dfSflorian 	size_t zonelen;
218e02bc0dfSflorian 	uint8_t* zone;
219e02bc0dfSflorian 
220e02bc0dfSflorian 	/* parse content from buffer */
221e02bc0dfSflorian 	if(!buffer_available(buf, 4+1+4)) return;
222e02bc0dfSflorian 	buffer_skip(buf, 4); /* skip msglen */
223e02bc0dfSflorian 	is_response = buffer_read_u8(buf);
224e02bc0dfSflorian 	addrlen = buffer_read_u32(buf);
2253aefc4aaSsthen 	if(addrlen > sizeof(local_addr) || addrlen > sizeof(addr)) return;
2263aefc4aaSsthen 	if(!buffer_available(buf, 2*addrlen)) return;
2273aefc4aaSsthen 	buffer_read(buf, &local_addr, addrlen);
228e02bc0dfSflorian 	buffer_read(buf, &addr, addrlen);
229e02bc0dfSflorian 	if(!buffer_available(buf, 1+4)) return;
230e02bc0dfSflorian 	is_tcp = buffer_read_u8(buf);
231e02bc0dfSflorian 	pktlen = buffer_read_u32(buf);
232e02bc0dfSflorian 	if(!buffer_available(buf, pktlen)) return;
233e02bc0dfSflorian 	data = buffer_current(buf);
234e02bc0dfSflorian 	buffer_skip(buf, pktlen);
235e02bc0dfSflorian 	if(!buffer_available(buf, 4)) return;
236e02bc0dfSflorian 	zonelen = buffer_read_u32(buf);
237e02bc0dfSflorian 	if(zonelen == 0) {
238e02bc0dfSflorian 		zone = NULL;
239e02bc0dfSflorian 	} else {
240e02bc0dfSflorian 		if(zonelen > MAXDOMAINLEN) return;
241e02bc0dfSflorian 		if(!buffer_available(buf, zonelen)) return;
242e02bc0dfSflorian 		zone = buffer_current(buf);
243e02bc0dfSflorian 		buffer_skip(buf, zonelen);
244e02bc0dfSflorian 	}
245e02bc0dfSflorian 
246e02bc0dfSflorian 	/* submit it */
247e02bc0dfSflorian 	if(is_response) {
2483aefc4aaSsthen 		dt_msg_send_auth_response(dt_env, &local_addr, &addr, is_tcp, zone,
249e02bc0dfSflorian 			zonelen, data, pktlen);
250e02bc0dfSflorian 	} else {
2513aefc4aaSsthen 		dt_msg_send_auth_query(dt_env, &local_addr, &addr, is_tcp, zone,
252e02bc0dfSflorian 			zonelen, data, pktlen);
253e02bc0dfSflorian 	}
254e02bc0dfSflorian }
255e02bc0dfSflorian 
256e02bc0dfSflorian /* handle input from worker for dnstap */
257e02bc0dfSflorian void
dt_handle_input(int fd,short event,void * arg)258e02bc0dfSflorian dt_handle_input(int fd, short event, void* arg)
259e02bc0dfSflorian {
260e02bc0dfSflorian 	struct dt_collector_input* dt_input = (struct dt_collector_input*)arg;
261e02bc0dfSflorian 	if((event&EV_READ) != 0) {
262063644e9Sflorian 		/* receive */
263063644e9Sflorian 		int r = recv_into_buffer(fd, dt_input->buffer);
264063644e9Sflorian 		if(r == 0)
265e02bc0dfSflorian 			return;
266063644e9Sflorian 		else if(r < 0) {
267063644e9Sflorian 			event_base_loopexit(dt_input->dt_collector->event_base, NULL);
268063644e9Sflorian 			return;
269063644e9Sflorian 		}
270063644e9Sflorian 		/* once data is complete, send it to dnstap */
271e02bc0dfSflorian 		VERBOSITY(4, (LOG_INFO, "dnstap collector: received msg len %d",
272e02bc0dfSflorian 			(int)buffer_remaining(dt_input->buffer)));
273e02bc0dfSflorian 		if(dt_input->dt_collector->dt_env) {
274e02bc0dfSflorian 			dt_submit_content(dt_input->dt_collector->dt_env,
275e02bc0dfSflorian 				dt_input->buffer);
276e02bc0dfSflorian 		}
277e02bc0dfSflorian 
278e02bc0dfSflorian 		/* clear buffer for next message */
279e02bc0dfSflorian 		buffer_clear(dt_input->buffer);
280e02bc0dfSflorian 	}
281e02bc0dfSflorian }
282e02bc0dfSflorian 
283e02bc0dfSflorian /* init dnstap */
dt_init_dnstap(struct dt_collector * dt_col,struct nsd * nsd)284e02bc0dfSflorian static void dt_init_dnstap(struct dt_collector* dt_col, struct nsd* nsd)
285e02bc0dfSflorian {
286e02bc0dfSflorian 	int num_workers = 1;
287e02bc0dfSflorian #ifdef HAVE_CHROOT
288e02bc0dfSflorian 	if(nsd->chrootdir && nsd->chrootdir[0]) {
289e02bc0dfSflorian 		int l = strlen(nsd->chrootdir)-1; /* ends in trailing slash */
290e02bc0dfSflorian 		if (nsd->options->dnstap_socket_path &&
291e02bc0dfSflorian 			nsd->options->dnstap_socket_path[0] == '/' &&
292e02bc0dfSflorian 			strncmp(nsd->options->dnstap_socket_path,
293e02bc0dfSflorian 				nsd->chrootdir, l) == 0)
294e02bc0dfSflorian 			nsd->options->dnstap_socket_path += l;
295e02bc0dfSflorian 	}
296e02bc0dfSflorian #endif
2973efee2e1Sflorian 	dt_col->dt_env = dt_create(nsd->options->dnstap_socket_path,
2983efee2e1Sflorian 		nsd->options->dnstap_ip, num_workers, nsd->options->dnstap_tls,
2993efee2e1Sflorian 		nsd->options->dnstap_tls_server_name,
3003efee2e1Sflorian 		nsd->options->dnstap_tls_cert_bundle,
3013efee2e1Sflorian 		nsd->options->dnstap_tls_client_key_file,
3023efee2e1Sflorian 		nsd->options->dnstap_tls_client_cert_file);
303e02bc0dfSflorian 	if(!dt_col->dt_env) {
304e02bc0dfSflorian 		log_msg(LOG_ERR, "could not create dnstap env");
305e02bc0dfSflorian 		return;
306e02bc0dfSflorian 	}
307e02bc0dfSflorian 	dt_apply_cfg(dt_col->dt_env, nsd->options);
308e02bc0dfSflorian 	dt_init(dt_col->dt_env);
309e02bc0dfSflorian }
310e02bc0dfSflorian 
311e02bc0dfSflorian /* cleanup dt collector process for exit */
dt_collector_cleanup(struct dt_collector * dt_col,struct nsd * nsd)312e02bc0dfSflorian static void dt_collector_cleanup(struct dt_collector* dt_col, struct nsd* nsd)
313e02bc0dfSflorian {
314e02bc0dfSflorian 	int i;
315e02bc0dfSflorian 	dt_delete(dt_col->dt_env);
316e02bc0dfSflorian 	event_del(dt_col->cmd_event);
317e02bc0dfSflorian 	for(i=0; i<dt_col->count; i++) {
318e02bc0dfSflorian 		event_del(dt_col->inputs[i].event);
319e02bc0dfSflorian 	}
320e02bc0dfSflorian 	dt_collector_close(dt_col, nsd);
321e02bc0dfSflorian 	event_base_free(dt_col->event_base);
322e02bc0dfSflorian #ifdef MEMCLEAN
323e02bc0dfSflorian 	free(dt_col->cmd_event);
324e02bc0dfSflorian 	if(dt_col->inputs) {
325e02bc0dfSflorian 		for(i=0; i<dt_col->count; i++) {
326e02bc0dfSflorian 			free(dt_col->inputs[i].event);
327e02bc0dfSflorian 		}
328e02bc0dfSflorian 		free(dt_col->inputs);
329e02bc0dfSflorian 	}
330e02bc0dfSflorian 	dt_collector_destroy(dt_col, nsd);
3313efee2e1Sflorian 	daemon_remote_delete(nsd->rc); /* ssl-delete secret keys */
3323efee2e1Sflorian 	nsd_options_destroy(nsd->options);
3333efee2e1Sflorian 	region_destroy(nsd->region);
334e02bc0dfSflorian #endif
335e02bc0dfSflorian }
336e02bc0dfSflorian 
337e02bc0dfSflorian /* attach events to the event base to listen to the workers and cmd channel */
dt_attach_events(struct dt_collector * dt_col,struct nsd * nsd)338e02bc0dfSflorian static void dt_attach_events(struct dt_collector* dt_col, struct nsd* nsd)
339e02bc0dfSflorian {
340e02bc0dfSflorian 	int i;
341e02bc0dfSflorian 	/* create event base */
342e02bc0dfSflorian 	dt_col->event_base = nsd_child_event_base();
343e02bc0dfSflorian 	if(!dt_col->event_base) {
344e02bc0dfSflorian 		error("dnstap collector: event_base create failed");
345e02bc0dfSflorian 	}
346e02bc0dfSflorian 
347e02bc0dfSflorian 	/* add command handler */
348e02bc0dfSflorian 	dt_col->cmd_event = (struct event*)xalloc_zero(
349e02bc0dfSflorian 		sizeof(*dt_col->cmd_event));
350e02bc0dfSflorian 	event_set(dt_col->cmd_event, dt_col->cmd_socket_dt,
351e02bc0dfSflorian 		EV_PERSIST|EV_READ, dt_handle_cmd_from_nsd, dt_col);
352e02bc0dfSflorian 	if(event_base_set(dt_col->event_base, dt_col->cmd_event) != 0)
353e02bc0dfSflorian 		log_msg(LOG_ERR, "dnstap collector: event_base_set failed");
354e02bc0dfSflorian 	if(event_add(dt_col->cmd_event, NULL) != 0)
355e02bc0dfSflorian 		log_msg(LOG_ERR, "dnstap collector: event_add failed");
356e02bc0dfSflorian 
357e02bc0dfSflorian 	/* add worker input handlers */
358e02bc0dfSflorian 	dt_col->inputs = xalloc_array_zero(dt_col->count,
359e02bc0dfSflorian 		sizeof(*dt_col->inputs));
360e02bc0dfSflorian 	for(i=0; i<dt_col->count; i++) {
361e02bc0dfSflorian 		dt_col->inputs[i].dt_collector = dt_col;
362e02bc0dfSflorian 		dt_col->inputs[i].event = (struct event*)xalloc_zero(
363e02bc0dfSflorian 			sizeof(struct event));
364e02bc0dfSflorian 		event_set(dt_col->inputs[i].event,
365e02bc0dfSflorian 			nsd->dt_collector_fd_recv[i], EV_PERSIST|EV_READ,
366e02bc0dfSflorian 			dt_handle_input, &dt_col->inputs[i]);
367e02bc0dfSflorian 		if(event_base_set(dt_col->event_base,
368e02bc0dfSflorian 			dt_col->inputs[i].event) != 0)
369e02bc0dfSflorian 			log_msg(LOG_ERR, "dnstap collector: event_base_set failed");
370e02bc0dfSflorian 		if(event_add(dt_col->inputs[i].event, NULL) != 0)
371e02bc0dfSflorian 			log_msg(LOG_ERR, "dnstap collector: event_add failed");
372e02bc0dfSflorian 
373e02bc0dfSflorian 		dt_col->inputs[i].buffer = buffer_create(dt_col->region,
3743aefc4aaSsthen 			/* msglen + is_response + addrlen + is_tcp + packetlen + packet + zonelen + zone + spare + local_addr + addr */
375e02bc0dfSflorian 			4+1+4+1+4+TCP_MAX_MESSAGE_LEN+4+MAXHOSTNAMELEN + 32 +
376e02bc0dfSflorian #ifdef INET6
3773aefc4aaSsthen 			sizeof(struct sockaddr_storage) + sizeof(struct sockaddr_storage)
378e02bc0dfSflorian #else
3793aefc4aaSsthen 			sizeof(struct sockaddr_in) + sizeof(struct sockaddr_in)
380e02bc0dfSflorian #endif
381e02bc0dfSflorian 		);
382e02bc0dfSflorian 		assert(buffer_capacity(dt_col->inputs[i].buffer) ==
383e02bc0dfSflorian 			buffer_capacity(dt_col->send_buffer));
384e02bc0dfSflorian 	}
385e02bc0dfSflorian }
386e02bc0dfSflorian 
387e02bc0dfSflorian /* the dnstap collector process main routine */
dt_collector_run(struct dt_collector * dt_col,struct nsd * nsd)388e02bc0dfSflorian static void dt_collector_run(struct dt_collector* dt_col, struct nsd* nsd)
389e02bc0dfSflorian {
390e02bc0dfSflorian 	/* init dnstap */
391e02bc0dfSflorian 	VERBOSITY(1, (LOG_INFO, "dnstap collector started"));
392e02bc0dfSflorian 	dt_init_dnstap(dt_col, nsd);
393e02bc0dfSflorian 	dt_attach_events(dt_col, nsd);
394e02bc0dfSflorian 
395e02bc0dfSflorian 	/* run */
396e02bc0dfSflorian 	if(event_base_loop(dt_col->event_base, 0) == -1) {
397e02bc0dfSflorian 		error("dnstap collector: event_base_loop failed");
398e02bc0dfSflorian 	}
399e02bc0dfSflorian 
400e02bc0dfSflorian 	/* cleanup and done */
401e02bc0dfSflorian 	VERBOSITY(1, (LOG_INFO, "dnstap collector stopped"));
402e02bc0dfSflorian 	dt_collector_cleanup(dt_col, nsd);
403e02bc0dfSflorian 	exit(0);
404e02bc0dfSflorian }
405e02bc0dfSflorian 
dt_collector_start(struct dt_collector * dt_col,struct nsd * nsd)406e02bc0dfSflorian void dt_collector_start(struct dt_collector* dt_col, struct nsd* nsd)
407e02bc0dfSflorian {
408063644e9Sflorian 	int i, *fd_send;
409e02bc0dfSflorian 	/* fork */
410e02bc0dfSflorian 	dt_col->dt_pid = fork();
411e02bc0dfSflorian 	if(dt_col->dt_pid == -1) {
412e02bc0dfSflorian 		error("dnstap_collector: fork failed: %s", strerror(errno));
413e02bc0dfSflorian 	}
414e02bc0dfSflorian 	if(dt_col->dt_pid == 0) {
415e02bc0dfSflorian 		/* the dt collector process is this */
416e02bc0dfSflorian 		/* close the nsd side of the command channel */
417e02bc0dfSflorian 		close(dt_col->cmd_socket_nsd);
418e02bc0dfSflorian 		dt_col->cmd_socket_nsd = -1;
419063644e9Sflorian 
420063644e9Sflorian 		/* close the send side of the communication channels */
421063644e9Sflorian 		assert(nsd->dt_collector_fd_send < nsd->dt_collector_fd_swap);
422063644e9Sflorian 		fd_send = nsd->dt_collector_fd_send < nsd->dt_collector_fd_swap
423063644e9Sflorian 			? nsd->dt_collector_fd_send : nsd->dt_collector_fd_swap;
424063644e9Sflorian 		for(i=0; i<dt_col->count; i++) {
425063644e9Sflorian 			if(fd_send[i] != -1) {
426063644e9Sflorian 				close(fd_send[i]);
427063644e9Sflorian 				fd_send[i] = -1;
428063644e9Sflorian 			}
429063644e9Sflorian 		}
430063644e9Sflorian #ifdef HAVE_SETPROCTITLE
431063644e9Sflorian 		setproctitle("dnstap_collector");
432063644e9Sflorian #endif
433063644e9Sflorian 		/* Free serve process specific memory pages */
434063644e9Sflorian #ifdef RATELIMIT
435063644e9Sflorian 		rrl_mmap_deinit_keep_mmap();
436063644e9Sflorian #endif
437063644e9Sflorian 		udb_base_free_keep_mmap(nsd->task[0]);
438063644e9Sflorian 		udb_base_free_keep_mmap(nsd->task[1]);
439063644e9Sflorian 		namedb_close(nsd->db);
440063644e9Sflorian 
441e02bc0dfSflorian 		dt_collector_run(dt_col, nsd);
442e02bc0dfSflorian 		/* NOTREACH */
443e02bc0dfSflorian 		exit(0);
444e02bc0dfSflorian 	} else {
445e02bc0dfSflorian 		/* the parent continues on, with starting NSD */
446e02bc0dfSflorian 		/* close the dt side of the command channel */
447e02bc0dfSflorian 		close(dt_col->cmd_socket_dt);
448e02bc0dfSflorian 		dt_col->cmd_socket_dt = -1;
449063644e9Sflorian 
450063644e9Sflorian 		/* close the receive side of the communication channels */
451063644e9Sflorian 		for(i=0; i<dt_col->count; i++) {
452063644e9Sflorian 			if(nsd->dt_collector_fd_recv[i] != -1) {
453063644e9Sflorian 				close(nsd->dt_collector_fd_recv[i]);
454063644e9Sflorian 				nsd->dt_collector_fd_recv[i] = -1;
455063644e9Sflorian 			}
456063644e9Sflorian 		}
457e02bc0dfSflorian 	}
458e02bc0dfSflorian }
459e02bc0dfSflorian 
460e02bc0dfSflorian /* put data for sending to the collector process into the buffer */
461e02bc0dfSflorian static int
prep_send_data(struct buffer * buf,uint8_t is_response,struct sockaddr_storage * local_addr,struct sockaddr_storage * addr,socklen_t addrlen,int is_tcp,struct buffer * packet,struct zone * zone)462e02bc0dfSflorian prep_send_data(struct buffer* buf, uint8_t is_response,
463e02bc0dfSflorian #ifdef INET6
4643aefc4aaSsthen 	struct sockaddr_storage* local_addr,
465e02bc0dfSflorian 	struct sockaddr_storage* addr,
466e02bc0dfSflorian #else
4673aefc4aaSsthen 	struct sockaddr_in* local_addr,
468e02bc0dfSflorian 	struct sockaddr_in* addr,
469e02bc0dfSflorian #endif
470e02bc0dfSflorian 	socklen_t addrlen, int is_tcp, struct buffer* packet,
471e02bc0dfSflorian 	struct zone* zone)
472e02bc0dfSflorian {
473e02bc0dfSflorian 	buffer_clear(buf);
4743aefc4aaSsthen #ifdef INET6
4753aefc4aaSsthen 	if(local_addr->ss_family != addr->ss_family)
4763aefc4aaSsthen 		return 0; /* must be same length to send */
4773aefc4aaSsthen #else
4783aefc4aaSsthen 	if(local_addr->sin_family != addr->sin_family)
4793aefc4aaSsthen 		return 0; /* must be same length to send */
4803aefc4aaSsthen #endif
4813aefc4aaSsthen 	if(!buffer_available(buf, 4+1+4+2*addrlen+1+4+buffer_remaining(packet)))
482e02bc0dfSflorian 		return 0; /* does not fit in send_buffer, log is dropped */
483e02bc0dfSflorian 	buffer_skip(buf, 4); /* the length of the message goes here */
484e02bc0dfSflorian 	buffer_write_u8(buf, is_response);
485e02bc0dfSflorian 	buffer_write_u32(buf, addrlen);
4863aefc4aaSsthen 	buffer_write(buf, local_addr, (size_t)addrlen);
487e02bc0dfSflorian 	buffer_write(buf, addr, (size_t)addrlen);
488e02bc0dfSflorian 	buffer_write_u8(buf, (is_tcp?1:0));
489e02bc0dfSflorian 	buffer_write_u32(buf, buffer_remaining(packet));
490e02bc0dfSflorian 	buffer_write(buf, buffer_begin(packet), buffer_remaining(packet));
491e02bc0dfSflorian 	if(zone && zone->apex && domain_dname(zone->apex)) {
492e02bc0dfSflorian 		if(!buffer_available(buf, 4 + domain_dname(zone->apex)->name_size))
493e02bc0dfSflorian 			return 0;
494e02bc0dfSflorian 		buffer_write_u32(buf, domain_dname(zone->apex)->name_size);
495e02bc0dfSflorian 		buffer_write(buf, dname_name(domain_dname(zone->apex)),
496e02bc0dfSflorian 			domain_dname(zone->apex)->name_size);
497e02bc0dfSflorian 	} else {
498e02bc0dfSflorian 		if(!buffer_available(buf, 4))
499e02bc0dfSflorian 			return 0;
500e02bc0dfSflorian 		buffer_write_u32(buf, 0);
501e02bc0dfSflorian 	}
502e02bc0dfSflorian 
503e02bc0dfSflorian 	buffer_flip(buf);
504e02bc0dfSflorian 	/* write length of message */
505e02bc0dfSflorian 	buffer_write_u32_at(buf, 0, buffer_remaining(buf)-4);
506e02bc0dfSflorian 	return 1;
507e02bc0dfSflorian }
508e02bc0dfSflorian 
509063644e9Sflorian /* attempt to send buffer to socket, if it blocks do not send it.
510063644e9Sflorian  * return 0 on success, -1 on error */
attempt_to_send(int s,uint8_t * data,size_t len)511063644e9Sflorian static int attempt_to_send(int s, uint8_t* data, size_t len)
512e02bc0dfSflorian {
513e02bc0dfSflorian 	ssize_t r;
514063644e9Sflorian 	if(len == 0)
515063644e9Sflorian 		return 0;
516063644e9Sflorian 	r = send(s, data, len, MSG_DONTWAIT | MSG_NOSIGNAL);
517e02bc0dfSflorian 	if(r == -1) {
518063644e9Sflorian 		if(errno == EAGAIN || errno == EINTR ||
519063644e9Sflorian 				errno == ENOBUFS || errno == EMSGSIZE) {
520063644e9Sflorian 			/* check if pipe is full, if the nonblocking fd blocks,
521063644e9Sflorian 			 * then drop the message */
522063644e9Sflorian 			return 0;
523e02bc0dfSflorian 		}
524063644e9Sflorian 		/* some sort of error, print it */
525063644e9Sflorian 		log_msg(LOG_ERR, "dnstap collector: send failed: %s",
526e02bc0dfSflorian 			strerror(errno));
527063644e9Sflorian 		return -1;
528e02bc0dfSflorian 	}
529063644e9Sflorian 	assert(r > 0);
530063644e9Sflorian 	if(r > 0) {
531063644e9Sflorian 		assert((size_t)r == len);
532063644e9Sflorian 		return 0;
533e02bc0dfSflorian 	}
534063644e9Sflorian 	/* Other end closed the channel? */
535063644e9Sflorian 	log_msg(LOG_ERR, "dnstap collector: server child closed the channel");
536063644e9Sflorian 	return -1;
537e02bc0dfSflorian }
538e02bc0dfSflorian 
dt_collector_submit_auth_query(struct nsd * nsd,struct sockaddr_storage * local_addr,struct sockaddr_storage * addr,socklen_t addrlen,int is_tcp,struct buffer * packet)539e02bc0dfSflorian void dt_collector_submit_auth_query(struct nsd* nsd,
540e02bc0dfSflorian #ifdef INET6
5413aefc4aaSsthen 	struct sockaddr_storage* local_addr,
542e02bc0dfSflorian 	struct sockaddr_storage* addr,
543e02bc0dfSflorian #else
5443aefc4aaSsthen 	struct sockaddr_in* local_addr,
545e02bc0dfSflorian 	struct sockaddr_in* addr,
546e02bc0dfSflorian #endif
547e02bc0dfSflorian 	socklen_t addrlen, int is_tcp, struct buffer* packet)
548e02bc0dfSflorian {
549e02bc0dfSflorian 	if(!nsd->dt_collector) return;
550e02bc0dfSflorian 	if(!nsd->options->dnstap_log_auth_query_messages) return;
551063644e9Sflorian 	if(nsd->dt_collector_fd_send[nsd->this_child->child_num] == -1) return;
552e02bc0dfSflorian 	VERBOSITY(4, (LOG_INFO, "dnstap submit auth query"));
553e02bc0dfSflorian 
554e02bc0dfSflorian 	/* marshal data into send buffer */
5553aefc4aaSsthen 	if(!prep_send_data(nsd->dt_collector->send_buffer, 0, local_addr, addr, addrlen,
556e02bc0dfSflorian 		is_tcp, packet, NULL))
557e02bc0dfSflorian 		return; /* probably did not fit in buffer */
558e02bc0dfSflorian 
559e02bc0dfSflorian 	/* attempt to send data; do not block */
560063644e9Sflorian 	if(attempt_to_send(nsd->dt_collector_fd_send[nsd->this_child->child_num],
561e02bc0dfSflorian 			buffer_begin(nsd->dt_collector->send_buffer),
562063644e9Sflorian 			buffer_remaining(nsd->dt_collector->send_buffer))) {
563063644e9Sflorian 		/* Something went wrong sending to the socket. Don't send to
564063644e9Sflorian 		 * this socket again. */
565063644e9Sflorian 		close(nsd->dt_collector_fd_send[nsd->this_child->child_num]);
566063644e9Sflorian 		nsd->dt_collector_fd_send[nsd->this_child->child_num] = -1;
567063644e9Sflorian 	}
568e02bc0dfSflorian }
569e02bc0dfSflorian 
dt_collector_submit_auth_response(struct nsd * nsd,struct sockaddr_storage * local_addr,struct sockaddr_storage * addr,socklen_t addrlen,int is_tcp,struct buffer * packet,struct zone * zone)570e02bc0dfSflorian void dt_collector_submit_auth_response(struct nsd* nsd,
571e02bc0dfSflorian #ifdef INET6
5723aefc4aaSsthen 	struct sockaddr_storage* local_addr,
573e02bc0dfSflorian 	struct sockaddr_storage* addr,
574e02bc0dfSflorian #else
5753aefc4aaSsthen 	struct sockaddr_in* local_addr,
576e02bc0dfSflorian 	struct sockaddr_in* addr,
577e02bc0dfSflorian #endif
578e02bc0dfSflorian 	socklen_t addrlen, int is_tcp, struct buffer* packet,
579e02bc0dfSflorian 	struct zone* zone)
580e02bc0dfSflorian {
581e02bc0dfSflorian 	if(!nsd->dt_collector) return;
582e02bc0dfSflorian 	if(!nsd->options->dnstap_log_auth_response_messages) return;
583063644e9Sflorian 	if(nsd->dt_collector_fd_send[nsd->this_child->child_num] == -1) return;
584e02bc0dfSflorian 	VERBOSITY(4, (LOG_INFO, "dnstap submit auth response"));
585e02bc0dfSflorian 
586e02bc0dfSflorian 	/* marshal data into send buffer */
5873aefc4aaSsthen 	if(!prep_send_data(nsd->dt_collector->send_buffer, 1, local_addr, addr, addrlen,
588e02bc0dfSflorian 		is_tcp, packet, zone))
589e02bc0dfSflorian 		return; /* probably did not fit in buffer */
590e02bc0dfSflorian 
591e02bc0dfSflorian 	/* attempt to send data; do not block */
592063644e9Sflorian 	if(attempt_to_send(nsd->dt_collector_fd_send[nsd->this_child->child_num],
593e02bc0dfSflorian 			buffer_begin(nsd->dt_collector->send_buffer),
594063644e9Sflorian 			buffer_remaining(nsd->dt_collector->send_buffer))) {
595063644e9Sflorian 		/* Something went wrong sending to the socket. Don't send to
596063644e9Sflorian 		 * this socket again. */
597063644e9Sflorian 		close(nsd->dt_collector_fd_send[nsd->this_child->child_num]);
598063644e9Sflorian 		nsd->dt_collector_fd_send[nsd->this_child->child_num] = -1;
599063644e9Sflorian 	}
600e02bc0dfSflorian }
601