#include #include #include #include #include #include #include #include #include #include #include "dnsconf.h" #include "dnsconf.m" #include "internal.h" #include "../paths.h" #include #include extern DNSCONF_HELP_FILE help_dnsconf; static DNSCONF_HELP_FILE help_root ("root"); const char subsys_dnsserv[] = "dnsserv"; static LINUXCONF_SUBSYS subsyst (subsys_dnsserv ,P_MSG_U(M_DNSSERVER,"DNS server")); class CONFIG_FILE_DNS: public CONFIG_FILE{ /*~PROTOBEG~ CONFIG_FILE_DNS */ public: CONFIG_FILE_DNS (const char *_path); int extract (SSTREAM&ss); /*~PROTOEND~ CONFIG_FILE_DNS */ }; PUBLIC CONFIG_FILE_DNS::CONFIG_FILE_DNS(const char *_path) : CONFIG_FILE(_path,help_dnsconf ,CONFIGF_MANAGED|CONFIGF_OPTIONAL|CONFIGF_PROBED ,subsys_dnsserv) { } PUBLIC int CONFIG_FILE_DNS::extract(SSTREAM &ss) { int ret = CONFIG_FILE::extract (ss); DNS dns (true); return ret; } static CONFIG_FILE_DNS f_boot (ETC_NAMED_BOOT); static CONFIG_FILE_DNS f_conf (ETC_NAMED_CONF); static const char K_DIRECTORY[] = "directory"; static const char K_CACHE[] = "cache"; static const char K_PRIMARY[] = "primary"; static const char K_SECONDARY[] = "secondary"; static const char K_FORWARDERS[] = "forwarders"; static const char K_SLAVE[] = "slave"; static const char K_OPTIONS[] = "options"; static const char K_FORWARD_ONLY[] = "forward-only"; static const char K_FORWARD[] = "forward"; static const char K_ONLY[] = "only"; static const char K_FIRST[] = "first"; static const char K_NO_RECURSION[] = "no-recursion"; static const char K_RECURSION[] = "recursion"; static const char K_NO_FETCH_GLUE[] = "no-fetch-glue"; static const char K_FETCH_GLUE[] = "fetch-glue"; static const char K_QUERY_LOG[] = "query-log"; static const char K_FAKE_IQUERY[] = "fake-iquery"; static const char K_XFRNETS[] = "xfrnets"; static const char K_BOGUSNS[] = "bogusns"; static const char K_SEARCHLIST[] = "searchlist"; const char K_ACL[] = "acl"; const char K_LOGGING[] = "logging"; const char K_ZONE[] = "zone"; const char K_FILE[] = "file"; const char K_TYPE[] = "type"; const char K_MASTER[] = "master"; static const char K_MASTERS[] = "masters"; static const char K_HINT[] = "hint"; const char K_ALLOW_TRANSFER[]="allow-transfer"; const char K_ALLOW_QUERY[]="allow-query"; const char K_ALLOW_RECURSION[]="allow-recursion"; const char K_ALLOW_UPDATE[]="allow-update"; const char K_NOTIFY[]="notify"; const char K_ALSO_NOTIFY[]="also-notify"; static const char K_LISTEN_ON[]="listen-on"; static const char K_PORT[]="port"; static const char K_STUB[] = "stub"; static const char K_IN[] = "in"; static const char K_CHAOS[] = "chaos"; static const char K_HS[]="hs"; static const char K_HESIOD[]="hesiod"; static bool dns_is_reverse(const char *str) { bool ret = false; char buf[200]; strcpy (buf,str); strupr (buf); char *pt = strstr(buf,".IN-ADDR.ARPA"); if (pt != NULL){ if (pt[13] == '\0'){ ret = true; } } return ret; } /* Parse lines in /etc/named.boot */ PRIVATE void DNS::parse4( char *buf, const char *fpath, int noline, bool extract) { strip_end (buf); char *pt = str_skip(buf); /* #Specification: dnsconf / /etc/named.boot / comments Comments in /etc/named.boot are not preserved by dnsconf */ if (*pt != '\0' && *pt != ';'){ char words[10][500]; for (int i=0; i<10; i++) words[i][0] = '\0'; int nb = sscanf (buf,"%s %s %s %s %s %s %s %s %s %s" ,words[0],words[1],words[2],words[3] ,words[4],words[5],words[6],words[7] ,words[8],words[9]); const char *args = str_skipword (buf); if (stricmp(words[0],K_DIRECTORY)==0){ pathcfg.setfrom (linuxconf_getval(K_DNSCONF,K_CHROOT,"")); pathcfg.append (words[1]); pathcfg_conf.setfrom (words[1]); if (file_rtype(pathcfg.get())!=1){ xconf_error (MSG_U(E_DIRPATH ,"The \"directory\" directive in %s\n" "points to a missing directory (%s).\n" "Create the directory or fix the directive before\n" "using linuxconf to maintain your DNS.") ,fpath ,pathcfg.get()); } }else if (stricmp(words[0],K_CACHE)==0){ cachefiles.add (new CACHEFILE (words[1],words[2])); }else if (stricmp(words[0],K_PRIMARY)==0){ if (dns_is_reverse(words[1])){ primarys_rev.add (new PRIMARY_REV(words[1] ,words[2],pathcfg.get(),extract)); }else{ primarys.add (new PRIMARY(words[1],words[2] ,pathcfg.get(),extract)); } }else if (stricmp(words[0],K_SECONDARY)==0){ const char *tbip[8]; for (int i=0; i<8; i++) tbip[i] = words[i+2]; secondarys.add (new SECONDARY (words[1],words[nb-1] ,pathcfg.get(),extract ,tbip,nb-3)); }else if (stricmp(words[0],K_FORWARDERS)==0){ for (int f=1; fadd (tok,comment); parseunknown (un,lex); } } } } /* Parse a list of item, starting just after the opening brace */ PRIVATE void DNS::parselist_end ( BIND8_LEXPARSE &lex, SSTRINGS &tb) { while (1){ const char *tok = lex.getarg1(); if (tok == NULL){ break; }else if (strcmp(tok,"}")==0){ break; }else{ tb.add (new SSTRING(tok)); } } } /* Collect a list of items that look like this # { item ; . . } # */ PRIVATE void DNS::parselist ( BIND8_LEXPARSE &lex, SSTRINGS &tb) { if (lex.expect("{")!=-1){ parselist_end (lex,tb); } } PRIVATE DNS & DNS::operator =(const DNS &) { assert(0); return *this; } PRIVATE DNS::DNS (const DNS &) { assert(0); } PRIVATE void DNS::parsezone ( BIND8_LEXPARSE &lex, bool extract) { SSTRING zone,file; SSTRINGS masters,tforwarders,allowtrans,allowquery; SSTRINGS allowrecursion,allowupdate,alsonotify; char notify = NOTIFY_DEFAULT; char option_forward_only = FORWARD_FIRST; // the default ZONE_DATA_TYPE datatype = ZONE_DATA_NONE; zone.setfrom (lex.gettoken()); const char *tok = lex.gettoken(); if (tok == NULL) return; ZONE_TYPE type = ZONE_UNKNOWN; if (stricmp(tok, K_IN)==0){ datatype = ZONE_DATA_IN; tok = lex.gettoken(); }else if (stricmp(tok, K_CHAOS)==0){ datatype = ZONE_DATA_CHAOS; tok = lex.gettoken(); }else if (stricmp(tok, K_HS)==0){ datatype = ZONE_DATA_HS; tok = lex.gettoken(); }else if (stricmp(tok, K_HESIOD)==0){ datatype = ZONE_DATA_HESIOD; tok = lex.gettoken(); } if (!zone.is_empty() && tok != NULL && strcmp(tok,"{")==0){ while (!lex.waserr()){ tok = lex.gettoken(); if (tok == NULL){ break; }else if (strcmp(tok,"}")==0){ lex.expect (";"); break; }else if (stricmp(tok,K_FILE)==0){ file.setfrom(lex.getarg1()); }else if (stricmp(tok,K_TYPE)==0){ tok = lex.getarg1(); if (tok != NULL){ if (stricmp(tok,K_MASTER)==0){ type = ZONE_PRIMARY; }else if (stricmp(tok,K_SLAVE)==0){ type = ZONE_SECONDARY; }else if (stricmp(tok,K_HINT)==0){ type = ZONE_HINT; }else if (stricmp(tok,K_FORWARD)==0){ type = ZONE_FORWARD; }else if (stricmp(tok,K_STUB)==0){ type = ZONE_STUB; lex.error (MSG_U(E_STUBNOTIMPLEMENTED ,"This version of Linuxconf" " doesn't yet support stub zones")); }else{ lex.error (MSG_U(E_EXPECTTYPE ,"Expected either master," " slave, hint or forward")); } } }else if (stricmp(tok,K_MASTERS)==0){ if (lex.expect("{")!=-1){ while (1){ tok = lex.gettoken(); if (tok == NULL){ break; }else if (strcmp(tok,"}")==0){ lex.expect (";"); break; }else if (!ipnum_validip(tok,true)){ lex.error (MSG_U(E_IVLDIP,"Expected valid IP number: %s") ,tok); }else{ masters.add (new SSTRING(tok)); if (lex.expect (";") == -1) break; } } } }else if (stricmp(tok,K_FORWARDERS)==0){ if (lex.expect("{")!=-1){ while (1){ tok = lex.gettoken(); if (tok == NULL){ break; }else if (strcmp(tok,"}")==0){ lex.expect (";"); break; }else if (!ipnum_validip(tok,true)){ lex.error (MSG_R(E_IVLDIP) ,tok); }else{ tforwarders.add (new SSTRING(tok)); if (lex.expect (";") == -1) break; } } } }else if (stricmp(tok,K_ALLOW_TRANSFER)==0){ parselist (lex,allowtrans); }else if (stricmp(tok,K_ALLOW_QUERY)==0){ parselist (lex,allowquery); }else if (stricmp(tok,K_ALLOW_RECURSION)==0){ parselist (lex,allowrecursion); }else if (stricmp(tok,K_ALLOW_UPDATE)==0){ parselist (lex,allowupdate); }else if (stricmp(tok,K_ALSO_NOTIFY)==0){ parselist (lex,alsonotify); }else if (stricmp(tok,K_NOTIFY)==0){ notify = lex.getarg_yesno(); }else if (stricmp(tok,K_FORWARD)==0){ tok = lex.getarg1(); if (tok != NULL){ if (stricmp(tok,K_FIRST)==0){ option_forward_only = FORWARD_FIRST; }else if (stricmp(tok,K_ONLY)==0){ option_forward_only = FORWARD_ONLY; } } } } } if (type == ZONE_UNKNOWN){ lex.error (MSG_U(E_ZONETYPE,"Missing zone type (master, slave, hint, forward or stub)")); }else if (type == ZONE_PRIMARY){ const char *zn = zone.get(); PRIMARY *pri; if (dns_is_reverse(zn)){ pri = new PRIMARY_REV(zn,file.get(),pathcfg.get(),extract); primarys_rev.add (pri); }else{ pri = new PRIMARY(zn,file.get(),pathcfg.get(),extract); primarys.add (pri); } pri->datatype = datatype; pri->notify = notify; pri->allowtrans.append (allowtrans); pri->allowquery.append (allowquery); pri->allowrecursion.append (allowrecursion); pri->allowupdate.append (allowupdate); pri->alsonotify.append (alsonotify); }else if (type == ZONE_FORWARD){ const char *zn = zone.get(); FORWARD * forw; int n = tforwarders.getnb(); const char *tbip[n]; for (int i=0; iget(); forw = new FORWARD (zn, tbip, n ); zones_forward.add (forw); forw->option_forward_only=option_forward_only; }else if (type == ZONE_HINT){ cachefiles.add (new CACHEFILE(zone.get(),file.get())); }else{ /* zone stub support to add here */ int n = masters.getnb(); const char *tbip[n]; SECONDARY *sec; for (int i=0; iget(); sec=new SECONDARY (zone.get(),file.get() ,pathcfg.get(),extract ,tbip,n); secondarys.add (sec); sec->datatype = datatype; sec->notify = notify; sec->allowtrans.append (allowtrans); sec->allowquery.append (allowquery); sec->allowrecursion.append (allowrecursion); sec->allowupdate.append (allowupdate); sec->alsonotify.append (alsonotify); } } PRIVATE void DNS::parseacl(BIND8_LEXPARSE &lex) { SSTRING name; name.setfrom (lex.gettoken()); if (lex.expect("{")!=-1){ ACL *acl = new ACL (name.get()); acls.add (acl); while (1){ const char *tok1 = lex.gettoken(); if (tok1 == NULL){ break; }else if (strcmp(tok1,"}")==0){ lex.expect (";"); break; }else{ SSTRING ip (tok1); SSTRING comment (lex.getcomment()); const char *tok2 = lex.gettoken(); if (tok2 == NULL){ break; }else if (strcmp(tok2,";")==0){ acl->add (new ADDRESS_MATCH(ip.get(),comment.get())); }else if (strcmp(tok2,"/")==0){ const char *tok3 = lex.getarg1(); if (tok3 == NULL) break; acl->add (new ADDRESS_MATCH(ip.get(),tok3,comment.get())); } } } } } PRIVATE void DNS::parseunknown(UNKNOWN *un, BIND8_LEXPARSE &lex) { int level = 0; while (1){ const char *tok = lex.gettoken(true); if (tok == NULL){ break; }else{ if (tok[0] == ';' && tok[1] == '\0' && level == 0) break; const char *comment = lex.getcomment(); un->add (tok,comment); if (strcmp(tok,"}")==0){ level--; }else if (strcmp(tok,"{")==0){ level++; } } } } /* Parse lines in /etc/named.conf */ PRIVATE void DNS::parse8( BIND8_LEXPARSE &lex, bool extract) { while (1){ const char *tok = lex.gettoken(); const char *comment = lex.getcomment(); if (tok == NULL){ break; }else if (stricmp(tok,K_OPTIONS)==0){ parseoptions (lex); }else if (stricmp(tok,K_ZONE)==0){ parsezone (lex,extract); }else if (stricmp(tok,K_ACL)==0){ parseacl (lex); }else if (stricmp(tok,K_LOGGING)==0){ logging.add (K_LOGGING,comment); parseunknown (&logging,lex); }else{ UNKNOWN *un = new UNKNOWN; unknowns.add (un); un->add (tok,comment); parseunknown (un,lex); } } } /* Add the reverse mapping for the loopback network */ PUBLIC void DNS::setlocalhost() { PRIMARY_REV *pri = new PRIMARY_REV; pri->domainv.setfrom ("127.0.0"); pri->setfromv(); pri->file.setfrom ("127.0.0"); pri->addrec (new RECORD_IN_SOA); pri->addrec (new RECORD_IN_NS); pri->addrec (new RECORD_IN_PTR ("1","localhost.")); primarys_rev.add (pri); } PRIVATE void DNS::init(bool extract) { options.forward_only=0; options.no_recursion=0; options.no_fetch_glue=0; options.query_log=0; options.fake_iquery=0; options.notify=true; //notify is enabled by default (bind8) pathcfg.setfrom ("/var/named"); pathcfg_conf.setfrom ("/var/named"); /* #Specification: dns / bind4 and bind8 The dnsconf module supports both format (bind4 named.boot and bind8 named.conf) on the fly. It first checks for /etc/named.conf. If missing, it checks for named.boot. */ bind8 = false; if (f_conf.exist()){ bind8 = true; BIND8_LEXPARSE lex(f_conf,pathcfg); if (lex.isok()){ parse8 (lex,extract); } }else{ FILE_CFG *fin = f_boot.fopen ("r"); if (fin != NULL){ char buf[500]; int noline = 0; while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ noline++; parse4 (buf,f_boot.getpath(),noline,extract); } fclose (fin); } } } #if 0 static void dump (SSTRINGS &tb, const char *m) { for (int i=0; iget()); } } #endif PUBLIC DNS::DNS() { init(false); #if 0 dump (options.listenon,"listen"); dump (options.allowtrans,"allowtrans"); dump (options.allowquery,"allowquery"); #endif } /* Use for extracting a version from the archive */ PUBLIC DNS::DNS(bool extract) { init(extract); } /* Return != 0 if the DNS is not configured at all */ PUBLIC int DNS::empty() { return primarys.getnb()==0 && primarys_rev.getnb()==0; } PUBLIC bool DNS::getnotify() const { return options.notify; } PUBLIC void DNS::basiccheck() { static const char *root_cache = "root.cache"; if (cachefiles.getnb()==0){ cachefiles.add (new CACHEFILE(".",root_cache)); } if (!context_fexist(pathcfg.get())){ context_mkdir (pathcfg.get(),"root","root",0755); } for (int i=0; iabspath (pathcfg.get(),abspath); if (!file_exist(abspath)){ const char *cname = c->getname(); if (strcmp(cname,root_cache)==0){ char buf[2000]; snprintf (buf,sizeof(buf)-1,MSG_U(I_MISSCACHE ,"The file %s\n" "is not installed on your system.\n" "This will prevent normal operation of your DNS.\n" "I can install a copy immediatly.\n" "\n" "Get an up to date version at\n" "ftp://ftp.rs.internic.net/domain/named.root\n" "\n" "Do I install a copy of root.cache now ?") ,abspath); if (dialog_yesno(MSG_U(T_MISSCACHE,"Missing root file") ,buf,help_root)==MENU_YES){ if (file_copy (USR_LIB_LINUXCONF_ROOT_CACHE,abspath)!=0){ xconf_error (MSG_U(E_CANTCOPY ,"Can't copy\n" " %s\n" " to\n" " %s\n" "correctly") ,USR_LIB_LINUXCONF_ROOT_CACHE,abspath); } } }else{ xconf_error (MSG_U(E_MISSCACHE ,"Missing cache file %s\n" "This DNS won't operate normally") ,cname); } } } FQHOST rfq ("1.0.0.127.in-addr.arpa"); PRIMARY *rev = primarys_rev.getitem(rfq,NULL); if (rev == NULL){ /* #Specification; dnsconf / check / local domain Dnsconf make sure the domain 0.0.127.in-addr.arpa is defined. If not it is added with a single PTR entry. # 1 IN PTR localhost. # */ setlocalhost(); } } static void dns_writeopt_if (FILE_CFG *fout, char opt, const char *title) { if (opt) fprintf (fout,"%s %s\n",K_OPTIONS,title); } /* Create the directory for zone files if needed */ PRIVATE void DNS::mkzonedir() { const char *cfg = pathcfg.get(); if (!context_fexist (cfg)) context_mkdir (cfg,"root","root",0755); } void dns_writeopts(const SSTRINGS &tb, const char *keyword, FILE_CFG *fout) { if (tb.getnb()>0){ fprintf (fout,"\t%s{\n",keyword); for (int f=0; fget(); if (s[0] != '\0') fprintf (fout,"\t\t%s;\n",s); } fputs ("\t};\n",fout); } } /* Return the default directory where zone files are stored. */ PUBLIC const char *DNS::getcfgdir() const { return pathcfg.get(); } /* Write all configuration files of the DNS. Return -1 if any error. */ PUBLIC int DNS::write () { int ret = -1; /* #Specification: dnsconf / writing /etc/named.boot (or named.conf) The file /etc/named.conf is rewritten everytime any of the DNS files are rewritten. This give a hint to netconf to instruct named to restart(reload) just by looking at the revision date of /etc/named.conf */ if (bind8){ FILE_CFG *fout = f_conf.fopen ("w"); if (fout != NULL){ mkzonedir(); acls.write (fout); fputs ("options {\n",fout); fprintf (fout,"\t%s \"%s\";\n",K_DIRECTORY,pathcfg_conf.get()); if (options.forward_only){ fprintf (fout,"\t%s %s;\n",K_FORWARD,K_ONLY); } if (options.fake_iquery){ fprintf (fout,"\t%s yes;\n",K_FAKE_IQUERY); } if (options.no_fetch_glue){ fprintf (fout,"\t%s no;\n",K_FETCH_GLUE); } if (options.no_recursion){ fprintf (fout,"\t%s no;\n",K_RECURSION); } if (options.notify==false){ fprintf (fout,"\t%s no;\n",K_NOTIFY); } dns_writeopts (forwarders,K_FORWARDERS,fout); dns_writeopts (options.allowtrans,K_ALLOW_TRANSFER,fout); dns_writeopts (options.allowquery,K_ALLOW_QUERY,fout); dns_writeopts (options.allowrecursion,K_ALLOW_RECURSION,fout); { const char *keyw = K_LISTEN_ON; SSTRING buf; if (!options.listenonport.is_empty()){ buf.setfromf ("%s %s %s",K_LISTEN_ON,K_PORT ,options.listenonport.get()); keyw = buf.get(); } dns_writeopts (options.listenon,keyw,fout); } options.unknowns.write (fout,1); fputs ("};\n",fout); logging.write(fout,0); unknowns.write (fout,0); for (int i=0; idomain.get()); fprintf (fout,"\t%s %s;\n",K_TYPE,K_HINT); fprintf (fout,"\t%s \"%s\";\n",K_FILE,c->path.get()); fputs ("};\n",fout); } const char *named_dir = getcfgdir(); ret = primarys.write(true,fout,named_dir,this); ret |= primarys_rev.write(true,fout,named_dir,this); secondarys.write(true,fout,named_dir); zones_forward.write(true,fout); ret |= fclose (fout); } }else{ FILE_CFG *fout = f_boot.fopen ("w"); if (fout != NULL){ mkzonedir(); fprintf (fout,"%s\t%s\n",K_DIRECTORY,pathcfg_conf.get()); for (int i=0; idomain.get() ,c->path.get()); } const char *named_dir = pathcfg.get(); ret = primarys.write(false,fout,named_dir,this); ret = primarys_rev.write(false,fout,named_dir,this); secondarys.write(false,fout,named_dir); if (forwarders.getnb() > 0){ fputs (K_FORWARDERS,fout); for (int f=0; fget(),fout); } fputc ('\n',fout); } dns_writeopt_if (fout,options.forward_only,K_FORWARD_ONLY); dns_writeopt_if (fout,options.no_recursion,K_NO_RECURSION); dns_writeopt_if (fout,options.no_fetch_glue,K_NO_FETCH_GLUE); dns_writeopt_if (fout,options.query_log,K_QUERY_LOG); dns_writeopt_if (fout,options.fake_iquery,K_FAKE_IQUERY); if (!xfernets.is_empty()) fprintf (fout,"%s %s\n",K_XFRNETS,xfernets.get()); if (!bogusns.is_empty()) fprintf (fout,"%s %s\n",K_BOGUSNS,bogusns.get()); if (!searchlist.is_empty()) fprintf (fout,"%s %s\n",K_SEARCHLIST,searchlist.get()); ret = fclose (fout); } } return ret; } #if 0 static void qualify( const char *fqhost, char *host, char *domain) { char *pt = strchr(fqhost,'.'); if (pt != NULL){ strcpy (domain,pt+1); int len = (int)(pt-fqhost); memcpy (host,fqhost,len); host[len] = '\0'; }else{ strcpy (host,fqhost); RESOLV res; strcpy (domain,res.domain.get()); } } #endif /* Walk the DNS to preload a newly created reverse mapping zone */ PUBLIC void DNS::preload_rev (PRIMARY *pri) { const char *domain_norange = pri->getdomain_norange(); for (int p=0; porigins; for (int i=0; igetnb(); i++){ ORIGIN *ori = origins->getitem(i); for (int o=0; otbrec.getnb(); o++){ RECORD *rec = ori->tbrec.getitem(o); if (rec->is (RTYPE_A)){ RECORD_IN_A *a = (RECORD_IN_A*)rec; IP_ADDR *ip = &a->addr; char revdom[40]; ip->setrev(revdom); FQHOST rfq (revdom); char left[100]; if (rfq.is_member(domain_norange,left) && pri->matchrange(left)){ pri->unset_left (left); char fqdn[PATH_MAX]; snprintf (fqdn,sizeof(fqdn)-1,"%s.%s",a->name.get() ,ori->origin.get()); pri->set (rfq ,new RECORD_IN_PTR("dummy",fqdn)); } } } } } } /* Add or replace record (A and PTRs) for a host Return 1 if a domain was updated. 0 if none. Return the number of reverse mapping domains which were updated. */ PUBLIC int DNS::set ( const char *fqhost, // Fully qualified name, or name // relative to our own domain const char *tbip[], // May set several IP number for int nbip, // one host at once const SSTRINGS &ttls, char doreverse[], int &matchrev) // Number of reverse mapping domain which // were updated { /* #Specification: dnsconf / set a host / multiple IP A host may have several IP number in the DNS. The first one is known as the official one. So a host may have several A record and several corresponding PTR records. */ FQHOST fq (fqhost); /* #Specification: dnsconf / setting new spec for a host dnsconf use a fairly simple way to update a host definition. It simply delete everything relate to that host (A record(s) and PTR record(s)) and add records in the proper primarys. There is no chance, using this strategy, to keep the original ordering of the record files. This is why dnsconf always sort those before writing. For the PTR record, it use two cleanups. It delete all PTR pointing to the host and it deletes all PTR associated with the new IPs for that host. This way, we know that the PTR record are always unique and clean. */ unset (fqhost); // Checking if this DNS is the primary for the domain char hostpart[200]; PRIMARY *pri = primarys.getitem(fq,hostpart); bool is_domain = false; // fqhost is a domain itself // manage by this DNS // so we do not update the reverse mapping if (pri != NULL){ is_domain = strcmp(hostpart,"@")==0; for (int i=0; isetttl (ttls.getitem(i)); pri->set (fq,rec); } } matchrev = 0; if (!is_domain){ for (int noip=0; noip < nbip; noip++){ if (doreverse[noip]){ const char *ip = tbip[noip]; IP_ADDR ipa; ipa.setfrom(ip); char revdom[40]; ipa.setrev(revdom); FQHOST rfq (revdom); char left[100]; PRIMARY *rev = primarys_rev.getitem(rfq,left); if (rev != NULL){ rev->unset_left (left); char fqdn[PATH_MAX]; fq.formatfull (fqdn); rev->set (rfq ,new RECORD_IN_PTR("dummy",fqdn)); matchrev++; } } } } return pri != NULL ? 1 : 0; } /* Set the A record of a host, but use the IP to do the cleanup (delete all host pointing to this IP Return 1 if a domain was updated, 0 if none. */ PUBLIC int DNS::setfromip(const char *host, const char *ip) { SSTRINGS hosts; locate_ip (ip,hosts); // We delete all hosts which refer to this IP for (int i=0; iget()); } const char *tb[]={ip}; return set (host,tb,1); } /* Add or replace record (A and PTRs) for a host Return -1 if fqhost was not part of any domain (not even reverse mapping). Return the number of reverse mapping domains which were updated. */ PUBLIC int DNS::set ( const char *fqhost, // Fully qualified name, or name // relative to our own domain const char *tbip[], // May set several IP number for int nbip, // one host at once const SSTRINGS &ttls) { int matchrev; char doreverse[nbip]; memset (doreverse,1,nbip); return set (fqhost,tbip,nbip,ttls,doreverse,matchrev); } PUBLIC int DNS::set ( const char *fqhost, // Fully qualified name, or name // relative to our own domain const char *tbip[], // May set several IP number for int nbip) // one host at once { SSTRINGS ttls; return set (fqhost,tbip,nbip,ttls); } /* Add or replace NS record for a host Return -1 if the information could not be added to any domain. */ PUBLIC int DNS::setns ( const SSTRING &fqhost, // Fully qualified name, or name // relative to our own domain const SSTRINGS &tbns, // May set several NS for // one host at once const SSTRINGS &ttls) { /* #Specification: dnsconf / set a domain / multiple NS A domain may have several NS record in the DNS. The first one is known as the official one. The others are secondaries. */ FQHOST fq (fqhost); unsetns (fqhost); int ret = -1; PRIMARY *last = NULL; int is_reverse = fq.is_reverse(); for (int i=0; i<2; i++){ /* #Specification: dnsconf / domain and sub-domain A DNS may hold information for a domain and for sub-domain. When updating the NS records of a sub-domain, we also update NS records in the parent domain automaticly. */ PRIMARY *pri = is_reverse ? primarys_rev.getitem(fq,NULL,i) : primarys.getitem(fq,NULL,i); if (pri != NULL && pri != last){ /* #Specification: reverse zone / classless delegation / trick Classless reverse zones have the an origin which looks like this: x-y.c.b.a.in-addr.arpa. To help dispatch the IP number in those zones (the ptr records), it is easier to use the standard origin of a normal reverse zone: c.b.a.in-addr.arpa. After matching the proper zone, we just have to check that the IP is in the proper range. But this fake origin is causing problems when we are setting the NS record for the zone. We end up with a record looking like this x-y IN PTR some_server. instead of @ IN PTR some_server. So we use a special trick when setting the NS record for those zone. We use the PRIMARY::getdomain_norange() instead of the full zone name. */ FQHOST *ptfq = &fq; FQHOST fq_norange (pri->getdomain_norange()); if (pri->domain.icmp(fqhost)==0) ptfq = &fq_norange; int nbns = tbns.getnb(); for (int j=0; jis_empty()){ RECORD_IN_NS *rec = new RECORD_IN_NS("dummy",ns->get()); rec->setttl (ttls.getitem(j)); pri->set (*ptfq,rec); } } ret = 0; last = pri; } } return ret; } /* Add or replace MX record for a host */ PUBLIC int DNS::setmx ( const SSTRING &fqhost, // Fully qualified name, or name // relative to our own domain const SSTRINGS &tbmx, // May set several MX for // one host at once const SSTRINGS &ttls) { /* #Specification: dnsconf / set a host / multiple MX A host may have several MX record in the DNS. dnsconf automaticly assign a priority number based on the order the different MXs are given. */ FQHOST fq (fqhost); unsetmx (fqhost); PRIMARY *pri = primarys.getitem(fq,NULL); if (pri != NULL){ int nbmx = tbmx.getnb(); for (int i=0; iis_empty()){ SSTRING *ss = ttls.getitem(i); const char *s = ss != NULL ? ss->get() : ""; pri->setonemx (fq,mx->get(),i*5+5,s); } } } return (pri != NULL) ? 1 : -1; } /* Remove all reference to a host in the DNS */ PUBLIC int DNS::unset (const char *fqhost) { int ret = -1; FQHOST fq (fqhost); char hostpart[200]; PRIMARY *pri = primarys.getitem(fq,hostpart); if (pri != NULL){ ret |= pri->unset (new RECORD_IN_A(hostpart,"1.1.1.1")); } char full[200]; fq.formatfull (full); for (int i=0; iunset (new RECORD_IN_PTR("1",full)); } return ret; } /* Remove all reference to a host in the DNS */ PUBLIC int DNS::unsetns (const SSTRING &fqhost) { int ret = -1; FQHOST fq (fqhost); int is_reverse = fq.is_reverse(); for (int i=0; i<2; i++){ char hostpart[200]; PRIMARY *pri = is_reverse ? primarys_rev.getitem(fq,hostpart,i!=0) : primarys.getitem(fq,hostpart,i!=0); if (pri != NULL){ ret = pri->unset (new RECORD_IN_NS(hostpart,"dummy")); } } return ret; } /* Remove all reference to a host in the DNS */ PUBLIC int DNS::unsetmx (const SSTRING &fqhost) { int ret = -1; FQHOST fq (fqhost); char hostpart[200]; PRIMARY *pri = primarys.getitem(fq,hostpart); if (pri != NULL){ ret = pri->unset (new RECORD_IN_MX(hostpart,0,"dummy")); } return ret; } /* Set a cname record (replace/add/remove) */ PUBLIC int DNS::setcname (const SSTRING &fqhost, const SSTRING &cname) { FQHOST fq (fqhost); char hostpart[200]; PRIMARY *pri = primarys.getitem(fq,hostpart); if (pri != NULL){ pri->unset (new RECORD_IN_CNAME(hostpart,"dummy")); if (!cname.is_empty()){ pri->set (fq,new RECORD_IN_CNAME(hostpart ,cname.get())); } } return (pri != NULL) ? 1 : -1; } /* Set a cname record (replace/add/remove) */ PUBLIC int DNS::unsetcname (const SSTRING &fqhost) { int ret = -1; FQHOST fq (fqhost); char hostpart[200]; PRIMARY *pri = primarys.getitem(fq,hostpart); if (pri != NULL){ ret = pri->unset (new RECORD_IN_CNAME(hostpart,"dummy")); } return ret; } /* Locate a domain in the DNS */ PUBLIC PRIMARY *DNS::finddomain (const char *name) const { PRIMARY *ret = primarys.getitem(name); if (ret == NULL) ret = primarys_rev.getitem(name); return ret; } /* Locate a secondary in the DNS */ PUBLIC SECONDARY *DNS::findsecond (const char *name) const { return secondarys.getitem(name); } /* Locate a forward zone in the DNS */ PUBLIC FORWARD *DNS::findforward (const char *name) const { return zones_forward.getitem(name); } PRIVATE void DNS::setconfig(SSTRING &p) { char tmp[PATH_MAX]; const char *path = p.get(); CONFIG_FILE *cfg; // return value not used if (path[0] != '/'){ snprintf (tmp,sizeof(tmp)-1,"%s/%s",pathcfg.get(),path); path = tmp; } cfg=new CONFIG_FILE (tmp,help_dnsconf,CONFIGF_MANAGED|CONFIGF_OPTIONAL ,subsys_dnsserv); } PUBLIC void DNS::listconfigs() { if (!empty()){ int i; for (i=0; ipath); } PRIMARYS *rys = &primarys; for (int p=0; p<2; p++, rys=&primarys_rev){ for (i=0; igetnb(); i++){ PRIMARY *c = rys->getitem(i); setconfig(c->file); for (int o=0; oorigins.getnb(); o++){ ORIGIN *ori = c->origins.getitem(o); for (int r=0; rtbrec.getnb(); r++){ RECORD *rec = ori->tbrec.getitem(r); if (rec->is (RTYPE_INCLUDE)){ RECORD_INCLUDE *inc = (RECORD_INCLUDE*)rec; setconfig (inc->path); } } } } } } } /* Return the maximum between the revision date of file path and maxdate */ static long dns_date (long maxdate, const char *basedir, const SSTRING &path) { char tmp[PATH_MAX]; const char *fname = path.get(); if (fname[0] != '/'){ snprintf (tmp,sizeof(tmp)-1,"%s/%s",basedir,fname); fname = tmp; } long date = file_date(tmp); if (getenv("DNSCONF_DEBUG")!=NULL){ fprintf (stderr,"Compare file %s: maxdate %ld date %ld %s\n" ,tmp,maxdate,date ,date > maxdate ? "File is newer" : ""); } if (date > maxdate) maxdate = date; return maxdate; } /* Get the date of the newest configuration file of this DNS */ PUBLIC long DNS::getrevdate() { int i; long ret = f_boot.getdate(); long confdate = f_conf.getdate(); if (confdate > ret) ret = confdate; const char *basedir = pathcfg.get(); for (i=0; ipath); } PRIMARYS *rys = &primarys; for (int p=0; p<2; p++, rys=&primarys_rev){ for (i=0; igetnb(); i++){ PRIMARY *c = rys->getitem(i); ret = dns_date (ret,basedir,c->file); for (int o=0; oorigins.getnb(); o++){ ORIGIN *ori = c->origins.getitem(o); for (int r=0; rtbrec.getnb(); r++){ RECORD *rec = ori->tbrec.getitem(r); if (rec->is (RTYPE_INCLUDE)){ RECORD_INCLUDE *inc = (RECORD_INCLUDE*)rec; ret = dns_date (ret,basedir,inc->path); } } } } } return ret; } /* Locate the zone managing this host Return NULL if none found. */ PUBLIC PRIMARY *DNS::findprimary (const char *host) { FQHOST fq (host); return primarys.getitem(fq,NULL); } /* Return true if the DNS is configured (There is a named.boot) */ bool dns_configured() { return f_conf.exist() != 0 || f_boot.exist() != 0; } static void dns_lister_fct() { DNS dns; dns.listconfigs(); } static CONFIG_FILE_LISTER dns_lister(dns_lister_fct);