chiark / gitweb /
Major reconstruction work for new design.
[fwd] / fw.c
1 /* -*-c-*-
2  *
3  * $Id: fw.c,v 1.3 1999/07/26 23:30:42 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.3  1999/07/26 23:30:42  mdw
33  * Major reconstruction work for new design.
34  *
35  * Revision 1.2  1999/07/03 13:55:17  mdw
36  * Various changes.  Add configuration grammar to help text.  Change to
37  * root directory and open syslog when forking into background.
38  *
39  * Revision 1.1.1.1  1999/07/01 08:56:23  mdw
40  * Initial revision.
41  *
42  */
43
44 /*----- Header files ------------------------------------------------------*/
45
46 #include "config.h"
47
48 #include <ctype.h>
49 #include <errno.h>
50 #include <signal.h>
51 #include <stdarg.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <time.h>
56
57 #include <unistd.h>
58 #include <syslog.h>
59
60 #include <mLib/dstr.h>
61 #include <mLib/mdwopt.h>
62 #include <mLib/quis.h>
63 #include <mLib/report.h>
64 #include <mLib/sel.h>
65 #include <mLib/sig.h>
66 #include <mLib/sub.h>
67
68 #include "bres.h"
69 #include "conf.h"
70 #include "endpt.h"
71 #include "exec.h"
72 #include "fattr.h"
73 #include "fw.h"
74 #include "scan.h"
75 #include "source.h"
76
77 /*----- Global variables --------------------------------------------------*/
78
79 sel_state *sel;                         /* Multiplexor for nonblocking I/O */
80
81 /*----- Static variables --------------------------------------------------*/
82
83 static unsigned flags = 0;              /* Global state flags */
84 static unsigned active = 0;             /* Number of active things */
85
86 #define FW_SYSLOG 1u
87 #define FW_QUIET 2u
88 #define FW_SET 4u
89
90 /*----- Main code ---------------------------------------------------------*/
91
92 /* --- @fw_log@ --- *
93  *
94  * Arguments:   @time_t t@ = when the connection occurred or (@-1@)
95  *              @const char *fmt@ = format string to fill in
96  *              @...@ = other arguments
97  *
98  * Returns:     ---
99  *
100  * Use:         Logs a connection.
101  */
102
103 void fw_log(time_t t, const char *fmt, ...)
104 {
105   struct tm *tm;
106   dstr d = DSTR_INIT;
107   va_list ap;
108
109   if (flags & FW_QUIET)
110     return;
111
112   if (t == -1)
113     t = time(0);
114   tm = localtime(&t);
115   DENSURE(&d, 64);
116   d.len += strftime(d.buf, d.sz, "%Y-%m-%d %H:%M:%S", tm);
117   DPUTC(&d, ' ');
118   va_start(ap, fmt);
119   dstr_vputf(&d, fmt, ap);
120   va_end(ap);
121   if (flags & FW_SYSLOG)
122     syslog(LOG_NOTICE, "%s", d.buf);
123   else {
124     DPUTC(&d, '\n');
125     dstr_write(&d, stderr);
126   }
127   DDESTROY(&d);
128 }
129
130 /* --- @fw_inc@, @fw_dec@ --- *
131  *
132  * Arguments:   ---
133  *
134  * Returns:     ---
135  *
136  * Use:         Increments or decrements the active thing count.  `fw' won't
137  *              quit while there are active things.
138  */
139
140 void fw_inc(void) { flags |= FW_SET; active++; }
141 void fw_dec(void) { if (active) active--; }
142
143 /* --- @fw_exit@ --- *
144  *
145  * Arguments:   ---
146  *
147  * Returns:     ---
148  *
149  * Use:         Exits when appropriate.
150  */
151
152 static void fw_exit(void)
153 {
154   endpt_killall();
155   source_killall();
156 }
157
158 /* --- @fw_tidy@ --- *
159  *
160  * Arguments:   @int n@ = signal number
161  *              @void *p@ = an uninteresting argument
162  *
163  * Returns:     ---
164  *
165  * Use:         Handles various signals and causes a clean tidy-up.
166  */
167
168 static void fw_tidy(int n, void *p)
169 {
170   const char *sn = "unexpected signal (bug!)";
171   if (n == SIGTERM)
172     sn = "SIGTERM";
173   else if (n == SIGINT)
174     sn = "SIGINT";
175
176   fw_log(-1, "closing down on %s", sn);
177   fw_exit();
178 }
179
180 /* --- Standard GNU help options --- */
181
182 static void version(FILE *fp)
183 {
184   fprintf(fp, "%s version " VERSION "\n", QUIS);
185 }
186
187 static void usage(FILE *fp)
188 {
189   fprintf(stderr, "Usage: %s [-db] [-f file] [config statements...]\n",
190           QUIS);
191 }
192
193 static void help(FILE *fp)
194 {
195   version(fp);
196   fputc('\n', fp);
197   usage(fp);
198   fputs("\n\
199 An excessively full-featured port-forwarder, which subsumes large chunks\n\
200 of the functionality of inetd, netcat, and normal cat.  Options available\n\
201 are:\n\
202 \n\
203 -h, --help              Display this help message.\n\
204 -v, --version           Display the program's version number.\n\
205 -u, --usage             Display a terse usage summary.\n\
206 \n\
207 -f, --file=FILE         Read configuration from a file.\n\
208 -d, --daemon            Fork into background after initializing.\n\
209 \n\
210 Configuration may be supplied in one or more configuration files, or on\n\
211 the command line (or both).  If no `-f' option is present, and no\n\
212 configuration is given on the command line, the standard input stream is\n\
213 read.\n\
214 \n\
215 Configuration is free-form.  Comments begin with a `#' character and\n\
216 continue to the end of the line.  Each command line argument is considered\n\
217 to be a separate line.\n\
218 \n\
219 The syntax is too complicated to describe here.  Read the manual.\n\
220 ", fp);
221 }
222
223 /* --- @main@ --- *
224  *
225  * Arguments:   @int argc@ = number of command line arguments
226  *              @char *argv[]@ = vector of argument strings
227  *
228  * Returns:     ---
229  *
230  * Use:         Simple port-forwarding server.
231  */
232
233 int main(int argc, char *argv[])
234 {
235   unsigned f = 0;
236   sel_state sst;
237   sig s_term, s_int;
238   scanner sc;
239
240   enum {
241     f_bogus = 1,
242     f_file = 2,
243     f_fork = 8
244   };
245
246   /* --- Initialize things --- */
247
248   ego(argv[0]);
249   sel = &sst;
250   sel_init(sel);
251   sub_init();
252   sig_init(sel);
253   bres_init(sel);
254   exec_init();
255   fattr_init(&fattr_global);
256   scan_create(&sc);
257
258   /* --- Set up some signal handlers --- */
259
260   sig_add(&s_term, SIGTERM, fw_tidy, 0);
261   sig_add(&s_int, SIGINT, fw_tidy, 0);
262   atexit(fw_exit);
263
264   /* --- Parse command line options --- */
265
266   for (;;) {
267     static struct option opts[] = {
268
269       /* --- Standard GNU help options --- */
270
271       { "help",         0,              0,      'h' },
272       { "version",      0,              0,      'v' },
273       { "usage",        0,              0,      'u' },
274
275       /* --- Other useful arguments --- */
276
277       { "file",         OPTF_ARGREQ,    0,      'f' },
278       { "fork",         0,              0,      'd' },
279       { "daemon",       0,              0,      'd' },
280       { "quiet",        0,              0,      'q' },
281
282       /* --- Magic terminator --- */
283
284       { 0,              0,              0,      0 }
285     };
286     int i = mdwopt(argc, argv, "+hvu f:d", opts, 0, 0, 0);
287
288     if (i < 0)
289       break;
290     switch (i) {
291       case 'h':
292         help(stdout);
293         exit(0);
294         break;
295       case 'v':
296         version(stdout);
297         exit(0);
298         break;
299       case 'u':
300         usage(stdout);
301         exit(0);
302         break;
303       case 'f':
304         if (strcmp(optarg, "-") == 0)
305           scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
306         else {
307           FILE *fp;
308           if ((fp = fopen(optarg, "r")) == 0)
309             die(1, "couldn't open file `%s': %s", optarg, strerror(errno));
310           scan_add(&sc, scan_file(fp, optarg, 0));
311         }
312         f |= f_file;
313         break;
314       case 'd':
315         f |= f_fork;
316         break;
317       case 'q':
318         flags |= FW_QUIET;
319         break;
320       default:
321         f |= f_bogus;
322         break;
323     }
324   }
325
326   if (f & f_bogus) {
327     usage(stderr);
328     exit(1);
329   }
330
331   /* --- Deal with the remaining arguments --- */
332
333   if (optind < argc)
334     scan_add(&sc, scan_argv(argv + optind));
335   else if (f & f_file)
336     /* Cool */;
337   else if (!isatty(STDIN_FILENO))
338     scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
339   else {
340     moan("no configuration given and stdin is a terminal.");
341     moan("type `%s --help' for usage information.", QUIS);
342     exit(1);
343   }
344
345   /* --- Parse the configuration now gathered --- */
346
347   conf_parse(&sc);
348
349   /* --- Fork into the background --- */
350
351   if (f & f_fork) {
352     pid_t kid;
353
354     kid = fork();
355     if (kid == -1)
356       die(1, "couldn't fork: %s", strerror(errno));
357     if (kid != 0)
358       _exit(0);
359
360     close(0); close(1); close(2);
361     chdir("/");
362     setsid();
363
364     kid = fork();
365     if (kid != 0)
366       _exit(0);
367
368     flags |= FW_SYSLOG;
369     openlog(QUIS, 0, LOG_DAEMON);
370   }
371
372   /* --- Let rip --- */
373
374   if (!(flags & FW_SET))
375     moan("nothing to do!");
376   signal(SIGPIPE, SIG_IGN);
377   while (active)
378     sel_select(sel);
379   return (0);
380 }
381
382 /*----- That's all, folks -------------------------------------------------*/