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