From: jessevz Date: Wed, 12 Jun 2024 07:26:32 +0000 (+0200) Subject: Made it possible to change queries into _deleg queries X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=011c5a0e0f0ad717cabaf79b0f40dbc04b66d76c;p=thirdparty%2Funbound.git Made it possible to change queries into _deleg queries --- diff --git a/iterator/iterator.c b/iterator/iterator.c index 5a99ab0d7..ca945a472 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -32,7 +32,33 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - +// @JESSE: Iterator (this file) is the module that does all the needed DNS +// iterations and walking the DNS tree to try and find an answer. +// It includes but not limited to: +// 1. Finding the closest known delegation point +// (root at startup, or configured zones) +// 2. Sends a query (the original query if no qname-minimisation) +// to that and gets either an answer or a referral answer. +// 3. In case of referral answer goes back to 1. +// +// A lot more is happening here, like fallbacks and retries. Don't try +// to understand everything at once, it's better to go with the flow +// and see what is relevant for you. + +// @JESSE: We do not use '//' comments in the Unbound code base. I explicitly +// use them to identify non-relevant parts or WIP comments. + +// @JESSE: Some tips: +// - For printf kind of debug logging it's easier to use log_err(); +// these are printed on all verbosity levels. +// - For domain name manipulation methods you can have a look at +// dname.c/h. +// - If you need to do allocations; for your case everything should +// have the lifetime of the query state (qstate); you can use +// qstate->region as your arena allocator and pass that region to +// functions that require one. You can then alloc items there and +// forget about them. The whole region is freed when the qstate is +// no more. /** * \file * @@ -70,6 +96,7 @@ #include "sldns/parseutil.h" #include "sldns/sbuffer.h" + /* in msec */ int UNKNOWN_SERVER_NICENESS = 376; /* in msec */ @@ -136,6 +163,8 @@ iter_deinit(struct module_env* env, int id) static int iter_new(struct module_qstate* qstate, int id) { + // @JESSE: each module can have its own query state. The iter_qstate + // struct below is the state for the iterator. struct iter_qstate* iq = (struct iter_qstate*)regional_alloc( qstate->region, sizeof(struct iter_qstate)); qstate->minfo[id] = iq; @@ -164,7 +193,11 @@ iter_new(struct module_qstate* qstate, int id) iq->dnssec_lame_query = 0; iq->chase_flags = qstate->query_flags; /* Start with the (current) qname. */ + // @JESSE: iq->qchase will be the qinfo iterator will be working on and + // updating through the iteration process. It is set here to + // the initial query (qstate->qinfo) that started all this. iq->qchase = qstate->qinfo; + iq->deleg_state = 0; outbound_list_init(&iq->outlist); iq->minimise_count = 0; iq->timeout_count = 0; @@ -172,7 +205,7 @@ iter_new(struct module_qstate* qstate, int id) iq->minimisation_state = INIT_MINIMISE_STATE; else iq->minimisation_state = DONOT_MINIMISE_STATE; - + // @JESSE: iq->qinfo_out is the qinfo that will be sent out. memset(&iq->qinfo_out, 0, sizeof(struct query_info)); return 1; } @@ -1596,8 +1629,10 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, delname = iq->dp->name; delnamelen = iq->dp->namelen; } else { - delname = iq->qchase.qname; - delnamelen = iq->qchase.qname_len; + // @JESSE: Here we set the delegation name to be the original + // one ... + delname = iq->qchase.qname; + delnamelen = iq->qchase.qname_len; } if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue || (iq->qchase.qtype == LDNS_RR_TYPE_NS && qstate->prefetch_leeway @@ -1618,8 +1653,10 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, /* Lookup the delegation in the cache. If null, then the * cache needs to be primed for the qclass. */ + // @JESSE: ... and here we'll try to get the closest delegation + // from cache. if(delname) - iq->dp = dns_cache_find_delegation(qstate->env, delname, + iq->dp = dns_cache_find_delegation(qstate->env, delname, delnamelen, iq->qchase.qtype, iq->qchase.qclass, qstate->region, &iq->deleg_msg, *qstate->env->now+qstate->prefetch_leeway, 1, @@ -1950,6 +1987,7 @@ generate_parentside_target_query(struct module_qstate* qstate, * @param qclass: target qclass. * @return true on success, false on failure. */ +//JESSE Use this to generate new query? static int generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq, int id, uint8_t* name, size_t namelen, uint16_t qtype, uint16_t qclass) @@ -2384,6 +2422,7 @@ check_waiting_queries(struct iter_qstate* iq, struct module_qstate* qstate, } } +// @JESSE: This is where most of the iteration time will be spent. /** * This is the request event state where the request will be sent to one of * its current query targets. This state also handles issuing target lookup @@ -2538,6 +2577,57 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, delegpt_no_ipv4(iq->dp); delegpt_log(VERB_ALGO, iq->dp); + uint8_t root_len = iq->qchase.qname[0]; + //NICE point + if (iq->deleg_state == 0 && root_len > 0) { + //we have to add _deleg after the first label + //for ex. jesse.nlnetlabs.nl becomes jesse._deleg.nlnetlabs.nl + uint8_t deleg_wireformat[] = {6, 95, 100, 101, 108, 101, 103}; //{06}_deleg + size_t delnamelen = iq->qchase.qname_len + sizeof(deleg_wireformat); + log_err("deleg wireformat size: %d, original qname size: %d, qname len (variable): %d, size of first label orginal qname: %d allocated space: %d", sizeof(deleg_wireformat), sizeof(iq->qchase.qname), iq->qchase.qname_len, iq->qchase.qname[0], delnamelen); + // delnamelen = iq->qchase.qname_len + sizeof(deleg_wireformat); + // delname = (uint8_t *)malloc(delnamelen); + uint8_t *delname = (uint8_t *)malloc(delnamelen); + if (delname == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + } + //put first label of original qname + uint8_t first_label_len = iq->qchase.qname[0]; + log_err("First label length: %d", first_label_len); + uint8_t *qname_minus_first_label = iq->qchase.qname + first_label_len + 1; + uint8_t leftover_len = iq->qchase.qname_len - first_label_len - 1; + // memcpy(delname, iq->qchase.qname, sizeof(iq->qchase.qname)); //memcpy 1st label into delname + memcpy(delname, iq->qchase.qname, first_label_len + 1); //memcpy 1st label into delname + memcpy(delname + first_label_len + 1, deleg_wireformat, sizeof(deleg_wireformat)); //memcpy _deleg label in delname + memcpy(delname + first_label_len + sizeof(deleg_wireformat) + 1, qname_minus_first_label, leftover_len); //memcpy other labels in delname + log_err("Old delegation point: %s", iq->qchase.qname); + log_err("cname without first label: %s", qname_minus_first_label); + + // log_err("delegation name in bytes:"); + // for (size_t i = 0; i < delnamelen; ++i) { + // log_err("%u ", delname[i]); + // } + + log_err("The _deleg delegation point: %s", delname); + + //uncomment if not onlys used in stub zone + iq->deleg_state = 1; + + //left here + iq->dp->namelen = delnamelen; + // iq->dp->namelabs++; + iq->qchase.qtype = 64; + iq->qchase.qname = delname; + iq->qchase.qname_len = delnamelen; + + iq->qinfo_out.qtype = 64; + iq->qinfo_out.qname = delname; + iq->qinfo_out.qname_len = delnamelen; + // iq->deleg_original_qname = qstate->qinfo.qname; + // qstate->qinfo.qname = delname; + // generate_target_query(qstate, iq, id, uint8_t* name, size_t namelen, uint16_t qtype, uint16_t qclass) + } + if(iq->num_current_queries>0) { /* already busy answering a query, this restart is because * more delegpt addrs became available, wait for existing @@ -2547,6 +2637,13 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, return 0; } + //DELEG first check if _deleg in delegation point + //if no _deleg let unbound handle it +// @JESSE: The following ifs is where most qname-minimisation happens. + // We need something similar (adding a label at a time) but + // without the best-effort nature of qname-minimisation and its + // fallback. You can take inspiration from here on how to + // correctly set iq->qinfo_out. if(iq->minimisation_state == INIT_MINIMISE_STATE && !(iq->chase_flags & BIT_RD)) { /* (Re)set qinfo_out to (new) delegation point, except when @@ -3031,7 +3128,10 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, log_name_addr(VERB_QUERY, "applied NAT64:", iq->dp->name, &real_addr, real_addrlen); } - + // @JESSE: This is where a query is finally going out, hopefully. + log_err("JESSE send qname: %s", qstate->qinfo.qname); + log_err("JESSE send type: %d", qstate->qinfo.qtype); + fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query)); outq = (*qstate->env->send_query)(&iq->qinfo_out, iq->chase_flags | (iq->chase_to_rd?BIT_RD:0), @@ -3085,7 +3185,7 @@ find_NS(struct reply_info* rep, size_t from, size_t to) return NULL; } - +// @JESSE: This is where responses are read. /** * Process the query response. All queries end up at this state first. This * process generally consists of analyzing the response and routing the @@ -3180,8 +3280,83 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, dnsseclame = 1; } } else iq->dnssec_lame_query = 0; + // @JESSE: Answers to _deleg queries would end up here. You need to + // make sure you identify those answers correclty and treat + // them the same way as referrals below. + /* handle each of the type cases */ + //check deleg and change to referal type TODO add check to see if _deleg in name + uint16_t SVCB_QTYPE = 64; + log_err("JESSE: the qtype of the answer is: %d ", iq->qchase.qtype); + log_err("JESSE: the type is: %d", type); + if (iq->qchase.qtype == SVCB_QTYPE) { + struct ub_packed_rrset_key* rrset_key; + log_err("JESSE: the returnmsg: %s", iq->response->rep); + rrset_key = reply_find_answer_rrset(&iq->qchase, iq->response->rep); + // struct packed_rrset_key rr_records = rrset->rk; + + if(rrset_key) { + struct packed_rrset_data* rrset_data = (struct packed_rrset_data*) rrset_key->entry.data; + + size_t data_len = rrset_data->rr_len[0]; + uint8_t* svcb_data = rrset_data->rr_data[0]; + log_err("JESSE: The wireformat SVCB name:"); + for (size_t i = 0; i < data_len; ++i) { + log_err("%u ", svcb_data[i]); + } + size_t index = 4; + while(svcb_data[index] != 0) { //loop through dns labels, label length 0 mean root so stop looping + index = index + svcb_data[index] + 1; + } + index = index + 1;//add 1 for the root label + //Reference https://datatracker.ietf.org/doc/rfc9460/ section 2.2 + uint8_t *ipv4; + uint8_t *ipv6; + while(index < data_len && (ipv4 == NULL || ipv6 == NULL)) { + uint16_t svcParamkey = (svcb_data[index] << 8) | svcb_data[index+1]; + uint16_t svcParamValLen = (svcb_data[index+2] << 8) | svcb_data[index+3]; + index = index + 4; + //logic fault, will never enter the paramkey checks TODOOOOOOOO + if (svcParamkey == 4) { //parse IPv4 + ipv4 = (uint8_t *)malloc(4 * sizeof(uint8_t)); + memcpy(ipv4, svcb_data + index, 4); + log_err("Parsed IPv4 Hint:"); + for (size_t i = 0; i < 4; ++i) { + log_err("%u ", ipv4[i]); + } + } else if (svcParamkey == 6) { //parse ipv6 + ipv6 = (uint8_t *)malloc(16 * sizeof(uint8_t)); + memcpy(ipv6, svcb_data + index, 16); + log_err("Parsed IPv6 Hint:"); + for (size_t i = 0; i < 4; ++i) { + log_err("%u ", ipv6[i]); + } + } + index = index + svcParamValLen; + } + } else { + log_err("TESTTTTTTT2"); + //this means no _deleg record found + iq->qchase.qtype = 1; + qstate->qinfo.qtype = 1; + // iq->deleg_state = 0; + qstate->qinfo.qname = iq->deleg_original_qname; + } + // log_err("RRset result in bytes:"); + // if(rrset) { + // for (size_t i = 0; i < rr_records->count; ++i) { + // size_t len = rr_records->rr_len[i]; + // log_err("Resource record %d:", i); + // for (size_t j = 0; j < len; ++i) { + // log_err("%u ", rr_records->rr_data[i][j]); + // } + // } + // } + //set new delegation point + // iq->dp = delegpt_from_message(iq->response, qstate->region); + } /* see if referral brings us close to the target */ - if(type == RESPONSE_TYPE_REFERRAL) { + if(type == RESPONSE_TYPE_REFERRAL){ + // iq->deleg_state = 0; struct ub_packed_rrset_key* ns = find_NS( iq->response->rep, iq->response->rep->an_numrrsets, iq->response->rep->an_numrrsets @@ -3224,8 +3399,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, type = RESPONSE_TYPE_ANSWER; } - /* handle each of the type cases */ - if(type == RESPONSE_TYPE_ANSWER) { + if(type == RESPONSE_TYPE_ANSWER ){ /* ANSWER type responses terminate the query algorithm, * so they sent on their */ if(verbosity >= VERB_DETAIL) { @@ -3235,6 +3409,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, (iq->response->rep->an_numrrsets?"ANSWER": "nodata ANSWER")); } + /* if qtype is DS, check we have the right level of answer, * like grandchild answer but we need the middle, reject it */ if(iq->qchase.qtype == LDNS_RR_TYPE_DS && !iq->dsns_point @@ -3323,8 +3498,13 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, return next_state(iq, QUERYTARGETS_STATE); } return final_state(iq); + // @JESSE: I guess for the test environemnt we mostly don't care about + // referrals; these are traditional DNS referral responses. } else if(type == RESPONSE_TYPE_REFERRAL) { + //added code + // iq->deleg_state = 0; struct delegpt* old_dp = NULL; + /* REFERRAL type responses get a reset of the * delegation point, and back to the QUERYTARGETS_STATE. */ verbose(VERB_DETAIL, "query response was REFERRAL"); @@ -4140,6 +4320,12 @@ iter_inform_super(struct module_qstate* qstate, int id, else processTargetResponse(qstate, id, super); } +// @JESSE: These are all the available states the iterator could be for a given +// query. You should not worry about the following cases: +// - COLLECT_CLASS_STATE (it has to do with ANY queries) +// - DSNS_FIND_STATE (it tries to find the correct parent for a DS +// query; I think it is not relevant since the +// _deleg stuff always live at the parent) /** * Handle iterator state. * Handle events. This is the real processing loop for events, responsible