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