/* 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.
* 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 <sys/uio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
#include <zlib.h>
+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;
- in_addr_t client;
- time_t last_active;
+ struct sockaddr_storage client;
+ time_t last_active_mono;
enum {
RECV_REQUEST, /* receiving request */
SEND_HEADER_AND_REPLY, /* try to send header+reply together */
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.
struct connection *conn = xmalloc(sizeof(*conn));
conn->socket = -1;
- conn->client = INADDR_ANY;
- 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;
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;
/* ---------------------------------------------------------------------------
* 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_in addrin;
+ struct sockaddr_storage addrin;
socklen_t sin_size;
struct connection *conn;
+ char ipaddr[INET6_ADDRSTRLEN], portstr[12];
int sock;
- sin_size = (socklen_t)sizeof(struct sockaddr);
+ sin_size = (socklen_t)sizeof(addrin);
sock = accept(sockin, (struct sockaddr *)&addrin, &sin_size);
if (sock == -1)
{
conn = new_connection();
conn->socket = sock;
conn->state = RECV_REQUEST;
- conn->client = addrin.sin_addr.s_addr;
+ memcpy(&conn->client, &addrin, sizeof(conn->client));
LIST_INSERT_HEAD(&connlist, conn, entries);
- verbosef("accepted connection from %s:%u",
- inet_ntoa(addrin.sin_addr),
- ntohs(addrin.sin_port) );
+ getnameinfo((struct sockaddr *) &addrin, sin_size,
+ ipaddr, sizeof(ipaddr), portstr, sizeof(portstr),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ verbosef("accepted connection from %s:%s", ipaddr, portstr);
}
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);
}
* 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;
}
/* ---------------------------------------------------------------------------
* 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),
"<html><head><title>%d %s</title></head><body>\n"
"<h1>%s</h1>\n" /* errname */
"%s\n" /* reason */
"<hr>\n"
- "Generated by %s on %s\n"
+ "Generated by %s"
"</body></html>\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);
}
{
#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;
{
#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.
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;
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;
}
conn->reply_dont_free = 0;
else
free(conn->reply);
-
conn->reply = buf;
conn->reply_length -= zs.avail_out;
conn->encoding = encoding_gzip;
*/
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) {
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));
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");
}
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 */
}
*/
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)
{
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);
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 */
}
}
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) {
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;
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() */
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,
-/* ---------------------------------------------------------------------------
- * 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 in_addr_t bindaddr, const unsigned short bindport,
- const int max_conn)
+static struct addrinfo *get_bind_addr(
+ const char *bindaddr, const unsigned short bindport)
+{
+ struct addrinfo hints, *ai;
+ char portstr[6];
+ int ret;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ 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",
+ 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)
{
- struct sockaddr_in addrin;
- int sockopt;
+ 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 */
- sockin = socket(PF_INET, SOCK_STREAM, 0);
- if (sockin == -1) err(1, "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;
+ }
+
+ fd_set_nonblock(sockin);
/* reuse address */
sockopt = 1;
if (setsockopt(sockin, SOL_SOCKET, SO_REUSEADDR,
&sockopt, sizeof(sockopt)) == -1)
- err(1, "setsockopt(SO_REUSEADDR)");
+ err(1, "can't set SO_REUSEADDR");
- /* 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(%s:%u)", inet_ntoa(addrin.sin_addr), bindport);
+#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
- verbosef("listening on %s:%u", inet_ntoa(addrin.sin_addr), bindport);
+ /* bind socket */
+ if (bind(sockin, ai->ai_addr, ai->ai_addrlen) == -1) {
+ warn("bind(\"%s\") failed", ipaddr);
+ close(sockin);
+ return;
+ }
/* listen on socket */
- if (listen(sockin, max_conn) == -1)
- err(1, "listen()");
+ if (listen(sockin, 128) == -1)
+ err(1, "listen() failed");
+
+ verbosef("listening on http://%s%s%s:%u%s",
+ (ai->ai_family == AF_INET6) ? "[" : "",
+ ipaddr,
+ (ai->ai_family == AF_INET6) ? "]" : "",
+ bindport,
+ http_base_url);
+
+ /* add to insocks */
+ insocks = xrealloc(insocks, sizeof(*insocks) * (insock_num + 1));
+ insocks[insock_num++] = sockin;
+}
+
+/* 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);
+
+ /* 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);
+
+ /* 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)
{
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; i<insock_num; i++)
+ MAX_FD_SET(insocks[i], recv_set);
LIST_FOREACH_SAFE(conn, &connlist, entries, next)
{
- int idlefor = now - conn->last_active;
+ int idlefor = now_mono() - conn->last_active_mono;
/* Time out dead connections. */
- if (idlefor >= idletime)
- {
- struct sockaddr_in addrin;
- addrin.sin_addr.s_addr = conn->client;
- verbosef("http socket timeout from %s (fd %d)",
- inet_ntoa(addrin.sin_addr),
- conn->socket);
+ if (idlefor >= idletime) {
+ char ipaddr[INET6_ADDRSTRLEN];
+ /* 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)
{
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; i<insock_num; i++)
+ if (FD_ISSET(insocks[i], recv_set))
+ accept_connection(insocks[i]);
LIST_FOREACH(conn, &connlist, entries)
switch (conn->state)
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<insock_num; i++)
+ close(insocks[i]);
+ free(insocks);
+ insocks = NULL;
+
+ /* Close in-flight connections. */
+ LIST_FOREACH_SAFE(conn, &connlist, entries, next) {
+ LIST_REMOVE(conn, entries);
+ free_connection(conn);
+ free(conn);
+ }
+}
+
/* vim:set ts=4 sw=4 et tw=78: */