update.c 13.6 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 <sys/socket.h>
18

19
#include "dnssec/random.h"
20
#include "knot/common/log.h"
21 22
#include "knot/dnssec/zone-events.h"
#include "knot/events/log.h"
23
#include "knot/query/capture.h"
Jan Včelák's avatar
Jan Včelák committed
24
#include "knot/query/requestor.h"
25 26
#include "knot/nameserver/update.h"
#include "knot/nameserver/internet.h"
27
#include "knot/nameserver/process_query.h"
28
#include "knot/nameserver/log.h"
29
#include "knot/updates/ddns.h"
30
#include "knot/updates/apply.h"
31
#include "knot/events/events.h"
32
#include "libknot/libknot.h"
33
#include "contrib/net.h"
Jan Včelák's avatar
Jan Včelák committed
34
#include "contrib/time.h"
35

Jan Včelák's avatar
Jan Včelák committed
36 37
#define UPDATE_LOG(priority, qdata, fmt...) \
	ns_log(priority, knot_pkt_qname(qdata->query), LOG_OPERATION_UPDATE, \
38
	       LOG_DIRECTION_IN, (struct sockaddr *)qdata->params->remote, fmt)
39

40
static void init_qdata_from_request(knotd_qdata_t *qdata,
41
                                    const zone_t *zone,
42
                                    struct knot_request *req,
43 44
                                    knotd_qdata_params_t *params,
                                    knotd_qdata_extra_t *extra)
45
{
46
	memset(qdata, 0, sizeof(*qdata));
47
	qdata->params = params;
48 49
	qdata->query = req->query;
	qdata->sign = req->sign;
50 51 52
	qdata->extra = extra;
	memset(extra, 0, sizeof(*extra));
	qdata->extra->zone = zone;
53
}
54

55
static int check_prereqs(struct knot_request *request,
56
                         const zone_t *zone, zone_update_t *update,
57
                         knotd_qdata_t *qdata)
58
{
59
	uint16_t rcode = KNOT_RCODE_NOERROR;
60
	int ret = ddns_process_prereqs(request->query, update, &rcode);
61
	if (ret != KNOT_EOK) {
Jan Včelák's avatar
Jan Včelák committed
62
		UPDATE_LOG(LOG_WARNING, qdata, "prerequisites not met (%s)",
63
		           knot_strerror(ret));
64 65
		assert(rcode != KNOT_RCODE_NOERROR);
		knot_wire_set_rcode(request->resp->wire, rcode);
66
		return ret;
67
	}
68

69 70
	return KNOT_EOK;
}
71

72
static int process_single_update(struct knot_request *request,
73
                                 const zone_t *zone, zone_update_t *update,
74
                                 knotd_qdata_t *qdata)
75 76 77
{
	uint16_t rcode = KNOT_RCODE_NOERROR;
	int ret = ddns_process_update(zone, request->query, update, &rcode);
78
	if (ret != KNOT_EOK) {
Jan Včelák's avatar
Jan Včelák committed
79
		UPDATE_LOG(LOG_WARNING, qdata, "failed to apply (%s)",
80
		           knot_strerror(ret));
81 82
		assert(rcode != KNOT_RCODE_NOERROR);
		knot_wire_set_rcode(request->resp->wire, rcode);
83
		return ret;
84 85
	}

86
	return KNOT_EOK;
87 88
}

89
static void set_rcodes(list_t *requests, const uint16_t rcode)
90
{
91 92 93
	ptrnode_t *node = NULL;
	WALK_LIST(node, *requests) {
		struct knot_request *req = node->d;
94
		if (knot_wire_get_rcode(req->resp->wire) == KNOT_RCODE_NOERROR) {
95
			knot_wire_set_rcode(req->resp->wire, rcode);
96 97 98 99
		}
	}
}

100
static void store_original_qname(knotd_qdata_t *qdata, const knot_pkt_t *pkt)
101
{
102
	memcpy(qdata->extra->orig_qname, knot_pkt_qname(pkt), pkt->qname_size);
103 104
}

105
static int process_bulk(zone_t *zone, list_t *requests, zone_update_t *up)
106 107
{
	// Walk all the requests and process.
108 109 110
	ptrnode_t *node = NULL;
	WALK_LIST(node, *requests) {
		struct knot_request *req = node->d;
111
		// Init qdata structure for logging (unique per-request).
112
		knotd_qdata_params_t params = {
113 114
			.remote = &req->remote
		};
115 116 117
		knotd_qdata_t qdata;
		knotd_qdata_extra_t extra;
		init_qdata_from_request(&qdata, zone, req, &params, &extra);
118

119 120
		store_original_qname(&qdata, req->query);
		process_query_qname_case_lower(req->query);
121

122
		int ret = check_prereqs(req, zone, up, &qdata);
123 124 125 126
		if (ret != KNOT_EOK) {
			// Skip updates with failed prereqs.
			continue;
		}
127

128
		ret = process_single_update(req, zone, up, &qdata);
129 130 131
		if (ret != KNOT_EOK) {
			return ret;
		}
132

133
		process_query_qname_case_restore(req->query, &qdata);
134
	}
135

136 137 138
	return KNOT_EOK;
}

139
static int process_normal(conf_t *conf, zone_t *zone, list_t *requests)
140
{
141
	assert(requests);
142

143 144
	// Init zone update structure
	zone_update_t up;
145
	int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL | UPDATE_SIGN);
146
	if (ret != KNOT_EOK) {
147
		set_rcodes(requests, KNOT_RCODE_SERVFAIL);
148 149
		return ret;
	}
150

151
	// Process all updates.
152
	ret = process_bulk(zone, requests, &up);
153
	if (ret != KNOT_EOK) {
154
		zone_update_clear(&up);
155
		set_rcodes(requests, KNOT_RCODE_SERVFAIL);
156 157
		return ret;
	}
158

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
	// Sign update.
	conf_val_t val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name);
	bool dnssec_enable = (up.flags & UPDATE_SIGN) && conf_bool(&val);
	if (dnssec_enable) {
		zone_sign_reschedule_t resch = { 0 };
		ret = knot_dnssec_sign_update(&up, &resch);
		if (ret != KNOT_EOK) {
			zone_update_clear(&up);
			set_rcodes(requests, KNOT_RCODE_SERVFAIL);
			return ret;
		}
		log_dnssec_next(zone->name, (time_t)resch.next_sign);
		zone_events_schedule_at(zone, ZONE_EVENT_DNSSEC, (time_t)resch.next_sign);
	}

174
	// Apply changes.
175
	ret = zone_update_commit(conf, &up);
176
	zone_update_clear(&up);
177
	if (ret != KNOT_EOK) {
178
		if (ret == KNOT_EZONESIZE) {
179 180 181 182
			set_rcodes(requests, KNOT_RCODE_REFUSED);
		} else {
			set_rcodes(requests, KNOT_RCODE_SERVFAIL);
		}
183 184
		return ret;
	}
185

186
	return KNOT_EOK;
187 188
}

189
static void process_requests(conf_t *conf, zone_t *zone, list_t *requests)
190
{
191 192
	assert(zone);
	assert(requests);
193 194

	/* Keep original state. */
Jan Včelák's avatar
Jan Včelák committed
195
	struct timespec t_start = time_now();
196 197 198
	const uint32_t old_serial = zone_contents_serial(zone->contents);

	/* Process authenticated packet. */
199
	int ret = process_normal(conf, zone, requests);
200
	if (ret != KNOT_EOK) {
Jan Kadlec's avatar
Jan Kadlec committed
201
		log_zone_error(zone->name, "DDNS, processing failed (%s)",
202
		               knot_strerror(ret));
203
		return;
204 205 206 207 208
	}

	/* Evaluate response. */
	const uint32_t new_serial = zone_contents_serial(zone->contents);
	if (new_serial == old_serial) {
209
		log_zone_info(zone->name, "DDNS, finished, no changes to the zone were made");
210
		return;
211 212
	}

Jan Včelák's avatar
Jan Včelák committed
213
	struct timespec t_end = time_now();
214 215
	log_zone_info(zone->name, "DDNS, update finished, serial %u -> %u, "
	              "%.02f seconds", old_serial, new_serial,
216
	              time_diff_ms(&t_start, &t_end) / 1000.0);
217

218
	zone_events_schedule_at(zone, ZONE_EVENT_NOTIFY, time(NULL) + 1);
219 220
}

221
static int remote_forward(conf_t *conf, struct knot_request *request, conf_remote_t *remote)
222 223
{
	/* Copy request and assign new ID. */
224 225
	knot_pkt_t *query = knot_pkt_new(NULL, request->query->max_size, NULL);
	int ret = knot_pkt_copy(query, request->query);
226
	if (ret != KNOT_EOK) {
227
		knot_pkt_free(query);
228
		return ret;
229
	}
230
	knot_wire_set_id(query->wire, dnssec_random_uint16_t());
231 232
	knot_tsig_append(query->wire, &query->size, query->max_size, query->tsig_rr);

233
	/* Prepare packet capture layer. */
234 235
	const knot_layer_api_t *capture = query_capture_api();
	struct capture_param capture_param = {
236 237 238
		.sink = request->resp
	};

239 240 241
	/* Create requestor instance. */
	struct knot_requestor re;
	ret = knot_requestor_init(&re, capture, &capture_param, NULL);
242
	if (ret != KNOT_EOK) {
243
		knot_pkt_free(query);
244 245 246 247 248 249
		return ret;
	}

	/* Create a request. */
	const struct sockaddr *dst = (const struct sockaddr *)&remote->addr;
	const struct sockaddr *src = (const struct sockaddr *)&remote->via;
Jan Včelák's avatar
Jan Včelák committed
250
	struct knot_request *req = knot_request_make(re.mm, dst, src, query, NULL, 0);
251 252
	if (req == NULL) {
		knot_requestor_clear(&re);
253
		knot_pkt_free(query);
254 255 256
		return KNOT_ENOMEM;
	}

257
	/* Execute the request. */
Filip Siroky's avatar
Filip Siroky committed
258
	int timeout = 1000 * conf->cache.srv_tcp_reply_timeout;
259
	ret = knot_requestor_exec(&re, req, timeout);
260

261
	knot_request_free(req, re.mm);
262 263 264 265 266
	knot_requestor_clear(&re);

	return ret;
}

267
static void forward_request(conf_t *conf, zone_t *zone, struct knot_request *request)
268
{
269
	/* Read the ddns master or the first master. */
270
	conf_val_t remote = conf_zone_get(conf, C_DDNS_MASTER, zone->name);
271
	if (remote.code != KNOT_EOK) {
272
		remote = conf_zone_get(conf, C_MASTER, zone->name);
273 274
	}

275
	/* Get the number of remote addresses. */
276
	conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, &remote);
277
	size_t addr_count = conf_val_count(&addr);
278
	assert(addr_count > 0);
279 280

	/* Try all remote addresses to forward the request to. */
281
	int ret = KNOT_EOK;
282
	for (size_t i = 0; i < addr_count; i++) {
283
		conf_remote_t master = conf_remote(conf, &remote, i);
284

285
		ret = remote_forward(conf, request, &master);
286 287 288 289
		if (ret == KNOT_EOK) {
			break;
		}
	}
290 291

	/* Restore message ID and TSIG. */
292 293 294
	knot_wire_set_id(request->resp->wire, knot_wire_get_id(request->query->wire));
	knot_tsig_append(request->resp->wire, &request->resp->size,
	                 request->resp->max_size, request->resp->tsig_rr);
295 296 297

	/* Set RCODE if forwarding failed. */
	if (ret != KNOT_EOK) {
298
		knot_wire_set_rcode(request->resp->wire, KNOT_RCODE_SERVFAIL);
299
		log_zone_error(zone->name, "DDNS, failed to forward updates to the master (%s)",
300
		               knot_strerror(ret));
301
	} else {
302
		log_zone_info(zone->name, "DDNS, updates forwarded to the master");
303 304 305
	}
}

306
static void forward_requests(conf_t *conf, zone_t *zone, list_t *requests)
307
{
308 309 310 311 312 313
	assert(zone);
	assert(requests);

	ptrnode_t *node = NULL;
	WALK_LIST(node, *requests) {
		struct knot_request *req = node->d;
314
		forward_request(conf, zone, req);
315 316 317
	}
}

318
static bool update_tsig_check(conf_t *conf, knotd_qdata_t *qdata, struct knot_request *req)
319 320
{
	// Check that ACL is still valid.
321 322
	if (!process_query_acl_check(conf, qdata->extra->zone->name, ACL_ACTION_UPDATE, qdata) ||
	    process_query_verify(qdata) != KNOT_EOK) {
323
		knot_wire_set_rcode(req->resp->wire, qdata->rcode);
324
		return false;
325
	}
326

327
	// Store signing context for response.
328
	req->sign = qdata->sign;
329

330
	return true;
331 332
}

333
static void send_update_response(conf_t *conf, const zone_t *zone, struct knot_request *req)
334 335
{
	if (req->resp) {
336
		if (!zone_is_slave(conf, zone)) {
337
			// Sign the response with TSIG where applicable
338 339 340
			knotd_qdata_t qdata;
			knotd_qdata_extra_t extra;
			init_qdata_from_request(&qdata, zone, req, NULL, &extra);
341

342 343
			(void)process_query_sign_response(req->resp, &qdata);
		}
344

345
		if (net_is_stream(req->fd)) {
Filip Siroky's avatar
Filip Siroky committed
346
			int timeout = 1000 * conf->cache.srv_tcp_reply_timeout;
347
			net_dns_tcp_send(req->fd, req->resp->wire, req->resp->size,
348
			                 timeout);
349
		} else {
350
			net_dgram_send(req->fd, req->resp->wire, req->resp->size,
351
			               (struct sockaddr *)&req->remote);
352 353 354 355
		}
	}
}

356 357 358
static void free_request(struct knot_request *req)
{
	close(req->fd);
359 360
	knot_pkt_free(req->query);
	knot_pkt_free(req->resp);
361 362 363
	free(req);
}

364
static void send_update_responses(conf_t *conf, const zone_t *zone, list_t *updates)
365
{
366 367 368
	ptrnode_t *node = NULL, *nxt = NULL;
	WALK_LIST_DELSAFE(node, nxt, *updates) {
		struct knot_request *req = node->d;
369
		send_update_response(conf, zone, req);
370
		free_request(req);
371
	}
372
	ptrlist_free(updates, NULL);
373 374
}

375
static int init_update_responses(conf_t *conf, const zone_t *zone, list_t *updates,
376
                                 size_t *update_count)
377
{
378 379 380
	ptrnode_t *node = NULL, *nxt = NULL;
	WALK_LIST_DELSAFE(node, nxt, *updates) {
		struct knot_request *req = node->d;
381 382
		req->resp = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
		if (req->resp == NULL) {
383 384 385
			return KNOT_ENOMEM;
		}

386 387
		assert(req->query);
		knot_pkt_init_response(req->resp, req->query);
388
		if (zone_is_slave(conf, zone)) {
389 390 391
			// Don't check TSIG for forwards.
			continue;
		}
392

393
		knotd_qdata_params_t params = {
394 395
			.remote = &req->remote
		};
396 397 398
		knotd_qdata_t qdata;
		knotd_qdata_extra_t extra;
		init_qdata_from_request(&qdata, zone, req, &params, &extra);
399

400
		if (!update_tsig_check(conf, &qdata, req)) {
401
			// ACL/TSIG check failed, send response.
402
			send_update_response(conf, zone, req);
403
			// Remove this request from processing list.
404
			free_request(req);
405
			ptrlist_rem(node, NULL);
406
			*update_count -= 1;
407
		}
408 409
	}

410 411
	return KNOT_EOK;
}
412

413
int update_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
414 415 416 417 418 419 420 421
{
	/* RFC1996 require SOA question. */
	NS_NEED_QTYPE(qdata, KNOT_RRTYPE_SOA, KNOT_RCODE_FORMERR);

	/* Check valid zone. */
	NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);

	/* Need valid transaction security. */
422
	zone_t *zone = (zone_t *)qdata->extra->zone;
423
	NS_NEED_AUTH(qdata, zone->name, ACL_ACTION_UPDATE);
424 425 426
	/* Check expiration. */
	NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL);

427 428
	NS_NEED_NOT_FROZEN(qdata, KNOT_RCODE_REFUSED);

429
	/* Restore original QNAME for DDNS ACL checks. */
430
	process_query_qname_case_restore(qdata->query, qdata);
431
	/* Store update into DDNS queue. */
432
	int ret = zone_update_enqueue(zone, qdata->query, qdata->params);
433
	if (ret != KNOT_EOK) {
434
		return KNOT_STATE_FAIL;
435 436 437
	}

	/* No immediate response. */
438
	return KNOT_STATE_NOOP;
439 440
}

441
void updates_execute(conf_t *conf, zone_t *zone)
442 443 444
{
	/* Get list of pending updates. */
	list_t updates;
445 446
	size_t update_count = zone_update_dequeue(zone, &updates);
	if (update_count == 0) {
447
		return;
448
	}
449

450
	/* Init updates respones. */
451
	int ret = init_update_responses(conf, zone, &updates, &update_count);
452
	if (ret != KNOT_EOK) {
453 454
		/* Send what responses we can. */
		set_rcodes(&updates, KNOT_RCODE_SERVFAIL);
455
		send_update_responses(conf, zone, &updates);
456
		return;
457 458
	}

459 460
	if (update_count == 0) {
		/* All updates failed their ACL checks. */
461
		return;
462
	}
463

464 465
	/* Process update list - forward if zone has master, or execute.
	   RCODEs are set. */
466
	if (zone_is_slave(conf, zone)) {
467
		log_zone_info(zone->name,
Jan Kadlec's avatar
Jan Kadlec committed
468
		              "DDNS, forwarding %zu updates", update_count);
469
		forward_requests(conf, zone, &updates);
470
	} else {
471
		log_zone_info(zone->name,
Jan Kadlec's avatar
Jan Kadlec committed
472
		              "DDNS, processing %zu updates", update_count);
473
		process_requests(conf, zone, &updates);
474 475
	}

476
	/* Send responses. */
477
	send_update_responses(conf, zone, &updates);
478
}