diff options
Diffstat (limited to 'src/dump_cds.c')
-rw-r--r-- | src/dump_cds.c | 1962 |
1 files changed, 1962 insertions, 0 deletions
diff --git a/src/dump_cds.c b/src/dump_cds.c new file mode 100644 index 0000000..e151d35 --- /dev/null +++ b/src/dump_cds.c @@ -0,0 +1,1962 @@ +/* + * Copyright (c) 2016-2021, OARC, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "dump_cds.h" +#include "dnscap.h" +#include "hashtbl.h" +#include "iaddr.h" + +#if HAVE_LIBTINYCBOR + +#include <stdlib.h> +#if HAVE_CBOR_CBOR_H +#include <cbor/cbor.h> +#endif +#if HAVE_CBOR_H +#include <cbor.h> +#endif +#include <assert.h> + +#define need8(v, p, l, d) \ + if (l < 1) { \ + if (sizeof(d) > 1) \ + fprintf(stderr, "cds need 1B/8b, had %lu: %s\n", l, d); \ + return 1; \ + } \ + v = *p; \ + p += 1; \ + l -= 1 + +#define need16(v, p, l, d) \ + if (l < 2) { \ + if (sizeof(d) > 1) \ + fprintf(stderr, "cds need 2B/16b, had %lu: %s\n", l, d); \ + return 1; \ + } \ + v = (*p << 8) + *(p + 1); \ + p += 2; \ + l -= 2 + +#define need32(v, p, l, d) \ + if (l < 4) { \ + if (sizeof(d) > 1) \ + fprintf(stderr, "cds need 4B/32b, had %lu: %s\n", l, d); \ + return 1; \ + } \ + v = (*p << 24) + (*(p + 1) << 16) + (*(p + 2) << 8) + *(p + 3); \ + p += 4; \ + l -= 4 + +#define need64(v, p, l, d) \ + if (l < 8) { \ + if (sizeof(d) > 1) \ + fprintf(stderr, "cds need 8B/64b, had %lu: %s\n", l, d); \ + return 1; \ + } \ + v = (*p << 56) + (*(p + 1) << 48) + (*(p + 2) << 40) + (*(p + 3) << 32) + (*(p + 4) << 24) + (*(p + 5) << 16) + (*(p + 6) << 8) + *(p + 7); \ + p += 8; \ + l -= 8 + +#define needxb(b, x, p, l, d) \ + if (l < x) { \ + if (sizeof(d) > 1) \ + fprintf(stderr, "cds need %d bytes, had %lu: %s\n", x, l, d); \ + return 1; \ + } \ + memcpy(b, p, x); \ + p += x; \ + l -= x + +#define advancexb(x, p, l, d) \ + if (l < x) { \ + if (sizeof(d) > 1) \ + fprintf(stderr, "cds needed to advance %d bytes, had %lu: %s\n", x, l, d); \ + return 1; \ + } \ + p += x; \ + l -= x + +static uint8_t* cbor_buf = 0; +static uint8_t* cbor_buf_p = 0; +static size_t cbor_size = 1024 * 1024; +static uint8_t* message_buf = 0; +static size_t message_size = 64 * 1024; +static int cbor_flushed = 1; +static hashtbl* rdata_tbl = 0; +static size_t MAX_RLABELS = CDS_DEFAULT_MAX_RLABELS; +static size_t MIN_RLABEL_SIZE = CDS_DEFAULT_MIN_RLABEL_SIZE; +static int use_rdata_index = 0; +static int use_rdata_rindex = 0; +static size_t RDATA_RINDEX_SIZE = CDS_DEFAULT_RDATA_RINDEX_SIZE; +static size_t RDATA_RINDEX_MIN_SIZE = CDS_DEFAULT_RDATA_RINDEX_MIN_SIZE; +static size_t RDATA_INDEX_MIN_SIZE = CDS_DEFAULT_RDATA_INDEX_MIN_SIZE; + +struct rdata; +struct rdata { + struct rdata* prev; + struct rdata* next; + uint8_t* data; + size_t len; + size_t idx; +}; + +struct last { + my_bpftimeval ts; + ip_header_t ip; + + uint16_t dns_type; + uint16_t dns_class; + uint32_t dns_ttl; + + dns_rlabel_t* dns_rlabel; + dns_rlabel_t* dns_rlabel_last; + size_t dns_rlabels; + + size_t rdata_index; + size_t rdata_num; + struct rdata* rdata; + struct rdata* rdata_last; +}; +static struct last last; + +/* + * Set/Get + */ + +int cds_set_cbor_size(size_t size) +{ + if (!size) { + return DUMP_CDS_EINVAL; + } + + cbor_size = size; + if (message_size > cbor_size) { + message_size = cbor_size; + } + + return DUMP_CDS_OK; +} + +int cds_set_message_size(size_t size) +{ + if (!size) { + return DUMP_CDS_EINVAL; + } + + message_size = size; + if (message_size > cbor_size) { + message_size = cbor_size; + } + + return DUMP_CDS_OK; +} + +int cds_set_max_rlabels(size_t size) +{ + if (!size) { + return DUMP_CDS_EINVAL; + } + + MAX_RLABELS = size; + + return DUMP_CDS_OK; +} + +int cds_set_min_rlabel_size(size_t size) +{ + if (!size) { + return DUMP_CDS_EINVAL; + } + + MIN_RLABEL_SIZE = size; + + return DUMP_CDS_OK; +} + +int cds_set_use_rdata_index(int use) +{ + use_rdata_index = use ? 1 : 0; + + return DUMP_CDS_OK; +} + +int cds_set_use_rdata_rindex(int use) +{ + use_rdata_rindex = use ? 1 : 0; + + return DUMP_CDS_OK; +} + +int cds_set_rdata_index_min_size(size_t size) +{ + if (!size) { + return DUMP_CDS_EINVAL; + } + + RDATA_INDEX_MIN_SIZE = size; + + return DUMP_CDS_OK; +} + +int cds_set_rdata_rindex_min_size(size_t size) +{ + if (!size) { + return DUMP_CDS_EINVAL; + } + + RDATA_RINDEX_MIN_SIZE = size; + + return DUMP_CDS_OK; +} + +int cds_set_rdata_rindex_size(size_t size) +{ + if (!size) { + return DUMP_CDS_EINVAL; + } + + RDATA_RINDEX_SIZE = size; + + return DUMP_CDS_OK; +} + +/* + * DNS + */ + +static int check_dns_label(size_t* labels, uint8_t** p, size_t* l) +{ + uint8_t len; + + while (1) { + need8(len, *p, *l, ""); + *labels += 1; + + if ((len & 0xc0) == 0xc0) { + advancexb(1, *p, *l, ""); + break; + } else if (len & 0xc0) { + break; + } else if (len) { + advancexb(len, *p, *l, ""); + } else { + break; + } + } + + return 0; +} + +static unsigned int rdata_hash(const void* _item) +{ + const struct rdata* item = (const struct rdata*)_item; + size_t n, o, p; + unsigned int key = 0; + + for (n = 0, o = 0, p = 0; n < item->len; n++) { + p |= item->data[n] << (o * 8); + o++; + if (o > 3) { + key ^= p; + p = 0; + o = 0; + } + } + if (o) { + key ^= p; + } + + return key; +} + +static int rdata_cmp(const void* _a, const void* _b) +{ + const struct rdata *a = (const struct rdata*)_a, *b = (const struct rdata*)_b; + + if (a->len == b->len) { + return memcmp(a->data, b->data, a->len); + } else if (a->len < b->len) + return -1; + return 1; +} + +static void rdata_free(void* d) +{ + struct rdata* item = (struct rdata*)d; + + if (item) { + if (item->data) { + free(item->data); + } + free(item); + } +} + +static int rdata_add(uint8_t* p, size_t len) +{ + struct rdata* key; + + if (len < RDATA_INDEX_MIN_SIZE) + return 1; + + if (!(key = calloc(1, sizeof(struct rdata)))) { + return 0; + } + if (!(key->data = calloc(1, len))) { + free(key); + return 0; + } + + key->len = len; + memcpy(key->data, p, len); + key->idx = last.rdata_index++; + + /* printf("rdata_add %u: ", rdata_hash(key));*/ + /* {*/ + /* size_t n = len;*/ + /* uint8_t* x = p;*/ + /* while (n--) {*/ + /* printf("%02x", *x);*/ + /* x++;*/ + /* }*/ + /* }*/ + /* printf("\n");*/ + hash_add(key, key, rdata_tbl); + + return 0; +} + +static size_t rdata_find(uint8_t* p, size_t len, size_t* found) +{ + struct rdata key; + struct rdata* r; + + if (len < RDATA_INDEX_MIN_SIZE) + return 1; + + key.data = p; + key.len = len; + + /* printf("rdata_find %u: ", rdata_hash(&key));*/ + /* {*/ + /* size_t n = len;*/ + /* uint8_t* x = p;*/ + /* while (n--) {*/ + /* printf("%02x", *x);*/ + /* x++;*/ + /* }*/ + /* }*/ + /* printf("\n");*/ + + if ((r = hash_find(&key, rdata_tbl))) { + /* printf("rdata found %lu at %lu\n", len, found->idx);*/ + *found = r->idx; + return 0; + } + + return 1; +} + +int rdata_find2(uint8_t* p, size_t len, size_t* found) +{ + struct rdata* r = last.rdata; + size_t n = 0; + + if (len < RDATA_RINDEX_MIN_SIZE) + return 1; + + while (r) { + if (r->len == len && !memcmp(p, r->data, len)) { + break; + } + r = r->next; + n++; + } + if (r) { + /* printf("rdata found at %lu: ", n);*/ + /* {*/ + /* size_t n = len;*/ + /* uint8_t* x = p;*/ + /* while (n--) {*/ + /* printf("%02x", *x);*/ + /* x++;*/ + /* }*/ + /* }*/ + /* printf("\n");*/ + + if (last.rdata != r) { + struct rdata *prev = r->prev, *next = r->next; + + if (prev) { + prev->next = next; + } + if (next) { + next->prev = prev; + } + + r->prev = 0; + r->next = last.rdata; + last.rdata->prev = r; + last.rdata = r; + } + + *found = n; + return 0; + } + + return 1; +} + +int rdata_add2(uint8_t* p, size_t len) +{ + struct rdata* r; + + if (len < RDATA_RINDEX_MIN_SIZE) + return 1; + + if (!(r = calloc(1, sizeof(struct rdata)))) { + return -1; + } + if (!(r->data = calloc(1, len))) { + free(r); + return -1; + } + + r->len = len; + memcpy(r->data, p, len); + + /* printf("rdata_add: ");*/ + /* {*/ + /* size_t n = len;*/ + /* uint8_t* x = p;*/ + /* while (n--) {*/ + /* printf("%02x", *x);*/ + /* x++;*/ + /* }*/ + /* }*/ + /* printf("\n");*/ + + if (last.rdata) { + last.rdata->prev = r; + } + r->next = last.rdata; + last.rdata = r; + last.rdata_num++; + + if (last.rdata_last) { + if (last.rdata_num >= RDATA_RINDEX_SIZE) { + r = last.rdata_last; + + last.rdata_last = r->prev; + last.rdata_last->next = 0; + last.rdata_num--; + free(r->data); + free(r); + } + } else { + last.rdata_last = r; + } + + return 0; +} + +static int parse_dns_rr(char is_q, dns_rr_t* rr, size_t expected_rrs, size_t* actual_rrs, uint8_t** p, size_t* l) +{ + uint8_t len; + uint8_t* p2; + size_t l2, idx; + dns_label_t* label; + size_t num_labels, offset; + + while (expected_rrs--) { + /* first pass check number of labels */ + p2 = *p; + l2 = *l; + + if (check_dns_label(&(rr->labels), &p2, &l2)) { + if (!rr->labels) { + fprintf(stderr, "cds no labels\n"); + return 1; + } + } + + /* second pass, allocate labels and fill */ + if (!(rr->label = calloc(rr->labels, sizeof(dns_label_t)))) { + fprintf(stderr, "cds out of memory\n"); + return -1; + } + + *actual_rrs += 1; + + label = rr->label; + rr->have_labels = 1; + + while (1) { + need8(len, *p, *l, "name length"); + + if ((len & 0xc0) == 0xc0) { + label->offset_p = *p; + need8(label->offset, *p, *l, "name offset"); + label->offset |= (len & 0x3f) << 8; + label->have_offset = 1; + label->is_complete = 1; + break; + } else if (len & 0xc0) { + label->extension_bits = len; + label->have_extension_bits = 1; + label->is_complete = 1; + break; + } else if (len) { + label->size = len; + label->have_size = 1; + label->label = *p; + advancexb(len, *p, *l, "name label"); + label->have_label = 1; + } else { + label->have_size = 1; + label->is_complete = 1; + break; + } + + label->is_complete = 1; + label++; + } + + need16(rr->type, *p, *l, "type"); + rr->have_type = 1; + need16(rr->class, *p, *l, "class"); + rr->have_class = 1; + + if (!is_q) { + need32(rr->ttl, *p, *l, "ttl"); + rr->have_ttl = 1; + need16(rr->rdlength, *p, *l, "rdlength"); + rr->have_rdlength = 1; + rr->rdata = *p; + advancexb(rr->rdlength, *p, *l, "rdata"); + + if (use_rdata_index) { + if (!rdata_find(rr->rdata, rr->rdlength, &(rr->rdata_index))) { + rr->have_rdata_index = 1; + } else { + rdata_add(rr->rdata, rr->rdlength); + } + } else if (use_rdata_rindex) { + if (!rdata_find2(rr->rdata, rr->rdlength, &(rr->rdata_rindex))) { + rr->have_rdata_rindex = 1; + } else { + rdata_add2(rr->rdata, rr->rdlength); + } + } + + num_labels = offset = 0; + switch (rr->type) { + case 2: /* NS */ + case 3: /* MD */ + case 4: /* MF */ + case 5: /* CNAME */ + case 7: /* MB */ + case 8: /* MG */ + case 9: /* MR */ + case 12: /* PTR */ + case 30: /* NXT */ + case 39: /* DNAME */ + case 47: /* NSEC */ + case 249: /* TKEY */ + case 250: /* TSIG */ + num_labels = 1; + break; + + case 6: /* SOA */ + case 14: /* MINFO */ + case 17: /* RP */ + case 58: /* TALINK */ + num_labels = 2; + break; + + case 15: /* MX */ + case 18: /* AFSDB */ + case 21: /* RT */ + case 36: /* KX */ + case 107: /* LP */ + num_labels = 1; + offset = 2; + break; + + case 26: /* PX */ + num_labels = 2; + offset = 2; + break; + + case 24: /* SIG */ + case 46: /* RRSIG */ + num_labels = 1; + offset = 18; + break; + + case 33: /* SRV */ + num_labels = 1; + offset = 6; + break; + + case 35: /* NAPTR */ + num_labels = 1; + p2 = *p; + l2 = *l; + advancexb(2, p2, l2, "naptr int16 #1"); + advancexb(2, p2, l2, "naptr int16 #2"); + need8(len, p2, l2, "naptr str len #1"); + advancexb(len, p2, l2, "naptr str #1"); + need8(len, p2, l2, "naptr str len #2"); + advancexb(len, p2, l2, "naptr str #2"); + need8(len, p2, l2, "naptr str len #3"); + advancexb(len, p2, l2, "naptr str #3"); + offset = p2 - *p; + break; + + case 55: /* HIP TODO */ + break; + } + + if (num_labels) { + dns_rdata_t* rdata; + + rr->mixed_rdatas = num_labels + (offset ? 1 : 0) + 1; + if (!(rr->mixed_rdata = calloc(rr->mixed_rdatas, sizeof(dns_rdata_t)))) { + fprintf(stderr, "cds out of memory\n"); + return -1; + } + + p2 = rr->rdata; + l2 = rr->rdlength; + rdata = rr->mixed_rdata; + rr->have_mixed_rdata = 1; + + if (offset) { + rdata->rdata_len = offset; + rdata->rdata = p2; + advancexb((int)offset, p2, l2, "mixed rdata"); + rdata->have_rdata = 1; + rdata->is_complete = 1; + rdata++; + } + while (num_labels--) { + uint8_t* p3; + size_t l3; + + /* first pass check number of rdata labels */ + + p3 = p2; + l3 = l2; + + if (check_dns_label(&(rdata->labels), &p3, &l3)) { + if (!rdata->labels) { + fprintf(stderr, "cds mixed rdata no labels\n"); + return 1; + } + } + + /* second pass, allocate mixed rdata */ + if (!(rdata->label = calloc(rdata->labels, sizeof(dns_label_t)))) { + fprintf(stderr, "cds out of memory\n"); + return -1; + } + + label = rdata->label; + rdata->have_labels = 1; + while (1) { + need8(len, p2, l2, "name length"); + + if ((len & 0xc0) == 0xc0) { + label->offset_p = p2; + need8(label->offset, p2, l2, "name offset"); + label->offset |= (len & 0x3f) << 8; + label->have_offset = 1; + label->is_complete = 1; + break; + } else if (len & 0xc0) { + label->extension_bits = len; + label->have_extension_bits = 1; + label->is_complete = 1; + break; + } else if (len) { + label->size = len; + label->have_size = 1; + label->label = p2; + advancexb(len, p2, l2, "name label"); + label->have_label = 1; + } else { + label->have_size = 1; + label->is_complete = 1; + break; + } + + label->is_complete = 1; + label++; + } + rdata->is_complete = 1; + rdata++; + } + if (l2) { + /*printf("last rdata %lu\n", l2);*/ + rdata->rdata_len = l2; + rdata->rdata = p2; + advancexb((int)l2, p2, l2, "mixed rdata"); + rdata->have_rdata = 1; + rdata->is_complete = 1; + } else { + rr->mixed_rdatas--; + } + } + rr->have_rdata = 1; + } + + rr->is_complete = 1; + rr++; + } + + return 0; +} + +int print_cbor = 0; + +static int parse_dns(dns_t* dns, uint8_t** p, size_t* l) +{ + int ret; + + need16(dns->id, *p, *l, "dns id"); + dns->have_id = 1; + need16(dns->raw, *p, *l, "raw dns bits"); + dns->have_raw = 1; + need16(dns->qdcount, *p, *l, "qdcount"); + dns->have_qdcount = 1; + need16(dns->ancount, *p, *l, "ancount"); + dns->have_ancount = 1; + need16(dns->nscount, *p, *l, "nscount"); + dns->have_nscount = 1; + need16(dns->arcount, *p, *l, "arcount"); + dns->have_arcount = 1; + + dns->header_is_complete = 1; + + if (dns->qdcount) { + if (!(dns->question = calloc(dns->qdcount, sizeof(dns_rr_t)))) { + fprintf(stderr, "cds out of memory\n"); + return -1; + } + ret = parse_dns_rr(1, dns->question, dns->qdcount, &(dns->questions), p, l); + /*if (ret) printf("qr %d\n", ret);*/ + if (ret > -1 && dns->questions) { + dns->have_questions = 1; + } + if (ret) { + return ret; + } + } + + if (dns->ancount) { + if (!(dns->answer = calloc(dns->ancount, sizeof(dns_rr_t)))) { + fprintf(stderr, "cds out of memory\n"); + return -1; + } + ret = parse_dns_rr(0, dns->answer, dns->ancount, &(dns->answers), p, l); + /*if (ret) printf("an %d\n", ret);*/ + if (ret > -1 && dns->answers) { + dns->have_answers = 1; + } + if (ret) { + return ret; + } + } + + if (dns->nscount) { + if (!(dns->authority = calloc(dns->nscount, sizeof(dns_rr_t)))) { + fprintf(stderr, "cds out of memory\n"); + return -1; + } + ret = parse_dns_rr(0, dns->authority, dns->nscount, &(dns->authorities), p, l); + /*if (ret) { printf("ns %d %lu\n", ret, dns->authorities);*/ + /*{*/ + /* size_t n;*/ + /* for (n = 0; n < dns->authorities; n++) {*/ + /* printf("%lu %d\n", n, dns->authority[n].is_complete);*/ + /* if (!dns->authority[n].is_complete) print_cbor = 1;*/ + /* }*/ + /*} }*/ + if (ret > -1 && dns->authorities) { + dns->have_authorities = 1; + } + if (ret) { + return ret; + } + } + + if (dns->arcount) { + if (!(dns->additional = calloc(dns->arcount, sizeof(dns_rr_t)))) { + fprintf(stderr, "cds out of memory\n"); + return -1; + } + ret = parse_dns_rr(0, dns->additional, dns->arcount, &(dns->additionals), p, l); + /*if (ret) printf("ar %d\n", ret);*/ + if (ret > -1 && dns->additionals) { + dns->have_additionals = 1; + } + if (ret) { + return ret; + } + } + + return 0; +} + +static CborError encode_label(CborEncoder* encoder, dns_label_t* label, size_t labels) +{ + CborError cbor_err = CborNoError; + CborEncoder array; + + if (labels && label[labels - 1].have_size && !label[labels - 1].size) { + labels--; + } + + cbor_err = cbor_encoder_create_array(encoder, &array, labels); + while (labels--) { + if (label->have_offset) { + if (label->have_n_offset) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&array, label->n_offset); + } else { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_negative_int(&array, label->offset); + } + } else if (label->have_extension_bits) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_simple_value(&array, label->extension_bits >> 6); + } else if (label->have_label) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_text_string(&array, (const char*)label->label, label->size); + } else { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_null(&array); + } + + label++; + } + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_close_container_checked(encoder, &array); + + return cbor_err; +} + +/* + * OUTPUT + */ + +int print_label(dns_label_t* label, size_t labels) +{ + size_t n; + + for (n = 0; n < labels; n++) { + if (label[n].have_offset) { + if (label[n].have_n_offset) { + printf(" %lu", label[n].n_offset); + } else { + printf(" %d", -label[n].offset); + } + } else if (label[n].have_extension_bits) { + printf(" %x", label[n].extension_bits); + } else if (label[n].have_label) { + printf(" %.*s", label[n].size, label[n].label); + } else { + printf(" $"); + } + } + return 0; +} + +int print_rlabel(dns_rlabel_t* label) +{ + size_t n; + + for (n = 0; n < label->labels; n++) { + if (label->label[n].size) { + printf(" %.*s", label->label[n].size, label->label[n].label); + } else if (label->label[n].have_n_offset) { + printf(" %lu", label->label[n].n_offset); + } else { + printf(" $"); + } + } + return 0; +} + +int dns_rlabel_add(dns_label_t* label, size_t labels) +{ + dns_rlabel_t* copy; + size_t n, size = 0; + + for (n = 0; n < labels; n++) { + if ((label[n].have_offset && !label[n].have_n_offset) + || label[n].have_extension_bits) { + return 1; + } + if (label[n].have_size) { + size += label[n].size; + } + } + /*printf("label size: %lu\n", size);*/ + if (size < MIN_RLABEL_SIZE) { + return 1; + } + + if (!(copy = calloc(1, sizeof(dns_rlabel_t)))) { + return -1; + } + + assert(labels <= CDS_RLABEL_T_LABELS); + copy->labels = labels; + + for (n = 0; n < labels; n++) { + if (label[n].have_n_offset) { + copy->label[n].have_n_offset = 1; + copy->label[n].n_offset = label[n].n_offset; + continue; + } + if (label[n].size) { + assert(label[n].size <= CDS_RLABEL_LABEL_T_LABEL); + + copy->label[n].size = label[n].size; + memcpy(&(copy->label[n].label), label[n].label, label[n].size); + } + } + + /*printf("add"); print_label(label, labels); printf("\n");*/ + + if (last.dns_rlabel) { + last.dns_rlabel->prev = copy; + } + copy->next = last.dns_rlabel; + last.dns_rlabel = copy; + last.dns_rlabels++; + if (last.dns_rlabel_last) { + if (last.dns_rlabels >= MAX_RLABELS) { + dns_rlabel_t* remove = last.dns_rlabel_last; + + /*printf("remove %p %p\n", remove, remove->prev);*/ + + last.dns_rlabel_last = remove->prev; + last.dns_rlabel_last->next = 0; + free(remove); + last.dns_rlabels--; + } + } else { + last.dns_rlabel_last = copy; + } + + return 0; +} + +static size_t dns_rlabel_find(dns_label_t* label, size_t labels, size_t* rlabel_idx) +{ + size_t n, n2, size = 0; + dns_rlabel_t* rlabel; + + for (n = 0; n < labels; n++) { + if ((label[n].have_offset && !label[n].have_n_offset) + || label[n].have_extension_bits) { + return 1; + } + if (label[n].have_size) { + size += label[n].size; + } + } + /*printf("label size: %lu\n", size);*/ + if (size < MIN_RLABEL_SIZE) { + return 1; + } + + /*printf("find"); print_label(label, labels); printf("\n");*/ + + n = 0; + rlabel = last.dns_rlabel; + while (rlabel) { + if (rlabel->labels == labels) { + /*printf("check"); print_rlabel(rlabel); printf("\n");*/ + + for (n2 = 0; n2 < labels; n2++) { + /*printf("%d %lu <> %d %lu\n", label[n2].have_n_offset, label[n2].n_offset, rlabel->label[n2].have_n_offset, rlabel->label[n2].n_offset);*/ + if (label[n2].have_n_offset + || rlabel->label[n2].have_n_offset) { + if (label[n2].n_offset == rlabel->label[n2].n_offset) + continue; + } else if (label[n2].size == rlabel->label[n2].size + && !memcmp(label[n2].label, rlabel->label[n2].label, label[n2].size)) { + continue; + } + break; + } + + if (n2 == labels) { + /*printf("found at %lu: ", n); print_rlabel(rlabel); printf("\n");*/ + break; + } + } + rlabel = rlabel->next; + n++; + } + if (rlabel) { + if (last.dns_rlabel != rlabel) { + dns_rlabel_t *prev = rlabel->prev, *next = rlabel->next; + + if (prev) { + prev->next = next; + } + if (next) { + next->prev = prev; + } + + rlabel->prev = 0; + rlabel->next = last.dns_rlabel; + last.dns_rlabel->prev = rlabel; + last.dns_rlabel = rlabel; + } + + *rlabel_idx = n; + return 0; + } + + return 1; +} + +static void free_rdata(dns_rdata_t* rdata) +{ + if (rdata->label) { + free(rdata->label); + } +} + +static void free_rr(dns_rr_t* rr) +{ + size_t n; + + if (rr->label) { + free(rr->label); + } + for (n = 0; n < rr->mixed_rdatas; n++) { + free_rdata(&(rr->mixed_rdata[n])); + } + if (rr->mixed_rdata) { + free(rr->mixed_rdata); + } +} + +static void free_dns(dns_t* dns) +{ + size_t n; + + for (n = 0; n < dns->questions; n++) { + free_rr(&(dns->question[n])); + } + for (n = 0; n < dns->answers; n++) { + free_rr(&(dns->answer[n])); + } + for (n = 0; n < dns->authorities; n++) { + free_rr(&(dns->authority[n])); + } + for (n = 0; n < dns->additionals; n++) { + free_rr(&(dns->additional[n])); + } +} + +void dns_rr_build_offset(dns_rr_t* rr_list, size_t count, uint16_t* offset, size_t offsets, size_t* n_offset, const u_char* payload) +{ + dns_rr_t* rrp; + size_t rr, n, n2; + + for (rr = 0; rr < count && *n_offset < offsets; rr++) { + rrp = &(rr_list[rr]); + + for (n = 0; n < rrp->labels && *n_offset < offsets; n++) { + if (rrp->label[n].size) { + rrp->label[n].offset = rrp->label[n].label - payload - 1; + offset[*n_offset] = rrp->label[n].offset; + *n_offset += 1; + } else if (rrp->label[n].have_offset) { + offset[*n_offset] = rrp->label[n].offset_p - payload - 1; + *n_offset += 1; + } + + /* printf("%u %u %u %.*s\n",*/ + /* rrp->label[n].size,*/ + /* rrp->label[n].extension_bits,*/ + /* rrp->label[n].offset,*/ + /* rrp->label[n].size ? rrp->label[n].size : 0,*/ + /* rrp->label[n].size ? (char*)rrp->label[n].label : ""*/ + /* );*/ + } + for (n = 0; n < rrp->mixed_rdatas && *n_offset < offsets; n++) { + for (n2 = 0; n2 < rrp->mixed_rdata[n].labels; n2++) { + if (rrp->mixed_rdata[n].label[n2].size) { + rrp->mixed_rdata[n].label[n2].offset = rrp->mixed_rdata[n].label[n2].label - payload - 1; + offset[*n_offset] = rrp->mixed_rdata[n].label[n2].offset; + *n_offset += 1; + } else if (rrp->mixed_rdata[n].label[n2].have_offset) { + offset[*n_offset] = rrp->mixed_rdata[n].label[n2].offset_p - payload - 1; + *n_offset += 1; + } + + /* printf(" %u %u %u %.*s\n",*/ + /* rrp->mixed_rdata[n].label[n2].size,*/ + /* rrp->mixed_rdata[n].label[n2].extension_bits,*/ + /* rrp->mixed_rdata[n].label[n2].offset,*/ + /* rrp->mixed_rdata[n].label[n2].size ? rrp->mixed_rdata[n].label[n2].size : 0,*/ + /* rrp->mixed_rdata[n].label[n2].size ? (char*)rrp->mixed_rdata[n].label[n2].label : ""*/ + /* );*/ + } + } + } +} + +void dns_rr_set_offset(dns_rr_t* rr_list, size_t count, uint16_t* offset, size_t n_offset) +{ + dns_rr_t* rrp; + size_t rr, n, n2, n3; + + for (rr = 0; rr < count; rr++) { + rrp = &(rr_list[rr]); + + for (n = 0; n < rrp->labels; n++) { + if (!rrp->label[n].size && rrp->label[n].offset) { + for (n3 = 0; n3 < n_offset; n3++) { + if (rrp->label[n].offset == offset[n3]) { + /* printf("%u => %lu\n", rrp->label[n].offset, n3);*/ + rrp->label[n].n_offset = n3; + rrp->label[n].have_n_offset = 1; + break; + } + } + } + } + for (n = 0; n < rrp->mixed_rdatas; n++) { + for (n2 = 0; n2 < rrp->mixed_rdata[n].labels; n2++) { + if (!rrp->mixed_rdata[n].label[n2].size && rrp->mixed_rdata[n].label[n2].offset) { + for (n3 = 0; n3 < n_offset; n3++) { + if (rrp->mixed_rdata[n].label[n2].offset == offset[n3]) { + /* printf("%u => %lu\n", rrp->mixed_rdata[n].label[n2].offset, n3);*/ + rrp->mixed_rdata[n].label[n2].n_offset = n3; + rrp->mixed_rdata[n].label[n2].have_n_offset = 1; + break; + } + } + } + } + } + } +} + +void dns_rr_build_rlabel(dns_rr_t* rr_list, size_t count) +{ + dns_rr_t* rrp; + size_t rr, n; + + for (rr = 0; rr < count; rr++) { + rrp = &(rr_list[rr]); + + if (rrp->labels) { + if (!dns_rlabel_find(rrp->label, rrp->labels, &(rrp->rlabel_idx))) { + rrp->have_rlabel_idx = 1; + } else { + dns_rlabel_add(rrp->label, rrp->labels); + } + } + + for (n = 0; n < rrp->mixed_rdatas; n++) { + if (rrp->mixed_rdata[n].labels) { + if (!dns_rlabel_find(rrp->mixed_rdata[n].label, rrp->mixed_rdata[n].labels, &(rrp->mixed_rdata[n].rlabel_idx))) { + rrp->mixed_rdata[n].have_rlabel_idx = 1; + } else { + dns_rlabel_add(rrp->mixed_rdata[n].label, rrp->mixed_rdata[n].labels); + } + } + } + } +} + +CborError dns_build_rrs(CborEncoder* message, dns_rr_t* rr_list, size_t count) +{ + CborError cbor_err = CborNoError; + CborEncoder rrs; + dns_rr_t* rr = rr_list; + size_t n = count; + + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_create_array(message, &rrs, n); + while (n--) { + CborEncoder item; + if (!(rr->have_type && rr->type == 41)) { + if (rr->have_type && rr->type == last.dns_type) { + rr->have_type = 0; + } + if (rr->have_class && rr->class == last.dns_class) { + rr->have_class = 0; + } + if (rr->have_ttl && rr->ttl == last.dns_ttl) { + rr->have_ttl = 0; + } + } + if (rr->have_rdlength && rr->have_rdata) { + rr->have_rdlength = 0; + } + + rr->bits = rr->have_type + | rr->have_class << 1 + | rr->have_ttl << 2 + | rr->have_rdlength << 3; + if (rr->bits && rr->bits != 0xf) { + rr->have_bits = 1; + } + + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_create_array(&rrs, &item, + (rr->is_complete ? 0 : 1) + rr->have_labels + + rr->have_bits + rr->have_type + rr->have_class + rr->have_ttl + rr->have_rdlength + + rr->have_rdata); + if (!rr->is_complete) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_boolean(&item, false); + } + if (rr->have_labels) { + if (rr->have_rlabel_idx) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_negative_int(&item, rr->rlabel_idx); + } else { + if (cbor_err == CborNoError) + cbor_err = encode_label(&item, rr->label, rr->labels); + } + } + if (rr->have_bits && cbor_err == CborNoError) + cbor_err = cbor_encode_simple_value(&item, rr->bits); + if (rr->have_type && cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&item, rr->type); + if (rr->have_class && cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&item, rr->class); + if (rr->have_ttl && cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&item, rr->ttl); + if (rr->have_rdlength && cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&item, rr->rdlength); + if (rr->have_rdata_index) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&item, rr->rdata_index); + } else if (rr->have_rdata_rindex) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_negative_int(&item, rr->rdata_rindex); + } else if (rr->have_mixed_rdata) { + CborEncoder rdatas; + size_t n2 = rr->mixed_rdatas; + dns_rdata_t* rdata = rr->mixed_rdata; + + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_create_array(&item, &rdatas, rr->mixed_rdatas); + while (n2--) { + if (rdata->have_labels) { + if (rdata->have_rlabel_idx) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_negative_int(&rdatas, rdata->rlabel_idx); + } else { + if (cbor_err == CborNoError) + cbor_err = encode_label(&rdatas, rdata->label, rdata->labels); + } + } else if (rdata->have_rdata) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_byte_string(&rdatas, rdata->rdata, rdata->rdata_len); + } + + rdata++; + } + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_close_container_checked(&item, &rdatas); + } else if (rr->have_rdata && cbor_err == CborNoError) + cbor_err = cbor_encode_byte_string(&item, rr->rdata, rr->rdlength); + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_close_container_checked(&rrs, &item); + + if (!(rr->have_type && rr->type == 41)) { + if (rr->have_type) { + last.dns_type = rr->type; + } + if (rr->have_class) { + last.dns_class = rr->class; + } + if (rr->have_ttl) { + last.dns_ttl = rr->ttl; + } + } + rr++; + } + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_close_container_checked(message, &rrs); + + return cbor_err; +} + +int output_cds(iaddr from, iaddr to, uint8_t proto, unsigned flags, unsigned sport, unsigned dport, my_bpftimeval ts, const u_char* pkt_copy, size_t olen, const u_char* payload, size_t payloadlen) +{ + CborEncoder cbor, message; + CborError cbor_err = CborNoError; + ip_header_t ip; + dns_t dns; + uint8_t* malformed = 0; + size_t malformed_size = 0; + size_t dns_parts = 0; + + if (!payload) { + return DUMP_CDS_EINVAL; + } + if (!payloadlen) { + return DUMP_CDS_EINVAL; + } + + if (!cbor_buf) { + memset(&last, 0, sizeof(last)); + if (!(cbor_buf = calloc(1, cbor_size + message_size))) { + return DUMP_CDS_ENOMEM; + } + } + if (!cbor_buf_p) { + cbor_buf_p = cbor_buf; + } + if (!message_buf) { + if (!(message_buf = calloc(1, message_size))) { + return DUMP_CDS_ENOMEM; + } + } + if (cbor_flushed) { + dns_rlabel_t* rlabel; + struct rdata* r; + + cbor_buf_p = cbor_buf; + while ((rlabel = last.dns_rlabel)) { + last.dns_rlabel = rlabel->next; + free(rlabel); + } + while ((r = last.rdata)) { + last.rdata = r->next; + rdata_free(r); + } + memset(&last, 0, sizeof(last)); + if (rdata_tbl) { + hash_free(rdata_tbl); + rdata_tbl = 0; + } + + cbor_encoder_init(&cbor, message_buf, message_size, 0); + cbor_err = cbor_encoder_create_array(&cbor, &message, 5 + (use_rdata_index ? 3 : 0) + (use_rdata_rindex ? 4 : 0)); + if (cbor_err == CborNoError) + cbor_err = cbor_encode_text_stringz(&message, "CDSv1"); + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, CDS_OPTION_RLABELS); + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, MAX_RLABELS); + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, CDS_OPTION_RLABEL_MIN_SIZE); + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, MIN_RLABEL_SIZE); + if (use_rdata_index) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, CDS_OPTION_USE_RDATA_INDEX); + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, CDS_OPTION_RDATA_INDEX_MIN_SIZE); + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, RDATA_INDEX_MIN_SIZE); + } else if (use_rdata_rindex) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, CDS_OPTION_RDATA_RINDEX_SIZE); + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, RDATA_RINDEX_SIZE); + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, CDS_OPTION_RDATA_RINDEX_MIN_SIZE); + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, RDATA_RINDEX_MIN_SIZE); + } + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_close_container_checked(&cbor, &message); + if (cbor_err != CborNoError) { + fprintf(stderr, "cbor error[%d]: %s\n", cbor_err, cbor_error_string(cbor_err)); + return DUMP_CDS_ECBOR; + } + + /* *cbor_buf_p = 0x9f;*/ + /* cbor_buf_p++;*/ + + if ((cbor_size - (cbor_buf_p - cbor_buf)) < cbor_encoder_get_buffer_size(&cbor, message_buf)) { + return DUMP_CDS_EBUF; + } + memcpy(cbor_buf_p, message_buf, cbor_encoder_get_buffer_size(&cbor, message_buf)); + cbor_buf_p += cbor_encoder_get_buffer_size(&cbor, message_buf); + + cbor_flushed = 0; + } + if (!rdata_tbl) { + if (!(rdata_tbl = hash_create(64 * 1024, rdata_hash, rdata_cmp, rdata_free))) { + return DUMP_CDS_ENOMEM; + } + } + + /* + * IP Header + */ + + memset(&ip, 0, sizeof(ip_header_t)); + + /* fill ip */ + if (from.af == AF_INET6) { + ip.is_v6 = 1; + memcpy(&(ip.src_addr6), &(from.u.a6), sizeof(struct in6_addr)); + memcpy(&(ip.dest_addr6), &(to.u.a6), sizeof(struct in6_addr)); + ip.src_port6 = sport; + ip.dest_port6 = dport; + } else { + memcpy(&(ip.src_addr4), &(from.u.a4), sizeof(struct in_addr)); + memcpy(&(ip.dest_addr4), &(to.u.a4), sizeof(struct in_addr)); + ip.src_port4 = sport; + ip.dest_port4 = dport; + } + + /* deduplicate */ + { + int dedup = 0; + ip_header_t reverse; + + reverse = ip; + + /* check last.ip */ + if (ip.is_v6) { + if (!memcmp(&(ip.src_addr6), &(last.ip.src_addr6), sizeof(struct in6_addr))) + dedup++; + else + ip.have_src_addr = 1; + + if (!memcmp(&(ip.dest_addr6), &(last.ip.dest_addr6), sizeof(struct in6_addr))) + dedup++; + else + ip.have_dest_addr = 1; + + if (ip.src_port6 == last.ip.src_port6) + dedup++; + else + ip.have_src_port = 1; + + if (ip.dest_port6 == last.ip.dest_port6) + dedup++; + else + ip.have_dest_port = 1; + } else { + if (!memcmp(&(ip.src_addr4), &(last.ip.src_addr4), sizeof(struct in_addr))) + dedup++; + else + ip.have_src_addr = 1; + + if (!memcmp(&(ip.dest_addr4), &(last.ip.dest_addr4), sizeof(struct in_addr))) + dedup++; + else + ip.have_dest_addr = 1; + + if (ip.src_port4 == last.ip.src_port4) + dedup++; + else + ip.have_src_port = 1; + + if (ip.dest_port4 == last.ip.dest_port4) + dedup++; + else + ip.have_dest_port = 1; + } + + /* check reverse last.ip */ + if (ip.is_v6) { + if (!memcmp(&(ip.src_addr6), &(last.ip.dest_addr6), sizeof(struct in6_addr))) + dedup--; + else + reverse.have_src_addr = 1; + + if (!memcmp(&(ip.dest_addr6), &(last.ip.src_addr6), sizeof(struct in6_addr))) + dedup--; + else + reverse.have_dest_addr = 1; + + if (ip.src_port6 == last.ip.dest_port6) + dedup--; + else + reverse.have_src_port = 1; + + if (ip.dest_port6 == last.ip.src_port6) + dedup--; + else + reverse.have_dest_port = 1; + } else { + if (!memcmp(&(ip.src_addr4), &(last.ip.dest_addr4), sizeof(struct in_addr))) + dedup--; + else + reverse.have_src_addr = 1; + + if (!memcmp(&(ip.dest_addr4), &(last.ip.src_addr4), sizeof(struct in_addr))) + dedup--; + else + reverse.have_dest_addr = 1; + + if (ip.src_port4 == last.ip.dest_port4) + dedup--; + else + reverse.have_src_port = 1; + + if (ip.dest_port4 == last.ip.src_port4) + dedup--; + else + reverse.have_dest_port = 1; + } + + if (dedup < 0) { + ip = reverse; + ip.is_reverse = 1; + /*fprintf(stderr, "reverse of last ip ");*/ + } + /*fprintf(stderr, "v6:%d src:%d dest:%d sport:%d dport:%d\n", ip.is_v6, ip.have_src_addr, ip.have_dest_addr, ip.have_src_port, ip.have_dest_port);*/ + + ip.bits = ip.is_v6 + | ip.have_src_addr << 1 + | ip.have_dest_addr << 2 + | (ip.have_src_port | ip.have_dest_port) << 3; + + if (ip.is_v6) { + last.ip.src_addr6 = ip.src_addr6; + last.ip.dest_addr6 = ip.dest_addr6; + last.ip.src_port6 = ip.src_port6; + last.ip.dest_port6 = ip.dest_port6; + } else { + last.ip.src_addr4 = ip.src_addr4; + last.ip.dest_addr4 = ip.dest_addr4; + last.ip.src_port4 = ip.src_port4; + last.ip.dest_port4 = ip.dest_port4; + } + } + + /* + * DNS Message + */ + + if (flags & DNSCAP_OUTPUT_ISDNS) { + uint8_t* p = (uint8_t*)payload; + size_t l = payloadlen, rr, n, n2, n3; + int ret; + dns_rr_t* rrp; + + size_t n_offset = 0; + uint16_t offset[256]; /* TODO: Handle offsets better */ + + memset(&dns, 0, sizeof(dns)); + ret = parse_dns(&dns, &p, &l); + + if (ret < 0) { + free_dns(&dns); + return DUMP_CDS_ENOMEM; + } else if (ret > 0) { + malformed = p; + malformed_size = l; + } + + if (dns.have_qdcount && dns.qdcount == dns.questions) { + dns.have_qdcount = 0; + } + if (dns.have_ancount && dns.ancount == dns.answers) { + dns.have_ancount = 0; + } + if (dns.have_nscount && dns.nscount == dns.authorities) { + dns.have_nscount = 0; + } + if (dns.have_arcount && dns.arcount == dns.additionals) { + dns.have_arcount = 0; + } + + dns.cnt_bits = dns.have_qdcount + | dns.have_ancount << 1 + | dns.have_nscount << 2 + | dns.have_arcount << 3; + if (dns.cnt_bits && dns.cnt_bits != 0xf) { + dns.have_cnt_bits = 1; + } + + dns.rr_bits = dns.have_questions + | dns.have_answers << 1 + | dns.have_authorities << 2 + | dns.have_additionals << 3; + if (dns.rr_bits && dns.rr_bits != 0xf) { + dns.have_rr_bits = 1; + } + + dns_rr_build_offset(dns.question, dns.questions, &offset[0], sizeof(offset), &n_offset, payload); + dns_rr_build_offset(dns.answer, dns.answers, &offset[0], sizeof(offset), &n_offset, payload); + dns_rr_build_offset(dns.authority, dns.authorities, &offset[0], sizeof(offset), &n_offset, payload); + dns_rr_build_offset(dns.additional, dns.additionals, &offset[0], sizeof(offset), &n_offset, payload); + + /* for (n = 0; n < n_offset; n++) {*/ + /* printf("%lu: %u\n", n, offset[n]);*/ + /* }*/ + + dns_rr_set_offset(dns.question, dns.questions, &offset[0], n_offset); + dns_rr_set_offset(dns.answer, dns.answers, &offset[0], n_offset); + dns_rr_set_offset(dns.authority, dns.authorities, &offset[0], n_offset); + dns_rr_set_offset(dns.additional, dns.additionals, &offset[0], n_offset); + + dns_rr_build_rlabel(dns.question, dns.questions); + dns_rr_build_rlabel(dns.answer, dns.answers); + dns_rr_build_rlabel(dns.authority, dns.authorities); + dns_rr_build_rlabel(dns.additional, dns.additionals); + } + + /* + * CBOR + */ + + cbor_encoder_init(&cbor, message_buf, message_size, 0); + cbor_err = cbor_encoder_create_array(&cbor, &message, + /* timestamp */ + 1 + /* message bits */ + + 1 + /* ip header */ + + 1 + ip.have_src_addr + ip.have_dest_addr + (ip.have_src_port | ip.have_dest_port) + /* dns message */ + + dns.have_id + dns.have_raw + + dns.have_cnt_bits + dns.have_qdcount + dns.have_ancount + dns.have_nscount + dns.have_arcount + + dns.have_rr_bits + dns.have_questions + dns.have_answers + dns.have_authorities + dns.have_additionals + + (malformed ? 1 : 0)); + + /* + * Encode timestamp + */ + + { + CborEncoder timestamp; + + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_create_array(&message, ×tamp, 2); + if (last.ts.tv_sec && last.ts.tv_sec <= ts.tv_sec) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_negative_int(×tamp, ts.tv_sec - last.ts.tv_sec); + if (cbor_err == CborNoError) + cbor_err = cbor_encode_int(×tamp, ts.tv_usec - last.ts.tv_usec); + } else { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(×tamp, ts.tv_sec); + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(×tamp, ts.tv_usec); + } + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_close_container_checked(&message, ×tamp); + + last.ts = ts; + } + + /* + * Encode message bits + */ + + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, + (flags & DNSCAP_OUTPUT_ISDNS ? 1 : 0) + + (flags & DNSCAP_OUTPUT_ISDNS ? proto == IPPROTO_TCP ? 1 << 1 : 0 + : 0) + + (flags & DNSCAP_OUTPUT_ISFRAG ? 1 << 2 : 0) + + (malformed ? 1 << 3 : 0)); + + /* + * Encode IP Header + */ + + if (ip.is_reverse) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_negative_int(&message, ip.bits); + } else { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, ip.bits); + } + + if (ip.is_v6) { + if (ip.have_src_addr && cbor_err == CborNoError) + cbor_err = cbor_encode_byte_string(&message, (uint8_t*)&(ip.src_addr6), sizeof(struct in6_addr)); + if (ip.have_dest_addr && cbor_err == CborNoError) + cbor_err = cbor_encode_byte_string(&message, (uint8_t*)&(ip.dest_addr6), sizeof(struct in6_addr)); + if (ip.have_src_port && ip.have_dest_port) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, (ip.dest_port6 << 16) | ip.src_port6); + } else if (ip.have_src_port) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, ip.src_port6); + } else if (ip.have_dest_port) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_negative_int(&message, ip.dest_port6); + } + } else { + if (ip.have_src_addr && cbor_err == CborNoError) + cbor_err = cbor_encode_byte_string(&message, (uint8_t*)&(ip.src_addr4), sizeof(struct in_addr)); + if (ip.have_dest_addr && cbor_err == CborNoError) + cbor_err = cbor_encode_byte_string(&message, (uint8_t*)&(ip.dest_addr4), sizeof(struct in_addr)); + if (ip.have_src_port && ip.have_dest_port) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, (ip.dest_port4 << 16) | ip.src_port4); + } else if (ip.have_src_port) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, ip.src_port4); + } else if (ip.have_dest_port) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_negative_int(&message, ip.dest_port4); + } + } + + /* + * Encode DNS Message + */ + if (flags & DNSCAP_OUTPUT_ISDNS && !dns.header_is_complete) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_boolean(&message, false); + } + if (dns.have_id && cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, dns.id); + if (dns.have_raw && cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, dns.raw); + if (dns.have_cnt_bits && cbor_err == CborNoError) + cbor_err = cbor_encode_negative_int(&message, dns.cnt_bits); + if (dns.have_qdcount && cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, dns.qdcount); + if (dns.have_ancount && cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, dns.ancount); + if (dns.have_nscount && cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, dns.nscount); + if (dns.have_arcount && cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&message, dns.arcount); + if (dns.have_rr_bits && cbor_err == CborNoError) + cbor_err = cbor_encode_simple_value(&message, dns.rr_bits); + if (dns.have_questions) { + CborEncoder rrs; + dns_rr_t* rr = dns.question; + size_t n = dns.questions; + + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_create_array(&message, &rrs, n); + while (n--) { + CborEncoder item; + + if (rr->have_type && rr->type == last.dns_type) { + rr->have_type = 0; + } + if (rr->have_class && rr->class == last.dns_class) { + rr->have_class = 0; + } + + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_create_array(&rrs, &item, + (rr->is_complete ? 0 : 1) + rr->have_labels + rr->have_type + rr->have_class); + if (!rr->is_complete) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_boolean(&item, false); + } + if (rr->have_labels) { + if (rr->have_rlabel_idx) { + if (cbor_err == CborNoError) + cbor_err = cbor_encode_negative_int(&item, rr->rlabel_idx); + } else { + if (cbor_err == CborNoError) + cbor_err = encode_label(&item, rr->label, rr->labels); + } + } + if (rr->have_type && cbor_err == CborNoError) + cbor_err = cbor_encode_uint(&item, rr->type); + if (rr->have_class && cbor_err == CborNoError) + cbor_err = cbor_encode_negative_int(&item, rr->class); + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_close_container_checked(&rrs, &item); + + if (rr->have_type) { + last.dns_type = rr->type; + } + if (rr->have_class) { + last.dns_class = rr->class; + } + + rr++; + } + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_close_container_checked(&message, &rrs); + } + if (dns.have_answers && cbor_err == CborNoError) + cbor_err = dns_build_rrs(&message, dns.answer, dns.answers); + if (dns.have_authorities && cbor_err == CborNoError) + cbor_err = dns_build_rrs(&message, dns.authority, dns.authorities); + if (dns.have_additionals && cbor_err == CborNoError) + cbor_err = dns_build_rrs(&message, dns.additional, dns.additionals); + + /* + * Encode malformed + */ + + if (malformed && cbor_err == CborNoError) + cbor_err = cbor_encode_byte_string(&message, (uint8_t*)malformed, malformed_size); + + /* + * Close + */ + + free_dns(&dns); + + if (cbor_err == CborNoError) + cbor_err = cbor_encoder_close_container_checked(&cbor, &message); + if (cbor_err != CborNoError) { + fprintf(stderr, "cbor error[%d]: %s\n", cbor_err, cbor_error_string(cbor_err)); + return DUMP_CDS_ECBOR; + } + + /* if (print_cbor>1)*/ + /* {*/ + /* uint8_t* p = message_buf;*/ + /* size_t s = cbor_encoder_get_buffer_size(&cbor, message_buf);*/ + + /* while (s--) {*/ + /* printf("%02x", *p++);*/ + /* }*/ + /* printf("\n");*/ + /* }*/ + + if (((cbor_size + message_size) - (cbor_buf_p - cbor_buf)) < cbor_encoder_get_buffer_size(&cbor, message_buf)) { + return DUMP_CDS_EBUF; + } + memcpy(cbor_buf_p, message_buf, cbor_encoder_get_buffer_size(&cbor, message_buf)); + cbor_buf_p += cbor_encoder_get_buffer_size(&cbor, message_buf); + + if (cbor_buf_p < (cbor_buf + cbor_size)) { + return DUMP_CDS_OK; + } + + cbor_flushed = 1; + return DUMP_CDS_FLUSH; +} + +int dump_cds(FILE* fp) +{ + CborError cbor_err; + + if (!fp) { + return DUMP_CDS_EINVAL; + } + + /* *cbor_buf_p = 0xff;*/ + /* cbor_buf_p++;*/ + + /* fprintf(stderr, "cds output: %lu bytes\n", cbor_buf_p - cbor_buf);*/ + + if (fwrite(cbor_buf, cbor_buf_p - cbor_buf, 1, fp) != 1) { + return DUMP_CDS_EWRITE; + } + + return DUMP_CDS_OK; +} + +int have_cds_support() +{ + return 1; +} + +#else /* HAVE_LIBTINYCBOR */ + +int cds_set_cbor_size(size_t size) +{ + return DUMP_CDS_ENOSUP; +} + +int cds_set_message_size(size_t size) +{ + return DUMP_CDS_ENOSUP; +} + +int cds_set_max_rlabels(size_t size) +{ + return DUMP_CDS_ENOSUP; +} + +int cds_set_min_rlabel_size(size_t size) +{ + return DUMP_CDS_ENOSUP; +} + +int cds_set_use_rdata_index(int use) +{ + return DUMP_CDS_ENOSUP; +} + +int cds_set_use_rdata_rindex(int use) +{ + return DUMP_CDS_ENOSUP; +} + +int cds_set_rdata_index_min_size(size_t size) +{ + return DUMP_CDS_ENOSUP; +} + +int cds_set_rdata_rindex_min_size(size_t size) +{ + return DUMP_CDS_ENOSUP; +} + +int cds_set_rdata_rindex_size(size_t size) +{ + return DUMP_CDS_ENOSUP; +} + +int output_cds(iaddr from, iaddr to, uint8_t proto, unsigned flags, unsigned sport, unsigned dport, my_bpftimeval ts, const u_char* pkt_copy, size_t olen, const u_char* payload, size_t payloadlen) +{ + return DUMP_CDS_ENOSUP; +} + +int dump_cds(FILE* fp) +{ + return DUMP_CDS_ENOSUP; +} + +int have_cds_support() +{ + return 0; +} + +#endif |