Simplify how the http socket is bound.
authorEmil Mikulic <emikulic@gmail.com>
Sun, 29 May 2011 05:15:59 +0000 (15:15 +1000)
committerEmil Mikulic <emikulic@gmail.com>
Sat, 4 Jun 2011 14:00:30 +0000 (00:00 +1000)
- Use dual-stack socket for wildcard.
- Sadly, AI_V4MAPPED doesn't work on FreeBSD 8.2

http.c

diff --git a/http.c b/http.c
index 4bdc0e6..c2122c9 100644 (file)
--- a/http.c
+++ b/http.c
@@ -913,72 +913,89 @@ static void http_init_base(const char *url)
         base_url = safe_url;
 }
 
-/* --------------------------------------------------------------------------
- * Initialize the sockin global.  This is the socket that we accept
- * connections from.  Pass -1 as max_conn for system limit.
+/* Use getaddrinfo to figure out what type of socket to create and
+ * what to bind it to.  "bindaddr" can be NULL.  Remember to freeaddrinfo()
+ * the result.
  */
-void http_init(const char *base, const char *bindaddr,
-    const unsigned short bindport, const int max_conn)
+static struct addrinfo *get_bind_addr(
+    const char *bindaddr, const unsigned short bindport)
 {
-    struct sockaddr_storage addrin;
-    struct addrinfo hints, *ai, *aiptr;
-    char ipaddr[INET6_ADDRSTRLEN], portstr[12];
-    int sockopt, ret;
-
-    http_init_base(base);
+    struct addrinfo hints, *ai;
+    char portstr[6];
+    int ret;
 
     memset(&hints, 0, sizeof(hints));
     hints.ai_family = AF_UNSPEC;
+    if (bindaddr == NULL)
+        hints.ai_family = AF_INET6; /* dual stack socket */
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_flags = AI_PASSIVE;
 #ifdef AI_ADDRCONFIG
     hints.ai_flags |= AI_ADDRCONFIG;
 #endif
     snprintf(portstr, sizeof(portstr), "%u", bindport);
+    if ((ret = getaddrinfo(bindaddr, portstr, &hints, &ai)))
+        err(1, "getaddrinfo(%s,%s) failed: %s",
+            bindaddr ? bindaddr : "NULL", portstr, gai_strerror(ret));
+    if (ai == NULL)
+        err(1, "getaddrinfo() returned NULL pointer");
+    if (ai->ai_next != NULL)
+        warnx("getaddrinfo() returned multiple addresses");
+    return ai;
+}
 
-    if ((ret = getaddrinfo(bindaddr, portstr, &hints, &aiptr)))
-        err(1, "getaddrinfo(): %s", gai_strerror(ret));
-
-    for (ai = aiptr; ai; ai = ai->ai_next) {
-        /* create incoming socket */
-        sockin = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
-        if (sockin == -1)
-            continue;
-
-        /* reuse address */
-        sockopt = 1;
-        if (setsockopt(sockin, SOL_SOCKET, SO_REUSEADDR,
-                &sockopt, sizeof(sockopt)) == -1) {
-            close(sockin);
-            continue;
-        }
-
-        /* Recover address and port strings. */
-        getnameinfo(ai->ai_addr, ai->ai_addrlen, ipaddr, sizeof(ipaddr),
-                NULL, 0, NI_NUMERICHOST);
+/* --------------------------------------------------------------------------
+ * Initialize the sockin global.  This is the socket that we accept
+ * connections from.  Pass -1 as max_conn for system limit.
+ */
+void http_init(const char *base, const char *bindaddr,
+    const unsigned short bindport, const int max_conn)
+{
+    struct addrinfo *ai;
+    char ipaddr[INET6_ADDRSTRLEN];
+    int sockopt, ret;
 
-        /* bind socket */
-        memcpy(&addrin, ai->ai_addr, ai->ai_addrlen);
-        if (bind(sockin, (struct sockaddr *)&addrin, ai->ai_addrlen) == -1) {
-            close(sockin);
-            continue;
-        }
+    http_init_base(base);
+    ai = get_bind_addr(bindaddr, bindport);
+
+    /* create incoming socket */
+    if ((sockin = socket(ai->ai_family, SOCK_STREAM, 0)) == -1)
+        err(1, "socket() failed");
+
+    /* reuse address */
+    sockopt = 1;
+    if (setsockopt(sockin, SOL_SOCKET, SO_REUSEADDR,
+            &sockopt, sizeof(sockopt)) == -1)
+        err(1, "can't set SO_REUSEADDR");
+
+    /* dual stack socket */
+    if (ai->ai_family == AF_INET6) {
+        sockopt = 0;
+        if (setsockopt(sockin, IPPROTO_IPV6, IPV6_V6ONLY,
+                &sockopt, sizeof(sockopt)) == -1)
+            err(1, "can't unset IPV6_V6ONLY");
+    }
 
-        verbosef("listening on http://%s:%u%s", ipaddr, bindport, base_url);
+    /* format address into ipaddr string */
+    if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, ipaddr,
+                           sizeof(ipaddr), NULL, 0, NI_NUMERICHOST)) != 0)
+        err(1, "getnameinfo failed: %s", gai_strerror(ret));
 
-        /* listen on socket */
-        if (listen(sockin, max_conn) >= 0)
-            /* Successfully bound and now listening. */
-            break;
+    /* bind socket */
+    if (bind(sockin, ai->ai_addr, ai->ai_addrlen) == -1)
+        err(1, "bind(\"%s\") failed", ipaddr);
 
-        /* Next candidate. */
-        continue;
-    }
+    verbosef("listening on http://%s%s%s:%u%s",
+        (ai->ai_family == AF_INET6) ? "[" : "",
+        ipaddr,
+        (ai->ai_family == AF_INET6) ? "]" : "",
+        bindport, base_url);
 
-    freeaddrinfo(aiptr);
+    freeaddrinfo(ai);
 
-    if (ai == NULL)
-        err(1, "getaddrinfo() unable to locate address");
+    /* listen on socket */
+    if (listen(sockin, max_conn) == -1)
+        err(1, "listen() failed");
 
     /* ignore SIGPIPE */
     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)