mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-03-10 22:43:54 +00:00
Fixed Linux assumptions. Added parsing of overlay mesh interface specification from command line. Separated overlay mesh code out into several files. Overlay mesh is still far from working, but the infrastructure is coming together.
454 lines
15 KiB
C
454 lines
15 KiB
C
/*
|
|
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"
|
|
#include <stdarg.h>
|
|
|
|
char *gatewayuri=NULL;
|
|
|
|
char *outputtemplate=NULL;
|
|
char *instrumentation_file=NULL;
|
|
char *importFile=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 *fmt, ...)
|
|
{
|
|
va_list ap,ap2;
|
|
char msg[8192];
|
|
|
|
va_start(ap,fmt);
|
|
va_copy(ap2,ap);
|
|
|
|
vsnprintf(msg,8192,fmt,ap2); msg[8191]=0;
|
|
|
|
va_end(ap);
|
|
|
|
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] [-I import.txt] [-N interface,...] [-G SIP gateway]\n");
|
|
fprintf(stderr,"or\n");
|
|
fprintf(stderr," dna <-d|-s> id -A\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,"or\n");
|
|
fprintf(stderr," dna [-v ...] -f <hlr.dat> -E <export.txt>\n");
|
|
|
|
fprintf(stderr,"\n");
|
|
fprintf(stderr," -v - increase verbosity.\n");
|
|
fprintf(stderr," -A - Ask for address of subscriber.\n");
|
|
fprintf(stderr," -b - Specify BATMAN socket to obtain peer list (flaky).\n");
|
|
fprintf(stderr," -l - Specify BATMAN socket to obtain peer list (better, but requires Serval patched BATMAN).\n");
|
|
fprintf(stderr," -L - Log mesh statistics to specified file.\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 - Specify one or more interfaces for the DNA overlay mesh to operate.\n");
|
|
fprintf(stderr," Interface specifications take the form IP[:speed[:type[:port]]], and\n");
|
|
fprintf(stderr," multiple interfaces can be specified by comma separating them.\n");
|
|
fprintf(stderr," Speed is the interface speed in bits per second (K,M or G suffixes allowed)\n");
|
|
fprintf(stderr," Type is WiFi,ethernet,catear or other.\n");
|
|
fprintf(stderr," Port specifies an alternate UDP port, otherwise %d is used.\n",PORT_DNA);
|
|
fprintf(stderr," e.g., -N 10.1.2.3,10.1.130.45:2M:wifi\n");
|
|
fprintf(stderr,"\n");
|
|
exit(-1);
|
|
}
|
|
|
|
#ifndef DNA_NO_MAIN
|
|
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;
|
|
|
|
#if defined WIN32
|
|
WSADATA wsa_data;
|
|
WSAStartup(MAKEWORD(1,1), &wsa_data);
|
|
#endif
|
|
|
|
srandomdev();
|
|
|
|
while((c=getopt(argc,argv,"Ab:B:E:G:I:S:f:d:i:l:L:np:P:s:t:vR:W:U:D:CO:N:")) != -1 )
|
|
{
|
|
switch(c)
|
|
{
|
|
case 'N': /* Ask for overlay network to setup one or more interfaces */
|
|
if (overlay_interface_args(optarg))
|
|
return WHY("Invalid interface specification(s) passed to -N");
|
|
break;
|
|
case 'G': /* Offer gateway services */
|
|
gatewayuri=strdup(optarg);
|
|
break;
|
|
case 'E': /* Export HLR into plain text file that can be imported later */
|
|
if (!hlr_file) usage("You must specify an HLR file to export from, i.e., dna -f hlr.dat -E hlr.txt");
|
|
return exportHlr((unsigned char*)hlr_file,optarg);
|
|
break;
|
|
case 'I': /* Import HLR data from a plain text file into current HLR */
|
|
if (importFile) usage("-I multiply specified.");
|
|
importFile=optarg;
|
|
if (!hlr_file||!serverMode) usage("-I requires -S and -f.");
|
|
if (openHlrFile(hlr_file,hlr_size))
|
|
exit(setReason("Failed to open HLR database"));
|
|
importHlr(importFile);
|
|
return 0;
|
|
break;
|
|
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 'l': /* talk peers on a BATMAN mesh */
|
|
batman_peerfile=strdup(optarg);
|
|
break;
|
|
case 'L':
|
|
instrumentation_file=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) 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 'A': /* get address (IP or otherwise) of a given peer */
|
|
peerAddress(did,sid,3 /* 1 = print list of addresses to stdout, 2 = set peer list to responders */);
|
|
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.");
|
|
|
|
#if defined WIN32
|
|
WSACleanup();
|
|
#endif
|
|
|
|
/* Client mode: */
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
long long parse_quantity(char *q)
|
|
{
|
|
int m;
|
|
char units[80];
|
|
|
|
if (strlen(q)>=80) return WHY("quantity string >=80 characters");
|
|
|
|
if (sscanf(q,"%d%s",&m,units)==2)
|
|
{
|
|
if (units[1]) return WHY("Units should be single character");
|
|
switch(units[0])
|
|
{
|
|
case 'k': return m*1000LL;
|
|
case 'K': return m*1024LL;
|
|
case 'm': return m*1000LL*1000LL;
|
|
case 'M': return m*1024LL*1024LL;
|
|
case 'g': return m*1000LL*1000LL*1000LL;
|
|
case 'G': return m*1024LL*1024LL*1024LL;
|
|
default:
|
|
return WHY("Illegal unit: should be k,K,m,M,g, or G.");
|
|
}
|
|
}
|
|
if (sscanf(q,"%d",&m)==1)
|
|
{
|
|
return m;
|
|
}
|
|
else
|
|
{
|
|
return WHY("Could not parse quantity");
|
|
}
|
|
}
|