Support for proxy control option

This commit is contained in:
Philip Homburg 2022-08-18 12:02:47 +02:00
parent 3a71bd350e
commit 1f88087f92
7 changed files with 840 additions and 1 deletions

View File

@ -662,6 +662,38 @@ _getdns_upstreams_dereference(getdns_upstreams *upstreams)
GETDNS_FREE(upstreams->mf, upstreams); GETDNS_FREE(upstreams->mf, upstreams);
} }
static getdns_proxy_policies *
proxy_policies_create(getdns_context *context, size_t count)
{
getdns_proxy_policies *r = (void *) GETDNS_XMALLOC(context->mf, char,
sizeof(getdns_proxy_policies) +
sizeof(getdns_proxy_policy) * count);
#if 0
fprintf(stderr, "proxy_policies_create: alloc %d + %d * %d = %d\n",
sizeof(getdns_proxy_policies), count, sizeof(getdns_proxy_policy),
sizeof(getdns_proxy_policies) +
sizeof(getdns_proxy_policy) * count);
#endif
if (r) {
r->mf = context->mf;
r->referenced = 1;
r->count = count;
r->policy_opts = NULL;
}
return r;
}
void
_getdns_proxy_policies_dereference(getdns_proxy_policies *policies)
{
if (!policies || --policies->referenced > 0)
return;
GETDNS_FREE(policies->mf, policies);
}
static void static void
upstream_backoff(getdns_upstream *upstream) { upstream_backoff(getdns_upstream *upstream) {
upstream->conn_state = GETDNS_CONN_BACKOFF; upstream->conn_state = GETDNS_CONN_BACKOFF;
@ -1497,6 +1529,7 @@ getdns_context_create_with_extended_memory_functions(
} }
result->upstreams = NULL; result->upstreams = NULL;
result->proxy_policies = NULL;
result->edns_extended_rcode = 0; result->edns_extended_rcode = 0;
result->edns_version = 0; result->edns_version = 0;
@ -3071,6 +3104,713 @@ error:
} /* getdns_context_set_upstream_recursive_servers */ } /* getdns_context_set_upstream_recursive_servers */
/* Note: buf has to be 16-bit aligned */
static void proxy_policy2opt(getdns_proxy_policy *policy, int do_ipv6,
uint8_t *buf, size_t *sizep);
static void update_proxy_policy_opts(struct getdns_context *context)
{
int i, j, addr_count, policy_count, has_v4, has_v6;
size_t off, size;
getdns_proxy_policy *policy;
uint8_t *opt;
union
{
uint16_t u16; /* For alignment */
uint8_t buf[2048];
} buf;
/* Compute a list of proxy control options that correspond to
* the policy.
*/
/* Loop over policies. If an entry has both IPv4 and IPv6 addresses
* then we need 2 options.
*/
off= 0;
policy_count = context->proxy_policies->count;
fprintf(stderr, "update_proxy_policy_opts: policy_count %d\n", policy_count);
for (i = 0, policy = &context->proxy_policies->policies[0];
i<policy_count; i++, policy++)
{
/* Check whether we have IPv4 and/or IPv6 addresses */
has_v4= 0;
has_v6= 0;
addr_count= policy->addr_count;
for (j = 0; j<addr_count; j++)
{
if (policy->addrs[j].ss_family == AF_INET)
has_v4 = 1;
else if (policy->addrs[j].ss_family == AF_INET6)
has_v6 = 1;
else
{
/* Bad family. Ignore? */
}
}
if (has_v6 || addr_count == 0)
{
size= sizeof(buf)-off;
proxy_policy2opt(policy, 1 /*do_ipv6*/, buf.buf+off,
&size);
off= size;
}
if (has_v4)
{
size= sizeof(buf)-off;
proxy_policy2opt(policy, 0 /*!do_ipv6*/, buf.buf+off, &size);
off= size;
}
}
opt= NULL;
if (off)
{
opt = GETDNS_XMALLOC(context->my_mf, uint8_t, off);
memcpy(opt, buf.buf, off);
}
if (context->proxy_policies->policy_opts)
GETDNS_FREE(context->mf, context->proxy_policies->policy_opts);
context->proxy_policies->policy_opts= opt;
context->proxy_policies->policy_opts_size= off;
}
#define PROXY_POLICY_UNENCRYPTED (1 << 0)
#define PROXY_POLICY_UNAUTHENTICATED_ENCRYPTION (1 << 1)
#define PROXY_POLICY_AUTHENTICATED_ENCRYPTION (1 << 2)
#define PROXY_POLICY_PKIX_AUTH_REQUIRED (1 << 3)
#define PROXY_POLICY_DANE_AUTH_REQUIRED (1 << 4)
#define PROXY_POLICY_DEFAULT_DISALLOW_OTHER_TRANSPORTS (1 << 5)
#define PROXY_POLICY_A53 (1 << 6)
#define PROXY_POLICY_D53 (1 << 7)
#define PROXY_POLICY_AT (1 << 8)
#define PROXY_POLICY_DT (1 << 9)
#define PROXY_POLICY_AH2 (1 << 10)
#define PROXY_POLICY_DH2 (1 << 11)
#define PROXY_POLICY_AH3 (1 << 12)
#define PROXY_POLICY_DH3 (1 << 13)
#define PROXY_POLICY_AQ (1 << 14)
#define PROXY_POLICY_DQ (1 << 15)
#define PROXY_CONTROL_OPT_FLAGS1_UNENCRYPTED (1 << 15)
#define PROXY_CONTROL_OPT_FLAGS1_UNAUTHENTICATED_ENCRYPTION (1 << 14)
#define PROXY_CONTROL_OPT_FLAGS1_AUTHENTICATED_ENCRYPTION (1 << 13)
#define PROXY_CONTROL_OPT_FLAGS1_PKIX_AUTH_REQUIRED (1 << 12)
#define PROXY_CONTROL_OPT_FLAGS1_DANE_AUTH_REQUIRED (1 << 11)
#define PROXY_CONTROL_OPT_FLAGS1_DEFAULT_DISALLOW_OTHER_TRANSPORTS (1 << 10)
#define PROXY_CONTROL_OPT_FLAGS2_A53 (1 << 15)
#define PROXY_CONTROL_OPT_FLAGS2_D53 (1 << 14)
#define PROXY_CONTROL_OPT_FLAGS2_AT (1 << 13)
#define PROXY_CONTROL_OPT_FLAGS2_DT (1 << 12)
#define PROXY_CONTROL_OPT_FLAGS2_AH2 (1 << 11)
#define PROXY_CONTROL_OPT_FLAGS2_DH2 (1 << 10)
#define PROXY_CONTROL_OPT_FLAGS2_AH3 (1 << 9)
#define PROXY_CONTROL_OPT_FLAGS2_DH3 (1 << 8)
#define PROXY_CONTROL_OPT_FLAGS2_AQ (1 << 7)
#define PROXY_CONTROL_OPT_FLAGS2_DQ (1 << 6)
static struct proxy_control_flags
{
unsigned policy_flags;
uint16_t control_flags;
} proxy_control_flags1[] = {
{ PROXY_POLICY_UNENCRYPTED, PROXY_CONTROL_OPT_FLAGS1_UNENCRYPTED },
{ PROXY_POLICY_UNAUTHENTICATED_ENCRYPTION, PROXY_CONTROL_OPT_FLAGS1_UNAUTHENTICATED_ENCRYPTION },
{ PROXY_POLICY_AUTHENTICATED_ENCRYPTION, PROXY_CONTROL_OPT_FLAGS1_AUTHENTICATED_ENCRYPTION },
{ PROXY_POLICY_PKIX_AUTH_REQUIRED, PROXY_CONTROL_OPT_FLAGS1_PKIX_AUTH_REQUIRED },
{ PROXY_POLICY_DANE_AUTH_REQUIRED, PROXY_CONTROL_OPT_FLAGS1_DANE_AUTH_REQUIRED },
{ PROXY_POLICY_DEFAULT_DISALLOW_OTHER_TRANSPORTS, PROXY_CONTROL_OPT_FLAGS1_DEFAULT_DISALLOW_OTHER_TRANSPORTS },
{ 0, 0 }
}, proxy_control_flags2[] = {
{ PROXY_POLICY_A53, PROXY_CONTROL_OPT_FLAGS2_A53 },
{ PROXY_POLICY_D53, PROXY_CONTROL_OPT_FLAGS2_D53 },
{ PROXY_POLICY_AT, PROXY_CONTROL_OPT_FLAGS2_AT },
{ PROXY_POLICY_DT, PROXY_CONTROL_OPT_FLAGS2_DT },
{ PROXY_POLICY_AH2, PROXY_CONTROL_OPT_FLAGS2_AH2 },
{ PROXY_POLICY_DH2, PROXY_CONTROL_OPT_FLAGS2_DH2 },
{ PROXY_POLICY_AH3, PROXY_CONTROL_OPT_FLAGS2_AH3 },
{ PROXY_POLICY_DH3, PROXY_CONTROL_OPT_FLAGS2_DH3 },
{ PROXY_POLICY_AQ, PROXY_CONTROL_OPT_FLAGS2_AQ },
{ PROXY_POLICY_DQ, PROXY_CONTROL_OPT_FLAGS2_DQ },
{ 0, 0 }
};
#define SVC_KEY_ALPN 1
/* Note: this table must be kept sort based on 'value' */
struct
{
uint16_t value;
char *key;
} svckeys[] =
{
{ SVC_KEY_ALPN, "alpn" }, /* 1 */
{ 0, NULL }
};
static void proxy_policy2opt(getdns_proxy_policy *policy, int do_ipv6,
uint8_t *buf, size_t *sizep)
{
uint8_t inflen;
uint16_t flags1, flags2, key16, len16;
int i, j, addr_count;
ptrdiff_t len, totlen;
size_t datalen;
char *l, *dot, *key, *v, *vp, *comma;
uint8_t *bp, *addr_type, *addr_len, *addrp, *domainlenp,
*domainp, *svclenp, *svcp, *inflenp, *infp, *lastp, *endp,
*datap, *wire_keyp, *wire_lenp, *wire_datap;
uint16_t *codep, *lenp, *flags1p, *flags2p;
struct sockaddr_in *sin4p;
struct sockaddr_in6 *sin6p;
uint8_t wirebuf[256];
endp= buf + *sizep;
codep= (uint16_t *)buf;
*codep= htons(GLDNS_EDNS_PROXY_CONTROL);
lenp= &codep[1];
flags1p= &lenp[1];
flags2p= &flags1p[1];
addr_type= (uint8_t *)&flags2p[1];
addr_len= &addr_type[1];
/* Flags1 */
flags1 = 0;
for (i= 0; proxy_control_flags1[i].policy_flags != 0; i++)
{
if (policy->flags & proxy_control_flags1[i].policy_flags)
flags1 |= proxy_control_flags1[i].control_flags;
}
*flags1p= htons(flags1);
/* Flags2 */
flags2 = 0;
for (i= 0; proxy_control_flags2[i].policy_flags != 0; i++)
{
if (policy->flags & proxy_control_flags2[i].policy_flags)
flags2 |= proxy_control_flags2[i].control_flags;
}
*flags2p= htons(flags2);
/* Count the number of addresses to include */
addr_count= 0;
for (i= 0; i<policy->addr_count; i++)
{
if (!do_ipv6 && policy->addrs[i].ss_family == AF_INET)
addr_count++;
if (do_ipv6 && policy->addrs[i].ss_family == AF_INET6)
addr_count++;
}
if (addr_count)
{
if (do_ipv6)
{
*addr_type = 2;
*addr_len = addr_count * sizeof(struct in6_addr);
addrp= &addr_len[1];
if (endp - addrp < *addr_len)
{
fprintf(stderr,
"proxy_policy2opt: not enogh space\n");
abort();
}
for (i= 0; i<policy->addr_count; i++)
{
if (policy->addrs[i].ss_family != AF_INET6)
continue;
sin6p = (struct sockaddr_in6 *)
&policy->addrs[i];
memcpy(addrp, &sin6p->sin6_addr,
sizeof(sin6p->sin6_addr));
addrp += sizeof(sin6p->sin6_addr);
}
}
else
{
*addr_type = 1;
*addr_len = addr_count * sizeof(struct in_addr);
addrp= &addr_len[1];
if (endp - addrp < *addr_len)
{
fprintf(stderr,
"proxy_policy2opt: not enogh space\n");
abort();
}
for (i= 0; i<policy->addr_count; i++)
{
if (policy->addrs[i].ss_family != AF_INET)
continue;
sin4p = (struct sockaddr_in *)
&policy->addrs[i];
memcpy(addrp, &sin4p->sin_addr,
sizeof(sin4p->sin_addr));
addrp += sizeof(sin4p->sin_addr);
}
}
assert (addrp - &addr_len[1] == *addr_len);
domainlenp= addrp;
}
else
{
*addr_type = 0;
*addr_len = 0;
domainlenp= &addr_len[1];
}
if (endp - domainlenp < 1)
{
fprintf(stderr,
"proxy_policy2opt: not enough space\n");
abort();
}
*domainlenp = 0;
domainp= &domainlenp[1];
if (policy->domainname)
{
l= policy->domainname;
while(l)
{
dot= strchr(l, '.');
if (dot)
len= dot-l;
else
len= strlen(l);
if (len > 63)
{
fprintf(stderr,
"proxy_policy2opt: bad label length\n");
abort();
}
if (endp - domainlenp < 1)
{
fprintf(stderr,
"proxy_policy2opt: not enough space\n");
abort();
}
*domainp= len;
domainp++;
if (endp - domainlenp < len)
{
fprintf(stderr,
"proxy_policy2opt: not enough space\n");
abort();
}
memcpy(domainp, l, len);
domainp += len;
if (!dot)
break;
l= dot+1;
if (l[0] == '\0')
break;
}
/* Add trailing label */
if (endp - domainlenp < 1)
{
fprintf(stderr, "proxy_policy2opt: not enough space\n");
abort();
}
*domainp= 0;
domainp++;
*domainlenp = domainp - &domainlenp[1];
}
svclenp = domainp;
if (endp - svclenp < 1)
{
fprintf(stderr,
"proxy_policy2opt: not enough space\n");
abort();
}
*svclenp = 0;
svcp = &svclenp[1];
if (policy->svcparams[0].key != NULL)
{
for (i = 0; svckeys[i].key != NULL; i++)
{
key = svckeys[i].key;
for (j = 0; j<POLICY_N_SVCPARAMS; j++)
{
if (policy->svcparams[j].key == NULL)
break;
if (strcmp(key, policy->svcparams[j].key) == 0)
break;
}
if (j >= POLICY_N_SVCPARAMS ||
policy->svcparams[j].key == NULL)
{
/* Key not needed */
continue;
}
switch(svckeys[i].value)
{
case SVC_KEY_ALPN:
v = policy->svcparams[j].value;
if (v == NULL)
{
fprintf(stderr,
"proxy_policy2opt: value expected for alpn\n");
abort();
}
if (strlen(v) + 1 > sizeof(wirebuf))
{
fprintf(stderr,
"proxy_policy2opt: alpn list too long\n");
abort();
}
vp= v;
bp= wirebuf;
while (vp)
{
comma = strchr(vp, ',');
if (comma)
{
len = comma - vp;
*bp = len;
bp++;
memcpy(bp, vp, len);
bp += len;
vp = comma + 1;
continue;
}
len = strlen(vp);
*bp = len;
bp++;
memcpy(bp, vp, len);
bp += len;
break;;
}
datap = wirebuf;
datalen = bp-wirebuf;
break;
default:
fprintf(stderr,
"proxy_policy2opt: unknown svc key value\n");
abort();
}
totlen = 4 + datalen;
if (endp - svcp < totlen)
{
fprintf(stderr,
"proxy_policy2opt: not enough space\n");
abort();
}
wire_keyp = svcp;
wire_lenp = &wire_keyp[2];
wire_datap = &wire_lenp[2];
key16 = htons(svckeys[i].value);
memcpy(wire_keyp, &key16, 2);
len16 = htons(datalen);
memcpy(wire_lenp, &len16, 2);
if (datalen)
memcpy(wire_datap, datap, datalen);
svcp = wire_datap + datalen;
}
}
len = svcp - &svclenp[1];
if (len > 255)
{
fprintf(stderr, "svc too large\n");
abort();
}
*svclenp = len;
inflenp = svcp;
if (endp - inflenp < 1)
{
fprintf(stderr,
"proxy_policy2opt: not enough space\n");
abort();
}
inflen = 0;
if (policy->interface)
{
inflen= strlen(policy->interface);
}
*inflenp = inflen;
infp= &inflenp[1];
if (inflen)
{
if (endp - infp < inflen)
{
fprintf(stderr,
"proxy_policy2opt: not enough space\n");
abort();
}
memcpy(infp, policy->interface, inflen);
}
fprintf(stderr, "inflen %d, interface %s\n", inflen, policy->interface);
lastp = &infp[inflen];
*lenp = htons(lastp - (uint8_t *)flags1p);
*sizep= lastp - buf;
}
static struct policy_flags
{
char *name;
unsigned value;
} policy_flags[] = {
{ "unencrypted", PROXY_POLICY_UNENCRYPTED },
{ "unauthenticated-encryption", PROXY_POLICY_UNAUTHENTICATED_ENCRYPTION },
{ "authenticated-encryption", PROXY_POLICY_AUTHENTICATED_ENCRYPTION },
{ "pkix-auth-required", PROXY_POLICY_PKIX_AUTH_REQUIRED },
{ "dane-auth-required", PROXY_POLICY_DANE_AUTH_REQUIRED },
{ "default-disallow-other-transports", PROXY_POLICY_DEFAULT_DISALLOW_OTHER_TRANSPORTS },
{ "allow-do53", PROXY_POLICY_A53 },
{ "disallow-do53", PROXY_POLICY_D53 },
{ "allow-dot", PROXY_POLICY_AT },
{ "disallow-dot", PROXY_POLICY_DT },
{ "allow-doh2", PROXY_POLICY_AH2 },
{ "disallow-doh2", PROXY_POLICY_DH2 },
{ "allow-doh3", PROXY_POLICY_AH3 },
{ "disallow-doh3", PROXY_POLICY_DH3 },
{ "allow-doq", PROXY_POLICY_AQ },
{ "disallow-doq", PROXY_POLICY_DQ },
{ NULL, 0 }
};
getdns_return_t
getdns_context_set_local_proxy_policy(getdns_context *context,
getdns_dict *proxy_policy)
{
getdns_return_t r;
size_t count = 0, addr_count;
size_t i, j;
getdns_proxy_policies *policies;
getdns_list *resolvers;
struct sockaddr_in6 *sin6p;
fprintf(stderr, "in getdns_context_set_local_proxy_policy\n");
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
if (!proxy_policy) {
_getdns_proxy_policies_dereference(context->proxy_policies);
context->proxy_policies = NULL;
update_proxy_policy_opts(context);
dispatch_updated(context,
GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS);
return GETDNS_RETURN_GOOD;
}
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
if ((r = getdns_dict_get_list(proxy_policy, "resolvers",
&resolvers))) {
goto error;
}
if ((r = getdns_list_get_length(resolvers, &count))) {
goto error;
}
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
if (count == 0) {
_getdns_upstreams_dereference(context->upstreams);
context->upstreams = NULL;
dispatch_updated(context,
GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS);
return GETDNS_RETURN_GOOD;
}
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
policies = proxy_policies_create( context, count);
for (i = 0; i < count; i++) {
uint32_t u32;
size_t svccount;
char *key;
char *value;
getdns_dict *dict;
getdns_dict *addr_dict;
getdns_dict *svcparams;
getdns_list *addrs;
getdns_list *svcnames;
getdns_bindata *addr_type;
getdns_bindata *addr_data;
getdns_bindata *domain_name;
getdns_bindata *interface;
getdns_bindata *bindata;
if ((r = getdns_list_get_dict(resolvers, i, &dict))) {
dict = NULL;
goto error;
}
/* flags */
for (j= 0; policy_flags[j].name != NULL; j++)
{
if ((r = getdns_dict_get_int(dict,
policy_flags[j].name, &u32)) ==
GETDNS_RETURN_GOOD && u32 == 1)
policies->policies[i].flags |= policy_flags[j].value;
}
/* addrs */
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
if ((r = getdns_dict_get_list(
dict, "addrs", &addrs)) == GETDNS_RETURN_GOOD) {
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
if ((r = getdns_list_get_length(addrs, &addr_count)))
goto error;
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
if (addr_count > POLICY_N_ADDR)
goto error;
policies->policies[i].addr_count = addr_count;
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
for (j = 0; j < addr_count; j++) {
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
if ((r = getdns_list_get_dict(addrs, j,
&addr_dict))) {
goto error;
}
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
if ((r = getdns_dict_get_bindata(addr_dict,
"address-type", &addr_type))) {
goto error;
}
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
if ((r = getdns_dict_get_bindata(addr_dict,
"address-data", &addr_data))) {
goto error;
}
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
if (addr_type->size == 4 &&
memcmp(addr_type->data, "IPv4", 4)
== 0) {
if (addr_data->size != 4)
goto error;
fprintf(stderr, "getdns_context_set_local_proxy_policy: should store IPv4 address\n");
}
else if (addr_type->size == 4 &&
memcmp(addr_type->data, "IPv6", 4)
== 0) {
if (addr_data->size != 16)
goto error;
sin6p= (struct sockaddr_in6 *)
&policies->policies[i].addrs[j];
sin6p->sin6_family= AF_INET6;
memcpy(&sin6p->sin6_addr,
addr_data->data,
sizeof(sin6p->sin6_addr));
}
else
goto error;
}
}
else if (r != GETDNS_RETURN_NO_SUCH_DICT_NAME)
goto error;
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
/* domain-name */
if ((r = getdns_dict_get_bindata(
dict, "domain-name", &domain_name)) == GETDNS_RETURN_GOOD) {
policies->policies[i].domainname=
GETDNS_XMALLOC(context->mf,
char, domain_name->size+1);
memcpy(policies->policies[i].domainname,
domain_name->data, domain_name->size);
policies->policies[i].domainname[domain_name->size]=
'\0';
}
else if (r != GETDNS_RETURN_NO_SUCH_DICT_NAME)
goto error;
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
/* svcparams */
if ((r = getdns_dict_get_dict(
dict, "svcparams", &svcparams)) == GETDNS_RETURN_GOOD) {
getdns_dict_get_names(svcparams, &svcnames);
getdns_list_get_length(svcnames, &svccount);
if (svccount > POLICY_N_SVCPARAMS)
goto error;
for (j= 0; j<svccount; j++)
{
getdns_list_get_bindata(svcnames, j, &bindata);
key = _getdns_strdup2(&context->mf, bindata);
policies->policies[i].svcparams[j].key = key;
fprintf(stderr, "getdns_context_set_local_proxy_policy: key %s\n", key);
if (getdns_dict_get_int(svcparams, key, &u32)
== GETDNS_RETURN_GOOD)
{
/* Keywoard without value */
if (u32 != 1)
goto error;
policies->policies[i].svcparams[j].
value = NULL;
}
else if (getdns_dict_get_bindata(svcparams,
key, &bindata) == GETDNS_RETURN_GOOD)
{
value = _getdns_strdup2(&context->mf,
bindata);
policies->policies[i].svcparams[j].
value = value;
fprintf(stderr, "getdns_context_set_local_proxy_policy: value %s\n", value);
}
else
goto error;
}
}
else if (r != GETDNS_RETURN_NO_SUCH_DICT_NAME)
{
fprintf(stderr, "getdns_context_set_local_proxy_policy: r = %d\n", r);
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
goto error;
}
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
/* interface */
if ((r = getdns_dict_get_bindata(
dict, "interface", &interface)) == GETDNS_RETURN_GOOD) {
policies->policies[i].interface=
GETDNS_XMALLOC(context->mf,
char, interface->size+1);
memcpy(policies->policies[i].interface,
interface->data, interface->size);
policies->policies[i].interface[interface->size]= '\0';
}
else if (r != GETDNS_RETURN_NO_SUCH_DICT_NAME)
goto error;
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
}
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
_getdns_proxy_policies_dereference(context->proxy_policies);
context->proxy_policies = policies;
update_proxy_policy_opts(context);
dispatch_updated(context,
GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS);
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
return GETDNS_RETURN_GOOD;
#if 0
invalid_parameter:
_getdns_proxy_policies_dereference(policies);
return GETDNS_RETURN_INVALID_PARAMETER;
#endif
error:
_getdns_proxy_policies_dereference(policies);
fprintf(stderr, "getdns_context_set_local_proxy_policy: line %d\n", __LINE__);
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
}
/* /*
* getdns_context_unset_edns_maximum_udp_payload_size * getdns_context_unset_edns_maximum_udp_payload_size
* *

View File

@ -260,6 +260,22 @@ typedef struct getdns_upstream {
} getdns_upstream; } getdns_upstream;
#define POLICY_N_ADDR 3
#define POLICY_N_SVCPARAMS 8
typedef struct getdns_proxy_policy {
unsigned flags;
int addr_count;
struct sockaddr_storage addrs[POLICY_N_ADDR];
char *domainname;
struct
{
char *key;
char *value;
} svcparams[POLICY_N_SVCPARAMS];
char *interface;
} getdns_proxy_policy;
typedef struct getdns_log_config { typedef struct getdns_log_config {
getdns_logfunc_type func; getdns_logfunc_type func;
void *userarg; void *userarg;
@ -280,6 +296,15 @@ typedef struct getdns_upstreams {
getdns_upstream upstreams[]; getdns_upstream upstreams[];
} getdns_upstreams; } getdns_upstreams;
typedef struct getdns_proxy_policies {
struct mem_funcs mf;
size_t referenced;
size_t count;
uint8_t *policy_opts;
size_t policy_opts_size;
getdns_proxy_policy policies[];
} getdns_proxy_policies;
typedef enum tas_state { typedef enum tas_state {
TAS_LOOKUP_ADDRESSES = 0, TAS_LOOKUP_ADDRESSES = 0,
TAS_WRITE_GET_XML, TAS_WRITE_GET_XML,
@ -368,6 +393,8 @@ struct getdns_context {
getdns_tls_version_t tls_min_version; getdns_tls_version_t tls_min_version;
getdns_tls_version_t tls_max_version; getdns_tls_version_t tls_max_version;
getdns_proxy_policies *proxy_policies;
getdns_upstreams *upstreams; getdns_upstreams *upstreams;
uint16_t limit_outstanding_queries; uint16_t limit_outstanding_queries;
uint32_t dnssec_allowed_skew; uint32_t dnssec_allowed_skew;

View File

@ -1719,6 +1719,10 @@ getdns_return_t
getdns_context_set_upstream_recursive_servers(getdns_context *context, getdns_context_set_upstream_recursive_servers(getdns_context *context,
getdns_list *upstream_list); getdns_list *upstream_list);
getdns_return_t
getdns_context_set_local_proxy_policy(getdns_context *context,
getdns_dict *proxy_policy);
/** /**
* Set the maximum UDP payload size advertised in a EDNS0 OPT record. * Set the maximum UDP payload size advertised in a EDNS0 OPT record.
* When not set (the default), outgoing values will adhere to the suggestions * When not set (the default), outgoing values will adhere to the suggestions

View File

@ -441,7 +441,9 @@ enum gldns_enum_edns_option
GLDNS_EDNS_KEEPALIVE = 11, /* draft-ietf-dnsop-edns-tcp-keepalive*/ GLDNS_EDNS_KEEPALIVE = 11, /* draft-ietf-dnsop-edns-tcp-keepalive*/
GLDNS_EDNS_PADDING = 12, /* RFC7830 */ GLDNS_EDNS_PADDING = 12, /* RFC7830 */
GLDNS_EDNS_EDE = 15, /* RFC8914 */ GLDNS_EDNS_EDE = 15, /* RFC8914 */
GLDNS_EDNS_CLIENT_TAG = 16 /* draft-bellis-dnsop-edns-tags-01 */ GLDNS_EDNS_CLIENT_TAG = 16, /* draft-bellis-dnsop-edns-tags-01 */
GLDNS_EDNS_PROXY_CONTROL = 42 /* XXX unassigned */
}; };
typedef enum gldns_enum_edns_option gldns_edns_option; typedef enum gldns_enum_edns_option gldns_edns_option;

View File

@ -403,6 +403,51 @@ _getdns_network_req_add_upstream_option(getdns_network_req * req, uint16_t code,
return GETDNS_RETURN_GOOD; return GETDNS_RETURN_GOOD;
} }
/* add_upstream_options appends multiple options. */
getdns_return_t
_getdns_network_req_add_upstream_options(getdns_network_req * req, const void* data, size_t sz)
{
uint16_t oldlen;
uint32_t newlen;
uint32_t pktlen;
size_t cur_upstream_option_sz;
/* if no options are set, we can't add upstream options */
if (!req->opt)
return GETDNS_RETURN_GENERIC_ERROR;
/* if TCP, no overflow allowed for length field
https://tools.ietf.org/html/rfc1035#section-4.2.2 */
pktlen = req->response - req->query;
pktlen += sz;
if (pktlen > UINT16_MAX)
return GETDNS_RETURN_GENERIC_ERROR;
/* no overflow allowed for OPT size either (maybe this is overkill
given the above check?) */
oldlen = gldns_read_uint16(req->opt + 9);
newlen = oldlen + sz;
if (newlen > UINT16_MAX)
return GETDNS_RETURN_GENERIC_ERROR;
/* avoid overflowing MAXIMUM_UPSTREAM_OPTION_SPACE */
cur_upstream_option_sz = (size_t)oldlen - req->base_query_option_sz;
if (cur_upstream_option_sz + sz > MAXIMUM_UPSTREAM_OPTION_SPACE)
return GETDNS_RETURN_GENERIC_ERROR;
/* actually add the option: */
memcpy(req->opt + 11 + oldlen, data, sz);
gldns_write_uint16(req->opt + 9, newlen);
/* the response should start right after the options end: */
req->response = req->opt + 11 + newlen;
/* for TCP, adjust the size of the wire format itself: */
gldns_write_uint16(req->query - 2, pktlen);
return GETDNS_RETURN_GOOD;
}
size_t size_t
_getdns_network_req_add_tsig(getdns_network_req *req) _getdns_network_req_add_tsig(getdns_network_req *req)
{ {

View File

@ -177,6 +177,15 @@ attach_edns_cookie(getdns_network_req *req)
req, EDNS_COOKIE_OPCODE, 8, req->client_cookie); req, EDNS_COOKIE_OPCODE, 8, req->client_cookie);
} }
static getdns_return_t
attach_proxy_policies_opt(getdns_network_req *req)
{
fprintf(stderr, "attach_proxy_policies_opt: adding option\n");
return _getdns_network_req_add_upstream_options(req,
req->owner->context->proxy_policies->policy_opts,
req->owner->context->proxy_policies->policy_opts_size);
}
/* Will find a matching OPT RR, but leaves the caller to validate it /* Will find a matching OPT RR, but leaves the caller to validate it
*/ */
#define MATCH_OPT_FOUND 2 #define MATCH_OPT_FOUND 2
@ -845,6 +854,7 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq)
intptr_t query_id_intptr; intptr_t query_id_intptr;
int q = tcp_connected(netreq->upstream); int q = tcp_connected(netreq->upstream);
fprintf(stderr, "in stub_tcp_write\n");
if (q != 0) if (q != 0)
return q; return q;
@ -875,6 +885,10 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq)
if (netreq->owner->edns_client_subnet_private) if (netreq->owner->edns_client_subnet_private)
if (attach_edns_client_subnet_private(netreq)) if (attach_edns_client_subnet_private(netreq))
return STUB_OUT_OF_OPTIONS; return STUB_OUT_OF_OPTIONS;
fprintf(stderr, "stub_tcp_write: before proxy_policies check\n");
if (netreq->owner->context->proxy_policies)
if (attach_proxy_policies_opt(netreq))
return STUB_OUT_OF_OPTIONS;
if (netreq->upstream->queries_sent == 0 && if (netreq->upstream->queries_sent == 0 &&
netreq->owner->context->idle_timeout != 0) { netreq->owner->context->idle_timeout != 0) {
/* Add the keepalive option to the first query on this connection*/ /* Add the keepalive option to the first query on this connection*/
@ -1314,6 +1328,7 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp,
int q; int q;
fprintf(stderr, "in stub_tls_write\n");
if (netreq->owner->expires > upstream->expires) if (netreq->owner->expires > upstream->expires)
upstream->expires = netreq->owner->expires; upstream->expires = netreq->owner->expires;
@ -1569,6 +1584,7 @@ stub_udp_write_cb(void *userarg)
ssize_t written; ssize_t written;
DEBUG_STUB("%s %-35s: MSG: %p \n", STUB_DEBUG_WRITE, DEBUG_STUB("%s %-35s: MSG: %p \n", STUB_DEBUG_WRITE,
__FUNC__, (void *)netreq); __FUNC__, (void *)netreq);
fprintf(stderr, "in stub_udp_write_cb\n");
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event); GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
@ -1588,6 +1604,9 @@ stub_udp_write_cb(void *userarg)
if (netreq->owner->edns_client_subnet_private) if (netreq->owner->edns_client_subnet_private)
if (attach_edns_client_subnet_private(netreq)) if (attach_edns_client_subnet_private(netreq))
return; /* too many upstream options */ return; /* too many upstream options */
if (netreq->owner->context->proxy_policies)
if (attach_proxy_policies_opt(netreq))
return; /* too many upstream options */
} }
pkt_len = _getdns_network_req_add_tsig(netreq); pkt_len = _getdns_network_req_add_tsig(netreq);
if ((ssize_t)pkt_len != (written = sendto( if ((ssize_t)pkt_len != (written = sendto(

View File

@ -445,6 +445,8 @@ void _getdns_dns_req_free(getdns_dns_req * req);
/* network request utils */ /* network request utils */
getdns_return_t _getdns_network_req_add_upstream_option(getdns_network_req * req, getdns_return_t _getdns_network_req_add_upstream_option(getdns_network_req * req,
uint16_t code, uint16_t sz, const void* data); uint16_t code, uint16_t sz, const void* data);
getdns_return_t _getdns_network_req_add_upstream_options(getdns_network_req * req,
const void* data, size_t sz);
void _getdns_network_req_clear_upstream_options(getdns_network_req * req); void _getdns_network_req_clear_upstream_options(getdns_network_req * req);
/* Adds TSIG signature (if needed) and returns query length */ /* Adds TSIG signature (if needed) and returns query length */