From eeb1d2fd24daac1fb97a9732274af5deaa7d18cf Mon Sep 17 00:00:00 2001 From: Konrad Rosenbaum Date: Sat, 3 Oct 2009 18:46:47 +0000 Subject: [PATCH] server works too missing: some allocation checks, esp. in server git-svn-id: https://silmor.de/svn/misc/tdhcp@132 752dcaaa-03e5-0310-b894-97e397d490cd --- message.c | 34 ++++-- server.c | 348 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sock.c | 3 +- 3 files changed, 372 insertions(+), 13 deletions(-) diff --git a/message.c b/message.c index 145344b..e308abf 100644 --- a/message.c +++ b/message.c @@ -17,6 +17,7 @@ #include #include #include +#include /*increase allocation by ... entities*/ #define ALLOCINCR 8 @@ -309,11 +310,9 @@ static void encodeopt(struct dhcp_opt*opt,unsigned char*buf,int*pos,int max) encodeopt(&opt->subopt[l],buf,pos,max); break; case OPT_DNS_SERVER: - for(l=0;lopt_dns_server.num_dns;l++){ - *pos+=16; - if(*pos>max)return; - memcpy(&buf[*pos],&opt->opt_dns_server.addr[l],16); - } + *pos+=16*opt->opt_dns_server.num_dns; + if(*pos>max)return; + memcpy(buf+p+4,opt->opt_dns_server.addr,16*opt->opt_dns_server.num_dns); break; case OPT_DNS_NAME: for(l=0;lopt_dns_name.num_dns;l++){ @@ -334,10 +333,10 @@ static void encodeopt(struct dhcp_opt*opt,unsigned char*buf,int*pos,int max) case OPT_IAPREFIX: *pos+=25; if(*pos>max)return; - COPYINT4(buf+p,opt->opt_iaprefix.preferred_lifetime) - COPYINT4(buf+p+4,opt->opt_iaprefix.valid_lifetime) - buf[p+8]=opt->opt_iaprefix.prefixlen; - memcpy(buf+p+9,&opt->opt_iaprefix.prefix,16); + COPYINT4(buf+p+4,opt->opt_iaprefix.preferred_lifetime) + COPYINT4(buf+p+8,opt->opt_iaprefix.valid_lifetime) + buf[p+12]=opt->opt_iaprefix.prefixlen; + memcpy(buf+p+13,&opt->opt_iaprefix.prefix,16); /*sub-opts*/ for(l=0;lopt_numopts;l++) encodeopt(&opt->subopt[l],buf,pos,max); @@ -421,9 +420,9 @@ void sendmessage(struct dhcp_msg*msg) /*send*/ i=sendto(sockfd,buf,pos,0,(struct sockaddr*)&msg->msg_peer,sizeof(msg->msg_peer)); if(i<0) - td_log(LOGERROR,"unable to send message: %s",strerror(errno)); + td_log(LOGERROR,"unable to send message to %s: %s", inet_ntop(AF_INET6,&msg->msg_peer.sin6_addr,(char*)buf,sizeof(buf)), strerror(errno)); else{ - td_log(LOGDEBUG,"sent message of type %i, %i bytes",(int)msg->msg_type,pos); + td_log(LOGDEBUG,"sent message of type %i, %i bytes, to %s", (int)msg->msg_type, pos, inet_ntop(AF_INET6,&msg->msg_peer.sin6_addr,(char*)buf,sizeof(buf))); lastmsgid=msg->msg_id; } } @@ -658,21 +657,32 @@ static struct dhcp_msg* decodemessage(unsigned char*buf,int max) /*read a message from the line and return it (NULL on error)*/ struct dhcp_msg* readmessage() { - char buf[65536]; + char buf[65536],tmp[128]; int s; socklen_t p; struct sockaddr_in6 sa; struct dhcp_msg*ret; + unsigned char *llt; + /*receive*/ p=sizeof(sa); s=recvfrom(sockfd,buf,sizeof(buf),MSG_TRUNC,(struct sockaddr*)&sa,&p); + /*check message size*/ if(s<0){ td_log(LOGWARN,"error during read: %s",strerror(errno)); return 0; } + td_log(LOGDEBUG,"received message size %i from %s",s, inet_ntop(AF_INET6,&sa.sin6_addr,tmp,sizeof(tmp))); if(s>sizeof(buf)){ td_log(LOGWARN,"received oversized packet (%i bytes), ignoring it",s); return 0; } + /*check sender*/ + llt= (unsigned char*)&sa.sin6_addr; + if(llt[0]!=0xfe || (llt[1]&0xc0)!=0x80){ + td_log(LOGWARN,"received message from non-link-local sender, dropping it"); + return 0; + } + /*decode*/ td_log(LOGDEBUG,"read %i bytes, decoding now",s); ret=decodemessage((unsigned char*)buf,s); if(ret)memcpy(&ret->msg_peer,&sa,sizeof(sa)); diff --git a/server.c b/server.c index 20f3693..cb4667b 100644 --- a/server.c +++ b/server.c @@ -11,11 +11,359 @@ */ #include "common.h" +#include "sock.h" +#include "message.h" + +#include +#include +#include +#include +#include +#include /*side ID, allocated in server.c (0x00) and client.c (0x01) respectively*/ const unsigned char SIDEID=SIDE_SERVER; + +char shortopt[]="hl:p:a:d:D:u:L:"; +struct option longopt[]= { + {"local-id",1,0,'l'}, + {"log-level",1,0,'L'}, + {"prefix",1,0,'p'}, + {"address",1,0,'a'}, + {"dns-server",1,0,'d'}, + {"dns-name",1,0,'D'}, + {"rapid-commit",0,0,'c'}, + {"no-rapid-commit",0,0,'C'}, + {"retries",1,0,'r'}, + {"help",0,0,'h'}, + {"duid",1,0,'u'}, + {0,0,0,0} +}; + +#include "svnrev.h" +#define HELP \ + "Usage: %s [options] device\n" \ + "TDHCPc - Tunnel/Tiny DHCP server, revision " SVNREV "\n"\ + "(c) Konrad Rosenbaum, 2009\n"\ + "this program is protected under the GNU GPLv3 or at your option any newer\n"\ + "\n"\ + "TDHCP server parameters:\n"\ + " device: a network device (eg. eth0, ppp0, tun0)\n" \ + "\n"\ + "TDHCP server options:\n" \ + " -h | --help\n" \ + " displays this help text and exit\n" \ + \ + " -p prefix/length | --prefix=prefix/length\n" \ + " sets a prefix that is sent via prefix delegation\n" \ + \ + " -a addr | --address=addr\n" \ + " sets the address that is delegated to the client\n" \ + \ + " -d dnsaddr| --dns-server=dnsaddr\n -D domain| --dns-name=domain\n"\ + " sets the address of a DNS server (-d) or\n" \ + " sets a search domain name (-D) for the client\n" \ + \ + " -l ID | --local-id=ID\n" \ + " set the local ID from which the DUID is calculated\n" \ + \ + " -u DUID | --duid=DUID\n" \ + " set hex string as explicit DUID (overrides -l)\n" \ + \ + " -L level | --log-level=level\n" \ + " set the log level (default is warn), must be one of:\n" \ + " none, error, warn, info, debug\n" + +static char*argv0=0,*localid=0,*device=0; + +/*output the help text*/ +static void printhelp() +{ + fprintf(stderr,HELP,argv0); +} + + +/*maximum amount of any item that we can handle: 16 is sensitive for addresses, prefixes and DNS settings*/ +#define MAXITEMS 16 + +static struct in6_addr addresses[MAXITEMS], prefixes[MAXITEMS], dnsservers[MAXITEMS]; +static char **dnsnames; +static unsigned char prefixlens[MAXITEMS]; +static struct in6_addr NULLADDR; +static int addresscnt=0,prefixcnt=0,dnsservercnt=0,dnsnamecnt=0; + +static void inititems() +{ + dnsnames=malloc(MAXITEMS*sizeof(char*)); + memset(dnsnames,0,MAXITEMS*sizeof(char*)); + memset(addresses,0,16*MAXITEMS); + memset(prefixes,0,16*MAXITEMS); + memset(dnsservers,0,16*MAXITEMS); + memset(prefixlens,0,MAXITEMS); + memset(&NULLADDR,0,16); +} + +static void countitems() +{ + int i; + for(i=0;i128){ + td_log(LOGERROR,"prefix length must be between 1<=pl<=128 in %s, ignoring it",pre); + return -1; + } + }else{ + td_log(LOGWARN,"no prefix length in prefix %s, assuming /64",pre); + i=64; + } + /*add prefix*/ + j=addaddr(prefixes,buf,"prefix"); + if(j>=0)prefixlens[j]=i; + return j; +} + +static int adddomain(const char*itm) +{ + int i; + /*check for null items*/ + if(!itm)return -1; + if(*itm==0)return -1; + /*go through list...*/ + for(i=0;imsg_type==MSG_SOLICIT) + smsg=newmessage(MSG_ADVERTISE); + else + smsg=newmessage(MSG_REPLY); + /*copy...*/ + smsg->msg_id=rmsg->msg_id; + memcpy(&smsg->msg_peer,&rmsg->msg_peer,sizeof(rmsg->msg_peer)); + messageaddopt(smsg,OPT_SERVERID); + p=messagefindoption(rmsg,OPT_CLIENTID); + if(p>=0) + messageappendopt(smsg,&rmsg->msg_opt[p]); + if(messagefindoption(rmsg,OPT_RAPIDCOMMIT)>=0) + messageaddopt(smsg,OPT_RAPIDCOMMIT); + /*find DNS info*/ + if(dnsservercnt && messagehasoptionrequest(rmsg,OPT_DNS_SERVER)){ + p=messageaddopt(smsg,OPT_DNS_SERVER); + smsg->msg_opt[p].opt_dns_server.num_dns=dnsservercnt; + smsg->msg_opt[p].opt_dns_server.addr=malloc(dnsservercnt*sizeof(struct in6_addr)); + memcpy(smsg->msg_opt[p].opt_dns_server.addr,dnsservers,dnsservercnt*sizeof(struct in6_addr)); + } + if(dnsnamecnt && messagehasoptionrequest(rmsg,OPT_DNS_NAME)){ + p=messageaddopt(smsg,OPT_DNS_NAME); + smsg->msg_opt[p].opt_dns_name.num_dns=dnsnamecnt; + smsg->msg_opt[p].opt_dns_name.namelist=malloc(dnsnamecnt*sizeof(char*)); + for(i=0;imsg_opt[p].opt_dns_name.namelist[i]=malloc(strlen(dnsnames[i])+1); + strcpy(smsg->msg_opt[p].opt_dns_name.namelist[i],dnsnames[i]); + } + } + /*find PREFIX info*/ + if(prefixcnt && (j=messagefindoption(rmsg,OPT_IAPD))>=0){ + struct dhcp_opt pref; + memset(&pref,0,sizeof(pref)); + /*create opt, copy IAID*/ + p=messageaddopt(smsg,OPT_IAPD); + smsg->msg_opt[p].opt_iapd.iaid=rmsg->msg_opt[j].opt_iapd.iaid; + /*insert prefixes*/ + pref.opt_type=OPT_IAPREFIX; + pref.opt_iaprefix.preferred_lifetime=0xffffffff; + pref.opt_iaprefix.valid_lifetime=0xffffffff; + for(i=0;imsg_opt[p],&pref); + } + } + /*find IANA info*/ + if(addresscnt && (j=messagefindoption(rmsg,OPT_IANA))>=0){ + struct dhcp_opt addr; + memset(&addr,0,sizeof(addr)); + /*create opt, copy IAID*/ + p=messageaddopt(smsg,OPT_IANA); + smsg->msg_opt[p].opt_iana.iaid=rmsg->msg_opt[j].opt_iana.iaid; + /*insert prefixes*/ + addr.opt_type=OPT_IAADDR; + addr.opt_iaaddress.preferred_lifetime=0xffffffff; + addr.opt_iaaddress.valid_lifetime=0xffffffff; + for(i=0;imsg_opt[p],&addr); + } + } + /*free received msg*/ + freemessage(rmsg); + /*send*/ + sendmessage(smsg); + /*free sent msg*/ + freemessage(smsg); +} + +/*main loop, message sender, etc.pp.*/ int main(int argc,char**argv) { + int c,optindex=1; + /*init my own stuff*/ + inititems(); + /*parse options*/ + argv0=*argv; + while(1){ + c=getopt_long(argc,argv,shortopt,longopt,&optindex); + if(c==-1)break; + switch(c){ + case 'p':addprefix(optarg);break; + case 'a':addaddr(addresses,optarg,"address");break; + case 'd':addaddr(dnsservers,optarg,"DNS server address");break; + case 'D':adddomain(optarg);break; + case 'l':localid=optarg;break; + case 'u':setduid(optarg);break; + case 'L':setloglevel(optarg);break; + default: + fprintf(stderr,"Syntax error in arguments.\n"); + printhelp(); + return 1; + break; + case 'h': + printhelp(); + return 0; + break; + } + } + if((optind+1)!=argc){ + fprintf(stderr,"Syntax error.\n"); + printhelp(); + return 1; + } + device=argv[optind]; + + if(DUIDLEN==0){ + if(localid) + setlocalid(localid); + else + initlocalid(); + } + /*count my options*/ + countitems(); + /*init socket*/ + initsocket(DHCP_SERVERPORT,device); + if(sockfd<0){ + td_log(LOGERROR,"unable to allocate socket, exiting."); + return 1; + } + joindhcp(); + if(sockfd<0){ + td_log(LOGERROR,"unable to joind DHCP multicast group, exiting."); + return 1; + } + /*init filter*/ + clearrecvfilter(); + addrecvfilter(MSG_SOLICIT); + addrecvfilter(MSG_REQUEST); + addrecvfilter(MSG_IREQUEST); + /*start main loop*/ + while(1){ + fd_set rfd,xfd; + int sret; + //wait for event + FD_ZERO(&rfd); + FD_ZERO(&xfd); + FD_SET(sockfd,&rfd); + FD_SET(sockfd,&xfd); + sret=select(sockfd+1,&rfd,0,&xfd,0); + //check for errors + if(sret<0){ + int e=errno; + if(e==EAGAIN)continue; + td_log(LOGERROR,"Error caught: %s\n",strerror(e)); + return 1; + } + //check for event + if(sret>0){ + if(FD_ISSET(sockfd,&rfd)){ + struct dhcp_msg*msg2; + msg2=readmessage(); + if(msg2) + handlemessage(msg2); + } + if(FD_ISSET(sockfd,&xfd)){ + td_log(LOGERROR,"Exception on socket caught.\n"); + return 1; + } + } + } + /*should not be reachable*/ + td_log(LOGDEBUG,"hmm, Konrad needs better coffee - this line should not be reachable"); return 0; } diff --git a/sock.c b/sock.c index 2790dc7..718c746 100644 --- a/sock.c +++ b/sock.c @@ -98,7 +98,8 @@ void initsocket(short port,const char*dev) sa.sin6_family=AF_INET6; sa.sin6_port=htons(port); sa.sin6_scope_id=ifindex; - get_if_lladdr(dev,&sa); + if(SIDEID!=SIDE_SERVER) + get_if_lladdr(dev,&sa); if(bind(sockfd,(struct sockaddr*)&sa,sizeof(sa))<0){ td_log(LOGERROR,"Error binding socket: %s.\n",strerror(errno)); close(sockfd); -- 1.7.2.5