diff options
author | Russ Allbery <rra@stanford.edu> | 2011-05-25 20:11:47 -0700 |
---|---|---|
committer | Russ Allbery <rra@stanford.edu> | 2011-05-25 20:11:47 -0700 |
commit | a047130eee520269499ecc2fae81376f1a02dc32 (patch) | |
tree | 153af2d286e1be7871dda8156a90f57593725098 /util | |
parent | da2d584744c97220ed5d218bf7407d7d56d15bda (diff) |
Move accepting connections to a utility function
Accepting connections from an array of file descriptors is a bit
fiddly. Move all that code out to a utility function that we can
reuse and simplify the server code.
Diffstat (limited to 'util')
-rw-r--r-- | util/network.c | 47 | ||||
-rw-r--r-- | util/network.h | 12 |
2 files changed, 59 insertions, 0 deletions
diff --git a/util/network.c b/util/network.c index 51b6ef1..50d7f8d 100644 --- a/util/network.c +++ b/util/network.c @@ -285,6 +285,53 @@ network_bind_all(unsigned short port, socket_type **fds, unsigned int *count) /* + * Given an array of file descriptors and the length of that array (the same + * data that's returned by network_bind_all), wait for an incoming connection + * on any of those sockets, accept the connection with accept(), and return + * the new file descriptor. + * + * This is essentially a replacement for accept() with a single socket for + * daemons that are listening to multiple separate bound sockets, possibly + * because they need to listen to specific interfaces or possibly because + * they're listening for both IPv4 and IPv6 connections. + * + * Returns the new socket on success or INVALID_SOCKET on failure. On + * success, fills out the arguments with the address and address length of the + * accepted client. No error will be reported, so the caller should do that. + * Note that INVALID_SOCKET may be returned if the timeout is interrupted by a + * signal, which is not, precisely speaking, an error condition. In this + * case, errno will be set to EINTR. + */ +socket_type +network_accept_any(socket_type fds[], unsigned int count, + struct sockaddr *addr, socklen_t *addrlen) +{ + fd_set readfds; + socket_type maxfd, fd; + unsigned int i; + int status; + + FD_ZERO(&readfds); + maxfd = -1; + for (i = 0; i < count; i++) { + FD_SET(fds[i], &readfds); + if (fds[i] > maxfd) + maxfd = fds[i]; + } + status = select(maxfd + 1, &readfds, NULL, NULL, NULL); + if (status < 0) + return INVALID_SOCKET; + fd = INVALID_SOCKET; + for (i = 0; i < count; i++) + if (FD_ISSET(fds[i], &readfds)) { + fd = fds[i]; + break; + } + return accept(fd, addr, addrlen); +} + + +/* * Binds the given socket to an appropriate source address for its family * using the provided source address. Returns true on success and false on * failure. diff --git a/util/network.h b/util/network.h index 3631e9d..67cd403 100644 --- a/util/network.h +++ b/util/network.h @@ -63,6 +63,18 @@ void network_bind_all(unsigned short port, socket_type **fds, __attribute__((__nonnull__)); /* + * Accept an incoming connection from any file descriptor in an array. This + * is a blocking accept that will wait until there is an incoming connection, + * unless interrupted by receipt of a signal. Returns the new socket or + * INVALID_SOCKET, and fills out the arguments with the address of the remote + * client. If the wait for a connection was interrupted by a signal, returns + * INVALID_SOCKET with errno set to EINTR. + */ +socket_type network_accept_any(socket_type fds[], unsigned int count, + struct sockaddr *addr, socklen_t *addrlen) + __attribute__((__nonnull__(1))); + +/* * Create a socket and connect it to the remote service given by the linked * list of addrinfo structs. Returns the new file descriptor on success and * -1 on failure, with the error left in errno. Takes an optional source |