test_edns.c 31.7 KB
Newer Older
1
/*  Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
Lubos Slovak's avatar
Lubos Slovak committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

    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/>.
 */

#include <tap/basic.h>

Lubos Slovak's avatar
Lubos Slovak committed
19
#include <assert.h>
20
#include "libknot/libknot.h"
21
#include "libknot/rrtype/opt.h"
22
#include "libknot/descriptor.h"
23
#include "contrib/sockaddr.h"
24
#include "contrib/wire.h"
Lubos Slovak's avatar
Lubos Slovak committed
25 26

static const uint16_t E_MAX_PLD = 10000;
27
static const uint16_t E_MAX_PLD2 = 20000;
Lubos Slovak's avatar
Lubos Slovak committed
28
static const uint8_t E_VERSION = 1;
29
static const uint8_t E_VERSION2 = 2;
Lubos Slovak's avatar
Lubos Slovak committed
30 31
static const uint8_t E_RCODE = 0;
static const uint8_t E_RCODE2 = 200;
32

Lubos Slovak's avatar
Lubos Slovak committed
33
static const char *E_NSID_STR = "FooBar";
Lubos Slovak's avatar
Lubos Slovak committed
34 35
static const uint16_t E_NSID_LEN = 6;

36
#define E_NSID_SIZE (4 + E_NSID_LEN)
Lubos Slovak's avatar
Lubos Slovak committed
37 38 39 40 41 42 43

static const uint16_t E_OPT3_CODE = 15;
static const char *E_OPT3_FAKE_DATA = "Not used";
static const char *E_OPT3_DATA = NULL;
static const uint16_t E_OPT3_LEN = 0;
static const uint16_t E_OPT3_FAKE_LEN = 8;

44 45
#define E_OPT3_SIZE (4 + E_OPT3_LEN)

Lubos Slovak's avatar
Lubos Slovak committed
46 47 48
static const uint16_t E_OPT4_CODE = 30;
static const char *E_OPT4_DATA = NULL;
static const uint16_t E_OPT4_LEN = 0;
Lubos Slovak's avatar
Lubos Slovak committed
49

50 51
#define E_OPT4_SIZE (4 + E_OPT4_LEN)

Lubos Slovak's avatar
Lubos Slovak committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
enum offsets {
	/*! \brief Offset of Extended RCODE in wire order of TTL. */
	OFFSET_ERCODE = 0,
	/*! \brief Offset of Version in wire order of TTL. */
	OFFSET_VER = 1,
	/*! \brief Offset of Flags in wire order of TTL. */
	OFFSET_FLAGS = 2,
	/*! \brief Offset of OPTION code in one OPTION in RDATA. */
	OFFSET_OPT_CODE = 0,
	/*! \brief Offset of OPTION size in one OPTION in RDATA. */
	OFFSET_OPT_SIZE = 2,
	/*! \brief Offset of OPTION data in one OPTION in RDATA. */
	OFFSET_OPT_DATA = 4
};

67 68
static const uint16_t DO_FLAG = (uint16_t)1 << 15;

69 70
static void check_ttl(knot_rdata_t *rdata, uint8_t ext_rcode, uint8_t ver,
                      uint16_t flags, char *msg)
Lubos Slovak's avatar
Lubos Slovak committed
71
{
Lubos Slovak's avatar
Lubos Slovak committed
72 73 74
	/* TTL should be stored in machine byte order.
	   We need network byte order to compare its parts. */
	uint8_t ttl_wire[4] = { 0, 0, 0, 0 };
75
	wire_write_u32(ttl_wire, knot_rdata_ttl(rdata));
Lubos Slovak's avatar
Lubos Slovak committed
76 77

	/* Convert Flags from EDNS parameters to wire format for comparison. */
78
	uint8_t flags_wire[2] = { 0, 0 };
79
	wire_write_u16(flags_wire, flags);
80

Lubos Slovak's avatar
Lubos Slovak committed
81
	/* TTL = Ext RCODE + Version + Flags */
82
	bool check = (ttl_wire[OFFSET_ERCODE] == ext_rcode);
Lubos Slovak's avatar
Lubos Slovak committed
83
	ok(check, "%s: extended RCODE", msg);
84

85
	check = (ttl_wire[OFFSET_VER] == ver);
Lubos Slovak's avatar
Lubos Slovak committed
86
	ok(check, "%s: version", msg);
87

88
	check = (memcmp(flags_wire, ttl_wire + OFFSET_FLAGS, 2) == 0);
Lubos Slovak's avatar
Lubos Slovak committed
89
	ok(check, "%s: flags", msg);
90
}
Lubos Slovak's avatar
Lubos Slovak committed
91

92 93
static void check_option(knot_rdata_t *rdata, uint16_t opt_code,
                         uint16_t opt_len, uint8_t *opt_data, char *msg)
94
{
Lubos Slovak's avatar
Lubos Slovak committed
95
	assert(rdata != NULL);
Lubos Slovak's avatar
Lubos Slovak committed
96 97 98 99

	uint8_t *data = knot_rdata_data(rdata);
	uint16_t data_len = knot_rdata_rdlen(rdata);

100 101
	/* Check RDLENGTH according to given data length. */
	bool check = (data_len >= 4 + opt_len);
102
	ok(check, "%s: RDLENGTH (%u)", msg, data_len);
103 104 105 106 107

	/* Find the desired option. */
	bool found = false;
	int pos = 0;
	while (pos <= data_len - 4) {
108
		uint16_t code = wire_read_u16(data + pos + OFFSET_OPT_CODE);
109 110 111 112
		if (code == opt_code) {
			found = true;
			break;
		}
113
		uint16_t len = wire_read_u16(data + pos + OFFSET_OPT_SIZE);
Lubos Slovak's avatar
Lubos Slovak committed
114
		pos += 4 + len;
115 116 117
	}

	/* Check that the option is present. */
Lubos Slovak's avatar
Lubos Slovak committed
118
	ok(found, "%s: find OPTION %u in OPT RR", msg, opt_code);
Lubos Slovak's avatar
Lubos Slovak committed
119

120
	/* Check that the first OPTION's size si the size of the option data. */
121
	uint16_t opt_size = wire_read_u16(data + pos + OFFSET_OPT_SIZE);
122
	check = (opt_size == opt_len);
Lubos Slovak's avatar
Lubos Slovak committed
123
	ok(check, "%s: OPTION data size", msg);
Lubos Slovak's avatar
Lubos Slovak committed
124 125

	/* Check the actual NSID data. */
126
	check = (opt_data == 0 || memcmp(data + pos + OFFSET_OPT_DATA, opt_data, opt_len) == 0);
Lubos Slovak's avatar
Lubos Slovak committed
127
	ok(check, "%s: OPTION data", msg);
128 129
}

130 131
static void check_header(knot_rrset_t *opt_rr, uint16_t payload, uint8_t ver,
                         uint16_t flags, uint8_t ext_rcode, char *msg)
132 133 134 135 136 137
{
	assert(opt_rr != NULL);
	bool check;

	/* Check values in OPT RR by hand. */
	/* CLASS == Max UDP payload */
138
	check = (opt_rr->rclass == payload);
Lubos Slovak's avatar
Lubos Slovak committed
139
	ok(check, "%s: max payload", msg);
140 141

	/* The OPT RR should have exactly one RDATA. */
142
	check = (opt_rr->rrs.rr_count == 1);
Lubos Slovak's avatar
Lubos Slovak committed
143
	ok(check, "%s: RR count == 1", msg);
144 145

	knot_rdata_t *rdata = knot_rdataset_at(&opt_rr->rrs, 0);
146
	check = (rdata != NULL);
Lubos Slovak's avatar
Lubos Slovak committed
147
	ok(check, "%s: RDATA exists", msg);
148

149
	check_ttl(rdata, ext_rcode, ver, flags, msg);
Lubos Slovak's avatar
Lubos Slovak committed
150 151
}

152
static void test_getters(knot_rrset_t *opt_rr)
153
{
Lubos Slovak's avatar
Lubos Slovak committed
154
	assert(opt_rr != NULL);
155

156 157 158 159 160 161 162 163 164 165
	/* These values should be set from the setters test:
	 * Max UDP payload: E_MAX_PLD2
	 * Version:         E_VERSION2
	 * RCODE:           E_RCODE2
	 * Flags:           E_FLAGS | KNOT_EDNS_FLAG_DO
	 * OPTIONs:         1) KNOT_EDNS_OPTION_NSID, E_NSID_LEN, E_NSID_STR
	 *                  2) E_OPT3_CODE, 0, 0
	 *                  3) E_OPT4_CODE, 0, 0
	 */

166
	/* Payload */
167
	bool check = (knot_edns_get_payload(opt_rr) == E_MAX_PLD2);
Lubos Slovak's avatar
Lubos Slovak committed
168
	ok(check, "OPT RR getters: payload");
169 170

	/* Extended RCODE */
171
	check = (knot_edns_get_ext_rcode(opt_rr) == E_RCODE2);
Lubos Slovak's avatar
Lubos Slovak committed
172
	ok(check, "OPT RR getters: extended RCODE");
173 174

	/* Extended RCODE */
175
	check = (knot_edns_get_version(opt_rr) == E_VERSION2);
Lubos Slovak's avatar
Lubos Slovak committed
176
	ok(check, "OPT RR getters: version");
177 178

	/* DO bit */
179
	check = knot_edns_do(opt_rr);
Lubos Slovak's avatar
Lubos Slovak committed
180
	ok(check, "OPT RR getters: DO bit check");
181

182 183 184 185 186 187 188 189
	/* Wire size */
	size_t total_size = KNOT_EDNS_MIN_SIZE
	                    + E_NSID_SIZE + E_OPT3_SIZE + E_OPT4_SIZE;
	size_t actual_size = knot_edns_wire_size(opt_rr);
	check = actual_size == total_size;
	ok(check, "OPT RR getters: wire size (expected: %zu, actual: %zu)",
	   total_size, actual_size);

190
	/* NSID */
191
	check = knot_edns_has_option(opt_rr, KNOT_EDNS_OPTION_NSID);
Lubos Slovak's avatar
Lubos Slovak committed
192
	ok(check, "OPT RR getters: NSID check");
193

194 195 196 197 198 199
	/* Other OPTIONs */
	check = knot_edns_has_option(opt_rr, E_OPT3_CODE);
	ok(check, "OPT RR getters: empty option 1");

	check = knot_edns_has_option(opt_rr, E_OPT4_CODE);
	ok(check, "OPT RR getters: empty option 2");
Lubos Slovak's avatar
Lubos Slovak committed
200 201
}

202
static void test_setters(knot_rrset_t *opt_rr)
203
{
Lubos Slovak's avatar
Lubos Slovak committed
204 205 206
	assert(opt_rr != NULL);

	/* Header-related setters. */
207 208 209
	knot_edns_set_payload(opt_rr, E_MAX_PLD2);
	knot_edns_set_ext_rcode(opt_rr, E_RCODE2);
	knot_edns_set_version(opt_rr, E_VERSION2);
Lubos Slovak's avatar
Lubos Slovak committed
210
	knot_edns_set_do(opt_rr);
211

212 213
	check_header(opt_rr, E_MAX_PLD2, E_VERSION2, DO_FLAG, E_RCODE2,
	             "OPT RR setters");
Lubos Slovak's avatar
Lubos Slovak committed
214 215 216 217

	/* OPTION(RDATA)-related setters. */

	/* Proper option. */
218 219
	int ret = knot_edns_add_option(opt_rr, KNOT_EDNS_OPTION_NSID,
	                           E_NSID_LEN, (uint8_t *)E_NSID_STR, NULL);
220
	is_int(KNOT_EOK, ret, "OPT RR setters: add option with data (ret = %s)",
Lubos Slovak's avatar
Lubos Slovak committed
221 222 223 224 225
	   knot_strerror(ret));

	/* Wrong argument: no OPT RR. */
	ret = knot_edns_add_option(NULL, E_OPT3_CODE, E_OPT3_FAKE_LEN,
	                           (uint8_t *)E_OPT3_FAKE_DATA, NULL);
226
	is_int(KNOT_EINVAL, ret, "OPT RR setters: add option (rr == NULL) "
Lubos Slovak's avatar
Lubos Slovak committed
227 228 229 230 231
	   "(ret = %s)", knot_strerror(ret));

	/* Wrong argument: option length != 0 && data == NULL. */
	ret = knot_edns_add_option(opt_rr, E_OPT3_CODE, E_OPT3_FAKE_LEN, NULL,
	                           NULL);
232
	is_int(KNOT_EINVAL, ret, "OPT RR setters: add option (data == NULL, "
Lubos Slovak's avatar
Lubos Slovak committed
233 234 235 236 237
	   "len != 0) (ret = %s)", knot_strerror(ret));

	/* Empty OPTION (length 0, data != NULL). */
	ret = knot_edns_add_option(opt_rr, E_OPT3_CODE, E_OPT3_LEN,
	                           (uint8_t *)E_OPT3_FAKE_DATA, NULL);
238
	is_int(KNOT_EOK, ret, "OPT RR setters: add empty option 1 (ret = %s)",
Lubos Slovak's avatar
Lubos Slovak committed
239 240 241 242
	   knot_strerror(ret));

	/* Empty OPTION (length 0, data == NULL). */
	ret = knot_edns_add_option(opt_rr, E_OPT4_CODE, E_OPT4_LEN,
243
	                           (uint8_t *)E_OPT4_DATA, NULL);
244
	is_int(KNOT_EOK, ret, "OPT RR setters: add empty option 2 (ret = %s)",
Lubos Slovak's avatar
Lubos Slovak committed
245 246 247
	   knot_strerror(ret));

	knot_rdata_t *rdata = knot_rdataset_at(&opt_rr->rrs, 0);
248
	ok(rdata != NULL, "OPT RR setters: non-empty RDATA");
Lubos Slovak's avatar
Lubos Slovak committed
249

250
	/* Check proper option */
251 252
	check_option(rdata, KNOT_EDNS_OPTION_NSID, E_NSID_LEN,
	             (uint8_t *)E_NSID_STR, "OPT RR setters (proper option)");
253 254

	/* Check empty option 1 */
255 256
	check_option(rdata, E_OPT3_CODE, E_OPT3_LEN,
	             (uint8_t *)E_OPT3_DATA, "OPT RR setters (empty option 1)");
Lubos Slovak's avatar
Lubos Slovak committed
257 258

	/* Check empty option 2 */
259 260
	check_option(rdata, E_OPT4_CODE, E_OPT4_LEN,
	             (uint8_t *)E_OPT4_DATA, "OPT RR setters (empty option 2)");
261 262
}

263 264 265 266 267
#define OPT_ID_0 0xaaa0
#define OPT_ID_1 0xaaa1
#define OPT_ID_2 0xaaa2
#define OPT_ID_3 0xaaa3

268
static bool prepare_edns_data(knot_rrset_t *opt_rr, bool fill)
269 270 271 272 273 274 275 276 277 278 279 280 281
{
	knot_rrset_clear(opt_rr, NULL);
	int ret = knot_edns_init(opt_rr, E_MAX_PLD, E_RCODE, E_VERSION, NULL);
	if (ret != KNOT_EOK) {
		return false;
	}

	/* Header-related setters. */
	knot_edns_set_payload(opt_rr, E_MAX_PLD2);
	knot_edns_set_ext_rcode(opt_rr, E_RCODE2);
	knot_edns_set_version(opt_rr, E_VERSION2);
	knot_edns_set_do(opt_rr);

282 283 284 285 286
	static const uint8_t OPT_DATA[] = {
		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
	};

287 288 289 290
	if (!fill) {
		return true;
	}

291
	ret = knot_edns_add_option(opt_rr, OPT_ID_1, 3, OPT_DATA, NULL);
292 293 294 295
	if (ret != KNOT_EOK) {
		return false;
	}

296
	ret = knot_edns_add_option(opt_rr, OPT_ID_0, 4, OPT_DATA, NULL);
297 298 299 300
	if (ret != KNOT_EOK) {
		return false;
	}

301
	ret = knot_edns_add_option(opt_rr, OPT_ID_1, 3, OPT_DATA, NULL);
302 303 304 305
	if (ret != KNOT_EOK) {
		return false;
	}

306
	ret = knot_edns_add_option(opt_rr, OPT_ID_2, 8, OPT_DATA, NULL);
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
	if (ret != KNOT_EOK) {
		return false;
	}

	return true;
}

static bool check_rdata(const knot_rrset_t *opt_rr, uint16_t len, const uint8_t *data)
{
	knot_rdata_t *rdata = knot_rdataset_at(&opt_rr->rrs, 0);
	assert(rdata != NULL);

	const uint8_t *data_ptr = knot_rdata_data(rdata);
	uint16_t data_len = knot_rdata_rdlen(rdata);

	if (data_len != len) {
		return false;
	}

	return memcmp(data_ptr, data, data_len) == 0;
}

329
static void test_remove(void)
330 331 332 333 334 335 336
{
	int iret;
	bool bret;
	knot_rrset_t opt_rr;
	uint16_t new_expected_len;

	iret = knot_edns_init(&opt_rr, E_MAX_PLD, E_RCODE, E_VERSION, NULL);
337
	ok(iret == KNOT_EOK, "OPT RR remove: init");
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364

	/* Test helper function for data preparation. */
	new_expected_len = 0;
	bret = prepare_edns_data(&opt_rr, false);
	ok(bret, "OPT RR remove: internal data preparation");
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"");
	ok(bret, "OPT RR remove: data preparation");

	new_expected_len = 34;
	bret = prepare_edns_data(&opt_rr, true);
	ok(bret, "OPT RR remove: internal data preparation");
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(bret, "OPT RR remove: data preparation");

	/* Invalid parameter. */
	iret = knot_edns_remove_options(NULL, OPT_ID_0);
	ok(iret == KNOT_EINVAL, "OPT RR remove: invalid parameter (ret = %s)",
	   knot_strerror(iret));

	/* Removing from empty OPT RR. */
	new_expected_len = 0;
	bret = prepare_edns_data(&opt_rr, false);
365
	ok(bret, "OPT RR remove: internal data preparation");
366 367 368 369 370 371 372 373 374 375
	iret = knot_edns_remove_options(&opt_rr, OPT_ID_3);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"");
	ok(iret == KNOT_EOK && bret,
	   "OPT RR remove: removing from empty OPT RR (ret = %s)",
	   knot_strerror(iret));

	/* Removing non-existent option. */
	new_expected_len = 34;
	bret = prepare_edns_data(&opt_rr, true);
376
	ok(bret, "OPT RR remove: internal data preparation");
377 378 379 380 381 382 383 384 385 386 387 388 389
	iret = knot_edns_remove_options(&opt_rr, OPT_ID_3);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(iret == KNOT_EOK && bret,
	   "OPT RR remove: removing non-existent option (ret = %s)",
	   knot_strerror(iret));

	/* Removing existent option. */
	new_expected_len = 26;
	bret = prepare_edns_data(&opt_rr, true);
390
	ok(bret, "OPT RR remove: internal data preparation");
391 392 393 394 395 396 397 398 399 400 401 402
	iret = knot_edns_remove_options(&opt_rr, OPT_ID_0);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(iret == KNOT_EOK && bret,
	   "OPT RR remove: removing existent option (ret = %s)",
	   knot_strerror(iret));

	/* Removing existent options. */
	new_expected_len = 20;
	bret = prepare_edns_data(&opt_rr, true);
403
	ok(bret, "OPT RR remove: internal data preparation");
404 405 406 407 408 409 410 411 412 413 414
	iret = knot_edns_remove_options(&opt_rr, OPT_ID_1);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(iret == KNOT_EOK && bret,
	   "OPT RR remove: removing existent options (ret = %s)",
	   knot_strerror(iret));

	/* Removing existent option. */
	new_expected_len = 22;
	bret = prepare_edns_data(&opt_rr, true);
415
	ok(bret, "OPT RR remove: internal data preparation");
416 417 418 419 420 421 422 423 424 425 426 427
	iret = knot_edns_remove_options(&opt_rr, OPT_ID_2);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2");
	ok(iret == KNOT_EOK && bret,
	   "OPT RR remove: removing existent option (ret = %s)",
	   knot_strerror(iret));

	knot_rrset_clear(&opt_rr, NULL);
}

428
static void test_unique(void)
429 430 431 432 433 434 435
{
	int iret;
	bool bret;
	knot_rrset_t opt_rr;
	uint16_t new_opt_size, new_expected_len;
	uint8_t *reserved_data;

436 437 438 439 440
	static const uint8_t OPT_DATA[] = {
		0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
		0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
	};

441
	iret = knot_edns_init(&opt_rr, E_MAX_PLD, E_RCODE, E_VERSION, NULL);
442
	ok(iret == KNOT_EOK, "OPT RR unique: init");
443 444 445 446

	new_opt_size = 4;
	iret = knot_edns_reserve_unique_option(NULL, OPT_ID_3, new_opt_size,
	                                       &reserved_data, NULL);
447
	ok(iret == KNOT_EINVAL, "OPT RR unique: invalid parameter (ret = %s)",
448 449
	   knot_strerror(iret));

450 451 452 453
	/* Add non-existent into empty OPT RR. */
	new_opt_size = 4;
	new_expected_len = 8;
	bret = prepare_edns_data(&opt_rr, false);
454
	ok(bret, "OPT RR remove: internal data preparation");
455 456
	iret = knot_edns_reserve_unique_option(&opt_rr, OPT_ID_3, new_opt_size,
	                                       &reserved_data, NULL);
457
	bret = check_rdata(&opt_rr, new_expected_len,
458 459 460 461
	                   (const uint8_t *)"\xaa\xa3\x00\x04\x00\x00\x00\x00");
	ok(iret == KNOT_EOK && reserved_data != NULL && bret,
	   "OPT RR unique: reserve unique non-existent option into empty OPT RR (ret = %s)",
	   knot_strerror(iret));
462 463 464
	if (reserved_data == NULL) {
		return;
	}
465 466 467 468
	memcpy(reserved_data, OPT_DATA, new_opt_size);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa3\x00\x04\xe0\xe1\xe2\xe3");
	ok(bret, "OPT RR unique: check written option");
469 470 471 472

	/* Add non-existent option. */
	new_opt_size = 4;
	new_expected_len = 42;
473
	bret = prepare_edns_data(&opt_rr, true);
474
	ok(bret, "OPT RR remove: internal data preparation");
475 476 477 478 479 480 481 482 483 484 485
	iret = knot_edns_reserve_unique_option(&opt_rr, OPT_ID_3, new_opt_size,
	                                       &reserved_data, NULL);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
	                                    "\xaa\xa3\x00\x04\x00\x00\x00\x00");
	ok(iret == KNOT_EOK && reserved_data != NULL && bret,
	   "OPT RR unique: reserve unique non-existent option (ret = %s)",
	   knot_strerror(iret));
486 487 488
	if (reserved_data == NULL) {
		return;
	}
489
	memcpy(reserved_data, OPT_DATA, new_opt_size);
490 491 492 493 494 495 496 497 498 499 500
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
	                                    "\xaa\xa3\x00\x04\xe0\xe1\xe2\xe3");
	ok(bret, "OPT RR unique: check written option");

	/* Firs should be cleared, remaining with same id removed. */
	new_opt_size = 3;
	new_expected_len = 27;
501
	bret = prepare_edns_data(&opt_rr, true);
502
	ok(bret, "OPT RR remove: internal data preparation");
503 504 505 506 507 508 509 510 511
	iret = knot_edns_reserve_unique_option(&opt_rr, OPT_ID_1, new_opt_size,
	                                       &reserved_data, NULL);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\x00\x00\x00"
	                                    "\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(iret == KNOT_EOK && reserved_data != NULL && bret,
	   "OPT RR unique: reserve unique existent option (ret = %s)",
	   knot_strerror(iret));
512 513 514
	if (reserved_data == NULL) {
		return;
	}
515
	memcpy(reserved_data, OPT_DATA, new_opt_size);
516 517 518 519 520 521 522 523 524
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xe0\xe1\xe2"
	                                    "\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(bret, "OPT RR unique: check written option");

	/* First should be shortened, remaining with same id removed. */
	new_opt_size = 2;
	new_expected_len = 26;
525
	bret = prepare_edns_data(&opt_rr, true);
526
	ok(bret, "OPT RR remove: internal data preparation");
527 528 529 530 531 532 533 534 535
	iret = knot_edns_reserve_unique_option(&opt_rr, OPT_ID_1, new_opt_size,
	                                       &reserved_data, NULL);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x02\x00\x00"
	                                    "\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(iret == KNOT_EOK && reserved_data != NULL && bret,
	   "OPT RR unique: reserve unique existent option (ret = %s)",
	   knot_strerror(iret));
536 537 538
	if (reserved_data == NULL) {
		return;
	}
539
	memcpy(reserved_data, OPT_DATA, new_opt_size);
540 541 542 543 544 545 546 547 548
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x02\xe0\xe1"
	                                    "\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(bret, "OPT RR unique: check written option");

	/* First removed, placed into second place, last shoved. */
	new_opt_size = 6;
	new_expected_len = 30;
549
	bret = prepare_edns_data(&opt_rr, true);
550
	ok(bret, "OPT RR remove: internal data preparation");
551 552 553 554 555 556 557 558 559
	iret = knot_edns_reserve_unique_option(&opt_rr, OPT_ID_1, new_opt_size,
	                                       &reserved_data, NULL);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa1\x00\x06\x00\x00\x00\x00\x00\x00"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(iret == KNOT_EOK && reserved_data != NULL && bret,
	   "OPT RR unique: reserve unique existent option (ret = %s)",
	   knot_strerror(iret));
560 561 562
	if (reserved_data == NULL) {
		return;
	}
563
	memcpy(reserved_data, OPT_DATA, new_opt_size);
564 565 566 567 568 569 570 571 572
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa1\x00\x06\xe0\xe1\xe2\xe3\xe4\xe5"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(bret, "OPT RR unique: check written option");

	/* First removed, placed into second place, last left untouched. */
	new_opt_size = 10;
	new_expected_len = 34;
573
	bret = prepare_edns_data(&opt_rr, true);
574
	ok(bret, "OPT RR remove: internal data preparation");
575 576 577 578 579 580 581 582 583
	iret = knot_edns_reserve_unique_option(&opt_rr, OPT_ID_1, new_opt_size,
	                                       &reserved_data, NULL);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa1\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(iret == KNOT_EOK && reserved_data != NULL && bret,
	   "OPT RR unique: reserve unique existent option (ret = %s)",
	   knot_strerror(iret));
584 585 586
	if (reserved_data == NULL) {
		return;
	}
587
	memcpy(reserved_data, OPT_DATA, new_opt_size);
588 589 590 591 592 593 594 595 596
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa1\x00\x0a\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(bret, "OPT RR unique: check written option");

	/* Second cleared. */
	new_opt_size = 4;
	new_expected_len = 34;
597
	bret = prepare_edns_data(&opt_rr, true);
598
	ok(bret, "OPT RR remove: internal data preparation");
599 600 601 602 603 604 605 606 607 608
	iret = knot_edns_reserve_unique_option(&opt_rr, OPT_ID_0, new_opt_size,
	                                       &reserved_data, NULL);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa0\x00\x04\x00\x00\x00\x00"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(iret == KNOT_EOK && reserved_data != NULL && bret,
	   "OPT RR unique: reserve unique existent option (ret = %s)",
	   knot_strerror(iret));
609 610 611
	if (reserved_data == NULL) {
		return;
	}
612
	memcpy(reserved_data, OPT_DATA, new_opt_size);
613 614 615 616 617 618 619 620 621 622
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa0\x00\x04\xe0\xe1\xe2\xe3"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(bret, "OPT RR unique: check written option");

	/* Second shortened to zero, remaining shoved. */
	new_opt_size = 0;
	new_expected_len = 30;
623
	bret = prepare_edns_data(&opt_rr, true);
624
	ok(bret, "OPT RR remove: internal data preparation");
625 626 627 628 629 630 631 632 633 634 635 636 637 638
	iret = knot_edns_reserve_unique_option(&opt_rr, OPT_ID_0, new_opt_size,
	                                       &reserved_data, NULL);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa0\x00\x00"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7");
	ok(iret == KNOT_EOK && reserved_data != NULL && bret,
	   "OPT RR unique: reserve unique existent option (ret = %s)",
	   knot_strerror(iret));

	/* Second deleted, remaining shoved, new put last. */
	new_opt_size = 6;
	new_expected_len = 36;
639
	bret = prepare_edns_data(&opt_rr, true);
640
	ok(bret, "OPT RR remove: internal data preparation");
641 642 643 644 645 646 647 648 649 650
	iret = knot_edns_reserve_unique_option(&opt_rr, OPT_ID_0, new_opt_size,
	                                       &reserved_data, NULL);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
	                                    "\xaa\xa0\x00\x06\x00\x00\x00\x00\x00\x00");
	ok(iret == KNOT_EOK && reserved_data != NULL && bret,
	   "OPT RR unique: reserve unique existent option (ret = %s)",
	   knot_strerror(iret));
651 652 653
	if (reserved_data == NULL) {
		return;
	}
654
	memcpy(reserved_data, OPT_DATA, new_opt_size);
655 656 657 658 659 660 661 662 663 664
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x08\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
	                                    "\xaa\xa0\x00\x06\xe0\xe1\xe2\xe3\xe4\xe5");
	ok(bret, "OPT RR unique: check written option");

	/* Last shortened. */
	new_opt_size = 4;
	new_expected_len = 30;
665
	bret = prepare_edns_data(&opt_rr, true);
666
	ok(bret, "OPT RR remove: internal data preparation");
667 668 669 670 671 672 673 674 675 676
	iret = knot_edns_reserve_unique_option(&opt_rr, OPT_ID_2, new_opt_size,
	                                       &reserved_data, NULL);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x04\x00\x00\x00\x00");
	ok(iret == KNOT_EOK && reserved_data != NULL && bret,
	   "OPT RR unique: reserve unique existent option (ret = %s)",
	   knot_strerror(iret));
677 678 679
	if (reserved_data == NULL) {
		return;
	}
680
	memcpy(reserved_data, OPT_DATA, new_opt_size);
681 682 683 684 685 686 687 688 689 690
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x04\xe0\xe1\xe2\xe3");
	ok(bret, "OPT RR unique: check written option");

	/* Last enlarged. */
	new_opt_size = 10;
	new_expected_len = 36;
691
	bret = prepare_edns_data(&opt_rr, true);
692
	ok(bret, "OPT RR remove: internal data preparation");
693 694 695 696 697 698 699 700 701 702
	iret = knot_edns_reserve_unique_option(&opt_rr, OPT_ID_2, new_opt_size,
	                                       &reserved_data, NULL);
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
	ok(iret == KNOT_EOK && reserved_data != NULL && bret,
	   "OPT RR unique: reserve unique existent option (ret = %s)",
	   knot_strerror(iret));
703 704 705
	if (reserved_data == NULL) {
		return;
	}
706
	memcpy(reserved_data, OPT_DATA, new_opt_size);
707 708 709 710 711 712 713
	bret = check_rdata(&opt_rr, new_expected_len,
	                   (const uint8_t *)"\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa0\x00\x04\xf0\xf1\xf2\xf3"
	                                    "\xaa\xa1\x00\x03\xf0\xf1\xf2"
	                                    "\xaa\xa2\x00\x0a\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9");
	ok(bret, "OPT RR unique: check written option");

Jan Včelák's avatar
Jan Včelák committed
714
	knot_rrset_clear(&opt_rr, NULL);
715 716
}

717
static void test_alignment(void)
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
{
	int ret;

	ret = knot_edns_alignment_size(1, 1, 1);
	ok(ret == -1, "no alignment");

	ret = knot_edns_alignment_size(1, 1, 2);
	ok(ret == -1, "no alignment");

	ret = knot_edns_alignment_size(1, 1, 3);
	ok(ret == (6 - (1 + 1 + KNOT_EDNS_OPTION_HDRLEN)), "%i-Byte alignment", ret);

	ret = knot_edns_alignment_size(1, 1, 4);
	ok(ret == (8 - (1 + 1 + KNOT_EDNS_OPTION_HDRLEN)), "%i-Byte alignment", ret);

	ret = knot_edns_alignment_size(1, 1, 512);
	ok(ret == (512 - (1 + 1 + KNOT_EDNS_OPTION_HDRLEN)), "%i-Byte alignment", ret);
}

737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
static void test_keepalive(void)
{
	typedef struct {
		char *msg;
		uint16_t opt_len;
		char *opt;
		uint16_t val;
	} test_t;

	// OK tests.

	static const test_t TESTS[] = {
		{ "ok 0",     0, "",         0 },
		{ "ok 1",     2, "\x00\x01", 1 },
		{ "ok 258",   2, "\x01\x02", 258 },
		{ "ok 65535", 2, "\xFF\xFF", 65535 },
		{ NULL }
	};

	for (const test_t *t = TESTS; t->msg != NULL; t++) {
		size_t len = knot_edns_keepalive_size(t->val);
		ok(len == t->opt_len, "%s: %s, size", __func__, t->msg);

		uint8_t wire[8] = { 0 };
		int ret = knot_edns_keepalive_write(wire, sizeof(wire), t->val);
762
		is_int(KNOT_EOK, ret, "%s: %s, write, return", __func__, t->msg);
763 764 765 766 767
		ok(memcmp(wire, t->opt, t->opt_len) == 0, "%s: %s, write, value",
		                                          __func__, t->msg);

		uint16_t timeout = 0;
		ret = knot_edns_keepalive_parse(&timeout, (uint8_t *)t->opt, t->opt_len);
768
		is_int(KNOT_EOK, ret, "%s: %s, parse, return", __func__, t->msg);
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
		ok(timeout == t->val, "%s: %s, parse, value", __func__, t->msg);
	}

	// Error tests.

	uint8_t wire[8] = { 0 };
	ok(knot_edns_keepalive_write(NULL, 0, 0) == KNOT_EINVAL,
	   "%s: write, NULL", __func__);
	ok(knot_edns_keepalive_write(wire, 1, 1) == KNOT_ESPACE,
	   "%s: write, no room", __func__);

	uint16_t timeout = 0;
	ok(knot_edns_keepalive_parse(NULL, (const uint8_t *)"", 0) == KNOT_EINVAL,
	   "%s: parse, NULL", __func__);
	ok(knot_edns_keepalive_parse(&timeout, NULL, 0) == KNOT_EINVAL,
	   "%s: parse, NULL", __func__);
	ok(knot_edns_keepalive_parse(&timeout, (const uint8_t *)"\x01", 1) == KNOT_EMALF,
	   "%s: parse, malformed", __func__);
}

static void test_chain(void)
{
	typedef struct {
		char *msg;
		uint16_t opt_len;
		knot_dname_t *dname;
	} test_t;

	// OK tests.

	static const test_t TESTS[] = {
		{ ".",  1, (knot_dname_t *)"" },
		{ "a.", 3, (knot_dname_t *)"\x01" "a" },
		{ NULL }
	};

	for (const test_t *t = TESTS; t->msg != NULL; t++) {
		size_t len = knot_edns_chain_size(t->dname);
		ok(len == t->opt_len, "%s: dname %s, size", __func__, t->msg);

		uint8_t wire[8] = { 0 };
		int ret = knot_edns_chain_write(wire, sizeof(wire), t->dname);
811
		is_int(KNOT_EOK, ret, "%s: dname %s, write, return", __func__, t->msg);
812 813 814 815 816
		ok(memcmp(wire, t->dname, t->opt_len) == 0, "%s: dname %s, write, value",
		                                            __func__, t->msg);

		knot_dname_t *dname = NULL;
		ret = knot_edns_chain_parse(&dname, (uint8_t *)t->dname, t->opt_len);
817
		is_int(KNOT_EOK, ret, "%s: dname %s, parse, return", __func__, t->msg);
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
		ok(knot_dname_cmp(dname, t->dname) == 0, "%s: dname %s, parse, value",
		                                         __func__, t->msg);
		knot_dname_free(&dname, NULL);
	}

	// Error tests.

	ok(knot_edns_chain_size(NULL) == 0, "%s: size, NULL", __func__);

	uint8_t wire[8] = { 0 };
	ok(knot_edns_chain_write(NULL, 0, wire) == KNOT_EINVAL,
	   "%s: write, NULL", __func__);
	ok(knot_edns_chain_write(wire, 0, NULL) == KNOT_EINVAL,
	   "%s: write, NULL", __func__);
	ok(knot_edns_chain_write(wire, 0, (const knot_dname_t *)"") == KNOT_ESPACE,
	   "%s: write, no room", __func__);

	knot_dname_t *dname = NULL;
	ok(knot_edns_chain_parse(NULL, wire, 0) == KNOT_EINVAL,
	   "%s: parse, NULL", __func__);
	ok(knot_edns_chain_parse(&dname, NULL, 0) == KNOT_EINVAL,
	   "%s: parse, NULL", __func__);
	ok(knot_edns_chain_parse(&dname, (const uint8_t *)"\x01", 1) == KNOT_EMALF,
	   "%s: parse, malformed", __func__);
}

844 845
int main(int argc, char *argv[])
{
846
	plan_lazy();
847

848 849
	knot_rrset_t opt_rr;
	int ret = knot_edns_init(&opt_rr, E_MAX_PLD, E_RCODE, E_VERSION, NULL);
850
	is_int(KNOT_EOK, ret, "OPT RR: init");
Lubos Slovak's avatar
Lubos Slovak committed
851

852
	/* Check initialized values (no NSID yet). */
853
	check_header(&opt_rr, E_MAX_PLD, E_VERSION, 0, E_RCODE, "OPT RR: check header");
Lubos Slovak's avatar
Lubos Slovak committed
854

855 856
	test_setters(&opt_rr);
	test_getters(&opt_rr);
857
	test_remove();
858
	test_unique();
859
	test_alignment();
860 861
	test_keepalive();
	test_chain();
862

863
	knot_rrset_clear(&opt_rr, NULL);
Lubos Slovak's avatar
Lubos Slovak committed
864 865 866

	return 0;
}