Introduce the new nonblock API.
[paraslash.git] / net.c
diff --git a/net.c b/net.c
index d9b3a9defa8164e98be1dda341df3a62b1e4bf9b..92ae217d6b4bf7cdc438ff018774f9c0d9bdbc03 100644 (file)
--- a/net.c
+++ b/net.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2010 Andre Noll <maan@systemlinux.org>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -279,7 +279,7 @@ struct flowopts *flowopt_new(void)
  * Append new socket option to flowopt queue.
  *
  * \param fo   The flowopt queue to append to.
- * \param lev  Level at which \opt resides.
+ * \param lev  Level at which \opt resides.
  * \param opt  New option to add.
  * \param name Stringified name of \a opt.
  * \param val  The value to set \a opt to.
@@ -351,7 +351,6 @@ static void flowopt_cleanup(struct flowopts *fo)
 /**
  * Resolve IPv4/IPv6 address and create a ready-to-use active or passive socket.
  *
- * \param l3type The layer-3 type (\p AF_INET, \p AF_INET6, \p AF_UNSPEC).
  * \param l4type The layer-4 type (\p IPPROTO_xxx).
  * \param passive Whether this is a passive (1) or active (0) socket.
  * \param host Remote or local hostname or IPv/6 address string.
@@ -384,7 +383,7 @@ int makesock(unsigned l4type, bool passive,
        struct addrinfo *local = NULL, *src,
                        *remote = NULL, *dst, hints;
        unsigned int    l3type = AF_UNSPEC;
-       int             rc, sockfd = -1,
+       int             rc, on = 1, sockfd = -1,
                        socktype = sock_type(l4type);
        char port[6]; /* port number has at most 5 digits */
 
@@ -429,6 +428,19 @@ int makesock(unsigned l4type, bool passive,
                if (sockfd < 0)
                        goto get_next_dst;
 
+               /*
+                * Reuse the address on passive sockets to avoid failure on
+                * restart (protocols using listen()) and when creating
+                * multiple listener instances (UDP multicast).
+                */
+               if (passive && setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
+                                                 &on, sizeof(on)) == -1) {
+                       rc = errno;
+                       close(sockfd);
+                       PARA_ERROR_LOG("can not set SO_REUSEADDR: %s\n",
+                                      strerror(rc));
+                       return -ERRNO_TO_PARA_ERROR(rc);
+               }
                flowopt_setopts(sockfd, fo);
 
                if (src) {
@@ -457,10 +469,11 @@ get_next_src:
        flowopt_cleanup(fo);
 
        if (src == NULL && dst == NULL) {
+               rc = errno;
                PARA_ERROR_LOG("can not create %s socket %s#%s.\n",
                        layer4_name(l4type), host? host : (passive?
                        "[loopback]" : "[localhost]"), port);
-               return -ERRNO_TO_PARA_ERROR(errno);
+               return -ERRNO_TO_PARA_ERROR(rc);
        }
        return sockfd;
 }
@@ -479,20 +492,14 @@ get_next_src:
  */
 int para_listen(unsigned l4type, uint16_t port, struct flowopts *fo)
 {
-       int fd, ret;
-
-       if (fo == NULL)
-               fo = flowopt_new();
-
-       /* Reuse the address to avoid failure on restart. */
-       OPT_ENABLE(fo, SOL_SOCKET, SO_REUSEADDR);
+       int ret, fd = makesock(l4type, 1, NULL, port, fo);
 
-       fd = makesock(l4type, 1, NULL, port, fo);
        if (fd > 0) {
                ret = listen(fd, BACKLOG);
                if (ret < 0) {
+                       ret = errno;
                        close(fd);
-                       return -ERRNO_TO_PARA_ERROR(errno);
+                       return -ERRNO_TO_PARA_ERROR(ret);
                }
                PARA_INFO_LOG("listening on %s port %u, fd %d\n",
                        layer4_name(l4type), port, fd);
@@ -769,30 +776,35 @@ int para_accept(int fd, void *addr, socklen_t size)
 }
 
 /**
- * Probe the list of DCCP CCIDs supported locally by the host.
- * \param ccids Array to be filled in.
- * \param nccids Length of \a ccids.
- * \return Pointer to \a ccids, NULL on failure.
+ * Probe the list of DCCP CCIDs configured on this host.
+ * \param ccid_array Pointer to return statically allocated array in.
+ * \return Number of elements returned in \a ccid_array or error.
  *
  * NB: This feature is only available on Linux > 2.6.30; on older kernels
  * ENOPROTOOPT ("Protocol not available") will be returned.
  */
-const uint8_t *dccp_available_ccids(uint8_t *ccids, uint8_t *nccids)
+int dccp_available_ccids(uint8_t **ccid_array)
 {
-       int fd = makesock(IPPROTO_DCCP, 1, NULL, 0, NULL);
+       static uint8_t ccids[DCCP_MAX_HOST_CCIDS];
+       socklen_t nccids = sizeof(ccids);
+       int ret, fd;
 
-       if (fd < 0)
-               return NULL;
+       ret = fd = makesock(IPPROTO_DCCP, 1, NULL, 0, NULL);
+       if (ret < 0)
+               return ret;
 
        if (getsockopt(fd, SOL_DCCP, DCCP_SOCKOPT_AVAILABLE_CCIDS,
-                                       ccids, (socklen_t *)nccids) < 0) {
+                                               ccids, &nccids) < 0) {
+               ret = errno;
+               close(fd);
                PARA_ERROR_LOG("No DCCP_SOCKOPT_AVAILABLE_CCIDS: %s\n",
-                               strerror(errno));
-               *nccids = 0;
+                               strerror(ret));
+               return -ERRNO_TO_PARA_ERROR(ret);
        }
-       close(fd);
 
-       return *nccids ? ccids : NULL;
+       close(fd);
+       *ccid_array = ccids;
+       return nccids;
 }
 
 /**