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, &registered_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