Avoid file size overflow on 32-bit systems.
[darkhttpd] / darkhttpd.c
1 /* darkhttpd - a simple, single-threaded, static content webserver.
2 * https://unix4lyfe.org/darkhttpd/
3 * Copyright (c) 2003-2018 Emil Mikulic <emikulic@gmail.com>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 static const char
21 pkgname[] = "darkhttpd/1.12.from.git",
22 copyright[] = "copyright (c) 2003-2018 Emil Mikulic";
23
24 /* Possible build options: -DDEBUG -DNO_IPV6 */
25
26 #ifndef NO_IPV6
27 # define HAVE_INET6
28 #endif
29
30 #ifndef DEBUG
31 # define NDEBUG
32 static const int debug = 0;
33 #else
34 static const int debug = 1;
35 #endif
36
37 #ifdef __linux
38 # define _GNU_SOURCE /* for strsignal() and vasprintf() */
39 # define _FILE_OFFSET_BITS 64 /* stat() files bigger than 2GB */
40 # include <sys/sendfile.h>
41 #endif
42
43 #ifdef __sun__
44 # include <sys/sendfile.h>
45 #endif
46
47 #include <sys/time.h>
48 #include <sys/types.h>
49 #include <sys/socket.h>
50 #include <sys/stat.h>
51 #include <sys/resource.h>
52 #include <sys/wait.h>
53 #include <sys/param.h>
54 #include <netinet/in.h>
55 #include <netinet/tcp.h>
56 #include <arpa/inet.h>
57 #include <assert.h>
58 #include <ctype.h>
59 #include <dirent.h>
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <grp.h>
63 #include <limits.h>
64 #include <pwd.h>
65 #include <signal.h>
66 #include <stdarg.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <time.h>
71 #include <unistd.h>
72
73 #if defined(__has_feature)
74 # if __has_feature(memory_sanitizer)
75 # include <sanitizer/msan_interface.h>
76 # endif
77 #endif
78
79 #ifdef __sun__
80 # ifndef INADDR_NONE
81 # define INADDR_NONE -1
82 # endif
83 #endif
84
85 #ifndef MAXNAMLEN
86 # ifdef NAME_MAX
87 # define MAXNAMLEN NAME_MAX
88 # else
89 # define MAXNAMLEN 255
90 # endif
91 #endif
92
93 #if defined(O_EXCL) && !defined(O_EXLOCK)
94 # define O_EXLOCK O_EXCL
95 #endif
96
97 #ifndef __printflike
98 # ifdef __GNUC__
99 /* [->] borrowed from FreeBSD's src/sys/sys/cdefs.h,v 1.102.2.2.2.1 */
100 # define __printflike(fmtarg, firstvararg) \
101 __attribute__((__format__(__printf__, fmtarg, firstvararg)))
102 /* [<-] */
103 # else
104 # define __printflike(fmtarg, firstvararg)
105 # endif
106 #endif
107
108 #if defined(__GNUC__) || defined(__INTEL_COMPILER)
109 # define unused __attribute__((__unused__))
110 #else
111 # define unused
112 #endif
113
114 /* [->] borrowed from FreeBSD's src/sys/sys/systm.h,v 1.276.2.7.4.1 */
115 #ifndef CTASSERT /* Allow lint to override */
116 # define CTASSERT(x) _CTASSERT(x, __LINE__)
117 # define _CTASSERT(x, y) __CTASSERT(x, y)
118 # define __CTASSERT(x, y) typedef char __assert ## y[(x) ? 1 : -1]
119 #endif
120 /* [<-] */
121
122 CTASSERT(sizeof(unsigned long long) >= sizeof(off_t));
123 #define llu(x) ((unsigned long long)(x))
124
125 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux)
126 # include <err.h>
127 #else
128 /* err - prints "error: format: strerror(errno)" to stderr and exit()s with
129 * the given code.
130 */
131 static void err(const int code, const char *format, ...) __printflike(2, 3);
132 static void err(const int code, const char *format, ...) {
133 va_list va;
134
135 va_start(va, format);
136 fprintf(stderr, "error: ");
137 vfprintf(stderr, format, va);
138 fprintf(stderr, ": %s\n", strerror(errno));
139 va_end(va);
140 exit(code);
141 }
142
143 /* errx - err() without the strerror */
144 static void errx(const int code, const char *format, ...) __printflike(2, 3);
145 static void errx(const int code, const char *format, ...) {
146 va_list va;
147
148 va_start(va, format);
149 fprintf(stderr, "error: ");
150 vfprintf(stderr, format, va);
151 fprintf(stderr, "\n");
152 va_end(va);
153 exit(code);
154 }
155
156 /* warn - err() without the exit */
157 static void warn(const char *format, ...) __printflike(1, 2);
158 static void warn(const char *format, ...) {
159 va_list va;
160
161 va_start(va, format);
162 fprintf(stderr, "warning: ");
163 vfprintf(stderr, format, va);
164 fprintf(stderr, ": %s\n", strerror(errno));
165 va_end(va);
166 }
167 #endif
168
169 /* [->] LIST_* macros taken from FreeBSD's src/sys/sys/queue.h,v 1.56
170 * Copyright (c) 1991, 1993
171 * The Regents of the University of California. All rights reserved.
172 *
173 * Under a BSD license.
174 */
175 #define LIST_HEAD(name, type) \
176 struct name { \
177 struct type *lh_first; /* first element */ \
178 }
179
180 #define LIST_HEAD_INITIALIZER(head) \
181 { NULL }
182
183 #define LIST_ENTRY(type) \
184 struct { \
185 struct type *le_next; /* next element */ \
186 struct type **le_prev; /* address of previous next element */ \
187 }
188
189 #define LIST_FIRST(head) ((head)->lh_first)
190
191 #define LIST_FOREACH_SAFE(var, head, field, tvar) \
192 for ((var) = LIST_FIRST((head)); \
193 (var) && ((tvar) = LIST_NEXT((var), field), 1); \
194 (var) = (tvar))
195
196 #define LIST_INSERT_HEAD(head, elm, field) do { \
197 if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
198 LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
199 LIST_FIRST((head)) = (elm); \
200 (elm)->field.le_prev = &LIST_FIRST((head)); \
201 } while (0)
202
203 #define LIST_NEXT(elm, field) ((elm)->field.le_next)
204
205 #define LIST_REMOVE(elm, field) do { \
206 if (LIST_NEXT((elm), field) != NULL) \
207 LIST_NEXT((elm), field)->field.le_prev = \
208 (elm)->field.le_prev; \
209 *(elm)->field.le_prev = LIST_NEXT((elm), field); \
210 } while (0)
211 /* [<-] */
212
213 static LIST_HEAD(conn_list_head, connection) connlist =
214 LIST_HEAD_INITIALIZER(conn_list_head);
215
216 struct connection {
217 LIST_ENTRY(connection) entries;
218
219 int socket;
220 #ifdef HAVE_INET6
221 struct in6_addr client;
222 #else
223 in_addr_t client;
224 #endif
225 time_t last_active;
226 enum {
227 RECV_REQUEST, /* receiving request */
228 SEND_HEADER, /* sending generated header */
229 SEND_REPLY, /* sending reply */
230 DONE /* connection closed, need to remove from queue */
231 } state;
232
233 /* char request[request_length+1] is null-terminated */
234 char *request;
235 size_t request_length;
236
237 /* request fields */
238 char *method, *url, *referer, *user_agent, *authorization;
239 off_t range_begin, range_end;
240 off_t range_begin_given, range_end_given;
241
242 char *header;
243 size_t header_length, header_sent;
244 int header_dont_free, header_only, http_code, conn_close;
245
246 enum { REPLY_GENERATED, REPLY_FROMFILE } reply_type;
247 char *reply;
248 int reply_dont_free;
249 int reply_fd;
250 off_t reply_start, reply_length, reply_sent,
251 total_sent; /* header + body = total, for logging */
252 };
253
254 struct forward_mapping {
255 const char *host, *target_url; /* These point at argv. */
256 };
257
258 static struct forward_mapping *forward_map = NULL;
259 static size_t forward_map_size = 0;
260 static const char *forward_all_url = NULL;
261
262 struct mime_mapping {
263 char *extension, *mimetype;
264 };
265
266 static struct mime_mapping *mime_map = NULL;
267 static size_t mime_map_size = 0;
268 static size_t longest_ext = 0;
269
270 /* If a connection is idle for timeout_secs or more, it gets closed and
271 * removed from the connlist.
272 */
273 static int timeout_secs = 30;
274 static char *keep_alive_field = NULL;
275
276 /* Time is cached in the event loop to avoid making an excessive number of
277 * gettimeofday() calls.
278 */
279 static time_t now;
280
281 /* To prevent a malformed request from eating up too much memory, die once the
282 * request exceeds this many bytes:
283 */
284 #define MAX_REQUEST_LENGTH 4000
285
286 /* Defaults can be overridden on the command-line */
287 static const char *bindaddr;
288 static uint16_t bindport = 8080; /* or 80 if running as root */
289 static int max_connections = -1; /* kern.ipc.somaxconn */
290 static const char *index_name = "index.html";
291 static int no_listing = 0;
292
293 static int sockin = -1; /* socket to accept connections from */
294 #ifdef HAVE_INET6
295 static int inet6 = 0; /* whether the socket uses inet6 */
296 #endif
297 static char *wwwroot = NULL; /* a path name */
298 static char *logfile_name = NULL; /* NULL = no logging */
299 static FILE *logfile = NULL;
300 static char *pidfile_name = NULL; /* NULL = no pidfile */
301 static int want_chroot = 0, want_daemon = 0, want_accf = 0,
302 want_keepalive = 1, want_server_id = 1;
303 static char *server_hdr = NULL;
304 static char *auth_key = NULL;
305 static uint64_t num_requests = 0, total_in = 0, total_out = 0;
306 static int accepting = 1; /* set to 0 to stop accept()ing */
307
308 static volatile int running = 1; /* signal handler sets this to false */
309
310 #define INVALID_UID ((uid_t) -1)
311 #define INVALID_GID ((gid_t) -1)
312
313 static uid_t drop_uid = INVALID_UID;
314 static gid_t drop_gid = INVALID_GID;
315
316 /* Default mimetype mappings - make sure this array is NULL terminated. */
317 static const char *default_extension_map[] = {
318 "application/ogg" " ogg",
319 "application/pdf" " pdf",
320 "application/wasm" " wasm",
321 "application/xml" " xsl xml",
322 "application/xml-dtd" " dtd",
323 "application/xslt+xml" " xslt",
324 "application/zip" " zip",
325 "audio/mpeg" " mp2 mp3 mpga",
326 "image/gif" " gif",
327 "image/jpeg" " jpeg jpe jpg",
328 "image/png" " png",
329 "image/svg+xml" " svg",
330 "text/css" " css",
331 "text/html" " html htm",
332 "text/javascript" " js",
333 "text/plain" " txt asc",
334 "video/mpeg" " mpeg mpe mpg",
335 "video/quicktime" " qt mov",
336 "video/x-msvideo" " avi",
337 "video/mp4" " mp4",
338 NULL
339 };
340
341 static const char octet_stream[] = "application/octet-stream";
342 static const char *default_mimetype = octet_stream;
343
344 /* Prototypes. */
345 static void poll_recv_request(struct connection *conn);
346 static void poll_send_header(struct connection *conn);
347 static void poll_send_reply(struct connection *conn);
348
349 /* close() that dies on error. */
350 static void xclose(const int fd) {
351 if (close(fd) == -1)
352 err(1, "close()");
353 }
354
355 /* malloc that dies if it can't allocate. */
356 static void *xmalloc(const size_t size) {
357 void *ptr = malloc(size);
358 if (ptr == NULL)
359 errx(1, "can't allocate %zu bytes", size);
360 return ptr;
361 }
362
363 /* realloc() that dies if it can't reallocate. */
364 static void *xrealloc(void *original, const size_t size) {
365 void *ptr = realloc(original, size);
366 if (ptr == NULL)
367 errx(1, "can't reallocate %zu bytes", size);
368 return ptr;
369 }
370
371 /* strdup() that dies if it can't allocate.
372 * Implement this ourselves since regular strdup() isn't C89.
373 */
374 static char *xstrdup(const char *src) {
375 size_t len = strlen(src) + 1;
376 char *dest = xmalloc(len);
377 memcpy(dest, src, len);
378 return dest;
379 }
380
381 #ifdef __sun /* unimpressed by Solaris */
382 static int vasprintf(char **strp, const char *fmt, va_list ap) {
383 char tmp;
384 int result = vsnprintf(&tmp, 1, fmt, ap);
385 *strp = xmalloc(result+1);
386 result = vsnprintf(*strp, result+1, fmt, ap);
387 return result;
388 }
389 #endif
390
391 /* vasprintf() that dies if it fails. */
392 static unsigned int xvasprintf(char **ret, const char *format, va_list ap)
393 __printflike(2,0);
394 static unsigned int xvasprintf(char **ret, const char *format, va_list ap) {
395 int len = vasprintf(ret, format, ap);
396 if (ret == NULL || len == -1)
397 errx(1, "out of memory in vasprintf()");
398 return (unsigned int)len;
399 }
400
401 /* asprintf() that dies if it fails. */
402 static unsigned int xasprintf(char **ret, const char *format, ...)
403 __printflike(2,3);
404 static unsigned int xasprintf(char **ret, const char *format, ...) {
405 va_list va;
406 unsigned int len;
407
408 va_start(va, format);
409 len = xvasprintf(ret, format, va);
410 va_end(va);
411 return len;
412 }
413
414 /* Append buffer code. A somewhat efficient string buffer with pool-based
415 * reallocation.
416 */
417 #ifndef APBUF_INIT
418 # define APBUF_INIT 4096
419 #endif
420 #define APBUF_GROW APBUF_INIT
421 struct apbuf {
422 size_t length, pool;
423 char *str;
424 };
425
426 static struct apbuf *make_apbuf(void) {
427 struct apbuf *buf = xmalloc(sizeof(struct apbuf));
428 buf->length = 0;
429 buf->pool = APBUF_INIT;
430 buf->str = xmalloc(buf->pool);
431 return buf;
432 }
433
434 /* Append s (of length len) to buf. */
435 static void appendl(struct apbuf *buf, const char *s, const size_t len) {
436 size_t need = buf->length + len;
437 if (buf->pool < need) {
438 /* pool has dried up */
439 while (buf->pool < need)
440 buf->pool += APBUF_GROW;
441 buf->str = xrealloc(buf->str, buf->pool);
442 }
443 memcpy(buf->str + buf->length, s, len);
444 buf->length += len;
445 }
446
447 #ifdef __GNUC__
448 #define append(buf, s) appendl(buf, s, \
449 (__builtin_constant_p(s) ? sizeof(s)-1 : strlen(s)) )
450 #else
451 static void append(struct apbuf *buf, const char *s) {
452 appendl(buf, s, strlen(s));
453 }
454 #endif
455
456 static void appendf(struct apbuf *buf, const char *format, ...)
457 __printflike(2, 3);
458 static void appendf(struct apbuf *buf, const char *format, ...) {
459 char *tmp;
460 va_list va;
461 size_t len;
462
463 va_start(va, format);
464 len = xvasprintf(&tmp, format, va);
465 va_end(va);
466 appendl(buf, tmp, len);
467 free(tmp);
468 }
469
470 /* Make the specified socket non-blocking. */
471 static void nonblock_socket(const int sock) {
472 int flags = fcntl(sock, F_GETFL);
473
474 if (flags == -1)
475 err(1, "fcntl(F_GETFL)");
476 flags |= O_NONBLOCK;
477 if (fcntl(sock, F_SETFL, flags) == -1)
478 err(1, "fcntl() to set O_NONBLOCK");
479 }
480
481 /* Split string out of src with range [left:right-1] */
482 static char *split_string(const char *src,
483 const size_t left, const size_t right) {
484 char *dest;
485 assert(left <= right);
486 assert(left < strlen(src)); /* [left means must be smaller */
487 assert(right <= strlen(src)); /* right) means can be equal or smaller */
488
489 dest = xmalloc(right - left + 1);
490 memcpy(dest, src+left, right-left);
491 dest[right-left] = '\0';
492 return dest;
493 }
494
495 /* Resolve /./ and /../ in a URL, in-place.
496 * Returns NULL if the URL is invalid/unsafe, or the original buffer if
497 * successful.
498 */
499 static char *make_safe_url(char *const url) {
500 char *src = url, *dst;
501 #define ends(c) ((c) == '/' || (c) == '\0')
502
503 /* URLs not starting with a slash are illegal. */
504 if (*src != '/')
505 return NULL;
506
507 /* Fast case: skip until first double-slash or dot-dir. */
508 for ( ; *src; ++src) {
509 if (*src == '/') {
510 if (src[1] == '/')
511 break;
512 else if (src[1] == '.') {
513 if (ends(src[2]))
514 break;
515 else if (src[2] == '.' && ends(src[3]))
516 break;
517 }
518 }
519 }
520
521 /* Copy to dst, while collapsing multi-slashes and handling dot-dirs. */
522 dst = src;
523 while (*src) {
524 if (*src != '/')
525 *dst++ = *src++;
526 else if (*++src == '/')
527 ;
528 else if (*src != '.')
529 *dst++ = '/';
530 else if (ends(src[1]))
531 /* Ignore single-dot component. */
532 ++src;
533 else if (src[1] == '.' && ends(src[2])) {
534 /* Double-dot component. */
535 src += 2;
536 if (dst == url)
537 return NULL; /* Illegal URL */
538 else
539 /* Backtrack to previous slash. */
540 while (*--dst != '/' && dst > url);
541 }
542 else
543 *dst++ = '/';
544 }
545
546 if (dst == url)
547 ++dst;
548 *dst = '\0';
549 return url;
550 #undef ends
551 }
552
553 static void add_forward_mapping(const char * const host,
554 const char * const target_url) {
555 forward_map_size++;
556 forward_map = xrealloc(forward_map,
557 sizeof(*forward_map) * forward_map_size);
558 forward_map[forward_map_size - 1].host = host;
559 forward_map[forward_map_size - 1].target_url = target_url;
560 }
561
562 /* Associates an extension with a mimetype in the mime_map. Entries are in
563 * unsorted order. Makes copies of extension and mimetype strings.
564 */
565 static void add_mime_mapping(const char *extension, const char *mimetype) {
566 size_t i;
567 assert(strlen(extension) > 0);
568 assert(strlen(mimetype) > 0);
569
570 /* update longest_ext */
571 i = strlen(extension);
572 if (i > longest_ext)
573 longest_ext = i;
574
575 /* look through list and replace an existing entry if possible */
576 for (i = 0; i < mime_map_size; i++)
577 if (strcmp(mime_map[i].extension, extension) == 0) {
578 free(mime_map[i].mimetype);
579 mime_map[i].mimetype = xstrdup(mimetype);
580 return;
581 }
582
583 /* no replacement - add a new entry */
584 mime_map_size++;
585 mime_map = xrealloc(mime_map,
586 sizeof(struct mime_mapping) * mime_map_size);
587 mime_map[mime_map_size - 1].extension = xstrdup(extension);
588 mime_map[mime_map_size - 1].mimetype = xstrdup(mimetype);
589 }
590
591 /* qsort() the mime_map. The map must be sorted before it can be
592 * binary-searched.
593 */
594 static int mime_mapping_cmp(const void *a, const void *b) {
595 return strcmp(((const struct mime_mapping *)a)->extension,
596 ((const struct mime_mapping *)b)->extension);
597 }
598
599 static void sort_mime_map(void) {
600 qsort(mime_map, mime_map_size, sizeof(struct mime_mapping),
601 mime_mapping_cmp);
602 }
603
604 /* Parses a mime.types line and adds the parsed data to the mime_map. */
605 static void parse_mimetype_line(const char *line) {
606 unsigned int pad, bound1, lbound, rbound;
607
608 /* parse mimetype */
609 for (pad=0; (line[pad] == ' ') || (line[pad] == '\t'); pad++)
610 ;
611 if (line[pad] == '\0' || /* empty line */
612 line[pad] == '#') /* comment */
613 return;
614
615 for (bound1=pad+1;
616 (line[bound1] != ' ') &&
617 (line[bound1] != '\t');
618 bound1++) {
619 if (line[bound1] == '\0')
620 return; /* malformed line */
621 }
622
623 lbound = bound1;
624 for (;;) {
625 char *mimetype, *extension;
626
627 /* find beginning of extension */
628 for (; (line[lbound] == ' ') || (line[lbound] == '\t'); lbound++)
629 ;
630 if (line[lbound] == '\0')
631 return; /* end of line */
632
633 /* find end of extension */
634 for (rbound = lbound;
635 line[rbound] != ' ' &&
636 line[rbound] != '\t' &&
637 line[rbound] != '\0';
638 rbound++)
639 ;
640
641 mimetype = split_string(line, pad, bound1);
642 extension = split_string(line, lbound, rbound);
643 add_mime_mapping(extension, mimetype);
644 free(mimetype);
645 free(extension);
646
647 if (line[rbound] == '\0')
648 return; /* end of line */
649 else
650 lbound = rbound + 1;
651 }
652 }
653
654 /* Adds contents of default_extension_map[] to mime_map list. The array must
655 * be NULL terminated.
656 */
657 static void parse_default_extension_map(void) {
658 size_t i;
659
660 for (i = 0; default_extension_map[i] != NULL; i++)
661 parse_mimetype_line(default_extension_map[i]);
662 }
663
664 /* read a line from fp, return its contents in a dynamically allocated buffer,
665 * not including the line ending.
666 *
667 * Handles CR, CRLF and LF line endings, as well as NOEOL correctly. If
668 * already at EOF, returns NULL. Will err() or errx() in case of
669 * unexpected file error or running out of memory.
670 */
671 static char *read_line(FILE *fp) {
672 char *buf;
673 long startpos, endpos;
674 size_t linelen, numread;
675 int c;
676
677 startpos = ftell(fp);
678 if (startpos == -1)
679 err(1, "ftell()");
680
681 /* find end of line (or file) */
682 linelen = 0;
683 for (;;) {
684 c = fgetc(fp);
685 if ((c == EOF) || (c == (int)'\n') || (c == (int)'\r'))
686 break;
687 linelen++;
688 }
689
690 /* return NULL on EOF (and empty line) */
691 if (linelen == 0 && c == EOF)
692 return NULL;
693
694 endpos = ftell(fp);
695 if (endpos == -1)
696 err(1, "ftell()");
697
698 /* skip CRLF */
699 if ((c == (int)'\r') && (fgetc(fp) == (int)'\n'))
700 endpos++;
701
702 buf = xmalloc(linelen + 1);
703
704 /* rewind file to where the line stared and load the line */
705 if (fseek(fp, startpos, SEEK_SET) == -1)
706 err(1, "fseek()");
707 numread = fread(buf, 1, linelen, fp);
708 if (numread != linelen)
709 errx(1, "fread() %zu bytes, expecting %zu bytes", numread, linelen);
710
711 /* terminate buffer */
712 buf[linelen] = 0;
713
714 /* advance file pointer over the endline */
715 if (fseek(fp, endpos, SEEK_SET) == -1)
716 err(1, "fseek()");
717
718 return buf;
719 }
720
721 /* ---------------------------------------------------------------------------
722 * Adds contents of specified file to mime_map list.
723 */
724 static void parse_extension_map_file(const char *filename) {
725 char *buf;
726 FILE *fp = fopen(filename, "rb");
727
728 if (fp == NULL)
729 err(1, "fopen(\"%s\")", filename);
730 while ((buf = read_line(fp)) != NULL) {
731 parse_mimetype_line(buf);
732 free(buf);
733 }
734 fclose(fp);
735 }
736
737 /* Uses the mime_map to determine a Content-Type: for a requested URL. This
738 * bsearch()es mime_map, so make sure it's sorted first.
739 */
740 static int mime_mapping_cmp_str(const void *a, const void *b) {
741 return strcmp((const char *)a,
742 ((const struct mime_mapping *)b)->extension);
743 }
744
745 static const char *url_content_type(const char *url) {
746 int period, urllen = (int)strlen(url);
747
748 for (period = urllen - 1;
749 (period > 0) && (url[period] != '.') &&
750 (urllen - period - 1 <= (int)longest_ext);
751 period--)
752 ;
753
754 if ((period >= 0) && (url[period] == '.')) {
755 struct mime_mapping *result =
756 bsearch((url + period + 1), mime_map, mime_map_size,
757 sizeof(struct mime_mapping), mime_mapping_cmp_str);
758 if (result != NULL) {
759 assert(strcmp(url + period + 1, result->extension) == 0);
760 return result->mimetype;
761 }
762 }
763 /* else no period found in the string */
764 return default_mimetype;
765 }
766
767 static const char *get_address_text(const void *addr) {
768 #ifdef HAVE_INET6
769 if (inet6) {
770 static char text_addr[INET6_ADDRSTRLEN];
771 inet_ntop(AF_INET6, (const struct in6_addr *)addr, text_addr,
772 INET6_ADDRSTRLEN);
773 return text_addr;
774 } else
775 #endif
776 {
777 return inet_ntoa(*(const struct in_addr *)addr);
778 }
779 }
780
781 /* Initialize the sockin global. This is the socket that we accept
782 * connections from.
783 */
784 static void init_sockin(void) {
785 struct sockaddr_in addrin;
786 #ifdef HAVE_INET6
787 struct sockaddr_in6 addrin6;
788 #endif
789 socklen_t addrin_len;
790 int sockopt;
791
792 #ifdef HAVE_INET6
793 if (inet6) {
794 memset(&addrin6, 0, sizeof(addrin6));
795 if (inet_pton(AF_INET6, bindaddr ? bindaddr : "::",
796 &addrin6.sin6_addr) == -1) {
797 errx(1, "malformed --addr argument");
798 }
799 sockin = socket(PF_INET6, SOCK_STREAM, 0);
800 } else
801 #endif
802 {
803 memset(&addrin, 0, sizeof(addrin));
804 addrin.sin_addr.s_addr = bindaddr ? inet_addr(bindaddr) : INADDR_ANY;
805 if (addrin.sin_addr.s_addr == (in_addr_t)INADDR_NONE)
806 errx(1, "malformed --addr argument");
807 sockin = socket(PF_INET, SOCK_STREAM, 0);
808 }
809
810 if (sockin == -1)
811 err(1, "socket()");
812
813 /* reuse address */
814 sockopt = 1;
815 if (setsockopt(sockin, SOL_SOCKET, SO_REUSEADDR,
816 &sockopt, sizeof(sockopt)) == -1)
817 err(1, "setsockopt(SO_REUSEADDR)");
818
819 #if 0
820 /* disable Nagle since we buffer everything ourselves */
821 sockopt = 1;
822 if (setsockopt(sockin, IPPROTO_TCP, TCP_NODELAY,
823 &sockopt, sizeof(sockopt)) == -1)
824 err(1, "setsockopt(TCP_NODELAY)");
825 #endif
826
827 #ifdef TORTURE
828 /* torture: cripple the kernel-side send buffer so we can only squeeze out
829 * one byte at a time (this is for debugging)
830 */
831 sockopt = 1;
832 if (setsockopt(sockin, SOL_SOCKET, SO_SNDBUF,
833 &sockopt, sizeof(sockopt)) == -1)
834 err(1, "setsockopt(SO_SNDBUF)");
835 #endif
836
837 /* bind socket */
838 #ifdef HAVE_INET6
839 if (inet6) {
840 addrin6.sin6_family = AF_INET6;
841 addrin6.sin6_port = htons(bindport);
842 if (bind(sockin, (struct sockaddr *)&addrin6,
843 sizeof(struct sockaddr_in6)) == -1)
844 err(1, "bind(port %u)", bindport);
845
846 addrin_len = sizeof(addrin6);
847 if (getsockname(sockin, (struct sockaddr *)&addrin6, &addrin_len) == -1)
848 err(1, "getsockname()");
849 printf("listening on: http://[%s]:%u/\n",
850 get_address_text(&addrin6.sin6_addr), bindport);
851 } else
852 #endif
853 {
854 addrin.sin_family = (u_char)PF_INET;
855 addrin.sin_port = htons(bindport);
856 if (bind(sockin, (struct sockaddr *)&addrin,
857 sizeof(struct sockaddr_in)) == -1)
858 err(1, "bind(port %u)", bindport);
859 addrin_len = sizeof(addrin);
860 if (getsockname(sockin, (struct sockaddr *)&addrin, &addrin_len) == -1)
861 err(1, "getsockname()");
862 printf("listening on: http://%s:%u/\n",
863 get_address_text(&addrin.sin_addr), bindport);
864 }
865
866 /* listen on socket */
867 if (listen(sockin, max_connections) == -1)
868 err(1, "listen()");
869
870 /* enable acceptfilter (this is only available on FreeBSD) */
871 if (want_accf) {
872 #if defined(__FreeBSD__)
873 struct accept_filter_arg filt = {"httpready", ""};
874 if (setsockopt(sockin, SOL_SOCKET, SO_ACCEPTFILTER,
875 &filt, sizeof(filt)) == -1)
876 fprintf(stderr, "cannot enable acceptfilter: %s\n",
877 strerror(errno));
878 else
879 printf("enabled acceptfilter\n");
880 #else
881 printf("this platform doesn't support acceptfilter\n");
882 #endif
883 }
884 }
885
886 static void usage(const char *argv0) {
887 printf("usage:\t%s /path/to/wwwroot [flags]\n\n", argv0);
888 printf("flags:\t--port number (default: %u, or 80 if running as root)\n"
889 "\t\tSpecifies which port to listen on for connections.\n"
890 "\t\tPass 0 to let the system choose any free port for you.\n\n", bindport);
891 printf("\t--addr ip (default: all)\n"
892 "\t\tIf multiple interfaces are present, specifies\n"
893 "\t\twhich one to bind the listening port to.\n\n");
894 printf("\t--maxconn number (default: system maximum)\n"
895 "\t\tSpecifies how many concurrent connections to accept.\n\n");
896 printf("\t--log filename (default: stdout)\n"
897 "\t\tSpecifies which file to append the request log to.\n\n");
898 printf("\t--chroot (default: don't chroot)\n"
899 "\t\tLocks server into wwwroot directory for added security.\n\n");
900 printf("\t--daemon (default: don't daemonize)\n"
901 "\t\tDetach from the controlling terminal and run in the background.\n\n");
902 printf("\t--index filename (default: %s)\n"
903 "\t\tDefault file to serve when a directory is requested.\n\n",
904 index_name);
905 printf("\t--no-listing\n"
906 "\t\tDo not serve listing if directory is requested.\n\n");
907 printf("\t--mimetypes filename (optional)\n"
908 "\t\tParses specified file for extension-MIME associations.\n\n");
909 printf("\t--default-mimetype string (optional, default: %s)\n"
910 "\t\tFiles with unknown extensions are served as this mimetype.\n\n",
911 octet_stream);
912 printf("\t--uid uid/uname, --gid gid/gname (default: don't privdrop)\n"
913 "\t\tDrops privileges to given uid:gid after initialization.\n\n");
914 printf("\t--pidfile filename (default: no pidfile)\n"
915 "\t\tWrite PID to the specified file. Note that if you are\n"
916 "\t\tusing --chroot, then the pidfile must be relative to,\n"
917 "\t\tand inside the wwwroot.\n\n");
918 printf("\t--no-keepalive\n"
919 "\t\tDisables HTTP Keep-Alive functionality.\n\n");
920 #ifdef __FreeBSD__
921 printf("\t--accf (default: don't use acceptfilter)\n"
922 "\t\tUse acceptfilter. Needs the accf_http module loaded.\n\n");
923 #endif
924 printf("\t--forward host url (default: don't forward)\n"
925 "\t\tWeb forward (301 redirect).\n"
926 "\t\tRequests to the host are redirected to the corresponding url.\n"
927 "\t\tThe option may be specified multiple times, in which case\n"
928 "\t\tthe host is matched in order of appearance.\n\n");
929 printf("\t--forward-all url (default: don't forward)\n"
930 "\t\tWeb forward (301 redirect).\n"
931 "\t\tAll requests are redirected to the corresponding url.\n\n");
932 printf("\t--no-server-id\n"
933 "\t\tDon't identify the server type in headers\n"
934 "\t\tor directory listings.\n\n");
935 printf("\t--timeout secs (default: %d)\n"
936 "\t\tIf a connection is idle for more than this many seconds,\n"
937 "\t\tit will be closed. Set to zero to disable timeouts.\n\n",
938 timeout_secs);
939 printf("\t--auth username:password\n"
940 "\t\tEnable basic authentication.\n\n");
941 #ifdef HAVE_INET6
942 printf("\t--ipv6\n"
943 "\t\tListen on IPv6 address.\n\n");
944 #else
945 printf("\t(This binary was built without IPv6 support: -DNO_IPV6)\n\n");
946 #endif
947 }
948
949 static char *base64_encode(char *str) {
950 const char base64_table[] = {
951 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
952 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
953 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
954 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
955 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
956 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
957 'w', 'x', 'y', 'z', '0', '1', '2', '3',
958 '4', '5', '6', '7', '8', '9', '+', '/'};
959
960 int input_length = strlen(str);
961 int output_length = 4 * ((input_length + 2) / 3);
962
963 char *encoded_data = malloc(output_length+1);
964 if (encoded_data == NULL) return NULL;
965
966 for (int i = 0, j = 0; i < input_length;) {
967
968 uint32_t octet_a = i < input_length ? (unsigned char)str[i++] : 0;
969 uint32_t octet_b = i < input_length ? (unsigned char)str[i++] : 0;
970 uint32_t octet_c = i < input_length ? (unsigned char)str[i++] : 0;
971
972 uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
973
974 encoded_data[j++] = base64_table[(triple >> 3 * 6) & 0x3F];
975 encoded_data[j++] = base64_table[(triple >> 2 * 6) & 0x3F];
976 encoded_data[j++] = base64_table[(triple >> 1 * 6) & 0x3F];
977 encoded_data[j++] = base64_table[(triple >> 0 * 6) & 0x3F];
978 }
979
980 const int mod_table[] = {0, 2, 1};
981 for (int i = 0; i < mod_table[input_length % 3]; i++)
982 encoded_data[output_length - 1 - i] = '=';
983 encoded_data[output_length] = '\0';
984
985 return encoded_data;
986 }
987
988 /* Returns 1 if string is a number, 0 otherwise. Set num to NULL if
989 * disinterested in value.
990 */
991 static int str_to_num(const char *str, long long *num) {
992 char *endptr;
993 long long n;
994
995 errno = 0;
996 n = strtoll(str, &endptr, 10);
997 if (*endptr != '\0')
998 return 0;
999 if (n == LLONG_MIN && errno == ERANGE)
1000 return 0;
1001 if (n == LLONG_MAX && errno == ERANGE)
1002 return 0;
1003 if (num != NULL)
1004 *num = n;
1005 return 1;
1006 }
1007
1008 /* Returns a valid number or dies. */
1009 static long long xstr_to_num(const char *str) {
1010 long long ret;
1011
1012 if (!str_to_num(str, &ret)) {
1013 errx(1, "number \"%s\" is invalid", str);
1014 }
1015 return ret;
1016 }
1017
1018 static void parse_commandline(const int argc, char *argv[]) {
1019 int i;
1020 size_t len;
1021
1022 if ((argc < 2) || (argc == 2 && strcmp(argv[1], "--help") == 0)) {
1023 usage(argv[0]); /* no wwwroot given */
1024 exit(EXIT_SUCCESS);
1025 }
1026
1027 if (getuid() == 0)
1028 bindport = 80;
1029
1030 wwwroot = xstrdup(argv[1]);
1031 /* Strip ending slash. */
1032 len = strlen(wwwroot);
1033 if (len > 0)
1034 if (wwwroot[len - 1] == '/')
1035 wwwroot[len - 1] = '\0';
1036
1037 /* walk through the remainder of the arguments (if any) */
1038 for (i = 2; i < argc; i++) {
1039 if (strcmp(argv[i], "--port") == 0) {
1040 if (++i >= argc)
1041 errx(1, "missing number after --port");
1042 bindport = (uint16_t)xstr_to_num(argv[i]);
1043 }
1044 else if (strcmp(argv[i], "--addr") == 0) {
1045 if (++i >= argc)
1046 errx(1, "missing ip after --addr");
1047 bindaddr = argv[i];
1048 }
1049 else if (strcmp(argv[i], "--maxconn") == 0) {
1050 if (++i >= argc)
1051 errx(1, "missing number after --maxconn");
1052 max_connections = (int)xstr_to_num(argv[i]);
1053 }
1054 else if (strcmp(argv[i], "--log") == 0) {
1055 if (++i >= argc)
1056 errx(1, "missing filename after --log");
1057 logfile_name = argv[i];
1058 }
1059 else if (strcmp(argv[i], "--chroot") == 0) {
1060 want_chroot = 1;
1061 }
1062 else if (strcmp(argv[i], "--daemon") == 0) {
1063 want_daemon = 1;
1064 }
1065 else if (strcmp(argv[i], "--index") == 0) {
1066 if (++i >= argc)
1067 errx(1, "missing filename after --index");
1068 index_name = argv[i];
1069 }
1070 else if (strcmp(argv[i], "--no-listing") == 0) {
1071 no_listing = 1;
1072 }
1073 else if (strcmp(argv[i], "--mimetypes") == 0) {
1074 if (++i >= argc)
1075 errx(1, "missing filename after --mimetypes");
1076 parse_extension_map_file(argv[i]);
1077 }
1078 else if (strcmp(argv[i], "--default-mimetype") == 0) {
1079 if (++i >= argc)
1080 errx(1, "missing string after --default-mimetype");
1081 default_mimetype = argv[i];
1082 }
1083 else if (strcmp(argv[i], "--uid") == 0) {
1084 struct passwd *p;
1085 if (++i >= argc)
1086 errx(1, "missing uid after --uid");
1087 p = getpwnam(argv[i]);
1088 if (!p) {
1089 p = getpwuid((uid_t)xstr_to_num(argv[i]));
1090 }
1091 if (!p)
1092 errx(1, "no such uid: `%s'", argv[i]);
1093 drop_uid = p->pw_uid;
1094 }
1095 else if (strcmp(argv[i], "--gid") == 0) {
1096 struct group *g;
1097 if (++i >= argc)
1098 errx(1, "missing gid after --gid");
1099 g = getgrnam(argv[i]);
1100 if (!g) {
1101 g = getgrgid((gid_t)xstr_to_num(argv[i]));
1102 }
1103 if (!g) {
1104 errx(1, "no such gid: `%s'", argv[i]);
1105 }
1106 drop_gid = g->gr_gid;
1107 }
1108 else if (strcmp(argv[i], "--pidfile") == 0) {
1109 if (++i >= argc)
1110 errx(1, "missing filename after --pidfile");
1111 pidfile_name = argv[i];
1112 }
1113 else if (strcmp(argv[i], "--no-keepalive") == 0) {
1114 want_keepalive = 0;
1115 }
1116 else if (strcmp(argv[i], "--accf") == 0) {
1117 want_accf = 1;
1118 }
1119 else if (strcmp(argv[i], "--forward") == 0) {
1120 const char *host, *url;
1121 if (++i >= argc)
1122 errx(1, "missing host after --forward");
1123 host = argv[i];
1124 if (++i >= argc)
1125 errx(1, "missing url after --forward");
1126 url = argv[i];
1127 add_forward_mapping(host, url);
1128 }
1129 else if (strcmp(argv[i], "--forward-all") == 0) {
1130 if (++i >= argc)
1131 errx(1, "missing url after --forward-all");
1132 forward_all_url = argv[i];
1133 }
1134 else if (strcmp(argv[i], "--no-server-id") == 0) {
1135 want_server_id = 0;
1136 }
1137 else if (strcmp(argv[i], "--timeout") == 0) {
1138 if (++i >= argc)
1139 errx(1, "missing number after --timeout");
1140 timeout_secs = (int)xstr_to_num(argv[i]);
1141 }
1142 else if (strcmp(argv[i], "--auth") == 0) {
1143 if (++i >= argc || strchr(argv[i], ':') == NULL)
1144 errx(1, "missing 'user:pass' after --auth");
1145
1146 char *key = base64_encode(argv[i]);
1147 xasprintf(&auth_key, "Basic %s", key);
1148 free(key);
1149 }
1150 #ifdef HAVE_INET6
1151 else if (strcmp(argv[i], "--ipv6") == 0) {
1152 inet6 = 1;
1153 }
1154 #endif
1155 else
1156 errx(1, "unknown argument `%s'", argv[i]);
1157 }
1158 }
1159
1160 /* Allocate and initialize an empty connection. */
1161 static struct connection *new_connection(void) {
1162 struct connection *conn = xmalloc(sizeof(struct connection));
1163
1164 conn->socket = -1;
1165 memset(&conn->client, 0, sizeof(conn->client));
1166 conn->last_active = now;
1167 conn->request = NULL;
1168 conn->request_length = 0;
1169 conn->method = NULL;
1170 conn->url = NULL;
1171 conn->referer = NULL;
1172 conn->user_agent = NULL;
1173 conn->authorization = NULL;
1174 conn->range_begin = 0;
1175 conn->range_end = 0;
1176 conn->range_begin_given = 0;
1177 conn->range_end_given = 0;
1178 conn->header = NULL;
1179 conn->header_length = 0;
1180 conn->header_sent = 0;
1181 conn->header_dont_free = 0;
1182 conn->header_only = 0;
1183 conn->http_code = 0;
1184 conn->conn_close = 1;
1185 conn->reply = NULL;
1186 conn->reply_dont_free = 0;
1187 conn->reply_fd = -1;
1188 conn->reply_start = 0;
1189 conn->reply_length = 0;
1190 conn->reply_sent = 0;
1191 conn->total_sent = 0;
1192
1193 /* Make it harmless so it gets garbage-collected if it should, for some
1194 * reason, fail to be correctly filled out.
1195 */
1196 conn->state = DONE;
1197
1198 return conn;
1199 }
1200
1201 /* Accept a connection from sockin and add it to the connection queue. */
1202 static void accept_connection(void) {
1203 struct sockaddr_in addrin;
1204 #ifdef HAVE_INET6
1205 struct sockaddr_in6 addrin6;
1206 #endif
1207 socklen_t sin_size;
1208 struct connection *conn;
1209 int fd;
1210
1211 #ifdef HAVE_INET6
1212 if (inet6) {
1213 sin_size = sizeof(addrin6);
1214 memset(&addrin6, 0, sin_size);
1215 fd = accept(sockin, (struct sockaddr *)&addrin6, &sin_size);
1216 } else
1217 #endif
1218 {
1219 sin_size = sizeof(addrin);
1220 memset(&addrin, 0, sin_size);
1221 fd = accept(sockin, (struct sockaddr *)&addrin, &sin_size);
1222 }
1223
1224 if (fd == -1) {
1225 /* Failed to accept, but try to keep serving existing connections. */
1226 if (errno == EMFILE || errno == ENFILE) accepting = 0;
1227 warn("accept()");
1228 return;
1229 }
1230
1231 /* Allocate and initialize struct connection. */
1232 conn = new_connection();
1233 conn->socket = fd;
1234 nonblock_socket(conn->socket);
1235 conn->state = RECV_REQUEST;
1236
1237 #ifdef HAVE_INET6
1238 if (inet6) {
1239 conn->client = addrin6.sin6_addr;
1240 } else
1241 #endif
1242 {
1243 *(in_addr_t *)&conn->client = addrin.sin_addr.s_addr;
1244 }
1245 LIST_INSERT_HEAD(&connlist, conn, entries);
1246
1247 if (debug)
1248 printf("accepted connection from %s:%u (fd %d)\n",
1249 inet_ntoa(addrin.sin_addr),
1250 ntohs(addrin.sin_port),
1251 conn->socket);
1252
1253 /* Try to read straight away rather than going through another iteration
1254 * of the select() loop.
1255 */
1256 poll_recv_request(conn);
1257 }
1258
1259 /* Should this character be logencoded?
1260 */
1261 static int needs_logencoding(const unsigned char c) {
1262 return ((c <= 0x1F) || (c >= 0x7F) || (c == '"'));
1263 }
1264
1265 /* Encode string for logging.
1266 */
1267 static void logencode(const char *src, char *dest) {
1268 static const char hex[] = "0123456789ABCDEF";
1269 int i, j;
1270
1271 for (i = j = 0; src[i] != '\0'; i++) {
1272 if (needs_logencoding((unsigned char)src[i])) {
1273 dest[j++] = '%';
1274 dest[j++] = hex[(src[i] >> 4) & 0xF];
1275 dest[j++] = hex[ src[i] & 0xF];
1276 }
1277 else
1278 dest[j++] = src[i];
1279 }
1280 dest[j] = '\0';
1281 }
1282
1283 /* Format [when] as a CLF date format, stored in the specified buffer. The same
1284 * buffer is returned for convenience.
1285 */
1286 #define CLF_DATE_LEN 29 /* strlen("[10/Oct/2000:13:55:36 -0700]")+1 */
1287 static char *clf_date(char *dest, const time_t when) {
1288 time_t when_copy = when;
1289 if (strftime(dest, CLF_DATE_LEN,
1290 "[%d/%b/%Y:%H:%M:%S %z]", localtime(&when_copy)) == 0)
1291 errx(1, "strftime() failed [%s]", dest);
1292 return dest;
1293 }
1294
1295 /* Add a connection's details to the logfile. */
1296 static void log_connection(const struct connection *conn) {
1297 char *safe_method, *safe_url, *safe_referer, *safe_user_agent,
1298 dest[CLF_DATE_LEN];
1299
1300 if (logfile == NULL)
1301 return;
1302 if (conn->http_code == 0)
1303 return; /* invalid - died in request */
1304 if (conn->method == NULL)
1305 return; /* invalid - didn't parse - maybe too long */
1306
1307 #define make_safe(x) do { \
1308 if (conn->x) { \
1309 safe_##x = xmalloc(strlen(conn->x)*3 + 1); \
1310 logencode(conn->x, safe_##x); \
1311 } else { \
1312 safe_##x = NULL; \
1313 } \
1314 } while(0)
1315
1316 make_safe(method);
1317 make_safe(url);
1318 make_safe(referer);
1319 make_safe(user_agent);
1320
1321 #define use_safe(x) safe_##x ? safe_##x : ""
1322
1323 fprintf(logfile, "%s - - %s \"%s %s HTTP/1.1\" %d %llu \"%s\" \"%s\"\n",
1324 get_address_text(&conn->client),
1325 clf_date(dest, now),
1326 use_safe(method),
1327 use_safe(url),
1328 conn->http_code,
1329 llu(conn->total_sent),
1330 use_safe(referer),
1331 use_safe(user_agent)
1332 );
1333 fflush(logfile);
1334
1335 #define free_safe(x) if (safe_##x) free(safe_##x)
1336
1337 free_safe(method);
1338 free_safe(url);
1339 free_safe(referer);
1340 free_safe(user_agent);
1341
1342 #undef make_safe
1343 #undef use_safe
1344 #undef free_safe
1345 }
1346
1347 /* Log a connection, then cleanly deallocate its internals. */
1348 static void free_connection(struct connection *conn) {
1349 if (debug) printf("free_connection(%d)\n", conn->socket);
1350 log_connection(conn);
1351 if (conn->socket != -1) xclose(conn->socket);
1352 if (conn->request != NULL) free(conn->request);
1353 if (conn->method != NULL) free(conn->method);
1354 if (conn->url != NULL) free(conn->url);
1355 if (conn->referer != NULL) free(conn->referer);
1356 if (conn->user_agent != NULL) free(conn->user_agent);
1357 if (conn->authorization != NULL) free(conn->authorization);
1358 if (conn->header != NULL && !conn->header_dont_free) free(conn->header);
1359 if (conn->reply != NULL && !conn->reply_dont_free) free(conn->reply);
1360 if (conn->reply_fd != -1) xclose(conn->reply_fd);
1361 /* If we ran out of sockets, try to resume accepting. */
1362 accepting = 1;
1363 }
1364
1365 /* Recycle a finished connection for HTTP/1.1 Keep-Alive. */
1366 static void recycle_connection(struct connection *conn) {
1367 int socket_tmp = conn->socket;
1368 if (debug)
1369 printf("recycle_connection(%d)\n", socket_tmp);
1370 conn->socket = -1; /* so free_connection() doesn't close it */
1371 free_connection(conn);
1372 conn->socket = socket_tmp;
1373
1374 /* don't reset conn->client */
1375 conn->request = NULL;
1376 conn->request_length = 0;
1377 conn->method = NULL;
1378 conn->url = NULL;
1379 conn->referer = NULL;
1380 conn->user_agent = NULL;
1381 conn->authorization = NULL;
1382 conn->range_begin = 0;
1383 conn->range_end = 0;
1384 conn->range_begin_given = 0;
1385 conn->range_end_given = 0;
1386 conn->header = NULL;
1387 conn->header_length = 0;
1388 conn->header_sent = 0;
1389 conn->header_dont_free = 0;
1390 conn->header_only = 0;
1391 conn->http_code = 0;
1392 conn->conn_close = 1;
1393 conn->reply = NULL;
1394 conn->reply_dont_free = 0;
1395 conn->reply_fd = -1;
1396 conn->reply_start = 0;
1397 conn->reply_length = 0;
1398 conn->reply_sent = 0;
1399 conn->total_sent = 0;
1400
1401 conn->state = RECV_REQUEST; /* ready for another */
1402 }
1403
1404 /* Uppercasify all characters in a string of given length. */
1405 static void strntoupper(char *str, const size_t length) {
1406 size_t i;
1407
1408 for (i = 0; i < length; i++)
1409 str[i] = (char)toupper(str[i]);
1410 }
1411
1412 /* If a connection has been idle for more than timeout_secs, it will be
1413 * marked as DONE and killed off in httpd_poll().
1414 */
1415 static void poll_check_timeout(struct connection *conn) {
1416 if (timeout_secs > 0) {
1417 if (now - conn->last_active >= timeout_secs) {
1418 if (debug)
1419 printf("poll_check_timeout(%d) closing connection\n",
1420 conn->socket);
1421 conn->conn_close = 1;
1422 conn->state = DONE;
1423 }
1424 }
1425 }
1426
1427 /* Format [when] as an RFC1123 date, stored in the specified buffer. The same
1428 * buffer is returned for convenience.
1429 */
1430 #define DATE_LEN 30 /* strlen("Fri, 28 Feb 2003 00:02:08 GMT")+1 */
1431 static char *rfc1123_date(char *dest, const time_t when) {
1432 time_t when_copy = when;
1433 if (strftime(dest, DATE_LEN,
1434 "%a, %d %b %Y %H:%M:%S GMT", gmtime(&when_copy)) == 0)
1435 errx(1, "strftime() failed [%s]", dest);
1436 return dest;
1437 }
1438
1439 /* Decode URL by converting %XX (where XX are hexadecimal digits) to the
1440 * character it represents. Don't forget to free the return value.
1441 */
1442 static char *urldecode(const char *url) {
1443 size_t i, pos, len = strlen(url);
1444 char *out = xmalloc(len+1);
1445
1446 for (i = 0, pos = 0; i < len; i++) {
1447 if ((url[i] == '%') && (i+2 < len) &&
1448 isxdigit(url[i+1]) && isxdigit(url[i+2])) {
1449 /* decode %XX */
1450 #define HEX_TO_DIGIT(hex) ( \
1451 ((hex) >= 'A' && (hex) <= 'F') ? ((hex)-'A'+10): \
1452 ((hex) >= 'a' && (hex) <= 'f') ? ((hex)-'a'+10): \
1453 ((hex)-'0') )
1454
1455 out[pos++] = HEX_TO_DIGIT(url[i+1]) * 16 +
1456 HEX_TO_DIGIT(url[i+2]);
1457 i += 2;
1458 #undef HEX_TO_DIGIT
1459 } else {
1460 /* straight copy */
1461 out[pos++] = url[i];
1462 }
1463 }
1464 out[pos] = '\0';
1465 return out;
1466 }
1467
1468 /* Returns Connection or Keep-Alive header, depending on conn_close. */
1469 static const char *keep_alive(const struct connection *conn)
1470 {
1471 return (conn->conn_close ? "Connection: close\r\n" : keep_alive_field);
1472 }
1473
1474 /* "Generated by " + pkgname + " on " + date + "\n"
1475 * 1234567890123 1234 2 ('\n' and '\0')
1476 */
1477 static char _generated_on_buf[13 + sizeof(pkgname) - 1 + 4 + DATE_LEN + 2];
1478 static const char *generated_on(const char date[DATE_LEN]) {
1479 if (!want_server_id)
1480 return "";
1481 snprintf(_generated_on_buf, sizeof(_generated_on_buf),
1482 "Generated by %s on %s\n",
1483 pkgname, date);
1484 return _generated_on_buf;
1485 }
1486
1487 /* A default reply for any (erroneous) occasion. */
1488 static void default_reply(struct connection *conn,
1489 const int errcode, const char *errname, const char *format, ...)
1490 __printflike(4, 5);
1491 static void default_reply(struct connection *conn,
1492 const int errcode, const char *errname, const char *format, ...) {
1493 char *reason, date[DATE_LEN];
1494 va_list va;
1495
1496 va_start(va, format);
1497 xvasprintf(&reason, format, va);
1498 va_end(va);
1499
1500 /* Only really need to calculate the date once. */
1501 rfc1123_date(date, now);
1502
1503 conn->reply_length = xasprintf(&(conn->reply),
1504 "<html><head><title>%d %s</title></head><body>\n"
1505 "<h1>%s</h1>\n" /* errname */
1506 "%s\n" /* reason */
1507 "<hr>\n"
1508 "%s" /* generated on */
1509 "</body></html>\n",
1510 errcode, errname, errname, reason, generated_on(date));
1511 free(reason);
1512
1513 const char auth_header[] =
1514 "WWW-Authenticate: Basic realm=\"User Visible Realm\"\r\n";
1515
1516 conn->header_length = xasprintf(&(conn->header),
1517 "HTTP/1.1 %d %s\r\n"
1518 "Date: %s\r\n"
1519 "%s" /* server */
1520 "Accept-Ranges: bytes\r\n"
1521 "%s" /* keep-alive */
1522 "Content-Length: %llu\r\n"
1523 "Content-Type: text/html; charset=UTF-8\r\n"
1524 "%s"
1525 "\r\n",
1526 errcode, errname, date, server_hdr, keep_alive(conn),
1527 llu(conn->reply_length),
1528 (auth_key != NULL ? auth_header : ""));
1529
1530 conn->reply_type = REPLY_GENERATED;
1531 conn->http_code = errcode;
1532 }
1533
1534 static void redirect(struct connection *conn, const char *format, ...)
1535 __printflike(2, 3);
1536 static void redirect(struct connection *conn, const char *format, ...) {
1537 char *where, date[DATE_LEN];
1538 va_list va;
1539
1540 va_start(va, format);
1541 xvasprintf(&where, format, va);
1542 va_end(va);
1543
1544 /* Only really need to calculate the date once. */
1545 rfc1123_date(date, now);
1546
1547 conn->reply_length = xasprintf(&(conn->reply),
1548 "<html><head><title>301 Moved Permanently</title></head><body>\n"
1549 "<h1>Moved Permanently</h1>\n"
1550 "Moved to: <a href=\"%s\">%s</a>\n" /* where x 2 */
1551 "<hr>\n"
1552 "%s" /* generated on */
1553 "</body></html>\n",
1554 where, where, generated_on(date));
1555
1556 conn->header_length = xasprintf(&(conn->header),
1557 "HTTP/1.1 301 Moved Permanently\r\n"
1558 "Date: %s\r\n"
1559 "%s" /* server */
1560 /* "Accept-Ranges: bytes\r\n" - not relevant here */
1561 "Location: %s\r\n"
1562 "%s" /* keep-alive */
1563 "Content-Length: %llu\r\n"
1564 "Content-Type: text/html; charset=UTF-8\r\n"
1565 "\r\n",
1566 date, server_hdr, where, keep_alive(conn), llu(conn->reply_length));
1567
1568 free(where);
1569 conn->reply_type = REPLY_GENERATED;
1570 conn->http_code = 301;
1571 }
1572
1573 /* Parses a single HTTP request field. Returns string from end of [field] to
1574 * first \r, \n or end of request string. Returns NULL if [field] can't be
1575 * matched.
1576 *
1577 * You need to remember to deallocate the result.
1578 * example: parse_field(conn, "Referer: ");
1579 */
1580 static char *parse_field(const struct connection *conn, const char *field) {
1581 size_t bound1, bound2;
1582 char *pos;
1583
1584 /* find start */
1585 pos = strstr(conn->request, field);
1586 if (pos == NULL)
1587 return NULL;
1588 assert(pos >= conn->request);
1589 bound1 = (size_t)(pos - conn->request) + strlen(field);
1590
1591 /* find end */
1592 for (bound2 = bound1;
1593 ((bound2 < conn->request_length) &&
1594 (conn->request[bound2] != '\r') &&
1595 (conn->request[bound2] != '\n'));
1596 bound2++)
1597 ;
1598
1599 /* copy to buffer */
1600 return split_string(conn->request, bound1, bound2);
1601 }
1602
1603 /* Parse a Range: field into range_begin and range_end. Only handles the
1604 * first range if a list is given. Sets range_{begin,end}_given to 1 if
1605 * either part of the range is given.
1606 */
1607 static void parse_range_field(struct connection *conn) {
1608 char *range;
1609
1610 range = parse_field(conn, "Range: bytes=");
1611 if (range == NULL)
1612 return;
1613
1614 do {
1615 size_t bound1, bound2, len;
1616 len = strlen(range);
1617
1618 /* parse number up to hyphen */
1619 bound1 = 0;
1620 for (bound2=0;
1621 (bound2 < len) && isdigit((int)range[bound2]);
1622 bound2++)
1623 ;
1624
1625 if ((bound2 == len) || (range[bound2] != '-'))
1626 break; /* there must be a hyphen here */
1627
1628 if (bound1 != bound2) {
1629 conn->range_begin_given = 1;
1630 conn->range_begin = (off_t)strtoll(range+bound1, NULL, 10);
1631 }
1632
1633 /* parse number after hyphen */
1634 bound2++;
1635 for (bound1=bound2;
1636 (bound2 < len) && isdigit((int)range[bound2]);
1637 bound2++)
1638 ;
1639
1640 if ((bound2 != len) && (range[bound2] != ','))
1641 break; /* must be end of string or a list to be valid */
1642
1643 if (bound1 != bound2) {
1644 conn->range_end_given = 1;
1645 conn->range_end = (off_t)strtoll(range+bound1, NULL, 10);
1646 }
1647 } while(0);
1648 free(range);
1649 }
1650
1651 /* Parse an HTTP request like "GET / HTTP/1.1" to get the method (GET), the
1652 * url (/), the referer (if given) and the user-agent (if given). Remember to
1653 * deallocate all these buffers. The method will be returned in uppercase.
1654 */
1655 static int parse_request(struct connection *conn) {
1656 size_t bound1, bound2;
1657 char *tmp;
1658 assert(conn->request_length == strlen(conn->request));
1659
1660 /* parse method */
1661 for (bound1 = 0;
1662 (bound1 < conn->request_length) &&
1663 (conn->request[bound1] != ' ');
1664 bound1++)
1665 ;
1666
1667 conn->method = split_string(conn->request, 0, bound1);
1668 strntoupper(conn->method, bound1);
1669
1670 /* parse url */
1671 for (;
1672 (bound1 < conn->request_length) &&
1673 (conn->request[bound1] == ' ');
1674 bound1++)
1675 ;
1676
1677 if (bound1 == conn->request_length)
1678 return 0; /* fail */
1679
1680 for (bound2 = bound1 + 1;
1681 (bound2 < conn->request_length) &&
1682 (conn->request[bound2] != ' ') &&
1683 (conn->request[bound2] != '\r') &&
1684 (conn->request[bound2] != '\n');
1685 bound2++)
1686 ;
1687
1688 conn->url = split_string(conn->request, bound1, bound2);
1689
1690 /* parse protocol to determine conn_close */
1691 if (conn->request[bound2] == ' ') {
1692 char *proto;
1693 for (bound1 = bound2;
1694 (bound1 < conn->request_length) &&
1695 (conn->request[bound1] == ' ');
1696 bound1++)
1697 ;
1698
1699 for (bound2 = bound1 + 1;
1700 (bound2 < conn->request_length) &&
1701 (conn->request[bound2] != ' ') &&
1702 (conn->request[bound2] != '\r');
1703 bound2++)
1704 ;
1705
1706 proto = split_string(conn->request, bound1, bound2);
1707 if (strcasecmp(proto, "HTTP/1.1") == 0)
1708 conn->conn_close = 0;
1709 free(proto);
1710 }
1711
1712 /* parse connection field */
1713 tmp = parse_field(conn, "Connection: ");
1714 if (tmp != NULL) {
1715 if (strcasecmp(tmp, "close") == 0)
1716 conn->conn_close = 1;
1717 else if (strcasecmp(tmp, "keep-alive") == 0)
1718 conn->conn_close = 0;
1719 free(tmp);
1720 }
1721
1722 /* cmdline flag can be used to deny keep-alive */
1723 if (!want_keepalive)
1724 conn->conn_close = 1;
1725
1726 /* parse important fields */
1727 conn->referer = parse_field(conn, "Referer: ");
1728 conn->user_agent = parse_field(conn, "User-Agent: ");
1729 conn->authorization = parse_field(conn, "Authorization: ");
1730 parse_range_field(conn);
1731 return 1;
1732 }
1733
1734 static int file_exists(const char *path) {
1735 struct stat filestat;
1736 if ((stat(path, &filestat) == -1) && (errno == ENOENT))
1737 return 0;
1738 else
1739 return 1;
1740 }
1741
1742 struct dlent {
1743 char *name;
1744 int is_dir;
1745 off_t size;
1746 };
1747
1748 static int dlent_cmp(const void *a, const void *b) {
1749 return strcmp((*((const struct dlent * const *)a))->name,
1750 (*((const struct dlent * const *)b))->name);
1751 }
1752
1753 /* Make sorted list of files in a directory. Returns number of entries, or -1
1754 * if error occurs.
1755 */
1756 static ssize_t make_sorted_dirlist(const char *path, struct dlent ***output) {
1757 DIR *dir;
1758 struct dirent *ent;
1759 size_t entries = 0;
1760 size_t pool = 128;
1761 char *currname;
1762 struct dlent **list = NULL;
1763
1764 dir = opendir(path);
1765 if (dir == NULL)
1766 return -1;
1767
1768 currname = xmalloc(strlen(path) + MAXNAMLEN + 1);
1769 list = xmalloc(sizeof(struct dlent*) * pool);
1770
1771 /* construct list */
1772 while ((ent = readdir(dir)) != NULL) {
1773 struct stat s;
1774
1775 if ((ent->d_name[0] == '.') && (ent->d_name[1] == '\0'))
1776 continue; /* skip "." */
1777 assert(strlen(ent->d_name) <= MAXNAMLEN);
1778 sprintf(currname, "%s%s", path, ent->d_name);
1779 if (stat(currname, &s) == -1)
1780 continue; /* skip un-stat-able files */
1781 if (entries == pool) {
1782 pool *= 2;
1783 list = xrealloc(list, sizeof(struct dlent*) * pool);
1784 }
1785 list[entries] = xmalloc(sizeof(struct dlent));
1786 list[entries]->name = xstrdup(ent->d_name);
1787 list[entries]->is_dir = S_ISDIR(s.st_mode);
1788 list[entries]->size = s.st_size;
1789 entries++;
1790 }
1791 closedir(dir);
1792 free(currname);
1793 qsort(list, entries, sizeof(struct dlent*), dlent_cmp);
1794 *output = list;
1795 return (ssize_t)entries;
1796 }
1797
1798 /* Cleanly deallocate a sorted list of directory files. */
1799 static void cleanup_sorted_dirlist(struct dlent **list, const ssize_t size) {
1800 ssize_t i;
1801
1802 for (i = 0; i < size; i++) {
1803 free(list[i]->name);
1804 free(list[i]);
1805 }
1806 }
1807
1808 /* Is this an unreserved character according to
1809 * https://tools.ietf.org/html/rfc3986#section-2.3
1810 */
1811 static int is_unreserved(const unsigned char c) {
1812 if (c >= 'a' && c <= 'z') return 1;
1813 if (c >= 'A' && c <= 'Z') return 1;
1814 if (c >= '0' && c <= '9') return 1;
1815 switch (c) {
1816 case '-':
1817 case '.':
1818 case '_':
1819 case '~':
1820 return 1;
1821 }
1822 return 0;
1823 }
1824
1825 /* Encode string to be an RFC3986-compliant URL part.
1826 * Contributed by nf.
1827 */
1828 static void urlencode(const char *src, char *dest) {
1829 static const char hex[] = "0123456789ABCDEF";
1830 int i, j;
1831
1832 for (i = j = 0; src[i] != '\0'; i++) {
1833 if (!is_unreserved((unsigned char)src[i])) {
1834 dest[j++] = '%';
1835 dest[j++] = hex[(src[i] >> 4) & 0xF];
1836 dest[j++] = hex[ src[i] & 0xF];
1837 }
1838 else
1839 dest[j++] = src[i];
1840 }
1841 dest[j] = '\0';
1842 }
1843
1844 static void generate_dir_listing(struct connection *conn, const char *path) {
1845 char date[DATE_LEN], *spaces;
1846 struct dlent **list;
1847 ssize_t listsize;
1848 size_t maxlen = 2; /* There has to be ".." */
1849 int i;
1850 struct apbuf *listing;
1851
1852 listsize = make_sorted_dirlist(path, &list);
1853 if (listsize == -1) {
1854 default_reply(conn, 500, "Internal Server Error",
1855 "Couldn't list directory: %s", strerror(errno));
1856 return;
1857 }
1858
1859 for (i=0; i<listsize; i++) {
1860 size_t tmp = strlen(list[i]->name);
1861 if (maxlen < tmp)
1862 maxlen = tmp;
1863 }
1864
1865 listing = make_apbuf();
1866 append(listing, "<html>\n<head>\n <title>");
1867 append(listing, conn->url);
1868 append(listing, "</title>\n</head>\n<body>\n<h1>");
1869 append(listing, conn->url);
1870 append(listing, "</h1>\n<tt><pre>\n");
1871
1872 spaces = xmalloc(maxlen);
1873 memset(spaces, ' ', maxlen);
1874
1875 for (i=0; i<listsize; i++) {
1876 /* If a filename is made up of entirely unsafe chars,
1877 * the url would be three times its original length.
1878 */
1879 char safe_url[MAXNAMLEN*3 + 1];
1880
1881 urlencode(list[i]->name, safe_url);
1882
1883 append(listing, "<a href=\"");
1884 append(listing, safe_url);
1885 append(listing, "\">");
1886 append(listing, list[i]->name);
1887 append(listing, "</a>");
1888
1889 if (list[i]->is_dir)
1890 append(listing, "/\n");
1891 else {
1892 appendl(listing, spaces, maxlen-strlen(list[i]->name));
1893 appendf(listing, "%10llu\n", llu(list[i]->size));
1894 }
1895 }
1896
1897 cleanup_sorted_dirlist(list, listsize);
1898 free(list);
1899 free(spaces);
1900
1901 append(listing,
1902 "</pre></tt>\n"
1903 "<hr>\n");
1904
1905 rfc1123_date(date, now);
1906 append(listing, generated_on(date));
1907 append(listing, "</body>\n</html>\n");
1908
1909 conn->reply = listing->str;
1910 conn->reply_length = (off_t)listing->length;
1911 free(listing); /* don't free inside of listing */
1912
1913 conn->header_length = xasprintf(&(conn->header),
1914 "HTTP/1.1 200 OK\r\n"
1915 "Date: %s\r\n"
1916 "%s" /* server */
1917 "Accept-Ranges: bytes\r\n"
1918 "%s" /* keep-alive */
1919 "Content-Length: %llu\r\n"
1920 "Content-Type: text/html; charset=UTF-8\r\n"
1921 "\r\n",
1922 date, server_hdr, keep_alive(conn), llu(conn->reply_length));
1923
1924 conn->reply_type = REPLY_GENERATED;
1925 conn->http_code = 200;
1926 }
1927
1928 /* Process a GET/HEAD request. */
1929 static void process_get(struct connection *conn) {
1930 char *decoded_url, *end, *target, *if_mod_since;
1931 char date[DATE_LEN], lastmod[DATE_LEN];
1932 const char *mimetype = NULL;
1933 const char *forward_to = NULL;
1934 struct stat filestat;
1935
1936 /* strip out query params */
1937 if ((end = strchr(conn->url, '?')) != NULL)
1938 *end = '\0';
1939
1940 /* work out path of file being requested */
1941 decoded_url = urldecode(conn->url);
1942
1943 /* make sure it's safe */
1944 if (make_safe_url(decoded_url) == NULL) {
1945 default_reply(conn, 400, "Bad Request",
1946 "You requested an invalid URL: %s", conn->url);
1947 free(decoded_url);
1948 return;
1949 }
1950
1951 /* test the host against web forward options */
1952 if (forward_map) {
1953 char *host = parse_field(conn, "Host: ");
1954 if (host) {
1955 size_t i;
1956 if (debug)
1957 printf("host=\"%s\"\n", host);
1958 for (i = 0; i < forward_map_size; i++) {
1959 if (strcasecmp(forward_map[i].host, host) == 0) {
1960 forward_to = forward_map[i].target_url;
1961 break;
1962 }
1963 }
1964 free(host);
1965 }
1966 }
1967 if (!forward_to) {
1968 forward_to = forward_all_url;
1969 }
1970 if (forward_to) {
1971 redirect(conn, "%s%s", forward_to, decoded_url);
1972 free(decoded_url);
1973 return;
1974 }
1975
1976 /* does it end in a slash? serve up url/index_name */
1977 if (decoded_url[strlen(decoded_url)-1] == '/') {
1978 xasprintf(&target, "%s%s%s", wwwroot, decoded_url, index_name);
1979 if (!file_exists(target)) {
1980 free(target);
1981 if (no_listing) {
1982 free(decoded_url);
1983 /* Return 404 instead of 403 to make --no-listing
1984 * indistinguishable from the directory not existing.
1985 * i.e.: Don't leak information.
1986 */
1987 default_reply(conn, 404, "Not Found",
1988 "The URL you requested (%s) was not found.", conn->url);
1989 return;
1990 }
1991 xasprintf(&target, "%s%s", wwwroot, decoded_url);
1992 generate_dir_listing(conn, target);
1993 free(target);
1994 free(decoded_url);
1995 return;
1996 }
1997 mimetype = url_content_type(index_name);
1998 }
1999 else {
2000 /* points to a file */
2001 xasprintf(&target, "%s%s", wwwroot, decoded_url);
2002 mimetype = url_content_type(decoded_url);
2003 }
2004 free(decoded_url);
2005 if (debug)
2006 printf("url=\"%s\", target=\"%s\", content-type=\"%s\"\n",
2007 conn->url, target, mimetype);
2008
2009 /* open file */
2010 conn->reply_fd = open(target, O_RDONLY | O_NONBLOCK);
2011 free(target);
2012
2013 if (conn->reply_fd == -1) {
2014 /* open() failed */
2015 if (errno == EACCES)
2016 default_reply(conn, 403, "Forbidden",
2017 "You don't have permission to access (%s).", conn->url);
2018 else if (errno == ENOENT)
2019 default_reply(conn, 404, "Not Found",
2020 "The URL you requested (%s) was not found.", conn->url);
2021 else
2022 default_reply(conn, 500, "Internal Server Error",
2023 "The URL you requested (%s) cannot be returned: %s.",
2024 conn->url, strerror(errno));
2025
2026 return;
2027 }
2028
2029 /* stat the file */
2030 if (fstat(conn->reply_fd, &filestat) == -1) {
2031 default_reply(conn, 500, "Internal Server Error",
2032 "fstat() failed: %s.", strerror(errno));
2033 return;
2034 }
2035
2036 /* make sure it's a regular file */
2037 if (S_ISDIR(filestat.st_mode)) {
2038 redirect(conn, "%s/", conn->url);
2039 return;
2040 }
2041 else if (!S_ISREG(filestat.st_mode)) {
2042 default_reply(conn, 403, "Forbidden", "Not a regular file.");
2043 return;
2044 }
2045
2046 conn->reply_type = REPLY_FROMFILE;
2047 rfc1123_date(lastmod, filestat.st_mtime);
2048
2049 /* check for If-Modified-Since, may not have to send */
2050 if_mod_since = parse_field(conn, "If-Modified-Since: ");
2051 if ((if_mod_since != NULL) &&
2052 (strcmp(if_mod_since, lastmod) == 0)) {
2053 if (debug)
2054 printf("not modified since %s\n", if_mod_since);
2055 conn->http_code = 304;
2056 conn->header_length = xasprintf(&(conn->header),
2057 "HTTP/1.1 304 Not Modified\r\n"
2058 "Date: %s\r\n"
2059 "%s" /* server */
2060 "Accept-Ranges: bytes\r\n"
2061 "%s" /* keep-alive */
2062 "\r\n",
2063 rfc1123_date(date, now), server_hdr, keep_alive(conn));
2064 conn->reply_length = 0;
2065 conn->reply_type = REPLY_GENERATED;
2066 conn->header_only = 1;
2067
2068 free(if_mod_since);
2069 return;
2070 }
2071 free(if_mod_since);
2072
2073 if (conn->range_begin_given || conn->range_end_given) {
2074 off_t from, to;
2075
2076 if (conn->range_begin_given && conn->range_end_given) {
2077 /* 100-200 */
2078 from = conn->range_begin;
2079 to = conn->range_end;
2080
2081 /* clamp end to filestat.st_size-1 */
2082 if (to > (filestat.st_size - 1))
2083 to = filestat.st_size - 1;
2084 }
2085 else if (conn->range_begin_given && !conn->range_end_given) {
2086 /* 100- :: yields 100 to end */
2087 from = conn->range_begin;
2088 to = filestat.st_size - 1;
2089 }
2090 else if (!conn->range_begin_given && conn->range_end_given) {
2091 /* -200 :: yields last 200 */
2092 to = filestat.st_size - 1;
2093 from = to - conn->range_end + 1;
2094
2095 /* clamp start */
2096 if (from < 0)
2097 from = 0;
2098 }
2099 else
2100 errx(1, "internal error - from/to mismatch");
2101
2102 if (from >= filestat.st_size) {
2103 default_reply(conn, 416, "Requested Range Not Satisfiable",
2104 "You requested a range outside of the file.");
2105 return;
2106 }
2107
2108 if (to < from) {
2109 default_reply(conn, 416, "Requested Range Not Satisfiable",
2110 "You requested a backward range.");
2111 return;
2112 }
2113
2114 conn->reply_start = from;
2115 conn->reply_length = to - from + 1;
2116
2117 conn->header_length = xasprintf(&(conn->header),
2118 "HTTP/1.1 206 Partial Content\r\n"
2119 "Date: %s\r\n"
2120 "%s" /* server */
2121 "Accept-Ranges: bytes\r\n"
2122 "%s" /* keep-alive */
2123 "Content-Length: %llu\r\n"
2124 "Content-Range: bytes %llu-%llu/%llu\r\n"
2125 "Content-Type: %s\r\n"
2126 "Last-Modified: %s\r\n"
2127 "\r\n"
2128 ,
2129 rfc1123_date(date, now), server_hdr, keep_alive(conn),
2130 llu(conn->reply_length), llu(from), llu(to),
2131 llu(filestat.st_size), mimetype, lastmod
2132 );
2133 conn->http_code = 206;
2134 if (debug)
2135 printf("sending %llu-%llu/%llu\n",
2136 llu(from), llu(to), llu(filestat.st_size));
2137 }
2138 else {
2139 /* no range stuff */
2140 conn->reply_length = filestat.st_size;
2141 conn->header_length = xasprintf(&(conn->header),
2142 "HTTP/1.1 200 OK\r\n"
2143 "Date: %s\r\n"
2144 "%s" /* server */
2145 "Accept-Ranges: bytes\r\n"
2146 "%s" /* keep-alive */
2147 "Content-Length: %llu\r\n"
2148 "Content-Type: %s\r\n"
2149 "Last-Modified: %s\r\n"
2150 "\r\n"
2151 ,
2152 rfc1123_date(date, now), server_hdr, keep_alive(conn),
2153 llu(conn->reply_length), mimetype, lastmod
2154 );
2155 conn->http_code = 200;
2156 }
2157 }
2158
2159 /* Process a request: build the header and reply, advance state. */
2160 static void process_request(struct connection *conn) {
2161 num_requests++;
2162
2163 if (!parse_request(conn)) {
2164 default_reply(conn, 400, "Bad Request",
2165 "You sent a request that the server couldn't understand.");
2166 }
2167 /* fail if: (auth_enabled) AND (client supplied invalid credentials) */
2168 if (auth_key != NULL &&
2169 (conn->authorization == NULL ||
2170 strcmp(conn->authorization, auth_key)))
2171 {
2172 default_reply(conn, 401, "Unauthorized",
2173 "Access denied due to invalid credentials.");
2174 }
2175 else if (strcmp(conn->method, "GET") == 0) {
2176 process_get(conn);
2177 }
2178 else if (strcmp(conn->method, "HEAD") == 0) {
2179 process_get(conn);
2180 conn->header_only = 1;
2181 }
2182 else if ((strcmp(conn->method, "OPTIONS") == 0) ||
2183 (strcmp(conn->method, "POST") == 0) ||
2184 (strcmp(conn->method, "PUT") == 0) ||
2185 (strcmp(conn->method, "DELETE") == 0) ||
2186 (strcmp(conn->method, "TRACE") == 0) ||
2187 (strcmp(conn->method, "CONNECT") == 0)) {
2188 default_reply(conn, 501, "Not Implemented",
2189 "The method you specified (%s) is not implemented.",
2190 conn->method);
2191 }
2192 else {
2193 default_reply(conn, 400, "Bad Request",
2194 "%s is not a valid HTTP/1.1 method.", conn->method);
2195 }
2196
2197 /* advance state */
2198 conn->state = SEND_HEADER;
2199
2200 /* request not needed anymore */
2201 free(conn->request);
2202 conn->request = NULL; /* important: don't free it again later */
2203 }
2204
2205 /* Receiving request. */
2206 static void poll_recv_request(struct connection *conn) {
2207 char buf[1<<15];
2208 ssize_t recvd;
2209
2210 assert(conn->state == RECV_REQUEST);
2211 recvd = recv(conn->socket, buf, sizeof(buf), 0);
2212 if (debug)
2213 printf("poll_recv_request(%d) got %d bytes\n",
2214 conn->socket, (int)recvd);
2215 if (recvd < 1) {
2216 if (recvd == -1) {
2217 if (errno == EAGAIN) {
2218 if (debug) printf("poll_recv_request would have blocked\n");
2219 return;
2220 }
2221 if (debug) printf("recv(%d) error: %s\n",
2222 conn->socket, strerror(errno));
2223 }
2224 conn->conn_close = 1;
2225 conn->state = DONE;
2226 return;
2227 }
2228 conn->last_active = now;
2229
2230 /* append to conn->request */
2231 assert(recvd > 0);
2232 conn->request = xrealloc(
2233 conn->request, conn->request_length + (size_t)recvd + 1);
2234 memcpy(conn->request+conn->request_length, buf, (size_t)recvd);
2235 conn->request_length += (size_t)recvd;
2236 conn->request[conn->request_length] = 0;
2237 total_in += (size_t)recvd;
2238
2239 /* process request if we have all of it */
2240 if ((conn->request_length > 2) &&
2241 (memcmp(conn->request+conn->request_length-2, "\n\n", 2) == 0))
2242 process_request(conn);
2243 else if ((conn->request_length > 4) &&
2244 (memcmp(conn->request+conn->request_length-4, "\r\n\r\n", 4) == 0))
2245 process_request(conn);
2246
2247 /* die if it's too large */
2248 if (conn->request_length > MAX_REQUEST_LENGTH) {
2249 default_reply(conn, 413, "Request Entity Too Large",
2250 "Your request was dropped because it was too long.");
2251 conn->state = SEND_HEADER;
2252 }
2253
2254 /* if we've moved on to the next state, try to send right away, instead of
2255 * going through another iteration of the select() loop.
2256 */
2257 if (conn->state == SEND_HEADER)
2258 poll_send_header(conn);
2259 }
2260
2261 /* Sending header. Assumes conn->header is not NULL. */
2262 static void poll_send_header(struct connection *conn) {
2263 ssize_t sent;
2264
2265 assert(conn->state == SEND_HEADER);
2266 assert(conn->header_length == strlen(conn->header));
2267
2268 sent = send(conn->socket,
2269 conn->header + conn->header_sent,
2270 conn->header_length - conn->header_sent,
2271 0);
2272 conn->last_active = now;
2273 if (debug)
2274 printf("poll_send_header(%d) sent %d bytes\n",
2275 conn->socket, (int)sent);
2276
2277 /* handle any errors (-1) or closure (0) in send() */
2278 if (sent < 1) {
2279 if ((sent == -1) && (errno == EAGAIN)) {
2280 if (debug) printf("poll_send_header would have blocked\n");
2281 return;
2282 }
2283 if (debug && (sent == -1))
2284 printf("send(%d) error: %s\n", conn->socket, strerror(errno));
2285 conn->conn_close = 1;
2286 conn->state = DONE;
2287 return;
2288 }
2289 assert(sent > 0);
2290 conn->header_sent += (size_t)sent;
2291 conn->total_sent += (size_t)sent;
2292 total_out += (size_t)sent;
2293
2294 /* check if we're done sending header */
2295 if (conn->header_sent == conn->header_length) {
2296 if (conn->header_only)
2297 conn->state = DONE;
2298 else {
2299 conn->state = SEND_REPLY;
2300 /* go straight on to body, don't go through another iteration of
2301 * the select() loop.
2302 */
2303 poll_send_reply(conn);
2304 }
2305 }
2306 }
2307
2308 /* Send chunk on socket <s> from FILE *fp, starting at <ofs> and of size
2309 * <size>. Use sendfile() if possible since it's zero-copy on some platforms.
2310 * Returns the number of bytes sent, 0 on closure, -1 if send() failed, -2 if
2311 * read error.
2312 */
2313 static ssize_t send_from_file(const int s, const int fd,
2314 off_t ofs, size_t size) {
2315 #ifdef __FreeBSD__
2316 off_t sent;
2317 int ret = sendfile(fd, s, ofs, size, NULL, &sent, 0);
2318
2319 /* It is possible for sendfile to send zero bytes due to a blocking
2320 * condition. Handle this correctly.
2321 */
2322 if (ret == -1)
2323 if (errno == EAGAIN)
2324 if (sent == 0)
2325 return -1;
2326 else
2327 return sent;
2328 else
2329 return -1;
2330 else
2331 return size;
2332 #else
2333 #if defined(__linux) || defined(__sun__)
2334 /* Limit truly ridiculous (LARGEFILE) requests. */
2335 if (size > 1<<20)
2336 size = 1<<20;
2337 return sendfile(s, fd, &ofs, size);
2338 #else
2339 /* Fake sendfile() with read(). */
2340 # ifndef min
2341 # define min(a,b) ( ((a)<(b)) ? (a) : (b) )
2342 # endif
2343 char buf[1<<15];
2344 size_t amount = min(sizeof(buf), size);
2345 ssize_t numread;
2346
2347 if (lseek(fd, ofs, SEEK_SET) == -1)
2348 err(1, "fseek(%d)", (int)ofs);
2349 numread = read(fd, buf, amount);
2350 if (numread == 0) {
2351 fprintf(stderr, "premature eof on fd %d\n", fd);
2352 return -1;
2353 }
2354 else if (numread == -1) {
2355 fprintf(stderr, "error reading on fd %d: %s", fd, strerror(errno));
2356 return -1;
2357 }
2358 else if ((size_t)numread != amount) {
2359 fprintf(stderr, "read %zd bytes, expecting %zu bytes on fd %d\n",
2360 numread, amount, fd);
2361 return -1;
2362 }
2363 else
2364 return send(s, buf, amount, 0);
2365 #endif
2366 #endif
2367 }
2368
2369 /* Sending reply. */
2370 static void poll_send_reply(struct connection *conn)
2371 {
2372 ssize_t sent;
2373 /* off_t can be wider than size_t, avoid overflow in send_len */
2374 const size_t max_size_t = ~((size_t)0);
2375 off_t send_len = conn->reply_length - conn->reply_sent;
2376 if (send_len > max_size_t) send_len = max_size_t;
2377
2378 assert(conn->state == SEND_REPLY);
2379 assert(!conn->header_only);
2380 if (conn->reply_type == REPLY_GENERATED) {
2381 assert(conn->reply_length >= conn->reply_sent);
2382 sent = send(conn->socket,
2383 conn->reply + conn->reply_start + conn->reply_sent,
2384 (size_t)send_len, 0);
2385 }
2386 else {
2387 errno = 0;
2388 assert(conn->reply_length >= conn->reply_sent);
2389 sent = send_from_file(conn->socket, conn->reply_fd,
2390 conn->reply_start + conn->reply_sent, (size_t)send_len);
2391 if (debug && (sent < 1))
2392 printf("send_from_file returned %lld (errno=%d %s)\n",
2393 (long long)sent, errno, strerror(errno));
2394 }
2395 conn->last_active = now;
2396 if (debug)
2397 printf("poll_send_reply(%d) sent %d: %llu+[%llu-%llu] of %llu\n",
2398 conn->socket, (int)sent, llu(conn->reply_start),
2399 llu(conn->reply_sent), llu(conn->reply_sent + sent - 1),
2400 llu(conn->reply_length));
2401
2402 /* handle any errors (-1) or closure (0) in send() */
2403 if (sent < 1) {
2404 if (sent == -1) {
2405 if (errno == EAGAIN) {
2406 if (debug)
2407 printf("poll_send_reply would have blocked\n");
2408 return;
2409 }
2410 if (debug)
2411 printf("send(%d) error: %s\n", conn->socket, strerror(errno));
2412 }
2413 else if (sent == 0) {
2414 if (debug)
2415 printf("send(%d) closure\n", conn->socket);
2416 }
2417 conn->conn_close = 1;
2418 conn->state = DONE;
2419 return;
2420 }
2421 conn->reply_sent += sent;
2422 conn->total_sent += (size_t)sent;
2423 total_out += (size_t)sent;
2424
2425 /* check if we're done sending */
2426 if (conn->reply_sent == conn->reply_length)
2427 conn->state = DONE;
2428 }
2429
2430 /* Main loop of the httpd - a select() and then delegation to accept
2431 * connections, handle receiving of requests, and sending of replies.
2432 */
2433 static void httpd_poll(void) {
2434 fd_set recv_set, send_set;
2435 int max_fd, select_ret;
2436 struct connection *conn, *next;
2437 int bother_with_timeout = 0;
2438 struct timeval timeout, t0, t1;
2439
2440 timeout.tv_sec = timeout_secs;
2441 timeout.tv_usec = 0;
2442
2443 FD_ZERO(&recv_set);
2444 FD_ZERO(&send_set);
2445 max_fd = 0;
2446
2447 /* set recv/send fd_sets */
2448 #define MAX_FD_SET(sock, fdset) do { FD_SET(sock,fdset); \
2449 max_fd = (max_fd<sock) ? sock : max_fd; } \
2450 while (0)
2451 if (accepting) MAX_FD_SET(sockin, &recv_set);
2452
2453 LIST_FOREACH_SAFE(conn, &connlist, entries, next) {
2454 switch (conn->state) {
2455 case DONE:
2456 /* do nothing */
2457 break;
2458
2459 case RECV_REQUEST:
2460 MAX_FD_SET(conn->socket, &recv_set);
2461 bother_with_timeout = 1;
2462 break;
2463
2464 case SEND_HEADER:
2465 case SEND_REPLY:
2466 MAX_FD_SET(conn->socket, &send_set);
2467 bother_with_timeout = 1;
2468 break;
2469 }
2470 }
2471 #undef MAX_FD_SET
2472
2473 #if defined(__has_feature)
2474 # if __has_feature(memory_sanitizer)
2475 __msan_unpoison(&recv_set, sizeof(recv_set));
2476 __msan_unpoison(&send_set, sizeof(send_set));
2477 # endif
2478 #endif
2479
2480 /* -select- */
2481 if (debug) {
2482 printf("select() with max_fd %d timeout %d\n",
2483 max_fd, bother_with_timeout ? (int)timeout.tv_sec : 0);
2484 gettimeofday(&t0, NULL);
2485 }
2486 select_ret = select(max_fd + 1, &recv_set, &send_set, NULL,
2487 (bother_with_timeout) ? &timeout : NULL);
2488 if (select_ret == 0) {
2489 if (!bother_with_timeout)
2490 errx(1, "select() timed out");
2491 }
2492 if (select_ret == -1) {
2493 if (errno == EINTR)
2494 return; /* interrupted by signal */
2495 else
2496 err(1, "select() failed");
2497 }
2498 if (debug) {
2499 long long sec, usec;
2500 gettimeofday(&t1, NULL);
2501 sec = t1.tv_sec - t0.tv_sec;
2502 usec = t1.tv_usec - t0.tv_usec;
2503 if (usec < 0) {
2504 usec += 1000000;
2505 sec--;
2506 }
2507 printf("select() returned %d after %lld.%06lld secs\n",
2508 select_ret, sec, usec);
2509 }
2510
2511 /* update time */
2512 now = time(NULL);
2513
2514 /* poll connections that select() says need attention */
2515 if (FD_ISSET(sockin, &recv_set))
2516 accept_connection();
2517
2518 LIST_FOREACH_SAFE(conn, &connlist, entries, next) {
2519 poll_check_timeout(conn);
2520 switch (conn->state) {
2521 case RECV_REQUEST:
2522 if (FD_ISSET(conn->socket, &recv_set)) poll_recv_request(conn);
2523 break;
2524
2525 case SEND_HEADER:
2526 if (FD_ISSET(conn->socket, &send_set)) poll_send_header(conn);
2527 break;
2528
2529 case SEND_REPLY:
2530 if (FD_ISSET(conn->socket, &send_set)) poll_send_reply(conn);
2531 break;
2532
2533 case DONE:
2534 /* (handled later; ignore for now as it's a valid state) */
2535 break;
2536 }
2537
2538 /* Handling SEND_REPLY could have set the state to done. */
2539 if (conn->state == DONE) {
2540 /* clean out finished connection */
2541 if (conn->conn_close) {
2542 LIST_REMOVE(conn, entries);
2543 free_connection(conn);
2544 free(conn);
2545 } else {
2546 recycle_connection(conn);
2547 /* and go right back to recv_request without going through
2548 * select() again.
2549 */
2550 poll_recv_request(conn);
2551 }
2552 }
2553 }
2554 }
2555
2556 /* Daemonize helpers. */
2557 #define PATH_DEVNULL "/dev/null"
2558 static int lifeline[2] = { -1, -1 };
2559 static int fd_null = -1;
2560
2561 static void daemonize_start(void) {
2562 pid_t f;
2563
2564 if (pipe(lifeline) == -1)
2565 err(1, "pipe(lifeline)");
2566
2567 fd_null = open(PATH_DEVNULL, O_RDWR, 0);
2568 if (fd_null == -1)
2569 err(1, "open(" PATH_DEVNULL ")");
2570
2571 f = fork();
2572 if (f == -1)
2573 err(1, "fork");
2574 else if (f != 0) {
2575 /* parent: wait for child */
2576 char tmp[1];
2577 int status;
2578 pid_t w;
2579
2580 if (close(lifeline[1]) == -1)
2581 warn("close lifeline in parent");
2582 if (read(lifeline[0], tmp, sizeof(tmp)) == -1)
2583 warn("read lifeline in parent");
2584 w = waitpid(f, &status, WNOHANG);
2585 if (w == -1)
2586 err(1, "waitpid");
2587 else if (w == 0)
2588 /* child is running happily */
2589 exit(EXIT_SUCCESS);
2590 else
2591 /* child init failed, pass on its exit status */
2592 exit(WEXITSTATUS(status));
2593 }
2594 /* else we are the child: continue initializing */
2595 }
2596
2597 static void daemonize_finish(void) {
2598 if (fd_null == -1)
2599 return; /* didn't daemonize_start() so we're not daemonizing */
2600
2601 if (setsid() == -1)
2602 err(1, "setsid");
2603 if (close(lifeline[0]) == -1)
2604 warn("close read end of lifeline in child");
2605 if (close(lifeline[1]) == -1)
2606 warn("couldn't cut the lifeline");
2607
2608 /* close all our std fds */
2609 if (dup2(fd_null, STDIN_FILENO) == -1)
2610 warn("dup2(stdin)");
2611 if (dup2(fd_null, STDOUT_FILENO) == -1)
2612 warn("dup2(stdout)");
2613 if (dup2(fd_null, STDERR_FILENO) == -1)
2614 warn("dup2(stderr)");
2615 if (fd_null > 2)
2616 close(fd_null);
2617 }
2618
2619 /* [->] pidfile helpers, based on FreeBSD src/lib/libutil/pidfile.c,v 1.3
2620 * Original was copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
2621 */
2622 static int pidfile_fd = -1;
2623 #define PIDFILE_MODE 0600
2624
2625 static void pidfile_remove(void) {
2626 if (unlink(pidfile_name) == -1)
2627 err(1, "unlink(pidfile) failed");
2628 /* if (flock(pidfile_fd, LOCK_UN) == -1)
2629 err(1, "unlock(pidfile) failed"); */
2630 xclose(pidfile_fd);
2631 pidfile_fd = -1;
2632 }
2633
2634 static int pidfile_read(void) {
2635 char buf[16];
2636 int fd, i;
2637 long long pid;
2638
2639 fd = open(pidfile_name, O_RDONLY);
2640 if (fd == -1)
2641 err(1, " after create failed");
2642
2643 i = (int)read(fd, buf, sizeof(buf) - 1);
2644 if (i == -1)
2645 err(1, "read from pidfile failed");
2646 xclose(fd);
2647 buf[i] = '\0';
2648
2649 if (!str_to_num(buf, &pid)) {
2650 err(1, "invalid pidfile contents: \"%s\"", buf);
2651 }
2652 return (int)pid;
2653 }
2654
2655 static void pidfile_create(void) {
2656 int error, fd;
2657 char pidstr[16];
2658
2659 /* Open the PID file and obtain exclusive lock. */
2660 fd = open(pidfile_name,
2661 O_WRONLY | O_CREAT | O_EXLOCK | O_TRUNC | O_NONBLOCK, PIDFILE_MODE);
2662 if (fd == -1) {
2663 if ((errno == EWOULDBLOCK) || (errno == EEXIST))
2664 errx(1, "daemon already running with PID %d", pidfile_read());
2665 else
2666 err(1, "can't create pidfile %s", pidfile_name);
2667 }
2668 pidfile_fd = fd;
2669
2670 if (ftruncate(fd, 0) == -1) {
2671 error = errno;
2672 pidfile_remove();
2673 errno = error;
2674 err(1, "ftruncate() failed");
2675 }
2676
2677 snprintf(pidstr, sizeof(pidstr), "%d", (int)getpid());
2678 if (pwrite(fd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) {
2679 error = errno;
2680 pidfile_remove();
2681 errno = error;
2682 err(1, "pwrite() failed");
2683 }
2684 }
2685 /* [<-] end of pidfile helpers. */
2686
2687 /* Close all sockets and FILEs and exit. */
2688 static void stop_running(int sig unused) {
2689 running = 0;
2690 }
2691
2692 /* Execution starts here. */
2693 int main(int argc, char **argv) {
2694 printf("%s, %s.\n", pkgname, copyright);
2695 parse_default_extension_map();
2696 parse_commandline(argc, argv);
2697 /* parse_commandline() might override parts of the extension map by
2698 * parsing a user-specified file.
2699 */
2700 sort_mime_map();
2701 xasprintf(&keep_alive_field, "Keep-Alive: timeout=%d\r\n", timeout_secs);
2702 if (want_server_id)
2703 xasprintf(&server_hdr, "Server: %s\r\n", pkgname);
2704 else
2705 server_hdr = xstrdup("");
2706 init_sockin();
2707
2708 /* open logfile */
2709 if (logfile_name == NULL)
2710 logfile = stdout;
2711 else {
2712 logfile = fopen(logfile_name, "ab");
2713 if (logfile == NULL)
2714 err(1, "opening logfile: fopen(\"%s\")", logfile_name);
2715 }
2716
2717 if (want_daemon)
2718 daemonize_start();
2719
2720 /* signals */
2721 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
2722 err(1, "signal(ignore SIGPIPE)");
2723 if (signal(SIGINT, stop_running) == SIG_ERR)
2724 err(1, "signal(SIGINT)");
2725 if (signal(SIGTERM, stop_running) == SIG_ERR)
2726 err(1, "signal(SIGTERM)");
2727
2728 /* security */
2729 if (want_chroot) {
2730 tzset(); /* read /etc/localtime before we chroot */
2731 if (chdir(wwwroot) == -1)
2732 err(1, "chdir(%s)", wwwroot);
2733 if (chroot(wwwroot) == -1)
2734 err(1, "chroot(%s)", wwwroot);
2735 printf("chrooted to `%s'\n", wwwroot);
2736 wwwroot[0] = '\0'; /* empty string */
2737 }
2738 if (drop_gid != INVALID_GID) {
2739 gid_t list[1];
2740 list[0] = drop_gid;
2741 if (setgroups(1, list) == -1)
2742 err(1, "setgroups([%d])", (int)drop_gid);
2743 if (setgid(drop_gid) == -1)
2744 err(1, "setgid(%d)", (int)drop_gid);
2745 printf("set gid to %d\n", (int)drop_gid);
2746 }
2747 if (drop_uid != INVALID_UID) {
2748 if (setuid(drop_uid) == -1)
2749 err(1, "setuid(%d)", (int)drop_uid);
2750 printf("set uid to %d\n", (int)drop_uid);
2751 }
2752
2753 /* create pidfile */
2754 if (pidfile_name) pidfile_create();
2755
2756 if (want_daemon) daemonize_finish();
2757
2758 /* main loop */
2759 while (running) httpd_poll();
2760
2761 /* clean exit */
2762 xclose(sockin);
2763 if (logfile != NULL) fclose(logfile);
2764 if (pidfile_name) pidfile_remove();
2765
2766 /* close and free connections */
2767 {
2768 struct connection *conn, *next;
2769
2770 LIST_FOREACH_SAFE(conn, &connlist, entries, next) {
2771 LIST_REMOVE(conn, entries);
2772 free_connection(conn);
2773 free(conn);
2774 }
2775 }
2776
2777 /* free the mallocs */
2778 {
2779 size_t i;
2780 for (i=0; i<mime_map_size; i++) {
2781 free(mime_map[i].extension);
2782 free(mime_map[i].mimetype);
2783 }
2784 free(mime_map);
2785 if (forward_map)
2786 free(forward_map);
2787 free(keep_alive_field);
2788 free(wwwroot);
2789 free(server_hdr);
2790 free(auth_key);
2791 }
2792
2793 /* usage stats */
2794 {
2795 struct rusage r;
2796
2797 getrusage(RUSAGE_SELF, &r);
2798 printf("CPU time used: %u.%02u user, %u.%02u system\n",
2799 (unsigned int)r.ru_utime.tv_sec,
2800 (unsigned int)(r.ru_utime.tv_usec/10000),
2801 (unsigned int)r.ru_stime.tv_sec,
2802 (unsigned int)(r.ru_stime.tv_usec/10000)
2803 );
2804 printf("Requests: %llu\n", llu(num_requests));
2805 printf("Bytes: %llu in, %llu out\n", llu(total_in), llu(total_out));
2806 }
2807
2808 return 0;
2809 }
2810
2811 /* vim:set ts=4 sw=4 sts=4 expandtab tw=78: */