710a0e04f40c50dc55aa79653b1f7cc989246720
[darkstat] / hosts_db.c
1 /* darkstat 3
2 * copyright (c) 2001-2014 Emil Mikulic.
3 *
4 * hosts_db.c: database of hosts, ports, protocols.
5 *
6 * You may use, modify and redistribute this file under the terms of the
7 * GNU General Public License version 2. (see COPYING.GPL)
8 */
9
10 #include "cdefs.h"
11 #include "conv.h"
12 #include "decode.h"
13 #include "dns.h"
14 #include "err.h"
15 #include "hosts_db.h"
16 #include "db.h"
17 #include "html.h"
18 #include "ncache.h"
19 #include "now.h"
20 #include "opt.h"
21 #include "str.h"
22
23 #include <netdb.h> /* struct addrinfo */
24 #include <assert.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h> /* memset(), strcmp() */
29 #include <time.h>
30 #include <unistd.h>
31
32 int hosts_db_show_macs = 0;
33
34 /* FIXME: specify somewhere more sane/tunable */
35 #define MAX_ENTRIES 30 /* in an HTML table rendered from a hashtable */
36
37 typedef uint32_t (hash_func_t)(const struct hashtable *, const void *);
38 typedef void (free_func_t)(struct bucket *);
39 typedef const void * (key_func_t)(const struct bucket *);
40 typedef int (find_func_t)(const struct bucket *, const void *);
41 typedef struct bucket * (make_func_t)(const void *);
42 typedef void (format_cols_func_t)(struct str *);
43 typedef void (format_row_func_t)(struct str *, const struct bucket *);
44
45 struct hashtable {
46 uint8_t bits; /* size of hashtable in bits */
47 uint32_t size, mask;
48 uint32_t count, count_max, count_keep; /* items in table */
49 uint32_t coeff; /* coefficient for Fibonacci hashing */
50 struct bucket **table;
51
52 struct {
53 uint64_t inserts, searches, deletions, rehashes;
54 } stats;
55
56 hash_func_t *hash_func;
57 /* returns hash value of given key (passed as void*) */
58
59 free_func_t *free_func;
60 /* free of bucket payload */
61
62 key_func_t *key_func;
63 /* returns pointer to key of bucket (to pass to hash_func) */
64
65 find_func_t *find_func;
66 /* returns true if given bucket matches key (passed as void*) */
67
68 make_func_t *make_func;
69 /* returns bucket containing new record with key (passed as void*) */
70
71 format_cols_func_t *format_cols_func;
72 /* append table columns to str */
73
74 format_row_func_t *format_row_func;
75 /* format record and append to str */
76 };
77
78 static void hashtable_reduce(struct hashtable *ht);
79 static void hashtable_free(struct hashtable *h);
80
81 #define HOST_BITS 1 /* initial size of hosts table */
82 #define PORT_BITS 1 /* initial size of ports tables */
83 #define PROTO_BITS 1 /* initial size of proto table */
84
85 /* We only use one hosts_db hashtable and this is it. */
86 static struct hashtable *hosts_db = NULL;
87
88 /* phi^-1 (reciprocal of golden ratio) = (sqrt(5) - 1) / 2 */
89 static const double phi_1 =
90 0.61803398874989490252573887119069695472717285156250;
91
92 /* Co-prime of u, using phi^-1 */
93 static uint32_t coprime(const uint32_t u) {
94 return ( (uint32_t)( (double)(u) * phi_1 ) | 1U );
95 }
96
97 /*
98 * This is the "recommended" IPv4 hash function, as seen in FreeBSD's
99 * src/sys/netinet/tcp_hostcache.c 1.1
100 */
101 static uint32_t ipv4_hash(const struct addr *const a) {
102 uint32_t ip = a->ip.v4;
103 return ( (ip) ^ ((ip) >> 7) ^ ((ip) >> 17) );
104 }
105
106 #ifndef s6_addr32
107 # ifdef sun
108 /*
109 * http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/netinet/in.h#130
110 */
111 # define s6_addr32 _S6_un._S6_u32
112 # else
113 /* Covers OpenBSD and FreeBSD. The macro __USE_GNU has
114 * taken care of GNU/Linux and GNU/kfreebsd. */
115 # define s6_addr32 __u6_addr.__u6_addr32
116 # endif
117 #endif
118
119 /*
120 * This is the IPv6 hash function used by FreeBSD in the same file as above,
121 * svn rev 122922.
122 */
123 static uint32_t ipv6_hash(const struct addr *const a) {
124 const struct in6_addr *const ip6 = &(a->ip.v6);
125 return ( ip6->s6_addr32[0] ^ ip6->s6_addr32[1] ^
126 ip6->s6_addr32[2] ^ ip6->s6_addr32[3] );
127 }
128
129 /* ---------------------------------------------------------------------------
130 * hash_func collection
131 */
132 static uint32_t
133 hash_func_host(const struct hashtable *h _unused_, const void *key)
134 {
135 const struct addr *a = key;
136 if (a->family == IPv4)
137 return (ipv4_hash(a));
138 else {
139 assert(a->family == IPv6);
140 return (ipv6_hash(a));
141 }
142 }
143
144 #define CASTKEY(type) (*((const type *)key))
145
146 static uint32_t
147 hash_func_short(const struct hashtable *h, const void *key)
148 {
149 return (CASTKEY(uint16_t) * h->coeff);
150 }
151
152 static uint32_t
153 hash_func_byte(const struct hashtable *h, const void *key)
154 {
155 return (CASTKEY(uint8_t) * h->coeff);
156 }
157
158 /* ---------------------------------------------------------------------------
159 * key_func collection
160 */
161
162 static const void *
163 key_func_host(const struct bucket *b)
164 {
165 return &(b->u.host.addr);
166 }
167
168 static const void *
169 key_func_port_tcp(const struct bucket *b)
170 {
171 return &(b->u.port_tcp.port);
172 }
173
174 static const void *
175 key_func_port_udp(const struct bucket *b)
176 {
177 return &(b->u.port_udp.port);
178 }
179
180 static const void *
181 key_func_ip_proto(const struct bucket *b)
182 {
183 return &(b->u.ip_proto.proto);
184 }
185
186 /* ---------------------------------------------------------------------------
187 * find_func collection
188 */
189
190 static int
191 find_func_host(const struct bucket *b, const void *key)
192 {
193 return (addr_equal(key, &(b->u.host.addr)));
194 }
195
196 static int
197 find_func_port_tcp(const struct bucket *b, const void *key)
198 {
199 return (b->u.port_tcp.port == CASTKEY(uint16_t));
200 }
201
202 static int
203 find_func_port_udp(const struct bucket *b, const void *key)
204 {
205 return (b->u.port_udp.port == CASTKEY(uint16_t));
206 }
207
208 static int
209 find_func_ip_proto(const struct bucket *b, const void *key)
210 {
211 return (b->u.ip_proto.proto == CASTKEY(uint8_t));
212 }
213
214 /* ---------------------------------------------------------------------------
215 * make_func collection
216 */
217
218 #define MAKE_BUCKET(name_bucket, name_content, type) struct { \
219 struct bucket *next; \
220 uint64_t in, out, total; \
221 union { struct type t; } u; } _custom_bucket; \
222 struct bucket *name_bucket = xcalloc(1, sizeof(_custom_bucket)); \
223 struct type *name_content = &(name_bucket->u.type); \
224 name_bucket->next = NULL; \
225 name_bucket->in = name_bucket->out = name_bucket->total = 0;
226
227 static struct bucket *
228 make_func_host(const void *key)
229 {
230 MAKE_BUCKET(b, h, host);
231 h->addr = CASTKEY(struct addr);
232 h->dns = NULL;
233 h->last_seen_mono = 0;
234 memset(&h->mac_addr, 0, sizeof(h->mac_addr));
235 h->ports_tcp = NULL;
236 h->ports_tcp_remote = NULL;
237 h->ports_udp = NULL;
238 h->ports_udp_remote = NULL;
239 h->ip_protos = NULL;
240 return (b);
241 }
242
243 static void
244 free_func_host(struct bucket *b)
245 {
246 struct host *h = &(b->u.host);
247 if (h->dns != NULL) free(h->dns);
248 hashtable_free(h->ports_tcp);
249 hashtable_free(h->ports_tcp_remote);
250 hashtable_free(h->ports_udp);
251 hashtable_free(h->ports_udp_remote);
252 hashtable_free(h->ip_protos);
253 }
254
255 static struct bucket *
256 make_func_port_tcp(const void *key)
257 {
258 MAKE_BUCKET(b, p, port_tcp);
259 p->port = CASTKEY(uint16_t);
260 p->syn = 0;
261 return (b);
262 }
263
264 static struct bucket *
265 make_func_port_udp(const void *key)
266 {
267 MAKE_BUCKET(b, p, port_udp);
268 p->port = CASTKEY(uint16_t);
269 return (b);
270 }
271
272 static struct bucket *
273 make_func_ip_proto(const void *key)
274 {
275 MAKE_BUCKET(b, p, ip_proto);
276 p->proto = CASTKEY(uint8_t);
277 return (b);
278 }
279
280 static void
281 free_func_simple(struct bucket *b _unused_)
282 {
283 /* nop */
284 }
285
286 /* ---------------------------------------------------------------------------
287 * format_func collection (ordered by struct)
288 */
289
290 static void
291 format_cols_host(struct str *buf)
292 {
293 /* FIXME: don't clobber parts of the query string
294 * specifically "full" and "start"
295 * when setting sort direction
296 */
297 str_append(buf,
298 "<table>\n"
299 "<tr>\n"
300 " <th>IP</th>\n"
301 " <th>Hostname</th>\n");
302 if (hosts_db_show_macs) str_append(buf,
303 " <th>MAC Address</th>\n");
304 str_append(buf,
305 " <th><a href=\"?sort=in\">In</a></th>\n"
306 " <th><a href=\"?sort=out\">Out</a></th>\n"
307 " <th><a href=\"?sort=total\">Total</a></th>\n");
308 if (opt_want_lastseen) str_append(buf,
309 " <th><a href=\"?sort=lastseen\">Last seen</a></th>\n");
310 str_append(buf,
311 "</tr>\n");
312 }
313
314 static void
315 format_row_host(struct str *buf, const struct bucket *b)
316 {
317 const char *ip = addr_to_str(&(b->u.host.addr));
318
319 str_appendf(buf,
320 "<tr>\n"
321 " <td><a href=\"./%s/\">%s</a></td>\n"
322 " <td>%s</td>\n",
323 ip, ip,
324 (b->u.host.dns == NULL) ? "" : b->u.host.dns);
325
326 if (hosts_db_show_macs)
327 str_appendf(buf,
328 " <td><tt>%x:%x:%x:%x:%x:%x</tt></td>\n",
329 b->u.host.mac_addr[0],
330 b->u.host.mac_addr[1],
331 b->u.host.mac_addr[2],
332 b->u.host.mac_addr[3],
333 b->u.host.mac_addr[4],
334 b->u.host.mac_addr[5]);
335
336 str_appendf(buf,
337 " <td class=\"num\">%'qu</td>\n"
338 " <td class=\"num\">%'qu</td>\n"
339 " <td class=\"num\">%'qu</td>\n",
340 (qu)b->in,
341 (qu)b->out,
342 (qu)b->total);
343
344 if (opt_want_lastseen) {
345 int64_t last = b->u.host.last_seen_mono;
346 int64_t now = (int64_t)now_mono();
347 struct str *last_str = NULL;
348
349 if ((now >= last) && (last != 0))
350 last_str = length_of_time(now - last);
351
352 str_append(buf, " <td class=\"num\">");
353 if (last_str == NULL) {
354 if (last == 0)
355 str_append(buf, "(never)");
356 else
357 str_appendf(buf, "(clock error: last = %qd, now = %qu)",
358 (qd)last,
359 (qu)now);
360 } else {
361 str_appendstr(buf, last_str);
362 str_free(last_str);
363 }
364 str_append(buf, "</td>");
365 }
366
367 str_appendf(buf, "</tr>\n");
368
369 /* Only resolve hosts "on demand" */
370 if (b->u.host.dns == NULL)
371 dns_queue(&(b->u.host.addr));
372 }
373
374 static void
375 format_cols_port_tcp(struct str *buf)
376 {
377 str_append(buf,
378 "<table>\n"
379 "<tr>\n"
380 " <th>Port</td>\n"
381 " <th>Service</td>\n"
382 " <th>In</td>\n"
383 " <th>Out</td>\n"
384 " <th>Total</td>\n"
385 " <th>SYNs</td>\n"
386 "</tr>\n"
387 );
388 }
389
390 static void
391 format_row_port_tcp(struct str *buf, const struct bucket *b)
392 {
393 const struct port_tcp *p = &(b->u.port_tcp);
394
395 str_appendf(buf,
396 "<tr>\n"
397 " <td class=\"num\">%u</td>\n"
398 " <td>%s</td>\n"
399 " <td class=\"num\">%'qu</td>\n"
400 " <td class=\"num\">%'qu</td>\n"
401 " <td class=\"num\">%'qu</td>\n"
402 " <td class=\"num\">%'qu</td>\n"
403 "</tr>\n",
404 p->port,
405 getservtcp(p->port),
406 (qu)b->in,
407 (qu)b->out,
408 (qu)b->total,
409 (qu)p->syn
410 );
411 }
412
413 static void
414 format_cols_port_udp(struct str *buf)
415 {
416 str_append(buf,
417 "<table>\n"
418 "<tr>\n"
419 " <th>Port</td>\n"
420 " <th>Service</td>\n"
421 " <th>In</td>\n"
422 " <th>Out</td>\n"
423 " <th>Total</td>\n"
424 "</tr>\n"
425 );
426 }
427
428 static void
429 format_row_port_udp(struct str *buf, const struct bucket *b)
430 {
431 const struct port_udp *p = &(b->u.port_udp);
432
433 str_appendf(buf,
434 "<tr>\n"
435 " <td class=\"num\">%u</td>\n"
436 " <td>%s</td>\n"
437 " <td class=\"num\">%'qu</td>\n"
438 " <td class=\"num\">%'qu</td>\n"
439 " <td class=\"num\">%'qu</td>\n"
440 "</tr>\n",
441 p->port,
442 getservudp(p->port),
443 (qu)b->in,
444 (qu)b->out,
445 (qu)b->total
446 );
447 }
448
449 static void
450 format_cols_ip_proto(struct str *buf)
451 {
452 str_append(buf,
453 "<table>\n"
454 "<tr>\n"
455 " <th>#</td>\n"
456 " <th>Protocol</td>\n"
457 " <th>In</td>\n"
458 " <th>Out</td>\n"
459 " <th>Total</td>\n"
460 "</tr>\n"
461 );
462 }
463
464 static void
465 format_row_ip_proto(struct str *buf, const struct bucket *b)
466 {
467 const struct ip_proto *p = &(b->u.ip_proto);
468
469 str_appendf(buf,
470 "<tr>\n"
471 " <td class=\"num\">%u</td>\n"
472 " <td>%s</td>\n"
473 " <td class=\"num\">%'qu</td>\n"
474 " <td class=\"num\">%'qu</td>\n"
475 " <td class=\"num\">%'qu</td>\n"
476 "</tr>\n",
477 p->proto,
478 getproto(p->proto),
479 (qu)b->in,
480 (qu)b->out,
481 (qu)b->total
482 );
483 }
484
485 /* ---------------------------------------------------------------------------
486 * Initialise a hashtable.
487 */
488 static struct hashtable *
489 hashtable_make(const uint8_t bits,
490 const unsigned int count_max,
491 const unsigned int count_keep,
492 hash_func_t *hash_func,
493 free_func_t *free_func,
494 key_func_t *key_func,
495 find_func_t *find_func,
496 make_func_t *make_func,
497 format_cols_func_t *format_cols_func,
498 format_row_func_t *format_row_func)
499 {
500 struct hashtable *hash;
501 assert(bits > 0);
502
503 hash = xmalloc(sizeof(*hash));
504 hash->bits = bits;
505 hash->count_max = count_max;
506 hash->count_keep = count_keep;
507 hash->size = 1U << bits;
508 hash->mask = hash->size - 1;
509 hash->coeff = coprime(hash->size);
510 hash->hash_func = hash_func;
511 hash->free_func = free_func;
512 hash->key_func = key_func;
513 hash->find_func = find_func;
514 hash->make_func = make_func;
515 hash->format_cols_func = format_cols_func;
516 hash->format_row_func = format_row_func;
517 hash->count = 0;
518 hash->table = xcalloc(hash->size, sizeof(*hash->table));
519 memset(&(hash->stats), 0, sizeof(hash->stats));
520 return (hash);
521 }
522
523 /* ---------------------------------------------------------------------------
524 * Initialise global hosts_db.
525 */
526 void
527 hosts_db_init(void)
528 {
529 assert(hosts_db == NULL);
530 hosts_db = hashtable_make(HOST_BITS, opt_hosts_max, opt_hosts_keep,
531 hash_func_host, free_func_host, key_func_host, find_func_host,
532 make_func_host, format_cols_host, format_row_host);
533 }
534
535 static void
536 hashtable_rehash(struct hashtable *h, const uint8_t bits)
537 {
538 struct bucket **old_table, **new_table;
539 uint32_t i, old_size;
540 assert(h != NULL);
541 assert(bits > 0);
542
543 h->stats.rehashes++;
544 old_size = h->size;
545 old_table = h->table;
546
547 h->bits = bits;
548 h->size = 1U << bits;
549 h->mask = h->size - 1;
550 h->coeff = coprime(h->size);
551 new_table = xcalloc(h->size, sizeof(*new_table));
552
553 for (i=0; i<old_size; i++) {
554 struct bucket *next, *b = old_table[i];
555 while (b != NULL) {
556 uint32_t pos = h->hash_func(h, h->key_func(b)) & h->mask;
557 next = b->next;
558 b->next = new_table[pos];
559 new_table[pos] = b;
560 b = next;
561 }
562 }
563
564 free(h->table);
565 h->table = new_table;
566 }
567
568 static void
569 hashtable_insert(struct hashtable *h, struct bucket *b)
570 {
571 uint32_t pos;
572 assert(h != NULL);
573 assert(b != NULL);
574 assert(b->next == NULL);
575
576 /* Rehash on 80% occupancy */
577 if ((h->count > h->size) ||
578 ((h->size - h->count) < h->size / 5))
579 hashtable_rehash(h, h->bits+1);
580
581 pos = h->hash_func(h, h->key_func(b)) & h->mask;
582 if (h->table[pos] == NULL)
583 h->table[pos] = b;
584 else {
585 /* Insert at top of chain. */
586 b->next = h->table[pos];
587 h->table[pos] = b;
588 }
589 h->count++;
590 h->stats.inserts++;
591 }
592
593 /* Return bucket matching key, or NULL if no such entry. */
594 static struct bucket *
595 hashtable_search(struct hashtable *h, const void *key)
596 {
597 uint32_t pos;
598 struct bucket *b;
599
600 h->stats.searches++;
601 pos = h->hash_func(h, key) & h->mask;
602 b = h->table[pos];
603 while (b != NULL) {
604 if (h->find_func(b, key))
605 return (b);
606 else
607 b = b->next;
608 }
609 return (NULL);
610 }
611
612 typedef enum { NO_REDUCE = 0, ALLOW_REDUCE = 1 } reduce_bool;
613 /* Search for a key. If it's not there, make and insert a bucket for it. */
614 static struct bucket *
615 hashtable_find_or_insert(struct hashtable *h, const void *key,
616 const reduce_bool allow_reduce)
617 {
618 struct bucket *b = hashtable_search(h, key);
619
620 if (b == NULL) {
621 /* Not found, so insert after checking occupancy. */
622 if (allow_reduce && (h->count >= h->count_max))
623 hashtable_reduce(h);
624 b = h->make_func(key);
625 hashtable_insert(h, b);
626 }
627 return (b);
628 }
629
630 /*
631 * Frees the hashtable and the buckets. The contents are assumed to be
632 * "simple" -- i.e. no "destructor" action is required beyond simply freeing
633 * the bucket.
634 */
635 static void
636 hashtable_free(struct hashtable *h)
637 {
638 uint32_t i;
639
640 if (h == NULL)
641 return;
642 for (i=0; i<h->size; i++) {
643 struct bucket *tmp, *b = h->table[i];
644 while (b != NULL) {
645 tmp = b;
646 b = b->next;
647 h->free_func(tmp);
648 free(tmp);
649 }
650 }
651 free(h->table);
652 free(h);
653 }
654
655 /* ---------------------------------------------------------------------------
656 * Return existing host or insert a new one.
657 */
658 struct bucket *
659 host_get(const struct addr *const a)
660 {
661 return (hashtable_find_or_insert(hosts_db, a, NO_REDUCE));
662 }
663
664 /* ---------------------------------------------------------------------------
665 * Find host, returns NULL if not in DB.
666 */
667 struct bucket *
668 host_find(const struct addr *const a)
669 {
670 return (hashtable_search(hosts_db, a));
671 }
672
673 /* ---------------------------------------------------------------------------
674 * Find host, returns NULL if not in DB.
675 */
676 static struct bucket *
677 host_search(const char *ipstr)
678 {
679 struct addr a;
680 struct addrinfo hints, *ai;
681
682 memset(&hints, 0, sizeof(hints));
683 hints.ai_family = AF_UNSPEC;
684 hints.ai_flags = AI_NUMERICHOST;
685
686 if (getaddrinfo(ipstr, NULL, &hints, &ai))
687 return (NULL); /* invalid addr */
688
689 if (ai->ai_family == AF_INET) {
690 a.family = IPv4;
691 a.ip.v4 = ((const struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr;
692 }
693 else if (ai->ai_family == AF_INET6) {
694 a.family = IPv6;
695 memcpy(&(a.ip.v6),
696 ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr,
697 sizeof(a.ip.v6));
698 } else {
699 freeaddrinfo(ai);
700 return (NULL); /* unknown family */
701 }
702 freeaddrinfo(ai);
703
704 verbosef("search(%s) turned into %s", ipstr, addr_to_str(&a));
705 return (hashtable_search(hosts_db, &a));
706 }
707
708 /* ---------------------------------------------------------------------------
709 * Reduce a hashtable to the top <keep> entries.
710 */
711 static void
712 hashtable_reduce(struct hashtable *ht)
713 {
714 uint32_t i, pos, rmd;
715 const struct bucket **table;
716 uint64_t cutoff;
717
718 assert(ht->count_keep < ht->count);
719
720 /* Fill table with pointers to buckets in hashtable. */
721 table = xcalloc(ht->count, sizeof(*table));
722 for (pos=0, i=0; i<ht->size; i++) {
723 struct bucket *b = ht->table[i];
724 while (b != NULL) {
725 table[pos++] = b;
726 b = b->next;
727 }
728 }
729 assert(pos == ht->count);
730 qsort_buckets(table, ht->count, 0, ht->count_keep, TOTAL);
731 cutoff = table[ht->count_keep]->total;
732 free(table);
733
734 /* Remove all elements with total <= cutoff. */
735 rmd = 0;
736 for (i=0; i<ht->size; i++) {
737 struct bucket *last = NULL, *next, *b = ht->table[i];
738 while (b != NULL) {
739 next = b->next;
740 if (b->total <= cutoff) {
741 /* Remove this one. */
742 ht->free_func(b);
743 free(b);
744 if (last == NULL)
745 ht->table[i] = next;
746 else
747 last->next = next;
748 rmd++;
749 ht->count--;
750 } else {
751 last = b;
752 }
753 b = next;
754 }
755 }
756 verbosef("hashtable_reduce: removed %u buckets, left %u",
757 rmd, ht->count);
758 hashtable_rehash(ht, ht->bits); /* is this needed? */
759 }
760
761 /* Reduce hosts_db if needed. */
762 void hosts_db_reduce(void)
763 {
764 if (hosts_db->count >= hosts_db->count_max)
765 hashtable_reduce(hosts_db);
766 }
767
768 /* ---------------------------------------------------------------------------
769 * Reset hosts_db to empty.
770 */
771 void
772 hosts_db_reset(void)
773 {
774 unsigned int i;
775
776 for (i=0; i<hosts_db->size; i++) {
777 struct bucket *next, *b = hosts_db->table[i];
778 while (b != NULL) {
779 next = b->next;
780 hosts_db->free_func(b);
781 free(b);
782 b = next;
783 }
784 hosts_db->table[i] = NULL;
785 }
786 verbosef("hosts_db reset to empty, freed %u hosts", hosts_db->count);
787 hosts_db->count = 0;
788 }
789
790 /* ---------------------------------------------------------------------------
791 * Deallocate hosts_db.
792 */
793 void hosts_db_free(void)
794 {
795 uint32_t i;
796
797 assert(hosts_db != NULL);
798 for (i=0; i<hosts_db->size; i++) {
799 struct bucket *tmp, *b = hosts_db->table[i];
800 while (b != NULL) {
801 tmp = b;
802 b = b->next;
803 hosts_db->free_func(tmp);
804 free(tmp);
805 }
806 }
807 free(hosts_db->table);
808 free(hosts_db);
809 hosts_db = NULL;
810 }
811
812 /* ---------------------------------------------------------------------------
813 * Find or create a port_tcp inside a host.
814 */
815 struct bucket *
816 host_get_port_tcp(struct bucket *host, const uint16_t port)
817 {
818 struct host *h = &host->u.host;
819 if (h->ports_tcp == NULL)
820 h->ports_tcp = hashtable_make(PORT_BITS, opt_ports_max, opt_ports_keep,
821 hash_func_short, free_func_simple, key_func_port_tcp,
822 find_func_port_tcp, make_func_port_tcp,
823 format_cols_port_tcp, format_row_port_tcp);
824 return (hashtable_find_or_insert(h->ports_tcp, &port, ALLOW_REDUCE));
825 }
826
827 struct bucket *
828 host_get_port_tcp_remote(struct bucket *host, const uint16_t port)
829 {
830 struct host *h = &host->u.host;
831 if (h->ports_tcp_remote == NULL)
832 h->ports_tcp_remote = hashtable_make(
833 PORT_BITS, opt_ports_max, opt_ports_keep, hash_func_short,
834 free_func_simple, key_func_port_tcp, find_func_port_tcp,
835 make_func_port_tcp, format_cols_port_tcp, format_row_port_tcp);
836 return (hashtable_find_or_insert(h->ports_tcp_remote, &port, ALLOW_REDUCE));
837 }
838
839 /* ---------------------------------------------------------------------------
840 * Find or create a port_udp inside a host.
841 */
842 struct bucket *
843 host_get_port_udp(struct bucket *host, const uint16_t port)
844 {
845 struct host *h = &host->u.host;
846 if (h->ports_udp == NULL)
847 h->ports_udp = hashtable_make(PORT_BITS, opt_ports_max, opt_ports_keep,
848 hash_func_short, free_func_simple, key_func_port_udp,
849 find_func_port_udp, make_func_port_udp,
850 format_cols_port_udp, format_row_port_udp);
851 return (hashtable_find_or_insert(h->ports_udp, &port, ALLOW_REDUCE));
852 }
853
854 struct bucket *
855 host_get_port_udp_remote(struct bucket *host, const uint16_t port)
856 {
857 struct host *h = &host->u.host;
858 if (h->ports_udp_remote == NULL)
859 h->ports_udp_remote = hashtable_make(
860 PORT_BITS, opt_ports_max, opt_ports_keep, hash_func_short,
861 free_func_simple, key_func_port_udp, find_func_port_udp,
862 make_func_port_udp, format_cols_port_udp, format_row_port_udp);
863 return (hashtable_find_or_insert(h->ports_udp_remote, &port, ALLOW_REDUCE));
864 }
865
866 /* ---------------------------------------------------------------------------
867 * Find or create an ip_proto inside a host.
868 */
869 struct bucket *
870 host_get_ip_proto(struct bucket *host, const uint8_t proto)
871 {
872 struct host *h = &host->u.host;
873 static const unsigned int PROTOS_MAX = 512, PROTOS_KEEP = 256;
874 assert(h != NULL);
875 if (h->ip_protos == NULL)
876 h->ip_protos = hashtable_make(PROTO_BITS, PROTOS_MAX, PROTOS_KEEP,
877 hash_func_byte, free_func_simple, key_func_ip_proto,
878 find_func_ip_proto, make_func_ip_proto,
879 format_cols_ip_proto, format_row_ip_proto);
880 return (hashtable_find_or_insert(h->ip_protos, &proto, ALLOW_REDUCE));
881 }
882
883 static struct str *html_hosts_main(const char *qs);
884 static struct str *html_hosts_detail(const char *ip);
885
886 /* ---------------------------------------------------------------------------
887 * Web interface: delegate the /hosts/ space.
888 */
889 struct str *
890 html_hosts(const char *uri, const char *query)
891 {
892 unsigned int i, num_elems;
893 char **elem = split('/', uri, &num_elems);
894 struct str *buf = NULL;
895
896 assert(num_elems >= 1);
897 assert(strcmp(elem[0], "hosts") == 0);
898
899 if (num_elems == 1)
900 /* /hosts/ */
901 buf = html_hosts_main(query);
902 else if (num_elems == 2)
903 /* /hosts/<IP of host>/ */
904 buf = html_hosts_detail(elem[1]);
905
906 for (i=0; i<num_elems; i++)
907 free(elem[i]);
908 free(elem);
909 return (buf); /* FIXME: a NULL here becomes 404 Not Found, we might want
910 other codes to be possible */
911 }
912
913 /* ---------------------------------------------------------------------------
914 * Format hashtable into HTML.
915 */
916 static void
917 format_table(struct str *buf, struct hashtable *ht, unsigned int start,
918 const enum sort_dir sort, const int full)
919 {
920 const struct bucket **table;
921 unsigned int i, pos, end;
922 int alt = 0;
923
924 if ((ht == NULL) || (ht->count == 0)) {
925 str_append(buf, "<p>The table is empty.</p>\n");
926 return;
927 }
928
929 /* Fill table with pointers to buckets in hashtable. */
930 table = xcalloc(ht->count, sizeof(*table));
931 for (pos=0, i=0; i<ht->size; i++) {
932 struct bucket *b = ht->table[i];
933 while (b != NULL) {
934 table[pos++] = b;
935 b = b->next;
936 }
937 }
938 assert(pos == ht->count);
939
940 if (full) {
941 /* full report overrides start and end */
942 start = 0;
943 end = ht->count;
944 } else
945 end = MIN(ht->count, (uint32_t)start+MAX_ENTRIES);
946
947 str_appendf(buf, "(%u-%u of %u)<br>\n", start+1, end, ht->count);
948 qsort_buckets(table, ht->count, start, end, sort);
949 ht->format_cols_func(buf);
950
951 for (i=start; i<end; i++) {
952 ht->format_row_func(buf, table[i]);
953 alt = !alt; /* alternate class for table rows */
954 }
955 free(table);
956 str_append(buf, "</table>\n");
957 }
958
959 /* ---------------------------------------------------------------------------
960 * Web interface: sorted table of hosts.
961 */
962 static struct str *
963 html_hosts_main(const char *qs)
964 {
965 struct str *buf = str_make();
966 char *qs_start, *qs_sort, *qs_full, *ep;
967 const char *sortstr;
968 int start, full = 0;
969 enum sort_dir sort;
970
971 /* parse query string */
972 qs_start = qs_get(qs, "start");
973 qs_sort = qs_get(qs, "sort");
974 qs_full = qs_get(qs, "full");
975 if (qs_full != NULL) {
976 full = 1;
977 free(qs_full);
978 }
979
980 /* validate sort */
981 if (qs_sort == NULL) sort = TOTAL;
982 else if (strcmp(qs_sort, "total") == 0) sort = TOTAL;
983 else if (strcmp(qs_sort, "in") == 0) sort = IN;
984 else if (strcmp(qs_sort, "out") == 0) sort = OUT;
985 else if (strcmp(qs_sort, "lastseen") == 0) sort = LASTSEEN;
986 else {
987 str_append(buf, "Error: invalid value for \"sort\".\n");
988 goto done;
989 }
990
991 /* parse start */
992 if (qs_start == NULL)
993 start = 0;
994 else {
995 start = (int)strtoul(qs_start, &ep, 10);
996 if (*ep != '\0') {
997 str_append(buf, "Error: \"start\" is not a number.\n");
998 goto done;
999 }
1000 if ((errno == ERANGE) ||
1001 (start < 0) || (start >= (int)hosts_db->count)) {
1002 str_append(buf, "Error: \"start\" is out of bounds.\n");
1003 goto done;
1004 }
1005 }
1006
1007 #define PREV "&lt;&lt;&lt; prev page"
1008 #define NEXT "next page &gt;&gt;&gt;"
1009 #define FULL "full table"
1010
1011 html_open(buf, "Hosts", /*path_depth=*/1, /*want_graph_js=*/0);
1012 format_table(buf, hosts_db, start, sort, full);
1013
1014 /* <prev | full | stats | next> */
1015 sortstr = qs_sort;
1016 if (sortstr == NULL) sortstr = "total";
1017 if (start > 0) {
1018 int prev = start - MAX_ENTRIES;
1019 if (prev < 0)
1020 prev = 0;
1021 str_appendf(buf, "<a href=\"?start=%d&sort=%s\">" PREV "</a>",
1022 prev, sortstr);
1023 } else
1024 str_append(buf, PREV);
1025
1026 if (full)
1027 str_append(buf, " | " FULL);
1028 else
1029 str_appendf(buf, " | <a href=\"?full=yes&sort=%s\">" FULL "</a>",
1030 sortstr);
1031
1032 if (start+MAX_ENTRIES < (int)hosts_db->count)
1033 str_appendf(buf, " | <a href=\"?start=%d&sort=%s\">" NEXT "</a>",
1034 start+MAX_ENTRIES, sortstr);
1035 else
1036 str_append(buf, " | " NEXT);
1037
1038 str_append(buf, "<br>\n");
1039
1040 html_close(buf);
1041 done:
1042 if (qs_start != NULL) free(qs_start);
1043 if (qs_sort != NULL) free(qs_sort);
1044 return buf;
1045 #undef PREV
1046 #undef NEXT
1047 #undef FULL
1048 }
1049
1050 /* ---------------------------------------------------------------------------
1051 * Web interface: detailed view of a single host.
1052 */
1053 static struct str *html_hosts_detail(const char *ip) {
1054 struct bucket *h;
1055 struct str *buf, *ls_len;
1056 char ls_when[100];
1057 const char *canonical;
1058 time_t last_seen_real;
1059
1060 h = host_search(ip);
1061 if (h == NULL)
1062 return (NULL); /* no such host */
1063
1064 canonical = addr_to_str(&(h->u.host.addr));
1065
1066 /* Overview. */
1067 buf = str_make();
1068 html_open(buf, ip, /*path_depth=*/2, /*want_graph_js=*/0);
1069 if (strcmp(ip, canonical) != 0)
1070 str_appendf(buf, "(canonically <b>%s</b>)\n", canonical);
1071 str_appendf(buf,
1072 "<p>\n"
1073 "<b>Hostname:</b> %s<br>\n",
1074 (h->u.host.dns == NULL)?"(resolving...)":h->u.host.dns);
1075
1076 /* Resolve host "on demand" */
1077 if (h->u.host.dns == NULL)
1078 dns_queue(&(h->u.host.addr));
1079
1080 if (hosts_db_show_macs)
1081 str_appendf(buf,
1082 "<b>MAC Address:</b> "
1083 "<tt>%x:%x:%x:%x:%x:%x</tt><br>\n",
1084 h->u.host.mac_addr[0],
1085 h->u.host.mac_addr[1],
1086 h->u.host.mac_addr[2],
1087 h->u.host.mac_addr[3],
1088 h->u.host.mac_addr[4],
1089 h->u.host.mac_addr[5]);
1090
1091 str_append(buf,
1092 "</p>\n"
1093 "<p>\n"
1094 "<b>Last seen:</b> ");
1095
1096 if (h->u.host.last_seen_mono == 0) {
1097 str_append(buf, "(never)");
1098 } else {
1099 last_seen_real = mono_to_real(h->u.host.last_seen_mono);
1100 if (strftime(ls_when, sizeof(ls_when),
1101 "%Y-%m-%d %H:%M:%S %Z%z", localtime(&last_seen_real)) != 0)
1102 str_append(buf, ls_when);
1103
1104 if (h->u.host.last_seen_mono <= now_mono()) {
1105 ls_len =
1106 length_of_time((int64_t)now_mono() - h->u.host.last_seen_mono);
1107 str_append(buf, " (");
1108 str_appendstr(buf, ls_len);
1109 str_free(ls_len);
1110 str_append(buf, " ago)");
1111 } else {
1112 str_appendf(buf, " (in the future, possible clock problem, "
1113 "last = %qd, now = %qu)",
1114 (qd)h->u.host.last_seen_mono,
1115 (qu)now_mono());
1116 }
1117 }
1118
1119 str_appendf(buf,
1120 "</p>\n"
1121 "<p>\n"
1122 " <b>In:</b> %'qu<br>\n"
1123 " <b>Out:</b> %'qu<br>\n"
1124 " <b>Total:</b> %'qu<br>\n"
1125 "</p>\n",
1126 (qu)h->in,
1127 (qu)h->out,
1128 (qu)h->total);
1129
1130 str_append(buf, "<h3>TCP ports on this host</h3>\n");
1131 format_table(buf, h->u.host.ports_tcp, 0,TOTAL,0);
1132
1133 str_append(buf, "<h3>TCP ports on remote hosts</h3>\n");
1134 format_table(buf, h->u.host.ports_tcp_remote, 0,TOTAL,0);
1135
1136 str_append(buf, "<h3>UDP ports on this host</h3>\n");
1137 format_table(buf, h->u.host.ports_udp, 0,TOTAL,0);
1138
1139 str_append(buf, "<h3>UDP ports on remote hosts</h3>\n");
1140 format_table(buf, h->u.host.ports_udp_remote, 0,TOTAL,0);
1141
1142 str_append(buf, "<h3>IP protocols</h3>\n");
1143 format_table(buf, h->u.host.ip_protos, 0,TOTAL,0);
1144
1145 str_append(buf, "<br>\n");
1146 html_close(buf);
1147 return buf;
1148 }
1149
1150 /* ---------------------------------------------------------------------------
1151 * Database import and export code:
1152 * Initially written and contributed by Ben Stewart.
1153 * copyright (c) 2007-2014 Ben Stewart, Emil Mikulic.
1154 */
1155 static int hosts_db_export_ip(const struct hashtable *h, const int fd);
1156 static int hosts_db_export_tcp(const char magic, const struct hashtable *h,
1157 const int fd);
1158 static int hosts_db_export_udp(const char magic, const struct hashtable *h,
1159 const int fd);
1160
1161 static const char
1162 export_proto_ip = 'P',
1163 export_proto_tcp = 'T',
1164 export_proto_tcp_remote = 't',
1165 export_proto_udp = 'U',
1166 export_proto_udp_remote = 'u';
1167
1168 static const unsigned char
1169 export_tag_host_ver1[] = {'H', 'S', 'T', 0x01},
1170 export_tag_host_ver2[] = {'H', 'S', 'T', 0x02},
1171 export_tag_host_ver3[] = {'H', 'S', 'T', 0x03},
1172 export_tag_host_ver4[] = {'H', 'S', 'T', 0x04};
1173
1174 /* ---------------------------------------------------------------------------
1175 * Load a host's ip_proto table from a file.
1176 * Returns 0 on failure, 1 on success.
1177 */
1178 static int
1179 hosts_db_import_ip(const int fd, struct bucket *host)
1180 {
1181 uint8_t count, i;
1182
1183 if (!expect8(fd, export_proto_ip)) return 0;
1184 if (!read8(fd, &count)) return 0;
1185
1186 for (i=0; i<count; i++) {
1187 struct bucket *b;
1188 uint8_t proto;
1189 uint64_t in, out;
1190
1191 if (!read8(fd, &proto)) return 0;
1192 if (!read64(fd, &in)) return 0;
1193 if (!read64(fd, &out)) return 0;
1194
1195 /* Store data */
1196 b = host_get_ip_proto(host, proto);
1197 b->in = in;
1198 b->out = out;
1199 b->total = in + out;
1200 assert(b->u.ip_proto.proto == proto); /* should be done by make fn */
1201 }
1202 return 1;
1203 }
1204
1205 /* ---------------------------------------------------------------------------
1206 * Load a host's port_tcp{,_remote} table from a file.
1207 * Returns 0 on failure, 1 on success.
1208 */
1209 static int hosts_db_import_tcp(const int fd, const char magic,
1210 struct bucket *host,
1211 struct bucket *(get_port_fn)(struct bucket *host,
1212 uint16_t port)) {
1213 uint16_t count, i;
1214
1215 if (!expect8(fd, magic)) return 0;
1216 if (!read16(fd, &count)) return 0;
1217
1218 for (i=0; i<count; i++) {
1219 struct bucket *b;
1220 uint16_t port;
1221 uint64_t in, out, syn;
1222
1223 if (!read16(fd, &port)) return 0;
1224 if (!read64(fd, &syn)) return 0;
1225 if (!read64(fd, &in)) return 0;
1226 if (!read64(fd, &out)) return 0;
1227
1228 /* Store data */
1229 b = get_port_fn(host, port);
1230 b->in = in;
1231 b->out = out;
1232 b->total = in + out;
1233 assert(b->u.port_tcp.port == port); /* done by make_func_port_tcp */
1234 b->u.port_tcp.syn = syn;
1235 }
1236 return 1;
1237 }
1238
1239 /* ---------------------------------------------------------------------------
1240 * Load a host's port_tcp table from a file.
1241 * Returns 0 on failure, 1 on success.
1242 */
1243 static int hosts_db_import_udp(const int fd, const char magic,
1244 struct bucket *host,
1245 struct bucket *(get_port_fn)(struct bucket *host,
1246 uint16_t port)) {
1247 uint16_t count, i;
1248
1249 if (!expect8(fd, magic)) return 0;
1250 if (!read16(fd, &count)) return 0;
1251
1252 for (i=0; i<count; i++) {
1253 struct bucket *b;
1254 uint16_t port;
1255 uint64_t in, out;
1256
1257 if (!read16(fd, &port)) return 0;
1258 if (!read64(fd, &in)) return 0;
1259 if (!read64(fd, &out)) return 0;
1260
1261 /* Store data */
1262 b = get_port_fn(host, port);
1263 b->in = in;
1264 b->out = out;
1265 b->total = in + out;
1266 assert(b->u.port_udp.port == port); /* done by make_func */
1267 }
1268 return 1;
1269 }
1270
1271 /* ---------------------------------------------------------------------------
1272 * Load all hosts from a file.
1273 * Returns 0 on failure, 1 on success.
1274 */
1275 static int
1276 hosts_db_import_host(const int fd)
1277 {
1278 struct bucket *host;
1279 struct addr a;
1280 uint8_t hostname_len;
1281 uint64_t in, out;
1282 unsigned int pos = xtell(fd);
1283 char hdr[4];
1284 int ver = 0;
1285
1286 if (!readn(fd, hdr, sizeof(hdr))) return 0;
1287 if (memcmp(hdr, export_tag_host_ver4, sizeof(hdr)) == 0)
1288 ver = 4;
1289 else if (memcmp(hdr, export_tag_host_ver3, sizeof(hdr)) == 0)
1290 ver = 3;
1291 else if (memcmp(hdr, export_tag_host_ver2, sizeof(hdr)) == 0)
1292 ver = 2;
1293 else if (memcmp(hdr, export_tag_host_ver1, sizeof(hdr)) == 0)
1294 ver = 1;
1295 else {
1296 warnx("bad host header: %02x%02x%02x%02x",
1297 hdr[0], hdr[1], hdr[2], hdr[3]);
1298 return 0;
1299 }
1300
1301 if (ver >= 3) {
1302 if (!readaddr(fd, &a))
1303 return 0;
1304 } else {
1305 assert((ver == 1) || (ver == 2));
1306 if (!readaddr_ipv4(fd, &a))
1307 return 0;
1308 }
1309 verbosef("at file pos %u, importing host %s", pos, addr_to_str(&a));
1310 host = host_get(&a);
1311 assert(addr_equal(&(host->u.host.addr), &a));
1312
1313 if (ver > 1) {
1314 uint64_t t;
1315 if (!read64(fd, &t)) return 0;
1316 host->u.host.last_seen_mono = real_to_mono(t);
1317 }
1318
1319 assert(sizeof(host->u.host.mac_addr) == 6);
1320 if (!readn(fd, host->u.host.mac_addr, sizeof(host->u.host.mac_addr)))
1321 return 0;
1322
1323 /* HOSTNAME */
1324 assert(host->u.host.dns == NULL); /* make fn? */
1325 if (!read8(fd, &hostname_len)) return 0;
1326 if (hostname_len > 0) {
1327 host->u.host.dns = xmalloc(hostname_len + 1);
1328 host->u.host.dns[0] = '\0';
1329
1330 /* At this point, the hostname is attached to a host which is in our
1331 * hosts_db, so if we bail out due to an import error, this pointer
1332 * isn't lost and leaked, it can be cleaned up in hosts_db_{free,reset}
1333 */
1334
1335 if (!readn(fd, host->u.host.dns, hostname_len)) return 0;
1336 host->u.host.dns[hostname_len] = '\0';
1337 }
1338
1339 if (!read64(fd, &in)) return 0;
1340 if (!read64(fd, &out)) return 0;
1341
1342 host->in = in;
1343 host->out = out;
1344 host->total = in + out;
1345
1346 /* Host's port and proto subtables: */
1347 if (!hosts_db_import_ip(fd, host)) return 0;
1348 if (!hosts_db_import_tcp(fd, export_proto_tcp, host, host_get_port_tcp))
1349 return 0;
1350 if (!hosts_db_import_udp(fd, export_proto_udp, host, host_get_port_udp))
1351 return 0;
1352
1353 if (ver == 4) {
1354 if (!hosts_db_import_tcp(fd, export_proto_tcp_remote, host,
1355 host_get_port_tcp_remote))
1356 return 0;
1357 if (!hosts_db_import_udp(fd, export_proto_udp_remote, host,
1358 host_get_port_udp_remote))
1359 return 0;
1360 }
1361 return 1;
1362 }
1363
1364 /* ---------------------------------------------------------------------------
1365 * Database Import: Grab hosts_db from a file provided by the caller.
1366 *
1367 * This function will retrieve the data sans the header. We expect the caller
1368 * to have validated the header of the hosts_db segment, and left the file
1369 * sitting at the start of the data.
1370 */
1371 int hosts_db_import(const int fd)
1372 {
1373 uint32_t host_count, i;
1374
1375 if (!read32(fd, &host_count)) return 0;
1376
1377 for (i=0; i<host_count; i++)
1378 if (!hosts_db_import_host(fd)) return 0;
1379
1380 return 1;
1381 }
1382
1383 /* ---------------------------------------------------------------------------
1384 * Database Export: Dump hosts_db into a file provided by the caller.
1385 * The caller is responsible for writing out export_tag_hosts_ver1 first.
1386 */
1387 int hosts_db_export(const int fd)
1388 {
1389 uint32_t i;
1390 struct bucket *b;
1391
1392 if (!write32(fd, hosts_db->count)) return 0;
1393
1394 for (i = 0; i<hosts_db->size; i++)
1395 for (b = hosts_db->table[i]; b != NULL; b = b->next) {
1396 /* For each host: */
1397 if (!writen(fd, export_tag_host_ver4, sizeof(export_tag_host_ver4)))
1398 return 0;
1399
1400 if (!writeaddr(fd, &(b->u.host.addr)))
1401 return 0;
1402
1403 if (!write64(fd, (uint64_t)mono_to_real(b->u.host.last_seen_mono)))
1404 return 0;
1405
1406 assert(sizeof(b->u.host.mac_addr) == 6);
1407 if (!writen(fd, b->u.host.mac_addr, sizeof(b->u.host.mac_addr)))
1408 return 0;
1409
1410 /* HOSTNAME */
1411 if (b->u.host.dns == NULL) {
1412 if (!write8(fd, 0)) return 0;
1413 } else {
1414 int dnslen = strlen(b->u.host.dns);
1415
1416 if (dnslen > 255) {
1417 warnx("found a very long hostname: \"%s\"\n"
1418 "wasn't expecting one longer than 255 chars (this one is %d)",
1419 b->u.host.dns, dnslen);
1420 dnslen = 255;
1421 }
1422
1423 if (!write8(fd, (uint8_t)dnslen)) return 0;
1424 if (!writen(fd, b->u.host.dns, dnslen)) return 0;
1425 }
1426
1427 if (!write64(fd, b->in)) return 0;
1428 if (!write64(fd, b->out)) return 0;
1429
1430 if (!hosts_db_export_ip(b->u.host.ip_protos, fd)) return 0;
1431 if (!hosts_db_export_tcp(export_proto_tcp, b->u.host.ports_tcp, fd))
1432 return 0;
1433 if (!hosts_db_export_udp(export_proto_udp, b->u.host.ports_udp, fd))
1434 return 0;
1435 if (!hosts_db_export_tcp(export_proto_tcp_remote,
1436 b->u.host.ports_tcp_remote, fd))
1437 return 0;
1438 if (!hosts_db_export_udp(export_proto_udp_remote,
1439 b->u.host.ports_udp_remote, fd))
1440 return 0;
1441 }
1442 return 1;
1443 }
1444
1445 /* ---------------------------------------------------------------------------
1446 * Dump the ip_proto table of a host.
1447 */
1448 static int
1449 hosts_db_export_ip(const struct hashtable *h, const int fd)
1450 {
1451 uint32_t i, written = 0;
1452 struct bucket *b;
1453
1454 /* IP DATA */
1455 if (!write8(fd, export_proto_ip)) return 0;
1456
1457 /* If no data, write a IP Proto count of 0 and we're done. */
1458 if (h == NULL) {
1459 if (!write8(fd, 0)) return 0;
1460 return 1;
1461 }
1462
1463 assert(h->count < 256);
1464 if (!write8(fd, (uint8_t)h->count)) return 0;
1465
1466 for (i = 0; i<h->size; i++)
1467 for (b = h->table[i]; b != NULL; b = b->next) {
1468 /* For each ip_proto bucket: */
1469
1470 if (!write8(fd, b->u.ip_proto.proto)) return 0;
1471 if (!write64(fd, b->in)) return 0;
1472 if (!write64(fd, b->out)) return 0;
1473 written++;
1474 }
1475 assert(written == h->count);
1476 return 1;
1477 }
1478
1479 /* ---------------------------------------------------------------------------
1480 * Dump the port_tcp table of a host.
1481 */
1482 static int
1483 hosts_db_export_tcp(const char magic, const struct hashtable *h, const int fd)
1484 {
1485 struct bucket *b;
1486 uint32_t i, written = 0;
1487
1488 /* TCP DATA */
1489 if (!write8(fd, magic)) return 0;
1490
1491 /* If no data, write a count of 0 and we're done. */
1492 if (h == NULL) {
1493 if (!write16(fd, 0)) return 0;
1494 return 1;
1495 }
1496
1497 assert(h->count < 65536);
1498 if (!write16(fd, (uint16_t)h->count)) return 0;
1499
1500 for (i = 0; i<h->size; i++)
1501 for (b = h->table[i]; b != NULL; b = b->next) {
1502 if (!write16(fd, b->u.port_tcp.port)) return 0;
1503 if (!write64(fd, b->u.port_tcp.syn)) return 0;
1504 if (!write64(fd, b->in)) return 0;
1505 if (!write64(fd, b->out)) return 0;
1506 written++;
1507 }
1508 assert(written == h->count);
1509 return 1;
1510 }
1511
1512 /* ---------------------------------------------------------------------------
1513 * Dump the port_udp table of a host.
1514 */
1515 static int
1516 hosts_db_export_udp(const char magic, const struct hashtable *h, const int fd)
1517 {
1518 struct bucket *b;
1519 uint32_t i, written = 0;
1520
1521 /* UDP DATA */
1522 if (!write8(fd, magic)) return 0;
1523
1524 /* If no data, write a count of 0 and we're done. */
1525 if (h == NULL) {
1526 if (!write16(fd, 0)) return 0;
1527 return 1;
1528 }
1529
1530 assert(h->count < 65536);
1531 if (!write16(fd, (uint16_t)h->count)) return 0;
1532
1533 for (i = 0; i<h->size; i++)
1534 for (b = h->table[i]; b != NULL; b = b->next) {
1535 if (!write16(fd, b->u.port_udp.port)) return 0;
1536 if (!write64(fd, b->in)) return 0;
1537 if (!write64(fd, b->out)) return 0;
1538 written++;
1539 }
1540 assert(written == h->count);
1541 return 1;
1542 }
1543
1544 /* vim:set ts=3 sw=3 tw=80 expandtab: */