From 9d0466784892c7768c68e0bd02cf76db2ab1185a Mon Sep 17 00:00:00 2001 From: Robert James Kaes Date: Fri, 13 Aug 2004 20:19:50 +0000 Subject: Added the new configuration parsing system (conffile.c and conffile.h.) The new system is intended to replace the existing grammar.y and scanner.l files. I don't want to depend on flex/bison any longer. --- src/Makefile.am | 3 +- src/conffile.c | 612 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/conffile.h | 24 +++ 3 files changed, 638 insertions(+), 1 deletion(-) create mode 100644 src/conffile.c create mode 100644 src/conffile.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index a45e6c1..438a2d0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -# $Id: Makefile.am,v 1.18 2003-08-07 16:32:12 rjkaes Exp $ +# $Id: Makefile.am,v 1.19 2004-08-13 20:19:50 rjkaes Exp $ # # Copyright (C) 2000 Robert James Kaes (rjkaes@flarenet.com) # @@ -24,6 +24,7 @@ tinyproxy_SOURCES = \ buffer.c buffer.h \ child.c child.h \ common.h \ + conffile.c conffile.h \ conns.c conns.h \ daemon.c daemon.h \ hashmap.c hashmap.h \ diff --git a/src/conffile.c b/src/conffile.c new file mode 100644 index 0000000..e7a3373 --- /dev/null +++ b/src/conffile.c @@ -0,0 +1,612 @@ +/* $Id: conffile.c,v 1.1 2004-08-13 20:19:50 rjkaes Exp $ + * + * Parses the configuration file and sets up the config_s structure for + * use by the application. This file replaces the old grammar.y and + * scannar.l files. It takes up less space and _I_ think is easier to + * add new directives to. Who knows if I'm right though. + * + * Copyright (C) 2004 Robert James Kaes (rjkaes@users.sourceforge.net) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include "tinyproxy.h" + +#include "conffile.h" + +#include "acl.h" +#include "anonymous.h" +#include "child.h" +#include "filter.h" +#include "heap.h" +#include "htmlerror.h" +#include "log.h" +#include "reqs.h" + +/* + * The configuration directives are defined in the structure below. Each + * directive requires a regular expression to match against, and a + * function to call when the regex is matched. + * + * Below are defined certain constant regular expression strings that + * can (and likely should) be used when building the regex for the + * given directive. + */ +#define WS "[[:space:]]+" +#define STR "\"([^\"]+)\"" +#define BOOL "(yes|on|no|off)" +#define INT "((0x)?[[:digit:]]+)" +#define ALNUM "([-a-z0-9._]+)" +#define IP "((([0-9]{1,3})\\.){3}[0-9]{1,3})" +#define IPMASK "(" IP "(/[[:digit:]]+)?)" +#define BEGIN "^[[:space:]]*" +#define END "[[:space:]]*$" + +/* + * Limit the maximum number of substring matches to a reasonably high + * number. Given the usual structure of the configuration file, sixteen + * substring matches should be plenty. + */ +#define RE_MAX_MATCHES 16 + + +/* + * All configuration handling functions are REQUIRED to be defined + * with the same function template as below. + */ +typedef int (*CONFFILE_HANDLER)(struct config_s*, const char*, regmatch_t[]); + +/* + * Define the pattern used by any directive handling function. The + * following arguments are defined: + * + * struct config_s* conf pointer to the current configuration structure + * const char* line full line matched by the regular expression + * regmatch_t match[] offsets to the substrings matched + * + * The handling function must return 0 if the directive was processed + * properly. Any errors are reported by returning a non-zero value. + */ +#define HANDLE_FUNC(func) int func(struct config_s* conf, const char* line, regmatch_t match[]) + + +/* + * This is a do nothing function used for the comment and blank lines + * in the configuration file. We don't do anything for those, but + * the function pointer needs to be defined to something so we simply + * return true for those lines. + */ +static HANDLE_FUNC(handle_nop) +{ + return 0; +} + +/* + * List all the handling functions. These are defined later, but they need + * to be in-scope before the big structure below. + */ + +static HANDLE_FUNC(handle_allow); +static HANDLE_FUNC(handle_anonymous); +static HANDLE_FUNC(handle_bind); +static HANDLE_FUNC(handle_bindsame); +static HANDLE_FUNC(handle_connectport); +static HANDLE_FUNC(handle_defaulterrorfile); +static HANDLE_FUNC(handle_deny); +static HANDLE_FUNC(handle_errorfile); +static HANDLE_FUNC(handle_errorfile); +#ifdef FILTER_ENABLE +static HANDLE_FUNC(handle_filter); +static HANDLE_FUNC(handle_filtercasesensitive); +static HANDLE_FUNC(handle_filterdefaultdeny); +static HANDLE_FUNC(handle_filterextended); +static HANDLE_FUNC(handle_filterurls); +#endif +static HANDLE_FUNC(handle_group); +static HANDLE_FUNC(handle_listen); +static HANDLE_FUNC(handle_logfile); +static HANDLE_FUNC(handle_logfile); +static HANDLE_FUNC(handle_loglevel); +static HANDLE_FUNC(handle_maxclients); +static HANDLE_FUNC(handle_maxrequestsperchild); +static HANDLE_FUNC(handle_maxspareservers); +static HANDLE_FUNC(handle_minspareservers); +static HANDLE_FUNC(handle_pidfile); +static HANDLE_FUNC(handle_port); +#ifdef REVERSE_SUPPORT +static HANDLE_FUNC(handle_reversebaseurl); +static HANDLE_FUNC(handle_reversemagic); +static HANDLE_FUNC(handle_reverseonly); +static HANDLE_FUNC(handle_reversepath); +#endif +static HANDLE_FUNC(handle_startservers); +static HANDLE_FUNC(handle_statfile); +static HANDLE_FUNC(handle_stathost); +static HANDLE_FUNC(handle_syslog); +static HANDLE_FUNC(handle_timeout); +//static HANDLE_FUNC(handle_upstream); +static HANDLE_FUNC(handle_user); +static HANDLE_FUNC(handle_viaproxyname); +#ifdef XTINYPROXY_ENABLE +static HANDLE_FUNC(handle_xtinyproxy); +#endif + + +/* + * This macro can be used to make standard directives in the form: + * directive arguments [arguments ...] + * + * The directive itself will be the first matched substring. + * + * Note that this macro is not required. As you can see below, the + * comment and blank line elements are defined explicitly since they + * do not follow the pattern above. This macro is for convenience + * only. + */ +#define STDCONF(d, re, func) { BEGIN "(" d ")" WS re END, func, NULL } + + +/* + * Holds the regular expression used to match the configuration directive, + * the function pointer to the rountine to handle the directive, and + * for internal use, a pointer to the compiled regex so it only needs + * to be compiled one. + */ +struct { + const char* re; + CONFFILE_HANDLER handler; + regex_t* cre; +} directives[] = { + /* comments */ + { BEGIN "#", handle_nop }, + + /* blank lines */ + { "^[[:space:]]+$", handle_nop }, + + /* string arguments */ + STDCONF("logfile", STR, handle_logfile), + STDCONF("pidfile", STR, handle_pidfile), + STDCONF("anonymous", STR, handle_anonymous), + STDCONF("viaproxyname", STR, handle_viaproxyname), + STDCONF("defaulterrorfile", STR, handle_defaulterrorfile), + STDCONF("statfile", STR, handle_statfile), + STDCONF("stathost", STR, handle_stathost), + STDCONF("xtinyproxy", STR, handle_xtinyproxy), + + /* boolean arguments */ + STDCONF("syslog", BOOL, handle_syslog), + STDCONF("bindsame", BOOL, handle_bindsame), + + /* integer arguments */ + STDCONF("port", INT, handle_port), + STDCONF("maxclients", INT, handle_maxclients), + STDCONF("maxspareservers", INT, handle_maxspareservers), + STDCONF("minspareservers", INT, handle_minspareservers), + STDCONF("startservers", INT, handle_startservers), + STDCONF("maxrequestsperchild", INT, handle_maxrequestsperchild), + STDCONF("timeout", INT, handle_timeout), + STDCONF("connectport", INT, handle_connectport), + + /* alphanumeric arguments */ + STDCONF("user", ALNUM, handle_user), + STDCONF("group", ALNUM, handle_group), + + /* ip arguments */ + STDCONF("listen", IP, handle_listen), + STDCONF("allow", "(" IPMASK "|" ALNUM ")", handle_allow), + STDCONF("deny", "(" IPMASK "|" ALNUM ")", handle_deny), + STDCONF("bind", IP, handle_bind), + + /* error files */ + STDCONF("errorfile", INT WS STR, handle_errorfile), + +#ifdef FILTER_ENABLE + STDCONF("filter", STR, handle_filter), + STDCONF("filterurls", BOOL, handle_filterurls), + STDCONF("filterextended", BOOL, handle_filterextended), + STDCONF("filterdefaultdeny", BOOL, handle_filterdefaultdeny), + STDCONF("filtercasesensitive", BOOL, handle_filtercasesensitive), +#endif + + +#ifdef REVERSE_SUPPORT + /* Reverse proxy arguments */ + STDCONF("reversebaseurl", STR, handle_reversebaseurl), + STDCONF("reverseonly", BOOL, handle_reverseonly), + STDCONF("reversemagic", BOOL, handle_reversemagic), + STDCONF("reversepath", STR WS STR, handle_reversepath), +#endif + + /* upstream is rather complicated */ +// { BEGIN "(no[[:space:]]+)?upstream" WS, handle_upstream }, + + /* loglevel */ + STDCONF("loglevel", "(critical|error|warning|notice|connect|info)", handle_loglevel) +}; +const unsigned int ndirectives = sizeof(directives)/sizeof(directives[0]); + +/* + * Compiles the regular expressions used by the configuration file. This + * routine MUST be called before trying to parse the configuration file. + * + * Returns 0 on success; negative upon failure. + */ +int +config_compile(void) +{ + int i, r; + + for (i = 0; i != ndirectives; ++i) { + assert(!directives[i].cre); + + directives[i].cre = safemalloc(sizeof(regex_t)); + if (!directives[i].cre) + return -1; + + r = regcomp(directives[i].cre, + directives[i].re, + REG_EXTENDED | REG_ICASE | REG_NEWLINE); + + if (r) return r; + } + return 0; +} + + +/* + * Attempt to match the supplied line with any of the configuration + * regexes defined above. If a match is found, call the handler + * function to process the directive. + * + * Returns 0 if a match was found and successfully processed; otherwise, + * a negative number is returned. + */ +static int +check_match(struct config_s* conf, const char* line) +{ + regmatch_t match[RE_MAX_MATCHES]; + unsigned int i; + + assert(ndirectives > 0); + + for (i = 0; i != ndirectives; ++i) { + assert(directives[i].cre); + if (!regexec(directives[i].cre, line, RE_MAX_MATCHES, match, 0)) { + assert(directives[i].handler); + return (*directives[i].handler)(conf, line, match); + } + } + + return -1; +} + +/* + * Parse the previously opened configuration stream. + */ +int +config_parse(struct config_s* conf, FILE* f) +{ + char buffer[1024]; /* 1KB lines should be plenty */ + unsigned long lineno = 1; + + while (fgets(buffer, sizeof(buffer), f)) { + if (check_match(conf, buffer)) { + printf("Problem with line %ld\n", lineno); + return 1; + } + ++lineno; + } + return 0; +} + + +/* + * Functions to handle the various configuration file directives. + */ + +/* + * String arguments. + */ + +static char* +get_string_arg(const char* line, regmatch_t* match) +{ + char *p; + const unsigned int len = match->rm_eo - match->rm_so; + + assert(line); + assert(len > 0); + + p = safemalloc(len + 1); + if (!p) + return NULL; + + memcpy(p, line + match->rm_so, len); + p[len] = '\0'; + return p; +} + +static int +set_string_arg(char** var, const char* line, regmatch_t* match) +{ + char* arg = get_string_arg(line, match); + if (!arg) + return -1; + *var = safestrdup(arg); + safefree(arg); + return *var ? 0 : -1; +} + +static HANDLE_FUNC(handle_logfile) +{ + return set_string_arg(&conf->logf_name, line, &match[2]); +} +static HANDLE_FUNC(handle_pidfile) +{ + return set_string_arg(&conf->pidpath, line, &match[2]); +} +static HANDLE_FUNC(handle_anonymous) +{ + char *arg = get_string_arg(line, &match[2]); + if (!arg) + return -1; + + anonymous_insert(arg); + safefree(arg); + return 0; +} +static HANDLE_FUNC(handle_viaproxyname) +{ + return set_string_arg(&conf->via_proxy_name, line, &match[2]); +} +static HANDLE_FUNC(handle_defaulterrorfile) +{ + return set_string_arg(&conf->errorpage_undef, line, &match[2]); +} +static HANDLE_FUNC(handle_statfile) +{ + return set_string_arg(&conf->statpage, line, &match[2]); +} +static HANDLE_FUNC(handle_stathost) +{ + return set_string_arg(&conf->stathost, line, &match[2]); +} +static HANDLE_FUNC(handle_xtinyproxy) +{ + return set_string_arg(&conf->my_domain, line, &match[2]); +} + + +/* + * Boolean arguments. + */ + +static int +get_bool_arg(const char* line, regmatch_t* match) +{ + assert(line); + assert(match && match->rm_so != -1); + + const char* p = line + match->rm_so; + + /* "y"es or o"n" map as true, otherwise it's false. */ + if (tolower(p[0]) == 'y' || tolower(p[1]) == 'n') + return 1; + else + return 0; +} + +static int +set_bool_arg(unsigned int* var, const char* line, regmatch_t* match) +{ + assert(var); + assert(line); + assert(match && match->rm_so != -1); + + *var = get_bool_arg(line, match); + return 0; +} + +static HANDLE_FUNC(handle_syslog) +{ + return set_bool_arg(&conf->syslog, line, &match[2]); +} +static HANDLE_FUNC(handle_bindsame) +{ + return set_bool_arg(&conf->bindsame, line, &match[2]); +} + + +/* + * Integer arguments. + */ + +static inline long int +get_int_arg(const char* line, regmatch_t* match) +{ + assert(line); + assert(match && match->rm_so != -1); + + return strtol(line + match->rm_so, NULL, 0); +} +static int +set_int_arg(int long* var, const char* line, regmatch_t* match) +{ + assert(var); + assert(line); + assert(match); + + *var = get_int_arg(line, match); + return 0; +} +static HANDLE_FUNC(handle_port) +{ + return set_int_arg((long int*)&conf->port, line, &match[2]); +} +static HANDLE_FUNC(handle_maxclients) +{ + child_configure(CHILD_MAXCLIENTS, get_int_arg(line, &match[2])); + return 0; +} +static HANDLE_FUNC(handle_maxspareservers) +{ + child_configure(CHILD_MAXSPARESERVERS, get_int_arg(line, &match[2])); + return 0; +} +static HANDLE_FUNC(handle_minspareservers) +{ + child_configure(CHILD_MINSPARESERVERS, get_int_arg(line, &match[2])); + return 0; +} +static HANDLE_FUNC(handle_startservers) +{ + child_configure(CHILD_STARTSERVERS, get_int_arg(line, &match[2])); + return 0; +} +static HANDLE_FUNC(handle_maxrequestsperchild) +{ + child_configure(CHILD_MAXREQUESTSPERCHILD, get_int_arg(line, &match[2])); + return 0; +} +static HANDLE_FUNC(handle_timeout) +{ + return set_int_arg((long int*)&conf->idletimeout, line, &match[2]); +} +static HANDLE_FUNC(handle_connectport) +{ + add_connect_port_allowed(get_int_arg(line, &match[2])); + return 0; +} + + +/* + * Alpha numeric argument + */ +static HANDLE_FUNC(handle_user) +{ + return set_string_arg(&conf->username, line, &match[2]); +} +static HANDLE_FUNC(handle_group) +{ + return set_string_arg(&conf->group, line, &match[2]); +} + + +/* + * IP addresses + */ +static HANDLE_FUNC(handle_allow) +{ + char* arg = get_string_arg(line, &match[2]); + insert_acl(arg, ACL_ALLOW); + safefree(arg); + return 0; +} +static HANDLE_FUNC(handle_deny) +{ + char *arg = get_string_arg(line, &match[2]); + insert_acl(arg, ACL_DENY); + safefree(arg); + return 0; +} +static HANDLE_FUNC(handle_bind) +{ + return set_string_arg(&conf->bind_address, line, &match[2]); +} +static HANDLE_FUNC(handle_listen) +{ + return set_string_arg(&conf->ipAddr, line, &match[2]); +} + + +/* + * Error file has a integer and string argument + */ +static HANDLE_FUNC(handle_errorfile) +{ + long int err = get_int_arg(line, &match[2]); + char *page = get_string_arg(line, &match[3]); + add_new_errorpage(page, err); + safefree(page); + return 0; +} + + +/* + * Log level's are strings. + */ +struct log_levels_s { + const char* string; + int level; +}; +static struct log_levels_s log_levels[] = { + { "critical", LOG_CRIT }, + { "error", LOG_ERR }, + { "warning", LOG_WARNING }, + { "notice", LOG_NOTICE }, + { "connect", LOG_CONN }, + { "info", LOG_INFO } +}; + +static HANDLE_FUNC(handle_loglevel) +{ + static const unsigned int nlevels = sizeof(log_levels)/sizeof(log_levels[0]); + unsigned int i; + + char *arg = get_string_arg(line, &match[2]); + for (i = 0; i != nlevels; ++i) { + if (!strcasecmp(arg, log_levels[i].string)) { + set_log_level(log_levels[i].level); + return 0; + } + } + return -1; +} + + +#ifdef FILTER_ENABLE +static HANDLE_FUNC(handle_filter) +{ + return set_string_arg(&conf->filter, line, &match[2]); +} +static HANDLE_FUNC(handle_filterurls) +{ + return set_bool_arg(&conf->filter_url, line, &match[2]); +} +static HANDLE_FUNC(handle_filterextended) +{ + return set_bool_arg(&conf->filter_extended, line, &match[2]); +} +static HANDLE_FUNC(handle_filterdefaultdeny) +{ + assert(match[2].rm_so != -1); + + if (get_bool_arg(line, &match[2])) + filter_set_default_policy(FILTER_DEFAULT_DENY); + return 0; +} +static HANDLE_FUNC(handle_filtercasesensitive) +{ + return set_bool_arg(&conf->filter_casesensitive, line, &match[2]); +} +#endif + + +#ifdef REVERSE_SUPPORT +static HANDLE_FUNC(handle_reverseonly) +{ + return set_bool_arg(&conf->reverseonly, line, &match[2]); +} +static HANDLE_FUNC(handle_reversemagic) +{ + return set_bool_arg(&conf->reversemagic, line, &match[2]); +} +#endif diff --git a/src/conffile.h b/src/conffile.h new file mode 100644 index 0000000..518bf3b --- /dev/null +++ b/src/conffile.h @@ -0,0 +1,24 @@ +/* $Id: conffile.h,v 1.1 2004-08-13 20:19:50 rjkaes Exp $ + * + * See 'conffile.c' for more details. + * + * Copyright (C) 2004 Robert James Kaes (rjkaes@users.sourceforge.net) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef TINYPROXY_CONFFILE_H +#define TINYPROXY_CONFFILE_H + +extern int config_compile(void); +extern int config_parse(struct config_s* conf, FILE* f); + +#endif -- cgit v1.2.3