serval-dna/server.c

541 lines
17 KiB
C
Raw Normal View History

/*
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;
2011-05-05 09:10:38 +00:00
FILE *i_f=NULL;
struct in_addr client_addr;
int client_port;
int getBackingStore(char *s,int size);
int createServerSocket();
int simpleServerMode();
int server(char *backing_file,int size,int foregroundMode)
{
/* Get backing store for HLR */
getBackingStore(backing_file,size);
if (overlayMode)
{
/* Now find and initialise all the suitable network interfaces, i.e.,
those running IPv4.
Packet radio dongles will get discovered later as the interfaces get probed.
This will setup the sockets for the server to communicate on each interface.
XXX - Problems may persist where the same address is used on multiple interfaces,
but otherwise hopefully it will allow us to bridge multiple networks.
*/
overlay_interface_discover();
}
else
{
/* Create a simple socket for listening on if we are not in overlay mesh mode. */
createServerSocket();
}
/* Detach from the console */
if (!foregroundMode) daemon(0,0);
if (!overlayMode) simpleServerMode();
else overlayServerMode();
return 0;
}
int getBackingStore(char *backing_file,int size)
{
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;
seedHlr();
return 0;
}
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=0;
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,sender,CRYPT_CIPHERED|CRYPT_SIGNED);
if (sid[0]) return respondSimple(sid,ACTION_DECLINED,NULL,0,transaction_id,sender,CRYPT_CIPHERED|CRYPT_SIGNED);
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,sender,CRYPT_CIPHERED|CRYPT_SIGNED);
else
return respondSimple(NULL,ACTION_DECLINED,NULL,0,transaction_id,sender,CRYPT_CIPHERED|CRYPT_SIGNED);
}
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;
2011-05-05 09:10:38 +00:00
case ACTION_STATS:
/* short16 variable id,
int32 value */
{
pofs++;
short field=packet[pofs+1]+(packet[pofs]<<8);
int value=packet[pofs+5]+(packet[pofs+4]<<8)+(packet[pofs+3]<<16)+(packet[pofs+2]<<24);
pofs+=6;
if (instrumentation_file)
{
if (!i_f) { if (strcmp(instrumentation_file,"-")) i_f=fopen(instrumentation_file,"a"); else i_f=stdout; }
if (i_f) fprintf(i_f,"%ld:%08x:%d:%d\n",time(0),*(unsigned int *)&sender->sa_data[0],field,value);
if (i_f) fflush(i_f);
}
}
break;
case ACTION_DIGITALTELEGRAM:
// Unpack SMS message.
if (debug>1) fprintf(stderr,"In ACTION_DIGITALTELEGRAM\n");
{
char emitterPhoneNumber[256];
char message[256];
pofs++;
char messageType = packet[pofs];
pofs++;
char emitterPhoneNumberLen = packet[pofs];
pofs++;
char messageLen = packet[pofs];
pofs++;
strncpy(emitterPhoneNumber, (const char*)packet+pofs, emitterPhoneNumberLen);
emitterPhoneNumber[(unsigned int)emitterPhoneNumberLen]=0;
pofs+=emitterPhoneNumberLen;
strncpy(message, (const char*)packet+pofs, messageLen);
message[(unsigned int)messageLen]=0;
pofs+=messageLen;
// Check if I'm the recipient
2011-05-23 21:52:43 +00:00
ofs=0;
if (findHlr(hlr, &ofs, sid, did)){
// Send SMS to android
char amCommand[576]; // 64 char + 2*256(max) char = 576
sprintf(amCommand, "am broadcast -a org.servalproject.DT -e number \"%s\" -e content \"%s\"", emitterPhoneNumber, message);
if (debug>1) fprintf(stderr,"Delivering DT message via intent: %s\n",amCommand);
int exitcode = runCommand(amCommand);
respondSimple(hlrSid(hlr, ofs),ACTION_OKAY,NULL,0,transaction_id,sender,CRYPT_CIPHERED|CRYPT_SIGNED);
2011-05-23 21:52:43 +00:00
}
}
break;
case ACTION_SET:
ofs=0;
if (debug>1) fprintf(stderr,"Looking for hlr entries with sid='%s' / did='%s'\n",sid,did);
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,
sender,CRYPT_CIPHERED|CRYPT_SIGNED);
}
while(findHlr(hlr,&ofs,sid,did))
{
int itemId,instance,start_offset,bytes,flags;
unsigned char value[9000],oldvalue[65536];
int oldr,oldl;
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) */
/* XXX Doesn't verify PIN authentication */
/* Get write request */
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,
sender,CRYPT_CIPHERED|CRYPT_SIGNED);
}
/* 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,sender,CRYPT_CIPHERED|CRYPT_SIGNED);
}
} 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,sender,CRYPT_CIPHERED|CRYPT_SIGNED);
}
}
/* 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,
sender,CRYPT_CIPHERED|CRYPT_SIGNED);
}
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,sender,CRYPT_CIPHERED|CRYPT_SIGNED);
/* Advance to next record and keep searching */
if (nextHlr(hlr,&ofs)) break;
}
break;
case ACTION_GET:
{
/* Limit transfer size to MAX_DATA_BYTES, plus an allowance for variable packing. */
unsigned char data[MAX_DATA_BYTES+16];
int dlen=0;
int sendDone=0;
int var_id=packet[pofs+1];
int instance=packet[pofs+2];
int offset=(packet[pofs+3]<<8)+packet[pofs+4];
char *hlr_sid=NULL;
pofs+=7;
if (debug>2) dump("Request bytes",&packet[pofs],8);
if (debug>1) fprintf(stderr,"Processing ACTION_GET (var_id=%02x, instance=%02x, pofs=0x%x, len=%d)\n",var_id,instance,pofs,len);
ofs=0;
if (debug>1) fprintf(stderr,"Looking for hlr entries with sid='%s' / did='%s'\n",sid?sid:"null",did?did:"null");
while(1)
{
struct hlrentry_handle *h;
// if an empty did was passed in, get results from all hlr records
if (*sid || *did){
if (!findHlr(hlr,&ofs,sid,did)) break;
if (debug>1) fprintf(stderr,"findHlr found a match @ 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 */
if (instance==0xff) instance=-1;
/* 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)
{
if (debug>1) fprintf(stderr,"Sending matching variable value instance (instance #%d), value offset %d.\n",
h->var_instance,offset);
// only send each value when the *next* record is found, that way we can easily stamp the last response with DONE
if (sendDone>0)
respondSimple(hlr_sid,ACTION_DATA,data,dlen,transaction_id,sender,CRYPT_CIPHERED|CRYPT_SIGNED);
dlen=0;
if (packageVariableSegment(data,&dlen,h,offset,MAX_DATA_BYTES+16))
return setReason("packageVariableSegment() failed.");
hlr_sid=hlrSid(hlr,ofs);
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);
}
/* Advance to next record and keep searching */
if (nextHlr(hlr,&ofs)) break;
}
if (sendDone)
{
data[dlen++]=ACTION_DONE;
data[dlen++]=sendDone&0xff;
respondSimple(hlr_sid,ACTION_DATA,data,dlen,transaction_id,sender,CRYPT_CIPHERED|CRYPT_SIGNED);
}
if (gatewayspec&&(var_id==VAR_LOCATIONS)&&did&&strlen(did))
{
/* We are a gateway, so offer connection via the gateway as well */
unsigned char data[MAX_DATA_BYTES+16];
int dlen=0;
struct hlrentry_handle fake;
unsigned char uri[1024];
/* We use asterisk to provide the gateway service,
so we need to create a temporary extension in extensions.conf,
ask asterisk to re-read extensions.conf, and then make sure it has
a functional SIP gateway.
*/
2011-05-23 21:52:43 +00:00
if (!asteriskObtainGateway(sid,did,(char *)uri))
{
fake.value_len=strlen((char *)uri);
fake.var_id=var_id;
fake.value=uri;
if (packageVariableSegment(data,&dlen,&fake,offset,MAX_DATA_BYTES+16))
return setReason("packageVariableSegment() of gateway URI failed.");
respondSimple(hlrSid(hlr,0),ACTION_DATA,data,dlen,transaction_id,sender,CRYPT_CIPHERED|CRYPT_SIGNED);
}
else
{
/* Should we indicate the gateway is not available? */
}
}
}
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,struct sockaddr *recvaddr,int cryptoFlags)
{
unsigned char packet[8000];
int pl=0;
int *packet_len=&pl;
int packet_maxlen=8000;
int i;
/* XXX Complain about invalid crypto flags.
XXX We don't do anything with the crypto flags right now
XXX Other packet sending routines need this as well. */
if (!cryptoFlags) return -1;
/* 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,recvaddr,NULL)) return -1;
return 0;
}
int createServerSocket()
{
struct sockaddr_in bind_addr;
sock=socket(PF_INET,SOCK_DGRAM,0);
if (sock<0) {
fprintf(stderr,"Could not create UDP socket.\n");
perror("socket");
exit(-3);
}
int TRUE=1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &TRUE, sizeof(TRUE));
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = htons( PORT_DNA );
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 %d\n", PORT_DNA);
perror("bind");
exit(-3);
}
return 0;
}
int simpleServerMode()
{
while(1) {
unsigned char buffer[16384];
struct sockaddr recvaddr;
socklen_t recvaddrlen=sizeof(recvaddr);
struct pollfd fds;
int len;
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);
len=recvfrom(sock,buffer,sizeof(buffer),0,&recvaddr,&recvaddrlen);
client_port=((struct sockaddr_in*)&recvaddr)->sin_port;
client_addr=((struct sockaddr_in*)&recvaddr)->sin_addr;
if (debug) fprintf(stderr,"Received packet from %s:%d (len=%d).\n",inet_ntoa(client_addr),client_port,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;
}
/* Simple server mode doesn't really use interface numbers, so lie and say interface -1 */
if (packetOk(-1,buffer,len,NULL,&recvaddr,recvaddrlen,1)) {
if (debug) setReason("Ignoring invalid packet");
}
if (debug>1) fprintf(stderr,"Finished processing packet, waiting for next one.\n");
}
return 0;
}