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