Require mLib 2.0.3 for connection stuff.
[fwd] / fw.c
1 /* -*-c-*-
2 *
3 * $Id: fw.c,v 1.16 2003/11/29 20:36:07 mdw Exp $
4 *
5 * Port forwarding thingy
6 *
7 * (c) 1999 Straylight/Edgeware
8 */
9
10 /*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of the `fw' port forwarder.
13 *
14 * `fw' is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * `fw' is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with `fw'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29 /*----- Revision history --------------------------------------------------*
30 *
31 * $Log: fw.c,v $
32 * Revision 1.16 2003/11/29 20:36:07 mdw
33 * Privileged outgoing connections.
34 *
35 * Revision 1.15 2003/11/25 14:46:50 mdw
36 * Update docco for new options.
37 *
38 * Revision 1.14 2003/01/24 20:12:40 mdw
39 * Correctly cast uid and gid sentinel values.
40 *
41 * Revision 1.13 2002/02/22 23:45:20 mdw
42 * Add option to change the listen(2) parameter. Receive `fw'-specific
43 * code from `conf.c'.
44 *
45 * Revision 1.12 2002/01/13 14:49:17 mdw
46 * Track @dstr_vputf@ change.
47 *
48 * Revision 1.11 2001/02/03 20:33:26 mdw
49 * Fix flags to be unsigned.
50 *
51 * Revision 1.10 2001/02/03 20:30:03 mdw
52 * Support re-reading config files on SIGHUP.
53 *
54 * Revision 1.9 2001/01/20 11:55:17 mdw
55 * Handle select errors more robustly.
56 *
57 * Revision 1.8 2000/03/23 23:19:19 mdw
58 * Fix changed options in parser table.
59 *
60 * Revision 1.7 2000/03/23 00:37:33 mdw
61 * Add option to change user and group after initialization. Naughtily
62 * reassign short equivalents of --grammar and --options.
63 *
64 * Revision 1.6 1999/12/22 15:44:10 mdw
65 * Make syslog a separate option, and do it better.
66 *
67 * Revision 1.5 1999/10/22 22:47:50 mdw
68 * Grammar changes. Also, don't enable SIGINT if it's currently ignored.
69 *
70 * Revision 1.4 1999/10/10 16:46:12 mdw
71 * New resolver to initialize. Also, include options for grammar and
72 * options references.
73 *
74 * Revision 1.3 1999/07/26 23:30:42 mdw
75 * Major reconstruction work for new design.
76 *
77 * Revision 1.2 1999/07/03 13:55:17 mdw
78 * Various changes. Add configuration grammar to help text. Change to
79 * root directory and open syslog when forking into background.
80 *
81 * Revision 1.1.1.1 1999/07/01 08:56:23 mdw
82 * Initial revision.
83 *
84 */
85
86 /*----- Header files ------------------------------------------------------*/
87
88 #include "config.h"
89
90 #include <assert.h>
91 #include <ctype.h>
92 #include <errno.h>
93 #include <signal.h>
94 #include <stdarg.h>
95 #include <stdio.h>
96 #include <stdlib.h>
97 #include <string.h>
98 #include <time.h>
99
100 #include <unistd.h>
101 #include <syslog.h>
102
103 #include <grp.h>
104 #include <pwd.h>
105
106 #include <mLib/bres.h>
107 #include <mLib/dstr.h>
108 #include <mLib/mdwopt.h>
109 #include <mLib/quis.h>
110 #include <mLib/report.h>
111 #include <mLib/sel.h>
112 #include <mLib/sig.h>
113 #include <mLib/sub.h>
114
115 #include "conf.h"
116 #include "endpt.h"
117 #include "exec.h"
118 #include "fattr.h"
119 #include "file.h"
120 #include "fw.h"
121 #include "privconn.h"
122 #include "scan.h"
123 #include "socket.h"
124 #include "source.h"
125
126 /*----- Global variables --------------------------------------------------*/
127
128 sel_state *sel; /* Multiplexor for nonblocking I/O */
129
130 /*----- Static variables --------------------------------------------------*/
131
132 typedef struct conffile {
133 struct conffile *next;
134 char *name;
135 } conffile;
136
137 static unsigned flags = 0; /* Global state flags */
138 static unsigned active = 0; /* Number of active things */
139 static conffile *conffiles = 0; /* List of configuration files */
140
141 #define FW_SYSLOG 1u
142 #define FW_QUIET 2u
143 #define FW_SET 4u
144
145 /*----- Configuration parsing ---------------------------------------------*/
146
147 /* --- @parse@ --- *
148 *
149 * Arguments: @scanner *sc@ = pointer to scanner definition
150 *
151 * Returns: ---
152 *
153 * Use: Parses a configuration file from the scanner.
154 */
155
156 static source_ops *sources[] =
157 { &xsource_ops, &fsource_ops, &ssource_ops, 0 };
158 static target_ops *targets[] =
159 { &xtarget_ops, &ftarget_ops, &starget_ops, 0 };
160
161 void parse(scanner *sc)
162 {
163 token(sc);
164
165 for (;;) {
166 if (sc->t == CTOK_EOF)
167 break;
168 if (sc->t != CTOK_WORD)
169 error(sc, "parse error, keyword expected");
170
171 /* --- Handle a forwarding request --- */
172
173 if (strcmp(sc->d.buf, "forward") == 0 ||
174 strcmp(sc->d.buf, "fw") == 0 ||
175 strcmp(sc->d.buf, "from") == 0) {
176 source *s;
177 target *t;
178
179 token(sc);
180
181 /* --- Read a source description --- */
182
183 {
184 source_ops **sops;
185
186 /* --- Try to find a source type which understands --- */
187
188 s = 0;
189 for (sops = sources; *sops; sops++) {
190 if ((s = (*sops)->read(sc)) != 0)
191 goto found_source;
192 }
193 error(sc, "unknown source name `%s'", sc->d.buf);
194
195 /* --- Read any source-specific options --- */
196
197 found_source:
198 if (sc->t == '{') {
199 token(sc);
200 while (sc->t == CTOK_WORD) {
201 if (!s->ops->option || !s->ops->option(s, sc)) {
202 error(sc, "unknown %s source option `%s'",
203 s->ops->name, sc->d.buf);
204 }
205 if (sc->t == ';')
206 token(sc);
207 }
208 if (sc->t != '}')
209 error(sc, "parse error, missing `}'");
210 token(sc);
211 }
212 }
213
214 /* --- Read a destination description --- */
215
216 if (sc->t == CTOK_WORD && (strcmp(sc->d.buf, "to") == 0 ||
217 strcmp(sc->d.buf, "->") == 0))
218 token(sc);
219
220 {
221 target_ops **tops;
222
223 /* --- Try to find a target which understands --- */
224
225 t = 0;
226 for (tops = targets; *tops; tops++) {
227 if ((t = (*tops)->read(sc)) != 0)
228 goto found_target;
229 }
230 error(sc, "unknown target name `%s'", sc->d.buf);
231
232 /* --- Read any target-specific options --- */
233
234 found_target:
235 if (sc->t == '{') {
236 token(sc);
237 while (sc->t == CTOK_WORD) {
238 if (!t->ops->option || !t->ops->option(t, sc)) {
239 error(sc, "unknown %s target option `%s'",
240 t->ops->name, sc->d.buf);
241 }
242 if (sc->t == ';')
243 token(sc);
244 }
245 if (sc->t != '}')
246 error(sc, "parse error, `}' expected");
247 token(sc);
248 }
249 }
250
251 /* --- Combine the source and target --- */
252
253 s->ops->attach(s, sc, t);
254 if (t->ops->confirm)
255 t->ops->confirm(t);
256 }
257
258 /* --- Include configuration from a file --- *
259 *
260 * Slightly tricky. Scan the optional semicolon from the including
261 * stream, not the included one.
262 */
263
264 else if (strcmp(sc->d.buf, "include") == 0) {
265 FILE *fp;
266 dstr d = DSTR_INIT;
267
268 token(sc);
269 conf_name(sc, '/', &d);
270 if ((fp = fopen(d.buf, "r")) == 0)
271 error(sc, "can't include `%s': %s", d.buf, strerror(errno));
272 if (sc->t == ';')
273 token(sc);
274 pushback(sc);
275 scan_push(sc, scan_file(fp, d.buf, 0));
276 token(sc);
277 dstr_destroy(&d);
278 continue; /* Don't parse a trailing `;' */
279 }
280
281 /* --- Other configuration is handled elsewhere --- */
282
283 else {
284
285 /* --- First try among the sources --- */
286
287 {
288 source_ops **sops;
289
290 for (sops = sources; *sops; sops++) {
291 if ((*sops)->option && (*sops)->option(0, sc))
292 goto found_option;
293 }
294 }
295
296 /* --- Then try among the targets --- */
297
298 {
299 target_ops **tops;
300
301 for (tops = targets; *tops; tops++) {
302 if ((*tops)->option && (*tops)->option(0, sc))
303 goto found_option;
304 }
305 }
306
307 /* --- Nobody wants the option --- */
308
309 error(sc, "unknown global option or prefix `%s'", sc->d.buf);
310
311 found_option:;
312 }
313
314 if (sc->t == ';')
315 token(sc);
316 }
317 }
318
319 /*----- General utility functions -----------------------------------------*/
320
321 /* --- @fw_log@ --- *
322 *
323 * Arguments: @time_t t@ = when the connection occurred or (@-1@)
324 * @const char *fmt@ = format string to fill in
325 * @...@ = other arguments
326 *
327 * Returns: ---
328 *
329 * Use: Logs a connection.
330 */
331
332 void fw_log(time_t t, const char *fmt, ...)
333 {
334 struct tm *tm;
335 dstr d = DSTR_INIT;
336 va_list ap;
337
338 if (flags & FW_QUIET)
339 return;
340
341 if (t == -1)
342 t = time(0);
343 tm = localtime(&t);
344 DENSURE(&d, 64);
345 d.len += strftime(d.buf, d.sz, "%Y-%m-%d %H:%M:%S ", tm);
346 va_start(ap, fmt);
347 dstr_vputf(&d, fmt, &ap);
348 va_end(ap);
349 if (flags & FW_SYSLOG)
350 syslog(LOG_NOTICE, "%s", d.buf);
351 else {
352 DPUTC(&d, '\n');
353 dstr_write(&d, stderr);
354 }
355 DDESTROY(&d);
356 }
357
358 /* --- @fw_inc@, @fw_dec@ --- *
359 *
360 * Arguments: ---
361 *
362 * Returns: ---
363 *
364 * Use: Increments or decrements the active thing count. `fw' won't
365 * quit while there are active things.
366 */
367
368 void fw_inc(void) { flags |= FW_SET; active++; }
369 void fw_dec(void) { if (active) active--; }
370
371 /* --- @fw_exit@ --- *
372 *
373 * Arguments: ---
374 *
375 * Returns: ---
376 *
377 * Use: Exits when appropriate.
378 */
379
380 static void fw_exit(void)
381 {
382 endpt_killall();
383 source_killall();
384 }
385
386 /* --- @fw_tidy@ --- *
387 *
388 * Arguments: @int n@ = signal number
389 * @void *p@ = an uninteresting argument
390 *
391 * Returns: ---
392 *
393 * Use: Handles various signals and causes a clean tidy-up.
394 */
395
396 static void fw_tidy(int n, void *p)
397 {
398 const char *sn = 0;
399 switch (n) {
400 case SIGTERM: sn = "SIGTERM"; break;
401 case SIGINT: sn = "SIGINT"; break;
402 default: abort();
403 }
404
405 fw_log(-1, "closing down gracefully on %s", sn);
406 source_killall();
407 }
408
409 /* --- @fw_die@ --- *
410 *
411 * Arguments: @int n@ = signal number
412 * @void *p@ = an uninteresting argument
413 *
414 * Returns: ---
415 *
416 * Use: Handles various signals and causes an abrupt shutdown.
417 */
418
419 static void fw_die(int n, void *p)
420 {
421 const char *sn = 0;
422 switch (n) {
423 case SIGQUIT: sn = "SIGQUIT"; break;
424 default: abort();
425 }
426
427 fw_log(-1, "closing down abruptly on %s", sn);
428 source_killall();
429 endpt_killall();
430 }
431
432 /* --- @fw_reload@ --- *
433 *
434 * Arguments: @int n@ = a signal number
435 * @void *p@ = an uninteresting argument
436 *
437 * Returns: ---
438 *
439 * Use: Handles a hangup signal by re-reading configuration files.
440 */
441
442 static void fw_reload(int n, void *p)
443 {
444 FILE *fp;
445 scanner sc;
446 conffile *cf;
447
448 assert(n == SIGHUP);
449 if (!conffiles) {
450 fw_log(-1, "no configuration files to reload: ignoring SIGHUP");
451 return;
452 }
453 fw_log(-1, "reloading configuration files...");
454 source_killall();
455 scan_create(&sc);
456 for (cf = conffiles; cf; cf = cf->next) {
457 if ((fp = fopen(cf->name, "r")) == 0)
458 fw_log(-1, "error loading `%s': %s", cf->name, strerror(errno));
459 else
460 scan_add(&sc, scan_file(fp, cf->name, 0));
461 }
462 parse(&sc);
463 fw_log(-1, "... reload completed OK");
464 }
465
466 /*----- Startup and options parsing ---------------------------------------*/
467
468 /* --- Standard GNU help options --- */
469
470 static void version(FILE *fp)
471 {
472 pquis(fp, "$ version " VERSION "\n");
473 }
474
475 static void usage(FILE *fp)
476 {
477 pquis(fp, "Usage: $ [-dql] [-f file] [config statements...]\n");
478 }
479
480 static void help(FILE *fp)
481 {
482 version(fp);
483 fputc('\n', fp);
484 usage(fp);
485 pquis(fp, "\n\
486 An excessively full-featured port-forwarder, which subsumes large chunks\n\
487 of the functionality of inetd, netcat, and normal cat. Options available\n\
488 are:\n\
489 \n\
490 -h, --help Display this help message.\n\
491 -v, --version Display the program's version number.\n\
492 -u, --usage Display a terse usage summary.\n\
493 \n\
494 -G, --grammar Show a summary of the configuration language.\n\
495 -O, --options Show a summary of the source and target options.\n\
496 \n\
497 -f, --file=FILE Read configuration from a file.\n\
498 -q, --quiet Don't emit any logging information.\n\
499 -d, --daemon Fork into background after initializing.\n\
500 -l, --syslog Send log output to the system logger.\n\
501 -s, --setuid=USER Change uid to USER after initializing sources.\n\
502 -g, --setgid=GRP Change gid to GRP after initializing sources.\n\
503 \n\
504 Configuration may be supplied in one or more configuration files, or on\n\
505 the command line (or both). If no `-f' option is present, and no\n\
506 configuration is given on the command line, the standard input stream is\n\
507 read.\n\
508 \n\
509 Configuration is free-form. Comments begin with a `#' character and\n\
510 continue to the end of the line. Each command line argument is considered\n\
511 to be a separate line.\n\
512 \n\
513 The grammar is fairly complicated. For a summary, run `$ --grammar'.\n\
514 For a summary of the various options, run `$ --options'.\n\
515 ");
516 }
517
518 /* --- Other helpful options --- */
519
520 static void grammar(FILE *fp)
521 {
522 version(fp);
523 pquis(fp, "\n\
524 Grammar summary\n\
525 \n\
526 Basic syntax\n\
527 FILE ::= EMPTY | FILE STMT [`;']\n\
528 STMT ::= OPTION-STMT | FW-STMT\n\
529 FW-STMT ::= `fw' SOURCE OPTIONS [`to'|`->'] TARGET OPTIONS\n\
530 OPTIONS ::= `{' OPTION-SEQ `}'\n\
531 OPTION-SEQ ::= EMPTY | OPTION-STMT [`;'] OPTION-SEQ\n\
532 \n\
533 Option syntax\n\
534 OPTION-STMT ::= Q-OPTION\n\
535 Q-OPTION ::= OPTION\n\
536 | PREFIX `.' Q-OPTION\n\
537 | PREFIX `{' OPTION-SEQ `}'\n\
538 PREFIX ::= WORD\n\
539 \n\
540 File source and target\n\
541 SOURCE ::= FILE\n\
542 TARGET ::= FILE\n\
543 FILE ::= `file' [`.'] FSPEC [`,' FSPEC]\n\
544 FSPEC ::= FD-SPEC | NAME-SPEC | NULL-SPEC\n\
545 FD-SPEC ::= [[`:']`fd'[`:']] NUMBER|`stdin'|`stdout'\n\
546 NAME-SPEC ::= [[`:']`file'[`:']] FILE-NAME\n\
547 FILE-NAME ::= PATH-SEQ | [ PATH-SEQ ]\n\
548 PATH-SEQ ::= PATH-ELT | PATH-SEQ PATH-ELT\n\
549 PATH-ELT ::= `/' | WORD\n\
550 NULL-SPEC ::= [`:']`null'[`:']\n\
551 \n\
552 Exec source and target\n\
553 SOURCE ::= EXEC\n\
554 TARGET ::= EXEC\n\
555 EXEC ::= `exec' [`.'] CMD-SPEC\n\
556 CMD-SPEC ::= SHELL-CMD | [PROG-NAME] `[' ARGV0 ARG-SEQ `]'\n\
557 ARG-SEQ ::= WORD | ARG-SEQ WORD\n\
558 SHELL-CMD ::= WORD\n\
559 ARGV0 ::= WORD\n\
560 \n\
561 Socket source and target\n\
562 SOURCE ::= SOCKET-SOURCE\n\
563 TARGET ::= SOCKET-TARGET\n\
564 SOCKET-SOURCE ::= [`socket'[`.']] [[`:']ADDR-TYPE[`:']] SOURCE-ADDR\n\
565 SOCKET-TARGET ::= [`socket'[`.']] [[`:']ADDR-TYPE[`:']] TARGET-ADDR\n\
566 \n\
567 INET-SOURCE-ADDR ::= [`port'] PORT\n\
568 INET-TARGET-ADDR ::= ADDRESS [`:'] PORT\n\
569 ADDRESS ::= ADDR-ELT | ADDRESS ADDR-ELT\n\
570 ADDR-ELT ::= `.' | WORD\n\
571 \n\
572 UNIX-SOURCE-ADDR ::= FILE-NAME\n\
573 UNIX-TARGET-ADDR ::= FILE-NAME\n\
574 ");
575 }
576
577 static void options(FILE *fp)
578 {
579 version(fp);
580 pquis(fp, "\n\
581 Options summary\n\
582 \n\
583 File attributes (`fattr')\n\
584 prefix.FATTR.MODE [=] MODE\n\
585 prefix.FATTR.OWNER [=] USER\n\
586 prefix.FATTR.GROUP [=] GROUP\n\
587 \n\
588 File options\n\
589 file.create [=] yes|no\n\
590 file.open [=] no|truncate|append\n\
591 file.fattr.*\n\
592 \n\
593 Exec options\n\
594 exec.logging [=] yes|no\n\
595 exec.dir [=] FILE-NAME\n\
596 exec.root [=] FILE-NAME\n\
597 exec.user [=] USER\n\
598 exec.group [=] GROUP\n\
599 exec.rlimit.LIMIT[.hard|.soft] [=] VALUE\n\
600 exec.env.clear\n\
601 exec.env.unset VAR\n\
602 exec.env.[set] VAR [=] VALUE\n\
603 \n\
604 Socket options\n\
605 socket.conn [=] NUMBER|unlimited|one-shot\n\
606 socket.listen [=] NUMBER\n\
607 socket.logging [=] yes|no\n\
608 \n\
609 socket.inet.source.[allow|deny] [host] ADDR [/ ADDR]\n\
610 socket.inet.source.[allow|deny] priv-port\n\
611 socket.inet.source.addr [=] any|ADDR\n\
612 socket.inet.dest.addr [=] any|ADDR\n\
613 socket.inet.dest.priv-port [=] yes|no\n\
614 \n\
615 socket.unix.fattr.*\n\
616 ");
617 }
618
619 /* --- @main@ --- *
620 *
621 * Arguments: @int argc@ = number of command line arguments
622 * @char *argv[]@ = vector of argument strings
623 *
624 * Returns: ---
625 *
626 * Use: Simple port-forwarding server.
627 */
628
629 int main(int argc, char *argv[])
630 {
631 unsigned f = 0;
632 sel_state sst;
633 sig s_term, s_quit, s_int, s_hup;
634 scanner sc;
635 uid_t drop = -1;
636 gid_t dropg = -1;
637 conffile *cf, **cff = &conffiles;
638
639 #define f_bogus 1u
640 #define f_file 2u
641 #define f_syslog 4u
642 #define f_fork 8u
643
644 /* --- Initialize things --- */
645
646 ego(argv[0]);
647 sel = &sst;
648 sel_init(sel);
649 sub_init();
650 sig_init(sel);
651 bres_init(sel);
652 bres_exec(0);
653 exec_init();
654 fattr_init(&fattr_global);
655 scan_create(&sc);
656
657 atexit(fw_exit);
658
659 /* --- Parse command line options --- */
660
661 for (;;) {
662 static struct option opts[] = {
663
664 /* --- Standard GNU help options --- */
665
666 { "help", 0, 0, 'h' },
667 { "version", 0, 0, 'v' },
668 { "usage", 0, 0, 'u' },
669
670 /* --- Other help options --- */
671
672 { "grammar", 0, 0, 'G' },
673 { "options", 0, 0, 'O' },
674
675 /* --- Other useful arguments --- */
676
677 { "file", OPTF_ARGREQ, 0, 'f' },
678 { "fork", 0, 0, 'd' },
679 { "daemon", 0, 0, 'd' },
680 { "syslog", 0, 0, 'l' },
681 { "log", 0, 0, 'l' },
682 { "quiet", 0, 0, 'q' },
683 { "setuid", OPTF_ARGREQ, 0, 's' },
684 { "uid", OPTF_ARGREQ, 0, 's' },
685 { "setgid", OPTF_ARGREQ, 0, 'g' },
686 { "gid", OPTF_ARGREQ, 0, 'g' },
687
688 /* --- Magic terminator --- */
689
690 { 0, 0, 0, 0 }
691 };
692 int i = mdwopt(argc, argv, "+hvu GO f:dls:g:", opts, 0, 0, 0);
693
694 if (i < 0)
695 break;
696 switch (i) {
697 case 'h':
698 help(stdout);
699 exit(0);
700 break;
701 case 'v':
702 version(stdout);
703 exit(0);
704 break;
705 case 'u':
706 usage(stdout);
707 exit(0);
708 break;
709 case 'G':
710 grammar(stdout);
711 exit(0);
712 break;
713 case 'O':
714 options(stdout);
715 exit(0);
716 break;
717 case 'f':
718 if (strcmp(optarg, "-") == 0)
719 scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
720 else {
721 FILE *fp;
722 if ((fp = fopen(optarg, "r")) == 0)
723 die(1, "couldn't open file `%s': %s", optarg, strerror(errno));
724 cf = CREATE(conffile);
725 cf->name = optarg;
726 *cff = cf;
727 cff = &cf->next;
728 scan_add(&sc, scan_file(fp, optarg, 0));
729 }
730 f |= f_file;
731 break;
732 case 'd':
733 f |= f_fork;
734 break;
735 case 'l':
736 f |= f_syslog;
737 break;
738 case 'q':
739 flags |= FW_QUIET;
740 break;
741 case 's':
742 if (isdigit((unsigned char )optarg[0])) {
743 char *q;
744 drop = strtol(optarg, &q, 0);
745 if (*q)
746 die(1, "bad uid `%s'", optarg);
747 } else {
748 struct passwd *pw = getpwnam(optarg);
749 if (!pw)
750 die(1, "unknown user `%s'", optarg);
751 drop = pw->pw_uid;
752 }
753 break;
754 case 'g':
755 if (isdigit((unsigned char )optarg[0])) {
756 char *q;
757 dropg = strtol(optarg, &q, 0);
758 if (*q)
759 die(1, "bad gid `%s'", optarg);
760 } else {
761 struct group *gr = getgrnam(optarg);
762 if (!gr)
763 die(1, "unknown group `%s'", optarg);
764 dropg = gr->gr_gid;
765 }
766 break;
767 default:
768 f |= f_bogus;
769 break;
770 }
771 }
772
773 if (f & f_bogus) {
774 usage(stderr);
775 exit(1);
776 }
777 *cff = 0;
778
779 /* --- Deal with the remaining arguments --- */
780
781 if (optind < argc)
782 scan_add(&sc, scan_argv(argv + optind));
783 else if (f & f_file)
784 /* Cool */;
785 else if (!isatty(STDIN_FILENO))
786 scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
787 else {
788 moan("no configuration given and stdin is a terminal.");
789 moan("type `%s --help' for usage information.", QUIS);
790 exit(1);
791 }
792
793 /* --- Parse the configuration now gathered --- */
794
795 parse(&sc);
796
797 /* --- Set up some signal handlers --- *
798 *
799 * Don't enable @SIGINT@ if the caller already disabled it.
800 */
801
802 {
803 struct sigaction sa;
804
805 sig_add(&s_term, SIGTERM, fw_tidy, 0);
806 sig_add(&s_quit, SIGQUIT, fw_die, 0);
807 sigaction(SIGINT, 0, &sa);
808 if (sa.sa_handler != SIG_IGN)
809 sig_add(&s_int, SIGINT, fw_tidy, 0);
810 sig_add(&s_hup, SIGHUP, fw_reload, 0);
811 }
812
813 /* --- Drop privileges --- */
814
815 if (drop != (uid_t)-1)
816 privconn_split(sel);
817 #ifdef HAVE_SETGROUPS
818 if ((dropg != (gid_t)-1 && (setgid(dropg) || setgroups(1, &dropg))) ||
819 (drop != (uid_t)-1 && setuid(drop)))
820 die(1, "couldn't drop privileges: %s", strerror(errno));
821 #else
822 if ((dropg != (gid_t)-1 && setgid(dropg)) ||
823 (drop != (uid_t)-1 && setuid(drop)))
824 die(1, "couldn't drop privileges: %s", strerror(errno));
825 #endif
826
827 /* --- Fork into the background --- */
828
829 if (f & f_fork) {
830 pid_t kid;
831
832 kid = fork();
833 if (kid == -1)
834 die(1, "couldn't fork: %s", strerror(errno));
835 if (kid != 0)
836 _exit(0);
837
838 close(0); close(1); close(2);
839 chdir("/");
840 setsid();
841
842 kid = fork();
843 if (kid != 0)
844 _exit(0);
845 }
846
847 if (f & f_syslog) {
848 flags |= FW_SYSLOG;
849 openlog(QUIS, 0, LOG_DAEMON);
850 }
851
852 /* --- Let rip --- */
853
854 if (!(flags & FW_SET))
855 moan("nothing to do!");
856 signal(SIGPIPE, SIG_IGN);
857
858 {
859 int selerr = 0;
860 while (active) {
861 if (!sel_select(sel))
862 selerr = 0;
863 else if (errno != EINTR && errno != EAGAIN) {
864 fw_log(-1, "error from select: %s", strerror(errno));
865 selerr++;
866 if (selerr > 8) {
867 fw_log(-1, "too many consecutive select errors: bailing out");
868 exit(EXIT_FAILURE);
869 }
870 }
871 }
872 }
873
874 return (0);
875 }
876
877 /*----- That's all, folks -------------------------------------------------*/