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