synth_record.c 12.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*  Copyright (C) 2011 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 18 19
#include "knot/modules/synth_record.h"
#include "knot/nameserver/process_query.h"
#include "knot/nameserver/internet.h"
20
#include "knot/common/log.h"
21
#include "libknot/descriptor.h"
Daniel Salzman's avatar
Daniel Salzman committed
22
#include "contrib/mempattern.h"
23 24
#include "contrib/net.h"
#include "contrib/sockaddr.h"
25

26
/* Module configuration scheme. */
27 28
#define MOD_NET		"\x07""network"
#define MOD_ORIGIN	"\x06""origin"
29 30 31
#define MOD_PREFIX	"\x06""prefix"
#define MOD_TTL		"\x03""ttl"
#define MOD_TYPE	"\x04""type"
32 33 34

/*! \brief Supported answer synthesis template types. */
enum synth_template_type {
35 36 37
	SYNTH_NULL    = 0,
	SYNTH_FORWARD = 1,
	SYNTH_REVERSE = 2
38 39
};

40
static const knot_lookup_t synthetic_types[] = {
41 42 43 44 45
	{ SYNTH_FORWARD, "forward" },
	{ SYNTH_REVERSE, "reverse" },
	{ 0, NULL }
};

46 47
int check_prefix(conf_check_t *args)
{
48 49
	if (strchr((const char *)args->data, '.') != NULL) {
		args->err_str = "dot '.' is not allowed";
50 51 52 53 54 55
		return KNOT_EINVAL;
	}

	return KNOT_EOK;
}

56 57 58
const yp_item_t scheme_mod_synth_record[] = {
	{ C_ID,       YP_TSTR,   YP_VNONE },
	{ MOD_TYPE,   YP_TOPT,   YP_VOPT = { synthetic_types, SYNTH_NULL } },
59
	{ MOD_PREFIX, YP_TSTR,   YP_VSTR = { "" }, YP_FNONE, { check_prefix } },
60
	{ MOD_ORIGIN, YP_TDNAME, YP_VNONE },
61
	{ MOD_TTL,    YP_TINT,   YP_VINT = { 0, UINT32_MAX, 3600, YP_STIME } },
62 63
	{ MOD_NET,    YP_TDATA,  YP_VDATA = { 0, NULL, addr_range_to_bin,
	                                      addr_range_to_txt }, YP_FMULTI },
64 65 66 67
	{ C_COMMENT,  YP_TSTR,   YP_VNONE },
	{ NULL }
};

68 69
int check_mod_synth_record(conf_check_t *args)
{
70
	// Check type.
71
	conf_val_t type = conf_rawid_get_txn(args->conf, args->txn, C_MOD_SYNTH_RECORD,
72
	                                     MOD_TYPE, args->id, args->id_len);
73
	if (type.code != KNOT_EOK) {
74
		args->err_str = "no synthesis type specified";
75 76 77 78
		return KNOT_EINVAL;
	}

	// Check origin.
79
	conf_val_t origin = conf_rawid_get_txn(args->conf, args->txn, C_MOD_SYNTH_RECORD,
80
	                                       MOD_ORIGIN, args->id, args->id_len);
81
	if (origin.code != KNOT_EOK && conf_opt(&type) == SYNTH_REVERSE) {
82
		args->err_str = "no origin specified";
83 84 85
		return KNOT_EINVAL;
	}
	if (origin.code == KNOT_EOK && conf_opt(&type) == SYNTH_FORWARD) {
86
		args->err_str = "origin not allowed with forward type";
87 88 89
		return KNOT_EINVAL;
	}

90
	// Check network subnet.
91
	conf_val_t net = conf_rawid_get_txn(args->conf, args->txn, C_MOD_SYNTH_RECORD,
92
	                                    MOD_NET, args->id, args->id_len);
93
	if (net.code != KNOT_EOK) {
94
		args->err_str = "no network subnet specified";
95 96 97 98 99 100
		return KNOT_EINVAL;
	}

	return KNOT_EOK;
}

101 102 103
/* Defines. */
#define ARPA_ZONE_LABELS 2

104 105 106 107 108 109
/*!
 * \brief Synthetic response template.
 */
typedef struct synth_template {
	node_t node;
	enum synth_template_type type;
110 111
	char *prefix;
	char *zone;
112
	uint32_t ttl;
113
	struct sockaddr_storage addr;
114
	struct sockaddr_storage addr_max;
115
	int mask;
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
} synth_template_t;

/*! \brief Substitute all occurences of given character. */
static void str_subst(char *str, size_t len, char from, char to)
{
	for (int i = 0; i < len; ++i) {
		if (str[i] == from) {
			str[i] = to;
		}
	}
}

/*! \brief Separator character for address family. */
static char str_separator(int addr_family)
{
	if (addr_family == AF_INET6) {
		return ':';
	}
	return '.';
}

137 138
/*! \brief Return true if query type is satisfied with provided address family. */
static bool query_satisfied_by_family(uint16_t qtype, int family)
139
{
140
	switch (qtype) {
141 142 143 144
	case KNOT_RRTYPE_A:    return family == AF_INET;
	case KNOT_RRTYPE_AAAA: return family == AF_INET6;
	case KNOT_RRTYPE_ANY:  return true;
	default:               return false;
145 146 147
	}
}

148
/*! \brief Parse address from reverse query QNAME and return address family. */
149
static int reverse_addr_parse(struct query_data *qdata, synth_template_t *tpl, char *addr_str)
150 151 152 153
{
	/* QNAME required format is [address].[subnet/zone]
	 * f.e.  [1.0...0].[h.g.f.e.0.0.0.0.d.c.b.a.ip6.arpa] represents
	 *       [abcd:0:efgh::1] */
154
	const knot_dname_t* label = qdata->name;
155 156 157 158 159
	const uint8_t *query_wire = qdata->query->wire;

	/* Push labels on stack for reverse walkthrough. */
	const uint8_t* label_stack[KNOT_DNAME_MAXLABELS];
	const uint8_t** sp = label_stack;
160
	int label_count = knot_dname_labels(label, query_wire);
161 162 163 164 165 166 167
	while(label_count > ARPA_ZONE_LABELS) {
		*sp++ = label;
		label = knot_wire_next_label(label, query_wire);
		--label_count;
	}

	/* Write formatted address string. */
168
	char sep = str_separator(tpl->addr.ss_family);
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
	int sep_frequency = 1;
	if (sep == ':') {
		sep_frequency = 4; /* Separator per 4 hexdigits. */
	}

	char *dst = addr_str;
	label_count = 0;
	while(sp != label_stack) {
		label = *--sp;
		/* Write separator for each Nth label. */
		if (label_count == sep_frequency) {
			*dst = sep;
			dst += 1;
			label_count = 0;
		}
		/* Write label */
		memcpy(dst, label + 1, label[0]);
		dst += label[0];
		label_count += 1;
	}

190
	return KNOT_EOK;
191 192 193 194 195
}

static int forward_addr_parse(struct query_data *qdata, synth_template_t *tpl, char *addr_str)
{
	/* Find prefix label count (additive to prefix length). */
196
	const knot_dname_t *addr_label = qdata->name;
197 198 199 200 201 202

	/* Mismatch if label shorter/equal than prefix. */
	int prefix_len = strlen(tpl->prefix);
	if (addr_label == NULL || addr_label[0] <= prefix_len) {
		return KNOT_EINVAL;
	}
203

204 205
	int addr_len = *addr_label - prefix_len;
	memcpy(addr_str, addr_label + 1 + prefix_len, addr_len);
206

207
	/* Restore correct address format. */
208
	char sep = str_separator(tpl->addr.ss_family);
209
	str_subst(addr_str, addr_len, '-', sep);
210 211

	return KNOT_EOK;
212 213 214 215
}

static int addr_parse(struct query_data *qdata, synth_template_t *tpl, char *addr_str)
{
216 217
	/* Check if we have at least 1 label below zone. */
	int zone_labels = knot_dname_labels(qdata->zone->name, NULL);
218 219
	int query_labels = knot_dname_labels(qdata->name, qdata->query->wire);
	if (query_labels < zone_labels + 1) {
220 221 222
		return KNOT_EINVAL;
	}

223
	switch (tpl->type) {
224
	case SYNTH_REVERSE: return reverse_addr_parse(qdata, tpl, addr_str);
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
	case SYNTH_FORWARD: return forward_addr_parse(qdata, tpl, addr_str);
	default:            return KNOT_EINVAL;
	}
}

static knot_dname_t *synth_ptrname(const char *addr_str, synth_template_t *tpl)
{
	/* PTR right-hand value is [prefix][address][zone] */
	char ptrname[KNOT_DNAME_MAXLEN] = {'\0'};
	int prefix_len = strlen(tpl->prefix);
	int addr_len = strlen(addr_str);
	int zone_len = strlen(tpl->zone);

	/* Check required space (zone requires extra leading dot). */
	if (prefix_len + addr_len + zone_len + 1 >= KNOT_DNAME_MAXLEN) {
		return NULL;
	}

	/* Write prefix string. */
	memcpy(ptrname, tpl->prefix, prefix_len);
	int written = prefix_len;

	/* Write address with substituted separator to '-'. */
248
	char sep = str_separator(tpl->addr.ss_family);
249 250 251 252 253 254 255 256 257 258
	memcpy(ptrname + written, addr_str, addr_len);
	str_subst(ptrname + written, addr_len, sep, '-');
	written += addr_len;

	/* Write zone name. */
	ptrname[written] = '.';
	written += 1;
	memcpy(ptrname + written, tpl->zone, zone_len);

	/* Convert to domain name. */
259
	return knot_dname_from_str_alloc(ptrname);
260 261
}

262
static int reverse_rr(char *addr_str, synth_template_t *tpl, knot_pkt_t *pkt, knot_rrset_t *rr)
263 264 265 266
{
	/* Synthetize PTR record data. */
	knot_dname_t *ptrname = synth_ptrname(addr_str, tpl);
	if (ptrname == NULL) {
267
		return KNOT_ENOMEM;
268
	}
269 270

	rr->type = KNOT_RRTYPE_PTR;
271
	knot_rrset_add_rdata(rr, ptrname, knot_dname_size(ptrname), tpl->ttl, &pkt->mm);
272
	knot_dname_free(&ptrname, NULL);
273

274
	return KNOT_EOK;
275 276
}

277
static int forward_rr(char *addr_str, synth_template_t *tpl, knot_pkt_t *pkt, knot_rrset_t *rr)
278 279
{
	struct sockaddr_storage query_addr = {'\0'};
280
	sockaddr_set(&query_addr, tpl->addr.ss_family, addr_str, 0);
281

282
	/* Specify address type and data. */
283
	if (tpl->addr.ss_family == AF_INET6) {
284
		rr->type = KNOT_RRTYPE_AAAA;
285
		const struct sockaddr_in6* ip = (const struct sockaddr_in6*)&query_addr;
286
		knot_rrset_add_rdata(rr, (const uint8_t *)&ip->sin6_addr, sizeof(struct in6_addr),
287
		                  tpl->ttl, &pkt->mm);
288
	} else if (tpl->addr.ss_family == AF_INET) {
289
		rr->type = KNOT_RRTYPE_A;
290
		const struct sockaddr_in* ip = (const struct sockaddr_in*)&query_addr;
291
		knot_rrset_add_rdata(rr, (const uint8_t *)&ip->sin_addr, sizeof(struct in_addr),
292
		                  tpl->ttl, &pkt->mm);
293 294
	} else {
		return KNOT_EINVAL;
295 296
	}

297
	return KNOT_EOK;
298 299 300 301
}

static knot_rrset_t *synth_rr(char *addr_str, synth_template_t *tpl, knot_pkt_t *pkt, struct query_data *qdata)
{
302 303
	knot_rrset_t *rr = knot_rrset_new(qdata->name, 0, KNOT_CLASS_IN,
	                                  &pkt->mm);
304 305 306 307 308 309
	if (rr == NULL) {
		return NULL;
	}

	/* Fill in the specific data. */
	int ret = KNOT_ERROR;
310
	switch (tpl->type) {
311 312 313 314 315 316
	case SYNTH_REVERSE: ret = reverse_rr(addr_str, tpl, pkt, rr); break;
	case SYNTH_FORWARD: ret = forward_rr(addr_str, tpl, pkt, rr); break;
	default: break;
	}

	if (ret != KNOT_EOK) {
317
		knot_rrset_free(&rr, &pkt->mm);
318
		return NULL;
319
	}
320 321

	return rr;
322 323 324 325 326 327 328
}

/*! \brief Check if query fits the template requirements. */
static int template_match(int state, synth_template_t *tpl, knot_pkt_t *pkt, struct query_data *qdata)
{
	/* Parse address from query name. */
	char addr_str[SOCKADDR_STRLEN] = { '\0' };
329 330
	int ret = addr_parse(qdata, tpl, addr_str);
	if (ret != KNOT_EOK) {
331 332 333 334
		return state; /* Can't identify addr in QNAME, not applicable. */
	}

	/* Match against template netblock. */
335
	struct sockaddr_storage query_addr = { '\0' };
336
	int provided_af = tpl->addr.ss_family;
337
	ret = sockaddr_set(&query_addr, provided_af, addr_str, 0);
338 339
	if (ret != KNOT_EOK) {
		return state;
340
	}
341 342 343 344 345 346 347 348
	if (tpl->addr_max.ss_family == AF_UNSPEC) {
		if (!netblock_match(&query_addr, &tpl->addr, tpl->mask)) {
			return state; /* Out of our netblock, not applicable. */
		}
	} else {
		if (!netrange_match(&query_addr, &tpl->addr, &tpl->addr_max)) {
			return state; /* Out of our netblock, not applicable. */
		}
349 350
	}

351
	/* Check if the request is for an available query type. */
352
	uint16_t qtype = knot_pkt_qtype(qdata->query);
353
	switch (tpl->type) {
354 355
	case SYNTH_FORWARD:
		if (!query_satisfied_by_family(qtype, provided_af)) {
356 357 358
			qdata->rcode = KNOT_RCODE_NOERROR;
			return NODATA;
		}
359 360 361 362 363 364 365 366 367
		break;
	case SYNTH_REVERSE:
		if (qtype != KNOT_RRTYPE_PTR && qtype != KNOT_RRTYPE_ANY) {
			qdata->rcode = KNOT_RCODE_NOERROR;
			return NODATA;
		}
		break;
	default:
		break;
368 369
	}

370 371 372 373 374 375 376
	/* Synthetise record from template. */
	knot_rrset_t *rr = synth_rr(addr_str, tpl, pkt, qdata);
	if (rr == NULL) {
		qdata->rcode = KNOT_RCODE_SERVFAIL;
		return ERROR;
	}

377 378
	/* Insert synthetic response into packet. */
	if (knot_pkt_put(pkt, 0, rr, KNOT_PF_FREE) != KNOT_EOK) {
379 380 381
		return ERROR;
	}

382 383 384
	/* Authoritative response. */
	knot_wire_set_aa(pkt->wire);

385 386 387
	return HIT;
}

388
static int solve_synth_record(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx)
389 390 391 392 393 394 395 396 397 398 399 400 401 402
{
	if (pkt == NULL || qdata == NULL || ctx == NULL) {
		return ERROR;
	}

	/* Applicable when search in zone fails. */
	if (state != MISS) {
		return state;
	}

	/* Check if template fits. */
	return template_match(state, (synth_template_t *)ctx, pkt, qdata);
}

403 404
int synth_record_load(struct query_plan *plan, struct query_module *self,
                      const knot_dname_t *zone)
405
{
406 407
	if (plan == NULL || self == NULL) {
		return KNOT_EINVAL;
408 409 410 411 412 413 414 415
	}

	/* Create synthesis template. */
	struct synth_template *tpl = mm_alloc(self->mm, sizeof(struct synth_template));
	if (tpl == NULL) {
		return KNOT_ENOMEM;
	}

416
	conf_val_t val;
417

418 419 420
	/* Set type. */
	val = conf_mod_get(self->config, MOD_TYPE, self->id);
	tpl->type = conf_opt(&val);
421

422 423
	/* Set prefix. */
	val = conf_mod_get(self->config, MOD_PREFIX, self->id);
424
	tpl->prefix = strdup(conf_str(&val));
425

426
	/* Set origin if generating reverse record. */
427
	if (tpl->type == SYNTH_REVERSE) {
428
		val = conf_mod_get(self->config, MOD_ORIGIN, self->id);
429 430 431 432 433 434
		tpl->zone = knot_dname_to_str_alloc(conf_dname(&val));
		if (tpl->zone == NULL) {
			free(tpl->prefix);
			mm_free(self->mm, tpl);
			return KNOT_ENOMEM;
		}
435 436
	}

437 438 439 440 441
	/* Set ttl. */
	val = conf_mod_get(self->config, MOD_TTL, self->id);
	tpl->ttl = conf_int(&val);

	/* Set address. */
442
	val = conf_mod_get(self->config, MOD_NET, self->id);
443
	tpl->addr = conf_addr_range(&val, &tpl->addr_max, &tpl->mask);
444

445
	self->ctx = tpl;
446

447
	return query_plan_step(plan, QPLAN_ANSWER, solve_synth_record, self->ctx);
448 449 450 451
}

int synth_record_unload(struct query_module *self)
{
452 453 454 455 456 457 458 459
	if (self == NULL) {
		return KNOT_EINVAL;
	}

	synth_template_t *tpl = (synth_template_t *)self->ctx;
	free(tpl->zone);
	free(tpl->prefix);
	mm_free(self->mm, tpl);
460 461
	return KNOT_EOK;
}