server works too
authorKonrad Rosenbaum <konrad@silmor.de>
Sat, 3 Oct 2009 18:46:47 +0000 (18:46 +0000)
committerKonrad Rosenbaum <konrad@silmor.de>
Sat, 3 Oct 2009 18:46:47 +0000 (18:46 +0000)
missing: some allocation checks, esp. in server

git-svn-id: https://silmor.de/svn/misc/tdhcp@132 752dcaaa-03e5-0310-b894-97e397d490cd

message.c
server.c
sock.c

index 145344b..e308abf 100644 (file)
--- a/message.c
+++ b/message.c
@@ -17,6 +17,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <arpa/inet.h>
 
 /*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;l<opt->opt_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;l<opt->opt_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;l<opt->opt_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));
index 20f3693..cb4667b 100644 (file)
--- a/server.c
+++ b/server.c
 */
 
 #include "common.h"
+#include "sock.h"
+#include "message.h"
+
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <arpa/inet.h>
 
 /*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;i<MAXITEMS;i++)if(memcmp(&addresses[i],&NULLADDR,16)==0)break;addresscnt=i;
+       for(i=0;i<MAXITEMS;i++)if(memcmp(&prefixes[i],&NULLADDR,16)==0)break;prefixcnt=i;
+       for(i=0;i<MAXITEMS;i++)if(memcmp(&dnsservers[i],&NULLADDR,16)==0)break;dnsservercnt=i;
+       for(i=0;i<MAXITEMS;i++)if(dnsnames[i]==0)break;dnsnamecnt=i;
+}
+
+
+static int addaddr(struct in6_addr*list,const char*addr,const char*atype)
+{
+       int i;
+       struct in6_addr itm;
+       if(!inet_pton(AF_INET6,addr,&itm)){
+               td_log(LOGERROR,"while parsing %s \"%s\" - not a valid IPv6 address",atype,addr);
+               return -1;
+       }
+       /*do nothing if it is a null addr*/
+       if(memcmp(&itm,&NULLADDR,16)==0){
+               td_log(LOGWARN,"cannot add a null address (%s) as %s",addr,atype);
+               return -1;
+       }
+       /*go through list...*/
+       for(i=0;i<MAXITEMS;i++){
+               /*if the current one is a null addr, insert it here*/
+               if(memcmp(&list[i],&NULLADDR,16)==0){
+                       memcpy(&list[i],&itm,16);
+                       return i;
+               }
+               /*otherwise: check whether it is already known*/
+               if(memcmp(&list[i],&itm,16)==0)
+                       return i;
+       }
+       /*no free space*/
+       td_log(LOGWARN,"unable to add yet another %s (%s), a maximum of %i is allowed",atype,addr,MAXITEMS);
+       return -1;
+}
+
+static int addprefix(const char*pre)
+{
+       /*copy*/
+       char buf[1024],*p,*e;
+       int i,j;
+       strncpy(buf,pre,sizeof(buf));
+       /*find slash, get prefix length*/
+       p=strchr(buf,'/');
+       if(p){
+               *p++=0;
+               i=strtol(p,&e,10);
+               if(e && *e!=0){
+                       td_log(LOGERROR,"invalid prefix length in %s, ignoring it",pre);
+                       return -1;
+               }
+               if(i<1 || i>128){
+                       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;i<MAXITEMS;i++){
+               /*if current position is empty, insert here*/
+               if(!dnsnames[i]){
+                       dnsnames[i]=malloc(strlen(itm)+1);
+                       strcpy(dnsnames[i],itm);
+                       return i;
+               }
+               /*otherwise: check whether item is known*/
+               if(strcmp(dnsnames[i],itm)==0)
+                       return i;
+       }
+       /*no free space*/
+       td_log(LOGWARN,"unable to add yet another domain (%s) to the search list, a maximum of %i is allowed",itm,MAXITEMS);
+       return -1;
+}
+
+/*parse the response message and manipulate the send message*/
+static void handlemessage(struct dhcp_msg*rmsg)
+{
+       int i,j,p;
+       struct dhcp_msg*smsg;
+       /*create reply*/
+       if(rmsg->msg_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;i<dnsnamecnt;i++){
+                       smsg->msg_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;i<prefixcnt;i++){
+                       pref.opt_iaprefix.prefixlen=prefixlens[i];
+                       memcpy(&pref.opt_iaprefix.prefix,&prefixes[i],16);
+                       optappendopt(&smsg->msg_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;i<addresscnt;i++){
+                       memcpy(&addr.opt_iaaddress.addr,&addresses[i],16);
+                       optappendopt(&smsg->msg_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 (file)
--- 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);