/* Serval Distributed Numbering Architecture (DNA) Copyright (C) 2010 Paul Gardner-Stephen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mphlr.h" unsigned char *hlr=NULL; int hlr_size=0; struct sockaddr recvaddr; struct in_addr client_addr; int client_port; int server(char *backing_file,int size,int foregroundMode) { struct sockaddr_in bind_addr; /* Get backing store */ if (!backing_file) { /* transitory storage of HLR data, so just malloc() the memory */ hlr=calloc(size,1); if (!hlr) exit(setReason("Failed to calloc() HLR database.")); if (debug) fprintf(stderr,"Allocated %d byte temporary HLR store\n",size); } else { unsigned char zero[8192]; FILE *f=fopen(backing_file,"r+"); if (!f) f=fopen(backing_file,"w+"); if (!f) exit(setReason("Could not open backing file.")); bzero(&zero[0],8192); fseek(f,0,SEEK_END); errno=0; while(ftell(f)sin_port; 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=0; int records_searched=0; int prev_pofs=0; int pofs=OFS_PAYLOAD; while(pofs1) fprintf(stderr," processRequest: len=%d, pofs=%d, pofs_prev=%d\n",len,pofs,prev_pofs); /* Avoid infinite loops */ if (pofs<=prev_pofs) break; prev_pofs=pofs; if (packet[pofs]==ACTION_CREATEHLR) { /* Creating an HLR requires an initial DID number and definately no SID - you can't choose a SID. */ if (debug>1) fprintf(stderr,"Creating a new HLR record. did='%s', sid='%s'\n",did,sid); if (!did[0]) return respondSimple(NULL,ACTION_DECLINED,NULL,0,transaction_id); if (sid[0]) return respondSimple(sid,ACTION_DECLINED,NULL,0,transaction_id); if (debug>1) fprintf(stderr,"Verified that create request supplies DID but not SID\n"); { char sid[128]; /* make HLR with new random SID and initial DID */ if (!createHlr(did,sid)) return respondSimple(sid,ACTION_OKAY,NULL,0,transaction_id); else return respondSimple(NULL,ACTION_DECLINED,NULL,0,transaction_id); } pofs+=1; pofs+=1+SID_SIZE; } else { switch(packet[pofs]) { case ACTION_PAD: /* Skip padding */ pofs++; pofs+=1+packet[pofs]; break; case ACTION_EOT: /* EOT */ pofs=len; break; case ACTION_SENDSMS: /* Send an SMS to the specified SID. */ /* You cannot use a DID */ if (did[0]) return respondSimple(NULL,ACTION_DECLINED,NULL,0,transaction_id); /* XXX Thomas to complete and make sure it works: 1. Unpack SMS message. 2. Make sure it hasn't already been delivered. 3. Make sure there is space to deliver it . 4. Deliver it to the next free message slot */ { // extractSMS(...); -- don't forget to check maximum message length int instance=-1; /* use first free slot */ unsigned char oldvalue[65536]; int oldl=65536; int oldr=hlrGetVariable(hlr,ofs,VAR_SMESSAGES,instance,oldvalue,&oldl); if (oldr) { /* Already exists, so no need to deliver it again */ respondSimple(sid,ACTION_SMSRECEIVED,NULL,0,transaction_id); } else { /* Write new value back */ if (hlrSetVariable(hlr,ofs,VAR_SMESSAGES,instance,oldvalue,oldl)) { setReason("Failed to write variable"); return respondSimple(NULL,ACTION_ERROR,(unsigned char *)"No space for message",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); } } 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); } 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); } /* 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: { /* 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); 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); } } break; default: setReason("Asked to perform unsupported action"); if (debug) fprintf(stderr,"Packet offset = 0x%x\n",pofs); if (debug) dump("Packet",packet,len); return -1; } } } if (debug>1) fprintf(stderr,"Searched %d HLR entries.\n",records_searched); return 0; } int respondSimple(char *sid,int action,unsigned char *action_text,int action_len, unsigned char *transaction_id) { unsigned char packet[8000]; int pl=0; int *packet_len=&pl; int packet_maxlen=8000; int i; /* ACTION_ERROR is associated with an error message. For syntactic simplicity, we do not require the respondSimple() call to provide the length of the error message. */ if (action==ACTION_ERROR) { action_len=strlen((char *)action_text); /* Make sure the error text isn't too long. IF it is, trim it, as we still need to communicate the error */ if (action_len>255) action_len=255; } /* Prepare the request packet */ if (packetMakeHeader(packet,8000,packet_len,transaction_id)) return -1; if (sid&&sid[0]) { if (packetSetSid(packet,8000,packet_len,sid)) return setReason("invalid SID in reply"); } else { if (packetSetDid(packet,8000,packet_len,"")) return setReason("Could not set empty DID in reply"); } CHECK_PACKET_LEN(1+1+action_len); packet[(*packet_len)++]=action; if (action==ACTION_ERROR) packet[(*packet_len)++]=action_len; for(i=0;i2) dump("Simple response octets",action_text,action_len); if (packetFinalise(packet,8000,packet_len)) return -1; if (debug) fprintf(stderr,"Sending response of %d bytes.\n",*packet_len); if (packetSendRequest(REQ_REPLY,packet,*packet_len,NONBATCH,transaction_id,NULL)) return -1; return 0; }