summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorRuss Allbery <rra@stanford.edu>2011-05-25 20:11:47 -0700
committerRuss Allbery <rra@stanford.edu>2011-05-25 20:11:47 -0700
commita047130eee520269499ecc2fae81376f1a02dc32 (patch)
tree153af2d286e1be7871dda8156a90f57593725098 /util
parentda2d584744c97220ed5d218bf7407d7d56d15bda (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.c47
-rw-r--r--util/network.h12
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