1 /*
2  * Copyright (c) 2003, 2004 Niels Provos <provos@citi.umich.edu>
3  * All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  * This program is free software; you can redistribute it and/or modify
19  * it under the terms of the GNU General Public License as published by
20  * the Free Software Foundation; either version 2 of the License, or
21  * (at your option) any later version.
22  *
23  * This program is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  * GNU General Public License for more details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program; if not, write to the Free Software
30  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
31  */
32 
33 #include <sys/types.h>
34 #include <sys/param.h>
35 
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39 
40 #include <sys/stat.h>
41 #include <sys/queue.h>
42 #include <sys/tree.h>
43 #ifdef HAVE_SYS_TIME_H
44 #include <sys/time.h>
45 #endif
46 
47 #include <ctype.h>
48 #include <err.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 
53 #include <dnet.h>
54 #include <event.h>
55 #include <evdns.h>
56 
57 #include "tagging.h"
58 #include "histogram.h"
59 #include "keycount.h"
60 #include "analyze.h"
61 #include "filter.h"
62 
63 char *os_report_file = NULL;
64 char *port_report_file = NULL;
65 char *spammer_report_file = NULL;
66 char *country_report_file = NULL;
67 
68 static int checkpoint_doreplay;		/* externally set by honeydstats */
69 static struct event ev_analyze;
70 
71 struct kctree oses;
72 struct kctree ports;
73 struct kctree spammers;
74 struct kctree countries;
75 struct kctree country_cache;
76 
77 #define ROL64(x, b)	(((x) << b) | ((x) >> (64 - b)))
78 #define ROR64(x, b)	(((x) >> b) | ((x) << (64 - b)))
79 
80 /*
81  * Thomas Wang's 64-bit hash function from
82  *   www.concentric.net/~Ttwang/tech/inthash.htm
83  */
84 static __inline uint64_t
longhash1(uint64_t key)85 longhash1(uint64_t key)
86 {
87   key += ~(key << 32);
88   key ^= ROR64(key, 22);
89   key += ~(key << 13);
90   key ^= ROR64(key, 8);
91   key += (key << 3);
92   key ^= ROR64(key, 15);
93   key += ~(key << 27);
94   key ^= ROR64(key, 31);
95   return key;
96 }
97 
98 static __inline uint32_t
port_hash(const struct addr * src,const struct addr * dst)99 port_hash(const struct addr *src, const struct addr *dst)
100 {
101 	uint32_t a = src->addr_ip;
102 	uint32_t b = dst->addr_ip;
103 	return ((uint32_t)(longhash1(((uint64_t)a << 32) | b)));
104 }
105 
106 void
port_key_extract(struct keycount * keycount,void ** pkey,size_t * pkeylen)107 port_key_extract(struct keycount *keycount, void **pkey, size_t *pkeylen)
108 {
109 	if ((*pkey = calloc(1, sizeof(uint16_t))) == NULL)
110 		err(1, "%s: calloc");
111 	memcpy(*pkey, keycount->key, sizeof(uint16_t));
112 	*pkeylen = sizeof(uint16_t);
113 }
114 
115 char *
port_key_print(void * key,size_t keylen)116 port_key_print(void *key, size_t keylen)
117 {
118 	static char sport[7];
119 	snprintf(sport, sizeof(sport), "%d", *(uint16_t *)key);
120 	return (sport);
121 }
122 
123 void
spammer_key_extract(struct keycount * keycount,void ** pkey,size_t * pkeylen)124 spammer_key_extract(struct keycount *keycount, void **pkey, size_t *pkeylen)
125 {
126 	if ((*pkey = calloc(1, keycount->keylen)) == NULL)
127 		err(1, "%s: calloc");
128 	memcpy(*pkey, keycount->key, keycount->keylen);
129 	*pkeylen = keycount->keylen;
130 }
131 
132 char *
spammer_key_print(void * key,size_t keylen)133 spammer_key_print(void *key, size_t keylen)
134 {
135 	struct addr addr;
136 	addr_pack(&addr, ADDR_TYPE_IP, IP_ADDR_BITS, key, IP_ADDR_LEN);
137 	return (addr_ntoa(&addr));
138 }
139 
140 void
country_key_extract(struct keycount * keycount,void ** pkey,size_t * pkeylen)141 country_key_extract(struct keycount *keycount, void **pkey, size_t *pkeylen)
142 {
143 	if ((*pkey = calloc(1, keycount->keylen)) == NULL)
144 		err(1, "%s: calloc");
145 	memcpy(*pkey, keycount->key, keycount->keylen);
146 	*pkeylen = keycount->keylen;
147 }
148 
149 char *
country_key_print(void * key,size_t keylen)150 country_key_print(void *key, size_t keylen)
151 {
152 	return (key);
153 }
154 
155 struct aux {
156 	SPLAY_HEAD(auxtree, auxkey) tree;
157 	TAILQ_HEAD(auxq, auxkey) queue;
158 	int limit;
159 	int entries;
160 };
161 
162 static int
aux_compare(struct auxkey * a,struct auxkey * b)163 aux_compare(struct auxkey *a, struct auxkey *b)
164 {
165 	if (a->value < b->value)
166 		return (-1);
167 	if (a->value > b->value)
168 		return (1);
169 	return (0);
170 }
171 
172 SPLAY_PROTOTYPE(auxtree, auxkey, node, aux_compare);
173 SPLAY_GENERATE(auxtree, auxkey, node, aux_compare);
174 
175 void *
aux_create(void)176 aux_create(void)
177 {
178 	struct aux *aux;
179 
180 	if ((aux = calloc(1, sizeof(struct aux))) == NULL)
181 		err(1, "%s: calloc");
182 	SPLAY_INIT(&aux->tree);
183 	TAILQ_INIT(&aux->queue);
184 	aux->limit = 100000;	/* Make better at some point */
185 
186 	return (aux);
187 }
188 
189 void
aux_free(void * arg)190 aux_free(void *arg)
191 {
192 	struct aux *aux = arg;
193 	struct auxtree *tree = &aux->tree;
194 	struct auxkey *key;
195 
196 	while ((key = SPLAY_ROOT(tree)) != NULL) {
197 		SPLAY_REMOVE(auxtree, tree, key);
198 		free(key);
199 	}
200 	free(arg);
201 }
202 
203 /* Returns one if the key is new */
204 
205 int
aux_enter(struct aux * aux,uint32_t value)206 aux_enter(struct aux *aux, uint32_t value)
207 {
208 	struct auxtree *tree = &aux->tree;
209 	struct auxq *queue = &aux->queue;
210 	struct auxkey tmp, *key;
211 
212 	tmp.value = value;
213 	if ((key = SPLAY_FIND(auxtree, tree, &tmp)) != NULL) {
214 		/* Mark this entry as recently used - LRU fashion */
215 		TAILQ_REMOVE(queue, key, next);
216 		TAILQ_INSERT_HEAD(queue, key, next);
217 		return (0);
218 	}
219 
220 	if (aux->entries >= aux->limit) {
221 		key = TAILQ_LAST(queue, auxq);
222 
223 		/* The old entry has to go, bye bye */
224 		SPLAY_REMOVE(auxtree, tree, key);
225 		TAILQ_REMOVE(queue, key, next);
226 		memset(key, 0, sizeof(struct auxkey));
227 		aux->entries--;
228 	} else {
229 		if ((key = calloc(1, sizeof(struct auxkey))) == NULL)
230 			err(1, "%s: calloc");
231 	}
232 	key->value = tmp.value;
233 
234 	/* Insert the new key */
235 	SPLAY_INSERT(auxtree, tree, key);
236 	TAILQ_INSERT_TAIL(queue, key, next);
237 	aux->entries++;
238 
239 	return (1);
240 }
241 
242 void
os_key_extract(struct keycount * keycount,void ** pkey,size_t * pkeylen)243 os_key_extract(struct keycount *keycount, void **pkey, size_t *pkeylen)
244 {
245 	const char *key = keycount->key;
246 
247 	if ((*pkey = strdup(key)) == NULL)
248 		err(1, "%s: strdup");
249 	*pkeylen = strlen(key) + 1;
250 }
251 
252 char *
os_key_print(void * key,size_t keylen)253 os_key_print(void *key, size_t keylen)
254 {
255 	return (key);
256 }
257 
258 static int
report_compare(struct report * a,struct report * b)259 report_compare(struct report *a, struct report *b)
260 {
261 	return (key_compare(a->key, a->keylen, b->key, b->keylen));
262 }
263 
264 SPLAY_HEAD(reporttree, report);
265 SPLAY_PROTOTYPE(reporttree, report, node, report_compare);
266 SPLAY_GENERATE(reporttree, report, node, report_compare);
267 
268 void
analyze_init(void)269 analyze_init(void)
270 {
271 	struct timeval tv;
272 
273 	evtimer_set(&ev_analyze, analyze_report_cb, &ev_analyze);
274 
275 	timerclear(&tv);
276 	tv.tv_sec = ANALYZE_REPORT_INTERVAL;
277 	evtimer_add(&ev_analyze, &tv);
278 
279 	SPLAY_INIT(&oses);
280 	SPLAY_INIT(&ports);
281 	SPLAY_INIT(&spammers);
282 	SPLAY_INIT(&countries);
283 	SPLAY_INIT(&country_cache);
284 
285 	evdns_init();
286 }
287 
288 void
analyze_set_checkpoint_doreplay(int doit)289 analyze_set_checkpoint_doreplay(int doit)
290 {
291 	checkpoint_doreplay = doit;
292 }
293 
294 void
analyze_record(const struct record * record)295 analyze_record(const struct record *record)
296 {
297 	if (record->dst_port == 25 && record->bytes != 0)
298 		analyze_spammer_enter(&record->src, record->bytes);
299 
300 	/*
301 	 * Records may be duplicated if they carry extra payload hashes.
302 	 * In those cases, we need to ignore the connection information.
303 	 * We also want to ignore connection information for connections
304 	 * that were generated by the honeypots.
305 	 */
306 	if ((record->state & RECORD_STATE_NEW) == 0 ||
307 	    (record->flags & REC_FLAG_LOCAL) != 0)
308 		return;
309 
310 	/* Enter OS fingerprint */
311 	if (record->os_fp == NULL)
312 		analyze_os_enter(&record->src, "unknown");
313 	else
314 		analyze_os_enter(&record->src, record->os_fp);
315 
316 	/* Enter Port Analysis */
317 	analyze_port_enter(record->dst_port, &record->src, &record->dst);
318 
319 	/* Entry country analysis */
320 	analyze_country_enter(&record->src, &record->dst);
321 }
322 
323 void
analyze_spammer_enter(const struct addr * src,uint32_t bytes)324 analyze_spammer_enter(const struct addr *src, uint32_t bytes)
325 {
326 	struct keycount tmpkey, *key;
327 
328 	tmpkey.key = &src->addr_ip;
329 	tmpkey.keylen = sizeof(src->addr_ip);
330 
331 	if ((key = SPLAY_FIND(kctree, &spammers, &tmpkey)) == NULL) {
332 		key = keycount_new(&src->addr_ip, sizeof(src->addr_ip),
333 		    NULL, NULL);
334 		SPLAY_INSERT(kctree, &spammers, key);
335 	}
336 
337 	count_increment(key->count, bytes);
338 }
339 
340 struct country_state {
341 	struct addr src;
342 	struct addr dst;
343 	int result_from_cache;
344 };
345 
346 void
analyze_country_enter_cb(int result,char type,int count,int ttl,void * addresses,void * arg)347 analyze_country_enter_cb(int result, char type, int count, int ttl,
348     void *addresses, void *arg)
349 {
350 	struct country_state *state = arg;
351 	struct addr *src = &state->src;
352 	struct keycount tmpkey, *key;
353 	char tld[20];
354 
355 	if (result != DNS_ERR_NONE || count != 1 || type != DNS_PTR) {
356 		/* Enter into our negative cache */
357 		if (!state->result_from_cache) {
358 			tmpkey.key = &src->addr_ip;
359 			tmpkey.keylen = sizeof(src->addr_ip);
360 			key = SPLAY_FIND(kctree, &country_cache, &tmpkey);
361 			if (!key) {
362 				key = keycount_new(
363 					&src->addr_ip,
364 					sizeof(src->addr_ip),
365 					NULL, NULL);
366 				SPLAY_INSERT(kctree, &country_cache, key);
367 			}
368 			count_increment(key->count, 1);
369 		}
370 
371 		strlcpy(tld, "unknown", sizeof(tld));
372 	} else {
373 		const char *hostname = *(char **)addresses;
374 		int i;
375 		/* Extract the country key */
376 		for (i = strlen(hostname) - 1; i >= 0; --i) {
377 			if (hostname[i] == '.') {
378 				i += 1;
379 				break;
380 			}
381 		}
382 
383 		strlcpy(tld, hostname + i, sizeof(tld));
384 		for (i = 0; i < strlen(tld); i++) {
385 			if (isdigit(tld[i])) {
386 				strlcpy(tld, "unknown", sizeof(tld));
387 				break;
388 			}
389 			tld[i] = tolower(tld[i]);
390 		}
391 	}
392 
393 	tmpkey.key = tld;
394 	tmpkey.keylen = strlen(tmpkey.key) + 1;
395 	if ((key = SPLAY_FIND(kctree, &countries, &tmpkey)) == NULL) {
396 		key = keycount_new(tld, strlen(tld) + 1, aux_create, aux_free);
397 		SPLAY_INSERT(kctree, &countries, key);
398 	}
399 
400 	/* If the address is new, we are going to resolve it */
401 	if (aux_enter(key->auxilary, port_hash(&state->src, &state->dst)))
402 		count_increment(key->count, 1);
403 	free(state);
404 }
405 
406 void
analyze_country_enter(const struct addr * addr,const struct addr * dst)407 analyze_country_enter(const struct addr *addr, const struct addr *dst)
408 {
409 	struct keycount tmpkey, *key;
410 
411 	struct country_state *state = calloc(1, sizeof(struct country_state));
412 	if (state == NULL)
413 		err(1, "%s: calloc", __func__);
414 
415 	state->src = *addr;
416 	state->dst = *dst;
417 
418 	/*
419 	 * Check if this IP returned a resolver error in the last hour.
420 	 */
421 	tmpkey.key = &addr->addr_ip;
422 	tmpkey.keylen = sizeof(addr->addr_ip);
423 	if ((key = SPLAY_FIND(kctree, &country_cache, &tmpkey)) != NULL) {
424 		if (count_get_minute(key->count) ||
425 		    count_get_hour(key->count)) {
426 			state->result_from_cache = 1;
427 			analyze_country_enter_cb(DNS_ERR_NOTEXIST, DNS_PTR,
428 			    0, 0, NULL, state);
429 			return;
430 		}
431 	}
432 
433 	if (!checkpoint_doreplay) {
434 		struct in_addr in;
435 		in.s_addr = addr->addr_ip;
436 		evdns_resolve_reverse(&in, 0, analyze_country_enter_cb, state);
437 	} else {
438 		/*
439 		 * If we are replaying a checkpoint, we do not want to do
440 		 * async calls.
441 		 */
442 		struct hostent *hp = gethostbyaddr(
443 			(const char *)&addr->addr_ip, IP_ADDR_LEN, AF_INET);
444 		if (hp == NULL) {
445 			analyze_country_enter_cb(DNS_ERR_NOTEXIST, DNS_PTR,
446 			    0, 0, NULL, state);
447 		} else {
448 			analyze_country_enter_cb(DNS_ERR_NONE, DNS_PTR,
449 			    1, 1200 /* ttl */, (void *)&hp->h_name, state);
450 		}
451 	}
452 }
453 
454 void
analyze_os_enter(const struct addr * addr,const char * osfp)455 analyze_os_enter(const struct addr *addr, const char *osfp)
456 {
457 	struct keycount tmpkey, *key;
458 
459 	tmpkey.key = osfp;
460 	tmpkey.keylen = strlen(osfp) + 1;
461 
462 	if ((key = SPLAY_FIND(kctree, &oses, &tmpkey)) == NULL) {
463 		key = keycount_new(osfp, strlen(osfp) + 1,
464 		    aux_create, aux_free);
465 		SPLAY_INSERT(kctree, &oses, key);
466 	}
467 
468 	/* If the address is new, we are going to increase the counter */
469 	if (aux_enter(key->auxilary, addr->addr_ip))
470 		count_increment(key->count, 1);
471 }
472 
473 void
analyze_port_enter(uint16_t port,const struct addr * src,const struct addr * dst)474 analyze_port_enter(uint16_t port,
475     const struct addr *src, const struct addr *dst)
476 {
477 	struct keycount tmpkey, *key;
478 
479 	tmpkey.key = &port;
480 	tmpkey.keylen = sizeof(port);
481 
482 	if ((key = SPLAY_FIND(kctree, &ports, &tmpkey)) == NULL) {
483 		key = keycount_new(&port, sizeof(port),
484 		    aux_create, aux_free);
485 		SPLAY_INSERT(kctree, &ports, key);
486 	}
487 
488 	/* If the address is new, we are going to increase the counter */
489 	if (aux_enter(key->auxilary, port_hash(src, dst)))
490 		count_increment(key->count, 1);
491 }
492 
493 void
report_to_file(struct reporttree * tree,char * filename,char * (* print)(void *,size_t))494 report_to_file(struct reporttree *tree, char *filename,
495     char *(*print)(void *, size_t))
496 {
497 	static char line[1024];
498 	FILE *fout;
499 
500 	/* We do not create report files while we are replaying a checkpoint */
501 	if (checkpoint_doreplay)
502 		return;
503 
504 	/* create a temporary file */
505 	strlcpy(line, filename, sizeof(line));
506 	strlcat(line, ".tmp", sizeof(line));
507 
508 	if ((fout = fopen(line, "w")) != NULL) {
509 		report_print(tree, fout, print);
510 		fclose(fout);
511 		/* This is an atomic operation */
512 		rename(line, filename);
513 		chmod(filename, S_IRWXU | S_IRGRP | S_IROTH);
514 	} else {
515 		warn("%s: fopen('%s')", __func__, line);
516 	}
517 }
518 
519 void
report_print(struct reporttree * tree,FILE * out,char * (* print)(void *,size_t))520 report_print(struct reporttree *tree, FILE *out,
521     char *(*print)(void *, size_t))
522 {
523 	struct report *report;
524 
525 	/* Now print the information in alphabetical order */
526 	SPLAY_FOREACH(report, reporttree, tree) {
527 		fprintf(out, "%25s: %7d %7d %7d\n",
528 		    print(report->key, report->keylen),
529 		    report->minute, report->hour, report->day);
530 	}
531 }
532 
533 void
report_free(struct reporttree * tree)534 report_free(struct reporttree *tree)
535 {
536 	struct report *report;
537 
538 	/* And then free the whole tree */
539 	while ((report = SPLAY_ROOT(tree)) != NULL) {
540 		SPLAY_REMOVE(reporttree, tree, report);
541 
542 		free(report->key);
543 		free(report);
544 	}
545 	free(tree);
546 }
547 
548 struct reporttree *
report_create(struct kctree * kctree,void (* extract)(struct keycount *,void **,size_t *))549 report_create(struct kctree *kctree,
550     void (*extract)(struct keycount *, void **, size_t *))
551 {
552 	struct reporttree *tree;
553 	struct report *report;
554 	struct keycount *kc, *next;
555 
556 	if ((tree = calloc(1, sizeof(struct reporttree))) == NULL)
557 		err(1, "%s: calloc", __func__);
558 
559 	SPLAY_INIT(tree);
560 
561 	for (kc = SPLAY_MIN(kctree, kctree); kc != NULL; kc = next) {
562 		struct report tmp;
563 		uint32_t sum = 0;
564 
565 		next = SPLAY_NEXT(kctree, kctree, kc);
566 
567 		(*extract)(kc, &tmp.key, &tmp.keylen);
568 		if ((report = SPLAY_FIND(reporttree, tree, &tmp)) == NULL) {
569 			report = calloc(1, sizeof(struct report));
570 			if (report == NULL)
571 				err(1, "%s: calloc");
572 			report->key = tmp.key;
573 			report->keylen = tmp.keylen;
574 			SPLAY_INSERT(reporttree, tree, report);
575 		}
576 
577 		/* Now get the data together */
578 		if ((sum = count_get_minute(kc->count)) != 0)
579 			report->minute += sum;
580 		if ((sum += count_get_hour(kc->count)) != 0)
581 			report->hour += sum;
582 		if ((sum += count_get_day(kc->count)) != 0)
583 			report->day += sum;
584 
585 		if (!sum) {
586 			SPLAY_REMOVE(kctree, kctree, kc);
587 			keycount_free(kc);
588 		}
589 	}
590 
591 	return (tree);
592 }
593 
594 void
make_report(struct kctree * kctree,char * filename,void (* extract)(struct keycount *,void **,size_t *),char * (* print)(void *,size_t))595 make_report(struct kctree *kctree, char *filename,
596     void (*extract)(struct keycount *, void **, size_t *),
597     char *(*print)(void *, size_t))
598 {
599 	struct reporttree *tree = report_create(kctree, extract);
600 
601 	report_print(tree, stderr, print);
602 
603 	if (filename != NULL)
604 		report_to_file(tree, filename, print);
605 
606 	report_free(tree);
607 }
608 
609 struct filterarg {
610 	struct reporttree *src;
611 	struct reporttree *dst;
612 };
613 
614 void
analyze_filter_cb(void * reparg,void * treearg)615 analyze_filter_cb(void *reparg, void *treearg)
616 {
617 	struct report *report = reparg;
618 	struct filterarg *fa = treearg;
619 
620 	if (SPLAY_FIND(reporttree, fa->dst, report) != NULL)
621 		return;
622 
623 	SPLAY_REMOVE(reporttree, fa->src, report);
624 	SPLAY_INSERT(reporttree, fa->dst, report);
625 }
626 
627 void
analyze_print_port_report()628 analyze_print_port_report()
629 {
630 	struct reporttree *tree, *filtered_tree;
631 	struct filtertree *min_filters, *hour_filters, *day_filters;
632 	struct report *report;
633 	struct filterarg fa;
634 
635 	tree = report_create(&ports, port_key_extract);
636 
637 	/* Filter trees for Minutes, Hours and Days */
638 	min_filters = filter_create();
639 	hour_filters = filter_create();
640 	day_filters = filter_create();
641 	SPLAY_FOREACH(report, reporttree, tree) {
642 		filter_insert(min_filters, report->minute, report);
643 		filter_insert(hour_filters, report->hour, report);
644 		filter_insert(day_filters, report->day, report);
645 	}
646 
647 	if ((filtered_tree = calloc(1, sizeof(struct reporttree))) == NULL)
648 		err(1, "%s: calloc");
649 	SPLAY_INIT(filtered_tree);
650 
651 	/*
652 	 * Object passed to the call back function to  merge the different
653 	 * filter trees.
654 	 */
655 	fa.src = tree;
656 	fa.dst = filtered_tree;
657 
658 	filter_top(min_filters, 5, analyze_filter_cb, &fa);
659 	filter_top(hour_filters,10, analyze_filter_cb, &fa);
660 	filter_top(day_filters, 15, analyze_filter_cb, &fa);
661 
662 	filter_free(min_filters);
663 	filter_free(hour_filters);
664 	filter_free(day_filters);
665 	report_free(tree);
666 
667 	fprintf(stderr, "Destination Port Statistics\n");
668 	report_print(filtered_tree, stderr, port_key_print);
669 
670 	if (port_report_file != NULL)
671 		report_to_file(filtered_tree, port_report_file,
672 		    port_key_print);
673 
674 	report_free(filtered_tree);
675 }
676 
677 void
analyze_print_spammer_report()678 analyze_print_spammer_report()
679 {
680 	struct reporttree *tree, *filtered_tree;
681 	struct filtertree *min_filters, *hour_filters, *day_filters;
682 	struct report *report;
683 	struct filterarg fa;
684 
685 	tree = report_create(&spammers, spammer_key_extract);
686 
687 	/* Filter trees for Minutes, Hours and Days */
688 	min_filters = filter_create();
689 	hour_filters = filter_create();
690 	day_filters = filter_create();
691 	SPLAY_FOREACH(report, reporttree, tree) {
692 		filter_insert(min_filters, report->minute, report);
693 		filter_insert(hour_filters, report->hour, report);
694 		filter_insert(day_filters, report->day, report);
695 	}
696 
697 	if ((filtered_tree = calloc(1, sizeof(struct reporttree))) == NULL)
698 		err(1, "%s: calloc");
699 	SPLAY_INIT(filtered_tree);
700 
701 	/*
702 	 * Object passed to the call back function to  merge the different
703 	 * filter trees.
704 	 */
705 	fa.src = tree;
706 	fa.dst = filtered_tree;
707 
708 	filter_top(min_filters, 5, analyze_filter_cb, &fa);
709 	filter_top(hour_filters,10, analyze_filter_cb, &fa);
710 	filter_top(day_filters, 20, analyze_filter_cb, &fa);
711 
712 	filter_free(min_filters);
713 	filter_free(hour_filters);
714 	filter_free(day_filters);
715 	report_free(tree);
716 
717 	fprintf(stderr, "Spammer Address Statistics\n");
718 	report_print(filtered_tree, stderr, spammer_key_print);
719 
720 	if (spammer_report_file != NULL)
721 		report_to_file(filtered_tree, spammer_report_file,
722 		    spammer_key_print);
723 
724 	report_free(filtered_tree);
725 }
726 
727 void
analyze_print_country_report()728 analyze_print_country_report()
729 {
730 	struct reporttree *tree, *filtered_tree;
731 	struct filtertree *min_filters, *hour_filters, *day_filters;
732 	struct report *report;
733 	struct filterarg fa;
734 
735 	tree = report_create(&countries, country_key_extract);
736 
737 	/* Filter trees for Minutes, Hours and Days */
738 	min_filters = filter_create();
739 	hour_filters = filter_create();
740 	day_filters = filter_create();
741 	SPLAY_FOREACH(report, reporttree, tree) {
742 		filter_insert(min_filters, report->minute, report);
743 		filter_insert(hour_filters, report->hour, report);
744 		filter_insert(day_filters, report->day, report);
745 	}
746 
747 	if ((filtered_tree = calloc(1, sizeof(struct reporttree))) == NULL)
748 		err(1, "%s: calloc");
749 	SPLAY_INIT(filtered_tree);
750 
751 	/*
752 	 * Object passed to the call back function to  merge the different
753 	 * filter trees.
754 	 */
755 	fa.src = tree;
756 	fa.dst = filtered_tree;
757 
758 	filter_top(min_filters, 5, analyze_filter_cb, &fa);
759 	filter_top(hour_filters,10, analyze_filter_cb, &fa);
760 	filter_top(day_filters, 20, analyze_filter_cb, &fa);
761 
762 	filter_free(min_filters);
763 	filter_free(hour_filters);
764 	filter_free(day_filters);
765 	report_free(tree);
766 
767 	fprintf(stderr, "Country Activity Statistics\n");
768 	report_print(filtered_tree, stderr, country_key_print);
769 
770 	if (country_report_file != NULL)
771 		report_to_file(filtered_tree, country_report_file,
772 		    country_key_print);
773 
774 	report_free(filtered_tree);
775 }
776 
777 void
analyze_print_report()778 analyze_print_report()
779 {
780 	fprintf(stderr, "Operating System Statistics\n");
781 	make_report(&oses, os_report_file, os_key_extract, os_key_print);
782 
783 	analyze_print_port_report();
784 	analyze_print_spammer_report();
785 	analyze_print_country_report();
786 }
787 
788 void
analyze_report_cb(int fd,short what,void * arg)789 analyze_report_cb(int fd, short what, void *arg)
790 {
791 	struct event *ev = arg;
792 	struct timeval tv;
793 
794 	timerclear(&tv);
795 	tv.tv_sec = ANALYZE_REPORT_INTERVAL;
796 	evtimer_add(ev, &tv);
797 
798 	analyze_print_report();
799 }
800 
801 #define OS_NUM_OSES	12
802 
803 void
os_test()804 os_test()
805 {
806 	char *fingerprints[OS_NUM_OSES] = {
807 		"Linux",
808 		"Windows",
809 		"NetBSD",
810 		"OpenBSD",
811 		"Windows",
812 		"Windows",
813 		"Linux",
814 		"Windows",
815 		"Linux",
816 		"OpenBSD",
817 		"FreeBSD",
818 		"unknown"
819 	};
820 	struct addr src;
821 	struct timeval tv;
822 	rand_t *rand = rand_open();
823 	int i, j;
824 
825 	gettimeofday(&tv, NULL);
826 	count_set_time(&tv);
827 
828 	addr_pton("127.0.0.1", &src);
829 	for (i = 0; i < 24 * 60 * 2 + 60 * 4; i++) {
830 		for (j = 0; i < 24 * 60 * 2 &&
831 		    j < rand_uint8(rand) % 50000 + 5000; j++) {
832 			src.addr_ip = rand_uint32(rand);
833 			analyze_os_enter(&src,
834 			    fingerprints[rand_uint8(rand) % OS_NUM_OSES]);
835 		}
836 		tv.tv_sec += 30;
837 
838 		if (i % 120 == 0) {
839 			fprintf(stderr, "%ld:\n", tv.tv_sec);
840 			make_report(&oses, NULL, os_key_extract, os_key_print);
841 		}
842 	}
843 
844 	if (SPLAY_ROOT(&oses) != NULL)
845 		errx(1, "oses fingerprints should have been purged");
846 
847 	count_set_time(NULL);
848 
849 	rand_close(rand);
850 	fprintf(stderr, "\t%s: OK\n", __func__);
851 }
852 
853 void
analyze_test(void)854 analyze_test(void)
855 {
856 	os_test();
857 }
858