1/*	$NetBSD: main.c,v 1.31 2023/06/07 20:12:31 mrg Exp $	*/
2
3/*	$eterna: main.c,v 1.6 2011/11/18 09:21:15 mrg Exp $	*/
4/* from: eterna: bozohttpd.c,v 1.159 2009/05/23 02:14:30 mrg Exp 	*/
5
6/*
7 * Copyright (c) 1997-2023 Matthew R. Green
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer and
17 *    dedication in the documentation and/or other materials provided
18 *    with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 */
33
34/* this program is dedicated to the Great God of Processed Cheese */
35
36/*
37 * main.c:  C front end to bozohttpd
38 */
39
40#include <sys/types.h>
41#include <sys/param.h>
42
43#include <errno.h>
44#include <stdlib.h>
45#include <string.h>
46#include <syslog.h>
47#include <time.h>
48#include <unistd.h>
49
50#include "bozohttpd.h"
51
52/* variables and functions */
53#ifndef LOG_FTP
54#define LOG_FTP LOG_DAEMON
55#endif
56
57/* print a usage message, and then exit */
58BOZO_DEAD static void
59usage(bozohttpd_t *httpd, char *progname)
60{
61	bozowarn(httpd, "usage: %s [options] slashdir [virtualhostname]",
62			progname);
63	bozowarn(httpd, "options:");
64
65	if (have_daemon_mode)
66		bozowarn(httpd, "   -b\t\t\tbackground in daemon mode");
67	if (have_cgibin &&
68	    have_dynamic_content)
69		bozowarn(httpd, "   -C suffix handler\tadd this CGI handler "
70				"for paths ending with `suffix'");
71	if (have_cgibin)
72		bozowarn(httpd, "   -c cgibin\t\tenable cgi-bin support in "
73				"this directory");
74	if (have_debug)
75		bozowarn(httpd, "   -d\t\t\tenable debug support");
76	if (have_user &&
77	    have_cgibin)
78		bozowarn(httpd, "   -E\t\t\tenable CGI support for user dirs");
79	if (have_core)
80		bozowarn(httpd, "   -e\t\t\tdon't clean the environment "
81				"(-t and -U only)");
82	if (have_daemon_mode)
83		bozowarn(httpd, "   -f\t\t\tforeground in daemon mode");
84	if (have_core)
85		bozowarn(httpd, "   -G\t\t\tprint version number and exit");
86	if (have_dirindex)
87		bozowarn(httpd, "   -H\t\t\thide files starting with a period "
88				"(.) in index mode");
89	if (have_core)
90		bozowarn(httpd, "   -I port\t\tbind or use on this port");
91	if (have_daemon_mode)
92		bozowarn(httpd, "   -i address\t\tbind on this address "
93				"(daemon mode only)");
94	if (have_lua)
95		bozowarn(httpd, "   -L prefix script\tadd this Lua script for "
96				"paths starting with `prefix'");
97	if (have_dynamic_content)
98		bozowarn(httpd, "   -M suffix t c c11\tadd this mime entry");
99	if (have_core)
100		bozowarn(httpd, "   -n\t\t\tdon't resolve host names");
101	if (have_daemon_mode)
102		bozowarn(httpd, "   -P pidfile\t\tpid file path");
103	if (have_user)
104		bozowarn(httpd, "   -p dir\t\t\"public_html\" directory name");
105	if (have_core)
106		bozowarn(httpd, "   -q\t\tquiet mode, no logging");
107	if (have_dirindex)
108		bozowarn(httpd, "   -R readme\t\tput readme file in footer "
109				"of directory index");
110	if (have_core) {
111		bozowarn(httpd, "   -S version\t\tset server version string");
112		bozowarn(httpd, "   -s\t\t\talways log to stderr");
113		bozowarn(httpd, "   -T type timeout\t"
114				"set <ssl|initial|header|request> timeout");
115		bozowarn(httpd, "   -t dir\t\tchroot to `dir'");
116		bozowarn(httpd, "   -U user\t\tchange user to `user'");
117	}
118	if (have_user)
119		bozowarn(httpd, "   -u\t\t\tenable ~user/public_html support");
120	if (have_core) {
121		bozowarn(httpd, "   -V\t\t\tUnknown virtual hosts go to "
122				"`slashdir'");
123		bozowarn(httpd, "   -v virtualroot\tenable virtual host "
124				"support in this directory");
125	}
126	if (have_dirindex)
127		bozowarn(httpd, "   -X\t\t\tdirectory index support");
128	if (have_core)
129		bozowarn(httpd, "   -x index\t\tdefault \"index.html\" "
130				"file name");
131	if (have_ssl) {
132		bozowarn(httpd, "   -Z cert privkey\tspecify path to server "
133				"certificate and private key file\n"
134				"\t\t\tin pem format and enable bozohttpd in "
135				"SSL mode");
136		bozowarn(httpd, "   -z ciphers\t\tspecify SSL ciphers");
137	}
138
139	bozoerr(httpd, 1, "%s failed to start", progname);
140}
141
142int
143main(int argc, char **argv)
144{
145	bozo_httpreq_t	*request;
146	bozohttpd_t	 httpd;
147	bozoprefs_t	 prefs;
148	char		*progname;
149	const char	*val;
150	int		 c;
151
152	(void) memset(&httpd, 0x0, sizeof(httpd));
153	(void) memset(&prefs, 0x0, sizeof(prefs));
154
155	if ((progname = strrchr(argv[0], '/')) == NULL)
156		progname = argv[0];
157	else
158		progname++;
159
160	openlog(progname, LOG_PID|LOG_NDELAY, LOG_FTP);
161
162	bozo_set_defaults(&httpd, &prefs);
163
164	/*
165	 * -r option was removed, do not reuse it for a while
166	 */
167
168	while ((c = getopt(argc, argv,
169	    "C:EGHI:L:M:m:P:R:S:T:U:VXZ:bc:defhi:np:qst:uv:x:z:")) != -1) {
170		switch (c) {
171
172		case 'b':
173			if (!have_daemon_mode)
174 no_daemon_mode:
175				bozoerr(&httpd, 1, "Daemon mode not enabled");
176
177			/*
178			 * test suite support - undocumented
179			 * background == 2 (aka, -b -b) means to
180			 * only process 1 per kid
181			 */
182			val = bozo_get_pref(&prefs, "background") == NULL ?
183			    "1" : "2";
184			bozo_set_pref(&httpd, &prefs, "background", val);
185			break;
186
187		case 'C':
188			if (!have_dynamic_content ||
189			    !have_cgibin)
190				bozoerr(&httpd, 1,
191				    "dynamic CGI handler support not enabled");
192
193			/* make sure there's two arguments */
194			if (argc - optind < 1)
195				usage(&httpd, progname);
196			bozo_add_content_map_cgi(&httpd, optarg,
197					argv[optind++]);
198			break;
199
200		case 'c':
201			if (!have_cgibin)
202				bozoerr(&httpd, 1, "CGI not enabled");
203
204			bozo_cgi_setbin(&httpd, optarg);
205			break;
206
207		case 'd':
208			if (!have_debug)
209				bozowarn(&httpd, "Debugging not enabled");
210			httpd.debug++;
211			break;
212
213		case 'E':
214			if (!have_user ||
215			    !have_cgibin)
216				bozoerr(&httpd, 1, "CGI not enabled");
217
218			bozo_set_pref(&httpd, &prefs, "enable user cgibin",
219				      "true");
220			break;
221
222		case 'e':
223			bozo_set_pref(&httpd, &prefs, "dirty environment",
224				      "true");
225			break;
226
227		case 'f':
228			if (!have_daemon_mode)
229				goto no_daemon_mode;
230
231			bozo_set_pref(&httpd, &prefs, "foreground", "true");
232			break;
233
234		case 'G':
235			{
236				char	version[128];
237
238				bozo_get_version(version, sizeof(version));
239				printf("bozohttpd version %s\n", version);
240			}
241			return 0;
242
243		case 'H':
244			if (!have_dirindex)
245 no_dirindex_support:
246				bozoerr(&httpd, 1,
247					"directory indexing not enabled");
248
249			bozo_set_pref(&httpd, &prefs, "hide dots", "true");
250			break;
251
252		case 'I':
253			bozo_set_pref(&httpd, &prefs, "port number", optarg);
254			break;
255
256		case 'i':
257			if (!have_daemon_mode)
258				goto no_daemon_mode;
259
260			bozo_set_pref(&httpd, &prefs, "bind address", optarg);
261			break;
262
263		case 'L':
264			if (!have_lua)
265				bozoerr(&httpd, 1, "Lua support not enabled");
266
267			/* make sure there's two argument */
268			if (argc - optind < 1)
269				usage(&httpd, progname);
270			bozo_add_lua_map(&httpd, optarg, argv[optind]);
271			optind++;
272			break;
273
274		case 'M':
275			if (!have_dynamic_content)
276				bozoerr(&httpd, 1,
277				    "dynamic mime content support not enabled");
278
279			/* make sure there're four arguments */
280			if (argc - optind < 3)
281				usage(&httpd, progname);
282			bozo_add_content_map_mime(&httpd, optarg, argv[optind],
283			    argv[optind+1], argv[optind+2]);
284			optind += 3;
285			break;
286
287		case 'm':
288			if (!have_ssl)
289				goto no_ssl;
290
291			httpd.ssl_min_proto = optarg;
292			debug((&httpd, DEBUG_NORMAL,
293			    "using minimum protocol version: %s", optarg));
294			break;
295
296		case 'n':
297			bozo_set_pref(&httpd, &prefs, "numeric", "true");
298			break;
299
300		case 'P':
301			if (!have_daemon_mode)
302				goto no_daemon_mode;
303
304			bozo_set_pref(&httpd, &prefs, "pid file", optarg);
305			break;
306
307		case 'p':
308			if (!have_user)
309 no_user_support:
310				bozoerr(&httpd, 1, "User support not enabled");
311
312			bozo_set_pref(&httpd, &prefs, "public_html", optarg);
313			break;
314
315		case 'q':
316			bozo_set_pref(&httpd, &prefs, "no log", "true");
317			break;
318
319		case 'R':
320			if (!have_dirindex)
321				goto no_dirindex_support;
322
323			bozo_set_pref(&httpd, &prefs, "directory index readme",
324				      optarg);
325			break;
326
327		case 'S':
328			bozo_set_pref(&httpd, &prefs, "server software",
329				      optarg);
330			break;
331
332		case 's':
333			bozo_set_pref(&httpd, &prefs, "log to stderr", "true");
334			break;
335
336		case 'T':
337			/* make sure there're two arguments */
338			if (argc - optind < 1)
339				usage(&httpd, progname);
340			if (bozo_set_timeout(&httpd, &prefs,
341					     optarg, argv[optind])) {
342				bozoerr(&httpd, 1,
343					"invalid type '%s'", optarg);
344				/* NOTREACHED */
345			}
346			optind++;
347			break;
348
349		case 't':
350			bozo_set_pref(&httpd, &prefs, "chroot dir", optarg);
351			break;
352
353		case 'U':
354			bozo_set_pref(&httpd, &prefs, "username", optarg);
355			break;
356
357		case 'u':
358			if (!have_user)
359				goto no_user_support;
360
361			bozo_set_pref(&httpd, &prefs, "enable users", "true");
362			break;
363
364		case 'V':
365			bozo_set_pref(&httpd, &prefs, "unknown slash", "true");
366			break;
367
368		case 'v':
369			bozo_set_pref(&httpd, &prefs, "virtual base", optarg);
370			break;
371
372		case 'X':
373			if (!have_dirindex)
374				goto no_dirindex_support;
375
376			bozo_set_pref(&httpd, &prefs, "directory indexing",
377				      "true");
378			break;
379
380		case 'x':
381			bozo_set_pref(&httpd, &prefs, "index.html", optarg);
382			break;
383
384		case 'Z':
385			if (!have_ssl)
386 no_ssl:
387				bozoerr(&httpd, 1, "ssl support not enabled");
388
389			/* make sure there's two arguments */
390			if (argc - optind < 1)
391				usage(&httpd, progname);
392			bozo_ssl_set_opts(&httpd, optarg, argv[optind++]);
393			break;
394
395		case 'z':
396			if (!have_ssl)
397				goto no_ssl;
398
399			bozo_ssl_set_ciphers(&httpd, optarg);
400			break;
401
402		default:
403			usage(&httpd, progname);
404			/* NOTREACHED */
405		}
406	}
407
408	argc -= optind;
409	argv += optind;
410
411	if (argc == 0 || argc > 2) {
412		usage(&httpd, progname);
413	}
414
415	/* virtual host, and root of tree to serve */
416	bozo_setup(&httpd, &prefs, argv[1], argv[0]);
417
418	/*
419	 * read and process the HTTP request.
420	 */
421	do {
422		if ((request = bozo_read_request(&httpd)) != NULL) {
423			bozo_process_request(request);
424			bozo_clean_request(request);
425		}
426	} while (httpd.background);
427
428	bozo_cleanup(&httpd, &prefs);
429
430	return (0);
431}
432