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