// dhcp server management #include #include #include #include "dhcpd.h" #include "dhcpd.m" #include "../../paths.h" #include #include #include #include #include #include #include const char subsys_dhcpd[]="dhcpd"; static LINUXCONF_SUBSYS subb (subsys_dhcpd,P_MSG_R(M_DHCP)); static HELP_FILE help_dhcp("dhcpd","dhcp"); CONFIG_FILE f_dhcp (ETC_DHCPD_CONF,help_dhcp ,CONFIGF_OPTIONAL|CONFIGF_MANAGED|CONFIGF_PROBED |CONFIGF_SIGNPOUND ,subsys_dhcpd); CONFIG_FILE f_lease (ETC_DHCPD_LEASES,help_dhcp ,CONFIGF_PROBED,subsys_dhcpd); PRIVATE void DHCP_OPTION::init() { title = "???"; maxvalues = 1; help = NULL; section = 0; } PUBLIC DHCP_OPTION::DHCP_OPTION(const char *_keyw) { keyw.setfrom (_keyw); init(); } PUBLIC DHCP_OPTION::DHCP_OPTION() { init(); } /* Add one value to the value list of this option. */ PUBLIC void DHCP_OPTION::addvalue(const char *value) { values.add (new SSTRING (value)); } PUBLIC void DHCP_OPTION::write (const char *indent, FILE_CFG *fout) { if (values.getnb() > 0){ fprintf (fout,"%soption %s",indent,keyw.get()); const char *quote = ""; if (keyw.cmp("domain-name")==0 || keyw.cmp("host-name")==0) quote = "\""; for (int i=0; iget(),quote); } fputs (";\n",fout); } } PUBLIC DHCP_OPTION *DHCP_OPTIONS::getitem (int no) { return (DHCP_OPTION*)ARRAY::getitem(no); } PUBLIC DHCP_OPTION *DHCP_OPTIONS::getitem (const char *keyw) { DHCP_OPTION *ret = NULL; for (int i=0; ikeyw.cmp(keyw)==0){ ret = o; break; } } return ret; } PUBLIC void DHCP_OPTIONS::write (const char *indent, FILE_CFG *fout) { for (int i=0; iwrite (indent,fout); } static int cmp_opt_name (const ARRAY_OBJ *o1, const ARRAY_OBJ *o2) { DHCP_OPTION *op1 = (DHCP_OPTION*)o1; DHCP_OPTION *op2 = (DHCP_OPTION*)o2; return op1->keyw.cmp(op2->keyw); } /* Add the missing options in the list with empty values */ PUBLIC void DHCP_OPTIONS::fill() { // List of options and option title for the dialog static const COMBO_OPTION optnode[]={ {"0x1", "IE"}, {"0x2", "Cache"}, {"0x4", "WINS"}, {"0x8", "Broadcast"}, {NULL,NULL} }; static struct { const char *keyw; const char *title; char maxvalues; char section; // Used to split the dialog in multiple section const COMBO_OPTION *help; } tb[]={ {"routers", MSG_U(F_ROUTERS,"Default gateways"),3,0,NULL}, {"subnet-mask", MSG_U(F_SUBNETMASK,"Netmask"),1,0,NULL}, {"time-offset", MSG_U(F_TIMEOFFSET,"Time offset"),1,0,NULL}, {"domain-name-servers", MSG_U(F_DNS,"Name servers(DNS)"),3,1,NULL}, {"domain-name", MSG_U(F_DOMAIN,"Domain name"),1,1,NULL}, {"host-name", MSG_U(F_DHHOSTNAME,"Host name"),1,1,NULL}, {"nis-domain", MSG_U(F_NISDOMAIN,"NIS domain"),1,2,NULL}, {"nis-servers", MSG_U(F_NISSERVER,"NIS servers"),1,2,NULL}, {"netbios-name-servers", MSG_U(F_WINS,"Name servers(Netbios)"),3,3,NULL}, {"netbios-node-type", MSG_U(F_NETBIOSNODETYPE,"Node type(Netbios)"),1,3,optnode}, {"log-servers", MSG_U(F_LOGSERVERS,"Log servers"),1,4,NULL}, {"lpr-servers", MSG_U(F_LPRSERVERS,"Print servers"),1,4,NULL}, {"time-servers", MSG_U(F_TIMESERVERS,"Time servers"),1,4,NULL}, {"cookie-servers", MSG_U(F_COOKIESERVERS,"Cookie servers"),1,4,NULL}, {NULL,NULL, 0} }; // Put unknown option in section 5 for (int n=0; nsection = 5; // Known option receive a translated title and a section number // as well as a max value. for (int n=0; tb[n].keyw != NULL; n++){ const char *keyw = tb[n].keyw; const char *title = tb[n].title; int maxvalues = tb[n].maxvalues; DHCP_OPTION *o = getitem(keyw); if (o == NULL){ o = new DHCP_OPTION (keyw); add (o); } o->title = title; o->maxvalues = maxvalues; o->section = tb[n].section; o->help = tb[n].help; } sort(cmp_opt_name); } /* Remove options with no value */ PUBLIC void DHCP_OPTIONS::unfill() { for (int i=0; ivalues.remove_empty(); if (o->values.getnb()==0){ remove_del (o); i--; } } } PUBLIC DHCP_RANGE::DHCP_RANGE() { dyn_bootp = 0; } PUBLIC DHCP_RANGE::DHCP_RANGE( char _dyn_bootp, SSTRING &_start, SSTRING &_stop) { dyn_bootp = _dyn_bootp; alloc_start.setfrom (_start); alloc_stop.setfrom (_stop); } PUBLIC DHCP_RANGE *DHCP_RANGES::getitem (int no) const { return (DHCP_RANGE*)ARRAY::getitem(no); } PUBLIC DHCP_SUBNET::DHCP_SUBNET() { } PUBLIC void DHCP_SUBNET::write (FILE_CFG *fout) { fprintf (fout,"subnet %s netmask %s{\n",network.get(),netmask.get()); for (int i=0; idyn_bootp ? "dynamic-bootp" : "" ,r->alloc_start.get() ,r->alloc_stop.get()); } if (default_lease_time.seconds != 0){ fprintf (fout,"\tdefault-lease-time %ld;\n",default_lease_time.seconds); } if (max_lease_time.seconds != 0){ fprintf (fout,"\tmax-lease-time %ld;\n",max_lease_time.seconds); } if (filename.is_filled()){ fprintf (fout,"\tfilename \"%s\";\n",filename.get()); } if (next_server.is_filled()){ fprintf (fout,"\tnext-server \"%s\";\n",next_server.get()); } options.write ("\t",fout); fputs ("}\n",fout); } PUBLIC DHCP_SUBNET *DHCP_SUBNETS::getitem (int no) { return (DHCP_SUBNET*)ARRAY::getitem(no); } PUBLIC void DHCP_SUBNETS::write (FILE_CFG *fout) { for (int i=0; iwrite (fout); } // Definition for one bootp client PUBLIC void DHCP_HOST::write (const char *indent, FILE_CFG *fout) { fprintf (fout,"%shost %s{\n",indent,ip_host.get()); char sindent[10]; sprintf (sindent,"%s\t",indent); options.write (sindent,fout); } PUBLIC DHCP_HOST* DHCP_HOSTS::getitem (int no) { return (DHCP_HOST*)ARRAY::getitem(no); } PUBLIC void DHCP_HOSTS::write (const char *indent, FILE_CFG *fout) { for (int i=0; iwrite(indent,fout); } } // Group of bootp entries which share a common definition PUBLIC void DHCP_BOOTP::write (FILE_CFG *fout) { fputs ("group {\n",fout); options.write ("\t",fout); hosts.write ("\t",fout); fputs ("}\n",fout); } PUBLIC DHCP_BOOTP *DHCP_BOOTPS::getitem (int no) { return (DHCP_BOOTP*)ARRAY::getitem(no); } static const char K_DHCP[]="DHCP"; static const char K_UPDDNSDOM[]="upddnsdom"; static const char K_CRONUPDDNS[]="cronupddns"; static const char K_VALIDNAME[]="validname"; void dhcp_createlease() { if (!f_lease.exist()){ const char *path = f_lease.getpath(); net_prtlog (NETLOG_WHY ,MSG_U(I_CREATELEASE,"File %s missing, must be created\n") ,path); if (!simul_ison()){ // Create the sub-directory if missing char pathdir[strlen(path)+1]; strcpy (pathdir,path); char *last = strrchr(pathdir,'/'); if (last != NULL){ *last = '\0'; file_mkdirp(pathdir,"root","root",0755); } FILE_CFG *fout = f_lease.fopen ("w"); fclose (fout); } } } PUBLIC int DHCP::write() { int ret = -1; if (!error){ FILE_CFG *fout = f_dhcp.fopen ("w"); if (fout != NULL){ fprintf (fout,"server-identifier %s;\n",identifier.get()); if (ddns_update_style.is_filled()){ fprintf (fout,"ddns-update-style %s;\n",ddns_update_style.get()); }else{ fprintf (fout,"ddns-update-style ad-hoc;\n"); } if (default_lease_time.seconds != 0){ fprintf (fout,"default-lease-time %ld;\n",default_lease_time.seconds); } if (max_lease_time.seconds){ fprintf (fout,"max-lease-time %ld;\n",max_lease_time.seconds); } options.write("",fout); subnets.write(fout); ret = fclose (fout); dhcp_createlease(); linuxconf_setcursys (subsys_dhcpd); linuxconf_replace (K_DHCP,K_UPDDNSDOM,dnsdom); linuxconf_replace (K_DHCP,K_CRONUPDDNS,cronupddns); linuxconf_replace (K_DHCP,K_VALIDNAME,validname); linuxconf_save(); } } return ret; } static void dhcp_parseerror (DHCP_PARSE &par) { if (!par.error){ xconf_error (MSG_U(E_DHPARSE ,"Parse error in file %s, line %d\n" "\n" "\t%s\n" "\t%*s\n" "\n" "Linuxconf won't be able to use or edit the DHCP\n" "configuration. Please correct.") ,ETC_DHCPD_CONF,par.noline,par.buf,par.offset_last,"^"); par.error = 1; } } static void dhcp_fillbuf(DHCP_PARSE &par) { par.pt = str_skip (par.pt); while (*par.pt == '\0'){ char tmp[sizeof(par.buf)]; if (fgets_strip (tmp,sizeof(par.buf)-1,par.fin ,'\0','#',&par.noline)==NULL){ par.buf[0] = '\0'; par.pt = par.buf; par.eof = 1; break; }else{ // This make error message nicer as it position the // ^ properly. str_exptab (tmp,8,par.buf); par.pt = str_skip(par.buf); } } } static int dhcp_is_eof (DHCP_PARSE &par) { if (!par.eof) dhcp_fillbuf(par); return par.eof; } static int dhcp_char_is_spc(char car) { return car == '{' || car == ',' || car == ';' || car == '}'; } static char *dhcp_copyword (char *dest, const char *str) { str = str_skip(str); if (*str == '"'){ str = str_copyquote (dest,str); }else{ while (isgraph(*str) && !dhcp_char_is_spc(*str)) *dest++ = *str++; *dest = '\0'; } return (char*) str; } /* Read the next word or special character. Return -1 if end of file 0 if a word was read 1 if a special character was read */ static int dhcp_readone (DHCP_PARSE &par, char *word) { int ret = -1; word[0] = '\0'; if (!par.error && !par.eof){ dhcp_fillbuf (par); char first = *par.pt; if (first != '\0'){ par.offset_last = (int)(par.pt - par.buf); if (dhcp_char_is_spc(first)){ par.pt++; word[0] = first; word[1] = '\0'; ret = 1; }else{ par.pt = dhcp_copyword (word,par.pt); ret = 0; } } } return ret; } /* Read one word from the input stream and make sure it is not a special character. */ static int dhcp_readword (DHCP_PARSE &par, char *word) { int ret = dhcp_readone (par,word); if (ret != 0){ dhcp_parseerror (par); ret = -1; } return ret; } /* Read a special character. Produce an error if this is not the case. Return the character read or -1. */ static int dhcp_readtok(DHCP_PARSE &par) { char word[100]; int ret = dhcp_readone(par,word); if (ret == 1){ ret = word[0]; }else{ ret = -1; dhcp_parseerror (par); } return ret; } static int dhcp_readword (DHCP_PARSE &par, SSTRING &word) { char tmp[100]; int ret = dhcp_readword(par,tmp); word.setfrom (tmp); return ret; } /* Read conditionnally a closing brace in the input stream. If the stream do not contain a closing brace, nothing is read. */ static int dhcp_readbrc (DHCP_PARSE &par) { int ret = -1; dhcp_fillbuf (par); if (*par.pt == '}'){ par.pt++; ret = 0; } return ret; } static int dhcp_readip (DHCP_PARSE &par, char *str) { int ret = dhcp_readword (par,str); if (ret != -1){ if (ipnum_validip(str,false)){ ret = 0; }else{ dhcp_parseerror (par); } } return ret; } static int dhcp_readip (DHCP_PARSE &par, SSTRING &s) { char tmp[100]; int ret = dhcp_readip(par,tmp); s.setfrom (tmp); return ret; } /* read an option optionname value [ , value ... ] ; sequence */ static int dhcp_readoption (DHCP_PARSE &par, DHCP_OPTION &option) { int ret = -1; if (dhcp_readword (par,option.keyw)!=-1){ while (1){ char val[200]; if (dhcp_readword (par,val)!=-1){ option.addvalue (val); int tok = dhcp_readtok(par); if (tok == -1){ break; }else if (tok == ','){ // We parse another value }else if (tok == ';'){ ret = 0; break; }else{ // Not good dhcp_parseerror(par); } } } } return ret; } /* read a sequence value ; */ static int dhcp_readval (DHCP_PARSE &par, SSTRING &str) { int ret = -1; if (dhcp_readword (par,str)!=-1 && dhcp_readtok(par)==';'){ ret = 0; }else{ dhcp_parseerror (par); } return ret; } static int dhcp_readstop (DHCP_PARSE &par, SSTRING &stop) { int ret = -1; char word[100]; int ok = dhcp_readone (par,word); stop.setfrom (""); if (ok == 1){ if (word[0] == ';') ret = 0; }else if (ok == 0){ if (ipnum_validip (word,false) && dhcp_readtok(par)==';'){ stop.setfrom (word); ret = 0; } } return ret; } PRIVATE int DHCP_SUBNET::readline ( DHCP_PARSE &par) { int ret = -1; char word[100]; if (dhcp_readword (par,word)!=-1){ if (strcmp(word,"option")==0){ DHCP_OPTION *option = new DHCP_OPTION; options.add (option); ret = dhcp_readoption(par,*option); }else if (strcmp(word,"range")==0){ char keyw[100]; if (dhcp_readword (par,keyw)!=-1){ if (strcmp(keyw,"dynamic-bootp")==0){ SSTRING alloc_start,alloc_stop; if (dhcp_readip (par,alloc_start)!=-1 && dhcp_readstop (par,alloc_stop)!=-1){ ranges.add (new DHCP_RANGE(1,alloc_start,alloc_stop)); ret = 0; } }else if (ipnum_validip (keyw,false)){ SSTRING alloc_start,alloc_stop; alloc_start.setfrom (keyw); if (dhcp_readstop (par,alloc_stop)!=-1){ ranges.add (new DHCP_RANGE(0,alloc_start,alloc_stop)); ret = 0; } }else{ dhcp_parseerror (par); } } }else if (strcmp(word,"default-lease-time")==0){ ret = dhcp_readval (par,default_lease_time); }else if (strcmp(word,"max-lease-time")==0){ ret = dhcp_readval (par,max_lease_time); }else if (strcmp(word,"filename")==0){ ret = dhcp_readval (par,filename); }else if (strcmp(word,"next-server")==0){ ret = dhcp_readval (par,next_server); } } return ret; } PRIVATE int DHCP_SUBNET::readlines ( DHCP_PARSE &par) { int ret = 0; while (dhcp_readbrc(par)){ if (dhcp_is_eof(par)){ par.error = 1; ret = -1; break; }else if (readline(par)==-1){ ret = -1; break; } } return ret; } PUBLIC int DHCP_SUBNET::read (DHCP_PARSE &par) { int ret = -1; char s_network[100],s_netmask[100],keyw[100]; if (dhcp_readip (par,s_network)!=-1 && dhcp_readword (par,keyw)!=-1 && strcmp (keyw,"netmask")==0 && dhcp_readip (par,s_netmask)!=-1 && dhcp_readtok(par) == '{'){ network.setfrom (s_network); netmask.setfrom (s_netmask); ret = readlines (par); }else{ dhcp_parseerror(par); } return ret; } PRIVATE int DHCP::readline ( DHCP_PARSE &par) { int ret = -1; char word[100]; if (dhcp_readword (par,word)!=-1){ if (strcmp(word,"option")==0){ DHCP_OPTION *option = new DHCP_OPTION; options.add (option); ret = dhcp_readoption(par,*option); }else if (strcmp(word,"subnet")==0){ DHCP_SUBNET *sub = new DHCP_SUBNET; subnets.add (sub); ret = sub->read (par); }else if (strcmp(word,"server-identifier")==0){ ret = dhcp_readval (par,identifier); }else if (strcmp(word,"default-lease-time")==0){ ret = dhcp_readval (par,default_lease_time); }else if (strcmp(word,"max-lease-time")==0){ ret = dhcp_readval (par,max_lease_time); }else if (strcmp(word,"ddns-update-style")==0){ ret = dhcp_readval (par,ddns_update_style); } } return ret; } PRIVATE int DHCP::readlines ( DHCP_PARSE &par) { int ret = 0; while (!dhcp_is_eof(par)){ if (readline(par)==-1){ ret = -1; break; } } return ret; } /* Return the dns domain to use to update the DNS from the dhcpd.leases file */ const char *dhcp_getdefdom() { return linuxconf_getval (K_DHCP,K_UPDDNSDOM,""); } /* Return the flag telling if only valid name must be exported to the DNS */ bool dhcp_getvalidname() { return linuxconf_getvalnum(K_DHCP,K_VALIDNAME,0) != 0; } PUBLIC DHCP::DHCP() { error = 0; dnsdom.setfrom (dhcp_getdefdom()); validname = dhcp_getvalidname() ? 1 : 0; cronupddns = linuxconf_getvalnum (K_DHCP,K_CRONUPDDNS,0); FILE_CFG *fin = f_dhcp.fopen ("r"); if (fin != NULL){ DHCP_PARSE par; par.fin = fin; par.pt = par.buf; par.buf[0] = '\0'; par.error = 0; par.eof = 0; par.noline = 0; readlines (par); fclose (fin); error = par.error; } } PUBLIC void DHCP_OPTIONS::setdia (DIALOG &dia) { fill(); static const char *tbsect[]={ MSG_U(T_BASIC,"Basic"), MSG_U(T_DNS,"Dns"), MSG_U(T_NIS,"NIS"), MSG_U(T_METBIOS,"Netbios"), MSG_U(T_SERVERS,"Servers"), MSG_U(T_OTHERS,"Others"), }; int nbsect = 5; for (int i=0; isection == 6){ nbsect = 5; break; } } for (int s=0; ssection == s){ o->nofield = dia.getnb(); for (int n=o->values.getnb(); n maxvalues; n++){ o->addvalue (""); } for (int v=0; vvalues.getnb(); v++){ SSTRING *s = o->values.getitem(v); const char *title = v== 0 ? o->title : ""; if (o->help != NULL){ FIELD_COMBO *comb = dia.newf_combo (title ,*s); for (int cb=0; o->help[cb].val != NULL; cb++){ comb->addopt (o->help[cb].val,o->help[cb].desc); } }else{ dia.newf_str (title ,*s); } } } } } } static void dhcp_valid_net ( SSTRING &ip, SSTRING &net, int nofield, int &new_nofield) { if (new_nofield == -1){ const char *s_ip = ip.get(); const char *s_mask = net.get(); if (!ipnum_validnet (s_ip,s_mask)){ unsigned long b_ip = ipnum_aip2l (s_ip); unsigned long b_mask = ipnum_aip2l (s_mask); unsigned long b_newnet = b_ip & b_mask; char hint[LEN_IP_ASC+1]; ipnum_ip2a (b_newnet,hint); new_nofield = nofield; xconf_error (MSG_U(E_IVLDNETWORKIP ,"Invalid Network number %s for netmask %s\n" "t\t\tHint: %s") ,ip.get(),net.get(),hint); } } } static void dhcp_valid_range ( SSTRING &net, SSTRING &mask, SSTRING &ip, int nofield, int &new_nofield) { if (new_nofield == -1){ unsigned long bnetwork = ipnum_aip2l (net.get()); unsigned long bnetmask = ipnum_aip2l (mask.get()); unsigned long bipaddr = ipnum_aip2l (ip.get()); unsigned long network = bipaddr & bnetmask; if (network != bnetwork){ new_nofield = nofield; xconf_error (MSG_U(E_IVLDNETHOSTIP ,"Invalid IP range %s for network %s") ,ip.get(),net.get()); } } } /* Check if an IP number is valid */ static void dhcp_checkip ( SSTRING &ip, bool ishost, bool may_be_empty, int nofield, int &new_nofield) { // Only signal the first error if (new_nofield == -1){ const char *ipstr = ip.get(); if (may_be_empty && ipstr[0] == '\0'){ // ok }else if (!ipnum_validip(ipstr,ishost)){ new_nofield = nofield; if (ishost){ xconf_error (MSG_U(E_IVLDHOSTIP,"Invalid host IP number %s") ,ipstr); }else{ xconf_error (MSG_U(E_IVLDNETIP,"Invalid network IP number %s") ,ipstr); } } } } PUBLIC void DHCP_OPTIONS::validate (int &new_nof) { for (int i=0; ikeyw.cmp("routers")==0 || o->keyw.cmp("domain-name-servers")==0 || o->keyw.cmp("netbios-name-servers")==0){ for (int j=0; jvalues.getnb(); j++){ SSTRING *ss = o->values.getitem(j); dhcp_checkip (*ss,true,true,o->nofield+j,new_nof); } }else if (o->keyw.cmp("subnet-mask")==0){ SSTRING *ss = o->values.getitem(0); dhcp_checkip (*ss,false,true,o->nofield,new_nof); } } } PUBLIC int DHCP_SUBNET::edit() { int ret = -1; DIALOG dia; dia.newf_str (MSG_U(F_NETWORK,"Network number"),network); dia.last_noempty(); dia.newf_str (MSG_U(F_NETMASK,"netmask"),netmask); dia.last_noempty(); dia.newf_str (MSG_U(F_DEFLEASETIME,"Default lease time"),default_lease_time); dia.newf_str (MSG_U(F_MAXLEASETIME,"Max lease time"),max_lease_time); dia.newf_str (MSG_U(F_FILENAME,"Boot file"),filename); dia.newf_title (MSG_U(T_RANGES,"Ranges"),1,"",MSG_R(T_RANGES)); int field_range = dia.getnb(); ranges.add (new DHCP_RANGE); for (int i=0; idyn_bootp,MSG_U(F_DYNBOOTP,"Allocate to BOOTP clients")); dia.newf_str (MSG_U(F_STARTIP,"IP range start"),r->alloc_start); // Only the first range is mandatory if (i==0) dia.last_noempty(); dia.newf_str (MSG_U(F_STOPIP,"IP range stop"),r->alloc_stop); } options.setdia (dia); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_ONESUB,"One subnet definition") ,MSG_U(I_ONESUB ,"You control here the information for\n" "one network. Anything left blank use the defaults.") ,help_dhcp ,nof ,MENUBUT_DEL|MENUBUT_CANCEL|MENUBUT_ACCEPT); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_DEL){ if (xconf_delok()){ ret = 1; break; } }else if (code == MENU_ACCEPT){ int new_nof = -1; dhcp_checkip (network,false,false,0,new_nof); dhcp_checkip (netmask,false,false,1,new_nof); dhcp_valid_net(network,netmask,0,new_nof); // Added to check the whether user entered a valid // network address for the mask which he/she entered. for (int i=0; ialloc_start.is_filled() || r->alloc_stop.is_filled() || i == 0){ int nofield = field_range + i*3 +1; dhcp_checkip (r->alloc_start,true,false ,nofield,new_nof); dhcp_valid_range (network,netmask,r->alloc_start ,nofield,new_nof); if (r->alloc_stop.is_filled()){ nofield++; dhcp_checkip (r->alloc_stop,true,true ,nofield,new_nof); dhcp_valid_range (network,netmask,r->alloc_stop ,nofield,new_nof); } } } options.validate(new_nof); if (new_nof != -1){ nof = new_nof; }else{ char dev[20]; if (netconf_finddevfromnet(network.get(),netmask.get() ,dev)== -1){ xconf_notice (MSG_U(E_NOMATCH ,"This subnet is not associated with any\n" "network adaptor (basic host information).\n" "This means that IP numbers in this subnet\n" "will only by allocated to some other network\n" "using a dhcp relay to this dhcpd server")); } ret = 0; break; } } } if (ret != 0) dia.restore(); options.unfill(); for (int i=0; ialloc_start.is_empty()){ ranges.remove_del (r); i--; } } return ret; } /* Edit default values which apply to Return -1 if the changes were not accepted. */ PUBLIC int DHCP::editdefaults() { int ret = -1; DIALOG dia; dia.newf_str (MSG_U(F_IDENT,"Server identification"),identifier); dia.last_noempty(); dia.newf_str (MSG_R(F_DEFLEASETIME),default_lease_time); dia.newf_str (MSG_R(F_MAXLEASETIME),max_lease_time); bool dns_api_ok = dnsconf_api_available("dhcpd"); int field_upddns = dia.getnb(); if (dns_api_ok){ dia.newf_str (MSG_U(F_DEFDNSDOM,"Update DNS domain"),dnsdom); dia.newf_chk (MSG_U(F_UPDDNS,"Update DNS"),cronupddns ,MSG_U(I_UPDDNS,"from cron")); dia.newf_chk ("",validname,MSG_U(I_VALIDNAMEONLY,"Export only valid names")); } options.setdia (dia); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_DHCPDEF,"DHCP defaults") ,MSG_U(I_DHCPDEF ,"You can enter the default setting which will\n" "be shared by all subnets setups unless overriden\n") ,help_dhcp ,nof); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (dns_api_ok && cronupddns && dnsdom.is_empty()){ nof = field_upddns; xconf_error (MSG_U(E_CANUPDDNS ,"The DNS can't be update from the dhcp server\n" "unless you provide the domain in which to\n" "register hosts.")); }else{ int new_nof = -1; options.validate (new_nof); if (new_nof != -1){ nof = new_nof; }else{ if (dns_api_ok){ // Update the crontab const char *cmd = "/usr/lib/linuxconf/lib/dhcp2dns.sh"; if (cronupddns){ cron_addcmd ("root",cmd ,"0-59/5","*","*","*","*"); }else{ cron_delcmd ("root",cmd); } } ret = 0; break; } } } if (ret != 0) dia.restore(); options.unfill(); return ret; } PUBLIC void DHCP::editloop() { int nof = 0; DNSCONF_API *dns_api = dnsconf_api_init("dhcpd"); while (1){ DIALOG dia; dia.new_menuitem (MSG_U(M_EDIT,"Edit"), MSG_U(M_DEFOPT,"defaults")); const char *viewleases = MSG_U(M_VIEWLEASES,"DHCP allocations"); dia.new_menuitem (MSG_U(M_VIEW,"View"),viewleases); const char *upddns = MSG_U(M_UPDATEDNS,"DNS with dhcp hosts"); if (dns_api != NULL){ dia.new_menuitem(MSG_U(M_UPDATE,"Update"),upddns); } dia.newf_title ("",MSG_U(T_SUBNETS,"Subnets")); dia.newf_head ("",MSG_U(H_SUBNETS,"Network\tNetmask\tAlloc from\t->\tTo")); int start_subnets = dia.getnb(); int i; for (i=0; iranges.getitem(0); const char *start = ""; const char *stop = ""; if (r != NULL){ start = r->alloc_start.get(); stop = r->alloc_stop.get(); } char buf[100]; sprintf (buf,"%s\t%s\t\t%s" ,s->netmask.get() ,start,stop); dia.new_menuitem (s->network.get(),buf); } int stop_subnets = dia.getnb(); dia.newf_title ("",MSG_U(T_BOOTPHOST,"Bootp hosts")); int start_bootp = dia.getnb(); for (i=0; i\t%s" ,s->network.get(),s->netmask.get() ,s->alloc_start.get(),s->alloc_stop.get()); #else strcpy (buf,"bootp"); #endif dia.new_menuitem ("",buf); } int stop_bootp = dia.getnb(); bool save = false; dia.setbutinfo (MENU_USR1,MSG_U(B_ADDSUBNET,"AddNet"),"AddNet"); dia.setbutinfo (MENU_USR2,MSG_U(B_ADDBOOTP,"AddBootp"),"AddBootp"); MENU_STATUS code = dia.editmenu ( MSG_U(T_DHCPCONF,"Dhcp configuration") ,MSG_U(I_DHCPCONF ,"You can define how IP numbers are allocated\n" "dynamicly to workstation on your net.\n" "This is a major time saver when managing an IP network\n" "with more than a few computers.") ,help_dhcp ,nof,MENUBUT_USR1|MENUBUT_USR2); if (code == MENU_ESCAPE || code == MENU_QUIT){ break; }else if (code == MENU_USR1){ DHCP_SUBNET *s = new DHCP_SUBNET; int ok = s->edit(); if (ok == 0){ subnets.add (s); save = true; }else{ delete s; } }else if (code == MENU_USR2){ DHCP_BOOTP *s = new DHCP_BOOTP; bootps.add (s); int ok = s->edit(); if (ok != -1){ if (ok == 1) bootps.remove_del (s); save = true; } }else if (nof == 0){ save = editdefaults() == 0; }else if (dia.getmenustr(nof)==upddns){ //hubert@id-pro.de: care about the ranges UWE_RANGES ranges(*this); leases_updatedns (dns_api,false,false,&ranges,f_lease.getpath(),dhcp_getdefdom()); }else if (dia.getmenustr(nof)==viewleases){ leases_showalloc (); }else if (nof >= start_subnets && nof < stop_subnets){ DHCP_SUBNET *s = subnets.getitem(nof-start_subnets); int ok = s->edit(); if (ok != -1){ if (ok == 1) subnets.remove_del (s); save = true; } }else if (nof >= start_bootp && nof < stop_bootp){ DHCP_BOOTP *s = bootps.getitem(nof-start_bootp); int ok = s->edit(); if (ok != -1){ if (ok == 1) bootps.remove_del (s); save = true; } } if (save) write(); } dnsconf_api_end(dns_api); } /* Command line version to update the dns from the dhcpd.leases */ int dhcpd_updatedns(bool verbose, const char *fname, const char *domain) { int ret = -1; DNSCONF_API *dns_api = dnsconf_api_init("dhcpd"); if (dns_api == NULL){ fprintf (stderr,MSG_U(E_NODNSSUPPORT ,"The DNSCONF_API is not registered, can't update the DNS\n" "(probably because the dnsconf module is not enabled)")); }else{ //hubert@id-pro.de: care about the ranges DHCP dh; UWE_RANGES ranges(dh); ret = leases_updatedns (dns_api,true,verbose,&ranges,fname,domain); } dnsconf_api_end(dns_api); return ret; } PUBLIC void DHCP::edit() { if (identifier.is_empty()){ if(editdefaults() == 0){ write(); editloop(); } }else{ editloop(); } } void dhcp_edit() { DHCP dh; if (!dh.error){ dh.edit(); } } /* Compute the hint/activation for the startup script of the dhcpd server The interface to monitor and the special routes to create */ PUBLIC int DHCP::probe() { int ret = 0; ROUTES active; int n = subnets.getnb(); if (n > 0 && active.readactive() != -1){ int nr = active.getnb(); char devices[n*20],routes[n*8]; char *ptdev = devices, *ptr = routes; for (int i=0; inetwork.get(),net->netmask.get() ,dev)!= -1){ if (ptdev != devices) *ptdev++ = ' '; ptdev = stpcpy (ptdev,dev); bool ok = false; // Check if the special route exist for the device for (int r=0; rgetiface(),dev)==0 && strcmp(ro->getdst(),"255.255.255.255")==0 && strcmp(ro->getmask(),"255.255.255.255")==0){ ok = true; break; } } /* #Specification: dhcpd / special routes / no cleanup The dhcpd module adds the proper routes for the served network/device. It does not removed the unneeded one (The ones that it added previously maybe). It does not matter much. */ if (!ok){ char args[1000]; sprintf (args,"add -net 255.255.255.255 netmask 255.255.255.255 dev %s" ,dev); netconf_system_if ("route",args); } if (ptr != routes) *ptr++ = ' '; ptr = stpcpy (ptr, ok ? "ok" : "missing"); } } *ptdev = '\0'; *ptr = '\0'; net_hint ("DEVICES",devices); net_hint ("ROUTES",routes); dhcp_createlease(); DAEMON_INTERNAL *dae = daemon_find ("dhcpd"); if (dae != NULL && dae->is_managed()){ char cmdargs[100]; sprintf (cmdargs,"%s 2>&1",devices); dae->setargs (cmdargs); const char *old_args = dae->getlastargs(); if (!f_dhcp.exist()){ ret = dae->stop(); dae->recordargs (NULL); }else if (old_args == NULL || strcmp(devices,old_args)!=0){ ret = dae->restart(); dae->recordargs (devices); net_hint ("STATUS","changes"); }else{ ret = dae->startif_file(f_dhcp); dae->recordargs (devices); } } } return ret; } int dhcp_probe() { int ret = 0; if (f_dhcp.exist()){ DHCP dh; if (!dh.error){ ret = dh.probe(); } } return ret; } PUBLIC void DHCP::editsub (DHCP_SUBNET *sub, bool isnew) { int ok = sub->edit(); if (ok >= 0){ if (ok == 1){ subnets.remove_del (sub); }else{ if (isnew) subnets.add (sub); } write (); }else if (isnew){ delete sub; } } PUBLIC void DHCP::locate (const char *net, bool setting) { bool found = false; for (int i=0; inetwork.cmp(net)==0){ editsub (sub,false); found = true; break; } } if (!found && setting){ DHCP_SUBNET *sub = new DHCP_SUBNET; editsub (sub,true); } } static void dhcp_locate (const char *net, bool setting) { DHCP dh; dh.locate (net,setting); } static void dhcp_editdefaults() { DHCP dh; if (dh.editdefaults()==0) dh.write(); } #include static REGISTER_VARIABLE_LOOKUP_MSG dhcp_var_list[]={ // Variables for the default {"serverid",NULL,P_MSG_R(F_IDENT),dhcp_editdefaults,NULL}, {"defleasetime",NULL,P_MSG_R(F_DEFLEASETIME),dhcp_editdefaults,NULL}, {"maxleasetime",NULL,P_MSG_R(F_MAXLEASETIME),dhcp_editdefaults,NULL}, {"upddnsdom",NULL,P_MSG_R(F_DEFDNSDOM),dhcp_editdefaults,NULL}, {"upddnscron",NULL,P_MSG_R(F_UPDDNS),dhcp_editdefaults,NULL}, {"dnsvalidname",NULL,P_MSG_R(I_VALIDNAMEONLY),dhcp_editdefaults,NULL}, // Variables common to the default and subnets {"routers",NULL,P_MSG_R(F_ROUTERS),dhcp_editdefaults,NULL}, {"subnet-mask",NULL,P_MSG_R(F_SUBNETMASK),dhcp_editdefaults,NULL}, {"time-offset",NULL,P_MSG_R(F_TIMEOFFSET),dhcp_editdefaults,NULL}, {"domain-name-servers",NULL,P_MSG_R(F_DNS),dhcp_editdefaults,NULL}, {"domain-name",NULL,P_MSG_R(F_DOMAIN),dhcp_editdefaults,NULL}, {"host-name",NULL,P_MSG_R(F_DHHOSTNAME),dhcp_editdefaults,NULL}, {"nis-domain",NULL,P_MSG_R(F_NISDOMAIN),dhcp_editdefaults,NULL}, {"nis-servers",NULL,P_MSG_R(F_NISSERVER),dhcp_editdefaults,NULL}, {"netbios-name-servers",NULL,P_MSG_R(F_WINS),dhcp_editdefaults,NULL}, {"netbios-node-type",NULL,P_MSG_R(F_NETBIOSNODETYPE),dhcp_editdefaults,NULL}, {"log-servers",NULL,P_MSG_R(F_LOGSERVERS),dhcp_editdefaults,NULL}, {"lpr-servers",NULL,P_MSG_R(F_LPRSERVERS),dhcp_editdefaults,NULL}, {"time-servers",NULL,P_MSG_R(F_TIMESERVERS),dhcp_editdefaults,NULL}, {"cookie-servers",NULL,P_MSG_R(F_COOKIESERVERS),dhcp_editdefaults,NULL}, // Variables for one subnet {"network",NULL,P_MSG_R(F_NETWORK),NULL,dhcp_locate}, {"netmask",NULL,P_MSG_R(F_NETMASK),NULL,dhcp_locate}, {"defleasetime",NULL,P_MSG_R(F_DEFLEASETIME),NULL,dhcp_locate}, {"maxleasetime",NULL,P_MSG_R(F_MAXLEASETIME),NULL,dhcp_locate}, {"dynbootp",NULL,P_MSG_R(F_DYNBOOTP),NULL,dhcp_locate}, {"startip",NULL,P_MSG_R(F_STARTIP),NULL,dhcp_locate}, {"stopip",NULL,P_MSG_R(F_STOPIP),NULL,dhcp_locate}, // The same variables are repeated for the subnet and the default {"routers",NULL,P_MSG_R(F_ROUTERS),NULL,dhcp_locate}, {"subnet-mask",NULL,P_MSG_R(F_SUBNETMASK),NULL,dhcp_locate}, {"time-offset",NULL,P_MSG_R(F_TIMEOFFSET),NULL,dhcp_locate}, {"domain-name-servers",NULL,P_MSG_R(F_DNS),NULL,dhcp_locate}, {"domain-name",NULL,P_MSG_R(F_DOMAIN),NULL,dhcp_locate}, {"host-name",NULL,P_MSG_R(F_DHHOSTNAME),NULL,dhcp_locate}, {"nis-domain",NULL,P_MSG_R(F_NISDOMAIN),NULL,dhcp_locate}, {"nis-servers",NULL,P_MSG_R(F_NISSERVER),NULL,dhcp_locate}, {"netbios-name-servers",NULL,P_MSG_R(F_WINS),NULL,dhcp_locate}, {"netbios-node-type",NULL,P_MSG_R(F_NETBIOSNODETYPE),NULL,dhcp_locate}, {"log-servers",NULL,P_MSG_R(F_LOGSERVERS),NULL,dhcp_locate}, {"lpr-servers",NULL,P_MSG_R(F_LPRSERVERS),NULL,dhcp_locate}, {"time-servers",NULL,P_MSG_R(F_TIMESERVERS),NULL,dhcp_locate}, {"cookie-servers",NULL,P_MSG_R(F_COOKIESERVERS),NULL,dhcp_locate}, { NULL, NULL, NULL, NULL } }; static REGISTER_VARIABLES host_registry1("dhcpd",dhcp_var_list);