Add support for IPv6.
authorHugh Wang <hghwng@gmail.com>
Sun, 20 Dec 2015 13:59:20 +0000 (21:59 +0800)
committerHugh Wang <hghwng@gmail.com>
Sun, 20 Dec 2015 13:59:20 +0000 (21:59 +0800)
darkhttpd.c

index b8d4180..e7a1ec8 100644 (file)
@@ -204,7 +204,11 @@ struct connection {
     LIST_ENTRY(connection) entries;
 
     int socket;
+#ifdef HAVE_INET6
+    struct in6_addr client;
+#else
     in_addr_t client;
+#endif
     time_t last_active;
     enum {
         RECV_REQUEST,   /* receiving request */
@@ -268,13 +272,14 @@ static time_t now;
 #define MAX_REQUEST_LENGTH 4000
 
 /* Defaults can be overridden on the command-line */
-static in_addr_t bindaddr = INADDR_ANY;
+static const char *bindaddr;
 static uint16_t bindport = 8080; /* or 80 if running as root */
 static int max_connections = -1;        /* kern.ipc.somaxconn */
 static const char *index_name = "index.html";
 static int no_listing = 0;
 
 static int sockin = -1;             /* socket to accept connections from */
+static int inet6 = 0;               /* whether the socket uses inet6 */
 static char *wwwroot = NULL;        /* a path name */
 static char *logfile_name = NULL;   /* NULL = no logging */
 static FILE *logfile = NULL;
@@ -796,16 +801,47 @@ static const char *url_content_type(const char *url) {
     return default_mimetype;
 }
 
+static const char *get_address_text(const void *addr) {
+    if (inet6) {
+#ifdef HAVE_INET6
+        static char text_addr[INET6_ADDRSTRLEN];
+        inet_ntop(AF_INET6, (struct in6_addr *)addr, text_addr,
+                  INET6_ADDRSTRLEN);
+        return text_addr;
+#endif
+    } else {
+        return inet_ntoa(*(struct in_addr *)addr);
+    }
+}
+
 /* Initialize the sockin global.  This is the socket that we accept
  * connections from.
  */
 static void init_sockin(void) {
     struct sockaddr_in addrin;
+#ifdef HAVE_INET6
+    struct sockaddr_in6 addrin6;
+#endif
     socklen_t addrin_len;
     int sockopt;
 
-    /* create incoming socket */
-    sockin = socket(PF_INET, SOCK_STREAM, 0);
+    if (inet6) {
+#ifdef HAVE_INET6
+        memset(&addrin6, 0, sizeof(addrin6));
+        if (inet_pton(AF_INET6, bindaddr ? bindaddr : "::",
+                      &addrin6.sin6_addr) == -1) {
+            errx(1, "malformed --addr argument");
+        }
+        sockin = socket(PF_INET6, SOCK_STREAM, 0);
+#endif
+    } else {
+        memset(&addrin, 0, sizeof(addrin));
+        addrin.sin_addr.s_addr = bindaddr ? inet_addr(bindaddr) : INADDR_ANY;
+        if (addrin.sin_addr.s_addr == (in_addr_t)INADDR_NONE)
+            errx(1, "malformed --addr argument");
+        sockin = socket(PF_INET, SOCK_STREAM, 0);
+    }
+
     if (sockin == -1)
         err(1, "socket()");
 
@@ -834,20 +870,38 @@ static void init_sockin(void) {
 #endif
 
     /* bind socket */
-    addrin.sin_family = (u_char)PF_INET;
-    addrin.sin_port = htons(bindport);
-    addrin.sin_addr.s_addr = bindaddr;
-    memset(&(addrin.sin_zero), 0, 8);
-    if (bind(sockin, (struct sockaddr *)&addrin,
-             sizeof(struct sockaddr)) == -1)
-        err(1, "bind(port %u)", bindport);
-
-    addrin_len = sizeof(addrin);
+    if (inet6) {
+#ifdef HAVE_INET6
+        addrin6.sin6_family = AF_INET6;
+        addrin6.sin6_port = htons(bindport);
+        if (bind(sockin, (struct sockaddr *)&addrin6,
+                 sizeof(struct sockaddr_in6)) == -1)
+            err(1, "bind(port %u)", bindport);
+
+        addrin_len = sizeof(addrin6);
+#endif
+    } else {
+        addrin.sin_family = (u_char)PF_INET;
+        addrin.sin_port = htons(bindport);
+        if (bind(sockin, (struct sockaddr *)&addrin,
+                 sizeof(struct sockaddr_in)) == -1)
+            err(1, "bind(port %u)", bindport);
+
+        addrin_len = sizeof(addrin);
+    }
+
     if (getsockname(sockin, (struct sockaddr *)&addrin, &addrin_len) == -1)
         err(1, "getsockname()");
 
-    printf("listening on: http://%s:%u/\n",
-        inet_ntoa(addrin.sin_addr), ntohs(addrin.sin_port));
+    if (inet6) {
+#ifdef HAVE_INET6
+        printf("listening on: http://[%s]:%u/\n",
+            get_address_text(&addrin6.sin6_addr), bindport);
+#endif
+    } else {
+        printf("listening on: http://%s:%u/\n",
+            get_address_text(&addrin.sin_addr), bindport);
+    }
 
     /* listen on socket */
     if (listen(sockin, max_connections) == -1)
@@ -918,6 +972,10 @@ static void usage(const char *argv0) {
     printf("\t--no-server-id\n"
     "\t\tDon't identify the server type in headers\n"
     "\t\tor directory listings.\n");
+#ifdef HAVE_INET6
+    printf("\t--ipv6\n"
+    "\t\tListen on IPv6 address.\n");
+#endif
 }
 
 /* Returns 1 if string is a number, 0 otherwise.  Set num to NULL if
@@ -979,9 +1037,7 @@ static void parse_commandline(const int argc, char *argv[]) {
         else if (strcmp(argv[i], "--addr") == 0) {
             if (++i >= argc)
                 errx(1, "missing ip after --addr");
-            bindaddr = inet_addr(argv[i]);
-            if (bindaddr == (in_addr_t)INADDR_NONE)
-                errx(1, "malformed --addr argument");
+            bindaddr = argv[i];
         }
         else if (strcmp(argv[i], "--maxconn") == 0) {
             if (++i >= argc)
@@ -1071,6 +1127,11 @@ static void parse_commandline(const int argc, char *argv[]) {
         else if (strcmp(argv[i], "--no-server-id") == 0) {
             want_server_id = 0;
         }
+#ifdef HAVE_INET6
+        else if (strcmp(argv[i], "--ipv6") == 0) {
+            inet6 = 1;
+        }
+#endif
         else
             errx(1, "unknown argument `%s'", argv[i]);
     }
@@ -1081,7 +1142,7 @@ static struct connection *new_connection(void) {
     struct connection *conn = xmalloc(sizeof(struct connection));
 
     conn->socket = -1;
-    conn->client = INADDR_ANY;
+    memset(&conn->client, 0, sizeof(conn->client));
     conn->last_active = now;
     conn->request = NULL;
     conn->request_length = 0;
@@ -1119,22 +1180,41 @@ static struct connection *new_connection(void) {
 /* Accept a connection from sockin and add it to the connection queue. */
 static void accept_connection(void) {
     struct sockaddr_in addrin;
+#ifdef HAVE_INET6
+    struct sockaddr_in6 addrin6;
+#endif
     socklen_t sin_size;
     struct connection *conn;
 
     /* allocate and initialise struct connection */
     conn = new_connection();
 
-    sin_size = sizeof(addrin);
-    memset(&addrin, 0, sin_size);
-    conn->socket = accept(sockin, (struct sockaddr *)&addrin, &sin_size);
+    if (inet6) {
+#ifdef HAVE_INET6
+        sin_size = sizeof(addrin6);
+        memset(&addrin6, 0, sin_size);
+        conn->socket = accept(sockin, (struct sockaddr *)&addrin6, &sin_size);
+#endif
+    } else {
+        sin_size = sizeof(addrin);
+        memset(&addrin, 0, sin_size);
+        conn->socket = accept(sockin, (struct sockaddr *)&addrin, &sin_size);
+    }
+
     if (conn->socket == -1)
         err(1, "accept()");
 
     nonblock_socket(conn->socket);
 
     conn->state = RECV_REQUEST;
-    conn->client = addrin.sin_addr.s_addr;
+
+    if (inet6) {
+#ifdef HAVE_INET6
+        conn->client = addrin6.sin6_addr;
+#endif
+    } else {
+        *(in_addr_t *)&conn->client = addrin.sin_addr.s_addr;
+    }
     LIST_INSERT_HEAD(&connlist, conn, entries);
 
     if (debug)
@@ -1173,7 +1253,6 @@ static void logencode(const char *src, char *dest) {
 
 /* Add a connection's details to the logfile. */
 static void log_connection(const struct connection *conn) {
-    struct in_addr inaddr;
     char *safe_method, *safe_url, *safe_referer, *safe_user_agent;
 
     if (logfile == NULL)
@@ -1183,8 +1262,6 @@ static void log_connection(const struct connection *conn) {
     if (conn->method == NULL)
         return; /* invalid - didn't parse - maybe too long */
 
-    inaddr.s_addr = conn->client;
-
 #define make_safe(x) \
     if (conn->x) { \
         safe_##x = xmalloc(strlen(conn->x)*3 + 1); \
@@ -1202,7 +1279,7 @@ static void log_connection(const struct connection *conn) {
 
     fprintf(logfile, "%lu %s \"%s %s\" %d %llu \"%s\" \"%s\"\n",
         (unsigned long int)now,
-        inet_ntoa(inaddr),
+        get_address_text(&conn->client),
         use_safe(method),
         use_safe(url),
         conn->http_code,