resolve.c 10.5 KB
Newer Older
Marek Vavruša's avatar
Marek Vavruša committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*  Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
 */

17
#include <stdio.h>
Marek Vavruša's avatar
Marek Vavruša committed
18
#include <sys/fcntl.h>
19

Marek Vavruša's avatar
Marek Vavruša committed
20
#include <libknot/internal/mempool.h>
21
#include <libknot/rrtype/rdname.h>
22
#include <libknot/descriptor.h>
Marek Vavruša's avatar
Marek Vavruša committed
23
#include <libknot/internal/net.h>
Marek Vavruša's avatar
Marek Vavruša committed
24

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

Marek Vavruša's avatar
Marek Vavruša committed
30
#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(rplan), "resl",  fmt)
31

32
/** Invalidate current NS/addr pair. */
33
static int invalidate_ns(struct kr_rplan *rplan, struct kr_query *qry)
Marek Vavruša's avatar
Marek Vavruša committed
34
{
35 36 37 38 39
	uint8_t *addr = kr_nsrep_inaddr(qry->ns.addr);
	size_t addr_len = kr_nsrep_inaddr_len(qry->ns.addr);
	knot_rdata_t rdata[knot_rdata_array_size(addr_len)];
	knot_rdata_init(rdata, addr_len, addr, 0);
	return kr_zonecut_del(&qry->zone_cut, qry->ns.name, rdata);
40
}
41

Marek Vavruša's avatar
Marek Vavruša committed
42
static int ns_resolve_addr(struct kr_query *qry, struct kr_request *param)
43
{
Marek Vavruša's avatar
Marek Vavruša committed
44 45 46 47
	struct kr_rplan *rplan = &param->rplan;
	if (kr_rplan_satisfies(qry, qry->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_A) ||
	    kr_rplan_satisfies(qry, qry->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA) ||
	    qry->flags & QUERY_AWAIT_ADDR) {
48
		DEBUG_MSG("=> dependency loop, bailing out\n");
Marek Vavruša's avatar
Marek Vavruša committed
49 50
		kr_rplan_pop(rplan, qry);
		return KNOT_STATE_PRODUCE;
51 52
	}

Marek Vavruša's avatar
Marek Vavruša committed
53 54 55 56
	(void) kr_rplan_push(rplan, qry, qry->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA);
	(void) kr_rplan_push(rplan, qry, qry->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_A);
	qry->flags |= QUERY_AWAIT_ADDR;
	return KNOT_STATE_PRODUCE;
57 58
}

Marek Vavruša's avatar
Marek Vavruša committed
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
static void prepare_layers(struct kr_request *param)
{
	struct kr_context *ctx = param->ctx;
	for (size_t i = 0; i < ctx->modules->len; ++i) {
		struct kr_module *mod = &ctx->modules->at[i];
		if (mod->layer) {
			knot_overlay_add(&param->overlay, mod->layer(), param);
		}
	}
}

static int connected(struct sockaddr *addr, int proto, struct timeval *timeout)
{
	unsigned flags = (proto == SOCK_STREAM) ? O_NONBLOCK : 0;
	int fd = net_connected_socket(proto, (struct sockaddr_storage *)addr, NULL, flags);
	if (fd < 0) {
		return kr_error(ECONNREFUSED);
	}

	/* Workaround for timeout, as we have no control over
	 * connect() time limit in blocking mode. */
	if (proto == SOCK_STREAM) {
		fd_set set;
		FD_ZERO(&set);
		FD_SET(fd, &set);
		int ret = select(fd + 1, NULL, &set, NULL, timeout);
		if (ret == 0) {
			close(fd);
			return kr_error(ETIMEDOUT);
		}
		if (ret < 0) {
			close(fd);
			return kr_error(ECONNREFUSED);
		}
		fcntl(fd, F_SETFL, 0);
	}

	return fd;
}

static int sendrecv(struct sockaddr *addr, int proto, const knot_pkt_t *query, knot_pkt_t *resp)
100
{
101
	struct timeval timeout = { KR_CONN_RTT_MAX / 1000, 0 };
Marek Vavruša's avatar
Marek Vavruša committed
102 103 104 105
	auto_close int fd = connected(addr, proto, &timeout);
	if (fd < 0) {
		return fd;
	}
Marek Vavruša's avatar
Marek Vavruša committed
106

Marek Vavruša's avatar
Marek Vavruša committed
107 108 109 110 111 112 113 114 115 116
	/* Send packet */
	int ret = 0;
	if (proto == SOCK_STREAM) {
		ret = tcp_send_msg(fd, query->wire, query->size, &timeout);
	} else {
		ret = udp_send_msg(fd, query->wire, query->size, NULL);
	}
	if (ret != query->size) {
		return kr_error(EIO);
	}
117

Marek Vavruša's avatar
Marek Vavruša committed
118 119 120
	/* Receive it */
	if (proto == SOCK_STREAM) {
		ret = tcp_recv_msg(fd, resp->wire, resp->max_size, &timeout);
121
	} else {
Marek Vavruša's avatar
Marek Vavruša committed
122 123 124 125
		ret = udp_recv_msg(fd, resp->wire, resp->max_size, &timeout);
	}
	if (ret <= 0) {
		return kr_error(ETIMEDOUT);
126
	}
127

Marek Vavruša's avatar
Marek Vavruša committed
128 129 130 131 132
	/* Parse and return */
	resp->size = ret;
	if (knot_pkt_parse(resp, 0) != 0) {
		return kr_error(EBADMSG);
	}
Marek Vavruša's avatar
Marek Vavruša committed
133

Marek Vavruša's avatar
Marek Vavruša committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
	return kr_ok();
}

int kr_resolve(struct kr_context* ctx, knot_pkt_t *answer,
               const knot_dname_t *qname, uint16_t qclass, uint16_t qtype)
{
	if (ctx == NULL || answer == NULL || qname == NULL) {
		return kr_error(EINVAL);
	}

	/* Create memory pool */
	mm_ctx_t pool;
	mm_ctx_mempool(&pool, MM_DEFAULT_BLKSIZE);
	knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MIN_PKTSIZE, &pool);
	knot_pkt_t *resp = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, &pool);
	if (!query || !resp) {
		mp_delete(pool.ctx);
		return kr_error(ENOMEM);
	}

	/* Initialize context. */
	struct kr_request request;
	request.pool = pool;
	kr_resolve_begin(&request, ctx, answer);
#ifndef NDEBUG
	struct kr_rplan *rplan = &request.rplan; /* for DEBUG_MSG */
#endif

	/* Resolve query, iteratively */
	int proto = 0;
	struct sockaddr *addr = NULL;
	unsigned iter_count = 0;
	int state = kr_resolve_query(&request, qname, qclass, qtype);
	while (state == KNOT_STATE_PRODUCE) {
		/* Hardlimit on iterative queries */
169
		if (++iter_count > KR_ITER_LIMIT) {
170
			DEBUG_MSG("iteration limit %d reached\n", KR_ITER_LIMIT);
Marek Vavruša's avatar
Marek Vavruša committed
171 172
			state = KNOT_STATE_FAIL;
			break;
173
		}
Marek Vavruša's avatar
Marek Vavruša committed
174 175 176 177 178 179 180 181 182 183 184
		/* Produce next query or finish */
		state = kr_resolve_produce(&request, &addr, &proto, query);
		while (state == KNOT_STATE_CONSUME) {
			/* Get answer from nameserver and consume it */
			int ret = sendrecv(addr, proto, query, resp);
			if (ret != 0) {
				DEBUG_MSG("sendrecv: %s\n", kr_strerror(ret));
				resp->size = 0;
			}
			state = kr_resolve_consume(&request, resp);
			knot_pkt_clear(resp);
185
		}
Marek Vavruša's avatar
Marek Vavruša committed
186
		knot_pkt_clear(query);
187 188
	}

Marek Vavruša's avatar
Marek Vavruša committed
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
	/* Cleanup */
	kr_resolve_finish(&request, state);
	mp_delete(pool.ctx);
	return state == KNOT_STATE_DONE ? 0 : kr_error(EIO);
}


int kr_resolve_begin(struct kr_request *request, struct kr_context *ctx, knot_pkt_t *answer)
{
	/* Initialize request */
	kr_rplan_init(&request->rplan, ctx, &request->pool);
	knot_overlay_init(&request->overlay, &request->pool);
	request->ctx = ctx;
	request->answer = answer;
	prepare_layers(request);
204

Marek Vavruša's avatar
Marek Vavruša committed
205 206
	/* Expect first query */
	return KNOT_STATE_CONSUME;
207 208
}

Marek Vavruša's avatar
Marek Vavruša committed
209
int kr_resolve_query(struct kr_request *request, const knot_dname_t *qname, uint16_t qclass, uint16_t qtype)
210
{
Marek Vavruša's avatar
Marek Vavruša committed
211 212 213 214
	struct kr_rplan *rplan = &request->rplan;
	struct kr_query *qry = kr_rplan_push(rplan, NULL, qname, qclass, qtype);
	if (!qry) {
		return KNOT_STATE_FAIL;
215
	}
Marek Vavruša's avatar
Marek Vavruša committed
216

217
	/* Initialize answer packet */
Marek Vavruša's avatar
Marek Vavruša committed
218 219 220 221 222 223 224 225
	knot_pkt_t *answer = request->answer;
	knot_wire_set_qr(answer->wire);
	knot_wire_clear_aa(answer->wire);
	knot_wire_set_ra(answer->wire);
	knot_wire_set_rcode(answer->wire, KNOT_RCODE_NOERROR);

	/* Expect answer */
	return KNOT_STATE_PRODUCE;
226 227
}

Marek Vavruša's avatar
Marek Vavruša committed
228
int kr_resolve_consume(struct kr_request *request, knot_pkt_t *packet)
229
{
Marek Vavruša's avatar
Marek Vavruša committed
230 231
	struct kr_rplan *rplan = &request->rplan;
	struct kr_query *qry = kr_rplan_current(rplan);
232

Marek Vavruša's avatar
Marek Vavruša committed
233
	/* Empty resolution plan, push packet as the new query */
234 235 236 237
	if (packet && kr_rplan_empty(rplan)) {
		if (!knot_wire_get_rd(packet->wire)) {
			return KNOT_STATE_FAIL;
		}
238 239 240
		if (knot_pkt_init_response(request->answer, packet) != 0) {
			return KNOT_STATE_FAIL;
		}
Marek Vavruša's avatar
Marek Vavruša committed
241 242 243 244 245 246 247 248
		const knot_dname_t *qname = knot_pkt_qname(packet);
		uint16_t qclass = knot_pkt_qclass(packet);
		uint16_t qtype = knot_pkt_qtype(packet);
		return kr_resolve_query(request, qname, qclass, qtype);
	}

	/* Different processing for network error */
	int state = KNOT_STATE_FAIL;
249
	if (!packet || packet->size == 0) {
Marek Vavruša's avatar
Marek Vavruša committed
250 251 252 253 254 255
		/* Network error, retry over TCP. */
		if (!(qry->flags & QUERY_TCP)) {
			/** @todo This should just penalize UDP and elect next best. */
			DEBUG_MSG("=> ns unreachable, retrying over TCP\n");
			qry->flags |= QUERY_TCP;
			return KNOT_STATE_CONSUME; /* Try again */
256
		}
Marek Vavruša's avatar
Marek Vavruša committed
257 258
	} else {
		state = knot_overlay_consume(&request->overlay, packet);
259 260
	}

Marek Vavruša's avatar
Marek Vavruša committed
261 262 263 264 265
	/* Resolution failed, invalidate current NS and reset to UDP. */
	if (state == KNOT_STATE_FAIL) {
		DEBUG_MSG("=> resolution failed, invalidating\n");
		if (invalidate_ns(rplan, qry) == 0) {
			qry->flags &= ~QUERY_TCP;
266 267 268
		}
	}

Marek Vavruša's avatar
Marek Vavruša committed
269 270 271 272 273 274 275 276
	/* Pop query if resolved. */
	if (qry->flags & QUERY_RESOLVED) {
		kr_rplan_pop(rplan, qry);

	}

	knot_overlay_reset(&request->overlay);
	return kr_rplan_empty(&request->rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE;
277 278
}

279
int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *type, knot_pkt_t *packet)
280
{
Marek Vavruša's avatar
Marek Vavruša committed
281 282 283 284 285 286
	struct kr_rplan *rplan = &request->rplan;
	struct kr_query *qry = kr_rplan_current(rplan);
	
	/* No query left for resolution */
	if (kr_rplan_empty(rplan)) {
		return KNOT_STATE_FAIL;
287 288
	}

Marek Vavruša's avatar
Marek Vavruša committed
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
#ifndef NDEBUG
	char name_str[KNOT_DNAME_MAXLEN], type_str[16];
	knot_dname_to_str(name_str, qry->sname, sizeof(name_str));
	knot_rrtype_to_string(qry->stype, type_str, sizeof(type_str));
	DEBUG_MSG("query '%s %s'\n", type_str, name_str);
#endif

	/* Resolve current query and produce dependent or finish */
	int state = knot_overlay_produce(&request->overlay, packet);
	switch(state) {
	case KNOT_STATE_FAIL: return state; break;
	case KNOT_STATE_CONSUME: break;
	default: /* Current query is done */
		knot_overlay_reset(&request->overlay);
		kr_rplan_pop(rplan, qry);
		return kr_rplan_empty(rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE;
	}

	/* Elect best nameserver candidate */
	kr_nsrep_elect(&qry->ns, &qry->zone_cut.nsset);
	if (qry->ns.score < KR_NS_VALID) {
		DEBUG_MSG("=> no valid NS left\n");
		knot_overlay_reset(&request->overlay);
		kr_rplan_pop(rplan, qry);
		return KNOT_STATE_PRODUCE;
314
	} else {
Marek Vavruša's avatar
Marek Vavruša committed
315 316 317 318 319
		if (qry->ns.addr.ip.sa_family == AF_UNSPEC) {
			DEBUG_MSG("=> ns missing A/AAAA, fetching\n");
			knot_overlay_reset(&request->overlay);
			return ns_resolve_addr(qry, request);
		}
320
	}
321

Marek Vavruša's avatar
Marek Vavruša committed
322 323 324 325 326 327 328 329 330 331 332
#ifndef NDEBUG
	char qname_str[KNOT_DNAME_MAXLEN], zonecut_str[KNOT_DNAME_MAXLEN], ns_str[SOCKADDR_STRLEN];
	knot_dname_to_str(qname_str, knot_pkt_qname(packet), sizeof(qname_str));
	struct sockaddr *addr = &qry->ns.addr.ip;
	inet_ntop(addr->sa_family, kr_nsrep_inaddr(qry->ns.addr), ns_str, sizeof(ns_str));
	knot_dname_to_str(zonecut_str, qry->zone_cut.name, sizeof(zonecut_str));
	DEBUG_MSG("=> querying: '%s' zone cut: '%s' m12n: '%s'\n", ns_str, zonecut_str, qname_str);
#endif

	/* Issue dependent query to this address */
	*dst = &qry->ns.addr.ip;
333
	*type = (qry->flags & QUERY_TCP) ? SOCK_STREAM : SOCK_DGRAM;
Marek Vavruša's avatar
Marek Vavruša committed
334 335 336 337 338
	return state;
}

int kr_resolve_finish(struct kr_request *request, int state)
{
339
#ifndef NDEBUG
Marek Vavruša's avatar
Marek Vavruša committed
340 341
	struct kr_rplan *rplan = &request->rplan;
	DEBUG_MSG("finished: %d, mempool: %zu B\n", state, (size_t) mp_total_size(request->pool.ctx));
342 343 344
#endif
	/* Error during procesing, internal failure */
	if (state != KNOT_STATE_DONE) {
Marek Vavruša's avatar
Marek Vavruša committed
345 346 347 348
		knot_pkt_t *answer = request->answer;
		if (knot_wire_get_rcode(answer->wire) == KNOT_RCODE_NOERROR) {
			knot_wire_set_rcode(answer->wire, KNOT_RCODE_SERVFAIL);
		}
349 350
	}

Marek Vavruša's avatar
Marek Vavruša committed
351
	/* Clean up. */
Marek Vavruša's avatar
Marek Vavruša committed
352
	knot_overlay_deinit(&request->overlay);
353
	request->overlay.state = KNOT_STATE_NOOP;
Marek Vavruša's avatar
Marek Vavruša committed
354 355
	kr_rplan_deinit(&request->rplan);
	return KNOT_STATE_DONE;
356
}