mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-04-09 12:01:15 +00:00
Initial check in
git-svn-id: http://serval-dna.googlecode.com/svn/trunk/trunk/dna@2 6d01b88e-114a-4e5d-930d-818638e25ad7
This commit is contained in:
commit
f225465dfc
36
Makefile
Normal file
36
Makefile
Normal file
@ -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
|
36
Makefile.in
Normal file
36
Makefile.in
Normal file
@ -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
|
38
TODO
Normal file
38
TODO
Normal file
@ -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
|
141
batman.c
Normal file
141
batman.c
Normal file
@ -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(ofs<bytes)
|
||||
{
|
||||
if(debug>1) fprintf(stderr,"New line @ %d\n",ofs);
|
||||
/* Check for IP address of peers */
|
||||
if (isdigit(buf[ofs]))
|
||||
{
|
||||
int i;
|
||||
for(i=0;ofs+i<bytes;i++)
|
||||
if (buf[i+ofs]==' ') {
|
||||
buf[i+ofs]=0;
|
||||
if (*peer_count<peer_max) peers[(*peer_count)++]=inet_addr((char *)&buf[ofs]);
|
||||
if (debug>1) 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]>=' '&&(ofs<bytes)) ofs++;
|
||||
while(buf[ofs]<' '&&(ofs<bytes)) ofs++;
|
||||
}
|
||||
bytes=0;
|
||||
}
|
||||
return 0;
|
||||
}
|
26
ciphers.c
Normal file
26
ciphers.c
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
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 packetDecipher(unsigned char *packet,int len,int cipher)
|
||||
{
|
||||
if (cipher) return setReason("Unknown packet cipher");
|
||||
else return 0; /* plain text */
|
||||
}
|
604
client.c
Normal file
604
client.c
Normal file
@ -0,0 +1,604 @@
|
||||
/*
|
||||
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 packetSendFollowup(struct in_addr destination,
|
||||
unsigned char *packet,int packet_len)
|
||||
{
|
||||
struct sockaddr_in peer_addr;
|
||||
peer_addr.sin_family=AF_INET;
|
||||
peer_addr.sin_port = htons(4110);
|
||||
peer_addr.sin_addr.s_addr=destination.s_addr;
|
||||
|
||||
if (!serverMode) {
|
||||
sock=socket(PF_INET,SOCK_DGRAM,0);
|
||||
if (sock<0) {
|
||||
fprintf(stderr,"Could not create UDP socket.\n");
|
||||
exit(-3);
|
||||
}
|
||||
}
|
||||
|
||||
int r=sendto(sock,packet,packet_len,0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));
|
||||
if (r<packet_len) {
|
||||
if (debug) fprintf(stderr,"Could not send to %s (r=%d, packet_len=%d)\n",inet_ntoa(destination),r,packet_len);
|
||||
perror("sendto");
|
||||
} else {
|
||||
if (debug>1) 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 (r<packet_len) {
|
||||
if (debug) fprintf(stderr,"Could not send to client %s\n",inet_ntoa(client_addr));
|
||||
} else {
|
||||
if (debug>1) 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<peer_count;i++) sendToPeers(packet,packet_len,method,i,responses);
|
||||
|
||||
/* If in serial mode, send request to peers in turn until one responds positively,
|
||||
otherwise just deal with the reply fetching loop to listen to as many or few reply. */
|
||||
for(i=peer_low;i<=peer_high;i++) {
|
||||
struct response *rr;
|
||||
if (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;o<value_length;o+=MAX_DATA_BYTES)
|
||||
{
|
||||
int bytes=MAX_DATA_BYTES;
|
||||
if (o+bytes>value_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_bytes<r->value_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;i<recv_map_size;i++) recv_map[i]=0;
|
||||
|
||||
/* Ask for all remaining pieces in parallel, then keep track of what has arrived
|
||||
XXX - Not yet implemented. Currently uses a slow serial method, worse than TFTP */
|
||||
needMoreData=recv_map_size-1;
|
||||
|
||||
while(needMoreData&&(tries++<15))
|
||||
{
|
||||
if (debug>1) fprintf(stderr,"Multi-packet request: try %d, %d fragments remaining.\n",tries,needMoreData);
|
||||
needMoreData=0;
|
||||
for(i=0;i<recv_map_size;i++)
|
||||
if (!recv_map[i])
|
||||
{
|
||||
needMoreData++;
|
||||
offset=i*MAX_DATA_BYTES;
|
||||
|
||||
if (debug>1) 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;
|
||||
}
|
14
configure.in
Normal file
14
configure.in
Normal file
@ -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)
|
191
dataformats.c
Normal file
191
dataformats.c
Normal file
@ -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))
|
||||
{
|
||||
switch(did[d])
|
||||
{
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
nybl=did[d]-'0'; break;
|
||||
case '*': nybl=0xa; break;
|
||||
case '#': nybl=0xb; break;
|
||||
case '+': nybl=0xc; break;
|
||||
default:
|
||||
setReason("Illegal digits in DID number");
|
||||
return -1;
|
||||
}
|
||||
if (highP) { packet[*ofs]=nybl<<4; highP=0; }
|
||||
else {
|
||||
packet[(*ofs)++]|=nybl; highP=1;
|
||||
}
|
||||
d++; len++;
|
||||
}
|
||||
if (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<DID_MAXSIZE;len++) packet[(*ofs)++]=random()&0xff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int extractSid(unsigned char *packet,int *ofs,char *sid)
|
||||
{
|
||||
int i=0;
|
||||
int d=0;
|
||||
for(i=0;i<SID_SIZE;i++)
|
||||
{
|
||||
sid[d++]=hexdigit[packet[*ofs]>>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;i<SID_SIZE;i++)
|
||||
{
|
||||
if (hexvalue(sid[i<<1])<0) return -1;
|
||||
packet[ofs]=hexvalue(sid[i<<1])<<4;
|
||||
if (hexvalue(sid[(i<<1)+1])<0) return -1;
|
||||
packet[ofs++]|=hexvalue(sid[(i<<1)+1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int packetGetID(unsigned char *packet,int len,char *did,char *sid)
|
||||
{
|
||||
int ofs=HEADERFIELDS_LEN;
|
||||
|
||||
switch(packet[ofs])
|
||||
{
|
||||
case 0: /* DID */
|
||||
ofs++;
|
||||
if (extractDid(packet,&ofs,did)) return setReason("Could not decode DID");
|
||||
if (debug>1) 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;i<start+count;i++)
|
||||
{
|
||||
mod+=packet[i];
|
||||
mod&=0xff;
|
||||
}
|
||||
|
||||
if (debug>3) {
|
||||
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;
|
||||
}
|
||||
|
351
dna.c
Normal file
351
dna.c
Normal file
@ -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<len;i++) c[i]=0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int dump(char *name,unsigned char *addr,int len)
|
||||
{
|
||||
int i,j;
|
||||
fprintf(stderr,"Dump of %s\n",name);
|
||||
for(i=0;i<len;i+=16)
|
||||
{
|
||||
fprintf(stderr," %04x :",i);
|
||||
for(j=0;j<16&&(i+j)<len;j++) fprintf(stderr," %02x",addr[i+j]);
|
||||
for(;j<16;j++) fprintf(stderr," ");
|
||||
fprintf(stderr," ");
|
||||
for(j=0;j<16&&(i+j)<len;j++) fprintf(stderr,"%c",addr[i+j]>=' '&&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;i++) if (text[i]=='=') break;
|
||||
for(v=0;vars[v].name;v++) if (!strncasecmp(vars[v].name,(char *)text,i)) break;
|
||||
|
||||
if (!vars[v].name) return setReason("Illegal variable name in assignment");
|
||||
*var_id=vars[v].id;
|
||||
|
||||
i++;
|
||||
switch(text[i])
|
||||
{
|
||||
case '$': /* hex */
|
||||
i++;
|
||||
while(i<tlen) {
|
||||
int b=hexvalue(text[i++])<<4;
|
||||
if (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 <hlr size in MB> [-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 (optind<argc) usage("Extraneous options after HLR creation request");
|
||||
if ((!did)||(sid)) usage("Specify exactly one DID and no SID to create a new HLR entry");
|
||||
return requestNewHLR(did,pin,sid);
|
||||
}
|
||||
break;
|
||||
case 'O': /* output to templated files */
|
||||
if (outputtemplate) usage("You can only specify -O once");
|
||||
outputtemplate=strdup(optarg);
|
||||
break;
|
||||
default:
|
||||
usage("Invalid option");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind<argc) usage("Extraneous options at end of command");
|
||||
|
||||
if (hlr_file&&clientMode) usage("Only servers use backing files");
|
||||
if (serverMode&&clientMode) usage("You asked me to be both server and client. That's silly.");
|
||||
if (serverMode) return server(hlr_file,hlr_size,foregroundMode);
|
||||
if (!clientMode) usage("Mesh Potato Home Location Register (HLR) Tool.");
|
||||
|
||||
/* Client mode: */
|
||||
return 0;
|
||||
}
|
437
hlrdata.c
Normal file
437
hlrdata.c
Normal file
@ -0,0 +1,437 @@
|
||||
/*
|
||||
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 bcompare(unsigned char *a,unsigned char *b,size_t len)
|
||||
{
|
||||
int i;
|
||||
for(i=0;i<len;i++) if (a[i]<b[i]) return -1; else if (a[i]>b[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_len<DID_MAXSIZE;pid_len++) if ((packed_id[pid_len]&0x0f)==0x0f) { pid_len++; break; }
|
||||
if (debug>1) 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;pdidlen<DID_MAXSIZE;pdidlen++) if (packeddid[pdidlen-1]==0xff) break;
|
||||
hlrSetVariable(hlr,record_offset,VAR_DIDS,0x00,packeddid,pdidlen);
|
||||
}
|
||||
|
||||
if (debug) fprintf(stderr,"Created new 36 byte HLR record for DID=[%s] @ 0x%x with SID=[%s]\n",
|
||||
did,record_offset,sid);
|
||||
if (debug>2) 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_id<varid)
|
||||
||(h->var_id==varid&&h->var_instance<varinstance))
|
||||
hlr_offset=h->entry_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_id<varid)
|
||||
||(h->var_id==varid&&h->var_instance<varinstance))
|
||||
{
|
||||
hlr_offset=h->entry_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;
|
||||
}
|
1
meshpotato_build
Normal file
1
meshpotato_build
Normal file
@ -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
|
319
mphlr.h
Normal file
319
mphlr.h
Normal file
@ -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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
#include <arpa/inet.h>
|
||||
#else
|
||||
typedef unsigned int in_addr_t;
|
||||
struct in_addr {
|
||||
in_addr_t s_addr;
|
||||
};
|
||||
#endif
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_MMAN_H
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#ifdef HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#ifdef HAVE_CTYPE_H
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <net/if.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
|
||||
/* 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);
|
31
openwrt-makefile
Normal file
31
openwrt-makefile
Normal file
@ -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))
|
||||
|
518
packetformats.c
Normal file
518
packetformats.c
Normal file
@ -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 (len<HEADERFIELDS_LEN) return setReason("Packet is too short");
|
||||
if (packet[0]!=0x41||packet[1]!=0x10) return setReason("Packet has incorrect magic value");
|
||||
|
||||
version=(packet[2]<<8)|packet[3];
|
||||
length=(packet[4]<<8)|packet[5];
|
||||
cipher=(packet[6]<<8)|packet[7];
|
||||
if (version!=1) return setReason("Unknown packet format version");
|
||||
if (cipher!=0) return setReason("Unknown packet cipher");
|
||||
if (length!=len) return setReason("Packet length incorrect");
|
||||
|
||||
if (cipher) if (packetDecipher(packet,len,cipher)) return setReason("Could not decipher packet");
|
||||
|
||||
/* Make sure the transaction ID matches */
|
||||
if (transaction_id)
|
||||
{
|
||||
int i;
|
||||
for(i=0;i<8;i++) if (packet[OFS_TRANSIDFIELD+i]!=transaction_id[i]) return setReason("transaction ID mismatch");
|
||||
}
|
||||
|
||||
/* Unrotate the payload */
|
||||
payloadRotation=packet[OFS_ROTATIONFIELD];
|
||||
{
|
||||
unsigned char temp[256];
|
||||
bcopy(&packet[len-payloadRotation],&temp[0],payloadRotation);
|
||||
bcopy(&packet[HEADERFIELDS_LEN],&packet[HEADERFIELDS_LEN+payloadRotation],
|
||||
len-(HEADERFIELDS_LEN)-payloadRotation);
|
||||
bcopy(&temp[0],&packet[HEADERFIELDS_LEN],payloadRotation);
|
||||
}
|
||||
|
||||
if (debug>1) 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(ofs<len)
|
||||
{
|
||||
/* XXX should allocate responses from a temporary and bounded slab of memory */
|
||||
struct response *r=calloc(sizeof(struct response),1);
|
||||
if (!r) exit(setReason("calloc() failed."));
|
||||
|
||||
r->code=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_id<peer_count;r->peer_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)<bytes) bytes=h->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;
|
||||
}
|
126
peers.c
Normal file
126
peers.c
Normal file
@ -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;i<nom_peer_count;i++) peers[peer_count++]=nominated_peers[i];
|
||||
|
||||
/* Add ourselves as a peer */
|
||||
peers[peer_count]=inet_addr("127.0.0.1");
|
||||
peer_replied[peer_count++]=0;
|
||||
|
||||
/* XXX Add broadcast address of every running interface */
|
||||
|
||||
/* XXX Query BATMANd for other peers */
|
||||
if (batman_socket) getBatmanPeerList(batman_socket,peers,&peer_count,MAX_PEERS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sendToPeers(unsigned char *packet,int packet_len,int method,int peerId,struct response_set *r)
|
||||
{
|
||||
/* The normal version of BATMAN works at layer 3, so we cannot simply use an ethernet broadcast
|
||||
to get the message out. BATMAN Advanced might solve this, though.
|
||||
|
||||
So, in the mean time, we need to explicitly send the request to each peer.
|
||||
If this is a re-send, we don't want to bother the peers who have already responded,
|
||||
so check the peer_replied[] flags.
|
||||
*/
|
||||
int i;
|
||||
int maxPeer=peer_count-1;
|
||||
int n=0;
|
||||
struct sockaddr_in peer_addr;
|
||||
peer_addr.sin_family=AF_INET;
|
||||
peer_addr.sin_port = htons(4110);
|
||||
|
||||
if (method==REQ_PARALLEL) i=0; else { i=peerId; maxPeer=i; }
|
||||
for(;i<=maxPeer;i++)
|
||||
if (!responseFromPeerP(r,i))
|
||||
{
|
||||
peer_addr.sin_addr.s_addr=peers[i];
|
||||
|
||||
if (debug>1) 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 (r<packet_len)
|
||||
{
|
||||
/* XXX something bad happened */
|
||||
if (debug) fprintf(stderr,"Could not send to peer %s\n",inet_ntoa(peer_addr.sin_addr));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debug>1) 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;
|
||||
|
||||
}
|
104
responses.c
Normal file
104
responses.c
Normal file
@ -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<<bit;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int responseFromPeerP(struct response_set *responses,int peerId)
|
||||
{
|
||||
if (!responses) return 0;
|
||||
if (!responses->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<<bit);
|
||||
}
|
||||
|
||||
int clearResponses(struct response_set *responses)
|
||||
{
|
||||
struct response *r;
|
||||
|
||||
if (!responses) return -1;
|
||||
|
||||
r=responses->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;
|
||||
}
|
382
server.c
Normal file
382
server.c
Normal file
@ -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)<size)
|
||||
{
|
||||
int r;
|
||||
fseek(f,0,SEEK_END);
|
||||
if ((r=fwrite(zero,8192,1,f))!=1)
|
||||
{
|
||||
perror("fwrite");
|
||||
exit(setReason("Could not enlarge backing file to requested size (short write)"));
|
||||
}
|
||||
fseek(f,0,SEEK_END);
|
||||
}
|
||||
|
||||
if (errno) perror("fseek");
|
||||
if (fwrite("",1,1,f)!=1)
|
||||
{
|
||||
fprintf(stderr,"Failed to set backing file size.\n");
|
||||
perror("fwrite");
|
||||
}
|
||||
hlr=(unsigned char *)mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_NORESERVE,fileno(f),0);
|
||||
if (hlr==MAP_FAILED) {
|
||||
perror("mmap");
|
||||
exit(setReason("Memory mapping of HLR backing file failed."));
|
||||
}
|
||||
if (debug) fprintf(stderr,"Allocated %d byte HLR store backed by file `%s'\n",
|
||||
size,backing_file);
|
||||
}
|
||||
hlr_size=size;
|
||||
|
||||
|
||||
sock=socket(PF_INET,SOCK_DGRAM,0);
|
||||
if (sock<0) {
|
||||
fprintf(stderr,"Could not create UDP socket.\n");
|
||||
perror("socket");
|
||||
exit(-3);
|
||||
}
|
||||
|
||||
bind_addr.sin_family = AF_INET;
|
||||
bind_addr.sin_port = htons( 4110 );
|
||||
bind_addr.sin_addr.s_addr = htonl( INADDR_ANY );
|
||||
if(bind(sock,(struct sockaddr *)&bind_addr,sizeof(bind_addr))) {
|
||||
fprintf(stderr,"MP HLR server could not bind to UDP port 4110\n");
|
||||
perror("bind");
|
||||
exit(-3);
|
||||
}
|
||||
|
||||
/* Detach from the console */
|
||||
if (!foregroundMode) daemon(0,0);
|
||||
|
||||
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;
|
||||
|
||||
/* 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(pofs<len)
|
||||
{
|
||||
if (debug>1) 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;i<action_len;i++) packet[(*packet_len)++]=action_text[i];
|
||||
|
||||
if (debug>2) 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;
|
||||
}
|
41
simulate.c
Normal file
41
simulate.c
Normal file
@ -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<packet_len;i++)
|
||||
for(b=0;b<8;b++)
|
||||
if (random()<=berThreshold) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
77
srandomdev.c
Normal file
77
srandomdev.c
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2006 Verdens Gang AS
|
||||
* Copyright (c) 2006-2008 Linpro AS
|
||||
* All rights reserved.
|
||||
*
|
||||
* Author: Dag-Erling SmÃ\u017ergrav <des@linpro.no>
|
||||
*
|
||||
* 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 <sys/time.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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
|
315
testdna
Executable file
315
testdna
Executable file
@ -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."
|
Loading…
x
Reference in New Issue
Block a user