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