INN commit: trunk (26 files)

INN Commit rra at isc.org
Wed Sep 17 16:31:36 UTC 2014


    Date: Wednesday, September 17, 2014 @ 09:31:35
  Author: iulius
Revision: 9691

update network functions to their latest upstream version

Use DEFAULT_TIMEOUT (300 seconds) for getlist and ident.

Entirely remove IP_OPTIONS and network_kill_options(), no longer
necessary nowadays.

Also remove a few parts of code related to IPV6_V6ONLY, that should no
longer be a problem (AF_UNSPEC can be used instead of specifically
AF_INET when IPv6 is not available).  CURRENT will enable IPv6
unconditionally.

Add a new network-innbind.c file, along with its headers, to deal
with network connections using innbind.  These functions are called by
INN; in case innbind is not necessary (port >= 1024 or we are root),
handle the connection to the generic functions provided by network.c
(from rra-c-util).

Split the network test suite into 4 specific tests (IPv4, IPv6, client
and server).


Changes mentioned in rra-c-util changelogs are:

- When binding IPv6 sockets, restrict them to only IPv6 connections
rather than also allowing IPv4 connections where possible.  The default
behavior, for maximum backward compatibility, is for IPv6-bound sockets
to accept IPv4 connections and expose those connections as IPv4 mapped
addresses.  This causes various problems, however, such as with reuse
of bound ports (which was causing test suite failures) and requirements
to handle IPv4 mapped addresses.  The network model (also used by BSD
systems) where IPv6 sockets only accept IPv6 connections is cleaner,
even if it requires juggling multiple sockets in some situations.

- network_addr_match now always fails (returns false) if either of the
strings are the empty string.  AIX 7.1's inet_aton treats the empty
string as equivalent to 0.0.0.0, but we want to treat it as a syntax
error since it's too easy to get an empty string by accident.

- The network_connect utility functions now take an optional timeout.
If non-zero, a non-blocking connect is done with that timeout, rather
than blocking on connect until the TCP stack gives up.  The network
utility code now depends on the fdflag code.

- network_connect, when given a timeout, now resumes waiting for the
nonblocking connect after being interrupted by a signal.  This can
mean that a connect can take longer than the timeout if interrupted;
hopefully both timeouts and catching signals are rare enough that this
won't pose a serious issue. 

- In network_read and network_write with a timeout, restart the I/O
attempt if a system call failed with EINTR instead of aborting the
operation. 

- When binding an IPv6-only socket with network_bind_ipv6 and not
binding to all local addresses, use IP_FREEBIND if it's available.
This allows binding to addresses that are not yet configured, which is
much more common with IPv6 given IPv6 autoconfiguration.

- Add new network_accept_any() function, which takes an array of file
descriptors (similar to what's returned by network_bind_all) and blocks
accepting incoming connections on any of those file descriptors. 

- Prefer reallocarray to realloc for multiplied sizes.

- The network_bind_* functions now more reliably set the socket errno
on failure and log somewhat more informative error messages with warn.

- Also improve the error handling and reporting from some of the other
network functions, and refactor the code to avoid more #ifdefs embedded
in the middle of other code.

- The network_bind_* functions now take a socket type as an additional
argument so that they can be used with UDP-based services.

- network_bind_all now returns a boolean, which will be false if no
sockets could be bound due to some error.  Callers may check this
instead of checking if the socket count is zero.

- Check the return status of snprintf when converting port numbers
to strings in network_bind_all and network_connect_host, and use the
correct format for the port number for the latter.

Added:
  trunk/include/inn/network-innbind.h
  trunk/lib/network-innbind.c
  trunk/tests/lib/network/
  trunk/tests/lib/network/addr-ipv4-t.c
  trunk/tests/lib/network/addr-ipv6-t.c
  trunk/tests/lib/network/client-t.c
  trunk/tests/lib/network/server-t.c
Modified:
  trunk/MANIFEST
  trunk/authprogs/ident.c
  trunk/doc/IPv6-info
  trunk/frontends/getlist.c
  trunk/include/inn/network.h
  trunk/include/portable/socket.h
  trunk/innd/rc.c
  trunk/innfeed/host.c
  trunk/lib/Makefile
  trunk/lib/network.c
  trunk/lib/nntp.c
  trunk/lib/remopen.c
  trunk/nnrpd/nnrpd.c
  trunk/support/mkmanifest
  trunk/tests/Makefile
  trunk/tests/TESTS
  trunk/tests/authprogs/ident-t.c
  trunk/tests/lib/	(properties)
Deleted:
  trunk/tests/lib/network-t.c

---------------------------------+
 MANIFEST                        |    8 
 authprogs/ident.c               |    3 
 doc/IPv6-info                   |    7 
 frontends/getlist.c             |    2 
 include/inn/network-innbind.h   |   71 ++
 include/inn/network.h           |  201 ++++---
 include/portable/socket.h       |   36 -
 innd/rc.c                       |   24 
 innfeed/host.c                  |    2 
 lib/Makefile                    |  597 ++++++++++++++--------
 lib/network-innbind.c           |  413 +++++++++++++++
 lib/network.c                   | 1024 ++++++++++++++++++++++----------------
 lib/nntp.c                      |    7 
 lib/remopen.c                   |    2 
 nnrpd/nnrpd.c                   |   17 
 support/mkmanifest              |    5 
 tests/Makefile                  |   26 
 tests/TESTS                     |    5 
 tests/authprogs/ident-t.c       |    4 
 tests/lib/network-t.c           |  566 ---------------------
 tests/lib/network/addr-ipv4-t.c |  130 ++++
 tests/lib/network/addr-ipv6-t.c |  149 +++++
 tests/lib/network/client-t.c    |  451 ++++++++++++++++
 tests/lib/network/server-t.c    |  532 +++++++++++++++++++
 24 files changed, 2991 insertions(+), 1291 deletions(-)

Modified: MANIFEST
===================================================================
--- MANIFEST	2014-09-15 20:07:40 UTC (rev 9690)
+++ MANIFEST	2014-09-17 16:31:35 UTC (rev 9691)
@@ -394,6 +394,7 @@
 include/inn/md5.h                     Header file for MD5 digests
 include/inn/messages.h                Header file for message functions
 include/inn/mmap.h                    Header file for mmap() functions
+include/inn/network-innbind.h         Header file for functions using innbind
 include/inn/network.h                 Header file for network functions
 include/inn/newsuser.h                Header file for ensuring running as news
 include/inn/nntp.h                    Header file for NNTP functions and codes
@@ -523,6 +524,7 @@
 lib/messages.c                        Error reporting and debug output
 lib/mkstemp.c                         mkstemp replacement
 lib/mmap.c                            mmap manipulation routines
+lib/network-innbind.c                 Network utility functions using innbind
 lib/network.c                         Network utility functions
 lib/newsuser.c                        Ensure running as news user/group
 lib/nntp.c                            NNTP utility library
@@ -893,7 +895,11 @@
 tests/lib/memcmp-t.c                  Tests for lib/memcmp.c
 tests/lib/messages-t.c                Tests for lib/messages.c
 tests/lib/mkstemp-t.c                 Tests for lib/mkstemp.c
-tests/lib/network-t.c                 Tests for lib/network.c
+tests/lib/network                     Test suite for network (Directory)
+tests/lib/network/addr-ipv4-t.c       Tests for lib/network.c (IPv4-oriented)
+tests/lib/network/addr-ipv6-t.c       Tests for lib/network.c (IPv6-oriented)
+tests/lib/network/client-t.c          Tests for lib/network.c (client-oriented)
+tests/lib/network/server-t.c          Tests for lib/network.c (server-oriented)
 tests/lib/pread-t.c                   Tests for lib/pread.c
 tests/lib/pwrite-t.c                  Tests for lib/pwrite.c
 tests/lib/qio-t.c                     Tests for lib/qio.c

Modified: authprogs/ident.c
===================================================================
--- authprogs/ident.c	2014-09-15 20:07:40 UTC (rev 9690)
+++ authprogs/ident.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -81,7 +81,8 @@
         die("did not get client information from nnrpd");
 
     /* Connect back to the client system. */
-    sock = network_connect_host(res->clientip, identport, res->localip);
+    sock = network_connect_host(res->clientip, identport, res->localip,
+                                DEFAULT_TIMEOUT);
     if (sock < 0) {
         if (errno != ECONNREFUSED)
             sysdie("cannot connect to ident server");

Modified: doc/IPv6-info
===================================================================
--- doc/IPv6-info	2014-09-15 20:07:40 UTC (rev 9690)
+++ doc/IPv6-info	2014-09-17 16:31:35 UTC (rev 9691)
@@ -6,13 +6,6 @@
    INN (see also the parts of the code marked FIXME):
 
 
-Things that will break if you compile with --enable-ipv6:
-
-    * IP_OPTIONS are not cleared for any incoming connections to innd even
-      over IPv4
-  
-
-
 Some comments as of the completion of the original patch:
 
     Date: Wed, 13 Feb 2002 00:10:59 -0500 (EST)

Modified: frontends/getlist.c
===================================================================
--- frontends/getlist.c	2014-09-15 20:07:40 UTC (rev 9690)
+++ frontends/getlist.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -265,7 +265,7 @@
     /* Connect to the server. */
     if (host == NULL)
         sysdie("cannot get server name");
-    nntp = nntp_connect(host, port, 128 * 1024, 10 * 60);
+    nntp = nntp_connect(host, port, 128 * 1024, DEFAULT_TIMEOUT);
     if (nntp == NULL)
         sysdie("cannot connect to server %s:%hu", host, port);
     status = nntp_read_response(nntp, &response, &line);

Added: include/inn/network-innbind.h
===================================================================
--- include/inn/network-innbind.h	                        (rev 0)
+++ include/inn/network-innbind.h	2014-09-17 16:31:35 UTC (rev 9691)
@@ -0,0 +1,71 @@
+/* $Id$
+ *
+ * Prototypes for network connection utility functions using innbind.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle at eyrie.org>
+ * Copyright 2009, 2010, 2011, 2012, 2013
+ *     The Board of Trustees of the Leland Stanford Junior University
+ * Copyright (c) 2004, 2005, 2006, 2007, 2008, 2010
+ *     by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ *     2002, 2003 by The Internet Software Consortium and Rich Salz
+ *
+ * This code is derived from software contributed to the Internet Software
+ * Consortium by Rich Salz.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INN_NETWORK_INNBIND_H
+#define INN_NETWORK_INNBIND_H 1
+
+#include <inn/defines.h>
+#include "portable/macros.h"
+#include "portable/socket.h"
+#include "portable/stdbool.h"
+
+#include <sys/types.h>
+
+BEGIN_DECLS
+
+/*
+ * Create a socket of the given type and bind it to the specified address and
+ * port (either IPv4 or IPv6), returning the resulting file descriptor or -1
+ * on error.  Errors are reported using warn/syswarn.  To bind to all
+ * interfaces, use "any" or "all" for address.
+ */
+socket_type network_innbind_ipv4(int type, const char *addr, unsigned short port)
+    __attribute__((__nonnull__));
+socket_type network_innbind_ipv6(int type, const char *addr, unsigned short port)
+    __attribute__((__nonnull__));
+
+/*
+ * Create and bind sockets of the given type for every local address (normally
+ * two, one for IPv4 and one for IPv6, if IPv6 support is enabled).  If IPv6
+ * is not enabled, just one socket will be created and bound to the IPv4
+ * wildcard address.  Returns true on success and false (setting errno) on
+ * failure.
+ *
+ * fds will be set to an array containing the resulting file descriptors, with
+ * count holding the count returned.
+ */
+bool network_innbind_all(int type, unsigned short port, socket_type **fds,
+                         unsigned int *count)
+    __attribute__((__nonnull__));
+
+END_DECLS
+
+#endif /* INN_NETWORK_INNBIND_H */


Property changes on: trunk/include/inn/network-innbind.h
___________________________________________________________________
Added: svn:eol-style
   + native
Added: svn:keywords
   + Author Date Id Revision

Modified: include/inn/network.h
===================================================================
--- include/inn/network.h	2014-09-15 20:07:40 UTC (rev 9690)
+++ include/inn/network.h	2014-09-17 16:31:35 UTC (rev 9691)
@@ -1,51 +1,87 @@
-/*  $Id$
-**
-**  Utility functions for network connections.
-**
-**  This is a collection of utility functions for network connections and
-**  socket creation, encapsulating some of the complexities of IPv4 and IPv6
-**  support and abstracting operations common to most network code.
-*/
+/* $Id$
+ *
+ * Prototypes for network connection utility functions.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle at eyrie.org>
+ * Copyright 2014 Russ Allbery <eagle at eyrie.org>
+ * Copyright 2009, 2010, 2011, 2012, 2013
+ *     The Board of Trustees of the Leland Stanford Junior University
+ * Copyright (c) 2004, 2005, 2006, 2007, 2008, 2010
+ *     by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ *     2002, 2003 by The Internet Software Consortium and Rich Salz
+ *
+ * This code is derived from software contributed to the Internet Software
+ * Consortium by Rich Salz.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
 
 #ifndef INN_NETWORK_H
 #define INN_NETWORK_H 1
 
 #include <inn/defines.h>
+#include "portable/macros.h"
 #include "portable/socket.h"
-#include <sys/types.h>          /* socklen_t */
+#include "portable/stdbool.h"
 
-/* Forward declarations to avoid unnecessary includes. */
-struct addrinfo;
-struct sockaddr;
+#include <sys/types.h>
 
-/* We don't want to accidentally use IPv6 if we were built without it, even if
-   we end up using the system getaddrinfo with IPv6 support.  We can do that
-   by hinting getaddrinfo away from returning IPv6 addresses.  We set this
-   constant to AF_UNSPEC if we have IPv6 support or AF_INET if we don't and
-   then use it in getaddrinfo hints. */
-#if INN_HAVE_INET6
-# define NETWORK_AF_HINT AF_UNSPEC
-#else
-# define NETWORK_AF_HINT AF_INET
-#endif
-
 BEGIN_DECLS
 
-/* Create a socket and bind it to the specified address and port (either IPv4
-   or IPv6), returning the resulting file descriptor or -1 on error.  Errors
-   are reported using warn/syswarn.  To bind to all interfaces, use "any" or
-   "all" for address. */
-int network_bind_ipv4(const char *address, unsigned short port);
-int network_bind_ipv6(const char *address, unsigned short port);
+/*
+ * Create a socket of the given type and bind it to the specified address and
+ * port (either IPv4 or IPv6), returning the resulting file descriptor or -1
+ * on error.  Errors are reported using warn/syswarn.  To bind to all
+ * interfaces, use "any" or "all" for address.
+ */
+socket_type network_bind_ipv4(int type, const char *addr, unsigned short port)
+    __attribute__((__nonnull__));
+socket_type network_bind_ipv6(int type, const char *addr, unsigned short port)
+    __attribute__((__nonnull__));
 
-/* Create and bind sockets for every local address (normally two, one for IPv4
-   and one for IPv6, if IPv6 support is enabled).  If IPv6 is not enabled,
-   just one socket will be created and bound to the IPv4 wildcard address.
-   fds will be set to an array containing the resulting file descriptors, with
-   count holding the count returned. */
-void network_bind_all(unsigned short port, int **fds, unsigned int *count);
+/*
+ * Create and bind sockets of the given type for every local address (normally
+ * two, one for IPv4 and one for IPv6, if IPv6 support is enabled).  If IPv6
+ * is not enabled, just one socket will be created and bound to the IPv4
+ * wildcard address.  Returns true on success and false (setting errno) on
+ * failure.
+ *
+ * fds will be set to an array containing the resulting file descriptors, with
+ * count holding the count returned.  Use network_bind_all_free to free the
+ * array of file descriptors when no longer needed.
+ */
+bool network_bind_all(int type, unsigned short port, socket_type **fds,
+                      unsigned int *count)
+    __attribute__((__nonnull__));
+void network_bind_all_free(socket_type *fds);
 
 /*
+ * Wait on an array of file descriptor for one of them to select ready for
+ * read, and return the first file descriptor that does so.  This is primarily
+ * intended for UDP services listening on multiple file descriptors.  TCP
+ * services will probably want to use network_accept_any instead.
+ *
+ * This is not intended to be a replacement for a full event loop, just some
+ * simple shared code for UDP services.
+ */
+socket_type network_wait_any(socket_type fds[], unsigned int count)
+    __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
@@ -57,47 +93,76 @@
                                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
-   address; if not provided and inn.conf has been read, the source address is
-   set based on innconf->sourceaddress and innconf->sourceaddress6. */
-int network_connect(struct addrinfo *, const char *source);
+/*
+ * 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
+ * address and a timeout in seconds, which may be 0 for no timeout.  (Source
+ * may also be "all" or "any", which mean the same thing as NULL: do not use
+ * any particular source address.)
+ */
+socket_type network_connect(const struct addrinfo *, const char *source,
+                            time_t)
+    __attribute__((__nonnull__(1)));
 
-/* Like network_connect but takes a host and port instead.  If host lookup
-   fails, errno may not be set to anything useful. */
-int network_connect_host(const char *host, unsigned short port,
-                         const char *source);
+/*
+ * Like network_connect but takes a host and port instead.  If host lookup
+ * fails, errno may not be set to anything useful.
+ */
+socket_type network_connect_host(const char *host, unsigned short port,
+                                 const char *source, time_t)
+    __attribute__((__nonnull__(1)));
 
-/* Creates a socket of the specified domain and type and binds it to the
-   appropriate source address, either the one supplied or the appropriate
-   innconf setting if the provided source address is NULL.  To bind to all
-   interfaces, use "all" for address.  Returns the newly created file
-   descriptor or -1 on error.
+/*
+ * Creates a socket of the specified domain and type and binds it to the
+ * appropriate source address, either the one supplied or all addresses if the
+ * source address is NULL, "all", or "any".  Returns the newly created file
+ * descriptor or -1 on error.
+ *
+ * This is a lower-level function intended primarily for the use of clients
+ * that will then go on to do a non-blocking connect.
+ */
+socket_type network_client_create(int domain, int type, const char *source);
 
-   This is a lower-level function intended primarily for the use of clients
-   that will then go on to do a non-blocking connect. */
-int network_client_create(int domain, int type, const char *source);
+/*
+ * Read or write the specified number of bytes to the network, enforcing a
+ * timeout.  Both return true on success and false on failure; on failure, the
+ * socket errno is set.
+ *
+ * network_write will set the file descriptor non-blocking and then set it
+ * back to blocking at the conclusion of the write, so don't use this function
+ * with file descriptors that should stay non-blocking.
+ */
+bool network_read(socket_type, void *, size_t, time_t)
+    __attribute__((__nonnull__));
+bool network_write(socket_type, const void *, size_t, time_t)
+    __attribute__((__nonnull__));
 
-/* Kill IP options (like source routing) if possible.  Returns false only when
-   IP options have been found but clearing them failed. */
-bool network_kill_options(int fd, struct sockaddr *remote);
+/*
+ * Put an ASCII representation of the address in a sockaddr into the provided
+ * buffer, which should hold at least INET6_ADDRSTRLEN characters.
+ */
+bool network_sockaddr_sprint(char *, size_t, const struct sockaddr *)
+    __attribute__((__nonnull__));
 
-/* Put an ASCII representation of the address in a sockaddr into the provided
-   buffer, which should hold at least INET6_ADDRSTRLEN characters. */
-bool network_sockaddr_sprint(char *, size_t, const struct sockaddr *);
+/*
+ * Returns if the addresses from the two sockaddrs are equal.  The ports are
+ * ignored, and only AF_INET or AF_INET6 sockaddrs are supported (all others
+ * will return false).
+ */
+bool network_sockaddr_equal(const struct sockaddr *, const struct sockaddr *)
+    __attribute__((__nonnull__));
 
-/* Returns if the addresses from the two sockaddrs are equal.  The ports are
-   ignored, and only AF_INET or AF_INET6 sockaddrs are supported (all others
-   will return false). */
-bool network_sockaddr_equal(const struct sockaddr *, const struct sockaddr *);
-
 /* Returns the port number from a sockaddr. */
-unsigned short network_sockaddr_port(const struct sockaddr *);
+unsigned short network_sockaddr_port(const struct sockaddr *)
+    __attribute__((__nonnull__));
 
-/* Compare two addresses relative to an optional mask.  Returns true if
-   they're equal, false otherwise or on a parse error. */
-bool network_addr_match(const char *, const char *, const char *mask);
+/*
+ * Compare two addresses relative to an optional mask.  Returns true if
+ * they're equal, false otherwise or on a parse error.
+ */
+bool network_addr_match(const char *, const char *, const char *mask)
+    __attribute__((__nonnull__(1, 2)));
 
 END_DECLS
 

Modified: include/portable/socket.h
===================================================================
--- include/portable/socket.h	2014-09-15 20:07:40 UTC (rev 9690)
+++ include/portable/socket.h	2014-09-17 16:31:35 UTC (rev 9691)
@@ -251,7 +251,9 @@
  * When reporting errors from socket functions, use socket_errno and
  * socket_strerror instead of errno and strerror.  When setting errno to
  * something for socket errors (to preserve errors through close, for
- * example), use socket_set_errno instead of just assigning to errno.
+ * example), use socket_set_errno instead of just assigning to errno.  Use
+ * socket_set_errno_einval when setting the socket error to EINVAL (for
+ * invalid arguments), since Windows doesn't use the C errno value for this.
  *
  * Socket file descriptors must be passed and stored in variables of type
  * socket_type rather than an int.  Use INVALID_SOCKET for invalid socket file
@@ -260,24 +262,26 @@
  */
 #ifdef _WIN32
 int socket_init(void);
-# define socket_shutdown()      WSACleanup()
-# define socket_close(fd)       closesocket(fd)
-# define socket_read(fd, b, s)  recv((fd), (b), (s), 0)
-# define socket_write(fd, b, s) send((fd), (b), (s), 0)
-# define socket_errno           WSAGetLastError()
-# define socket_set_errno(e)    WSASetLastError(e)
+# define socket_shutdown()              WSACleanup()
+# define socket_close(fd)               closesocket(fd)
+# define socket_read(fd, b, s)          recv((fd), (b), (s), 0)
+# define socket_write(fd, b, s)         send((fd), (b), (s), 0)
+# define socket_errno                   WSAGetLastError()
+# define socket_set_errno(e)            WSASetLastError(e)
+# define socket_set_errno_einval()      WSASetLastError(WSAEINVAL)
 const char *socket_strerror(int);
 typedef SOCKET socket_type;
 #else
-# define socket_init()          1
-# define socket_shutdown()      /* empty */
-# define socket_close(fd)       close(fd)
-# define socket_read(fd, b, s)  read((fd), (b), (s))
-# define socket_write(fd, b, s) write((fd), (b), (s))
-# define socket_errno           errno
-# define socket_set_errno(e)    errno = (e)
-# define socket_strerror(e)     strerror(e)
-# define INVALID_SOCKET         -1
+# define socket_init()                  1
+# define socket_shutdown()              /* empty */
+# define socket_close(fd)               close(fd)
+# define socket_read(fd, b, s)          read((fd), (b), (s))
+# define socket_write(fd, b, s)         write((fd), (b), (s))
+# define socket_errno                   errno
+# define socket_set_errno(e)            errno = (e)
+# define socket_set_errno_einval()      errno = EINVAL
+# define socket_strerror(e)             strerror(e)
+# define INVALID_SOCKET                 -1
 typedef int socket_type;
 #endif
 

Modified: innd/rc.c
===================================================================
--- innd/rc.c	2014-09-15 20:07:40 UTC (rev 9690)
+++ innd/rc.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -14,6 +14,7 @@
 #include "inn/fdflag.h"
 #include "inn/innconf.h"
 #include "inn/network.h"
+#include "inn/network-innbind.h"
 #include "inn/vector.h"
 #include "innd.h"
 
@@ -457,11 +458,6 @@
 	    syslog(L_ERROR, "%s cant accept RCreader %m", LogName);
 	return;
     }
-    if (!network_kill_options(fd, (struct sockaddr *) &remote)) {
-        if (close(fd) < 0)
-            syslog(L_ERROR, "%s cant close %d %m", LogName, fd);
-        return;
-    }
 
     /* If RemoteTimer is not zero, then check the limits on incoming
        connections on a total and per host basis.
@@ -1743,19 +1739,23 @@
        descriptors.  Two is probably always sufficient, but we can't tell for
        sure how many will be returned by getaddrinfo and this only happens on
        startup so doing a bit of memory allocation won't hurt. */
-    if (innconf->bindaddress == NULL && innconf->bindaddress6 == NULL)
-        network_bind_all(innconf->port, &fds, &count);
-    else {
+    if (innconf->bindaddress == NULL && innconf->bindaddress6 == NULL) {
+        network_innbind_all(SOCK_STREAM, innconf->port, &fds, &count);
+    } else {
         if (innconf->bindaddress != NULL && innconf->bindaddress6 != NULL)
             count = 2;
         else
             count = 1;
         fds = xmalloc(count * sizeof(int));
         i = 0;
-        if (innconf->bindaddress6 != NULL)
-            fds[i++] = network_bind_ipv6(innconf->bindaddress6, innconf->port);
-        if (innconf->bindaddress != NULL)
-            fds[i] = network_bind_ipv4(innconf->bindaddress, innconf->port);
+        if (innconf->bindaddress6 != NULL) {
+            fds[i++] = network_innbind_ipv6(SOCK_STREAM, innconf->bindaddress6,
+                                            innconf->port);
+        }
+        if (innconf->bindaddress != NULL) {
+            fds[i] = network_innbind_ipv4(SOCK_STREAM, innconf->bindaddress,
+                                          innconf->port);
+        }
     }
 
     /* Make sure that the RCchan array is of the appropriate size. */

Modified: innfeed/host.c
===================================================================
--- innfeed/host.c	2014-09-15 20:07:40 UTC (rev 9690)
+++ innfeed/host.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -1171,7 +1171,7 @@
       char port[20];
 
       memset(&hints, 0, sizeof(hints));
-      hints.ai_family = NETWORK_AF_HINT;
+      hints.ai_family = AF_UNSPEC;
 #ifdef HAVE_INET6
       if (host->params->bindAddr && strcmp(host->params->bindAddr, "none") == 0)
         hints.ai_family = AF_INET6;

Modified: lib/Makefile
===================================================================
--- lib/Makefile	2014-09-15 20:07:40 UTC (rev 9690)
+++ lib/Makefile	2014-09-17 16:31:35 UTC (rev 9691)
@@ -20,7 +20,8 @@
 	      	getfqdn.c getmodaddr.c hash.c hashtab.c headers.c hex.c	   \
 	      	innconf.c inndcomm.c list.c localopen.c lockfile.c	   \
 	      	makedir.c md5.c messageid.c messages.c mmap.c network.c	   \
-	      	newsuser.c nntp.c numbers.c qio.c radix32.c readin.c	   \
+	      	network-innbind.c newsuser.c nntp.c numbers.c qio.c        \
+		radix32.c readin.c					   \
 	      	remopen.c reservedfd.c resource.c sendarticle.c sendpass.c \
 	      	sequence.c timer.c tst.c uwildmat.c vector.c wire.c	   \
 	      	xfopena.c xmalloc.c xsignal.c xwrite.c
@@ -94,295 +95,505 @@
 
 # DO NOT DELETE THIS LINE -- make depend depends on it.
 argparse.o: argparse.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/innconf.h ../include/inn/defines.h \
-  ../include/inn/libinn.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/innconf.h \
+  ../include/inn/defines.h ../include/inn/libinn.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 buffer.o: buffer.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/buffer.h ../include/inn/defines.h \
-  ../include/inn/libinn.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/buffer.h ../include/inn/xmalloc.h
 cleanfrom.o: cleanfrom.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 clientactive.o: clientactive.c ../include/config.h \
-  ../include/inn/defines.h ../include/inn/system.h \
+  ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
   ../include/inn/options.h ../include/clibrary.h ../include/config.h \
-  ../include/inn/innconf.h ../include/inn/defines.h \
-  ../include/inn/libinn.h ../include/inn/nntp.h ../include/inn/paths.h
+  ../include/inn/macros.h ../include/inn/innconf.h \
+  ../include/inn/defines.h ../include/inn/libinn.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/nntp.h \
+  ../include/inn/paths.h
 clientlib.o: clientlib.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/innconf.h ../include/inn/defines.h \
-  ../include/inn/libinn.h ../include/inn/nntp.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/innconf.h \
+  ../include/inn/defines.h ../include/inn/libinn.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/nntp.h
 commands.o: commands.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 concat.o: concat.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/inn/libinn.h \
-  ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/inn/libinn.h \
+  ../include/inn/defines.h ../include/inn/xmalloc.h \
+  ../include/inn/xwrite.h
 conffile.o: conffile.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/conffile.h ../include/inn/libinn.h \
-  ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/conffile.h ../include/inn/libinn.h \
+  ../include/inn/defines.h ../include/inn/xmalloc.h \
+  ../include/inn/xwrite.h
 confparse.o: confparse.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/confparse.h ../include/inn/defines.h \
-  ../include/inn/hashtab.h ../include/inn/messages.h \
-  ../include/inn/vector.h ../include/inn/libinn.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/confparse.h \
+  ../include/inn/defines.h ../include/inn/hashtab.h \
+  ../include/inn/messages.h ../include/inn/vector.h \
+  ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h
 daemonize.o: daemonize.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/messages.h ../include/inn/defines.h \
-  ../include/inn/libinn.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/messages.h \
+  ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 date.o: date.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 dbz.o: dbz.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/portable/mmap.h ../include/inn/dbz.h \
-  ../include/inn/libinn.h ../include/inn/defines.h ../include/inn/fdflag.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/mmap.h ../include/inn/dbz.h \
+  ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/fdflag.h \
   ../include/portable/socket.h ../include/portable/getaddrinfo.h \
   ../include/portable/getnameinfo.h ../include/inn/messages.h \
   ../include/inn/innconf.h ../include/inn/mmap.h
 defdist.o: defdist.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/innconf.h ../include/inn/defines.h \
-  ../include/inn/libinn.h ../include/inn/paths.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/innconf.h \
+  ../include/inn/defines.h ../include/inn/libinn.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/paths.h
 dispatch.o: dispatch.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/dispatch.h ../include/inn/vector.h \
-  ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/dispatch.h \
+  ../include/inn/vector.h
 fdflag.o: fdflag.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/fdflag.h ../include/inn/defines.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/fdflag.h \
   ../include/portable/socket.h ../include/portable/getaddrinfo.h \
-  ../include/portable/getnameinfo.h ../include/inn/libinn.h
+  ../include/portable/getnameinfo.h
 fdlimit.o: fdlimit.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 getfqdn.o: getfqdn.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/paths.h
+getmodaddr.o: getmodaddr.c ../include/config.h ../include/inn/defines.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/innconf.h \
+  ../include/inn/defines.h ../include/inn/libinn.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/nntp.h \
   ../include/inn/paths.h
-getmodaddr.o: getmodaddr.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/innconf.h ../include/inn/defines.h \
-  ../include/inn/libinn.h ../include/inn/nntp.h ../include/inn/paths.h
 hash.o: hash.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/md5.h ../include/inn/defines.h \
-  ../include/inn/utility.h ../include/inn/libinn.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/md5.h ../include/inn/defines.h \
+  ../include/inn/utility.h ../include/inn/libinn.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 hashtab.o: hashtab.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/hashtab.h ../include/inn/defines.h \
-  ../include/inn/libinn.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/hashtab.h \
+  ../include/inn/defines.h ../include/inn/libinn.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 headers.o: headers.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 hex.o: hex.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/utility.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/utility.h \
+  ../include/inn/defines.h
 innconf.o: innconf.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/confparse.h ../include/inn/defines.h \
-  ../include/inn/innconf.h ../include/inn/messages.h \
-  ../include/inn/vector.h ../include/inn/libinn.h ../include/inn/paths.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/confparse.h \
+  ../include/inn/defines.h ../include/inn/innconf.h \
+  ../include/inn/messages.h ../include/inn/vector.h \
+  ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \
+  ../include/inn/paths.h
 inndcomm.o: inndcomm.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/portable/time.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/time.h \
   ../include/portable/socket.h ../include/portable/getaddrinfo.h \
   ../include/portable/getnameinfo.h ../include/inn/innconf.h \
   ../include/inn/defines.h ../include/inn/inndcomm.h \
-  ../include/inn/libinn.h ../include/inn/paths.h
+  ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \
+  ../include/inn/paths.h
 list.o: list.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/list.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/list.h ../include/inn/defines.h
 localopen.o: localopen.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/innconf.h ../include/inn/defines.h \
-  ../include/inn/libinn.h ../include/inn/nntp.h ../include/inn/paths.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/innconf.h \
+  ../include/inn/defines.h ../include/inn/libinn.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/nntp.h \
+  ../include/inn/paths.h
 lockfile.o: lockfile.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 makedir.o: makedir.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 md5.o: md5.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/md5.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/md5.h ../include/inn/defines.h
 messageid.o: messageid.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/innconf.h ../include/inn/defines.h \
-  ../include/inn/libinn.h ../include/inn/nntp.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/innconf.h \
+  ../include/inn/defines.h ../include/inn/libinn.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/nntp.h
 messages.o: messages.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/messages.h ../include/inn/defines.h \
-  ../include/inn/libinn.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/messages.h \
+  ../include/inn/xmalloc.h
 mmap.o: mmap.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/portable/mmap.h ../include/inn/messages.h \
-  ../include/inn/defines.h ../include/inn/mmap.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/mmap.h \
+  ../include/inn/messages.h ../include/inn/mmap.h ../include/inn/defines.h
 network.o: network.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/portable/socket.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/socket.h \
   ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \
-  ../include/portable/time.h ../include/portable/wait.h \
-  ../include/inn/innconf.h ../include/inn/defines.h \
+  ../include/inn/fdflag.h ../include/inn/innconf.h \
+  ../include/inn/defines.h ../include/inn/messages.h \
+  ../include/inn/network.h ../include/inn/xmalloc.h \
+  ../include/inn/xwrite.h
+network-innbind.o: network-innbind.c ../include/config.h \
+  ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/socket.h \
+  ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \
+  ../include/portable/wait.h ../include/inn/innconf.h \
+  ../include/inn/defines.h ../include/inn/libinn.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h \
   ../include/inn/messages.h ../include/inn/network.h \
-  ../include/inn/libinn.h
+  ../include/inn/network-innbind.h
 newsuser.o: newsuser.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/innconf.h ../include/inn/defines.h \
-  ../include/inn/messages.h ../include/inn/newsuser.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/innconf.h \
+  ../include/inn/defines.h ../include/inn/messages.h \
+  ../include/inn/newsuser.h
 nntp.o: nntp.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/portable/socket.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/socket.h \
   ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \
   ../include/portable/time.h ../include/inn/buffer.h \
-  ../include/inn/defines.h ../include/inn/innconf.h \
+  ../include/inn/innconf.h ../include/inn/defines.h \
   ../include/inn/network.h ../include/inn/nntp.h ../include/inn/vector.h \
-  ../include/inn/libinn.h
+  ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h
 numbers.o: numbers.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 qio.o: qio.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/qio.h ../include/inn/defines.h \
-  ../include/inn/libinn.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/qio.h ../include/inn/defines.h \
+  ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h
 radix32.o: radix32.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 readin.o: readin.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 remopen.o: remopen.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/portable/socket.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/socket.h \
   ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \
   ../include/inn/innconf.h ../include/inn/defines.h \
-  ../include/inn/libinn.h ../include/inn/network.h ../include/inn/nntp.h
+  ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h \
+  ../include/inn/network.h ../include/inn/nntp.h
 reservedfd.o: reservedfd.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 resource.o: resource.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 sendarticle.o: sendarticle.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h \
-  ../include/inn/nntp.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/nntp.h
 sendpass.o: sendpass.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/innconf.h ../include/inn/defines.h \
-  ../include/inn/libinn.h ../include/inn/nntp.h ../include/inn/paths.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/innconf.h \
+  ../include/inn/defines.h ../include/inn/libinn.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h ../include/inn/nntp.h \
+  ../include/inn/paths.h
 sequence.o: sequence.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/sequence.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/sequence.h \
+  ../include/inn/defines.h
 timer.o: timer.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/portable/time.h ../include/inn/messages.h \
-  ../include/inn/defines.h ../include/inn/timer.h ../include/inn/libinn.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/time.h \
+  ../include/inn/messages.h ../include/inn/timer.h \
+  ../include/inn/defines.h ../include/inn/libinn.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 tst.o: tst.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/tst.h ../include/inn/defines.h \
-  ../include/inn/libinn.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/tst.h ../include/inn/defines.h \
+  ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h
 uwildmat.o: uwildmat.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 vector.o: vector.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/vector.h ../include/inn/defines.h \
-  ../include/inn/libinn.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/vector.h ../include/inn/xmalloc.h
 wire.o: wire.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/wire.h ../include/inn/defines.h \
-  ../include/inn/libinn.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/wire.h ../include/inn/defines.h \
+  ../include/inn/libinn.h ../include/inn/xmalloc.h ../include/inn/xwrite.h
 xfopena.o: xfopena.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h
 xmalloc.o: xmalloc.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/messages.h ../include/inn/defines.h \
-  ../include/inn/libinn.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/messages.h \
+  ../include/inn/xmalloc.h
 xsignal.o: xsignal.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/inn/libinn.h \
-  ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/inn/libinn.h \
+  ../include/inn/defines.h ../include/inn/xmalloc.h \
+  ../include/inn/xwrite.h
 xwrite.o: xwrite.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/uio.h \
+  ../include/inn/xwrite.h
 alloca.o: alloca.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h
 asprintf.o: asprintf.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h
 fseeko.o: fseeko.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h
 ftello.o: ftello.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h
 getaddrinfo.o: getaddrinfo.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/portable/socket.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/socket.h \
   ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h
 getnameinfo.o: getnameinfo.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/portable/socket.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/socket.h \
   ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h
 getpagesize.o: getpagesize.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h
 inet_aton.o: inet_aton.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/portable/socket.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/socket.h \
   ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h
 inet_ntoa.o: inet_ntoa.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/portable/socket.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/socket.h \
   ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h
 inet_ntop.o: inet_ntop.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/portable/socket.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/socket.h \
   ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h
 memcmp.o: memcmp.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h
 mkstemp.o: mkstemp.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/portable/time.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/portable/time.h
 pread.o: pread.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h
 pwrite.o: pwrite.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h
 setenv.o: setenv.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h
 seteuid.o: seteuid.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h
 setproctitle.o: setproctitle.c ../include/config.h \
-  ../include/inn/defines.h ../include/inn/system.h \
+  ../include/inn/defines.h ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
   ../include/inn/options.h ../include/clibrary.h ../include/config.h \
-  ../include/portable/setproctitle.h ../include/inn/messages.h \
-  ../include/inn/defines.h
+  ../include/inn/macros.h ../include/portable/setproctitle.h \
+  ../include/inn/messages.h
 snprintf.o: snprintf.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h
 strcasecmp.o: strcasecmp.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h
 strlcat.o: strlcat.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h
 strlcpy.o: strlcpy.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h
 strspn.o: strspn.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h
 strtok.o: strtok.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h
 symlink.o: symlink.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h
 perl.o: perl.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/portable/macros.h ../include/portable/stdbool.h \
+  ../include/inn/options.h ../include/clibrary.h ../include/config.h \
+  ../include/inn/macros.h ../include/inn/libinn.h ../include/inn/defines.h \
+  ../include/inn/xmalloc.h ../include/inn/xwrite.h \
   ../include/ppport.h ../include/innperl.h

Added: lib/network-innbind.c
===================================================================
--- lib/network-innbind.c	                        (rev 0)
+++ lib/network-innbind.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -0,0 +1,413 @@
+/* $Id$
+ *
+ * Utility functions for network connections using innbind.
+ *
+ * This is a collection of utility functions for network connections and
+ * socket creation, encapsulating some of the complexities of IPv4 and IPv6
+ * support and abstracting operations common to most network code.
+ *
+ * All of the portability difficulties with supporting IPv4 and IPv6 should be
+ * encapsulated in the combination of this code and replacement
+ * implementations for functions that aren't found on some pre-IPv6 systems.
+ * No other part of the source tree should have to care about IPv4 vs. IPv6.
+ *
+ * This file is heavily based on lib/network.c.
+ */
+
+#include "config.h"
+#include "clibrary.h"
+#include "portable/socket.h"
+#include "portable/wait.h"
+
+#include <errno.h>
+#ifdef HAVE_STREAMS_SENDFD
+# include <stropts.h>
+#endif
+
+#include "inn/innconf.h"
+#include "inn/libinn.h"
+#include "inn/messages.h"
+#include "inn/network.h"
+#include "inn/network-innbind.h"
+#include "inn/xmalloc.h"
+
+/* If SO_REUSEADDR isn't available, make calls to set_reuseaddr go away. */
+#ifndef SO_REUSEADDR
+# define network_set_reuseaddr(fd)      /* empty */
+#endif
+
+/* If IPV6_V6ONLY isn't available, make calls to set_v6only go away. */
+#ifndef IPV6_V6ONLY
+# define network_set_v6only(fd)         /* empty */
+#endif
+
+/* If IP_FREEBIND isn't available, make calls to set_freebind go away. */
+#ifndef IP_FREEBIND
+# define network_set_freebind(fd)       /* empty */
+#endif
+
+
+/*
+ * Set SO_REUSEADDR on a socket if possible (so that something new can listen
+ * on the same port immediately if the daemon dies unexpectedly).
+ */
+#ifdef SO_REUSEADDR
+static void
+network_set_reuseaddr(socket_type fd)
+{
+    int flag = 1;
+    const void *flagaddr = &flag;
+
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, flagaddr, sizeof(flag)) < 0)
+        syswarn("cannot mark bind address reusable");
+}
+#endif
+
+
+/*
+ * Set IPV6_V6ONLY on a socket if possible, since the IPv6 behavior is more
+ * consistent and easier to understand.
+ */
+#ifdef IPV6_V6ONLY
+static void
+network_set_v6only(socket_type fd)
+{
+    int flag = 1;
+
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
+        syswarn("cannot set IPv6 socket to v6only");
+}
+#endif
+
+
+/*
+ * Set IP_FREEBIND on a socket if possible, which allows binding servers to
+ * IPv6 addresses that may not have been set up yet.
+ */
+#ifdef IP_FREEBIND
+static void
+network_set_freebind(socket_type fd)
+{
+    int flag = 1;
+
+    if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &flag, sizeof(flag)) < 0)
+        syswarn("cannot set IPv6 socket to free binding");
+}
+#endif
+
+
+/*
+ * Function used as a die handler in child processes to prevent any atexit
+ * functions from being run and any buffers from being flushed twice.
+ */
+static int
+network_child_fatal(void)
+{
+    _exit(1);
+    return 1;
+}
+
+
+/*
+ * Receive a file descriptor from a STREAMS pipe if supported and return the
+ * file descriptor.  If not supported, return INVALID_SOCKET for failure.
+ */
+#ifdef HAVE_STREAMS_SENDFD
+static socket_type
+network_recvfd(int pipe)
+{
+    struct strrecvfd fdrec;
+
+    if (ioctl(pipe, I_RECVFD, &fdrec) < 0) {
+        syswarn("cannot receive file descriptor from innbind");
+        return INVALID_SOCKET;
+    } else
+        return fdrec.fd;
+}
+#else /* !HAVE_STREAMS_SENDFD */
+static socket_type
+network_recvfd(int pipe UNUSED)
+{
+    return INVALID_SOCKET;
+}
+#endif
+
+
+/*
+ * Call innbind to bind a socket to a privileged port.  Takes the file
+ * descriptor, the family, the bind address (as a string), and the port
+ * number.  Returns the bound file descriptor, which may be different than
+ * the provided file descriptor if the system didn't support binding in a
+ * subprocess, or INVALID_SOCKET on error.
+ */
+static socket_type
+network_innbind(int fd, int family, const char *address, unsigned short port)
+{
+    char *path;
+    char buff[128];
+    int pipefds[2];
+    pid_t child, result;
+    int status;
+
+    /* We need innconf in order to find innbind. */
+    if (innconf == NULL || innconf->pathbin == NULL)
+        return INVALID_SOCKET;
+
+    /* Open a pipe to innbind and run it to bind the socket. */
+    if (pipe(pipefds) < 0) {
+        syswarn("cannot create pipe");
+        return INVALID_SOCKET;
+    }
+    path = concatpath(innconf->pathbin, "innbind");
+    snprintf(buff, sizeof(buff), "%d,%d,%s,%hu", fd, family, address, port);
+    child = fork();
+    if (child < 0) {
+        syswarn("cannot fork innbind for %s, port %hu", address, port);
+        return INVALID_SOCKET;
+    } else if (child == 0) {
+        message_fatal_cleanup = network_child_fatal;
+        socket_close(1);
+        if (dup2(pipefds[1], 1) < 0)
+            sysdie("cannot dup pipe to stdout");
+        socket_close(pipefds[0]);
+        if (execl(path, path, buff, (char *) 0) < 0)
+            sysdie("cannot exec innbind for %s, port %hu", address, port);
+    }
+    socket_close(pipefds[1]);
+    free(path);
+
+    /*
+     * Read the results from innbind.  This will either be "ok\n" or "no\n"
+     * followed by an attempt to pass a new file descriptor back.
+     */
+    status = socket_read(pipefds[0], buff, 3);
+    buff[3] = '\0';
+    if (status == 0) {
+        warn("innbind returned no output, assuming failure");
+        fd = INVALID_SOCKET;
+    } else if (status < 0) {
+        syswarn("cannot read from innbind");
+        fd = INVALID_SOCKET;
+    } else if (strcmp(buff, "no\n") == 0) {
+        fd = network_recvfd(pipefds[0]);
+    } else if (strcmp(buff, "ok\n") != 0) {
+        fd = INVALID_SOCKET;
+    }
+
+    /* Wait for the results of the child process. */
+    do {
+        result = waitpid(child, &status, 0);
+    } while (result == -1 && errno == EINTR);
+    if (result != child) {
+        syswarn("cannot wait for innbind for %s, port %hu", address, port);
+        return INVALID_SOCKET;
+    }
+    if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+        return fd;
+    else {
+        warn("innbind failed for %s, port %hu", address, port);
+        return INVALID_SOCKET;
+    }
+}
+
+
+/*
+ * Create an IPv4 socket and bind it, returning the resulting file descriptor
+ * (or INVALID_SOCKET on a failure).
+ */
+socket_type
+network_innbind_ipv4(int type, const char *address, unsigned short port)
+{
+    socket_type fd, bindfd;
+
+    /* Use the generic network function when innbind is not necessary. */
+    if (innconf->port >= 1024 || geteuid() == 0) {
+        return network_bind_ipv4(type, address, port);
+    }
+
+    /* Create the socket. */
+    fd = socket(PF_INET, type, IPPROTO_IP);
+    if (fd == INVALID_SOCKET) {
+        syswarn("cannot create IPv4 socket for %s, port %hu", address, port);
+        return INVALID_SOCKET;
+    }
+    network_set_reuseaddr(fd);
+
+    /* Accept "any" or "all" in the bind address to mean 0.0.0.0. */
+    if (!strcmp(address, "any") || !strcmp(address, "all"))
+        address = "0.0.0.0";
+
+    /* Do the bind. */
+    bindfd = network_innbind(fd, AF_INET, address, port);
+    if (bindfd != fd)
+        socket_close(fd);
+    return bindfd;
+}
+
+
+/*
+ * Create an IPv6 socket and bind it, returning the resulting file descriptor
+ * (or INVALID_SOCKET on a failure).  This socket will be restricted to IPv6
+ * only if possible (as opposed to the standard behavior of binding IPv6
+ * sockets to both IPv6 and IPv4).
+ *
+ * Note that we don't warn (but still return failure) if the reason for the
+ * socket creation failure is that IPv6 isn't supported; this is to handle
+ * systems like many Linux hosts where IPv6 is available in userland but the
+ * kernel doesn't support it.
+ */
+#if HAVE_INET6
+socket_type
+network_innbind_ipv6(int type, const char *address, unsigned short port)
+{
+    socket_type fd, bindfd;
+
+    /* Use the generic network function when innbind is not necessary. */
+    if (innconf->port >= 1024 || geteuid() == 0) {
+        return network_bind_ipv6(type, address, port);
+    }
+
+    /* Create the socket. */
+    fd = socket(PF_INET6, type, IPPROTO_IP);
+    if (fd == INVALID_SOCKET) {
+        if (socket_errno != EAFNOSUPPORT && socket_errno != EPROTONOSUPPORT)
+            syswarn("cannot create IPv6 socket for %s, port %hu", address,
+                    port);
+        return INVALID_SOCKET;
+    }
+    network_set_reuseaddr(fd);
+
+    /*
+     * Restrict the socket to IPv6 only if possible.  The default behavior is
+     * to bind IPv6 sockets to both IPv6 and IPv4 for backward compatibility,
+     * but this causes various other problems (such as with reusing sockets
+     * and requiring handling of mapped addresses).  Continue on if this
+     * fails, however.
+     */
+    network_set_v6only(fd);
+
+    /* Accept "any" or "all" in the bind address to mean ::. */
+    if (!strcmp(address, "any") || !strcmp(address, "all"))
+        address = "::";
+
+    /*
+     * If the address is not ::, use IP_FREEBIND if it's available.  This
+     * allows the network stack to bind to an address that isn't configured.
+     * We lose diagnosis of errors from specifying bind addresses that don't
+     * exist on the system, but we gain the ability to bind to IPv6 addresses
+     * that aren't yet configured.  Since IPv6 address configuration can take
+     * unpredictable amounts of time during system setup, this is more robust.
+     */
+    if (strcmp(address, "::") != 0)
+        network_set_freebind(fd);
+
+    /* Do the bind. */
+    bindfd = network_innbind(fd, AF_INET6, address, port);
+    if (bindfd != fd)
+        socket_close(fd);
+    return bindfd;
+}
+#else /* HAVE_INET6 */
+socket_type
+network_innbind_ipv6(int type UNUSED, const char *address, unsigned short port)
+{
+    warn("cannot bind %s, port %hu: IPv6 not supported", address, port);
+    socket_set_errno(EPROTONOSUPPORT);
+    return INVALID_SOCKET;
+}
+#endif /* HAVE_INET6 */
+
+
+/*
+ * Create and bind sockets for every local address, as determined by
+ * getaddrinfo if IPv6 is available (otherwise, just use the IPv4 loopback
+ * address).  Takes the socket type and port number, and then a pointer to an
+ * array of integers and a pointer to a count of them.  Allocates a new array
+ * to hold the file descriptors and stores the count in the fourth argument.
+ */
+#if HAVE_INET6
+bool
+network_innbind_all(int type, unsigned short port, socket_type **fds,
+                    unsigned int *count)
+{
+    struct addrinfo hints, *addrs, *addr;
+    unsigned int size;
+    int status;
+    socket_type fd;
+    char service[16], name[INET6_ADDRSTRLEN];
+
+    /* Use the generic network function when innbind is not necessary. */
+    if (innconf->port >= 1024 || geteuid() == 0) {
+        return network_bind_all(type, port, fds, count);
+    }
+
+    *count = 0;
+
+    /* Do the query to find all the available addresses. */
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = type;
+    status = snprintf(service, sizeof(service), "%hu", port);
+    if (status < 0 || (size_t) status > sizeof(service)) {
+        warn("cannot convert port %hu to string", port);
+        socket_set_errno_einval();
+        return false;
+    }
+    status = getaddrinfo(NULL, service, &hints, &addrs);
+    if (status < 0) {
+        warn("getaddrinfo for %s failed: %s", service, gai_strerror(status));
+        socket_set_errno_einval();
+        return false;
+    }
+
+    /*
+     * Now, try to bind each of them.  Start the fds array at two entries,
+     * assuming an IPv6 and IPv4 socket, and grow it by two when necessary.
+     */
+    size = 2;
+    *fds = xcalloc(size, sizeof(socket_type));
+    for (addr = addrs; addr != NULL; addr = addr->ai_next) {
+        network_sockaddr_sprint(name, sizeof(name), addr->ai_addr);
+        if (addr->ai_family == AF_INET)
+            fd = network_innbind_ipv4(type, name, port);
+        else if (addr->ai_family == AF_INET6)
+            fd = network_innbind_ipv6(type, name, port);
+        else
+            continue;
+        if (fd != INVALID_SOCKET) {
+            if (*count >= size) {
+                size += 2;
+                *fds = xreallocarray(*fds, size, sizeof(socket_type));
+            }
+            (*fds)[*count] = fd;
+            (*count)++;
+        }
+    }
+    freeaddrinfo(addrs);
+    return (*count > 0);
+}
+#else /* HAVE_INET6 */
+bool
+network_innbind_all(int type, unsigned short port, socket_type **fds,
+                    unsigned int *count)
+{
+    socket_type fd;
+
+    /* Use the generic network function when innbind is not necessary. */
+    if (innconf->port >= 1024 || geteuid() == 0) {
+        return network_bind_all(type, port, fds, count);
+    }
+
+    fd = network_innbind_ipv4(type, "0.0.0.0", port);
+    if (fd == INVALID_SOCKET) {
+        *fds = NULL;
+        *count = 0;
+        return false;
+    }
+    *fds = xmalloc(sizeof(socket_type));
+    *fds[0] = fd;
+    *count = 1;
+    return true;
+}
+#endif /* HAVE_INET6 */


Property changes on: trunk/lib/network-innbind.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision
Added: svn:eol-style
   + native

Modified: lib/network.c
===================================================================
--- lib/network.c	2014-09-15 20:07:40 UTC (rev 9690)
+++ lib/network.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -1,30 +1,69 @@
-/*  $Id$
-**
-**  Utility functions for network connections.
-**
-**  This is a collection of utility functions for network connections and
-**  socket creation, encapsulating some of the complexities of IPv4 and IPv6
-**  support and abstracting operations common to most network code.
-**
-**  All of the portability difficulties with supporting IPv4 and IPv6 are
-**  encapsulated in the combination of this code and innbind.  No other INN
-**  code should have to care whether IPv6 is supported or not.
-*/
+/* $Id$
+ *
+ * Utility functions for network connections.
+ *
+ * This is a collection of utility functions for network connections and
+ * socket creation, encapsulating some of the complexities of IPv4 and IPv6
+ * support and abstracting operations common to most network code.
+ *
+ * All of the portability difficulties with supporting IPv4 and IPv6 should be
+ * encapsulated in the combination of this code and replacement
+ * implementations for functions that aren't found on some pre-IPv6 systems.
+ * No other part of the source tree should have to care about IPv4 vs. IPv6.
+ *
+ * In this file, casts through void * or const void * of struct sockaddr *
+ * parameters are to silence gcc warnings with -Wcast-align.  The specific
+ * address types often require stronger alignment than a struct sockaddr, and
+ * were originally allocated with that alignment.  GCC doesn't have a good way
+ * of knowing that this code is correct.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle at eyrie.org>
+ * Copyright 2014 Russ Allbery <eagle at eyrie.org>
+ * Copyright 2009, 2011, 2012, 2013, 2014
+ *     The Board of Trustees of the Leland Stanford Junior University
+ * Copyright (c) 2004, 2005, 2006, 2007, 2008
+ *     by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ *     2002, 2003 by The Internet Software Consortium and Rich Salz
+ *
+ * This code is derived from software contributed to the Internet Software
+ * Consortium by Rich Salz.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
 
 #include "config.h"
 #include "clibrary.h"
 #include "portable/socket.h"
-#include "portable/time.h"
-#include "portable/wait.h"
+
 #include <errno.h>
-#ifdef HAVE_STREAMS_SENDFD
-# include <stropts.h>
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
 #endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#include <time.h>
 
+#include "inn/fdflag.h"
 #include "inn/innconf.h"
 #include "inn/messages.h"
 #include "inn/network.h"
-#include "inn/libinn.h"
+#include "inn/xmalloc.h"
+#include "inn/xwrite.h"
 
 /* Macros to set the len attribute of sockaddrs. */
 #if HAVE_STRUCT_SOCKADDR_SA_LEN
@@ -40,14 +79,33 @@
 # define network_set_reuseaddr(fd)      /* empty */
 #endif
 
+/* If IPV6_V6ONLY isn't available, make calls to set_v6only go away. */
+#ifndef IPV6_V6ONLY
+# define network_set_v6only(fd)         /* empty */
+#endif
 
+/* If IP_FREEBIND isn't available, make calls to set_freebind go away. */
+#ifndef IP_FREEBIND
+# define network_set_freebind(fd)       /* empty */
+#endif
+
 /*
-**  Set SO_REUSEADDR on a socket if possible (so that something new can listen
-**  on the same port immediately if INN dies unexpectedly).
-*/
+ * Windows requires a different function when sending to sockets, but can't
+ * return short writes on blocking sockets.
+ */
+#ifdef _WIN32
+# define socket_xwrite(fd, b, s)        send((fd), (b), (s), 0)
+#else
+# define socket_xwrite(fd, b, s)        xwrite((fd), (b), (s))
+#endif
+
+/*
+ * Set SO_REUSEADDR on a socket if possible (so that something new can listen
+ * on the same port immediately if the daemon dies unexpectedly).
+ */
 #ifdef SO_REUSEADDR
 static void
-network_set_reuseaddr(int fd)
+network_set_reuseaddr(socket_type fd)
 {
     int flag = 1;
     const void *flagaddr = &flag;
@@ -59,134 +117,53 @@
 
 
 /*
-**  Function used as a die handler in child processes to prevent any atexit
-**  functions from being run and any buffers from being flushed twice.
-*/
-static int
-network_child_fatal(void)
+ * Set IPV6_V6ONLY on a socket if possible, since the IPv6 behavior is more
+ * consistent and easier to understand.
+ */
+#ifdef IPV6_V6ONLY
+static void
+network_set_v6only(socket_type fd)
 {
-    _exit(1);
-    return 1;
-}
+    int flag = 1;
 
-
-/*
-**  Receive a file descriptor from a STREAMS pipe if supported and return the
-**  file descriptor.  If not supported, return -1 for failure.
-*/
-#ifdef HAVE_STREAMS_SENDFD
-static int
-network_recvfd(int pipe)
-{
-    struct strrecvfd fdrec;
-
-    if (ioctl(pipe, I_RECVFD, &fdrec) < 0) {
-        syswarn("cannot receive file descriptor from innbind");
-        return -1;
-    } else
-        return fdrec.fd;
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
+        syswarn("cannot set IPv6 socket to v6only");
 }
-#else /* !HAVE_STREAMS_SENDFD */
-static int
-network_recvfd(int pipe UNUSED)
-{
-    return -1;
-}
 #endif
 
 
 /*
-**  Call innbind to bind a socket to a privileged port.  Takes the file
-**  descriptor, the family, the bind address (as a string), and the port
-**  number.  Returns the bound file descriptor, which may be different than
-**  the provided file descriptor if the system didn't support binding in a
-**  subprocess, or -1 on error.
-*/
-static int
-network_innbind(int fd, int family, const char *address, unsigned short port)
+ * Set IP_FREEBIND on a socket if possible, which allows binding servers to
+ * IPv6 addresses that may not have been set up yet.
+ */
+#ifdef IP_FREEBIND
+static void
+network_set_freebind(socket_type fd)
 {
-    char *path;
-    char buff[128];
-    int pipefds[2];
-    pid_t child, result;
-    int status;
+    int flag = 1;
 
-    /* We need innconf in order to find innbind. */
-    if (innconf == NULL || innconf->pathbin == NULL)
-        return -1;
-
-    /* Open a pipe to innbind and run it to bind the socket. */
-    if (pipe(pipefds) < 0) {
-        syswarn("cannot create pipe");
-        return -1;
-    }
-    path = concatpath(innconf->pathbin, "innbind");
-    snprintf(buff, sizeof(buff), "%d,%d,%s,%hu", fd, family, address, port);
-    child = fork();
-    if (child < 0) {
-        syswarn("cannot fork innbind for %s,%hu", address, port);
-        return -1;
-    } else if (child == 0) {
-        message_fatal_cleanup = network_child_fatal;
-        close(1);
-        if (dup2(pipefds[1], 1) < 0)
-            sysdie("cannot dup pipe to stdout");
-        close(pipefds[0]);
-        if (execl(path, path, buff, (char *) 0) < 0)
-            sysdie("cannot exec innbind for %s,%hu", address, port);
-    }
-    close(pipefds[1]);
-    free(path);
-
-    /* Read the results from innbind.  This will either be ok\n or no\n
-       followed by an attempt to pass a new file descriptor back. */
-    status = read(pipefds[0], buff, 3);
-    buff[3] = '\0';
-    if (status == 0) {
-        warn("innbind returned no output, assuming failure");
-        fd = -1;
-    } else if (status < 0) {
-        syswarn("cannot read from innbind");
-        fd = -1;
-    } else if (strcmp(buff, "no\n") == 0) {
-        fd = network_recvfd(pipefds[0]);
-    } else if (strcmp(buff, "ok\n") != 0) {
-        fd = -1;
-    }
-
-    /* Wait for the results of the child process. */
-    do {
-        result = waitpid(child, &status, 0);
-    } while (result == -1 && errno == EINTR);
-    if (result != child) {
-        syswarn("cannot wait for innbind for %s,%hu", address, port);
-        return -1;
-    }
-    if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
-        return fd;
-    else {
-        warn("innbind failed for %s,%hu", address, port);
-        return -1;
-    }
+    if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &flag, sizeof(flag)) < 0)
+        syswarn("cannot set IPv6 socket to free binding");
 }
+#endif
 
 
 /*
-**  Create an IPv4 socket and bind it, returning the resulting file
-**  descriptor (or -1 on a failure).
-*/
-int
-network_bind_ipv4(const char *address, unsigned short port)
+ * Create an IPv4 socket and bind it, returning the resulting file descriptor
+ * (or INVALID_SOCKET on a failure).
+ */
+socket_type
+network_bind_ipv4(int type, const char *address, unsigned short port)
 {
-    int fd, bindfd;
+    socket_type fd;
     struct sockaddr_in server;
     struct in_addr addr;
 
     /* Create the socket. */
-    fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
-    if (fd < 0) {
-        syswarn("cannot create IPv4 socket for %s,%hu", address, port);
-        return -1;
+    fd = socket(PF_INET, type, IPPROTO_IP);
+    if (fd == INVALID_SOCKET) {
+        syswarn("cannot create IPv4 socket for %s, port %hu", address, port);
+        return INVALID_SOCKET;
     }
     network_set_reuseaddr(fd);
 
@@ -194,214 +171,233 @@
     if (!strcmp(address, "any") || !strcmp(address, "all"))
         address = "0.0.0.0";
 
-    /* Flesh out the socket and do the bind if we can.  Otherwise, call
-       network_innbind to do the work. */
-    if (port < 1024 && geteuid() != 0) {
-        bindfd = network_innbind(fd, AF_INET, address, port);
-        if (bindfd != fd)
-            close(fd);
-        return bindfd;
-    } else {
-        memset(&server, 0, sizeof server);
-        server.sin_family = AF_INET;
-        server.sin_port = htons(port);
-        if (!inet_aton(address, &addr)) {
-            warn("invalid IPv4 address %s", address);
-            return -1;
-        }
-        server.sin_addr = addr;
-        sin_set_length(&server);
-        if (bind(fd, (struct sockaddr *) &server, sizeof(server)) < 0) {
-            syswarn("cannot bind socket for %s,%hu", address, port);
-            return -1;
-        }
-        return fd;
+    /* Flesh out the socket and do the bind. */
+    memset(&server, 0, sizeof(server));
+    server.sin_family = AF_INET;
+    server.sin_port = htons(port);
+    if (!inet_aton(address, &addr)) {
+        warn("invalid IPv4 address %s", address);
+        socket_set_errno_einval();
+        return INVALID_SOCKET;
     }
+    server.sin_addr = addr;
+    sin_set_length(&server);
+    if (bind(fd, (struct sockaddr *) &server, sizeof(server)) < 0) {
+        syswarn("cannot bind socket for %s, port %hu", address, port);
+        socket_close(fd);
+        return INVALID_SOCKET;
+    }
+    return fd;
 }
 
 
 /*
-**  Create an IPv6 socket and bind it, returning the resulting file
-**  descriptor (or -1 on a failure).  Note that we don't warn (but still
-**  return failure) if the reason for the socket creation failure is that IPv6
-**  isn't supported; this is to handle systems like many Linux hosts where
-**  IPv6 is available in userland but the kernel doesn't support it.
-*/
+ * Create an IPv6 socket and bind it, returning the resulting file descriptor
+ * (or INVALID_SOCKET on a failure).  This socket will be restricted to IPv6
+ * only if possible (as opposed to the standard behavior of binding IPv6
+ * sockets to both IPv6 and IPv4).
+ *
+ * Note that we don't warn (but still return failure) if the reason for the
+ * socket creation failure is that IPv6 isn't supported; this is to handle
+ * systems like many Linux hosts where IPv6 is available in userland but the
+ * kernel doesn't support it.
+ */
 #if HAVE_INET6
-int
-network_bind_ipv6(const char *address, unsigned short port)
+
+socket_type
+network_bind_ipv6(int type, const char *address, unsigned short port)
 {
-    int fd, bindfd;
+    socket_type fd;
     struct sockaddr_in6 server;
     struct in6_addr addr;
-#ifdef IPV6_V6ONLY
-    int flag;
-#endif
 
     /* Create the socket. */
-    fd = socket(PF_INET6, SOCK_STREAM, IPPROTO_IP);
-    if (fd < 0) {
-        if (errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
-            syswarn("cannot create IPv6 socket for %s,%hu", address, port);
-        return -1;
+    fd = socket(PF_INET6, type, IPPROTO_IP);
+    if (fd == INVALID_SOCKET) {
+        if (socket_errno != EAFNOSUPPORT && socket_errno != EPROTONOSUPPORT)
+            syswarn("cannot create IPv6 socket for %s, port %hu", address,
+                    port);
+        return INVALID_SOCKET;
     }
     network_set_reuseaddr(fd);
 
-#ifdef IPV6_V6ONLY
-    flag = 1;
-    if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
-        syswarn("cannot set IPv6 socket to v6only");
-#endif
+    /*
+     * Restrict the socket to IPv6 only if possible.  The default behavior is
+     * to bind IPv6 sockets to both IPv6 and IPv4 for backward compatibility,
+     * but this causes various other problems (such as with reusing sockets
+     * and requiring handling of mapped addresses).  Continue on if this
+     * fails, however.
+     */
+    network_set_v6only(fd);
 
     /* Accept "any" or "all" in the bind address to mean ::. */
     if (!strcmp(address, "any") || !strcmp(address, "all"))
         address = "::";
 
-    /* Flesh out the socket and do the bind if we can.  Otherwise, call
-       network_innbind to do the work. */
-    if (port < 1024 && geteuid() != 0) {
-        bindfd = network_innbind(fd, AF_INET6, address, port);
-        if (bindfd != fd)
-            close(fd);
-        return bindfd;
-    } else {
-        memset(&server, 0, sizeof server);
-        server.sin6_family = AF_INET6;
-        server.sin6_port = htons(port);
-        if (inet_pton(AF_INET6, address, &addr) < 1) {
-            warn("invalid IPv6 address %s", address);
-            close(fd);
-            return -1;
-        }
-        server.sin6_addr = addr;
-        sin6_set_length(&server);
-        if (bind(fd, (struct sockaddr *) &server, sizeof(server)) < 0) {
-            syswarn("cannot bind socket for %s,%hu", address, port);
-            close(fd);
-            return -1;
-        }
-        return fd;
+    /*
+     * If the address is not ::, use IP_FREEBIND if it's available.  This
+     * allows the network stack to bind to an address that isn't configured.
+     * We lose diagnosis of errors from specifying bind addresses that don't
+     * exist on the system, but we gain the ability to bind to IPv6 addresses
+     * that aren't yet configured.  Since IPv6 address configuration can take
+     * unpredictable amounts of time during system setup, this is more robust.
+     */
+    if (strcmp(address, "::") != 0)
+        network_set_freebind(fd);
+
+    /* Flesh out the socket and do the bind. */
+    memset(&server, 0, sizeof(server));
+    server.sin6_family = AF_INET6;
+    server.sin6_port = htons(port);
+    if (inet_pton(AF_INET6, address, &addr) < 1) {
+        warn("invalid IPv6 address %s", address);
+        socket_set_errno_einval();
+        return INVALID_SOCKET;
     }
+    server.sin6_addr = addr;
+    sin6_set_length(&server);
+    if (bind(fd, (struct sockaddr *) &server, sizeof(server)) < 0) {
+        syswarn("cannot bind socket for %s, port %hu", address, port);
+        socket_close(fd);
+        return INVALID_SOCKET;
+    }
+    return fd;
 }
+
 #else /* HAVE_INET6 */
-int
-network_bind_ipv6(const char *address, unsigned short port)
+
+socket_type
+network_bind_ipv6(int type UNUSED, const char *address, unsigned short port)
 {
-    warn("cannot bind %s,%hu: not built with IPv6 support", address, port);
-    return -1;
+    warn("cannot bind %s, port %hu: IPv6 not supported", address, port);
+    socket_set_errno(EPROTONOSUPPORT);
+    return INVALID_SOCKET;
 }
+
 #endif /* HAVE_INET6 */
 
 
 /*
-**  Create and bind sockets for every local address, as determined by
-**  getaddrinfo if IPv6 is enabled (otherwise, just uses the wildcard IPv4
-**  address).  Takes the port number, and then a pointer to an array of
-**  integers and a pointer to a count of them.  Allocates a new array to hold
-**  the file descriptors and stores the count in the third argument.
-*/
+ * Create and bind sockets for every local address, as determined by
+ * getaddrinfo if IPv6 is available (otherwise, just use the IPv4 loopback
+ * address).  Takes the socket type and port number, and then a pointer to an
+ * array of integers and a pointer to a count of them.  Allocates a new array
+ * to hold the file descriptors and stores the count in the fourth argument.
+ */
 #if HAVE_INET6
-void
-network_bind_all(unsigned short port, int **fds, unsigned int *count)
+
+bool
+network_bind_all(int type, unsigned short port, socket_type **fds,
+                 unsigned int *count)
 {
     struct addrinfo hints, *addrs, *addr;
     unsigned int size;
-    int error, fd;
+    int status;
+    socket_type fd;
     char service[16], name[INET6_ADDRSTRLEN];
 
     *count = 0;
 
-    /* Start the fds array at two entries, assuming
-       an IPv6 and IPv4 socket, and grow it by two when necessary. */
-    size = 2;
-    *fds = xmalloc(size * sizeof(int));
-#ifdef IPV6_V6ONLY
-    /* Start with an IPv4 socket. */
-    fd = network_bind_ipv4("0.0.0.0", port);
-    if (fd >= 0) {
-        (*fds)[*count] = fd;
-        (*count)++;
-    }
-#endif
-
     /* Do the query to find all the available addresses. */
     memset(&hints, 0, sizeof(hints));
     hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
-#ifdef IPV6_V6ONLY
-    hints.ai_family = AF_INET6;
-#else
     hints.ai_family = AF_UNSPEC;
-#endif
-    hints.ai_socktype = SOCK_STREAM;
-    snprintf(service, sizeof(service), "%hu", port);
-    error = getaddrinfo(NULL, service, &hints, &addrs);
-    if (error < 0) {
-#ifdef IPV6_V6ONLY
-        if (error != EAI_ADDRFAMILY && error != EAI_FAMILY)
-#endif
-            warn("getaddrinfo failed: %s", gai_strerror(error));
-        return;
+    hints.ai_socktype = type;
+    status = snprintf(service, sizeof(service), "%hu", port);
+    if (status < 0 || (size_t) status > sizeof(service)) {
+        warn("cannot convert port %hu to string", port);
+        socket_set_errno_einval();
+        return false;
     }
+    status = getaddrinfo(NULL, service, &hints, &addrs);
+    if (status < 0) {
+        warn("getaddrinfo for %s failed: %s", service, gai_strerror(status));
+        socket_set_errno_einval();
+        return false;
+    }
 
-    /* Now, try to bind each of them. */
+    /*
+     * Now, try to bind each of them.  Start the fds array at two entries,
+     * assuming an IPv6 and IPv4 socket, and grow it by two when necessary.
+     */
+    size = 2;
+    *fds = xcalloc(size, sizeof(socket_type));
     for (addr = addrs; addr != NULL; addr = addr->ai_next) {
         network_sockaddr_sprint(name, sizeof(name), addr->ai_addr);
         if (addr->ai_family == AF_INET)
-            fd = network_bind_ipv4(name, port);
+            fd = network_bind_ipv4(type, name, port);
         else if (addr->ai_family == AF_INET6)
-            fd = network_bind_ipv6(name, port);
+            fd = network_bind_ipv6(type, name, port);
         else
             continue;
-        if (fd >= 0) {
+        if (fd != INVALID_SOCKET) {
             if (*count >= size) {
                 size += 2;
-                *fds = xrealloc(*fds, size * sizeof(int));
+                *fds = xreallocarray(*fds, size, sizeof(socket_type));
             }
             (*fds)[*count] = fd;
             (*count)++;
         }
     }
     freeaddrinfo(addrs);
+    return (*count > 0);
 }
+
 #else /* HAVE_INET6 */
-void
-network_bind_all(unsigned short port, int **fds, unsigned int *count)
+
+bool
+network_bind_all(int type, unsigned short port, socket_type **fds,
+                 unsigned int *count)
 {
-    int fd;
+    socket_type fd;
 
-    fd = network_bind_ipv4("0.0.0.0", port);
-    if (fd >= 0) {
-        *fds = xmalloc(sizeof(int));
-        *fds[0] = fd;
-        *count = 1;
-    } else {
+    fd = network_bind_ipv4(type, "0.0.0.0", port);
+    if (fd == INVALID_SOCKET) {
         *fds = NULL;
         *count = 0;
+        return false;
     }
+    *fds = xmalloc(sizeof(socket_type));
+    *fds[0] = fd;
+    *count = 1;
+    return true;
 }
+
 #endif /* HAVE_INET6 */
 
+
 /*
+ * Free the array of file descriptors allocated by network_bind_all.  This is
+ * a simple wrapper around free, needed on platforms where libraries allocate
+ * memory from a different memory domain than programs (such as Windows).
+ */
+void
+network_bind_all_free(socket_type *fds)
+{
+    free(fds);
+}
+
+
+/*
  * 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.
+ * on any of those sockets and return the file descriptor that selects ready
+ * for read.
  *
- * 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.
+ * This is primarily intended for UDP services listening on multiple file
+ * descriptors, and also provides part of the code for network_accept_any.
+ * TCP services will probably want to use network_accept_any instead.
  *
- * 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.
-*/
+ * Returns the new socket on success or INVALID_SOCKET on failure.  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.
+ *
+ * This is not intended to be a replacement for a full event loop, just some
+ * simple shared code for UDP services.
+ */
 socket_type
-network_accept_any(socket_type fds[], unsigned int count,
-                   struct sockaddr *addr, socklen_t *addrlen)
+network_wait_any(socket_type fds[], unsigned int count)
 {
     fd_set readfds;
     socket_type maxfd, fd;
@@ -424,6 +420,35 @@
             fd = fds[i];
             break;
         }
+    return fd;
+}
+
+
+/*
+ * 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)
+{
+    socket_type fd;
+
+    fd = network_wait_any(fds, count);
     if (fd == INVALID_SOCKET)
         return INVALID_SOCKET;
     else
@@ -432,12 +457,12 @@
 
 
 /*
-**  Binds the given socket to an appropriate source address for its family,
-**  using innconf information or the provided source address.  Returns true on
-**  success and false on failure.
-*/
+ * 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.
+ */
 static bool
-network_source(int fd, int family, const char *source)
+network_source(socket_type fd, int family, const char *source)
 {
     if (source == NULL && innconf == NULL)
         return true;
@@ -449,193 +474,365 @@
         if (source == NULL ||
             strcmp(source, "all") == 0 || strcmp(source, "any") == 0)
               return true;
+
         memset(&saddr, 0, sizeof(saddr));
         saddr.sin_family = AF_INET;
-        if (!inet_aton(source, &saddr.sin_addr))
+        if (!inet_aton(source, &saddr.sin_addr)) {
+            socket_set_errno_einval();
             return false;
+        }
         return bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) == 0;
     }
 #ifdef HAVE_INET6
     else if (family == AF_INET6) {
         struct sockaddr_in6 saddr;
 
+        memset(&saddr, 0, sizeof(saddr));
         if (source == NULL && innconf != NULL)
             source = innconf->sourceaddress6;
         if (source == NULL ||
             strcmp(source, "all") == 0 || strcmp(source, "any") == 0)
               return true;
-        memset(&saddr, 0, sizeof(saddr));
+
         saddr.sin6_family = AF_INET6;
-        if (inet_pton(AF_INET6, source, &saddr.sin6_addr) < 1)
+        if (inet_pton(AF_INET6, source, &saddr.sin6_addr) < 1) {
+            socket_set_errno_einval();
             return false;
+        }
         return bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) == 0;
     }
 #endif
-    else
-        return true;
+    else {
+        socket_set_errno(EAFNOSUPPORT);
+        return false;
+    }
 }
 
 
 /*
-**  Given a linked list of addrinfo structs representing the remote service,
-**  try to create a local socket and connect to that service.  Takes an
-**  optional source address.  Try each address in turn until one of them
-**  connects.  Returns the file descriptor of the open socket on success, or
-**  -1 on failure.  Tries to leave the reason for the failure in errno.
-*/
-int
-network_connect(struct addrinfo *ai, const char *source)
+ * Internal helper function that waits for a non-blocking connect to complete
+ * on a socket.  Takes the file descriptor and the timeout.  Returns 0 on a
+ * successful completion of the connect within the timeout and -1 on failure.
+ * On failure, sets the socket errno.
+ */
+static int
+connect_wait(socket_type fd, time_t timeout)
 {
-    int fd = -1;
-    int oerrno;
-    bool success;
+    int status, err;
+    socklen_t length;
+    struct timeval tv;
+    fd_set set;
 
-    for (success = false; ai != NULL; ai = ai->ai_next) {
-        if (fd >= 0)
-            close(fd);
+    /*
+     * Use select to poll the file descriptor.  Loop if interrupted by a
+     * caught signal.  This means we could wait for longer than the timeout
+     * when interrupted, but there's no good way of recovering the elapsed
+     * time that's worth the hassle.
+     */
+    do {
+        tv.tv_sec = timeout;
+        tv.tv_usec = 0;
+        FD_ZERO(&set);
+        FD_SET(fd, &set);
+        status = select(fd + 1, NULL, &set, NULL, &tv);
+    } while (status < 0 && socket_errno == EINTR);
+
+    /*
+     * If we timed out, set errno appropriately.  If the connection completes,
+     * retrieve the actual status from the socket.
+     */
+    if (status == 0 && !FD_ISSET(fd, &set)) {
+        status = -1;
+        socket_set_errno(ETIMEDOUT);
+    } else if (status > 0 && FD_ISSET(fd, &set)) {
+        length = sizeof(err);
+        status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &length);
+        if (status == 0) {
+            status = (err == 0) ? 0 : -1;
+            socket_set_errno(err);
+        }
+    }
+    return status;
+}
+
+
+/*
+ * Given a linked list of addrinfo structs representing the remote service,
+ * try to create a local socket and connect to that service.  Takes an
+ * optional source address.  Try each address in turn until one of them
+ * connects.  Returns the file descriptor of the open socket on success, or
+ * INVALID_SOCKET on failure.  Tries to leave the reason for the failure in
+ * errno.
+ */
+socket_type
+network_connect(const struct addrinfo *ai, const char *source, time_t timeout)
+{
+    socket_type fd = INVALID_SOCKET;
+    int oerrno, status;
+
+    for (status = -1; status != 0 && ai != NULL; ai = ai->ai_next) {
+        if (fd != INVALID_SOCKET)
+            socket_close(fd);
         fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
-        if (fd < 0)
+        if (fd == INVALID_SOCKET)
             continue;
         if (!network_source(fd, ai->ai_family, source))
             continue;
-        if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0) {
-            success = true;
-            break;
+        if (timeout == 0)
+            status = connect(fd, ai->ai_addr, ai->ai_addrlen);
+        else {
+            fdflag_nonblocking(fd, true);
+            status = connect(fd, ai->ai_addr, ai->ai_addrlen);
+            if (status < 0 && socket_errno == EINPROGRESS)
+                status = connect_wait(fd, timeout);
+            oerrno = socket_errno;
+            fdflag_nonblocking(fd, false);
+            socket_set_errno(oerrno);
         }
     }
-
-#ifdef SO_RCVTIMEO
-    struct timeval connect_timeout;
-    connect_timeout.tv_sec = DEFAULT_TIMEOUT;
-    connect_timeout.tv_usec = 0;
-
-    if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &connect_timeout, sizeof(connect_timeout)) < 0) {
-        syswarn("setsockopt SO_RCVTIMEO %d failed", DEFAULT_TIMEOUT);
-        /* No need to return an error, as failure would just be like before
-         * (no timeout on socket).  Better create the socket anyway! */
-    }
-#endif
-
-    if (success)
+    if (status == 0)
         return fd;
     else {
-        if (fd >= 0) {
-            oerrno = errno;
-            close(fd);
-            errno = oerrno;
+        if (fd != INVALID_SOCKET) {
+            oerrno = socket_errno;
+            socket_close(fd);
+            socket_set_errno(oerrno);
         }
-        return -1;
+        return INVALID_SOCKET;
     }
 }
 
 
 /*
-**  Like network_connect, but takes a host and a port instead of an addrinfo
-**  struct list.  Returns the file descriptor of the open socket on success,
-**  or -1 on failure.  If getaddrinfo fails, errno may not be set to anything
-**  useful.
-*/
-int
+ * Like network_connect, but takes a host and a port instead of an addrinfo
+ * struct list.  Returns the file descriptor of the open socket on success, or
+ * INVALID_SOCKET on failure.  If getaddrinfo fails, errno may not be set to
+ * anything useful.
+ */
+socket_type
 network_connect_host(const char *host, unsigned short port,
-                     const char *source)
+                     const char *source, time_t timeout)
 {
     struct addrinfo hints, *ai;
     char portbuf[16];
-    int fd, oerrno;
+    socket_type fd;
+    int status, oerrno;
 
     memset(&hints, 0, sizeof(hints));
-    hints.ai_family = NETWORK_AF_HINT;
+    hints.ai_family = AF_UNSPEC;
     hints.ai_socktype = SOCK_STREAM;
-    snprintf(portbuf, sizeof(portbuf), "%d", port);
+    status = snprintf(portbuf, sizeof(portbuf), "%hu", port);
+    if (status > 0 && (size_t) status > sizeof(portbuf)) {
+        status = -1;
+        socket_set_errno_einval();
+    }
+    if (status < 0)
+        return INVALID_SOCKET;
     if (getaddrinfo(host, portbuf, &hints, &ai) != 0)
-        return -1;
-    fd = network_connect(ai, source);
-    oerrno = errno;
+        return INVALID_SOCKET;
+    fd = network_connect(ai, source, timeout);
+    oerrno = socket_errno;
     freeaddrinfo(ai);
-    errno = oerrno;
+    socket_set_errno(oerrno);
     return fd;
 }
 
 
 /*
-**  Create a new socket of the specified domain and type and do the binding as
-**  if we were a regular client socket, but then return before connecting.
-**  Returns the file descriptor of the open socket on success, or -1 on
-**  failure.  Intended primarily for the use of clients that will then go on
-**  to do a non-blocking connect.
-*/
-int
+ * Create a new socket of the specified domain and type and do the binding as
+ * if we were a regular client socket, but then return before connecting.
+ * Returns the file descriptor of the open socket on success, or
+ * INVALID_SOCKET on failure.  Intended primarily for the use of clients that
+ * will then go on to do a non-blocking connect.
+ */
+socket_type
 network_client_create(int domain, int type, const char *source)
 {
-    int fd, oerrno;
+    socket_type fd;
+    int oerrno;
 
     fd = socket(domain, type, 0);
-    if (fd < 0)
-        return -1;
+    if (fd == INVALID_SOCKET)
+        return INVALID_SOCKET;
     if (!network_source(fd, domain, source)) {
-        oerrno = errno;
-        close(fd);
-        errno = oerrno;
-        return -1;
+        oerrno = socket_errno;
+        socket_close(fd);
+        socket_set_errno(oerrno);
+        return INVALID_SOCKET;
     }
     return fd;
 }
 
 
 /*
-**  Strip IP options if possible (source routing and similar sorts of things)
-**  just out of paranoia.  This function currently only supports IPv4.  If
-**  anyone knows for sure whether this is necessary for IPv6 as well and knows
-**  how to support it if it is necessary, please submit a patch.  If any
-**  options are found, they're reported using notice.
-**
-**  Based on 4.4BSD rlogind source by way of Wietse Venema (tcp_wrappers),
-**  adapted for INN by smd and reworked some by Russ Allbery.
-*/
-#ifdef IP_OPTIONS
+ * Equivalent to read, but reads all the available data up to the buffer
+ * length, using multiple reads if needed and handling EINTR and EAGAIN.  If
+ * we get EOF before we get enough data, set the socket errno to EPIPE.
+ */
+static ssize_t
+socket_xread(socket_type fd, void *buffer, size_t size)
+{
+    size_t total;
+    ssize_t status;
+    int count = 0;
+
+    /* Abort the read if we try 100 times with no forward progress. */
+    for (total = 0, status = 0; total < size; total += status) {
+        if (++count > 100)
+            break;
+        status = socket_read(fd, (char *) buffer + total, size - total);
+        if (status > 0)
+            count = 0;
+        else if (status == 0)
+            break;
+        else {
+            if ((socket_errno != EINTR) && (socket_errno != EAGAIN))
+                break;
+            status = 0;
+        }
+    }
+    if (status == 0 && total < size)
+        socket_set_errno(EPIPE);
+    return (total < size) ? -1 : (ssize_t) total;
+}
+
+
+/*
+ * Read the specified number of bytes from the network, enforcing a timeout
+ * (in seconds).  We use select to wait for data to become available and then
+ * keep reading until either we time out or we've gotten all the data we're
+ * looking for.  timeout may be 0 to never time out.  Return true on success
+ * and false (setting socket_errno) on failure.
+ */
 bool
-network_kill_options(int fd, struct sockaddr *remote)
+network_read(socket_type fd, void *buffer, size_t total, time_t timeout)
 {
-    int status;
-    char options[BUFSIZ / 3];
-    char addr[INET6_ADDRSTRLEN];
-    socklen_t optsize = sizeof(options);
+    time_t start, now;
+    fd_set set;
+    struct timeval tv;
+    size_t got = 0;
+    ssize_t status;
 
-    if (remote->sa_family != AF_INET)
-        return true;
-    status = getsockopt(fd, IPPROTO_IP, IP_OPTIONS, options, &optsize);
-    if (status == 0 && optsize != 0) {
-        char hex[BUFSIZ];
-        char *opt, *output;
+    /* If there's no timeout, do this the easy way. */
+    if (timeout == 0)
+        return (socket_xread(fd, buffer, total) >= 0);
 
-        output = hex;
-        for (opt = options; optsize > 0; opt++, optsize--, output += 3)
-            snprintf(output, sizeof(hex) - (output - hex), " %2.2x", *opt);
-        network_sockaddr_sprint(addr, sizeof(addr), remote);
-        notice("connect from %s with IP options (ignored):%s", addr, hex);
-        if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, NULL, 0) != 0) {
-            syswarn("setsockopt IP_OPTIONS NULL failed");
+    /*
+     * The hard way.  We try to apply the timeout on the whole read.  If
+     * either select or read fails with EINTR, restart the loop, and rely on
+     * the overall timeout to limit how long we wait without forward
+     * progress.
+     */
+    start = time(NULL);
+    now = start;
+    do {
+        FD_ZERO(&set);
+        FD_SET(fd, &set);
+        tv.tv_sec = timeout - (now - start);
+        if (tv.tv_sec < 1)
+            tv.tv_sec = 1;
+        tv.tv_usec = 0;
+        status = select(fd + 1, &set, NULL, NULL, &tv);
+        if (status < 0) {
+            if (socket_errno == EINTR)
+                continue;
             return false;
+        } else if (status == 0) {
+            socket_set_errno(ETIMEDOUT);
+            return false;
         }
-    }
-    return true;
+        status = socket_read(fd, (char *) buffer + got, total - got);
+        if (status < 0) {
+            if (socket_errno == EINTR)
+                continue;
+            return false;
+        } else if (status == 0) {
+            socket_set_errno(EPIPE);
+            return false;
+        }
+        got += status;
+        if (got == total)
+            return true;
+        now = time(NULL);
+    } while (now - start < timeout);
+    socket_set_errno(ETIMEDOUT);
+    return false;
 }
-#else /* !IP_OPTIONS */
+
+
+/*
+ * Write the specified number of bytes from the network, enforcing a timeout
+ * (in seconds).  We use select to wait for the socket to become available and
+ * then keep reading until either we time out or we've sent all the data.
+ * timeout may be 0 to never time out.  Return true on success and false
+ * (setting socket_errno) on failure.
+ */
 bool
-network_kill_options(int fd UNUSED, struct sockaddr *remote UNUSED)
+network_write(socket_type fd, const void *buffer, size_t total, time_t timeout)
 {
-    return true;
+    time_t start, now;
+    fd_set set;
+    struct timeval tv;
+    size_t sent = 0;
+    ssize_t status;
+    int err;
+
+    /* If there's no timeout, do this the easy way. */
+    if (timeout == 0)
+        return (socket_xwrite(fd, buffer, total) >= 0);
+
+    /* The hard way.  We try to apply the timeout on the whole write.  If
+     * either select or read fails with EINTR, restart the loop, and rely on
+     * the overall timeout to limit how long we wait without forward progress.
+     */
+    fdflag_nonblocking(fd, true);
+    start = time(NULL);
+    now = start;
+    do {
+        FD_ZERO(&set);
+        FD_SET(fd, &set);
+        tv.tv_sec = timeout - (now - start);
+        if (tv.tv_sec < 1)
+            tv.tv_sec = 1;
+        tv.tv_usec = 0;
+        status = select(fd + 1, NULL, &set, NULL, &tv);
+        if (status < 0) {
+            if (socket_errno == EINTR)
+                continue;
+            goto fail;
+        } else if (status == 0) {
+            socket_set_errno(ETIMEDOUT);
+            goto fail;
+        }
+        status = socket_write(fd, (const char *) buffer + sent, total - sent);
+        if (status < 0) {
+            if (socket_errno == EINTR)
+                continue;
+            goto fail;
+        }
+        sent += status;
+        if (sent == total) {
+            fdflag_nonblocking(fd, false);
+            return true;
+        }
+        now = time(NULL);
+    } while (now - start < timeout);
+    socket_set_errno(ETIMEDOUT);
+
+fail:
+    err = socket_errno;
+    fdflag_nonblocking(fd, false);
+    socket_set_errno(err);
+    return false;
 }
-#endif
 
 
 /*
-**  Print an ASCII representation of the address of the given sockaddr into
-**  the provided buffer.  This buffer must hold at least INET_ADDRSTRLEN
-**  characters for IPv4 addresses and INET6_ADDRSTRLEN characters for IPv6, so
-**  generally it should always be as large as the latter.  Returns success or
-**  failure.
-*/
+ * Print an ASCII representation of the address of the given sockaddr into the
+ * provided buffer.  This buffer must hold at least INET_ADDRSTRLEN characters
+ * for IPv4 addresses and INET6_ADDRSTRLEN characters for IPv6, so generally
+ * it should always be as large as the latter.  Returns success or failure.
+ */
 bool
 network_sockaddr_sprint(char *dst, size_t size, const struct sockaddr *addr)
 {
@@ -663,28 +860,34 @@
         result = inet_ntop(AF_INET, &sin->sin_addr, dst, size);
         return (result != NULL);
     } else {
-        errno = EAFNOSUPPORT;
+        socket_set_errno(EAFNOSUPPORT);
         return false;
     }
 }
 
 
 /*
-**  Compare the addresses from two sockaddrs and see whether they're equal.
-**  IPv4 addresses that have been mapped to IPv6 addresses compare equal to
-**  the corresponding IPv4 address.
-*/
+ * Compare the addresses from two sockaddrs and see whether they're equal.
+ * IPv4 addresses that have been mapped to IPv6 addresses compare equal to the
+ * corresponding IPv4 address.
+ */
 bool
 network_sockaddr_equal(const struct sockaddr *a, const struct sockaddr *b)
 {
-    const struct sockaddr_in *a4 = (const struct sockaddr_in *) (const void *) a;
-    const struct sockaddr_in *b4 = (const struct sockaddr_in *) (const void *) b;
-
+    const struct sockaddr_in *a4;
+    const struct sockaddr_in *b4;
 #ifdef HAVE_INET6
-    const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) (const void *) a;
-    const struct sockaddr_in6 *b6 = (const struct sockaddr_in6 *) (const void *) b;
+    const struct sockaddr_in6 *a6;
+    const struct sockaddr_in6 *b6;
     const struct sockaddr *tmp;
+#endif
 
+    a4 = (const struct sockaddr_in *) (const void *) a;
+    b4 = (const struct sockaddr_in *) (const void *) b;
+
+#ifdef HAVE_INET6
+    a6 = (const struct sockaddr_in6 *) (const void *) a;
+    b6 = (const struct sockaddr_in6 *) (const void *) b;
     if (a->sa_family == AF_INET && b->sa_family == AF_INET6) {
         tmp = a;
         a = b;
@@ -715,8 +918,8 @@
 
 
 /*
-**  Returns the port of a sockaddr or 0 on error.
-*/
+ * Returns the port of a sockaddr or 0 on error.
+ */
 unsigned short
 network_sockaddr_port(const struct sockaddr *sa)
 {
@@ -740,11 +943,10 @@
 
 
 /*
-**  Compare two addresses given as strings, applying an optional mask.
-**  Returns true if the addresses are equal modulo the mask and false
-**  otherwise, including on syntax errors in the addresses or mask
-**  specification.
-*/
+ * Compare two addresses given as strings, applying an optional mask.  Returns
+ * true if the addresses are equal modulo the mask and false otherwise,
+ * including on syntax errors in the addresses or mask specification.
+ */
 bool
 network_addr_match(const char *a, const char *b, const char *mask)
 {
@@ -757,10 +959,20 @@
     struct in6_addr a6, b6;
 #endif
 
-    /* If the addresses are IPv4, the mask may be in one of two forms.  It can
-       either be a traditional mask, like 255.255.0.0, or it can be a CIDR
-       subnet designation, like 16.  (The caller should have already removed
-       the slash separating it from the address.) */
+    /*
+     * AIX 7.1 treats the empty string as equivalent to 0.0.0.0 and allows it
+     * to match, but it's too easy to get the empty string from some sort of
+     * syntax error.  Special-case the empty string to always return false.
+     */
+    if (a[0] == '\0' || b[0] == '\0')
+        return false;
+
+    /*
+     * If the addresses are IPv4, the mask may be in one of two forms.  It can
+     * either be a traditional mask, like 255.255.0.0, or it can be a CIDR
+     * subnet designation, like 16.  (The caller should have already removed
+     * the slash separating it from the address.)
+     */
     if (inet_aton(a, &a4) && inet_aton(b, &b4)) {
         if (mask == NULL)
             addr_mask = htonl(0xffffffffUL);
@@ -779,8 +991,10 @@
     }
             
 #ifdef HAVE_INET6
-    /* Otherwise, if the address is IPv6, the mask is required to be a CIDR
-       subnet designation. */
+    /*
+     * Otherwise, if the address is IPv6, the mask is required to be a CIDR
+     * subnet designation.
+     */
     if (!inet_pton(AF_INET6, a, &a6) || !inet_pton(AF_INET6, b, &b6))
         return false;
     if (mask == NULL)

Modified: lib/nntp.c
===================================================================
--- lib/nntp.c	2014-09-15 20:07:40 UTC (rev 9690)
+++ lib/nntp.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -80,8 +80,9 @@
 
 /*
 **  Connect to a remote NNTP server.  Allocates and returns a new nntp struct.
-**  Takes the server name, the port to connect to, and the maximum buffer size
-**  to use.  On failure to connect to the remote host, returns NULL.
+**  Takes the server name, the port to connect to, the maximum buffer size
+**  to use, and the timeout value.  On failure to connect to the remote host,
+**  returns NULL.
 */
 struct nntp *
 nntp_connect(const char *host, unsigned short port, size_t maxsize,
@@ -89,7 +90,7 @@
 {
     int fd;
 
-    fd = network_connect_host(host, port, NULL);
+    fd = network_connect_host(host, port, NULL, timeout);
     if (fd < 0)
         return NULL;
     return nntp_new(fd, fd, maxsize, timeout);

Modified: lib/remopen.c
===================================================================
--- lib/remopen.c	2014-09-15 20:07:40 UTC (rev 9690)
+++ lib/remopen.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -35,7 +35,7 @@
     }
     *buff = '\0';
 
-    fd = network_connect_host(host, port, NULL);
+    fd = network_connect_host(host, port, NULL, DEFAULT_TIMEOUT);
     if (fd < 0)
         return -1;
 

Modified: nnrpd/nnrpd.c
===================================================================
--- nnrpd/nnrpd.c	2014-09-15 20:07:40 UTC (rev 9690)
+++ nnrpd/nnrpd.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -24,6 +24,7 @@
 #include "inn/libinn.h"
 #include "inn/messages.h"
 #include "inn/network.h"
+#include "inn/network-innbind.h"
 #include "inn/newsuser.h"
 #include "inn/ov.h"
 #include "inn/version.h"
@@ -499,7 +500,7 @@
 
     /* Get addresses for this host. */
     memset(&hints, 0, sizeof(hints));
-    hints.ai_family = NETWORK_AF_HINT;
+    hints.ai_family = AF_UNSPEC;
     hints.ai_socktype = SOCK_STREAM;
     ret = getaddrinfo(hostname, NULL, &hints, &ai);
     if (ret != 0) {
@@ -997,7 +998,7 @@
         /* Allocate an lfds array to hold the file descriptors
          * for IPv4 and/or IPv6 connections. */
         if (ListenAddr == NULL && ListenAddr6 == NULL) {
-            network_bind_all(ListenPort, &lfds, &lfdcount);
+            network_innbind_all(SOCK_STREAM, ListenPort, &lfds, &lfdcount);
         } else {
             if (ListenAddr != NULL && ListenAddr6 != NULL)
                 lfdcount = 2;
@@ -1005,10 +1006,14 @@
                 lfdcount = 1;
             lfds = xmalloc(lfdcount * sizeof(int));
             i = 0;
-            if (ListenAddr6 != NULL)
-                lfds[i++] = network_bind_ipv6(ListenAddr6, ListenPort);
-            if (ListenAddr != NULL)
-                lfds[i] = network_bind_ipv4(ListenAddr, ListenPort);
+            if (ListenAddr6 != NULL) {
+                lfds[i++] = network_innbind_ipv6(SOCK_STREAM, ListenAddr6,
+                                                 ListenPort);
+            }
+            if (ListenAddr != NULL) {
+                lfds[i] = network_innbind_ipv4(SOCK_STREAM, ListenAddr,
+                                               ListenPort);
+            }
         }
 
         /* Bail if we couldn't listen on any sockets. */

Modified: support/mkmanifest
===================================================================
--- support/mkmanifest	2014-09-15 20:07:40 UTC (rev 9690)
+++ support/mkmanifest	2014-09-17 16:31:35 UTC (rev 9691)
@@ -281,7 +281,10 @@
 tests/lib/memcmp.t
 tests/lib/messages.t
 tests/lib/mkstemp.t
-tests/lib/network.t
+tests/lib/network/addr-ipv4.t
+tests/lib/network/addr-ipv6.t
+tests/lib/network/client.t
+tests/lib/network/server.t
 tests/lib/pread.t
 tests/lib/pwrite.t
 tests/lib/qio.t

Modified: tests/Makefile
===================================================================
--- tests/Makefile	2014-09-15 20:07:40 UTC (rev 9690)
+++ tests/Makefile	2014-09-17 16:31:35 UTC (rev 9691)
@@ -21,8 +21,10 @@
 	lib/getaddrinfo.t lib/getnameinfo.t lib/hash.t \
 	lib/hashtab.t lib/hex.t lib/inet_aton.t \
 	lib/inet_ntoa.t lib/inet_ntop.t lib/innconf.t lib/list.t lib/md5.t \
-	lib/memcmp.t lib/messages.t lib/mkstemp.t lib/network.t lib/pread.t \
-	lib/pwrite.t lib/qio.t lib/reallocarray.t \
+	lib/memcmp.t lib/messages.t lib/mkstemp.t \
+	lib/network/addr-ipv4.t lib/network/addr-ipv6.t \
+	lib/network/client.t lib/network/server.t \
+	lib/pread.t lib/pwrite.t lib/qio.t lib/reallocarray.t \
 	lib/setenv.t lib/snprintf.t lib/strlcat.t \
 	lib/strlcpy.t lib/tst.t lib/uwildmat.t lib/vector.t lib/wire.t \
 	lib/xwrite.t nnrpd/auth-ext.t overview/api.t overview/buffindexed.t \
@@ -41,8 +43,9 @@
 	$(MAKE) COPT='$(WARNINGS)' build
 
 clean clobber distclean maintclean:
-	rm -f *.o *.lo */*.o */*.lo .pure */.pure $(TESTS) $(EXTRA)
-	rm -rf .libs */.libs
+	rm -f *.o *.lo */*.o */*.lo */*/*.o */*/*.o \
+	  .pure */.pure */*/.pure $(TESTS) $(EXTRA)
+	rm -rf .libs */.libs */*/.libs
 
 $(FIXSCRIPT):
 	@echo Run configure before running make.  See INSTALL for details.
@@ -177,9 +180,20 @@
 lib/mkstemp.t: lib/mkstemp.o lib/mkstemp-t.o tap/basic.o
 	$(LINK) lib/mkstemp.o lib/mkstemp-t.o tap/basic.o $(LIBINN)
 
-lib/network.t: lib/network-t.o tap/basic.o $(LIBINN)
-	$(LINK) lib/network-t.o tap/basic.o $(LIBINN) $(LIBS)
+lib/network/addr-ipv4.t: lib/network/addr-ipv4-t.o tap/basic.o $(LIBINN)
+	$(LINK) lib/network/addr-ipv4-t.o tap/basic.o $(LIBINN) $(LIBS)
 
+lib/network/addr-ipv6.t: lib/network/addr-ipv6-t.o tap/basic.o $(LIBINN)
+	$(LINK) lib/network/addr-ipv6-t.o tap/basic.o $(LIBINN) $(LIBS)
+
+lib/network/client.t: lib/network/client-t.o tap/basic.o tap/messages.o tap/string.o $(LIBINN)
+	$(LINK) lib/network/client-t.o tap/basic.o tap/messages.o \
+	    tap/string.o $(LIBINN) $(LIBS)
+
+lib/network/server.t: lib/network/server-t.o tap/basic.o tap/messages.o tap/string.o $(LIBINN)
+	$(LINK) lib/network/server-t.o tap/basic.o tap/messages.o \
+	    tap/string.o $(LIBINN) $(LIBS)
+
 lib/pread.o: ../lib/pread.c
 	$(CC) $(CFLAGS) -DTESTING -c -o $@ ../lib/pread.c
 

Modified: tests/TESTS
===================================================================
--- tests/TESTS	2014-09-15 20:07:40 UTC (rev 9690)
+++ tests/TESTS	2014-09-17 16:31:35 UTC (rev 9691)
@@ -26,7 +26,10 @@
 lib/memcmp
 lib/messages
 lib/mkstemp
-lib/network
+lib/network/addr-ipv4
+lib/network/addr-ipv6
+lib/network/client
+lib/network/server
 lib/pread
 lib/pwrite
 lib/qio

Modified: tests/authprogs/ident-t.c
===================================================================
--- tests/authprogs/ident-t.c	2014-09-15 20:07:40 UTC (rev 9690)
+++ tests/authprogs/ident-t.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -23,7 +23,7 @@
     int fd;
     char buffer[32];
 
-    fd = network_connect_host(host, 11119, NULL);
+    fd = network_connect_host(host, 11119, NULL, DEFAULT_TIMEOUT);
     if (fd < 0)
         _exit(1);
     read(fd, buffer, sizeof(buffer));
@@ -59,7 +59,7 @@
     snprintf(wanted, sizeof(wanted), "User:%s\r\n", pwd->pw_name);
 
     /* Create the network connection so ident has something to look at. */
-    fd = network_bind_ipv4("127.0.0.1", 11119);
+    fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119);
     if (fd < 0)
         sysdie("cannot bind");
     if (listen(fd, 1) < 0)


Property changes on: trunk/tests/lib
___________________________________________________________________
Modified: svn:ignore
   - .libs
.pure
asprintf.t
buffer.t
concat.t
conffile.t
confparse.t
date.t
dispatch.t
fdflag.t
getaddrinfo.t
getnameinfo.t
hash.t
hashtab.t
hex.t
hstrerror.t
inet_aton.t
inet_ntoa.t
inet_ntop.t
innconf.t
list.t
md5.t
memcmp.t
messages.t
mkstemp.t
network.t
pread.t
pwrite.t
qio.t
reallocarray.t
setenv.t
snprintf.t
strlcat.t
strlcpy.t
tst.t
uwildmat.t
vector.t
wire.t
xmalloc
xwrite.t

   + .libs
.pure
asprintf.t
buffer.t
concat.t
conffile.t
confparse.t
date.t
dispatch.t
fdflag.t
getaddrinfo.t
getnameinfo.t
hash.t
hashtab.t
hex.t
hstrerror.t
inet_aton.t
inet_ntoa.t
inet_ntop.t
innconf.t
list.t
md5.t
memcmp.t
messages.t
mkstemp.t
pread.t
pwrite.t
qio.t
reallocarray.t
setenv.t
snprintf.t
strlcat.t
strlcpy.t
tst.t
uwildmat.t
vector.t
wire.t
xmalloc
xwrite.t



Property changes on: trunk/tests/lib/network
___________________________________________________________________
Added: svn:ignore
   + addr-ipv4.t
addr-ipv6.t
client.t
server.t


Added: tests/lib/network/addr-ipv4-t.c
===================================================================
--- tests/lib/network/addr-ipv4-t.c	                        (rev 0)
+++ tests/lib/network/addr-ipv4-t.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -0,0 +1,130 @@
+/* $Id$
+ *
+ * Test network address functions for IPv4.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle at eyrie.org>
+ * Copyright 2005, 2013 Russ Allbery <eagle at eyrie.org>
+ * Copyright 2009, 2010, 2011, 2012, 2013
+ *     The Board of Trustees of the Leland Stanford Junior University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#define LIBTEST_NEW_FORMAT 1
+
+#include "config.h"
+#include "clibrary.h"
+#include "portable/socket.h"
+
+#include "tap/basic.h"
+#include "inn/network.h"
+
+
+/*
+ * Tests network_addr_compare.  Takes the expected result, the two addresses,
+ * and the mask.
+ */
+static void
+is_addr_compare(bool expected, const char *a, const char *b, const char *mask)
+{
+    const char *smask = (mask == NULL) ? "(null)" : mask;
+
+    if (expected)
+        ok(network_addr_match(a, b, mask), "compare %s %s %s", a, b, smask);
+    else
+        ok(!network_addr_match(a, b, mask), "compare %s %s %s", a, b, smask);
+}
+
+
+int
+main(void)
+{
+    int status;
+    struct addrinfo *ai, *ai2;
+    struct addrinfo hints;
+    char addr[INET6_ADDRSTRLEN];
+    static const char *port = "119";
+
+    /* Set up the plan. */
+    plan(29);
+
+    /* Get a sockaddr to use for subsequent tests. */
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_flags = AI_NUMERICHOST;
+    hints.ai_socktype = SOCK_STREAM;
+    status = getaddrinfo("127.0.0.1", port, &hints, &ai);
+    if (status != 0)
+        bail("getaddrinfo on 127.0.0.1 failed: %s", gai_strerror(status));
+
+    /* Test network_sockaddr_sprint. */
+    ok(network_sockaddr_sprint(addr, sizeof(addr), ai->ai_addr),
+       "sprint of 127.0.0.1");
+    is_string("127.0.0.1", addr, "...with right results");
+
+    /* Test network_sockaddr_port. */
+    is_int(119, network_sockaddr_port(ai->ai_addr), "sockaddr_port");
+
+    /* Test network_sockaddr_equal. */
+    ok(network_sockaddr_equal(ai->ai_addr, ai->ai_addr), "sockaddr_equal");
+    status = getaddrinfo("127.0.0.2", NULL, &hints, &ai2);
+    if (status != 0)
+        bail("getaddrinfo on 127.0.0.2 failed: %s", gai_strerror(status));
+    ok(!network_sockaddr_equal(ai->ai_addr, ai2->ai_addr),
+       "sockaddr_equal of unequal addresses");
+    ok(!network_sockaddr_equal(ai2->ai_addr, ai->ai_addr),
+       "...and the other way around");
+    freeaddrinfo(ai2);
+
+    /* Check the domains of functions and their error handling. */
+    ai->ai_addr->sa_family = AF_UNIX;
+    ok(!network_sockaddr_equal(ai->ai_addr, ai->ai_addr),
+       "network_sockaddr_equal returns false for equal AF_UNIX addresses");
+    is_int(0, network_sockaddr_port(ai->ai_addr),
+           "port meaningless for AF_UNIX");
+    freeaddrinfo(ai);
+
+    /* Tests for network_addr_compare. */
+    is_addr_compare(1, "127.0.0.1", "127.0.0.1",   NULL);
+    is_addr_compare(0, "127.0.0.1", "127.0.0.2",   NULL);
+    is_addr_compare(1, "127.0.0.1", "127.0.0.0",   "31");
+    is_addr_compare(0, "127.0.0.1", "127.0.0.0",   "32");
+    is_addr_compare(0, "127.0.0.1", "127.0.0.0",   "255.255.255.255");
+    is_addr_compare(1, "127.0.0.1", "127.0.0.0",   "255.255.255.254");
+    is_addr_compare(1, "10.10.4.5", "10.10.4.255", "24");
+    is_addr_compare(0, "10.10.4.5", "10.10.4.255", "25");
+    is_addr_compare(1, "10.10.4.5", "10.10.4.255", "255.255.255.0");
+    is_addr_compare(0, "10.10.4.5", "10.10.4.255", "255.255.255.128");
+    is_addr_compare(0, "129.0.0.0", "1.0.0.0",     "1");
+    is_addr_compare(1, "129.0.0.0", "1.0.0.0",     "0");
+    is_addr_compare(1, "129.0.0.0", "1.0.0.0",     "0.0.0.0");
+
+    /* Test some invalid addresses. */
+    is_addr_compare(0, "fred",      "fred",        NULL);
+    is_addr_compare(0, "",          "",            NULL);
+    is_addr_compare(0, "",          "",            "0");
+    is_addr_compare(0, "127.0.0.1", "127.0.0.1",   "pete");
+    is_addr_compare(0, "127.0.0.1", "127.0.0.1",   "1p");
+    is_addr_compare(0, "127.0.0.1", "127.0.0.1",   "1p");
+    is_addr_compare(0, "127.0.0.1", "127.0.0.1",   "-1");
+    is_addr_compare(0, "127.0.0.1", "127.0.0.1",   "33");
+    return 0;
+}


Property changes on: trunk/tests/lib/network/addr-ipv4-t.c
___________________________________________________________________
Added: svn:eol-style
   + native
Added: svn:keywords
   + Author Date Id Revision

Added: tests/lib/network/addr-ipv6-t.c
===================================================================
--- tests/lib/network/addr-ipv6-t.c	                        (rev 0)
+++ tests/lib/network/addr-ipv6-t.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -0,0 +1,149 @@
+/* $Id$
+ *
+ * Test network address functions for IPv6.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle at eyrie.org>
+ * Copyright 2005, 2013 Russ Allbery <eagle at eyrie.org>
+ * Copyright 2009, 2010, 2011, 2012, 2013
+ *     The Board of Trustees of the Leland Stanford Junior University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#define LIBTEST_NEW_FORMAT 1
+
+#include "config.h"
+#include "clibrary.h"
+#include "portable/socket.h"
+
+#include <ctype.h>
+
+#include "tap/basic.h"
+#include "inn/network.h"
+
+
+/*
+ * Tests network_addr_compare.  Takes the expected result, the two addresses,
+ * and the mask.
+ */
+static void
+is_addr_compare(bool expected, const char *a, const char *b, const char *mask)
+{
+    const char *smask = (mask == NULL) ? "(null)" : mask;
+
+    if (expected)
+        ok(network_addr_match(a, b, mask), "compare %s %s %s", a, b, smask);
+    else
+        ok(!network_addr_match(a, b, mask), "compare %s %s %s", a, b, smask);
+}
+
+
+int
+main(void)
+{
+    int status;
+    struct addrinfo *ai4, *ai6;
+    struct addrinfo hints;
+    char addr[INET6_ADDRSTRLEN];
+    char *p;
+    static const char *port = "119";
+    static const char *ipv6_addr = "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210";
+
+#ifndef HAVE_INET6
+    skip_all("IPv6 not supported");
+#endif
+
+    /* Set up the plan. */
+    plan(28);
+
+    /* Get IPv4 and IPv6 sockaddrs to use for subsequent tests. */
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_flags = AI_NUMERICHOST;
+    hints.ai_socktype = SOCK_STREAM;
+    status = getaddrinfo("127.0.0.1", port, &hints, &ai4);
+    if (status != 0)
+        bail("getaddrinfo on 127.0.0.1 failed: %s", gai_strerror(status));
+    status = getaddrinfo(ipv6_addr, port, &hints, &ai6);
+    if (status != 0)
+        bail("getaddr on %s failed: %s", ipv6_addr, gai_strerror(status));
+
+    /* Test network_sockaddr_sprint. */
+    ok(network_sockaddr_sprint(addr, sizeof(addr), ai6->ai_addr),
+       "sprint of IPv6 address");
+    for (p = addr; *p != '\0'; p++)
+        if (islower((unsigned char) *p))
+            *p = toupper((unsigned char) *p);
+    is_string(ipv6_addr, addr, "...with right results");
+
+    /* Test network_sockaddr_port. */
+    is_int(119, network_sockaddr_port(ai6->ai_addr), "sockaddr_port IPv6");
+
+    /* Test network_sockaddr_equal. */
+    ok(network_sockaddr_equal(ai6->ai_addr, ai6->ai_addr),
+       "sockaddr_equal IPv6");
+    ok(!network_sockaddr_equal(ai4->ai_addr, ai6->ai_addr),
+       "...and not equal to IPv4");
+    ok(!network_sockaddr_equal(ai6->ai_addr, ai4->ai_addr),
+       "...other way around");
+    freeaddrinfo(ai6);
+
+    /* Test IPv4 mapped addresses. */
+    status = getaddrinfo("::ffff:7f00:1", NULL, &hints, &ai6);
+    if (status != 0)
+        bail("getaddr on ::ffff:7f00:1 failed: %s", gai_strerror(status));
+    ok(network_sockaddr_sprint(addr, sizeof(addr), ai6->ai_addr),
+       "sprint of IPv4-mapped address");
+    is_string("127.0.0.1", addr, "...with right IPv4 result");
+    ok(network_sockaddr_equal(ai4->ai_addr, ai6->ai_addr),
+       "sockaddr_equal of IPv4-mapped address");
+    ok(network_sockaddr_equal(ai6->ai_addr, ai4->ai_addr),
+       "...and other way around");
+    freeaddrinfo(ai4);
+    status = getaddrinfo("127.0.0.2", NULL, &hints, &ai4);
+    if (status != 0)
+        bail("getaddrinfo on 127.0.0.2 failed: %s", gai_strerror(status));
+    ok(!network_sockaddr_equal(ai4->ai_addr, ai6->ai_addr),
+       "...but not some other address");
+    ok(!network_sockaddr_equal(ai6->ai_addr, ai4->ai_addr),
+       "...and the other way around");
+    freeaddrinfo(ai6);
+    freeaddrinfo(ai4);
+
+    /* Tests for network_addr_compare. */
+    is_addr_compare(1, ipv6_addr,   ipv6_addr,     NULL);
+    is_addr_compare(1, ipv6_addr,   ipv6_addr,     "128");
+    is_addr_compare(1, ipv6_addr,   ipv6_addr,     "60");
+    is_addr_compare(1, "::127",     "0:0::127",    "128");
+    is_addr_compare(1, "::127",     "0:0::128",    "120");
+    is_addr_compare(0, "::127",     "0:0::128",    "128");
+    is_addr_compare(0, "::7fff",    "0:0::8000",   "113");
+    is_addr_compare(1, "::7fff",    "0:0::8000",   "112");
+    is_addr_compare(0, "::3:ffff",  "::2:ffff",    "120");
+    is_addr_compare(0, "::3:ffff",  "::2:ffff",    "119");
+    is_addr_compare(0, "ffff::1",   "7fff::1",     "1");
+    is_addr_compare(1, "ffff::1",   "7fff::1",     "0");
+    is_addr_compare(0, "fffg::1",   "fffg::1",     NULL);
+    is_addr_compare(0, "ffff::1",   "7fff::1",     "-1");
+    is_addr_compare(0, "ffff::1",   "ffff::1",     "-1");
+    is_addr_compare(0, "ffff::1",   "ffff::1",     "129");
+    return 0;
+}


Property changes on: trunk/tests/lib/network/addr-ipv6-t.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision
Added: svn:eol-style
   + native

Added: tests/lib/network/client-t.c
===================================================================
--- tests/lib/network/client-t.c	                        (rev 0)
+++ tests/lib/network/client-t.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -0,0 +1,451 @@
+/* $Id$
+ *
+ * Test suite for network client and read/write functions.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle at eyrie.org>
+ * Copyright 2005, 2013, 2014 Russ Allbery <eagle at eyrie.org>
+ * Copyright 2009, 2010, 2011, 2012, 2013
+ *     The Board of Trustees of the Leland Stanford Junior University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#define LIBTEST_NEW_FORMAT 1
+
+#include "config.h"
+#include "clibrary.h"
+#include "portable/socket.h"
+
+#include <errno.h>
+#include "portable/wait.h"
+#include <signal.h>
+
+#include "tap/basic.h"
+#include "inn/macros.h"
+#include "inn/messages.h"
+#include "inn/network.h"
+
+
+/*
+ * A client writer to test network_client_create.  Connects to IPv4 localhost,
+ * and expects to always succeed on the connection, taking the source address
+ * to pass into network_client_create.
+ */
+static void
+client_create_writer(const char *source)
+{
+    socket_type fd;
+    struct sockaddr_in sin;
+    FILE *out;
+
+    fd = network_client_create(PF_INET, SOCK_STREAM, source);
+    if (fd == INVALID_SOCKET)
+        _exit(1);
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_family = AF_INET;
+    sin.sin_port = htons(11119);
+    sin.sin_addr.s_addr = htonl(0x7f000001UL);
+    if (connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0)
+        _exit(1);
+    out = fdopen(fd, "w");
+    if (out == NULL)
+        _exit(1);
+    fputs("socket test\r\n", out);
+    fclose(out);
+    _exit(0);
+}
+
+
+/*
+ * Used to test network_read.  Connects, sends a couple of strings, then
+ * sleeps for 10 seconds before sending another string so that timeouts can be
+ * tested.  Meant to be run in a child process.
+ */
+static void
+client_delay_writer(const char *host)
+{
+    socket_type fd;
+
+    fd = network_connect_host(host, 11119, NULL, 0);
+    if (fd == INVALID_SOCKET)
+        _exit(1);
+    if (socket_write(fd, "one\n", 4) != 4)
+        _exit(1);
+    if (socket_write(fd, "two\n", 4) != 4)
+        _exit(1);
+    sleep(10);
+    if (socket_write(fd, "three\n", 6) != 6)
+        _exit(1);
+    _exit(0);
+}
+
+
+/*
+ * Used to test network_write.  Connects, reads 64KB from the network, then
+ * sleeps before reading another 64KB.  Meant to be run in a child process.
+ */
+static void
+client_delay_reader(const char *host)
+{
+    char *buffer;
+    socket_type fd;
+
+    fd = network_connect_host(host, 11119, NULL, 0);
+    if (fd == INVALID_SOCKET)
+        _exit(1);
+    buffer = malloc(64 * 1024);
+    if (buffer == NULL)
+        _exit(1);
+    if (!network_read(fd, buffer, 64 * 1024, 0))
+        _exit(1);
+    sleep(10);
+    if (!network_read(fd, buffer, 64 * 1024, 0))
+        _exit(1);
+    free(buffer);
+    _exit(0);
+}
+
+
+/*
+ * When testing the bind (server) functions, we create listening sockets, fork
+ * a child process to connect to it, and accept the connection and read the
+ * data in the server.  The test reporting is therefore done by the listener.
+ * There are two listeners, depending on whether we're listening to a single
+ * socket or an array of sockets, both of which invoke this handler when the
+ * connection is accepted.
+ *
+ * Check that the result of accept, read data from the client, and ensure we
+ * got the expected data, reporting all results through the normal test
+ * reporting mechanism.
+ */
+static void
+test_server_connection(socket_type client)
+{
+    FILE *out;
+    char buffer[512];
+
+    /* Verify that the result of accept is good. */
+    if (client == INVALID_SOCKET) {
+        sysdiag("cannot accept connection from socket");
+        ok_block(2, 0, "...socket read test");
+        return;
+    }
+    ok(1, "...socket accept");
+
+    /* Read data from the client and ensure it matches our expectations. */
+    out = fdopen(client, "r");
+    if (fgets(buffer, sizeof(buffer), out) == NULL) {
+        sysdiag("cannot read from socket");
+        ok(0, "...socket read");
+    }
+    is_string("socket test\r\n", buffer, "...socket read");
+    fclose(out);
+}
+
+
+/*
+ * Test a single listening socket.  Accepts one connection and invokes
+ * test_server_connection.  For skipping purposes, this produces two tests.
+ */
+static void
+test_server_accept(socket_type fd)
+{
+    socket_type client;
+
+    client = accept(fd, NULL, NULL);
+    test_server_connection(client);
+    socket_close(fd);
+}
+
+
+/*
+ * Bring up a server on port 11119 on the loopback address and test connecting
+ * to it via IPv4 using network_client_create.  Takes an optional source
+ * address to use for client connections.
+ */
+static void
+test_create_ipv4(const char *source)
+{
+    socket_type fd;
+    pid_t child;
+    int status;
+
+    /* Create the socket and listen to it. */
+    fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119);
+    if (fd == INVALID_SOCKET)
+        sysbail("cannot create or bind socket");
+    ok(fd != INVALID_SOCKET, "IPv4 network client");
+    if (listen(fd, 1) < 0)
+        sysbail("cannot listen to socket");
+
+    /* Fork off a child that uses network_client_create. */
+    child = fork();
+    if (child < 0)
+        sysbail("cannot fork");
+    else if (child == 0)
+        client_create_writer(source);
+    else {
+        test_server_accept(fd);
+        waitpid(child, &status, 0);
+        is_int(0, status, "client made correct connections");
+    }
+}
+
+
+/*
+ * Test connect timeouts using IPv4.  Bring up a server on port 11119 on the
+ * loopback address and test connections to it.  The server only accepts one
+ * connection at a time, so a subsequent connection will time out.
+ */
+static void
+test_timeout_ipv4(void)
+{
+    socket_type fd, c;
+    pid_t child;
+    socket_type block[20];
+    int i, err;
+
+    /*
+     * Create the listening socket.  We set the listening queue size to 1,
+     * but some operating systems, including Linux, will allow more
+     * connection attempts to succeed than the backlog size.  We'll therefore
+     * have to hammer this server with connections to try to get it to fail.
+     */
+    fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119);
+    if (fd == INVALID_SOCKET)
+        sysbail("cannot create or bind socket");
+    if (listen(fd, 1) < 0)
+        sysbail("cannot listen to socket");
+
+    /* Fork off a child that just runs accept once and then sleeps. */
+    child = fork();
+    if (child < 0)
+        sysbail("cannot fork");
+    else if (child == 0) {
+        alarm(10);
+        c = accept(fd, NULL, 0);
+        if (c == INVALID_SOCKET)
+            _exit(1);
+        sleep(9);
+        _exit(0);
+    }
+
+    /* In the parent.  Open that first connection. */
+    socket_close(fd);
+    c = network_connect_host("127.0.0.1", 11119, NULL, 1);
+    ok(c != INVALID_SOCKET, "Timeout: first connection worked");
+
+    /*
+     * It can take up to fifteen connections on Linux before connections start
+     * actually timing out, and sometimes they never do.
+     */
+    alarm(20);
+    for (i = 0; i < (int) ARRAY_SIZE(block); i++) {
+        block[i] = network_connect_host("127.0.0.1", 11119, NULL, 1);
+        if (block[i] == INVALID_SOCKET)
+            break;
+    }
+    err = socket_errno;
+
+    /*
+     * If we reached the end of the array, we can't force a connection
+     * timeout, so just skip this test.  It's also possible that the
+     * connection will fail with ECONNRESET or ECONNREFUSED if the nine second
+     * sleep in the child passed, so skip in that case as well.  Otherwise,
+     * expect a failure due to timeout in a reasonable amount of time (less
+     * than our 20-second alarm).
+     */
+    if (i == ARRAY_SIZE(block))
+        skip_block(2, "short listen queue does not prevent connections");
+    else {
+        diag("Finally timed out on socket %d", i);
+        ok(block[i] == INVALID_SOCKET, "Later connection timed out");
+        if (err == ECONNRESET || err == ECONNREFUSED)
+            skip("connections rejected without timeout");
+        else
+            is_int(ETIMEDOUT, err, "...with correct error code");
+    }
+    alarm(0);
+
+    /* Shut down the client and clean up resources. */
+    kill(child, SIGTERM);
+    waitpid(child, NULL, 0);
+    socket_close(c);
+    for (i--; i >= 0; i--)
+        if (block[i] != INVALID_SOCKET)
+            socket_close(block[i]);
+    socket_close(fd);
+}
+
+
+/*
+ * Test the network read function with a timeout.  We fork off a child process
+ * that runs delay_writer, and then we read from the network twice, once with
+ * a timeout and once without, and then try a third time when we should time
+ * out.
+ */
+static void
+test_network_read(void)
+{
+    socket_type fd, c;
+    pid_t child;
+    struct sockaddr_in sin;
+    socklen_t slen;
+    char buffer[4];
+
+    /* Create the listening socket. */
+    fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119);
+    if (fd == INVALID_SOCKET)
+        sysbail("cannot create or bind socket");
+    if (listen(fd, 1) < 0)
+        sysbail("cannot listen to socket");
+
+    /* Fork off a child process that writes some data with delays. */
+    child = fork();
+    if (child < 0)
+        sysbail("cannot fork");
+    else if (child == 0) {
+        socket_close(fd);
+        client_delay_writer("127.0.0.1");
+    }
+
+    /* Set an alarm just in case our timeouts don't work. */
+    alarm(10);
+
+    /* Accept the client connection. */
+    slen = sizeof(sin);
+    c = accept(fd, &sin, &slen);
+    if (c == INVALID_SOCKET)
+        sysbail("cannot accept on socket");
+
+    /* Now test a couple of simple reads, with and without timeout. */
+    socket_set_errno(0);
+    ok(network_read(c, buffer, sizeof(buffer), 0), "network_read");
+    ok(memcmp("one\n", buffer, sizeof(buffer)) == 0, "...with good data");
+    ok(network_read(c, buffer, sizeof(buffer), 1),
+       "network_read with timeout");
+    ok(memcmp("two\n", buffer, sizeof(buffer)) == 0, "...with good data");
+
+    /*
+     * The third read should abort with a timeout, since the writer is writing
+     * with a ten second delay.
+     */
+    ok(!network_read(c, buffer, sizeof(buffer), 1),
+       "network_read aborted with timeout");
+    is_int(ETIMEDOUT, socket_errno, "...with correct error");
+    ok(memcmp("two\n", buffer, sizeof(buffer)) == 0, "...and data unchanged");
+    alarm(0);
+
+    /* Clean up. */
+    socket_close(c);
+    kill(child, SIGTERM);
+    waitpid(child, NULL, 0);
+    socket_close(fd);
+}
+
+
+/*
+ * Test the network write function with a timeout.  We fork off a child
+ * process that runs delay_reader, and then we write 64KB to the network in
+ * two chunks, once with a timeout and once without, and then try a third time
+ * when we should time out.
+ */
+static void
+test_network_write(void)
+{
+    socket_type fd, c;
+    pid_t child;
+    struct sockaddr_in sin;
+    socklen_t slen;
+    char *buffer;
+
+    /* Create the data that we're going to send. */
+    buffer = bmalloc(8192 * 1024);
+    memset(buffer, 'a', 8192 * 1024);
+
+    /* Create the listening socket. */
+    fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119);
+    if (fd == INVALID_SOCKET)
+        sysbail("cannot create or bind socket");
+    if (listen(fd, 1) < 0)
+        sysbail("cannot listen to socket");
+
+    /* Create the child, which will connect and then read data with delay. */
+    child = fork();
+    if (child < 0)
+        sysbail("cannot fork");
+    else if (child == 0) {
+        socket_close(fd);
+        client_delay_reader("127.0.0.1");
+    }
+
+    /* Set an alarm just in case our timeouts don't work. */
+    alarm(10);
+
+    /* Accept the client connection. */
+    slen = sizeof(struct sockaddr_in);
+    c = accept(fd, &sin, &slen);
+    if (c == INVALID_SOCKET)
+        sysbail("cannot accept on socket");
+
+    /* Test some successful writes with and without a timeout. */
+    socket_set_errno(0);
+    ok(network_write(c, buffer, 32 * 1024, 0), "network_write");
+    ok(network_write(c, buffer, 32 * 1024, 1),
+       "network_write with timeout");
+
+    /*
+     * A longer write cannot be completely absorbed before the client sleep,
+     * so should fail with a timeout.
+     */
+    ok(!network_write(c, buffer, 8192 * 1024, 1),
+       "network_write aborted with timeout");
+    is_int(ETIMEDOUT, socket_errno, "...with correct error");
+    alarm(0);
+
+    /* Clean up. */
+    socket_close(c);
+    kill(child, SIGTERM);
+    waitpid(child, NULL, 0);
+    socket_close(fd);
+    free(buffer);
+}
+
+
+int
+main(void)
+{
+    /* Set up the plan. */
+    plan(22);
+
+    /* Test network_client_create. */
+    test_create_ipv4(NULL);
+    test_create_ipv4("127.0.0.1");
+
+    /* Test network_connect with a timeout. */
+    test_timeout_ipv4();
+
+    /* Test network_read and network_write. */
+    test_network_read();
+    test_network_write();
+    return 0;
+}


Property changes on: trunk/tests/lib/network/client-t.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision
Added: svn:eol-style
   + native

Added: tests/lib/network/server-t.c
===================================================================
--- tests/lib/network/server-t.c	                        (rev 0)
+++ tests/lib/network/server-t.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -0,0 +1,532 @@
+/* $Id$
+ *
+ * Test suite for network server functions.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle at eyrie.org>
+ * Copyright 2005, 2013 Russ Allbery <eagle at eyrie.org>
+ * Copyright 2009, 2010, 2011, 2012, 2013
+ *     The Board of Trustees of the Leland Stanford Junior University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#define LIBTEST_NEW_FORMAT 1
+
+#include "config.h"
+#include "clibrary.h"
+#include "portable/socket.h"
+
+#include <errno.h>
+#include "portable/wait.h"
+#include <signal.h>
+
+#include "tap/basic.h"
+#include "inn/macros.h"
+#include "inn/messages.h"
+#include "inn/network.h"
+
+
+/*
+ * Check whether IPv6 actually works.  On some systems (such as Solaris 8
+ * without IPv6 configured), it's possible to create an IPv6 socket, but
+ * binding the socket will fail.  We therefore attempt an IPv6 socket creation
+ * and, if it fails, check errno for several errors that indicate that IPv6
+ * is supported but doesn't work.  This will also handle the case where IPv6
+ * support is not configured, since network_bind_ipv6 will return
+ * INVALID_SOCKET.
+ */
+static bool
+ipv6_works(void)
+{
+    socket_type fd;
+
+    /* Create the socket.  If this works, ipv6 is supported. */
+    fd = network_bind_ipv6(SOCK_STREAM, "::1", 11119);
+    if (fd != INVALID_SOCKET) {
+        close(fd);
+        return true;
+    }
+
+    /* IPv6 not recognized, indicating no support. */
+    if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT)
+        return false;
+
+    /* IPv6 is recognized but we can't actually use it. */
+    if (errno == EADDRNOTAVAIL)
+        return false;
+
+    /*
+     * Some other error.  Assume it's not related to IPv6.  We'll probably
+     * fail later.
+     */
+    return true;
+}
+
+
+/*
+ * A client writer used to generate data for a server test.  Connect to the
+ * given host on port 11119 and send a constant string to a socket.  Takes the
+ * source address as well to pass into network_connect_host.  If the flag is
+ * true, expects to succeed in connecting; otherwise, fail the test (by
+ * exiting with a non-zero status) if the connection is successful.
+ *
+ * If the succeed argument is true, this is guarateed to never return.
+ */
+static void
+client_writer(const char *host, const char *source, bool succeed)
+{
+    socket_type fd;
+    FILE *out;
+
+    fd = network_connect_host(host, 11119, source, 0);
+    if (fd == INVALID_SOCKET) {
+        if (succeed)
+            _exit(1);
+        else
+            return;
+    }
+    out = fdopen(fd, "w");
+    if (out == NULL)
+        sysdie("fdopen failed");
+    fputs("socket test\r\n", out);
+    fclose(out);
+    _exit(succeed ? 0 : 1);
+}
+
+
+/*
+ * A client writer for testing UDP.  Sends a UDP packet to port 11119 on
+ * localhost, from the given source address, containing a constant string.
+ * This also verifies that network_client_create works properly.
+ */
+static void
+client_udp_writer(const char *source)
+{
+    socket_type fd;
+    struct sockaddr_in sin;
+
+    /* Create and bind the socket. */
+    fd = network_client_create(PF_INET, SOCK_DGRAM, source);
+    if (fd == INVALID_SOCKET)
+        _exit(1);
+
+    /* Connect to localhost port 11119. */
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_family = AF_INET;
+    sin.sin_port = htons(11119);
+    sin.sin_addr.s_addr = htonl(0x7f000001UL);
+    if (connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0)
+        _exit(1);
+
+    /* Send our fixed UDP packet. */
+    if (send(fd, "socket test\r\n", 13, 0) < 13)
+        _exit(1);
+    _exit(0);
+}
+
+
+/*
+ * When testing the bind (server) functions, we create listening sockets, fork
+ * a child process to connect to it, and accept the connection and read the
+ * data in the server.  The test reporting is therefore done by the listener.
+ * There are two listeners, depending on whether we're listening to a single
+ * socket or an array of sockets, both of which invoke this handler when the
+ * connection is accepted.
+ *
+ * Check that the result of accept, read data from the client, and ensure we
+ * got the expected data, reporting all results through the normal test
+ * reporting mechanism.
+ */
+static void
+test_server_connection(socket_type client)
+{
+    FILE *out;
+    char buffer[512];
+
+    /* Verify that the result of accept is good. */
+    if (client == INVALID_SOCKET) {
+        sysdiag("cannot accept connection from socket");
+        ok_block(2, 0, "...socket read test");
+        return;
+    }
+    ok(1, "...socket accept");
+
+    /* Read data from the client and ensure it matches our expectations. */
+    out = fdopen(client, "r");
+    if (fgets(buffer, sizeof(buffer), out) == NULL) {
+        sysdiag("cannot read from socket");
+        ok(0, "...socket read");
+    }
+    is_string("socket test\r\n", buffer, "...socket read");
+    fclose(out);
+}
+
+
+/*
+ * Test a single listening socket.  Accepts one connection and invokes
+ * test_server_connection.  For skipping purposes, this produces two tests.
+ */
+static void
+test_server_accept(socket_type fd)
+{
+    socket_type client;
+
+    client = accept(fd, NULL, NULL);
+    test_server_connection(client);
+    socket_close(fd);
+}
+
+
+/*
+ * A varient version of the server portion of the test.  Takes an array of
+ * sockets and the size of the sockets and accepts a connection on any of
+ * those sockets.  Ensures that the client address information is stored
+ * correctly by checking that it is set to IPv4 localhost.  For skipping
+ * purposes, this produces four tests.
+ *
+ * saddr is allocated from the heap instead of using a local struct
+ * sockaddr_storage to work around a misdiagnosis of strict aliasing
+ * violations from gcc 4.4 (fixed in later versions).
+ */
+static void
+test_server_accept_any(socket_type fds[], unsigned int count)
+{
+    socket_type client;
+    unsigned int i;
+    struct sockaddr *saddr;
+    socklen_t slen;
+
+    slen = sizeof(struct sockaddr_storage);
+    saddr = bcalloc(1, slen);
+    client = network_accept_any(fds, count, saddr, &slen);
+    test_server_connection(client);
+    is_int(AF_INET, saddr->sa_family, "...address family is IPv4");
+    is_int(htonl(0x7f000001UL),
+           ((struct sockaddr_in *) (void *) saddr)->sin_addr.s_addr,
+           "...and client address is 127.0.0.1");
+    free(saddr);
+    for (i = 0; i < count; i++)
+        socket_close(fds[i]);
+}
+
+
+/*
+ * Bring up a server on port 11119 on the loopback address and test connecting
+ * to it via IPv4.  Takes an optional source address to use for client
+ * connections.  For skipping purposes, this produces four tests.
+ */
+static void
+test_ipv4(const char *source)
+{
+    socket_type fd;
+    pid_t child;
+    int status;
+
+    /* Set up the server socket. */
+    fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119);
+    if (fd == INVALID_SOCKET)
+        sysbail("cannot create or bind socket");
+    ok(fd != INVALID_SOCKET, "IPv4 server test");
+    if (listen(fd, 1) < 0)
+        sysbail("cannot listen to socket");
+
+    /* Fork off a child writer and test the server accept. */
+    child = fork();
+    if (child < 0)
+        sysbail("cannot fork");
+    else if (child == 0) {
+        socket_close(fd);
+        client_writer("127.0.0.1", source, true);
+    } else {
+        test_server_accept(fd);
+        waitpid(child, &status, 0);
+        is_int(0, status, "client made correct connections");
+    }
+}
+
+
+/*
+ * Bring up a server on port 11119 on the loopback address and test connecting
+ * to it via IPv6.  Takes an optional source address to use for client
+ * connections.  For skipping purposes, this produces four tests.
+ */
+static void
+test_ipv6(const char *source)
+{
+    socket_type fd;
+    pid_t child;
+    int status;
+
+    /* Set up the server socket. */
+    fd = network_bind_ipv6(SOCK_STREAM, "::1", 11119);
+    if (fd == INVALID_SOCKET)
+        sysbail("cannot create socket");
+    ok(fd != INVALID_SOCKET, "IPv6 server test");
+    if (listen(fd, 1) < 0)
+        sysbail("cannot listen to socket");
+
+    /*
+     * Fork off a child writer and test the server accept.  If IPV6_V6ONLY is
+     * supported, we can also check that connecting to 127.0.0.1 will fail.
+     */
+    child = fork();
+    if (child < 0)
+        sysbail("cannot fork");
+    else if (child == 0) {
+        socket_close(fd);
+#ifdef IPV6_V6ONLY
+        client_writer("127.0.0.1", NULL, false);
+#endif
+        client_writer("::1", source, true);
+    } else {
+        test_server_accept(fd);
+        waitpid(child, &status, 0);
+        is_int(0, status, "client made correct connections");
+    }
+}
+
+
+/*
+ * Returns the struct sockaddr * corresponding to a local socket.  Handles the
+ * initial allocation being too small and dynamically increasing it.  Caller
+ * is responsible for freeing the allocated sockaddr.
+ */
+static struct sockaddr *
+get_sockaddr(socket_type fd)
+{
+    struct sockaddr *saddr;
+    socklen_t size;
+
+    saddr = bmalloc(sizeof(struct sockaddr_storage));
+    size = sizeof(struct sockaddr_storage);
+    if (getsockname(fd, saddr, &size) < 0)
+        sysbail("cannot getsockname");
+    if (size > sizeof(struct sockaddr)) {
+        free(saddr);
+        saddr = bmalloc(size);
+        if (getsockname(fd, saddr, &size) < 0)
+            sysbail("cannot getsockname");
+    }
+    return saddr;
+}
+
+
+/*
+ * Bring up a server on port 11119 on all addresses and try connecting to it
+ * via all of the available protocols.  Takes an optional source address to
+ * use for client connections.  For skipping purposes, this produces eight
+ * tests.
+ */
+static void
+test_all(const char *source_ipv4, const char *source_ipv6 UNUSED)
+{
+    socket_type *fds, fd;
+    unsigned int count, i;
+    pid_t child;
+    struct sockaddr *saddr;
+    int status;
+
+    /* Bind sockets for all available local addresses. */
+    if (!network_bind_all(SOCK_STREAM, 11119, &fds, &count))
+        sysbail("cannot create or bind socket");
+
+    /*
+     * There should be at most two, one for IPv4 and one for IPv6, but allow
+     * for possible future weirdness in networking.
+     */
+    if (count > 2) {
+        diag("got more than two sockets, using just the first two");
+        count = 2;
+    }
+
+    /* We'll test each socket in turn by listening and trying to connect. */
+    for (i = 0; i < count; i++) {
+        fd = fds[i];
+        if (listen(fd, 1) < 0)
+            sysbail("cannot listen to socket %d", fd);
+        ok(fd != INVALID_SOCKET, "all address server test (part %d)", i + 1);
+
+        /* Get the socket type to determine what type of client to run. */
+        saddr = get_sockaddr(fd);
+
+        /*
+         * Fork off a child writer and test the server accept.  If IPV6_V6ONLY
+         * is supported, we can also check that connecting to 127.0.0.1 will
+         * fail.
+         */
+        child = fork();
+        if (child < 0)
+            sysbail("cannot fork");
+        else if (child == 0) {
+            if (saddr->sa_family == AF_INET) {
+                client_writer("::1", source_ipv6, false);
+                client_writer("127.0.0.1", source_ipv4, true);
+#ifdef HAVE_INET6
+            } else if (saddr->sa_family == AF_INET6) {
+# ifdef IPV6_V6ONLY
+                client_writer("127.0.0.1", source_ipv4, false);
+# endif
+                client_writer("::1", source_ipv6, true);
+#endif
+            }
+        } else {
+            test_server_accept(fd);
+            waitpid(child, &status, 0);
+            is_int(0, status, "client made correct connections");
+        }
+        free(saddr);
+    }
+    network_bind_all_free(fds);
+
+    /* If we only got one listening socket, skip for consistent test count. */
+    if (count == 1)
+        skip_block(4, "only one listening socket");
+}
+
+
+/*
+ * Bring up a server on port 11119 on all addresses and try connecting to it
+ * via 127.0.0.1, using network_accept_any underneath.  For skipping purposes,
+ * this runs three tests.
+ */
+static void
+test_any(void)
+{
+    socket_type *fds;
+    unsigned int count, i;
+    pid_t child;
+    int status;
+
+    if (!network_bind_all(SOCK_STREAM, 11119, &fds, &count))
+        sysbail("cannot create or bind socket");
+    for (i = 0; i < count; i++)
+        if (listen(fds[i], 1) < 0)
+            sysbail("cannot listen to socket %d", fds[i]);
+    child = fork();
+    if (child < 0)
+        sysbail("cannot fork");
+    else if (child == 0)
+        client_writer("127.0.0.1", NULL, true);
+    else {
+        test_server_accept_any(fds, count);
+        waitpid(child, &status, 0);
+        is_int(0, status, "client made correct connections");
+    }
+    network_bind_all_free(fds);
+}
+
+
+/*
+ * Bring up a UDP server on port 11119 on all addresses and try connecting to
+ * it via 127.0.0.1, using network_wait_any underneath.  This tests the bind
+ * functions for UDP sockets, network_client_create for UDP addresses, and
+ * network_wait_any.
+ */
+static void
+test_any_udp(void)
+{
+    socket_type *fds, fd;
+    unsigned int count, i;
+    pid_t child;
+    char buffer[BUFSIZ];
+    ssize_t length;
+    int status;
+    struct sockaddr_storage addr;
+    struct sockaddr *saddr;
+    struct sockaddr_in sin;
+    socklen_t addrlen;
+
+    /* Bind our UDP socket. */
+    if (!network_bind_all(SOCK_DGRAM, 11119, &fds, &count))
+        sysbail("cannot create or bind socket");
+
+    /* Create a child that writes a single UDP packet to the server. */
+    child = fork();
+    if (child < 0)
+        sysbail("cannot fork");
+    else if (child == 0)
+        client_udp_writer("127.0.0.1");
+
+    /* Set an alarm, since if the client malfunctions, nothing happens. */
+    alarm(5);
+
+    /* Wait for the UDP packet and then read and confirm it. */
+    fd = network_wait_any(fds, count);
+    ok(fd != INVALID_SOCKET, "network_wait_any found UDP message");
+    if (fd == INVALID_SOCKET)
+        ok_block(3, false, "could not accept client");
+    else {
+        saddr = (struct sockaddr *) &addr;
+        addrlen = sizeof(addr);
+        length = recvfrom(fd, buffer, sizeof(buffer), 0, saddr, &addrlen);
+        is_int(13, length, "...of correct length");
+        sin.sin_family = AF_INET;
+        sin.sin_port = htons(11119);
+        sin.sin_addr.s_addr = htonl(0x7f000001UL);
+        ok(network_sockaddr_equal((struct sockaddr *) &sin, saddr),
+           "...from correct address");
+        buffer[13] = '\0';
+        is_string("socket test\r\n", buffer, "...and correct contents");
+    }
+
+    /* Wait for the child and be sure it exited successfully. */
+    waitpid(child, &status, 0);
+    is_int(0, status, "client made correct connections");
+
+    /* Clean up. */
+    for (i = 0; i < count; i++)
+        socket_close(fds[i]);
+    network_bind_all_free(fds);
+}
+
+
+int
+main(void)
+{
+    /* Set up the plan. */
+    plan(42);
+
+    /* Test network_bind functions. */
+    test_ipv4(NULL);
+    test_ipv4("127.0.0.1");
+
+    /*
+     * Optionally test IPv6 support.  If IPv6 support appears to be available
+     * but doesn't work, we have to explicitly skip test_all, since it will
+     * create a socket that we then can't connect to.
+     */
+    if (ipv6_works()) {
+        test_ipv6(NULL);
+        test_ipv6("::1");
+        test_all(NULL, NULL);
+        test_all("127.0.0.1", "::1");
+    } else {
+        skip_block(24, "IPv6 not configured");
+    }
+
+    /* Test network_accept_any. */
+    test_any();
+
+    /* Test UDP socket handling and network_wait_any. */
+    test_any_udp();
+    return 0;
+}


Property changes on: trunk/tests/lib/network/server-t.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision
Added: svn:eol-style
   + native

Deleted: tests/lib/network-t.c
===================================================================
--- tests/lib/network-t.c	2014-09-15 20:07:40 UTC (rev 9690)
+++ tests/lib/network-t.c	2014-09-17 16:31:35 UTC (rev 9691)
@@ -1,566 +0,0 @@
-/*
- * network test suite.
- *
- * $Id$
- *
- * The canonical version of this file is maintained in the rra-c-util package,
- * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
- *
- * Written by Russ Allbery <rra at stanford.edu>
- * Copyright 2005 Russ Allbery <rra at stanford.edu>
- * Copyright 2009, 2010, 2011
- *     The Board of Trustees of the Leland Stanford Junior University
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-#define LIBTEST_NEW_FORMAT 1
-
-#include "config.h"
-#include "clibrary.h"
-#include "portable/socket.h"
-#include "portable/wait.h"
-#include <ctype.h>
-#include <errno.h>
-
-#include "inn/innconf.h"
-#include "inn/messages.h"
-#include "inn/network.h"
-#include "inn/libinn.h"
-#include "tap/basic.h"
-
-/* Set this globally to 0 if IPv6 is available but doesn't work. */
-static int ipv6 = 1;
-
-/*
- * The core of the listener.  Takes the return value of accept and handles our
- * test protocol.
- */
-static void
-listener_handler(socket_type client)
-{
-    FILE *out;
-    char buffer[512];
-
-    if (client == INVALID_SOCKET) {
-        sysdiag("cannot accept connection from socket");
-        ok_block(2, 0, "...socket read test");
-        return;
-    }
-    ok(1, "...socket accept");
-    out = fdopen(client, "r");
-    if (fgets(buffer, sizeof(buffer), out) == NULL) {
-        sysdiag("cannot read from socket");
-        ok(0, "...socket read");
-    }
-    is_string("socket test\r\n", buffer, "...socket read");
-    fclose(out);
-}
-
-
-/*
- * The server portion of the test.  Listens to a socket and accepts a
- * connection, making sure what is printed on that connection matches what the
- * client is supposed to print.
- */
-static void
-listener(socket_type fd)
-{
-    socket_type client;
-
-    client = accept(fd, NULL, NULL);
-    listener_handler(client);
-    close(fd);
-}
-
-
-/*
- * A varient version of the server portion of the test.  Takes an array of
- * sockets and the size of the sockets and accepts a connection on any of
- * those sockets.
- *
- * saddr is allocated from the heap instead of using a local struct
- * sockaddr_storage to work around a misdiagnosis of strict aliasing
- * violations from gcc 4.4 (fixed in later versions).
- */
-static void
-listener_any(socket_type fds[], unsigned int count)
-{
-    socket_type client;
-    unsigned int i;
-    struct sockaddr *saddr;
-    socklen_t slen;
-
-    slen = sizeof(struct sockaddr_storage);
-    saddr = xmalloc(slen);
-    client = network_accept_any(fds, count, saddr, &slen);
-    listener_handler(client);
-    is_int(AF_INET, saddr->sa_family, "...address family is IPv4");
-    is_int(htonl(0x7f000001UL),
-           ((struct sockaddr_in *) (void *) saddr)->sin_addr.s_addr,
-           "...and client address is 127.0.0.1");
-    free(saddr);
-    for (i = 0; i < count; i++)
-        close(fds[i]);
-}
-
-
-/*
- * Connect to the given host on port 11119 and send a constant string to a
- * socket, used to do the client side of the testing.  Takes the source
- * address as well to pass into network_connect_host.
- */
-static void
-client(const char *host, const char *source)
-{
-    socket_type fd;
-    FILE *out;
-
-    fd = network_connect_host(host, 11119, source);
-    if (fd == INVALID_SOCKET)
-        sysdie("connect failed");
-    out = fdopen(fd, "w");
-    if (out == NULL)
-        sysdie("fdopen failed");
-    fputs("socket test\r\n", out);
-    fclose(out);
-    _exit(0);
-}
-
-
-/*
- * Bring up a server on port 11119 on the loopback address and test connecting
- * to it via IPv4.  Takes an optional source address to use for client
- * connections.
- */
-static void
-test_ipv4(const char *source)
-{
-    socket_type fd;
-    pid_t child;
-
-    fd = network_bind_ipv4("127.0.0.1", 11119);
-    if (fd == INVALID_SOCKET)
-        sysbail("cannot create or bind socket");
-    if (listen(fd, 1) < 0) {
-        sysdiag("cannot listen to socket");
-        ok_block(3, 0, "IPv4 server test");
-    } else {
-        ok(1, "IPv4 server test");
-        child = fork();
-        if (child < 0)
-            sysbail("cannot fork");
-        else if (child == 0) {
-            close(fd);
-            client("127.0.0.1", source);
-        } else {
-            listener(fd);
-            waitpid(child, NULL, 0);
-        }
-    }
-}
-
-
-/*
- * Bring up a server on port 11119 on the loopback address and test connecting
- * to it via IPv6.  Takes an optional source address to use for client
- * connections.
- */
-#ifdef HAVE_INET6
-static void
-test_ipv6(const char *source)
-{
-    socket_type fd;
-    pid_t child;
-
-    fd = network_bind_ipv6("::1", 11119);
-    if (fd == INVALID_SOCKET) {
-        if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT
-            || errno == EADDRNOTAVAIL) {
-            ipv6 = 0;
-            skip_block(3, "IPv6 not supported");
-            return;
-        } else
-            sysbail("cannot create socket");
-    }
-    if (listen(fd, 1) < 0) {
-        sysdiag("cannot listen to socket");
-        ok_block(3, 0, "IPv6 server test");
-    } else {
-        ok(1, "IPv6 server test");
-        child = fork();
-        if (child < 0)
-            sysbail("cannot fork");
-        else if (child == 0) {
-            close(fd);
-            client("::1", source);
-        } else {
-            listener(fd);
-            waitpid(child, NULL, 0);
-        }
-    }
-}
-#else /* !HAVE_INET6 */
-static void
-test_ipv6(const char *source UNUSED)
-{
-    skip_block(3, "IPv6 not supported");
-}
-#endif /* !HAVE_INET6 */
-
-
-/*
- * Bring up a server on port 11119 on all addresses and try connecting to it
- * via all of the available protocols.  Takes an optional source address to
- * use for client connections.
- */
-static void
-test_all(const char *source_ipv4, const char *source_ipv6 UNUSED)
-{
-    socket_type *fds, fd;
-    unsigned int count, i;
-    pid_t child;
-    struct sockaddr_storage saddr;
-    socklen_t size = sizeof(saddr);
-
-    network_bind_all(11119, &fds, &count);
-    if (count == 0)
-        sysbail("cannot create or bind socket");
-    if (count > 2) {
-        diag("got more than two sockets, using just the first two");
-        count = 2;
-    }
-    for (i = 0; i < count; i++) {
-        fd = fds[i];
-        if (listen(fd, 1) < 0) {
-            sysdiag("cannot listen to socket %d", fd);
-            ok_block(3, 0, "all address server test");
-        } else {
-            ok(1, "all address server test (part %d)", i);
-            child = fork();
-            if (child < 0)
-                sysbail("cannot fork");
-            else if (child == 0) {
-                if (getsockname(fd, (struct sockaddr *) &saddr, &size) < 0)
-                    sysbail("cannot getsockname");
-                if (saddr.ss_family == AF_INET)
-                    client("127.0.0.1", source_ipv4);
-#ifdef HAVE_INET6
-                else if (saddr.ss_family == AF_INET6)
-                    client("::1", source_ipv6);
-#endif
-                else
-                    skip_block(2, "unknown socket family %d", saddr.ss_family);
-                size = sizeof(saddr);
-            } else {
-                listener(fd);
-                waitpid(child, NULL, 0);
-            }
-        }
-    }
-    if (count == 1)
-        skip_block(3, "only one listening socket");
-}
-
-
-/*
- * Bring up a server on port 11119 on all addresses and try connecting to it
- * via 127.0.0.1, using network_accept_any underneath.
- */
-static void
-test_any(void)
-{
-    socket_type *fds;
-    unsigned int count, i;
-    pid_t child;
-
-    network_bind_all(11119, &fds, &count);
-    if (count == 0)
-        sysbail("cannot create or bind socket");
-    for (i = 0; i < count; i++)
-        if (listen(fds[i], 1) < 0) {
-            sysdiag("cannot listen to socket %d", fds[i]);
-            ok_block(2, 0, "accept any server test");
-        }
-    child = fork();
-    if (child < 0)
-        sysbail("cannot fork");
-    else if (child == 0)
-        client("127.0.0.1", NULL);
-    else {
-        listener_any(fds, count);
-        waitpid(child, NULL, 0);
-    }
-}
-
-
-/*
- * Bring up a server on port 11119 on the loopback address and test connecting
- * to it via IPv4 using network_client_create.  Takes an optional source
- * address to use for client connections.
- */
-static void
-test_create_ipv4(const char *source)
-{
-    socket_type fd;
-    pid_t child;
-
-    fd = network_bind_ipv4("127.0.0.1", 11119);
-    if (fd == INVALID_SOCKET)
-        sysbail("cannot create or bind socket");
-    if (listen(fd, 1) < 0) {
-        sysdiag("cannot listen to socket");
-        ok_block(3, 0, "IPv4 network client");
-    } else {
-        ok(1, "IPv4 network client");
-        child = fork();
-        if (child < 0)
-            sysbail("cannot fork");
-        else if (child == 0) {
-            struct sockaddr_in sin;
-            FILE *out;
-
-            fd = network_client_create(PF_INET, SOCK_STREAM, source);
-            if (fd == INVALID_SOCKET)
-                _exit(1);
-            memset(&sin, 0, sizeof(sin));
-            sin.sin_family = AF_INET;
-            sin.sin_port = htons(11119);
-            sin.sin_addr.s_addr = htonl(0x7f000001UL);
-            if (connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0)
-                _exit(1);
-            out = fdopen(fd, "w");
-            if (out == NULL)
-                _exit(1);
-            fputs("socket test\r\n", out);
-            fclose(out);
-            _exit(0);
-        } else {
-            listener(fd);
-            waitpid(child, NULL, 0);
-        }
-    }
-}
-
-
-/*
- * Tests network_addr_compare.  Takes the expected result, the two addresses,
- * and the mask.
- */
-static void
-ok_addr(int expected, const char *a, const char *b, const char *mask)
-{
-    if (expected)
-        ok(network_addr_match(a, b, mask), "compare %s %s %s", a, b, mask);
-    else
-        ok(!network_addr_match(a, b, mask), "compare %s %s %s", a, b, mask);
-}
-
-
-int
-main(void)
-{
-    int status;
-    struct addrinfo *ai, *ai4;
-    struct addrinfo hints;
-    char addr[INET6_ADDRSTRLEN];
-    static const char *port = "119";
-
-#ifdef HAVE_INET6
-    struct addrinfo *ai6;
-    char *p;
-    static const char *ipv6_addr = "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210";
-#endif
-
-    plan(121);
-
-    /*
-     * If IPv6 support appears to be available but doesn't work, we have to
-     * skip the test_all tests since they'll create a socket that we then
-     * can't connect to.  This is the case on Solaris 8 without IPv6
-     * configured.
-     */
-    test_ipv4(NULL);
-    test_ipv6(NULL);
-    if (ipv6)
-        test_all(NULL, NULL);
-    else
-        skip_block(6, "IPv6 not configured");
-    test_create_ipv4(NULL);
-
-    /* This won't make a difference for loopback connections. */
-    test_ipv4("127.0.0.1");
-    test_ipv6("::1");
-    if (ipv6)
-        test_all("127.0.0.1", "::1");
-    else
-        skip_block(6, "IPv6 not configured");
-    test_create_ipv4("127.0.0.1");
-
-    /* We need an initialized innconf struct, but it doesn't need to contain
-     * anything interesting. */
-    innconf = xcalloc(1, sizeof(struct innconf));
-
-    /* This should be equivalent to the previous tests. */
-    innconf->sourceaddress = xstrdup("all");
-    innconf->sourceaddress6 = xstrdup("all");
-
-    test_ipv4(NULL);
-    test_ipv6(NULL);
-    if (ipv6)
-        test_all(NULL, NULL);
-    else
-        skip_block(6, "IPv6 not configured");
-    test_create_ipv4(NULL);
-                            
-    /* This won't make a difference for loopback connections. */
-    free(innconf->sourceaddress);
-    free(innconf->sourceaddress6);
-    innconf->sourceaddress = xstrdup("127.0.0.1");
-    innconf->sourceaddress6 = xstrdup("::1");
-    test_ipv4(NULL);
-    test_ipv6(NULL);
-    if (ipv6)
-        test_all(NULL, NULL);
-    else
-        skip_block(6, "IPv6 not configured");
-    test_create_ipv4(NULL);
-
-    /* Test network_accept_any. */
-    test_any();
-
-    /*
-     * Now, test network_sockaddr_sprint, network_sockaddr_equal, and
-     * network_sockaddr_port.
-     */
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_flags = AI_NUMERICHOST;
-    hints.ai_socktype = SOCK_STREAM;
-    status = getaddrinfo("127.0.0.1", port, &hints, &ai4);
-    if (status != 0)
-        bail("getaddrinfo on 127.0.0.1 failed: %s", gai_strerror(status));
-    ok(network_sockaddr_sprint(addr, sizeof(addr), ai4->ai_addr),
-       "sprint of 127.0.0.1");
-    is_string("127.0.0.1", addr, "...with right results");
-    is_int(119, network_sockaddr_port(ai4->ai_addr),
-           "sockaddr_port");
-    ok(network_sockaddr_equal(ai4->ai_addr, ai4->ai_addr), "sockaddr_equal");
-    status = getaddrinfo("127.0.0.2", NULL, &hints, &ai);
-    if (status != 0)
-        bail("getaddrinfo on 127.0.0.2 failed: %s", gai_strerror(status));
-    ok(!network_sockaddr_equal(ai->ai_addr, ai4->ai_addr),
-       "sockaddr_equal of unequal addresses");
-    ok(!network_sockaddr_equal(ai4->ai_addr, ai->ai_addr),
-       "...and the other way around");
-
-    /* The same for IPv6. */
-#ifdef HAVE_INET6
-    status = getaddrinfo(ipv6_addr, port, &hints, &ai6);
-    if (status != 0)
-        bail("getaddr on %s failed: %s", ipv6_addr, gai_strerror(status));
-    ok(network_sockaddr_sprint(addr, sizeof(addr), ai6->ai_addr),
-       "sprint of IPv6 address");
-    for (p = addr; *p != '\0'; p++)
-        if (islower((unsigned char) *p))
-            *p = toupper((unsigned char) *p);
-    is_string(ipv6_addr, addr, "...with right results");
-    is_int(119, network_sockaddr_port(ai6->ai_addr), "sockaddr_port IPv6");
-    ok(network_sockaddr_equal(ai6->ai_addr, ai6->ai_addr),
-       "sockaddr_equal IPv6");
-    ok(!network_sockaddr_equal(ai4->ai_addr, ai6->ai_addr),
-       "...and not equal to IPv4");
-    ok(!network_sockaddr_equal(ai6->ai_addr, ai4->ai_addr),
-       "...other way around");
-
-    /* Test IPv4 mapped addresses. */
-    status = getaddrinfo("::ffff:7f00:1", NULL, &hints, &ai6);
-    if (status != 0)
-        bail("getaddr on ::ffff:7f00:1 failed: %s", gai_strerror(status));
-    ok(network_sockaddr_sprint(addr, sizeof(addr), ai6->ai_addr),
-       "sprint of IPv4-mapped address");
-    is_string("127.0.0.1", addr, "...with right IPv4 result");
-    ok(network_sockaddr_equal(ai4->ai_addr, ai6->ai_addr),
-       "sockaddr_equal of IPv4-mapped address");
-    ok(network_sockaddr_equal(ai6->ai_addr, ai4->ai_addr),
-       "...and other way around");
-    ok(!network_sockaddr_equal(ai->ai_addr, ai6->ai_addr),
-       "...but not some other address");
-    ok(!network_sockaddr_equal(ai6->ai_addr, ai->ai_addr),
-       "...and the other way around");
-    freeaddrinfo(ai6);
-#else
-    skip_block(12, "IPv6 not supported");
-#endif
-
-    /* Check the domains of functions and their error handling. */
-    ai4->ai_addr->sa_family = AF_UNIX;
-    ok(!network_sockaddr_equal(ai4->ai_addr, ai4->ai_addr),
-       "equal not equal with address mismatches");
-    is_int(0, network_sockaddr_port(ai4->ai_addr),
-           "port meaningless for AF_UNIX");
-
-    /* Tests for network_addr_compare. */
-    ok_addr(1, "127.0.0.1", "127.0.0.1",   NULL);
-    ok_addr(0, "127.0.0.1", "127.0.0.2",   NULL);
-    ok_addr(1, "127.0.0.1", "127.0.0.0",   "31");
-    ok_addr(0, "127.0.0.1", "127.0.0.0",   "32");
-    ok_addr(0, "127.0.0.1", "127.0.0.0",   "255.255.255.255");
-    ok_addr(1, "127.0.0.1", "127.0.0.0",   "255.255.255.254");
-    ok_addr(1, "10.10.4.5", "10.10.4.255", "24");
-    ok_addr(0, "10.10.4.5", "10.10.4.255", "25");
-    ok_addr(1, "10.10.4.5", "10.10.4.255", "255.255.255.0");
-    ok_addr(0, "10.10.4.5", "10.10.4.255", "255.255.255.128");
-    ok_addr(0, "129.0.0.0", "1.0.0.0",     "1");
-    ok_addr(1, "129.0.0.0", "1.0.0.0",     "0");
-    ok_addr(1, "129.0.0.0", "1.0.0.0",     "0.0.0.0");
-
-    /* Try some IPv6 addresses. */
-#ifdef HAVE_INET6
-    ok_addr(1, ipv6_addr,   ipv6_addr,     NULL);
-    ok_addr(1, ipv6_addr,   ipv6_addr,     "128");
-    ok_addr(1, ipv6_addr,   ipv6_addr,     "60");
-    ok_addr(1, "::127",     "0:0::127",    "128");
-    ok_addr(1, "::127",     "0:0::128",    "120");
-    ok_addr(0, "::127",     "0:0::128",    "128");
-    ok_addr(0, "::7fff",    "0:0::8000",   "113");
-    ok_addr(1, "::7fff",    "0:0::8000",   "112");
-    ok_addr(0, "::3:ffff",  "::2:ffff",    "120");
-    ok_addr(0, "::3:ffff",  "::2:ffff",    "119");
-    ok_addr(0, "ffff::1",   "7fff::1",     "1");
-    ok_addr(1, "ffff::1",   "7fff::1",     "0");
-    ok_addr(0, "fffg::1",   "fffg::1",     NULL);
-    ok_addr(0, "ffff::1",   "7fff::1",     "-1");
-    ok_addr(0, "ffff::1",   "ffff::1",     "-1");
-    ok_addr(0, "ffff::1",   "ffff::1",     "129");
-#else
-    skip_block(16, "IPv6 not supported");
-#endif
-
-    /* Test some invalid addresses. */
-    ok_addr(0, "fred",      "fred",        NULL);
-    ok_addr(0, "",          "",            NULL);
-    ok_addr(0, "",          "",            "0");
-    ok_addr(0, "127.0.0.1", "127.0.0.1",   "pete");
-    ok_addr(0, "127.0.0.1", "127.0.0.1",   "1p");
-    ok_addr(0, "127.0.0.1", "127.0.0.1",   "1p");
-    ok_addr(0, "127.0.0.1", "127.0.0.1",   "-1");
-    ok_addr(0, "127.0.0.1", "127.0.0.1",   "33");
-
-    return 0;
-}



More information about the inn-committers mailing list