/* The oRTP library is an RTP (Realtime Transport Protocol - rfc3550) stack. Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* ==================================================================== * The Vovida Software License, Version 1.0 * * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The names "VOCAL", "Vovida Open Communication Application Library", * and "Vovida Open Communication Application Library (VOCAL)" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact vocal@vovida.org. * * 4. Products derived from this software may not be called "VOCAL", nor * may "VOCAL" appear in their name, without prior written * permission of Vovida Networks, Inc. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * ==================================================================== * * This software consists of voluntary contributions made by Vovida * Networks, Inc. and many individuals on behalf of Vovida Networks, * Inc. For more information on Vovida Networks, Inc., please see * . * */ #ifndef _WIN32_WCE #include #endif #include #if defined(WIN32) || defined(_WIN32_WCE) #include #include /* #include */ #include #include /*for isdigit() */ #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif #define NOSSL /* #if defined(__sparc__) || defined(WIN32) #define NOSSL #endif #define NOSSL */ #include "ortp/stun_udp.h" #include "ortp/stun.h" static char *ipaddr(const StunAddress4 *addr) { static char tmp[512]; struct in_addr inaddr; char *atmp; inaddr.s_addr = htonl(addr->addr); atmp = (char *)inet_ntoa(inaddr); snprintf(tmp, 512, "%s:%i", atmp, addr->port); return tmp; } static void computeHmac(char* hmac, const char* input, int length, const char* key, int keySize); static bool stunParseAtrAddress( char* body, unsigned int hdrLen, StunAtrAddress4 *result ) { if ( hdrLen != 8 ) { printf("hdrLen wrong for Address\n"); return false; } result->pad = *body++; result->family = *body++; if (result->family == IPv4Family) { UInt16 nport; UInt32 naddr; memcpy(&nport, body, 2); body+=2; result->ipv4.port = ntohs(nport); memcpy(&naddr, body, 4); body+=4; result->ipv4.addr = ntohl(naddr); return true; } else if (result->family == IPv6Family) { printf("ipv6 not supported\n"); } else { printf("bad address family: %i\n", result->family); } return false; } static bool stunParseAtrChangeRequest( char* body, unsigned int hdrLen, StunAtrChangeRequest *result ) { if ( hdrLen != 4 ) { /* printf("hdr length = %i expecting %i\n",hdrLen, sizeof(result)); */ printf("Incorrect size for ChangeRequest"); return false; } else { memcpy(&result->value, body, 4); result->value = ntohl(result->value); return true; } } static bool stunParseAtrError( char* body, unsigned int hdrLen, StunAtrError *result ) { if ( hdrLen >= sizeof(result) ) { printf("head on Error too large"); return false; } else { memcpy(&result->pad, body, 2); body+=2; result->pad = ntohs(result->pad); result->errorClass = *body++; result->number = *body++; result->sizeReason = hdrLen - 4; memcpy(&result->reason, body, result->sizeReason); result->reason[result->sizeReason] = 0; return true; } } static bool stunParseAtrUnknown( char* body, unsigned int hdrLen, StunAtrUnknown *result ) { if ( hdrLen >= sizeof(result) ) { return false; } else { int i; if (hdrLen % 4 != 0) return false; result->numAttributes = hdrLen / 4; for (i=0; inumAttributes; i++) { memcpy(&result->attrType[i], body, 2); body+=2; result->attrType[i] = ntohs(result->attrType[i]); } return true; } } static bool stunParseAtrString( char* body, unsigned int hdrLen, StunAtrString *result ) { if ( hdrLen >= STUN_MAX_STRING ) { printf("String is too large"); return false; } else { if (hdrLen % 4 != 0) { printf("Bad length string %i\n", hdrLen); return false; } result->sizeValue = hdrLen; memcpy(&result->value, body, hdrLen); result->value[hdrLen] = 0; return true; } } static bool stunParseAtrIntegrity( char* body, unsigned int hdrLen, StunAtrIntegrity *result ) { if ( hdrLen != 20) { printf("MessageIntegrity must be 20 bytes"); return false; } else { memcpy(&result->hash, body, hdrLen); return true; } } bool stunParseMessage( char* buf, unsigned int bufLen, StunMessage *msg, bool verbose) { char* body; unsigned int size; if (verbose) printf("Received stun message: %i bytes\n", bufLen); memset(msg, 0, sizeof(msg)); if (sizeof(StunMsgHdr) > bufLen) { printf("Bad message\n"); return false; } memcpy(&msg->msgHdr, buf, sizeof(StunMsgHdr)); msg->msgHdr.msgType = ntohs(msg->msgHdr.msgType); msg->msgHdr.msgLength = ntohs(msg->msgHdr.msgLength); if (msg->msgHdr.msgLength + sizeof(StunMsgHdr) != bufLen) { printf("Message header length doesn't match message size: %i - %i\n", msg->msgHdr.msgLength, bufLen); return false; } body = buf + sizeof(StunMsgHdr); size = msg->msgHdr.msgLength; /*printf("bytes after header = %i\n", size); */ while ( size > 0 ) { /* !jf! should check that there are enough bytes left in the buffer */ StunAtrHdr* attr = (StunAtrHdr*)body; /*reinterpret_cast(body);*/ unsigned int attrLen = ntohs(attr->length); int atrType = ntohs(attr->type); /*if (verbose) printf("Found attribute type=" << AttrNames[atrType] << " length=" << attrLen << endl;*/ if ( attrLen+4 > size ) { printf("claims attribute is larger than size of message (attribute type=%i)\n", atrType); return false; } body += 4; /* skip the length and type in attribute header */ size -= 4; if (atrType == MappedAddress) { msg->hasMappedAddress = true; if ( stunParseAtrAddress( body, attrLen, &msg->mappedAddress )== false ) { printf("problem parsing MappedAddress\n"); return false; } else { if (verbose) printf("MappedAddress = %s\n", ipaddr(&msg->mappedAddress.ipv4)); } } else if (atrType == ResponseAddress) { msg->hasResponseAddress = true; if ( stunParseAtrAddress( body, attrLen, &msg->responseAddress )== false ) { printf("problem parsing ResponseAddress"); return false; } else { if (verbose) printf("ResponseAddress = %s\n", ipaddr(&msg->responseAddress.ipv4)); } } else if (atrType == ChangeRequest) { msg->hasChangeRequest = true; if (stunParseAtrChangeRequest( body, attrLen, &msg->changeRequest) == false) { printf("problem parsing ChangeRequest\n"); return false; } else { if (verbose) printf("ChangeRequest = %i\n", msg->changeRequest.value); } } else if (atrType == SourceAddress) { msg->hasSourceAddress = true; if ( stunParseAtrAddress( body, attrLen, &msg->sourceAddress )== false ) { printf("problem parsing SourceAddress\n"); return false; } else { if (verbose) printf("SourceAddress = %s\n", ipaddr(&msg->sourceAddress.ipv4) ); } } else if (atrType == ChangedAddress) { msg->hasChangedAddress = true; if ( stunParseAtrAddress( body, attrLen, &msg->changedAddress )== false ) { printf("problem parsing ChangedAddress\n"); return false; } else { if (verbose) printf("ChangedAddress = %s\n", ipaddr(&msg->changedAddress.ipv4)); } } else if (atrType == Username) { msg->hasUsername = true; if (stunParseAtrString( body, attrLen, &msg->username) == false) { printf("problem parsing Username"); return false; } else { if (verbose) printf("Username = %s\n", msg->username.value ); } } else if (atrType == Password) { msg->hasPassword = true; if (stunParseAtrString( body, attrLen, &msg->password) == false) { printf("problem parsing Password"); return false; } else { if (verbose) printf("Password = %s\n", msg->password.value ); } } else if (atrType == MessageIntegrity) { msg->hasMessageIntegrity = true; if (stunParseAtrIntegrity( body, attrLen, &msg->messageIntegrity) == false) { printf("problem parsing MessageIntegrity"); return false; } else { /*if (verbose) printf("MessageIntegrity = " << msg->messageIntegrity.hash ); */ } /* read the current HMAC look up the password given the user of given the transaction id compute the HMAC on the buffer decide if they match or not */ } else if (atrType == ErrorCode) { msg->hasErrorCode = true; if (stunParseAtrError(body, attrLen, &msg->errorCode) == false) { printf("problem parsing ErrorCode"); return false; } else { if (verbose) printf("ErrorCode = %i %i %s\n", msg->errorCode.errorClass , msg->errorCode.number , msg->errorCode.reason ); } } else if (atrType == UnknownAttribute) { msg->hasUnknownAttributes = true; if (stunParseAtrUnknown(body, attrLen, &msg->unknownAttributes) == false) { printf("problem parsing UnknownAttribute"); return false; } } else if (atrType == ReflectedFrom) { msg->hasReflectedFrom = true; if ( stunParseAtrAddress( body, attrLen, &msg->reflectedFrom ) == false ) { printf("problem parsing ReflectedFrom"); return false; } } else if (atrType == XorMappedAddress) { msg->hasXorMappedAddress = true; if ( stunParseAtrAddress( body, attrLen, &msg->xorMappedAddress ) == false ) { printf("problem parsing XorMappedAddress"); return false; } else { if (verbose) printf("XorMappedAddress = %s\n", ipaddr(&msg->mappedAddress.ipv4) ); } } else if (atrType == XorOnly) { msg->xorOnly = true; if (verbose) { printf("xorOnly = true"); } } else if (atrType == ServerName) { msg->hasServerName = true; if (stunParseAtrString( body, attrLen, &msg->serverName) == false) { printf("problem parsing ServerName"); return false; } else { if (verbose) printf("ServerName = %s\n", msg->serverName.value ); } } else if (atrType == SecondaryAddress) { msg->hasSecondaryAddress = true; if ( stunParseAtrAddress( body, attrLen, &msg->secondaryAddress ) == false ) { printf("problem parsing secondaryAddress"); return false; } else { if (verbose) printf("SecondaryAddress = %s\n", ipaddr(&msg->secondaryAddress.ipv4) ); } } else { if (verbose) printf("Unknown attribute: %i\n", atrType ); if ( atrType <= 0x7FFF ) { return false; } } body += attrLen; size -= attrLen; } return true; } static char* encode16(char* buf, UInt16 data) { UInt16 ndata = htons(data); /*memcpy(buf, reinterpret_cast(&ndata), sizeof(UInt16)); */ memcpy(buf, &ndata, sizeof(UInt16)); return buf + sizeof(UInt16); } static char* encode32(char* buf, UInt32 data) { UInt32 ndata = htonl(data); /*memcpy(buf, reinterpret_cast(&ndata), sizeof(UInt32));*/ memcpy(buf, &ndata, sizeof(UInt32)); return buf + sizeof(UInt32); } static char* encode(char* buf, const char* data, unsigned int length) { memcpy(buf, data, length); return buf + length; } static char* encodeAtrAddress4(char* ptr, UInt16 type, const StunAtrAddress4 *atr) { ptr = encode16(ptr, type); ptr = encode16(ptr, 8); *ptr++ = atr->pad; *ptr++ = IPv4Family; ptr = encode16(ptr, atr->ipv4.port); ptr = encode32(ptr, atr->ipv4.addr); return ptr; } static char* encodeAtrChangeRequest(char* ptr, const StunAtrChangeRequest *atr) { ptr = encode16(ptr, ChangeRequest); ptr = encode16(ptr, 4); ptr = encode32(ptr, atr->value); return ptr; } static char* encodeAtrError(char* ptr, const StunAtrError *atr) { ptr = encode16(ptr, ErrorCode); ptr = encode16(ptr, 6 + atr->sizeReason); ptr = encode16(ptr, atr->pad); *ptr++ = atr->errorClass; *ptr++ = atr->number; ptr = encode(ptr, atr->reason, atr->sizeReason); return ptr; } static char* encodeAtrUnknown(char* ptr, const StunAtrUnknown *atr) { int i; ptr = encode16(ptr, UnknownAttribute); ptr = encode16(ptr, 2+2*atr->numAttributes); for (i=0; inumAttributes; i++) { ptr = encode16(ptr, atr->attrType[i]); } return ptr; } static char* encodeXorOnly(char* ptr) { ptr = encode16(ptr, XorOnly ); return ptr; } static char* encodeAtrString(char* ptr, UInt16 type, const StunAtrString *atr) { /*assert(atr->sizeValue % 4 == 0);*/ ptr = encode16(ptr, type); ptr = encode16(ptr, atr->sizeValue); ptr = encode(ptr, atr->value, atr->sizeValue); return ptr; } static char* encodeAtrIntegrity(char* ptr, const StunAtrIntegrity *atr) { ptr = encode16(ptr, MessageIntegrity); ptr = encode16(ptr, 20); ptr = encode(ptr, atr->hash, sizeof(atr->hash)); return ptr; } unsigned int stunEncodeMessage( const StunMessage *msg, char* buf, unsigned int bufLen, const StunAtrString *password, bool verbose) { /*assert(bufLen >= sizeof(StunMsgHdr));*/ char* ptr = buf; char* lengthp; ptr = encode16(ptr, msg->msgHdr.msgType); lengthp = ptr; ptr = encode16(ptr, 0); /*ptr = encode(ptr, reinterpret_cast(msg->msgHdr.id.octet), sizeof(msg->msgHdr.id));*/ ptr = encode(ptr, (const char*)msg->msgHdr.id.octet, sizeof(msg->msgHdr.id)); if (verbose) printf("Encoding stun message: "); if (msg->hasMappedAddress) { if (verbose) printf("Encoding MappedAddress: %s\n", ipaddr(&msg->mappedAddress.ipv4) ); ptr = encodeAtrAddress4 (ptr, MappedAddress, &msg->mappedAddress); } if (msg->hasResponseAddress) { if (verbose) printf("Encoding ResponseAddress: %s\n", ipaddr(&msg->responseAddress.ipv4) ); ptr = encodeAtrAddress4(ptr, ResponseAddress, &msg->responseAddress); } if (msg->hasChangeRequest) { if (verbose) printf("Encoding ChangeRequest: %i\n", msg->changeRequest.value ); ptr = encodeAtrChangeRequest(ptr, &msg->changeRequest); } if (msg->hasSourceAddress) { if (verbose) printf("Encoding SourceAddress: %s\n", ipaddr(&msg->sourceAddress.ipv4) ); ptr = encodeAtrAddress4(ptr, SourceAddress, &msg->sourceAddress); } if (msg->hasChangedAddress) { if (verbose) printf("Encoding ChangedAddress: %s\n", ipaddr(&msg->changedAddress.ipv4) ); ptr = encodeAtrAddress4(ptr, ChangedAddress, &msg->changedAddress); } if (msg->hasUsername) { if (verbose) printf("Encoding Username: %s\n", msg->username.value ); ptr = encodeAtrString(ptr, Username, &msg->username); } if (msg->hasPassword) { if (verbose) printf("Encoding Password: %s\n", msg->password.value ); ptr = encodeAtrString(ptr, Password, &msg->password); } if (msg->hasErrorCode) { if (verbose) printf("Encoding ErrorCode: class=%i number=%i reason=%s\n" , msg->errorCode.errorClass , msg->errorCode.number , msg->errorCode.reason ); ptr = encodeAtrError(ptr, &msg->errorCode); } if (msg->hasUnknownAttributes) { if (verbose) printf("Encoding UnknownAttribute: ???"); ptr = encodeAtrUnknown(ptr, &msg->unknownAttributes); } if (msg->hasReflectedFrom) { if (verbose) printf("Encoding ReflectedFrom: %s\n", ipaddr(&msg->reflectedFrom.ipv4) ); ptr = encodeAtrAddress4(ptr, ReflectedFrom, &msg->reflectedFrom); } if (msg->hasXorMappedAddress) { if (verbose) printf("Encoding XorMappedAddress: %s\n", ipaddr(&msg->xorMappedAddress.ipv4) ); ptr = encodeAtrAddress4 (ptr, XorMappedAddress, &msg->xorMappedAddress); } if (msg->xorOnly) { if (verbose) printf("Encoding xorOnly: "); ptr = encodeXorOnly( ptr ); } if (msg->hasServerName) { if (verbose) printf("Encoding ServerName: %s\n", msg->serverName.value ); ptr = encodeAtrString(ptr, ServerName, &msg->serverName); } if (msg->hasSecondaryAddress) { if (verbose) printf("Encoding SecondaryAddress: %s\n", ipaddr(&msg->secondaryAddress.ipv4) ); ptr = encodeAtrAddress4 (ptr, SecondaryAddress, &msg->secondaryAddress); } if (password->sizeValue > 0) { StunAtrIntegrity integrity; if (verbose) printf("HMAC with password: %s\n", password->value ); computeHmac(integrity.hash, buf, (int)(ptr-buf) , password->value, password->sizeValue); ptr = encodeAtrIntegrity(ptr, &integrity); } if (verbose) printf("\n"); encode16(lengthp, (UInt16)(ptr - buf - sizeof(StunMsgHdr))); return (int)(ptr - buf); } int stunRand(void) { /* return 32 bits of random stuff */ /* assert( sizeof(int) == 4 ); */ static bool init=false; if ( !init ) { UInt64 tick; int seed; init = true; #if defined(_WIN32_WCE) tick = GetTickCount (); #elif defined(_MSC_VER) { volatile unsigned int lowtick=0,hightick=0; __asm { rdtsc mov lowtick, eax mov hightick, edx } tick = hightick; tick <<= 32; tick |= lowtick; } #elif defined(__GNUC__) && ( defined(__i686__) || defined(__i386__) ) asm("rdtsc" : "=A" (tick)); #elif defined(__GNUC__) && defined(__amd64__) asm("rdtsc" : "=A" (tick)); #elif defined (__SUNPRO_CC) && defined( __sparc__ ) tick = gethrtime(); #elif defined(__MACH__) { int fd=open("/dev/random",O_RDONLY); read(fd,&tick,sizeof(tick)); closesocket(fd); } #elif defined(__linux) || defined (__FreeBSD__) { fd_set fdSet; int maxFd=0; struct timeval tv; int e; int fd=open("/dev/random",O_RDONLY); if (fd<0) { printf("Failed to open random device\n"); return random(); } FD_ZERO(&fdSet); FD_SET(fd,&fdSet); maxFd=fd+1; printf("random device opened\n"); tv.tv_sec = 0; tv.tv_usec = 500; e = select( maxFd, &fdSet, NULL,NULL, &tv ); if (e <= 0) { printf("Failed to get data from random device\n"); closesocket(fd); return random(); } read(fd,&tick,sizeof(tick)); printf("random device returned data\n"); closesocket(fd); } #else # error Need some way to seed the random number generator #endif seed = (int)(tick); #if defined(_WIN32) || defined(_WIN32_WCE) srand(seed); #else srandom(seed); #endif } #if defined(_WIN32) || defined(_WIN32_WCE) /* assert( RAND_MAX == 0x7fff ); */ { int r1 = rand(); int r2 = rand(); int ret = (r1<<16) + r2; return ret; } #else return random(); #endif } /* return a random number to use as a port */ static int randomPort() { int min=0x4000; int max=0x7FFF; int ret = stunRand(); ret = ret|min; ret = ret&max; return ret; } #ifdef NOSSL static void computeHmac(char* hmac, const char* input, int length, const char* key, int sizeKey) { strncpy(hmac,"hmac-not-implemented",20); } #else #include static void computeHmac(char* hmac, const char* input, int length, const char* key, int sizeKey) { unsigned int resultSize=0; HMAC(EVP_sha1(), key, sizeKey, (const unsigned char*) input, length, (unsigned char*)hmac, &resultSize); /* HMAC(EVP_sha1(), key, sizeKey, reinterpret_cast(input), length, reinterpret_cast(hmac), &resultSize); assert(resultSize == 20); */ } #endif static void toHex(const char* buffer, int bufferSize, char* output) { int i; static char hexmap[] = "0123456789abcdef"; const char* p = buffer; char* r = output; for (i=0; i < bufferSize; i++) { unsigned char temp = *p++; int hi = (temp & 0xf0)>>4; int low = (temp & 0xf); *r++ = hexmap[hi]; *r++ = hexmap[low]; } *r = 0; } void stunCreateUserName(const StunAddress4* source, StunAtrString* username) { UInt64 time = stunGetSystemTimeSecs(); UInt64 lotime; char buffer[1024]; char hmac[20]; char key[] = "Jason"; char hmacHex[41]; int l; time -= (time % 20*60); /* UInt64 hitime = time >> 32; */ lotime = time & 0xFFFFFFFF; sprintf(buffer, "%08x:%08x:%08x:", (UInt32)(source->addr), (UInt32)(stunRand()), (UInt32)(lotime)); /*assert( strlen(buffer) < 1024 ); */ /*assert(strlen(buffer) + 41 < STUN_MAX_STRING); */ computeHmac(hmac, buffer, strlen(buffer), key, strlen(key) ); toHex(hmac, 20, hmacHex ); hmacHex[40] =0; strcat(buffer,hmacHex); l = strlen(buffer); /* assert( l+1 < STUN_MAX_STRING );*/ /* assert( l%4 == 0 ); */ username->sizeValue = l; memcpy(username->value,buffer,l); username->value[l]=0; /* if (verbose) printf("computed username=%s\n", username.value ); */ } void stunCreatePassword(const StunAtrString *username, StunAtrString* password) { char hmac[20]; char key[] = "Fluffy"; /* char buffer[STUN_MAX_STRING]; */ computeHmac(hmac, username->value, strlen(username->value), key, strlen(key)); toHex(hmac, 20, password->value); password->sizeValue = 40; password->value[40]=0; /* printf("password=%s\n", password->value ); */ } UInt64 stunGetSystemTimeSecs(void) { UInt64 time=0; #if defined(_WIN32) || defined(_WIN32_WCE) SYSTEMTIME t; /* CJ TODO - this probably has bug on wrap around every 24 hours */ GetSystemTime( &t ); time = (t.wHour*60+t.wMinute)*60+t.wSecond; #else struct timeval now; gettimeofday( &now , NULL ); /* assert( now ); */ time = now.tv_sec; #endif return time; } /* returns true if it scucceeded */ bool stunParseHostName( char* peerName, UInt32* ip, UInt16* portVal, UInt16 defaultPort ) { struct in_addr sin_addr; char host[512]; char* port = NULL; int portNum = defaultPort; char* sep; struct hostent* h; strncpy(host,peerName,512); host[512-1]='\0'; /* pull out the port part if present. */ sep = strchr(host,':'); if ( sep == NULL ) { portNum = defaultPort; } else { char* endPtr=NULL; *sep = '\0'; port = sep + 1; /* set port part */ portNum = strtol(port,&endPtr,10); if ( endPtr != NULL ) { if ( *endPtr != '\0' ) { portNum = defaultPort; } } } if ( portNum < 1024 ) return false; if ( portNum >= 0xFFFF ) return false; /* figure out the host part */ #if defined(_WIN32) || defined(_WIN32_WCE) /* assert( strlen(host) >= 1 ); */ if ( isdigit( host[0] ) ) { /* assume it is a ip address */ unsigned long a = inet_addr(host); /* cerr << "a=0x" << hex << a << dec ); */ *ip = ntohl( a ); } else { /* assume it is a host name */ h = gethostbyname( host ); if ( h == NULL ) { /*int err = getErrno();*/ /* printf("error was %i\n", err); */ /* std::cerr << "error was " << err << std::endl; */ /* assert( err != WSANOTINITIALISED ); */ *ip = ntohl( 0x7F000001L ); return false; } else { sin_addr = *(struct in_addr*)h->h_addr; *ip = ntohl( sin_addr.s_addr ); } } #else h = gethostbyname( host ); if ( h == NULL ) { /* int err = getErrno(); printf("error was %i\n", err); */ *ip = ntohl( 0x7F000001L ); return false; } else { sin_addr = *(struct in_addr*)h->h_addr; *ip = ntohl( sin_addr.s_addr ); } #endif *portVal = portNum; return true; } bool stunParseServerName( char* name, StunAddress4 *addr) { /* assert(name); */ /* TODO - put in DNS SRV stuff. */ bool ret = stunParseHostName( name, &addr->addr, &addr->port, 3478); if ( ret != true ) { addr->port=0xFFFF; } return ret; } static void stunCreateErrorResponse(StunMessage *response, int cl, int number, const char* msg) { response->msgHdr.msgType = BindErrorResponseMsg; response->hasErrorCode = true; response->errorCode.errorClass = cl; response->errorCode.number = number; strcpy(response->errorCode.reason, msg); } #if 0 static void stunCreateSharedSecretErrorResponse(StunMessage& response, int cl, int number, const char* msg) { response.msgHdr.msgType = SharedSecretErrorResponseMsg; response.hasErrorCode = true; response.errorCode.errorClass = cl; response.errorCode.number = number; strcpy(response.errorCode.reason, msg); } #endif static void stunCreateSharedSecretResponse(const StunMessage *request, const StunAddress4 *source, StunMessage *response) { response->msgHdr.msgType = SharedSecretResponseMsg; response->msgHdr.id = request->msgHdr.id; response->hasUsername = true; stunCreateUserName( source, &response->username); response->hasPassword = true; stunCreatePassword( &response->username, &response->password); } /* This funtion takes a single message sent to a stun server, parses and constructs an apropriate repsonse - returns true if message is valid */ bool stunServerProcessMsg( char* buf, unsigned int bufLen, StunAddress4 *from, StunAddress4 *secondary, StunAddress4 *myAddr, StunAddress4 *altAddr, StunMessage *resp, StunAddress4 *destination, StunAtrString *hmacPassword, bool* changePort, bool* changeIp, bool verbose) { int i; StunMessage req; StunAddress4 mapped; StunAddress4 respondTo; UInt32 flags; bool ok; /* set up information for default response */ memset( &req, 0 , sizeof(req) ); memset( resp, 0 , sizeof(*resp) ); *changeIp = false; *changePort = false; ok = stunParseMessage( buf,bufLen, &req, verbose); if (!ok) /* Complete garbage, drop it on the floor */ { if (verbose) printf("Request did not parse"); return false; } if (verbose) printf("Request parsed ok"); mapped = req.mappedAddress.ipv4; respondTo = req.responseAddress.ipv4; flags = req.changeRequest.value; if (req.msgHdr.msgType==SharedSecretRequestMsg) { if(verbose) printf("Received SharedSecretRequestMsg on udp. send error 433."); /* !cj! - should fix so you know if this came over TLS or UDP */ stunCreateSharedSecretResponse(&req, from, resp); /* stunCreateSharedSecretErrorResponse(*resp, 4, 33, "this request must be over TLS"); */ return true; } else if (req.msgHdr.msgType==BindRequestMsg) { if (!req.hasMessageIntegrity) { if (verbose) printf("BindRequest does not contain MessageIntegrity"); if (0) /* !jf! mustAuthenticate */ { if(verbose) printf("Received BindRequest with no MessageIntegrity. Sending 401."); stunCreateErrorResponse(resp, 4, 1, "Missing MessageIntegrity"); return true; } } else { if (!req.hasUsername) { if (verbose) printf("No UserName. Send 432."); stunCreateErrorResponse(resp, 4, 32, "No UserName and contains MessageIntegrity"); return true; } else { if (verbose) printf("Validating username: %s", req.username.value ); /* !jf! could retrieve associated password from provisioning here */ if (strcmp(req.username.value, "test") == 0) { if (0) { /* !jf! if the credentials are stale */ stunCreateErrorResponse(resp, 4, 30, "Stale credentials on BindRequest"); return true; } else { unsigned char hmac[20]; if (verbose) printf("Validating MessageIntegrity"); /* need access to shared secret */ #ifndef NOSSL { unsigned int hmacSize=20; HMAC(EVP_sha1(), "1234", 4, (const unsigned char*) buf, bufLen-20-4, hmac, &hmacSize); /*HMAC(EVP_sha1(), "1234", 4, reinterpret_cast(buf), bufLen-20-4, hmac, &hmacSize); assert(hmacSize == 20); */ } #endif if (memcmp(buf, hmac, 20) != 0) { if (verbose) printf("MessageIntegrity is bad. Sending "); stunCreateErrorResponse(resp, 4, 3, "Unknown username. Try test with password 1234"); return true; } /* need to compute this later after message is filled in */ resp->hasMessageIntegrity = true; /* assert(req.hasUsername); */ resp->hasUsername = true; resp->username = req.username; /* copy username in */ } } else { if (verbose) printf("Invalid username: %s Send 430", req.username.value); } } } /* TODO !jf! should check for unknown attributes here and send 420 listing the unknown attributes. */ if ( respondTo.port == 0 ) { /* respondTo = from; */ memcpy(&respondTo, from, sizeof(StunAddress4)); } if ( mapped.port == 0 ) { /* mapped = from; */ memcpy(&mapped, from, sizeof(StunAddress4)); } *changeIp = ( flags & ChangeIpFlag )?true:false; *changePort = ( flags & ChangePortFlag )?true:false; if (verbose) { printf("Request is valid:\n"); printf("\t flags= %i\n", flags ); printf("\t changeIp= %i\n", *changeIp ); printf("\t changePort=%i\n", *changePort ); printf("\t from= %i\n", from->addr ); printf("\t respond to= %i\n", respondTo.addr ); printf("\t mapped= %i\n", mapped.addr ); } /* form the outgoing message */ resp->msgHdr.msgType = BindResponseMsg; for (i=0; i<16; i++ ) { resp->msgHdr.id.octet[i] = req.msgHdr.id.octet[i]; } if ( req.xorOnly == false ) { resp->hasMappedAddress = true; resp->mappedAddress.ipv4.port = mapped.port; resp->mappedAddress.ipv4.addr = mapped.addr; } if (1) /* do xorMapped address or not */ { UInt16 id16; UInt32 id32; resp->hasXorMappedAddress = true; id16 = req.msgHdr.id.octet[7]<<8 | req.msgHdr.id.octet[6]; id32 = req.msgHdr.id.octet[7]<<24 | req.msgHdr.id.octet[6]<<16 | req.msgHdr.id.octet[5]<<8 | req.msgHdr.id.octet[4]; resp->xorMappedAddress.ipv4.port = mapped.port^id16; resp->xorMappedAddress.ipv4.addr = mapped.addr^id32; } resp->hasSourceAddress = true; resp->sourceAddress.ipv4.port = (*changePort) ? altAddr->port : myAddr->port; resp->sourceAddress.ipv4.addr = (*changeIp) ? altAddr->addr : myAddr->addr; resp->hasChangedAddress = true; resp->changedAddress.ipv4.port = altAddr->port; resp->changedAddress.ipv4.addr = altAddr->addr; if ( secondary->port != 0 ) { resp->hasSecondaryAddress = true; resp->secondaryAddress.ipv4.port = secondary->port; resp->secondaryAddress.ipv4.addr = secondary->addr; } if ( req.hasUsername && req.username.sizeValue > 0 ) { /* copy username in */ resp->hasUsername = true; /* assert( req.username.sizeValue % 4 == 0 ); */ /* assert( req.username.sizeValue < STUN_MAX_STRING ); */ memcpy( resp->username.value, req.username.value, req.username.sizeValue ); resp->username.sizeValue = req.username.sizeValue; } if (1) /* add ServerName */ { const char serverName[] = "Vovida.org " STUN_VERSION; /* must pad to mult of 4 */ resp->hasServerName = true; /* assert( sizeof(serverName) < STUN_MAX_STRING ); */ /* cerr << "sizeof serverName is " << sizeof(serverName) ); */ /* assert( sizeof(serverName)%4 == 0 ); */ memcpy( resp->serverName.value, serverName, sizeof(serverName)); resp->serverName.sizeValue = sizeof(serverName); } if ( req.hasMessageIntegrity & req.hasUsername ) { /* this creates the password that will be used in the HMAC when then */ /* messages is sent */ stunCreatePassword( &req.username, hmacPassword ); } if (req.hasUsername && (req.username.sizeValue > 64 ) ) { UInt32 source; /* assert( sizeof(int) == sizeof(UInt32) ); */ sscanf(req.username.value, "%x", &source); resp->hasReflectedFrom = true; resp->reflectedFrom.ipv4.port = 0; resp->reflectedFrom.ipv4.addr = source; } destination->port = respondTo.port; destination->addr = respondTo.addr; return true; } else { if (verbose) printf("Unknown or unsupported request "); return false; } /* assert(0); */ return false; } bool stunInitServer(StunServerInfo *info, const StunAddress4 *myAddr, const StunAddress4 *altAddr, int startMediaPort, bool verbose ) { /* assert( myAddr.port != 0 ); */ /* assert( altAddr.port!= 0 ); */ /* assert( myAddr.addr != 0 ); */ /* assert( altAddr.addr != 0 ); */ /* info->myAddr = myAddr; */ info->myAddr.port = myAddr->port; info->myAddr.addr = myAddr->addr; /* info->altAddr = altAddr; */ info->altAddr.port = altAddr->port; info->altAddr.addr = altAddr->addr; info->myFd = INVALID_SOCKET; info->altPortFd = INVALID_SOCKET; info->altIpFd = INVALID_SOCKET; info->altIpPortFd = INVALID_SOCKET; memset(info->relays, 0, sizeof(info->relays)); if (startMediaPort > 0) { int i; info->relay = true; for (i=0; irelays[i]; relay->relayPort = startMediaPort+i; relay->fd = 0; relay->expireTime = 0; } } else { info->relay = false; } if ((info->myFd = openPort(myAddr->port, myAddr->addr,verbose)) == INVALID_SOCKET) { printf("Can't open %i\n", myAddr->addr ); stunStopServer(info); return false; } /*if (verbose) printf("Opened " << myAddr->addr << ":" << myAddr->port << " --> " << info->myFd ); */ if ((info->altPortFd = openPort(altAddr->port,myAddr->addr,verbose)) == INVALID_SOCKET) { printf("Can't open %i\n", myAddr->addr ); stunStopServer(info); return false; } /* if (verbose) printf("Opened " << myAddr->addr << ":" << altAddr->port << " --> " << info->altPortFd ); */ info->altIpFd = INVALID_SOCKET; if ( altAddr->addr != 0 ) { if ((info->altIpFd = openPort( myAddr->port, altAddr->addr,verbose)) == INVALID_SOCKET) { printf("Can't open %i\n", altAddr->addr ); stunStopServer(info); return false; } /* if (verbose) printf("Opened " << altAddr->addr << ":" << myAddr->port << " --> " << info->altIpFd ); */ } info->altIpPortFd = INVALID_SOCKET; if ( altAddr->addr != 0 ) { if ((info->altIpPortFd = openPort(altAddr->port, altAddr->addr,verbose)) == INVALID_SOCKET) { printf("Can't open %i\n", altAddr->addr ); stunStopServer(info); return false; } /* if (verbose) printf("Opened " << altAddr->addr << ":" << altAddr->port << " --> " << info->altIpPortFd ); */ } return true; } void stunStopServer(StunServerInfo *info) { if (info->myFd > 0) closesocket(info->myFd); if (info->altPortFd > 0) closesocket(info->altPortFd); if (info->altIpFd > 0) closesocket(info->altIpFd); if (info->altIpPortFd > 0) closesocket(info->altIpPortFd); if (info->relay) { int i; for (i=0; irelays[i]; if (relay->fd) { closesocket(relay->fd); relay->fd = 0; } } } } #if 0 /* no usefull here */ bool stunServerProcess(StunServerInfo *info, bool verbose) { char msg[STUN_MAX_MESSAGE_SIZE]; int msgLen = sizeof(msg); bool ok = false; bool recvAltIp =false; bool recvAltPort = false; fd_set fdSet; #if defined(_WIN32) || defined(_WIN32_WCE) unsigned int maxFd=0; #else int maxFd=0; #endif struct timeval tv; int e; FD_ZERO(&fdSet); FD_SET(info->myFd,&fdSet); if ( info->myFd >= maxFd ) maxFd=info->myFd+1; FD_SET(info->altPortFd,&fdSet); if ( info->altPortFd >= maxFd ) maxFd=info->altPortFd+1; if ( info->altIpFd != INVALID_SOCKET ) { FD_SET(info->altIpFd,&fdSet); if (info->altIpFd>=maxFd) maxFd=info->altIpFd+1; } if ( info->altIpPortFd != INVALID_SOCKET ) { FD_SET(info->altIpPortFd,&fdSet); if (info->altIpPortFd>=maxFd) maxFd=info->altIpPortFd+1; } if (info->relay) { int i; for (i=0; irelays[i]; if (relay->fd) { FD_SET(relay->fd, &fdSet); if (relay->fd >= maxFd) maxFd=relay->fd+1; } } } if ( info->altIpFd != INVALID_SOCKET ) { FD_SET(info->altIpFd,&fdSet); if (info->altIpFd>=maxFd) maxFd=info->altIpFd+1; } if ( info->altIpPortFd != INVALID_SOCKET ) { FD_SET(info->altIpPortFd,&fdSet); if (info->altIpPortFd>=maxFd) maxFd=info->altIpPortFd+1; } tv.tv_sec = 0; tv.tv_usec = 1000; e = select( maxFd, &fdSet, NULL,NULL, &tv ); if (e < 0) { int err = getErrno(); #if !defined(_WIN32_WCE) printf("Error on select: %s\n", strerror(err) ); #else printf("Error on select: %i\n", err ); #endif } else if (e >= 0) { StunAddress4 from; int relayPort = 0; bool changePort = false; bool changeIp = false; StunMessage resp; StunAddress4 dest; StunAtrString hmacPassword; StunAddress4 secondary; char buf[STUN_MAX_MESSAGE_SIZE]; int len = sizeof(buf); hmacPassword.sizeValue = 0; secondary.port = 0; secondary.addr = 0; /* do the media relaying */ if (info->relay) { time_t now; int i; #if !defined(_WIN32_WCE) now = time(0); #else DWORD timemillis = GetTickCount(); now = timemillis/1000; #endif for (i=0; irelays[i]; if (relay->fd) { if (FD_ISSET(relay->fd, &fdSet)) { char msg[MAX_RTP_MSG_SIZE]; int msgLen = sizeof(msg); StunAddress4 rtpFrom; ok = getMessage( relay->fd, msg, &msgLen, &rtpFrom.addr, &rtpFrom.port ,verbose); if (ok) { sendMessage(info->myFd, msg, msgLen, relay->destination.addr, relay->destination.port, verbose); relay->expireTime = now + MEDIA_RELAY_TIMEOUT; if ( verbose ) printf("Relay packet on %i from %i -> %i", relay->fd, rtpFrom.addr, relay->destination.addr ); } } else if (now > relay->expireTime) { closesocket(relay->fd); relay->fd = 0; } } } } if (FD_ISSET(info->myFd,&fdSet)) { if (verbose) printf("received on A1:P1"); recvAltIp = false; recvAltPort = false; ok = getMessage( info->myFd, msg, &msgLen, &from.addr, &from.port,verbose ); } else if (FD_ISSET(info->altPortFd, &fdSet)) { if (verbose) printf("received on A1:P2"); recvAltIp = false; recvAltPort = true; ok = getMessage( info->altPortFd, msg, &msgLen, &from.addr, &from.port,verbose ); } else if ( (info->altIpFd!=INVALID_SOCKET) && FD_ISSET(info->altIpFd,&fdSet)) { if (verbose) printf("received on A2:P1"); recvAltIp = true; recvAltPort = false; ok = getMessage( info->altIpFd, msg, &msgLen, &from.addr, &from.port ,verbose); } else if ( (info->altIpPortFd!=INVALID_SOCKET) && FD_ISSET(info->altIpPortFd, &fdSet)) { if (verbose) printf("received on A2:P2"); recvAltIp = true; recvAltPort = true; ok = getMessage( info->altIpPortFd, msg, &msgLen, &from.addr, &from.port,verbose ); } else { return true; } if (info->relay) { int i; for (i=0; irelays[i]; if (relay->destination.addr == from.addr && relay->destination.port == from.port) { relayPort = relay->relayPort; relay->expireTime = time(0) + MEDIA_RELAY_TIMEOUT; break; } } if (relayPort == 0) { int i; for (i=0; irelays[i]; if (relay->fd == 0) { if ( verbose ) printf("Open relay port %i\n", relay->relayPort ); relay->fd = openPort(relay->relayPort, info->myAddr.addr, verbose); relay->destination.addr = from.addr; relay->destination.port = from.port; relay->expireTime = time(0) + MEDIA_RELAY_TIMEOUT; relayPort = relay->relayPort; break; } } } } if ( !ok ) { if ( verbose ) printf("Get message did not return a valid message\n"); return true; } if ( verbose ) printf("Got a request (len=%i) from %i", msgLen, from.addr); if ( msgLen <= 0 ) { return true; } if (info->relay && relayPort) { secondary = from; from.addr = info->myAddr.addr; from.port = relayPort; } ok = stunServerProcessMsg( msg, msgLen, &from, &secondary, recvAltIp ? &info->altAddr : &info->myAddr, recvAltIp ? &info->myAddr : &info->altAddr, &resp, &dest, &hmacPassword, &changePort, &changeIp, verbose ); if ( !ok ) { if ( verbose ) printf("Failed to parse message"); return true; } len = stunEncodeMessage( &resp, buf, len, &hmacPassword,verbose ); if ( dest.addr == 0 ) ok=false; if ( dest.port == 0 ) ok=false; if ( ok ) { /* assert( dest.addr != 0 ); */ /* assert( dest.port != 0 ); */ Socket sendFd; bool sendAltIp = recvAltIp; /* send on the received IP address */ bool sendAltPort = recvAltPort; /* send on the received port */ if ( changeIp ) sendAltIp = !sendAltIp; /* if need to change IP, then flip logic */ if ( changePort ) sendAltPort = !sendAltPort; /* if need to change port, then flip logic */ if ( !sendAltPort ) { if ( !sendAltIp ) { sendFd = info->myFd; } else { sendFd = info->altIpFd; } } else { if ( !sendAltIp ) { sendFd = info->altPortFd; } else { sendFd = info->altIpPortFd; } } if ( sendFd != INVALID_SOCKET ) { sendMessage( sendFd, buf, len, dest.addr, dest.port, verbose ); } } } return true; } #endif int stunFindLocalInterfaces(UInt32* addresses,int maxRet) { #if defined(WIN32) || defined(_WIN32_WCE) || defined(__sparc__) return 0; #else struct ifconf ifc; int e; int s = socket( AF_INET, SOCK_DGRAM, 0 ); int len = 100 * sizeof(struct ifreq); char buf[ 100 * sizeof(struct ifreq) ]; char *ptr; int tl; int count=0; ifc.ifc_len = len; ifc.ifc_buf = buf; e = ioctl(s,SIOCGIFCONF,&ifc); ptr = buf; tl = ifc.ifc_len; while ( (tl > 0) && ( count < maxRet) ) { struct ifreq* ifr = (struct ifreq *)ptr; struct ifreq ifr2; struct sockaddr a; struct sockaddr_in* addr; UInt32 ai; int si = sizeof(ifr->ifr_name) + sizeof(struct sockaddr); tl -= si; ptr += si; /* char* name = ifr->ifr_ifrn.ifrn_name; */ /* cerr << "name = " << name ); */ ifr2 = *ifr; e = ioctl(s,SIOCGIFADDR,&ifr2); if ( e == -1 ) { break; } /* cerr << "ioctl addr e = " << e ; */ a = ifr2.ifr_addr; addr = (struct sockaddr_in*) &a; ai = ntohl( addr->sin_addr.s_addr ); if ((int)((ai>>24)&0xFF) != 127) { addresses[count++] = ai; } } closesocket(s); return count; #endif } void stunBuildReqSimple( StunMessage* msg, const StunAtrString *username, bool changePort, bool changeIp, unsigned int id ) { int i; /* assert( msg ); */ memset( msg , 0 , sizeof(*msg) ); msg->msgHdr.msgType = BindRequestMsg; for ( i=0; i<16; i=i+4 ) { /* assert(i+3<16); */ int r = stunRand(); msg->msgHdr.id.octet[i+0]= r>>0; msg->msgHdr.id.octet[i+1]= r>>8; msg->msgHdr.id.octet[i+2]= r>>16; msg->msgHdr.id.octet[i+3]= r>>24; } if ( id != 0 ) { msg->msgHdr.id.octet[0] = id; } msg->hasChangeRequest = true; msg->changeRequest.value =(changeIp?ChangeIpFlag:0) | (changePort?ChangePortFlag:0); if ( username->sizeValue > 0 ) { msg->hasUsername = true; /* msg->username = username; */ memcpy(&msg->username, username, sizeof(StunAtrString)); } } static void stunSendTest( Socket myFd, StunAddress4 *dest, const StunAtrString *username, const StunAtrString *password, int testNum, bool verbose ) { /* assert( dest.addr != 0 ); */ /* assert( dest.port != 0 ); */ bool changePort=false; bool changeIP=false; bool discard=false; StunMessage req; char buf[STUN_MAX_MESSAGE_SIZE]; int len = STUN_MAX_MESSAGE_SIZE; switch (testNum) { case 1: case 10: case 11: break; case 2: /* changePort=true; */ changeIP=true; break; case 3: changePort=true; break; case 4: changeIP=true; break; case 5: discard=true; break; default: printf("Test %i is unkown\n", testNum); return ; /* error */ } memset(&req, 0, sizeof(StunMessage)); stunBuildReqSimple( &req, username, changePort , changeIP , testNum ); len = stunEncodeMessage( &req, buf, len, password,verbose ); if ( verbose ) { printf("About to send msg of len %i to %s\n", len, ipaddr(dest) ); } sendMessage( myFd, buf, len, dest->addr, dest->port, verbose ); /* add some delay so the packets don't get sent too quickly */ #if defined(_WIN32_WCE) Sleep (10); #elif defined(WIN32)/* !cj! TODO - should fix this up in windows */ { clock_t now = clock(); assert( CLOCKS_PER_SEC == 1000 ); while ( clock() <= now+10 ) { }; } #else usleep(10*1000); #endif } void stunGetUserNameAndPassword( const StunAddress4 *dest, StunAtrString* username, StunAtrString* password) { /* !cj! This is totally bogus - need to make TLS connection to dest and get a */ /* username and password to use */ stunCreateUserName(dest, username); stunCreatePassword(username, password); } int stunTest( StunAddress4 *dest, int testNum, bool verbose, StunAddress4* sAddr , StunAddress4 *sMappedAddr, StunAddress4* sChangedAddr) { /* assert( dest.addr != 0 ); */ /* assert( dest.port != 0 ); */ int port = randomPort(); UInt32 interfaceIp=0; Socket myFd; StunAtrString username; StunAtrString password; char msg[STUN_MAX_MESSAGE_SIZE]; int msgLen = STUN_MAX_MESSAGE_SIZE; StunAddress4 from; StunMessage resp; bool ok; if (sAddr) { interfaceIp = sAddr->addr; if ( sAddr->port != 0 ) { port = sAddr->port; } } myFd = openPort(port,interfaceIp,verbose); if ( myFd == INVALID_SOCKET) return -1; username.sizeValue = 0; password.sizeValue = 0; #ifdef USE_TLS stunGetUserNameAndPassword( dest, &username, &password ); #endif stunSendTest( myFd, dest, &username, &password, testNum, verbose ); ok = getMessage( myFd, msg, &msgLen, &from.addr, &from.port,verbose ); closesocket(myFd); if (!ok) return -1; memset(&resp, 0, sizeof(StunMessage)); if ( verbose ) printf("Got a response"); ok = stunParseMessage( msg,msgLen, &resp,verbose ); if ( verbose ) { printf("\t ok=%i\n", ok ); #if defined(WIN32) || defined(_WIN32_WCE) printf("\t id=%u\n", *(unsigned int*)&resp.msgHdr.id ); #endif printf("\t mappedAddr=%i\n", resp.mappedAddress.ipv4.addr ); printf("\t changedAddr=%i\n", resp.changedAddress.ipv4.addr ); printf("\n"); } if (sAddr) { sAddr->port = port; } if (sMappedAddr) { sMappedAddr->port = resp.mappedAddress.ipv4.port; sMappedAddr->addr = resp.mappedAddress.ipv4.addr; } if (sChangedAddr) { sChangedAddr->port = resp.changedAddress.ipv4.port; sChangedAddr->addr = resp.changedAddress.ipv4.addr; } if (ok) return 0; else return -1; } NatType stunNatType( StunAddress4 *dest, bool verbose, bool* preservePort, /* if set, is return for if NAT preservers ports or not */ bool* hairpin, /* if set, is the return for if NAT will hairpin packets */ int port, /* port to use for the test, 0 to choose random port */ StunAddress4* sAddr /* NIC to use */ ) { /* assert( dest.addr != 0 ); */ /* assert( dest.port != 0 ); */ UInt32 interfaceIp=0; Socket myFd1; Socket myFd2; bool respTestI=false; bool isNat=true; StunAddress4 testIchangedAddr; StunAddress4 testImappedAddr; bool respTestI2=false; bool mappedIpSame = true; StunAddress4 testI2mappedAddr; /* StunAddress4 testI2dest=dest; */ StunAddress4 testI2dest; bool respTestII=false; bool respTestIII=false; bool respTestHairpin=false; StunAtrString username; StunAtrString password; int count=0; int second_started; int second_elapsed; Socket s; if ( hairpin ) { *hairpin = false; } if ( port == 0 ) { port = randomPort(); } if (sAddr) { interfaceIp = sAddr->addr; } myFd1 = openPort(port,interfaceIp,verbose); myFd2 = openPort(port+1,interfaceIp,verbose); if ( ( myFd1 == INVALID_SOCKET) || ( myFd2 == INVALID_SOCKET) ) { printf("Some problem opening port/interface to send on\n"); return StunTypeFailure; } /* assert( myFd1 != INVALID_SOCKET ); */ /* assert( myFd2 != INVALID_SOCKET ); */ memcpy(&testI2dest, dest, sizeof(StunAddress4)); memset(&testImappedAddr,0,sizeof(testImappedAddr)); username.sizeValue = 0; password.sizeValue = 0; #ifdef USE_TLS stunGetUserNameAndPassword( dest, username, password ); #endif /* stunSendTest( myFd1, dest, username, password, 1, verbose ); */ second_started = stunGetSystemTimeSecs(); second_elapsed = 1; while ( count < 7 && second_elapsed < 5) { struct timeval tv; fd_set fdSet; int err; int e; #if defined(WIN32) || defined(_WIN32_WCE) unsigned int fdSetSize; #else int fdSetSize; #endif second_elapsed = stunGetSystemTimeSecs() - second_started ; FD_ZERO(&fdSet); fdSetSize=0; FD_SET(myFd1,&fdSet); fdSetSize = (myFd1+1>fdSetSize) ? myFd1+1 : fdSetSize; FD_SET(myFd2,&fdSet); fdSetSize = (myFd2+1>fdSetSize) ? myFd2+1 : fdSetSize; tv.tv_sec=0; tv.tv_usec=500*1000; /* 150 ms */ if ( count == 0 ) tv.tv_usec=0; err = select(fdSetSize, &fdSet, NULL, NULL, &tv); e = getErrno(); if ( err == SOCKET_ERROR ) { /* error occured */ #if !defined(_WIN32_WCE) printf("Error %i %s in select\n", e, strerror(e)); #else printf("Error %i in select\n", e); #endif closesocket(myFd1); /* AMD */ closesocket(myFd2); /* AMD */ return StunTypeFailure; } else if ( err == 0 ) { /* timeout occured */ count++; if ( !respTestI ) { stunSendTest( myFd1, dest, &username, &password, 1 ,verbose ); } if ( (!respTestI2) && respTestI ) { /* check the address to send to if valid */ if ( ( testI2dest.addr != 0 ) && ( testI2dest.port != 0 ) ) { stunSendTest( myFd1, &testI2dest, &username, &password, 10 ,verbose); } } if ( !respTestII ) { stunSendTest( myFd2, dest, &username, &password, 2 ,verbose ); } if ( !respTestIII ) { stunSendTest( myFd2, dest, &username, &password, 3 ,verbose ); } if ( respTestI && (!respTestHairpin) ) { if ( ( testImappedAddr.addr != 0 ) && ( testImappedAddr.port != 0 ) ) { stunSendTest( myFd1, &testImappedAddr, &username, &password, 11 ,verbose ); } } } else { int i; /* if (verbose) printf("-----------------------------------------"); */ /* assert( err>0 ); */ /* data is avialbe on some fd */ for ( i=0; i<2; i++) { Socket myFd; if ( i==0 ) { myFd=myFd1; } else { myFd=myFd2; } if ( myFd!=INVALID_SOCKET ) { if ( FD_ISSET(myFd,&fdSet) ) { char msg[STUN_MAX_MESSAGE_SIZE]; int msgLen = sizeof(msg); StunAddress4 from; StunMessage resp; getMessage( myFd, msg, &msgLen, &from.addr, &from.port,verbose ); memset(&resp, 0, sizeof(StunMessage)); stunParseMessage( msg,msgLen, &resp,verbose ); if ( verbose ) { printf("Received message of type %i id=%i\n", resp.msgHdr.msgType, (int)(resp.msgHdr.id.octet[0]) ); } switch( resp.msgHdr.id.octet[0] ) { case 1: { if ( !respTestI ) { testIchangedAddr.addr = resp.changedAddress.ipv4.addr; testIchangedAddr.port = resp.changedAddress.ipv4.port; testImappedAddr.addr = resp.mappedAddress.ipv4.addr; testImappedAddr.port = resp.mappedAddress.ipv4.port; if ( preservePort ) { *preservePort = ( testImappedAddr.port == port ); } testI2dest.addr = resp.changedAddress.ipv4.addr; if (sAddr) { sAddr->port = testImappedAddr.port; sAddr->addr = testImappedAddr.addr; } count = 0; } respTestI=true; } break; case 2: { respTestII=true; } break; case 3: { respTestIII=true; } break; case 10: { if ( !respTestI2 ) { testI2mappedAddr.addr = resp.mappedAddress.ipv4.addr; testI2mappedAddr.port = resp.mappedAddress.ipv4.port; mappedIpSame = false; if ( (testI2mappedAddr.addr == testImappedAddr.addr ) && (testI2mappedAddr.port == testImappedAddr.port )) { mappedIpSame = true; } } respTestI2=true; } break; case 11: { if ( hairpin ) { *hairpin = true; } respTestHairpin = true; } break; } } } } } } closesocket(myFd1); /* AMD */ closesocket(myFd2); /* AMD */ /* see if we can bind to this address */ /* cerr << "try binding to " << testImappedAddr ); */ s = openPort( 0/*use ephemeral*/, testImappedAddr.addr, false ); if ( s != INVALID_SOCKET ) { isNat = false; /* cerr << "binding worked"); */ } else { isNat = true; /* cerr << "binding failed"); */ } closesocket(s); /* AMD */ if (verbose) { printf("test I = %i\n", respTestI ); printf("test II = %i\n", respTestII ); printf("test III = %i\n", respTestIII ); printf("test I(2) = %i\n", respTestI2 ); printf("is nat = %i\n", isNat); printf("mapped IP same = %i\n", mappedIpSame ); } /* implement logic flow chart from draft RFC */ if ( respTestI ) { if ( isNat ) { if (respTestII) { return StunTypeConeNat; } else { if ( mappedIpSame ) { if ( respTestIII ) { return StunTypeRestrictedNat; } else { return StunTypePortRestrictedNat; } } else { return StunTypeSymNat; } } } else { if (respTestII) { return StunTypeOpen; } else { return StunTypeSymFirewall; } } } else { return StunTypeBlocked; } return StunTypeUnknown; } int stunOpenSocket( StunAddress4 *dest, StunAddress4* mapAddr, int port, StunAddress4* srcAddr, bool verbose ) { /* assert( dest.addr != 0 ); */ /* assert( dest.port != 0 ); */ /* assert( mapAddr );*/ unsigned int interfaceIp = 0; Socket myFd; char msg[STUN_MAX_MESSAGE_SIZE]; int msgLen = sizeof(msg); StunAtrString username; StunAtrString password; StunAddress4 from; StunMessage resp; bool ok; StunAddress4 mappedAddr; if ( port == 0 ) { port = randomPort(); } if ( srcAddr ) { interfaceIp = srcAddr->addr; } myFd = openPort(port,interfaceIp,verbose); if (myFd == INVALID_SOCKET) { return myFd; } username.sizeValue = 0; password.sizeValue = 0; #ifdef USE_TLS stunGetUserNameAndPassword( dest, username, password ); #endif stunSendTest(myFd, dest, &username, &password, 1, 0/*false*/ ); getMessage( myFd, msg, &msgLen, &from.addr, &from.port,verbose ); memset(&resp, 0, sizeof(StunMessage)); ok = stunParseMessage( msg, msgLen, &resp,verbose ); if (!ok) { closesocket(myFd); return -1; } mappedAddr = resp.mappedAddress.ipv4; /* printf("--- stunOpenSocket --- "); printf("\treq id=" << req.id ); printf("\tresp id=" << id ); printf("\tmappedAddr=" << mappedAddr ); */ *mapAddr = mappedAddr; return myFd; } bool stunOpenSocketPair(StunAddress4 *dest, StunAddress4* mapAddr_rtp, StunAddress4* mapAddr_rtcp, int* fd1, int* fd2, int port, StunAddress4* srcAddr, bool verbose ) { /* assert( dest.addr!= 0 ); */ /* assert( dest.port != 0 ); */ /* assert( mapAddr ); */ const int NUM=2; char msg[STUN_MAX_MESSAGE_SIZE]; int msgLen =sizeof(msg); StunAddress4 from; int fd[2/*NUM*/]; int i; unsigned int interfaceIp = 0; StunAtrString username; StunAtrString password; StunAddress4 mappedAddr[2/*NUM*/]; if ( port == 0 ) { port = randomPort(); } *fd1=-1; *fd2=-1; if ( srcAddr ) { interfaceIp = srcAddr->addr; } for( i=0; i 0) { closesocket(fd[--i]); } return false; } } username.sizeValue = 0; password.sizeValue = 0; #ifdef USE_TLS stunGetUserNameAndPassword( dest, username, password ); #endif for( i=0; i