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