#include #include #include #include #include "dnsconf.h" #include "internal.h" #include #include "dnsconf.m" #include static DNSCONF_HELP_FILE help_primary("primary"); static DNSCONF_HELP_FILE help_secondary("secondary"); PUBLIC ZONE::ZONE() { datatype = ZONE_DATA_NONE; isrev = 0; notify = NOTIFY_DEFAULT; option_forward_only = 0; zonetype = ZONE_PRIMARY; transfered = false; } PRIVATE void PRIMARY::init( const char *_domain, const char *_file) // File used to store the configuration { domain.setfrom (_domain); file.setfrom (_file); domainv.setfrom (_domain); isrev = 0; zonetype=ZONE_PRIMARY; transfered = true; } /* Used to read back an existing configuration */ PUBLIC PRIMARY::PRIMARY( const char *_domain, const char *_file, // File used to store the configuration const char *named_dir, // Default directory for configuration files bool extract) // Extract from the archive { init (_domain,_file); origins.read (named_dir,_file,_domain,extract); } /* Used to read back an existing configuration */ PROTECTED PRIMARY::PRIMARY( const char *_domain, const char *_file) // File used to store the configuration { init (_domain,_file); } PUBLIC PRIMARY::PRIMARY() { isrev = 0; zonetype=ZONE_PRIMARY; transfered = true; notify = NOTIFY_DEFAULT; } /* Update the domain name from the visual one. */ PUBLIC VIRTUAL void ZONE::setfromv() { domain.setfrom (domainv); } /* Check if this domaine is a member of the IN-ADDR.ARPA domain Return true if this is the case. */ static bool primary_is_arpadom (const char *dom) { char buf[strlen(dom)+1]; strcpy (buf,dom); strupr (buf); char *pt = strstr(buf,".IN-ADDR.ARPA"); return pt !=NULL && pt[13] == '\0'; } /* Extract the IP number of a in-addr.arpa domain and reverse it x,y.z.in-addr.arpa becomes z.y.x */ static void primary_reverse_arpa( const char *arpadom, SSTRING &domainv, SSTRING &range) { range.setfrom (""); const char *pt = str_skipdig (arpadom); if (pt != arpadom && *pt == '-'){ // This is an RFC2317? reverse zone. // See (http://www.dns.net/dnsrd/rfc/rfc2317.html ) // Well instead of using / to specify subnet, we use - to specify // ranges. this is more flexible since any range may be delegated // and also because / is invalid for DNS. pt++; pt = str_skipdig(pt); int len = (int)(pt-arpadom); range.setfrom (arpadom,len); if (*pt == '.') pt++; arpadom = pt; } IP_ADDR ipa; ipa.setfrom (arpadom); ipa.reverse (); ipa.shift(); domainv.setfrom (ipa.get()); } /* Used to read back an existing configuration */ PUBLIC PRIMARY_REV::PRIMARY_REV( const char *_domain, const char *_file, // File used to store the configuration const char *named_dir, // Default directory for configuration files bool extract) : PRIMARY(_domain,_file) { primary_reverse_arpa (_domain,domainv,range); isrev = 1; origins.read (named_dir,_file,getdomain_norange(),extract); } PUBLIC PRIMARY_REV::PRIMARY_REV() { isrev = 1; zonetype=ZONE_PRIMARY; } /* Update the domain name from the visual one. */ PUBLIC void PRIMARY_REV::setfromv() { IP_ADDR ipa; ipa.setfrom (domainv.get()); char buf[30]; ipa.setrev (buf); if (range.is_filled()){ domain.setfromf ("%s.%s",range.get(),buf); }else{ domain.setfrom (buf); } } PUBLIC VIRTUAL bool PRIMARY::matchrange (const char *) { return true; } #if 0 static bool primary_evalsubnet ( const SSTRING &subnet, unsigned &net, unsigned &width, unsigned long &mask) { bool ret = true; net = 0; width = 24; mask = 0xffffff00l; if (subnet.is_filled()){ ret = false; const char *s = subnet.get(); net = atoi(s); const char *slash = str_skipdig(s); if (slash > s && *slash=='/'){ slash++; width = atoi(slash); const char *end = str_skipdig(slash); if (end > slash && *end == '\0' && width > 24 && width < 32){ mask = 0xffffffff << (32-width); if ((net & mask) == net){ ret = true; } } } } return ret; } #endif static bool primary_evalrange ( const SSTRING &range, unsigned &start, unsigned &stop) { bool ret = true; start = 0; stop = 255; if (range.is_filled()){ ret = false; const char *s = range.get(); start = atoi(s); const char *dash = str_skipdig(s); if (dash > s && *dash=='-'){ dash++; stop = atoi(dash); const char *endpt = str_skipdig(dash); if (endpt > dash && *endpt == '\0' && stop >= start && start >= 0 && stop <= 255){ ret = true; } } } return ret; } /* Return true if range is a valid range (x-y) */ static bool primary_validrange (SSTRING &range) { bool ret = true; if (range.is_filled()){ unsigned start,end; ret = primary_evalrange (range,start,end); } return ret; } /* Return true if this fq is a member of the range */ PUBLIC bool PRIMARY_REV::matchrange (const char *host) { bool ret = true; if (range.is_filled()){ unsigned start,end; primary_evalrange (range,start,end); unsigned num = atoi(host); ret = num >= start && num <= end; } return ret; } PUBLIC VIRTUAL const char *ZONE::getdomain_norange() { return domain.get(); } /* Return the domain without the range part (for RFC2317? reverse zone). */ PUBLIC const char *PRIMARY_REV::getdomain_norange() { /* #Specification: reverse zone / classless delegation / principle Linuxconf supports reverse mapping to special zone handling only a subset of an IP network. Not a subnet, a subset. A special reverse zone may handle a specific range of a network. To make things simple in the implementation, those special zone still behave like full network zone and use the same origin except that a special field is used to hold the range covered by the zone. See PRIMARY_REV::matchrange(). */ const char *ret = domain.get(); const char *pt = str_skipdig(ret); if (pt > ret && *pt == '-'){ pt++; pt = str_skipdig(pt); if (*pt == '.') ret = pt+1; } return ret; } PUBLIC VIRTUAL const char *PRIMARY::getrange() const { return ""; } PUBLIC const char *PRIMARY_REV::getrange() const { return range.get(); } /* Return != if any component of the PRIMARY was modified */ PUBLIC int ZONE::was_modified() { int ret = ARRAY_OBJ::was_modified(); if ((!ret) && (zonetype!=ZONE_SECONDARY)) { ret = origins.was_modified(); } return ret; } /* Return != 0 if the PRIMARY describe the reverse mapping of an IP network (x.y,z.in-addr.arpa) */ PUBLIC VIRTUAL bool ZONE::is_reverse() { return false; } /* Return != 0 if the PRIMARY describe the reverse mapping of an IP network (x.y,z.in-addr.arpa) */ PUBLIC bool PRIMARY_REV::is_reverse() { return true; } /* Locate all IP number in use in a domain. Return the number of IPs added to adrs */ PUBLIC int ZONE::getalladr(IP_ADDRS &adrs) { int ret = 0; for (int i=0; igetalladr(adrs); } return ret; } /* Find the first record of a certain type in the PRIMARY Returne NULL if not found. */ PRIVATE RECORD *ZONE::getfirst(RECORD_TYPE rtype) const { RECORD *ret = NULL; for (int i=0; ret == NULL && itbrec.getnb(); o++){ RECORD *rec = ori->tbrec.getitem(o); if (rec->is(rtype)){ ret = rec; break; } } } return ret; } /* Find the (first) soa record of a primary Return NULL if it can be found. */ PUBLIC RECORD_IN_SOA *ZONE::getsoa() const { return (RECORD_IN_SOA*)getfirst(RTYPE_SOA); } /* Find the (first) $ttl record of a primary Return NULL if it can be found. */ PUBLIC RECORD_TTL *ZONE::getdefaultttl() { return (RECORD_TTL*)getfirst(RTYPE_TTL); } /* Find all the NS records for a name. Return the number of record found. */ PUBLIC int ZONE::getns( SSTRING &dom, RECORDS &recs) { FQHOST fq (dom); return locate_left (fq,RTYPE_NS,recs); } /* Find all the NS records for a name. Return the number of record found. */ PUBLIC int ZONE::getns( SSTRING &dom, SSTRINGS &strs, SSTRINGS &ttls) { RECORDS recs; int nb = getns (dom,recs); for (int i=0; ins)); ttls.add (new TIMESTR (ns->getttl())); } return nb; } /* Find all the MX records for name. Return the number of record found. */ PUBLIC int ZONE::getmx( SSTRING &dom, RECORDS &recs) { FQHOST fq (dom); return locate_left (fq,RTYPE_MX,recs); } /* Find all the MX records for a name. Return the number of record found. */ PUBLIC int ZONE::getmx( SSTRING &dom, SSTRINGS &strs, SSTRINGS &ttls) { RECORDS recs; int nb = getmx (dom,recs); int tbpri[nb]; memset (tbpri,-1,sizeof(tbpri)); for (int i=0; iprefer){ strs.getitem(m)->appendf (" %s",mx->servname.get()); found = true; } } tbpri[i] = mx->prefer; if (!found){ strs.add (new SSTRING (mx->servname)); ttls.add (new TIMESTR (mx->getttl())); } } return nb; } /* Find all the A records for a name. Return the number of record found. */ PUBLIC int ZONE::geta( SSTRING &dom, RECORDS &recs) { FQHOST fq (dom); return locate_left (fq,RTYPE_A,recs); } /* Find all the A records for a name. Return the number of record found. */ PUBLIC int ZONE::geta( SSTRING &dom, IP_ADDRS &adrs, SSTRINGS &ttls) { RECORDS recs; int nb = geta (dom,recs); for (int i=0; iaddr)); ttls.add (new TIMESTR (a->getttl())); } return nb; } /* Find the CNAME record for a name. Return -1 if not found. cname will be empty. */ PUBLIC int ZONE::getcname( SSTRING &dom, SSTRING &cname) { FQHOST fq (dom); RECORDS recs; int nb = locate_left (fq,RTYPE_CNAME,recs); cname.setfrom (""); int ret = -1; if (nb > 0){ RECORD_IN_CNAME *a = (RECORD_IN_CNAME*)recs.getitem(0); cname.setfrom (a->name); ret = 0; } return ret; } /* Increment if needed the serial number of the SOA This function may be called several time. The serial number will be incremented only once per session though. */ PUBLIC void ZONE::updatesoa() { if (origins.was_modified()){ RECORD_IN_SOA *soa = getsoa(); if (soa != NULL) soa->update(domain.get()); } } /* Format the serial number (revision number) of the zone (selection menu) Put an empty string if the zone has no revision so far */ PUBLIC void ZONE::format_revision (char *buf) { RECORD_IN_SOA *soa = getsoa(); buf[0] = '\0'; if (soa != NULL){ long serial = soa->new_serial; if (serial > 1997000000){ sprintf (buf,"%04ld/%02ld/%02ld\t%ld" ,serial/1000000 ,(serial % 1000000)/10000 ,(serial % 10000)/ 100 ,serial % 100); }else{ sprintf (buf,"-\t%ld",serial); } } } PROTECTED void ZONE::writeaccess(FILE_CFG *fout) const { if (notify!=NOTIFY_DEFAULT) { fprintf (fout,"\t%s %s;\n",K_NOTIFY,notify ? "yes" : "no"); } dns_writeopts (allowtrans,K_ALLOW_TRANSFER,fout); dns_writeopts (allowquery,K_ALLOW_QUERY,fout); dns_writeopts (allowrecursion,K_ALLOW_RECURSION,fout); dns_writeopts (allowupdate,K_ALLOW_UPDATE,fout); dns_writeopts (alsonotify,K_ALSO_NOTIFY,fout); } const char *dnsconf_datatypes[] = {"","in","chaos","hs", "hesiod"}; /* Write the records of the domain and the entry in named.boot Return -1 if any error. */ PUBLIC int PRIMARY::write (bool bind8, FILE_CFG *fout, const char *named_dir, const DNS * dns) const { SSTRING file2; RECORD_IN_SOA *soa = getsoa(); int ret = 0; if (soa == NULL){ xconf_error ("SOA records for zone %s is missing\n" "This is not possible. There must be a bug\n" "somewhere. Please send me (jack@solucorp.qc.ca)\n" "your /etc/named.conf and /var/named directory\n" "so I can replicate the problem.\n" "Tell me how you did it.\n" "The zone won't be updated",domainv.get()); }else if (soa->was_modified()){ ret= origins.save (named_dir,file.get()); if (ret == 0) soa->rstmodified(); } if (bind8){ fprintf (fout,"%s \"%s\" %s{\n",K_ZONE,domain.get() ,dnsconf_datatypes[datatype]); fprintf (fout,"\t%s %s;\n",K_TYPE,K_MASTER); fprintf (fout,"\t%s \"%s\";\n",K_FILE,file.get()); writeaccess(fout); fputs ("};\n",fout); }else{ if (zonetype==ZONE_PRIMARY) { fprintf (fout,"primary\t%s\t%s\n",domain.get(),file.get()); } else { fprintf (fout,"secondary\t%s",domain.get()); for (int i=0; iget()); } fprintf (fout,"\t%s\n",file2.get()); } } return ret; } /* Add a record in the PRIMARY. The record is recorded relative to the main origin of the primary. */ PUBLIC void ZONE::addrec (RECORD *rec) { /* do something for secondary here */ if (origins.getnb()==0){ ORIGIN *ori = new ORIGIN(domain.get()); origins.add (ori); } origins.getitem(0)->tbrec.add (rec); } /* Insert a record at the top of the first origin. */ PUBLIC void ZONE::insrec (RECORD *rec) { if (origins.getnb()==0){ ORIGIN *ori = new ORIGIN(domain.get()); origins.add (ori); } origins.getitem(0)->tbrec.insert (0,rec); } static char key_allowtrans[10]; static char key_allowquery[10]; static char key_allowrecursion[10]; static char key_allowupdate[10]; static char key_alsonotify[10]; PROTECTED void ZONE::setupzoneaccess (DIALOG &dia, int level) { dia.newf_title (MSG_U(T_ACCESS,"Access control"),level,"",MSG_R(T_ACCESS)); int level2 = level + 1; feature_editlist (dia,level2,MSG_R(F_ALLOWTRANS),allowtrans,key_allowtrans); feature_editlist (dia,level2,MSG_R(F_ALLOWQUERY),allowquery,key_allowquery); feature_editlist (dia,level2,MSG_R(F_ALLOWRECURSION),allowrecursion,key_allowrecursion); feature_editlist (dia,level2,MSG_U(F_ALLOWUPDATE,"Allow update from"),allowupdate,key_allowupdate); { static const char *tbmode[]={ MSG_U(I_NO,"No"), MSG_U(I_YES,"Yes"), MSG_U(I_DEFAULT,"Default"), NULL }; dia.newf_chkm (MSG_R(F_NOTIFY),notify,tbmode); } feature_editlist (dia,level2,MSG_U(F_ALSONOTIFY,"Also notify"),alsonotify,key_alsonotify); } static PRIMARY *primary_gettemplatedom (DNS &dns) { PRIMARY *ret = NULL; const char *dom = features_gettemplatedom(); if (dom != NULL){ ret = dns.locate_domain (dom); } return ret; } /* Make sure there is at least one blank SSTRING at the end of the table and there is also a minimum SSTRING in the table. */ void primary_addblank(SSTRINGS &tb, SSTRINGS &ttls, int minimum) { while (tb.getnb() < minimum){ tb.add (new SSTRING); ttls.add (new TIMESTR); } if (tb.getitem(minimum-1)->is_filled()){ tb.add (new SSTRING); ttls.add (new TIMESTR); } } static struct{ char ip[10]; char ip_ttl[10]; char ns[10]; char mx[10]; } regkeys; PUBLIC void ZONE::setupdia ( DIALOG &dia, DNS &dns, int level, // Level for notebook pads PRIMARY_EDITINFO &info) { THISHOST thost; info.soa = getsoa(); info.ttl = getdefaultttl(); SSTRING tmpdom (getdomain_norange()); if (getns(tmpdom,info.rns,info.rnsttls) == 0){ PRIMARY *pri = primary_gettemplatedom(dns); if (pri != NULL) pri->getns (pri->domain,info.rns,info.rnsttls); if (info.rns.getnb()==0){ info.rns.add (new SSTRING (thost.getname1())); info.rnsttls.add (new TIMESTR); } } /* #Specification: dnsconf / main ns records of the domain dnsconf allows the specification of up to 3 NS record for a primary. If there is 3 or more, it adds a blank line. */ primary_addblank (info.rns,info.rnsttls,3); if (!isrev){ if (getmx(domain,info.rmx,info.rmxttls) == 0){ PRIMARY *pri = primary_gettemplatedom(dns); if (pri != NULL) pri->getmx (pri->domain,info.rmx,info.rmxttls); if (info.rmx.getnb()==0){ info.rmx.add (new SSTRING (thost.getname1())); info.rmxttls.add (new TIMESTR); } } /* #Specification: dnsconf / main mx records of the domain dnsconf allows the specification of up to 3 MX records for a primary. If there is 3 or more, it adds a blank line. */ primary_addblank (info.rmx,info.rmxttls,3); } if (info.soa == NULL) info.soa = new RECORD_IN_SOA; if (info.ttl == NULL && dns.bind8) info.ttl = new RECORD_TTL; dia.newf_str (MSG_U(F_MAINSERV,"Main server"),info.soa->machine); dia.newf_str (MSG_U(F_ADMINMAIL,"Administrator email"),info.soa->admin); dia.newf_title (MSG_R(F_DNSADV),level,"",MSG_R(F_DNSADV)); info.start_ns = dia.getnb(); int i; for (i=0; iset_registry_key (®keys.ns[i]); } info.start_mx = dia.getnb(); if (!isrev){ dia.newf_title (MSG_R(F_EMAILADV),level,"",MSG_R(F_EMAILADV)); for (i=0; iset_registry_key (®keys.mx[i]); } } if (!isrev) { geta(domainv,info.tba,info.tbattls); while (info.tba.getnb()<4){ info.tba.add (new IP_ADDR); info.tbattls.add (new TIMESTR); } int start_ip; dns.setip_in_dia (dia,level,info.tba,info.tbattls ,MSG_U(T_DEFAULTIP,"Default IPs"),start_ip); } dia.newf_title (MSG_U(T_FEATURES,"Features"),level,"",MSG_R(T_FEATURES)); dia.newf_title ("",MSG_U(F_SECREQ,"Secondaries requirements")); dia.newf_str (MSG_U(F_REFRESH,"Refresh"),info.soa->refresh); dia.last_noempty(); dia.newf_str (MSG_U(F_RETRY,"Retry"),info.soa->retry); dia.last_noempty(); dia.newf_str (MSG_U(F_EXPIRE,"Expire"),info.soa->expire); dia.last_noempty(); dia.newf_title ("",MSG_U(F_EVERYHOSTS,"Every hosts requirements")); if (info.ttl != NULL){ dia.newf_str (MSG_U(F_TTL,"Time to live (ttl)"),info.ttl->ttl); dia.newf_str (MSG_U(F_MINTTL,"Minimum ttl"),info.soa->default_ttl); }else{ dia.newf_str (MSG_R(F_TTL),info.soa->default_ttl); } dia.last_noempty(); if (dns.bind8 && zonetype == ZONE_PRIMARY){ setupzoneaccess(dia,level); } dia.newf_title (MSG_U(T_EXTRAINFO,"Extra info"),level,"",MSG_R(T_EXTRAINFO)); dia.newf_info ("",MSG_U(I_ALLISOPT,"Optional information")); dia.newf_str (MSG_U(F_ADMIN,"Administrator"),info.admin); dia.newf_str (MSG_U(F_COMMENT,"Comment"),info.comment); dia.newf_str (MSG_U(F_RENEWDATE,"Renewal date"),info.renewdate); dia.newf_str (MSG_U(F_REGISTRAR,"Registrar"),info.registrar); } static const char K_REGISTRAR[]="registrar"; static const char K_ADMIN[]="admin"; static const char K_COMMENT[]="comment"; static const char K_RENEWDATE[]="renewdate"; PROTECTED void ZONE::load_extrainfo( PRIMARY_EDITINFO &info) { if (!domainv.is_empty()){ SSTRING key; key.setfromf ("%s.%s",K_DNSCONF,domainv.get()); const char *ptk = key.get(); info.registrar.setfrom (linuxconf_getval(ptk,K_REGISTRAR)); info.comment.setfrom (linuxconf_getval(ptk,K_COMMENT)); info.renewdate.setfrom (linuxconf_getval(ptk,K_RENEWDATE)); info.admin.setfrom (linuxconf_getval(ptk,K_ADMIN)); } } /* Replace a value in /etc/conf.linuxconf or remove it if the new value is empty. */ static void primary_repdel ( const char *key1, const char *key2, const SSTRING &val) { if (val.is_empty()){ linuxconf_removeall (key1,key2); }else{ linuxconf_replace (key1,key2,val); } } PROTECTED void ZONE::save_extrainfo( PRIMARY_EDITINFO &info) { if (!domainv.is_empty()){ linuxconf_setcursys(subsys_dnsserv); SSTRING key; key.setfromf ("%s.%s",K_DNSCONF,domainv.get()); const char *ptk = key.get(); primary_repdel (ptk,K_REGISTRAR,info.registrar); primary_repdel (ptk,K_COMMENT,info.comment); primary_repdel (ptk,K_RENEWDATE,info.renewdate); primary_repdel (ptk,K_ADMIN,info.admin); linuxconf_save(); } } PROTECTED void ZONE::del_extrainfo() { PRIMARY_EDITINFO info; save_extrainfo(info); // Since all the info is empty, it will be wiped // from conf.linuxconf } /* Edit the basic specs of a domain. Return -1 if the user abort edition Return 0 if the user accepted the changes Return 1 if the user wish to delete this domain. */ PUBLIC int ZONE::editshow ( DNS &dns, bool readonly, SSTRING *range, const char *dialog_id) { DIALOG dia; dia.set_registry_id (dialog_id); dia.newf_str (isrev ? MSG_U(F_NETNUM,"Network number") : MSG_U(F_DOMAIN,"Domain"),domainv); if (range != NULL){ dia.newf_str (MSG_U(F_SUBNETSPEC,"Subnet range x-y"),*range); } PRIMARY_EDITINFO info; load_extrainfo (info); setupdia (dia,dns,1,info); int ret = -1; int nof = 0; if (readonly) dia.set_readonly(); while (1){ MENU_STATUS status = dia.edit ( (zonetype==ZONE_PRIMARY) ? MSG_U(T_PRIMSPEC,"Primary specification") : MSG_U(T_SECSPEC,"Secondary specification") ,(zonetype==ZONE_PRIMARY) ? MSG_U(I_PRIMSPEC ,"You must enter a domain name\n") : "" ,(zonetype==ZONE_PRIMARY) ? help_primary : help_secondary ,nof ,readonly ? MENUBUT_CANCEL : MENUBUT_CANCEL|MENUBUT_ACCEPT|MENUBUT_DEL); if (status == MENU_CANCEL || status == MENU_ESCAPE){ break; }else if (status == MENU_DEL){ if (xconf_areyousure(MSG_U(Q_DELPRIMARY ,"Confirm deletion of a domain"))){ ret = 1; del_extrainfo(); break; } }else{ if (domainv.is_empty()){ xconf_error(MSG_U(E_NODOMAIN,"Fill at least the domain\n" "and the first IP address")); }else if (range != NULL && !primary_validrange (*range)){ xconf_error (MSG_U(E_IVLDSUBNET ,"Invalid range specification\n" "Expecting START-END")); nof = 2; }else{ bool err = false; dns_lexcheck (domainv,nof,0,err,false); dns_lexcheck (info.rns,nof,info.start_ns,err,false); if (!isrev) dns_lexcheck (info.rmx,nof,info.start_mx,err,true); if (!err){ if (isrev && primary_is_arpadom(domainv.get())){ primary_reverse_arpa (domainv.get(),domainv,*range); } setfromv(); { // Fix the origin created nameless by initfrom() ORIGIN *first = origins.getitem(0); if (first != NULL && first->origin.is_empty()){ first->origin.setfrom(getdomain_norange()); } } const char *domname = domain.get(); PRIMARY *dom = dns.finddomain(domname); SECONDARY *sec = dns.findsecond(domname); if (zonetype==ZONE_PRIMARY && dom != NULL && dom != this){ xconf_error (MSG_U(E_DOMEXIST ,"Domain already exist in this DNS")); } else if (zonetype==ZONE_SECONDARY && sec != NULL && sec != this){ xconf_error (MSG_R(E_DOMEXIST)); }else if ((sec != NULL) && zonetype==ZONE_PRIMARY) { xconf_error (MSG_U(E_ISASECOND ,"This DNS is already a secondary for this domain.\n" "It can't be both at the same time")); }else{ if (getsoa()==NULL) addrec (info.soa); if (info.ttl != NULL && getdefaultttl()==NULL){ insrec (info.ttl); } /* #Specification: dnsconf / primary / record file dnsconf use the domain name as the file name which will contain the record. You can change the name in /etc/named.boot and dnsconf will use this one instead. */ if (file.is_empty()){ if (range != NULL && range->is_filled()){ unsigned start,end; primary_evalrange (*range,start,end); file.setfromf ("%s.%d-%d",domainv.get(),start,end); }else{ file.setfrom (domainv); } } dns.setmx (domain,info.rmx,info.rmxttls); // This will register the NS record both // in this zone, but also in a parent zone if // there is one, creating the glue records // This is why we call the DNS::setns // which have to locate the domain // instead of calling the ZONE::set function. dns.setns (domain,info.rns,info.rnsttls); if (!isrev){ const char *tbip[info.tba.getnb()]; int nbip = dnsrecs_tbip(info.tba,tbip); dns.set (domainv.get(),tbip,nbip,info.tbattls); } dns_cnv2abs (info.soa->machine); dns_cnv2abs (info.soa->admin); save_extrainfo (info); setmodified(); ret = 0; break; } } } } } if (!readonly){ if (ret != 0) dia.restore(); allowtrans.remove_empty(); allowquery.remove_empty(); allowrecursion.remove_empty(); allowupdate.remove_empty(); alsonotify.remove_empty(); } return ret; } static char ID_PRIMARY[]="primary"; static char ID_ZONE[]="zone"; static char ID_PRIMARY_REV[]="primary_rev"; /* Present the setting of a zone in read-only mode */ PUBLIC void ZONE::show(DNS &dns) { editshow (dns,true,NULL,ID_ZONE); } PUBLIC VIRTUAL int PRIMARY::edit(DNS &dns) { return editshow(dns,false,NULL,ID_PRIMARY); } PUBLIC int PRIMARY_REV::edit(DNS &dns) { return editshow(dns,false,&range,ID_PRIMARY_REV); } PUBLIC PRIMARY *PRIMARYS::getitem (int no) const { return (PRIMARY*)ARRAY::getitem(no); } PUBLIC PRIMARY *PRIMARYS::getitem (const char *name) const { PRIMARY *ret = NULL; int n = getnb(); for (int i=0; idomain.cmp(name)==0){ ret = p; break; } } return ret; } /* Locate all IP number in use in all domain of this DNS. Return the number added to adrs */ PUBLIC int PRIMARYS::getalladr(IP_ADDRS &adrs) { int ret = 0; for (int i=0; igetalladr(adrs); } return ret; } /* Get a PRIMARY for a fully qualified host. Get the primary that is the closest to the hostname (ie: x.y.z.com will select y.z.com instead of z.com if both domain are defined in this DNS). Return NULL if not found. */ PUBLIC PRIMARY *PRIMARYS::getitem( FQHOST &fq, char *hostpart, bool dontitself) // if dontitself is true // && fq is itself a domain // don't select it { /* #Specification: dnsconf / matching a primary / closest When trying to dispatch information about a host in the DNS, dnsconf try to find the primary which has the closest match. This means that if a DNS is a primary for x.y.com and y.com, and dnsconf dispatch info about the host host.x.y.com, it will select the domain x.y.com. On the other end, host.w.y.com will be dispatch in the domain y.com. */ PRIMARY *ret = NULL; int minlevel = 100; if (hostpart != NULL) hostpart[0] = '\0'; for (int i=0; idomain.get(),tmp); if (level == 0){ level = fq.is_member(pri->getdomain_norange(),tmp); } if (level > 0 && level < minlevel && (strcmp(tmp,"@")==0 || pri->matchrange(tmp))){ if (!dontitself || strcmp(tmp,"@")!=0){ ret = pri; // fprintf (stderr,"ret = %s\n",pri->domain.get()); minlevel = level; if (hostpart != NULL) strcpy (hostpart,tmp); } } } return ret; } /* Get a PRIMARY for a fully qualified host. Get the primary that is the closest to the hostname (ie: x.y.z.com will select y.z.com instead of z.com if both domain are defined in this DNS). Return NULL if not found. */ PUBLIC PRIMARY *PRIMARYS::getitem(FQHOST &fq, char *hostpart) { return getitem (fq,hostpart,false); } PUBLIC int PRIMARYS::write (bool bind8, FILE_CFG *fout, const char *named_dir, const DNS* dns) const { int ret = 0; for (int i=0; iwrite(bind8,fout,named_dir,dns) == -1) ret = -1; } return ret; } PUBLIC VIRTUAL PRIMARY *PRIMARYS::new_PRIMARY() { return new PRIMARY; } PUBLIC VIRTUAL bool PRIMARYS::is_reverse() { return false; } PUBLIC bool PRIMARYS_REV::is_reverse() { return true; } PUBLIC PRIMARY *PRIMARYS_REV::new_PRIMARY() { return new PRIMARY_REV; } /* Present the list of primarys. Show selectivly the standard domain primaris or the reverse mapping primarys. */ PUBLIC void PRIMARYS::edit(DNS &dns) { int choice=0; DIALOG_LISTE dia; dia.addwhat (MSG_U(I_ADDPRIM,"Select [Add] to define a new primary")); while (1){ PRIMARYS sorted; setselect (dia,sorted); MENU_STATUS code = dia.editmenu( MSG_U(T_PRIMARYS,"Primary zones") ,MSG_U(I_PRIMARYS ,"You are allowed to edit/add/remove zones\n") ,help_primary ,choice,MENUBUT_ADD); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else if (code == MENU_ADD){ PRIMARY *pri = new_PRIMARY(); initfrom (dns,pri,features_gettemplatedom()); add (pri); if (pri->edit(dns) != 0){ remove_del (pri); }else{ if (pri->is_reverse()){ /* #Specification: dnsconf / new primary / is reverse When adding a pseudo domain for reverse mapping we walk the DNS to stuff it with currently defined IP numbers. */ dns.preload_rev(pri); } /* #Specification: dnsconf / new primary / host info Whenever we add a new primary, we try to update it with the basic host information of this host so the DNS will be automaticly current. */ dnsconf_updatedns(true); dns.write(); } }else if (choice >= 0 && choice < nb){ PRIMARY *pri = sorted.getitem(choice); int ok = pri->edit(dns); if (ok >= 0){ if (ok == 1) remove_del(pri); dns.write(); } } } } /* Initialise a domain p from an example domain */ PUBLIC int PRIMARYS::initfrom ( DNS &dns, PRIMARY *p, const char *example) { int ret = 0; if (example != NULL){ PRIMARY *templ = dns.locate_domain(example); if (templ == NULL){ ret = -1; xconf_error (MSG_U(E_EXMISSING ,"Template domain %s does not exist") ,example); }else{ RECORD_IN_SOA *tsoa = templ->getsoa(); RECORD_IN_SOA *soa = p->getsoa(); if (soa == NULL){ soa = new RECORD_IN_SOA; p->addrec (soa); } if (tsoa != NULL){ soa->domain.setfrom (tsoa->domain); soa->machine.setfrom (tsoa->machine); soa->admin.setfrom (tsoa->admin); soa->refresh.setfrom (tsoa->refresh); soa->retry.setfrom (tsoa->retry); soa->expire.setfrom (tsoa->expire); soa->default_ttl.setfrom (tsoa->default_ttl); } RECORD_TTL *templ_ttl = templ->getdefaultttl(); RECORD_TTL *ttl = p->getdefaultttl(); if (ttl == NULL && dns.bind8){ ttl = new RECORD_TTL; p->insrec (ttl); } if (ttl != NULL && templ_ttl != NULL){ ttl->ttl.setfrom (templ_ttl->ttl); } FQHOST fq (p->domain.get()); if (!p->isrev){ SSTRINGS rmx,rmxttls; templ->getmx(templ->domain,rmx,rmxttls); for (int i=0; isetonemx (fq,rmx.getitem(i)->get(),i*5+5 ,rmxttls.getitem(i)->get()); } } SSTRINGS rns,rnsttls; templ->getns(templ->domain,rns,rnsttls); for (int i=0; iset (fq ,new RECORD_IN_NS("dummy" ,rns.getitem(i)->get())); } // Copy bind8 information p->allowquery.append (templ->allowquery); p->allowrecursion.append (templ->allowrecursion); p->allowtrans.append (templ->allowtrans); p->allowupdate.append (templ->allowupdate); p->alsonotify.append (templ->alsonotify); p->notify = templ->notify; p->option_forward_only = templ->option_forward_only; } } return ret; } /* Add a new domain and use a template domain to setup few field Return -1 if any errors. */ PUBLIC int PRIMARYS::adddomain ( const char *domain, const char *example, DNS &dns) { int ret = -1; if (getitem(domain)!=NULL){ // domain exist xconf_error (MSG_R(E_DOMEXIST)); }else{ PRIMARY *p = new PRIMARY; p->domainv.setfrom (domain); p->setfromv(); p->file.setfrom (domain); RECORD_IN_SOA *soa = new RECORD_IN_SOA; p->addrec (soa); if (dns.bind8){ RECORD_TTL *ttl = new RECORD_TTL; p->insrec (ttl); } add (p); if (example == NULL) example = features_gettemplatedom(); ret = initfrom (dns,p,example); if (ret != 0){ remove_del (p); } } return ret; } PUBLIC int DNS::newdomain( const char *domain, const char *example) { return primarys.adddomain (domain,example,*this); } PUBLIC int DNS::deldomain (const char *domain) { int ret = -1; PRIMARY *pri = locate_domain (domain); if (pri != NULL){ primarys.remove_del(pri); ret = 0; } return ret; } static void primary_listdomains(SSTRINGS &tb) { DNS *dns = dns_load(); dns->listdomains(tb); dns_unload(); } static void primary_editdomain(const char *key, bool force) { DNS *dns = dns_load(); PRIMARY *pri = dns->locate_domain (key); if (pri == NULL){ if (dns->newdomain(key,NULL)!=-1){ pri = dns->locate_domain (key); if (pri->edit(*dns)!=0){ dns->deldomain(key); }else{ dns->write(); } } }else{ if (pri->edit(*dns)==0){ dns->write(); } } dns_unload(); } #include static PUBLISH_VARIABLES_MSG primary_list1[]={ {"domain",P_MSG_R(F_DOMAIN)}, {"subnet",P_MSG_R(F_SUBNETSPEC)}, {"mainserv",P_MSG_R(F_MAINSERV)}, {"adminemail",P_MSG_R(F_ADMINMAIL)}, {"refresh",P_MSG_R(F_REFRESH)}, {"retry",P_MSG_R(F_RETRY)}, {"expire",P_MSG_R(F_EXPIRE)}, {"defaultttl",P_MSG_R(F_TTL)}, {"minimumttl",P_MSG_R(F_MINTTL)}, {"administrator",P_MSG_R(F_ADMIN)}, {"comment",P_MSG_R(F_COMMENT)}, {"renewdate",P_MSG_R(F_RENEWDATE)}, {"registrar",P_MSG_R(F_REGISTRAR)}, {"notify",P_MSG_R(F_NOTIFY)}, { NULL, NULL } }; static REGISTER_VARIABLES primary_registry1("primary","domains",primary_list1 ,ID_PRIMARY,primary_editdomain,primary_listdomains); extern char dnsrecs_ip[],dnsrecs_ip_ttl[]; static PUBLISH_VARIABLES_STR primary_list2[]={ {"ns1",®keys.ns[0]}, {"ns2",®keys.ns[1]}, {"ns3",®keys.ns[2]}, {"ns4",®keys.ns[3]}, {"mx1",®keys.mx[0]}, {"mx2",®keys.mx[1]}, {"mx3",®keys.mx[2]}, {"mx4",®keys.mx[3]}, {"ip1",&dnsrecs_ip[0]}, {"ip2",&dnsrecs_ip[1]}, {"ip3",&dnsrecs_ip[2]}, {"ip4",&dnsrecs_ip[3]}, {"ipttl1",&dnsrecs_ip_ttl[0]}, {"ipttl2",&dnsrecs_ip_ttl[1]}, {"ipttl3",&dnsrecs_ip_ttl[2]}, {"ipttl4",&dnsrecs_ip_ttl[3]}, {NULL,NULL} }; static REGISTER_VARIABLES primary_registry2("primary","domains",primary_list2 ,ID_PRIMARY,primary_editdomain,primary_listdomains); PUBLISH_VARIABLES_STR zone_variables[]={ {"allowtrans1",&key_allowtrans[0]}, {"allowtrans2",&key_allowtrans[1]}, {"allowtrans3",&key_allowtrans[2]}, {"allowtrans4",&key_allowtrans[3]}, {"allowtrans5",&key_allowtrans[4]}, {"allowupdate1",&key_allowupdate[0]}, {"allowupdate2",&key_allowupdate[1]}, {"allowupdate3",&key_allowupdate[2]}, {"allowupdate4",&key_allowupdate[3]}, {"allowupdate5",&key_allowupdate[4]}, {"allowquery1",&key_allowquery[0]}, {"allowquery2",&key_allowquery[1]}, {"allowquery3",&key_allowquery[2]}, {"allowquery4",&key_allowquery[3]}, {"allowquery5",&key_allowquery[4]}, {"allowrecursion1",&key_allowrecursion[0]}, {"allowrecursion2",&key_allowrecursion[1]}, {"allowrecursion3",&key_allowrecursion[2]}, {"allowrecursion4",&key_allowrecursion[3]}, {"allowrecursion5",&key_allowrecursion[4]}, {"alsonotify1",&key_alsonotify[0]}, {"alsonotify2",&key_alsonotify[1]}, {"alsonotify3",&key_alsonotify[2]}, {"alsonotify4",&key_alsonotify[3]}, {"alsonotify5",&key_alsonotify[4]}, {NULL,NULL} }; static REGISTER_VARIABLES primary_registry3("primary","domains",zone_variables ,ID_PRIMARY,primary_editdomain,primary_listdomains);