X-Git-Url: https://unix4lyfe.org/gitweb/darkstat/blobdiff_plain/d0fabd45b8c905493257ed2eeaa2dc506de09ee1..cc427e0a0ebb5433af7d11c63198a53d812c2442:/http.c diff --git a/http.c b/http.c index b9d8c58..f32dce0 100644 --- a/http.c +++ b/http.c @@ -1,5 +1,5 @@ /* darkstat 3 - * copyright (c) 2001-2008 Emil Mikulic. + * copyright (c) 2001-2016 Emil Mikulic. * * http.c: embedded webserver. * This borrows a lot of code from darkhttpd. @@ -8,14 +8,16 @@ * GNU General Public License version 2. (see COPYING.GPL) */ -#include "darkstat.h" -#include "http.h" +#include "cdefs.h" +#include "config.h" #include "conv.h" -#include "hosts_db.h" -#include "graph_db.h" #include "err.h" -#include "queue.h" +#include "graph_db.h" +#include "hosts_db.h" +#include "http.h" #include "now.h" +#include "queue.h" +#include "str.h" #include #include @@ -31,32 +33,34 @@ #include #include #include +#include #include #include +static char *http_base_url = NULL; +static int http_base_len = 0; + static const char mime_type_xml[] = "text/xml"; static const char mime_type_html[] = "text/html; charset=us-ascii"; static const char mime_type_css[] = "text/css"; static const char mime_type_js[] = "text/javascript"; -static const char encoding_gzip[] = - "Vary: Accept-Encoding\r\n" - "Content-Encoding: gzip\r\n"; +static const char mime_type_png[] = "image/png"; +static const char encoding_identity[] = "identity"; +static const char encoding_gzip[] = "gzip"; static const char server[] = PACKAGE_NAME "/" PACKAGE_VERSION; static int idletime = 60; -static int sockin = -1; /* socket to accept connections from */ #define MAX_REQUEST_LENGTH 4000 -#ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif +static int *insocks = NULL; +static unsigned int insock_num = 0; struct connection { LIST_ENTRY(connection) entries; int socket; struct sockaddr_storage client; - time_t last_active; + time_t last_active_mono; enum { RECV_REQUEST, /* receiving request */ SEND_HEADER_AND_REPLY, /* try to send header+reply together */ @@ -88,6 +92,13 @@ struct connection { static LIST_HEAD(conn_list_head, connection) connlist = LIST_HEAD_INITIALIZER(conn_list_head); +struct bindaddr_entry { + STAILQ_ENTRY(bindaddr_entry) entries; + const char *s; +}; +static STAILQ_HEAD(bindaddrs_head, bindaddr_entry) bindaddrs = + STAILQ_HEAD_INITIALIZER(bindaddrs); + /* --------------------------------------------------------------------------- * Decode URL by converting %XX (where XX are hexadecimal digits) to the * character it represents. Don't forget to free the return value. @@ -259,8 +270,8 @@ static struct connection *new_connection(void) struct connection *conn = xmalloc(sizeof(*conn)); conn->socket = -1; - memset(&conn->client, '\0', sizeof(conn->client)); - conn->last_active = now; + memset(&conn->client, 0, sizeof(conn->client)); + conn->last_active_mono = now_mono(); conn->request = NULL; conn->request_length = 0; conn->accept_gzip = 0; @@ -269,7 +280,7 @@ static struct connection *new_connection(void) conn->query = NULL; conn->header = NULL; conn->mime_type = NULL; - conn->encoding = ""; + conn->encoding = NULL; conn->header_extra = ""; conn->header_length = 0; conn->header_sent = 0; @@ -295,7 +306,7 @@ static struct connection *new_connection(void) /* --------------------------------------------------------------------------- * Accept a connection from sockin and add it to the connection queue. */ -static void accept_connection(void) +static void accept_connection(const int sockin) { struct sockaddr_storage addrin; socklen_t sin_size; @@ -324,10 +335,10 @@ static void accept_connection(void) memcpy(&conn->client, &addrin, sizeof(conn->client)); LIST_INSERT_HEAD(&connlist, conn, entries); - getnameinfo((struct sockaddr *) &addrin, sizeof(addrin), + getnameinfo((struct sockaddr *) &addrin, sin_size, ipaddr, sizeof(ipaddr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); - verbosef("accepted connection from %s:%u", ipaddr, portstr); + verbosef("accepted connection from %s:%s", ipaddr, portstr); } @@ -338,13 +349,16 @@ static void accept_connection(void) 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); } @@ -354,13 +368,47 @@ static void free_connection(struct connection *conn) * buffer is returned for convenience. */ #define DATE_LEN 30 /* strlen("Fri, 28 Feb 2003 00:02:08 GMT")+1 */ -static char *rfc1123_date(char *dest, const time_t when) -{ - time_t tmp = when; +static char *rfc1123_date(char *dest, time_t when) { if (strftime(dest, DATE_LEN, - "%a, %d %b %Y %H:%M:%S %Z", gmtime(&tmp) ) == 0) + "%a, %d %b %Y %H:%M:%S %Z", gmtime(&when) ) == 0) errx(1, "strftime() failed [%s]", dest); - return (dest); + return dest; +} + +static void generate_header(struct connection *conn, + const int code, const char *text) +{ + char date[DATE_LEN]; + + assert(conn->header == NULL); + assert(conn->mime_type != NULL); + if (conn->encoding == NULL) + conn->encoding = encoding_identity; + + verbosef("http: %d %s (%s: %zu bytes)", + code, + text, + conn->encoding, + conn->reply_length); + conn->header_length = xasprintf(&(conn->header), + "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Server: %s\r\n" + "Vary: Accept-Encoding\r\n" + "Content-Type: %s\r\n" + "Content-Length: %qu\r\n" + "Content-Encoding: %s\r\n" + "X-Robots-Tag: noindex, noarchive\r\n" + "%s" + "\r\n", + code, text, + rfc1123_date(date, now_real()), + server, + conn->mime_type, + (qu)conn->reply_length, + conn->encoding, + conn->header_extra); + conn->http_code = code; } @@ -368,39 +416,34 @@ static char *rfc1123_date(char *dest, const time_t when) /* --------------------------------------------------------------------------- * A default reply for any (erroneous) occasion. */ +static void default_reply(struct connection *conn, + const int errcode, const char *errname, const char *format, ...) + _printflike_(4, 5); static void default_reply(struct connection *conn, const int errcode, const char *errname, const char *format, ...) { - char *reason, date[DATE_LEN]; + char *reason; va_list va; va_start(va, format); xvasprintf(&reason, format, va); va_end(va); - /* Only really need to calculate the date once. */ - (void)rfc1123_date(date, now); - conn->reply_length = xasprintf(&(conn->reply), "%d %s\n" "

%s

\n" /* errname */ "%s\n" /* reason */ "
\n" - "Generated by %s on %s\n" + "Generated by %s" "\n", - errcode, errname, errname, reason, server, date); + errcode, errname, errname, reason, server); free(reason); - conn->header_length = xasprintf(&(conn->header), - "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Server: %s\r\n" - "Content-Length: %d\r\n" - "Content-Type: text/html\r\n" - "\r\n", - errcode, errname, date, server, conn->reply_length); - - conn->http_code = errcode; + /* forget any dangling metadata */ + conn->mime_type = mime_type_html; + conn->encoding = encoding_identity; + + generate_header(conn, errcode, errname); } @@ -499,7 +542,7 @@ static_style_css(struct connection *conn) { #include "stylecss.h" - conn->reply = style_css; + conn->reply = (char*)style_css; conn->reply_length = style_css_len; conn->reply_dont_free = 1; conn->mime_type = mime_type_css; @@ -513,12 +556,26 @@ static_graph_js(struct connection *conn) { #include "graphjs.h" - conn->reply = graph_js; + conn->reply = (char*)graph_js; conn->reply_length = graph_js_len; conn->reply_dont_free = 1; conn->mime_type = mime_type_js; } +/* --------------------------------------------------------------------------- + * Web interface: favicon. + */ +static void +static_favicon(struct connection *conn) +{ +#include "favicon.h" + + conn->reply = (char*)favicon_png; + conn->reply_length = sizeof(favicon_png); + conn->reply_dont_free = 1; + conn->mime_type = mime_type_png; +} + /* --------------------------------------------------------------------------- * gzip a reply, if requested and possible. Don't bother with a minimum * length requirement, I've never seen a page fail to compress. @@ -540,11 +597,16 @@ process_gzip(struct connection *conn) zs.zfree = Z_NULL; zs.opaque = Z_NULL; - if (deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED, - 15+16, /* 15 = biggest window, 16 = add gzip header+trailer */ - 8 /* default */, - Z_DEFAULT_STRATEGY) != Z_OK) - return; + if (deflateInit2(&zs, + Z_BEST_COMPRESSION, + Z_DEFLATED, + 15+16, /* 15 = biggest window, + 16 = add gzip header+trailer */ + 8 /* default */, + Z_DEFAULT_STRATEGY) != Z_OK) { + free(buf); + return; + } zs.avail_in = conn->reply_length; zs.next_in = (unsigned char *)conn->reply; @@ -555,7 +617,7 @@ process_gzip(struct connection *conn) if (deflate(&zs, Z_FINISH) != Z_STREAM_END) { deflateEnd(&zs); free(buf); - verbosef("failed to compress %u bytes", (unsigned int)len); + verbosef("failed to compress %zu bytes", len); return; } @@ -563,7 +625,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; @@ -575,23 +636,32 @@ process_gzip(struct connection *conn) */ static void process_get(struct connection *conn) { - char *decoded_url, *safe_url; - char date[DATE_LEN]; + char *safe_url; verbosef("http: %s \"%s\" %s", conn->method, conn->uri, (conn->query == NULL)?"":conn->query); - /* work out path of file being requested */ - decoded_url = urldecode(conn->uri); - - /* make sure it's safe */ - safe_url = make_safe_uri(decoded_url); - free(decoded_url); - if (safe_url == NULL) { - default_reply(conn, 400, "Bad Request", - "You requested an invalid URI: %s", conn->uri); - return; + /* Decode the URL being requested. */ + char *decoded_url; + char *decoded_url_offset; + + decoded_url = urldecode(conn->uri); + + /* Optionally strip the base. */ + decoded_url_offset = decoded_url; + if (str_starts_with(decoded_url, http_base_url)) { + decoded_url_offset += http_base_len - 1; + } + + /* Make sure it's safe. */ + safe_url = make_safe_uri(decoded_url_offset); + free(decoded_url); + if (safe_url == NULL) { + default_reply(conn, 400, "Bad Request", + "You requested an invalid URI: %s", conn->uri); + return; + } } if (strcmp(safe_url, "/") == 0) { @@ -605,6 +675,7 @@ static void process_get(struct connection *conn) if (buf == NULL) { default_reply(conn, 404, "Not Found", "The page you requested could not be found."); + free(safe_url); return; } str_extract(buf, &(conn->reply_length), &(conn->reply)); @@ -621,29 +692,20 @@ static void process_get(struct connection *conn) static_style_css(conn); else if (strcmp(safe_url, "/graph.js") == 0) static_graph_js(conn); - else { + else if (strcmp(safe_url, "/favicon.ico") == 0) { + /* serves a PNG instead of an ICO, might cause problems for IE6 */ + static_favicon(conn); + } else { default_reply(conn, 404, "Not Found", "The page you requested could not be found."); + free(safe_url); return; } free(safe_url); process_gzip(conn); assert(conn->mime_type != NULL); - conn->header_length = xasprintf(&(conn->header), - "HTTP/1.1 200 OK\r\n" - "Date: %s\r\n" - "Server: %s\r\n" - "Content-Length: %d\r\n" - "Content-Type: %s\r\n" - "%s" - "%s" - "\r\n" - , - rfc1123_date(date, now), server, - conn->reply_length, conn->mime_type, conn->encoding, - conn->header_extra); - conn->http_code = 200; + generate_header(conn, 200, "OK"); } @@ -679,10 +741,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 */ } @@ -692,11 +750,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) { @@ -705,8 +762,7 @@ static void poll_recv_request(struct connection *conn) conn->state = DONE; return; } - conn->last_active = now; - #undef BUFSIZE + conn->last_active_mono = now_mono(); /* append to conn->request */ conn->request = xrealloc(conn->request, conn->request_length+recvd+1); @@ -714,17 +770,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 */ } } @@ -748,13 +811,11 @@ static void poll_send_header_and_reply(struct connection *conn) iov[0].iov_base = conn->header; iov[0].iov_len = conn->header_length; - iov[1].iov_base = conn->reply + conn->reply_sent; - iov[1].iov_len = conn->reply_length - conn->reply_sent; + iov[1].iov_base = conn->reply; + iov[1].iov_len = conn->reply_length; sent = writev(conn->socket, iov, 2); - conn->last_active = now; - verbosef("poll_send_header_and_reply(%d) sent %d bytes", - conn->socket, (int)sent); + conn->last_active_mono = now_mono(); /* handle any errors (-1) or closure (0) in send() */ if (sent < 1) { @@ -776,7 +837,7 @@ static void poll_send_header_and_reply(struct connection *conn) conn->header_sent = conn->header_length; sent -= conn->header_length; - if (conn->reply_sent + sent < conn->reply_length) { + if (sent < (ssize_t)conn->reply_length) { verbosef("partially sent reply"); conn->reply_sent += sent; conn->state = SEND_REPLY; @@ -796,7 +857,7 @@ static void poll_send_header(struct connection *conn) sent = send(conn->socket, conn->header + conn->header_sent, conn->header_length - conn->header_sent, 0); - conn->last_active = now; + conn->last_active_mono = now_mono(); dverbosef("poll_send_header(%d) sent %d bytes", conn->socket, (int)sent); /* handle any errors (-1) or closure (0) in send() */ @@ -832,7 +893,7 @@ static void poll_send_reply(struct connection *conn) sent = send(conn->socket, conn->reply + conn->reply_sent, conn->reply_length - conn->reply_sent, 0); - conn->last_active = now; + conn->last_active_mono = now_mono(); dverbosef("poll_send_reply(%d) sent %d: [%d-%d] of %d", conn->socket, (int)sent, (int)conn->reply_sent, @@ -858,68 +919,165 @@ static void poll_send_reply(struct connection *conn) -/* --------------------------------------------------------------------------- - * Initialize the sockin global. This is the socket that we accept - * connections from. Pass -1 as max_conn for system limit. +/* -------------------------------------------------------------------------- + * Initialize the base url. + */ +void http_init_base(const char *url) { + char *slashed_url, *safe_url; + size_t urllen; + + if (url == NULL) { + http_base_url = strdup("/"); + } else { + /* Make sure that the url has leading and trailing slashes. */ + urllen = strlen(url); + slashed_url = xmalloc(urllen+3); + slashed_url[0] = '/'; + memcpy(slashed_url+1, url, urllen); /* don't copy NUL */ + slashed_url[urllen+1] = '/'; + slashed_url[urllen+2] = '\0'; + + /* Clean the url. */ + safe_url = make_safe_uri(slashed_url); + free(slashed_url); + if (safe_url == NULL) { + verbosef("invalid base \"%s\", ignored", url); + http_base_url = strdup("/"); /* set to default */ + } else { + http_base_url = safe_url; + } + } + http_base_len = strlen(http_base_url); + verbosef("set base url to \"%s\"", http_base_url); +} + +/* 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 *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; + struct addrinfo hints, *ai; + char portstr[6]; + int ret; - memset(&hints, '\0', sizeof(hints)); + memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + 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", + bindaddr ? bindaddr : "NULL", portstr, gai_strerror(ret)); + if (ai == NULL) + err(1, "getaddrinfo() returned NULL pointer"); + return ai; +} + +void http_add_bindaddr(const char *bindaddr) +{ + struct bindaddr_entry *ent; + + ent = xmalloc(sizeof(*ent)); + ent->s = bindaddr; + STAILQ_INSERT_TAIL(&bindaddrs, ent, entries); +} + +static void http_listen_one(struct addrinfo *ai, + const unsigned short bindport) +{ + char ipaddr[INET6_ADDRSTRLEN]; + int sockin, sockopt, ret; + + /* 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)); + + /* create incoming socket */ + if ((sockin = socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol)) == -1) { + warn("http_listen_one(%s, %u): socket(%d (%s), %d, %d) failed", + ipaddr, (unsigned int)bindport, + ai->ai_family, + (ai->ai_family == AF_INET6) ? "AF_INET6" : + (ai->ai_family == AF_INET) ? "AF_INET" : + "?", + ai->ai_socktype, ai->ai_protocol); + return; + } - if (ret = getaddrinfo(bindaddr, portstr, &hints, &aiptr)) - err(1, "getaddrinfo(): %s", gai_strerror(ret)); + fd_set_nonblock(sockin); - 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) + err(1, "can't set SO_REUSEADDR"); - /* reuse address */ +#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, SOL_SOCKET, SO_REUSEADDR, - &sockopt, sizeof(sockopt)) == -1) { - close(sockin); - continue; - } + if (setsockopt(sockin, IPPROTO_IPV6, IPV6_V6ONLY, + &sockopt, sizeof(sockopt)) == -1) + err(1, "can't set IPV6_V6ONLY"); + } +#endif - /* Recover address and port strings. */ - getnameinfo(ai->ai_addr, ai->ai_addrlen, ipaddr, sizeof(ipaddr), - NULL, 0, NI_NUMERICHOST); + /* bind socket */ + if (bind(sockin, ai->ai_addr, ai->ai_addrlen) == -1) { + warn("bind(\"%s\") failed", ipaddr); + close(sockin); + return; + } - /* bind socket */ - memcpy(&addrin, ai->ai_addr, ai->ai_addrlen); - if (bind(sockin, (struct sockaddr *)&addrin, - sizeof(addrin)) == -1) { - close(sockin); - continue; - } + /* listen on socket */ + if (listen(sockin, 128) == -1) + err(1, "listen() failed"); - verbosef("listening on %s:%u", ipaddr, bindport); + verbosef("listening on http://%s%s%s:%u%s", + (ai->ai_family == AF_INET6) ? "[" : "", + ipaddr, + (ai->ai_family == AF_INET6) ? "]" : "", + bindport, + http_base_url); - /* listen on socket */ - if (listen(sockin, max_conn) >= 0) - /* Successfully bound and now listening. */ - break; + /* add to insocks */ + insocks = xrealloc(insocks, sizeof(*insocks) * (insock_num + 1)); + insocks[insock_num++] = sockin; +} - /* Next candidate. */ - continue; - } +/* Initialize the http sockets and listen on them. */ +void http_listen(const unsigned short bindport) +{ + /* If the user didn't specify any bind addresses, add a NULL. + * This will become a wildcard. + */ + if (STAILQ_EMPTY(&bindaddrs)) + http_add_bindaddr(NULL); - freeaddrinfo(aiptr); + /* Listen on every specified interface. */ + while (!STAILQ_EMPTY(&bindaddrs)) { + struct bindaddr_entry *bindaddr = STAILQ_FIRST(&bindaddrs); + struct addrinfo *ai, *ais = get_bind_addr(bindaddr->s, bindport); - if (ai == NULL) - err(1, "getaddrinfo() unable to locate address"); + /* There could be multiple addresses returned, handle them all. */ + for (ai = ais; ai; ai = ai->ai_next) + http_listen_one(ai, bindport); + + freeaddrinfo(ais); + + STAILQ_REMOVE_HEAD(&bindaddrs, entries); + free(bindaddr); + } + + if (insocks == NULL) + errx(1, "was not able to bind any ports for http interface"); /* ignore SIGPIPE */ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) @@ -937,30 +1095,37 @@ http_fd_set(fd_set *recv_set, fd_set *send_set, int *max_fd, { struct connection *conn, *next; int minidle = idletime + 1; + unsigned int i; #define MAX_FD_SET(sock, fdset) do { \ - FD_SET(sock, fdset); *max_fd = max(*max_fd, sock); } while(0) + FD_SET(sock, fdset); *max_fd = MAX(*max_fd, sock); } while(0) - MAX_FD_SET(sockin, recv_set); + for (i=0; ilast_active; + int idlefor = now_mono() - conn->last_active_mono; /* Time out dead connections. */ - if (idlefor >= idletime) - { + if (idlefor >= idletime) { char ipaddr[INET6_ADDRSTRLEN]; - getnameinfo((struct sockaddr *) &conn->client, sizeof(conn->client), - ipaddr, sizeof(ipaddr), NULL, 0, NI_NUMERICHOST); - verbosef("http socket timeout from %s (fd %d)", - ipaddr, conn->socket); + /* FIXME: this is too late on FreeBSD, socket is invalid */ + int ret = getnameinfo((struct sockaddr *)&conn->client, + sizeof(conn->client), ipaddr, sizeof(ipaddr), + NULL, 0, NI_NUMERICHOST); + if (ret == 0) + verbosef("http socket timeout from %s (fd %d)", + ipaddr, conn->socket); + else + warn("http socket timeout: getnameinfo error: %s", + gai_strerror(ret)); conn->state = DONE; } /* Connections that need a timeout. */ if (conn->state != DONE) - minidle = min(minidle, (idletime - idlefor)); + minidle = MIN(minidle, (idletime - idlefor)); switch (conn->state) { @@ -1002,8 +1167,11 @@ http_fd_set(fd_set *recv_set, fd_set *send_set, int *max_fd, void http_poll(fd_set *recv_set, fd_set *send_set) { struct connection *conn; + unsigned int i; - if (FD_ISSET(sockin, recv_set)) accept_connection(); + for (i=0; istate) @@ -1024,8 +1192,30 @@ void http_poll(fd_set *recv_set, fd_set *send_set) if (FD_ISSET(conn->socket, send_set)) poll_send_reply(conn); break; + case DONE: /* fallthrough */ default: errx(1, "invalid state"); } } +void http_stop(void) { + struct connection *conn; + struct connection *next; + unsigned int i; + + free(http_base_url); + + /* Close listening sockets. */ + for (i=0; i