mirror of
https://gitlab.nic.cz/knot/knot-dns.git
synced 2026-02-03 18:49:28 -05:00
190 lines
5.8 KiB
C
190 lines
5.8 KiB
C
/* Copyright (C) CZ.NIC, z.s.p.o. and contributors
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* For more information, see <https://www.knot-dns.cz/>
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <tap/basic.h>
|
|
#include <tap/files.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "libknot/descriptor.h"
|
|
#include "libknot/packet/wire.h"
|
|
#include "knot/nameserver/process_query.h"
|
|
#include "test_server.h"
|
|
#include "contrib/sockaddr.h"
|
|
#include "contrib/ucw/mempool.h"
|
|
|
|
/* Basic response check (4 TAP tests). */
|
|
static void answer_sanity_check(const uint8_t *query,
|
|
const uint8_t *answer, uint16_t answer_len,
|
|
uint8_t expected_rcode, const char *name)
|
|
{
|
|
ok(answer_len >= KNOT_WIRE_HEADER_SIZE, "ns: len(%s answer) >= DNS header", name);
|
|
if (answer_len >= KNOT_WIRE_HEADER_SIZE) {
|
|
ok(knot_wire_get_qr(answer), "ns: %s answer has QR=1", name);
|
|
is_int(expected_rcode, knot_wire_get_rcode(answer), "ns: %s answer RCODE=%d", name, expected_rcode);
|
|
is_int(knot_wire_get_id(query), knot_wire_get_id(answer), "ns: %s MSGID match", name);
|
|
} else {
|
|
skip_block(3, "ns: can't check DNS header");
|
|
}
|
|
}
|
|
|
|
/* Resolve query and check answer for sanity (2 TAP tests). */
|
|
static void exec_query(knot_layer_t *layer, const char *name,
|
|
knot_pkt_t *query,
|
|
uint8_t expected_rcode)
|
|
{
|
|
knot_pkt_t *answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
|
|
assert(answer);
|
|
|
|
/* Input packet. */
|
|
knot_pkt_parse(query, 0);
|
|
knot_layer_consume(layer, query);
|
|
|
|
ok(layer->state == KNOT_STATE_PRODUCE ||
|
|
layer->state == KNOT_STATE_FAIL, "ns: process %s query", name);
|
|
|
|
/* Create answer. */
|
|
knot_layer_produce(layer, answer);
|
|
if (layer->state == KNOT_STATE_FAIL) {
|
|
/* Allow 1 generic error response. */
|
|
knot_layer_produce(layer, answer);
|
|
}
|
|
|
|
ok(layer->state == KNOT_STATE_DONE, "ns: answer %s query", name);
|
|
|
|
/* Check answer. */
|
|
answer_sanity_check(query->wire, answer->wire, answer->size, expected_rcode, name);
|
|
|
|
knot_pkt_free(answer);
|
|
}
|
|
|
|
/* \internal Helpers */
|
|
#define WIRE_COPY(dst, dst_len, src, src_len) \
|
|
memcpy(dst, src, src_len); \
|
|
dst_len = src_len;
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
plan_lazy();
|
|
|
|
knot_mm_t mm;
|
|
mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);
|
|
|
|
/* Create processing context. */
|
|
knot_layer_t proc;
|
|
memset(&proc, 0, sizeof(knot_layer_t));
|
|
knot_layer_init(&proc, &mm, process_query_layer());
|
|
|
|
/* Create temporary storage directory. */
|
|
char *temp_dir = test_mkdtemp();
|
|
ok(temp_dir != NULL, "make temporary directory");
|
|
|
|
/* Create fake server environment. */
|
|
server_t server;
|
|
int ret = create_fake_server(&server, proc.mm, temp_dir);
|
|
is_int(KNOT_EOK, ret, "ns: fake server initialization");
|
|
if (ret != KNOT_EOK) {
|
|
goto fatal;
|
|
}
|
|
|
|
zone_t *zone = knot_zonedb_find(server.zone_db, ROOT_DNAME);
|
|
|
|
/* Prepare. */
|
|
knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, proc.mm);
|
|
|
|
/* Create query processing parameter. */
|
|
struct sockaddr_storage ss;
|
|
memset(&ss, 0, sizeof(struct sockaddr_storage));
|
|
sockaddr_set(&ss, AF_INET, "127.0.0.1", 53);
|
|
knotd_qdata_params_t params = {
|
|
.proto = KNOTD_QUERY_PROTO_TCP,
|
|
.remote = &ss,
|
|
.server = &server
|
|
};
|
|
|
|
/* Query processor (CH zone) */
|
|
knot_layer_begin(&proc, ¶ms);
|
|
knot_pkt_clear(query);
|
|
knot_pkt_put_question(query, IDSERVER_DNAME, KNOT_CLASS_CH, KNOT_RRTYPE_TXT);
|
|
exec_query(&proc, "CH TXT", query, KNOT_RCODE_NOERROR);
|
|
|
|
/* Query processor (valid input). */
|
|
knot_layer_reset(&proc);
|
|
knot_pkt_clear(query);
|
|
knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
|
|
exec_query(&proc, "IN/root", query, KNOT_RCODE_NOERROR);
|
|
|
|
/* Query processor (-1 bytes, not enough data). */
|
|
knot_layer_reset(&proc);
|
|
query->size -= 1;
|
|
exec_query(&proc, "IN/few-data", query, KNOT_RCODE_FORMERR);
|
|
query->size += 1;
|
|
|
|
/* Query processor (+1 bytes trailing). */
|
|
knot_layer_reset(&proc);
|
|
query->wire[query->size] = '\1'; /* Initialize the "garbage" value. */
|
|
query->size += 1;
|
|
exec_query(&proc, "IN/trail-garbage", query, KNOT_RCODE_FORMERR);
|
|
query->size -= 1;
|
|
|
|
/* Forge NOTIFY query from SOA query. */
|
|
knot_layer_reset(&proc);
|
|
knot_wire_set_opcode(query->wire, KNOT_OPCODE_NOTIFY);
|
|
exec_query(&proc, "IN/notify", query, KNOT_RCODE_NOTAUTH);
|
|
|
|
/* Forge AXFR query. */
|
|
knot_layer_reset(&proc);
|
|
knot_pkt_clear(query);
|
|
knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_AXFR);
|
|
exec_query(&proc, "IN/axfr", query, KNOT_RCODE_NOTAUTH);
|
|
|
|
/* Forge IXFR query (well formed). */
|
|
knot_layer_reset(&proc);
|
|
knot_pkt_clear(query);
|
|
knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_IXFR);
|
|
/* Append SOA RR. */
|
|
knot_rrset_t soa_rr = node_rrset(zone->contents->apex, KNOT_RRTYPE_SOA);
|
|
knot_pkt_begin(query, KNOT_AUTHORITY);
|
|
knot_pkt_put(query, KNOT_COMPR_HINT_NONE, &soa_rr, 0);
|
|
exec_query(&proc, "IN/ixfr", query, KNOT_RCODE_NOTAUTH);
|
|
|
|
/* \note Tests below are not possible without proper zone and zone data. */
|
|
/* #189 Process UPDATE query. */
|
|
/* #189 Process AXFR client. */
|
|
/* #189 Process IXFR client. */
|
|
|
|
/* Query processor (smaller than DNS header, ignore). */
|
|
knot_layer_reset(&proc);
|
|
knot_pkt_clear(query);
|
|
knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
|
|
size_t orig_query_size = query->size;
|
|
query->size = KNOT_WIRE_HEADER_SIZE - 1;
|
|
knot_layer_consume(&proc, query);
|
|
ok(proc.state == KNOT_STATE_NOOP, "ns: IN/less-than-header query ignored");
|
|
query->size = orig_query_size;
|
|
|
|
/* Query processor (response, ignore). */
|
|
knot_layer_reset(&proc);
|
|
knot_wire_set_qr(query->wire);
|
|
knot_layer_consume(&proc, query);
|
|
ok(proc.state == KNOT_STATE_NOOP, "ns: IN/less-than-header query ignored");
|
|
|
|
/* Finish. */
|
|
knot_layer_finish(&proc);
|
|
ok(proc.state == KNOT_STATE_NOOP, "ns: processing end" );
|
|
|
|
fatal:
|
|
/* Cleanup. */
|
|
mp_delete((struct mempool *)mm.ctx);
|
|
server_deinit(&server);
|
|
conf_free(conf());
|
|
test_rm_rf(temp_dir);
|
|
free(temp_dir);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#undef WIRE_COPY
|