xref: /openbsd/usr.sbin/nsd/netio.c (revision ee5153b7)
162ac0c33Sjakob /*
262ac0c33Sjakob  * netio.c -- network I/O support.
362ac0c33Sjakob  *
4d3fecca9Ssthen  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
562ac0c33Sjakob  *
662ac0c33Sjakob  * See LICENSE for the license.
762ac0c33Sjakob  *
862ac0c33Sjakob  */
9aee1b7aaSsthen #include "config.h"
1062ac0c33Sjakob 
1162ac0c33Sjakob #include <assert.h>
1262ac0c33Sjakob #include <errno.h>
1362ac0c33Sjakob #include <sys/time.h>
1462ac0c33Sjakob #include <string.h>
1562ac0c33Sjakob #include <stdlib.h>
163126abd5Ssthen #include <poll.h>
1762ac0c33Sjakob 
1862ac0c33Sjakob #include "netio.h"
1962ac0c33Sjakob #include "util.h"
2062ac0c33Sjakob 
213126abd5Ssthen #define MAX_NETIO_FDS 1024
2262ac0c33Sjakob 
2362ac0c33Sjakob netio_type *
netio_create(region_type * region)2462ac0c33Sjakob netio_create(region_type *region)
2562ac0c33Sjakob {
2662ac0c33Sjakob 	netio_type *result;
2762ac0c33Sjakob 
2862ac0c33Sjakob 	assert(region);
2962ac0c33Sjakob 
3062ac0c33Sjakob 	result = (netio_type *) region_alloc(region, sizeof(netio_type));
3162ac0c33Sjakob 	result->region = region;
3262ac0c33Sjakob 	result->handlers = NULL;
3362ac0c33Sjakob 	result->deallocated = NULL;
3462ac0c33Sjakob 	result->dispatch_next = NULL;
3562ac0c33Sjakob 	return result;
3662ac0c33Sjakob }
3762ac0c33Sjakob 
3862ac0c33Sjakob void
netio_add_handler(netio_type * netio,netio_handler_type * handler)3962ac0c33Sjakob netio_add_handler(netio_type *netio, netio_handler_type *handler)
4062ac0c33Sjakob {
4162ac0c33Sjakob 	netio_handler_list_type *elt;
4262ac0c33Sjakob 
4362ac0c33Sjakob 	assert(netio);
4462ac0c33Sjakob 	assert(handler);
4562ac0c33Sjakob 
4662ac0c33Sjakob 	if (netio->deallocated) {
4762ac0c33Sjakob 		/*
4862ac0c33Sjakob 		 * If we have deallocated handler list elements, reuse
4962ac0c33Sjakob 		 * the first one.
5062ac0c33Sjakob 		 */
5162ac0c33Sjakob 		elt = netio->deallocated;
5262ac0c33Sjakob 		netio->deallocated = elt->next;
5362ac0c33Sjakob 	} else {
5462ac0c33Sjakob 		/*
5562ac0c33Sjakob 		 * Allocate a new one.
5662ac0c33Sjakob 		 */
5762ac0c33Sjakob 		elt = (netio_handler_list_type *) region_alloc(
5862ac0c33Sjakob 			netio->region, sizeof(netio_handler_list_type));
5962ac0c33Sjakob 	}
6062ac0c33Sjakob 
6162ac0c33Sjakob 	elt->next = netio->handlers;
6262ac0c33Sjakob 	elt->handler = handler;
633126abd5Ssthen 	elt->handler->pfd = -1;
6462ac0c33Sjakob 	netio->handlers = elt;
6562ac0c33Sjakob }
6662ac0c33Sjakob 
6762ac0c33Sjakob void
netio_remove_handler(netio_type * netio,netio_handler_type * handler)6862ac0c33Sjakob netio_remove_handler(netio_type *netio, netio_handler_type *handler)
6962ac0c33Sjakob {
7062ac0c33Sjakob 	netio_handler_list_type **elt_ptr;
7162ac0c33Sjakob 
7262ac0c33Sjakob 	assert(netio);
7362ac0c33Sjakob 	assert(handler);
7462ac0c33Sjakob 
7562ac0c33Sjakob 	for (elt_ptr = &netio->handlers; *elt_ptr; elt_ptr = &(*elt_ptr)->next) {
7662ac0c33Sjakob 		if ((*elt_ptr)->handler == handler) {
7762ac0c33Sjakob 			netio_handler_list_type *next = (*elt_ptr)->next;
7862ac0c33Sjakob 			if ((*elt_ptr) == netio->dispatch_next)
7962ac0c33Sjakob 				netio->dispatch_next = next;
8062ac0c33Sjakob 			(*elt_ptr)->handler = NULL;
8162ac0c33Sjakob 			(*elt_ptr)->next = netio->deallocated;
8262ac0c33Sjakob 			netio->deallocated = *elt_ptr;
8362ac0c33Sjakob 			*elt_ptr = next;
8462ac0c33Sjakob 			break;
8562ac0c33Sjakob 		}
8662ac0c33Sjakob 	}
8762ac0c33Sjakob }
8862ac0c33Sjakob 
8962ac0c33Sjakob const struct timespec *
netio_current_time(netio_type * netio)9062ac0c33Sjakob netio_current_time(netio_type *netio)
9162ac0c33Sjakob {
9262ac0c33Sjakob 	assert(netio);
9362ac0c33Sjakob 
9462ac0c33Sjakob 	if (!netio->have_current_time) {
9562ac0c33Sjakob 		struct timeval current_timeval;
9662ac0c33Sjakob 		if (gettimeofday(&current_timeval, NULL) == -1) {
97*ee5153b7Sflorian 			log_msg(LOG_ERR, "gettimeofday: %s, aborting.", strerror(errno));
9862ac0c33Sjakob 			abort();
9962ac0c33Sjakob 		}
10062ac0c33Sjakob 		timeval_to_timespec(&netio->cached_current_time, &current_timeval);
10162ac0c33Sjakob 		netio->have_current_time = 1;
10262ac0c33Sjakob 	}
10362ac0c33Sjakob 
10462ac0c33Sjakob 	return &netio->cached_current_time;
10562ac0c33Sjakob }
10662ac0c33Sjakob 
10762ac0c33Sjakob int
netio_dispatch(netio_type * netio,const struct timespec * timeout,const sigset_t * sigmask)10862ac0c33Sjakob netio_dispatch(netio_type *netio, const struct timespec *timeout, const sigset_t *sigmask)
10962ac0c33Sjakob {
1103126abd5Ssthen 	/* static arrays to avoid allocation */
1113126abd5Ssthen 	static struct pollfd fds[MAX_NETIO_FDS];
1123126abd5Ssthen 	int numfd;
11362ac0c33Sjakob 	int have_timeout = 0;
11462ac0c33Sjakob 	struct timespec minimum_timeout;
11562ac0c33Sjakob 	netio_handler_type *timeout_handler = NULL;
11662ac0c33Sjakob 	netio_handler_list_type *elt;
11762ac0c33Sjakob 	int rc;
11862ac0c33Sjakob 	int result = 0;
1193126abd5Ssthen #ifndef HAVE_PPOLL
1203126abd5Ssthen 	sigset_t origmask;
1213126abd5Ssthen #endif
12262ac0c33Sjakob 
12362ac0c33Sjakob 	assert(netio);
12462ac0c33Sjakob 
12562ac0c33Sjakob 	/*
12662ac0c33Sjakob 	 * Clear the cached current time.
12762ac0c33Sjakob 	 */
12862ac0c33Sjakob 	netio->have_current_time = 0;
12962ac0c33Sjakob 
13062ac0c33Sjakob 	/*
13162ac0c33Sjakob 	 * Initialize the minimum timeout with the timeout parameter.
13262ac0c33Sjakob 	 */
13362ac0c33Sjakob 	if (timeout) {
13462ac0c33Sjakob 		have_timeout = 1;
13562ac0c33Sjakob 		memcpy(&minimum_timeout, timeout, sizeof(struct timespec));
13662ac0c33Sjakob 	}
13762ac0c33Sjakob 
13862ac0c33Sjakob 	/*
13962ac0c33Sjakob 	 * Initialize the fd_sets and timeout based on the handler
14062ac0c33Sjakob 	 * information.
14162ac0c33Sjakob 	 */
1423126abd5Ssthen 	numfd = 0;
14362ac0c33Sjakob 
14462ac0c33Sjakob 	for (elt = netio->handlers; elt; elt = elt->next) {
14562ac0c33Sjakob 		netio_handler_type *handler = elt->handler;
1463126abd5Ssthen 		if (handler->fd != -1 && numfd < MAX_NETIO_FDS) {
1473126abd5Ssthen 			fds[numfd].fd = handler->fd;
1483126abd5Ssthen 			fds[numfd].events = 0;
1493126abd5Ssthen 			fds[numfd].revents = 0;
1503126abd5Ssthen 			handler->pfd = numfd;
15162ac0c33Sjakob 			if (handler->event_types & NETIO_EVENT_READ) {
1523126abd5Ssthen 				fds[numfd].events |= POLLIN;
15362ac0c33Sjakob 			}
15462ac0c33Sjakob 			if (handler->event_types & NETIO_EVENT_WRITE) {
1553126abd5Ssthen 				fds[numfd].events |= POLLOUT;
15662ac0c33Sjakob 			}
1573126abd5Ssthen 			numfd++;
1583126abd5Ssthen 		} else {
1593126abd5Ssthen 			handler->pfd = -1;
16062ac0c33Sjakob 		}
16162ac0c33Sjakob 		if (handler->timeout && (handler->event_types & NETIO_EVENT_TIMEOUT)) {
16262ac0c33Sjakob 			struct timespec relative;
16362ac0c33Sjakob 
16462ac0c33Sjakob 			relative.tv_sec = handler->timeout->tv_sec;
16562ac0c33Sjakob 			relative.tv_nsec = handler->timeout->tv_nsec;
16662ac0c33Sjakob 			timespec_subtract(&relative, netio_current_time(netio));
16762ac0c33Sjakob 
16862ac0c33Sjakob 			if (!have_timeout ||
16962ac0c33Sjakob 			    timespec_compare(&relative, &minimum_timeout) < 0)
17062ac0c33Sjakob 			{
17162ac0c33Sjakob 				have_timeout = 1;
17262ac0c33Sjakob 				minimum_timeout.tv_sec = relative.tv_sec;
17362ac0c33Sjakob 				minimum_timeout.tv_nsec = relative.tv_nsec;
17462ac0c33Sjakob 				timeout_handler = handler;
17562ac0c33Sjakob 			}
17662ac0c33Sjakob 		}
17762ac0c33Sjakob 	}
17862ac0c33Sjakob 
17962ac0c33Sjakob 	if (have_timeout && minimum_timeout.tv_sec < 0) {
18062ac0c33Sjakob 		/*
1813126abd5Ssthen 		 * On negative timeout for a handler, immediately
18262ac0c33Sjakob 		 * dispatch the timeout event without checking for
18362ac0c33Sjakob 		 * other events.
18462ac0c33Sjakob 		 */
18562ac0c33Sjakob 		if (timeout_handler && (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) {
18662ac0c33Sjakob 			timeout_handler->event_handler(netio, timeout_handler, NETIO_EVENT_TIMEOUT);
18762ac0c33Sjakob 		}
18862ac0c33Sjakob 		return result;
18962ac0c33Sjakob 	}
19062ac0c33Sjakob 
19162ac0c33Sjakob 	/* Check for events.  */
1923126abd5Ssthen #ifdef HAVE_PPOLL
1933126abd5Ssthen 	rc = ppoll(fds, numfd, (have_timeout?&minimum_timeout:NULL), sigmask);
1943126abd5Ssthen #else
1953126abd5Ssthen 	sigprocmask(SIG_SETMASK, sigmask, &origmask);
1963126abd5Ssthen 	rc = poll(fds, numfd, (have_timeout?minimum_timeout.tv_sec*1000+
1973126abd5Ssthen 		minimum_timeout.tv_nsec/1000000:-1));
1983126abd5Ssthen 	sigprocmask(SIG_SETMASK, &origmask, NULL);
1993126abd5Ssthen #endif /* HAVE_PPOLL */
20062ac0c33Sjakob 	if (rc == -1) {
20162ac0c33Sjakob 		if(errno == EINVAL || errno == EACCES || errno == EBADF) {
2023126abd5Ssthen 			log_msg(LOG_ERR, "fatal error poll: %s.",
20362ac0c33Sjakob 				strerror(errno));
20462ac0c33Sjakob 			exit(1);
20562ac0c33Sjakob 		}
20662ac0c33Sjakob 		return -1;
20762ac0c33Sjakob 	}
20862ac0c33Sjakob 
20962ac0c33Sjakob 	/*
21062ac0c33Sjakob 	 * Clear the cached current_time (pselect(2) may block for
21162ac0c33Sjakob 	 * some time so the cached value is likely to be old).
21262ac0c33Sjakob 	 */
21362ac0c33Sjakob 	netio->have_current_time = 0;
21462ac0c33Sjakob 
21562ac0c33Sjakob 	if (rc == 0) {
21662ac0c33Sjakob 		/*
21762ac0c33Sjakob 		 * No events before the minimum timeout expired.
21862ac0c33Sjakob 		 * Dispatch to handler if interested.
21962ac0c33Sjakob 		 */
22062ac0c33Sjakob 		if (timeout_handler && (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) {
22162ac0c33Sjakob 			timeout_handler->event_handler(netio, timeout_handler, NETIO_EVENT_TIMEOUT);
22262ac0c33Sjakob 		}
22362ac0c33Sjakob 	} else {
22462ac0c33Sjakob 		/*
22562ac0c33Sjakob 		 * Dispatch all the events to interested handlers
22662ac0c33Sjakob 		 * based on the fd_sets.  Note that a handler might
22762ac0c33Sjakob 		 * deinstall itself, so store the next handler before
22862ac0c33Sjakob 		 * calling the current handler!
22962ac0c33Sjakob 		 */
23062ac0c33Sjakob 		assert(netio->dispatch_next == NULL);
2313126abd5Ssthen 
23262ac0c33Sjakob 		for (elt = netio->handlers; elt && rc; ) {
23362ac0c33Sjakob 			netio_handler_type *handler = elt->handler;
23462ac0c33Sjakob 			netio->dispatch_next = elt->next;
2353126abd5Ssthen 			if (handler->fd != -1 && handler->pfd != -1) {
23662ac0c33Sjakob 				netio_event_types_type event_types
23762ac0c33Sjakob 					= NETIO_EVENT_NONE;
2383126abd5Ssthen 				if ((fds[handler->pfd].revents & POLLIN)) {
23962ac0c33Sjakob 					event_types |= NETIO_EVENT_READ;
24062ac0c33Sjakob 				}
2413126abd5Ssthen 				if ((fds[handler->pfd].revents & POLLOUT)) {
24262ac0c33Sjakob 					event_types |= NETIO_EVENT_WRITE;
24362ac0c33Sjakob 				}
2443126abd5Ssthen 				if ((fds[handler->pfd].revents &
2453126abd5Ssthen 					(POLLNVAL|POLLHUP|POLLERR))) {
2463126abd5Ssthen 					/* closed/error: give a read event,
2473126abd5Ssthen 					 * or otherwise, a write event */
2483126abd5Ssthen 					if((handler->event_types&NETIO_EVENT_READ))
2493126abd5Ssthen 						event_types |= NETIO_EVENT_READ;
2503126abd5Ssthen 					else if((handler->event_types&NETIO_EVENT_WRITE))
2513126abd5Ssthen 						event_types |= NETIO_EVENT_WRITE;
25262ac0c33Sjakob 				}
25362ac0c33Sjakob 
25462ac0c33Sjakob 				if (event_types & handler->event_types) {
25562ac0c33Sjakob 					handler->event_handler(netio, handler, event_types & handler->event_types);
25662ac0c33Sjakob 					++result;
25762ac0c33Sjakob 				}
25862ac0c33Sjakob 			}
25962ac0c33Sjakob 			elt = netio->dispatch_next;
26062ac0c33Sjakob 		}
26162ac0c33Sjakob 		netio->dispatch_next = NULL;
26262ac0c33Sjakob 	}
26362ac0c33Sjakob 
26462ac0c33Sjakob 	return result;
26562ac0c33Sjakob }
266