From ffb4e6d398f7330ae13872fef0ba57d355744477 Mon Sep 17 00:00:00 2001 From: Daniel Adolfsson Date: Tue, 31 Jan 2012 23:53:45 +0100 Subject: [PATCH] New configuration implementation First step in removing the libconfuse dependency. --- src/conf.cc | 309 +++++++++++++++++++++++++++++++++++---------------- src/conf.h | 49 ++++++-- src/ndppd.cc | 78 ++++++++++++- 3 files changed, 330 insertions(+), 106 deletions(-) diff --git a/src/conf.cc b/src/conf.cc index 13bd109..e885756 100644 --- a/src/conf.cc +++ b/src/conf.cc @@ -15,136 +15,251 @@ // along with this program. If not, see . #include #include +#include +#include +#include +#include +#include #include -#include #include "ndppd.h" NDPPD_NS_BEGIN -void conf::error_printf(cfg_t *cfg, const char *fmt, va_list ap) +conf::conf() : + _is_block(false) { - char buf[256]; - if (vsnprintf(buf, sizeof(buf), fmt, ap) <= 0) - return; - - logger::error() << "[Config] " << buf; } -int conf::validate_rule(cfg_t *cfg, cfg_opt_t *opt) +const std::string &conf::value() const { - struct in6_addr addr, mask; - - cfg_t *rule_cfg = cfg_opt_getnsec(opt, cfg_opt_size(opt) - 1); - - if (!rule_cfg) - return -1; - - // TODO: Maybe we should validate IP here? - - return 0; + return _value; } -bool conf::setup(cfg_t *cfg) +bool conf::bool_value() const { - int i; + if (!strcasecmp(_value.c_str(), "true") || !strcasecmp(_value.c_str(), "yes")) + return true; + else + return false; +} - for (i = 0; i < cfg_size(cfg, "proxy"); i++) { - cfg_t *proxy_cfg = cfg_getnsec(cfg, "proxy", i); +int conf::int_value() const +{ + return atoi(_value.c_str()); +} - if (proxy_cfg) { - cfg_t *rule_cfg; - int i2; +void conf::value(const std::string &value) +{ + _value = value; +} - std::shared_ptr pr = proxy::open(cfg_title(proxy_cfg)); +std::shared_ptr conf::load(const std::string &path) +{ + std::ifstream ifs; + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); - if (!pr) - continue; + try { + ifs.open(path, std::ios::in); + std::string buf((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + ifs.close(); + const char *c_buf = buf.c_str(); - pr->router(cfg_getbool(proxy_cfg, "router")); + std::shared_ptr cf(new conf); - pr->ttl(cfg_getint(proxy_cfg, "ttl")); + if (cf->parse_block(&c_buf)) { + logger l(LOG_DEBUG); + cf->dump(l, 0); + return cf; + } - pr->timeout(cfg_getint(proxy_cfg, "timeout")); + logger::error() << "Could not parse configuration file"; + } catch (std::ifstream::failure e) { + logger::error() << "Failed to load configuration file '" << path << "'"; + } - for (i2 = 0; i2 < cfg_size(proxy_cfg, "rule"); i2++) { - cfg_t *rule_cfg; + return std::shared_ptr(); +} - if (!(rule_cfg = cfg_getnsec(proxy_cfg, "rule", i2))) - continue; +bool conf::is_block() const +{ + return _is_block; +} - address addr(cfg_title(rule_cfg)); +const char *conf::skip(const char *str, bool all) +{ + if (!all) { + while (*str && (*str != '\n') && isspace(*str)) + str++; - std::string ifname(cfg_getstr(rule_cfg, "iface")); + return str; + } - if (ifname.empty()) { - if (addr.prefix() <= 120) { - logger::warning() << "Static rule prefix /" << addr.prefix() << " <= 120 - is this what you want?"; - } + while (*str) { + while (*str && isspace(*str)) + str++; - pr->add_rule(addr); - } - else - { - pr->add_rule(addr, iface::open_ifd(ifname)); + if (!*str) + break; + + if ((*str == '#') || ((*str == '/') && (*(str + 1) == '/'))) { + while (*str && (*str != '\n')) + str++; + continue; + } + + if ((*str == '/') && (*(str + 1) == '*')) { + while (*str) { + if ((*str == '*') && (*(str + 1) == '/')) { + str += 2; + break; } + str++; } + continue; + } + + break; + } + + return str; +} + +bool conf::parse_block(const char **str) +{ + const char *p = *str; + + _is_block = true; + + while (*p) { + std::stringstream ss; + + p = skip(p); + + if (*p == '}') { + *str = p; + return true; + } + + while (*p && isalpha(*p)) { + ss << *p++; + } + + p = skip(p); + + if (*p == '=') { + p++; + } + + std::shared_ptr cf(new conf); + + if (cf->parse(&p)) { + _map.insert(std::pair >(ss.str(), cf)); } } - return 0; -} - -bool conf::load(const std::string& path) -{ - cfg_t *cfg; - int i, sz; - - #define _S (char *) - - static cfg_opt_t rule_opts[] = - { - CFG_STR (_S "iface", _S "", CFGF_NONE), - CFG_END () - }; - - static cfg_opt_t proxy_opts[] = - { - CFG_SEC (_S "rule", rule_opts, CFGF_MULTI | CFGF_TITLE), - CFG_BOOL (_S "router", cfg_true, CFGF_NONE), - CFG_INT (_S "ttl", 30000, CFGF_NONE), - CFG_INT (_S "timeout", 500, CFGF_NONE), - CFG_END () - }; - - static cfg_opt_t opts[] = - { - CFG_SEC (_S "proxy", proxy_opts, CFGF_MULTI | CFGF_TITLE), - CFG_FUNC (_S "include", &cfg_include), - CFG_END() - }; - - cfg = cfg_init(opts, CFGF_NOCASE); - - cfg_set_error_function(cfg, &error_printf); - - cfg_set_validate_func(cfg, "proxy|rule", &validate_rule); - - switch (cfg_parse(cfg, path.c_str())) { - case CFG_SUCCESS: - break; - - default: - logger::error() << "Failed to load configuration file '" << path << "'"; - return false; - } - - setup(cfg); - - cfg_free(cfg); - + *str = p; return true; } +bool conf::parse(const char **str) +{ + const char *p = *str; + std::stringstream ss; + + p = skip(p, false); + + if (!*p) { + return false; + } else if ((*p == '\'') || (*p == '"')) { + for (char e = *p++; *p && (*p != e); p++) + ss << *p; + } else if (isalnum(*p)) { + while (*p && (isalnum(*p) || strchr(":/.", *p))) + ss << *p++; + } else { + return false; + } + + _value = ss.str(); + + p = skip(p, false); + + if (*p == '{') { + p++; + + if (!parse_block(&p)) + return false; + + if (*p != '}') + return false; + + p++; + } + + *str = p; + return true; +} + +void conf::dump() const +{ + logger l(LOG_ERR); + dump(l, 0); +} + +void conf::dump(logger &l, int level) const +{ + int i; + + std::string pfx; + for (int i = 0; i < level; i++) { + pfx += " "; + } + + if (_value != "") { + l << _value << " "; + } + + if (_is_block) { + l << "{" << logger::endl; + + std::multimap >::const_iterator it; + + for (it = _map.begin(); it != _map.end(); it++) { + l << pfx << " " << it->first << " "; + it->second->dump(l, level + 1); + } + + l << pfx << "}" << logger::endl; + } + + l << logger::endl; +} + +std::shared_ptr conf::operator[](const std::string& name) const +{ + std::multimap >::const_iterator it; + + if ((it = _map.find(name)) == _map.end()) + return std::shared_ptr(); + else + return it->second; +} + +std::vector > conf::find(const std::string& name) const +{ + std::vector > vec; + std::multimap >::const_iterator it; + for (it = _map.find(name); it != _map.end(); it++) { + vec.push_back(it->second); + } + return vec; +} + +conf::operator const std::string&() +{ + return _value; +} + NDPPD_NS_END diff --git a/src/conf.h b/src/conf.h index bda8c8e..9b72ab3 100644 --- a/src/conf.h +++ b/src/conf.h @@ -16,23 +16,56 @@ #pragma once #include +#include +#include +#include #include #include "ndppd.h" -struct cfg_t; -struct cfg_opt_t; - NDPPD_NS_BEGIN class conf { -private: - static bool setup(::cfg_t *cfg); - static void error_printf(::cfg_t *cfg, const char *fmt, va_list ap); - static int validate_rule(::cfg_t *cfg, ::cfg_opt_t *opt); public: - static bool load(const std::string& path); + +private: + std::string _value; + + bool _is_block; + + std::multimap > _map; + + void dump(logger &l, int level) const; + + static const char *skip(const char *str, bool all = true); + + bool parse_block(const char **str); + + bool parse(const char **str); + +public: + conf(); + + const std::string &value() const; + + bool bool_value() const; + + int int_value() const; + + void value(const std::string &value); + + static std::shared_ptr load(const std::string &path); + + bool is_block() const; + + std::shared_ptr operator[](const std::string &name) const; + std::vector > find(const std::string &name) const; + + void dump() const; + + operator const std::string&(); + }; NDPPD_NS_END diff --git a/src/ndppd.cc b/src/ndppd.cc index a3f21f6..d5bc120 100644 --- a/src/ndppd.cc +++ b/src/ndppd.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,77 @@ int daemonize() return 0; } +bool configure(const std::string &path) +{ + std::shared_ptr cf; + + if (!(cf = conf::load(path))) + return false; + + std::vector >::const_iterator p_it; + + std::vector > proxies(cf->find("proxy")); + + for (p_it = proxies.begin(); p_it != proxies.end(); p_it++) { + std::shared_ptr pr_cf = *p_it, x_cf; + + if (pr_cf->value() == "") { + logger::error() << "'proxy' section is missing interface name"; + return false; + } + + std::shared_ptr pr = proxy::open(pr_cf->value()); + + if (!pr) { + logger::error() << "Configuration failed for proxy '" << pr_cf->value() << "'"; + return false; + } + + if (!(x_cf = (*pr_cf)["router"])) + pr->router(true); + else + pr->router(x_cf->bool_value()); + + if (!(x_cf = (*pr_cf)["ttl"])) + pr->ttl(30000); + else + pr->ttl(x_cf->int_value()); + + if (!(x_cf = (*pr_cf)["timeout"])) + pr->timeout(500); + else + pr->timeout(x_cf->int_value()); + + std::vector >::const_iterator r_it; + + std::vector > rules(pr_cf->find("rule")); + + for (r_it = rules.begin(); r_it != rules.end(); r_it++) { + std::shared_ptr ru_cf = *r_it; + + if (ru_cf->value() == "") { + logger::error() << "'rule' is missing an IPv6 address/net"; + return false; + } + + address addr(ru_cf->value()); + + if (!(x_cf = (*ru_cf)["iface"])) { + if (addr.prefix() <= 120) { + logger::warning() << "Static rule prefix /" << addr.prefix() << " <= 120 - is this what you want?"; + pr->add_rule(addr); + } + } else if (x_cf->value() == "") { + logger::error() << "'iface' expected an interface name or 'auto' as argument"; + } else { + pr->add_rule(addr, iface::open_ifd(x_cf->value())); + } + } + } + + return true; +} + int main(int argc, char *argv[], char *env[]) { std::string config_path("/etc/ndppd.conf"); @@ -116,9 +188,13 @@ int main(int argc, char *argv[], char *env[]) << "ndppd (NDP Proxy Daemon) version " NDPPD_VERSION << logger::endl << "Using configuration file '" << config_path << "'"; - if (!conf::load(config_path)) + // Load configuration. + + if (!configure(config_path)) return -1; + // Time stuff. + struct timeval t1, t2; gettimeofday(&t1, 0);