Mercurial > dnsbl
diff src/context.cpp @ 71:dd21c8e13074
start coding on new config syntax
author | carl |
---|---|
date | Sat, 09 Jul 2005 19:24:41 -0700 |
parents | |
children | e6a2d0be7c5e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/context.cpp Sat Jul 09 19:24:41 2005 -0700 @@ -0,0 +1,727 @@ +#include "context.h" + +static char* context_version="$Id:"; + +char *token_black; +char *token_content; +char *token_context; +char *token_dccfrom; +char *token_dccto; +char *token_default; +char *token_dnsbl; +char *token_dnsbll; +char *token_envfrom; +char *token_envto; +char *token_filter; +char *token_host_limit; +char *token_html_limit; +char *token_html_tags; +char *token_ignore; +char *token_include; +char *token_inherit; +char *token_lbrace; +char *token_many; +char *token_off; +char *token_ok; +char *token_ok2; +char *token_on; +char *token_rbrace; +char *token_semi; +char *token_soft; +char *token_tld; +char *token_unknown; +char *token_white; + +string_set all_strings; // owns all the strings, only modified by the config loader thread + +DNSBL::DNSBL(char *n, char *s, char *m) { + name = n; + suffix = s; + message = m; +} + + +CONFIG::CONFIG() { + reference_count = 0; + generation = 0; + load_time = 0; + default_context = NULL; +} + + +CONFIG::~CONFIG() { + for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) { + CONTEXT *c = *i; + delete c; + } +} + + +void CONFIG::add_context(CONTEXTP con) { + contexts.push_back(con); + if (!default_context && !con->get_parent()) { + // first global context + default_context = con; + } +} + + +CONTEXTP CONFIG::find_context(char *to, char *from) { + CONTEXTP con = NULL; + context_map::iterator i = env_to.find(from); + if (i != env_to.end()) { + con = (*i).second; + return con->find_from_context(from); + } + char *x = strchr(to, '@'); + if (x) { + x++; + i = env_to.find(x); + if (i != env_to.end()) { + con = (*i).second; + return con->find_from_context(from); + } + } + if (default_context) { + return default_context->find_from_context(from); + } + return NULL; +} + + +void CONFIG::dump() { + if (default_context) default_context->dump(); + for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) { + CONTEXTP c = *i; + CONTEXTP p = c->get_parent(); + if (!p && (c != default_context)) c->dump(); + } +} + + +CONTEXT::CONTEXT(CONTEXTP parent_, char *name_) { + parent = parent_; + name = name_; + env_from_default = (parent) ? token_inherit : token_unknown; + content_filtering = (parent) ? parent->content_filtering : false; + content_suffix = NULL; + content_message = NULL; + host_limit = 0; + host_limit_message = NULL; + host_random = false; + tag_limit = 0; + tag_limit_message = NULL; +} + + +CONTEXT::~CONTEXT() { + for (dnsblp_map::iterator i=dnsbl_names.begin(); i!=dnsbl_names.end(); i++) { + DNSBLP d = (*i).second; + // delete the underlying DNSBL objects. + delete d; + } +} + + +char *CONTEXT::get_full_name(char *buffer, int size) { + if (!parent) return name; + const int maxlen = 1000; + char buf[maxlen]; + snprintf(buffer, size, "%s.%s", parent->get_full_name(buf, maxlen), name); + return buffer; +} + + +bool CONTEXT::cover_env_to(char *to) { + const int maxlen = 1000; + char buffer[maxlen]; + char *x = strchr(to, '@'); + if (x) x++; + else x = to; + string_set::iterator i = env_to.find(x); + if (i != env_to.end()) return true; + return (parent) ? parent->cover_env_to(to) : false; +} + + +char *CONTEXT::find_from(char *from) { + // do we have a white/black/unknown for this full from value? + string_map::iterator i = env_from.find(from); + if (i != env_from.end()) return (*i).second; + // do we have a white/black/unknown for the source domain name? + char *x = strchr(from, '@'); + if (x) { + x++; + i = env_from.find(x); + if (i != env_from.end()) return (*i).second; + } + if ((env_from_default == token_inherit) && parent) { + return parent->find_from(from); + } + return env_from_default; +} + + +CONTEXTP CONTEXT::find_from_context(char *from) { + // do we have a special child context for this full from value? + context_map::iterator j = env_from_context.find(from); + if (j != env_from_context.end()) { + CONTEXTP con = (*j).second; + return con->find_from_context(from); + } + char *x = strchr(from, '@'); + if (x) { + x++; + // do we have a special context for the source domain name? + j = env_from_context.find(x); + if (j != env_from_context.end()) { + CONTEXTP con = (*j).second; + return con->find_from_context(from); + } + } + return this; +} + + +CONTEXTP CONTEXT::find_from_context_name(char *name) { + context_map::iterator i = children.find(name); + if (i != children.end()) return (*i).second; + return NULL; +} + + +DNSBLP CONTEXT::find_dnsbl(char *name) { + dnsblp_map::iterator i = dnsbl_names.find(name); + if (i != dnsbl_names.end()) return (*i).second; + if (parent) return parent->find_dnsbl(name); + return NULL; +} + + +void CONTEXT::dump(int level) { + const int maxlen = 1000; + char indent[maxlen]; + int i = min(maxlen-1, level*4); + memset(indent, ' ', i); + indent[i] = '\0'; + printf("%s context %s { \n", indent, name); + + for (dnsblp_map::iterator i=dnsbl_names.begin(); i!=dnsbl_names.end(); i++) { + char *n = (*i).first; + DNSBL &d = *(*i).second; + printf("%s dnsbl %s %s \"%s\"; \n", indent, n, d.suffix, d.message); + } + + if (!dnsbl_list.empty()) { + printf("%s dnsbl_list", indent); + for (dnsblp_list::iterator i=dnsbl_list.begin(); i!=dnsbl_list.end(); i++) { + DNSBL &d = *(*i); + printf(" %s", d.name); + } + printf("; \n"); + } + + if (content_filtering) { + printf("%s content on { \n", indent, env_from_default); + if (content_suffix) { + printf("%s filter %s \"%s\"; \n", indent, content_suffix, content_message); + } + if (!content_host_ignore.empty()) { + printf("%s ignore { \n", indent); + for (string_set::iterator i=content_host_ignore.begin(); i!=content_host_ignore.end(); i++) { + printf("%s %s; \n", indent, *i); + } + printf("%s }; \n", indent); + } + if (!content_tlds.empty()) { + printf("%s tld { \n", indent); + printf("%s ", indent); + for (string_set::iterator i=content_tlds.begin(); i!=content_tlds.end(); i++) { + printf("%s; ", *i); + } + printf("\n%s }; \n", indent); + } + if (!html_tags.empty()) { + printf("%s html_tags { \n", indent); + printf("%s ", indent); + for (string_set::iterator i=html_tags.begin(); i!=html_tags.end(); i++) { + printf("%s; ", *i); + } + printf("\n%s }; \n", indent); + } + if (host_limit_message) { + printf("%s host_limit on %d \"%s\"; \n", indent, host_limit, host_limit_message); + } + else if (host_random) { + printf("%s host_limit soft %d; \n", indent, host_limit); + } + else { + printf("%s host_limit off; \n", indent); + } + if (tag_limit_message) { + printf("%s tag_limit on %d \"%s\"; \n", indent, tag_limit, tag_limit_message); + } + else { + printf("%s tag_limit off; \n", indent); + } + printf("%s }; \n", indent); + } + else { + printf("%s content off {}; \n", indent, env_from_default); + } + + printf("%s env_to { \n", indent); + for (string_set::iterator i=env_to.begin(); i!=env_to.end(); i++) { + printf("%s %s; \n", indent, *i); + } + printf("%s }; \n", indent); + + for (context_map::iterator i=children.begin(); i!=children.end(); i++) { + CONTEXTP c = (*i).second; + c->dump(level+1); + } + + printf("%s env_from %s { \n", indent, env_from_default); + if (!env_from.empty()) { + printf("%s // white/black/unknown \n", indent); + for (string_map::iterator i=env_from.begin(); i!=env_from.end(); i++) { + char *f = (*i).first; + char *t = (*i).second; + printf("%s %s \t %s; \n", indent, f, t); + } + } + if (!env_from_context.empty()) { + printf("%s // child contexts \n", indent); + for (context_map::iterator j=env_from_context.begin(); j!=env_from_context.end(); j++) { + char *f = (*j).first; + CONTEXTP t = (*j).second; + printf("%s %s \t %s; \n", indent, f, t->name); + } + } + printf("%s }; \n", indent); + + printf("%s }; \n", indent); +} + + +//////////////////////////////////////////////// +// helper to discard the strings held by a string_set +// +static void discard(string_set &s) { + for (string_set::iterator i=s.begin(); i!=s.end(); i++) { + free(*i); + } + s.clear(); +} + + +//////////////////////////////////////////////// +// helper to register a string in a string set +// +char* register_string(string_set &s, char *name) { + string_set::iterator i = s.find(name); + if (i != s.end()) return *i; + char *x = strdup(name); + s.insert(x); + return x; +} + + +//////////////////////////////////////////////// +// register a global string +// +char* register_string(char *name) { + return register_string(all_strings, name); +} + + +//////////////////////////////////////////////// +// +bool tsa(TOKEN &tok, char *token); +bool tsa(TOKEN &tok, char *token) { + char *have = tok.next(); + if (have == token) return true; + tok.token_error(token, have); + return false; +} + + +//////////////////////////////////////////////// +// +bool parse_dnsbl(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_dnsbl(TOKEN &tok, CONFIG &dc, CONTEXT &me) { + char *name = tok.next(); + char *suf = tok.next(); + char *msg = tok.next(); + if (!tsa(tok, token_semi)) return false; + DNSBLP dns = new DNSBL(name, suf, msg); + me.add_dnsbl(name, dns); + return true; +} + + +//////////////////////////////////////////////// +// +bool parse_dnsbll(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_dnsbll(TOKEN &tok, CONFIG &dc, CONTEXT &me) { + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_semi) break; + DNSBLP dns = me.find_dnsbl(have); + if (dns) { + me.add_dnsbl(dns); + } + else { + tok.token_error("dnsbl name", have); + return false; + } + } + return true; +} + + +//////////////////////////////////////////////// +// +bool parse_content(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_content(TOKEN &tok, CONFIG &dc, CONTEXT &me) { + char *setting = tok.next(); + if (setting == token_on) { + me.set_content_filtering(true); + } + else if (setting == token_off) { + me.set_content_filtering(false); + } + else { + tok.token_error("on/off", setting); + return false; + } + if (!tsa(tok, token_lbrace)) return false; + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_filter) { + me.set_content_suffix(tok.next()); + me.set_content_message(tok.next()); + if (!tsa(tok, token_semi)) return false; + } + else if (have == token_ignore) { + if (!tsa(tok, token_lbrace)) return false; + while (true) { + if (!have) break; + char *have = tok.next(); + if (have == token_rbrace) { + break; // done + } + else { + me.add_ignore(have); + } + } + if (!tsa(tok, token_semi)) return false; + } + else if (have == token_tld) { + if (!tsa(tok, token_lbrace)) return false; + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) { + break; // done + } + else { + me.add_tld(have); + } + } + if (!tsa(tok, token_semi)) return false; + } + else if (have == token_html_limit) { + have = tok.next(); + if (have == token_on) { + me.set_tag_limit(tok.nextint()); + me.set_tag_message(tok.next()); + } + else if (have == token_off) { + me.set_tag_limit(0); + me.set_tag_message(NULL); + } + else { + tok.token_error("on/off", have); + return false; + } + if (!tsa(tok, token_semi)) return false; + } + else if (have == token_html_tags) { + if (!tsa(tok, token_lbrace)) return false; + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) { + break; // done + } + else { + me.add_tag(have); + } + } + if (!tsa(tok, token_semi)) return false; + } + else if (have == token_host_limit) { + have = tok.next(); + if (have == token_on) { + me.set_host_limit(tok.nextint()); + me.set_host_message(tok.next()); + me.set_host_random(false); + } + else if (have == token_off) { + me.set_host_limit(0); + me.set_host_message(NULL); + me.set_host_random(false); + } + else if (have == token_soft) { + me.set_host_limit(tok.nextint()); + me.set_host_message(NULL); + me.set_host_random(true); + } + else { + tok.token_error("on/off/soft", have); + return false; + } + if (!tsa(tok, token_semi)) return false; + } + else if (have == token_rbrace) { + break; // done + } + else { + tok.token_error("content keyword", have); + return false; + } + } + return tsa(tok, token_semi); +} + + +//////////////////////////////////////////////// +// +bool parse_envto(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_envto(TOKEN &tok, CONFIG &dc, CONTEXT &me) { + if (!tsa(tok, token_lbrace)) return false; + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + if (have == token_semi) { + // optional separators + } + else if (have == token_dccto) { + char *flavor = tok.next(); + if (!tsa(tok, token_lbrace)) return false; + bool keeping = false; + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + if (have == flavor) { + keeping = true; + continue; + } + else if ((have == token_ok) || (have == token_ok2) || (have == token_many)) { + keeping = false; + continue; + } + if (have == token_envto) { + have = tok.next(); + if (keeping) { + if (me.allow_env_to(have)) { + me.add_to(have); + dc.add_to(have, &me); + } + } + } + tok.skipeol(); + } + } + else if (me.allow_env_to(have)) { + me.add_to(have); + dc.add_to(have, &me); + } + else { + tok.token_error("valid env_to address or domain name", have); + return false; + } + } + return tsa(tok, token_semi); +} + + +//////////////////////////////////////////////// +// +bool parse_envfrom(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_envfrom(TOKEN &tok, CONFIG &dc, CONTEXT &me) { + char *st = tok.next(); + if ((st == token_black) || (st == token_white) || (st == token_unknown)) { + me.set_from_default(st); + } + else { + tok.push(st); + } + if (!tsa(tok, token_lbrace)) return false; + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + if (have == token_semi) { + // optional separators + } + else if (have == token_dccfrom) { + if (!tsa(tok, token_lbrace)) return false; + bool keeping = false; + bool many = false; + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + if (have == token_ok) { + keeping = true; + many = false; + continue; + } + else if (have == token_many) { + keeping = true; + many = true; + continue; + } + else if (have == token_ok2) { + keeping = false; + continue; + } + if (have == token_envfrom) { + have = tok.next(); + if (keeping) { + me.add_from(have, (many) ? token_black : token_white); + } + } + tok.skipeol(); + } + } + else { + // may be a valid email address or domain name + char *st = tok.next(); + if ((st == token_black) || (st == token_white) || (st == token_unknown)) { + me.add_from(have, st); + } + else { + CONTEXTP con = me.find_from_context_name(st); + if (con) { + me.add_from_context(have, con); + } + else { + tok.token_error("white/black/unknown or child context name", st); + return false; + } + } + } + } + return tsa(tok, token_semi); +} + + +//////////////////////////////////////////////// +// +bool parse_context(TOKEN &tok, CONFIG &dc, CONTEXTP parent); +bool parse_context(TOKEN &tok, CONFIG &dc, CONTEXTP parent) { + char *name = tok.next(); + if (!tsa(tok, token_lbrace)) return false; + CONTEXTP con = new CONTEXT(parent, name); + + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; // done + if (have == token_dnsbl) { + if (!parse_dnsbl(tok, dc, *con)) return false; + } + else if (have == token_dnsbll) { + if (!parse_dnsbll(tok, dc, *con)) return false; + } + else if (have == token_content) { + if (!parse_content(tok, dc, *con)) return false; + } + else if (have == token_envto) { + if (!parse_envto(tok, dc, *con)) return false; + } + else if (have == token_envfrom) { + if (!parse_envfrom(tok, dc, *con)) return false; + } + else if (have == token_context) { + if (!parse_context(tok, dc, con)) return false; + } + else { + tok.token_error("context keyword", have); + return false; + } + } + + if (!tsa(tok, token_semi)) { + delete con; + return false; + } + dc.add_context(con); + if (parent) parent->add_context(con); + return true; +} + + +//////////////////////////////////////////////// +// parse a config file +// +bool load_conf(CONFIG &dc, char *fn) { + TOKEN tok(fn, &dc.config_files); + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_context) { + if (!parse_context(tok, dc, NULL)) { + return false; + } + } + else { + tok.token_error(token_context, have); + return false; + } + } + return true; +} + + +//////////////////////////////////////////////// +// init the tokens +// +void token_init() { + token_black = register_string("black"); + token_content = register_string("content"); + token_context = register_string("context"); + token_dccfrom = register_string("dcc_from"); + token_dccto = register_string("dcc_to"); + token_default = register_string("default"); + token_dnsbl = register_string("dnsbl"); + token_dnsbll = register_string("dnsbl_list"); + token_envfrom = register_string("env_from"); + token_envto = register_string("env_to"); + token_filter = register_string("filter"); + token_host_limit = register_string("host_limit"); + token_html_limit = register_string("html_limit"); + token_html_tags = register_string("html_tags"); + token_ignore = register_string("ignore"); + token_include = register_string("include"); + token_inherit = register_string("inherit"); + token_lbrace = register_string("{"); + token_many = register_string("many"); + token_off = register_string("off"); + token_ok = register_string("ok"); + token_ok2 = register_string("ok2"); + token_on = register_string("on"); + token_rbrace = register_string("}"); + token_semi = register_string(";"); + token_soft = register_string("soft"); + token_tld = register_string("tld"); + token_unknown = register_string("unknown"); + token_white = register_string("white"); +}