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