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