synth_record.c 12.9 KB
Newer Older
1
/*  Copyright (C) 2016 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/>.
 */

Daniel Salzman's avatar
Daniel Salzman committed
17
#include "contrib/mempattern.h"
18 19
#include "contrib/net.h"
#include "contrib/sockaddr.h"
20
#include "knot/modules/synth_record/synth_record.h"
21

22
/* Module configuration scheme. */
23 24
#define MOD_NET		"\x07""network"
#define MOD_ORIGIN	"\x06""origin"
25 26 27
#define MOD_PREFIX	"\x06""prefix"
#define MOD_TTL		"\x03""ttl"
#define MOD_TYPE	"\x04""type"
28 29 30

/*! \brief Supported answer synthesis template types. */
enum synth_template_type {
31 32 33
	SYNTH_NULL    = 0,
	SYNTH_FORWARD = 1,
	SYNTH_REVERSE = 2
34 35
};

36
static const knot_lookup_t synthetic_types[] = {
37 38 39 40 41
	{ SYNTH_FORWARD, "forward" },
	{ SYNTH_REVERSE, "reverse" },
	{ 0, NULL }
};

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

	return KNOT_EOK;
}

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

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

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

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

	return KNOT_EOK;
}

97 98 99
/* Defines. */
#define ARPA_ZONE_LABELS 2

100 101 102 103 104 105
/*!
 * \brief Synthetic response template.
 */
typedef struct synth_template {
	node_t node;
	enum synth_template_type type;
106 107
	char *prefix;
	char *zone;
108
	uint32_t ttl;
109
	struct sockaddr_storage addr;
110
	struct sockaddr_storage addr_max;
111
	int mask;
112 113
} synth_template_t;

114
/*! \brief Substitute all occurrences of given character. */
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
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 '.';
}

133 134
/*! \brief Return true if query type is satisfied with provided address family. */
static bool query_satisfied_by_family(uint16_t qtype, int family)
135
{
136
	switch (qtype) {
137 138 139 140
	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;
141 142 143
	}
}

144
/*! \brief Parse address from reverse query QNAME and return address family. */
145
static int reverse_addr_parse(struct query_data *qdata, synth_template_t *tpl, char *addr_str)
146 147 148 149
{
	/* 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] */
150
	const knot_dname_t* label = qdata->name;
151 152 153 154 155
	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;
156
	int label_count = knot_dname_labels(label, query_wire);
157 158 159 160 161 162 163
	while(label_count > ARPA_ZONE_LABELS) {
		*sp++ = label;
		label = knot_wire_next_label(label, query_wire);
		--label_count;
	}

	/* Write formatted address string. */
164
	char sep = str_separator(tpl->addr.ss_family);
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
	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;
	}

186
	return KNOT_EOK;
187 188 189 190 191
}

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

	/* 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;
	}
199

200 201
	int addr_len = *addr_label - prefix_len;
	memcpy(addr_str, addr_label + 1 + prefix_len, addr_len);
202

203
	/* Restore correct address format. */
204
	char sep = str_separator(tpl->addr.ss_family);
205
	str_subst(addr_str, addr_len, '-', sep);
206 207

	return KNOT_EOK;
208 209 210 211
}

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

219
	switch (tpl->type) {
220
	case SYNTH_REVERSE: return reverse_addr_parse(qdata, tpl, addr_str);
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
	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 '-'. */
244
	char sep = str_separator(tpl->addr.ss_family);
245 246 247 248 249 250 251 252 253 254
	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. */
255
	return knot_dname_from_str_alloc(ptrname);
256 257
}

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

	rr->type = KNOT_RRTYPE_PTR;
267
	knot_rrset_add_rdata(rr, ptrname, knot_dname_size(ptrname), tpl->ttl, &pkt->mm);
268
	knot_dname_free(&ptrname, NULL);
269

270
	return KNOT_EOK;
271 272
}

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

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

293
	return KNOT_EOK;
294 295 296 297
}

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

	/* Fill in the specific data. */
	int ret = KNOT_ERROR;
306
	switch (tpl->type) {
307 308 309 310 311 312
	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) {
313
		knot_rrset_free(&rr, &pkt->mm);
314
		return NULL;
315
	}
316 317

	return rr;
318 319 320 321 322 323 324
}

/*! \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' };
325 326
	int ret = addr_parse(qdata, tpl, addr_str);
	if (ret != KNOT_EOK) {
327 328 329 330
		return state; /* Can't identify addr in QNAME, not applicable. */
	}

	/* Match against template netblock. */
331
	struct sockaddr_storage query_addr = { '\0' };
332
	int provided_af = tpl->addr.ss_family;
333
	ret = sockaddr_set(&query_addr, provided_af, addr_str, 0);
334 335
	if (ret != KNOT_EOK) {
		return state;
336
	}
337
	if (tpl->addr_max.ss_family == AF_UNSPEC) {
338 339 340
		if (!sockaddr_net_match((struct sockaddr *)&query_addr,
		                        (struct sockaddr *)&tpl->addr,
		                        tpl->mask)) {
341 342 343
			return state; /* Out of our netblock, not applicable. */
		}
	} else {
344 345 346
		if (!sockaddr_range_match((struct sockaddr *)&query_addr,
		                          (struct sockaddr *)&tpl->addr,
		                          (struct sockaddr *)&tpl->addr_max)) {
347 348
			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;
}