/* 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 #include #include "serval.h" unsigned char *hlr=NULL; int hlr_size=0; FILE *i_f=NULL; struct in_addr client_addr; int client_port; int getBackingStore(char *s,int size); int createServerSocket(); int simpleServerMode(); int recvwithttl(int sock,unsigned char *buffer,int bufferlen,int *ttl, struct sockaddr *recvaddr,unsigned int *recvaddrlen) { struct msghdr msg; struct iovec iov[1]; iov[0].iov_base=buffer; iov[0].iov_len=bufferlen; bzero(&msg,sizeof(msg)); msg.msg_name = recvaddr; msg.msg_namelen = *recvaddrlen; msg.msg_iov = &iov[0]; msg.msg_iovlen = 1; // setting the following makes the data end up in the wrong place // msg.msg_iov->iov_base=iov_buffer; // msg.msg_iov->iov_len=sizeof(iov_buffer); struct cmsghdr cmsgcmsg[16]; msg.msg_control = &cmsgcmsg[0]; msg.msg_controllen = sizeof(struct cmsghdr)*16; msg.msg_flags = 0; fcntl(sock,F_SETFL, O_NONBLOCK); int len = recvmsg(sock,&msg,0); if (debug&DEBUG_PACKETXFER) fprintf(stderr,"recvmsg returned %d bytes (flags=%d,msg_controllen=%d)\n", len,msg.msg_flags,msg.msg_controllen); struct cmsghdr *cmsg; if (len>0) { for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) { if ((cmsg->cmsg_level == IPPROTO_IP) && ((cmsg->cmsg_type == IP_RECVTTL) ||(cmsg->cmsg_type == IP_TTL)) &&(cmsg->cmsg_len) ){ if (debug&DEBUG_PACKETXFER) fprintf(stderr," TTL (%p) data location resolves to %p\n", ttl,CMSG_DATA(cmsg)); if (CMSG_DATA(cmsg)) { *ttl = *(unsigned char *) CMSG_DATA(cmsg); if (debug&DEBUG_PACKETXFER) fprintf(stderr," TTL of packet is %d\n",*ttl); } } else { if (debug&DEBUG_PACKETXFER) fprintf(stderr,"I didn't expect to see level=%02x, type=%02x\n", cmsg->cmsg_level,cmsg->cmsg_type); } } } *recvaddrlen=msg.msg_namelen; return len; } 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); /* Record PID */ char filename[1024]; if (!FORM_SERVAL_INSTANCE_PATH(filename, "serval.pid")) exit(-1); FILE *f=fopen(filename,"w"); if (!f) { WHY("Could not write to PID file"); perror("fopen"); exit(-1); } fprintf(f,"%d\n",getpid()); fclose(f); 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&DEBUG_HLR) 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)sa_data[0],field,value); if (i_f) fflush(i_f); } } break; case ACTION_DIGITALTELEGRAM: if (debug&DEBUG_DNAREQUESTS) fprintf(stderr,"In ACTION_DIGITALTELEGRAM\n"); { // Unpack SMS message. 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 ofs=0; if (findHlr(hlr, &ofs, sid, did)) { // Check transaction cache to see if message has already been delivered. If not, // then deliver it now. if (!isTransactionInCache(transaction_id)) { // Deliver 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&DEBUG_DNAREQUESTS) fprintf(stderr,"Delivering DT message via intent: %s\n",amCommand); runCommand(amCommand); // Record in cache to prevent re-delivering the same message if a duplicate is received. insertTransactionInCache(transaction_id); } char sid[SID_STRLEN+1]; respondSimple(hlrSid(hlr, ofs, sid), ACTION_OKAY, NULL, 0, transaction_id, recvttl, sender, CRYPT_CIPHERED|CRYPT_SIGNED); } } break; case ACTION_SET: ofs=0; if (debug&DEBUG_DNAREQUESTS) 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,recvttl, 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&DEBUG_HLR) fprintf(stderr,"findHlr found a match for writing at 0x%x\n",ofs); if (debug&DEBUG_HLR) 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,recvttl, 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,recvttl,sender,CRYPT_CIPHERED|CRYPT_SIGNED); } } else { if (flags==SET_NOREPLACE) { setReason("Tried to SET_NOREPLACE an existing value"); if (debug&DEBUG_DNAREQUESTS) dump("Existing value (in SET_NOREPLACE flagged request)",oldvalue,oldl); return respondSimple(NULL,ACTION_ERROR, (unsigned char *)"Cannot SET NOREPLACE; a value exists", 0,transaction_id,recvttl,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,recvttl, sender,CRYPT_CIPHERED|CRYPT_SIGNED); } if (debug&DEBUG_HLR) { fprintf(stderr,"HLR after writing:\n"); hlrDump(hlr,ofs); } /* Reply that we wrote the fragment */ respondSimple(sid,ACTION_WROTE,&packet[rofs],6, transaction_id,recvttl, 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; if (debug&DEBUG_HLR) dump("Request bytes",&packet[pofs],8); pofs++; int var_id=packet[pofs]; int instance=-1; if (var_id&0x80) instance=packet[++pofs]; pofs++; int offset=(packet[pofs]<<8)+packet[pofs+1]; pofs+=2; char *hlr_sid=NULL; char hlr_sid_s[SID_STRLEN+1]; pofs+=2; if (debug&DEBUG_DNAREQUESTS) 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&DEBUG_HLR) fprintf(stderr,"Looking for hlr entries with sid='%s' / did='%s'\n",(sid&&sid[0])?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&DEBUG_HLR) fprintf(stderr,"findHlr found a match @ 0x%x\n",ofs); } if (debug&DEBUG_HLR) 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&DEBUG_HLR) fprintf(stderr,"openhlrentry(hlr,%d) returned %p\n",ofs,h); while(h) { /* Is this the variable? */ if (debug&DEBUG_HLR) 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&DEBUG_HLR) 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,recvttl,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, hlr_sid_s); sendDone++; } else if (debug&DEBUG_HLR) fprintf(stderr,"Ignoring variable instance %d (not %d)\n", h->var_instance,instance); } else if (debug&DEBUG_HLR) 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, recvttl,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. */ 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."); char sid[SID_STRLEN+1]; respondSimple(hlrSid(hlr, 0, sid),ACTION_DATA,data,dlen, transaction_id,recvttl,sender, CRYPT_CIPHERED|CRYPT_SIGNED); } else { /* Should we indicate the gateway is not available? */ } } } break; default: setReason("Asked to perform unsupported action"); if (debug&DEBUG_PACKETFORMATS) fprintf(stderr,"Asked to perform unsipported action at Packet offset = 0x%x\n",pofs); if (debug&DEBUG_PACKETFORMATS) dump("Packet",packet,len); return WHY("Asked to perform unsupported action."); } } } if (debug&DEBUG_HLR) 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,int recvttl, 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 WHY("Crypto-flags not set."); /* 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,cryptoFlags)) return WHY("packetMakeHeader() failed."); 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;isin_port; client_addr=((struct sockaddr_in*)&recvaddr)->sin_addr; if (debug&DEBUG_DNAREQUESTS) fprintf(stderr,"Received packet from %s:%d (len=%d).\n",inet_ntoa(client_addr),client_port,len); if (debug&DEBUG_PACKETXFER) dump("recvaddr",(unsigned char *)&recvaddr,recvaddrlen); if (debug&DEBUG_PACKETXFER) dump("packet",(unsigned char *)buffer,len); if (dropPacketP(len)) { if (debug&DEBUG_SIMULATION) 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,ttl,&recvaddr,recvaddrlen,1)) { if (debug&DEBUG_PACKETFORMATS) setReason("Ignoring invalid packet"); } if (debug&DEBUG_PACKETXFER) fprintf(stderr,"Finished processing packet, waiting for next one.\n"); } } return 0; } #ifdef DEBUG_MEM_ABUSE unsigned char groundzero[65536]; int memabuseInitP=0; int memabuseInit() { if (memabuseInitP) { fprintf(stderr,"WARNING: memabuseInit() called more than once.\n"); return memabuseCheck(); } unsigned char *zero=(unsigned char *)0; int i; for(i=0;i<65536;i++) { groundzero[i]=zero[i]; printf("%04x\n",i); } memabuseInitP=1; return 0; } int _memabuseCheck(const char *func,const char *file,const int line) { unsigned char *zero=(unsigned char *)0; int firstAddr=-1; int lastAddr=-1; int i; for(i=0;i<65536;i++) if (groundzero[i]!=zero[i]) { lastAddr=i; if (firstAddr==-1) firstAddr=i; } if (lastAddr>0) { fprintf(stderr,"WARNING: Memory corruption in first 64KB of RAM detected.\n"); fprintf(stderr," Changed bytes exist in range 0x%04x - 0x%04x\n",firstAddr,lastAddr); dump("Changed memory content",&zero[firstAddr],lastAddr-firstAddr+1); dump("Initial memory content",&groundzero[firstAddr],lastAddr-firstAddr+1); sleep(1); } else { fprintf(stderr,"All's well at %s() %s:%d\n",func,file,line); } return 0; } #endif