comparison src/syslogconfig.cpp @ 3:8fe310e5cd44

initial coding
author carl
date Sun, 27 Nov 2005 21:12:01 -0800
parents 6e88da080f08
children 2737ab01659a
comparison
equal deleted inserted replaced
2:6e88da080f08 3:8fe310e5cd44
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/ 19 ***************************************************************************/
20 20
21 #include "includes.h" 21 #include "includes.h"
22 #include <fcntl.h> 22 #include <fcntl.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <netdb.h>
23 27
24 static char* syslogconfig_version="$Id$"; 28 static char* syslogconfig_version="$Id$";
25 29
26 char *token_cisco; 30 char *token_bucket;
27 char *token_file; 31 char *token_file;
32 char *token_ignore;
28 char *token_include; 33 char *token_include;
34 char *token_index;
29 char *token_lbrace; 35 char *token_lbrace;
30 char *token_parser; 36 char *token_pattern;
31 char *token_rbrace; 37 char *token_rbrace;
32 char *token_semi; 38 char *token_semi;
33 char *token_ssh; 39 char *token_slash;
40 char *token_threshold;
41
42 struct ltint
43 {
44 bool operator()(const int s1, const int s2) const
45 {
46 return (unsigned)s1 < (unsigned)s2;
47 }
48 };
34 49
35 string_set all_strings; // owns all the strings, only modified by the config loader thread 50 string_set all_strings; // owns all the strings, only modified by the config loader thread
36 const int maxlen = 1000; // used for snprintf buffers 51 const int maxlen = 1000; // used for snprintf buffers
37 52 typedef map<int, int, ltint> ip_buckets;
38 53
54 class IPR {
55 ip_buckets violations;
56 public:
57 void add(int ip, int bucket, CONFIG &con);
58 void changed(CONFIG &con);
59 void leak(int amount, CONFIG &con);
60 };
61
62 IPR recorder;
63
64
65 ////////////////////////////////////////////////
66 //
67 void IPR::add(int ip, int bucket, CONFIG &con) {
68 if (con.looking(ip)) {
69 ip_buckets::iterator i = violations.find(ip);
70 if (i == violations.end()) violations[ip] = bucket;
71 else {
72 (*i).second += bucket;
73 if ((*i).second > con.get_threshold()) changed(con);
74 }
75 }
76 }
77
78
79 void IPR::leak(int amount, CONFIG &con) {
80 bool ch = false;
81 for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); ) {
82 int ip = (*i).first;
83 int n = (*i).second;
84 in_addr ad;
85 ad.s_addr = htonl(ip);
86 char buf[maxlen];
87 snprintf(buf, maxlen, "leak %s with %d count", inet_ntoa(ad), n);
88 my_syslog(buf);
89 if (n <= amount) {
90 ch = true;
91 violations.erase(i++);
92 }
93 else {
94 (*i).second -= amount;
95 i++;
96 }
97 }
98 if (ch) changed(con);
99 }
100
101
102 void IPR::changed(CONFIG &con) {
103 my_syslog("**** dump");
104 for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); i++) {
105 int ip = (*i).first;
106 int n = (*i).second;
107 in_addr ad;
108 ad.s_addr = htonl(ip);
109 char buf[maxlen];
110 snprintf(buf, maxlen, "%s with %d count", inet_ntoa(ad), n);
111 my_syslog(buf);
112 }
113 }
114
115
116 ////////////////////////////////////////////////
117 //
118 int ip_address(char *have);
119 int ip_address(char *have) {
120 int ipaddr = 0;
121 in_addr ip;
122 if (inet_aton(have, &ip)) ipaddr = ip.s_addr;
123 else {
124 struct hostent *host = gethostbyname(have);
125 if (host && host->h_addrtype == AF_INET) memcpy(&ipaddr, host->h_addr, sizeof(ipaddr));
126 }
127 return ntohl(ipaddr);
128 }
129
130
131 ////////////////////////////////////////////////
132 //
133 PATTERN::PATTERN(TOKEN &tok, char *pattern_, int index_, int bucket_) {
134 pattern = pattern_;
135 index = index_;
136 bucket = bucket_;
137 if (pattern) {
138 int rc = regcomp(&re, pattern, REG_ICASE | REG_EXTENDED);
139 if (rc) {
140 char bu[maxlen];
141 regerror(rc, &re, bu, maxlen);
142 char buf[maxlen];
143 snprintf(buf, sizeof(buf), "pattern %s not valid - %s", pattern, bu);
144 tok.token_error(buf);
145 pattern = NULL;
146 }
147 }
148 }
149
150
151 PATTERN::~PATTERN() {
152 regfree(&re);
153 }
154
155
156 bool PATTERN::process(char *buf, CONFIG &con) {
157 if (pattern) {
158 const int nmatch = index+1;
159 regmatch_t match[nmatch];
160 if (0 == regexec(&re, buf, nmatch, match, 0)) {
161 int s = match[index].rm_so;
162 int e = match[index].rm_eo;
163 // char bu[maxlen];
164 // snprintf(bu, maxlen, "re match from %d to %d", s, e);
165 // my_syslog(bu);
166 if (s != -1) {
167 my_syslog(buf);
168 buf[e] = '\0';
169 int ip = ip_address(buf+s);
170 if (ip) {
171 my_syslog(buf+s);
172 recorder.add(ip, bucket, con);
173 }
174 return true;
175 }
176 }
177 }
178 return false;
179 }
180
181
182 void PATTERN::dump(int level) {
183 char indent[maxlen];
184 int i = min(maxlen-1, level*4);
185 memset(indent, ' ', i);
186 indent[i] = '\0';
187 printf("%s pattern \"%s\" {; \n", indent, pattern);
188 printf("%s index %d; \n", indent, index);
189 printf("%s bucket %d; \n", indent, bucket);
190 printf("%s }; \n", indent);
191 }
192
193
194 ////////////////////////////////////////////////
195 //
39 CONFIG::CONFIG() { 196 CONFIG::CONFIG() {
40 reference_count = 0; 197 reference_count = 0;
41 generation = 0; 198 generation = 0;
42 load_time = 0; 199 load_time = 0;
43 } 200 }
46 CONFIG::~CONFIG() { 203 CONFIG::~CONFIG() {
47 for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { 204 for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) {
48 SYSLOGCONFIG *c = *i; 205 SYSLOGCONFIG *c = *i;
49 delete c; 206 delete c;
50 } 207 }
208 ignore.clear();
51 } 209 }
52 210
53 211
54 void CONFIG::add_syslogconfig(SYSLOGCONFIGP con) { 212 void CONFIG::add_syslogconfig(SYSLOGCONFIGP con) {
55 syslogconfigs.push_back(con); 213 syslogconfigs.push_back(con);
56 } 214 }
57 215
58 216
217 void CONFIG::add_pair(IPPAIR pair) {
218 ignore.push_back(pair);
219 }
220
221
59 void CONFIG::dump() { 222 void CONFIG::dump() {
223 printf(" threshold %d; \n\n", threshold);
224
225 printf(" ignore { \n");
226 for (ippair_list::iterator i=ignore.begin(); i!=ignore.end(); i++) {
227 IPPAIR &p = *i;
228 in_addr ip;
229 ip.s_addr = htonl(p.first);
230 printf(" %s/%d; \n", inet_ntoa(ip), p.cidr);
231 }
232 printf(" }; \n\n");
233
60 for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { 234 for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) {
61 SYSLOGCONFIGP c = *i; 235 SYSLOGCONFIGP c = *i;
62 c->dump(0); 236 c->dump(0);
63 } 237 }
64 } 238 }
65 239
66 240
67 void CONFIG::read() { 241 void CONFIG::read() {
68 for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { 242 while (true) {
69 SYSLOGCONFIGP c = *i; 243 bool have = false;
70 c->read(); 244 for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) {
71 } 245 SYSLOGCONFIGP c = *i;
72 } 246 have |= c->read(*this);
73 247 }
74 248 break; //!!
75 SYSLOGCONFIG::SYSLOGCONFIG(TOKEN &tok, char *file_name_, parser_style parser_) { 249 if (!have) break;
250 }
251 }
252
253
254 void CONFIG::sleep(int duration) {
255 ::sleep(duration);
256 recorder.leak(duration, *this);
257 }
258
259
260 bool CONFIG::looking(int ip) {
261 for (ippair_list::iterator i=ignore.begin(); i!=ignore.end(); i++) {
262 IPPAIR &p = *i;
263 if ((p.first <= ip) && (ip <= p.last)) return false;
264 }
265 return true;
266 }
267
268 ////////////////////////////////////////////////
269 //
270 SYSLOGCONFIG::SYSLOGCONFIG(TOKEN &tok, char *file_name_) {
76 file_name = file_name_; 271 file_name = file_name_;
77 parser = parser_;
78 fd = open(file_name, O_RDONLY); 272 fd = open(file_name, O_RDONLY);
79 len = 0; 273 len = 0;
80 if (fd == -1) { 274 if (fd == -1) {
81 char buf[maxlen]; 275 char buf[maxlen];
82 snprintf(buf, sizeof(buf), "syslog file %s not readable", file_name); 276 snprintf(buf, sizeof(buf), "syslog file %s not readable", file_name);
83 tok.token_error(buf); 277 tok.token_error(buf);
84 } 278 }
85 else { 279 else {
86 lseek(fd, 0, SEEK_END); 280 //lseek(fd, 0, SEEK_END); //!!
87 } 281 }
88 } 282 }
89 283
90 284
91 SYSLOGCONFIG::~SYSLOGCONFIG() { 285 SYSLOGCONFIG::~SYSLOGCONFIG() {
92 if (fd != -1) close(fd); 286 if (fd != -1) close(fd);
93 fd = -1; 287 fd = -1;
94 } 288 for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) {
95 289 PATTERN *p = *i;
96 290 delete p;
97 void SYSLOGCONFIG::read() { 291 }
98 if (failed()) return; 292 }
99 int n = ::read(fd, buf, buflen-len); 293
100 if (n > 0) { 294
295 void SYSLOGCONFIG::add_pattern(PATTERNP pat) {
296 patterns.push_back(pat);
297 }
298
299
300 bool SYSLOGCONFIG::read(CONFIG &con) {
301 if (failed()) return false;
302 int n = ::read(fd, buf+len, buflen-len);
303 bool have = (n > 0);
304 if (have) {
101 len += n; 305 len += n;
102 while (true) { 306 while (true) {
103 char *p = (char*)memchr(buf, '\n', len); 307 char *p = (char*)memchr(buf, '\n', len);
104 if (!p) break; 308 if (!p) break;
105 n = p-buf; 309 n = p-buf;
106 *p = '\0'; 310 *p = '\0';
107 process(); // process null terminated string 311 process(con); // process null terminated string
108 len -= n+1; 312 len -= n+1;
109 memmove(buf, p+1, len); 313 memmove(buf, p+1, len);
110 } 314 }
111 // no <lf> in a full buffer 315 // no <lf> in a full buffer
112 if (len == buflen) len = 0; 316 if (len == buflen) len = 0;
113 } 317 }
114 } 318 return have;
115 319 }
116 320
117 void SYSLOGCONFIG::process() { 321
118 my_syslog(buf); 322 void SYSLOGCONFIG::process(CONFIG &con) {
323 for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) {
324 PATTERN *p = *i;
325 if (p->process(buf, con)) break;
326 }
119 } 327 }
120 328
121 329
122 void SYSLOGCONFIG::dump(int level) { 330 void SYSLOGCONFIG::dump(int level) {
123 char indent[maxlen]; 331 char indent[maxlen];
124 int i = min(maxlen-1, level*4); 332 int i = min(maxlen-1, level*4);
125 memset(indent, ' ', i); 333 memset(indent, ' ', i);
126 indent[i] = '\0'; 334 indent[i] = '\0';
127 char buf[maxlen]; 335 char buf[maxlen];
128 printf("%s file \"%s\" {\n", indent, file_name); 336 printf("%s file \"%s\" {\n", indent, file_name);
129 switch (parser) { 337 for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) {
130 case cisco: 338 PATTERN *p = *i;
131 printf("%s parser cisco; \n", indent); 339 p->dump(level);
132 break; 340 }
133 case ssh:
134 printf("%s parser ssh; \n", indent);
135 break;
136 }
137 printf("%s }; \n", indent); 341 printf("%s }; \n", indent);
138 } 342 }
139 343
140 344
141 //////////////////////////////////////////////// 345 ////////////////////////////////////////////////
180 } 384 }
181 385
182 386
183 //////////////////////////////////////////////// 387 ////////////////////////////////////////////////
184 // 388 //
389 bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con);
390 bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con) {
391 char *pat = tok.next();
392 int ind, buc;
393 if (!tsa(tok, token_lbrace)) return false;
394 while (true) {
395 char *have = tok.next();
396 if (!have) break;
397 if (have == token_rbrace) break;
398 if (have == token_index) {
399 have = tok.next();
400 ind = atoi(have);
401 if (!tsa(tok, token_semi)) return false;
402 }
403 else if (have == token_bucket) {
404 have = tok.next();
405 buc = atoi(have);
406 if (!tsa(tok, token_semi)) return false;
407 }
408 else {
409 tok.token_error("index/bucket", have);
410 return false;
411 }
412 }
413 if (!tsa(tok, token_semi)) return false;
414 PATTERNP patt = new PATTERN(tok, pat, ind, buc);
415 con.add_pattern(patt);
416 return true;
417 }
418
419
420 ////////////////////////////////////////////////
421 //
422 bool parse_ignore(TOKEN &tok, CONFIG &dc);
423 bool parse_ignore(TOKEN &tok, CONFIG &dc) {
424 if (!tsa(tok, token_lbrace)) return false;
425 while (true) {
426 char *have = tok.next();
427 if (!have) break;
428 if (have == token_rbrace) break;
429 int ipaddr = ip_address(have);
430 if (ipaddr == 0) {
431 tok.token_error("ip address", have);
432 return false;
433 }
434 if (!tsa(tok, token_slash)) return false;
435 have = tok.next();
436 int mask = atoi(have);
437 if ((mask < 8) || (mask > 32)) {
438 tok.token_error("cidr 8..32 value", have);
439 return false;
440 }
441 if (!tsa(tok, token_semi)) return false;
442 IPPAIR pair;
443 const int masks[33] = {0xffffffff, // 0
444 0x7fffffff, // 1
445 0x3fffffff, // 2
446 0x1fffffff, // 3
447 0x0fffffff, // 4
448 0x07ffffff, // 5
449 0x03ffffff, // 6
450 0x01ffffff, // 7
451 0x00ffffff, // 8
452 0x007fffff, // 9
453 0x003fffff, // 10
454 0x001fffff, // 11
455 0x000fffff, // 12
456 0x0007ffff, // 13
457 0x0003ffff, // 14
458 0x0001ffff, // 15
459 0x0000ffff, // 16
460 0x00007fff, // 17
461 0x00003fff, // 18
462 0x00001fff, // 19
463 0x00000fff, // 20
464 0x000007ff, // 21
465 0x000003ff, // 22
466 0x000001ff, // 23
467 0x000000ff, // 24
468 0x0000007f, // 25
469 0x0000003f, // 26
470 0x0000001f, // 27
471 0x0000000f, // 28
472 0x00000007, // 29
473 0x00000003, // 30
474 0x00000001, // 31
475 0x00000000}; // 32
476 pair.first = ipaddr;
477 pair.last = ipaddr | masks[mask];
478 pair.cidr = mask;
479 dc.add_pair(pair);
480 }
481 if (!tsa(tok, token_semi)) return false;
482 return true;
483 }
484
485
486 ////////////////////////////////////////////////
487 //
185 bool parse_syslogconfig(TOKEN &tok, CONFIG &dc); 488 bool parse_syslogconfig(TOKEN &tok, CONFIG &dc);
186 bool parse_syslogconfig(TOKEN &tok, CONFIG &dc) { 489 bool parse_syslogconfig(TOKEN &tok, CONFIG &dc) {
187 char *name = tok.next(); 490 char *name = tok.next();
188 parser_style parser;
189 if (!tsa(tok, token_lbrace)) return false; 491 if (!tsa(tok, token_lbrace)) return false;
190 while (true) { 492 SYSLOGCONFIGP con = new SYSLOGCONFIG(tok, name);
191 char *have = tok.next();
192 if (have == token_parser) {
193 have = tok.next();
194 if (have == token_cisco) parser = cisco;
195 else if (have == token_ssh) parser = ssh;
196 else {
197 tok.token_error("cisco/ssh", have);
198 return false;
199 }
200 if (!tsa(tok, token_semi)) return false;
201 }
202 else if (have == token_rbrace) {
203 break; // done
204 }
205 else {
206 tok.token_error("file keyword", have);
207 return false;
208 }
209 }
210 if (!tsa(tok, token_semi)) return false;
211 SYSLOGCONFIGP con = new SYSLOGCONFIG(tok, name, parser);
212 if (con->failed()) { 493 if (con->failed()) {
213 delete con; 494 delete con;
214 return false; 495 return false;
215 } 496 }
216 dc.add_syslogconfig(con); 497 dc.add_syslogconfig(con);
498 while (true) {
499 char *have = tok.next();
500 if (!have) break;
501 if (have == token_rbrace) break;
502 if (have == token_pattern) {
503 if (!parse_pattern(tok, *con)) return false;
504 }
505 else {
506 tok.token_error("pattern", have);
507 return false;
508 }
509 }
510 if (!tsa(tok, token_semi)) return false;
217 return true; 511 return true;
218 } 512 }
219 513
220 514
221 //////////////////////////////////////////////// 515 ////////////////////////////////////////////////
225 int count = 0; 519 int count = 0;
226 TOKEN tok(fn, &dc.config_files); 520 TOKEN tok(fn, &dc.config_files);
227 while (true) { 521 while (true) {
228 char *have = tok.next(); 522 char *have = tok.next();
229 if (!have) break; 523 if (!have) break;
230 if (have == token_file) { 524 if (have == token_threshold) {
231 if (!parse_syslogconfig(tok, dc)) { 525 have = tok.next();
232 tok.token_error("load_conf() failed to parse syslogconfig"); 526 dc.set_threshold(atoi(have));
233 return false; 527 if (!tsa(tok, token_semi)) return false;
234 } 528 }
235 else count++; 529 else if (have == token_ignore) {
530 if (!parse_ignore(tok, dc)) return false;
531 }
532 else if (have == token_file) {
533 if (!parse_syslogconfig(tok, dc)) return false;
534 count++;
236 } 535 }
237 else { 536 else {
238 tok.token_error(token_file, have); 537 tok.token_error("threshold/ignore/file", have);
239 return false; 538 return false;
240 } 539 }
241 } 540 }
242 tok.token_error("load_conf() found %d syslog files in %s", count, fn); 541 tok.token_error("load_conf() found %d syslog files in %s", count, fn);
243 return (!dc.syslogconfigs.empty()); 542 return (!dc.syslogconfigs.empty());
246 545
247 //////////////////////////////////////////////// 546 ////////////////////////////////////////////////
248 // init the tokens 547 // init the tokens
249 // 548 //
250 void token_init() { 549 void token_init() {
251 token_cisco = register_string("cisco"); 550 token_bucket = register_string("bucket");
252 token_file = register_string("file"); 551 token_file = register_string("file");
552 token_ignore = register_string("ignore");
253 token_include = register_string("include"); 553 token_include = register_string("include");
554 token_index = register_string("index");
254 token_lbrace = register_string("{"); 555 token_lbrace = register_string("{");
255 token_parser = register_string("parser"); 556 token_pattern = register_string("pattern");
256 token_rbrace = register_string("}"); 557 token_rbrace = register_string("}");
257 token_semi = register_string(";"); 558 token_semi = register_string(";");
258 token_ssh = register_string("ssh"); 559 token_slash = register_string("/");
259 } 560 token_threshold = register_string("threshold");
561 }