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