diff options
Diffstat (limited to 'tests/modules')
-rw-r--r-- | tests/modules/test_onlinesign.c | 203 | ||||
-rw-r--r-- | tests/modules/test_rrl.c | 178 |
2 files changed, 381 insertions, 0 deletions
diff --git a/tests/modules/test_onlinesign.c b/tests/modules/test_onlinesign.c new file mode 100644 index 0000000..fbddb6e --- /dev/null +++ b/tests/modules/test_onlinesign.c @@ -0,0 +1,203 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <tap/basic.h> +#include <assert.h> + +#include "knot/modules/onlinesign/nsec_next.h" +#include "libknot/consts.h" +#include "libknot/dname.h" +#include "libknot/errcode.h" + +/*! + * \brief Assert that a domain name in a static buffer is valid. + */ +#define _assert_dname(name) \ + assert(knot_dname_wire_check(name, name + KNOT_DNAME_MAXLEN, NULL) > 0) + +static void _test_nsec_next(const char *msg, + const knot_dname_t *input, + const knot_dname_t *apex, + const knot_dname_t *expected) +{ + knot_dname_t *next = online_nsec_next(input, apex); + ok(next != NULL && knot_dname_is_equal(next, expected), + "nsec_next, %s", msg); + knot_dname_free(next, NULL); +} + +/*! + * \brief Check \a online_nsec_next. + * + * Intentionally implemented as a macro. The input domain names are copied + * into static buffers and validated. + */ +#define test_nsec_next(msg, _input, _apex, _expected) \ +{ \ + uint8_t input[KNOT_DNAME_MAXLEN] = _input; \ + uint8_t apex[KNOT_DNAME_MAXLEN] = _apex; \ + uint8_t expected[KNOT_DNAME_MAXLEN] = _expected; \ + \ + _assert_dname(input); \ + _assert_dname(apex); \ + _assert_dname(expected); \ + \ + _test_nsec_next(msg, input, apex, expected); \ +} + +int main(int argc, char *argv[]) +{ + plan_lazy(); + + // adding a single zero-byte label + + test_nsec_next( + "zero-byte label, apex", + "\x7""example""\x3""com", + "\x7""example""\x3""com", + "\x01\x00""\x07""example""\x03""com" + ); + + test_nsec_next( + "zero-byte label, subdomain", + "\x02""nx""\x7""example""\x3""com", + "\x7""example""\x3""com", + "\x01\x00""\x02""nx""\x07""example""\x03""com" + ); + + test_nsec_next( + "zero-byte label, binary", + "\x02\xff\xff""\x7""example""\x3""com", + "\x07""example""\x3""com", + "\x01\x00""\x02\xff\xff""\x7""example""\x3""com" + ); + + // zero byte label won't fit, increment + #define APEX \ + "\x05""bacon""\x05""salad" + + #define LONG_SUFFIX \ + "\x2e""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ + "\x2e""iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" \ + "\x2e""mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm" \ + "\x2e""qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" \ + "\x2c""zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" \ + APEX + assert(sizeof(LONG_SUFFIX) == 245 + 1); + + test_nsec_next( + "increment first label (simple)", + "\x08""icecream" LONG_SUFFIX, + APEX, + "\x08""icecrean" LONG_SUFFIX + ); + + test_nsec_next( + "increment first label (binary)", + "\x08""walrus\xff\xff" LONG_SUFFIX, + APEX, + "\x08""walrut\x00\x00" LONG_SUFFIX + ); + + test_nsec_next( + "increment first label (in place)", + "\x07""lobster" LONG_SUFFIX, + APEX, + "\x07""lobstes" LONG_SUFFIX + ); + + test_nsec_next( + "increment first label (extend)", + "\x07""\xff\xff\xff\xff\xff\xff\xff" LONG_SUFFIX, + APEX, + "\x08""\xff\xff\xff\xff\xff\xff\xff\x00" LONG_SUFFIX + ); + + // name too long + + test_nsec_next( + "name to long, strip label and increase next (simple)", + "\x03""\xff\xff\xff""\x04""newt" LONG_SUFFIX, + APEX, + "\x04""newu" LONG_SUFFIX + ); + + test_nsec_next( + "name to long, strip label and increase next (binary)", + "\x03""\xff\xff\xff""\x04""cc\xff\xff" LONG_SUFFIX, + APEX, + "\x04""cd\x00\x00" LONG_SUFFIX + ); + + test_nsec_next( + "name to long, strip label and increase next (extend)", + "\x04""\xff\xff\xff\xff""\x03""\xff\xff\xff" LONG_SUFFIX, + APEX, + "\x04""\xff\xff\xff\x00" LONG_SUFFIX + ); + + // label too long + + #define MAX_LABEL "\x3f" /* 63 */ \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff" + assert(sizeof(MAX_LABEL) == 64 + 1); + + #define PAD_LABEL "\x28" /* 40 */ \ + "iiiiiiiiiioooooooooottttttttttssssssssss" + assert(sizeof(PAD_LABEL) == 41 + 1); + + test_nsec_next( + "label too long, strip and increase next (simple)", + MAX_LABEL "\x08""mandrill" MAX_LABEL MAX_LABEL PAD_LABEL APEX, + APEX, + "\x08""mandrilm" MAX_LABEL MAX_LABEL PAD_LABEL APEX + ); + + test_nsec_next( + "label too long, strip and increase next (extend)", + MAX_LABEL "\x07""\xff\xff\xff\xff\xff\xff\xff" MAX_LABEL MAX_LABEL PAD_LABEL APEX, + APEX, + "\x08""\xff\xff\xff\xff\xff\xff\xff\x00" MAX_LABEL MAX_LABEL PAD_LABEL APEX + ); + + test_nsec_next( + "label too long, strip multiple", + MAX_LABEL MAX_LABEL "\x08""flamingo" MAX_LABEL PAD_LABEL APEX, + APEX, + "\x08""flamingp" MAX_LABEL PAD_LABEL APEX + ); + + test_nsec_next( + "label too long, wrap around to apex", + "\x31" /* 49 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff" + MAX_LABEL MAX_LABEL MAX_LABEL APEX, + APEX, + APEX + ); + + return 0; +} diff --git a/tests/modules/test_rrl.c b/tests/modules/test_rrl.c new file mode 100644 index 0000000..6a5210f --- /dev/null +++ b/tests/modules/test_rrl.c @@ -0,0 +1,178 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <tap/basic.h> + +#include "libdnssec/crypto.h" +#include "libdnssec/random.h" +#include "libknot/libknot.h" +#include "contrib/sockaddr.h" +#include "knot/modules/rrl/functions.c" +#include "stdio.h" + +/* Enable time-dependent tests. */ +//#define ENABLE_TIMED_TESTS +#define RRL_SIZE 196613 +#define RRL_THREADS 8 +#define RRL_INSERTS (RRL_SIZE/(5*RRL_THREADS)) /* lf = 1/5 */ + +/* Disabled as default as it depends on random input. + * Table may be consistent even if some collision occur (and they may occur). + * Note: Disabled due to reported problems when running on VMs due to time + * flow inconsistencies. Should work alright on a host machine. + */ +#ifdef ENABLE_TIMED_TESTS +struct bucketmap { + unsigned i; + uint64_t x; +}; + +/*! \brief Unit runnable. */ +struct runnable_data { + int passed; + rrl_table_t *rrl; + struct sockaddr_storage *addr; + rrl_req_t *rq; + knot_dname_t *zone; +}; + +static void* rrl_runnable(void *arg) +{ + struct runnable_data *d = (struct runnable_data *)arg; + struct sockaddr_storage addr; + memcpy(&addr, d->addr, sizeof(struct sockaddr_storage)); + int lock = -1; + uint32_t now = time(NULL); + struct bucketmap *m = malloc(RRL_INSERTS * sizeof(struct bucketmap)); + for (unsigned i = 0; i < RRL_INSERTS; ++i) { + m[i].i = dnssec_random_uint32_t(); + ((struct sockaddr_in *) &addr)->sin_addr.s_addr = m[i].i; + rrl_item_t *b = rrl_hash(d->rrl, &addr, d->rq, d->zone, now, &lock); + rrl_unlock(d->rrl, lock); + m[i].x = b->netblk; + } + for (unsigned i = 0; i < RRL_INSERTS; ++i) { + ((struct sockaddr_in *) &addr)->sin_addr.s_addr = m[i].i; + rrl_item_t *b = rrl_hash(d->rrl, &addr, d->rq, d->zone, now, &lock); + rrl_unlock(d->rrl, lock); + if (b->netblk != m[i].x) { + d->passed = 0; + } + } + free(m); + return NULL; +} + +static void rrl_hopscotch(struct runnable_data* rd) +{ + rd->passed = 1; + pthread_t thr[RRL_THREADS]; + for (unsigned i = 0; i < RRL_THREADS; ++i) { + pthread_create(thr + i, NULL, &rrl_runnable, rd); + } + for (unsigned i = 0; i < RRL_THREADS; ++i) { + pthread_join(thr[i], NULL); + } +} +#endif + +int main(int argc, char *argv[]) +{ + plan_lazy(); + + dnssec_crypto_init(); + + /* Prepare query. */ + knot_pkt_t *query = knot_pkt_new(NULL, 512, NULL); + if (query == NULL) { + return KNOT_ERROR; /* Fatal */ + } + + knot_dname_t *qname = knot_dname_from_str_alloc("beef."); + int ret = knot_pkt_put_question(query, qname, KNOT_CLASS_IN, KNOT_RRTYPE_A); + knot_dname_free(qname, NULL); + if (ret != KNOT_EOK) { + knot_pkt_free(query); + return KNOT_ERROR; /* Fatal */ + } + + /* Prepare response */ + uint8_t rbuf[65535]; + size_t rlen = sizeof(rbuf); + memcpy(rbuf, query->wire, query->size); + knot_wire_flags_set_qr(rbuf); + + rrl_req_t rq; + rq.wire = rbuf; + rq.len = rlen; + rq.query = query; + rq.flags = 0; + + /* 1. create rrl table */ + const uint32_t rate = 10; + rrl_table_t *rrl = rrl_create(RRL_SIZE, rate); + ok(rrl != NULL, "rrl: create"); + + /* 2. N unlimited requests. */ + knot_dname_t *zone = knot_dname_from_str_alloc("rrl."); + + struct sockaddr_storage addr; + struct sockaddr_storage addr6; + sockaddr_set(&addr, AF_INET, "1.2.3.4", 0); + sockaddr_set(&addr6, AF_INET6, "1122:3344:5566:7788::aabb", 0); + ret = 0; + for (unsigned i = 0; i < rate * RRL_CAPACITY; ++i) { + if (rrl_query(rrl, &addr, &rq, zone, NULL) != KNOT_EOK || + rrl_query(rrl, &addr6, &rq, zone, NULL) != KNOT_EOK) { + ret = KNOT_ELIMIT; + break; + } + } + is_int(0, ret, "rrl: unlimited IPv4/v6 requests"); + + /* 3. Endian-independent hash input buffer. */ + uint8_t buf[RRL_CLSBLK_MAXLEN]; + // CLS_LARGE + remote + dname wire. + uint8_t expectedv4[] = "\x10\x01\x02\x03\x00\x00\x00\x00\x00\x04""beef"; + rrl_classify(buf, sizeof(buf), &addr, &rq, qname); + is_int(0, memcmp(buf, expectedv4, sizeof(expectedv4)), "rrl: IPv4 hash input buffer"); + uint8_t expectedv6[] = "\x10\x11\x22\x33\x44\x55\x66\x77\x00\x04""beef"; + rrl_classify(buf, sizeof(buf), &addr6, &rq, qname); + is_int(0, memcmp(buf, expectedv6, sizeof(expectedv6)), "rrl: IPv6 hash input buffer"); + +#ifdef ENABLE_TIMED_TESTS + /* 5. limited request */ + ret = rrl_query(rrl, &addr, &rq, zone, NULL); + is_int(KNOT_ELIMIT, ret, "rrl: throttled IPv4 request"); + + /* 6. limited IPv6 request */ + ret = rrl_query(rrl, &addr6, &rq, zone, NULL); + is_int(KNOT_ELIMIT, ret, "rrl: throttled IPv6 request"); + + /* 8. hopscotch test */ + struct runnable_data rd = { + 1, rrl, &addr, &rq, zone + }; + rrl_hopscotch(&rd); + ok(rd.passed, "rrl: hashtable is ~ consistent"); +#endif + + knot_dname_free(zone, NULL); + knot_pkt_free(query); + rrl_destroy(rrl); + dnssec_crypto_cleanup(); + return 0; +} |