chiark / gitweb /
Change copyright notice.
[fwd] / conf.c
1 /* -*-c-*-
2  *
3  * $Id: conf.c,v 1.1 1999/07/01 08:56:23 mdw Exp $
4  *
5  * Configuration parsing
6  *
7  * (c) 1999 Mark Wooding
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: conf.c,v $
32  * Revision 1.1  1999/07/01 08:56:23  mdw
33  * Initial revision
34  *
35  */
36
37 /*----- Header files ------------------------------------------------------*/
38
39 #include "config.h"
40
41 #include <ctype.h>
42 #include <errno.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47
48 #include <sys/socket.h>
49 #include <netinet/in.h>
50 #include <arpa/inet.h>
51 #include <netdb.h>
52
53 #include <mLib/dstr.h>
54 #include <mLib/quis.h>
55 #include <mLib/report.h>
56
57 #include "acl.h"
58 #include "listener.h"
59 #include "scan.h"
60
61 /*----- Magic numbers -----------------------------------------------------*/
62
63 #define CTOK_EOF (-1)
64 #define CTOK_WORD 256
65
66 /*----- Main code ---------------------------------------------------------*/
67
68 /* --- @token@ --- *
69  *
70  * Arguments:   @scanner *sc@ = pointer to scanner definition
71  *
72  * Returns:     Type of token scanned.
73  *
74  * Use:         Reads the next token from the character scanner.
75  */
76
77 static int token(scanner *sc)
78 {
79 #define SCAN(sc) (sc)->ops->scan((sc))
80 #define UNSCAN(sc, x) (sc)->ops->unscan((x), (sc))
81
82   int ch;
83
84   for (;;) {
85     ch = SCAN(sc);
86     if (ch == '\n')
87       sc->line++;
88     else if (isspace((unsigned char)ch))
89       ;
90     else switch (ch) {
91       case EOF:
92         return (sc->t = CTOK_EOF);
93       case '#':
94         do ch = SCAN(sc); while (ch != EOF && ch != '\n');
95         if (ch == '\n')
96           sc->line++;
97         break;
98       case '{':
99       case '}':
100       case ':':
101       case '/':
102       case ';':
103         return (sc->t = ch);
104       default:
105         DRESET(&sc->d);
106         do {
107           DPUTC(&sc->d, ch);
108           ch = SCAN(sc);
109         } while (ch != EOF && ch != '{' && ch != ';' &&
110                  ch != '}' && ch != ':' && ch != '/' &&
111                  !isspace((unsigned char)(ch)));
112         UNSCAN(sc, ch);
113         DPUTZ(&sc->d);
114         return (sc->t = CTOK_WORD);
115     }
116   }
117
118 #undef SCAN
119 #undef UNSCAN
120 }
121
122 /* --- @error@ --- *
123  *
124  * Arguments:   @scanner *sc@ = pointer to scanner definition
125  *              @const char *msg@ = message skeleton string
126  *              @...@ = extra arguments for the skeleton
127  *
128  * Returns:     Doesn't
129  *
130  * Use:         Reports an error at the current scanner location.
131  */
132
133 static void error(scanner *sc, const char *msg, ...)
134 {
135   va_list ap;
136   va_start(ap, msg);
137   fprintf(stderr, "%s: %s:%i: ", QUIS, sc->src, sc->line);
138   vfprintf(stderr, msg, ap);
139   fputc('\n', stderr);
140   exit(1);
141 }
142
143 /* --- @portnum@ --- *
144  *
145  * Arguments:   @scanner *sc@ = pointer to scanner (for error reporting)
146  *              @const char *p@ = pointer to port name
147  *
148  * Returns:     Port number (network byte order)
149  *
150  * Use:         Converts a textual port name or number into a usable thing.
151  */
152
153 static unsigned short portnum(scanner *sc, const char *p)
154 {
155   struct servent *s;
156   if (isdigit((unsigned char)*p))
157     return (htons(atoi(p)));
158   if ((s = getservbyname(p, "tcp")) == 0)
159     error(sc, "unknown tcp service `%s'", p);
160   return (s->s_port);
161 }
162
163 /* --- @getconf@ --- *
164  *
165  * Arguments:   @scanner *sc@ = pointer to scanner to read from
166  *              @listener *l@ = listener to configure (or zero)
167  *              @acl_entry ***a@ = pointer to tail of ACL (or zero)
168  *
169  * Returns:     ---
170  *
171  * Use:         Reads a local or global configuration statement.
172  */
173
174 static void getconf(scanner *sc, listener *l, acl_entry ***a)
175 {
176   unsigned act;
177
178   /* --- Access control limitations --- */
179
180   if ((strcmp(sc->d.buf, "allow") == 0 && (act = ACL_ALLOW, 1)) ||
181       (strcmp(sc->d.buf, "deny") == 0 && (act = ACL_DENY, 1))) {
182     struct hostent *h;
183     struct netent *n;
184     struct in_addr addr, mask;
185
186     /* --- Find the host or network address --- */
187
188     token(sc);
189     if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "from") == 0)
190       token(sc);
191     if (sc->t != CTOK_WORD)
192       error(sc, "parse error, address expected");
193     if ((n = getnetbyname(sc->d.buf)) != 0)
194       addr.s_addr = htonl(n->n_net);
195     else if ((h = gethostbyname(sc->d.buf)) == 0)
196       error(sc, "couldn't resolve address `%s'", sc->d.buf);
197     else
198       memcpy(&addr, h->h_addr, sizeof(struct in_addr));
199     token(sc);
200
201     /* --- Find the netmask, if any --- */
202
203     if (sc->t != '/')
204       mask.s_addr = ~0ul;
205     else {
206       token(sc);
207       if (sc->t != CTOK_WORD)
208         error(sc, "parse error, netmask expected");
209       if (strchr(sc->d.buf, '.') == 0)
210         mask.s_addr = htonl((~0ul << (32 - atoi(sc->d.buf))) & 0xffffffff);
211       else {
212 #ifdef HAVE_INET_ATON
213         if (!inet_aton(sc->d.buf, &mask))
214           error(sc, "bad netmask `%s'", sc->d.buf);
215 #else
216         mask.s_addr = inet_addr(sc->d.buf);
217 #endif
218       }
219       token(sc);
220     }
221
222     /* --- Add the access control entry --- */
223
224     acl_add(a, act, addr, mask);
225   }
226
227   /* --- Anything unrecognized --- */
228
229   else
230     error(sc, "parse error, unknown configuration keyword `%s'", sc->d.buf);
231 }
232
233 /* --- @conf_parse@ --- *
234  *
235  * Arguments:   @void *scp@ = pointer to scanner definition
236  *
237  * Returns:     ---
238  *
239  * Use:         Parses a configuration file from the scanner.
240  */
241
242 void conf_parse(void *scp)
243 {
244   scanner *sc = scp;
245
246   token(sc);
247
248   for (;;) {
249     if (sc->t == CTOK_EOF)
250       break;
251     if (sc->t != CTOK_WORD)
252       error(sc, "parse error, keyword expected");
253
254     /* --- Handle a forwarding request --- */
255
256     if (strcmp(sc->d.buf, "forward") == 0 ||
257         strcmp(sc->d.buf, "fw") == 0) {
258       unsigned short sp, dp;
259       struct sockaddr_in sin;
260       struct hostent *h;
261       int fd;
262       listener *l;
263
264       /* --- Read the source port --- */
265
266       token(sc);
267       if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "port") == 0)
268         token(sc);
269       if (sc->t != CTOK_WORD)
270         error(sc, "parse error, source port expected");
271       sp = portnum(sc, sc->d.buf);
272
273       /* --- Read the destination address --- */
274
275       token(sc);
276       if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "to") == 0)
277         token(sc);
278       if (sc->t != CTOK_WORD)
279         error(sc, "parse error, destination address expected");
280       if ((h = gethostbyname(sc->d.buf)) == 0)
281         error(sc, "couldn't resolve address `%s'", sc->d.buf);
282
283       token(sc);
284       if (sc->t == ':')
285         token(sc);
286       if (sc->t != CTOK_WORD)
287         error(sc, "parse error, destination port expected");
288       dp = portnum(sc, sc->d.buf);
289
290       /* --- Make the socket --- */
291
292       if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
293         error(sc, "couldn't create socket: %s", strerror(errno));
294
295       /* --- Set it to allow address reuse --- */
296
297       {
298         int opt = 1;
299         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
300       }
301
302       /* --- Bind it to the right port --- */
303
304       sin.sin_family = AF_INET;
305       sin.sin_addr.s_addr = htonl(INADDR_ANY);
306       sin.sin_port = sp;
307       if (bind(fd, (struct sockaddr *)&sin, sizeof(sin))) {
308         error(sc, "couldn't bind to port %i: %s",
309               ntohs(sp), strerror(errno));
310       }
311
312       /* --- Set it to listen for connections --- */
313
314       if (listen(fd, 5))
315         error(sc, "couldn't listen on socket: %s", strerror(errno));
316
317       /* --- Fill in a new listener --- */
318
319       memcpy(&sin.sin_addr, h->h_addr, sizeof(struct in_addr));
320       sin.sin_port = dp;
321       l = listener_add(fd, sp, &sin);
322
323       /* --- Snarf access controls and other attributes --- */
324
325       token(sc);
326       if (sc->t == '{') {
327         acl_entry **a = &l->acl;
328         token(sc);
329         while (sc->t != '}') {
330           if (sc->t != CTOK_WORD)
331             error(sc, "parse error, keyword or `}' expected");
332           getconf(sc, l, &a);
333           if (sc->t == ';')
334             token(sc);
335         }
336         *a = 0;
337         token(sc);
338       }
339     }
340
341     /* --- Other configuration is handled elsewhere --- */
342
343     else
344       getconf(sc, 0, 0);
345
346     if (sc->t == ';')
347       token(sc);
348   }
349 }
350
351 /*----- That's all, folks -------------------------------------------------*/