#include #include #include #include #include "dnsconf.h" #include "internal.h" #include "../paths.h" #include "dnsconf.m" #include #include #include DNSCONF_HELP_FILE help_dnsedit ("edit"); static DNSCONF_HELP_FILE help_rename("rename"); static DNSCONF_HELP_FILE help_reverse("reverse"); static DNSCONF_HELP_FILE help_conflict("conflict"); /* Extract the non empty IP number from tba and place them in tbip. Return the number extracted */ int dnsrecs_tbip ( IP_ADDRS &tba, const char *tbip[]) { int nbip = 0; for (int i=0; iis_empty()){ tbip[nbip++] = a->get(); } } return nbip; } /* Update all record associated with a name Return -1 if full was not part of any reverse domain. */ PRIVATE int DNS::update_one( SSTRING &full, IP_ADDRS &tba, SSTRINGS &tbattls, SSTRINGS &tbns, SSTRINGS &tbnsttls, SSTRINGS &tbmx, SSTRINGS &tbmxttls, SSTRING &cname, char doreverse[4]) { const char *tbip[tba.getnb()]; int nbip = dnsrecs_tbip(tba,tbip); int matchrev; set (full.get(),tbip,nbip,tbattls,doreverse,matchrev); setns (full,tbns,tbnsttls); setmx (full,tbmx,tbmxttls); setcname (full,cname); return matchrev != nbip ? -1 : 0; } PRIVATE int DNS::update_one( SSTRING &full, IP_ADDRS &tba, SSTRINGS &tbattls, SSTRINGS &tbns, SSTRINGS &tbnsttls, SSTRINGS &tbmx, SSTRINGS &tbmxttls, SSTRING &cname) { char doreverse[4]; memset (doreverse,1,sizeof(doreverse)); return update_one (full,tba,tbattls,tbns,tbnsttls,tbmx,tbmxttls,cname,doreverse); } /* Collect all IP address in use in this DNS */ PUBLIC void DNS::getalladr (IP_ADDRS &adrs) { primarys.getalladr(adrs); primarys_rev.getalladr(adrs); } char dnsrecs_ip[10]; char dnsrecs_ip_ttl[10]; static struct{ char ns[10]; char ns_ttl[10]; char mx[10]; char mx_ttl[10]; } regkeys; PUBLIC void DNS::setip_in_dia( DIALOG &dia, int level, IP_ADDRS &tba, SSTRINGS &ttls, const char *title, int &first_field) { IPMAPS maps; IP_ADDRS adrs; getalladr (adrs); maps.setuse (adrs); dia.auto_newline(false); dia.newf_title (title,level,"",title); const char *t1 = MSG_U(F_IP,"IP number"); const char *t2 = MSG_R(F_TTL); if (dialog_mode == DIALOG_GUI){ // Put a heading dia.newf_info (NULL,t1); dia.newf_info (NULL,t2); dia.newline(); t1 = NULL; t2 = NULL; } first_field = dia.getnb(); for (int i=0; iset_registry_key (&dnsrecs_ip[i]); f = dia.newf_str (t2,*ttls.getitem(i),13); f->set_registry_key (&dnsrecs_ip_ttl[i]); dia.newline(); } dia.auto_newline(true); } PUBLIC void DNS::setip_in_dia( DIALOG &dia, IP_ADDRS &tba, SSTRINGS &ttls, int &first_field) { setip_in_dia (dia,1,tba,ttls,MSG_U(F_IPADRS,"IP addrs."),first_field); } /* Edit the reverse mapping of a host not part of any domain on this server */ PRIVATE void DNS::editone_rev(SSTRING &full) { IP_ADDRS tba; SSTRING absfull (full); dns_cnv2abs (absfull); SSTRINGS ttls; for (int i=0; ilocate_rev (absfull.get(),tba); } while (tba.getnb()<4){ tba.add (new IP_ADDR); ttls.add (new TIMESTR); } DIALOG dia; dia.newf_str (MSG_U(F_HOST,"host name"),full); int start_ip; setip_in_dia (dia,tba,ttls,start_ip); int nof = 0; while (1){ if (dia.edit(MSG_U(T_IPREVERSE,"Host address") ,MSG_U(I_IPREVERSE,"You can enter multiple IP numbers\n" "for one host. These IP numbers will be used to lookup\n" "a host name from its IP number. Another DNS should\n" "contain the official definition of this host.") ,help_reverse ,nof)==MENU_ACCEPT){ SSTRINGS tbempty; SSTRING empty; if (update_one (full,tba,ttls,tbempty,tbempty,tbempty,tbempty,empty)==-1){ xconf_error (MSG_U(E_NOMATCH ,"The IP numbers you have entered was not part\n" "of any reverse mapping pseudo-domain")); }else{ write(); break; } }else{ break; } } } /* Check if a name is lexically acceptable */ void dns_lexcheck ( SSTRING &s, int &nof, int new_nof, bool &err, bool allow_space) { if (!err && s.is_filled()){ const char *start = s.get(); const char *pt = start; if (*pt == '*') pt++; // Accept the *, needed for wild card MX char space = allow_space ? ' ' : '\0'; while (*pt != '\0'){ if (!isdigit(*pt) && !isalpha(*pt) && *pt != '-' && *pt != '.' && *pt != space){ xconf_error (MSG_U(E_LEXDNS ,"Invalid character in name\n" "\t%s\n" "only digits, letter, - and . are allowed\n" "May also start with a *") ,start); err = true; nof = new_nof; break; } pt++; } } } /* Check if a set of names are lexically acceptable */ void dns_lexcheck ( SSTRINGS &tb, int &nof, int new_nof, bool &err, bool allow_space) { for (int i=0; iremove_del (hosts->lookup(full.get())); // We also remove domains which may have a default IP // and this is normal for (int h=0; hgetnb(); h++){ const char *hh = hosts->getitem(h)->get(); if (locate_domain(hh)!=NULL){ // This is a domain hosts->remove_del (h); h--; } } if (hosts->getnb() > 0){ // Ok, we have a conflict, but we have to check if there // is a reverse zone handling this IP. If not, no need // to care. IP_ADDR ipa; ipa.setfrom(tbip[i]); char revdom[40]; ipa.setrev(revdom); FQHOST rfq (revdom); char left[100]; if (primarys_rev.getitem(rfq,left) != NULL){ doreverse[i] = 0; conflict = true; } } } if (conflict){ /* #Specification: DNS management / Two host sharing one IP /strategy Linuxconf detect if one is trying to use an IP already allocated on this DNS. This is done for each of the four possible IPs. When there is a conflict, linuxconf pops a dialog allowing one to select which one of the IP will be the "main" A record and will be used to generate the reverse mapping IP per IP, the user may select the proper behavior. In general, it is a bad practice to have multiple A record pointing to the same IP. There is an exception (managed by linuxconf) for the defaults IPs of a domain. */ DIALOG dia; for (int i=0; i0 ? "" : MSG_U(I_DOREV,"Do reverse mapping on this IP")); SSTRINGS *hosts = tbhosts + i; for (int h=0; hgetnb(); h++){ SSTRING *ho = hosts->getitem(h); dia.newf_str (h==0 ? MSG_U(F_ALLOCTO,"Allocated to") : "" ,*ho); dia.set_lastreadonly(); } } MENU_STATUS code = dia.edit (MSG_U(T_IPCONFLICT,"IP allocation conflict resolution") ,MSG_U(I_IPCONFLICT,"The IPs you have allocated are already\n" "in use in this DNS.\n" "You should pick either different IPs\n" " (linuxconf supply available ones)\n" "Or you must specify which IP will be the \"main\" IP\n" "and will receive the reverse mapping\n") ,help_conflict); if (code == MENU_ESCAPE || code == MENU_CANCEL){ ret = -1; } } } return ret; } /* Check if the current record conflict with the basic host definition Return -1 if the user do not wish to continue. */ static int check_basicconflict( SSTRING &full, IP_ADDRS &tba) { int ret = 0; HOSTINFO info; if (netconf_loadinfos(info)!=-1){ bool hostname_same = info.hostname.cmp(full)==0; const int nbdev = info.nbdev; const char *tbip[nbdev]; int nbip = 0; for (int i=0; iipaddr.is_empty()){ const char *ip = itf->ipaddr.get(); if ((itf->name.is_empty() && hostname_same) || itf->name.cmp(full)==0){ tbip[nbip++] = ip; } } } if (nbip > 0){ bool conflict = false; const char *tbaip[tba.getnb()]; int nba = dnsrecs_tbip (tba,tbaip); if (nbip != nba){ conflict = true; }else{ int found = 0; for (int i=0; iset_registry_key (reg_key+i); f = dia.newf_str (fttl,*(ttls.getitem(i)),13); f->set_registry_key (reg_ttl_key+i); dia.newline(); } } /* Edit a host or sub-domain information. -A and PTR record) globally. -NS record -CNAME -MX All in a single dialog */ PUBLIC void DNS::editone(const char *name) { FQHOST fq (name); SSTRING full; fq.formatfull(full); PRIMARY *pri = primarys.getitem(fq,NULL); if (pri == NULL){ editone_rev (full); }else{ SSTRING original_name (full); DIALOG dia; dia.newf_str (MSG_U(F_HOST_DOM,"host or sub-domain"),full); SSTRINGS tbns,tbnsttls; pri->getns(full,tbns,tbnsttls); primary_addblank (tbns,tbnsttls,3); SSTRINGS tbmx,tbmxttls; pri->getmx(full,tbmx,tbmxttls); primary_addblank (tbmx,tbmxttls,3); IP_ADDRS tba; SSTRINGS tbattls; pri->geta(full,tba,tbattls); while (tba.getnb()<4){ tba.add (new IP_ADDR); tbattls.add (new TIMESTR); } SSTRING cname; pri->getcname (full,cname); int start_cn = dia.getnb(); dia.newf_str (MSG_U(F_NICKNAME,"nick name for (CNAME)"),cname); int start_ip; setip_in_dia(dia,tba,tbattls,start_ip); dia.auto_newline(false); dia.newf_title (MSG_U(F_DNSADV,"Name servers (NS)"),1,"",MSG_R(F_DNSADV)); int start_ns = dia.getnb(); dnsrecs_setnsdia (dia,MSG_U(F_SERVER,"Server"),tbns,tbnsttls,regkeys.ns,regkeys.ns_ttl); dia.newf_title (MSG_U(F_EMAILADV,"Mail servers (MX)"),1,"" ,MSG_R(F_EMAILADV)); int start_mx = dia.getnb(); dnsrecs_setnsdia (dia,MSG_R(F_SERVER),tbmx,tbmxttls,regkeys.mx,regkeys.mx_ttl); int nofield = 0; while (1){ MENU_STATUS code = dia.edit(MSG_U(T_HOSTINFO,"Host information") ,MSG_U(I_HOSTINFO,"Enter a host name + domain\n" "and its IP numbers (at least one)\n" "and all DNS's table will be updated\n") ,help_dnsedit ,nofield ,MENUBUT_ACCEPT|MENUBUT_CANCEL|MENUBUT_DEL); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_DEL){ if (dialog_yesno(MSG_R(Q_CONFIRM) ,MSG_U(I_CONFIRM,"Are you sure you want to\n" "delete this entry") ,help_dnsedit) == MENU_YES){ SSTRINGS tbempty; SSTRING empty; IP_ADDRS noip; update_one (full,noip,tbempty,tbempty,tbempty,tbempty,tbempty,empty); write(); break; } }else{ bool err = false; IPMAPS maps; SSTRING notice; for (int i=0; iis_filled()){ const char *ip = a->get(); if (cname.is_filled()){ xconf_error (MSG_U(E_NOIPWCNAME,"No IP number allowed with a nickname")); nofield = start_cn; err = true; break; }else if (!ipnum_validip(ip,false)){ xconf_error (MSG_R(E_IVLDIP),ip); nofield = start_ip+i*2; err = true; break; } if (maps.getnb()>0 && !maps.inrange(ip)){ notice.appendf (MSG_U(N_IPNOTINRANGE ,"The IP number %s is not a member of allocation space.\n" "This might be a mistake.\n"),ip); } } } dns_lexcheck (full,nofield,0,err,false); dns_lexcheck (cname,nofield,start_cn,err,false); dns_lexcheck (tbns,nofield,start_ns,err,false); dns_lexcheck (tbmx,nofield,start_mx,err,true); if (!err){ if (notice.is_filled()) xconf_notice ("%s",notice.get()); if (full.cmp(original_name)!=0){ char msg[2000]; sprintf (msg ,MSG_U(Q_RENAME ,"You are renaming the host or domain\n" " %s\n" "to\n" " %s\n" "\n" "Do you want to delete the informations\n" "associated with the original name ?") ,original_name.get(),full.get()); if (dialog_yesno(MSG_U(T_RENAME,"Renaming") ,msg,help_rename) == MENU_YES){ SSTRINGS tbempty; SSTRING empty; IP_ADDRS noip; update_one (original_name,noip,tbempty,tbempty,tbempty,tbempty,tbempty ,empty); } } char doreverse[tba.getnb()]; if (check_basicconflict(full,tba) != -1 && check_dup(full,tba,doreverse)!=-1){ update_one (full,tba,tbattls,tbns,tbnsttls,tbmx,tbmxttls,cname,doreverse); write(); break; } } } } } } /* Edit the host information (A, PTR, NS and MX records) globally. */ PUBLIC void DNS::editrecs(const char *preset) { /* #Specification: dnsconf / host editition The user is prompted to enter a host or domain name. The system query all configured information about this host. All this is displayed in a form. The user can correct and add information. */ while (1){ SSTRING name (preset); MENU_STATUS code; { // We want the dialog to go away once the user // has input the value. It goes away when out of scope. DIALOG dia; dia.newf_str ("",name); code = dia.edit(MSG_U(Q_HOSTNAME,"Host or domain specification") ,MSG_U(I_HOSTNAME,"Enter the name of a host or the name of a\n" "sub-domain.") ,help_dnsedit); } if (code==MENU_ACCEPT){ const char *pt = name.get(); if (pt[0] != '\0'){ if (strcmp(pt,preset)==0){ xconf_error (MSG_U(E_MUSTINSERT ,"Insert a name before\n" "the domain name")); }else{ editone (pt); } } }else{ break; } } } static void dnsrecs_locate (const char *host, bool setting) { DNS *dns = dns_load(); if (dns != NULL){ dns->editone (host); } dns_unload(); } PUBLIC void DNS::listhostdom(SSTRINGS &tb) { for (int i=0; iorigins.getnb(); j++){ ORIGIN *o = p->origins.getitem(j); const char *origin = o->origin.get(); for (int k=0; ktbrec.getnb(); k++){ RECORD *r = o->tbrec.getitem(k); if (r->is (RTYPE_A) || r->is(RTYPE_CNAME)){ const char *left = r->get_left(); if (left != NULL && strcmp(left,"@")!=0){ SSTRING *s = new SSTRING; s->setfromf ("%s.%s",left,origin); tb.add (s); } } } } } tb.sort(); tb.remove_dups(); } static void dnsrecs_list (SSTRINGS &tb) { DNS *dns = dns_load(); dns->listhostdom(tb); dns_unload(); } #include static PUBLISH_VARIABLES_MSG dnsrecs_var_list1[]={ {"host",P_MSG_R(F_HOST_DOM)}, {"nickname",P_MSG_R(F_NICKNAME)}, { NULL, NULL } }; static REGISTER_VARIABLES dnsrecs_registry1("dnsconf","hosts" ,dnsrecs_var_list1,NULL,dnsrecs_locate,dnsrecs_list); static PUBLISH_VARIABLES_STR dnsrecs_var_list2[]={ {"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]}, {"ns1",®keys.ns[0]}, {"ns2",®keys.ns[1]}, {"ns3",®keys.ns[2]}, {"ns4",®keys.ns[3]}, {"nsttl1",®keys.ns_ttl[0]}, {"nsttl2",®keys.ns_ttl[1]}, {"nsttl3",®keys.ns_ttl[2]}, {"nsttl4",®keys.ns_ttl[3]}, {"mx1",®keys.mx[0]}, {"mx2",®keys.mx[1]}, {"mx3",®keys.mx[2]}, {"mx4",®keys.mx[3]}, {"mxttl1",®keys.mx_ttl[0]}, {"mxttl2",®keys.mx_ttl[1]}, {"mxttl3",®keys.mx_ttl[2]}, {"mxttl4",®keys.mx_ttl[3]}, { NULL, NULL } }; static REGISTER_VARIABLES dnsrecs_registry2("dnsconf","hosts" ,dnsrecs_var_list2,NULL,dnsrecs_locate,dnsrecs_list);