iterate.c 8.98 KB
Newer Older
Marek Vavruša's avatar
Marek Vavruša committed
1
/*  Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2

Marek Vavruša's avatar
Marek Vavruša committed
3 4 5 6
    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.
7

Marek Vavruša's avatar
Marek Vavruša committed
8 9 10 11
    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.
12

Marek Vavruša's avatar
Marek Vavruša committed
13 14 15
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
16

17 18
#include <sys/time.h>

19 20
#include <libknot/descriptor.h>
#include <libknot/rrtype/rdname.h>
21 22
#include <libknot/processing/requestor.h>
#include <libknot/dnssec/random.h>
23 24

#include "lib/layer/iterate.h"
25
#include "lib/resolve.h"
26
#include "lib/rplan.h"
27
#include "lib/utils.h"
28

29 30
#define DEBUG_MSG(fmt, ...) fprintf(stderr, "[qiter] " fmt, ## __VA_ARGS__)

31 32
/*! \brief Fetch NS record address from additionals. */
static int glue_ns_addr(const knot_pkt_t *pkt, struct sockaddr_storage *addr, const knot_dname_t *name, uint16_t type)
33 34 35
{
	const knot_pktsection_t *ar = knot_pkt_section(pkt, KNOT_ADDITIONAL);
	for (unsigned i = 0; i < ar->count; ++i) {
36 37 38
		const knot_rrset_t *rr = knot_pkt_rr(ar, i);
		if (rr->type == type && knot_dname_is_equal(name, rr->owner)) {
			return kr_rrset_to_addr(addr, rr);
39
		}
40 41
	}

42
	return KNOT_ENOENT;
43 44
}

45
static int set_zone_cut(struct kr_rplan *rplan, knot_pkt_t *pkt, const knot_rrset_t *rr)
46
{
47 48 49 50 51 52 53 54 55 56 57 58 59
	static const uint16_t type_list[] = { KNOT_RRTYPE_A, KNOT_RRTYPE_AAAA };

	/* Set zone cut to given name server. */
	const knot_dname_t *ns_name = knot_ns_name(&rr->rrs, 0);
	kr_set_zone_cut(&rplan->zone_cut, rr->owner, ns_name);

	/* Check if we can find a valid address in glue / cache */
	for (unsigned i = 0; i < sizeof(type_list)/sizeof(uint16_t); ++i) {

		/* Find address in the additionals (optional). */
		int ret = glue_ns_addr(pkt, &rplan->zone_cut.addr, ns_name, type_list[i]);
		if (ret == KNOT_EOK) {
			break;
60
		}
61 62
	}

63 64 65 66
	/* Query for address records to get it in the cache. */
	(void) kr_rplan_push(rplan, rplan->zone_cut.ns, KNOT_CLASS_IN, KNOT_RRTYPE_A);
	(void) kr_rplan_push(rplan, rplan->zone_cut.ns, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA);

67
	return KNOT_EOK;
68 69
}

70
static int inspect_ns(const knot_rrset_t *ns_rr, knot_pkt_t *pkt, struct kr_layer_param *param)
71
{
72 73
	/* Authority MUST be at/below the authority of the nameserver, otherwise
	 * possible cache injection attempt. */
74 75
	if (!knot_dname_in(param->rplan->zone_cut.name, ns_rr->owner)) {
		DEBUG_MSG("NS in query outside of its authority => rejecting\n");
76 77
		return KNOT_EMALF;
	}
78

79
	return KNOT_EOK;
80 81
}

82
static void follow_cname_chain(const knot_dname_t **cname, const knot_rrset_t *rr,
83
                               struct kr_query *cur)
84 85
{
	/* Follow chain from SNAME. */
86
	if (knot_dname_is_equal(rr->owner, *cname)) {
87
		if (rr->type == KNOT_RRTYPE_CNAME) {
88
			*cname = knot_cname_name(&rr->rrs);
89 90
		} else {
			/* Terminate CNAME chain. */
91
			*cname = cur->sname;
92
		}
93 94 95
	}
}

96 97
/*! \brief Result updates the original query response. */
static int update_answer(knot_pkt_t *answer, const knot_rrset_t *rr)
98
{
99
	knot_rrset_t *rr_copy = knot_rrset_copy(rr, &answer->mm);
100
	if (rr_copy == NULL) {
101
		return KNOT_ENOMEM;
102 103 104
	}

	/* Write copied RR to the result packet. */
105 106 107 108
	int ret = knot_pkt_put(answer, KNOT_COMPR_HINT_NONE, rr_copy, KNOT_PF_FREE);
	if (ret != KNOT_EOK) {
		knot_rrset_free(&rr_copy, &answer->mm);
		knot_wire_set_tc(answer->wire);
109 110 111
	}

	/* Free just the allocated container. */
112 113
	mm_free(&answer->mm, rr_copy);
	return KNOT_EOK;
114 115
}

116
static void update_zone_cut(struct kr_rplan *rplan, const knot_rrset_t *rr)
117
{
118 119 120 121
	if (rr->type == KNOT_RRTYPE_A || rr->type == KNOT_RRTYPE_AAAA) {
		if (knot_dname_is_equal(rplan->zone_cut.ns, rr->owner)) {
			kr_rrset_to_addr(&rplan->zone_cut.addr, rr);
		}
122 123 124
	}
}

125
static int resolve_referral(knot_pkt_t *pkt, struct kr_layer_param *param)
126
{
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
	bool have_first_ns = false;
	const knot_pktsection_t *ns = knot_pkt_section(pkt, KNOT_AUTHORITY);
	for (unsigned i = 0; i < ns->count; ++i) {
		const knot_rrset_t *rr = knot_pkt_rr(ns, i);
		if (rr->type != KNOT_RRTYPE_NS) {
			continue;
		}
		/* Check each nameserver. */
		if (inspect_ns(rr, pkt, param) != KNOT_EOK) {
			return KNOT_NS_PROC_FAIL;
		}
		/* Resolve the first nameserver address, rest will be cached. */
		if (!have_first_ns) {
			set_zone_cut(param->rplan, pkt, rr);
			have_first_ns = true;
		}
143 144
	}

145
	return KNOT_NS_PROC_DONE;
146 147
}

148
static int resolve_auth(knot_pkt_t *pkt, struct kr_layer_param *param)
149
{
150 151
	knot_pkt_t *answer = param->answer;
	struct kr_query *cur = kr_rplan_current(param->rplan);
152
	if (cur == NULL) {
153
		return KNOT_NS_PROC_FAIL;
154
	}
155

156 157
	/* Is relevant for original query? */
	bool update_orig_answer = (cur == kr_rplan_last(param->rplan));
158

159
	const knot_dname_t *cname = cur->sname;
160 161
	const knot_pktsection_t *an = knot_pkt_section(pkt, KNOT_ANSWER);
	for (unsigned i = 0; i < an->count; ++i) {
162
		const knot_rrset_t *rr = knot_pkt_rr(an, i);
163

164 165 166 167 168 169 170
		/* Update original answer or current zone cut. */
		if (update_orig_answer) {
			if (update_answer(answer, rr) != KNOT_EOK) {
				return KNOT_NS_PROC_FAIL;
			}
		} else {
			update_zone_cut(param->rplan, rr);
171
		}
172

173
		/* Check canonical name. */
174
		follow_cname_chain(&cname, rr, cur);
175 176 177
	}

	/* Follow canonical name as next SNAME. */
178
	if (cname != cur->sname) {
179
		struct kr_query *next = kr_rplan_push(param->rplan, cname,
180
		                                      cur->sclass, cur->stype);
181
		if (next == NULL) {
182
			return KNOT_NS_PROC_FAIL;
183
		}
184 185
	}

186 187 188 189
	/* Authoritative answer to original response => DONE. */
	if (update_orig_answer) {
		knot_wire_set_rcode(answer->wire, knot_wire_get_rcode(pkt->wire));
	}
190

191
	kr_rplan_pop(param->rplan, cur);
192
	return KNOT_NS_PROC_DONE;
193 194 195
}

/*! \brief Error handling, RFC1034 5.3.3, 4d. */
196
static int resolve_error(knot_pkt_t *pkt, struct kr_layer_param *param, int errcode)
197
{
198
	DEBUG_MSG("resolution error => %s\n", knot_strerror(errcode));
199
	return KNOT_NS_PROC_FAIL;
200 201 202
}

/*! \brief Answer is paired to query. */
203
static bool is_answer_to_query(const knot_pkt_t *answer, struct kr_rplan *rplan)
204
{
205
	struct kr_query *expect = kr_rplan_current(rplan);
206 207 208 209
	if (expect == NULL) {
		return -1;
	}

210
	return expect->id      == knot_wire_get_id(answer->wire) &&
211 212 213
	       expect->sclass  == knot_pkt_qclass(answer) &&
	       expect->stype   == knot_pkt_qtype(answer) &&
	       knot_dname_is_equal(expect->sname, knot_pkt_qname(answer));
214 215
}

216
/* State-less single resolution iteration step, not needed. */
217 218
static int reset(knot_layer_t *ctx)  { return KNOT_NS_PROC_FULL; }
static int finish(knot_layer_t *ctx) { return KNOT_NS_PROC_NOOP; }
219 220

/* Set resolution context and parameters. */
221
static int begin(knot_layer_t *ctx, void *module_param)
222 223
{
	ctx->data = module_param;
224 225 226 227 228 229 230
	return reset(ctx);
}

static int prepare_query(knot_layer_t *ctx, knot_pkt_t *pkt)
{
	assert(pkt && ctx);
	struct kr_layer_param *param = ctx->data;
231 232 233
	struct kr_query *cur = kr_rplan_current(param->rplan);
	if (cur == NULL || ctx->state == KNOT_NS_PROC_DONE) {
		return ctx->state;
234 235
	}

236
	/* Form a query for the authoritative. */
237
	knot_pkt_clear(pkt);
238
	int ret = knot_pkt_put_question(pkt, cur->sname, cur->sclass, cur->stype);
239
	if (ret != KNOT_EOK) {
240
		return KNOT_NS_PROC_FAIL;
241 242
	}

243 244
	cur->id = knot_random_uint16_t();
	knot_wire_set_id(pkt->wire, cur->id);
245

246
#ifndef NDEBUG
247 248 249
	char name_str[KNOT_DNAME_MAXLEN];
	knot_dname_to_str(name_str, param->rplan->zone_cut.ns, sizeof(name_str));
	DEBUG_MSG("sending query to %s\n", name_str);
250 251
#endif

252
	/* Query built, expect answer. */
253
	return KNOT_NS_PROC_MORE;
254 255
}

256 257 258 259
/*! \brief Resolve input query or continue resolution with followups.
 *
 *  This roughly corresponds to RFC1034, 5.3.3 4a-d.
 */
260
static int resolve(knot_layer_t *ctx, knot_pkt_t *pkt)
261 262
{
	assert(pkt && ctx);
263
	struct kr_layer_param *param = ctx->data;
264
	int state = ctx->state;
265

266 267
	/* Check for packet processing errors first. */
	if (pkt->parsed < pkt->size) {
268
		return resolve_error(pkt, param, KNOT_EMALF);
269
	}
270

271
	/* Is this the droid we're looking for? */
272 273 274
	if (!is_answer_to_query(pkt, param->rplan)) {
		DEBUG_MSG("ignoring mismatching response\n");
		return KNOT_NS_PROC_MORE;
275
	}
276

277 278 279 280 281 282 283 284
	/* TODO: classify response type
	 * 1. authoritative, noerror/nxdomain
	 *     cname chain => update current sname
	 *     qname is current ns => update ns address
	 *     qname is tail => original query
	 * 2. referral, update current zone cut
	 */

285 286 287 288 289 290
	/* Check response code. */
	switch(knot_wire_get_rcode(pkt->wire)) {
	case KNOT_RCODE_NOERROR:
	case KNOT_RCODE_NXDOMAIN:
		break; /* OK */
	default:
291
		return resolve_error(pkt, param, KNOT_ERROR);
292 293
	}

294 295 296 297 298
	/* Is the answer authoritative? */
	if (knot_wire_get_aa(pkt->wire)) {
		state = resolve_auth(pkt, param);
	} else {
		state = resolve_referral(pkt, param);
299
	}
300

301
	return state;
302 303
}

304
/*! \brief Module implementation. */
305
static const knot_layer_api_t LAYER_ITERATE_MODULE = {
306 307 308 309
	&begin,
	&reset,
	&finish,
	&resolve,
310 311
	&prepare_query,
	NULL
312 313
};

314
const knot_layer_api_t *layer_iterate_module(void)
315 316 317
{
	return &LAYER_ITERATE_MODULE;
}