Mercurial > syslog2iptables
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 } |