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