Merge branch 't/misc'
[paraslash.git] / net.c
diff --git a/net.c b/net.c
index a95f145a78ac111416201aebe4b1f628e2a73544..c11f67c4bac3f7f316daa317ae71ed8ebe6f10a9 100644 (file)
--- a/net.c
+++ b/net.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2014 Andre Noll <maan@systemlinux.org>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -333,7 +333,14 @@ static void flowopt_setopts(int sockfd, struct flowopts *fo)
                }
 }
 
-static void flowopt_cleanup(struct flowopts *fo)
+/**
+ * Deallocate all resources of a flowopts structure.
+ *
+ * \param fo A pointer as returned from flowopt_new().
+ *
+ * It's OK to pass \p NULL here in which case the function does nothing.
+ */
+void flowopt_cleanup(struct flowopts *fo)
 {
        struct pre_conn_opt *cur, *next;
 
@@ -348,7 +355,7 @@ static void flowopt_cleanup(struct flowopts *fo)
        free(fo);
 }
 
-/*
+/**
  * Resolve an IPv4/IPv6 address.
  *
  * \param l4type The layer-4 type (\p IPPROTO_xxx).
@@ -366,7 +373,7 @@ static void flowopt_cleanup(struct flowopts *fo)
  *
  * \sa getaddrinfo(3).
  */
-static int lookup_address(unsigned l4type, bool passive, const char *host,
+int lookup_address(unsigned l4type, bool passive, const char *host,
                int port_number, struct addrinfo **result)
 {
        int ret;
@@ -402,7 +409,7 @@ static int lookup_address(unsigned l4type, bool passive, const char *host,
        return 1;
 }
 
-/*
+/**
  * Create an active or passive socket.
  *
  * \param l4type \p IPPROTO_TCP, \p IPPROTO_UDP, or \p IPPROTO_DCCP.
@@ -412,71 +419,48 @@ static int lookup_address(unsigned l4type, bool passive, const char *host,
  *
  * bind(2) is called on passive sockets, and connect(2) on active sockets. The
  * algorithm tries all possible address combinations until it succeeds. If \a
- * fo is supplied, options are set and cleanup is performed.
+ * fo is supplied, options are set but cleanup must be performed in the caller.
  *
  * \return File descriptor on success, \p E_MAKESOCK on errors.
  *
  * \sa \ref lookup_address(), \ref makesock(), ip(7), ipv6(7), bind(2),
  * connect(2).
  */
-static int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai,
+int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai,
                struct flowopts *fo)
 {
-       struct addrinfo *local, *remote, *src, *dst;
-       int ret = -E_MAKESOCK, on = 1, sockfd = -1;
+       int ret = -E_MAKESOCK, on = 1;
 
-       if (passive) {
-               local = ai;
-               remote = NULL;
-       } else {
-               local = NULL;
-               remote = ai;
-       }
-
-       /* Iterate over all src/dst combination, exhausting dst first */
-       for (src = local, dst = remote; src != NULL || dst != NULL; /* no op */ ) {
-               ret = socket(src ? src->ai_family : dst->ai_family,
-                       sock_type(l4type), l4type);
+       for (; ai; ai = ai->ai_next) {
+               int fd;
+               ret = socket(ai->ai_family, sock_type(l4type), l4type);
                if (ret < 0)
-                       goto get_next_dst;
-               sockfd = ret;
-               flowopt_setopts(sockfd, fo);
+                       continue;
+               fd = ret;
+               flowopt_setopts(fd, fo);
+               if (!passive) {
+                       if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0)
+                               return fd;
+                       close(fd);
+                       continue;
+               }
                /*
                 * 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) {
-                       ret = -ERRNO_TO_PARA_ERROR(errno);
-                       close(sockfd);
-                       PARA_ERROR_LOG("can not set SO_REUSEADDR: %s\n",
-                               para_strerror(-ret));
-                       break;
-               }
-
-               if (passive) {
-                       if (bind(sockfd, src->ai_addr, src->ai_addrlen) < 0) {
-                               close(sockfd);
-                               goto get_next_src;
-                       }
-                       /* bind completed successfully */
-                       break;
-               } else {
-                       if (connect(sockfd, dst->ai_addr, dst->ai_addrlen) == 0)
-                               break; /* connection completed successfully */
+               if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on,
+                               sizeof(on)) == -1) {
+                       close(fd);
+                       continue;
                }
-               close(sockfd);
-get_next_dst:
-               if (dst && (dst = dst->ai_next))
+               if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) {
+                       close(fd);
                        continue;
-get_next_src:
-               if (src && (src = src->ai_next)) /* restart inner loop */
-                       dst = remote;
+               }
+               return fd;
        }
-       if (src == NULL && dst == NULL)
-               return ret < 0? ret : -E_MAKESOCK;
-       return sockfd;
+       return -E_MAKESOCK;
 }
 
 /**
@@ -508,7 +492,6 @@ int makesock(unsigned l4type, bool passive, const char *host, uint16_t port_numb
                ret = makesock_addrinfo(l4type, passive, ai, fo);
        if (ai)
                freeaddrinfo(ai);
-       flowopt_cleanup(fo);
        if (ret < 0) {
                PARA_ERROR_LOG("can not create %s socket %s#%d.\n",
                layer4_name(l4type), host? host : (passive?
@@ -727,6 +710,32 @@ void extract_v4_addr(const struct sockaddr_storage *ss, struct in_addr *ia)
                *ia = ((struct sockaddr_in *)sa)->sin_addr;
 }
 
+/**
+ * Compare the address part of IPv4/6 addresses.
+ *
+ * \param sa1 First address.
+ * \param sa2 Second address.
+ *
+ * \return True iff the IP address of \a sa1 and \a sa2 match.
+ */
+bool sockaddr_equal(const struct sockaddr *sa1, const struct sockaddr *sa2)
+{
+       if (!sa1 || !sa2)
+               return false;
+       if (sa1->sa_family != sa2->sa_family)
+               return false;
+       if (sa1->sa_family == AF_INET) {
+               struct sockaddr_in *a1 = (typeof(a1))sa1,
+                       *a2 = (typeof (a2))sa2;
+               return a1->sin_addr.s_addr == a2->sin_addr.s_addr;
+       } else if (sa1->sa_family == AF_INET6) {
+               struct sockaddr_in6 *a1 = (typeof(a1))sa1,
+                       *a2 = (typeof (a2))sa2;
+               return !memcmp(a1, a2, sizeof(*a1));
+       } else
+               return false;
+}
+
 /**
  * Receive data from a file descriptor.
  *