1/***********************************************************************
2*
3* options.c
4*
5* Code for parsing options out of configuration file.
6*
7* Copyright (C) 2002 by Roaring Penguin Software Inc.
8*
9* This software may be distributed under the terms of the GNU General
10* Public License, Version 2, or (at your option) any later version.
11*
12* LIC: GPL
13*
14***********************************************************************/
15
16static char const RCSID[] =
17"$Id: options.c 3323 2011-09-21 18:45:48Z lly.dev $";
18
19#include "l2tp.h"
20#include <string.h>
21#include <stdlib.h>
22#include <errno.h>
23#include <netdb.h>
24#include <stdio.h>
25#include <ctype.h>
26
27l2tp_settings Settings;
28
29static option_handler *option_handlers = NULL;
30
31/* Function for currently-active option context */
32static int (*option_context_fn)(EventSelector *es,
33				char const *name, char const *value);
34static int do_load_handler(EventSelector *es,
35			   l2tp_opt_descriptor *desc, char const *value);
36static int set_option(EventSelector *es,
37		      l2tp_opt_descriptor *desc, char const *value);
38
39/* Global options */
40static l2tp_opt_descriptor global_opts[] = {
41    /*  name               type                 addr */
42    { "load-handler",      OPT_TYPE_CALLFUNC,   (void *) do_load_handler },
43    { "listen-port",       OPT_TYPE_PORT,       &Settings.listen_port },
44    { "listen-addr",       OPT_TYPE_IPADDR,     &Settings.listen_addr },
45    { NULL,                OPT_TYPE_BOOL,       NULL }
46};
47
48/**********************************************************************
49* %FUNCTION: do_load_handler
50* %ARGUMENTS:
51*  es -- event selector
52*  desc -- option descriptor
53*  value -- name of handler to load
54* %RETURNS:
55*  0 on success, -1 on failure
56* %DESCRIPTION:
57*  Loads a DLL as a handler
58***********************************************************************/
59static int
60do_load_handler(EventSelector *es,
61		l2tp_opt_descriptor *desc,
62		char const *value)
63{
64    return l2tp_load_handler(es, value);
65}
66
67/**********************************************************************
68* %FUNCTION: set_option
69* %ARGUMENTS:
70*  es -- event selector
71*  desc -- option descriptor
72*  value -- value string parsed from config file
73* %RETURNS:
74*  -1 on error, 0 if all is OK
75* %DESCRIPTION:
76*  Sets an option value.
77***********************************************************************/
78static int
79set_option(EventSelector *es,
80	   l2tp_opt_descriptor *desc,
81	   char const *value)
82{
83    long x;
84    char *end;
85    struct hostent *he;
86    int (*fn)(EventSelector *, l2tp_opt_descriptor *, char const *);
87
88    switch(desc->type) {
89    case OPT_TYPE_BOOL:
90	if (!strcasecmp(value, "true") ||
91	    !strcasecmp(value, "yes") ||
92	    !strcasecmp(value, "on") ||
93	    !strcasecmp(value, "1")) {
94	    * (int *) (desc->addr) = 1;
95	    return 0;
96	}
97	if (!strcasecmp(value, "false") ||
98	    !strcasecmp(value, "no") ||
99	    !strcasecmp(value, "off") ||
100	    !strcasecmp(value, "0")) {
101	    * (int *) (desc->addr) = 0;
102	    return 0;
103	}
104	l2tp_set_errmsg("Expecting boolean value, found '%s'", value);
105	return -1;
106
107    case OPT_TYPE_INT:
108    case OPT_TYPE_PORT:
109	x = strtol(value, &end, 0);
110	if (*end) {
111	    l2tp_set_errmsg("Expecting integer value, found '%s'", value);
112	    return -1;
113	}
114	if (desc->type == OPT_TYPE_PORT) {
115	    if (x < 1 || x > 65535) {
116		l2tp_set_errmsg("Port values must range from 1 to 65535");
117		return -1;
118	    }
119	}
120
121	* (int *) desc->addr = (int) x;
122	return 0;
123
124    case OPT_TYPE_IPADDR:
125	he = gethostbyname(value);
126	if (!he) {
127	    l2tp_set_errmsg("Could not resolve %s as IP address: %s",
128		       value, strerror(errno));
129	    return -1;
130	}
131
132	memcpy(desc->addr, he->h_addr, sizeof(he->h_addr));
133	return 0;
134
135    case OPT_TYPE_STRING:
136	if (* (char **) desc->addr) {
137	    free(* (char **) desc->addr);
138	}
139	* (char **) desc->addr = strdup(value);
140	if (! * (char **) desc->addr) {
141	    l2tp_set_errmsg("Out of memory");
142	    return -1;
143	}
144	return 0;
145
146    case OPT_TYPE_CALLFUNC:
147	fn = (int (*)(EventSelector *, l2tp_opt_descriptor *, char const *)) desc->addr;
148	return fn(es, desc, value);
149    }
150    l2tp_set_errmsg("Unknown value type %d", desc->type);
151    return -1;
152}
153
154/**********************************************************************
155* %FUNCTION: chomp_word
156* %ARGUMENTS:
157*  line -- the input line
158*  word -- buffer for storing word
159* %RETURNS:
160*  Updated value of line
161* %DESCRIPTION:
162*  Chomps a word from line
163***********************************************************************/
164char const *
165l2tp_chomp_word(char const *line, char *word)
166{
167    *word = 0;
168
169    /* Chew up whitespace */
170    while(*line && isspace(*line)) line++;
171
172    if (*line != '"') {
173	/* Not quoted string */
174	while (*line && !isspace(*line)) {
175	    *word++ = *line++;
176	}
177	*word = 0;
178	return line;
179    }
180
181    /* Quoted string */
182    line++;
183    while(*line) {
184	if (*line != '\\') {
185	    if (*line == '"') {
186		line++;
187		*word = 0;
188		return line;
189	    }
190	    *word++ = *line++;
191	    continue;
192	}
193	line++;
194	if (*line) *word++ = *line++;
195    }
196    *word = 0;
197    return line;
198}
199
200/**********************************************************************
201* %FUNCTION: split_line_into_words
202* %ARGUMENTS:
203*  line -- the input line
204*  name, value -- buffers which are large enough to contain all chars in line
205* %RETURNS:
206*  Nothing
207* %DESCRIPTION:
208*  Splits line into two words.  A word is:
209*    - Non-whitespace chars:  foobarbazblech_3
210*    - Quoted text:           "Here is text \"with embedded quotes\""
211***********************************************************************/
212static void
213split_line_into_words(char const *line, char *name, char *value)
214{
215    line = l2tp_chomp_word(line, name);
216    line = l2tp_chomp_word(line, value);
217}
218
219/**********************************************************************
220* %FUNCTION: parser_switch_context
221* %ARGUMENTS:
222*  name, value -- words read from line.  Either "global ..ignored.."
223*                 or "section context"
224* %RETURNS:
225*  0 if context-switch proceeded OK, -1 if not.
226* %DESCRIPTION:
227*  Switches configuration contexts
228***********************************************************************/
229static int
230parser_switch_context(EventSelector *es,
231		      char const *name,
232		      char const *value)
233{
234    int r;
235    option_handler *handler;
236
237    /* Switch out of old context */
238    if (option_context_fn) {
239	r = option_context_fn(es, "*end*", "*end*");
240	option_context_fn = NULL;
241	if (r < 0) return r;
242    }
243
244    if (!strcasecmp(name, "global")) {
245	return 0;
246    }
247
248    /* Must be "section foo" */
249    handler = option_handlers;
250    while(handler) {
251	if (!strcasecmp(value, handler->section)) {
252	    option_context_fn = handler->process_option;
253	    option_context_fn(es, "*begin*", "*begin*");
254	    return 0;
255	}
256	handler = handler->next;
257    }
258    l2tp_set_errmsg("No handler for section %s", value);
259    return -1;
260}
261
262/**********************************************************************
263* %FUNCTION: option_set
264* %ARGUMENTS:
265*  es -- event selector
266*  name -- name of option
267*  value -- value of option
268*  descriptors -- array of option descriptors for this context
269* %RETURNS:
270*  0 on success, -1 on failure
271* %DESCRIPTION:
272*  Sets an option
273***********************************************************************/
274int
275l2tp_option_set(EventSelector *es,
276		char const *name,
277		char const *value,
278		l2tp_opt_descriptor descriptors[])
279{
280    int i;
281
282    for (i=0; descriptors[i].name; i++) {
283	if (!strcasecmp(descriptors[i].name, name)) {
284	    return set_option(es, &descriptors[i], value);
285	}
286    }
287    l2tp_set_errmsg("Option %s is not known in this context",
288	       name);
289    return -1;
290}
291
292/**********************************************************************
293* %FUNCTION: option_register_section
294* %ARGUMENTS:
295*  handler -- an option-handler
296* %RETURNS:
297*  Nothing
298* %DESCRIPTION:
299*  Adds handler to linked-list of sections.
300***********************************************************************/
301void
302l2tp_option_register_section(option_handler *h)
303{
304    h->next = option_handlers;
305    option_handlers = h;
306}
307
308/**********************************************************************
309* %FUNCTION: handle_option
310* %ARGUMENTS:
311*  es -- event selector
312*  name -- name of option
313*  value -- option's value
314* %RETURNS:
315*  0 on success, -1 on failure
316* %DESCRIPTION:
317*  Handles an option
318***********************************************************************/
319static int
320handle_option(EventSelector *es,
321	      char const *name,
322	      char const *value)
323{
324    if (option_context_fn) {
325	return option_context_fn(es, name, value);
326    }
327
328    return l2tp_option_set(es, name, value, global_opts);
329}
330
331/**********************************************************************
332* %FUNCTION: parse_config_file
333* %ARGUMENTS:
334*  es -- event selector
335*  fname -- filename to parse
336* %RETURNS:
337*  -1 on error, 0 if all is OK
338* %DESCRIPTION:
339*  Parses configuration file.
340***********************************************************************/
341int
342l2tp_parse_config_file(EventSelector *es,
343		       char const *fname)
344{
345    char buf[512];
346    char name[512];
347    char value[512];
348    int r = 0;
349    size_t l;
350    char *line;
351    FILE *fp;
352
353    /* Defaults */
354    Settings.listen_port = 1701;
355    Settings.listen_addr.s_addr = htonl(INADDR_ANY);
356
357    fp = fopen(fname, "r");
358    if (!fp) {
359	l2tp_set_errmsg("Could not open '%s' for reading: %s",
360		   fname, strerror(errno));
361	return -1;
362    }
363
364    /* Start in global context */
365    option_context_fn = NULL;
366    while (fgets(buf, sizeof(buf), fp) != NULL) {
367	l = strlen(buf);
368	if (l && (buf[l] == '\n')) {
369	    buf[l--] = 0;
370	}
371
372	/* Skip leading whitespace */
373	line = buf;
374	while(*line && isspace(*line)) line++;
375
376	/* Ignore blank lines and comments */
377	if (!*line || *line == '#') {
378	    continue;
379	}
380
381	/* Split line into two words */
382	split_line_into_words(line, name, value);
383
384	/* Check for context switch */
385	if (!strcasecmp(name, "global") ||
386	    !strcasecmp(name, "section")) {
387	    r = parser_switch_context(es, name, value);
388	    if (r < 0) break;
389	    continue;
390	}
391
392	r = handle_option(es, name, value);
393	if (r < 0) break;
394    }
395    fclose(fp);
396    if (r >= 0) {
397	if (option_context_fn) {
398	    r = option_context_fn(es, "*end*", "*end*");
399	    option_context_fn = NULL;
400	}
401    }
402
403    return r;
404}
405