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