1 : /*
2 : * Copyright (C) 2004, 2005, 2006 James Antill
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Lesser General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this library; if not, write to the Free Software
16 : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 : *
18 : * email: james@and.org
19 : */
20 : /* conditionally compliant HTTP/1.1 server. */
21 : #define _GNU_SOURCE 1 /* strsignal() / posix_fadvise64 */
22 : #include <vstr.h>
23 :
24 : #include <socket_poll.h>
25 : #include <timer_q.h>
26 :
27 : #include <stdlib.h>
28 : #include <sys/types.h>
29 : #include <sys/socket.h>
30 : #include <unistd.h>
31 : #include <fcntl.h>
32 : #include <stdlib.h>
33 : #include <sys/types.h>
34 : #include <sys/socket.h>
35 : #include <errno.h>
36 : #include <err.h>
37 : #include <netinet/in.h>
38 : #include <netinet/tcp.h>
39 : #include <arpa/inet.h>
40 :
41 : #include <grp.h>
42 :
43 : #define EX_UTILS_NO_USE_INIT 1
44 : #define EX_UTILS_NO_USE_EXIT 1
45 : #define EX_UTILS_NO_USE_LIMIT 1
46 : #define EX_UTILS_NO_USE_OPEN 1
47 : #define EX_UTILS_NO_USE_GET 1
48 : #define EX_UTILS_NO_USE_IO_FD 1
49 : #define EX_UTILS_RET_FAIL 1
50 : #include "ex_utils.h"
51 :
52 : #include "mk.h"
53 :
54 : MALLOC_CHECK_DECL();
55 :
56 : #include "cntl.h"
57 : #include "date.h"
58 :
59 : #define HTTPD_HAVE_GLOBAL_OPTS 1
60 :
61 : #include "httpd.h"
62 : #include "httpd_policy.h"
63 :
64 :
65 : #ifndef TCP_CONGESTION
66 : # ifdef __linux__
67 : # define TCP_CONGESTION 13
68 : # else
69 : # define TCP_CONGESTION 0
70 : # endif
71 : #endif
72 :
73 : /* we get prctl.h from evnt.h */
74 : #if defined(HAVE_SYS_CAPABILITY_H) && defined(PR_SET_KEEPCAPS)
75 : # include <sys/capability.h>
76 : # define PROC_CNTL_KEEPCAPS(x1) prctl(PR_SET_KEEPCAPS, x1, 0, 0, 0)
77 : #else
78 : # define PROC_CNTL_KEEPCAPS(x1) (errno = ENOSYS, -1)
79 : # define cap_t void *
80 : # define cap_from_text(x) (errno = ENOSYS, NULL)
81 : # define cap_set_proc(x) (errno = ENOSYS, -1)
82 : # define cap_free(x) (errno = ENOSYS, -1)
83 : #endif
84 :
85 : #ifdef PR_SET_DUMPABLE
86 : # define PROC_CNTL_DUMPABLE(x1) prctl(PR_SET_DUMPABLE, x1, 0, 0, 0)
87 : #else
88 : # define PROC_CNTL_DUMPABLE(x1) (errno = ENOSYS, -1)
89 : #endif
90 :
91 :
92 :
93 :
94 : #define CLEN COMPILE_STRLEN
95 :
96 : /* is the cstr a prefix of the vstr */
97 : #define VPREFIX(vstr, p, l, cstr) \
98 : (((l) >= CLEN(cstr)) && \
99 : vstr_cmp_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
100 : /* is the cstr a suffix of the vstr */
101 : #define VSUFFIX(vstr, p, l, cstr) \
102 : (((l) >= CLEN(cstr)) && \
103 : vstr_cmp_eod_buf_eq(vstr, p, l, cstr, CLEN(cstr)))
104 :
105 : /* is the cstr a prefix of the vstr, no case */
106 : #define VIPREFIX(vstr, p, l, cstr) \
107 : (((l) >= CLEN(cstr)) && \
108 : vstr_cmp_case_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
109 :
110 : /* for simplicity */
111 : #define VEQ(vstr, p, l, cstr) vstr_cmp_cstr_eq(vstr, p, l, cstr)
112 :
113 : static Vlg *vlg = NULL;
114 :
115 : static void usage(const char *program_name, int ret, const char *prefix)
116 4 : {
117 4 : Vstr_base *out = vstr_make_base(NULL);
118 :
119 4 : if (!out)
120 0 : errno = ENOMEM, err(EXIT_FAILURE, "usage");
121 :
122 4 : vstr_add_fmt(out, 0, "%s\n\
123 : Format: %s [options] <dir>\n\
124 : Daemon options\n\
125 : --configuration-file\n\
126 : --config-file -C - Load configuration file specified.\n\
127 : --configuration-directory\n\
128 : --config-dir - Load <dir>/*.conf as configuration files.\n\
129 : --default-configuration\n\
130 : --def-configuration\n\
131 : - Do the default configuration, which is done if no\n\
132 : options are given.\n\
133 : \n\
134 : --configuration-data-daemon\n\
135 : - Parse configuration given in the\n\
136 : org.and.daemon-conf-1.0 namespace.\n\
137 : --daemon - Toggle becoming a daemon%s.\n\
138 : --chroot - Change root.\n\
139 : --drop-privs - Toggle droping privilages%s.\n\
140 : --priv-uid - Drop privilages to this uid.\n\
141 : --priv-gid - Drop privilages to this gid.\n\
142 : --pid-file - Log pid to file.\n\
143 : --cntl-file - Create control file.\n\
144 : --accept-filter-file\n\
145 : - Load Linux Socket Filter code for accept().\n\
146 : --processes - Number of processes to use (default: 1).\n\
147 : --debug -d - Raise debug level (can be used upto 3 times).\n\
148 : --host -H - IPv4 address to bind (default: \"all\").\n\
149 : --help -h - Print this message.\n\
150 : --max-connections\n\
151 : -M - Max connections allowed (0 = no limit).\n\
152 : --nagle -n - Toggle usage of nagle TCP option%s.\n\
153 : --port -P - Port to bind to.\n\
154 : --idle-timeout -t - Timeout (usecs) for connections that are idle.\n\
155 : --defer-accept - Time to defer dataless connections (default: 8s)\n\
156 : --version -V - Print the version string.\n\
157 : \n\
158 : HTTPD options\n\
159 : --configuration-data-httpd\n\
160 : - Parse configuration given in the\n\
161 : org.and.httpd-conf-main-1.0 namespace.\n\
162 : --mmap - Toggle use of mmap() to load files%s.\n\
163 : --sendfile - Toggle use of sendfile() to load files%s.\n\
164 : --keep-alive - Toggle use of Keep-Alive handling%s.\n\
165 : --keep-alive-1.0 - Toggle use of Keep-Alive handling for HTTP/1.0%s.\n\
166 : --virtual-hosts\n\
167 : --vhosts - Toggle use of directory virtual hostnames%s.\n\
168 : --range - Toggle use of partial responces%s.\n\
169 : --range-1.0 - Toggle use of partial responces for HTTP/1.0%s.\n\
170 : --public-only - Toggle use of public only privilages%s.\n\
171 : --directory-filename\n\
172 : --dir-filename - Filename to use when requesting directories.\n\
173 : --server-name - Contents of server header used in replies.\n\
174 : --gzip-content-replacement\n\
175 : - Toggle use of gzip content replacement%s.\n\
176 : --mime-types-main - Main mime types filename (default: /etc/mime.types).\n\
177 : --mime-types-xtra - Additional mime types filename.\n\
178 : --error-406 - Toggle sending 406 responses%s.\n\
179 : --canonize-host - Strip leading 'www.', strip trailing '.'%s.\n\
180 : --error-host-400 - Give an 400 error for a bad host%s.\n\
181 : --check-host - Whether we check host headers at all%s.\n\
182 : --unspecified-hostname\n\
183 : - Used for req with no Host header (default is hostname).\n\
184 : --max-header-sz - Max size of http header (0 = no limit).\n\
185 : ",
186 : prefix, program_name,
187 : opt_def_toggle(FALSE), opt_def_toggle(FALSE),
188 : opt_def_toggle(EVNT_CONF_NAGLE),
189 : opt_def_toggle(HTTPD_CONF_USE_MMAP),
190 : opt_def_toggle(HTTPD_CONF_USE_SENDFILE),
191 : opt_def_toggle(HTTPD_CONF_USE_KEEPA),
192 : opt_def_toggle(HTTPD_CONF_USE_KEEPA_1_0),
193 : opt_def_toggle(HTTPD_CONF_USE_VHOSTS_NAME),
194 : opt_def_toggle(HTTPD_CONF_USE_RANGE),
195 : opt_def_toggle(HTTPD_CONF_USE_RANGE_1_0),
196 : opt_def_toggle(HTTPD_CONF_USE_PUBLIC_ONLY),
197 : opt_def_toggle(HTTPD_CONF_USE_ENC_CONTENT_REPLACEMENT),
198 : opt_def_toggle(HTTPD_CONF_USE_ERR_406),
199 : opt_def_toggle(HTTPD_CONF_USE_CANONIZE_HOST),
200 : opt_def_toggle(HTTPD_CONF_USE_HOST_ERR_400),
201 : opt_def_toggle(HTTPD_CONF_USE_HOST_CHK));
202 :
203 4 : if (io_put_all(out, ret ? STDERR_FILENO : STDOUT_FILENO) == IO_FAIL)
204 0 : err(EXIT_FAILURE, "write");
205 :
206 4 : exit (ret);
207 : }
208 :
209 : static void serv_init(void)
210 64 : {
211 64 : if (!vstr_init()) /* init the library */
212 0 : errno = ENOMEM, err(EXIT_FAILURE, "init");
213 :
214 64 : vlg_init();
215 :
216 64 : if (!vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_TYPE_GRPALLOC_CACHE,
217 : VSTR_TYPE_CNTL_CONF_GRPALLOC_CSTR))
218 0 : errno = ENOMEM, err(EXIT_FAILURE, "init");
219 :
220 64 : if (!vstr_cntl_conf(NULL,
221 : VSTR_CNTL_CONF_SET_NUM_BUF_SZ, OPT_SERV_CONF_BUF_SZ))
222 0 : errno = ENOMEM, err(EXIT_FAILURE, "init");
223 :
224 : /* no passing of conf to evnt */
225 64 : if (!vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '$') ||
226 : !vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_LOC_CSTR_THOU_SEP, "_") ||
227 : !vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_LOC_CSTR_THOU_GRP, "\3") ||
228 : !vstr_sc_fmt_add_all(NULL) ||
229 : !vlg_sc_fmt_add_all(NULL) ||
230 : !VSTR_SC_FMT_ADD(NULL, http_fmt_add_vstr_add_vstr,
231 : "<http-esc.vstr", "p%zu%zu", ">") ||
232 : !VSTR_SC_FMT_ADD(NULL, http_fmt_add_vstr_add_sect_vstr,
233 : "<http-esc.vstr.sect", "p%p%u", ">"))
234 0 : errno = ENOMEM, err(EXIT_FAILURE, "init");
235 :
236 64 : if (!(vlg = vlg_make()))
237 0 : errno = ENOMEM, err(EXIT_FAILURE, "init");
238 :
239 64 : if (!VSTR_SC_FMT_ADD(vlg->out_vstr->conf, http_fmt_add_vstr_add_vstr,
240 : "<http-esc.vstr", "p%zu%zu", ">") ||
241 : !VSTR_SC_FMT_ADD(vlg->out_vstr->conf, http_fmt_add_vstr_add_sect_vstr,
242 : "<http-esc.vstr.sect", "p%p%u", ">"))
243 0 : errno = ENOMEM, err(EXIT_FAILURE, "init");
244 64 : if (!VSTR_SC_FMT_ADD(vlg->sig_out_vstr->conf, http_fmt_add_vstr_add_vstr,
245 : "<http-esc.vstr", "p%zu%zu", ">") ||
246 : !VSTR_SC_FMT_ADD(vlg->sig_out_vstr->conf, http_fmt_add_vstr_add_sect_vstr,
247 : "<http-esc.vstr.sect", "p%p%u", ">"))
248 0 : errno = ENOMEM, err(EXIT_FAILURE, "init");
249 :
250 64 : if (!socket_poll_init(0, SOCKET_POLL_TYPE_MAP_DIRECT))
251 0 : errno = ENOMEM, err(EXIT_FAILURE, "init");
252 :
253 64 : evnt_logger(vlg);
254 64 : evnt_poll_init();
255 64 : evnt_timeout_init();
256 :
257 64 : vlg_time_set(vlg, evnt_sc_time);
258 :
259 64 : opt_serv_logger(vlg);
260 :
261 64 : httpd_init(vlg);
262 :
263 64 : opt_serv_sc_signals();
264 64 : }
265 :
266 : static int serv_cb_func_send(struct Evnt *evnt)
267 17786860 : {
268 17786860 : struct Con *con = (struct Con *)evnt;
269 :
270 : assert(HTTPD_CONF_SEND_CALL_LIMIT >= 1);
271 17786860 : con->io_limit_num = HTTPD_CONF_SEND_CALL_LIMIT;
272 17786860 : return (httpd_serv_send(con));
273 : }
274 :
275 : static int serv_cb_func_recv(struct Evnt *evnt)
276 50488 : {
277 50488 : struct Con *con = (struct Con *)evnt;
278 :
279 : assert(HTTPD_CONF_RECV_CALL_LIMIT >= 1);
280 50488 : con->io_limit_num = HTTPD_CONF_RECV_CALL_LIMIT;
281 50488 : return (httpd_serv_recv(con));
282 : }
283 :
284 : static void serv_cb_func_free(struct Evnt *evnt)
285 12052 : {
286 12052 : struct Con *con = (struct Con *)evnt;
287 :
288 12052 : httpd_fin_fd_close(con);
289 :
290 12052 : opt_serv_sc_free_beg(evnt, "FREE");
291 :
292 6026 : ASSERT(con->fs && !con->fs_num && !con->fs_off && (con->fs->fd == -1));
293 :
294 12052 : vstr_free_base(con->mpbr_ct);
295 :
296 12052 : if (con->fs_sz > 1)
297 : {
298 32 : ASSERT(con->fs != con->fs_store);
299 64 : F(con->fs);
300 : }
301 : else
302 5994 : ASSERT(con->fs == con->fs_store);
303 :
304 12052 : vstr_free_base(con->tag);
305 12052 : F(con);
306 12052 : }
307 :
308 : static struct Evnt *serv_cb_func_accept(struct Evnt *from_evnt, int fd,
309 : struct sockaddr *sa, socklen_t len)
310 12052 : {
311 12052 : struct Acpt_listener *acpt_listener = (struct Acpt_listener *)from_evnt;
312 12052 : struct Con *con = MK(sizeof(struct Con));
313 :
314 12052 : if (sa->sa_family != AF_INET) /* only support IPv4 atm. */
315 0 : goto sa_fail;
316 :
317 12052 : if (!(con->tag = vstr_dup_cstr_ptr(NULL, "<default>")))
318 0 : goto con_tag_fail;
319 :
320 12052 : if (!con || !evnt_make_acpt_dup(con->evnt, fd, sa, len))
321 : goto make_acpt_fail;
322 :
323 12052 : con->evnt->cbs->cb_func_recv = serv_cb_func_recv;
324 12052 : con->evnt->cbs->cb_func_send = serv_cb_func_send;
325 12052 : con->evnt->cbs->cb_func_free = serv_cb_func_free;
326 :
327 12052 : if (!evnt_limit_dup(con->evnt, &httpd_opts->s->io_nslimit))
328 0 : goto evnt_fail; /* redone on policy changes... */
329 :
330 12052 : if (!evnt_limit_add(con->evnt, httpd_opts->s->ref_io_limit))
331 0 : goto evnt_fail; /* redone on policy changes... */
332 :
333 12052 : if (!evnt_limit_dup(con->evnt, &httpd_opts->s->io_nslimit))
334 0 : goto evnt_fail;
335 :
336 12052 : if (!evnt_limit_add(con->evnt, httpd_opts->s->ref_io_limit))
337 0 : goto evnt_fail;
338 :
339 12052 : if (!httpd_con_init(con, acpt_listener))
340 0 : goto evnt_fail;
341 :
342 12052 : if (!evnt_sc_timeout_via_mtime(con->evnt,
343 : con->policy->s->idle_timeout * 1000))
344 0 : VLG_WARNNOMEM_GOTO(evnt_fail, (vlg, "timeout: %m\n"));
345 :
346 12052 : if (!opt_serv_sc_acpt_end(con->policy->s, from_evnt, con->evnt))
347 0 : goto evnt_fail;
348 :
349 6026 : ASSERT(!con->evnt->flag_q_closed);
350 :
351 12052 : vlg_dbg2(vlg, "con($<sa:%p>): congestion=$<sockopt.s:%d%d%d>\n",
352 : CON_CEVNT_SA(con), evnt_fd(con->evnt), IPPROTO_TCP, TCP_CONGESTION);
353 :
354 12052 : return (con->evnt);
355 :
356 0 : evnt_fail:
357 0 : evnt_close(con->evnt);
358 0 : return (con->evnt);
359 :
360 0 : make_acpt_fail:
361 0 : vstr_free_base(con->tag);
362 0 : con_tag_fail:
363 0 : F(con);
364 0 : VLG_WARNNOMEM_RET(NULL, (vlg, "%s: %m\n", "accept"));
365 :
366 0 : sa_fail:
367 0 : F(con);
368 0 : VLG_WARNNOMEM_RET(NULL, (vlg, "%s: HTTPD sa == ipv4 fail\n", "accept"));
369 : }
370 :
371 : static void serv_make_bind(const char *program_name)
372 40 : {
373 40 : Opt_serv_addr_opts *addr = httpd_opts->s->addr_beg;
374 :
375 200 : while (addr)
376 : {
377 120 : const char *acpt_address = NULL;
378 120 : const char *acpt_filter_file = NULL;
379 120 : const char *acpt_cong = NULL;
380 120 : struct Evnt *evnt = NULL;
381 :
382 120 : OPT_SC_EXPORT_CSTR(acpt_address, addr->acpt_address, FALSE,
383 : "accept address");
384 120 : OPT_SC_EXPORT_CSTR(acpt_filter_file, addr->acpt_filter_file, FALSE,
385 : "accept filter file");
386 120 : OPT_SC_EXPORT_CSTR(acpt_cong, addr->acpt_cong, FALSE,
387 : "accept congestion mode");
388 :
389 120 : evnt = evnt_sc_serv_make_bind_ipv4(acpt_address, addr->tcp_port,
390 : addr->q_listen_len,
391 : addr->max_connections,
392 : addr->defer_accept,
393 : acpt_filter_file, acpt_cong);
394 :
395 120 : if (addr->def_policy->len)
396 : {
397 16 : Vstr_base *dp = addr->def_policy;
398 16 : Opt_serv_policy_opts *po = opt_policy_find(httpd_opts->s, dp, 1, dp->len);
399 16 : Acpt_listener *acpt_listener = (Acpt_listener *)evnt;
400 :
401 16 : if (!po)
402 0 : usage(program_name, EXIT_FAILURE,
403 : " The name of the accept policy isn't valid.\n");
404 :
405 16 : acpt_listener->def_policy = vstr_ref_add(po->ref);
406 : }
407 :
408 120 : evnt->cbs->cb_func_accept = serv_cb_func_accept;
409 :
410 120 : addr = addr->next;
411 : }
412 40 : }
413 :
414 : #define VCMP_MT_EQ_ALL(x, y, z) \
415 : vstr_cmp_eq(x -> mime_types_ ## z, 1, x -> mime_types_ ## z ->len, \
416 : y -> mime_types_ ## z, 1, y -> mime_types_ ## z ->len)
417 : static Httpd_policy_opts *serv__mime_types_eq(Httpd_policy_opts *node)
418 138 : { /* compares both mime filenames ... */
419 138 : Opt_serv_policy_opts *scan = httpd_opts->s->def_policy;
420 :
421 69 : ASSERT(node);
422 :
423 315 : while (scan != node->s)
424 : {
425 176 : Httpd_policy_opts *tmp = (Httpd_policy_opts *)scan;
426 :
427 176 : if (VCMP_MT_EQ_ALL(tmp, node, main) && VCMP_MT_EQ_ALL(tmp, node, xtra))
428 68 : return (tmp);
429 :
430 108 : scan = scan->next;
431 : }
432 :
433 70 : return (NULL);
434 : }
435 : #undef VCMP_MT_EQ_ALL
436 :
437 : static void serv_mime_types(const char *program_name)
438 40 : {
439 40 : Opt_serv_policy_opts *scan = httpd_opts->s->def_policy;
440 :
441 218 : while (scan)
442 : {
443 138 : Httpd_policy_opts *tmp = (Httpd_policy_opts *)scan;
444 138 : Httpd_policy_opts *prev = NULL;
445 :
446 138 : if (!mime_types_init(tmp->mime_types,
447 : tmp->mime_types_def_ct, 1,
448 : tmp->mime_types_def_ct->len))
449 0 : errno = ENOMEM, err(EXIT_FAILURE, "init");
450 :
451 138 : if ((prev = serv__mime_types_eq(tmp)))
452 68 : mime_types_combine_filedata(tmp->mime_types, prev->mime_types);
453 : else
454 : {
455 70 : const char *mime_types_main = NULL;
456 70 : const char *mime_types_xtra = NULL;
457 :
458 70 : OPT_SC_EXPORT_CSTR(mime_types_main, tmp->mime_types_main, FALSE,
459 : "MIME types main file");
460 70 : OPT_SC_EXPORT_CSTR(mime_types_xtra, tmp->mime_types_xtra, FALSE,
461 : "MIME types extra file");
462 :
463 70 : if (!mime_types_load_simple(tmp->mime_types, mime_types_main))
464 0 : err(EXIT_FAILURE, "load_mime(%s)", mime_types_main);
465 :
466 70 : if (!mime_types_load_simple(tmp->mime_types, mime_types_xtra))
467 0 : err(EXIT_FAILURE, "load_mime(%s)", mime_types_xtra);
468 : }
469 :
470 138 : scan = scan->next;
471 : }
472 :
473 : /* we don't need the filenames after we've loaded... */
474 40 : scan = httpd_opts->s->def_policy;
475 218 : while (scan)
476 : {
477 138 : Httpd_policy_opts *tmp = (Httpd_policy_opts *)scan;
478 :
479 138 : vstr_free_base(tmp->mime_types_main); tmp->mime_types_main = NULL;
480 138 : vstr_free_base(tmp->mime_types_xtra); tmp->mime_types_xtra = NULL;
481 :
482 138 : scan = scan->next;
483 : }
484 40 : }
485 :
486 : static void serv_canon_policies(void)
487 40 : {
488 40 : Opt_serv_policy_opts *scan = httpd_opts->s->def_policy;
489 :
490 218 : while (scan)
491 : { /* check variables for things which will screw us too much */
492 138 : Httpd_policy_opts *def_opt = (Httpd_policy_opts *)httpd_opts->s->def_policy;
493 138 : Httpd_policy_opts *opt = (Httpd_policy_opts *)scan;
494 138 : Vstr_base *def_doc_root = def_opt->document_root;
495 138 : Vstr_base *s1 = NULL;
496 138 : int ec = EXIT_FAILURE;
497 :
498 69 : ASSERT(scan->beg == httpd_opts->s);
499 :
500 138 : s1 = opt->document_root;
501 138 : if (!httpd_canon_dir_path(s1))
502 0 : VLG_ERRNOMEM((vlg, ec, "canon_dir_path($<vstr.all:%p>): %m\n", s1));
503 138 : if (!s1->len && !vstr_add_vstr(s1, 0, def_doc_root, 1, def_doc_root->len,
504 : VSTR_TYPE_ADD_BUF_REF))
505 0 : VLG_ERRNOMEM((vlg, ec, "canon_dir_path(): %m\n"));
506 :
507 138 : s1 = opt->req_conf_dir;
508 138 : if (!httpd_canon_dir_path(s1))
509 0 : VLG_ERRNOMEM((vlg, ec, "canon_dir_path($<vstr.all:%p>): %m\n", s1));
510 :
511 138 : s1 = opt->req_err_dir;
512 138 : if (!httpd_canon_dir_path(s1))
513 0 : VLG_ERRNOMEM((vlg, ec, "canon_dir_path($<vstr.all:%p>): %m\n", s1));
514 :
515 138 : if (!httpd_init_default_hostname(scan))
516 0 : VLG_ERRNOMEM((vlg, ec, "hostname(): %m\n"));
517 :
518 138 : s1 = opt->dir_filename;
519 138 : if (!httpd_valid_url_filename(s1, 1, s1->len) &&
520 : !vstr_sub_cstr_ptr(s1, 1, s1->len, HTTPD_CONF_DEF_DIR_FILENAME))
521 0 : VLG_ERRNOMEM((vlg, ec, "dir_filename(): %m\n"));
522 :
523 138 : scan = scan->next;
524 : }
525 40 : }
526 :
527 : static int serv_def_conf(Vstr_base *out)
528 0 : { /* pretend we passed (if it works...):
529 : * -C /etc/and-httpd/and-httpd.conf --conf-dir /etc/and-httpd/conf.d ... */
530 : struct stat64 d_stat[1];
531 : static const char pc_file[] = PATH_SYSCONFDIR "/and-httpd/and-httpd.conf";
532 : static const char pc_dir[] = PATH_SYSCONFDIR "/and-httpd/conf.d";
533 :
534 0 : if ((stat64(pc_file, d_stat) != -1) &&
535 : !httpd_conf_main_parse_file(out, httpd_opts, pc_file))
536 0 : return (FALSE);
537 0 : if ((stat64(pc_file, d_stat) != -1) &&
538 : !opt_serv_sc_config_dir(out, httpd_opts, pc_dir,
539 : httpd_sc_conf_main_parse_dir_file))
540 0 : return (FALSE);
541 :
542 0 : return (TRUE);
543 : }
544 :
545 : #define POPT_TOGGLE_ARG(x) do { \
546 : Opt_serv_policy_opts *popt_scan = httpd_opts->s->def_policy; \
547 : \
548 : while (popt_scan) \
549 : { \
550 : Httpd_policy_opts *popt_tmp = (Httpd_policy_opts *)popt_scan; \
551 : OPT_TOGGLE_ARG(popt_tmp->x); \
552 : popt_scan = popt_scan->next; \
553 : } \
554 : } while (FALSE)
555 :
556 : #define POPT_NUM_NR_ARG(x, y) do { \
557 : Opt_serv_policy_opts *popt_scan = httpd_opts->s->def_policy; \
558 : \
559 : while (popt_scan) \
560 : { \
561 : Httpd_policy_opts *popt_tmp = (Httpd_policy_opts *)popt_scan; \
562 : OPT_NUM_NR_ARG(popt_tmp->x, y); \
563 : popt_scan = popt_scan->next; \
564 : } \
565 : } while (FALSE)
566 :
567 : #define POPT_VSTR_ARG(x) do { \
568 : Opt_serv_policy_opts *popt_scan = httpd_opts->s->def_policy; \
569 : \
570 : while (popt_scan) \
571 : { \
572 : Httpd_policy_opts *popt_tmp = (Httpd_policy_opts *)popt_scan; \
573 : OPT_VSTR_ARG(popt_tmp->x); \
574 : popt_scan = popt_scan->next; \
575 : } \
576 : } while (FALSE)
577 :
578 : static void serv_cmd_line(int argc, char *argv[])
579 64 : {
580 64 : int optchar = 0;
581 64 : const char *program_name = NULL;
582 : struct option long_options[] =
583 : {
584 : OPT_SERV_DECL_GETOPTS(),
585 :
586 : {"configuration-file", required_argument, NULL, 'C'},
587 : {"config-file", required_argument, NULL, 'C'},
588 : {"conf-file", required_argument, NULL, 'C'},
589 : {"configuration-directory", required_argument, NULL, 140},
590 : {"config-dir", required_argument, NULL, 140},
591 : {"conf-dir", required_argument, NULL, 140},
592 : {"configuration-data-daemon", required_argument, NULL, 143},
593 : {"config-data-daemon", required_argument, NULL, 143},
594 : {"configuration-data-httpd", required_argument, NULL, 144},
595 : {"config-data-httpd", required_argument, NULL, 144},
596 : {"configuration-data-and-httpd", required_argument, NULL, 144},
597 : {"config-data-and-httpd", required_argument, NULL, 144},
598 : {"default-configuration", no_argument, NULL, 149},
599 : {"def-config", no_argument, NULL, 149},
600 :
601 : {"sendfile", optional_argument, NULL, 31},
602 : {"mmap", optional_argument, NULL, 30},
603 :
604 : {"max-header-sz", required_argument, NULL, 128},
605 : {"keep-alive", optional_argument, NULL, 129},
606 : {"keep-alive-1.0", optional_argument, NULL, 130},
607 : {"vhosts", optional_argument, NULL, 131},
608 : {"virtual-hosts", optional_argument, NULL, 131},
609 : {"range", optional_argument, NULL, 132},
610 : {"range-1.0", optional_argument, NULL, 133},
611 : {"public-only", optional_argument, NULL, 134}, /* FIXME: rm ? */
612 : {"dir-filename", required_argument, NULL, 135},
613 : {"server-name", required_argument, NULL, 136},
614 : {"gzip-content-replacement", optional_argument, NULL, 137},
615 : /* 138 */
616 : {"error-406", optional_argument, NULL, 139},
617 : /* 140 -- config. dir above */
618 : {"mime-types-main", required_argument, NULL, 141},
619 : {"mime-types-extra", required_argument, NULL, 142},
620 : {"mime-types-xtra", required_argument, NULL, 142},
621 : /* 143 -- config data above */
622 : /* 144 -- config data above */
623 : {"unspecified-hostname", required_argument, NULL, 145},
624 : {"canonize-host", optional_argument, NULL, 146},
625 : {"error-host-400", optional_argument, NULL, 147},
626 : {"check-host", optional_argument, NULL, 148},
627 : /* 149 -- config data above */
628 : /* {"404-file", required_argument, NULL, 0}, */
629 : {NULL, 0, NULL, 0}
630 64 : };
631 64 : const char *chroot_dir = NULL;
632 64 : const char *document_root = NULL;
633 64 : Vstr_base *out = vstr_make_base(NULL);
634 64 : Httpd_policy_opts *popts = NULL;
635 :
636 64 : if (!out)
637 0 : errno = ENOMEM, err(EXIT_FAILURE, "command line");
638 :
639 64 : evnt_opt_nagle = TRUE;
640 :
641 64 : program_name = opt_program_name(argv[0], PACKAGE);
642 64 : httpd_opts->s->name_cstr = program_name;
643 64 : httpd_opts->s->name_len = strlen(program_name);
644 :
645 64 : httpd_opts->s->make_policy = httpd_policy_make;
646 64 : httpd_opts->s->copy_policy = httpd_policy_copy;
647 :
648 64 : if (!httpd_conf_main_init(httpd_opts))
649 0 : errno = ENOMEM, err(EXIT_FAILURE, "options");
650 :
651 64 : if (!geteuid()) /* If root */
652 0 : httpd_opts->s->addr_beg->tcp_port = HTTPD_CONF_SERV_DEF_PORT;
653 : else /* somewhat common unprived port */
654 64 : httpd_opts->s->addr_beg->tcp_port = 8008;
655 :
656 64 : popts = (Httpd_policy_opts *)httpd_opts->s->def_policy;
657 434 : while ((optchar = getopt_long(argc, argv, "C:dhH:M:nP:t:V",
658 : long_options, NULL)) != -1)
659 : {
660 330 : switch (optchar)
661 : {
662 0 : case '?': usage(program_name, EXIT_FAILURE, "");
663 4 : case 'h': usage(program_name, EXIT_SUCCESS, "");
664 :
665 : case 'V':
666 4 : vstr_add_fmt(out, 0, " %s version %s.\n", program_name, VERSION);
667 :
668 4 : if (io_put_all(out, STDOUT_FILENO) == IO_FAIL)
669 0 : err(EXIT_FAILURE, "write");
670 :
671 4 : exit (EXIT_SUCCESS);
672 :
673 3 : OPT_SERV_GETOPTS(httpd_opts->s);
674 :
675 : case 'C':
676 30 : if (!httpd_conf_main_parse_file(out, httpd_opts, optarg))
677 4 : goto out_err_conf_msg;
678 13 : break;
679 : case 140:
680 6 : if (!opt_serv_sc_config_dir(out, httpd_opts, optarg,
681 : httpd_sc_conf_main_parse_dir_file))
682 4 : goto out_err_conf_msg;
683 1 : break;
684 : case 143:
685 32 : if (!opt_serv_conf_parse_cstr(out, httpd_opts->s, optarg))
686 4 : goto out_err_conf_msg;
687 14 : break;
688 : case 144:
689 46 : if (!httpd_conf_main_parse_cstr(out, httpd_opts, optarg))
690 4 : goto out_err_conf_msg;
691 21 : break;
692 : case 149:
693 0 : if (!serv_def_conf(out))
694 0 : goto out_err_conf_msg;
695 0 : break;
696 :
697 2 : case 128: POPT_NUM_NR_ARG(max_header_sz, "max header size"); break;
698 :
699 6 : case 31: POPT_TOGGLE_ARG(use_sendfile); break;
700 6 : case 30: POPT_TOGGLE_ARG(use_mmap); break;
701 :
702 2 : case 129: POPT_TOGGLE_ARG(use_keep_alive); break;
703 0 : case 130: POPT_TOGGLE_ARG(use_keep_alive_1_0); break;
704 10 : case 131: POPT_TOGGLE_ARG(use_vhosts_name); break;
705 2 : case 132: POPT_TOGGLE_ARG(use_range); break;
706 0 : case 133: POPT_TOGGLE_ARG(use_range_1_0); break;
707 2 : case 134: POPT_TOGGLE_ARG(use_public_only); break;
708 2 : case 135: POPT_VSTR_ARG(dir_filename); break;
709 2 : case 136: POPT_VSTR_ARG(server_name); break;
710 2 : case 137: POPT_TOGGLE_ARG(use_enc_content_replacement); break;
711 2 : case 139: POPT_TOGGLE_ARG(use_err_406); break;
712 : /* case 140: */
713 42 : case 141: POPT_VSTR_ARG(mime_types_main); break;
714 12 : case 142: POPT_VSTR_ARG(mime_types_xtra); break;
715 : /* case 143: */
716 : /* case 144: */
717 10 : case 145: POPT_VSTR_ARG(default_hostname); break;
718 2 : case 146: POPT_TOGGLE_ARG(use_canonize_host); break;
719 2 : case 147: POPT_TOGGLE_ARG(use_host_err_400); break;
720 0 : case 148: POPT_TOGGLE_ARG(use_host_chk);
721 :
722 :
723 0 : ASSERT_NO_SWITCH_DEF();
724 : }
725 : }
726 :
727 20 : ASSERT(optind >= 1);
728 40 : if (optind <= 1) /* just program name, no options so use default... */
729 0 : if (!serv_def_conf(out))
730 0 : goto out_err_conf_msg;
731 40 : vstr_free_base(out); out = NULL;
732 :
733 40 : argc -= optind;
734 40 : argv += optind;
735 :
736 40 : if (argc > 1)
737 0 : usage(program_name, EXIT_FAILURE, " Too many arguments.\n");
738 :
739 40 : if (argc == 1)
740 : {
741 40 : Vstr_base *tmp = popts->document_root;
742 40 : vstr_sub_cstr_ptr(tmp, 1, tmp->len, argv[0]);
743 : }
744 :
745 40 : if (!popts->document_root->len)
746 0 : usage(program_name, EXIT_FAILURE, " Not specified a document root.\n");
747 :
748 : {
749 40 : const char *pid_file = NULL;
750 40 : Opt_serv_opts *opts = httpd_opts->s;
751 :
752 40 : vlg_syslog_facility_set(vlg, opts->syslog_facility);
753 : /* vlg_date_set(vlg, opts->vlg_date_fmt_type); */
754 40 : if (!opts->vlg_tweaked_size)
755 : {
756 40 : if (opts->become_daemon)
757 0 : opts->vlg_size = OPT_SERV_CONF_VLG_SIZE_UNTWEAKED_DAEMON;
758 : else
759 40 : opts->vlg_size = OPT_SERV_CONF_VLG_SIZE_UNTWEAKED_CONSOLE;
760 : }
761 40 : vlg_size_set(vlg, opts->vlg_size);
762 40 : vlg_syslog_native_set(vlg, opts->vlg_syslog_native);
763 :
764 40 : OPT_SC_EXPORT_CSTR(pid_file, opts->pid_file, FALSE, "pid file");
765 40 : OPT_SC_EXPORT_CSTR(chroot_dir, opts->chroot_dir, FALSE, "chroot directory");
766 :
767 40 : if (opts->drop_privs)
768 : {
769 0 : OPT_SERV_SC_RESOLVE_UID(opts);
770 0 : OPT_SERV_SC_RESOLVE_GID(opts);
771 : }
772 :
773 40 : if (!httpd_init_default_hostname(opts->def_policy))
774 0 : errno = ENOMEM, err(EXIT_FAILURE, "default_hostname");
775 :
776 40 : serv_make_bind(program_name);
777 :
778 40 : serv_mime_types(program_name);
779 :
780 40 : serv_canon_policies();
781 :
782 40 : OPT_SC_EXPORT_CSTR(document_root, popts->document_root, TRUE,
783 : "document root");
784 :
785 40 : if (opts->become_daemon)
786 : {
787 0 : if (daemon(FALSE, FALSE) == -1)
788 0 : err(EXIT_FAILURE, "daemon");
789 0 : vlg_daemon(vlg, program_name);
790 : }
791 :
792 : /* NOTE: after daemon so don't use err() anymore ... */
793 :
794 40 : if (httpd_opts->s->rlim_as_call)
795 0 : opt_serv_sc_rlim_as_num(httpd_opts->s->rlim_as_num);
796 40 : if (httpd_opts->s->rlim_core_call)
797 28 : opt_serv_sc_rlim_core_num(httpd_opts->s->rlim_core_num);
798 40 : if (httpd_opts->s->rlim_file_call)
799 0 : opt_serv_sc_rlim_file_num(httpd_opts->s->rlim_file_num);
800 :
801 40 : if (pid_file)
802 20 : vlg_pid_file(vlg, pid_file);
803 :
804 40 : if (opts->cntl_file->len)
805 40 : cntl_make_file(vlg, opts->cntl_file);
806 :
807 40 : if (chroot_dir)
808 : { /* preload locale info. so syslog can log in localtime, this doesn't work
809 : * with current glibc's as they re-stat ... maybe set the ENV somehow? */
810 0 : time_t now = time(NULL);
811 0 : (void)localtime(&now);
812 :
813 0 : vlg_sc_bind_mount(chroot_dir);
814 : }
815 :
816 40 : if (chroot_dir && ((chroot(chroot_dir) == -1) || (chdir("/") == -1)))
817 0 : vlg_err(vlg, EXIT_FAILURE, "chroot(%s): %m\n", chroot_dir);
818 :
819 40 : if (opts->drop_privs)
820 : {
821 0 : if (opts->keep_cap_fowner && (PROC_CNTL_KEEPCAPS(TRUE) == -1))
822 : {
823 0 : vlg_warn(vlg, "prctl(%s, %s): %m\n", "PR_SET_KEEPCAPS", "TRUE");
824 0 : opts->keep_cap_fowner = FALSE;
825 : }
826 :
827 0 : opt_serv_sc_drop_privs(opts);
828 :
829 0 : if (opts->keep_cap_fowner)
830 : { /* use + instead of = because that's how getpcaps prints it,
831 : * however they do the same due to no input */
832 0 : cap_t caps = cap_from_text("cap_fowner+ep-i");
833 :
834 0 : if (!caps)
835 0 : vlg_err(vlg, EXIT_FAILURE, "cap_from_text(%s): %m\n",
836 : "cap_fowner+pe-i");
837 :
838 0 : if (PROC_CNTL_KEEPCAPS(FALSE) == -1)
839 0 : vlg_err(vlg, EXIT_FAILURE,
840 : "prctl(%s, %s): %m\n", "PR_SET_KEEPCAPS", "FALSE");
841 :
842 0 : if (cap_set_proc(caps) == -1)
843 0 : vlg_err(vlg, EXIT_FAILURE, "cap_set_proc(%s): %m\n",
844 : "cap_fowner+ep-i");
845 0 : if (cap_free(caps) == -1)
846 0 : vlg_err(vlg, EXIT_FAILURE, "cap_free(): %m\n");
847 : }
848 : }
849 :
850 40 : if (opts->num_procs > 1)
851 : {
852 4 : int has_cntl = !!opts->cntl_file->len;
853 4 : cntl_sc_multiproc(vlg, opts->num_procs, has_cntl, opts->use_pdeathsig);
854 : }
855 :
856 44 : if (opts->make_dumpable && (PROC_CNTL_DUMPABLE(TRUE) == -1))
857 0 : vlg_warn(vlg, "prctl(%s, %s): %m\n", "PR_SET_DUMPABLE", "TRUE");
858 : }
859 :
860 44 : httpd_opts->beg_time = time(NULL);
861 :
862 : {
863 44 : struct Evnt *evnt = evnt_queue("accept");
864 :
865 254 : while (evnt)
866 : {
867 166 : vlg_info(vlg, "READY [$<sa:%p>]: %s%s%s\n", EVNT_SA(evnt),
868 : chroot_dir ? chroot_dir : "",
869 : chroot_dir ? "/" : "",
870 : document_root);
871 166 : evnt = evnt->next;
872 : }
873 : }
874 :
875 44 : opt_serv_conf_free_beg(httpd_opts->s);
876 : return;
877 :
878 16 : out_err_conf_msg:
879 16 : vstr_add_cstr_ptr(out, out->len, "\n");
880 16 : if (io_put_all(out, STDERR_FILENO) == IO_FAIL)
881 0 : err(EXIT_FAILURE, "write");
882 16 : exit (EXIT_FAILURE);
883 : }
884 :
885 : int main(int argc, char *argv[])
886 64 : {
887 : if (sizeof(uintmax_t) != sizeof(VSTR_AUTOCONF_uintmax_t))
888 : errx(EXIT_FAILURE, "uintmax_t size is different between program and Vstr");
889 :
890 32 : assert((F(M0(1, 1)), 1)); /* for coverage */
891 :
892 64 : serv_init();
893 :
894 64 : serv_cmd_line(argc, argv);
895 :
896 9176427 : while (evnt_waiting())
897 : {
898 9176339 : evnt_sc_main_loop(HTTPD_CONF_MAX_WAIT_SEND);
899 9176339 : opt_serv_sc_check_children();
900 9176339 : opt_serv_sc_cntl_resources(httpd_opts->s);
901 : }
902 44 : evnt_out_dbg3("E");
903 :
904 44 : evnt_timeout_exit();
905 44 : cntl_child_free();
906 44 : evnt_close_all();
907 :
908 44 : httpd_exit();
909 :
910 44 : http_req_exit();
911 :
912 44 : vlg_free(vlg);
913 44 : vlg_exit();
914 :
915 44 : httpd_conf_main_free(httpd_opts);
916 :
917 44 : timer_q_exit();
918 :
919 44 : vstr_exit();
920 :
921 22 : MALLOC_CHECK_EMPTY();
922 :
923 44 : exit (EXIT_SUCCESS);
924 : }
|