#include "daylog.h"
#include "db.h"
#include "dns.h"
+#include "err.h"
#include "http.h"
#include "hosts_db.h"
#include "localip.h"
#include "ncache.h"
#include "pidfile.h"
-#include "err.h"
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <netdb.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
static void cb_port(const char *arg)
{ opt_bindport = (unsigned short)parsenum(arg, 65536); }
-const char *opt_bindaddr = NULL;
-static void cb_bindaddr(const char *arg)
-{
- struct addrinfo hints, *ai;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_flags = AI_PASSIVE;
-#ifdef AI_ADDRCONFIG
- hints.ai_flags |= AI_ADDRCONFIG;
-#endif
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
-
- if (getaddrinfo(arg, NULL, &hints, &ai))
- errx(1, "malformed address \"%s\"", arg);
-
- freeaddrinfo(ai);
- opt_bindaddr = arg;
-}
+static void cb_bindaddr(const char *arg) { http_add_bindaddr(arg); }
const char *opt_filter = NULL;
static void cb_filter(const char *arg) { opt_filter = arg; }
{"--no-macs", NULL, cb_no_macs, 0},
{"--no-lastseen", NULL, cb_no_lastseen, 0},
{"-p", "port", cb_port, 0},
- {"-b", "bindaddr", cb_bindaddr, 0},
+ {"-b", "bindaddr", cb_bindaddr, -1},
{"-f", "filter", cb_filter, 0},
{"-l", "network/netmask", cb_local, 0},
{"--chroot", "dir", cb_chroot, 0},
exit(EXIT_FAILURE);
}
- arg->num_seen++;
+ if (arg->num_seen != -1) /* accept more than one */
+ arg->num_seen++;
+
if (arg->arg_name == NULL) {
arg->callback(NULL);
parse_sub_cmdline(argc-1, argv+1);
/* do this first as it forks - minimize memory use */
if (opt_want_dns) dns_init(opt_privdrop_user);
cap_init(opt_interface, opt_filter, opt_want_promisc); /* needs root */
- http_init(opt_bindaddr, opt_bindport, /*maxconn=*/-1);
+ http_listen(opt_bindport);
ncache_init(); /* must do before chroot() */
privdrop(opt_chroot_dir, opt_privdrop_user);
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
+static int *insocks = NULL;
+static unsigned int insock_num = 0;
+
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
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.
/* ---------------------------------------------------------------------------
* 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;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
+#ifdef linux
if (bindaddr == NULL)
hints.ai_family = AF_INET6; /* dual stack socket */
+#endif
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
#ifdef AI_ADDRCONFIG
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;
}
-/* --------------------------------------------------------------------------
- * 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 *bindaddr, const unsigned short bindport,
- const int max_conn)
+void http_add_bindaddr(const char *bindaddr)
{
- struct addrinfo *ai;
- char ipaddr[INET6_ADDRSTRLEN];
- int sockopt, ret;
+ struct bindaddr_entry *ent;
+
+ ent = xmalloc(sizeof(*ent));
+ ent->s = bindaddr;
+ STAILQ_INSERT_TAIL(&bindaddrs, ent, entries);
+}
- ai = get_bind_addr(bindaddr, bindport);
+static void http_listen_one(struct addrinfo *ai,
+ const unsigned short bindport)
+{
+ char ipaddr[INET6_ADDRSTRLEN];
+ int sockin, sockopt, ret;
/* create incoming socket */
if ((sockin = socket(ai->ai_family, SOCK_STREAM, 0)) == -1)
&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");
- }
-
/* format address into ipaddr string */
if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, ipaddr,
sizeof(ipaddr), NULL, 0, NI_NUMERICHOST)) != 0)
if (bind(sockin, ai->ai_addr, ai->ai_addrlen) == -1)
err(1, "bind(\"%s\") failed", ipaddr);
+ /* listen on socket */
+ if (listen(sockin, -1) == -1)
+ err(1, "listen() failed");
+
verbosef("listening on http://%s%s%s:%u/",
(ai->ai_family == AF_INET6) ? "[" : "",
ipaddr,
(ai->ai_family == AF_INET6) ? "]" : "",
bindport);
- freeaddrinfo(ai);
+ /* add to insocks */
+ insocks = xrealloc(insocks, sizeof(*insocks) * (insock_num + 1));
+ insocks[insock_num++] = sockin;
+}
- /* listen on socket */
- if (listen(sockin, max_conn) == -1)
- err(1, "listen() failed");
+/* 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);
+ }
/* 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)
- 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)
{
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)
}
void http_stop(void) {
- close(sockin);
+ struct connection *conn;
+ unsigned int i;
+
+ /* Close listening sockets. */
+ for (i=0; i<insock_num; i++)
+ close(insocks[i]);
+ free(insocks);
+ insocks = NULL;
+
+ /* Close in-flight connections. */
+ LIST_FOREACH(conn, &connlist, entries) {
+ LIST_REMOVE(conn, entries);
+ free_connection(conn);
+ free(conn);
+ }
}
/* vim:set ts=4 sw=4 et tw=78: */