From f225465dfc88d548c19b70373f9198d9840f172c Mon Sep 17 00:00:00 2001 From: "paul.gardner.stephen" Date: Tue, 13 Jul 2010 12:15:46 +0000 Subject: [PATCH] Initial check in git-svn-id: http://serval-dna.googlecode.com/svn/trunk/trunk/dna@2 6d01b88e-114a-4e5d-930d-818638e25ad7 --- Makefile | 36 +++ Makefile.in | 36 +++ TODO | 38 +++ batman.c | 141 +++++++++++ ciphers.c | 26 ++ client.c | 604 +++++++++++++++++++++++++++++++++++++++++++++++ configure.in | 14 ++ dataformats.c | 191 +++++++++++++++ dna.c | 351 +++++++++++++++++++++++++++ hlrdata.c | 437 ++++++++++++++++++++++++++++++++++ meshpotato_build | 1 + mphlr.h | 319 +++++++++++++++++++++++++ openwrt-makefile | 31 +++ packetformats.c | 518 ++++++++++++++++++++++++++++++++++++++++ peers.c | 126 ++++++++++ responses.c | 104 ++++++++ server.c | 382 ++++++++++++++++++++++++++++++ simulate.c | 41 ++++ srandomdev.c | 77 ++++++ testdna | 315 ++++++++++++++++++++++++ 20 files changed, 3788 insertions(+) create mode 100644 Makefile create mode 100644 Makefile.in create mode 100644 TODO create mode 100644 batman.c create mode 100644 ciphers.c create mode 100644 client.c create mode 100644 configure.in create mode 100644 dataformats.c create mode 100644 dna.c create mode 100644 hlrdata.c create mode 100644 meshpotato_build create mode 100644 mphlr.h create mode 100644 openwrt-makefile create mode 100644 packetformats.c create mode 100644 peers.c create mode 100644 responses.c create mode 100644 server.c create mode 100644 simulate.c create mode 100644 srandomdev.c create mode 100755 testdna diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..7b5f0e3b --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +OBJS= dna.o server.o client.o peers.o ciphers.o responses.o packetformats.o dataformats.o hlrdata.o srandomdev.o simulate.o batman.o +HDRS= Makefile mphlr.h +LDFLAGS= +DEFS= -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE_URL=\"\" -DHAVE_LIBC=1 -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_STDIO_H=1 -DHAVE_ERRNO_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRINGS_H=1 -DHAVE_UNISTD_H=1 -DHAVE_STRING_H=1 -DHAVE_ARPA_INET_H=1 -DHAVE_SYS_SOCKET_H=1 -DHAVE_SYS_MMAN_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_POLL_H=1 -DHAVE_NETDB_H=1 + +all: dna + +%.o: %.c $(HDRS) + $(CC) $(DEFS) -Os -g -Wall -c $< + +dna: $(OBJS) + $(CC) -Os -g -Wall -o dna $(OBJS) $(LDFLAGS) + +testserver: dna + clear + rm hlr.dat + ./dna -vvv -S 1 -f hlr.dat + +testcreate: dna + clear + ./dna -vvv -d 0427679796 -C + @touch testcreate + +testget: dna testcreate + clear + ./dna -vvv -d 0427679796 -R dids | tee testget + +testset: dna testget + clear + # Try writing a value to a variable + echo "short value" >shortvalue.txt + ./dna -vvv -s `cat testget | cut -f2 -d: | tail -1` -i 0 -W note=@shortvalue.txt + +testbigset: testget + clear + ./dna -vvv -s `cat testget | cut -f2 -d: | tail -1` -i 0 -W note=@411.txt diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 00000000..bb73de70 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,36 @@ +OBJS= dna.o server.o client.o peers.o ciphers.o responses.o packetformats.o dataformats.o hlrdata.o srandomdev.o simulate.o batman.o +HDRS= Makefile mphlr.h +LDFLAGS= @LDFLAGS@ +DEFS= @DEFS@ + +all: dna + +%.o: %.c $(HDRS) + $(CC) $(DEFS) -Os -g -Wall -c $< + +dna: $(OBJS) + $(CC) -Os -g -Wall -o dna $(OBJS) $(LDFLAGS) + +testserver: dna + clear + rm hlr.dat + ./dna -vvv -S 1 -f hlr.dat + +testcreate: dna + clear + ./dna -vvv -d 0427679796 -C + @touch testcreate + +testget: dna testcreate + clear + ./dna -vvv -d 0427679796 -R dids | tee testget + +testset: dna testget + clear + # Try writing a value to a variable + echo "short value" >shortvalue.txt + ./dna -vvv -s `cat testget | cut -f2 -d: | tail -1` -i 0 -W note=@shortvalue.txt + +testbigset: testget + clear + ./dna -vvv -s `cat testget | cut -f2 -d: | tail -1` -i 0 -W note=@411.txt diff --git a/TODO b/TODO new file mode 100644 index 00000000..230be036 --- /dev/null +++ b/TODO @@ -0,0 +1,38 @@ +Pending: + +- Parallelise large-value reads and writes +- Fix out-of-order problem for writing large values to stdout (file is fine) +- Non-mmap() mode for Mesh Potato and JFFS +- Return early if all peers have responded (what about broadcast address virtual-peers?) +- Activation flag in HLR records so that create without further action does not make a completed record. +- async mode: fork()/background after first response with file output (for pipelining with MP IVRs) +- async mode: work with broadcast requests +- async mode: work with instance=-1 requests +- SID based Peer targeting, e.g., for writeItem +- cache peer:SID mappings to allow later unicasting +- consider NACK for peers with no results to return + +Done: +20100623 - testdna script now uses /bin/sh instead of /bin/csh facilitating testing on more embedded targets +20100623 - Retries for requests (already deals with SET, including multi-packet SET?) +20100623 - Multi-packet GET retry policy (okay, it is as bad as TFTP with retries, but that is probably okay for now, + at least we have the potential to pipe-line) +20100623 - Multi-packet set doesn't send multiple packets (ok to confirm each in turn) +20100623 - Output to file confirmed functional +20100623 - BATMAN peer list retrieval +20100623 - excluding previous responders from re-broadcast requests +20100611 - Added code for template based file output when reading variables +20100611 - Added code to allow user specified peers +20100601 - Space retries properly in time, and allow multiple recvfroms() per retry. +20100601 - Do not request results from peers that have already responsed. +20100601 - Ensure broadcasts do not result in duplicate identical results +20100601 - broadcast get: compulsory retry regime +20100531 - Simulated packet loss / BER for server for testing. +20100531 - "response set" structure for efficient chaining of responses, as well as making it easier to manage retries +20100529 - SET working (replace and noreplace modes) +20100529 - Get rid of unnecessary timeouts +20100529 - Regression test script +20100529 - Batch-mode responses (for instance=-1) +20100529 - Multi-instance requests now working (and with no unnecessary delays) +20100529 - Search by SID now works +20100529 - Reading all instances of a variable by SID without timeout, but actually reading them all diff --git a/batman.c b/batman.c new file mode 100644 index 00000000..5aaa360a --- /dev/null +++ b/batman.c @@ -0,0 +1,141 @@ +/* +Serval Distributed Numbering Architecture (DNA) +Copyright (C) 2010 Paul Gardner-Stephen + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "mphlr.h" + + +int getBatmanPeerList(char *socket_path,in_addr_t peers[],int *peer_count,int peer_max) +{ + int sock; + struct sockaddr_un socket_address; + unsigned char buf[16384]; /* big enough for a gigabit jumbo frame or loopback godzilla-gram */ + int ofs=0; + int bytes=0; + struct pollfd fds; + char cmd[30]; + int notDone=1; + int res; + int tries=0; + + askagain: + + /* Make socket */ + sock=socket(AF_LOCAL,SOCK_STREAM,0); + memset(&socket_address,0,sizeof(struct sockaddr_un)); + socket_address.sun_family=AF_LOCAL; + if (strlen(socket_path)>256) return setReason("BATMAN socket path too long"); + strcpy(socket_address.sun_path,socket_path); + + /* Connect the socket */ + if (connect(sock,(struct sockaddr*)&socket_address,sizeof(socket_address))<0) + return setReason("connect() to BATMAN socket failed."); + + memset(&cmd[0],0,30); + snprintf(cmd,30,"d:%c",1); + if (write(sock,cmd,30)!=30) + { close(sock); return setReason("write() command failed to BATMAN socket."); } + + fds.fd=sock; + fds.events=POLLIN; + + getmore: + + while(notDone) + { + switch (poll(&fds,1,1500)) { + case 1: /* Excellent - we have a response */ break; + case 0: if (debug>1) fprintf(stderr,"BATMAN did not respond to peer enquiry.\n"); + close(sock); + if (tries++<=3) goto askagain; + return setReason("No response from BATMAN."); + default: /* some sort of error, e.g., lost connection */ + close(sock); + return setReason("poll() of BATMAN socket failed."); + } + + res=read(sock,&buf[bytes],16383-bytes); close(sock); + ofs=0; + if (res<1) { + if (bytes) + { + /* Got a partial response, then a dead line. + Should probably ask again unless we have tried too many times. + */ + if (debug>2) fprintf(stderr,"Trying again after cold drop.\n"); + close(sock); + bytes=0; + if (tries++<=3) goto askagain; + else return setReason("failed to read() from BATMAN socket (too many tries)."); + } + return setReason("failed to read() from BATMAN socket."); + } + if (!res) return 0; + if (debug>1) fprintf(stderr,"BATMAN has responded with %d bytes.\n",res); + + if (debug>2) dump("BATMAN says",&buf[bytes],res); + + if (res<80 /*||buf[bytes]!='B' -- turns out we can't rely on this, either */ + ||buf[bytes+res-1]!=0x0a||buf[bytes+res-4]!='E') + { + /* Jolly BATMAN on Android sometimes sends us bung packets from time to + time. Sometimes it is fragmenting, other times it is just plain + odd. + If this happens, we should just ask again. + We should also try to reassemble fragments. + + Sometimes we get cold-drops and resumes, too. Yay. + */ + if (buf[bytes+res-4]!='E') { + /* no end marker, so try adding record to the end. */ + if (debug>2) fprintf(stderr,"Data has no end marker, accumulating.\n"); + bytes+=res; + goto getmore; + } + close(sock); + goto askagain; + } + bytes+=res; + + while(ofs1) fprintf(stderr,"New line @ %d\n",ofs); + /* Check for IP address of peers */ + if (isdigit(buf[ofs])) + { + int i; + for(i=0;ofs+i1) fprintf(stderr,"Found BATMAN peer '%s'\n",&buf[ofs]); + buf[ofs+i]=' '; + break; + } + } + /* Check for end of transmission */ + if (buf[ofs]=='E') { notDone=0; } + + /* Skip to next line */ + while(buf[ofs]>=' '&&(ofs1) fprintf(stderr,"Sent request to client %s\n",inet_ntoa(destination)); + } + return 0; +} + +int packetSendRequest(int method,unsigned char *packet,int packet_len,int batchP, + unsigned char *transaction_id,struct response_set *responses) +{ + int i; + int cumulative_timeout=0; /* ms */ + int this_timeout=125; /* ms */ + int peer_low,peer_high; + + struct timeval time_in,now; + + /* Prepare ephemeral UDP socket (hence no binding) + If in server mode, then we already have a socket available to us and appropriately bound */ + if (!serverMode) { + sock=socket(PF_INET,SOCK_DGRAM,0); + if (sock<0) { + fprintf(stderr,"Could not create UDP socket.\n"); + exit(-3); + } + } + + /* Deal with special case */ + if (method==REQ_REPLY) + { + int r=sendto(sock,packet,packet_len,0,(struct sockaddr *)&recvaddr,sizeof(recvaddr)); + if (r1) fprintf(stderr,"Sent request to client %s\n",inet_ntoa(client_addr)); + } + return 0; + } + + getPeerList(); + + gettimeofday(&time_in,NULL); + + /* REQ_SERIAL & REQ_PARALLEL work in fundamentally different ways, + but it turns out the retry/timeout code is the dominant part. + So we do a bit of fiddling around to make one loop that can handle both */ + if (method==REQ_SERIAL) { + peer_low=0; peer_high=peer_count-1; + /* If there are too many peers to allow sending to each three times, then we should + adjust our incremental timeout accordingly, so far as is practicable */ + if (this_timeout*peer_count*3>timeout) + { + this_timeout=timeout/(3*peer_count); + if (this_timeout<10) this_timeout=10; /* 10ms minimum sending interval */ + } + } else + { peer_low=-1; peer_high=-1;} + + while(cumulative_timeout<=timeout) + { + /* If not in serial mode, then send request to everyone immediately. + Make sure we only ask once in parallel mode, since it will always ask everyone */ + if (method==REQ_PARALLEL) sendToPeers(packet,packet_len,method,0,responses); + else if (method!=REQ_SERIAL) + for(i=0;i-1) sendToPeers(packet,packet_len,REQ_SERIAL,i,responses); + + /* Placing the timeout calculation here means that the total timeout is shared among + all peers in a serial request, but round-robining after each time-step. + We adjust this_timeout if there are many peers to allow 3 sends to each peer where possible. + */ + cumulative_timeout+=this_timeout; + int timeout_remaining=this_timeout; + + while(1) + { + /* Wait for response */ + int r=getReplyPackets(method,i,batchP,responses,transaction_id,timeout_remaining); + if (r&&debug>1) fprintf(stderr,"getReplyPackets(): Returned on timeout\n"); + + switch(method) + { + case REQ_PARALLEL: + /* XXX We could stop once all peers have replied. + (need to update the test script if we do that, so that it tests with multiple + peers and so tests that we wait if not all peers have responded) */ + break; + case REQ_FIRSTREPLY: + if (debug>1) fprintf(stderr,"Returning with first reply (REQ_FIRSTREPLY)\n"); + if (!r) return 0; + break; + case REQ_SERIAL: + if (!r) { + /* Stop if we have an affirmative response. + XXX - doesn't allow for out of order replies. */ + if (debug>1) dumpResponses(responses); + rr=responses->last_response; + while (rr) + { + if (rr->checked) break; + if (debug>1) + fprintf(stderr,"Got a response code 0x%02x, checking if that is what we need.\n",rr->code); + switch (rr->code) + { + case ACTION_OKAY: case ACTION_DATA: + /* bingo */ + if (!batchP) return 0; + break; + } + rr->checked=1; + rr=rr->prev; + } + + /* not what we are after, so clear response and try with next peer */ + clearResponses(responses); + } + break; + } + + /* Wait for the previous timeout to really expire, + (this is for the case where all peers have replied) */ + { + int elapsed_usecs=0; + int cumulative_usecs=cumulative_timeout*1000; + int remaining_usecs; + + gettimeofday(&now,NULL); + elapsed_usecs=(now.tv_sec-time_in.tv_sec)*1000000; + elapsed_usecs+=(now.tv_usec-time_in.tv_usec); + + remaining_usecs=cumulative_usecs-elapsed_usecs; + + if (remaining_usecs<=0) break; + else timeout_remaining=remaining_usecs/1000; + } + + } + } + cumulative_timeout+=this_timeout; + } + if (debug>1) if (cumulative_timeout>=timeout) + fprintf(stderr,"Request timed out after retries (timeout=%d, elapsed=%d).\n", + timeout,cumulative_timeout); + + return 0; +} + +/* Create a new HLR entry on a peer. + Should always try localhost as first peer, which is why + sendToPeers() does it that way. We just have to remember to + ask for serialised attempts, rather than all at once. +*/ +int requestNewHLR(char *did,char *pin,char *sid) +{ + unsigned char packet[8000]; + int packet_len=0; + struct response_set responses; + unsigned char transaction_id[8]; + + bzero(&responses,sizeof(responses)); + + /* Prepare the request packet */ + if (packetMakeHeader(packet,8000,&packet_len,NULL)) return -1; + bcopy(&packet[8],transaction_id,8); + if (packetSetDid(packet,8000,&packet_len,did)) return -1; + if (packetAddHLRCreateRequest(packet,8000,&packet_len)) return -1; + if (packetFinalise(packet,8000,&packet_len)) return -1; + + /* Send it to peers, starting with ourselves, one at a time until one succeeds. + XXX - This could take a while if we have long timeouts for each. */ + if (packetSendRequest(REQ_SERIAL,packet,packet_len,NONBATCH,transaction_id,&responses)) return -1; + + /* Extract response */ + if (debug>2) dumpResponses(&responses); + if (!responses.response_count) { + printf("NOREPLY\n"); + return -1; + } + switch(responses.responses->code) + { + case ACTION_DECLINED: + printf("DECLINED\n"); + return -1; + break; + case ACTION_OKAY: + { + char sid[128]; + int ofs=0; + extractSid(&responses.responses->sid[0],&ofs,&sid[0]); + printf("OK:%s\n",sid); + } + return 0; + break; + default: + printf("ERROR:Unknown response 0x%02x\n",responses.responses->code); + return -1; + } + + return setReason("Request creation of new HLR not implemented"); +} + +int getReplyPackets(int method,int peer,int batchP, + struct response_set *responses, + unsigned char *transaction_id,int timeout) +{ + /* set timeout alarm */ + + /* get packets until timeout, or until we get a packet from the specified peer + if method==REQ_SERIAL. If REQ_SERIAL we also reject packets from other + senders as they must be spoofs. + */ + struct timeval t; + int timeout_secs; + int timeout_usecs; + int to=timeout; + + if (debug>1) printf("getReplyPackets(policy=%d)\n",method); + + /* Work out when the timeout will expire */ + gettimeofday(&t,NULL); + timeout_secs=t.tv_sec; timeout_usecs=t.tv_usec; + if (to>1000) { timeout_secs+=(to/1000); to=to%1000; } + timeout_usecs+=to*1000; if (timeout_usecs>1000000) { timeout_secs++; timeout_usecs-=1000000; } + + while(1) { + unsigned char buffer[16384]; + socklen_t recvaddrlen=sizeof(recvaddr); + struct pollfd fds; + client_port=((struct sockaddr_in*)&recvaddr)->sin_port; + bzero((void *)&recvaddr,sizeof(recvaddr)); + fds.fd=sock; fds.events=POLLIN; + while (poll(&fds,1,10 /* wait for 10ms at a time */)<1) + { + gettimeofday(&t,NULL); + if (t.tv_sec>timeout_secs) return 1; + if (t.tv_sec==timeout_secs&&t.tv_usec>=timeout_usecs) return 1; + } + client_port=((struct sockaddr_in*)&recvaddr)->sin_port; + int len=recvfrom(sock,buffer,sizeof(buffer),0,&recvaddr,&recvaddrlen); + client_addr=((struct sockaddr_in*)&recvaddr)->sin_addr; + if (debug) fprintf(stderr,"Received reply from %s (len=%d).\n",inet_ntoa(client_addr),len); + if (debug>1) dump("recvaddr",(unsigned char *)&recvaddr,recvaddrlen); + if (debug>2) dump("packet",(unsigned char *)buffer,len); + if (dropPacketP(len)) { + if (debug) fprintf(stderr,"Simulation mode: Dropped packet due to simulated link parameters.\n"); + continue; + } + if (!packetOk(buffer,len,transaction_id)) { + /* Packet passes tests - extract responses and append them to the end of the response list */ + if (extractResponses(client_addr,buffer,len,responses)) + return setReason("Problem extracting response fields from reply packets"); + if (method==REQ_SERIAL||method==REQ_FIRSTREPLY) { + if (!batchP) return 0; + /* In batch mode we need ACTION_DONE to mark end of transmission. + While it gets sent last, out-of-order delivery means we can't rely on + such a nice arrangement. */ + { + /* XXX inefficient for long lists. + XXX can be made better by working backwards from end using double-linked list and + remembering the previous length of the list */ + struct response *r=responses->responses; + while(r) + { + if (r->code==ACTION_DONE) return 0; + r=r->next; + } + } + } + else { + if (debug>1) printf("Waiting for more packets, since called with policy %d\n",method); + } + } else { + if (debug) setReason("Ignoring invalid packet"); + } + } +} + +int writeItem(char *sid,int var_id,int instance,unsigned char *value, + int value_start,int value_length,int flags) +{ + unsigned char packet[8000]; + int packet_len=0; + struct response_set responses; + struct response *r; + unsigned char transaction_id[8]; + + bzero(&responses,sizeof(responses)); + + if (debug>1) fprintf(stderr,"Writing %d bytes of var %02x/%02x @ 0x%d flags=%d\n", + value_length,var_id,instance,value_start,flags); + + if (!sid) { + printf("ERROR:Must use SID when writing values.\n"); + return -1; + } + + /* Split long writes into many short writes. + (since each write is acknowledged, we don't have to worry about batch mode) */ + if (value_length-value_start>MAX_DATA_BYTES) + { + int o; + if (debug) fprintf(stderr,"Writing large value (%d bytes)\n",value_length-value_start); + for(o=value_start;ovalue_length) bytes=value_length-o; + if (debug>1) fprintf(stderr," writing [%d,%d)\n",o,o+bytes-1); + if (writeItem(sid,var_id,instance,&value[o-value_start],o,bytes, + flags|((o>value_start)?SET_FRAGMENT:0))) + { + if (debug) fprintf(stderr," - writing installment failed\n"); + return setReason("Failure during multi-packet write of long-value"); + } + } + printf("OK:%s\n",sid); + return 0; + } + + /* Prepare the request packet */ + if (packetMakeHeader(packet,8000,&packet_len,NULL)) return -1; + bcopy(&packet[8],transaction_id,8); + if (packetSetSid(packet,8000,&packet_len,sid)) return -1; + if (packetAddVariableWrite(packet,8000,&packet_len,var_id,instance, + value,value_start,value_length,flags)) return -1; + if (packetFinalise(packet,8000,&packet_len)) return -1; + + /* XXX should be able to target to the peer holding the SID, if we have it. + In any case, we */ + if (packetSendRequest(REQ_FIRSTREPLY,packet,packet_len,NONBATCH,transaction_id,&responses)) return -1; + + r=responses.responses; + while(r) + { + int slen; + char sid[SID_SIZE*2+1]; + extractSid(r->sid,&slen,sid); + switch(r->code) + { + case ACTION_ERROR: + /* We allocate an extra byte to allow us to do this */ + r->response[r->response_len]=0; + printf("ERROR:%s\n",(char *)r->response); + break; + case ACTION_OKAY: printf("ERROR:Unexpected OK response\n"); break; + case ACTION_DECLINED: printf("DECLINED:%s\n",sid); break; + case ACTION_WROTE: + /* Supress success messages when writing fragments */ + if (!(flags&SET_FRAGMENT)) printf("WROTE:%s\n",sid); break; + case ACTION_DATA: printf("ERROR:DATA reponse not implemented\n"); break; + case ACTION_GET: printf("ERROR:You cant respond with GET\n"); break; + case ACTION_SET: printf("ERROR:You cant respond with SET\n"); break; + case ACTION_DEL: printf("ERROR:You cant respond with DEL\n"); break; + case ACTION_CREATEHLR: printf("ERROR:You cant respond with CREATEHLR\n"); break; + case ACTION_PAD: /* ignore it */ break; + case ACTION_EOT: /* ignore it */ break; + default: printf("ERROR:Unexpected response code 0x%02x\n",r->code); + } + fflush(stdout); + r=r->next; + } + + return 0; +} + +int requestItem(char *did,char *sid,char *item,int instance,unsigned char *buffer,int buffer_length,int *len, + unsigned char *transaction_id) +{ + unsigned char packet[8000]; + int packet_len=0; + struct response *r; + struct response_set responses; + + bzero(&responses,sizeof(responses)); + + /* Prepare the request packet */ + if (packetMakeHeader(packet,8000,&packet_len,transaction_id)) return -1; + if (did&&(!sid)) + { if (packetSetDid(packet,8000,&packet_len,did)) return -1; } + else if (sid&&(!did)) + { if (packetSetSid(packet,8000,&packet_len,sid)) return -1; } + else return setReason("You must request items by DID or SID, not neither, nor both"); + + + if (packetAddVariableRequest(packet,8000,&packet_len, + item,instance,0,buffer_length)) return -1; + if (packetFinalise(packet,8000,&packet_len)) return -1; + + int method=REQ_PARALLEL; + if (sid) method=REQ_FIRSTREPLY; + if (packetSendRequest(method,packet,packet_len,(instance==-1)?BATCH:NONBATCH,transaction_id,&responses)) return -1; + + r=responses.responses; + while(r) + { + char sid[SID_SIZE*2+1]; + int slen=0; + extractSid(r->sid,&slen,sid); + switch(r->code) + { + case ACTION_OKAY: printf("OK:%s\n",sid); break; + case ACTION_DECLINED: printf("DECLINED:%s\n",sid); break; + case ACTION_DATA: + /* Display data. + The trick is knowing the format of the data. + Fortunately we get the variable id etc. + XXX Need to deal with fragmented values. + (perhaps the fragments should get auto-assembled when accepting the responses) + */ + switch(r->var_id) + { + case VAR_DIDS: + { + char did[DID_MAXSIZE+1]; + int dlen=0; + did[0]=0; + extractDid(r->response,&dlen,did); + printf("DIDS:%s:%d:%s\n",sid,r->var_instance,did); + } + break; + case VAR_NOTE: + default: + /* ASCII formatted variable types */ + { + int v=0; + int i=0; + FILE *outputfile=stdout; + while(vars[v].name&&vars[v].id!=r->var_id) v++; + if (!vars[v].id) printf("0x%02x",r->var_id); + while(vars[v].name[i]) fputc(toupper(vars[v].name[i++]),stdout); + printf(":%s:%d:",sid,r->var_instance); + + if (outputtemplate) + { + char outputname[8192]; + snprintf(outputname,8192,outputtemplate,sid,r->var_id,r->var_instance); + outputfile=fopen(outputname,"w"); + if (!outputfile) printf("ERROR:Could not open output file '%s'",outputname); + if (debug) fprintf(stderr,"Writing output to '%s'\n",outputname); + } + + if (outputfile) fwrite(r->response,r->value_bytes,1,outputfile); + + if (r->value_bytesvalue_len) + { + /* Partial response, so ask for the rest of it */ + unsigned char packet[8000]; + int packet_len=0; + struct response *rr; + struct response_set responses; + int offset,max_bytes; + int recv_map[1+(r->value_len/MAX_DATA_BYTES)]; + int recv_map_size=1+(r->value_len/MAX_DATA_BYTES); + int needMoreData; + int tries=0; + + /* work out EXACTLY how many installments we need */ + while (((recv_map_size-1)*MAX_DATA_BYTES)>=r->value_len) recv_map_size--; + + recv_map[0]=0; /* we received the first installment, so mark it off ... */ + /* ... but we haven't received the rest */ + for(i=1;i1) fprintf(stderr,"Multi-packet request: try %d, %d fragments remaining.\n",tries,needMoreData); + needMoreData=0; + for(i=0;i1) fprintf(stderr,"Asking for variable segment @ offset %d\n",offset); + + /* Send accumulated request direct to the responder */ + if (packet_len>=MAX_DATA_BYTES) + { + if (packetFinalise(packet,8000,&packet_len)) return -1; + packetSendFollowup(r->sender,packet,packet_len); + packet_len=0; + } + /* Prepare a new request packet if one is not currently being built */ + if (!packet_len) + { + if (packetMakeHeader(packet,8000,&packet_len,transaction_id)) return -1; + if (packetSetSid(packet,8000,&packet_len,sid)) return setReason("SID went mouldy during multi-packet get"); + } + + max_bytes=65535-offset; + if (max_bytes>buffer_length) max_bytes=buffer_length; + if (packetAddVariableRequest(packet,8000,&packet_len, + item,r->var_instance,offset,max_bytes)) return -1; + } + /* Send accumulated request direct to the responder */ + if (packet_len) + { + if (packetFinalise(packet,8000,&packet_len)) return -1; + packetSendFollowup(r->sender,packet,packet_len); + packet_len=0; + } + + /* Collect responses to our multiple requests */ + bzero(&responses,sizeof(responses)); + + /* XXX should target specific peer that sent first piece */ + getReplyPackets(REQ_PARALLEL,i,0,&responses,transaction_id,250); + rr=responses.responses; + while(rr) + { + if (rr->code==ACTION_DATA&&rr->var_id==r->var_id&&rr->var_instance==r->var_instance) + { + int piece=rr->value_offset/MAX_DATA_BYTES; + if (!recv_map[piece]) + { + if (debug>1) fprintf(stderr,"Extracted value fragment @ offset %d, with %d bytes\n",rr->value_offset,rr->value_bytes); + if (debug>2) dump("Fragment",rr->response,rr->value_bytes); + fseek(outputfile,rr->value_offset,SEEK_SET); + fwrite(rr->response,rr->value_bytes,1,outputfile); + recv_map[piece]=1; + } + else + { + if (debug>1) fprintf(stderr,"DUPLICATE value fragment @ offset %d, with %d bytes\n",rr->value_offset,rr->value_bytes); + + } + } + rr=rr->next; + } + clearResponses(&responses); + } + } + if (outputtemplate) fclose(outputfile); else fflush(outputfile); + printf("\n"); + break; + } + } + break; + case ACTION_DONE: + printf("DONE:%s:%d\n",sid,r->response[0]); + break; + case ACTION_GET: printf("ERROR:You cant respond with GET\n"); break; + case ACTION_SET: printf("ERROR:You cant respond with SET\n"); break; + case ACTION_WROTE: printf("ERROR:You cant respond with WROTE\n"); break; + case ACTION_DEL: printf("ERROR:You cant respond with DEL\n"); break; + case ACTION_CREATEHLR: printf("ERROR:You cant respond with CREATEHLR\n"); break; + case ACTION_PAD: /* ignore it */ break; + case ACTION_EOT: /* ignore it */ break; + default: printf("ERROR:Unexpected response code 0x%02x\n",r->code); + } + fflush(stdout); + r=r->next; + } + + return -1; +} diff --git a/configure.in b/configure.in new file mode 100644 index 00000000..54bd7e6e --- /dev/null +++ b/configure.in @@ -0,0 +1,14 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(dna.c) + +dnl Checks for programs. +AC_PROG_CC + +AC_CHECK_LIB(c,srandomdev) + +AC_CHECK_HEADERS(stdio.h errno.h stdlib.h strings.h unistd.h string.h arpa/inet.h sys/socket.h sys/mman.h sys/time.h poll.h netdb.h) + +AC_CHECK_LIB(nsl,callrpc,[LDFLAGS="$LDFLAGS -lnsl"]) +AC_CHECK_LIB(socket,socket,[LDFLAGS="$LDFLAGS -lsocket"]) + +AC_OUTPUT(Makefile) diff --git a/dataformats.c b/dataformats.c new file mode 100644 index 00000000..84a752b1 --- /dev/null +++ b/dataformats.c @@ -0,0 +1,191 @@ +/* +Serval Distributed Numbering Architecture (DNA) +Copyright (C) 2010 Paul Gardner-Stephen + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "mphlr.h" + +int extractDid(unsigned char *packet,int *ofs,char *did) +{ + int d=0; + int highP=1; + int nybl; + + nybl=0; + while(nybl!=0xf&&(*ofs<(OFS_SIDDIDFIELD+SIDDIDFIELD_LEN))&&(d<64)) + { + if (highP) nybl=packet[*ofs]>>4; else nybl=packet[*ofs]&0xf; + if (nybl<0xa) did[d++]='0'+nybl; + else + switch(nybl) { + case 0xa: did[d++]='*'; break; + case 0xb: did[d++]='#'; break; + case 0xc: did[d++]='+'; break; + } + if (highP) highP=0; else { (*ofs)++; highP=1; } + } + if (d>63) return setReason("DID too long"); + did[d]=0; + + return 0; +} + +int stowDid(unsigned char *packet,int *ofs,char *did) +{ + int highP=1; + int nybl; + int d=0; + int len=0; + + while(did[d]&&(len=DID_MAXSIZE) + { + setReason("DID number too long"); + return -1; + } + /* Append end of number code, filling the whole byte for fast and easy comparison */ + if (highP) packet[(*ofs)++]=0xff; + else packet[(*ofs)++]|=0x0f; + + /* Fill remainder of field with randomness to protect any encryption */ + for(;len>4]; + sid[d++]=hexdigit[packet[*ofs]&0xf]; + (*ofs)++; + } + sid[64]=0; + return 0; +} + +int stowSid(unsigned char *packet,int ofs,char *sid) +{ + int i; + if (strlen(sid)!=64) return setReason("Asked to stow invalid SID (should be 64 hex digits)"); + for(i=0;i1) fprintf(stderr,"Decoded DID as %s\n",did); + return 0; + break; + case 1: /* SID */ + ofs++; + if (len<(OFS_SIDDIDFIELD+SID_SIZE)) return setReason("Packet too short"); + if (extractSid(packet,&ofs,sid)) return setReason("Could not decode SID"); + return 0; + break; + default: /* no idea */ + break; + return setReason("Unknown ID key"); + } + + return setReason("Impossible event #1 just occurred"); +} + +/* + One of the goals of our packet format is to make it very difficult to mount a known plain-text + attack against the ciphered part of the packet. + One defence is to make sure that no fixed fields are actually left zero. + We accomplish this by filling "zero" fields with randomised data that meets a simple test condition. + We have chosen to use the condition that if the modulo 256 sum of the bytes equals zero, then the packet + is assumed to be zero/empty. + The following two functions allow us to test this, and also to fill a field with safe "zero" data. +*/ + +int isFieldZeroP(unsigned char *packet,int start,int count) +{ + int mod=0; + int i; + + for(i=start;i3) { + if (mod) fprintf(stderr,"Field [%d,%d) is non-zero (mod=0x%02x)\n",start,start+count,mod); + else fprintf(stderr,"Field [%d,%d) is zero\n",start,start+count); + } + + if (mod) return 0; else return 1; +} + +int safeZeroField(unsigned char *packet,int start,int count) +{ + int mod=0; + int i; + + if (debug>3) fprintf(stderr,"Known plain-text counter-measure: safe-zeroing [%d,%d)\n", + start,start+count); + + for(i=start;i<(start+count-1);i++) + { + packet[i]=random()&0xff; + mod+=packet[i]; + mod&=0xff; + } + /* set final byte so that modulo sum is zero */ + packet[i]=(0x100-mod)&0xff; + + return 0; +} + diff --git a/dna.c b/dna.c new file mode 100644 index 00000000..5b24b863 --- /dev/null +++ b/dna.c @@ -0,0 +1,351 @@ +/* +Serval Distributed Numbering Architecture (DNA) +Copyright (C) 2010 Paul Gardner-Stephen + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "mphlr.h" + +char *outputtemplate=NULL; + +int debug=0; +int timeout=3000; /* 3000ms request timeout */ + +int serverMode=0; +int clientMode=0; + +int hexdigit[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + +struct mphlr_variable vars[]={ + /* Variables that can have only a single value */ + {VAR_EOR,"eor","Marks end of record"}, + {VAR_CREATETIME,"createtime","Time HLR record was created"}, + {VAR_CREATOR,"creator","Device that created this HLR record"}, + {VAR_REVISION,"revision","Revision number of this HLR record"}, + {VAR_REVISOR,"revisor","Device that revised this HLR record"}, + {VAR_PIN,"pin","Secret PIN for this HLR record"}, + + /* GSM encoded audio, so a 16KB MPHLR maximum size shouldn't + pose a problem. 8KB = ~4.5 seconds, which is a long time + to say your name in, leaving 8KB for other variables. */ + {VAR_VOICESIG,"voicesig","Voice signature of this subscriber"}, + + {VAR_HLRMASTER,"hlrmaster","Location where the master copy of this HLR record is maintained."}, + + /* Variables that can take multiple values */ + {VAR_DIDS,"dids","Numbers claimed by this subscriber"}, + {VAR_LOCATIONS,"locations","Locations where this subscriber wishes to receive calls"}, + {VAR_IEMIS,"iemis","GSM IEMIs claimed by this subscriber"}, + {VAR_TEMIS,"temis","GSM TEMIs claimed by this subscriber"}, + + /* Each entry here has a flag byte (unread, ...) */ + {VAR_CALLS_IN,"callsin","Calls received by this subscriber"}, + {VAR_CALLS_MISSED,"callsmissed","Calls missed by this subscriber"}, + {VAR_CALLS_OUT,"callsout","Calls made by this subscriber"}, + + {VAR_SMESSAGES,"smessages","SMS received by this subscriber"}, + + {VAR_DID2SUBSCRIBER,"did2subscriber","Preferred subscribers for commonly called DIDs"}, + + {VAR_HLRBACKUPS,"hlrbackups","Locations where backups of this HLR record are maintained."}, + + {VAR_NOTE,"note","Free-form notes on this HLR record"}, + + {0x00,NULL,NULL} +}; + +int sock; + +#ifndef HAVE_BZERO +/* OpenWRT doesn't have bzero */ +void bzero(void *m,size_t len) +{ + unsigned char *c=m; + int i; + for(i=0;i=' '&&addr[i+j]<0x7f?addr[i+j]:'.'); + fprintf(stderr,"\n"); + } + return 0; +} + +int dumpResponses(struct response_set *responses) +{ + struct response *r; + if (!responses) {fprintf(stderr,"Response set is NULL\n"); return 0; } + fprintf(stderr,"Response set claims to contain %d entries.\n",responses->response_count); + r=responses->responses; + while(r) + { + fprintf(stderr," response code 0x%02x\n",r->code); + if (r->next) + if (r->next->prev!=r) fprintf(stderr," !! response chain is broken\n"); + r=r->next; + } + return 0; +} + +int setReason(char *msg) +{ + fprintf(stderr,"Error: %s\n",msg); + return -1; +} + +int hexvalue(unsigned char c) +{ + if (c>='0'&&c<='9') return c-'0'; + if (c>='A'&&c<='F') return c-'A'+10; + if (c>='a'&&c<='f') return c-'a'+10; + return setReason("Invalid hex digit in SID"); +} + +int parseAssignment(unsigned char *text,int *var_id,unsigned char *value,int *value_len) +{ + /* Parse an assignment. + + Valid formats are: + + var=@file - value comes from named file. + var=[[$]value] - value comes from string, and may be empty. $ means value is in hex + + Values are length limited to 65535 bytes. + */ + + int i,v; + int max_len=*value_len; + int vlen=0; + int tlen=strlen((char *)text); + + if (tlen>3072) { + return setReason("Variable assignment string is too long, use =@file to read value from a file"); + } + + /* Identify which variable */ + for(i=0;i=tlen) return setReason("Variable value has an odd number of hex digits."); + b|=hexvalue(text[i++]); + if (b<0) return setReason("That doesn't look like hex to me"); + if (vlen>=max_len) return setReason("Variable hex value too long"); + value[vlen++]=b; + } + *value_len=vlen; + return 0; + break; + case '@': /* file */ + { + FILE *f=fopen((char *)&text[i+1],"r"); + int flen; + fseek(f,0,SEEK_END); + flen=ftell(f); + if (flen>max_len) return setReason("Variable value from file too long"); + fseek(f,0,SEEK_SET); + vlen=fread(value,1,flen,f); + if (vlen!=flen) return setReason("Could not read all of file"); + fclose(f); + *value_len=vlen; + return 0; + } + break; + default: /* literal string */ + vlen=strlen((char *)&text[i]); + if (vlen>max_len) return setReason("Variable value too long"); + bcopy(&text[i],value,vlen); + *value_len=vlen; + return 0; + } + + return 0; +} + +int usage(char *complaint) +{ + fprintf(stderr,"dna: %s\n",complaint); + fprintf(stderr,"usage:\n"); + fprintf(stderr," dna [-v ...] -S [-f HLR backing file]\n"); + fprintf(stderr,"or\n"); + fprintf(stderr," dna <-d|-s> id [-p pin] [-i variable instance] <-R variable[=value]>\n"); + fprintf(stderr," [-v ...] [-t request timeout in ms] [-O output file name template]\n"); + fprintf(stderr,"or\n"); + fprintf(stderr," dna <-d|-s> id [-p pin] [-i variable instance] <-W|-U|-D variable[=[$|@]value]>\n"); + fprintf(stderr," [-v ...] [-t request timeout in ms]\n"); + fprintf(stderr,"or\n"); + fprintf(stderr," dna [-v ...] [-t timeout] -d did -C\n"); + + fprintf(stderr,"\n"); + fprintf(stderr," -v - increase verbosity.\n"); + fprintf(stderr," -b - Specify BATMAN socket to obtain peer list.\n"); + fprintf(stderr," -n - Do not detach from foreground in server mode.\n"); + fprintf(stderr," -S - Run in server mode with an HLR of the specified size.\n"); + fprintf(stderr," -f - Use the specified file as a permanent store for HLR data.\n"); + fprintf(stderr," -d - Search by Direct Inward Dial (DID) number.\n"); + fprintf(stderr," -s - Search by Subscriber ID (SID) number.\n"); + fprintf(stderr," -p - Specify additional DNA nodes to query.\n"); + fprintf(stderr," -P - Authenticate using the supplied pin.\n"); + fprintf(stderr," -R - Read a variable value.\n"); + fprintf(stderr," -O - Place read variable value into files using argument as a template.\n"); + fprintf(stderr," The following template codes can be used (interpretted by sprintf):\n"); + fprintf(stderr," %%1$s - Subscriber ID\n"); + fprintf(stderr," %%2$d - Variable ID (0-255)\n"); + fprintf(stderr," %%3$d - Variable instance number (0-255)\n"); + fprintf(stderr," -W - Write a variable value, keeping previous values.\n"); + fprintf(stderr," -U - Update a variable value, replacing the previous value.\n"); + fprintf(stderr," -D - Delete a variable value.\n"); + fprintf(stderr," $value means interpret value as hexidecimal bytes.\n"); + fprintf(stderr," @value means read value from file called value.\n"); + fprintf(stderr," -C - Request the creation of a new subscriber with the specified DID.\n"); + fprintf(stderr," -t - Specify the request timeout period.\n"); + fprintf(stderr,"\n"); + exit(-1); +} + +int main(int argc,char **argv) +{ + int c; + char *pin=NULL; + char *did=NULL; + char *sid=NULL; + char *hlr_file=NULL; + int instance=-1; + int foregroundMode=0; + + srandomdev(); + + while((c=getopt(argc,argv,"b:B:S:f:d:i:np:P:s:t:vR:W:U:D:CO:")) != -1 ) + { + switch(c) + { + case 'n': /* don't detach from foreground in server mode */ + foregroundMode=1; break; + case 'b': /* talk peers on a BATMAN mesh */ + batman_socket=strdup(optarg); + break; + case 'B': /* Set simulated Bit Error Rate for bench-testing */ + simulatedBER=atof(optarg); + fprintf(stderr,"WARNING: Bit error injection enabled -- this will cause packet loss and is intended only for testing.\n"); + break; + case 'S': + if (atof(optarg)<0.1||atof(optarg)>16384) usage("HLR must be 0.1MB - 16384MB in size."); + hlr_size=(int)(atof(optarg)*1048576.0); + serverMode=1; + break; + case 'i': + instance=atoi(optarg); + if (instance<-1||instance>255) usage("Illegal variable instance ID."); + break; + case 'f': + if (clientMode||(!serverMode)) usage("Only servers use backing files"); + hlr_file=strdup(optarg); + break; + case 'p': /* additional peers to query */ + if (additionalPeer(optarg)) exit(-3); + break; + case 'P': /* Supply pin */ + pin=strdup(optarg); + clientMode=1; + break; + case 'd': /* Ask by DID */ + clientMode=1; + did=strdup(optarg); + break; + case 's': /* Ask by subscriber ID */ + clientMode=1; + sid=strdup(optarg); + break; + case 't': /* request timeout (ms) */ + timeout=atoi(optarg); + break; + case 'v': /* Increase verbosity */ + debug++; + break; + case 'R': /* read a variable */ + { + unsigned char buffer[65535]; + int len=0; + requestItem(did,sid,(char *)optarg,instance,buffer,sizeof(buffer),&len,NULL); + } + break; + case 'W': /* write a variable */ + { + int var_id; + unsigned char value[65536]; + int value_len=65535; + if (parseAssignment((unsigned char *)optarg,&var_id,value,&value_len)) return -1; + value[value_len]=0; + return writeItem(did?did:sid,var_id,instance,value,0,value_len,SET_NOREPLACE); + } + break; + case 'U': /* write or update a variable */ + { + int var_id; + unsigned char value[65536]; + int value_len=65535; + if (parseAssignment((unsigned char *)optarg,&var_id,value,&value_len)) return -1; + value[value_len]=0; + return writeItem(did?did:sid,var_id,instance,value,0,value_len,SET_REPLACE); + } + break; + case 'C': /* create a new HLR entry */ + { + if (optindb[i]) return 1; + return 0; +} + +int nextHlr(unsigned char *hlr,int *ofs) +{ + int record_length; + + if (!ofs) return setReason("nextHlr passed NULL pointer."); + if (*ofs>=hlr_size) return -1; + + /* Get length of this record */ + record_length =hlr[(*ofs)+3]<<0; + record_length|=hlr[(*ofs)+2]<<8; + record_length|=hlr[(*ofs)+1]<<16; + record_length|=hlr[(*ofs)+0]<<24; + + if (!record_length) return 0; + + (*ofs)+=record_length; + return 0; +} + +char sid_string[SID_SIZE*2+1]; +char *hlrSid(unsigned char *hlr,int ofs) +{ + int o=ofs+4; + extractSid(hlr,&o,sid_string); + return sid_string; +} + +int findHlr(unsigned char *hlr,int *ofs,char *sid,char *did) +{ + unsigned int record_length; + int match=0; + int records_searched=0; + int pid_len=0; + unsigned char packed_id[40]; + + if ((*ofs)>=hlr_size) return 0; + + if (debug>1) fprintf(stderr,"Searching for HLR record sid=[%s]/did=[%s]\n",sid?sid:"NULL",did?did:"NULL"); + + if (did&&did[0]) { + /* Make packed version of DID so that we can compare faster with the DIDs in the HLR */ + if (stowDid(packed_id,&pid_len,did)) return setReason("DID appears to be invalid"); + /* Find significant length of packed DID */ + for(pid_len=0;pid_len1) dump("Searching for DID records that match",packed_id,pid_len); + } + + if (sid&&sid[0]) { + /* Make packed version of SID for fast comparison */ + if (stowSid(packed_id,pid_len,sid)) return setReason("SID appears to be invalid"); + pid_len=SID_SIZE; + } + + while(!match) + { + /* Get length of this record */ + record_length =hlr[(*ofs)+3]<<0; + record_length|=hlr[(*ofs)+2]<<8; + record_length|=hlr[(*ofs)+1]<<16; + record_length|=hlr[(*ofs)+0]<<24; + + if (!record_length) return 0; + + if (debug>1) fprintf(stderr,"Considering HLR entry @ 0x%x\n",*ofs); + + records_searched++; + + if (sid&&sid[0]) { + /* Lookup by SID, so just see if it matches */ + if (!bcompare(packed_id,&hlr[(*ofs)+4],SID_SIZE)) { + if (debug>1) fprintf(stderr,"Found requested SID at address 0x%x.\n",*ofs); + match=1; + } + } + if (did&&did[0]) { + /* Lookup by did, so see if there are any matching DID entries for this subscriber */ + int rofs=(*ofs); + struct hlrentry_handle *h=openhlrentry(hlr,rofs); + while(h) + { + /* Search through variables for matching DIDs */ + if (debug>2) { + fprintf(stderr,"Considering variable 0x%02x, instance %d.\n", + h->var_id,h->var_instance); + dump("variable value",h->value,h->value_len); + } + if (h->var_id==VAR_DIDS) { /* DID entry */ + if (debug>2) fprintf(stderr,"Checking DID against record DID\n"); + if (!bcompare(packed_id,h->value,pid_len)) { + if (debug>1) fprintf(stderr,"Found matching DID in HLR record #%d\n",records_searched); + match=1; + break; + } + } + else + { + if (debug>2) fprintf(stderr,"Skipping non-DID variable while searching for DID.\n"); + } + h=hlrentrygetent(h); + } + } + + /* For each match ... */ + if (match) + { + if (debug>1) fprintf(stderr,"Returning HLR entry @ 0x%x\n",*ofs); + return 1; + } + + /* Consider next record */ + (*ofs)+=record_length; + + if ((*ofs)>=hlr_size) return 0; + } + + return 0; +} + +int createHlr(char *did,char *sid) { + int i; + int record_offset=0; + + /* Generate random SID */ + for(i=0;i<64;i++) sid[i]=hexdigit[random()&0xf]; sid[64]=0; + if (debug>1) fprintf(stderr,"Creating new HLR entry with sid %s\n",sid); + + /* Find first free byte of HLR */ + findHlr(hlr,&record_offset,NULL,NULL); + + if (record_offset>=hlr_size) + { + /* No space */ + return setReason("No space in HLR for a new record"); + } + else + { + /* We have found space, but is it enough? */ + int bytes=hlr_size-record_offset; + if (bytes<1024) return setReason("<1KB space in HLR"); + + /* Write shiny fresh new record. + 32bit - record length + 32 bytes - SID + Total length = 4+32=36 bytes. + */ + if (stowSid(hlr,record_offset+4,sid)) return setReason("Could not store SID in new HLR entry"); + + /* Write length last of all to make entry valid */ + hlr[record_offset]=0; + hlr[record_offset+1]=0; + hlr[record_offset+2]=0; + hlr[record_offset+3]=36; + + /* Store the DID */ + { + unsigned char packeddid[DID_MAXSIZE]; + int pdidlen=0; + stowDid(packeddid,&pdidlen,did); + /* Work out reduced length of DID */ + for(pdidlen=1;pdidlen2) dump("after HLR create",&hlr[0],256); + return 0; + } + + return setReason("Unreachable code turned out not to be"); +} + +struct hlrentry_handle hlr_handle; + +int hlrGetRecordLength(unsigned char *hlr,int hofs) +{ + int record_length; + record_length =hlr[hofs+3]<<0; + record_length|=hlr[hofs+2]<<8; + record_length|=hlr[hofs+1]<<16; + record_length|=hlr[hofs+0]<<24; + + if (debug>2) fprintf(stderr,"HLR record @ 0x%x is %d bytes long.\n",hofs,record_length); + + return record_length; +} + +int hlrSetRecordLength(unsigned char *hlr,int hofs,int length) +{ + hlr[hofs+3]=length&0xff; + hlr[hofs+2]=(length>>8)&0xff; + hlr[hofs+1]=(length>>16)&0xff; + hlr[hofs+0]=(length>>24)&0xff; + return 0; +} + +/* + XXX We could return a fancy struct, and maybe we should. + But returning a long long and using the two 32bit halves is easy for now, and has a + certain efficiency to it. */ +struct hlrentry_handle *openhlrentry(unsigned char *hlr,int hofs) +{ + int record_length=hlrGetRecordLength(hlr,hofs); + + /* If record has zero length, then open fails */ + if (!record_length) + { + if (debug>2) fprintf(stderr,"HLR record is zero length -- aborting.\n"); + return NULL; + } + + bzero(&hlr_handle,sizeof(hlr_handle)); + + hlr_handle.record_length=record_length; + hlr_handle.hlr=hlr; + hlr_handle.hlr_offset=hofs; + hlr_handle.var_id=-1; + hlr_handle.var_instance=-1; + hlr_handle.value=NULL; + hlr_handle.value_len=-1; + hlr_handle.entry_offset=0; + + /* Return offset of start of HLR entry and the offset of the first variable */ + return hlrentrygetent(&hlr_handle); +} + +struct hlrentry_handle *hlrentrygetent(struct hlrentry_handle *h) +{ + if (!h) return NULL; + + if (h->entry_offset==0) + { + /* First entry */ + if (debug>2) fprintf(stderr,"Considering first entry of HLR record.\n"); + h->entry_offset=HLR_RECORD_LEN_SIZE+SID_SIZE; + } + else + { + /* subsequent entry */ + if (debug>2) fprintf(stderr,"Considering entry @ 0x%x\n",h->entry_offset); + h->entry_offset+=1+2+h->value_len+(h->var_id&0x80?1:0); + } + + /* XXX Check if end of record */ + if (h->entry_offset>=h->record_length) { + if (debug>2) fprintf(stderr,"Reached end of HLR record (%d>=%d).\n",h->entry_offset,h->record_length); + return NULL; + } + + /* XXX Extract variable */ + int ptr=h->hlr_offset+h->entry_offset; + if (debug>2) fprintf(stderr,"Extracting HLR variable @ 0x%x\n",ptr); + h->var_id=hlr[ptr]; + h->value_len=(hlr[ptr+1]<<8)+hlr[ptr+2]; + ptr+=3; + if (h->var_id&0x80) h->var_instance=hlr[ptr++]; + h->value=&h->hlr[ptr]; + + return h; +} + +int hlrGetVariable(unsigned char *hlr,int hofs,int varid,int varinstance, + unsigned char *value,int *len) +{ + struct hlrentry_handle *h; + int hlr_offset=-1; + + h=openhlrentry(hlr,hofs); + + /* Find the place in the HLR record where this variable is */ + while(h) + { + if ((h->var_idvar_id==varid&&h->var_instanceentry_offset; + else + { + /* Value is here if anywhere */ + if (h->var_id>varid||h->var_instance>varinstance) + return setReason("No such variable instance"); + if (h->value_len>*len) return setReason("Value too long for buffer"); + bcopy(h->value,value,h->value_len); + *len=h->value_len; + return 0; + break; + } + h=hlrentrygetent(h); + } + + return setReason("No such variable instance"); +} + +int hlrSetVariable(unsigned char *hlr,int hofs,int varid,int varinstance, + unsigned char *value,int len) +{ + /* hlr & hofs identify the start of a HLR entry. */ + + struct hlrentry_handle *h; + int hlr_offset=-1; + int hlr_size=hlrGetRecordLength(hlr,hofs); + + if (debug) fprintf(stderr,"hlrSetVariable(varid=%02x, instance=%02x, len=%d)\n", + varid,varinstance,len); + + h=openhlrentry(hlr,hofs); + + /* Find the place in the HLR record where this variable should go */ + while(h) + { + if (debug>1) fprintf(stderr,"h->var_id=%02x, h->h->var_instance=%02x, h->entry_offset=%x\n", + h->var_id,h->var_instance,h->entry_offset); + if ((h->var_idvar_id==varid&&h->var_instanceentry_offset; + if (debug>1) fprintf(stderr,"Found variable instance prior: hlr_offset=%d.\n",hlr_offset); + } + else + { + /* Value goes here */ + if (debug>1) fprintf(stderr,"Found variable instance to overwrite: hlr_offset=%d.\n",hlr_offset); + hlr_offset=h->entry_offset; + break; + } + h=hlrentrygetent(h); + } + + /* XXX Race condition: If power is lost half way through here, then it is possible for + the record to be left in an inconsistent state */ + + if (h&&hlr_offset>-1) + { + if (debug>2) printf("hlr_offset=%d\n",hlr_offset); + if (h&&h->var_id==varid&&h->var_instance==varinstance) + { + /* Replace existing value */ + if (debug) fprintf(stderr,"Replacing value in HLR\n"); + int existing_size=1+2+(h->var_id&0x80?1:0)+h->value_len; + hlrMakeSpace(hlr,hofs,hlr_offset,1+2+len+(varid&0x80?1:0)-existing_size); + } + else + { + /* Insert value here */ + if (debug) fprintf(stderr,"Inserting value in HLR\n"); + hlrMakeSpace(hlr,hofs,hlr_offset,1+2+len+(varid&0x80?1:0)); + } + } + else + { + /* HLR record has no entries, or this entry needs to go at the end, + so insert value at end of the record */ + if (debug) fprintf(stderr,"Inserting value at end of HLR @ 0x%x\n",hlr_size); + hlrMakeSpace(hlr,hofs,hlr_size,1+2+len+(varid&0x80?1:0)); + hlr_offset=hlr_size; + } + + return hlrStowValue(hlr,hofs,hlr_offset,varid,varinstance,value,len); +} + +int hlrStowValue(unsigned char *hlr,int hofs,int hlr_offset, + int varid,int varinstance,unsigned char *value,int len) +{ + int ptr=hofs+hlr_offset; + + hlr[ptr++]=varid; + hlr[ptr++]=(len>>8)&0xff; + hlr[ptr++]=len&0xff; + if (varid&0x80) hlr[ptr++]=varinstance; + bcopy(value,&hlr[ptr],len); + ptr+=len; + + return 0; +} + +int hlrMakeSpace(unsigned char *hlr,int hofs,int hlr_offset,int bytes) +{ + /* Deal with easy case first */ + if (!bytes) return 0; + + /* Shift rest of HLR up/down. + If down, back-fill bytes with zeros. */ + bcopy(&hlr[hofs+hlr_offset],&hlr[hofs+hlr_offset+bytes], + hlr_size-(hofs+hlr_offset+bytes)); + if (bytes<0) bzero(&hlr[hlr_size-bytes],0-bytes); + + /* Update record length */ + int length=hlrGetRecordLength(hlr,hofs); + length+=bytes; + hlrSetRecordLength(hlr,hofs,length); + if (debug>1) fprintf(stderr,"hlrMakeSpace: HLR entry now %d bytes long.\n",length); + + return 0; +} + +int hlrDump(unsigned char *hlr,int hofs) +{ + struct hlrentry_handle *h=openhlrentry(hlr,hofs); + + fprintf(stderr,"Dumping HLR entry @ 0x%x\n",hofs); + while(h) + { + fprintf(stderr," var=%02x",h->var_id); + if (h->var_id&0x80) fprintf(stderr,"/%02x",h->var_instance); + fprintf(stderr," len=%d\n",h->value_len); + h=hlrentrygetent(h); + } + + return 0; +} diff --git a/meshpotato_build b/meshpotato_build new file mode 100644 index 00000000..e3305b8b --- /dev/null +++ b/meshpotato_build @@ -0,0 +1 @@ +staging_dir/toolchain-mips_gcc4.1.2/bin/mips-linux-gcc -Os -o dna package/dna/src/*.c -DHAVE_SYS_MMAN_H -DHAVE_STDINT_H -DHAVE_STDINT_H -DHAVE_SYS_STAT_H -DHAVE_POLL_H -DHAVE_ARPA_INET_H -DHAVE_BZERO diff --git a/mphlr.h b/mphlr.h new file mode 100644 index 00000000..458f4404 --- /dev/null +++ b/mphlr.h @@ -0,0 +1,319 @@ +/* +Serval Distributed Numbering Architecture (DNA) +Copyright (C) 2010 Paul Gardner-Stephen + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif +#include +#ifdef HAVE_ARPA_INET_H +#include +#else +typedef unsigned int in_addr_t; +struct in_addr { + in_addr_t s_addr; +}; +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_SYS_MMAN_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_POLL_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_CTYPE_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include + + + +/* OpenWRT libc doesn't have bcopy, but has memmove */ +#define bcopy(A,B,C) memmove(B,A,C) + +#define BATCH 1 +#define NONBATCH 0 + +#define REQ_SERIAL 0 +#define REQ_PARALLEL -1 +#define REQ_FIRSTREPLY -2 +#define REQ_REPLY -101 + + +#define SET_NOREPLACE 1 +#define SET_REPLACE 2 +#define SET_NOCREATE 3 +#define SET_FRAGMENT 0x80 + +#define WITHDATA 1 +#define WITHOUTDATA 0 + +/* Limit packet payloads to minimise packet loss of big packets in mesh networks */ +#define MAX_DATA_BYTES 256 + +extern int debug; +extern int timeout; +extern int hlr_size; +extern unsigned char *hlr; + +double simulatedBER; + +extern int serverMode; + +extern struct sockaddr recvaddr; +extern struct in_addr client_addr; +extern int client_port; + +#define MAX_PEERS 1024 +extern int peer_count; +extern in_addr_t peers[MAX_PEERS]; + +struct mphlr_variable { + unsigned char id; + char *name; + char *desc; +}; + +extern char *outputtemplate; + +extern char *batman_socket; + +/* HLR records can be upto 4GB, so 4x8bits are needed to encode the size */ +#define HLR_RECORD_LEN_SIZE 4 + +/* Packet format: + + 16 bit - Magic value 0x4110 + 16 bit - Version number (0001 initially) + 16 bit - Payload length + 16 bit - Cipher method (0000 = clear text) + + Ciphered payload follows: + (needs to have no predictable data to protect against known plain-text attacks) + + 64bit transaction id (random) + 8bit - payload rotation (random, to help protect encryption from cribs) + + Remainder of payload, after correcting for rotation: + + 33byte did|subscriber id + 16byte salt + 16byte hash of PIN+salt + + Remainder of packet is interpretted as a series of operations + + 8 bit operation: + 00 = get, 01 = set, 02 = delete, 03 = update, + 80 = decline, 81 = okay (+optional result), + f0 = xfer HLR record + fe = random padding follows (to help protect cryptography from cribs) + ff = end of transaction + + get - 8 bit variable value + +*/ +#define SID_SIZE 32 +#define DID_MAXSIZE 32 +#define SIDDIDFIELD_LEN (SID_SIZE+1) +#define PINFIELD_LEN 32 +#define HEADERFIELDS_LEN (2+2+2+2+8+1) +#define OFS_TRANSIDFIELD (2+2+2+2) +#define TRANSID_SIZE 8 +#define OFS_ROTATIONFIELD (OFS_TRANSIDFIELD+TRANSID_SIZE) +#define OFS_SIDDIDFIELD HEADERFIELDS_LEN +#define OFS_PINFIELD (OFS_SIDDIDFIELD+SIDDIDFIELD_LEN) +#define OFS_PAYLOAD (OFS_PINFIELD+16+16) + +struct response { + int code; + unsigned char sid[32]; + struct in_addr sender; + unsigned char *response; + int response_len; + int var_id; + int var_instance; + int value_len; + int value_offset; + int value_bytes; + struct response *next,*prev; + + /* who sent it? */ + unsigned short peer_id; + /* have we checked it to see if it allows us to stop requesting? */ + unsigned char checked; +}; + +struct response_set { + struct response *responses; + struct response *last_response; + int response_count; + + /* Bit mask of peers who have replied */ + unsigned char *reply_bitmask; +}; + +struct hlrentry_handle { + int record_length; + unsigned char *hlr; + int hlr_offset; + + int var_id; + int var_instance; + unsigned char *value; + int value_len; + + int entry_offset; +}; + +/* Array of variables that can be placed in an MPHLR */ +#define VAR_EOR 0x00 +#define VAR_CREATETIME 0x01 +#define VAR_CREATOR 0x02 +#define VAR_REVISION 0x03 +#define VAR_REVISOR 0x04 +#define VAR_PIN 0x05 +#define VAR_VOICESIG 0x08 +#define VAR_HLRMASTER 0x0f +#define VAR_DIDS 0x80 +#define VAR_LOCATIONS 0x81 +#define VAR_IEMIS 0x82 +#define VAR_TEMIS 0x83 +#define VAR_CALLS_IN 0x90 +#define VAR_CALLS_MISSED 0x91 +#define VAR_CALLS_OUT 0x92 +#define VAR_SMESSAGES 0xa0 +#define VAR_DID2SUBSCRIBER 0xb0 +#define VAR_HLRBACKUPS 0xf0 +#define VAR_NOTE 0xff +extern struct mphlr_variable vars[]; + +#define ACTION_GET 0x00 +#define ACTION_SET 0x01 +#define ACTION_DEL 0x02 +#define ACTION_INSERT 0x03 +#define ACTION_CREATEHLR 0x0f + +#define ACTION_DONE 0x7e +#define ACTION_ERROR 0x7f + +#define ACTION_DECLINED 0x80 +#define ACTION_OKAY 0x81 +#define ACTION_DATA 0x82 +#define ACTION_WROTE 0x83 + +#define ACTION_XFER 0xf0 +#define ACTION_PAD 0xfe +#define ACTION_EOT 0xff + +extern int hexdigit[16]; + +/* Make sure we have space to put bytes of the packet as we go along */ +#define CHECK_PACKET_LEN(B) {if (((*packet_len)+(B))>=packet_maxlen) { setReason("Packet composition ran out of space."); return -1; } } + +extern int sock; + +int stowSid(unsigned char *packet,int ofs,char *sid); +int stowDid(unsigned char *packet,int *ofs,char *did); +int isFieldZeroP(unsigned char *packet,int start,int count); +void srandomdev(); +int respondSimple(char *sid,int action,unsigned char *action_text,int action_len, + unsigned char *transaction_id); +int requestItem(char *did,char *sid,char *item,int instance,unsigned char *buffer,int buffer_length,int *len, + unsigned char *transaction_id); +int requestNewHLR(char *did,char *pin,char *sid); +int server(char *backing_file,int size,int foregroundMode); + +int setReason(char *msg); +int hexvalue(unsigned char c); +int dump(char *name,unsigned char *addr,int len); +int packetOk(unsigned char *packet,int len,unsigned char *transaction_id); +int process_packet(unsigned char *packet,int len,struct sockaddr *sender,int sender_len); +int packetMakeHeader(unsigned char *packet,int packet_maxlen,int *packet_len,unsigned char *transaction_id); +int packetSetDid(unsigned char *packet,int packet_maxlen,int *packet_len,char *did); +int packetSetSid(unsigned char *packet,int packet_maxlen,int *packet_len,char *sid); +int packetFinalise(unsigned char *packet,int packet_maxlen,int *packet_len); +int packetAddHLRCreateRequest(unsigned char *packet,int packet_maxlen,int *packet_len); +int extractResponses(struct in_addr sender,unsigned char *buffer,int len,struct response_set *responses); +int packetAddVariableRequest(unsigned char *packet,int packet_maxlen,int *packet_len, + char *item,int instance,int start_offset,int max_offset); +int packetGetID(unsigned char *packet,int len,char *did,char *sid); +int getPeerList(); +int sendToPeers(unsigned char *packet,int packet_len,int method,int peerId,struct response_set *responses); +int getReplyPackets(int method,int peer,int batchP, + struct response_set *responses, + unsigned char *transaction_id,int timeout); +int clearResponse(struct response **response); +int nextHlr(unsigned char *hlr,int *ofs); +int findHlr(unsigned char *hlr,int *ofs,char *sid,char *did); +int createHlr(char *did,char *sid); +struct hlrentry_handle *openhlrentry(unsigned char *hlr,int hofs); +struct hlrentry_handle *hlrentrygetent(struct hlrentry_handle *h); +int hlrStowValue(unsigned char *hlr,int hofs,int hlr_offset, + int varid,int varinstance,unsigned char *value,int len); +int hlrMakeSpace(unsigned char *hlr,int hofs,int hlr_offset,int bytes); +int packageVariableSegment(unsigned char *data,int *dlen,struct hlrentry_handle *h, + int offset,int buffer_size); +int packetDecipher(unsigned char *packet,int len,int cipher); +int safeZeroField(unsigned char *packet,int start,int count); +int unpackageVariableSegment(unsigned char *data,int dlen,int flags,struct response *r); +int extractSid(unsigned char *packet,int *ofs,char *sid); +int hlrSetVariable(unsigned char *hlr,int hofs,int varid,int varinstance, + unsigned char *value,int len); +int extractDid(unsigned char *packet,int *ofs,char *did); +char *hlrSid(unsigned char *hlr,int ofs); +int parseAssignment(unsigned char *text,int *var_id,unsigned char *value,int *value_len); +int writeItem(char *id,int var_id,int instance,unsigned char *value,int value_start,int value_len,int policy); +int packetAddVariableWrite(unsigned char *packet,int packet_maxlen,int *packet_len, + int itemId,int instance,unsigned char *value,int start_offset,int value_len,int flags); +int processRequest(unsigned char *packet,int len,struct sockaddr *sender,int sender_len, + unsigned char *transaction_id,char *did,char *sid); + +int extractRequest(unsigned char *packet,int *packet_ofs,int packet_len, + int *itemId,int *instance,unsigned char *value, + int *start_offset,int *max_offset,int *flags); +int hlrGetVariable(unsigned char *hlr,int hofs,int varid,int varinstance, + unsigned char *value,int *len); +int packetSendRequest(int method,unsigned char *packet,int packet_len,int batchP, + unsigned char *transaction_id,struct response_set *responses); +int dumpResponses(struct response_set *responses); +int eraseLastResponse(struct response_set *responses); +int dropPacketP(int packet_len); +int clearResponses(struct response_set *responses); +int responseFromPeerP(struct response_set *responses,int peerId); +int responseFromPeer(struct response_set *responses,int peerId); +int additionalPeer(char *peer); +int getBatmanPeerList(char *socket_path,in_addr_t peers[],int *peer_count,int peer_max); +int hlrDump(unsigned char *hlr,int hofs); diff --git a/openwrt-makefile b/openwrt-makefile new file mode 100644 index 00000000..927157f3 --- /dev/null +++ b/openwrt-makefile @@ -0,0 +1,31 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=dna +PKG_RELEASE:=1 +PKG_VERSION:=1 + +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) + +include $(INCLUDE_DIR)/package.mk + +define Package/dna +    SECTION:=utils +    CATEGORY:=Utilities +    TITLE:=dna +    DESCRIPTION:=Serval Distributed Numbering Architecture Reference Implementation +endef + +define Build/Compile +    mkdir -p $(PKG_BUILD_DIR) +    $(CP) ./src/* $(PKG_BUILD_DIR)/ +    $(TARGET_CC) $(PKG_BUILD_DIR)/*.c -o $(PKG_BUILD_DIR)/dna +    ## $(MAKE) CC=$(TARGET_CC) -C $(PKG_BUILD_DIR) +endef + +define Package/dna/install +    $(INSTALL_DIR) $(1)/usr/bin +    $(INSTALL_BIN) $(PKG_BUILD_DIR)/dna $(1)/usr/bin +endef + +$(eval $(call BuildPackage,dna)) + diff --git a/packetformats.c b/packetformats.c new file mode 100644 index 00000000..2f7808ac --- /dev/null +++ b/packetformats.c @@ -0,0 +1,518 @@ +/* +Serval Distributed Numbering Architecture (DNA) +Copyright (C) 2010 Paul Gardner-Stephen + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "mphlr.h" + +int process_packet(unsigned char *packet,int len,struct sockaddr *sender,int sender_len) +{ + int authenticatedP=0; + char did[128]; + char sid[128]; + unsigned char *transaction_id=&packet[OFS_TRANSIDFIELD]; + + did[0]=0; sid[0]=0; + + /* Get DID or SID */ + if (packetGetID(packet,len,did,sid)) return setReason("Could not parse DID or SID"); + + /* Check for PIN */ + if (!isFieldZeroP(packet,OFS_PINFIELD,16)) + { + /* Authentication has been attempted. + If it is incorrect, then we need to return with ACTION_DECLINED + */ + if (debug>1) fprintf(stderr,"A PIN has been supplied.\n"); + + /* Can only authenticate by SID, not DID (since DIDs are ambiguous) */ + if (packet[OFS_SIDDIDFIELD]!=1) return setReason("You can only authenticate against a SID"); + + /* XXX check authentication */ + return setReason("Authentication not yet supported"); + } + else + { + /* No attempt at authentication was made */ + authenticatedP=0; + if (debug>1) fprintf(stderr,"No PIN was supplied.\n"); + } + + if (serverMode) return processRequest(packet,len,sender,sender_len,transaction_id,did,sid); + + return 0; +} + +int packetOk(unsigned char *packet,int len,unsigned char *transaction_id) +{ + /* Make sure that the packet is meant for us, and is not mal-formed */ + int version; + int cipher; + int length; + int payloadRotation; + + if (len1) fprintf(stderr,"Packet passes sanity checks and is ready for decoding.\n"); + if (debug>2) dump("unrotated packet",packet,len); + + return 0; +} + +int packetMakeHeader(unsigned char *packet,int packet_maxlen,int *packet_len, + unsigned char *transaction_id) +{ + int i; + + CHECK_PACKET_LEN(OFS_PAYLOAD); + + /* 0x4110 magic value */ + packet[0]=0x41; + packet[1]=0x10; + + /* encoding version */ + packet[2]=0x00; + packet[3]=0x01; + + /* Payload length (to be filled in later) */ + packet[4]=0x00; + packet[5]=0x00; + + /* Payload cipher (0x0000 = plain text) */ + packet[6]=0x00; + packet[7]=0x00; + + /* Add 64bit transaction id */ + if (transaction_id) + /* Use supplied transaction ID */ + for(i=0;i<8;i++) packet[OFS_TRANSIDFIELD+i]=transaction_id[i]; + else + /* No transaction ID supplied, so create random transaction ID */ + for(i=0;i<8;i++) packet[OFS_TRANSIDFIELD+i]=random()&0xff; + + /* payload rotation (not yet applied) */ + packet[14]=0x00; + + *packet_len=HEADERFIELDS_LEN; + + /* Clear did/subscriber ID, salt and hashed pin fields. + However, we cannot zero them, because that would provide significant knowable plain-text + for a known plain text attack. + Thus, instead we fill it with random date, but make the modulo sum of each field == 0x00 + to indicate that no PIN has been provided. */ + safeZeroField(packet,*packet_len,SIDDIDFIELD_LEN); *packet_len+=SIDDIDFIELD_LEN; + safeZeroField(packet,*packet_len,16); *packet_len+=16; + safeZeroField(packet,*packet_len,16); *packet_len+=16; + + return 0; +} + +int packetSetDid(unsigned char *packet,int packet_maxlen,int *packet_len,char *did) +{ + /* Set the subject field to the supplied DID. + DIDs get encoded 4bits per digit (0-9,#,*,+,SPARE1,ESCAPE,END) + */ + int ofs=OFS_SIDDIDFIELD; /* where the DID/subscriber ID gets written */ + + /* Put DID (ie not SID) marker into packet */ + packet[ofs++]=0x00; + + return stowDid(packet,&ofs,did); +} + +int packetSetSid(unsigned char *packet,int packet_maxlen,int *packet_len,char *sid) +{ + /* Convert and store hex formatted sid */ + int ofs=OFS_SIDDIDFIELD; /* where the DID/subscriber ID gets written */ + + if (strlen(sid)!=64) { + if (debug) fprintf(stderr,"Invalid SID: [%s] - should be 64 hex digits\n",sid); + return setReason("SID must consist of 64 hex digits"); + } + + packet[ofs++]=0x01; /* SID */ + return stowSid(packet,ofs,sid); +} + +int packetFinalise(unsigned char *packet,int packet_maxlen,int *packet_len) +{ + /* Add any padding bytes and EOT to packet */ + int paddingBytes=rand()&0xf; + + if (paddingBytes) + { + CHECK_PACKET_LEN(2+paddingBytes); + packet[(*packet_len)++]=ACTION_PAD; + packet[(*packet_len)++]=paddingBytes; + while(paddingBytes--) packet[(*packet_len)++]=random()&0xff; + } + + packet[(*packet_len)++]=ACTION_EOT; + + /* Set payload length */ + packet[4]=((*packet_len)>>8)&0xff; + packet[5]=((*packet_len)&0xff); + + /* Work out by how much to rotate the packet payload. + The purpose of the rotation is to make it more difficult to + conduct a known-plaintext attack against any ciphers that we + may later support. + */ + int payloadRotation=(*packet_len)-HEADERFIELDS_LEN; + if (payloadRotation>0xff) payloadRotation=0xff; + payloadRotation=random()%payloadRotation; + if (debug>2) + fprintf(stderr,"Known Plaintext counter-measure: rotating packet payload by 0x%02x bytes.\n", + payloadRotation); + if (debug>2) dump("unrotated packet",packet,*packet_len); + + /* Now rotate the payload */ + { + unsigned char temp[256]; + + /*Copy first part of payload to a temporary buffer */ + bcopy(&packet[HEADERFIELDS_LEN],&temp[0],payloadRotation); + /* Copy the main part of the payload left by the rotation factor */ + bcopy(&packet[HEADERFIELDS_LEN+payloadRotation],&packet[HEADERFIELDS_LEN], + (*packet_len)-(HEADERFIELDS_LEN)-payloadRotation); + /* Copy the temporary buffer to the end of the packet to complete the rotation */ + bcopy(&temp[0],&packet[(*packet_len)-payloadRotation],payloadRotation); + } + packet[OFS_ROTATIONFIELD]=payloadRotation; + if (debug>3) dump("rotated packet",packet,*packet_len); + + return 0; +} + +int packetAddHLRCreateRequest(unsigned char *packet,int packet_maxlen,int *packet_len) +{ + int packet_len_in=*packet_len; + + CHECK_PACKET_LEN(1); + packet[(*packet_len)++]=ACTION_CREATEHLR; + + if (debug>2) dump("Variable request octets (HLR create)",&packet[packet_len_in],(*packet_len)-packet_len_in); + + return 0; +} + +int packetAddVariableRequest(unsigned char *packet,int packet_maxlen,int *packet_len, + char *item,int instance,int start_offset,int bytes) +{ + /* Work out which item type we are asking for */ + int itemId; + int packet_len_in=*packet_len; + for(itemId=0;vars[itemId].name;itemId++) + if (!strcmp(item,vars[itemId].name)) { + break; + } + + /* Sanity check the request */ + if (!vars[itemId].name) { + if (debug) fprintf(stderr,"`%s' is not a known HLR variable.\n",item); + return setReason("Requested unknown HLR variable"); + } + itemId=vars[itemId].id; + if (instance<-1) return setReason("Asked for illegal variable value instance"); + if (instance>0xfe) return setReason("Asked for illegal variable value instance"); + if ((itemId<0x80)&&instance) return setReason("Asked for secondary value of single-value variable"); + if (start_offset<0||start_offset>0xffff) return setReason("Asked for illegal variable value starting offset"); + if (bytes<0||(start_offset+bytes)>0xffff) { + if (debug) fprintf(stderr,"Asked for %d bytes at offset %d\n",bytes,start_offset); + return setReason("Asked for illegal variable value ending offset"); + } + + /* Add request to the packet */ + CHECK_PACKET_LEN(1+1+((itemId&0x80)?1:0)+2+2); + packet[(*packet_len)++]=ACTION_GET; + packet[(*packet_len)++]=itemId; + if (instance==-1) instance=0xff; + if (itemId&0x80) packet[(*packet_len)++]=instance; + packet[(*packet_len)++]=start_offset>>8; + packet[(*packet_len)++]=start_offset&0xff; + packet[(*packet_len)++]=bytes>>8; + packet[(*packet_len)++]=bytes&0xff; + + if (debug>2) dump("Variable request octets (var)",&packet[packet_len_in],(*packet_len)-packet_len_in); + + return 0; +} + +int packetAddVariableWrite(unsigned char *packet,int packet_maxlen, + int *packet_len, + int itemId,int instance,unsigned char *value, + int start_offset,int value_len,int flags) +{ + /* Work out which item type we are asking for */ + int packet_len_in=*packet_len; + + int max_offset=start_offset+value_len-1; + + if (debug>1) printf("packetAddVariableWrite(start=%d,len=%d,flags=%d)\n",start_offset,value_len,flags); + + /* Sanity check */ + if (instance<0) return setReason("Asked for illegal variable value instance"); + if (instance>0xfe) return setReason("Asked for illegal variable value instance"); + if ((itemId<0x80)&&instance) return setReason("Asked for secondary value of single-value variable"); + if (start_offset<0||start_offset>0xffff) return setReason("Asked for illegal variable value starting offset"); + if (max_offset<0||max_offset>0xffff) return setReason("Asked for illegal variable value ending offset"); + + /* Add request to the packet */ + CHECK_PACKET_LEN(1+1+((itemId&0x80)?1:0)+2+2+1); + packet[(*packet_len)++]=ACTION_SET; + packet[(*packet_len)++]=itemId; + if (itemId&0x80) packet[(*packet_len)++]=instance; + packet[(*packet_len)++]=start_offset>>8; + packet[(*packet_len)++]=start_offset&0xff; + packet[(*packet_len)++]=value_len>>8; + packet[(*packet_len)++]=value_len&0xff; + packet[(*packet_len)++]=flags; + + if (debug>2) dump("Packet with var write header",&packet[0],*packet_len); + + CHECK_PACKET_LEN(value_len); + bcopy(&value[0],&packet[*packet_len],value_len); + (*packet_len)+=value_len; + + if (debug>2) dump("Variable request octets (write)",&packet[packet_len_in],(*packet_len)-packet_len_in); + + return 0; +} + +int extractRequest(unsigned char *packet,int *packet_ofs,int packet_len, + int *itemId,int *instance,unsigned char *value, + int *start_offset,int *bytes,int *flags) +{ + if (*packet_ofs<0||(*packet_ofs)+6>=packet_len) + return setReason("mal-formed request packet (packet too short/bad offset)"); + + *itemId=packet[(*packet_ofs)++]; + + if ((*itemId)&0x80) *instance=packet[(*packet_ofs)++]; + if (*instance==0xff) *instance=-1; + + *start_offset=packet[(*packet_ofs)++]<<8; + *start_offset|=packet[(*packet_ofs)++]; + + *bytes=packet[(*packet_ofs)++]<<8; + *bytes|=packet[(*packet_ofs)++]; + + *flags=packet[(*packet_ofs)++]; + if (debug>2) printf("Write flags = 0x%02x\n",*flags); + + if (*packet_ofs<0||(*packet_ofs)+(*bytes)>=packet_len) + { + if (debug) fprintf(stderr,"Packet offset is %d, length is %d, and asked for %d bytes.\n",*packet_ofs,packet_len,*bytes); + return setReason("mal-formed request packet (too short for claimed data)"); + } + + bcopy(&packet[*packet_ofs],value,*bytes); + (*packet_ofs)+=*bytes; + + return 0; +} + + +int extractResponses(struct in_addr sender,unsigned char *buffer,int len,struct response_set *responses) +{ + int ofs=OFS_PAYLOAD; + + while(ofscode=buffer[ofs]; + r->sender=sender; + /* XXX doesn't make sure it is SID instead of DID */ + bcopy(&buffer[HEADERFIELDS_LEN+1],r->sid,SID_SIZE); + + switch(buffer[ofs]) + { + case ACTION_EOT: + if (debug>1) fprintf(stderr,"Reached response packet EOT.\n"); + case ACTION_DECLINED: case ACTION_OKAY: + case ACTION_CREATEHLR: + r->response_len=0; break; + case ACTION_GET: + /* Followed by variable # to fetch. + XXX If variable number >=0x80 then get instance information */ + r->response_len=1; break; + case ACTION_ERROR: + r->response_len=buffer[++ofs]; + break; + case ACTION_DATA: + /* Extract variable value */ + unpackageVariableSegment(&buffer[ofs+1],len-ofs,WITHDATA,r); + break; + case ACTION_DONE: + r->value_offset=buffer[ofs+1]; + r->response_len=1; + break; + case ACTION_PAD: + /* Skip padding bytes */ + r->response_len=1+buffer[ofs+1]; + break; + case ACTION_WROTE: + /* Extract info about the variable segment that was written. + This uses the same format as the request to write it, but without the data */ + unpackageVariableSegment(&buffer[ofs+1],len-ofs,WITHOUTDATA,r); + r->response=NULL; + break; + case ACTION_SET: + case ACTION_DEL: + case ACTION_XFER: + default: + free(r); + if (debug>1) fprintf(stderr,"Encountered unimplemented response code 0x%02x @ 0x%x\n",buffer[ofs],ofs); + return setReason("Encountered unimplemented response type"); + } + ofs++; + if (r->response_len) { + /* extract bytes of response */ + unsigned char *rr; + if (r->response) rr=r->response; else rr=&buffer[ofs]; + r->response=malloc(r->response_len+1); + if (!r->response) exit(setReason("malloc() failed.")); + bcopy(&rr[0],r->response,r->response_len); + ofs+=r->response_len; + } + + /* Work out peer ID */ + r->sender=sender; + for(r->peer_id=0;r->peer_idpeer_id++) + { + if (sender.s_addr==peers[r->peer_id]) break; + } + if (r->peer_id>peer_count) r->peer_id=-1; + + /* Link new response into chain */ + if (debug>2) printf("Linking response into response set.\n"); + r->prev=responses->last_response; + if (responses->last_response) + responses->last_response->next=r; + else + responses->responses=r; + responses->last_response=r; + responses->response_count++; + + responseFromPeer(responses,r->peer_id); + + if (debug>2) dumpResponses(responses); + } + + return 0; +} + +int packageVariableSegment(unsigned char *data,int *dlen,struct hlrentry_handle *h, + int offset,int buffer_size) +{ + int bytes; + int dlen_in=*dlen; + + if ((buffer_size-(*dlen))<8) return setReason("Insufficient buffer space for packageVariableSegment()"); + + /* Figure out how many bytes we need to package */ + bytes=buffer_size-(*dlen)-8; + if ((h->value_len-offset)value_len-offset; + if (bytes<0) bytes=0; + if (debug>1) fprintf(stderr,"Packaging %d bytes of variable\n",bytes); + + /* Describe variable */ + + /* Variable id and instance # (if required) */ + data[(*dlen)++]=h->var_id; + if (h->var_id&0x80) data[(*dlen)++]=h->var_instance; + + /* Variable length */ + data[(*dlen)++]=h->value_len>>8; + data[(*dlen)++]=h->value_len&0xff; + + /* Start offset in this segment */ + data[(*dlen)++]=(offset>>8)&0xff; + data[(*dlen)++]=offset&0xff; + + /* Number of bytes in this segment */ + data[(*dlen)++]=(bytes>>8)&0xff; + data[(*dlen)++]=bytes&0xff; + if (debug>1) fprintf(stderr,"Packaging %d bytes\n",bytes); + + /* Package the variable value itself (or part thereof) */ + bcopy(&h->value[offset],&data[*dlen],bytes); + (*dlen)+=bytes; + + if (debug>2) dump("Variable segment octets",&data[dlen_in],(*dlen)-dlen_in); + + return 0; +} + +int unpackageVariableSegment(unsigned char *data,int dlen,int flags,struct response *r) +{ + r->response_len=0; + if (dlen<7) return setReason("unpackageVariableSegment() fed insufficient data"); + + r->var_id=data[r->response_len++]; + if (r->var_id&0x80) r->var_instance=data[r->response_len++]; else r->var_instance=0; + if (r->var_instance==0xff) r->var_instance=-1; + + r->value_len=data[r->response_len++]<<8; + r->value_len|=data[r->response_len++]; + + r->value_offset=data[r->response_len++]<<8; + r->value_offset|=data[r->response_len++]; + + r->value_bytes=data[r->response_len++]<<8; + r->value_bytes|=data[r->response_len++]; + + r->response=&data[r->response_len]; + + r->response_len+=r->value_bytes; + + if (flags!=WITHOUTDATA) + if (r->response_len>dlen) + return setReason("unpackageVariableSegment() fed insufficient or corrupt data"); + + return 0; +} diff --git a/peers.c b/peers.c new file mode 100644 index 00000000..6771f122 --- /dev/null +++ b/peers.c @@ -0,0 +1,126 @@ +/* +Serval Distributed Numbering Architecture (DNA) +Copyright (C) 2010 Paul Gardner-Stephen + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "mphlr.h" + +char *batman_socket=NULL; + +int peer_count=0; +in_addr_t peers[MAX_PEERS]; +unsigned char peer_replied[MAX_PEERS]; + +in_addr_t nominated_peers[256]; +int nom_peer_count=0; + +int additionalPeer(char *peer) +{ + in_addr_t pa; + + if (nom_peer_count>255) return setReason("Too many peers. You can only nominate 255 peers in this version."); + + pa=inet_addr(peer); + if (pa==INADDR_NONE) return setReason("Invalid peer address specified."); + nominated_peers[nom_peer_count++]=pa; + + return 0; +} + +int getPeerList() +{ + /* Generate the list of known peers. + If using BATMAN layer 3, this needs to be the list of exact IP addresses of the peers, + as we cannot reliably broadcast. + Once BATMAN Advanced is available, we will be able to do that. + In the mean time, we need to query BATMANd to find the known list of peers. This is not + quite as easy as we might wish. + + Also, while using layer 3 routing we should keep note of which nodes have repied so that + we can not waste bandwidth by resending to them. For this purpose we maintain a set of + flags, peer_replied[], which is set to zero by us, and then set non-zero if that peer + solicits a reply, letting us know that we can suppress resends to that address. + + Broadcasting to interfaces is a special problem for managing replies, as we should never mark those + peers as replied. We will do this by setting their peer_replied[] flag to 2 instead of zero. + */ + int i; + + peer_count=0; + + /* Add user specified peers */ + for(i=0;i1) fprintf(stderr,"Sending packet to peer #%d\n",i); + + int r=sendto(sock,packet,packet_len,0,(struct sockaddr *)&peer_addr,sizeof(peer_addr)); + if (r1) fprintf(stderr,"Sent request to peer %s\n",inet_ntoa(peer_addr.sin_addr)); + n++; + /* If sending to only one peer, return now */ + if (method==i) break; + } + } + else + if (debug>1) fprintf(stderr,"Peer %s has already replied, so not sending again.\n", + inet_ntoa(peer_addr.sin_addr)); + + if (debug) fprintf(stderr,"Sent request to %d peers.\n",n); + + return 0; + +} diff --git a/responses.c b/responses.c new file mode 100644 index 00000000..43e9b741 --- /dev/null +++ b/responses.c @@ -0,0 +1,104 @@ +/* +Serval Distributed Numbering Architecture (DNA) +Copyright (C) 2010 Paul Gardner-Stephen + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "mphlr.h" + +int clearResponse(struct response **response) +{ + while(*response) + { + struct response *r=*response; + *response=(*response)->next; + if (r->response) free(r->response); + r->response=NULL; + free(r); + } + return 0; +} + +int eraseLastResponse(struct response_set *responses) +{ + if (!responses) return -1; + if (responses->last_response) + { + struct response *newtail; + if (responses->last_response->prev) responses->last_response->prev->next=NULL; + newtail=responses->last_response->prev; + if (responses->responses==responses->last_response) responses->responses=NULL; + clearResponse(&responses->last_response); + responses->last_response=newtail; + responses->response_count--; + } + return 0; +} + +int responseFromPeer(struct response_set *responses,int peerId) +{ + if (peerId<0||peerId>peer_count) return -1; + if (!responses) return -1; + if (!responses->reply_bitmask) + { + responses->reply_bitmask=calloc(1,(peer_count>>3)+(peer_count&7)?1:0); + if (!responses->reply_bitmask) return -1; + } + + int byte=peerId>>3; + int bit=peerId&7; + + responses->reply_bitmask[byte]|=1<reply_bitmask) return 0; + + if (peerId<0||peerId>peer_count) return 0; + + int byte=peerId>>3; + int bit=peerId&7; + + return responses->reply_bitmask[byte]&(1<responses; + while(r) + { + struct response *rr=r; + r=r->next; + free(rr); + } + + if (responses->reply_bitmask) free(responses->reply_bitmask); + responses->reply_bitmask=NULL; + + responses->last_response=NULL; + responses->responses=NULL; + responses->response_count=0; + return 0; +} diff --git a/server.c b/server.c new file mode 100644 index 00000000..56587511 --- /dev/null +++ b/server.c @@ -0,0 +1,382 @@ +/* +Serval Distributed Numbering Architecture (DNA) +Copyright (C) 2010 Paul Gardner-Stephen + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "mphlr.h" + +unsigned char *hlr=NULL; +int hlr_size=0; + +struct sockaddr recvaddr; +struct in_addr client_addr; +int client_port; + +int server(char *backing_file,int size,int foregroundMode) +{ + + struct sockaddr_in bind_addr; + + /* Get backing store */ + if (!backing_file) + { + /* transitory storage of HLR data, so just malloc() the memory */ + hlr=calloc(size,1); + if (!hlr) exit(setReason("Failed to calloc() HLR database.")); + if (debug) fprintf(stderr,"Allocated %d byte temporary HLR store\n",size); + } + else + { + unsigned char zero[8192]; + FILE *f=fopen(backing_file,"r+"); + if (!f) f=fopen(backing_file,"w+"); + if (!f) exit(setReason("Could not open backing file.")); + bzero(&zero[0],8192); + fseek(f,0,SEEK_END); + errno=0; + while(ftell(f)sin_port; + bzero((void *)&recvaddr,sizeof(recvaddr)); + fds.fd=sock; fds.events=POLLIN; + + /* Wait patiently for packets to arrive */ + while (poll(&fds,1,1000)<1) sleep(0); + + int len=recvfrom(sock,buffer,sizeof(buffer),0,&recvaddr,&recvaddrlen); + client_addr=((struct sockaddr_in*)&recvaddr)->sin_addr; + if (debug) fprintf(stderr,"Received packet from %s (len=%d).\n",inet_ntoa(client_addr),len); + if (debug>1) dump("recvaddr",(unsigned char *)&recvaddr,recvaddrlen); + if (debug>3) dump("packet",(unsigned char *)buffer,len); + if (dropPacketP(len)) { + if (debug) fprintf(stderr,"Simulation mode: Dropped packet due to simulated link parameters.\n"); + continue; + } + if (!packetOk(buffer,len,NULL)) process_packet(buffer,len,&recvaddr,recvaddrlen); + else { + if (debug) setReason("Ignoring invalid packet"); + } + if (debug>1) fprintf(stderr,"Finished processing packet, waiting for next one.\n"); + } +} + +int processRequest(unsigned char *packet,int len, + struct sockaddr *sender,int sender_len, + unsigned char *transaction_id,char *did,char *sid) +{ + /* Find HLR entry by DID or SID, unless creating */ + int ofs,rofs; + int records_searched=0; + + int prev_pofs=0; + int pofs=OFS_PAYLOAD; + + while(pofs1) fprintf(stderr," processRequest: len=%d, pofs=%d, pofs_prev=%d\n",len,pofs,prev_pofs); + /* Avoid infinite loops */ + if (pofs<=prev_pofs) break; + prev_pofs=pofs; + + if (packet[pofs]==ACTION_CREATEHLR) + { + /* Creating an HLR requires an initial DID number and definately no SID - + you can't choose a SID. */ + if (debug>1) fprintf(stderr,"Creating a new HLR record. did='%s', sid='%s'\n",did,sid); + if (!did[0]) return respondSimple(NULL,ACTION_DECLINED,NULL,0,transaction_id); + if (sid[0]) return respondSimple(sid,ACTION_DECLINED,NULL,0,transaction_id); + if (debug>1) fprintf(stderr,"Verified that create request supplies DID but not SID\n"); + + { + char sid[128]; + /* make HLR with new random SID and initial DID */ + if (!createHlr(did,sid)) + return respondSimple(sid,ACTION_OKAY,NULL,0,transaction_id); + else + return respondSimple(NULL,ACTION_DECLINED,NULL,0,transaction_id); + } + pofs+=1; + pofs+=1+SID_SIZE; + } + else + { + switch(packet[pofs]) + { + case ACTION_PAD: /* Skip padding */ + pofs++; + pofs+=1+packet[pofs]; + break; + case ACTION_EOT: /* EOT */ + pofs=len; + break; + case ACTION_SET: + ofs=0; + if (debug>1) fprintf(stderr,"Looking for hlr entries with sid='%s' / did='%s'\n",sid,did); + while(findHlr(hlr,&ofs,sid,did)) + { + if (debug>1) fprintf(stderr,"findHlr found a match for writing at 0x%x\n",ofs); + if (debug>2) hlrDump(hlr,ofs); + + /* XXX consider taking action on this HLR + (check PIN first depending on the action requested) */ + + int itemId,instance,start_offset,bytes,flags; + unsigned char value[9000],oldvalue[65536]; + int oldr,oldl; + + /* XXX Doesn't verify PIN authentication */ + + /* Get write request */ + if ((!sid)||(!sid[0])) { + setReason("You can only set variables by SID"); + return + respondSimple(NULL,ACTION_ERROR,(unsigned char *)"SET requires authentication by SID",0,transaction_id); + } + + pofs++; rofs=pofs; + if (extractRequest(packet,&pofs,len, + &itemId,&instance,value, + &start_offset,&bytes,&flags)) + { + setReason("Could not extract ACTION_SET request"); + return + respondSimple(NULL,ACTION_ERROR,(unsigned char *)"Mal-formed SET request",0,transaction_id); + } + + /* Get the stored value */ + oldl=65536; + oldr=hlrGetVariable(hlr,ofs,itemId,instance,oldvalue,&oldl); + if (oldr) { + if (flags==SET_NOREPLACE) { + oldl=0; + } else { + setReason("Tried to SET_NOCREATE/SET_REPLACE a non-existing value"); + return + respondSimple(NULL,ACTION_ERROR, + (unsigned char *)"Cannot SET NOCREATE/REPLACE a value that does not exist", + 0,transaction_id); + } + } else { + if (flags==SET_NOREPLACE) { + setReason("Tried to SET_NOREPLACE an existing value"); + if (debug>1) dump("Existing value",oldvalue,oldl); + return + respondSimple(NULL,ACTION_ERROR, + (unsigned char *)"Cannot SET NOREPLACE; a value exists", + 0,transaction_id); + } + } + /* Replace the changed portion of the stored value */ + if ((start_offset+bytes)>oldl) { + bzero(&oldvalue[oldl],start_offset+bytes-oldl); + oldl=start_offset+bytes; + } + bcopy(&value[0],&oldvalue[start_offset],bytes); + + /* Write new value back */ + if (hlrSetVariable(hlr,ofs,itemId,instance,oldvalue,oldl)) + { + setReason("Failed to write variable"); + return + respondSimple(NULL,ACTION_ERROR,(unsigned char *)"Failed to SET variable",0,transaction_id); + } + if (debug>2) { fprintf(stderr,"HLR after writing:\n"); hlrDump(hlr,ofs); } + + /* Reply that we wrote the fragment */ + respondSimple(sid,ACTION_WROTE,&packet[rofs],6, + transaction_id); + /* Advance to next record and keep searching */ + if (nextHlr(hlr,&ofs)) break; + } + break; + case ACTION_GET: + ofs=0; + if (debug>1) fprintf(stderr,"Looking for hlr entries with sid='%s' / did='%s'\n",sid,did); + while(findHlr(hlr,&ofs,sid,did)) + { + if (debug>1) fprintf(stderr,"findHlr found a match at 0x%x\n",ofs); + if (debug>2) hlrDump(hlr,ofs); + + /* XXX consider taking action on this HLR + (check PIN first depending on the action requested) */ + + /* Form a reply packet containing the requested data */ + int var_id=packet[pofs+1]; + int instance=packet[pofs+2]; + int offset=(packet[pofs+3]<<8)+packet[pofs+4]; + int sendDone=0; + + struct hlrentry_handle *h; + + if (instance==0xff) instance=-1; + + if (debug>1) fprintf(stderr,"Responding to ACTION_GET (var_id=%02x, instance=%02x, pofs=0x%x, len=%d)\n",var_id,instance,pofs,len); + if (debug>2) dump("Request bytes",&packet[pofs],8); + /* Step through HLR to find any matching instances of the requested variable */ + h=openhlrentry(hlr,ofs); + if (debug>1) fprintf(stderr,"openhlrentry(hlr,%d) returned %p\n",ofs,h); + while(h) + { + /* Is this the variable? */ + if (debug>2) fprintf(stderr," considering var_id=%02x, instance=%02x\n", + h->var_id,h->var_instance); + if (h->var_id==var_id) + { + if (h->var_instance==instance||instance==-1) + { + /* Limit transfer size to MAX_DATA_BYTES, plus an allowance for variable packing. */ + unsigned char data[MAX_DATA_BYTES+16]; + int dlen=0; + + if (debug>1) fprintf(stderr,"Sending matching variable value instance (instance #%d), value offset %d.\n", + h->var_instance,offset); + + if (packageVariableSegment(data,&dlen,h,offset,MAX_DATA_BYTES+16)) + return setReason("packageVariableSegment() failed."); + + respondSimple(hlrSid(hlr,ofs),ACTION_DATA,data,dlen,transaction_id); + if (instance==-1) sendDone++; + } + else + if (debug>2) fprintf(stderr,"Ignoring variable instance %d (not %d)\n", + h->var_instance,instance); + } + else + if (debug>2) fprintf(stderr,"Ignoring variable ID %d (not %d)\n", + h->var_id,var_id); + h=hlrentrygetent(h); + } + if (sendDone) + { + unsigned char data[1]; + data[0]=sendDone&0xff; + respondSimple(hlrSid(hlr,ofs),ACTION_DONE,data,1,transaction_id); + } + + /* Advance to next record and keep searching */ + if (nextHlr(hlr,&ofs)) break; + } + + pofs+=7; + break; + default: + setReason("Asked to perform unsupported action"); + if (debug) fprintf(stderr,"Packet offset = 0x%x\n",pofs); + if (debug) dump("Packet",packet,len); + return -1; + } + } + } + + if (debug>1) fprintf(stderr,"Searched %d HLR entries.\n",records_searched); + + return 0; +} + +int respondSimple(char *sid,int action,unsigned char *action_text,int action_len, + unsigned char *transaction_id) +{ + unsigned char packet[8000]; + int pl=0; + int *packet_len=&pl; + int packet_maxlen=8000; + int i; + + /* ACTION_ERROR is associated with an error message. + For syntactic simplicity, we do not require the respondSimple() call to provide + the length of the error message. */ + if (action==ACTION_ERROR) { + action_len=strlen((char *)action_text); + /* Make sure the error text isn't too long. + IF it is, trim it, as we still need to communicate the error */ + if (action_len>255) action_len=255; + } + + /* Prepare the request packet */ + if (packetMakeHeader(packet,8000,packet_len,transaction_id)) return -1; + if (sid&&sid[0]) + { if (packetSetSid(packet,8000,packet_len,sid)) + return setReason("invalid SID in reply"); } + else + { if (packetSetDid(packet,8000,packet_len,"")) + return setReason("Could not set empty DID in reply"); } + + CHECK_PACKET_LEN(1+1+action_len); + packet[(*packet_len)++]=action; + if (action==ACTION_ERROR) packet[(*packet_len)++]=action_len; + for(i=0;i2) dump("Simple response octets",action_text,action_len); + + if (packetFinalise(packet,8000,packet_len)) return -1; + + if (debug) fprintf(stderr,"Sending response of %d bytes.\n",*packet_len); + + if (packetSendRequest(REQ_REPLY,packet,*packet_len,NONBATCH,transaction_id,NULL)) return -1; + + return 0; +} diff --git a/simulate.c b/simulate.c new file mode 100644 index 00000000..0bf6c2f7 --- /dev/null +++ b/simulate.c @@ -0,0 +1,41 @@ +/* +Serval Distributed Numbering Architecture (DNA) +Copyright (C) 2010 Paul Gardner-Stephen + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "mphlr.h" + +double simulatedBER=0; + +/* + We use this function to simulate a lossy link so that we can easily bench-test the + retransmission protocols. + */ +int dropPacketP(int packet_len) +{ + int i,b; + + long berThreshold=0x7fffffff*simulatedBER; + + if (!simulatedBER) return 0; + + for(i=0;i + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#ifndef HAVE_SRANDOMDEV + +#include + +#include +#include +#include +#include + +void +srandomdev(void) +{ + struct timeval tv; + unsigned int seed; + int fd; + + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0) { + read(fd, &seed, sizeof seed); + close(fd); + } else { + gettimeofday(&tv, NULL); + /* NOTE: intentional use of uninitialized variable */ + seed ^= (getpid() << 16) ^ tv.tv_sec ^ tv.tv_usec; + } + srandom(seed); +} +#endif diff --git a/testdna b/testdna new file mode 100755 index 00000000..90c1ca35 --- /dev/null +++ b/testdna @@ -0,0 +1,315 @@ +#!/bin/bash + +# Stop any existing DNA server +pids=`ps -ef | grep "\./dna" | awk '{ print $2;}' | wc -l` +if [ $pids -gt 0 ]; then + kill `ps -ef | grep "\./dna" | awk '{ print $2;}'` +fi + +# Start DNA server + +if [ -e hlr.dat ]; then + rm hlr.dat +fi +./dna -n -vvv -S 1 -f hlr.dat > dnatest.log 2>&1 & +sleep 1 + +# Test creating a new subscriber record +# Test: Should return in <<3 sec, rather than time out +# Test: Should return OK:SID +/usr/bin/time -p ./dna -d 0427679796 -C > dnatest.tmp 2>&1 +grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Create HLR was fast."; else print "FAIL: Create took too long."; }' +sid=`grep "^OK:" dnatest.tmp | cut -f2 -d:` +if [ "x$sid" = "x" ]; then + echo "FAIL: Create HLR failed (no OK:SID reply)" + echo -n "Got: " + cat dnatest.tmp + echo + echo "FATAL: Cannot continue" + kill %1 + exit 3 +else + echo "OK: Create HLR returned a SID" +fi + +# Test getting short variables +/usr/bin/time -p ./dna -d 0427679796 -R dids > dnatest.tmp 2>&1 +grep "^real" dnatest.tmp | awk '{ if ($2<2.9) printf("FAIL: Read variable didnt wait for all replies (only waited %f sec)\n",$2); else print "OK: Read variable waited for all replies."; }' +ok=`grep "^DIDS:${sid}:0:0427679796" dnatest.tmp | wc -l` +if [ $ok -lt 1 ]; then + echo "FAIL: Read variable didn't find existing value" + echo "FAIL: Search by DID may not work." + echo -n "Got: " ; cat dnatest.tmp | egrep -v "^real|^sys|^user" +else + echo "OK: Read variable correctly read an existing value" + if [ $ok -gt 1 ]; then + echo "FAIL: Compulsory retry policy does not filter duplicate results (got $ok copies)" + echo "FAIL: Search by DID has problems" + else + echo "OK: Compulsory retry policy filters duplicate results." + echo "OK: Search by DID works." + fi +fi + +# Test setting short variables +/usr/bin/time -p ./dna -s $sid -i 0 -W note="a short literal value" > dnatest.tmp 2>&1 +grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Set variable by SID was fast."; else print "FAIL: Set variable by SID was too slow."; }' +ok=`grep "^WROTE:$sid" dnatest.tmp | wc -l` +if [ $ok -ne 1 ]; then + echo "FAIL: Set new variable value failed." + echo -n "Got: " ; cat dnatest.tmp | egrep -v "^real|^sys|^user" + echo "Wanted: WROTE:$sid" +else + echo "OK: Set new variable value succeeded." +fi + +# Test getting short variable by SID, and that setting variables really works. +/usr/bin/time -p ./dna -s $sid -i 0 -R note > dnatest.tmp 2>&1 +grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Get variable by SID was fast."; else printf("FAIL: Get variable by SID was too slow (%s sec)\n",$2); }' +ok=`grep "^NOTE:${sid}:0:a short literal value" dnatest.tmp | wc -l` +if [ $ok -ne 1 ]; then + echo "FAIL: Set variable value could not be read back." + echo -n "Got: " ; cat dnatest.tmp | egrep -v "^real|^sys|^user" + echo "FAIL: Search by SID may not work." +else + echo "OK: Set variable value could be read back." + echo "OK: Search by SID works." +fi + +# Test directing output to a file +echo "WARN: Output to file does not work with DID lists (known issue)" +if [ -e dnatest.dat ]; then + rm dnatest.dat +fi +/usr/bin/time -p ./dna -s $sid -O dnatest.dat -i 0 -R note > dnatest.tmp 2>&1 +grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Get variable by SID was fast."; else printf("FAIL: Get variable by SID was too slow (%s sec)\n",$2); }' +ok=`grep "^NOTE:${sid}:0" dnatest.tmp | wc -l` +if [ $ok -ne 1 ]; then + echo "FAIL: Set variable value could not be read back without output going to file." +else + echo "OK: Set variable value could be read back with output going to file." +fi +ok=`grep "a short literal value" dnatest.dat | wc -l` +if [ $ok -ne 1 ]; then + echo "FAIL: Variable value could not be read from output file." +else + echo "OK: Variable values can be read from output files." +fi +rm dnatest.dat + +# Test setting multiple instances of short variables +/usr/bin/time -p ./dna -s $sid -i 1 -W note='$414243' >dnatest.tmp 2>&1 +ok=`grep "^WROTE:$sid" dnatest.tmp | wc -l` +if [ $ok -ne 1 ]; then + echo "FAIL: Set new variable value by instance failed." + echo -n "Got: " ; cat dnatest.tmp | egrep -v "^real|^sys|^user" +else + echo "OK: Set new variable value by instance succeeded." +fi + +# Test reading multiple instances of a variable by SID +echo "NOTE:${sid}:0:a short literal value" >dnatest.txt +echo "NOTE:${sid}:1:ABC" >>dnatest.txt +echo "DONE:${sid}:2" >>dnatest.txt +./dna -s $sid -i -1 -R note > dnatest.tmp 2>&1 +ok=`diff dnatest.tmp dnatest.txt | wc -l` +if [ $ok -ne 0 ]; then + echo "FAIL: Reading multiple variable instances has problems." + echo diff dnatest.tmp dnatest.txt + diff dnatest.tmp dnatest.txt + set ok=`grep ':ABC$' dnatest.tmp | wc -l` + if [ $ok != 1 ]; then + echo "FAIL: Specifying variable values by hex may have problems." + else + echo "OK: Specifying variable values by hex works." + fi + diff dnatest.tmp dnatest.txt +else + echo "OK: Reading multiple variable instances works." + echo "OK: Specifying variable values by hex works." +fi +if [ -e dnatest.tmp ]; then + rm dnatest.tmp +fi +/usr/bin/time -p ./dna -s $sid -R notes > dnatest.tmp 2>&1 +grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Reading multiple variable instances by SID is fast."; else print "FAIL: Reading multiple variable instances by SID was too slow."; }' + +# Try overwriting an existing variable value without update mode +/usr/bin/time -p ./dna -s $sid -i 0 -W note="replacement short literal value" > dnatest.tmp 2>&1 +grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Set variable by SID was fast."; else print "FAIL: Set variable by SID was too slow."; }' +ok=`grep "^WROTE:$sid" dnatest.tmp | wc -l` +if [ $ok -eq 1 ]; then + echo "FAIL: Accidental overwrite variable value was allowed." +else + echo "OK: Accidental overwrite variable value prevented." +fi + +# Try overwriting an existing variable value with update mode +/usr/bin/time -p ./dna -s $sid -i 0 -U note="replacement short literal value" > dnatest.tmp 2>&1 +grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Set variable by SID was fast."; else print "FAIL: Set variable by SID was too slow."; }' +ok=`grep "^WROTE:$sid" dnatest.tmp | wc -l` +if [ $ok -ne 1 ]; then + echo "FAIL: Purposeful overwrite variable value failed." +else + echo "OK: Purposeful overwrite variable value works." +fi + +# Try writing a long value that is too long to fit in one packet. +if [ -e dnatest.in ]; then + rm dnatest.in +fi +i=0 +while [ $i -lt 100 ]; do + echo "${i}ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-${i}" >>dnatest.in + let i=i+1 +done +/usr/bin/time -p ./dna -s $sid -i 0 -U note="@dnatest.in" > dnatest.tmp 2>&1 +ok=`grep "^WROTE:$sid" dnatest.tmp | wc -l` +if [ $ok -ne 1 ]; then + echo "FAIL: Setting long variable values failed." + echo "FAIL: Setting variable value from a file failed." +else + echo "OK: Setting long variable value might work (assuming it can be read back)." + echo "OK: Setting variable value from a file work (assuming it can be read back)." +fi + +if [ -e dnatest.out ]; then + rm dnatest.out +fi +/usr/bin/time -p ./dna -vvv -s $sid -O dnatest.out -i 0 -R note 2>&1 | tee dnatest.longread.log > dnatest.tmp 2>&1 +ok=`grep "^NOTE:${sid}:0" dnatest.tmp | wc -l` +if [ $ok -ne 1 ]; then + echo "FAIL: Reading long variable values failed." +else + echo "OK: Reading long variable values might work." +fi +ok=`diff dnatest.in dnatest.out | wc -l` +if [ $ok -ne 0 ]; then + echo "FAIL: Long variable value could not be read via multi-packet transactions." +else + echo "OK: Long variable values can be read via multi-packet transactions." +fi +ls -l dnatest.out dnatest.in +rm dnatest.out dnatest.in + +# +# Run tests that exercise the bit error simulation +# +# Kill old server +kill %1 +wait +# Start DNA server with a BER of 10E-3 +rm hlr.dat +./dna -n -vvv -B 0.001 -S 1 -f hlr.dat >> dnatest.log 2>&1 & +sleep 1 + +# Test creating a new subscriber record +# Test: should run in variable time, but never longer than it needs +# Test: Should return OK:SID +iterations=10 +i=0 +fails=0 +totaltime=0 +maxtime=0 + +while [ $i -lt $iterations ]; do + /usr/bin/time -p ./dna -B 0.001 -d 0427679796 -C > dnatest.tmp 2>&1 + + thistime=`grep "^real" dnatest.tmp | awk '{ printf("%d\n",$2*1000/'$iterations');}'` + let totaltime=totaltime+thistime + if [ $thistime -gt $maxtime ]; then + maxtime=$thistime + fi + + sid=`grep "^OK:" dnatest.tmp | cut -f2 -d:` + if [ "x$sid" = "x" ]; then + let fails=fails+1 + else + echo " OK after ${thistime}0 ms" + fi + + let i=i+1 +done +if [ $totaltime -lt 25 ]; then + echo "FAIL: Packet loss injection may have problems (no packet loss injected)" +else + echo "OK: Packet loss injection (test mode) works." +fi +if [ $fails -gt 4 ]; then + echo "FAIL: Create HLR too unreliable when faced with packet loss ($fails out of $iterations failed)" +else + echo "OK: Create HLR works in the face of intense packet loss (simulated BER = 10E-3), avg time = $totaltime ms" +fi +if [ $totaltime -gt 3000 -o $maxtime -gt 330 ]; then + echo "FAIL: Timeout routinely or grossly exceeded when employing retries in the face of intense packet loss" +else + echo "OK: Timeout behaves correctly in the face of packet loss" +fi + +# Try writing a long value that is too long to fit in one packet. + +# Kill old server +kill %1 +wait +# Start DNA server with a BER of 10E-4 +ber=0.00001 +rm hlr.dat +./dna -n -vvv -B $ber -S 1 -f hlr.dat >> dnatest.log 2>&1 & +sleep 1 + + +if [ -e dnatest.in ]; then + rm dnatest.in +fi +i=0 +while [ $i -lt 100 ]; do + echo "${i}ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-${i}" >>dnatest.in + let i=i+1 +done +/usr/bin/time -p ./dna -B $ber -s $sid -i 0 -U note="@dnatest.in" > dnatest.tmp 2>&1 +ok=`grep "^WROTE:$sid" dnatest.tmp | wc -l` +if [ $ok -ne 1 ]; then + echo "FAIL: Setting long variable values failed in the face of packet loss" + echo "FAIL: Setting variable value from a file failed in the face of packet loss." +else + echo "OK: Setting long variable value might work with packet loss (assuming it can be read back)." + echo "OK: Setting variable value from a file work with packet loss (assuming it can be read back)." +fi + +if [ -e dnatest.out ]; then + rm dnatest.out +fi +/usr/bin/time -p ./dna -B $ber -s $sid -O dnatest.out -i 0 -R note > dnatest.tmp 2>&1 +ok=`grep "^NOTE:${sid}:0" dnatest.tmp | wc -l` +if [ $ok -ne 1 ]; then + echo "FAIL: Reading long variable values failed when faced with packet loss (either READ or WRITE cycle failed)." +else + echo "OK: Reading long variable values might work when faced with packet loss." +fi +if [ ! -e dnatest.out ]; then + echo "FAIL: Long variable value could not be read via multi-packet transactions when faced with packet loss." + rm dnatest.in +else + ok=`diff dnatest.in dnatest.out | wc -l` + if [ $ok -ne 0 ]; then + echo "FAIL: Long variable value could not be read via multi-packet transactions when faced with packet loss." + else + echo "OK: Long variable values can be read via multi-packet transactions when faced with packet loss." + fi + ls -l dnatest.out dnatest.in + rm dnatest.out dnatest.in +fi + + +# Kill server +kill %1 + +rm dnatest.tmp + +echo "FAIL: No test for parallel mode with multiple peers" +echo "FAIL: No test for fast parallel return if all peers reply" +echo "FAIL: No test for slow parallel return if not all peers reply" +echo "FAIL: No test for serial request with many peers such that the timeout is insufficient to query them all" +echo "FAIL: No test for user specified peer list" +echo "FAIL: No test for BATMAN integration." +echo "FAIL: No test for writing output to files with templated file names."