Commit | Line | Data |
---|---|---|
e82f7154 | 1 | /* -*-c-*- |
2 | * | |
b69a615b | 3 | * $Id$ |
e82f7154 | 4 | * |
5 | * Port forwarding thingy | |
6 | * | |
61e3dbdf | 7 | * (c) 1999 Straylight/Edgeware |
e82f7154 | 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 | ||
e82f7154 | 29 | /*----- Header files ------------------------------------------------------*/ |
30 | ||
31 | #include "config.h" | |
32 | ||
372a98e2 | 33 | #include <assert.h> |
e82f7154 | 34 | #include <ctype.h> |
35 | #include <errno.h> | |
61e3dbdf | 36 | #include <signal.h> |
37 | #include <stdarg.h> | |
e82f7154 | 38 | #include <stdio.h> |
39 | #include <stdlib.h> | |
40 | #include <string.h> | |
61e3dbdf | 41 | #include <time.h> |
e82f7154 | 42 | |
43 | #include <unistd.h> | |
44 | #include <syslog.h> | |
45 | ||
fc170a33 | 46 | #include <grp.h> |
47 | #include <pwd.h> | |
48 | ||
2d9ec601 | 49 | #include <mLib/bres.h> |
61e3dbdf | 50 | #include <mLib/dstr.h> |
e82f7154 | 51 | #include <mLib/mdwopt.h> |
52 | #include <mLib/quis.h> | |
53 | #include <mLib/report.h> | |
54 | #include <mLib/sel.h> | |
61e3dbdf | 55 | #include <mLib/sig.h> |
e82f7154 | 56 | #include <mLib/sub.h> |
57 | ||
e82f7154 | 58 | #include "conf.h" |
61e3dbdf | 59 | #include "endpt.h" |
60 | #include "exec.h" | |
61 | #include "fattr.h" | |
8c0a939e | 62 | #include "file.h" |
afd7451e | 63 | #include "fw.h" |
8cf7c7c2 | 64 | #include "mantext.h" |
ee599f55 | 65 | #include "privconn.h" |
e82f7154 | 66 | #include "scan.h" |
8c0a939e | 67 | #include "socket.h" |
61e3dbdf | 68 | #include "source.h" |
e82f7154 | 69 | |
afd7451e | 70 | /*----- Global variables --------------------------------------------------*/ |
e82f7154 | 71 | |
72 | sel_state *sel; /* Multiplexor for nonblocking I/O */ | |
61e3dbdf | 73 | |
74 | /*----- Static variables --------------------------------------------------*/ | |
75 | ||
372a98e2 | 76 | typedef struct conffile { |
77 | struct conffile *next; | |
78 | char *name; | |
79 | } conffile; | |
80 | ||
61e3dbdf | 81 | static unsigned flags = 0; /* Global state flags */ |
82 | static unsigned active = 0; /* Number of active things */ | |
372a98e2 | 83 | static conffile *conffiles = 0; /* List of configuration files */ |
61e3dbdf | 84 | |
85 | #define FW_SYSLOG 1u | |
86 | #define FW_QUIET 2u | |
87 | #define FW_SET 4u | |
e82f7154 | 88 | |
8c0a939e | 89 | /*----- Configuration parsing ---------------------------------------------*/ |
90 | ||
91 | /* --- @parse@ --- * | |
92 | * | |
93 | * Arguments: @scanner *sc@ = pointer to scanner definition | |
94 | * | |
95 | * Returns: --- | |
96 | * | |
97 | * Use: Parses a configuration file from the scanner. | |
98 | */ | |
99 | ||
100 | static source_ops *sources[] = | |
101 | { &xsource_ops, &fsource_ops, &ssource_ops, 0 }; | |
102 | static target_ops *targets[] = | |
103 | { &xtarget_ops, &ftarget_ops, &starget_ops, 0 }; | |
104 | ||
105 | void parse(scanner *sc) | |
106 | { | |
107 | token(sc); | |
108 | ||
109 | for (;;) { | |
110 | if (sc->t == CTOK_EOF) | |
111 | break; | |
112 | if (sc->t != CTOK_WORD) | |
113 | error(sc, "parse error, keyword expected"); | |
114 | ||
115 | /* --- Handle a forwarding request --- */ | |
116 | ||
117 | if (strcmp(sc->d.buf, "forward") == 0 || | |
118 | strcmp(sc->d.buf, "fw") == 0 || | |
119 | strcmp(sc->d.buf, "from") == 0) { | |
120 | source *s; | |
121 | target *t; | |
122 | ||
123 | token(sc); | |
124 | ||
125 | /* --- Read a source description --- */ | |
126 | ||
127 | { | |
128 | source_ops **sops; | |
129 | ||
130 | /* --- Try to find a source type which understands --- */ | |
131 | ||
132 | s = 0; | |
133 | for (sops = sources; *sops; sops++) { | |
134 | if ((s = (*sops)->read(sc)) != 0) | |
135 | goto found_source; | |
136 | } | |
137 | error(sc, "unknown source name `%s'", sc->d.buf); | |
138 | ||
139 | /* --- Read any source-specific options --- */ | |
140 | ||
141 | found_source: | |
142 | if (sc->t == '{') { | |
143 | token(sc); | |
144 | while (sc->t == CTOK_WORD) { | |
145 | if (!s->ops->option || !s->ops->option(s, sc)) { | |
146 | error(sc, "unknown %s source option `%s'", | |
147 | s->ops->name, sc->d.buf); | |
148 | } | |
149 | if (sc->t == ';') | |
150 | token(sc); | |
151 | } | |
152 | if (sc->t != '}') | |
153 | error(sc, "parse error, missing `}'"); | |
154 | token(sc); | |
155 | } | |
156 | } | |
157 | ||
158 | /* --- Read a destination description --- */ | |
159 | ||
160 | if (sc->t == CTOK_WORD && (strcmp(sc->d.buf, "to") == 0 || | |
161 | strcmp(sc->d.buf, "->") == 0)) | |
162 | token(sc); | |
163 | ||
164 | { | |
165 | target_ops **tops; | |
166 | ||
167 | /* --- Try to find a target which understands --- */ | |
168 | ||
169 | t = 0; | |
170 | for (tops = targets; *tops; tops++) { | |
171 | if ((t = (*tops)->read(sc)) != 0) | |
172 | goto found_target; | |
173 | } | |
174 | error(sc, "unknown target name `%s'", sc->d.buf); | |
175 | ||
176 | /* --- Read any target-specific options --- */ | |
177 | ||
178 | found_target: | |
179 | if (sc->t == '{') { | |
180 | token(sc); | |
181 | while (sc->t == CTOK_WORD) { | |
182 | if (!t->ops->option || !t->ops->option(t, sc)) { | |
183 | error(sc, "unknown %s target option `%s'", | |
184 | t->ops->name, sc->d.buf); | |
185 | } | |
186 | if (sc->t == ';') | |
187 | token(sc); | |
188 | } | |
189 | if (sc->t != '}') | |
190 | error(sc, "parse error, `}' expected"); | |
191 | token(sc); | |
192 | } | |
193 | } | |
194 | ||
195 | /* --- Combine the source and target --- */ | |
196 | ||
197 | s->ops->attach(s, sc, t); | |
ee599f55 | 198 | if (t->ops->confirm) |
199 | t->ops->confirm(t); | |
8c0a939e | 200 | } |
201 | ||
202 | /* --- Include configuration from a file --- * | |
203 | * | |
204 | * Slightly tricky. Scan the optional semicolon from the including | |
205 | * stream, not the included one. | |
206 | */ | |
207 | ||
208 | else if (strcmp(sc->d.buf, "include") == 0) { | |
209 | FILE *fp; | |
210 | dstr d = DSTR_INIT; | |
211 | ||
212 | token(sc); | |
213 | conf_name(sc, '/', &d); | |
214 | if ((fp = fopen(d.buf, "r")) == 0) | |
215 | error(sc, "can't include `%s': %s", d.buf, strerror(errno)); | |
216 | if (sc->t == ';') | |
217 | token(sc); | |
218 | pushback(sc); | |
219 | scan_push(sc, scan_file(fp, d.buf, 0)); | |
220 | token(sc); | |
221 | dstr_destroy(&d); | |
222 | continue; /* Don't parse a trailing `;' */ | |
223 | } | |
224 | ||
225 | /* --- Other configuration is handled elsewhere --- */ | |
226 | ||
227 | else { | |
228 | ||
229 | /* --- First try among the sources --- */ | |
230 | ||
231 | { | |
232 | source_ops **sops; | |
233 | ||
234 | for (sops = sources; *sops; sops++) { | |
235 | if ((*sops)->option && (*sops)->option(0, sc)) | |
236 | goto found_option; | |
237 | } | |
238 | } | |
239 | ||
240 | /* --- Then try among the targets --- */ | |
241 | ||
242 | { | |
243 | target_ops **tops; | |
244 | ||
245 | for (tops = targets; *tops; tops++) { | |
246 | if ((*tops)->option && (*tops)->option(0, sc)) | |
247 | goto found_option; | |
248 | } | |
249 | } | |
250 | ||
251 | /* --- Nobody wants the option --- */ | |
252 | ||
253 | error(sc, "unknown global option or prefix `%s'", sc->d.buf); | |
254 | ||
255 | found_option:; | |
256 | } | |
257 | ||
258 | if (sc->t == ';') | |
259 | token(sc); | |
260 | } | |
261 | } | |
262 | ||
263 | /*----- General utility functions -----------------------------------------*/ | |
e82f7154 | 264 | |
61e3dbdf | 265 | /* --- @fw_log@ --- * |
266 | * | |
267 | * Arguments: @time_t t@ = when the connection occurred or (@-1@) | |
268 | * @const char *fmt@ = format string to fill in | |
269 | * @...@ = other arguments | |
270 | * | |
271 | * Returns: --- | |
272 | * | |
273 | * Use: Logs a connection. | |
274 | */ | |
275 | ||
276 | void fw_log(time_t t, const char *fmt, ...) | |
277 | { | |
278 | struct tm *tm; | |
279 | dstr d = DSTR_INIT; | |
280 | va_list ap; | |
281 | ||
282 | if (flags & FW_QUIET) | |
283 | return; | |
284 | ||
285 | if (t == -1) | |
286 | t = time(0); | |
287 | tm = localtime(&t); | |
288 | DENSURE(&d, 64); | |
17be1d6b | 289 | d.len += strftime(d.buf, d.sz, "%Y-%m-%d %H:%M:%S ", tm); |
61e3dbdf | 290 | va_start(ap, fmt); |
2f8a65a1 | 291 | dstr_vputf(&d, fmt, &ap); |
61e3dbdf | 292 | va_end(ap); |
293 | if (flags & FW_SYSLOG) | |
294 | syslog(LOG_NOTICE, "%s", d.buf); | |
295 | else { | |
296 | DPUTC(&d, '\n'); | |
297 | dstr_write(&d, stderr); | |
298 | } | |
299 | DDESTROY(&d); | |
300 | } | |
301 | ||
302 | /* --- @fw_inc@, @fw_dec@ --- * | |
303 | * | |
304 | * Arguments: --- | |
305 | * | |
306 | * Returns: --- | |
307 | * | |
308 | * Use: Increments or decrements the active thing count. `fw' won't | |
309 | * quit while there are active things. | |
310 | */ | |
311 | ||
312 | void fw_inc(void) { flags |= FW_SET; active++; } | |
313 | void fw_dec(void) { if (active) active--; } | |
314 | ||
315 | /* --- @fw_exit@ --- * | |
316 | * | |
317 | * Arguments: --- | |
318 | * | |
319 | * Returns: --- | |
320 | * | |
321 | * Use: Exits when appropriate. | |
322 | */ | |
323 | ||
324 | static void fw_exit(void) | |
325 | { | |
326 | endpt_killall(); | |
327 | source_killall(); | |
328 | } | |
329 | ||
330 | /* --- @fw_tidy@ --- * | |
331 | * | |
332 | * Arguments: @int n@ = signal number | |
333 | * @void *p@ = an uninteresting argument | |
334 | * | |
335 | * Returns: --- | |
336 | * | |
337 | * Use: Handles various signals and causes a clean tidy-up. | |
338 | */ | |
339 | ||
340 | static void fw_tidy(int n, void *p) | |
341 | { | |
372a98e2 | 342 | const char *sn = 0; |
343 | switch (n) { | |
344 | case SIGTERM: sn = "SIGTERM"; break; | |
345 | case SIGINT: sn = "SIGINT"; break; | |
346 | default: abort(); | |
347 | } | |
348 | ||
349 | fw_log(-1, "closing down gracefully on %s", sn); | |
350 | source_killall(); | |
351 | } | |
352 | ||
353 | /* --- @fw_die@ --- * | |
354 | * | |
355 | * Arguments: @int n@ = signal number | |
356 | * @void *p@ = an uninteresting argument | |
357 | * | |
358 | * Returns: --- | |
359 | * | |
360 | * Use: Handles various signals and causes an abrupt shutdown. | |
361 | */ | |
362 | ||
363 | static void fw_die(int n, void *p) | |
364 | { | |
365 | const char *sn = 0; | |
366 | switch (n) { | |
367 | case SIGQUIT: sn = "SIGQUIT"; break; | |
368 | default: abort(); | |
369 | } | |
370 | ||
371 | fw_log(-1, "closing down abruptly on %s", sn); | |
372 | source_killall(); | |
373 | endpt_killall(); | |
374 | } | |
375 | ||
376 | /* --- @fw_reload@ --- * | |
377 | * | |
378 | * Arguments: @int n@ = a signal number | |
379 | * @void *p@ = an uninteresting argument | |
380 | * | |
381 | * Returns: --- | |
382 | * | |
383 | * Use: Handles a hangup signal by re-reading configuration files. | |
384 | */ | |
385 | ||
386 | static void fw_reload(int n, void *p) | |
387 | { | |
388 | FILE *fp; | |
389 | scanner sc; | |
390 | conffile *cf; | |
391 | ||
392 | assert(n == SIGHUP); | |
393 | if (!conffiles) { | |
394 | fw_log(-1, "no configuration files to reload: ignoring SIGHUP"); | |
395 | return; | |
396 | } | |
397 | fw_log(-1, "reloading configuration files..."); | |
398 | source_killall(); | |
399 | scan_create(&sc); | |
400 | for (cf = conffiles; cf; cf = cf->next) { | |
401 | if ((fp = fopen(cf->name, "r")) == 0) | |
402 | fw_log(-1, "error loading `%s': %s", cf->name, strerror(errno)); | |
403 | else | |
404 | scan_add(&sc, scan_file(fp, cf->name, 0)); | |
405 | } | |
8c0a939e | 406 | parse(&sc); |
372a98e2 | 407 | fw_log(-1, "... reload completed OK"); |
61e3dbdf | 408 | } |
409 | ||
8c0a939e | 410 | /*----- Startup and options parsing ---------------------------------------*/ |
411 | ||
e82f7154 | 412 | /* --- Standard GNU help options --- */ |
413 | ||
414 | static void version(FILE *fp) | |
415 | { | |
2d9ec601 | 416 | pquis(fp, "$ version " VERSION "\n"); |
e82f7154 | 417 | } |
418 | ||
419 | static void usage(FILE *fp) | |
420 | { | |
ad4499e3 | 421 | pquis(fp, "Usage: $ [-dql] [-p PIDFILE] [-f FILE] [CONFIG-STMTS...]\n"); |
e82f7154 | 422 | } |
423 | ||
424 | static void help(FILE *fp) | |
425 | { | |
426 | version(fp); | |
427 | fputc('\n', fp); | |
428 | usage(fp); | |
2d9ec601 | 429 | pquis(fp, "\n\ |
61e3dbdf | 430 | An excessively full-featured port-forwarder, which subsumes large chunks\n\ |
ad4499e3 | 431 | of the functionality of inetd, netcat, and normal cat.\n\ |
e82f7154 | 432 | \n\ |
433 | Configuration may be supplied in one or more configuration files, or on\n\ | |
434 | the command line (or both). If no `-f' option is present, and no\n\ | |
435 | configuration is given on the command line, the standard input stream is\n\ | |
436 | read.\n\ | |
437 | \n\ | |
438 | Configuration is free-form. Comments begin with a `#' character and\n\ | |
afd7451e | 439 | continue to the end of the line. Each command line argument is considered\n\ |
61e3dbdf | 440 | to be a separate line.\n\ |
afd7451e | 441 | \n\ |
2d9ec601 | 442 | The grammar is fairly complicated. For a summary, run `$ --grammar'.\n\ |
443 | For a summary of the various options, run `$ --options'.\n\ | |
ad4499e3 | 444 | \n\ |
445 | Options available are:\n\ | |
446 | \n\ | |
447 | Help options:\n\ | |
448 | -h, --help Display this help message.\n\ | |
449 | -v, --version Display the program's version number.\n\ | |
450 | -u, --usage Display a terse usage summary.\n\ | |
451 | \n\ | |
452 | Configuration summary:\n\ | |
453 | -G, --grammar Show a summary of the configuration language.\n\ | |
454 | -O, --options Show a summary of the source and target options.\n\ | |
455 | \n\ | |
456 | Other options:\n\ | |
457 | -f, --file=FILE Read configuration from a file.\n\ | |
458 | -q, --quiet Don't emit any logging information.\n\ | |
459 | -d, --daemon Fork into background after initializing.\n\ | |
460 | -p, --pidfile=FILE Write process-id to the named FILE.\n\ | |
461 | -l, --syslog Send log output to the system logger.\n\ | |
462 | -s, --setuid=USER Change uid to USER after initializing sources.\n\ | |
463 | -g, --setgid=GRP Change gid to GRP after initializing sources.\n\ | |
2d9ec601 | 464 | "); |
465 | } | |
466 | ||
467 | /* --- Other helpful options --- */ | |
468 | ||
469 | static void grammar(FILE *fp) | |
470 | { | |
471 | version(fp); | |
8cf7c7c2 MW |
472 | fputs("\nGrammar summary\n\n", fp); |
473 | fputs(grammar_text, fp); | |
2d9ec601 | 474 | } |
475 | ||
476 | static void options(FILE *fp) | |
477 | { | |
478 | version(fp); | |
8cf7c7c2 MW |
479 | fputs("\nOption summary\n\n", fp); |
480 | fputs(option_text, fp); | |
e82f7154 | 481 | } |
482 | ||
483 | /* --- @main@ --- * | |
484 | * | |
485 | * Arguments: @int argc@ = number of command line arguments | |
486 | * @char *argv[]@ = vector of argument strings | |
487 | * | |
488 | * Returns: --- | |
489 | * | |
490 | * Use: Simple port-forwarding server. | |
491 | */ | |
492 | ||
493 | int main(int argc, char *argv[]) | |
494 | { | |
495 | unsigned f = 0; | |
496 | sel_state sst; | |
372a98e2 | 497 | sig s_term, s_quit, s_int, s_hup; |
61e3dbdf | 498 | scanner sc; |
fc170a33 | 499 | uid_t drop = -1; |
500 | gid_t dropg = -1; | |
4166ea7c | 501 | const char *pidfile = 0; |
372a98e2 | 502 | conffile *cf, **cff = &conffiles; |
e82f7154 | 503 | |
372a98e2 | 504 | #define f_bogus 1u |
a8b9c5eb | 505 | #define f_file 2u |
506 | #define f_syslog 4u | |
507 | #define f_fork 8u | |
e82f7154 | 508 | |
509 | /* --- Initialize things --- */ | |
510 | ||
511 | ego(argv[0]); | |
512 | sel = &sst; | |
513 | sel_init(sel); | |
514 | sub_init(); | |
61e3dbdf | 515 | sig_init(sel); |
e82f7154 | 516 | bres_init(sel); |
2d9ec601 | 517 | bres_exec(0); |
61e3dbdf | 518 | exec_init(); |
519 | fattr_init(&fattr_global); | |
520 | scan_create(&sc); | |
521 | ||
61e3dbdf | 522 | atexit(fw_exit); |
e82f7154 | 523 | |
524 | /* --- Parse command line options --- */ | |
525 | ||
526 | for (;;) { | |
527 | static struct option opts[] = { | |
528 | ||
529 | /* --- Standard GNU help options --- */ | |
530 | ||
531 | { "help", 0, 0, 'h' }, | |
532 | { "version", 0, 0, 'v' }, | |
533 | { "usage", 0, 0, 'u' }, | |
534 | ||
2d9ec601 | 535 | /* --- Other help options --- */ |
536 | ||
1e8a64e8 | 537 | { "grammar", 0, 0, 'G' }, |
538 | { "options", 0, 0, 'O' }, | |
2d9ec601 | 539 | |
e82f7154 | 540 | /* --- Other useful arguments --- */ |
541 | ||
542 | { "file", OPTF_ARGREQ, 0, 'f' }, | |
61e3dbdf | 543 | { "fork", 0, 0, 'd' }, |
544 | { "daemon", 0, 0, 'd' }, | |
4166ea7c | 545 | { "pidfile", OPTF_ARGREQ, 0, 'p' }, |
17be1d6b | 546 | { "syslog", 0, 0, 'l' }, |
547 | { "log", 0, 0, 'l' }, | |
61e3dbdf | 548 | { "quiet", 0, 0, 'q' }, |
fc170a33 | 549 | { "setuid", OPTF_ARGREQ, 0, 's' }, |
550 | { "uid", OPTF_ARGREQ, 0, 's' }, | |
551 | { "setgid", OPTF_ARGREQ, 0, 'g' }, | |
552 | { "gid", OPTF_ARGREQ, 0, 'g' }, | |
e82f7154 | 553 | |
554 | /* --- Magic terminator --- */ | |
555 | ||
556 | { 0, 0, 0, 0 } | |
557 | }; | |
4166ea7c | 558 | int i = mdwopt(argc, argv, "+hvu" "GO" "f:dp:ls:g:", opts, 0, 0, 0); |
e82f7154 | 559 | |
560 | if (i < 0) | |
561 | break; | |
562 | switch (i) { | |
563 | case 'h': | |
564 | help(stdout); | |
565 | exit(0); | |
566 | break; | |
567 | case 'v': | |
568 | version(stdout); | |
569 | exit(0); | |
570 | break; | |
571 | case 'u': | |
572 | usage(stdout); | |
573 | exit(0); | |
574 | break; | |
fc170a33 | 575 | case 'G': |
2d9ec601 | 576 | grammar(stdout); |
577 | exit(0); | |
578 | break; | |
fc170a33 | 579 | case 'O': |
2d9ec601 | 580 | options(stdout); |
581 | exit(0); | |
582 | break; | |
61e3dbdf | 583 | case 'f': |
584 | if (strcmp(optarg, "-") == 0) | |
585 | scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE)); | |
586 | else { | |
587 | FILE *fp; | |
588 | if ((fp = fopen(optarg, "r")) == 0) | |
589 | die(1, "couldn't open file `%s': %s", optarg, strerror(errno)); | |
372a98e2 | 590 | cf = CREATE(conffile); |
591 | cf->name = optarg; | |
592 | *cff = cf; | |
593 | cff = &cf->next; | |
61e3dbdf | 594 | scan_add(&sc, scan_file(fp, optarg, 0)); |
595 | } | |
e82f7154 | 596 | f |= f_file; |
e82f7154 | 597 | break; |
61e3dbdf | 598 | case 'd': |
e82f7154 | 599 | f |= f_fork; |
600 | break; | |
4166ea7c | 601 | case 'p': |
602 | pidfile = optarg; | |
603 | break; | |
17be1d6b | 604 | case 'l': |
605 | f |= f_syslog; | |
606 | break; | |
61e3dbdf | 607 | case 'q': |
608 | flags |= FW_QUIET; | |
609 | break; | |
fc170a33 | 610 | case 's': |
611 | if (isdigit((unsigned char )optarg[0])) { | |
612 | char *q; | |
613 | drop = strtol(optarg, &q, 0); | |
614 | if (*q) | |
615 | die(1, "bad uid `%s'", optarg); | |
616 | } else { | |
617 | struct passwd *pw = getpwnam(optarg); | |
618 | if (!pw) | |
619 | die(1, "unknown user `%s'", optarg); | |
620 | drop = pw->pw_uid; | |
621 | } | |
622 | break; | |
623 | case 'g': | |
624 | if (isdigit((unsigned char )optarg[0])) { | |
625 | char *q; | |
626 | dropg = strtol(optarg, &q, 0); | |
627 | if (*q) | |
628 | die(1, "bad gid `%s'", optarg); | |
629 | } else { | |
630 | struct group *gr = getgrnam(optarg); | |
631 | if (!gr) | |
632 | die(1, "unknown group `%s'", optarg); | |
633 | dropg = gr->gr_gid; | |
634 | } | |
635 | break; | |
e82f7154 | 636 | default: |
637 | f |= f_bogus; | |
638 | break; | |
639 | } | |
640 | } | |
641 | ||
642 | if (f & f_bogus) { | |
643 | usage(stderr); | |
644 | exit(1); | |
645 | } | |
372a98e2 | 646 | *cff = 0; |
e82f7154 | 647 | |
648 | /* --- Deal with the remaining arguments --- */ | |
649 | ||
61e3dbdf | 650 | if (optind < argc) |
651 | scan_add(&sc, scan_argv(argv + optind)); | |
652 | else if (f & f_file) | |
653 | /* Cool */; | |
654 | else if (!isatty(STDIN_FILENO)) | |
655 | scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE)); | |
656 | else { | |
657 | moan("no configuration given and stdin is a terminal."); | |
658 | moan("type `%s --help' for usage information.", QUIS); | |
659 | exit(1); | |
e82f7154 | 660 | } |
661 | ||
61e3dbdf | 662 | /* --- Parse the configuration now gathered --- */ |
e82f7154 | 663 | |
8c0a939e | 664 | parse(&sc); |
e82f7154 | 665 | |
372a98e2 | 666 | /* --- Set up some signal handlers --- * |
667 | * | |
668 | * Don't enable @SIGINT@ if the caller already disabled it. | |
669 | */ | |
670 | ||
671 | { | |
672 | struct sigaction sa; | |
673 | ||
674 | sig_add(&s_term, SIGTERM, fw_tidy, 0); | |
675 | sig_add(&s_quit, SIGQUIT, fw_die, 0); | |
676 | sigaction(SIGINT, 0, &sa); | |
677 | if (sa.sa_handler != SIG_IGN) | |
678 | sig_add(&s_int, SIGINT, fw_tidy, 0); | |
679 | sig_add(&s_hup, SIGHUP, fw_reload, 0); | |
680 | } | |
681 | ||
fc170a33 | 682 | /* --- Drop privileges --- */ |
683 | ||
ee599f55 | 684 | if (drop != (uid_t)-1) |
685 | privconn_split(sel); | |
fc170a33 | 686 | #ifdef HAVE_SETGROUPS |
00e3c0f1 | 687 | if ((dropg != (gid_t)-1 && (setgid(dropg) || setgroups(1, &dropg))) || |
688 | (drop != (uid_t)-1 && setuid(drop))) | |
f9d40245 | 689 | die(1, "couldn't drop privileges: %s", strerror(errno)); |
fc170a33 | 690 | #else |
00e3c0f1 | 691 | if ((dropg != (gid_t)-1 && setgid(dropg)) || |
692 | (drop != (uid_t)-1 && setuid(drop))) | |
fc170a33 | 693 | die(1, "couldn't drop privileges: %s", strerror(errno)); |
f9d40245 | 694 | #endif |
fc170a33 | 695 | |
e82f7154 | 696 | /* --- Fork into the background --- */ |
697 | ||
698 | if (f & f_fork) { | |
699 | pid_t kid; | |
700 | ||
701 | kid = fork(); | |
702 | if (kid == -1) | |
703 | die(1, "couldn't fork: %s", strerror(errno)); | |
704 | if (kid != 0) | |
705 | _exit(0); | |
706 | ||
707 | close(0); close(1); close(2); | |
afd7451e | 708 | chdir("/"); |
e82f7154 | 709 | setsid(); |
710 | ||
711 | kid = fork(); | |
712 | if (kid != 0) | |
713 | _exit(0); | |
17be1d6b | 714 | } |
4166ea7c | 715 | if (pidfile) { |
716 | FILE *fp = fopen(pidfile, "w"); | |
717 | if (!fp) { | |
718 | die(EXIT_FAILURE, "couldn't create pidfile `%s': %s", | |
719 | pidfile, strerror(errno)); | |
720 | } | |
721 | fprintf(fp, "%lu\n", (unsigned long)getpid()); | |
722 | if (fflush(fp) || ferror(fp) || fclose(fp)) { | |
723 | die(EXIT_FAILURE, "couldn't write pidfile `%s': %s", | |
724 | pidfile, strerror(errno)); | |
725 | } | |
726 | } | |
afd7451e | 727 | |
17be1d6b | 728 | if (f & f_syslog) { |
afd7451e | 729 | flags |= FW_SYSLOG; |
730 | openlog(QUIS, 0, LOG_DAEMON); | |
e82f7154 | 731 | } |
732 | ||
733 | /* --- Let rip --- */ | |
734 | ||
61e3dbdf | 735 | if (!(flags & FW_SET)) |
736 | moan("nothing to do!"); | |
737 | signal(SIGPIPE, SIG_IGN); | |
f9d40245 | 738 | |
739 | { | |
740 | int selerr = 0; | |
741 | while (active) { | |
742 | if (!sel_select(sel)) | |
743 | selerr = 0; | |
372a98e2 | 744 | else if (errno != EINTR && errno != EAGAIN) { |
f9d40245 | 745 | fw_log(-1, "error from select: %s", strerror(errno)); |
746 | selerr++; | |
747 | if (selerr > 8) { | |
748 | fw_log(-1, "too many consecutive select errors: bailing out"); | |
749 | exit(EXIT_FAILURE); | |
750 | } | |
751 | } | |
752 | } | |
753 | } | |
754 | ||
e82f7154 | 755 | return (0); |
756 | } | |
757 | ||
758 | /*----- That's all, folks -------------------------------------------------*/ |