if (edns.opt_list_in &&
(cookie = edns_list_get_option(edns.opt_list_in,
LDNS_EDNS_COOKIE))){
+ struct sockaddr_storage bound_addr;
+ socklen_t bound_addrlen = sizeof(struct sockaddr);
+
+ if(getsockname(qstate->reply->c->fd,
+ (struct sockaddr *) &bound_addr,
+ &bound_addrlen) != -1) {
+
+ log_addr(VERB_DETAIL, "!!!!! iterator:udp socket:", &bound_addr, bound_addrlen);
+ } else {
+ bound_addrlen = 0;
+ }
/* verify this is a 'complete cookie' (client+server)
* (RFC9018) with the length and store the complete
* cookie in the infra_cache. Do nothing when the cookie
if (cookie->opt_len == 24 &&
infra_set_server_cookie(qstate->env->infra_cache,
&qstate->reply->addr, qstate->reply->addrlen,
- iq->dp->name, iq->dp->namelen, cookie) >= 0) {
+ iq->dp->name, iq->dp->namelen, &bound_addr,
+ bound_addrlen, cookie) >= 0) {
/* log_hex() uses the verbosity levels of verbose() */
log_hex("complete cookie: ", cookie->opt_data,
cookie->opt_len);
}
}
- /* Copy the edns options we may got from the back end */
+ /* Copy the edns options we may have gotten from the back end */
if(edns.opt_list_in) {
qstate->edns_opts_back_in = edns_opt_copy_region(edns.opt_list_in,
qstate->region);
return 1;
}
+/** find the bound addr in the list of interfaces */
+static int
+get_bound_ip_if(struct outside_network* outnet,
+ struct sockaddr_storage bound_addr, socklen_t bound_addrlen,
+ struct port_if** pif_return)
+{
+ int i = 0;
+ struct port_if* pif_list;
+ int pif_list_len;
+
+ if(addr_is_ip6(&bound_addr, bound_addrlen)) {
+ pif_list = outnet->ip6_ifs;
+ pif_list_len = outnet->num_ip6;
+ } else {
+ pif_list = outnet->ip4_ifs;
+ pif_list_len = outnet->num_ip4;
+ }
+
+ for (i = 0; i < pif_list_len; i++) {
+ struct port_if *iface = &pif_list[i];
+
+ if (iface->addrlen == bound_addrlen &&
+ memcmp(&iface->addr, &bound_addr, bound_addrlen)) {
+ *pif_return = iface;
+ return 1;
+ }
+ }
+ return 0;
+}
+
int
infra_get_cookie(struct infra_cache* infra, struct sockaddr_storage* addr,
socklen_t addrlen, uint8_t* name, size_t namelen,
- time_t timenow, struct edns_cookie* cookie)
+ time_t timenow, struct outside_network* outnet, struct port_if** pif,
+ struct edns_cookie* cookie)
{
struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
name, namelen, 1);
data = (struct infra_data*) e->data;
+ if (data->cookie.state == SERVER_COOKIE_LEARNED) {
+ // lookup bound interface
+ log_err("!!!!! infra_get_cookie:bound_addrlen: %d", data->cookie.bound_addrlen);
+ log_addr(VERB_OPS, "!!!!! infra_get_cookie:bound_addr, len:", &data->cookie.bound_addr,
+ data->cookie.bound_addrlen);
+
+ if (!(get_bound_ip_if(outnet, data->cookie.bound_addr,
+ data->cookie.bound_addrlen, pif))) {
+ log_err("!!!!!!!! creating new cookie for changed interface");
+ data_entry_init(infra, e, timenow);
+ }
+ }
+
+ // @TODO uggo: data has changed
+ data = (struct infra_data*) e->data;
+
memcpy(cookie, &data->cookie, sizeof(struct edns_cookie));
if(needtoinsert) {
int
infra_set_server_cookie(struct infra_cache* infra, struct sockaddr_storage* addr,
- socklen_t addrlen, uint8_t* name, size_t namelen, struct edns_option* cookie)
+ socklen_t addrlen, uint8_t* name, size_t namelen,
+ struct sockaddr_storage* bound_addr, socklen_t bound_addrlen,
+ struct edns_option* cookie)
{
struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
name, namelen, 1);
return -1;
}
+ if (!(data->cookie.bound_addrlen == bound_addrlen) &&
+ memcpy(&data->cookie.bound_addr, bound_addr, bound_addrlen)){
+
+ // @TODO do something? this _should_ only happen on reloads?
+ }
+
/* the server cookie has changed, but the client cookie has not
* so we update the server cookie */
if (memcmp(data->cookie.data.cookie+8,
/* store the server cookie */
memcpy(data->cookie.data.cookie, cookie->opt_data, 24);
data->cookie.state = SERVER_COOKIE_LEARNED;
-
+ if (bound_addrlen > 0) {
+ memcpy(&data->cookie.bound_addr, bound_addr, bound_addrlen);
+ data->cookie.bound_addrlen = bound_addrlen;
+ }
verbose(VERB_QUERY, "storing received server cookie from upstream");
lock_rw_unlock(&e->lock);
return 1;
#include "util/rtt.h"
#include "util/netevent.h"
#include "util/data/msgreply.h"
+#include "services/outside_network.h"
struct slabhash;
struct config_file;
};
/**
- * Structure for an EDNS cookie (RFC9018) and it's internal state
+ * Structure for an EDNS cookie (RFC9018), it's internal state, and the
+ * the outgoing address that we bind this cookie to for privacy (RFC9018)
*/
struct edns_cookie {
enum edns_cookie_state state;
struct edns_cookie_data data;
+ struct sockaddr_storage bound_addr;
+ socklen_t bound_addrlen;
};
-
/**
* Host information kept for every server, per zone.
*/
*/
int infra_get_cookie(struct infra_cache* infra, struct sockaddr_storage* addr,
socklen_t addrlen, uint8_t* name, size_t namelen,
- time_t timenow, struct edns_cookie* cookie);
+ time_t timenow, struct outside_network* outnet, struct port_if** pif,
+ struct edns_cookie* cookie);
/**
* Find the cookie entry in the cache and update it with to make a 'complete cookie'
* @param name: name of zone
* @param namelen: length of name
* @param timenow: what time it is now.
+ * @param bound_addr: the outgoing address that we bind to this cookie
+ * @param bound_addr: the length of the bound address
* @param cookie: the EDNS cookie option we want to store.
* @return -1 if the wrong client cookie is found, 0 if the entry isn't found in
* the cache and a new one is inserted, 1 if the complete cookie is inserted
* or unchanged.
*/
int infra_set_server_cookie(struct infra_cache* infra, struct sockaddr_storage* addr,
- socklen_t addrlen, uint8_t* name, size_t namelen, struct edns_option* cookie);
-
+ socklen_t addrlen, uint8_t* name, size_t namelen,
+ struct sockaddr_storage* bound_addr, socklen_t bound_addrlen,
+ struct edns_option* cookie);
/**
* Get Lameness information and average RTT if host is in the cache.
pend->pkt_len = 0;
log_assert(!pend->sq->busy);
pend->sq->busy = 1;
+
if(!randomize_and_send_udp(pend, outnet->udp_buff,
pend->timeout)) {
/* callback error on pending */
return 1;
}
-
/** Select random interface and port */
static int
select_ifport(struct outside_network* outnet, struct pending* pend,
log_assert(outnet->unused_fds);
tries = 0;
while(1) {
- my_if = ub_random_max(outnet->rnd, num_if);
- pif = &ifs[my_if];
+ /* if we have a bound IP address for the EDNS cookie in the
+ * message, use that interface */
+ if (!(pend->sq->bound_interface)) {
+ my_if = ub_random_max(outnet->rnd, num_if);
+ pif = &ifs[my_if];
+ } else {
+ pif = pend->sq->bound_interface;
+ log_err("!!!!! select_ifport:bound_addrlen: %d", pif->addrlen);
+ log_addr(VERB_OPS, "!!!!! select_ifport:bound_addrlen:", &pif->addr, pif->addrlen);
+ }
+
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
if(outnet->udp_connect) {
/* if we connect() we cannot reuse fds for a port */
break;
}
}
+
+ log_err("!!!!! pif->inuse: %d, pif->maxout: %d", pif->inuse, pif->maxout);
+
/* try to open new port, if fails, loop to try again */
log_assert(pif->inuse < pif->maxout);
portno = pif->avail_ports[my_port - pif->inuse];
pend->cb = cb;
pend->cb_arg = cb_arg;
pend->node.key = pend;
+
pend->timer = comm_timer_create(sq->outnet->base, pending_udp_timer_cb,
pend);
if(!pend->timer) {
char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen,
uint8_t* zone, size_t zonelen, int qtype, struct edns_option* opt_list,
size_t pad_queries_block_size, struct alloc_cache* alloc,
- struct regional* region)
+ struct port_if* bound_interface, struct regional* region)
{
struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq));
struct timeval t;
sq->status = serviced_initial;
sq->retry = 0;
sq->to_be_deleted = 0;
+ sq->bound_interface = bound_interface;
sq->padding_block_size = pad_queries_block_size;
#ifdef UNBOUND_DEBUG
ins =
struct edns_option* backed_up_opt_list = qstate->edns_opts_back_out;
struct edns_option* per_upstream_opt_list = NULL;
time_t timenow = 0;
+ struct port_if* pif;
/* If we have an already populated EDNS option list make a copy since
* we may now add upstream specific EDNS options. */
if (env->cfg->upstream_cookies &&
infra_get_cookie(env->infra_cache, addr, addrlen, zone, zonelen,
- *env->now, &cookie)) {
+ *env->now, outnet, &pif, &cookie)) {
if (cookie.state == SERVER_COOKIE_LEARNED) {
/* We known the complete cookie, so we attach it */
edns_opt_list_append(&per_upstream_opt_list, LDNS_EDNS_COOKIE,
8, cookie.data.cookie, region);
} /* We ignore COOKIE_NOT_SUPPORTED */
+
}
serviced_gen_query(buff, qinfo->qname, qinfo->qname_len, qinfo->qtype,
per_upstream_opt_list,
( ssl_upstream && env->cfg->pad_queries
? env->cfg->pad_queries_block_size : 0 ),
- env->alloc, region);
+ env->alloc, pif, region);
if(!sq) {
if(check_ratelimit) {
infra_ratelimit_dec(env->infra_cache,
struct comm_timer* timer;
/** true if serviced_query is currently doing net I/O and may block */
int busy;
+ /** Interface bound to the EDNS cookie @TODO fix this */
+ struct port_if* bound_interface;
};
/**
qstate->edns_opts_back_out;
struct edns_option* per_upstream_opt_list = NULL;
struct edns_cookie cookie;
+ struct port_if* pif;
+
/* If we have an already populated EDNS option list make a copy
* since we may now add upstream specific EDNS options. */
if(qstate->edns_opts_back_out) {
if (qstate->env->cfg->upstream_cookies &&
infra_get_cookie(env->infra_cache, addr, addrlen,
- zone, zonelen, *env->now, &cookie)) {
+ zone, zonelen, *env->now, outnet, &pif, &cookie)) {
if (cookie.state == SERVER_COOKIE_LEARNED) {
/* We known the complete cookie, so we attach it */