Make syslog a separate option, and do it better.
[fwd] / fw.c
CommitLineData
e82f7154 1/* -*-c-*-
2 *
17be1d6b 3 * $Id: fw.c,v 1.6 1999/12/22 15:44:10 mdw Exp $
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
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: fw.c,v $
17be1d6b 32 * Revision 1.6 1999/12/22 15:44:10 mdw
33 * Make syslog a separate option, and do it better.
34 *
06d82312 35 * Revision 1.5 1999/10/22 22:47:50 mdw
36 * Grammar changes. Also, don't enable SIGINT if it's currently ignored.
37 *
2d9ec601 38 * Revision 1.4 1999/10/10 16:46:12 mdw
39 * New resolver to initialize. Also, include options for grammar and
40 * options references.
41 *
61e3dbdf 42 * Revision 1.3 1999/07/26 23:30:42 mdw
43 * Major reconstruction work for new design.
44 *
afd7451e 45 * Revision 1.2 1999/07/03 13:55:17 mdw
46 * Various changes. Add configuration grammar to help text. Change to
47 * root directory and open syslog when forking into background.
48 *
49 * Revision 1.1.1.1 1999/07/01 08:56:23 mdw
50 * Initial revision.
e82f7154 51 *
52 */
53
54/*----- Header files ------------------------------------------------------*/
55
56#include "config.h"
57
58#include <ctype.h>
59#include <errno.h>
61e3dbdf 60#include <signal.h>
61#include <stdarg.h>
e82f7154 62#include <stdio.h>
63#include <stdlib.h>
64#include <string.h>
61e3dbdf 65#include <time.h>
e82f7154 66
67#include <unistd.h>
68#include <syslog.h>
69
2d9ec601 70#include <mLib/bres.h>
61e3dbdf 71#include <mLib/dstr.h>
e82f7154 72#include <mLib/mdwopt.h>
73#include <mLib/quis.h>
74#include <mLib/report.h>
75#include <mLib/sel.h>
61e3dbdf 76#include <mLib/sig.h>
e82f7154 77#include <mLib/sub.h>
78
e82f7154 79#include "conf.h"
61e3dbdf 80#include "endpt.h"
81#include "exec.h"
82#include "fattr.h"
afd7451e 83#include "fw.h"
e82f7154 84#include "scan.h"
61e3dbdf 85#include "source.h"
e82f7154 86
afd7451e 87/*----- Global variables --------------------------------------------------*/
e82f7154 88
89sel_state *sel; /* Multiplexor for nonblocking I/O */
61e3dbdf 90
91/*----- Static variables --------------------------------------------------*/
92
93static unsigned flags = 0; /* Global state flags */
94static unsigned active = 0; /* Number of active things */
95
96#define FW_SYSLOG 1u
97#define FW_QUIET 2u
98#define FW_SET 4u
e82f7154 99
100/*----- Main code ---------------------------------------------------------*/
101
61e3dbdf 102/* --- @fw_log@ --- *
103 *
104 * Arguments: @time_t t@ = when the connection occurred or (@-1@)
105 * @const char *fmt@ = format string to fill in
106 * @...@ = other arguments
107 *
108 * Returns: ---
109 *
110 * Use: Logs a connection.
111 */
112
113void fw_log(time_t t, const char *fmt, ...)
114{
115 struct tm *tm;
116 dstr d = DSTR_INIT;
117 va_list ap;
118
119 if (flags & FW_QUIET)
120 return;
121
122 if (t == -1)
123 t = time(0);
124 tm = localtime(&t);
125 DENSURE(&d, 64);
17be1d6b 126 d.len += strftime(d.buf, d.sz, "%Y-%m-%d %H:%M:%S ", tm);
61e3dbdf 127 va_start(ap, fmt);
128 dstr_vputf(&d, fmt, ap);
129 va_end(ap);
130 if (flags & FW_SYSLOG)
131 syslog(LOG_NOTICE, "%s", d.buf);
132 else {
133 DPUTC(&d, '\n');
134 dstr_write(&d, stderr);
135 }
136 DDESTROY(&d);
137}
138
139/* --- @fw_inc@, @fw_dec@ --- *
140 *
141 * Arguments: ---
142 *
143 * Returns: ---
144 *
145 * Use: Increments or decrements the active thing count. `fw' won't
146 * quit while there are active things.
147 */
148
149void fw_inc(void) { flags |= FW_SET; active++; }
150void fw_dec(void) { if (active) active--; }
151
152/* --- @fw_exit@ --- *
153 *
154 * Arguments: ---
155 *
156 * Returns: ---
157 *
158 * Use: Exits when appropriate.
159 */
160
161static void fw_exit(void)
162{
163 endpt_killall();
164 source_killall();
165}
166
167/* --- @fw_tidy@ --- *
168 *
169 * Arguments: @int n@ = signal number
170 * @void *p@ = an uninteresting argument
171 *
172 * Returns: ---
173 *
174 * Use: Handles various signals and causes a clean tidy-up.
175 */
176
177static void fw_tidy(int n, void *p)
178{
179 const char *sn = "unexpected signal (bug!)";
180 if (n == SIGTERM)
181 sn = "SIGTERM";
182 else if (n == SIGINT)
183 sn = "SIGINT";
184
185 fw_log(-1, "closing down on %s", sn);
186 fw_exit();
187}
188
e82f7154 189/* --- Standard GNU help options --- */
190
191static void version(FILE *fp)
192{
2d9ec601 193 pquis(fp, "$ version " VERSION "\n");
e82f7154 194}
195
196static void usage(FILE *fp)
197{
17be1d6b 198 pquis(fp, "Usage: $ [-dql] [-f file] [config statements...]\n");
e82f7154 199}
200
201static void help(FILE *fp)
202{
203 version(fp);
204 fputc('\n', fp);
205 usage(fp);
2d9ec601 206 pquis(fp, "\n\
61e3dbdf 207An excessively full-featured port-forwarder, which subsumes large chunks\n\
208of the functionality of inetd, netcat, and normal cat. Options available\n\
209are:\n\
e82f7154 210\n\
17be1d6b 211-h, --help Display this help message.\n\
212-v, --version Display the program's version number.\n\
213-u, --usage Display a terse usage summary.\n\
e82f7154 214\n\
17be1d6b 215-g, --grammar Show a summary of the configuration language.\n\
216-o, --options Show a summary of the source and target options.\n\
2d9ec601 217\n\
17be1d6b 218-f, --file=FILE Read configuration from a file.\n\
219-q, --quiet Don't emit any logging information.\n\
220-d, --daemon Fork into background after initializing.\n\
221-l, --syslog Send log output to the system logger.\n\
e82f7154 222\n\
223Configuration may be supplied in one or more configuration files, or on\n\
224the command line (or both). If no `-f' option is present, and no\n\
225configuration is given on the command line, the standard input stream is\n\
226read.\n\
227\n\
228Configuration is free-form. Comments begin with a `#' character and\n\
afd7451e 229continue to the end of the line. Each command line argument is considered\n\
61e3dbdf 230to be a separate line.\n\
afd7451e 231\n\
2d9ec601 232The grammar is fairly complicated. For a summary, run `$ --grammar'.\n\
233For a summary of the various options, run `$ --options'.\n\
234");
235}
236
237/* --- Other helpful options --- */
238
239static void grammar(FILE *fp)
240{
241 version(fp);
242 pquis(fp, "\n\
243Grammar summary\n\
244\n\
245Basic syntax\n\
246 file ::= empty | file stmt [`;']\n\
247 stmt ::= option-stmt | fw-stmt\n\
248 fw-stmt ::= `fw' source options [`to'|`->'] target options\n\
249 options ::= `{' option-seq `}'\n\
250 option-seq ::= empty | option-stmt [`;'] option-seq\n\
251\n\
252Option syntax\n\
253 option-stmt ::= q-option\n\
254 q-option ::= option\n\
255 | prefix `.' q-option\n\
256 | prefix `{' option-seq `}'\n\
257 prefix ::= word\n\
258\n\
259File source and target\n\
260 source ::= file\n\
261 target ::= file\n\
262 file ::= `file' [`.'] fspec [`,' fspec]\n\
263 fspec ::= fd-spec | name-spec | null-spec\n\
264 fd-spec ::= [[`:']`fd'[`:']] number|`stdin'|`stdout'\n\
265 name-spec ::= [[`:']`file'[`:']] file-name\n\
266 file-name ::= path-seq | [ path-seq ]\n\
267 path-seq ::= path-elt | path-seq path-elt\n\
268 path-elt ::= `/' | word\n\
269 null-spec ::= [`:']`null'[`:']\n\
270\n\
271Exec source and target\n\
272 source ::= exec\n\
273 target ::= exec\n\
274 exec ::= `exec' [`.'] cmd-spec\n\
275 cmd-spec ::= shell-cmd | [prog-name] `[' argv0 arg-seq `]'\n\
276 arg-seq ::= word | arg-seq word\n\
277 shell-cmd ::= word\n\
278 argv0 ::= word\n\
279\n\
280Socket source and target\n\
281 source ::= socket-source\n\
282 target ::= socket-target\n\
283 socket-source ::= [`socket'[`.']] [[`:']addr-type[`:']] source-addr\n\
284 socket-target ::= [`socket'[`.']] [[`:']addr-type[`:']] target-addr\n\
285\n\
286 inet-source-addr ::= [port] port\n\
287 inet-target-addr ::= address [`:'] port\n\
288 address ::= addr-elt | address addr-elt\n\
289 addr-elt ::= `.' | word\n\
290\n\
291 unix-source-addr ::= file-name\n\
292 unix-target-addr ::= file-name\n\
293");
294}
295
296static void options(FILE *fp)
297{
298 version(fp);
299 pquis(fp, "\n\
300Options summary\n\
301\n\
302File attributes (`fattr')\n\
303 prefix.fattr.mode [=] mode\n\
304 prefix.fattr.owner [=] user\n\
305 prefix.fattr.group [=] group\n\
306\n\
307File options\n\
308 file.create [=] yes|no\n\
309 file.open [=] no|truncate|append\n\
310 file.fattr.*\n\
311\n\
312Exec options\n\
313 exec.logging [=] yes|no\n\
314 exec.dir [=] file-name\n\
315 exec.root [=] file-name\n\
316 exec.user [=] user\n\
317 exec.group [=] group\n\
318 exec.rlimit.limit[.hard|.soft] [=] value\n\
319 exec.env.clear\n\
320 exec.env.unset var\n\
321 exec.env.[set] var [=] value\n\
322\n\
323Socket options\n\
06d82312 324 socket.conn [=] number|unlimited|one-shot\n\
2d9ec601 325 socket.logging [=] yes|no\n\
326 socket.inet.[allow|deny] [from] address [/ address]\n\
327 socket.unix.fattr.*\n\
328");
e82f7154 329}
330
331/* --- @main@ --- *
332 *
333 * Arguments: @int argc@ = number of command line arguments
334 * @char *argv[]@ = vector of argument strings
335 *
336 * Returns: ---
337 *
338 * Use: Simple port-forwarding server.
339 */
340
341int main(int argc, char *argv[])
342{
343 unsigned f = 0;
344 sel_state sst;
61e3dbdf 345 sig s_term, s_int;
346 scanner sc;
e82f7154 347
348 enum {
349 f_bogus = 1,
350 f_file = 2,
17be1d6b 351 f_syslog = 4,
e82f7154 352 f_fork = 8
353 };
354
355 /* --- Initialize things --- */
356
357 ego(argv[0]);
358 sel = &sst;
359 sel_init(sel);
360 sub_init();
61e3dbdf 361 sig_init(sel);
e82f7154 362 bres_init(sel);
2d9ec601 363 bres_exec(0);
61e3dbdf 364 exec_init();
365 fattr_init(&fattr_global);
366 scan_create(&sc);
367
06d82312 368 /* --- Set up some signal handlers --- *
369 *
370 * Don't enable @SIGINT@ if the caller already disabled it.
371 */
372
373 {
374 struct sigaction sa;
375
376 sig_add(&s_term, SIGTERM, fw_tidy, 0);
377 sigaction(SIGINT, 0, &sa);
378 if (sa.sa_handler != SIG_IGN)
379 sig_add(&s_int, SIGINT, fw_tidy, 0);
380 }
61e3dbdf 381
61e3dbdf 382 atexit(fw_exit);
e82f7154 383
384 /* --- Parse command line options --- */
385
386 for (;;) {
387 static struct option opts[] = {
388
389 /* --- Standard GNU help options --- */
390
391 { "help", 0, 0, 'h' },
392 { "version", 0, 0, 'v' },
393 { "usage", 0, 0, 'u' },
394
2d9ec601 395 /* --- Other help options --- */
396
397 { "grammar", 0, 0, 'g' },
398 { "options", 0, 0, 'o' },
399
e82f7154 400 /* --- Other useful arguments --- */
401
402 { "file", OPTF_ARGREQ, 0, 'f' },
61e3dbdf 403 { "fork", 0, 0, 'd' },
404 { "daemon", 0, 0, 'd' },
17be1d6b 405 { "syslog", 0, 0, 'l' },
406 { "log", 0, 0, 'l' },
61e3dbdf 407 { "quiet", 0, 0, 'q' },
e82f7154 408
409 /* --- Magic terminator --- */
410
411 { 0, 0, 0, 0 }
412 };
17be1d6b 413 int i = mdwopt(argc, argv, "+hvu go f:dl", opts, 0, 0, 0);
e82f7154 414
415 if (i < 0)
416 break;
417 switch (i) {
418 case 'h':
419 help(stdout);
420 exit(0);
421 break;
422 case 'v':
423 version(stdout);
424 exit(0);
425 break;
426 case 'u':
427 usage(stdout);
428 exit(0);
429 break;
2d9ec601 430 case 'g':
431 grammar(stdout);
432 exit(0);
433 break;
434 case 'o':
435 options(stdout);
436 exit(0);
437 break;
61e3dbdf 438 case 'f':
439 if (strcmp(optarg, "-") == 0)
440 scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
441 else {
442 FILE *fp;
443 if ((fp = fopen(optarg, "r")) == 0)
444 die(1, "couldn't open file `%s': %s", optarg, strerror(errno));
445 scan_add(&sc, scan_file(fp, optarg, 0));
446 }
e82f7154 447 f |= f_file;
e82f7154 448 break;
61e3dbdf 449 case 'd':
e82f7154 450 f |= f_fork;
451 break;
17be1d6b 452 case 'l':
453 f |= f_syslog;
454 break;
61e3dbdf 455 case 'q':
456 flags |= FW_QUIET;
457 break;
e82f7154 458 default:
459 f |= f_bogus;
460 break;
461 }
462 }
463
464 if (f & f_bogus) {
465 usage(stderr);
466 exit(1);
467 }
468
469 /* --- Deal with the remaining arguments --- */
470
61e3dbdf 471 if (optind < argc)
472 scan_add(&sc, scan_argv(argv + optind));
473 else if (f & f_file)
474 /* Cool */;
475 else if (!isatty(STDIN_FILENO))
476 scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
477 else {
478 moan("no configuration given and stdin is a terminal.");
479 moan("type `%s --help' for usage information.", QUIS);
480 exit(1);
e82f7154 481 }
482
61e3dbdf 483 /* --- Parse the configuration now gathered --- */
e82f7154 484
61e3dbdf 485 conf_parse(&sc);
e82f7154 486
487 /* --- Fork into the background --- */
488
489 if (f & f_fork) {
490 pid_t kid;
491
492 kid = fork();
493 if (kid == -1)
494 die(1, "couldn't fork: %s", strerror(errno));
495 if (kid != 0)
496 _exit(0);
497
498 close(0); close(1); close(2);
afd7451e 499 chdir("/");
e82f7154 500 setsid();
501
502 kid = fork();
503 if (kid != 0)
504 _exit(0);
17be1d6b 505 }
afd7451e 506
17be1d6b 507 if (f & f_syslog) {
afd7451e 508 flags |= FW_SYSLOG;
509 openlog(QUIS, 0, LOG_DAEMON);
e82f7154 510 }
511
512 /* --- Let rip --- */
513
61e3dbdf 514 if (!(flags & FW_SET))
515 moan("nothing to do!");
516 signal(SIGPIPE, SIG_IGN);
517 while (active)
e82f7154 518 sel_select(sel);
519 return (0);
520}
521
522/*----- That's all, folks -------------------------------------------------*/