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