Mercurial > syslog2iptables
view src/syslogconfig.cpp @ 3:8fe310e5cd44
initial coding
author | carl |
---|---|
date | Sun, 27 Nov 2005 21:12:01 -0800 |
parents | 6e88da080f08 |
children | 2737ab01659a |
line wrap: on
line source
/*************************************************************************** * Copyright (C) 2005 by 510 Software Group * * * * * * 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 of the License, 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. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "includes.h" #include <fcntl.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> static char* syslogconfig_version="$Id$"; char *token_bucket; char *token_file; char *token_ignore; char *token_include; char *token_index; char *token_lbrace; char *token_pattern; char *token_rbrace; char *token_semi; char *token_slash; char *token_threshold; struct ltint { bool operator()(const int s1, const int s2) const { return (unsigned)s1 < (unsigned)s2; } }; string_set all_strings; // owns all the strings, only modified by the config loader thread const int maxlen = 1000; // used for snprintf buffers typedef map<int, int, ltint> ip_buckets; class IPR { ip_buckets violations; public: void add(int ip, int bucket, CONFIG &con); void changed(CONFIG &con); void leak(int amount, CONFIG &con); }; IPR recorder; //////////////////////////////////////////////// // void IPR::add(int ip, int bucket, CONFIG &con) { if (con.looking(ip)) { ip_buckets::iterator i = violations.find(ip); if (i == violations.end()) violations[ip] = bucket; else { (*i).second += bucket; if ((*i).second > con.get_threshold()) changed(con); } } } void IPR::leak(int amount, CONFIG &con) { bool ch = false; for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); ) { int ip = (*i).first; int n = (*i).second; in_addr ad; ad.s_addr = htonl(ip); char buf[maxlen]; snprintf(buf, maxlen, "leak %s with %d count", inet_ntoa(ad), n); my_syslog(buf); if (n <= amount) { ch = true; violations.erase(i++); } else { (*i).second -= amount; i++; } } if (ch) changed(con); } void IPR::changed(CONFIG &con) { my_syslog("**** dump"); for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); i++) { int ip = (*i).first; int n = (*i).second; in_addr ad; ad.s_addr = htonl(ip); char buf[maxlen]; snprintf(buf, maxlen, "%s with %d count", inet_ntoa(ad), n); my_syslog(buf); } } //////////////////////////////////////////////// // int ip_address(char *have); int ip_address(char *have) { int ipaddr = 0; in_addr ip; if (inet_aton(have, &ip)) ipaddr = ip.s_addr; else { struct hostent *host = gethostbyname(have); if (host && host->h_addrtype == AF_INET) memcpy(&ipaddr, host->h_addr, sizeof(ipaddr)); } return ntohl(ipaddr); } //////////////////////////////////////////////// // PATTERN::PATTERN(TOKEN &tok, char *pattern_, int index_, int bucket_) { pattern = pattern_; index = index_; bucket = bucket_; if (pattern) { int rc = regcomp(&re, pattern, REG_ICASE | REG_EXTENDED); if (rc) { char bu[maxlen]; regerror(rc, &re, bu, maxlen); char buf[maxlen]; snprintf(buf, sizeof(buf), "pattern %s not valid - %s", pattern, bu); tok.token_error(buf); pattern = NULL; } } } PATTERN::~PATTERN() { regfree(&re); } bool PATTERN::process(char *buf, CONFIG &con) { if (pattern) { const int nmatch = index+1; regmatch_t match[nmatch]; if (0 == regexec(&re, buf, nmatch, match, 0)) { int s = match[index].rm_so; int e = match[index].rm_eo; // char bu[maxlen]; // snprintf(bu, maxlen, "re match from %d to %d", s, e); // my_syslog(bu); if (s != -1) { my_syslog(buf); buf[e] = '\0'; int ip = ip_address(buf+s); if (ip) { my_syslog(buf+s); recorder.add(ip, bucket, con); } return true; } } } return false; } void PATTERN::dump(int level) { char indent[maxlen]; int i = min(maxlen-1, level*4); memset(indent, ' ', i); indent[i] = '\0'; printf("%s pattern \"%s\" {; \n", indent, pattern); printf("%s index %d; \n", indent, index); printf("%s bucket %d; \n", indent, bucket); printf("%s }; \n", indent); } //////////////////////////////////////////////// // CONFIG::CONFIG() { reference_count = 0; generation = 0; load_time = 0; } CONFIG::~CONFIG() { for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { SYSLOGCONFIG *c = *i; delete c; } ignore.clear(); } void CONFIG::add_syslogconfig(SYSLOGCONFIGP con) { syslogconfigs.push_back(con); } void CONFIG::add_pair(IPPAIR pair) { ignore.push_back(pair); } void CONFIG::dump() { printf(" threshold %d; \n\n", threshold); printf(" ignore { \n"); for (ippair_list::iterator i=ignore.begin(); i!=ignore.end(); i++) { IPPAIR &p = *i; in_addr ip; ip.s_addr = htonl(p.first); printf(" %s/%d; \n", inet_ntoa(ip), p.cidr); } printf(" }; \n\n"); for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { SYSLOGCONFIGP c = *i; c->dump(0); } } void CONFIG::read() { while (true) { bool have = false; for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { SYSLOGCONFIGP c = *i; have |= c->read(*this); } break; //!! if (!have) break; } } void CONFIG::sleep(int duration) { ::sleep(duration); recorder.leak(duration, *this); } bool CONFIG::looking(int ip) { for (ippair_list::iterator i=ignore.begin(); i!=ignore.end(); i++) { IPPAIR &p = *i; if ((p.first <= ip) && (ip <= p.last)) return false; } return true; } //////////////////////////////////////////////// // SYSLOGCONFIG::SYSLOGCONFIG(TOKEN &tok, char *file_name_) { file_name = file_name_; fd = open(file_name, O_RDONLY); len = 0; if (fd == -1) { char buf[maxlen]; snprintf(buf, sizeof(buf), "syslog file %s not readable", file_name); tok.token_error(buf); } else { //lseek(fd, 0, SEEK_END); //!! } } SYSLOGCONFIG::~SYSLOGCONFIG() { if (fd != -1) close(fd); fd = -1; for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { PATTERN *p = *i; delete p; } } void SYSLOGCONFIG::add_pattern(PATTERNP pat) { patterns.push_back(pat); } bool SYSLOGCONFIG::read(CONFIG &con) { if (failed()) return false; int n = ::read(fd, buf+len, buflen-len); bool have = (n > 0); if (have) { len += n; while (true) { char *p = (char*)memchr(buf, '\n', len); if (!p) break; n = p-buf; *p = '\0'; process(con); // process null terminated string len -= n+1; memmove(buf, p+1, len); } // no <lf> in a full buffer if (len == buflen) len = 0; } return have; } void SYSLOGCONFIG::process(CONFIG &con) { for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { PATTERN *p = *i; if (p->process(buf, con)) break; } } void SYSLOGCONFIG::dump(int level) { char indent[maxlen]; int i = min(maxlen-1, level*4); memset(indent, ' ', i); indent[i] = '\0'; char buf[maxlen]; printf("%s file \"%s\" {\n", indent, file_name); for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { PATTERN *p = *i; p->dump(level); } printf("%s }; \n", indent); } //////////////////////////////////////////////// // helper to discard the strings held by a string_set // 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_pattern(TOKEN &tok, SYSLOGCONFIG &con); bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con) { char *pat = tok.next(); int ind, buc; if (!tsa(tok, token_lbrace)) return false; while (true) { char *have = tok.next(); if (!have) break; if (have == token_rbrace) break; if (have == token_index) { have = tok.next(); ind = atoi(have); if (!tsa(tok, token_semi)) return false; } else if (have == token_bucket) { have = tok.next(); buc = atoi(have); if (!tsa(tok, token_semi)) return false; } else { tok.token_error("index/bucket", have); return false; } } if (!tsa(tok, token_semi)) return false; PATTERNP patt = new PATTERN(tok, pat, ind, buc); con.add_pattern(patt); return true; } //////////////////////////////////////////////// // bool parse_ignore(TOKEN &tok, CONFIG &dc); bool parse_ignore(TOKEN &tok, CONFIG &dc) { if (!tsa(tok, token_lbrace)) return false; while (true) { char *have = tok.next(); if (!have) break; if (have == token_rbrace) break; int ipaddr = ip_address(have); if (ipaddr == 0) { tok.token_error("ip address", have); return false; } if (!tsa(tok, token_slash)) return false; have = tok.next(); int mask = atoi(have); if ((mask < 8) || (mask > 32)) { tok.token_error("cidr 8..32 value", have); return false; } if (!tsa(tok, token_semi)) return false; IPPAIR pair; const int masks[33] = {0xffffffff, // 0 0x7fffffff, // 1 0x3fffffff, // 2 0x1fffffff, // 3 0x0fffffff, // 4 0x07ffffff, // 5 0x03ffffff, // 6 0x01ffffff, // 7 0x00ffffff, // 8 0x007fffff, // 9 0x003fffff, // 10 0x001fffff, // 11 0x000fffff, // 12 0x0007ffff, // 13 0x0003ffff, // 14 0x0001ffff, // 15 0x0000ffff, // 16 0x00007fff, // 17 0x00003fff, // 18 0x00001fff, // 19 0x00000fff, // 20 0x000007ff, // 21 0x000003ff, // 22 0x000001ff, // 23 0x000000ff, // 24 0x0000007f, // 25 0x0000003f, // 26 0x0000001f, // 27 0x0000000f, // 28 0x00000007, // 29 0x00000003, // 30 0x00000001, // 31 0x00000000}; // 32 pair.first = ipaddr; pair.last = ipaddr | masks[mask]; pair.cidr = mask; dc.add_pair(pair); } if (!tsa(tok, token_semi)) return false; return true; } //////////////////////////////////////////////// // bool parse_syslogconfig(TOKEN &tok, CONFIG &dc); bool parse_syslogconfig(TOKEN &tok, CONFIG &dc) { char *name = tok.next(); if (!tsa(tok, token_lbrace)) return false; SYSLOGCONFIGP con = new SYSLOGCONFIG(tok, name); if (con->failed()) { delete con; return false; } dc.add_syslogconfig(con); while (true) { char *have = tok.next(); if (!have) break; if (have == token_rbrace) break; if (have == token_pattern) { if (!parse_pattern(tok, *con)) return false; } else { tok.token_error("pattern", have); return false; } } if (!tsa(tok, token_semi)) return false; return true; } //////////////////////////////////////////////// // parse a config file // bool load_conf(CONFIG &dc, char *fn) { int count = 0; TOKEN tok(fn, &dc.config_files); while (true) { char *have = tok.next(); if (!have) break; if (have == token_threshold) { have = tok.next(); dc.set_threshold(atoi(have)); if (!tsa(tok, token_semi)) return false; } else if (have == token_ignore) { if (!parse_ignore(tok, dc)) return false; } else if (have == token_file) { if (!parse_syslogconfig(tok, dc)) return false; count++; } else { tok.token_error("threshold/ignore/file", have); return false; } } tok.token_error("load_conf() found %d syslog files in %s", count, fn); return (!dc.syslogconfigs.empty()); } //////////////////////////////////////////////// // init the tokens // void token_init() { token_bucket = register_string("bucket"); token_file = register_string("file"); token_ignore = register_string("ignore"); token_include = register_string("include"); token_index = register_string("index"); token_lbrace = register_string("{"); token_pattern = register_string("pattern"); token_rbrace = register_string("}"); token_semi = register_string(";"); token_slash = register_string("/"); token_threshold = register_string("threshold"); }