/*********************************************************************** * * options.c * * Code for parsing options out of configuration file. * * Copyright (C) 2002 by Roaring Penguin Software Inc. * * This software may be distributed under the terms of the GNU General * Public License, Version 2, or (at your option) any later version. * * LIC: GPL * ***********************************************************************/ static char const RCSID[] = "$Id: options.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; #include "l2tp.h" #include #include #include #include #include #include l2tp_settings Settings; static option_handler *option_handlers = NULL; /* Function for currently-active option context */ static int (*option_context_fn)(EventSelector *es, char const *name, char const *value); static int do_load_handler(EventSelector *es, l2tp_opt_descriptor *desc, char const *value); static int set_option(EventSelector *es, l2tp_opt_descriptor *desc, char const *value); /* Global options */ static l2tp_opt_descriptor global_opts[] = { /* name type addr */ { "load-handler", OPT_TYPE_CALLFUNC, (void *) do_load_handler }, { "listen-port", OPT_TYPE_PORT, &Settings.listen_port }, { "listen-addr", OPT_TYPE_IPADDR, &Settings.listen_addr }, { NULL, OPT_TYPE_BOOL, NULL } }; /********************************************************************** * %FUNCTION: do_load_handler * %ARGUMENTS: * es -- event selector * desc -- option descriptor * value -- name of handler to load * %RETURNS: * 0 on success, -1 on failure * %DESCRIPTION: * Loads a DLL as a handler ***********************************************************************/ static int do_load_handler(EventSelector *es, l2tp_opt_descriptor *desc, char const *value) { return l2tp_load_handler(es, value); } /********************************************************************** * %FUNCTION: set_option * %ARGUMENTS: * es -- event selector * desc -- option descriptor * value -- value string parsed from config file * %RETURNS: * -1 on error, 0 if all is OK * %DESCRIPTION: * Sets an option value. ***********************************************************************/ static int set_option(EventSelector *es, l2tp_opt_descriptor *desc, char const *value) { long x; char *end; struct hostent *he; int (*fn)(EventSelector *, l2tp_opt_descriptor *, char const *); switch(desc->type) { case OPT_TYPE_BOOL: if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on") || !strcasecmp(value, "1")) { * (int *) (desc->addr) = 1; return 0; } if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off") || !strcasecmp(value, "0")) { * (int *) (desc->addr) = 0; return 0; } l2tp_set_errmsg("Expecting boolean value, found '%s'", value); return -1; case OPT_TYPE_INT: case OPT_TYPE_PORT: x = strtol(value, &end, 0); if (*end) { l2tp_set_errmsg("Expecting integer value, found '%s'", value); return -1; } if (desc->type == OPT_TYPE_PORT) { if (x < 1 || x > 65535) { l2tp_set_errmsg("Port values must range from 1 to 65535"); return -1; } } * (int *) desc->addr = (int) x; return 0; case OPT_TYPE_IPADDR: he = gethostbyname(value); if (!he) { l2tp_set_errmsg("Could not resolve %s as IP address: %s", value, strerror(errno)); return -1; } memcpy(desc->addr, he->h_addr, sizeof(he->h_addr)); return 0; case OPT_TYPE_STRING: if (* (char **) desc->addr) { free(* (char **) desc->addr); } * (char **) desc->addr = strdup(value); if (! * (char *) desc->addr) { l2tp_set_errmsg("Out of memory"); return -1; } return 0; case OPT_TYPE_CALLFUNC: fn = (int (*)(EventSelector *, l2tp_opt_descriptor *, char const *)) desc->addr; return fn(es, desc, value); } l2tp_set_errmsg("Unknown value type %d", desc->type); return -1; } /********************************************************************** * %FUNCTION: chomp_word * %ARGUMENTS: * line -- the input line * word -- buffer for storing word * %RETURNS: * Updated value of line * %DESCRIPTION: * Chomps a word from line ***********************************************************************/ char const * l2tp_chomp_word(char const *line, char *word) { *word = 0; /* Chew up whitespace */ while(*line && isspace(*line)) line++; if (*line != '"') { /* Not quoted string */ while (*line && !isspace(*line)) { *word++ = *line++; } *word = 0; return line; } /* Quoted string */ line++; while(*line) { if (*line != '\\') { if (*line == '"') { line++; *word = 0; return line; } *word++ = *line++; continue; } line++; if (*line) *word++ = *line++; } *word = 0; return line; } /********************************************************************** * %FUNCTION: split_line_into_words * %ARGUMENTS: * line -- the input line * name, value -- buffers which are large enough to contain all chars in line * %RETURNS: * Nothing * %DESCRIPTION: * Splits line into two words. A word is: * - Non-whitespace chars: foobarbazblech_3 * - Quoted text: "Here is text \"with embedded quotes\"" ***********************************************************************/ static void split_line_into_words(char const *line, char *name, char *value) { line = l2tp_chomp_word(line, name); line = l2tp_chomp_word(line, value); } /********************************************************************** * %FUNCTION: parser_switch_context * %ARGUMENTS: * name, value -- words read from line. Either "global ..ignored.." * or "section context" * %RETURNS: * 0 if context-switch proceeded OK, -1 if not. * %DESCRIPTION: * Switches configuration contexts ***********************************************************************/ static int parser_switch_context(EventSelector *es, char const *name, char const *value) { int r; option_handler *handler; /* Switch out of old context */ if (option_context_fn) { r = option_context_fn(es, "*end*", "*end*"); option_context_fn = NULL; if (r < 0) return r; } if (!strcasecmp(name, "global")) { return 0; } /* Must be "section foo" */ handler = option_handlers; while(handler) { if (!strcasecmp(value, handler->section)) { option_context_fn = handler->process_option; option_context_fn(es, "*begin*", "*begin*"); return 0; } handler = handler->next; } l2tp_set_errmsg("No handler for section %s", value); return -1; } /********************************************************************** * %FUNCTION: option_set * %ARGUMENTS: * es -- event selector * name -- name of option * value -- value of option * descriptors -- array of option descriptors for this context * %RETURNS: * 0 on success, -1 on failure * %DESCRIPTION: * Sets an option ***********************************************************************/ int l2tp_option_set(EventSelector *es, char const *name, char const *value, l2tp_opt_descriptor descriptors[]) { int i; for (i=0; descriptors[i].name; i++) { if (!strcasecmp(descriptors[i].name, name)) { return set_option(es, &descriptors[i], value); } } l2tp_set_errmsg("Option %s is not known in this context", name); return -1; } /********************************************************************** * %FUNCTION: option_register_section * %ARGUMENTS: * handler -- an option-handler * %RETURNS: * Nothing * %DESCRIPTION: * Adds handler to linked-list of sections. ***********************************************************************/ void l2tp_option_register_section(option_handler *h) { h->next = option_handlers; option_handlers = h; } /********************************************************************** * %FUNCTION: handle_option * %ARGUMENTS: * es -- event selector * name -- name of option * value -- option's value * %RETURNS: * 0 on success, -1 on failure * %DESCRIPTION: * Handles an option ***********************************************************************/ static int handle_option(EventSelector *es, char const *name, char const *value) { if (option_context_fn) { return option_context_fn(es, name, value); } return l2tp_option_set(es, name, value, global_opts); } /********************************************************************** * %FUNCTION: parse_config_file * %ARGUMENTS: * es -- event selector * fname -- filename to parse * %RETURNS: * -1 on error, 0 if all is OK * %DESCRIPTION: * Parses configuration file. ***********************************************************************/ int l2tp_parse_config_file(EventSelector *es, char const *fname) { char buf[512]; char name[512]; char value[512]; int r = 0; size_t l; char *line; FILE *fp; /* Defaults */ Settings.listen_port = 1701; Settings.listen_addr.s_addr = htonl(INADDR_ANY); fp = fopen(fname, "r"); if (!fp) { l2tp_set_errmsg("Could not open '%s' for reading: %s", fname, strerror(errno)); return -1; } /* Start in global context */ option_context_fn = NULL; while (fgets(buf, sizeof(buf), fp) != NULL) { l = strlen(buf); if (l && (buf[l] == '\n')) { buf[l--] = 0; } /* Skip leading whitespace */ line = buf; while(*line && isspace(*line)) line++; /* Ignore blank lines and comments */ if (!*line || *line == '#') { continue; } /* Split line into two words */ split_line_into_words(line, name, value); /* Check for context switch */ if (!strcasecmp(name, "global") || !strcasecmp(name, "section")) { r = parser_switch_context(es, name, value); if (r < 0) break; continue; } r = handle_option(es, name, value); if (r < 0) break; } fclose(fp); if (r >= 0) { if (option_context_fn) { r = option_context_fn(es, "*end*", "*end*"); option_context_fn = NULL; } } return r; }