diff options
-rw-r--r-- | Makefile.am | 9 | ||||
-rw-r--r-- | src/basic/bitmap.c | 208 | ||||
-rw-r--r-- | src/basic/bitmap.h | 50 | ||||
-rw-r--r-- | src/basic/util.c | 350 | ||||
-rw-r--r-- | src/basic/util.h | 5 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.c | 284 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.h | 2 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.c | 99 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.h | 17 | ||||
-rw-r--r-- | src/test/test-bitmap.c | 96 | ||||
-rw-r--r-- | src/test/test-util.c | 175 |
11 files changed, 1287 insertions, 8 deletions
diff --git a/Makefile.am b/Makefile.am index 3c0ce32692..7d0f7575f2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -791,6 +791,8 @@ libbasic_la_SOURCES = \ src/basic/siphash24.h \ src/basic/set.h \ src/basic/ordered-set.h \ + src/basic/bitmap.c \ + src/basic/bitmap.h \ src/basic/fdset.c \ src/basic/fdset.h \ src/basic/prioq.c \ @@ -1412,6 +1414,7 @@ tests += \ test-time \ test-hashmap \ test-set \ + test-bitmap \ test-list \ test-unaligned \ test-tables \ @@ -1768,6 +1771,12 @@ test_set_SOURCES = \ test_set_LDADD = \ libshared.la +test_bitmap_SOURCES = \ + src/test/test-bitmap.c + +test_bitmap_LDADD = \ + libshared.la + test_xml_SOURCES = \ src/test/test-xml.c diff --git a/src/basic/bitmap.c b/src/basic/bitmap.c new file mode 100644 index 0000000000..d865e2fc6e --- /dev/null +++ b/src/basic/bitmap.c @@ -0,0 +1,208 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "util.h" + +#include "bitmap.h" + +struct Bitmap { + long long unsigned *bitmaps; + size_t n_bitmaps; + size_t bitmaps_allocated; + unsigned next_entry; +}; + +/* Bitmaps are only meant to store relatively small numbers + * (corresponding to, say, an enum), so it is ok to limit + * the max entry. 64k should be plenty. */ +#define BITMAPS_MAX_ENTRY 0xffff + +/* This indicates that we reached the end of the bitmap */ +#define BITMAP_END ((unsigned) -1) + +#define BITMAP_NUM_TO_OFFSET(n) ((n) / (sizeof(long long unsigned) * 8)) +#define BITMAP_NUM_TO_REM(n) ((n) % (sizeof(long long unsigned) * 8)) +#define BITMAP_OFFSET_TO_NUM(offset, rem) ((offset) * sizeof(long long unsigned) * 8 + (rem)) + +Bitmap *bitmap_new(void) { + return new0(Bitmap, 1); +} + +void bitmap_free(Bitmap *b) { + if (!b) + return; + + free(b->bitmaps); + free(b); +} + +int bitmap_ensure_allocated(Bitmap **b) { + Bitmap *a; + + if (*b) + return 0; + + a = bitmap_new(); + if (!a) + return -ENOMEM; + + *b = a; + + return 0; +} + +int bitmap_set(Bitmap *b, unsigned n) { + long long bitmask; + unsigned offset; + + assert(b); + + /* we refuse to allocate huge bitmaps */ + if (n > BITMAPS_MAX_ENTRY) + return -ERANGE; + + offset = BITMAP_NUM_TO_OFFSET(n); + + if (offset >= b->n_bitmaps) { + if (!GREEDY_REALLOC0(b->bitmaps, b->bitmaps_allocated, offset + 1)) + return -ENOMEM; + + b->n_bitmaps = offset + 1; + } + + bitmask = 1 << BITMAP_NUM_TO_REM(n); + + b->bitmaps[offset] |= bitmask; + + return 0; +} + +void bitmap_unset(Bitmap *b, unsigned n) { + long long bitmask; + unsigned offset; + + assert(b); + + offset = BITMAP_NUM_TO_OFFSET(n); + + if (offset >= b->n_bitmaps) + return; + + bitmask = 1 << BITMAP_NUM_TO_REM(n); + + b->bitmaps[offset] &= ~bitmask; +} + +bool bitmap_isset(Bitmap *b, unsigned n) { + long long bitmask; + unsigned offset; + + if (!b || !b->bitmaps) + return false; + + offset = BITMAP_NUM_TO_OFFSET(n); + + if (offset >= b->n_bitmaps) + return false; + + bitmask = 1 << BITMAP_NUM_TO_REM(n); + + return !!(b->bitmaps[offset] & bitmask); +} + +bool bitmap_isclear(Bitmap *b) { + unsigned i; + + assert(b); + + for (i = 0; i < b->n_bitmaps; i++) + if (b->bitmaps[i]) + return false; + + return true; +} + +void bitmap_clear(Bitmap *b) { + unsigned i; + + assert(b); + + for (i = 0; i < b->n_bitmaps; i++) + b->bitmaps[i] = 0; +} + +void bitmap_rewind(Bitmap *b) { + if (!b) + return; + + b->next_entry = 0; +} + +bool bitmap_next(Bitmap *b, unsigned *n) { + long long bitmask; + unsigned offset, rem; + + if (!b && b->next_entry == BITMAP_END) + return false; + + offset = BITMAP_NUM_TO_OFFSET(b->next_entry); + rem = BITMAP_NUM_TO_REM(b->next_entry); + bitmask = 1 << rem; + + for (; offset < b->n_bitmaps; offset ++) { + if (b->bitmaps[offset]) { + for (; bitmask; bitmask <<= 1, rem ++) { + if (b->bitmaps[offset] & bitmask) { + *n = BITMAP_OFFSET_TO_NUM(offset, rem); + b->next_entry = *n + 1; + + return true; + } + } + } + + rem = 0; + bitmask = 1; + } + + b->next_entry = BITMAP_END; + + return false; +} + +bool bitmap_equal(Bitmap *a, Bitmap *b) { + unsigned i; + + if (!a ^ !b) + return false; + + if (!a) + return true; + + if (a->n_bitmaps != b->n_bitmaps) + return false; + + for (i = 0; i < a->n_bitmaps; i++) + if (a->bitmaps[i] != b->bitmaps[i]) + return false; + + return true; +} diff --git a/src/basic/bitmap.h b/src/basic/bitmap.h new file mode 100644 index 0000000000..92bee51a2c --- /dev/null +++ b/src/basic/bitmap.h @@ -0,0 +1,50 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "macro.h" + +typedef struct Bitmap Bitmap; + +Bitmap *bitmap_new(void); + +void bitmap_free(Bitmap *b); + +int bitmap_ensure_allocated(Bitmap **b); + +int bitmap_set(Bitmap *b, unsigned n); +void bitmap_unset(Bitmap *b, unsigned n); +bool bitmap_isset(Bitmap *b, unsigned n); +bool bitmap_isclear(Bitmap *b); +void bitmap_clear(Bitmap *b); + +void bitmap_rewind(Bitmap *b); +bool bitmap_next(Bitmap *b, unsigned *n); + +bool bitmap_equal(Bitmap *a, Bitmap *b); + +#define BITMAP_FOREACH(n, b) \ + for (bitmap_rewind(b); bitmap_next((b), &(n)); ) + +DEFINE_TRIVIAL_CLEANUP_FUNC(Bitmap*, bitmap_free); + +#define _cleanup_bitmap_free_ _cleanup_(bitmap_freep) diff --git a/src/basic/util.c b/src/basic/util.c index a45f5f8e53..dc20fa9baf 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -954,6 +954,351 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) { return 0; } +/* https://tools.ietf.org/html/rfc4648#section-6 */ +char base32hexchar(int x) { + static const char table[32] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUV"; + + return table[x & 31]; +} + +int unbase32hexchar(char c) { + unsigned offset; + + if (c >= '0' && c <= '9') + return c - '0'; + + offset = '9' - '0' + 1; + + if (c >= 'A' && c <= 'V') + return c - 'A' + offset; + + return -EINVAL; +} + +char *base32hexmem(const void *p, size_t l, bool padding) { + char *r, *z; + const uint8_t *x; + size_t len; + + if (padding) + /* five input bytes makes eight output bytes, padding is added so we must round up */ + len = 8 * (l + 4) / 5; + else { + /* same, but round down as there is no padding */ + len = 8 * l / 5; + + switch (l % 5) { + case 4: + len += 7; + break; + case 3: + len += 5; + break; + case 2: + len += 4; + break; + case 1: + len += 2; + break; + } + } + + z = r = malloc(len + 1); + if (!r) + return NULL; + + for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) { + /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ + x[3] == QQQQQQQQ; x[4] == WWWWWWWW */ + *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ + *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ + *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ + *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ + *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */ + *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */ + *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5); /* 000QQWWW */ + *(z++) = base32hexchar((x[4] & 31)); /* 000WWWWW */ + } + + switch (l % 5) { + case 4: + *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ + *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ + *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ + *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ + *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */ + *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */ + *(z++) = base32hexchar((x[3] & 3) << 3); /* 000QQ000 */ + if (padding) + *(z++) = '='; + + break; + + case 3: + *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ + *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ + *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ + *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ + *(z++) = base32hexchar((x[2] & 15) << 1); /* 000ZZZZ0 */ + if (padding) { + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + } + + break; + + case 2: + *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ + *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ + *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ + *(z++) = base32hexchar((x[1] & 1) << 4); /* 000Y0000 */ + if (padding) { + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + } + + break; + + case 1: + *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ + *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */ + if (padding) { + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + } + + break; + } + + *z = 0; + return r; +} + +int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) { + _cleanup_free_ uint8_t *r = NULL; + int a, b, c, d, e, f, g, h; + uint8_t *z; + const char *x; + size_t len; + unsigned pad = 0; + + assert(p); + + /* padding ensures any base32hex input has input divisible by 8 */ + if (padding && l % 8 != 0) + return -EINVAL; + + if (padding) { + /* strip the padding */ + while (l > 0 && p[l - 1] == '=' && pad < 7) { + pad ++; + l --; + } + } + + /* a group of eight input bytes needs five output bytes, in case of + padding we need to add some extra bytes */ + len = (l / 8) * 5; + + switch (l % 8) { + case 7: + len += 4; + break; + case 5: + len += 3; + break; + case 4: + len += 2; + break; + case 2: + len += 1; + break; + case 0: + break; + default: + return -EINVAL; + } + + z = r = malloc(len + 1); + if (!r) + return -ENOMEM; + + for (x = p; x < p + (l / 8) * 8; x += 8) { + /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW + e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */ + a = unbase32hexchar(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase32hexchar(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase32hexchar(x[2]); + if (c < 0) + return -EINVAL; + + d = unbase32hexchar(x[3]); + if (d < 0) + return -EINVAL; + + e = unbase32hexchar(x[4]); + if (e < 0) + return -EINVAL; + + f = unbase32hexchar(x[5]); + if (f < 0) + return -EINVAL; + + g = unbase32hexchar(x[6]); + if (g < 0) + return -EINVAL; + + h = unbase32hexchar(x[7]); + if (h < 0) + return -EINVAL; + + *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ + *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ + *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ + *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */ + *(z++) = (uint8_t) g << 5 | (uint8_t) h; /* VVVRRRRR */ + } + + switch (l % 8) { + case 7: + a = unbase32hexchar(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase32hexchar(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase32hexchar(x[2]); + if (c < 0) + return -EINVAL; + + d = unbase32hexchar(x[3]); + if (d < 0) + return -EINVAL; + + e = unbase32hexchar(x[4]); + if (e < 0) + return -EINVAL; + + f = unbase32hexchar(x[5]); + if (f < 0) + return -EINVAL; + + g = unbase32hexchar(x[6]); + if (g < 0) + return -EINVAL; + + /* g == 000VV000 */ + if (g & 7) + return -EINVAL; + + *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ + *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ + *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ + *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */ + + break; + case 5: + a = unbase32hexchar(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase32hexchar(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase32hexchar(x[2]); + if (c < 0) + return -EINVAL; + + d = unbase32hexchar(x[3]); + if (d < 0) + return -EINVAL; + + e = unbase32hexchar(x[4]); + if (e < 0) + return -EINVAL; + + /* e == 000SSSS0 */ + if (e & 1) + return -EINVAL; + + *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ + *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ + *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ + + break; + case 4: + a = unbase32hexchar(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase32hexchar(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase32hexchar(x[2]); + if (c < 0) + return -EINVAL; + + d = unbase32hexchar(x[3]); + if (d < 0) + return -EINVAL; + + /* d == 000W0000 */ + if (d & 15) + return -EINVAL; + + *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ + *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ + + break; + case 2: + a = unbase32hexchar(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase32hexchar(x[1]); + if (b < 0) + return -EINVAL; + + /* b == 000YYY00 */ + if (b & 3) + return -EINVAL; + + *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ + + break; + case 0: + break; + default: + return -EINVAL; + } + + *z = 0; + + *mem = r; + r = NULL; + *_len = len; + + return 0; +} + /* https://tools.ietf.org/html/rfc4648#section-4 */ char base64char(int x) { static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -1117,6 +1462,11 @@ int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) { *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ break; + case 0: + + break; + default: + return -EINVAL; } *z = 0; diff --git a/src/basic/util.h b/src/basic/util.h index dae43006e4..c2e5cc610b 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -240,6 +240,8 @@ char octchar(int x) _const_; int unoctchar(char c) _const_; char decchar(int x) _const_; int undecchar(char c) _const_; +char base32hexchar(int x) _const_; +int unbase32hexchar(char c) _const_; char base64char(int x) _const_; int unbase64char(char c) _const_; @@ -618,6 +620,9 @@ static inline void *mempset(void *s, int c, size_t n) { char *hexmem(const void *p, size_t l); int unhexmem(const char *p, size_t l, void **mem, size_t *len); +char *base32hexmem(const void *p, size_t l, bool padding); +int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len); + char *base64mem(const void *p, size_t l); int unbase64mem(const char *p, size_t l, void **mem, size_t *len); diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 1ebebf8ea4..e44d3926d9 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -502,6 +502,89 @@ fail: return r; } +static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t length, uint8_t *types, size_t *start) { + size_t saved_size; + int r; + + assert(p); + assert(types); + + if (length == 0) + return 0; + + saved_size = p->size; + + r = dns_packet_append_uint8(p, window, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, length, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, types, length, NULL); + if (r < 0) + goto fail; + + if (start) + *start = saved_size; + + return 0; +fail: + dns_packet_truncate(p, saved_size); + return r; +} + +static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) { + uint8_t window = 0; + uint8_t len = 0; + uint8_t bitmaps[32] = {}; + unsigned n; + size_t saved_size; + int r; + + assert(p); + assert(types); + + saved_size = p->size; + + BITMAP_FOREACH(n, types) { + uint8_t entry; + + assert(n <= 0xffff); + + if ((n << 8) != window) { + r = dns_packet_append_type_window(p, window, len, bitmaps, NULL); + if (r < 0) + goto fail; + + if (len > 0) { + len = 0; + zero(bitmaps); + } + } + + window = n << 8; + len ++; + + entry = n & 255; + + bitmaps[entry / 8] |= 1 << (7 - (entry % 8)); + } + + r = dns_packet_append_type_window(p, window, len, bitmaps, NULL); + if (r < 0) + goto fail; + + if (start) + *start = saved_size; + + return 0; +fail: + dns_packet_truncate(p, saved_size); + return r; +} + int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start) { size_t saved_size, rdlength_offset, end, rdlength; int r; @@ -732,6 +815,50 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star r = dns_packet_append_blob(p, rr->rrsig.signature, rr->rrsig.signature_size, NULL); break; + case DNS_TYPE_NSEC: + r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_types(p, rr->nsec.types, NULL); + if (r < 0) + goto fail; + + break; + case DNS_TYPE_NSEC3: + r = dns_packet_append_uint8(p, rr->nsec3.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->nsec3.flags, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint16(p, rr->nsec3.iterations, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->nsec3.salt_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->nsec3.salt, rr->nsec3.salt_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->nsec3.next_hashed_name_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_types(p, rr->nsec3.types, NULL); + if (r < 0) + goto fail; + + break; case _DNS_TYPE_INVALID: /* unparseable */ default: @@ -996,6 +1123,79 @@ fail: return r; } +static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *start) { + uint8_t window; + uint8_t length; + const uint8_t *bitmap; + unsigned i; + bool found = false; + size_t saved_rindex; + int r; + + assert(p); + assert(types); + + saved_rindex = p->rindex; + + r = bitmap_ensure_allocated(types); + if (r < 0) + goto fail; + + r = dns_packet_read_uint8(p, &window, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint8(p, &length, NULL); + if (r < 0) + goto fail; + + if (length == 0 || length > 32) + return -EBADMSG; + + r = dns_packet_read(p, length, (const void **)&bitmap, NULL); + if (r < 0) + goto fail; + + for (i = 0; i < length; i++) { + uint8_t bitmask = 1 << 7; + uint8_t bit = 0; + + if (!bitmap[i]) { + found = false; + continue; + } + + found = true; + + while (bitmask) { + if (bitmap[i] & bitmask) { + uint16_t n; + + /* XXX: ignore pseudo-types? see RFC4034 section 4.1.2 */ + n = (uint16_t) window << 8 | (uint16_t) bit; + + r = bitmap_set(*types, n); + if (r < 0) + goto fail; + } + + bit ++; + bitmask >>= 1; + } + } + + if (!found) + return -EBADMSG; + + if (start) + *start = saved_rindex; + + return 0; +fail: + dns_packet_rewind(p, saved_rindex); + return r; +} + int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) { _cleanup_free_ char *name = NULL; uint16_t class, type; @@ -1382,6 +1582,72 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { NULL); break; + case DNS_TYPE_NSEC: + r = dns_packet_read_name(p, &rr->nsec.next_domain_name, false, NULL); + if (r < 0) + goto fail; + + while (p->rindex != offset + rdlength) { + r = dns_packet_read_type_window(p, &rr->nsec.types, NULL); + if (r < 0) + goto fail; + } + + break; + + case DNS_TYPE_NSEC3: { + uint8_t size; + + r = dns_packet_read_uint8(p, &rr->nsec3.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint8(p, &rr->nsec3.flags, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint16(p, &rr->nsec3.iterations, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint8(p, &size, NULL); + if (r < 0) + goto fail; + + rr->nsec3.salt_size = size; + + r = dns_packet_read_blob(p, &d, rr->nsec3.salt_size, NULL); + if (r < 0) + goto fail; + + rr->nsec3.salt = memdup(d, rr->nsec3.salt_size); + if (!rr->nsec3.salt) { + r = -ENOMEM; + goto fail; + } + + r = dns_packet_read_uint8(p, &size, NULL); + if (r < 0) + goto fail; + + rr->nsec3.next_hashed_name_size = size; + + r = dns_packet_read(p, rr->nsec3.next_hashed_name_size, &d, NULL); + if (r < 0) + goto fail; + + rr->nsec3.next_hashed_name = memdup(d, rr->nsec3.next_hashed_name_size); + if (!rr->nsec3.next_hashed_name) { + r = -ENOMEM; + goto fail; + } + + r = dns_packet_append_types(p, rr->nsec3.types, NULL); + if (r < 0) + goto fail; + + break; + } default: unparseable: r = dns_packet_read(p, rdlength, &d, NULL); @@ -1516,13 +1782,15 @@ static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = { DEFINE_STRING_TABLE_LOOKUP(dns_protocol, DnsProtocol); static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = { - [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5", - [DNSSEC_ALGORITHM_DH] = "DH", - [DNSSEC_ALGORITHM_DSA] = "DSA", - [DNSSEC_ALGORITHM_ECC] = "ECC", - [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1", - [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT", - [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS", - [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID", + [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5", + [DNSSEC_ALGORITHM_DH] = "DH", + [DNSSEC_ALGORITHM_DSA] = "DSA", + [DNSSEC_ALGORITHM_ECC] = "ECC", + [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1", + [DNSSEC_ALGORITHM_DSA_NSEC3_SHA1] = "DSA-NSEC3-SHA1", + [DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1", + [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT", + [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS", + [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID", }; DEFINE_STRING_TABLE_LOOKUP(dnssec_algorithm, int); diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 6588ed9df5..58559c85df 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -223,6 +223,8 @@ enum { DNSSEC_ALGORITHM_DSA, DNSSEC_ALGORITHM_ECC, DNSSEC_ALGORITHM_RSASHA1, + DNSSEC_ALGORITHM_DSA_NSEC3_SHA1, + DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1, DNSSEC_ALGORITHM_INDIRECT = 252, DNSSEC_ALGORITHM_PRIVATEDNS, DNSSEC_ALGORITHM_PRIVATEOID, diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 676b77713e..e9907eabc0 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -288,6 +288,17 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) { free(rr->rrsig.signature); break; + case DNS_TYPE_NSEC: + free(rr->nsec.next_domain_name); + bitmap_free(rr->nsec.types); + break; + + case DNS_TYPE_NSEC3: + free(rr->nsec3.next_hashed_name); + free(rr->nsec3.salt); + bitmap_free(rr->nsec3.types); + break; + case DNS_TYPE_LOC: case DNS_TYPE_A: case DNS_TYPE_AAAA: @@ -448,6 +459,19 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor return dns_name_equal(a->rrsig.signer, b->rrsig.signer); + case DNS_TYPE_NSEC: + return dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name) && + bitmap_equal(a->nsec.types, b->nsec.types); + + case DNS_TYPE_NSEC3: + return a->nsec3.algorithm == b->nsec3.algorithm && + a->nsec3.flags == b->nsec3.flags && + a->nsec3.iterations == b->nsec3.iterations && + a->nsec3.salt_size == b->nsec3.salt_size && + memcmp(a->nsec3.salt, b->nsec3.salt, a->nsec3.salt_size) == 0 && + memcmp(a->nsec3.next_hashed_name, b->nsec3.next_hashed_name, a->nsec3.next_hashed_name_size) == 0 && + bitmap_equal(a->nsec3.types, b->nsec3.types); + default: return a->generic.size == b->generic.size && memcmp(a->generic.data, b->generic.data, a->generic.size) == 0; @@ -500,6 +524,37 @@ static int format_timestamp_dns(char *buf, size_t l, time_t sec) { return 0; } +static char *format_types(Bitmap *types) { + _cleanup_strv_free_ char **strv = NULL; + _cleanup_free_ char *str = NULL; + unsigned type; + int r; + + BITMAP_FOREACH(type, types) { + if (dns_type_to_string(type)) { + r = strv_extend(&strv, strdup(dns_type_to_string(type))); + if (r < 0) + return NULL; + } else { + char *t; + + r = asprintf(&t, "TYPE%u", type); + if (r < 0) + return NULL; + + r = strv_extend(&strv, t); + if (r < 0) + return NULL; + } + } + + str = strv_join(strv, " "); + if (!str) + return NULL; + + return strjoin("( ", str, " )", NULL); +} + int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) { _cleanup_free_ char *k = NULL, *t = NULL; char *s; @@ -704,6 +759,50 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) { break; } + case DNS_TYPE_NSEC: + t = format_types(rr->nsec.types); + if (!t) + return -ENOMEM; + + r = asprintf(&s, "%s %s %s", + k, + rr->nsec.next_domain_name, + t); + if (r < 0) + return -ENOMEM; + break; + + case DNS_TYPE_NSEC3: { + _cleanup_free_ char *salt = NULL, *hash = NULL; + + if (rr->nsec3.salt_size) { + salt = hexmem(rr->nsec3.salt, rr->nsec3.salt_size); + if (!salt) + return -ENOMEM; + } + + hash = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false); + if (!hash) + return -ENOMEM; + + t = format_types(rr->nsec3.types); + if (!t) + return -ENOMEM; + + r = asprintf(&s, "%s %"PRIu8" %"PRIu8" %"PRIu16" %s %s %s", + k, + rr->nsec3.algorithm, + rr->nsec3.flags, + rr->nsec3.iterations, + rr->nsec3.salt_size ? salt : "-", + hash, + t); + if (r < 0) + return -ENOMEM; + + break; + } + default: t = hexmem(rr->generic.data, rr->generic.size); if (!t) diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index b375d6b9fc..bdd5a5c824 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -23,6 +23,7 @@ #include <netinet/in.h> +#include "bitmap.h" #include "hashmap.h" #include "in-addr-util.h" #include "dns-type.h" @@ -145,6 +146,22 @@ struct DnsResourceRecord { void *signature; size_t signature_size; } rrsig; + + struct { + char *next_domain_name; + Bitmap *types; + } nsec; + + struct { + uint8_t algorithm; + uint8_t flags; + uint16_t iterations; + void *salt; + size_t salt_size; + void *next_hashed_name; + size_t next_hashed_name_size; + Bitmap *types; + } nsec3; }; }; diff --git a/src/test/test-bitmap.c b/src/test/test-bitmap.c new file mode 100644 index 0000000000..304888beb9 --- /dev/null +++ b/src/test/test-bitmap.c @@ -0,0 +1,96 @@ +/*** + This file is part of systemd + + Copyright 2015 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "bitmap.h" + +int main(int argc, const char *argv[]) { + _cleanup_bitmap_free_ Bitmap *b = NULL; + unsigned n = (unsigned) -1, i = 0; + + b = bitmap_new(); + assert_se(b); + + assert_se(bitmap_ensure_allocated(&b) == 0); + bitmap_free(b); + b = NULL; + assert_se(bitmap_ensure_allocated(&b) == 0); + + assert_se(bitmap_isset(b, 0) == false); + assert_se(bitmap_isset(b, 1) == false); + assert_se(bitmap_isset(b, 256) == false); + assert_se(bitmap_isclear(b) == true); + + assert_se(bitmap_set(b, 0) == 0); + assert_se(bitmap_isset(b, 0) == true); + assert_se(bitmap_isclear(b) == false); + bitmap_unset(b, 0); + assert_se(bitmap_isset(b, 0) == false); + assert_se(bitmap_isclear(b) == true); + + assert_se(bitmap_set(b, 1) == 0); + assert_se(bitmap_isset(b, 1) == true); + assert_se(bitmap_isclear(b) == false); + bitmap_unset(b, 1); + assert_se(bitmap_isset(b, 1) == false); + assert_se(bitmap_isclear(b) == true); + + assert_se(bitmap_set(b, 256) == 0); + assert_se(bitmap_isset(b, 256) == true); + assert_se(bitmap_isclear(b) == false); + bitmap_unset(b, 256); + assert_se(bitmap_isset(b, 256) == false); + assert_se(bitmap_isclear(b) == true); + + assert_se(bitmap_set(b, 0) == 0); + assert_se(bitmap_set(b, 1) == 0); + assert_se(bitmap_set(b, 256) == 0); + + BITMAP_FOREACH(n, b) { + assert_se(n == i); + if (i == 0) + i = 1; + else if (i == 1) + i = 256; + else if (i == 256) + i = (unsigned) -1; + } + + assert_se(i == (unsigned) -1); + + i = 0; + + BITMAP_FOREACH(n, b) { + assert_se(n == i); + if (i == 0) + i = 1; + else if (i == 1) + i = 256; + else if (i == 256) + i = (unsigned) -1; + } + + assert_se(i == (unsigned) -1); + + bitmap_clear(b); + assert_se(bitmap_isclear(b) == true); + + assert_se(bitmap_set(b, (unsigned) -1) == -ERANGE); + + return 0; +} diff --git a/src/test/test-util.c b/src/test/test-util.c index 72fbc345c2..7906c4d7bb 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -390,6 +390,21 @@ static void test_unhexchar(void) { assert_se(unhexchar('0') == 0x0); } +static void test_base32hexchar(void) { + assert_se(base32hexchar(0) == '0'); + assert_se(base32hexchar(9) == '9'); + assert_se(base32hexchar(10) == 'A'); + assert_se(base32hexchar(31) == 'V'); +} + +static void test_unbase32hexchar(void) { + assert_se(unbase32hexchar('0') == 0); + assert_se(unbase32hexchar('9') == 9); + assert_se(unbase32hexchar('A') == 10); + assert_se(unbase32hexchar('V') == 31); + assert_se(unbase32hexchar('=') == -EINVAL); +} + static void test_base64char(void) { assert_se(base64char(0) == 'A'); assert_se(base64char(26) == 'a'); @@ -453,6 +468,162 @@ static void test_unhexmem(void) { } /* https://tools.ietf.org/html/rfc4648#section-10 */ +static void test_base32hexmem(void) { + char *b32; + + b32 = base32hexmem("", strlen(""), true); + assert_se(b32); + assert_se(streq(b32, "")); + free(b32); + + b32 = base32hexmem("f", strlen("f"), true); + assert_se(b32); + assert_se(streq(b32, "CO======")); + free(b32); + + b32 = base32hexmem("fo", strlen("fo"), true); + assert_se(b32); + assert_se(streq(b32, "CPNG====")); + free(b32); + + b32 = base32hexmem("foo", strlen("foo"), true); + assert_se(b32); + assert_se(streq(b32, "CPNMU===")); + free(b32); + + b32 = base32hexmem("foob", strlen("foob"), true); + assert_se(b32); + assert_se(streq(b32, "CPNMUOG=")); + free(b32); + + b32 = base32hexmem("fooba", strlen("fooba"), true); + assert_se(b32); + assert_se(streq(b32, "CPNMUOJ1")); + free(b32); + + b32 = base32hexmem("foobar", strlen("foobar"), true); + assert_se(b32); + assert_se(streq(b32, "CPNMUOJ1E8======")); + free(b32); + + b32 = base32hexmem("", strlen(""), false); + assert_se(b32); + assert_se(streq(b32, "")); + free(b32); + + b32 = base32hexmem("f", strlen("f"), false); + assert_se(b32); + assert_se(streq(b32, "CO")); + free(b32); + + b32 = base32hexmem("fo", strlen("fo"), false); + assert_se(b32); + assert_se(streq(b32, "CPNG")); + free(b32); + + b32 = base32hexmem("foo", strlen("foo"), false); + assert_se(b32); + assert_se(streq(b32, "CPNMU")); + free(b32); + + b32 = base32hexmem("foob", strlen("foob"), false); + assert_se(b32); + assert_se(streq(b32, "CPNMUOG")); + free(b32); + + b32 = base32hexmem("fooba", strlen("fooba"), false); + assert_se(b32); + assert_se(streq(b32, "CPNMUOJ1")); + free(b32); + + b32 = base32hexmem("foobar", strlen("foobar"), false); + assert_se(b32); + assert_se(streq(b32, "CPNMUOJ1E8")); + free(b32); +} + +static void test_unbase32hexmem(void) { + void *mem; + size_t len; + + assert_se(unbase32hexmem("", strlen(""), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "")); + free(mem); + + assert_se(unbase32hexmem("CO======", strlen("CO======"), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "f")); + free(mem); + + assert_se(unbase32hexmem("CPNG====", strlen("CPNG===="), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "fo")); + free(mem); + + assert_se(unbase32hexmem("CPNMU===", strlen("CPNMU==="), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foo")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOG=", strlen("CPNMUOG="), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foob")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "fooba")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOJ1E8======", strlen("CPNMUOJ1E8======"), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foobar")); + free(mem); + + assert_se(unbase32hexmem("A", strlen("A"), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("A=======", strlen("A======="), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAA=====", strlen("AAA====="), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAAAA==", strlen("AAAAAA=="), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AB======", strlen("AB======"), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAB====", strlen("AAAB===="), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAAB===", strlen("AAAAB==="), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAAAAB=", strlen("AAAAAAB="), true, &mem, &len) == -EINVAL); + + assert_se(unbase32hexmem("", strlen(""), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "")); + free(mem); + + assert_se(unbase32hexmem("CO", strlen("CO"), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "f")); + free(mem); + + assert_se(unbase32hexmem("CPNG", strlen("CPNG"), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "fo")); + free(mem); + + assert_se(unbase32hexmem("CPNMU", strlen("CPNMU"), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foo")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOG", strlen("CPNMUOG"), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foob")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOJ1", strlen("CPNMUOJ1"), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "fooba")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOJ1E8", strlen("CPNMUOJ1E8"), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foobar")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOG=", strlen("CPNMUOG="), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("CPNMUOJ1E8======", strlen("CPNMUOJ1E8======"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("A", strlen("A"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("A", strlen("A"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAA", strlen("AAA"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAAAA", strlen("AAAAAA"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AB", strlen("AB"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAB", strlen("AAAB"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAAB", strlen("AAAAB"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAAAAB", strlen("AAAAAAB"), false, &mem, &len) == -EINVAL); +} + +/* https://tools.ietf.org/html/rfc4648#section-10 */ static void test_base64mem(void) { char *b64; @@ -1924,6 +2095,8 @@ int main(int argc, char *argv[]) { test_in_charset(); test_hexchar(); test_unhexchar(); + test_base32hexchar(); + test_unbase32hexchar(); test_base64char(); test_unbase64char(); test_octchar(); @@ -1931,6 +2104,8 @@ int main(int argc, char *argv[]) { test_decchar(); test_undecchar(); test_unhexmem(); + test_base32hexmem(); + test_unbase32hexmem(); test_base64mem(); test_unbase64mem(); test_cescape(); |