Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/zone.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@ extern "C" {
#define ZONE_SVC_PARAM_KEY_DOCPATH (10u)
/** PvD configuration is available at the well-known path @draft{ietf, intarea-proxy-config} */
#define ZONE_SVC_PARAM_KEY_PVD (11u)
/** Per-transport operator confidence in serving the nameserver's query load over that transport, as a percentage @draft{johani, dnsop-svcb-oots} */
#define ZONE_SVC_PARAM_KEY_OOTS (12u)
/** Reserved ("invalid key") @rfc{9460} */
#define ZONE_SVC_PARAM_KEY_INVALID_KEY (65535u)
/** @} */
Expand Down
78 changes: 76 additions & 2 deletions src/generic/svcb.h
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,76 @@ static int32_t parse_docpath(
return parse_alpn(parser, type, field, key, param, rdata, token);
}

nonnull_all
static int32_t parse_oots(
parser_t *parser,
const type_info_t *type,
const rdata_info_t *field,
uint16_t key,
const svc_param_info_t *param,
rdata_t *rdata,
const token_t *token)
{
const char *t = token->data, *te = token->data + token->length;
const uint8_t *rdata_start = rdata->octets;

(void)field;
(void)key;
(void)param;

while (t < te) {
const char *transport = t;
const uint8_t *transport_out = rdata->octets;
const char *colon = memchr(t, ':', (size_t)(te - t));

if (!colon)
SYNTAX_ERROR(parser, "No colon found in oots DNS transport in %s", NAME(type));

if(colon == t)
SYNTAX_ERROR(parser, "DNS transport name must have at least 1 character in %s", NAME(type));

if (rdata->octets + (colon - t) + 2 > rdata->limit)
SYNTAX_ERROR(parser, "No space for oots DNS transport in %s", NAME(type));

if (colon - t > 255)
SYNTAX_ERROR(parser, "DNS transport name too large in %s", NAME(type));

*rdata->octets++ = (uint8_t)(colon - t);
memcpy(rdata->octets, transport, (size_t)(colon - t));
rdata->octets += (colon - t);

t = colon + 1;
uint64_t number = 0;
for (;; t++) {
const uint64_t digit = (uint8_t)*t - '0';
if (digit > 9)
break;
number = number * 10 + digit;
}
if(t == colon +1)
SYNTAX_ERROR(parser, "Oots percentage missing in %s", NAME(type));
if (number > 100)
SYNTAX_ERROR(parser, "Invalid oots percentage in %s", NAME(type));

*rdata->octets++ = (uint8_t)number;

const uint8_t *g;
for (g = rdata_start; g < transport_out; g += ((size_t)(*g)) + 2) {
if (memcmp(g, transport_out, ((size_t)(*transport_out)) + 1) == 0)
SEMANTIC_ERROR(parser, "Duplicate DNS transport in oots in %s", NAME(type));
}
if (*t != ',')
break;
else
t++;
}

if (t != te || rdata->octets > rdata->limit)
SYNTAX_ERROR(parser, "Invalid oots in %s", NAME(type));
return 0;
}


nonnull_all
static int32_t parse_mandatory_lax(
parser_t *parser,
Expand Down Expand Up @@ -498,6 +568,8 @@ static const svc_param_info_t svc_params[] = {
SVC_PARAM("docpath", 10u, OPTIONAL_VALUE, parse_docpath, parse_docpath),
// draft-ietf-intarea-proxy-config-13 section 2.1:
SVC_PARAM("pvd", 11u, NO_VALUE, parse_unknown, parse_unknown),
// draft-johani-dnsop-svcb-oots
SVC_PARAM("oots", 12u, MANDATORY_VALUE, parse_oots, parse_oots),
};

static const svc_param_info_t unknown_svc_param =
Expand Down Expand Up @@ -559,14 +631,16 @@ static really_inline size_t scan_svc_param(
return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_IPV6HINT)]), 8;
else if (memcmp(data, "dohpath", 7) == 0)
return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_DOHPATH)]), 7;
else if (memcmp(data, "docpath", 7) == 0)
return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_DOCPATH)]), 7;
else if (memcmp(data, "ohttp", 5) == 0)
return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_OHTTP)]), 5;
else if (memcmp(data, "tls-supported-groups", 20) == 0)
return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_TLS_SUPPORTED_GROUPS)]), 20;
else if (memcmp(data, "docpath", 7) == 0)
return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_DOCPATH)]), 7;
else if (memcmp(data, "pvd", 3) == 0)
return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_PVD)]), 3;
else if (memcmp(data, "oots", 4) == 0)
return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_OOTS)]), 4;
else if (memcmp(data, "key", 3) == 0)
return scan_unknown_svc_param_key(data, key, param);
else
Expand Down
90 changes: 82 additions & 8 deletions tests/svcb.c
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@ static const rdata_t docpath_s4_rdata =

// From the draft-ietf-intarea-proxy-config-13 Section-2.1 Discovery via HTTPS/SVCB Records:
static const char pvd_s1_text[] =
PAD("pvd-s1. 3600 IN HTTPS 1 . alpn=\"h3,h2\" pvd");
PAD("pvd-s1 3600 IN HTTPS 1 . alpn=\"h3,h2\" pvd");
static const rdata_t pvd_s1_rdata =
RDATA(
0x00, 0x01, // priority
Expand All @@ -822,7 +822,58 @@ static const rdata_t pvd_s1_rdata =
);

static const char pvd_f1_text[] =
PAD("pvd-f1. 3600 IN HTTPS 1 . alpn=\"h3,h2\" pvd=\"something\"");
PAD("pvd-f1 3600 IN HTTPS 1 . alpn=\"h3,h2\" pvd=\"something\"");

static const char oots_s1_text[] =
PAD("oots-s1 300 IN SVCB 1 . oots=\"do53:100,dot:10\"");

static const rdata_t oots_s1_rdata =
RDATA(
0x00, 0x01, // priority
0x00, // target
0x00, 0x0c, 0x00, 0x0b, 0x04, 'd', 'o', '5', '3', 100, 0x03, 'd', 'o', 't', 10 // outs="do53:100,dot:10"
);

static const char oots_s2_text[] =
PAD("oots-s2 300 IN SVCB 1 . oots=\"do53:100,dot:5,doq:5\"");

static const rdata_t oots_s2_rdata =
RDATA(
0x00, 0x01, // priority
0x00, // target
0x00, 0x0c, 0x00, 0x10, 0x04, 'd', 'o', '5', '3', 100, 0x03, 'd', 'o', 't', 5, 0x03, 'd', 'o', 'q', 5 // outs="do53:100,dot:5,doq:5"
);

static const char oots_s3_text[] =
PAD("oots-s3 300 IN SVCB 1 . oots=\"do53:100,dot:25,doh:10,doq:10\"");

static const rdata_t oots_s3_rdata =
RDATA(
0x00, 0x01, // priority
0x00, // target
0x00, 0x0c, 0x00, 0x15, 0x04, 'd', 'o', '5', '3', 100, 0x03, 'd', 'o', 't', 25, 0x03, 'd', 'o', 'h', 10, 0x03, 'd', 'o', 'q', 10// outs="do53:100,dot:25,doh:10,doq:10"
);

// Missing percentage
static const char oots_f1_text[] =
PAD("oots-f1 300 IN SVCB 1 . oots=\"do53:100,dot\"");

// Missing transport
static const char oots_f2_text[] =
PAD("oots-f2 300 IN SVCB 1 . oots=\"do53:100,:25\"");

// Invalid percentage
static const char oots_f3_text[] =
PAD("oots-f3 300 IN SVCB 1 . oots=\"do53:100,dot:101\"");

// Invalid percentage
static const char oots_f4_text[] =
PAD("oots-f4 300 IN SVCB 1 . oots=\"do53:100,dot:25%\"");

// Duplicate transport
static const char oots_f5_text[] =
PAD("oots-f5 300 IN SVCB 1 . oots=\"do53:100,dot:25,dot:10\"");


// FIXME: make a test that verifies correct behavior for no-default-alpn="some value"

Expand Down Expand Up @@ -924,7 +975,15 @@ static const test_t tests[] = {
{ false, ZONE_TYPE_SVCB, 0, docpath_s3_text, &docpath_s3_rdata },
{ false, ZONE_TYPE_SVCB, 0, docpath_s4_text, &docpath_s4_rdata },
{ false, ZONE_TYPE_HTTPS, 0, pvd_s1_text, &pvd_s1_rdata },
{ false, ZONE_TYPE_HTTPS, ZONE_SEMANTIC_ERROR, pvd_f1_text, NULL }
{ false, ZONE_TYPE_HTTPS, ZONE_SEMANTIC_ERROR, pvd_f1_text, NULL },
{ false, ZONE_TYPE_SVCB, 0, oots_s1_text, &oots_s1_rdata },
{ false, ZONE_TYPE_SVCB, 0, oots_s2_text, &oots_s2_rdata },
{ false, ZONE_TYPE_SVCB, 0, oots_s3_text, &oots_s3_rdata },
{ false, ZONE_TYPE_SVCB, ZONE_SYNTAX_ERROR, oots_f1_text, NULL },
{ false, ZONE_TYPE_SVCB, ZONE_SYNTAX_ERROR, oots_f2_text, NULL },
{ false, ZONE_TYPE_SVCB, ZONE_SYNTAX_ERROR, oots_f3_text, NULL },
{ false, ZONE_TYPE_SVCB, ZONE_SYNTAX_ERROR, oots_f4_text, NULL },
{ false, ZONE_TYPE_SVCB, ZONE_SEMANTIC_ERROR, oots_f5_text, NULL }
};

static int32_t add_rr(
Expand All @@ -942,15 +1001,30 @@ static int32_t add_rr(
(void)owner;
(void)class;
(void)ttl;
if (type != test->type)
if (type != test->type) {
fprintf(stderr, "type mismatch\n");
return ZONE_SYNTAX_ERROR;
}
if (test->code != ZONE_SUCCESS)
return ZONE_SUCCESS;
if (rdlength != test->rdata->length || !test->rdata->octets)
return ZONE_SYNTAX_ERROR;
if (memcmp(rdata, test->rdata->octets, rdlength) != 0)
return ZONE_SYNTAX_ERROR;
return ZONE_SUCCESS;
fprintf( stderr, "rdata length did not match %d != %d"
, (int)rdlength, (int)test->rdata->length);
else if (memcmp(rdata, test->rdata->octets, rdlength) != 0)
fprintf(stderr, "rdata bytes did not match");
else
return ZONE_SUCCESS;

size_t i;
for (i = 0; i < rdlength; i++) {
if (i % 16 == 0)
fprintf(stderr, "\n");
else if (i % 8 == 0)
fprintf(stderr, " ");
fprintf(stderr, " %.2x", rdata[i]);
}
fprintf(stderr, "\n");
return ZONE_SYNTAX_ERROR;
}

static uint8_t origin[] =
Expand Down
Loading