struct sockaddr_storage addr_fake;
socklen_t addr_fake_len = 0;
-
if (!ipstrtoaddr("0.0.0.0", 0, &addr_any, &addr_any_len)) {
- // @TODO do something
+ /* this shouldn't fail */
+ return 0;
}
- if (!ipstrtoaddr("10.10.1.1", 0, &addr_fake, &addr_fake_len)) {
- // @TODO do something
- }
+ // if (!ipstrtoaddr("10.10.1.1", 0, &addr_fake, &addr_fake_len)) {
+ // // @TODO do something
+ // }
log_addr(VERB_DETAIL, "!!!!! outnet->ip4_ifs->addr", &outnet->ip4_ifs->addr, bound_addrlen);
log_addr(VERB_DETAIL, "!!!!! addr_any", &addr_any, addr_any_len);
memcpy(&pif_return->addr, &addr_fake, addr_fake_len);
pif_return->addrlen = addr_fake_len;
- log_addr(VERB_DETAIL, "!!!!! get_bound_ip_if: addr from ip4_ifs == 0.0.0.0, new is:", &pif_return->addr, outnet->ip4_ifs->addrlen);
+ log_addr(VERB_DETAIL, "!!!!! get_bound_ip_if: addr from"
+ " ip4_ifs == 0.0.0.0, new is:", &pif_return->addr, outnet->ip4_ifs->addrlen);
return 1;
}
verbose(VERB_ALGO, "process_response: new external response event");
iq->response = NULL;
iq->state = QUERY_RESP_STATE;
+
+
+ if (event == module_event_interface_not_available) {
+ log_err("!!!!! process_response:event == module_event_interface_not_available");
+ }
+ if (!qstate->reply) {
+ log_err("!!!!! !qstate->reply");
+ }
+
+ // @TODO set renewed cookie here with infra_set_server_cookie, then bail out
+
if(event == module_event_noreply || event == module_event_error) {
if(event == module_event_noreply && iq->timeout_count >= 3 &&
qstate->env->cfg->use_caps_bits_for_id &&
}
goto handle_it;
}
- if( (event != module_event_reply && event != module_event_capsfail)
- || !qstate->reply) {
+ if( (event != module_event_reply && event != module_event_capsfail
+ && event != module_event_interface_not_available) || !qstate->reply) {
log_err("Bad event combined with response");
outbound_list_remove(&iq->outlist, outbound);
errinf(qstate, "module iterator received wrong internal event with a response message");
struct port_if pif;
struct port_if *pif_ptr = &pif;
- if(getsockname(qstate->reply->c->fd,
+ /* Get the outgoing interface to store with the cookie */
+ if(event != module_event_interface_not_available &&
+ getsockname(qstate->reply->c->fd,
(struct sockaddr *) &bound_addr,
&bound_addrlen) != -1) {
if (!(get_bound_ip_if(qstate->env->worker->back,
&bound_addr, bound_addrlen, pif_ptr))) {
- bound_addrlen = 0;
+ pif.addrlen = 0;
}
log_addr(VERB_DETAIL, "!!!!! iterator:pif addr:", &pif.addr, pif.addrlen);
} else {
- bound_addrlen = 0;
+ /* Set to zero so the cookie gets renewed */
+ pif.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
&qstate->reply->addr, qstate->reply->addrlen,
iq->dp->name, iq->dp->namelen, pif_ptr,
cookie) >= 0) {
- /* log_hex() uses the verbosity levels of verbose() */
- log_hex("complete cookie: ", cookie->opt_data,
- cookie->opt_len);
+ // @TODO do something
} else {
log_info("upstream response server cookie is not "
"added to cache; dropping response");
data = (struct infra_data*) e->data;
- // @TODO fix this logic. does the cookie status matter?
- /* renew cookie if the address isn't available isn't stored */
- // if (data->cookie->addrlen == 0) {
- // infra_fill_client_cookie_random(infra, &data->cookie->data);
- // }
+ /* renew cookie if the address that is stored isn't available */
+ if (data->cookie.pif.addrlen == 0 &&
+ data->cookie.state == SERVER_COOKIE_LEARNED) {
+ infra_fill_client_cookie_random(infra, (uint8_t*) &data->cookie.data);
+ data->cookie.state == SERVER_COOKIE_UNKNOWN;
+ }
memcpy(cookie, &data->cookie, sizeof(struct edns_cookie));
return -1;
}
- if (!(data->cookie.pif.addrlen == pif->addrlen) &&
- memcmp(&data->cookie.pif.addr, &pif->addr, pif->addrlen)){
-
- // @TODO do something? this _should_ only happen on reloads?
+ /* We set the local pif addrlen to 0 if the interface is not found
+ * so it must be unequal to the stored addrlen */
+ if (data->cookie.pif.addrlen != pif->addrlen &&
+ pif->addrlen == 0){
+ /* don't change the status, but change to cookie length
+ * so it gets renewed during the lookup (which is
+ * where all the cookie creation happens) */
+ data->cookie.pif.addrlen = 0;
+ lock_rw_unlock(&e->lock);
+ log_info("the interface to the upstream response server "
+ "that was bound to this EDNS cookie has changed;"
+ " renewing cookie");
+ return 0;
}
/* the server cookie has changed, but the client cookie has not
verbose(VERB_ALGO, "update new server cookie from upstream");
lock_rw_unlock(&e->lock);
+
+ /* log_hex() uses the verbosity levels of verbose() */
+ log_hex("complete cookie: ", cookie->opt_data,
+ cookie->opt_len);
+
return 1;
}
* remains unchanged */
verbose(VERB_ALGO, "correctly received indentical cookie from"
" upstream; don't update");
+
lock_rw_unlock(&e->lock);
+
+ /* log_hex() uses the verbosity levels of verbose() */
+ log_hex("complete cookie: ", cookie->opt_data,
+ cookie->opt_len);
+
return 1;
} else { /* cookie state == SERVER_COOKIE_UNKNOWN */
}
verbose(VERB_QUERY, "storing received server cookie from upstream");
lock_rw_unlock(&e->lock);
+
+ /* log_hex() uses the verbosity levels of verbose() */
+ log_hex("complete cookie: ", cookie->opt_data,
+ cookie->opt_len);
return 1;
}
enum edns_cookie_state state;
struct edns_cookie_data data;
struct port_if pif;
- // struct sockaddr_storage bound_addr;
- // socklen_t bound_addrlen;
};
event = module_event_noreply;
if(what == NETEVENT_CAPSFAIL)
event = module_event_capsfail;
+ if(what == NETEVENT_BOUND_INTERFACE_NOT_AVAILABLE)
+ event = module_event_interface_not_available;
}
mesh_run(mesh, e->qstate->mesh_info, event, e);
}
portno, &inuse, outnet->rnd, outnet->ip_dscp);
if(fd == -1 && !inuse) {
log_err("!!!! select_ifport:nonrecoverable error making socket");
+
+ /* we need to retry sending this message with a cookie
+ * without a bound interface. The cookie needs to be
+ * changed as to not leak the client cookie part that
+ * is linked to this outgoing interface. */
+ if (pend->sq->bound_interface != NULL) {
+ pend->sq->bound_interface_failed = 1;
+ }
/* nonrecoverable error making socket */
return 0;
}
* will get attached by the time we get an answer. */
return;
delete:
+ log_err("!!!!! serviced_timer_cb:delete serviced_udp_send");
serviced_callbacks(sq, NETEVENT_CLOSED, NULL, NULL);
}
} else {
sq->bound_interface = NULL;
}
+ sq->bound_interface_failed = 0;
sq->padding_block_size = pad_queries_block_size;
#ifdef UNBOUND_DEBUG
ins =
}
sq->outnet->svcd_overhead = backlen;
}
+
+ /* set the error to retry the cookie with a new client cookie set */
+ if (sq->bound_interface != NULL && sq->bound_interface_failed) {
+ error = NETEVENT_BOUND_INTERFACE_NOT_AVAILABLE;
+ }
+
/* test the actual sq->cblist, because the next elem could be deleted*/
while((p=sq->cblist) != NULL) {
sq->cblist = p->next; /* remove this element */
int busy;
/** interface bound to the EDNS cookie @TODO fix this */
struct port_if* bound_interface;
+ /** flag to create a retry when the opening of the socket on the
+ * bound interface failed. This enables rewriting of the cookie without
+ * leaking the previously sent client cookie */
+ int bound_interface_failed;
+
};
/**
case module_event_noreply: return "module_event_noreply";
case module_event_capsfail: return "module_event_capsfail";
case module_event_moddone: return "module_event_moddone";
+ case module_event_interface_not_available: return
+ "module_event_interface_not_available";
case module_event_error: return "module_event_error";
}
return "bad_event_value";
module_event_capsfail,
/** next module is done, and its reply is awaiting you */
module_event_moddone,
+ /** retry of the query is needed with a rewritten (client) cookie */
+ module_event_interface_not_available,
/** error */
module_event_error
};
/** to pass write of the write packet is done to callback function
* used when tcp_write_and_read is enabled */
#define NETEVENT_PKT_WRITTEN -5
+/** to pass a retry event when the bound interface of a cookie has failed
+ * and a retry is needed with a rewritten (client) cookie */
+#define NETEVENT_BOUND_INTERFACE_NOT_AVAILABLE -6
+
/** timeout to slow accept calls when not possible, in msec. */
#define NETEVENT_SLOW_ACCEPT_TIME 2000