Skip to content
Snippets Groups Projects
Commit 3305406c authored by Jan Včelák's avatar Jan Včelák :rocket:
Browse files

Merge branch 'Unique EDNS options reservation'

See merge request !555
parents a79e63da dbbe08cc
No related branches found
No related tags found
No related merge requests found
......@@ -187,6 +187,46 @@ void knot_edns_set_do(knot_rrset_t *opt_rr)
}
/*----------------------------------------------------------------------------*/
/*!
* \brief Skips an option from the supplied \a wire data.
*
* \param wire Wire data containing sequence of OPT RDATA.
* \param[out] code Code of the option that is at hand.
* \param[out] full_len Length of the entire skipped option with its header.
*
* \return Pointer to the first byte of the entire skipped option,
* NULL when none or incomplete option data left.
*/
static uint8_t *skip_option(wire_ctx_t *wire,
uint16_t *code, uint16_t *full_len)
{
assert(wire && code && full_len);
uint8_t *position = NULL;
if (wire_ctx_available(wire) > 0) {
position = wire->position;
uint16_t opt_code = wire_ctx_read_u16(wire);
if (wire->error != KNOT_EOK) {
return NULL;
}
uint16_t opt_len = wire_ctx_read_u16(wire);
wire_ctx_skip(wire, opt_len);
/*
* Return position only when the entire option fits
* in the RDATA.
*/
if (wire->error == KNOT_EOK) {
*code = opt_code;
*full_len = KNOT_EDNS_OPTION_HDRLEN + opt_len;
return position;
}
}
return NULL;
}
/*!
* \brief Find OPTION with the given code in the OPT RDATA.
*
......@@ -195,33 +235,21 @@ void knot_edns_set_do(knot_rrset_t *opt_rr)
*
* \param rdata RDATA to search in.
* \param opt_code Code of the OPTION to find.
* \param[out] pos Position of the OPTION or NULL if not found.
*
* \return Pointer to the first byte of the first option that matches
* \a opt_code, NULL if no option found or error occurred.
*/
static uint8_t *find_option(knot_rdata_t *rdata, uint16_t opt_code)
{
wire_ctx_t wire = wire_ctx_init_const(knot_rdata_data(rdata),
knot_rdata_rdlen(rdata));
uint8_t *found_position = NULL;
while (wire_ctx_available(&wire) > 0) {
found_position = wire.position;
uint16_t code = wire_ctx_read_u16(&wire);
if (wire.error != KNOT_EOK) {
break;
}
if (code != opt_code) {
found_position = NULL;
}
uint8_t *position = NULL;
uint16_t code, full_len;
uint16_t opt_len = wire_ctx_read_u16(&wire);
/* Return position only when the entire option fits
* in the RDATA. */
if (found_position != NULL && wire.error == KNOT_EOK &&
wire_ctx_available(&wire) >= opt_len) {
return found_position;
while ((position = skip_option(&wire, &code, &full_len)) != NULL) {
if (code == opt_code) {
return position;
}
wire_ctx_skip(&wire, opt_len);
}
return NULL;
......@@ -279,6 +307,85 @@ static uint8_t *edns_add(knot_rrset_t *opt, uint16_t code, uint16_t size,
return knot_rdata_data(knot_rdataset_at(&opt->rrs, 0)) + offset;
}
_public_
int knot_edns_reserve_unique_option(knot_rrset_t *opt_rr, uint16_t code,
uint16_t size, uint8_t **wire_ptr,
knot_mm_t *mm)
{
if (!opt_rr) {
return KNOT_EINVAL;
}
knot_rdata_t *rdata = knot_rdataset_at(&opt_rr->rrs, 0);
assert(rdata != NULL);
wire_ctx_t rd_wire = wire_ctx_init_const(knot_rdata_data(rdata),
knot_rdata_rdlen(rdata));
wire_ctx_t wr_wire = wire_ctx_init(knot_rdata_data(rdata),
knot_rdata_rdlen(rdata));
uint16_t deleted_len = 0; // Total area length acquired by deleting.
uint8_t *rd_pos = NULL;
uint8_t *wr_pos = NULL; // Set non-null if enough freed space found.
uint16_t opt_code, full_len;
/*
* Deletes all occurrences of options with given code. Shoves all
* remaining options towards beginning. Takes first opportunity to
* use freed space to reserve option.
*/
while ((rd_pos = skip_option(&rd_wire, &opt_code, &full_len)) != NULL) {
if (opt_code != code) {
if (deleted_len == 0) {
// No data must be shoved towards front.
wire_ctx_skip(&wr_wire, full_len);
assert(wr_wire.error == KNOT_EOK);
} else if (deleted_len >= full_len) {
// There is enough space for a copy.
wire_ctx_write(&wr_wire, rd_pos, full_len);
assert(wr_wire.error == KNOT_EOK);
} else {
// There isn't enough space for a copy.
memmove(knot_rdata_data(rdata) + wire_ctx_offset(&wr_wire),
rd_pos, full_len);
wire_ctx_skip(&wr_wire, full_len);
assert(wr_wire.error == KNOT_EOK);
}
} else {
deleted_len += full_len;
if (!wr_pos &&
deleted_len >= (KNOT_EDNS_OPTION_HDRLEN + size)) {
// Reserve this freed space.
wr_pos = knot_rdata_data(rdata) + wire_ctx_offset(&wr_wire);
deleted_len -= KNOT_EDNS_OPTION_HDRLEN + size;
wire_ctx_skip(&wr_wire, KNOT_EDNS_OPTION_HDRLEN + size);
}
}
}
if (deleted_len > 0) {
// Adjust data length.
assert(knot_rdata_rdlen(rdata) >= deleted_len);
knot_rdata_set_rdlen(rdata, knot_rdata_rdlen(rdata) - deleted_len);
}
if (wr_pos) {
// Found enough space when deleting entries.
wire_ctx_t wire = wire_ctx_init(wr_pos, KNOT_EDNS_OPTION_HDRLEN + size);
wire_ctx_write_u16(&wire, code);
wire_ctx_write_u16(&wire, size);
size_t offset = wire_ctx_offset(&wire);
wire_ctx_clear(&wire, size);
if (wire_ptr) {
*wire_ptr = wr_pos + offset;
}
} else {
return knot_edns_reserve_option(opt_rr, code, size, wire_ptr, mm);
}
return KNOT_EOK;
}
_public_
int knot_edns_reserve_option(knot_rrset_t *opt_rr, uint16_t code,
uint16_t size, uint8_t **wire_ptr, knot_mm_t *mm)
......@@ -292,7 +399,6 @@ int knot_edns_reserve_option(knot_rrset_t *opt_rr, uint16_t code,
return KNOT_ENOMEM;
}
memset(wire, 0, size);
if (wire_ptr) {
*wire_ptr = wire;
}
......
......@@ -217,6 +217,23 @@ bool knot_edns_do(const knot_rrset_t *opt_rr);
*/
void knot_edns_set_do(knot_rrset_t *opt_rr);
/*!
* \brief Adds EDNS option into the package with empty (zeroed) content.
*
* \note All other occurrences of the option type will be removed.
*
* \param[in] opt_rr OPT RR in the packet.
* \param[in] code Option code.
* \param[in] size Desired option size.
* \param[out] wire_ptr Pointer to reserved option data (can be NULL).
* \param[in] mm Memory context.
*
* \return Error code, KNOT_EOK if successful.
*/
int knot_edns_reserve_unique_option(knot_rrset_t *opt_rr, uint16_t code,
uint16_t size, uint8_t **wire_ptr,
knot_mm_t *mm);
/*!
* \brief Add EDNS option into the package with empty (zeroed) content.
*
......
......@@ -332,6 +332,313 @@ static bool test_setters(knot_rrset_t *opt_rr, int *done)
return success;
}
#define OPT_ID_0 0xaaa0
#define OPT_ID_1 0xaaa1
#define OPT_ID_2 0xaaa2
#define OPT_ID_3 0xaaa3
static bool prepare_edns_data(knot_rrset_t *opt_rr)
{
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);
static const uint8_t OPT_DATA[] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
};
ret = knot_edns_add_option(opt_rr, OPT_ID_1, 3, OPT_DATA, NULL);
if (ret != KNOT_EOK) {
return false;
}
ret = knot_edns_add_option(opt_rr, OPT_ID_0, 4, OPT_DATA, NULL);
if (ret != KNOT_EOK) {
return false;
}
ret = knot_edns_add_option(opt_rr, OPT_ID_1, 3, OPT_DATA, NULL);
if (ret != KNOT_EOK) {
return false;
}
ret = knot_edns_add_option(opt_rr, OPT_ID_2, 8, OPT_DATA, NULL);
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;
}
static bool test_unique(void)
{
int iret;
bool bret;
knot_rrset_t opt_rr;
uint16_t new_opt_size, new_expected_len;
uint8_t *reserved_data;
static const uint8_t OPT_DATA[] = {
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
};
iret = knot_edns_init(&opt_rr, E_MAX_PLD, E_RCODE, E_VERSION, NULL);
if (iret != KNOT_EOK) {
return false;
}
new_opt_size = 4;
iret = knot_edns_reserve_unique_option(NULL, OPT_ID_3, new_opt_size,
&reserved_data, NULL);
ok(iret == KNOT_EINVAL, "OPT RR unique: reserve unique option (ret = %s)",
knot_strerror(iret));
/* Test helper function for data preparation. */
bret = prepare_edns_data(&opt_rr);
new_expected_len = 34;
ok(bret, "OPT RR unique: 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 unique: data preparation");
/* Add non-existent option. */
new_opt_size = 4;
new_expected_len = 42;
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));
memcpy(reserved_data, OPT_DATA, new_opt_size);
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;
bret = prepare_edns_data(&opt_rr);
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));
memcpy(reserved_data, OPT_DATA, new_opt_size);
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;
bret = prepare_edns_data(&opt_rr);
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));
memcpy(reserved_data, OPT_DATA, new_opt_size);
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;
bret = prepare_edns_data(&opt_rr);
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));
memcpy(reserved_data, OPT_DATA, new_opt_size);
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;
bret = prepare_edns_data(&opt_rr);
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));
memcpy(reserved_data, OPT_DATA, new_opt_size);
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;
bret = prepare_edns_data(&opt_rr);
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));
memcpy(reserved_data, OPT_DATA, new_opt_size);
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;
bret = prepare_edns_data(&opt_rr);
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;
bret = prepare_edns_data(&opt_rr);
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));
memcpy(reserved_data, OPT_DATA, new_opt_size);
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;
bret = prepare_edns_data(&opt_rr);
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));
memcpy(reserved_data, OPT_DATA, new_opt_size);
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;
bret = prepare_edns_data(&opt_rr);
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));
memcpy(reserved_data, OPT_DATA, new_opt_size);
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");
knot_rrset_clear(&opt_rr, NULL);
return true;
}
static void test_client_subnet()
{
int ret;
......@@ -444,7 +751,7 @@ static void test_client_subnet()
"EDNS-client-subnet: parse (cmp addr)");
}
#define TEST_COUNT 68
#define TEST_COUNT 90
static inline int remaining(int done) {
return TEST_COUNT - done;
......@@ -486,6 +793,8 @@ int main(int argc, char *argv[])
goto exit;
}
test_unique();
/* EDNS client subnet */
test_client_subnet();
exit:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment