test_process_query.c 6.09 KB
Newer Older
1
/*  Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

    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 <http://www.gnu.org/licenses/>.
 */

17
#include <assert.h>
18
#include <tap/basic.h>
19 20
#include <string.h>
#include <stdlib.h>
21

22
#include "libknot/descriptor.h"
23
#include "libknot/packet/wire.h"
24
#include "knot/nameserver/process_query.h"
25
#include "test_server.h"
26
#include "contrib/sockaddr.h"
27
#include "contrib/ucw/mempool.h"
28 29 30 31 32 33 34

/* 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);
Marek Vavrusa's avatar
Marek Vavrusa committed
35 36 37 38 39 40 41
	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");
	}
42
}
43

44
/* Resolve query and check answer for sanity (2 TAP tests). */
45
static void exec_query(knot_layer_t *layer, const char *name,
46
                       knot_pkt_t *query,
47 48
                       uint8_t expected_rcode)
{
49 50
	knot_pkt_t *answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
	assert(answer);
51 52

	/* Input packet. */
53
	knot_pkt_parse(query, 0);
54
	knot_layer_consume(layer, query);
55

56 57
	ok(layer->state == KNOT_STATE_PRODUCE ||
	   layer->state == KNOT_STATE_FAIL, "ns: process %s query", name);
58 59

	/* Create answer. */
60 61
	knot_layer_produce(layer, answer);
	if (layer->state == KNOT_STATE_FAIL) {
62
		/* Allow 1 generic error response. */
63
		knot_layer_produce(layer, answer);
64 65
	}

66
	ok(layer->state == KNOT_STATE_DONE, "ns: answer %s query", name);
67 68

	/* Check answer. */
69 70
	answer_sanity_check(query->wire, answer->wire, answer->size, expected_rcode, name);

71
	knot_pkt_free(answer);
72 73 74 75 76 77 78 79 80
}

/* \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[])
{
81
	plan_lazy();
82

83
	knot_mm_t mm;
84
	mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);
85

86
	/* Create processing context. */
87 88
	knot_layer_t proc;
	memset(&proc, 0, sizeof(knot_layer_t));
89
	knot_layer_init(&proc, &mm, process_query_layer());
90

91
	/* Create fake server environment. */
92
	server_t server;
93
	int ret = create_fake_server(&server, proc.mm);
94
	is_int(KNOT_EOK, ret, "ns: fake server initialization");
95

96
	zone_t *zone = knot_zonedb_find(server.zone_db, ROOT_DNAME);
97

98
	/* Prepare. */
99
	knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, proc.mm);
100

Marek Vavrusa's avatar
Marek Vavrusa committed
101
	/* Create query processing parameter. */
102 103 104
	struct sockaddr_storage ss;
	memset(&ss, 0, sizeof(struct sockaddr_storage));
	sockaddr_set(&ss, AF_INET, "127.0.0.1", 53);
105 106 107 108
	knotd_qdata_params_t params = {
		.remote = &ss,
		.server = &server
	};
109

110
	/* Query processor (CH zone) */
111
	knot_layer_begin(&proc, &params);
112
	knot_pkt_clear(query);
113
	knot_pkt_put_question(query, IDSERVER_DNAME, KNOT_CLASS_CH, KNOT_RRTYPE_TXT);
114
	exec_query(&proc, "CH TXT", query, KNOT_RCODE_NOERROR);
115

116
	/* Query processor (valid input). */
117
	knot_layer_reset(&proc);
118 119
	knot_pkt_clear(query);
	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
120
	exec_query(&proc, "IN/root", query, KNOT_RCODE_NOERROR);
121

122
	/* Query processor (-1 bytes, not enough data). */
123 124 125 126
	knot_layer_reset(&proc);
	query->size -= 1;
	exec_query(&proc, "IN/few-data", query, KNOT_RCODE_FORMERR);
	query->size += 1;
127

128
	/* Query processor (+1 bytes trailing). */
129
	knot_layer_reset(&proc);
130
	query->wire[query->size] = '\1'; /* Initialize the "garbage" value. */
131 132 133
	query->size += 1;
	exec_query(&proc, "IN/trail-garbage", query, KNOT_RCODE_FORMERR);
	query->size -= 1;
134

135
	/* Forge NOTIFY query from SOA query. */
136
	knot_layer_reset(&proc);
137
	knot_wire_set_opcode(query->wire, KNOT_OPCODE_NOTIFY);
138
	exec_query(&proc, "IN/notify", query, KNOT_RCODE_NOTAUTH);
139

140
	/* Forge AXFR query. */
141
	knot_layer_reset(&proc);
142 143
	knot_pkt_clear(query);
	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_AXFR);
144
	exec_query(&proc, "IN/axfr", query, KNOT_RCODE_NOTAUTH);
145

146
	/* Forge IXFR query (well formed). */
147 148 149
	knot_layer_reset(&proc);
	knot_pkt_clear(query);
	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_IXFR);
150
	/* Append SOA RR. */
151
	knot_rrset_t soa_rr = node_rrset(zone->contents->apex, KNOT_RRTYPE_SOA);
152
	knot_pkt_begin(query, KNOT_AUTHORITY);
153
	knot_pkt_put(query, KNOT_COMPR_HINT_NONE, &soa_rr, 0);
154
	exec_query(&proc, "IN/ixfr", query, KNOT_RCODE_NOTAUTH);
155

156
	/* \note Tests below are not possible without proper zone and zone data. */
157 158 159
	/* #189 Process UPDATE query. */
	/* #189 Process AXFR client. */
	/* #189 Process IXFR client. */
160

161
	/* Query processor (smaller than DNS header, ignore). */
162
	knot_layer_reset(&proc);
163 164
	knot_pkt_clear(query);
	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
165 166
	size_t orig_query_size = query->size;
	query->size = KNOT_WIRE_HEADER_SIZE - 1;
167 168
	knot_layer_consume(&proc, query);
	ok(proc.state == KNOT_STATE_NOOP, "ns: IN/less-than-header query ignored");
169
	query->size = orig_query_size;
170

171
	/* Query processor (response, ignore). */
172
	knot_layer_reset(&proc);
173
	knot_wire_set_qr(query->wire);
174 175
	knot_layer_consume(&proc, query);
	ok(proc.state == KNOT_STATE_NOOP, "ns: IN/less-than-header query ignored");
176

177
	/* Finish. */
178 179
	knot_layer_finish(&proc);
	ok(proc.state == KNOT_STATE_NOOP, "ns: processing end" );
180

181
	/* Cleanup. */
182
	mp_delete((struct mempool *)mm.ctx);
183
	server_deinit(&server);
184
	conf_free(conf());
185 186 187

	return 0;
}
188

189
#undef WIRE_COPY