/*************************************************************************** zones.cpp - description ------------------- begin : Sun Dec 22 2002 copyright : (C) 2002 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 #include "zones.h" #include "resolver.h" #include "updates.h" #include "configuration.h" /* RrData ------------------------------------------------------------------ */ RrData::RrData() { rdlength = 0; rdata = NULL; } RrData::RrData(RrData *dat) { rdlength = dat->rdlength; rdata = (char *)memdup(dat->rdata, dat->rdlength); } RrData::RrData(uint16_t _rdlength, const char *_rdata) { rdlength = _rdlength; rdata = (char *)memdup(_rdata, _rdlength); } RrData::~RrData() { free(rdata); } /* RrSet ------------------------------------------------------------------- */ RrSet::RrSet(uint16_t _type) { type = _type; ttl = TTL_NOVAL; } void RrSet::clean() { rrs.destroy(); ttl = TTL_NOVAL; } void RrSet::add_rr(uint32_t _ttl, uint16_t _rdlength, const char *_rdata) { if (ttl == TTL_NOVAL || ttl > _ttl) ttl = _ttl; rrs.add(new RrData(_rdlength, _rdata)); } bool RrSet::expired() { return (ttl > TTL_NOVAL && ttl < (uint32_t)time(NULL)); } /* ZoneDomain -------------------------------------------------------------- */ ZoneDomain::ZoneDomain(ZoneDomain *_parent, const char *_name) { ext.ncache_time = 0; name = strdup(_name); parent = _parent; registered = 0; #ifdef DEBUG // printf("zonedomain creation: %s\n", getfqdn().tocstr()); #endif } ZoneDomain::~ZoneDomain() { cache_unreg(); #ifdef DEBUG subitems.clean(); // printf("zd::~zd(%s)\n", getfqdn().tocstr()); #endif free(name); } void ZoneDomain::cache_register() { if (registered != 0) return; #ifdef DEBUG // printf("Cache push: %s\n", getfqdn().tocstr()); #endif i.cache.next = NULL; i.cache.prev = zd_last; if (zd_last) zd_last->i.cache.next = this; if (!zd_first) zd_first = this; zd_last = this; registered = 2; n_cache_doms++; delta_n_cache_doms++; } void ZoneDomain::cache_unreg() { if (registered != 2) return; #ifdef DEBUG // printf("Cache_unreg: %s\n", getfqdn().tocstr()); #endif if (i.cache.prev == NULL) zd_first = i.cache.next; else i.cache.prev->i.cache.next = i.cache.next; if (i.cache.next == NULL) zd_last = i.cache.prev; else i.cache.next->i.cache.prev = i.cache.prev; n_cache_doms--; delta_n_cache_doms--; registered = 0; } void ZoneDomain::cache_bottom() { if (registered == 1) return; cache_unreg(); #ifdef DEBUG // printf("Cache push: %s\n", getfqdn().tocstr()); #endif i.cache.prev = NULL; i.cache.next = zd_first; if (zd_first) zd_first->i.cache.prev = this; zd_first = this; registered = 2; n_cache_doms++; delta_n_cache_doms++; } void ZoneDomain::cache_top() { if (registered == 1) return; cache_unreg(); cache_register(); } void ZoneDomain::cache_donotremove() { cache_unreg(); registered = 1; } RrSet *ZoneDomain::get_rrset(uint16_t type) { int x; RrSet *set; if (registered == 2) cache_top(); for (x = rrsets.count - 1; x >= 0; x--) { set = rrsets[x]; if (set->expired()) { rrsets.removeindex(x); continue; } if (set->type == type) return rrsets[x]; } return NULL; } RrSet *ZoneDomain::create_rrset(uint16_t type) { RrSet *set = get_rrset(type); if (set) return set; set = new RrSet(type); rrsets.add(set); return set; } void ZoneDomain::remove_rrset(uint16_t type) { int x; for (x = 0; x < rrsets.count; x++) { if (rrsets[x]->type == type) { rrsets.removeindex(x); if (rrsets.count == 0 && subitems.isempty() && registered == 2) cache_bottom(); return; } } } void ZoneDomain::remove_old_rrsets() { int x, ttl, curtime = time(NULL); for (x = rrsets.count - 1; x >= 0; x--) { ttl = rrsets[x]->ttl; if (ttl > TTL_MAX_AUTHORITIVE && ttl < curtime) rrsets.removeindex(x); } } domainname ZoneDomain::getfqdn() { domainname z = name; if (parent) z += parent->getfqdn(); return z; } void ZoneDomain::removeme() { #ifdef DEBUG printf("Domain name %s filing for removal...", getfqdn().tocstr()); #endif if (parent) parent->subitems.remove_item(name); } /* Domain name functions --------------------------------------------------- */ ZoneDomain *create_domainname(ZoneDomain *zdom, const domainname &root, domainname &dom, int flags) { ZoneDomain *si, *rootdom = zdom; int nl = dom.nlabels() - ((domainname)root).nlabels(), t; stl_string label; if (nl == 0) { /* zone root */ if (flags & ZF_MUSTCREATE) return NULL; else return zdom; } for (t = nl - 1; t >= 0; t--) { label = dom.label(t); si = zdom->subitems.get_item(label.c_str()); if (si == NULL) { /* no such item */ si = new ZoneDomain(zdom, label.c_str()); if (!(flags & ZF_DONOTREMOVE) && rootdom == cache_root_domain) si->cache_register(); zdom->subitems.add_item(label.c_str(), si); } else if (t == 0 && flags & ZF_MUSTCREATE) { return NULL; } if (flags & ZF_DONOTREMOVE) si->cache_donotremove(); zdom = si; } return si; } ZoneDomain *lookup_domainname(ZoneDomain *zdom, const domainname &root, domainname &dom) { ZoneDomain *si; int nl = dom.nlabels() - ((domainname)root).nlabels(), t; stl_string label; if (nl == 0) return zdom; for (t = nl - 1; t >= 0; t--) { label = dom.label(t); si = zdom->subitems.get_item(label.c_str()); if (si == NULL) return NULL; zdom = si; } return si; } /* Base zone (cache & auth) ------------------------------------------------ */ BaseZone::BaseZone() { name = NULL; is_placeholder = true; parent = NULL; } BaseZone::BaseZone(const char *_name) { name = strdup(_name); is_placeholder = true; parent = NULL; } BaseZone::~BaseZone() { if (name) free(name); } domainname BaseZone::getfqdn() { domainname ret; BaseZone *z = this; while (z) { ret += z->name; z = z->parent; } return ret; } BaseZone *lookup_base_zone(domainname &dom, domainname *ret, BaseZone *root, bool may_be_placeholder) { int x; stl_string label; BaseZone *zn = root, *zn2; BaseZone *retzn = (!root->is_placeholder) ? root : NULL; if (!zn) return NULL; for (x = dom.nlabels() - 2; x >= 0; x--) { zn2 = zn->subitems.get_item(dom.label(x).c_str()); if (!zn2) break; zn = zn2; } if (ret) *ret = dom.from(x + 1); if (!zn->is_placeholder || may_be_placeholder) retzn = zn; return retzn; } void basezone_iterator_fn(BaseZone *newparent, char *name, BaseZone *data) { newparent->subitems.add_item(name, data); data->parent = newparent; } void takeover_base_zone(BaseZone *zn, BaseZone *z, domainname &dom, domainname &ret, BaseZone** root) { BaseZone *zptr; /* this is kind of ugly: we need to replace the zone */ zn->subitems.iterate((iteratorfn)basezone_iterator_fn, (void*)z); zn->subitems.clean(false); if (z->name) free(z->name); z->name = strdup(zn->name); z->parent = zn->parent; /* find the parent zone */ if (dom.c_str()[0] != 0) { ret = dom.from(1); zptr = lookup_base_zone(ret, NULL, (*root), true); zptr->subitems.remove_item(zn->name); zptr->subitems.add_item(z->name, z); } else { delete (*root); *root = z; } } void remove_base_zone(domainname &dom, BaseZone ** root) { int x; stl_string label; BaseZone *zn = *root, *par = NULL; domainname tmp; if (!zn) throw PException("Zone does not exist"); for (x = dom.nlabels() - 2; x >= 0; x--) { par = zn; zn = zn->subitems.get_item(dom.label(x).c_str()); if (!zn) throw PException("Zone does not exist"); } if (zn->is_placeholder) throw PException("Zone does not exist"); if (zn->subitems.isempty()) { if (zn == *root) { delete zn; root = NULL; } else { par->subitems.remove_item(zn->name); } } else { par = new BaseZone(); par->name = strdup(dom.label(0).c_str()); takeover_base_zone(zn, par, dom, tmp, root); } } void add_base_zone(domainname &dom, BaseZone *z, BaseZone** root) { domainname ret; stl_string label; BaseZone *zptr; BaseZone *zn = lookup_base_zone(dom, &ret, (*root), true); if (zn == NULL) { zn = *root; ret = ""; } /* and add subitems */ int nl = dom.nlabels() - ret.nlabels(), t; if (nl == 0) { if (!zn->is_placeholder) throw PException("That zone already exists"); takeover_base_zone(zn, z, dom, ret, root); return; } for (t = nl - 1; t >= 0; t--) { label = dom.label(t); if (t != 0) { zptr = new BaseZone(); } else { zptr = z; } if (zptr->name) free(zptr->name); zptr->name = strdup(label.c_str()); zptr->parent = zn; zn->subitems.add_item(label.c_str(), zptr); zn = zptr; } return; } /* Zone -------------------------------------------------------------------- */ void delete_zone(Zone *zn) { delete zn; } Zone::Zone() : BaseZone() { type = Z_NONE; deletefn = delete_zone; } Zone::Zone(const char *_name) : BaseZone(_name) { type = Z_NONE; deletefn = delete_zone; } Zone::~Zone() { /* remove occurences, if any, in the update list */ update_list_unregister(this); } _auth_ret Zone::stdquery_lookup(pending_query *q, domainname &qname, uint16_t QTYPE, domainname &znroot, DnsMessage *a) { a->RCODE = RCODE_SRVFAIL; return step6; } bool Zone::additional_lookup(domainname &qname, uint16_t qtype, stl_list(DnsRR)& section, domainname &znroot, pending_query *qinfo) { return false; } void Zone::feed_setting(const char *name, const char *val) { } void Zone::end_setting() { if (!zonefile.empty()) { /* this zone has a zone file */ domainname dom = getfqdn(); enqueue_update(getcurtime() + update_ttl*1000, dom, this, (update_function)start_zone_update); } } pthread_mutex_t m_auth_zones; Zone *auth_root_zone = NULL; Zone *lookup_authoritative_zone(domainname &dom, domainname *ret, bool may_be_placeholder) { return (Zone *)lookup_base_zone(dom, ret, auth_root_zone, may_be_placeholder); } void add_authoritative_zone(domainname &dom, Zone *z) { add_base_zone(dom, z, (BaseZone **)&auth_root_zone); } void zone_reload(void *data, char *name, Zone *z) { try { if (!z->is_placeholder) z->feed_setting("reload", ""); } catch (PException p) { } z->subitems.iterate((iteratorfn)zone_reload, NULL); } /* reload zones */ void *zones_reload(void *data) { pthread_mutex_lock(&m_auth_zones); pos_log(context_server, log_info, "Reloading all zones..."); zone_reload(NULL, "", auth_root_zone); pthread_mutex_unlock(&m_auth_zones); return NULL; } /* ---- cache -------------------------------------------------------------- */ CacheZone::CacheZone() : BaseZone() { forwarders = NULL; is_placeholder = false; } CacheZone::CacheZone(const char *_name) : BaseZone(_name) { forwarders = NULL; is_placeholder = false; } CacheZone::~CacheZone() { if (forwarders) delete forwarders; } CacheZone *lookup_cache_zone(domainname &dom, domainname *ret, bool may_be_placeholder) { return (CacheZone *)lookup_base_zone(dom, ret, cache_root_zone, may_be_placeholder); } void add_cache_zone(domainname &dom, CacheZone *z) { add_base_zone(dom, z, (BaseZone **)&cache_root_zone); } pthread_mutex_t m_cache; ZoneDomain *cache_root_domain; CacheZone *cache_root_zone; domainname root_domain_name = ""; ZoneDomain *zd_first; ZoneDomain *zd_last; void zones_del() { if (auth_root_zone) { auth_root_zone->deletefn(auth_root_zone); auth_root_zone = NULL; } if (cache_root_domain) delete cache_root_domain; if (cache_root_zone) delete cache_root_zone; } void zones_init() { zones_del(); auth_root_zone = new Zone(""); cache_root_domain = new ZoneDomain(NULL, ""); cache_root_zone = new CacheZone(""); zd_first = NULL; zd_last = NULL; n_cache_doms = 0; delta_n_cache_doms = 0; } class _static_zones_initializer { public: _static_zones_initializer() { pthread_mutex_init(&m_auth_zones, NULL); pthread_mutex_init(&m_cache, NULL); } ~_static_zones_initializer() { pthread_mutex_lock(&m_auth_zones); pthread_mutex_destroy(&m_auth_zones); pthread_mutex_lock(&m_cache); pthread_mutex_destroy(&m_cache); zones_del(); } } __static_zones_initializer;