Compare commits

...
Author SHA1 Message Date
Willem TooropandGitHub 2f2d9229e7 Merge branch 'master' into hackathon/get-root-zone-with-etag 2026-10-20 14:39:58 +02:00
d4999d418c Apply suggestions from code review
Co-authored-by: Wouter Wijngaards <wcawijngaards@users.noreply.github.com>
2026-03-27 16:03:40 +01:00
Willem Toorop 7994365385 Send along ETag with auth zone xfrs over http
When fetching an authoritative zone over http(s), store the ETag if it
was send along in the response, and subsequentally send that along in
subsequent http(s) requests as a "If-None-Match: <ETag>| header.

This will prevent Unbound from fetching the full root zone every 30 minutes
when configured for local root wiht an url, but will instead only fetch
it when there is a different version.
2026-03-14 16:28:31 +08:00
6 changed files with 59 additions and 15 deletions
+42 -11
View File
@@ -5238,7 +5238,7 @@ apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
/** apply HTTP to zone in memory. z is locked. false on failure(mallocfail) */
static int
apply_http(struct auth_xfer* xfr, struct auth_zone* z,
struct sldns_buffer* scratch_buffer)
struct sldns_buffer* scratch_buffer, char* etag)
{
/* parse data in chunks */
/* parse RR's and read into memory. ignore $INCLUDE from the
@@ -5334,7 +5334,11 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,
pstate.lineno,
sldns_buffer_begin(scratch_buffer));
return 0;
}
}
if (etag) {
verbose(VERB_OPS, "etag %s stored", etag);
strcpy(z->etag, etag);
}
return 1;
}
@@ -5492,7 +5496,7 @@ static int xfr_process_reacquire_locks(struct auth_xfer* xfr,
* return false if it did not work */
static int
xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env,
int* ixfr_fail)
int* ixfr_fail, char *etag)
{
struct auth_zone* z;
int zonemd_in_progress;
@@ -5508,7 +5512,7 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env,
/* apply data */
if(xfr->task_transfer->master->http) {
if(!apply_http(xfr, z, env->scratch_buffer)) {
if(!apply_http(xfr, z, env->scratch_buffer, etag)) {
auth_zone_clear_data(z);
lock_rw_unlock(&z->lock);
verbose(VERB_ALGO, "http from %s: could not store data",
@@ -5788,6 +5792,21 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
xfr->task_transfer->start_time = *env->now_tv;
if(master->http) {
char etag[128], *etag_str = NULL;
struct auth_zone* z;
/* Obtain locks and structures. */
lock_basic_unlock(&xfr->lock);
if(!xfr_process_reacquire_locks(xfr, env, &z)) {
/* The zone is gone, ignore xfr transfer. */
return 1; /* Stop processing this xfr. */
}
/* Holding xfr and z locks. */
if(z && z->etag[0] && strlen(z->etag) < sizeof(etag)) {
strlcpy(etag, z->etag, sizeof(etag));
etag_str = etag;
}
lock_rw_unlock(&z->lock);
/* perform http fetch */
/* store http port number into sockaddr,
* unless someone used unbound's host@port notation */
@@ -5797,7 +5816,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
xfr->task_transfer->cp = outnet_comm_point_for_http(
env->outnet, auth_xfer_transfer_http_callback, xfr,
&addr, addrlen, -1, master->ssl, master->host,
master->file, env->cfg);
master->file, env->cfg, etag_str);
if(!xfr->task_transfer->cp) {
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
@@ -6389,10 +6408,10 @@ xfer_link_data(sldns_buffer* pkt, struct auth_xfer* xfr)
/** task transfer. the list of data is complete. process it and if failed
* move to next master, if succeeded, end the task transfer */
static void
process_list_end_transfer(struct auth_xfer* xfr, struct module_env* env)
process_list_end_transfer(struct auth_xfer* xfr, struct module_env* env, char* etag)
{
int ixfr_fail = 0;
if(xfr_process_chunk_list(xfr, env, &ixfr_fail)) {
if(xfr_process_chunk_list(xfr, env, &ixfr_fail, etag)) {
/* it worked! */
auth_chunks_delete(xfr->task_transfer);
@@ -6564,9 +6583,15 @@ auth_xfer_transfer_tcp_callback(struct comm_point* c, void* arg, int err,
}
/* if the transfer is done now, disconnect and process the list */
if(transferdone) {
comm_point_delete(xfr->task_transfer->cp);
/* Convey etag (captured on the comm_point) to auth zone,
* if there was any */
struct comm_point *cp;
cp = xfr->task_transfer->cp;
xfr->task_transfer->cp = NULL;
process_list_end_transfer(xfr, env);
process_list_end_transfer(xfr, env,
( xfr->task_transfer->master->http && cp->etag[0]
? cp->etag : NULL));
comm_point_delete(cp);
return 0;
}
@@ -6648,11 +6673,17 @@ auth_xfer_transfer_http_callback(struct comm_point* c, void* arg, int err,
}
/* if the transfer is done now, disconnect and process the list */
if(err == NETEVENT_DONE) {
/* Convey etag (captured on the comm_point) to auth zone,
* if there was any */
struct comm_point *cp;
if(repinfo) repinfo->c = NULL; /* signal cp deleted to
the routine calling this callback */
comm_point_delete(xfr->task_transfer->cp);
cp = xfr->task_transfer->cp;
xfr->task_transfer->cp = NULL;
process_list_end_transfer(xfr, env);
process_list_end_transfer(xfr, env,
( xfr->task_transfer->master->http && cp->etag[0]
? cp->etag : NULL));
comm_point_delete(cp);
return 0;
}
+2
View File
@@ -125,6 +125,8 @@ struct auth_zone {
int zone_expired;
/** zone is a slave zone (it has masters) */
int zone_is_slave;
/** store etag */
char etag[128];
/** for downstream: this zone answers queries towards the downstream
* clients */
int for_downstream;
+7 -3
View File
@@ -3900,11 +3900,15 @@ setup_http_user_agent(sldns_buffer* buf, struct config_file* cfg)
/** setup http request headers in buffer for sending query to destination */
static int
setup_http_request(sldns_buffer* buf, char* host, char* path,
struct config_file* cfg)
struct config_file* cfg, const char* etag)
{
sldns_buffer_clear(buf);
sldns_buffer_printf(buf, "GET /%s HTTP/1.1\r\n", path);
sldns_buffer_printf(buf, "Host: %s\r\n", host);
if (etag) {
verbose(VERB_OPS, "sending along ETag %s in http request", etag);
sldns_buffer_printf(buf, "If-None-Match: %s\r\n", etag);
}
setup_http_user_agent(buf, cfg);
/* We do not really do multiple queries per connection,
* but this header setting is also not needed.
@@ -3921,7 +3925,7 @@ struct comm_point*
outnet_comm_point_for_http(struct outside_network* outnet,
comm_point_callback_type* cb, void* cb_arg,
struct sockaddr_storage* to_addr, socklen_t to_addrlen, int timeout,
int ssl, char* host, char* path, struct config_file* cfg)
int ssl, char* host, char* path, struct config_file* cfg, char* etag)
{
/* cp calls cb with err=NETEVENT_DONE when transfer is done */
struct comm_point* cp;
@@ -3959,7 +3963,7 @@ outnet_comm_point_for_http(struct outside_network* outnet,
comm_point_start_listening(cp, fd, timeout);
/* setup http request in cp->buffer */
if(!setup_http_request(cp->buffer, host, path, cfg)) {
if(!setup_http_request(cp->buffer, host, path, cfg, etag)) {
log_err("error setting up http request");
comm_point_delete(cp);
return NULL;
+1 -1
View File
@@ -854,7 +854,7 @@ struct comm_point* outnet_comm_point_for_tcp(struct outside_network* outnet,
struct comm_point* outnet_comm_point_for_http(struct outside_network* outnet,
comm_point_callback_type* cb, void* cb_arg,
struct sockaddr_storage* to_addr, socklen_t to_addrlen, int timeout,
int ssl, char* host, char* path, struct config_file* cfg);
int ssl, char* host, char* path, struct config_file* cfg, char* etag);
/** connect tcp connection to addr, 0 on failure */
int outnet_tcp_connect(int s, struct sockaddr_storage* addr, socklen_t addrlen);
+5
View File
@@ -5037,6 +5037,10 @@ http_process_initial_header(struct comm_point* c)
} else if(strncasecmp(line, "Transfer-Encoding: chunked", 19+7) == 0) {
c->tcp_byte_count = 0;
c->http_is_chunked = 1;
} else if(strncasecmp(line, "ETag: ", 6) == 0) {
if (strlen(line + 6) < sizeof(c->etag)) {
strcpy(c->etag, line + 6);
}
} else if(line[0] == 0) {
/* end of initial headers */
c->http_in_headers = 0;
@@ -6581,6 +6585,7 @@ comm_point_create_http_out(struct comm_base *base, size_t bufsize,
c->http_in_chunk_headers = 0;
c->http_is_chunked = 0;
c->http_temp = temp;
c->etag[0] = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 1;
#endif
+2
View File
@@ -286,6 +286,8 @@ struct comm_point {
struct sldns_buffer* http_temp;
/** http stored content in buffer */
size_t http_stored;
/** http Entity-Tag */
char etag[128];
/* -------- HTTP/2 ------- */
/** http2 session */
struct http2_session* h2_session;