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