/* 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 "serval.h" #include "rhizome.h" #include #include #include char *gatewayspec=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 returnMultiVars=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=-1; #ifndef HAVE_BZERO /* OpenWRT doesn't have bzero */ void bzero(void *m,size_t len) { unsigned char *c=m; int i; for(i=0;i=' '&&addr[i+j]<0x7f?addr[i+j]:'.'); fprintf(stderr,"\n"); } return 0; } int dumpResponses(struct response_set *responses) { struct response *r; if (!responses) {fprintf(stderr,"Response set is NULL\n"); return 0; } fprintf(stderr,"Response set claims to contain %d entries.\n",responses->response_count); r=responses->responses; while(r) { fprintf(stderr," response code 0x%02x\n",r->code); if (r->next) if (r->next->prev!=r) fprintf(stderr," !! response chain is broken\n"); r=r->next; } return 0; } int setReason(char *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) return setReason("Variable value has an odd number of hex digits."); b|=hexvalue(text[i++]); if (b<0) return setReason("That doesn't look like hex to me"); if (vlen>=max_len) return setReason("Variable hex value too long"); value[vlen++]=b; } *value_len=vlen; return 0; break; case '@': /* file */ { FILE *f=fopen((char *)&text[i+1],"r"); int flen; fseek(f,0,SEEK_END); flen=ftell(f); if (flen>max_len) return setReason("Variable value from file too long"); fseek(f,0,SEEK_SET); vlen=fread(value,1,flen,f); if (vlen!=flen) return setReason("Could not read all of file"); fclose(f); *value_len=vlen; return 0; } break; default: /* literal string */ vlen=strlen((char *)&text[i]); if (vlen>max_len) return setReason("Variable value too long"); bcopy(&text[i],value,vlen); *value_len=vlen; return 0; } return 0; } int usage(char *complaint) { fprintf(stderr,"dna: %s\n",complaint); fprintf(stderr,"usage:\n"); fprintf(stderr," dna [-v ] -S [-f HLR backing file] [-I import.txt] [-N interface,...] [-G gateway specification] [-r rhizome path]\n"); fprintf(stderr,"or\n"); fprintf(stderr," dna [-v ] -f -E \n"); fprintf(stderr,"or\n"); fprintf(stderr," dna -r -M \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 -E \n"); fprintf(stderr,"\n"); fprintf(stderr," -v - Set verbosity.\n"); fprintf(stderr," -E - Export specified HLR database into specified flat text file.\n"); fprintf(stderr," -I - Import a previously exported HLR database into this one.\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," -m - Return multiple variable values instead of only first response.\n"); fprintf(stderr," -M - Create and import a new bundle from the specified manifest.\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 - Enable Rhizome store-and-forward transport using the specified data store directory.\n"); fprintf(stderr," To limit the storage: echo space=[KB] > path/rhizome.conf\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," -G - Offer gateway services. Argument specifies locations of necessary files.\n"); fprintf(stderr," Use -G [potato|android|custom:...] to set defaults for your device type.\n"); fprintf(stderr," -N - Specify one or more interfaces for the DNA overlay mesh to operate.\n"); fprintf(stderr," Interface specifications take the form <+|->[interface[=type][,...]\n"); fprintf(stderr," e.g., -N -en0,+ to use all interfaces except en0\n"); fprintf(stderr,"\n"); exit(-1); } #ifndef DNA_NO_MAIN char *exec_args[128]; int exec_argc=0; void signal_handler( int signal ) { /* oops - caught a bad signal -- exec() ourselves fresh */ char signalName[64]; snprintf(signalName,63,"signal %d",signal); signalName[63]=0; switch(signal) { #ifdef SIGHUP case SIGHUP: snprintf(signalName,63,"SIG %s (%d)","hangup",signal); break; #endif #ifdef SIGINT case SIGINT: snprintf(signalName,63,"SIG %s (%d)","interrupt",signal); break; #endif #ifdef SIGQUIT case SIGQUIT: snprintf(signalName,63,"SIG %s (%d)","quit",signal); break; #endif #ifdef SIGILL case SIGILL: snprintf(signalName,63,"SIG %s (%d)","illegal instruction (not reset when caught)",signal); break; #endif #ifdef SIGTRAP case SIGTRAP: snprintf(signalName,63,"SIG %s (%d)","trace trap (not reset when caught)",signal); break; #endif #ifdef SIGABRT case SIGABRT: snprintf(signalName,63,"SIG %s (%d)","abort()",signal); break; #endif #ifdef SIGPOLL case SIGPOLL: snprintf(signalName,63,"SIG %s (%d)","pollable event ([XSR] generated, not supported)",signal); break; #endif #ifdef SIGEMT case SIGEMT: snprintf(signalName,63,"SIG %s (%d)","EMT instruction",signal); break; #endif #ifdef SIGFPE case SIGFPE: snprintf(signalName,63,"SIG %s (%d)","floating point exception",signal); break; #endif #ifdef SIGKILL case SIGKILL: snprintf(signalName,63,"SIG %s (%d)","kill (cannot be caught or ignored)",signal); break; #endif #ifdef SIGBUS case SIGBUS: snprintf(signalName,63,"SIG %s (%d)","bus error",signal); break; #endif #ifdef SIGSEGV case SIGSEGV: snprintf(signalName,63,"SIG %s (%d)","segmentation violation",signal); break; #endif #ifdef SIGSYS case SIGSYS: snprintf(signalName,63,"SIG %s (%d)","bad argument to system call",signal); break; #endif #ifdef SIGPIPE case SIGPIPE: snprintf(signalName,63,"SIG %s (%d)","write on a pipe with no one to read it",signal); break; #endif #ifdef SIGALRM case SIGALRM: snprintf(signalName,63,"SIG %s (%d)","alarm clock",signal); break; #endif #ifdef SIGTERM case SIGTERM: snprintf(signalName,63,"SIG %s (%d)","software termination signal from kill",signal); break; #endif #ifdef SIGURG case SIGURG: snprintf(signalName,63,"SIG %s (%d)","urgent condition on IO channel",signal); break; #endif #ifdef SIGSTOP case SIGSTOP: snprintf(signalName,63,"SIG %s (%d)","sendable stop signal not from tty",signal); break; #endif #ifdef SIGTSTP case SIGTSTP: snprintf(signalName,63,"SIG %s (%d)","stop signal from tty",signal); break; #endif #ifdef SIGCONT case SIGCONT: snprintf(signalName,63,"SIG %s (%d)","continue a stopped process",signal); break; #endif #ifdef SIGCHLD case SIGCHLD: snprintf(signalName,63,"SIG %s (%d)","to parent on child stop or exit",signal); break; #endif #ifdef SIGTTIN case SIGTTIN: snprintf(signalName,63,"SIG %s (%d)","to readers pgrp upon background tty read",signal); break; #endif #ifdef SIGTTOU case SIGTTOU: snprintf(signalName,63,"SIG %s (%d)","like TTIN for output if (tp->t_local<OSTOP)",signal); break; #endif #ifdef SIGIO case SIGIO: snprintf(signalName,63,"SIG %s (%d)","input/output possible signal",signal); break; #endif #ifdef SIGXCPU case SIGXCPU: snprintf(signalName,63,"SIG %s (%d)","exceeded CPU time limit",signal); break; #endif #ifdef SIGXFSZ case SIGXFSZ: snprintf(signalName,63,"SIG %s (%d)","exceeded file size limit",signal); break; #endif #ifdef SIGVTALRM case SIGVTALRM: snprintf(signalName,63,"SIG %s (%d)","virtual time alarm",signal); break; #endif #ifdef SIGPROF case SIGPROF: snprintf(signalName,63,"SIG %s (%d)","profiling time alarm",signal); break; #endif #ifdef SIGWINCH case SIGWINCH: snprintf(signalName,63,"SIG %s (%d)","window size changes",signal); break; #endif #ifdef SIGINFO case SIGINFO: snprintf(signalName,63,"SIG %s (%d)","information request",signal); break; #endif #ifdef SIGUSR1 case SIGUSR1: snprintf(signalName,63,"SIG %s (%d)","user defined signal 1",signal); break; #endif #ifdef SIGUSR2 case SIGUSR2: snprintf(signalName,63,"SIG %s (%d)","user defined signal 2",signal); break; #endif } signalName[63]=0; fprintf(stderr,"Caught terminal signal %s -- respawning.\n",signalName); if (sock>-1) close(sock); int i; for(i=0;i-1) close(overlay_interfaces[i].fd); execv(exec_args[0],exec_args); /* Quit if the exec() fails */ exit(-3); } int setVerbosity(char *optarg) { long long old_debug=debug; debug=strtoll(optarg,NULL,10); if (strstr(optarg,"interfaces")) debug|=DEBUG_OVERLAYINTERFACES; if (strstr(optarg,"packetxfer")) debug|=DEBUG_PACKETXFER; if (strstr(optarg,"verbose")) debug|=DEBUG_VERBOSE; if (strstr(optarg,"verbio")) debug|=DEBUG_VERBOSE_IO; if (strstr(optarg,"peers")) debug|=DEBUG_PEERS; if (strstr(optarg,"dnaresponses")) debug|=DEBUG_DNARESPONSES; if (strstr(optarg,"dnarequests")) debug|=DEBUG_DNAREQUESTS; if (strstr(optarg,"simulation")) debug|=DEBUG_SIMULATION; if (strstr(optarg,"dnavars")) debug|=DEBUG_DNAVARS; if (strstr(optarg,"packetformats")) debug|=DEBUG_PACKETFORMATS; if (strstr(optarg,"gateway")) debug|=DEBUG_GATEWAY; if (strstr(optarg,"hlr")) debug|=DEBUG_HLR; if (strstr(optarg,"sockio")) debug|=DEBUG_IO; if (strstr(optarg,"frames")) debug|=DEBUG_OVERLAYFRAMES; if (strstr(optarg,"abbreviations")) debug|=DEBUG_OVERLAYABBREVIATIONS; if (strstr(optarg,"routing")) debug|=DEBUG_OVERLAYROUTING; if (strstr(optarg,"security")) debug|=DEBUG_SECURITY; if (strstr(optarg,"rhizome")) debug|=DEBUG_RHIZOME; if (strstr(optarg,"filesync")) debug|=DEBUG_RHIZOMESYNC; if (strstr(optarg,"monitorroutes")) debug|=DEBUG_OVERLAYROUTEMONITOR; if (strstr(optarg,"queues")) debug|=DEBUG_QUEUES; if (strstr(optarg,"broadcasts")) debug|=DEBUG_BROADCASTS; if (old_debug==debug) { fprintf(stderr,"WARNING: Option '%s' had no effect on existing debug/verbosity level.\n", optarg); } return 0; } 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; memabuseInit(); #if defined WIN32 WSADATA wsa_data; WSAStartup(MAKEWORD(1,1), &wsa_data); #else /* Catch sigsegv and other crash signals so that we can relaunch ourselves */ for(exec_argc=0;exec_argc16384) 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': /* set verbosity */ setVerbosity(optarg); 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,-1,NULL); } 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,-1,NULL); } break; case 'C': /* create a new HLR entry */ { if (optind=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"); } }