"Fossies" - the Fresh Open Source Software Archive

Member "darkstat-3.0.721/http.c" (12 Jan 2022, 34616 Bytes) of package /linux/privat/darkstat-3.0.721.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "http.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.0.719_vs_3.0.721.

    1 /* darkstat 3
    2  * copyright (c) 2001-2016 Emil Mikulic.
    3  *
    4  * http.c: embedded webserver.
    5  * This borrows a lot of code from darkhttpd.
    6  *
    7  * You may use, modify and redistribute this file under the terms of the
    8  * GNU General Public License version 2. (see COPYING.GPL)
    9  */
   10 
   11 #include "cdefs.h"
   12 #include "config.h"
   13 #include "conv.h"
   14 #include "err.h"
   15 #include "graph_db.h"
   16 #include "hosts_db.h"
   17 #include "http.h"
   18 #include "now.h"
   19 #include "queue.h"
   20 #include "str.h"
   21 
   22 #include <sys/uio.h>
   23 #include <sys/socket.h>
   24 #include <arpa/inet.h>
   25 #include <netinet/in.h>
   26 #include <netdb.h>
   27 #include <assert.h>
   28 #include <ctype.h>
   29 #include <errno.h>
   30 #include <fcntl.h>
   31 #include <signal.h>
   32 #include <stdarg.h>
   33 #include <stdio.h>
   34 #include <stdlib.h>
   35 #include <string.h>
   36 #include <time.h>
   37 #include <unistd.h>
   38 #include <zlib.h>
   39 
   40 static char *http_base_url = NULL;
   41 static int http_base_len = 0;
   42 
   43 static const char mime_type_xml[] = "text/xml";
   44 static const char mime_type_html[] = "text/html; charset=us-ascii";
   45 static const char mime_type_text_prometheus[] = "text/plain; version=0.0.4";
   46 static const char mime_type_css[] = "text/css";
   47 static const char mime_type_js[] = "text/javascript";
   48 static const char mime_type_png[] = "image/png";
   49 static const char encoding_identity[] = "identity";
   50 static const char encoding_gzip[] = "gzip";
   51 
   52 static const char server[] = PACKAGE_NAME "/" PACKAGE_VERSION;
   53 static int idletime = 60;
   54 #define MAX_REQUEST_LENGTH 4000
   55 
   56 static int *insocks = NULL;
   57 static unsigned int insock_num = 0;
   58 
   59 struct connection {
   60     LIST_ENTRY(connection) entries;
   61 
   62     int socket;
   63     struct sockaddr_storage client;
   64     time_t last_active_mono;
   65     enum {
   66         RECV_REQUEST,          /* receiving request */
   67         SEND_HEADER_AND_REPLY, /* try to send header+reply together */
   68         SEND_HEADER,           /* sending generated header */
   69         SEND_REPLY,            /* sending reply */
   70         DONE                   /* conn closed, need to remove from queue */
   71         } state;
   72 
   73     /* char request[request_length+1] is null-terminated */
   74     char *request;
   75     size_t request_length;
   76     int accept_gzip;
   77 
   78     /* request fields */
   79     char *method, *uri, *query; /* query can be NULL */
   80 
   81     char *header;
   82     const char *mime_type, *encoding, *header_extra;
   83     size_t header_length, header_sent;
   84     int header_dont_free, header_only, http_code;
   85 
   86     char *reply;
   87     int reply_dont_free;
   88     size_t reply_length, reply_sent;
   89 
   90     unsigned int total_sent; /* header + body = total, for logging */
   91 };
   92 
   93 static LIST_HEAD(conn_list_head, connection) connlist =
   94     LIST_HEAD_INITIALIZER(conn_list_head);
   95 
   96 struct bindaddr_entry {
   97     STAILQ_ENTRY(bindaddr_entry) entries;
   98     const char *s;
   99 };
  100 static STAILQ_HEAD(bindaddrs_head, bindaddr_entry) bindaddrs =
  101     STAILQ_HEAD_INITIALIZER(bindaddrs);
  102 
  103 /* ---------------------------------------------------------------------------
  104  * Decode URL by converting %XX (where XX are hexadecimal digits) to the
  105  * character it represents.  Don't forget to free the return value.
  106  */
  107 static char *urldecode(const char *url)
  108 {
  109     size_t i, len = strlen(url);
  110     char *out = xmalloc(len+1);
  111     int pos;
  112 
  113     for (i=0, pos=0; i<len; i++)
  114     {
  115         if (url[i] == '%' && i+2 < len &&
  116             isxdigit(url[i+1]) && isxdigit(url[i+2]))
  117         {
  118             /* decode %XX */
  119             #define HEX_TO_DIGIT(hex) ( \
  120                 ((hex) >= 'A' && (hex) <= 'F') ? ((hex)-'A'+10): \
  121                 ((hex) >= 'a' && (hex) <= 'f') ? ((hex)-'a'+10): \
  122                 ((hex)-'0') )
  123 
  124             out[pos++] = HEX_TO_DIGIT(url[i+1]) * 16 +
  125                          HEX_TO_DIGIT(url[i+2]);
  126             i += 2;
  127 
  128             #undef HEX_TO_DIGIT
  129         }
  130         else
  131         {
  132             /* straight copy */
  133             out[pos++] = url[i];
  134         }
  135     }
  136     out[pos] = 0;
  137 #if 0
  138     /* don't really need to realloc here - it's probably a performance hit */
  139     out = xrealloc(out, strlen(out)+1);  /* dealloc what we don't need */
  140 #endif
  141     return (out);
  142 }
  143 
  144 
  145 
  146 /* ---------------------------------------------------------------------------
  147  * Consolidate slashes in-place by shifting parts of the string over repeated
  148  * slashes.
  149  */
  150 static void consolidate_slashes(char *s)
  151 {
  152     size_t left = 0, right = 0;
  153     int saw_slash = 0;
  154 
  155     assert(s != NULL);
  156 
  157     while (s[right] != '\0')
  158     {
  159         if (saw_slash)
  160         {
  161             if (s[right] == '/') right++;
  162             else
  163             {
  164                 saw_slash = 0;
  165                 s[left++] = s[right++];
  166             }
  167         }
  168         else
  169         {
  170             if (s[right] == '/') saw_slash++;
  171             s[left++] = s[right++];
  172         }
  173     }
  174     s[left] = '\0';
  175 }
  176 
  177 
  178 
  179 /* ---------------------------------------------------------------------------
  180  * Resolve /./ and /../ in a URI, returing a new, safe URI, or NULL if the URI
  181  * is invalid/unsafe.  Returned buffer needs to be deallocated.
  182  */
  183 static char *make_safe_uri(char *uri)
  184 {
  185     char **elem, *out;
  186     unsigned int slashes = 0, elements = 0;
  187     size_t urilen, i, j, pos;
  188 
  189     assert(uri != NULL);
  190     if (uri[0] != '/')
  191         return (NULL);
  192     consolidate_slashes(uri);
  193     urilen = strlen(uri);
  194 
  195     /* count the slashes */
  196     for (i=0, slashes=0; i<urilen; i++)
  197         if (uri[i] == '/') slashes++;
  198 
  199     /* make an array for the URI elements */
  200     elem = xmalloc(sizeof(*elem) * slashes);
  201     for (i=0; i<slashes; i++)
  202         elem[i] = (NULL);
  203 
  204     /* split by slashes and build elem[] array */
  205     for (i=1; i<urilen;)
  206     {
  207         /* look for the next slash */
  208         for (j=i; j<urilen && uri[j] != '/'; j++)
  209             ;
  210 
  211         /* process uri[i,j) */
  212         if ((j == i+1) && (uri[i] == '.'))
  213             /* "." */;
  214         else if ((j == i+2) && (uri[i] == '.') && (uri[i+1] == '.'))
  215         {
  216             /* ".." */
  217             if (elements == 0)
  218             {
  219                 /*
  220                  * Unsafe string so free elem[].  All its elements are free
  221                  * at this point.
  222                  */
  223                 free(elem);
  224                 return (NULL);
  225             }
  226             else
  227             {
  228                 elements--;
  229                 free(elem[elements]);
  230             }
  231         }
  232         else elem[elements++] = split_string(uri, i, j);
  233 
  234         i = j + 1; /* uri[j] is a slash - move along one */
  235     }
  236 
  237     /* reassemble */
  238     out = xmalloc(urilen+1); /* it won't expand */
  239     pos = 0;
  240     for (i=0; i<elements; i++)
  241     {
  242         size_t delta = strlen(elem[i]);
  243 
  244         assert(pos <= urilen);
  245         out[pos++] = '/';
  246 
  247         assert(pos+delta <= urilen);
  248         memcpy(out+pos, elem[i], delta);
  249         free(elem[i]);
  250         pos += delta;
  251     }
  252     free(elem);
  253 
  254     if ((elements == 0) || (uri[urilen-1] == '/')) out[pos++] = '/';
  255     assert(pos <= urilen);
  256     out[pos] = '\0';
  257 
  258 #if 0
  259     /* don't really need to do this and it's probably a performance hit: */
  260     /* shorten buffer if necessary */
  261     if (pos != urilen) out = xrealloc(out, strlen(out)+1);
  262 #endif
  263     return (out);
  264 }
  265 
  266 /* ---------------------------------------------------------------------------
  267  * Allocate and initialize an empty connection.
  268  */
  269 static struct connection *new_connection(void)
  270 {
  271     struct connection *conn = xmalloc(sizeof(*conn));
  272 
  273     conn->socket = -1;
  274     memset(&conn->client, 0, sizeof(conn->client));
  275     conn->last_active_mono = now_mono();
  276     conn->request = NULL;
  277     conn->request_length = 0;
  278     conn->accept_gzip = 0;
  279     conn->method = NULL;
  280     conn->uri = NULL;
  281     conn->query = NULL;
  282     conn->header = NULL;
  283     conn->mime_type = NULL;
  284     conn->encoding = NULL;
  285     conn->header_extra = "";
  286     conn->header_length = 0;
  287     conn->header_sent = 0;
  288     conn->header_dont_free = 0;
  289     conn->header_only = 0;
  290     conn->http_code = 0;
  291     conn->reply = NULL;
  292     conn->reply_dont_free = 0;
  293     conn->reply_length = 0;
  294     conn->reply_sent = 0;
  295     conn->total_sent = 0;
  296 
  297     /* Make it harmless so it gets garbage-collected if it should, for some
  298      * reason, fail to be correctly filled out.
  299      */
  300     conn->state = DONE;
  301 
  302     return (conn);
  303 }
  304 
  305 
  306 
  307 /* ---------------------------------------------------------------------------
  308  * Accept a connection from sockin and add it to the connection queue.
  309  */
  310 static void accept_connection(const int sockin)
  311 {
  312     struct sockaddr_storage addrin;
  313     socklen_t sin_size;
  314     struct connection *conn;
  315     char ipaddr[INET6_ADDRSTRLEN], portstr[12];
  316     int sock;
  317 
  318     sin_size = (socklen_t)sizeof(addrin);
  319     sock = accept(sockin, (struct sockaddr *)&addrin, &sin_size);
  320     if (sock == -1)
  321     {
  322         if (errno == ECONNABORTED || errno == EINTR)
  323         {
  324             verbosef("accept() failed: %s", strerror(errno));
  325             return;
  326         }
  327         /* else */ err(1, "accept()");
  328     }
  329 
  330     fd_set_nonblock(sock);
  331 
  332     /* allocate and initialise struct connection */
  333     conn = new_connection();
  334     conn->socket = sock;
  335     conn->state = RECV_REQUEST;
  336     memcpy(&conn->client, &addrin, sizeof(conn->client));
  337     LIST_INSERT_HEAD(&connlist, conn, entries);
  338 
  339     getnameinfo((struct sockaddr *) &addrin, sin_size,
  340             ipaddr, sizeof(ipaddr), portstr, sizeof(portstr),
  341             NI_NUMERICHOST | NI_NUMERICSERV);
  342     verbosef("accepted connection from %s:%s", ipaddr, portstr);
  343 }
  344 
  345 
  346 
  347 /* ---------------------------------------------------------------------------
  348  * Log a connection, then cleanly deallocate its internals.
  349  */
  350 static void free_connection(struct connection *conn)
  351 {
  352     dverbosef("free_connection(%d)", conn->socket);
  353     if (conn->socket != -1)
  354         close(conn->socket);
  355     free(conn->request);
  356     free(conn->method);
  357     free(conn->uri);
  358     free(conn->query);
  359     if (!conn->header_dont_free)
  360         free(conn->header);
  361     if (!conn->reply_dont_free)
  362         free(conn->reply);
  363 }
  364 
  365 
  366 
  367 /* ---------------------------------------------------------------------------
  368  * Format [when] as an RFC1123 date, stored in the specified buffer.  The same
  369  * buffer is returned for convenience.
  370  */
  371 #define DATE_LEN 30 /* strlen("Fri, 28 Feb 2003 00:02:08 GMT")+1 */
  372 static char *rfc1123_date(char *dest, time_t when) {
  373     if (strftime(dest, DATE_LEN,
  374         "%a, %d %b %Y %H:%M:%S %Z", gmtime(&when) ) == 0)
  375             errx(1, "strftime() failed [%s]", dest);
  376     return dest;
  377 }
  378 
  379 static void generate_header(struct connection *conn,
  380     const int code, const char *text)
  381 {
  382     char date[DATE_LEN];
  383 
  384     assert(conn->header == NULL);
  385     assert(conn->mime_type != NULL);
  386     if (conn->encoding == NULL)
  387         conn->encoding = encoding_identity;
  388 
  389     verbosef("http: %d %s (%s: %zu bytes)",
  390              code,
  391              text,
  392              conn->encoding,
  393              conn->reply_length);
  394     conn->header_length = xasprintf(&(conn->header),
  395         "HTTP/1.1 %d %s\r\n"
  396         "Date: %s\r\n"
  397         "Server: %s\r\n"
  398         "Vary: Accept-Encoding\r\n"
  399         "Content-Type: %s\r\n"
  400         "Content-Length: %qu\r\n"
  401         "Content-Encoding: %s\r\n"
  402         "X-Robots-Tag: noindex, noarchive\r\n"
  403         "%s"
  404         "\r\n",
  405         code, text,
  406         rfc1123_date(date, now_real()),
  407         server,
  408         conn->mime_type,
  409         (qu)conn->reply_length,
  410         conn->encoding,
  411         conn->header_extra);
  412     conn->http_code = code;
  413 }
  414 
  415 
  416 
  417 /* ---------------------------------------------------------------------------
  418  * A default reply for any (erroneous) occasion.
  419  */
  420 static void default_reply(struct connection *conn,
  421     const int errcode, const char *errname, const char *format, ...)
  422     _printflike_(4, 5);
  423 static void default_reply(struct connection *conn,
  424     const int errcode, const char *errname, const char *format, ...)
  425 {
  426     char *reason;
  427     va_list va;
  428 
  429     va_start(va, format);
  430     xvasprintf(&reason, format, va);
  431     va_end(va);
  432 
  433     conn->reply_length = xasprintf(&(conn->reply),
  434      "<html><head><title>%d %s</title></head><body>\n"
  435      "<h1>%s</h1>\n" /* errname */
  436      "%s\n" /* reason */
  437      "<hr>\n"
  438      "Generated by %s"
  439      "</body></html>\n",
  440      errcode, errname, errname, reason, server);
  441     free(reason);
  442 
  443     /* forget any dangling metadata */
  444     conn->mime_type = mime_type_html;
  445     conn->encoding = encoding_identity;
  446 
  447     generate_header(conn, errcode, errname);
  448 }
  449 
  450 
  451 
  452 /* ---------------------------------------------------------------------------
  453  * Parses a single HTTP request field.  Returns string from end of [field] to
  454  * first \r, \n or end of request string.  Returns NULL if [field] can't be
  455  * matched.
  456  *
  457  * You need to remember to deallocate the result.
  458  * example: parse_field(conn, "Referer: ");
  459  */
  460 static char *parse_field(const struct connection *conn, const char *field)
  461 {
  462     size_t bound1, bound2;
  463     char *pos;
  464 
  465     /* find start */
  466     pos = strstr(conn->request, field);
  467     if (pos == NULL)
  468         return (NULL);
  469     bound1 = pos - conn->request + strlen(field);
  470 
  471     /* find end */
  472     for (bound2 = bound1;
  473         bound2 < conn->request_length &&
  474         conn->request[bound2] != '\r'; bound2++)
  475             ;
  476 
  477     /* copy to buffer */
  478     return (split_string(conn->request, bound1, bound2));
  479 }
  480 
  481 
  482 
  483 /* ---------------------------------------------------------------------------
  484  * Parse an HTTP request like "GET /hosts/?sort=in HTTP/1.1" to get the method
  485  * (GET), the uri (/hosts/), the query (sort=in) and whether the UA will
  486  * accept gzip encoding.  Remember to deallocate all these buffers.  Query
  487  * can be NULL.  The method will be returned in uppercase.
  488  */
  489 static int parse_request(struct connection *conn)
  490 {
  491     size_t bound1, bound2, mid;
  492     char *accept_enc;
  493 
  494     /* parse method */
  495     for (bound1 = 0; bound1 < conn->request_length &&
  496         conn->request[bound1] != ' '; bound1++)
  497             ;
  498 
  499     conn->method = split_string(conn->request, 0, bound1);
  500     strntoupper(conn->method, bound1);
  501 
  502     /* parse uri */
  503     for (; bound1 < conn->request_length &&
  504         conn->request[bound1] == ' '; bound1++)
  505             ;
  506 
  507     if (bound1 == conn->request_length)
  508         return (0); /* fail */
  509 
  510     for (bound2=bound1+1; bound2 < conn->request_length &&
  511         conn->request[bound2] != ' ' &&
  512         conn->request[bound2] != '\r'; bound2++)
  513             ;
  514 
  515     /* find query string */
  516     for (mid=bound1; mid<bound2 && conn->request[mid] != '?'; mid++)
  517         ;
  518 
  519     if (conn->request[mid] == '?') {
  520         conn->query = split_string(conn->request, mid+1, bound2);
  521         bound2 = mid;
  522     }
  523 
  524     conn->uri = split_string(conn->request, bound1, bound2);
  525 
  526     /* parse important fields */
  527     accept_enc = parse_field(conn, "Accept-Encoding: ");
  528     if (accept_enc != NULL) {
  529         if (strstr(accept_enc, "gzip") != NULL)
  530             conn->accept_gzip = 1;
  531         free(accept_enc);
  532     }
  533     return (1);
  534 }
  535 
  536 /* FIXME: maybe we need a smarter way of doing static pages: */
  537 
  538 /* ---------------------------------------------------------------------------
  539  * Web interface: static stylesheet.
  540  */
  541 static void
  542 static_style_css(struct connection *conn)
  543 {
  544 #include "stylecss.h"
  545 
  546     conn->reply = (char*)style_css;
  547     conn->reply_length = style_css_len;
  548     conn->reply_dont_free = 1;
  549     conn->mime_type = mime_type_css;
  550 }
  551 
  552 /* ---------------------------------------------------------------------------
  553  * Web interface: static JavaScript.
  554  */
  555 static void
  556 static_graph_js(struct connection *conn)
  557 {
  558 #include "graphjs.h"
  559 
  560     conn->reply = (char*)graph_js;
  561     conn->reply_length = graph_js_len;
  562     conn->reply_dont_free = 1;
  563     conn->mime_type = mime_type_js;
  564 }
  565 
  566 /* ---------------------------------------------------------------------------
  567  * Web interface: favicon.
  568  */
  569 static void
  570 static_favicon(struct connection *conn)
  571 {
  572 #include "favicon.h"
  573 
  574     conn->reply = (char*)favicon_png;
  575     conn->reply_length = sizeof(favicon_png);
  576     conn->reply_dont_free = 1;
  577     conn->mime_type = mime_type_png;
  578 }
  579 
  580 /* ---------------------------------------------------------------------------
  581  * gzip a reply, if requested and possible.  Don't bother with a minimum
  582  * length requirement, I've never seen a page fail to compress.
  583  */
  584 static void
  585 process_gzip(struct connection *conn)
  586 {
  587     char *buf;
  588     size_t len;
  589     z_stream zs;
  590 
  591     if (!conn->accept_gzip)
  592         return;
  593 
  594     buf = xmalloc(conn->reply_length);
  595     len = conn->reply_length;
  596 
  597     zs.zalloc = Z_NULL;
  598     zs.zfree = Z_NULL;
  599     zs.opaque = Z_NULL;
  600 
  601     if (deflateInit2(&zs,
  602                      Z_BEST_COMPRESSION,
  603                      Z_DEFLATED,
  604                      15+16, /* 15 = biggest window,
  605                                16 = add gzip header+trailer */
  606                      8 /* default */,
  607                      Z_DEFAULT_STRATEGY) != Z_OK) {
  608         free(buf);
  609         return;
  610     }
  611 
  612     zs.avail_in = conn->reply_length;
  613     zs.next_in = (unsigned char *)conn->reply;
  614 
  615     zs.avail_out = conn->reply_length;
  616     zs.next_out = (unsigned char *)buf;
  617 
  618     if (deflate(&zs, Z_FINISH) != Z_STREAM_END) {
  619         deflateEnd(&zs);
  620         free(buf);
  621         verbosef("failed to compress %zu bytes", len);
  622         return;
  623     }
  624 
  625     if (conn->reply_dont_free)
  626         conn->reply_dont_free = 0;
  627     else
  628         free(conn->reply);
  629     conn->reply = buf;
  630     conn->reply_length -= zs.avail_out;
  631     conn->encoding = encoding_gzip;
  632     deflateEnd(&zs);
  633 }
  634 
  635 /* ---------------------------------------------------------------------------
  636  * Process a GET/HEAD request
  637  */
  638 static void process_get(struct connection *conn)
  639 {
  640     char *safe_url;
  641 
  642     verbosef("http: %s \"%s\" %s", conn->method, conn->uri,
  643         (conn->query == NULL)?"":conn->query);
  644 
  645     {
  646         /* Decode the URL being requested. */
  647         char *decoded_url;
  648         char *decoded_url_offset;
  649 
  650         decoded_url = urldecode(conn->uri);
  651 
  652         /* Optionally strip the base. */
  653         decoded_url_offset = decoded_url;
  654         if (str_starts_with(decoded_url, http_base_url)) {
  655             decoded_url_offset += http_base_len - 1;
  656         }
  657 
  658         /* Make sure it's safe. */
  659         safe_url = make_safe_uri(decoded_url_offset);
  660         free(decoded_url);
  661         if (safe_url == NULL) {
  662             default_reply(conn, 400, "Bad Request",
  663                     "You requested an invalid URI: %s", conn->uri);
  664             return;
  665         }
  666     }
  667 
  668     if (strcmp(safe_url, "/") == 0) {
  669         struct str *buf = html_front_page();
  670         str_extract(buf, &(conn->reply_length), &(conn->reply));
  671         conn->mime_type = mime_type_html;
  672     }
  673     else if (str_starts_with(safe_url, "/hosts/")) {
  674         /* FIXME here - make this saner */
  675         struct str *buf = html_hosts(safe_url, conn->query);
  676         if (buf == NULL) {
  677             default_reply(conn, 404, "Not Found",
  678                 "The page you requested could not be found.");
  679             free(safe_url);
  680             return;
  681         }
  682         str_extract(buf, &(conn->reply_length), &(conn->reply));
  683         conn->mime_type = mime_type_html;
  684     }
  685     else if (str_starts_with(safe_url, "/graphs.xml")) {
  686         struct str *buf = xml_graphs();
  687         str_extract(buf, &(conn->reply_length), &(conn->reply));
  688         conn->mime_type = mime_type_xml;
  689         /* hack around Opera caching the XML */
  690         conn->header_extra = "Pragma: no-cache\r\n";
  691     }
  692     else if (str_starts_with(safe_url, "/metrics")) {
  693         struct str *buf = text_metrics();
  694         str_extract(buf, &(conn->reply_length), &(conn->reply));
  695         conn->mime_type = mime_type_text_prometheus;
  696     }
  697     else if (strcmp(safe_url, "/style.css") == 0)
  698         static_style_css(conn);
  699     else if (strcmp(safe_url, "/graph.js") == 0)
  700         static_graph_js(conn);
  701     else if (strcmp(safe_url, "/favicon.ico") == 0) {
  702         /* serves a PNG instead of an ICO, might cause problems for IE6 */
  703         static_favicon(conn);
  704     } else {
  705         default_reply(conn, 404, "Not Found",
  706             "The page you requested could not be found.");
  707         free(safe_url);
  708         return;
  709     }
  710     free(safe_url);
  711 
  712     process_gzip(conn);
  713     assert(conn->mime_type != NULL);
  714     generate_header(conn, 200, "OK");
  715 }
  716 
  717 
  718 
  719 /* ---------------------------------------------------------------------------
  720  * Process a request: build the header and reply, advance state.
  721  */
  722 static void process_request(struct connection *conn)
  723 {
  724     if (!parse_request(conn))
  725     {
  726         default_reply(conn, 400, "Bad Request",
  727             "You sent a request that the server couldn't understand.");
  728     }
  729     else if (strcmp(conn->method, "GET") == 0)
  730     {
  731         process_get(conn);
  732     }
  733     else if (strcmp(conn->method, "HEAD") == 0)
  734     {
  735         process_get(conn);
  736         conn->header_only = 1;
  737     }
  738     else
  739     {
  740         default_reply(conn, 501, "Not Implemented",
  741             "The method you specified (%s) is not implemented.",
  742             conn->method);
  743     }
  744 
  745     /* advance state */
  746     if (conn->header_only)
  747         conn->state = SEND_HEADER;
  748     else
  749         conn->state = SEND_HEADER_AND_REPLY;
  750 }
  751 
  752 
  753 
  754 /* ---------------------------------------------------------------------------
  755  * Receiving request.
  756  */
  757 static void poll_recv_request(struct connection *conn)
  758 {
  759     char buf[65536];
  760     ssize_t recvd;
  761 
  762     recvd = recv(conn->socket, buf, sizeof(buf), 0);
  763     dverbosef("poll_recv_request(%d) got %d bytes", conn->socket, (int)recvd);
  764     if (recvd <= 0)
  765     {
  766         if (recvd == -1)
  767             verbosef("recv(%d) error: %s", conn->socket, strerror(errno));
  768         conn->state = DONE;
  769         return;
  770     }
  771     conn->last_active_mono = now_mono();
  772 
  773     /* append to conn->request */
  774     conn->request = xrealloc(conn->request, conn->request_length+recvd+1);
  775     memcpy(conn->request+conn->request_length, buf, (size_t)recvd);
  776     conn->request_length += recvd;
  777     conn->request[conn->request_length] = 0;
  778 
  779     /* die if it's too long */
  780     if (conn->request_length > MAX_REQUEST_LENGTH)
  781     {
  782         default_reply(conn, 413, "Request Entity Too Large",
  783             "Your request was dropped because it was too long.");
  784         conn->state = SEND_HEADER;
  785         return;
  786     }
  787 
  788     /* process request if we have all of it */
  789     if (conn->request_length > 4 &&
  790         memcmp(conn->request+conn->request_length-4, "\r\n\r\n", 4) == 0)
  791     {
  792         process_request(conn);
  793 
  794         /* request not needed anymore */
  795         free(conn->request);
  796         conn->request = NULL; /* important: don't free it again later */
  797     }
  798 }
  799 
  800 
  801 
  802 /* ---------------------------------------------------------------------------
  803  * Try to send header and [a part of the] reply in one packet.
  804  */
  805 static void poll_send_header_and_reply(struct connection *conn)
  806 {
  807     ssize_t sent;
  808     struct iovec iov[2];
  809 
  810     assert(!conn->header_only);
  811     assert(conn->reply_length > 0);
  812     assert(conn->header_sent == 0);
  813 
  814     assert(conn->reply_sent == 0);
  815 
  816     /* Fill out iovec */
  817     iov[0].iov_base = conn->header;
  818     iov[0].iov_len = conn->header_length;
  819 
  820     iov[1].iov_base = conn->reply;
  821     iov[1].iov_len = conn->reply_length;
  822 
  823     sent = writev(conn->socket, iov, 2);
  824     conn->last_active_mono = now_mono();
  825 
  826     /* handle any errors (-1) or closure (0) in send() */
  827     if (sent < 1) {
  828         if (sent == -1)
  829             verbosef("writev(%d) error: %s", conn->socket, strerror(errno));
  830         conn->state = DONE;
  831         return;
  832     }
  833 
  834     /* Figure out what we've sent. */
  835     conn->total_sent += (unsigned int)sent;
  836     if (sent < (ssize_t)conn->header_length) {
  837         verbosef("partially sent header");
  838         conn->header_sent = sent;
  839         conn->state = SEND_HEADER;
  840         return;
  841     }
  842     /* else */
  843     conn->header_sent = conn->header_length;
  844     sent -= conn->header_length;
  845 
  846     if (sent < (ssize_t)conn->reply_length) {
  847         verbosef("partially sent reply");
  848         conn->reply_sent += sent;
  849         conn->state = SEND_REPLY;
  850         return;
  851     }
  852     /* else */
  853     conn->reply_sent = conn->reply_length;
  854     conn->state = DONE;
  855 }
  856 
  857 /* ---------------------------------------------------------------------------
  858  * Sending header.  Assumes conn->header is not NULL.
  859  */
  860 static void poll_send_header(struct connection *conn)
  861 {
  862     ssize_t sent;
  863 
  864     sent = send(conn->socket, conn->header + conn->header_sent,
  865         conn->header_length - conn->header_sent, 0);
  866     conn->last_active_mono = now_mono();
  867     dverbosef("poll_send_header(%d) sent %d bytes", conn->socket, (int)sent);
  868 
  869     /* handle any errors (-1) or closure (0) in send() */
  870     if (sent < 1)
  871     {
  872         if (sent == -1)
  873             verbosef("send(%d) error: %s", conn->socket, strerror(errno));
  874         conn->state = DONE;
  875         return;
  876     }
  877     conn->header_sent += (unsigned int)sent;
  878     conn->total_sent += (unsigned int)sent;
  879 
  880     /* check if we're done sending */
  881     if (conn->header_sent == conn->header_length)
  882     {
  883         if (conn->header_only)
  884             conn->state = DONE;
  885         else
  886             conn->state = SEND_REPLY;
  887     }
  888 }
  889 
  890 
  891 
  892 /* ---------------------------------------------------------------------------
  893  * Sending reply.
  894  */
  895 static void poll_send_reply(struct connection *conn)
  896 {
  897     ssize_t sent;
  898 
  899     sent = send(conn->socket,
  900         conn->reply + conn->reply_sent,
  901         conn->reply_length - conn->reply_sent, 0);
  902     conn->last_active_mono = now_mono();
  903     dverbosef("poll_send_reply(%d) sent %d: [%d-%d] of %d",
  904         conn->socket, (int)sent,
  905         (int)conn->reply_sent,
  906         (int)(conn->reply_sent + sent - 1),
  907         (int)conn->reply_length);
  908 
  909     /* handle any errors (-1) or closure (0) in send() */
  910     if (sent < 1)
  911     {
  912         if (sent == -1)
  913             verbosef("send(%d) error: %s", conn->socket, strerror(errno));
  914         else if (sent == 0)
  915             verbosef("send(%d) closure", conn->socket);
  916         conn->state = DONE;
  917         return;
  918     }
  919     conn->reply_sent += (unsigned int)sent;
  920     conn->total_sent += (unsigned int)sent;
  921 
  922     /* check if we're done sending */
  923     if (conn->reply_sent == conn->reply_length) conn->state = DONE;
  924 }
  925 
  926 
  927 
  928 /* --------------------------------------------------------------------------
  929  * Initialize the base url.
  930  */
  931 void http_init_base(const char *url) {
  932     char *slashed_url, *safe_url;
  933     size_t urllen;
  934 
  935     if (url == NULL) {
  936         http_base_url = strdup("/");
  937     } else {
  938         /* Make sure that the url has leading and trailing slashes. */
  939         urllen = strlen(url);
  940         slashed_url = xmalloc(urllen+3);
  941         slashed_url[0] = '/';
  942         memcpy(slashed_url+1, url, urllen); /* don't copy NUL */
  943         slashed_url[urllen+1] = '/';
  944         slashed_url[urllen+2] = '\0';
  945 
  946         /* Clean the url. */
  947         safe_url = make_safe_uri(slashed_url);
  948         free(slashed_url);
  949         if (safe_url == NULL) {
  950             verbosef("invalid base \"%s\", ignored", url);
  951             http_base_url = strdup("/"); /* set to default */
  952         } else {
  953             http_base_url = safe_url;
  954         }
  955     }
  956     http_base_len = strlen(http_base_url);
  957     verbosef("set base url to \"%s\"", http_base_url);
  958 }
  959 
  960 /* Use getaddrinfo to figure out what type of socket to create and
  961  * what to bind it to.  "bindaddr" can be NULL.  Remember to freeaddrinfo()
  962  * the result.
  963  */
  964 static struct addrinfo *get_bind_addr(
  965     const char *bindaddr, const unsigned short bindport)
  966 {
  967     struct addrinfo hints, *ai;
  968     char portstr[6];
  969     int ret;
  970 
  971     memset(&hints, 0, sizeof(hints));
  972     hints.ai_family = AF_UNSPEC;
  973     hints.ai_socktype = SOCK_STREAM;
  974     hints.ai_flags = AI_PASSIVE;
  975 
  976     snprintf(portstr, sizeof(portstr), "%u", bindport);
  977     if ((ret = getaddrinfo(bindaddr, portstr, &hints, &ai)))
  978         err(1, "getaddrinfo(%s, %s) failed: %s",
  979             bindaddr ? bindaddr : "NULL", portstr, gai_strerror(ret));
  980     if (ai == NULL)
  981         err(1, "getaddrinfo() returned NULL pointer");
  982     return ai;
  983 }
  984 
  985 void http_add_bindaddr(const char *bindaddr)
  986 {
  987     struct bindaddr_entry *ent;
  988 
  989     ent = xmalloc(sizeof(*ent));
  990     ent->s = bindaddr;
  991     STAILQ_INSERT_TAIL(&bindaddrs, ent, entries);
  992 }
  993 
  994 static void http_listen_one(struct addrinfo *ai,
  995     const unsigned short bindport)
  996 {
  997     char ipaddr[INET6_ADDRSTRLEN];
  998     int sockin, sockopt, ret;
  999 
 1000     /* format address into ipaddr string */
 1001     if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, ipaddr,
 1002                            sizeof(ipaddr), NULL, 0, NI_NUMERICHOST)) != 0)
 1003         err(1, "getnameinfo failed: %s", gai_strerror(ret));
 1004 
 1005     /* create incoming socket */
 1006     if ((sockin = socket(ai->ai_family, ai->ai_socktype,
 1007             ai->ai_protocol)) == -1) {
 1008         warn("http_listen_one(%s, %u): socket(%d (%s), %d, %d) failed",
 1009           ipaddr, (unsigned int)bindport,
 1010           ai->ai_family,
 1011           (ai->ai_family == AF_INET6) ? "AF_INET6" :
 1012           (ai->ai_family == AF_INET) ? "AF_INET" :
 1013           "?",
 1014           ai->ai_socktype,  ai->ai_protocol);
 1015         return;
 1016     }
 1017 
 1018     fd_set_nonblock(sockin);
 1019 
 1020     /* reuse address */
 1021     sockopt = 1;
 1022     if (setsockopt(sockin, SOL_SOCKET, SO_REUSEADDR,
 1023             &sockopt, sizeof(sockopt)) == -1)
 1024         err(1, "can't set SO_REUSEADDR");
 1025 
 1026 #ifdef IPV6_V6ONLY
 1027     /* explicitly disallow IPv4 mapped addresses since OpenBSD doesn't allow
 1028      * dual stack sockets under any circumstances
 1029      */
 1030     if (ai->ai_family == AF_INET6) {
 1031         sockopt = 1;
 1032         if (setsockopt(sockin, IPPROTO_IPV6, IPV6_V6ONLY,
 1033                 &sockopt, sizeof(sockopt)) == -1)
 1034             err(1, "can't set IPV6_V6ONLY");
 1035     }
 1036 #endif
 1037 
 1038     /* bind socket */
 1039     if (bind(sockin, ai->ai_addr, ai->ai_addrlen) == -1) {
 1040         warn("bind(\"%s\") failed", ipaddr);
 1041         close(sockin);
 1042         return;
 1043     }
 1044 
 1045     /* listen on socket */
 1046     if (listen(sockin, 128) == -1)
 1047         err(1, "listen() failed");
 1048 
 1049     verbosef("listening on http://%s%s%s:%u%s",
 1050         (ai->ai_family == AF_INET6) ? "[" : "",
 1051         ipaddr,
 1052         (ai->ai_family == AF_INET6) ? "]" : "",
 1053         bindport,
 1054         http_base_url);
 1055 
 1056     /* add to insocks */
 1057     insocks = xrealloc(insocks, sizeof(*insocks) * (insock_num + 1));
 1058     insocks[insock_num++] = sockin;
 1059 }
 1060 
 1061 /* Initialize the http sockets and listen on them. */
 1062 void http_listen(const unsigned short bindport)
 1063 {
 1064     /* If the user didn't specify any bind addresses, add a NULL.
 1065      * This will become a wildcard.
 1066      */
 1067     if (STAILQ_EMPTY(&bindaddrs))
 1068         http_add_bindaddr(NULL);
 1069 
 1070     /* Listen on every specified interface. */
 1071     while (!STAILQ_EMPTY(&bindaddrs)) {
 1072         struct bindaddr_entry *bindaddr = STAILQ_FIRST(&bindaddrs);
 1073         struct addrinfo *ai, *ais = get_bind_addr(bindaddr->s, bindport);
 1074 
 1075         /* There could be multiple addresses returned, handle them all. */
 1076         for (ai = ais; ai; ai = ai->ai_next)
 1077             http_listen_one(ai, bindport);
 1078 
 1079         freeaddrinfo(ais);
 1080 
 1081         STAILQ_REMOVE_HEAD(&bindaddrs, entries);
 1082         free(bindaddr);
 1083     }
 1084 
 1085     if (insocks == NULL)
 1086         errx(1, "was not able to bind any ports for http interface");
 1087 
 1088     /* ignore SIGPIPE */
 1089     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
 1090         err(1, "can't ignore SIGPIPE");
 1091 }
 1092 
 1093 
 1094 
 1095 /* ---------------------------------------------------------------------------
 1096  * Set recv/send fd_sets and calculate timeout length.
 1097  */
 1098 void
 1099 http_fd_set(fd_set *recv_set, fd_set *send_set, int *max_fd,
 1100     struct timeval *timeout, int *need_timeout)
 1101 {
 1102     struct connection *conn, *next;
 1103     int minidle = idletime + 1;
 1104     unsigned int i;
 1105 
 1106     #define MAX_FD_SET(sock, fdset) do { \
 1107         FD_SET(sock, fdset); *max_fd = MAX(*max_fd, sock); } while(0)
 1108 
 1109     for (i=0; i<insock_num; i++)
 1110         MAX_FD_SET(insocks[i], recv_set);
 1111 
 1112     LIST_FOREACH_SAFE(conn, &connlist, entries, next)
 1113     {
 1114         int idlefor = now_mono() - conn->last_active_mono;
 1115 
 1116         /* Time out dead connections. */
 1117         if (idlefor >= idletime) {
 1118             char ipaddr[INET6_ADDRSTRLEN];
 1119             /* FIXME: this is too late on FreeBSD, socket is invalid */
 1120             int ret = getnameinfo((struct sockaddr *)&conn->client,
 1121                 sizeof(conn->client), ipaddr, sizeof(ipaddr),
 1122                 NULL, 0, NI_NUMERICHOST);
 1123             if (ret == 0)
 1124                 verbosef("http socket timeout from %s (fd %d)",
 1125                         ipaddr, conn->socket);
 1126             else
 1127                 warn("http socket timeout: getnameinfo error: %s",
 1128                     gai_strerror(ret));
 1129             conn->state = DONE;
 1130         }
 1131 
 1132         /* Connections that need a timeout. */
 1133         if (conn->state != DONE)
 1134             minidle = MIN(minidle, (idletime - idlefor));
 1135 
 1136         switch (conn->state)
 1137         {
 1138         case DONE:
 1139             /* clean out stale connection */
 1140             LIST_REMOVE(conn, entries);
 1141             free_connection(conn);
 1142             free(conn);
 1143             break;
 1144 
 1145         case RECV_REQUEST:
 1146             MAX_FD_SET(conn->socket, recv_set);
 1147             break;
 1148 
 1149         case SEND_HEADER_AND_REPLY:
 1150         case SEND_HEADER:
 1151         case SEND_REPLY:
 1152             MAX_FD_SET(conn->socket, send_set);
 1153             break;
 1154 
 1155         default: errx(1, "invalid state");
 1156         }
 1157     }
 1158     #undef MAX_FD_SET
 1159 
 1160     /* Only set timeout if cap hasn't already. */
 1161     if ((*need_timeout == 0) && (minidle <= idletime)) {
 1162         *need_timeout = 1;
 1163         timeout->tv_sec = minidle;
 1164         timeout->tv_usec = 0;
 1165     }
 1166 }
 1167 
 1168 
 1169 
 1170 /* ---------------------------------------------------------------------------
 1171  * poll connections that select() says need attention
 1172  */
 1173 void http_poll(fd_set *recv_set, fd_set *send_set)
 1174 {
 1175     struct connection *conn;
 1176     unsigned int i;
 1177 
 1178     for (i=0; i<insock_num; i++)
 1179         if (FD_ISSET(insocks[i], recv_set))
 1180             accept_connection(insocks[i]);
 1181 
 1182     LIST_FOREACH(conn, &connlist, entries)
 1183     switch (conn->state)
 1184     {
 1185     case RECV_REQUEST:
 1186         if (FD_ISSET(conn->socket, recv_set)) poll_recv_request(conn);
 1187         break;
 1188 
 1189     case SEND_HEADER_AND_REPLY:
 1190         if (FD_ISSET(conn->socket, send_set)) poll_send_header_and_reply(conn);
 1191         break;
 1192 
 1193     case SEND_HEADER:
 1194         if (FD_ISSET(conn->socket, send_set)) poll_send_header(conn);
 1195         break;
 1196 
 1197     case SEND_REPLY:
 1198         if (FD_ISSET(conn->socket, send_set)) poll_send_reply(conn);
 1199         break;
 1200 
 1201     case DONE: /* fallthrough */
 1202     default: errx(1, "invalid state");
 1203     }
 1204 }
 1205 
 1206 void http_stop(void) {
 1207     struct connection *conn;
 1208     struct connection *next;
 1209     unsigned int i;
 1210 
 1211     free(http_base_url);
 1212 
 1213     /* Close listening sockets. */
 1214     for (i=0; i<insock_num; i++)
 1215         close(insocks[i]);
 1216     free(insocks);
 1217     insocks = NULL;
 1218 
 1219     /* Close in-flight connections. */
 1220     LIST_FOREACH_SAFE(conn, &connlist, entries, next) {
 1221         LIST_REMOVE(conn, entries);
 1222         free_connection(conn);
 1223         free(conn);
 1224     }
 1225 }
 1226 
 1227 /* vim:set ts=4 sw=4 et tw=78: */