#include #include #include #include #include #include #include "netconf.h" #include #include #include #include "netconf.m" #include "internal.h" #include #include "hostinfo.h" static NETCONF_HELP_FILE help_hosts ("hosts"); NETCONF_HELP_FILE help_networks ("networks"); static CONFIG_FILE f_hosts (ETC_HOSTS,help_hosts ,CONFIGF_MANAGED|CONFIGF_NOARCH); #ifndef PROTO_SKIP void *operator new(size_t size) { void *ret = malloc(size); if (ret == NULL){ xconf_error (MSG_U(E_OUTOFMEMORY,"Out of memory\n")); exit (-1); } return ret; } #endif PUBLIC void HOST::set (const char *buf) { /* #Specification: /etc/hosts / format A hosts file has the following format # comment ip_number hostname [ alias ... ] [ # comment ] When we read /etc/hosts, we split the line in four parts, being the ip_numer, the first name, all the alias and the comment. Further, when collecting the comment, we try to keep even the space between the last data and the #. We hope to be able to edit normal /etc/hosts file and rewrite it mostly respecting the original format. Blank line are also remembered as comment. */ is_valid = 1; freeall(); char tmp[strlen(buf)+1]; strcpy (tmp,buf); const char *pt = str_skip(tmp); if (isdigit(*pt)){ char *ptcom = strchr(tmp,'#'); if (ptcom != NULL){ *ptcom++ = '\0'; if (*ptcom == ' ') ptcom++; // At rewrite time, we always // output "# " in front a comment comment.setfrom (ptcom); } // First copy the ip number pt = ip_num.copyword(pt); pt = str_skip (pt); if (*pt > ' '){ pt = name1.copyword (pt); pt = str_skip (pt); if (*pt > ' '){ others.setfrom (pt); } }else{ is_valid = 0; } }else if (*pt == '#'){ comment.setfrom (buf); }else if (pt[0] != '\0'){ // Anything else than a blank link is an error at this point is_valid = 0; } } /* Setup and parse a record from /etc/hosts */ PUBLIC HOST::HOST(const char *buf) { set (buf); } PUBLIC HOST::HOST( const char *_ip_num, const char *_name1, const char *_others, const char *_comment) { ip_num.setfrom (_ip_num); name1.setfrom(_name1); others.setfrom (_others); comment.setfrom (_comment); } PUBLIC HOST::HOST() { } /* Cleanup of the HOST, same as destructor */ PRIVATE void HOST::freeall() { ip_num.setfrom((const char *)NULL); name1.setfrom((const char *)NULL); others.setfrom((const char *)NULL); comment.setfrom((const char *)NULL); } PUBLIC VIRTUAL HOST::~HOST() { } /* Return the principal name of a host */ PUBLIC const char *HOST::getname1() const { return name1.get(); } /* Record the principal name of a host */ PUBLIC void HOST::setname1(const char *_name1) { name1.setfrom (_name1); } /* Return != 0 if this entry is only a comment, not a host definition. */ PUBLIC int HOST::iscomment() const { return ip_num.is_empty(); } /* Return the comment associated with an entry. */ PUBLIC const char *HOST::getcomment() const { return comment.get(); } /* Record the comment associate with a host. */ PUBLIC void HOST::setcomment(const char *_comment) { comment.setfrom (_comment); } /* Return the alternatives names for a host (maybe more than one) as a single string or "" is none. */ PUBLIC const char *HOST::getothers() const { return others.get(); } /* Record the others names of a host */ PUBLIC void HOST::setothers(const char *_others) { others.setfrom (_others); } /* Return the IP number of a host. */ PUBLIC const char *HOST::getipnum() const { return ip_num.get(); } /* Record the IP adress of a host. */ PUBLIC void HOST::setipnum(const char *_ipnum) { ip_num.setfrom (_ipnum); } /* Output one HOST record in ascii */ PUBLIC VIRTUAL void HOST::print (SSTREAM &ss) const { if (!ip_num.is_empty()){ ss.printf ("%s",ip_num.get()); if (!name1.is_empty()) ss.printf ("\t%s",name1.get()); if (!others.is_empty()) ss.printf ("\t%s",others.get()); if (!comment.is_empty()) ss.printf (" # %s",comment.get()); }else if (!comment.is_empty()){ ss.printf ("%s",comment.get()); } ss.putch ('\n'); } /* Return !- 0 if this is a special entry of /etc/hosts. This entry is special because it must exist for this program to work (and many program). The loopback and the entry of the current machine. */ PUBLIC VIRTUAL int HOST::is_special() const { return ip_num.is_empty() || ip_num.cmp("127.0.0.1")==0; } /* Edit a host definition. Return -1 if the input is cancelled Return 0 if it is accepted Return 1 if the user request deletion of this record. */ PUBLIC VIRTUAL int HOST::edit (HELP_FILE &helpfile) { int ret = -1; DIALOG dia; dia.newf_str (MSG_R(F_PRIMNAME),name1); dia.newf_str (MSG_R(F_ALIASES),others); dia.newf_str (MSG_U(F_IPNUM,"IP number"),ip_num); dia.newf_str (MSG_U(F_COMMENT,"Comment (opt)"),comment); int nofield = 0; while (1){ int code = dia.edit (MSG_U(T_HOSTNETDEF,"host/network definition") ,NULL ,helpfile ,nofield,MENUBUT_CANCEL|MENUBUT_ACCEPT|MENUBUT_DEL); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_DEL){ ret = 1; break; }else if (code ==MENU_ACCEPT){ if (!ipnum_validip(ip_num.get(),false)){ xconf_error (MSG_U(E_IVLIPNUM,"Invalid IP number")); }else{ ret = 0; break; } } } if (ret != 0) dia.restore(); return ret; } /* Lookup a host name in the "other name" string. Return != 0 if found. */ int host_lookupother (const char *others, const char *name) { int ret = 0; while (1){ char buf[200]; others = str_skip(others); others = str_copyword (buf,others,sizeof(buf)); if (buf[0] == '\0') break; if (strcmp(buf,name)==0){ ret = 1; break; } } return ret; } /* Return true if this record define one of the interface of the host */ PUBLIC bool HOST::is_stationid() { bool ret = false; const char *oth = others.get(); for (int i=0; ifopen ("r"); if (fin != NULL){ char buf[500]; ret = 0; while (fgets_cont(buf,sizeof(buf)-1,fin) != -1){ add (buf); } fclose (fin); } return ret; } /* Make sure there is an entry for localhost in /etc/hosts Return 0 if it was added, -1 if nothing was changed. */ PUBLIC int HOSTS::setlocalhost() { int ret = -1; HOST *h = getitem("localhost"); if (h == NULL){ h = new HOST ("127.0.0.1","localhost","",""); add (h); ret = 0; } return ret; } /* Write a hosts file (normally /etc/hosts). Return -1 if any error. */ PUBLIC int HOSTS::write () const { int ret = -1; FILE_CFG *fout = cfgf->fopen ("w"); if (fout != NULL){ SSTREAM_FILE_CFG ss (fout); ret = 0; for (int i=0; iprint (ss); fclose (fout); } return ret; } /* Return one entry of the hosts file. Returne NULL if the entry is out of range. */ PUBLIC HOST *HOSTS::getitem(int no) const { return (HOST*)ARRAY::getitem(no); } /* Find a name in the in memory host table. Returne NULL or the entry. */ PUBLIC HOST *HOSTS::getitem(const char *name) const { HOST *ret = NULL; if (name[0] != '\0'){ for (int i=0; igetname1())==0 || host_lookupother(pt->getothers() ,name)){ ret = pt; break; } } } return ret; } /* Find a name in the in memory host table by IP Returne NULL or the entry. */ PUBLIC HOST *HOSTS::getbyip(const char *ip) const { HOST *ret = NULL; for (int i=0; igetipnum())==0){ ret = pt; break; } } return ret; } /* Sort host by name */ static int cmp_host_by_name (const void *p1, const void *p2) { HOST *h1 = *(HOST**)p1; HOST *h2 = *(HOST**)p2; return strcmp(h1->getname1(),h2->getname1()); } /* Edit the file /etc/hosts or /etc/networks */ PUBLIC int HOSTS::edit( const char *title, HELP_FILE &helpfile) { int ret = -1; int choice=0; DIALOG_LISTE dia; dia.newf_head ("",MSG_U(T_HOSTHEAD,"IP number\tname\taliases")); dia.addwhat (MSG_U(I_ADDDEF,"Select [Add] to add a new definition")); while (1){ int nbh = getnb(); // Number of entry including comments HOST **tbsort = new HOST *[nbh]; if (tbsort != NULL){ // Extracting hosts only, no comments // Hide information about this host int nb=0; for (int i=0; iiscomment() && !pt->is_stationid()){ tbsort[nb++] = pt; } } qsort (tbsort,nb,sizeof(HOST *),cmp_host_by_name); // Format all the entries in a menu. // The first entry is dummy. It allows addition to the list for (int i=0; igetname1(),pth->getothers() ,pth->getcomment()); dia.set_menuitem(i,pth->getipnum(),buf); } dia.remove_last (nb+1); MENU_STATUS code = dia.editmenu (title ,MSG_U(I_HOSTSDEF ,"Select a host/network definition to modify") ,helpfile,choice,0); if (code == MENU_ESCAPE || code == MENU_QUIT){ break; }else if (code == MENU_ADD){ HOST *hst = newhost(""); if (hst != NULL){ if (hst->edit(helpfile)==0){ add(hst); write (); }else{ delete hst; } } }else if(nb>0){ // Edit one hosts HOST *hst = tbsort[choice]; int ok = hst->edit(helpfile); if (ok != -1){ if (ok == 1) remove_del (hst); write(); } } } } return ret; } /* Edit the file /etc/hosts */ void netconf_edithosts() { HOSTS hosts; if (hosts.read () != -1){ hosts.edit(ETC_HOSTS,help_hosts); } } /* Edit the file /etc/networks */ void netconf_editnet() { NETWORKS nets; if (nets.read () != -1){ nets.edit(ETC_NETWORKS,help_networks); } } class CONFIG_FILE_HOSTSVIEW: public CONFIG_FILE{ bool hostid; // Which part of /etc/hosts are we manipulating /*~PROTOBEG~ CONFIG_FILE_HOSTSVIEW */ public: CONFIG_FILE_HOSTSVIEW (const char *path, const char *subsys, bool _hostid); int archive (SSTREAM&ss)const; int extract (SSTREAM&ss); /*~PROTOEND~ CONFIG_FILE_HOSTSVIEW */ }; PUBLIC CONFIG_FILE_HOSTSVIEW::CONFIG_FILE_HOSTSVIEW( const char *path, const char *subsys, bool _hostid) : CONFIG_FILE (path,help_nil,CONFIGF_VIRTUAL,subsys) { hostid = _hostid; } PUBLIC int CONFIG_FILE_HOSTSVIEW::archive (SSTREAM &ss) const { configf_sendexist (ss,true); HOSTS hosts; hosts.read(); int n = hosts.getnb(); for (int i=0; iis_stationid() || pt->is_special(); if (is_local == hostid){ pt->print (ss); } } return 0; } PUBLIC int CONFIG_FILE_HOSTSVIEW::extract (SSTREAM &ss) { HOSTS hosts; hosts.read(); // First we remove all stationid entries int n = hosts.getnb(); for (int i=0; iis_stationid() || pt->is_special(); if (is_local == hostid){ hosts.remove_del (i); i--; n--; } } char line[500]; while (ss.gets(line,sizeof(line)-1) != NULL){ strip_end (line); hosts.add (new HOST(line)); } hosts.write(); return 0; } class CONFIG_FILE_HOSTID: public CONFIG_FILE_HOSTSVIEW{ /*~PROTOBEG~ CONFIG_FILE_HOSTID */ public: CONFIG_FILE_HOSTID (void); /*~PROTOEND~ CONFIG_FILE_HOSTID */ }; PUBLIC CONFIG_FILE_HOSTID::CONFIG_FILE_HOSTID() :CONFIG_FILE_HOSTSVIEW (ETC_HOSTS "-stationid",subsys_stationid,true) { } class CONFIG_FILE_HOSTCLIENT: public CONFIG_FILE_HOSTSVIEW{ /*~PROTOBEG~ CONFIG_FILE_HOSTCLIENT */ public: CONFIG_FILE_HOSTCLIENT (void); /*~PROTOEND~ CONFIG_FILE_HOSTCLIENT */ }; PUBLIC CONFIG_FILE_HOSTCLIENT::CONFIG_FILE_HOSTCLIENT() :CONFIG_FILE_HOSTSVIEW (ETC_HOSTS "-client",subsys_netclient,false) { } static CONFIG_FILE_HOSTCLIENT hostclient; static CONFIG_FILE_HOSTID hostid; #ifdef TEST int main (int , char *[]) { HOSTS hosts; hosts.read (); hosts.write (); return 0; } #endif