/*************************************************************************** auth_mem.cpp - description ------------------- begin : do jan 2 2003 copyright : (C) 2003 by Meilof email : meilof@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "query.h" #include "stdquery.h" #include "zones.h" #include "resolver.h" #include "auth_mem.h" #include "configuration.h" _auth_ret auth_mem_lookup(pending_query *q, domainname &qname, int QTYPE, domainname &znroot, DnsMessage *a, ZoneDomain *rootdomain) { int x, nl; unsigned int sz; ZoneDomain *pdom, *dom; RrSet *set; stl_string label; /* we have an authoritative zone */ if (a->answers.begin() == a->answers.end()) a->AA = true; nl = qname.nlabels() - znroot.nlabels(); pdom = rootdomain; // 3. Start matching down, label by label, in the zone. The // matching process can terminate several ways: for (x = nl - 1; x >= 0; x--) { label = qname.label(x); dom = pdom->subitems.get_item(label.c_str()); if (!dom) { // c. If at some label, a match is impossible (i.e., the // corresponding label does not exist), look to see if a // the "*" label exists. pdom = pdom->subitems.get_item("*"); if (pdom) { // If the "*" label does exist, match RRs at that node // against QTYPE. If any match, copy them into the answer // section, but set the owner of the RR to be QNAME, and // not the node with the "*" label. Go to step 6. /* note that here we do not strictly follow the RFC to allow wildcard cnames */ break; } else { // If the "*" label does not exist, check whether the name // we are looking for is the original QNAME in the query // or a name we have followed due to a CNAME. If the name // is original, set an authoritative name error in the // response and exit. Otherwise just exit. a->RCODE = RCODE_NXDOMAIN; /* add the soa record */ add_sets_to_message(znroot, rootdomain, DNS_TYPE_SOA, a->authority); return step6; } } else if (dom->get_rrset(DNS_TYPE_NS) != NULL) { // b. If a match would take us out of the authoritative data, // we have a referral. This happens when we encounter a // node with NS RRs marking cuts along the bottom of a // zone. // // Copy the NS RRs for the subzone into the authority // section of the reply. Put whatever addresses are // available into the additional section, using glue RRs // if the addresses are not available from authoritative // data or the cache. Go to step 4. /* we're implementing this slightly different: we make sure the cache has valid NS information for our zone, and then just let the cache functions return the best information. This is because we don't have a separation between authoritative and recursive queries. If we maintain the "acdam.net" zone, we don't want to add delegation info for "europe.acdam.net" if we _know_ the answer should be in the "nl.europe.acdam.net" zone. */ if (a->answers.begin() == a->answers.end()) a->AA = false; pthread_mutex_lock(&m_cache); try { domainname tmp = qname.from(x); add_glue_to_cache(tmp, dom); } catch (PException p) { pthread_mutex_unlock(&m_cache); throw p; } pthread_mutex_unlock(&m_cache); return step4; } pdom = dom; } /* for each label */ /*query_step_3:*/ // a. If the whole of QNAME is matched, we have found the // node. if (QTYPE != DNS_TYPE_CNAME && QTYPE != QTYPE_ANY && (set = pdom->get_rrset(DNS_TYPE_CNAME)) != NULL && set->rrs.count != 0) { // If the data at the node is a CNAME, and QTYPE doesn't // match CNAME, copy the CNAME RR into the answer section // of the response, change QNAME to the canonical name in // the CNAME RR, and go back to step 1. add_set_to_message(qname, set, a->answers); qname = domainname(true, set->rrs[0]->rdata); return step1; } else { // Otherwise, copy all RRs which match QTYPE into the // answer section and go to step 6. if (pdom->rrsets.count == 0) { /* no rr sets at all. you know what, we call it a NXDOMAIN */ add_sets_to_message(znroot, rootdomain, DNS_TYPE_SOA, a->authority); if (a->answers.begin() == a->answers.end()) { a->RCODE = RCODE_NXDOMAIN; } return step6; } sz = a->answers.size(); add_sets_to_message(qname, pdom, QTYPE, a->answers); if (sz == a->answers.size()) { /* NODATA */ add_sets_to_message(znroot, rootdomain, DNS_TYPE_SOA, a->authority); } else { /* data allright */ if (!omit_ns_records && !has_rrset(a->answers, znroot, DNS_TYPE_NS) && !has_rrset(a->authority, znroot, DNS_TYPE_NS)) auth_mem_additional_lookup(znroot, DNS_TYPE_NS, a->authority, znroot, rootdomain, q); } return step6; } } bool auth_mem_additional_lookup(domainname &qname, uint16_t qtype, stl_list(DnsRR)& section, domainname &zroot, ZoneDomain *rootdomain, pending_query *qinfo) { ZoneDomain *zdom; zdom = rootdomain; int nl, t; stl_string label; nl = qname.nlabels() - zroot.nlabels(); for (t = nl - 1; t >= 0; t--) { label = qname.label(t); zdom = zdom->subitems.get_item(label.c_str()); if (!zdom) return false; if (zdom->get_rrset(DNS_TYPE_NS)) return false; } add_sets_to_message(qname, zdom, qtype, section); return true; } AuthMemZone::AuthMemZone() { is_placeholder = false; rootdomain = new ZoneDomain(NULL, ""); allow_xfr = slaves_only; zone_is_loaded = false; serial = 0; } AuthMemZone::~AuthMemZone() { } bool AuthMemZone::auth_feed_setting(const char *name, const char *val) { addrrange range; _addr ad; if (strcmpi(name, "reset") == 0) { slaves.clear(); allow_xfr_to.clear(); allow_xfr = slaves_only; return true; } if (strcmpi(name, "slaves") == 0) { txt_to_addr(&ad, val); slaves.push_front(ad); } else if (strcmpi(name, "allow_xfr_to") == 0) { txt_to_addrrange(range.range, val); allow_xfr_to.push_front(range); } else if (strcmpi(name, "allow_xfr") == 0) { if (strcmpi(val, "any") == 0) allow_xfr = any; else if (strcmpi(val, "slaves") == 0) allow_xfr = slaves_only; else if (strcmpi(val, "none") == 0) allow_xfr = none; else throw PException(true, "Invalid value %s for allow_xfr!", val); } else return false; return true; } void AuthMemZone::end_setting() { Zone::end_setting(); } _auth_ret AuthMemZone::stdquery_lookup(pending_query *q, domainname &qname, uint16_t QTYPE, domainname &znroot, DnsMessage *a) { if (!zone_is_loaded) { a->RCODE = RCODE_SRVFAIL; return step6; } return auth_mem_lookup(q, qname, QTYPE, znroot, a, rootdomain); } bool AuthMemZone::additional_lookup(domainname &qname, uint16_t qtype, stl_list(DnsRR)& section, domainname &znroot, pending_query *qinfo) { if (!zone_is_loaded) return false; return auth_mem_additional_lookup(qname, qtype, section, znroot, rootdomain, qinfo); } class NotifyData { public: stl_slist(_addr) slaves; domainname znroot; DnsRR soa; }; void *notify_thread(void *arg) { NotifyData *dat = (NotifyData *)arg; DnsRR soa = dat->soa; domainname znroot = dat->znroot; stl_slist(_addr) slaves = dat->slaves; stl_slist(_addr)::iterator it = slaves.begin(); pos_cliresolver res; delete dat; DnsMessage *q = NULL, *a = NULL; try { q = new DnsMessage(); q->OPCODE = OPCODE_NOTIFY; q->AA = true; q->questions.push_front(DnsQuestion(znroot, DNS_TYPE_SOA)); q->answers.push_front(soa); while (it != slaves.end()) { try { res.query(q, a, &*it); delete a; a = NULL; } catch (PException p) { if (a) delete a; a = NULL; } it++; } } catch (PException p) { if (q) delete q; if (a) delete a; } return NULL; } void AuthMemZone::start_notification() { RrSet *set; DnsRR rr; pthread_t tr; if (slaves.size() && rootdomain && (set = rootdomain->get_rrset(DNS_TYPE_SOA)) && set->rrs.count == 1) { NotifyData *dat = new NotifyData(); dat->znroot = getfqdn(); rr.NAME = dat->znroot; rr.TYPE = DNS_TYPE_SOA; rr.TTL = set->ttl; rr.RDATA = (char *)memdup(set->rrs[0]->rdata, set->rrs[0]->rdlength); rr.RDLENGTH = set->rrs[0]->rdlength; dat->soa = rr; dat->slaves = slaves; posthread_create(&tr, notify_thread, dat); } }