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