Minor modifications for new design.
[fwd] / conf.c
CommitLineData
e82f7154 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
77static 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
133static 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
153static 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
174static 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
242void 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 -------------------------------------------------*/