1 /*
2 * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
3 * All rights reserved
4 *
5 * "THE BEER-WARE LICENSE" (Revision 42):
6 * Sergey Lyubka wrote this file. As long as you retain this notice you
7 * can do whatever you want with this stuff. If we meet some day, and you think
8 * this stuff is worth it, you can buy me a beer in return.
9 */
10
11 /*
12 * Small and portable HTTP server, http://shttpd.sourceforge.net
13 * $Id: shttpd.c,v 1.57 2008/08/23 21:00:38 drozd Exp $
14 */
15
16 #include "defs.h"
17
18 time_t _shttpd_current_time; /* Current UTC time */
19 int _shttpd_tz_offset; /* Time zone offset from UTC */
20 int _shttpd_exit_flag; /* Program exit flag */
21
22 const struct vec _shttpd_known_http_methods[] = {
23 {"GET", 3},
24 {"POST", 4},
25 {"PUT", 3},
26 {"DELETE", 6},
27 {"HEAD", 4},
28 {NULL, 0}
29 };
30
31 /*
32 * This structure tells how HTTP headers must be parsed.
33 * Used by parse_headers() function.
34 */
35 #define OFFSET(x) offsetof(struct headers, x)
36 static const struct http_header http_headers[] = {
37 {16, HDR_INT, OFFSET(cl), "Content-Length: " },
38 {14, HDR_STRING, OFFSET(ct), "Content-Type: " },
39 {12, HDR_STRING, OFFSET(useragent), "User-Agent: " },
40 {19, HDR_DATE, OFFSET(ims), "If-Modified-Since: " },
41 {15, HDR_STRING, OFFSET(auth), "Authorization: " },
42 {9, HDR_STRING, OFFSET(referer), "Referer: " },
43 {8, HDR_STRING, OFFSET(cookie), "Cookie: " },
44 {10, HDR_STRING, OFFSET(location), "Location: " },
45 {8, HDR_INT, OFFSET(status), "Status: " },
46 {7, HDR_STRING, OFFSET(range), "Range: " },
47 {12, HDR_STRING, OFFSET(connection), "Connection: " },
48 {19, HDR_STRING, OFFSET(transenc), "Transfer-Encoding: " },
49 {0, HDR_INT, 0, NULL }
50 };
51
52 struct shttpd_ctx *init_ctx(const char *config_file, int argc, char *argv[]);
53 static void process_connection(struct conn *, int, int);
54
55 int
_shttpd_is_true(const char * str)56 _shttpd_is_true(const char *str)
57 {
58 static const char *trues[] = {"1", "yes", "true", "jawohl", NULL};
59 const char **p;
60
61 for (p = trues; *p != NULL; p++)
62 if (str && !strcmp(str, *p))
63 return (TRUE);
64
65 return (FALSE);
66 }
67
68 static void
free_list(struct llhead * head,void (* dtor)(struct llhead *))69 free_list(struct llhead *head, void (*dtor)(struct llhead *))
70 {
71 struct llhead *lp, *tmp;
72
73 LL_FOREACH_SAFE(head, lp, tmp) {
74 LL_DEL(lp);
75 dtor(lp);
76 }
77 }
78
79 static void
listener_destructor(struct llhead * lp)80 listener_destructor(struct llhead *lp)
81 {
82 struct listener *listener = LL_ENTRY(lp, struct listener, link);
83
84 (void) closesocket(listener->sock);
85 free(listener);
86 }
87
88 static void
registered_uri_destructor(struct llhead * lp)89 registered_uri_destructor(struct llhead *lp)
90 {
91 struct registered_uri *ruri = LL_ENTRY(lp, struct registered_uri, link);
92
93 free((void *) ruri->uri);
94 free(ruri);
95 }
96
97 static void
acl_destructor(struct llhead * lp)98 acl_destructor(struct llhead *lp)
99 {
100 struct acl *acl = LL_ENTRY(lp, struct acl, link);
101 free(acl);
102 }
103
104 int
_shttpd_url_decode(const char * src,int src_len,char * dst,int dst_len)105 _shttpd_url_decode(const char *src, int src_len, char *dst, int dst_len)
106 {
107 int i, j, a, b;
108 #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
109
110 for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++)
111 switch (src[i]) {
112 case '%':
113 if (isxdigit(((unsigned char *) src)[i + 1]) &&
114 isxdigit(((unsigned char *) src)[i + 2])) {
115 a = tolower(((unsigned char *)src)[i + 1]);
116 b = tolower(((unsigned char *)src)[i + 2]);
117 dst[j] = (HEXTOI(a) << 4) | HEXTOI(b);
118 i += 2;
119 } else {
120 dst[j] = '%';
121 }
122 break;
123 default:
124 dst[j] = src[i];
125 break;
126 }
127
128 dst[j] = '\0'; /* Null-terminate the destination */
129
130 return (j);
131 }
132
133 static const char *
is_alias(struct shttpd_ctx * ctx,const char * uri,struct vec * a_uri,struct vec * a_path)134 is_alias(struct shttpd_ctx *ctx, const char *uri,
135 struct vec *a_uri, struct vec *a_path)
136 {
137 const char *p, *s = ctx->options[OPT_ALIASES];
138 size_t len;
139
140 DBG(("is_alias: aliases [%s]", s == NULL ? "" : s));
141
142 FOR_EACH_WORD_IN_LIST(s, len) {
143
144 if ((p = memchr(s, '=', len)) == NULL || p >= s + len || p == s)
145 continue;
146
147 if (memcmp(uri, s, p - s) == 0) {
148 a_uri->ptr = s;
149 a_uri->len = p - s;
150 a_path->ptr = ++p;
151 a_path->len = (s + len) - p;
152 return (s);
153 }
154 }
155
156 return (NULL);
157 }
158
159 void
_shttpd_stop_stream(struct stream * stream)160 _shttpd_stop_stream(struct stream *stream)
161 {
162 if (stream->io_class != NULL && stream->io_class->close != NULL)
163 stream->io_class->close(stream);
164
165 stream->io_class= NULL;
166 stream->flags |= FLAG_CLOSED;
167 stream->flags &= ~(FLAG_R | FLAG_W | FLAG_ALWAYS_READY);
168
169 DBG(("%d %s stopped. %lu of content data, %d now in a buffer",
170 stream->conn->rem.chan.sock,
171 stream->io_class ? stream->io_class->name : "(null)",
172 (unsigned long) stream->io.total, (int) io_data_len(&stream->io)));
173 }
174
175 /*
176 * Setup listening socket on given port, return socket
177 */
178 static int
shttpd_open_listening_port(int port)179 shttpd_open_listening_port(int port)
180 {
181 int sock, on = 1;
182 struct usa sa;
183
184 #ifdef _WIN32
185 {WSADATA data; WSAStartup(MAKEWORD(2,2), &data);}
186 #endif /* _WIN32 */
187
188 sa.len = sizeof(sa.u.sin);
189 sa.u.sin.sin_family = AF_INET;
190 sa.u.sin.sin_port = htons((uint16_t) port);
191 sa.u.sin.sin_addr.s_addr = htonl(INADDR_ANY);
192
193 if ((sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
194 goto fail;
195 if (_shttpd_set_non_blocking_mode(sock) != 0)
196 goto fail;
197 if (setsockopt(sock, SOL_SOCKET,
198 SO_REUSEADDR,(char *) &on, sizeof(on)) != 0)
199 goto fail;
200 if (bind(sock, &sa.u.sa, sa.len) < 0)
201 goto fail;
202 if (listen(sock, 128) != 0)
203 goto fail;
204
205 #ifndef _WIN32
206 (void) fcntl(sock, F_SETFD, FD_CLOEXEC);
207 #endif /* !_WIN32 */
208
209 return (sock);
210 fail:
211 if (sock != -1)
212 (void) closesocket(sock);
213 _shttpd_elog(E_LOG, NULL, "open_listening_port(%d): %s", port, strerror(errno));
214 return (-1);
215 }
216
217 /*
218 * Check whether full request is buffered Return headers length, or 0
219 */
220 int
_shttpd_get_headers_len(const char * buf,size_t buflen)221 _shttpd_get_headers_len(const char *buf, size_t buflen)
222 {
223 const char *s, *e;
224 int len = 0;
225
226 for (s = buf, e = s + buflen - 1; len == 0 && s < e; s++)
227 /* Control characters are not allowed but >=128 is. */
228 if (!isprint(* (unsigned char *) s) && *s != '\r' &&
229 *s != '\n' && * (unsigned char *) s < 128)
230 len = -1;
231 else if (s[0] == '\n' && s[1] == '\n')
232 len = s - buf + 2;
233 else if (s[0] == '\n' && &s[1] < e &&
234 s[1] == '\r' && s[2] == '\n')
235 len = s - buf + 3;
236
237 return (len);
238 }
239
240 /*
241 * Send error message back to a client.
242 */
243 void
_shttpd_send_server_error(struct conn * c,int status,const char * reason)244 _shttpd_send_server_error(struct conn *c, int status, const char *reason)
245 {
246 struct llhead *lp;
247 struct error_handler *e;
248
249 LL_FOREACH(&c->ctx->error_handlers, lp) {
250 e = LL_ENTRY(lp, struct error_handler, link);
251
252 if (e->code == status) {
253 if (c->loc.io_class != NULL &&
254 c->loc.io_class->close != NULL)
255 c->loc.io_class->close(&c->loc);
256 io_clear(&c->loc.io);
257 _shttpd_setup_embedded_stream(c,
258 e->callback, e->callback_data);
259 return;
260 }
261 }
262
263 io_clear(&c->loc.io);
264 c->loc.io.head = _shttpd_snprintf(c->loc.io.buf, c->loc.io.size,
265 "HTTP/1.1 %d %s\r\n"
266 "Content-Type: text/plain\r\n"
267 "Content-Length: 12\r\n"
268 "\r\n"
269 "Error: %03d\r\n",
270 status, reason, status);
271 c->loc.content_len = 10;
272 c->status = status;
273 _shttpd_stop_stream(&c->loc);
274 }
275
276 /*
277 * Convert month to the month number. Return -1 on error, or month number
278 */
279 static int
montoi(const char * s)280 montoi(const char *s)
281 {
282 static const char *ar[] = {
283 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
284 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
285 };
286 size_t i;
287
288 for (i = 0; i < sizeof(ar) / sizeof(ar[0]); i++)
289 if (!strcmp(s, ar[i]))
290 return (i);
291
292 return (-1);
293 }
294
295 /*
296 * Parse date-time string, and return the corresponding time_t value
297 */
298 static time_t
date_to_epoch(const char * s)299 date_to_epoch(const char *s)
300 {
301 struct tm tm, *tmp;
302 char mon[32];
303 int sec, min, hour, mday, month, year;
304
305 (void) memset(&tm, 0, sizeof(tm));
306 sec = min = hour = mday = month = year = 0;
307
308 if (((sscanf(s, "%d/%3s/%d %d:%d:%d",
309 &mday, mon, &year, &hour, &min, &sec) == 6) ||
310 (sscanf(s, "%d %3s %d %d:%d:%d",
311 &mday, mon, &year, &hour, &min, &sec) == 6) ||
312 (sscanf(s, "%*3s, %d %3s %d %d:%d:%d",
313 &mday, mon, &year, &hour, &min, &sec) == 6) ||
314 (sscanf(s, "%d-%3s-%d %d:%d:%d",
315 &mday, mon, &year, &hour, &min, &sec) == 6)) &&
316 (month = montoi(mon)) != -1) {
317 tm.tm_mday = mday;
318 tm.tm_mon = month;
319 tm.tm_year = year;
320 tm.tm_hour = hour;
321 tm.tm_min = min;
322 tm.tm_sec = sec;
323 }
324
325 if (tm.tm_year > 1900)
326 tm.tm_year -= 1900;
327 else if (tm.tm_year < 70)
328 tm.tm_year += 100;
329
330 /* Set Daylight Saving Time field */
331 tmp = localtime(&_shttpd_current_time);
332 tm.tm_isdst = tmp->tm_isdst;
333
334 return (mktime(&tm));
335 }
336
337 static void
remove_double_dots(char * s)338 remove_double_dots(char *s)
339 {
340 char *p = s;
341
342 while (*s != '\0') {
343 *p++ = *s++;
344 if (s[-1] == '/' || s[-1] == '\\')
345 while (*s == '.' || *s == '/' || *s == '\\')
346 s++;
347 }
348 *p = '\0';
349 }
350
351 void
_shttpd_parse_headers(const char * s,int len,struct headers * parsed)352 _shttpd_parse_headers(const char *s, int len, struct headers *parsed)
353 {
354 const struct http_header *h;
355 union variant *v;
356 const char *p, *e = s + len;
357
358 DBG(("parsing headers (len %d): [%.*s]", len, len, s));
359
360 /* Loop through all headers in the request */
361 while (s < e) {
362
363 /* Find where this header ends */
364 for (p = s; p < e && *p != '\n'; ) p++;
365
366 /* Is this header known to us ? */
367 for (h = http_headers; h->len != 0; h++)
368 if (e - s > h->len &&
369 !_shttpd_strncasecmp(s, h->name, h->len))
370 break;
371
372 /* If the header is known to us, store its value */
373 if (h->len != 0) {
374
375 /* Shift to where value starts */
376 s += h->len;
377
378 /* Find place to store the value */
379 v = (union variant *) ((char *) parsed + h->offset);
380
381 /* Fetch header value into the connection structure */
382 if (h->type == HDR_STRING) {
383 v->v_vec.ptr = s;
384 v->v_vec.len = p - s;
385 if (p[-1] == '\r' && v->v_vec.len > 0)
386 v->v_vec.len--;
387 } else if (h->type == HDR_INT) {
388 v->v_big_int = strtoul(s, NULL, 10);
389 } else if (h->type == HDR_DATE) {
390 v->v_time = date_to_epoch(s);
391 }
392 }
393
394 s = p + 1; /* Shift to the next header */
395 }
396 }
397
398 static const struct {
399 const char *extension;
400 int ext_len;
401 const char *mime_type;
402 } builtin_mime_types[] = {
403 {"html", 4, "text/html" },
404 {"htm", 3, "text/html" },
405 {"txt", 3, "text/plain" },
406 {"css", 3, "text/css" },
407 {"ico", 3, "image/x-icon" },
408 {"gif", 3, "image/gif" },
409 {"jpg", 3, "image/jpeg" },
410 {"jpeg", 4, "image/jpeg" },
411 {"png", 3, "image/png" },
412 {"svg", 3, "image/svg+xml" },
413 {"torrent", 7, "application/x-bittorrent" },
414 {"wav", 3, "audio/x-wav" },
415 {"mp3", 3, "audio/x-mp3" },
416 {"mid", 3, "audio/mid" },
417 {"m3u", 3, "audio/x-mpegurl" },
418 {"ram", 3, "audio/x-pn-realaudio" },
419 {"ra", 2, "audio/x-pn-realaudio" },
420 {"doc", 3, "application/msword", },
421 {"exe", 3, "application/octet-stream" },
422 {"zip", 3, "application/x-zip-compressed" },
423 {"xls", 3, "application/excel" },
424 {"tgz", 3, "application/x-tar-gz" },
425 {"tar.gz", 6, "application/x-tar-gz" },
426 {"tar", 3, "application/x-tar" },
427 {"gz", 2, "application/x-gunzip" },
428 {"arj", 3, "application/x-arj-compressed" },
429 {"rar", 3, "application/x-arj-compressed" },
430 {"rtf", 3, "application/rtf" },
431 {"pdf", 3, "application/pdf" },
432 {"swf", 3, "application/x-shockwave-flash" },
433 {"mpg", 3, "video/mpeg" },
434 {"mpeg", 4, "video/mpeg" },
435 {"asf", 3, "video/x-ms-asf" },
436 {"avi", 3, "video/x-msvideo" },
437 {"bmp", 3, "image/bmp" },
438 {NULL, 0, NULL }
439 };
440
441 void
_shttpd_get_mime_type(struct shttpd_ctx * ctx,const char * uri,int len,struct vec * vec)442 _shttpd_get_mime_type(struct shttpd_ctx *ctx,
443 const char *uri, int len, struct vec *vec)
444 {
445 const char *eq, *p = ctx->options[OPT_MIME_TYPES];
446 int i, n, ext_len;
447
448 /* Firt, loop through the custom mime types if any */
449 FOR_EACH_WORD_IN_LIST(p, n) {
450 if ((eq = memchr(p, '=', n)) == NULL || eq >= p + n || eq == p)
451 continue;
452 ext_len = eq - p;
453 if (len > ext_len && uri[len - ext_len - 1] == '.' &&
454 !_shttpd_strncasecmp(p, &uri[len - ext_len], ext_len)) {
455 vec->ptr = eq + 1;
456 vec->len = p + n - vec->ptr;
457 return;
458 }
459 }
460
461 /* If no luck, try built-in mime types */
462 for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
463 ext_len = builtin_mime_types[i].ext_len;
464 if (len > ext_len && uri[len - ext_len - 1] == '.' &&
465 !_shttpd_strncasecmp(builtin_mime_types[i].extension,
466 &uri[len - ext_len], ext_len)) {
467 vec->ptr = builtin_mime_types[i].mime_type;
468 vec->len = strlen(vec->ptr);
469 return;
470 }
471 }
472
473 /* Oops. This extension is unknown to us. Fallback to text/plain */
474 vec->ptr = "text/plain";
475 vec->len = strlen(vec->ptr);
476 }
477
478 /*
479 * For given directory path, substitute it to valid index file.
480 * Return 0 if index file has been found, -1 if not found
481 */
482 static int
find_index_file(struct conn * c,char * path,size_t maxpath,struct stat * stp)483 find_index_file(struct conn *c, char *path, size_t maxpath, struct stat *stp)
484 {
485 char buf[FILENAME_MAX];
486 const char *s = c->ctx->options[OPT_INDEX_FILES];
487 size_t len;
488
489 FOR_EACH_WORD_IN_LIST(s, len) {
490 /* path must end with '/' character */
491 _shttpd_snprintf(buf, sizeof(buf), "%s%.*s", path, len, s);
492 if (_shttpd_stat(buf, stp) == 0) {
493 _shttpd_strlcpy(path, buf, maxpath);
494 _shttpd_get_mime_type(c->ctx, s, len, &c->mime_type);
495 return (0);
496 }
497 }
498
499 return (-1);
500 }
501
502 /*
503 * Try to open requested file, return 0 if OK, -1 if error.
504 * If the file is given arguments using PATH_INFO mechanism,
505 * initialize pathinfo pointer.
506 */
507 static int
get_path_info(struct conn * c,char * path,struct stat * stp)508 get_path_info(struct conn *c, char *path, struct stat *stp)
509 {
510 char *p, *e;
511
512 if (_shttpd_stat(path, stp) == 0)
513 return (0);
514
515 p = path + strlen(path);
516 e = path + strlen(c->ctx->options[OPT_ROOT]) + 2;
517
518 /* Strip directory parts of the path one by one */
519 for (; p > e; p--)
520 if (*p == '/') {
521 *p = '\0';
522 if (!_shttpd_stat(path, stp) && !S_ISDIR(stp->st_mode)) {
523 c->path_info = p + 1;
524 return (0);
525 } else {
526 *p = '/';
527 }
528 }
529
530 return (-1);
531 }
532
533 static void
decide_what_to_do(struct conn * c)534 decide_what_to_do(struct conn *c)
535 {
536 char path[URI_MAX], buf[1024], *root;
537 struct vec alias_uri, alias_path;
538 struct stat st;
539 int rc;
540 struct registered_uri *ruri;
541
542 DBG(("decide_what_to_do: [%s]", c->uri));
543
544 if ((c->query = strchr(c->uri, '?')) != NULL)
545 *c->query++ = '\0';
546
547 _shttpd_url_decode(c->uri, strlen(c->uri), c->uri, strlen(c->uri) + 1);
548 remove_double_dots(c->uri);
549
550 root = c->ctx->options[OPT_ROOT];
551 if (strlen(c->uri) + strlen(root) >= sizeof(path)) {
552 _shttpd_send_server_error(c, 400, "URI is too long");
553 return;
554 }
555
556 (void) _shttpd_snprintf(path, sizeof(path), "%s%s", root, c->uri);
557
558 /* User may use the aliases - check URI for mount point */
559 if (is_alias(c->ctx, c->uri, &alias_uri, &alias_path) != NULL) {
560 (void) _shttpd_snprintf(path, sizeof(path), "%.*s%s",
561 alias_path.len, alias_path.ptr, c->uri + alias_uri.len);
562 DBG(("using alias %.*s -> %.*s", alias_uri.len, alias_uri.ptr,
563 alias_path.len, alias_path.ptr));
564 }
565
566 #if !defined(NO_AUTH)
567 if (_shttpd_check_authorization(c, path) != 1) {
568 _shttpd_send_authorization_request(c);
569 } else
570 #endif /* NO_AUTH */
571 if ((ruri = _shttpd_is_registered_uri(c->ctx, c->uri)) != NULL) {
572 _shttpd_setup_embedded_stream(c,
573 ruri->callback, ruri->callback_data);
574 } else
575 if (strstr(path, HTPASSWD)) {
576 /* Do not allow to view passwords files */
577 _shttpd_send_server_error(c, 403, "Forbidden");
578 } else
579 #if !defined(NO_AUTH)
580 if ((c->method == METHOD_PUT || c->method == METHOD_DELETE) &&
581 (c->ctx->options[OPT_AUTH_PUT] == NULL ||
582 !_shttpd_is_authorized_for_put(c))) {
583 _shttpd_send_authorization_request(c);
584 } else
585 #endif /* NO_AUTH */
586 if (c->method == METHOD_PUT) {
587 c->status = _shttpd_stat(path, &st) == 0 ? 200 : 201;
588
589 if (c->ch.range.v_vec.len > 0) {
590 _shttpd_send_server_error(c, 501,
591 "PUT Range Not Implemented");
592 } else if ((rc = _shttpd_put_dir(path)) == 0) {
593 _shttpd_send_server_error(c, 200, "OK");
594 } else if (rc == -1) {
595 _shttpd_send_server_error(c, 500, "PUT Directory Error");
596 } else if (c->rem.content_len == 0) {
597 _shttpd_send_server_error(c, 411, "Length Required");
598 } else if ((c->loc.chan.fd = _shttpd_open(path, O_WRONLY | O_BINARY |
599 O_CREAT | O_NONBLOCK | O_TRUNC, 0644)) == -1) {
600 _shttpd_send_server_error(c, 500, "PUT Error");
601 } else {
602 DBG(("PUT file [%s]", c->uri));
603 c->loc.io_class = &_shttpd_io_file;
604 c->loc.flags |= FLAG_W | FLAG_ALWAYS_READY ;
605 }
606 } else if (c->method == METHOD_DELETE) {
607 DBG(("DELETE [%s]", c->uri));
608 if (_shttpd_remove(path) == 0)
609 _shttpd_send_server_error(c, 200, "OK");
610 else
611 _shttpd_send_server_error(c, 500, "DELETE Error");
612 } else if (get_path_info(c, path, &st) != 0) {
613 _shttpd_send_server_error(c, 404, "Not Found");
614 } else if (S_ISDIR(st.st_mode) && path[strlen(path) - 1] != '/') {
615 (void) _shttpd_snprintf(buf, sizeof(buf),
616 "Moved Permanently\r\nLocation: %s/", c->uri);
617 _shttpd_send_server_error(c, 301, buf);
618 } else if (S_ISDIR(st.st_mode) &&
619 find_index_file(c, path, sizeof(path) - 1, &st) == -1 &&
620 !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
621 _shttpd_send_server_error(c, 403, "Directory Listing Denied");
622 } else if (S_ISDIR(st.st_mode) && IS_TRUE(c->ctx, OPT_DIR_LIST)) {
623 if ((c->loc.chan.dir.path = _shttpd_strdup(path)) != NULL)
624 _shttpd_get_dir(c);
625 else
626 _shttpd_send_server_error(c, 500, "GET Directory Error");
627 } else if (S_ISDIR(st.st_mode) && !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
628 _shttpd_send_server_error(c, 403, "Directory listing denied");
629 #if !defined(NO_CGI)
630 } else if (_shttpd_match_extension(path,
631 c->ctx->options[OPT_CGI_EXTENSIONS])) {
632 if (c->method != METHOD_POST && c->method != METHOD_GET) {
633 _shttpd_send_server_error(c, 501, "Bad method ");
634 } else if ((_shttpd_run_cgi(c, path)) == -1) {
635 _shttpd_send_server_error(c, 500, "Cannot exec CGI");
636 } else {
637 _shttpd_do_cgi(c);
638 }
639 #endif /* NO_CGI */
640 #if !defined(NO_SSI)
641 } else if (_shttpd_match_extension(path,
642 c->ctx->options[OPT_SSI_EXTENSIONS])) {
643 if ((c->loc.chan.fd = _shttpd_open(path,
644 O_RDONLY | O_BINARY, 0644)) == -1) {
645 _shttpd_send_server_error(c, 500, "SSI open error");
646 } else {
647 _shttpd_do_ssi(c);
648 }
649 #endif /* NO_CGI */
650 } else if (c->ch.ims.v_time && st.st_mtime <= c->ch.ims.v_time) {
651 _shttpd_send_server_error(c, 304, "Not Modified");
652 } else if ((c->loc.chan.fd = _shttpd_open(path,
653 O_RDONLY | O_BINARY, 0644)) != -1) {
654 _shttpd_get_file(c, &st);
655 } else {
656 _shttpd_send_server_error(c, 500, "Internal Error");
657 }
658 }
659
660 static int
set_request_method(struct conn * c)661 set_request_method(struct conn *c)
662 {
663 const struct vec *v;
664
665 /* Set the request method */
666 for (v = _shttpd_known_http_methods; v->ptr != NULL; v++)
667 if (!memcmp(c->rem.io.buf, v->ptr, v->len)) {
668 c->method = v - _shttpd_known_http_methods;
669 break;
670 }
671
672 return (v->ptr == NULL);
673 }
674
675 static void
parse_http_request(struct conn * c)676 parse_http_request(struct conn *c)
677 {
678 char *s, *e, *p, *start;
679 int uri_len, req_len, n;
680
681 s = io_data(&c->rem.io);;
682 req_len = c->rem.headers_len =
683 _shttpd_get_headers_len(s, io_data_len(&c->rem.io));
684
685 if (req_len == 0 && io_space_len(&c->rem.io) == 0) {
686 io_clear(&c->rem.io);
687 _shttpd_send_server_error(c, 400, "Request is too big");
688 }
689
690 if (req_len == 0) {
691 return;
692 } else if (req_len < 16) { /* Minimal: "GET / HTTP/1.0\n\n" */
693 _shttpd_send_server_error(c, 400, "Bad request");
694 } else if (set_request_method(c)) {
695 _shttpd_send_server_error(c, 501, "Method Not Implemented");
696 } else if ((c->request = _shttpd_strndup(s, req_len)) == NULL) {
697 _shttpd_send_server_error(c, 500, "Cannot allocate request");
698 }
699
700 if (c->loc.flags & FLAG_CLOSED)
701 return;
702
703 io_inc_tail(&c->rem.io, req_len);
704
705 DBG(("Conn %d: parsing request: [%.*s]", c->rem.chan.sock, req_len, s));
706 c->rem.flags |= FLAG_HEADERS_PARSED;
707
708 /* Set headers pointer. Headers follow the request line */
709 c->headers = memchr(c->request, '\n', req_len);
710 assert(c->headers != NULL);
711 assert(c->headers < c->request + req_len);
712 if (c->headers > c->request && c->headers[-1] == '\r')
713 c->headers[-1] = '\0';
714 *c->headers++ = '\0';
715
716 /*
717 * Now make a copy of the URI, because it will be URL-decoded,
718 * and we need a copy of unmodified URI for the access log.
719 * First, we skip the REQUEST_METHOD and shift to the URI.
720 */
721 for (p = c->request, e = p + req_len; *p != ' ' && p < e; p++);
722 while (p < e && *p == ' ')
723 p++;
724
725 /* Now remember where URI starts, and shift to the end of URI */
726 for (start = p; p < e && !isspace((unsigned char)*p); ) p++;
727 uri_len = p - start;
728
729 /* Skip space following the URI */
730 while (p < e && *p == ' ')
731 p++;
732
733 /* Now comes the HTTP-Version in the form HTTP/<major>.<minor> */
734 if (sscanf(p, "HTTP/%lu.%lu%n",
735 &c->major_version, &c->minor_version, &n) != 2 || p[n] != '\0') {
736 _shttpd_send_server_error(c, 400, "Bad HTTP version");
737 } else if (c->major_version > 1 ||
738 (c->major_version == 1 && c->minor_version > 1)) {
739 _shttpd_send_server_error(c, 505, "HTTP version not supported");
740 } else if (uri_len <= 0) {
741 _shttpd_send_server_error(c, 400, "Bad URI");
742 } else if ((c->uri = malloc(uri_len + 1)) == NULL) {
743 _shttpd_send_server_error(c, 500, "Cannot allocate URI");
744 } else {
745 _shttpd_strlcpy(c->uri, (char *) start, uri_len + 1);
746 _shttpd_parse_headers(c->headers,
747 (c->request + req_len) - c->headers, &c->ch);
748
749 /* Remove the length of request from total, count only data */
750 assert(c->rem.io.total >= (big_int_t) req_len);
751 c->rem.io.total -= req_len;
752 c->rem.content_len = c->ch.cl.v_big_int;
753 decide_what_to_do(c);
754 }
755 }
756
757 static void
add_socket(struct worker * worker,int sock,int is_ssl)758 add_socket(struct worker *worker, int sock, int is_ssl)
759 {
760 struct shttpd_ctx *ctx = worker->ctx;
761 struct conn *c;
762 struct usa sa;
763 int l = IS_TRUE(ctx, OPT_INETD) ? E_FATAL : E_LOG;
764 #if !defined(NO_SSL)
765 SSL *ssl = NULL;
766 #else
767 is_ssl = is_ssl; /* supress warnings */
768 #endif /* NO_SSL */
769
770 sa.len = sizeof(sa.u.sin);
771 (void) _shttpd_set_non_blocking_mode(sock);
772
773 if (getpeername(sock, &sa.u.sa, &sa.len)) {
774 _shttpd_elog(l, NULL, "add_socket: %s", strerror(errno));
775 #if !defined(NO_SSL)
776 } else if (is_ssl && (ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
777 _shttpd_elog(l, NULL, "add_socket: SSL_new: %s", strerror(ERRNO));
778 (void) closesocket(sock);
779 } else if (is_ssl && SSL_set_fd(ssl, sock) == 0) {
780 _shttpd_elog(l, NULL, "add_socket: SSL_set_fd: %s", strerror(ERRNO));
781 (void) closesocket(sock);
782 SSL_free(ssl);
783 #endif /* NO_SSL */
784 } else if ((c = calloc(1, sizeof(*c) + 2 * URI_MAX)) == NULL) {
785 #if !defined(NO_SSL)
786 if (ssl)
787 SSL_free(ssl);
788 #endif /* NO_SSL */
789 (void) closesocket(sock);
790 _shttpd_elog(l, NULL, "add_socket: calloc: %s", strerror(ERRNO));
791 } else {
792 c->rem.conn = c->loc.conn = c;
793 c->ctx = ctx;
794 c->worker = worker;
795 c->sa = sa;
796 c->birth_time = _shttpd_current_time;
797 c->expire_time = _shttpd_current_time + EXPIRE_TIME;
798
799 (void) getsockname(sock, &sa.u.sa, &sa.len);
800 c->loc_port = sa.u.sin.sin_port;
801
802 _shttpd_set_close_on_exec(sock);
803
804 c->loc.io_class = NULL;
805
806 c->rem.io_class = &_shttpd_io_socket;
807 c->rem.chan.sock = sock;
808
809 /* Set IO buffers */
810 c->loc.io.buf = (char *) (c + 1);
811 c->rem.io.buf = c->loc.io.buf + URI_MAX;
812 c->loc.io.size = c->rem.io.size = URI_MAX;
813
814 #if !defined(NO_SSL)
815 if (is_ssl) {
816 c->rem.io_class = &_shttpd_io_ssl;
817 c->rem.chan.ssl.sock = sock;
818 c->rem.chan.ssl.ssl = ssl;
819 _shttpd_ssl_handshake(&c->rem);
820 }
821 #endif /* NO_SSL */
822
823 LL_TAIL(&worker->connections, &c->link);
824 worker->num_conns++;
825
826 DBG(("%s:%hu connected (socket %d)",
827 inet_ntoa(* (struct in_addr *) &sa.u.sin.sin_addr.s_addr),
828 ntohs(sa.u.sin.sin_port), sock));
829 }
830 }
831
832 static struct worker *
first_worker(struct shttpd_ctx * ctx)833 first_worker(struct shttpd_ctx *ctx)
834 {
835 return (LL_ENTRY(ctx->workers.next, struct worker, link));
836 }
837
838 static void
pass_socket(struct shttpd_ctx * ctx,int sock,int is_ssl)839 pass_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
840 {
841 struct llhead *lp;
842 struct worker *worker, *lazy;
843 int buf[3];
844
845 lazy = first_worker(ctx);
846
847 /* Find least busy worker */
848 LL_FOREACH(&ctx->workers, lp) {
849 worker = LL_ENTRY(lp, struct worker, link);
850 if (worker->num_conns < lazy->num_conns)
851 lazy = worker;
852 }
853
854 buf[0] = CTL_PASS_SOCKET;
855 buf[1] = sock;
856 buf[2] = is_ssl;
857
858 (void) send(lazy->ctl[1], (void *) buf, sizeof(buf), 0);
859 }
860
861 static int
set_ports(struct shttpd_ctx * ctx,const char * p)862 set_ports(struct shttpd_ctx *ctx, const char *p)
863 {
864 int sock, len, is_ssl, port;
865 struct listener *l;
866
867
868 free_list(&ctx->listeners, &listener_destructor);
869
870 FOR_EACH_WORD_IN_LIST(p, len) {
871
872 is_ssl = p[len - 1] == 's' ? 1 : 0;
873 port = atoi(p);
874
875 if ((sock = shttpd_open_listening_port(port)) == -1) {
876 _shttpd_elog(E_LOG, NULL, "cannot open port %d", port);
877 goto fail;
878 } else if (is_ssl && ctx->ssl_ctx == NULL) {
879 (void) closesocket(sock);
880 _shttpd_elog(E_LOG, NULL, "cannot add SSL socket, "
881 "please specify certificate file");
882 goto fail;
883 } else if ((l = calloc(1, sizeof(*l))) == NULL) {
884 (void) closesocket(sock);
885 _shttpd_elog(E_LOG, NULL, "cannot allocate listener");
886 goto fail;
887 } else {
888 l->is_ssl = is_ssl;
889 l->sock = sock;
890 l->ctx = ctx;
891 LL_TAIL(&ctx->listeners, &l->link);
892 DBG(("shttpd_listen: added socket %d", sock));
893 }
894 }
895
896 return (TRUE);
897 fail:
898 free_list(&ctx->listeners, &listener_destructor);
899 return (FALSE);
900 }
901
902 static void
read_stream(struct stream * stream)903 read_stream(struct stream *stream)
904 {
905 int n, len;
906
907 len = io_space_len(&stream->io);
908 assert(len > 0);
909
910 /* Do not read more that needed */
911 if (stream->content_len > 0 &&
912 stream->io.total + len > stream->content_len)
913 len = stream->content_len - stream->io.total;
914
915 /* Read from underlying channel */
916 assert(stream->io_class != NULL);
917 n = stream->io_class->read(stream, io_space(&stream->io), len);
918
919 if (n > 0)
920 io_inc_head(&stream->io, n);
921 else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
922 n = n; /* Ignore EINTR and EAGAIN */
923 else if (!(stream->flags & FLAG_DONT_CLOSE))
924 _shttpd_stop_stream(stream);
925
926 DBG(("read_stream (%d %s): read %d/%d/%lu bytes (errno %d)",
927 stream->conn->rem.chan.sock,
928 stream->io_class ? stream->io_class->name : "(null)",
929 n, len, (unsigned long) stream->io.total, ERRNO));
930
931 /*
932 * Close the local stream if everything was read
933 * XXX We do not close the remote stream though! It may be
934 * a POST data completed transfer, we do not want the socket
935 * to be closed.
936 */
937 if (stream->content_len > 0 && stream == &stream->conn->loc) {
938 assert(stream->io.total <= stream->content_len);
939 if (stream->io.total == stream->content_len)
940 _shttpd_stop_stream(stream);
941 }
942
943 stream->conn->expire_time = _shttpd_current_time + EXPIRE_TIME;
944 }
945
946 static void
write_stream(struct stream * from,struct stream * to)947 write_stream(struct stream *from, struct stream *to)
948 {
949 int n, len;
950
951 len = io_data_len(&from->io);
952 assert(len > 0);
953
954 /* TODO: should be assert on CAN_WRITE flag */
955 n = to->io_class->write(to, io_data(&from->io), len);
956 to->conn->expire_time = _shttpd_current_time + EXPIRE_TIME;
957 DBG(("write_stream (%d %s): written %d/%d bytes (errno %d)",
958 to->conn->rem.chan.sock,
959 to->io_class ? to->io_class->name : "(null)", n, len, ERRNO));
960
961 if (n > 0)
962 io_inc_tail(&from->io, n);
963 else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
964 n = n; /* Ignore EINTR and EAGAIN */
965 else if (!(to->flags & FLAG_DONT_CLOSE))
966 _shttpd_stop_stream(to);
967 }
968
969
970 static void
connection_desctructor(struct llhead * lp)971 connection_desctructor(struct llhead *lp)
972 {
973 struct conn *c = LL_ENTRY(lp, struct conn, link);
974 static const struct vec vec = {"close", 5};
975 int do_close;
976
977 DBG(("Disconnecting %d (%.*s)", c->rem.chan.sock,
978 c->ch.connection.v_vec.len, c->ch.connection.v_vec.ptr));
979
980 if (c->request != NULL && c->ctx->access_log != NULL)
981 _shttpd_log_access(c->ctx->access_log, c);
982
983 /* In inetd mode, exit if request is finished. */
984 if (IS_TRUE(c->ctx, OPT_INETD))
985 exit(0);
986
987 if (c->loc.io_class != NULL && c->loc.io_class->close != NULL)
988 c->loc.io_class->close(&c->loc);
989
990 /*
991 * Check the "Connection: " header before we free c->request
992 * If it its 'keep-alive', then do not close the connection
993 */
994 do_close = (c->ch.connection.v_vec.len >= vec.len &&
995 !_shttpd_strncasecmp(vec.ptr,c->ch.connection.v_vec.ptr,vec.len)) ||
996 (c->major_version < 1 ||
997 (c->major_version >= 1 && c->minor_version < 1));
998
999 if (c->request)
1000 free(c->request);
1001 if (c->uri)
1002 free(c->uri);
1003
1004 /* Keep the connection open only if we have Content-Length set */
1005 if (!do_close && c->loc.content_len > 0) {
1006 c->loc.io_class = NULL;
1007 c->loc.flags = 0;
1008 c->loc.content_len = 0;
1009 c->rem.flags = FLAG_W | FLAG_R | FLAG_SSL_ACCEPTED;
1010 c->query = c->request = c->uri = c->path_info = NULL;
1011 c->mime_type.len = 0;
1012 (void) memset(&c->ch, 0, sizeof(c->ch));
1013 io_clear(&c->loc.io);
1014 c->birth_time = _shttpd_current_time;
1015 if (io_data_len(&c->rem.io) > 0)
1016 process_connection(c, 0, 0);
1017 } else {
1018 if (c->rem.io_class != NULL)
1019 c->rem.io_class->close(&c->rem);
1020
1021 LL_DEL(&c->link);
1022 c->worker->num_conns--;
1023 assert(c->worker->num_conns >= 0);
1024
1025 free(c);
1026 }
1027 }
1028
1029 static void
worker_destructor(struct llhead * lp)1030 worker_destructor(struct llhead *lp)
1031 {
1032 struct worker *worker = LL_ENTRY(lp, struct worker, link);
1033
1034 free_list(&worker->connections, connection_desctructor);
1035 free(worker);
1036 }
1037
1038 static int
is_allowed(const struct shttpd_ctx * ctx,const struct usa * usa)1039 is_allowed(const struct shttpd_ctx *ctx, const struct usa *usa)
1040 {
1041 const struct acl *acl;
1042 const struct llhead *lp;
1043 int allowed = '+';
1044 uint32_t ip;
1045
1046 LL_FOREACH(&ctx->acl, lp) {
1047 acl = LL_ENTRY(lp, struct acl, link);
1048 (void) memcpy(&ip, &usa->u.sin.sin_addr, sizeof(ip));
1049 if (acl->ip == (ntohl(ip) & acl->mask))
1050 allowed = acl->flag;
1051 }
1052
1053 return (allowed == '+');
1054 }
1055
1056 static void
add_to_set(int fd,fd_set * set,int * max_fd)1057 add_to_set(int fd, fd_set *set, int *max_fd)
1058 {
1059 FD_SET(fd, set);
1060 if (fd > *max_fd)
1061 *max_fd = fd;
1062 }
1063
1064 static void
process_connection(struct conn * c,int remote_ready,int local_ready)1065 process_connection(struct conn *c, int remote_ready, int local_ready)
1066 {
1067 /* Read from remote end if it is ready */
1068 if (remote_ready && io_space_len(&c->rem.io))
1069 read_stream(&c->rem);
1070
1071 /* If the request is not parsed yet, do so */
1072 if (!(c->rem.flags & FLAG_HEADERS_PARSED))
1073 parse_http_request(c);
1074
1075 DBG(("loc: %d [%.*s]", (int) io_data_len(&c->loc.io),
1076 (int) io_data_len(&c->loc.io), io_data(&c->loc.io)));
1077 DBG(("rem: %d [%.*s]", (int) io_data_len(&c->rem.io),
1078 (int) io_data_len(&c->rem.io), io_data(&c->rem.io)));
1079
1080 /* Read from the local end if it is ready */
1081 if (local_ready && io_space_len(&c->loc.io))
1082 read_stream(&c->loc);
1083
1084 if (io_data_len(&c->rem.io) > 0 && (c->loc.flags & FLAG_W) &&
1085 c->loc.io_class != NULL && c->loc.io_class->write != NULL)
1086 write_stream(&c->rem, &c->loc);
1087
1088 if (io_data_len(&c->loc.io) > 0 && c->rem.io_class != NULL)
1089 write_stream(&c->loc, &c->rem);
1090
1091 /* Check whether we should close this connection */
1092 if ((_shttpd_current_time > c->expire_time) ||
1093 (c->rem.flags & FLAG_CLOSED) ||
1094 ((c->loc.flags & FLAG_CLOSED) && !io_data_len(&c->loc.io)))
1095 connection_desctructor(&c->link);
1096 }
1097
1098 static int
num_workers(const struct shttpd_ctx * ctx)1099 num_workers(const struct shttpd_ctx *ctx)
1100 {
1101 char *p = ctx->options[OPT_THREADS];
1102 return (p ? atoi(p) : 1);
1103 }
1104
1105 static void
handle_connected_socket(struct shttpd_ctx * ctx,struct usa * sap,int sock,int is_ssl)1106 handle_connected_socket(struct shttpd_ctx *ctx,
1107 struct usa *sap, int sock, int is_ssl)
1108 {
1109 #if !defined(_WIN32)
1110 if (sock >= (int) FD_SETSIZE) {
1111 _shttpd_elog(E_LOG, NULL, "ctx %p: discarding "
1112 "socket %d, too busy", ctx, sock);
1113 (void) closesocket(sock);
1114 } else
1115 #endif /* !_WIN32 */
1116 if (!is_allowed(ctx, sap)) {
1117 _shttpd_elog(E_LOG, NULL, "%s is not allowed to connect",
1118 inet_ntoa(sap->u.sin.sin_addr));
1119 (void) closesocket(sock);
1120 } else if (num_workers(ctx) > 1) {
1121 pass_socket(ctx, sock, is_ssl);
1122 } else {
1123 add_socket(first_worker(ctx), sock, is_ssl);
1124 }
1125 }
1126
1127 static int
do_select(int max_fd,fd_set * read_set,fd_set * write_set,int milliseconds)1128 do_select(int max_fd, fd_set *read_set, fd_set *write_set, int milliseconds)
1129 {
1130 struct timeval tv;
1131 int n;
1132
1133 tv.tv_sec = milliseconds / 1000;
1134 tv.tv_usec = (milliseconds % 1000) * 1000;
1135
1136 /* Check IO readiness */
1137 if ((n = select(max_fd + 1, read_set, write_set, NULL, &tv)) < 0) {
1138 #ifdef _WIN32
1139 /*
1140 * On windows, if read_set and write_set are empty,
1141 * select() returns "Invalid parameter" error
1142 * (at least on my Windows XP Pro). So in this case,
1143 * we sleep here.
1144 */
1145 Sleep(milliseconds);
1146 #endif /* _WIN32 */
1147 DBG(("select: %d", ERRNO));
1148 }
1149
1150 return (n);
1151 }
1152
1153 static int
multiplex_worker_sockets(const struct worker * worker,int * max_fd,fd_set * read_set,fd_set * write_set)1154 multiplex_worker_sockets(const struct worker *worker, int *max_fd,
1155 fd_set *read_set, fd_set *write_set)
1156 {
1157 struct llhead *lp;
1158 struct conn *c;
1159 int nowait = FALSE;
1160
1161 /* Add control socket */
1162 add_to_set(worker->ctl[0], read_set, max_fd);
1163
1164 /* Multiplex streams */
1165 LL_FOREACH(&worker->connections, lp) {
1166 c = LL_ENTRY(lp, struct conn, link);
1167
1168 /* If there is a space in remote IO, check remote socket */
1169 if (io_space_len(&c->rem.io))
1170 add_to_set(c->rem.chan.fd, read_set, max_fd);
1171
1172 #if !defined(NO_CGI)
1173 /*
1174 * If there is a space in local IO, and local endpoint is
1175 * CGI, check local socket for read availability
1176 */
1177 if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
1178 c->loc.io_class == &_shttpd_io_cgi)
1179 add_to_set(c->loc.chan.fd, read_set, max_fd);
1180
1181 /*
1182 * If there is some data read from remote socket, and
1183 * local endpoint is CGI, check local for write availability
1184 */
1185 if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
1186 c->loc.io_class == &_shttpd_io_cgi)
1187 add_to_set(c->loc.chan.fd, write_set, max_fd);
1188 #endif /* NO_CGI */
1189
1190 /*
1191 * If there is some data read from local endpoint, check the
1192 * remote socket for write availability
1193 */
1194 if (io_data_len(&c->loc.io) && !(c->loc.flags & FLAG_SUSPEND))
1195 add_to_set(c->rem.chan.fd, write_set, max_fd);
1196
1197 /*
1198 * Set select wait interval to zero if FLAG_ALWAYS_READY set
1199 */
1200 if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
1201 (c->loc.flags & FLAG_ALWAYS_READY))
1202 nowait = TRUE;
1203
1204 if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
1205 (c->loc.flags & FLAG_ALWAYS_READY))
1206 nowait = TRUE;
1207 }
1208
1209 return (nowait);
1210 }
1211
1212 int
shttpd_join(struct shttpd_ctx * ctx,fd_set * read_set,fd_set * write_set,int * max_fd)1213 shttpd_join(struct shttpd_ctx *ctx,
1214 fd_set *read_set, fd_set *write_set, int *max_fd)
1215 {
1216 struct llhead *lp;
1217 struct listener *l;
1218 int nowait = FALSE;
1219
1220 /* Add listening sockets to the read set */
1221 LL_FOREACH(&ctx->listeners, lp) {
1222 l = LL_ENTRY(lp, struct listener, link);
1223 add_to_set(l->sock, read_set, max_fd);
1224 DBG(("FD_SET(%d) (listening)", l->sock));
1225 }
1226
1227 if (num_workers(ctx) == 1)
1228 nowait = multiplex_worker_sockets(first_worker(ctx), max_fd,
1229 read_set, write_set);
1230
1231 return (nowait);
1232 }
1233
1234
1235 static void
process_worker_sockets(struct worker * worker,fd_set * read_set)1236 process_worker_sockets(struct worker *worker, fd_set *read_set)
1237 {
1238 struct llhead *lp, *tmp;
1239 int cmd, skt[2], sock = worker->ctl[0];
1240 struct conn *c;
1241
1242 /* Check if new socket is passed to us over the control socket */
1243 if (FD_ISSET(worker->ctl[0], read_set))
1244 while (recv(sock, (void *) &cmd, sizeof(cmd), 0) == sizeof(cmd))
1245 switch (cmd) {
1246 case CTL_PASS_SOCKET:
1247 (void)recv(sock, (void *) &skt, sizeof(skt), 0);
1248 add_socket(worker, skt[0], skt[1]);
1249 break;
1250 case CTL_WAKEUP:
1251 (void)recv(sock, (void *) &c, sizeof(c), 0);
1252 c->loc.flags &= FLAG_SUSPEND;
1253 break;
1254 default:
1255 _shttpd_elog(E_FATAL, NULL, "ctx %p: ctl cmd %d",
1256 worker->ctx, cmd);
1257 break;
1258 }
1259
1260 /* Process all connections */
1261 LL_FOREACH_SAFE(&worker->connections, lp, tmp) {
1262 c = LL_ENTRY(lp, struct conn, link);
1263 process_connection(c, FD_ISSET(c->rem.chan.sock, read_set),
1264 c->loc.io_class != NULL &&
1265 ((c->loc.flags & FLAG_ALWAYS_READY)
1266 #if !defined(NO_CGI)
1267 || (c->loc.io_class == &_shttpd_io_cgi &&
1268 FD_ISSET(c->loc.chan.fd, read_set))
1269 #endif /* NO_CGI */
1270 ));
1271 }
1272 }
1273
1274 /*
1275 * One iteration of server loop. This is the core of the data exchange.
1276 */
1277 void
shttpd_poll(struct shttpd_ctx * ctx,int milliseconds)1278 shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
1279 {
1280 struct llhead *lp;
1281 struct listener *l;
1282 fd_set read_set, write_set;
1283 int sock, max_fd = -1;
1284 struct usa sa;
1285
1286 _shttpd_current_time = time(0);
1287 FD_ZERO(&read_set);
1288 FD_ZERO(&write_set);
1289
1290 if (shttpd_join(ctx, &read_set, &write_set, &max_fd))
1291 milliseconds = 0;
1292
1293 if (do_select(max_fd, &read_set, &write_set, milliseconds) < 0)
1294 return;;
1295
1296 /* Check for incoming connections on listener sockets */
1297 LL_FOREACH(&ctx->listeners, lp) {
1298 l = LL_ENTRY(lp, struct listener, link);
1299 if (!FD_ISSET(l->sock, &read_set))
1300 continue;
1301 do {
1302 sa.len = sizeof(sa.u.sin);
1303 if ((sock = accept(l->sock, &sa.u.sa, &sa.len)) != -1)
1304 handle_connected_socket(ctx,&sa,sock,l->is_ssl);
1305 } while (sock != -1);
1306 }
1307
1308 if (num_workers(ctx) == 1)
1309 process_worker_sockets(first_worker(ctx), &read_set);
1310 }
1311
1312 /*
1313 * Deallocate shttpd object, free up the resources
1314 */
1315 void
shttpd_fini(struct shttpd_ctx * ctx)1316 shttpd_fini(struct shttpd_ctx *ctx)
1317 {
1318 size_t i;
1319
1320 free_list(&ctx->workers, worker_destructor);
1321 free_list(&ctx->registered_uris, registered_uri_destructor);
1322 free_list(&ctx->acl, acl_destructor);
1323 free_list(&ctx->listeners, listener_destructor);
1324 #if !defined(NO_SSI)
1325 free_list(&ctx->ssi_funcs, _shttpd_ssi_func_destructor);
1326 #endif /* !NO_SSI */
1327
1328 for (i = 0; i < NELEMS(ctx->options); i++)
1329 if (ctx->options[i] != NULL)
1330 free(ctx->options[i]);
1331
1332 if (ctx->access_log) (void) fclose(ctx->access_log);
1333 if (ctx->error_log) (void) fclose(ctx->error_log);
1334
1335 /* TODO: free SSL context */
1336
1337 free(ctx);
1338 }
1339
1340 /*
1341 * UNIX socketpair() implementation. Why? Because Windows does not have it.
1342 * Return 0 on success, -1 on error.
1343 */
1344 int
shttpd_socketpair(int sp[2])1345 shttpd_socketpair(int sp[2])
1346 {
1347 struct sockaddr_in sa;
1348 int sock, ret = -1;
1349 socklen_t len = sizeof(sa);
1350
1351 sp[0] = sp[1] = -1;
1352
1353 (void) memset(&sa, 0, sizeof(sa));
1354 sa.sin_family = AF_INET;
1355 sa.sin_port = htons(0);
1356 sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1357
1358 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) != -1 &&
1359 !bind(sock, (struct sockaddr *) &sa, len) &&
1360 !listen(sock, 1) &&
1361 !getsockname(sock, (struct sockaddr *) &sa, &len) &&
1362 (sp[0] = socket(AF_INET, SOCK_STREAM, 6)) != -1 &&
1363 !connect(sp[0], (struct sockaddr *) &sa, len) &&
1364 (sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) != -1) {
1365
1366 /* Success */
1367 ret = 0;
1368 } else {
1369
1370 /* Failure, close descriptors */
1371 if (sp[0] != -1)
1372 (void) closesocket(sp[0]);
1373 if (sp[1] != -1)
1374 (void) closesocket(sp[1]);
1375 }
1376
1377 (void) closesocket(sock);
1378 (void) _shttpd_set_non_blocking_mode(sp[0]);
1379 (void) _shttpd_set_non_blocking_mode(sp[1]);
1380
1381 #ifndef _WIN32
1382 (void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
1383 (void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
1384 #endif /* _WIN32*/
1385
1386 return (ret);
1387 }
1388
isbyte(int n)1389 static int isbyte(int n) { return (n >= 0 && n <= 255); }
1390
1391 static int
set_inetd(struct shttpd_ctx * ctx,const char * flag)1392 set_inetd(struct shttpd_ctx *ctx, const char *flag)
1393 {
1394 ctx = NULL; /* Unused */
1395
1396 if (_shttpd_is_true(flag)) {
1397 shttpd_set_option(ctx, "ports", NULL);
1398 (void) freopen("/dev/null", "a", stderr);
1399 add_socket(first_worker(ctx), 0, 0);
1400 }
1401
1402 return (TRUE);
1403 }
1404
1405 static int
set_uid(struct shttpd_ctx * ctx,const char * uid)1406 set_uid(struct shttpd_ctx *ctx, const char *uid)
1407 {
1408 struct passwd *pw;
1409
1410 ctx = NULL; /* Unused */
1411
1412 #if !defined(_WIN32)
1413 if ((pw = getpwnam(uid)) == NULL)
1414 _shttpd_elog(E_FATAL, 0, "%s: unknown user [%s]", __func__, uid);
1415 else if (setgid(pw->pw_gid) == -1)
1416 _shttpd_elog(E_FATAL, NULL, "%s: setgid(%s): %s",
1417 __func__, uid, strerror(errno));
1418 else if (setuid(pw->pw_uid) == -1)
1419 _shttpd_elog(E_FATAL, NULL, "%s: setuid(%s): %s",
1420 __func__, uid, strerror(errno));
1421 #endif /* !_WIN32 */
1422 return (TRUE);
1423 }
1424
1425 static int
set_acl(struct shttpd_ctx * ctx,const char * s)1426 set_acl(struct shttpd_ctx *ctx, const char *s)
1427 {
1428 struct acl *acl = NULL;
1429 char flag;
1430 int len, a, b, c, d, n, mask;
1431
1432 /* Delete the old ACLs if any */
1433 free_list(&ctx->acl, acl_destructor);
1434
1435 FOR_EACH_WORD_IN_LIST(s, len) {
1436
1437 mask = 32;
1438
1439 if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) {
1440 _shttpd_elog(E_FATAL, NULL, "[%s]: subnet must be "
1441 "[+|-]x.x.x.x[/x]", s);
1442 } else if (flag != '+' && flag != '-') {
1443 _shttpd_elog(E_FATAL, NULL, "flag must be + or -: [%s]", s);
1444 } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
1445 _shttpd_elog(E_FATAL, NULL, "bad ip address: [%s]", s);
1446 } else if ((acl = malloc(sizeof(*acl))) == NULL) {
1447 _shttpd_elog(E_FATAL, NULL, "%s", "cannot malloc subnet");
1448 } else if (sscanf(s + n, "/%d", &mask) == 0) {
1449 /* Do nothing, no mask specified */
1450 } else if (mask < 0 || mask > 32) {
1451 _shttpd_elog(E_FATAL, NULL, "bad subnet mask: %d [%s]", n, s);
1452 }
1453
1454 acl->ip = (a << 24) | (b << 16) | (c << 8) | d;
1455 acl->mask = mask ? 0xffffffffU << (32 - mask) : 0;
1456 acl->flag = flag;
1457 LL_TAIL(&ctx->acl, &acl->link);
1458 }
1459
1460 return (TRUE);
1461 }
1462
1463 #ifndef NO_SSL
1464 /*
1465 * Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
1466 */
1467 static int
set_ssl(struct shttpd_ctx * ctx,const char * pem)1468 set_ssl(struct shttpd_ctx *ctx, const char *pem)
1469 {
1470 SSL_CTX *CTX;
1471 void *lib;
1472 struct ssl_func *fp;
1473 int retval = FALSE;
1474
1475 /* Load SSL library dynamically */
1476 if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL) {
1477 _shttpd_elog(E_LOG, NULL, "set_ssl: cannot load %s", SSL_LIB);
1478 return (FALSE);
1479 }
1480
1481 for (fp = ssl_sw; fp->name != NULL; fp++)
1482 if ((fp->ptr.v_void = dlsym(lib, fp->name)) == NULL) {
1483 _shttpd_elog(E_LOG, NULL,"set_ssl: cannot find %s", fp->name);
1484 return (FALSE);
1485 }
1486
1487 /* Initialize SSL crap */
1488 SSL_library_init();
1489
1490 if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)
1491 _shttpd_elog(E_LOG, NULL, "SSL_CTX_new error");
1492 else if (SSL_CTX_use_certificate_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
1493 _shttpd_elog(E_LOG, NULL, "cannot open %s", pem);
1494 else if (SSL_CTX_use_PrivateKey_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
1495 _shttpd_elog(E_LOG, NULL, "cannot open %s", pem);
1496 else
1497 retval = TRUE;
1498
1499 ctx->ssl_ctx = CTX;
1500
1501 return (retval);
1502 }
1503 #endif /* NO_SSL */
1504
1505 static int
open_log_file(FILE ** fpp,const char * path)1506 open_log_file(FILE **fpp, const char *path)
1507 {
1508 int retval = TRUE;
1509
1510 if (*fpp != NULL)
1511 (void) fclose(*fpp);
1512
1513 if (path == NULL) {
1514 *fpp = NULL;
1515 } else if ((*fpp = fopen(path, "a")) == NULL) {
1516 _shttpd_elog(E_LOG, NULL, "cannot open log file %s: %s",
1517 path, strerror(errno));
1518 retval = FALSE;
1519 }
1520
1521 return (retval);
1522 }
1523
set_alog(struct shttpd_ctx * ctx,const char * path)1524 static int set_alog(struct shttpd_ctx *ctx, const char *path) {
1525 return (open_log_file(&ctx->access_log, path));
1526 }
1527
set_elog(struct shttpd_ctx * ctx,const char * path)1528 static int set_elog(struct shttpd_ctx *ctx, const char *path) {
1529 return (open_log_file(&ctx->error_log, path));
1530 }
1531
1532 static void show_cfg_page(struct shttpd_arg *arg);
1533
1534 static int
set_cfg_uri(struct shttpd_ctx * ctx,const char * uri)1535 set_cfg_uri(struct shttpd_ctx *ctx, const char *uri)
1536 {
1537 free_list(&ctx->registered_uris, ®istered_uri_destructor);
1538
1539 if (uri != NULL)
1540 shttpd_register_uri(ctx, uri, &show_cfg_page, ctx);
1541
1542 return (TRUE);
1543 }
1544
1545 static struct worker *
add_worker(struct shttpd_ctx * ctx)1546 add_worker(struct shttpd_ctx *ctx)
1547 {
1548 struct worker *worker;
1549
1550 if ((worker = calloc(1, sizeof(*worker))) == NULL)
1551 _shttpd_elog(E_FATAL, NULL, "Cannot allocate worker");
1552 LL_INIT(&worker->connections);
1553 worker->ctx = ctx;
1554 (void) shttpd_socketpair(worker->ctl);
1555 LL_TAIL(&ctx->workers, &worker->link);
1556
1557 return (worker);
1558 }
1559
1560 #if !defined(NO_THREADS)
1561 static void
poll_worker(struct worker * worker,int milliseconds)1562 poll_worker(struct worker *worker, int milliseconds)
1563 {
1564 fd_set read_set, write_set;
1565 int max_fd = -1;
1566
1567 FD_ZERO(&read_set);
1568 FD_ZERO(&write_set);
1569
1570 if (multiplex_worker_sockets(worker, &max_fd, &read_set, &write_set))
1571 milliseconds = 0;
1572
1573 if (do_select(max_fd, &read_set, &write_set, milliseconds) < 0)
1574 return;;
1575
1576 process_worker_sockets(worker, &read_set);
1577 }
1578
1579 static void
worker_function(void * param)1580 worker_function(void *param)
1581 {
1582 struct worker *worker = param;
1583
1584 while (worker->exit_flag == 0)
1585 poll_worker(worker, 1000 * 10);
1586
1587 free_list(&worker->connections, connection_desctructor);
1588 free(worker);
1589 }
1590
1591 static int
set_workers(struct shttpd_ctx * ctx,const char * value)1592 set_workers(struct shttpd_ctx *ctx, const char *value)
1593 {
1594 int new_num, old_num;
1595 struct llhead *lp, *tmp;
1596 struct worker *worker;
1597
1598 new_num = atoi(value);
1599 old_num = 0;
1600 LL_FOREACH(&ctx->workers, lp)
1601 old_num++;
1602
1603 if (new_num == 1) {
1604 if (old_num > 1)
1605 /* Stop old threads */
1606 LL_FOREACH_SAFE(&ctx->workers, lp, tmp) {
1607 worker = LL_ENTRY(lp, struct worker, link);
1608 LL_DEL(&worker->link);
1609 worker = LL_ENTRY(lp, struct worker, link);
1610 worker->exit_flag = 1;
1611 }
1612 (void) add_worker(ctx);
1613 } else {
1614 /* FIXME: we cannot here reduce the number of threads */
1615 while (new_num > 1 && new_num > old_num) {
1616 worker = add_worker(ctx);
1617 _beginthread(worker_function, 0, worker);
1618 old_num++;
1619 }
1620 }
1621
1622 return (TRUE);
1623 }
1624 #endif /* NO_THREADS */
1625
1626 static const struct opt {
1627 int index; /* Index in shttpd_ctx */
1628 const char *name; /* Option name in config file */
1629 const char *description; /* Description */
1630 const char *default_value; /* Default option value */
1631 int (*setter)(struct shttpd_ctx *, const char *);
1632 } known_options[] = {
1633 {OPT_ROOT, "root", "\tWeb root directory", ".", NULL},
1634 {OPT_INDEX_FILES, "index_files", "Index files", INDEX_FILES, NULL},
1635 #ifndef NO_SSL
1636 {OPT_SSL_CERTIFICATE, "ssl_cert", "SSL certificate file", NULL,set_ssl},
1637 #endif /* NO_SSL */
1638 {OPT_PORTS, "ports", "Listening ports", LISTENING_PORTS, set_ports},
1639 {OPT_DIR_LIST, "dir_list", "Directory listing", "yes", NULL},
1640 {OPT_CFG_URI, "cfg_uri", "Config uri", NULL, set_cfg_uri},
1641 {OPT_PROTECT, "protect", "URI to htpasswd mapping", NULL, NULL},
1642 #ifndef NO_CGI
1643 {OPT_CGI_EXTENSIONS, "cgi_ext", "CGI extensions", CGI_EXT, NULL},
1644 {OPT_CGI_INTERPRETER, "cgi_interp", "CGI interpreter", NULL, NULL},
1645 {OPT_CGI_ENVIRONMENT, "cgi_env", "Additional CGI env vars", NULL, NULL},
1646 #endif /* NO_CGI */
1647 {OPT_SSI_EXTENSIONS, "ssi_ext", "SSI extensions", SSI_EXT, NULL},
1648 #ifndef NO_AUTH
1649 {OPT_AUTH_REALM, "auth_realm", "Authentication domain name",REALM,NULL},
1650 {OPT_AUTH_GPASSWD, "auth_gpass", "Global passwords file", NULL, NULL},
1651 {OPT_AUTH_PUT, "auth_PUT", "PUT,DELETE auth file", NULL, NULL},
1652 #endif /* !NO_AUTH */
1653 #ifdef _WIN32
1654 {OPT_SERVICE, "service", "Manage WinNNT service (install"
1655 "|uninstall)", NULL, _shttpd_set_nt_service},
1656 {OPT_HIDE, "systray", "Hide console, show icon on systray",
1657 "no", _shttpd_set_systray},
1658 #else
1659 {OPT_INETD, "inetd", "Inetd mode", "no", set_inetd},
1660 {OPT_UID, "uid", "\tRun as user", NULL, set_uid},
1661 #endif /* _WIN32 */
1662 {OPT_ACCESS_LOG, "access_log", "Access log file", NULL, set_alog},
1663 {OPT_ERROR_LOG, "error_log", "Error log file", NULL, set_elog},
1664 {OPT_MIME_TYPES, "mime_types", "Additional mime types list", NULL,NULL},
1665 {OPT_ALIASES, "aliases", "Path=URI mappings", NULL, NULL},
1666 {OPT_ACL, "acl", "\tAllow/deny IP addresses/subnets", NULL, set_acl},
1667 #if !defined(NO_THREADS)
1668 {OPT_THREADS, "threads", "Number of worker threads", "1", set_workers},
1669 #endif /* !NO_THREADS */
1670 {-1, NULL, NULL, NULL, NULL}
1671 };
1672
1673 static const struct opt *
find_opt(const char * opt_name)1674 find_opt(const char *opt_name)
1675 {
1676 int i;
1677
1678 for (i = 0; known_options[i].name != NULL; i++)
1679 if (!strcmp(opt_name, known_options[i].name))
1680 return (known_options + i);
1681
1682 _shttpd_elog(E_FATAL, NULL, "no such option: [%s]", opt_name);
1683
1684 /* UNREACHABLE */
1685 return (NULL);
1686 }
1687
1688 int
shttpd_set_option(struct shttpd_ctx * ctx,const char * opt,const char * val)1689 shttpd_set_option(struct shttpd_ctx *ctx, const char *opt, const char *val)
1690 {
1691 const struct opt *o = find_opt(opt);
1692 int retval = TRUE;
1693
1694 /* Call option setter first, so it can use both new and old values */
1695 if (o->setter != NULL)
1696 retval = o->setter(ctx, val);
1697
1698 /* Free old value if any */
1699 if (ctx->options[o->index] != NULL)
1700 free(ctx->options[o->index]);
1701
1702 /* Set new option value */
1703 ctx->options[o->index] = val ? _shttpd_strdup(val) : NULL;
1704
1705 return (retval);
1706 }
1707
1708 static void
show_cfg_page(struct shttpd_arg * arg)1709 show_cfg_page(struct shttpd_arg *arg)
1710 {
1711 struct shttpd_ctx *ctx = arg->user_data;
1712 char opt_name[20], value[BUFSIZ];
1713 const struct opt *o;
1714
1715 opt_name[0] = value[0] = '\0';
1716
1717 if (!strcmp(shttpd_get_env(arg, "REQUEST_METHOD"), "POST")) {
1718 if (arg->flags & SHTTPD_MORE_POST_DATA)
1719 return;
1720 (void) shttpd_get_var("o", arg->in.buf, arg->in.len,
1721 opt_name, sizeof(opt_name));
1722 (void) shttpd_get_var("v", arg->in.buf, arg->in.len,
1723 value, sizeof(value));
1724 shttpd_set_option(ctx, opt_name, value[0] ? value : NULL);
1725 }
1726
1727 shttpd_printf(arg, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
1728 "<html><body><h1>SHTTPD v. %s</h1>", shttpd_version());
1729
1730 shttpd_printf(arg, "%s", "<table border=1"
1731 "<tr><th>Option</th><th>Description</th>"
1732 "<th colspan=2>Value</th></tr>");
1733
1734 if (opt_name[0] != '\0' && value[0] != '\0')
1735 shttpd_printf(arg, "<p style='color: green'>Saved: %s=%s</p>",
1736 opt_name, value[0] ? value : "NULL");
1737
1738
1739 for (o = known_options; o->name != NULL; o++) {
1740 shttpd_printf(arg,
1741 "<form method=post><tr><td>%s</td><td>%s</td>"
1742 "<input type=hidden name=o value='%s'>"
1743 "<td><input type=text name=v value='%s'></td>"
1744 "<td><input type=submit value=save></td></form></tr>",
1745 o->name, o->description, o->name,
1746 ctx->options[o->index] ? ctx->options[o->index] : "");
1747 }
1748
1749 shttpd_printf(arg, "%s", "</table></body></html>");
1750 arg->flags |= SHTTPD_END_OF_OUTPUT;
1751 }
1752
1753 /*
1754 * Show usage string and exit.
1755 */
1756 void
_shttpd_usage(const char * prog)1757 _shttpd_usage(const char *prog)
1758 {
1759 const struct opt *o;
1760
1761 (void) fprintf(stderr,
1762 "SHTTPD version %s (c) Sergey Lyubka\n"
1763 "usage: %s [options] [config_file]\n", VERSION, prog);
1764
1765 #if !defined(NO_AUTH)
1766 fprintf(stderr, " -A <htpasswd_file> <realm> <user> <passwd>\n");
1767 #endif /* NO_AUTH */
1768
1769 for (o = known_options; o->name != NULL; o++) {
1770 (void) fprintf(stderr, " -%s\t%s", o->name, o->description);
1771 if (o->default_value != NULL)
1772 fprintf(stderr, " (default: %s)", o->default_value);
1773 fputc('\n', stderr);
1774 }
1775
1776 exit(EXIT_FAILURE);
1777 }
1778
1779 static void
set_opt(struct shttpd_ctx * ctx,const char * opt,const char * value)1780 set_opt(struct shttpd_ctx *ctx, const char *opt, const char *value)
1781 {
1782 const struct opt *o;
1783
1784 o = find_opt(opt);
1785 if (ctx->options[o->index] != NULL)
1786 free(ctx->options[o->index]);
1787 ctx->options[o->index] = _shttpd_strdup(value);
1788 }
1789
1790 static void
process_command_line_arguments(struct shttpd_ctx * ctx,char * argv[])1791 process_command_line_arguments(struct shttpd_ctx *ctx, char *argv[])
1792 {
1793 const char *config_file = CONFIG_FILE;
1794 char line[BUFSIZ], opt[BUFSIZ],
1795 val[BUFSIZ], path[FILENAME_MAX], *p;
1796 FILE *fp;
1797 size_t i, line_no = 0;
1798
1799 /* First find out, which config file to open */
1800 for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
1801 if (argv[i + 1] == NULL)
1802 _shttpd_usage(argv[0]);
1803
1804 if (argv[i] != NULL && argv[i + 1] != NULL) {
1805 /* More than one non-option arguments are given w*/
1806 _shttpd_usage(argv[0]);
1807 } else if (argv[i] != NULL) {
1808 /* Just one non-option argument is given, this is config file */
1809 config_file = argv[i];
1810 } else {
1811 /* No config file specified. Look for one where shttpd lives */
1812 if ((p = strrchr(argv[0], DIRSEP)) != 0) {
1813 _shttpd_snprintf(path, sizeof(path), "%.*s%s",
1814 p - argv[0] + 1, argv[0], config_file);
1815 config_file = path;
1816 }
1817 }
1818
1819 fp = fopen(config_file, "r");
1820
1821 /* If config file was set in command line and open failed, exit */
1822 if (fp == NULL && argv[i] != NULL)
1823 _shttpd_elog(E_FATAL, NULL, "cannot open config file %s: %s",
1824 config_file, strerror(errno));
1825
1826 if (fp != NULL) {
1827
1828 _shttpd_elog(E_LOG, NULL, "Loading config file %s", config_file);
1829
1830 /* Loop over the lines in config file */
1831 while (fgets(line, sizeof(line), fp) != NULL) {
1832
1833 line_no++;
1834
1835 /* Ignore empty lines and comments */
1836 if (line[0] == '#' || line[0] == '\n')
1837 continue;
1838
1839 if (sscanf(line, "%s %[^\n#]", opt, val) != 2)
1840 _shttpd_elog(E_FATAL, NULL, "line %d in %s is invalid",
1841 line_no, config_file);
1842
1843 set_opt(ctx, opt, val);
1844 }
1845
1846 (void) fclose(fp);
1847 }
1848
1849 /* Now pass through the command line options */
1850 for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
1851 set_opt(ctx, &argv[i][1], argv[i + 1]);
1852 }
1853
1854 struct shttpd_ctx *
shttpd_init(int argc,char * argv[])1855 shttpd_init(int argc, char *argv[])
1856 {
1857 struct shttpd_ctx *ctx;
1858 struct tm *tm;
1859 const struct opt *o;
1860
1861 if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
1862 _shttpd_elog(E_FATAL, NULL, "cannot allocate shttpd context");
1863
1864 LL_INIT(&ctx->registered_uris);
1865 LL_INIT(&ctx->error_handlers);
1866 LL_INIT(&ctx->acl);
1867 LL_INIT(&ctx->ssi_funcs);
1868 LL_INIT(&ctx->listeners);
1869 LL_INIT(&ctx->workers);
1870
1871 /* Initialize options. First pass: set default option values */
1872 for (o = known_options; o->name != NULL; o++)
1873 ctx->options[o->index] = o->default_value ?
1874 _shttpd_strdup(o->default_value) : NULL;
1875
1876 /* Second and third passes: config file and argv */
1877 if (argc > 0 && argv != NULL)
1878 process_command_line_arguments(ctx, argv);
1879
1880 /* Call setter functions */
1881 for (o = known_options; o->name != NULL; o++)
1882 if (o->setter && ctx->options[o->index] != NULL)
1883 if (o->setter(ctx, ctx->options[o->index]) == FALSE) {
1884 shttpd_fini(ctx);
1885 return (NULL);
1886 }
1887
1888 _shttpd_current_time = time(NULL);
1889 tm = localtime(&_shttpd_current_time);
1890 _shttpd_tz_offset = 0;
1891
1892 if (num_workers(ctx) == 1)
1893 (void) add_worker(ctx);
1894 #if 0
1895 tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
1896 #endif
1897
1898 #ifdef _WIN32
1899 {WSADATA data; WSAStartup(MAKEWORD(2,2), &data);}
1900 #endif /* _WIN32 */
1901
1902 return (ctx);
1903 }
1904