Bail out early if the request is too large.
[darkstat] / http.c
diff --git a/http.c b/http.c
index 0707ccd..fa77183 100644 (file)
--- a/http.c
+++ b/http.c
@@ -344,13 +344,16 @@ static void accept_connection(const int sockin)
 static void free_connection(struct connection *conn)
 {
     dverbosef("free_connection(%d)", conn->socket);
-    if (conn->socket != -1) close(conn->socket);
-    if (conn->request != NULL) free(conn->request);
-    if (conn->method != NULL) free(conn->method);
-    if (conn->uri != NULL) free(conn->uri);
-    if (conn->query != NULL) free(conn->query);
-    if (conn->header != NULL && !conn->header_dont_free) free(conn->header);
-    if (conn->reply != NULL && !conn->reply_dont_free) free(conn->reply);
+    if (conn->socket != -1)
+        close(conn->socket);
+    free(conn->request);
+    free(conn->method);
+    free(conn->uri);
+    free(conn->query);
+    if (!conn->header_dont_free)
+        free(conn->header);
+    if (!conn->reply_dont_free)
+        free(conn->reply);
 }
 
 
@@ -592,7 +595,6 @@ process_gzip(struct connection *conn)
         conn->reply_dont_free = 0;
     else
         free(conn->reply);
-
     conn->reply = buf;
     conn->reply_length -= zs.avail_out;
     conn->encoding = encoding_gzip;
@@ -696,10 +698,6 @@ static void process_request(struct connection *conn)
         conn->state = SEND_HEADER;
     else
         conn->state = SEND_HEADER_AND_REPLY;
-
-    /* request not needed anymore */
-    free(conn->request);
-    conn->request = NULL; /* important: don't free it again later */
 }
 
 
@@ -709,11 +707,10 @@ static void process_request(struct connection *conn)
  */
 static void poll_recv_request(struct connection *conn)
 {
-    #define BUFSIZE 65536
-    char buf[BUFSIZE];
+    char buf[65536];
     ssize_t recvd;
 
-    recvd = recv(conn->socket, buf, BUFSIZE, 0);
+    recvd = recv(conn->socket, buf, sizeof(buf), 0);
     dverbosef("poll_recv_request(%d) got %d bytes", conn->socket, (int)recvd);
     if (recvd <= 0)
     {
@@ -723,7 +720,6 @@ static void poll_recv_request(struct connection *conn)
         return;
     }
     conn->last_active = now;
-    #undef BUFSIZE
 
     /* append to conn->request */
     conn->request = xrealloc(conn->request, conn->request_length+recvd+1);
@@ -731,17 +727,24 @@ static void poll_recv_request(struct connection *conn)
     conn->request_length += recvd;
     conn->request[conn->request_length] = 0;
 
-    /* process request if we have all of it */
-    if (conn->request_length > 4 &&
-        memcmp(conn->request+conn->request_length-4, "\r\n\r\n", 4) == 0)
-        process_request(conn);
-
     /* die if it's too long */
     if (conn->request_length > MAX_REQUEST_LENGTH)
     {
         default_reply(conn, 413, "Request Entity Too Large",
             "Your request was dropped because it was too long.");
         conn->state = SEND_HEADER;
+        return;
+    }
+
+    /* process request if we have all of it */
+    if (conn->request_length > 4 &&
+        memcmp(conn->request+conn->request_length-4, "\r\n\r\n", 4) == 0)
+    {
+        process_request(conn);
+
+        /* request not needed anymore */
+        free(conn->request);
+        conn->request = NULL; /* important: don't free it again later */
     }
 }
 
@@ -884,19 +887,9 @@ static struct addrinfo *get_bind_addr(
 
     memset(&hints, 0, sizeof(hints));
     hints.ai_family = AF_UNSPEC;
-#ifdef linux
-    /* Special case for Linux: with bindaddr=NULL and ai_family=AF_UNSPEC,
-     * we successfully bind to 0.0.0.0 and then fail to bind to ::, resulting
-     * in a v4-only http socket.
-     *
-     * Conversely, if we specify AF_INET6, we bind to just :: which is able to
-     * accept v4 as well as v6 connections.
-     */
-    if (bindaddr == NULL)
-        hints.ai_family = AF_INET6; /* we'll get a dual stack socket */
-#endif
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_flags = AI_PASSIVE;
+
     snprintf(portstr, sizeof(portstr), "%u", bindport);
     if ((ret = getaddrinfo(bindaddr, portstr, &hints, &ai)))
         err(1, "getaddrinfo(%s,%s) failed: %s",
@@ -922,7 +915,8 @@ static void http_listen_one(struct addrinfo *ai,
     int sockin, sockopt, ret;
 
     /* create incoming socket */
-    if ((sockin = socket(ai->ai_family, SOCK_STREAM, 0)) == -1)
+    if ((sockin = socket(ai->ai_family, ai->ai_socktype,
+            ai->ai_protocol)) == -1)
         err(1, "socket() failed");
 
     /* reuse address */
@@ -931,14 +925,28 @@ static void http_listen_one(struct addrinfo *ai,
             &sockopt, sizeof(sockopt)) == -1)
         err(1, "can't set SO_REUSEADDR");
 
+#ifdef IPV6_V6ONLY
+    /* explicitly disallow IPv4 mapped addresses since OpenBSD doesn't allow
+     * dual stack sockets under any circumstances
+     */
+    if (ai->ai_family == AF_INET6) {
+        sockopt = 1;
+        if (setsockopt(sockin, IPPROTO_IPV6, IPV6_V6ONLY,
+                &sockopt, sizeof(sockopt)) == -1)
+            err(1, "can't set IPV6_V6ONLY");
+    }
+#endif
+
     /* 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));
 
     /* bind socket */
-    if (bind(sockin, ai->ai_addr, ai->ai_addrlen) == -1)
-        err(1, "bind(\"%s\") failed", ipaddr);
+    if (bind(sockin, ai->ai_addr, ai->ai_addrlen) == -1) {
+        warn("bind(\"%s\") failed", ipaddr);
+        return;
+    }
 
     /* listen on socket */
     if (listen(sockin, -1) == -1)
@@ -979,6 +987,9 @@ void http_listen(const unsigned short bindport)
         free(bindaddr);
     }
 
+    if (insocks == 0)
+        errx(1, "was not able to bind any ports for http interface");
+
     /* ignore SIGPIPE */
     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
         err(1, "can't ignore SIGPIPE");