From 44af84dfc9d6245147f8b97ab432b96dec0bc6a9 Mon Sep 17 00:00:00 2001 From: TCY16 Date: Thu, 19 May 2022 13:24:00 +0200 Subject: [PATCH] store complete cookie when receiving from downstream. Add function through search through an edns_option list --- iterator/iterator.c | 17 +++++++++++++++++ services/cache/infra.c | 38 +++++++++++++++++++++++++++++++++----- services/cache/infra.h | 31 +++++++++++++++++++++++-------- services/outside_network.c | 6 ++---- util/data/msgparse.c | 13 +++++++++++++ util/data/msgparse.h | 9 +++++++++ 6 files changed, 97 insertions(+), 17 deletions(-) diff --git a/iterator/iterator.c b/iterator/iterator.c index 3cfb286f4..3f61651db 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -3764,6 +3764,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, { struct msg_parse* prs; struct edns_data edns; + struct edns_option* cookie; sldns_buffer* pkt; verbose(VERB_ALGO, "process_response: new external response event"); @@ -3823,6 +3824,22 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, goto handle_it; } + if((cookie = edns_list_get_option(edns.opt_list_in, 10 /*@TODO change to cookie with downstream merge*/))) { + /* verify this is a 'complete cookie' (RFC9018) with the length */ + if (cookie->opt_len == 24) { + /* store the complete cookie in the infra_cache */ + infra_set_server_cookie(qstate->env->infra_cache, + &qstate->reply->addr, qstate->reply->addrlen, + iq->dp->name, iq->dp->namelen, cookie); + + log_hex("complete cookie: ", cookie->opt_data, + cookie->opt_len); + } else { + // @TODO just log error? set state to COOKIE_NOT_SUPPORTED? + } + } + + /* Copy the edns options we may got from the back end */ if(edns.opt_list_in) { qstate->edns_opts_back_in = edns_opt_copy_region(edns.opt_list_in, diff --git a/services/cache/infra.c b/services/cache/infra.c index 2ae3c242d..e1c784540 100644 --- a/services/cache/infra.c +++ b/services/cache/infra.c @@ -697,16 +697,16 @@ infra_edns_update(struct infra_cache* infra, struct sockaddr_storage* addr, struct edns_cookie* infra_get_cookie(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* nm, size_t nmlen, + socklen_t addrlen, uint8_t* name, size_t namelen, time_t timenow) { struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, - nm, nmlen, 1); + name, namelen, 1); struct infra_data* data; int needtoinsert = 0; if(!e) { - if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow))) { + if(!(e = new_entry(infra, addr, addrlen, name, namelen, timenow))) { return 0; } needtoinsert = 1; @@ -717,8 +717,7 @@ infra_get_cookie(struct infra_cache* infra, struct sockaddr_storage* addr, data_entry_init(infra, e, timenow); } - /* we have an entry; update the rtt, and the ttl */ - data = (struct infra_data*)e->data; + data = (struct infra_data*) e->data; if(needtoinsert) { slabhash_insert(infra->hosts, e->hash, e, e->data, NULL); @@ -729,6 +728,35 @@ infra_get_cookie(struct infra_cache* infra, struct sockaddr_storage* addr, return &data->cookie; } +void +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) +{ + struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, + name, namelen, 1); + struct infra_data* data; + + /* cookie length verification should be checked and handled by caller */ + assert(cookie->opt_len == 24); + + /* the client cookie was set on the outgoing upstream, so the entry + * must exists here */ + assert(e); + + data = (struct infra_data*) e->data; + + /* check if we already know the complete cookie */ + if (data->cookie.state == SERVER_COOKIE_UNKNOWN) { + memcpy(data->cookie.data.complete, cookie->opt_data, 24); + data->cookie.state = SERVER_COOKIE_LEARNED; + } + else { + lock_rw_unlock(&e->lock); + } +} + + + int infra_get_lame_rtt(struct infra_cache* infra, struct sockaddr_storage* addr, socklen_t addrlen, diff --git a/services/cache/infra.h b/services/cache/infra.h index 5ecd3e2fd..90761eaa7 100644 --- a/services/cache/infra.h +++ b/services/cache/infra.h @@ -55,7 +55,7 @@ struct config_file; /* COOKIE @TODO move this to correct spot */ /** - * @TODO write this + * The actual EDNS cookie data */ union edns_cookie_data { uint8_t complete[24]; @@ -69,17 +69,17 @@ union edns_cookie_data { }; /** - * @TODO write this + * The different states the EDNS cookie can be in */ enum edns_cookie_state { SERVER_COOKIE_UNKNOWN = 0, /* server cookie unknown, client cookie known */ - SERVER_COOKIE_LEARNED = 1, /* server (and client) cookie learned */ + SERVER_COOKIE_LEARNED = 1, /* server (and client) cookie known */ COOKIE_NOT_SUPPORTED = 2, /* upstream does not supported EDNS/cookies */ }; /** - * @TODO write this + * Structure for an EDNS cookie (RFC9018) and it's internal state */ struct edns_cookie { enum edns_cookie_state state; @@ -125,7 +125,7 @@ struct infra_data { * and cause a timeout */ uint8_t edns_lame_known; - /* The EDNS cookie containing both the client and server cookie */ + /* The EDNS cookie containing the cookie and the internal state */ struct edns_cookie cookie; /** is the host lame (does not serve the zone authoritatively), @@ -366,16 +366,31 @@ int infra_edns_update(struct infra_cache* infra, * @param addrlen: length of addr. * @param name: name of zone * @param namelen: length of name - * @param edns_version: the version that it publishes. - * If it is known to support EDNS then no-EDNS is not stored over it. * @param timenow: what time it is now. * @return: struct ends_cookie* */ struct edns_cookie* infra_get_cookie(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* nm, size_t nmlen, + socklen_t addrlen, uint8_t* name, size_t namelen, time_t timenow); +/** + * Find the cookie entry in the cache and update it with to make a complete + * cookie (RFC9018). This function assumes that the cookie param contains a + * complete cookie with a length of 24 bytes. It also assumes that a previous + * entry in the cache already exists, as it will update the entry. + * @param infra: infrastructure cache. + * @param addr: host address. + * @param addrlen: length of addr. + * @param name: name of zone + * @param namelen: length of name + * @param timenow: what time it is now. + * @param cookie: the EDNS cookie option we want to store. + */ +void 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); + + /** * Get Lameness information and average RTT if host is in the cache. * This information is to be used for server selection. diff --git a/services/outside_network.c b/services/outside_network.c index c1fdb9fdd..78b06739b 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -3392,20 +3392,18 @@ outnet_serviced_query(struct outside_network* outnet, // @TODO Make configurable - // 1. lookup the cookie information in the cache cookie = infra_get_cookie(env->infra_cache, addr, addrlen, zone, zonelen, *env->now); - // 2. attach cookie, dependent on if we know just the client or also the server if (cookie->state == SERVER_COOKIE_LEARNED) { /* We known the complete cookie, so we attach it */ edns_opt_list_append(&per_upstream_opt_list, 10, /* @TODO 10 == COOKIE */ 24, cookie->data.complete, region); } else if (cookie->state == SERVER_COOKIE_UNKNOWN) { - /* We known just client cookie, so we attach it */ + /* We know just client cookie, so we attach it */ edns_opt_list_append(&per_upstream_opt_list, 10, /* @TODO 10 == COOKIE */ 8, cookie->data.components.client, region); - } + } /* We ignore COOKIE_NOT_SUPPORTED */ serviced_gen_query(buff, qinfo->qname, qinfo->qname_len, qinfo->qtype, qinfo->qclass, flags); diff --git a/util/data/msgparse.c b/util/data/msgparse.c index 5bb69d6ed..1923ff0d0 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -597,6 +597,19 @@ parse_query_section(sldns_buffer* pkt, struct msg_parse* msg) return 0; } +struct edns_option* +edns_list_get_option(struct edns_option* option, uint16_t code) +{ + while (option) { + if (option->opt_code == code) { + return option; + } + + option = option->next; + } + return NULL; +} + size_t get_rdf_size(sldns_rdf_type rdf) { diff --git a/util/data/msgparse.h b/util/data/msgparse.h index 0c458e6e8..f15ab2bcc 100644 --- a/util/data/msgparse.h +++ b/util/data/msgparse.h @@ -254,6 +254,15 @@ struct edns_option { uint8_t* opt_data; }; + +/** + * Search through an EDNS list to find if the specified option. + * @param option: The EDNS list which we search in + * @param code: the option code that we search for + * @return: returns the option if it is there, and NULL when it is not + */ +struct edns_option* edns_list_get_option(struct edns_option* option, uint16_t code); + /** * Obtain size in the packet of an rr type, that is before dname type. * Do TYPE_DNAME, and type STR, yourself. Gives size for most regular types.