/*
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"
#include "ortp/ortp.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_longterm(char* hmac, const char* input, int length,
const char *username, const char *realm, const char *password);
static void
computeHmac_shortterm(char* hmac, const char* input, int length, const char* key);
static bool_t
stunParseAtrAddress( char* body, unsigned int hdrLen, StunAtrAddress4 *result )
{
if ( hdrLen != 8 )
{
ortp_error("stun: 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)
{
ortp_error("stun: ipv6 not supported\n");
}
else
{
ortp_error("stun: bad address family: %i\n", result->family);
}
return FALSE;
}
static bool_t
stunParseAtrChangeRequest( char* body, unsigned int hdrLen, StunAtrChangeRequest *result )
{
if ( hdrLen != 4 )
{
/* ortp_error("stun: hdr length = %i expecting %i\n",hdrLen, sizeof(result)); */
ortp_error("stun: Incorrect size for SA_CHANGEREQUEST");
return FALSE;
}
else
{
memcpy(&result->value, body, 4);
result->value = ntohl(result->value);
return TRUE;
}
}
static bool_t
stunParseAtrError( char* body, unsigned int hdrLen, StunAtrError *result )
{
if ( hdrLen >= sizeof(StunAtrError) )
{
ortp_error("stun: Incorrect size for SA_ERRORCODE");
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_t
stunParseAtrUnknown( char* body, unsigned int hdrLen, StunAtrUnknown *result )
{
if ( hdrLen >= sizeof(result) )
{
ortp_error("stun: Incorrect size for SA_UNKNOWNATTRIBUTE");
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_t
stunParseAtrString( char* body, unsigned int hdrLen, StunAtrString *result )
{
if ( hdrLen >= STUN_MAX_STRING )
{
ortp_error("stun: String is too large");
return FALSE;
}
else
{
result->sizeValue = hdrLen;
memcpy(&result->value, body, hdrLen);
result->value[hdrLen] = 0;
return TRUE;
}
}
static bool_t
stunParseAtrIntegrity( char* body, unsigned int hdrLen, StunAtrIntegrity *result )
{
if ( hdrLen != 20)
{
ortp_error("stun: SA_MESSAGEINTEGRITY must be 20 bytes");
return FALSE;
}
else
{
memcpy(&result->hash, body, hdrLen);
return TRUE;
}
}
static bool_t
turnParseAtrChannelNumber( char* body, unsigned int hdrLen, TurnAtrChannelNumber *result )
{
if ( hdrLen >= sizeof(result) )
{
ortp_error("stun: Incorrect size for TA_CHANNELNUMBER");
return FALSE;
}
else
{
if (hdrLen % 4 != 0) return FALSE;
memcpy(&result->channelNumber, body, 2);
body+=2;
result->channelNumber = ntohs(result->channelNumber);
memcpy(&result->rffu, body, 2);
body+=2;
result->rffu = ntohs(result->rffu);
return TRUE;
}
}
static bool_t
turnParseAtrLifetime( char* body, unsigned int hdrLen, TurnAtrLifetime *result )
{
if ( hdrLen != sizeof(result) )
{
ortp_error("stun: Incorrect size for TA_LIFETIME");
return FALSE;
}
else
{
memcpy(&result->lifetime, body, 4);
result->lifetime = ntohl(result->lifetime);
return TRUE;
}
}
static bool_t
turnParseAtrData( char* body, unsigned int hdrLen, TurnAtrData *result )
{
if ( hdrLen >= 1500 )
{
ortp_error("stun: Incorrect size for TA_DATA");
return FALSE;
}
else
{
result->sizeValue = hdrLen;
memcpy(&result->value, body, hdrLen);
result->value[hdrLen] = 0;
return TRUE;
}
}
static bool_t
turnParseAtrRequestedTransport( char* body, unsigned int hdrLen, TurnAtrRequestedTransport *result )
{
if ( hdrLen != 4 )
{
ortp_error("stun: Incorrect size for TA_REQUESTEDTRANSPORT");
return FALSE;
}
result->proto = *body++;
result->pad1 = *body++;
result->pad2 = *body++;
result->pad3 = *body++;
return TRUE;
}
#ifdef ORTP_BIGENDIAN
#define htonq(n) n
#define ntohq(n) n
#else /* little endian */
static inline UInt64
htonq (UInt64 v)
{
return htonl ((UInt32) (v >> 32))
| (UInt64) htonl ((UInt32) v) << 32;
}
static inline UInt64
ntohq (UInt64 v)
{
return ntohl ((UInt32) (v >> 32))
| (UInt64) ntohl ((UInt32) v) << 32;
}
#endif /* little endian */
static bool_t
turnParseAtrReservationToken( char* body, unsigned int hdrLen, TurnAtrReservationToken *result )
{
if ( hdrLen != 8 )
{
ortp_error("stun: Incorrect size for TA_RESERVATIONTOKEN");
return FALSE;
}
memcpy(&result->value, body, 8);
result->value = ntohq(result->value);
return TRUE;
}
static bool_t
turnParseAtrFingerprint( char* body, unsigned int hdrLen, TurnAtrFingerprint *result )
{
if ( hdrLen != 4 )
{
ortp_error("stun: Incorrect size for SA_FINGERPRINT");
return FALSE;
}
memcpy(&result->fingerprint, body, 4);
result->fingerprint = ntohl(result->fingerprint);
return TRUE;
}
static bool_t
iceParseAtrPriority( char* body, unsigned int hdrLen, IceAtrPriority *result )
{
if ( hdrLen != sizeof(result) )
{
ortp_error("stun: Incorrect size for ICEA_PRIORITY");
return FALSE;
}
memcpy(&result->priority, body, 4);
result->priority = ntohl(result->priority);
return TRUE;
}
static bool_t
iceParseAtrIceControll( char* body, unsigned int hdrLen, IceAtrIceControll *result )
{
if ( hdrLen != sizeof(result) )
{
ortp_error("stun: Incorrect size for ICEA_ICECONTROLLED/ICEA_ICECONTROLLING");
return FALSE;
}
memcpy(&result->value, body, 8);
result->value = ntohq(result->value);
return TRUE;
}
bool_t
stunParseMessage( char* buf, unsigned int bufLen, StunMessage *msg)
{
char* body;
unsigned int size;
ortp_debug("stun: Received stun message: %i bytes\n", bufLen);
memset(msg, 0, sizeof(msg));
if (sizeof(StunMsgHdr) > bufLen)
{
ortp_warning("stun: message too short\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)
{
ortp_warning("stun: 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;
/*ortp_message("stun: 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 ( attrLen+4 > size )
{
ortp_error("stun: 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 == SA_MAPPEDADDRESS)
{
msg->hasMappedAddress = TRUE;
if ( stunParseAtrAddress( body, attrLen, &msg->mappedAddress )== FALSE )
{
ortp_error("stun: problem parsing SA_MAPPEDADDRESS\n");
return FALSE;
}
else
{
ortp_debug("stun: SA_MAPPEDADDRESS = %s\n", ipaddr(&msg->mappedAddress.ipv4));
}
}
else if (atrType == SA_RESPONSEADDRESS)
{
msg->hasResponseAddress = TRUE;
if ( stunParseAtrAddress( body, attrLen, &msg->responseAddress )== FALSE )
{
ortp_error("stun: problem parsing SA_RESPONSEADDRESS");
return FALSE;
}
else
{
ortp_debug("stun: SA_RESPONSEADDRESS = %s\n", ipaddr(&msg->responseAddress.ipv4));
}
}
else if (atrType == SA_CHANGEREQUEST)
{
msg->hasChangeRequest = TRUE;
if (stunParseAtrChangeRequest( body, attrLen, &msg->changeRequest) == FALSE)
{
ortp_error("stun: problem parsing SA_CHANGEREQUEST\n");
return FALSE;
}
else
{
ortp_debug("stun: SA_CHANGEREQUEST = %i\n", msg->changeRequest.value);
}
}
else if (atrType == SA_SOURCEADDRESS)
{
msg->hasSourceAddress = TRUE;
if ( stunParseAtrAddress( body, attrLen, &msg->sourceAddress )== FALSE )
{
ortp_error("stun: problem parsing SA_SOURCEADDRESS\n");
return FALSE;
}
else
{
ortp_debug("stun: SA_SOURCEADDRESS = %s\n", ipaddr(&msg->sourceAddress.ipv4) );
}
}
else if (atrType == SA_CHANGEDADDRESS)
{
msg->hasChangedAddress = TRUE;
if ( stunParseAtrAddress( body, attrLen, &msg->changedAddress )== FALSE )
{
ortp_error("stun: problem parsing SA_CHANGEDADDRESS\n");
return FALSE;
}
else
{
ortp_debug("stun: SA_CHANGEDADDRESS = %s\n", ipaddr(&msg->changedAddress.ipv4));
}
}
else if (atrType == SA_USERNAME)
{
msg->hasUsername = TRUE;
if (stunParseAtrString( body, attrLen, &msg->username) == FALSE)
{
ortp_error("stun: problem parsing SA_USERNAME");
return FALSE;
}
else
{
ortp_debug("stun: SA_USERNAME = %s\n", msg->username.value );
}
}
else if (atrType == SA_PASSWORD)
{
msg->hasPassword = TRUE;
if (stunParseAtrString( body, attrLen, &msg->password) == FALSE)
{
ortp_error("stun: problem parsing SA_PASSWORD");
return FALSE;
}
else
{
ortp_debug("stun: SA_PASSWORD = %s\n", msg->password.value );
}
}
else if (atrType == SA_MESSAGEINTEGRITY)
{
msg->hasMessageIntegrity = TRUE;
if (stunParseAtrIntegrity( body, attrLen, &msg->messageIntegrity) == FALSE)
{
ortp_error("stun: problem parsing SA_MESSAGEINTEGRITY");
return FALSE;
}
}
else if (atrType == SA_ERRORCODE)
{
msg->hasErrorCode = TRUE;
if (stunParseAtrError(body, attrLen, &msg->errorCode) == FALSE)
{
ortp_error("stun: problem parsing SA_ERRORCODE");
return FALSE;
}
else
{
ortp_debug("stun: SA_ERRORCODE = %i %i %s\n",
msg->errorCode.errorClass ,
msg->errorCode.number ,
msg->errorCode.reason );
}
}
else if (atrType == SA_UNKNOWNATTRIBUTE)
{
msg->hasUnknownAttributes = TRUE;
if (stunParseAtrUnknown(body, attrLen, &msg->unknownAttributes) == FALSE)
{
ortp_error("stun: problem parsing SA_UNKNOWNATTRIBUTE");
return FALSE;
}
}
else if (atrType == SA_REFLECTEDFROM)
{
msg->hasReflectedFrom = TRUE;
if ( stunParseAtrAddress( body, attrLen, &msg->reflectedFrom ) == FALSE )
{
ortp_error("stun: problem parsing SA_REFLECTEDFROM");
return FALSE;
}
}
else if (atrType == SA_REALM)
{
msg->hasRealm = TRUE;
if (stunParseAtrString( body, attrLen, &msg->realmName) == FALSE)
{
ortp_error("stun: problem parsing SA_REALM");
return FALSE;
}
else
{
ortp_debug("stun: SA_REALM = %s\n", msg->realmName.value );
}
}
else if (atrType == SA_NONCE)
{
msg->hasNonce = TRUE;
if (stunParseAtrString( body, attrLen, &msg->nonceName) == FALSE)
{
ortp_error("stun: problem parsing SA_NONCE");
return FALSE;
}
else
{
ortp_debug("stun: SA_NONCE = %s\n", msg->nonceName.value );
}
}
else if (atrType == SA_XORMAPPEDADDRESS || atrType == SA_XORMAPPEDADDRESS2)
{
msg->hasXorMappedAddress = TRUE;
if ( stunParseAtrAddress( body, attrLen, &msg->xorMappedAddress ) == FALSE )
{
ortp_error("stun: problem parsing SA_XORMAPPEDADDRESS");
return FALSE;
}
else
{
ortp_debug("stun: SA_XORMAPPEDADDRESS = %s\n", ipaddr(&msg->xorMappedAddress.ipv4) );
}
}
else if (atrType == SA_XORONLY)
{
ortp_warning("stun: SA_XORONLY - non standard extension ignored\n" );
}
else if (atrType == SA_SECONDARYADDRESS)
{
ortp_debug("stun: SA_SECONDARYADDRESS - non standard extension ignored\n" );
}
else if (atrType == SA_SOFTWARE)
{
msg->hasSoftware = TRUE;
if (stunParseAtrString( body, attrLen, &msg->softwareName) == FALSE)
{
ortp_error("stun: problem parsing SA_SOFTWARE");
return FALSE;
}
else
{
ortp_debug("stun: SA_SOFTWARE = %s\n", msg->softwareName.value );
}
}
else if (atrType == TA_CHANNELNUMBER)
{
msg->hasChannelNumberAttributes = TRUE;
if (turnParseAtrChannelNumber(body, attrLen, &msg->channelNumberAttributes) == FALSE)
{
ortp_error("stun: problem parsing TA_CHANNELNUMBER");
return FALSE;
}
}
else if (atrType == TA_LIFETIME)
{
msg->hasLifetimeAttributes = TRUE;
if (turnParseAtrLifetime(body, attrLen, &msg->lifetimeAttributes) == FALSE)
{
ortp_error("stun: problem parsing TA_LIFETIME");
return FALSE;
}
}
else if (atrType == TA_DEPRECATEDBANDWIDTH)
{
ortp_warning("stun: deprecated attribute TA_DEPRECATEDBANDWIDTH");
}
else if (atrType == TA_XORPEERADDRESS)
{
msg->hasXorPeerAddress = TRUE;
if ( stunParseAtrAddress( body, attrLen, &msg->xorPeerAddress )== FALSE )
{
ortp_error("stun: problem parsing SA_XORPEERADDRESS\n");
return FALSE;
}
else
{
ortp_debug("stun: SA_XORPEERADDRESS = %s\n", ipaddr(&msg->xorPeerAddress.ipv4));
}
}
else if (atrType == TA_DATA)
{
msg->hasData = TRUE;
if (turnParseAtrData( body, attrLen, &msg->data) == FALSE)
{
ortp_error("stun: problem parsing TA_DATA");
return FALSE;
}
else
{
}
}
else if (atrType == TA_XORRELAYEDADDRESS)
{
msg->hasXorRelayedAddress = TRUE;
if ( stunParseAtrAddress( body, attrLen, &msg->xorRelayedAddress )== FALSE )
{
ortp_error("stun: problem parsing TA_XORRELAYEDADDRESS\n");
return FALSE;
}
else
{
ortp_debug("stun: TA_XORRELAYEDADDRESS = %s\n", ipaddr(&msg->xorRelayedAddress.ipv4));
}
}
else if (atrType == TA_EVENPORT)
{
ortp_warning("stun: do we need this... TA_EVENPORT");
}
else if (atrType == TA_REQUESTEDTRANSPORT)
{
msg->hasRequestedTransport = TRUE;
if ( turnParseAtrRequestedTransport( body, attrLen, &msg->requestedTransport )== FALSE )
{
ortp_error("stun: problem parsing TA_REQUESTEDTRANSPORT\n");
return FALSE;
}
}
else if (atrType == TA_DONTFRAGMENT)
{
msg->hasDontFragment = TRUE;
}
else if (atrType == TA_DEPRECATEDTIMERVAL)
{
ortp_warning("stun: deprecated attribute TA_DEPRECATEDTIMERVAL");
}
else if (atrType == TA_RESERVATIONTOKEN)
{
msg->hasReservationToken = TRUE;
if ( turnParseAtrReservationToken( body, attrLen, &msg->reservationToken)== FALSE )
{
ortp_error("stun: problem parsing TA_RESERVATIONTOKEN\n");
return FALSE;
}
}
else if (atrType == SA_FINGERPRINT)
{
msg->hasFingerprint = TRUE;
if ( turnParseAtrFingerprint( body, attrLen, &msg->fingerprint)== FALSE )
{
ortp_error("stun: problem parsing SA_FINGERPRINT\n");
return FALSE;
}
}
else if (atrType == ICEA_PRIORITY)
{
msg->hasLifetimeAttributes = TRUE;
if (iceParseAtrPriority(body, attrLen, &msg->priority) == FALSE)
{
ortp_error("stun: problem parsing ICEA_PRIORITY");
return FALSE;
}
}
else if (atrType == ICEA_USECANDIDATE)
{
msg->hasUseCandidate = TRUE;
}
else if (atrType == ICEA_ICECONTROLLED)
{
msg->hasIceControlled = TRUE;
if (iceParseAtrIceControll(body, attrLen, &msg->iceControlled) == FALSE)
{
ortp_error("stun: problem parsing ICEA_ICECONTROLLED");
return FALSE;
}
}
else if (atrType == ICEA_ICECONTROLLING)
{
msg->hasIceControlling = TRUE;
if (iceParseAtrIceControll(body, attrLen, &msg->iceControlling) == FALSE)
{
ortp_error("stun: problem parsing ICEA_ICECONTROLLING");
return FALSE;
}
}
else
{
if ( atrType <= 0x7FFF )
{
ortp_error("stun: Unknown Comprehension-Required attribute: %04x\n", atrType );
return FALSE;
}
else
ortp_warning("stun: Unknown attribute: %04x\n", atrType );
}
if (attrLen%4>0)
attrLen += (4-(attrLen%4));
body += attrLen;
size -= attrLen;
}
return TRUE;
}
static char*
encode16(char* buf, UInt16 data)
{
UInt16 ndata = htons(data);
memcpy(buf, &ndata, sizeof(UInt16));
return buf + sizeof(UInt16);
}
static char*
encode32(char* buf, UInt32 data)
{
UInt32 ndata = htonl(data);
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, SA_CHANGEREQUEST);
ptr = encode16(ptr, 4);
ptr = encode32(ptr, atr->value);
return ptr;
}
static char*
encodeAtrError(char* ptr, const StunAtrError *atr)
{
ptr = encode16(ptr, SA_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, SA_UNKNOWNATTRIBUTE);
ptr = encode16(ptr, 2+2*atr->numAttributes);
for (i=0; inumAttributes; i++)
{
ptr = encode16(ptr, atr->attrType[i]);
}
return ptr;
}
static char*
encodeAtrString(char* ptr, UInt16 type, const StunAtrString *atr)
{
int padding;
int i;
ptr = encode16(ptr, type);
ptr = encode16(ptr, atr->sizeValue);
ptr = encode(ptr, atr->value, atr->sizeValue);
padding = atr->sizeValue % 4;
if (padding>0)
{
for (i=0;i<4-padding;i++)
{
*ptr++ = 0;
}
}
return ptr;
}
static char*
encodeAtrIntegrity(char* ptr, const StunAtrIntegrity *atr)
{
ptr = encode16(ptr, SA_MESSAGEINTEGRITY);
ptr = encode16(ptr, 20);
ptr = encode(ptr, atr->hash, sizeof(atr->hash));
return ptr;
}
static char*
encodeAtrRequestedTransport(char* ptr, const TurnAtrRequestedTransport *atr)
{
ptr = encode16(ptr, TA_REQUESTEDTRANSPORT);
ptr = encode16(ptr, 4);
*ptr++ = atr->proto;
*ptr++ = atr->pad1;
*ptr++ = atr->pad2;
*ptr++ = atr->pad3;
return ptr;
}
static char*
encodeAtrLifeTime(char* ptr, const TurnAtrLifetime *atr)
{
ptr = encode16(ptr, TA_LIFETIME);
ptr = encode16(ptr, 4);
ptr = encode32(ptr, atr->lifetime);
return ptr;
}
static char*
encodeAtrDontFragment(char* ptr)
{
ptr = encode16(ptr, TA_DONTFRAGMENT);
ptr = encode16(ptr, 0);
return ptr;
}
unsigned int
stunEncodeMessage( const StunMessage *msg,
char* buf,
unsigned int bufLen,
const StunAtrString *password)
{
char* ptr = buf;
char* lengthp;
ptr = encode16(ptr, msg->msgHdr.msgType);
lengthp = ptr;
ptr = encode16(ptr, 0);
ptr = encode32(ptr, msg->msgHdr.magic_cookie);
ptr = encode(ptr, (const char*)msg->msgHdr.tr_id.octet, sizeof(msg->msgHdr.tr_id));
ortp_debug("stun: Encoding stun message: ");
if (msg->hasRequestedTransport)
{
ortp_debug("stun: Encoding TA_REQUESTEDTRANSPORT: %i\n", msg->requestedTransport.proto );
ptr = encodeAtrRequestedTransport (ptr, &msg->requestedTransport);
}
if (msg->hasLifetimeAttributes)
{
ortp_debug("stun: Encoding TA_LIFETIME: %i\n", msg->lifetimeAttributes.lifetime );
ptr = encodeAtrLifeTime (ptr, &msg->lifetimeAttributes);
}
if (msg->hasDontFragment)
{
ortp_debug("stun: Encoding TA_DONTFRAGMENT: DF\n");
ptr = encodeAtrDontFragment (ptr);
}
if (msg->hasMappedAddress)
{
ortp_debug("stun: Encoding SA_MAPPEDADDRESS: %s\n", ipaddr(&msg->mappedAddress.ipv4) );
ptr = encodeAtrAddress4 (ptr, SA_MAPPEDADDRESS, &msg->mappedAddress);
}
if (msg->hasResponseAddress)
{
ortp_debug("stun: Encoding SA_RESPONSEADDRESS: %s\n", ipaddr(&msg->responseAddress.ipv4) );
ptr = encodeAtrAddress4(ptr, SA_RESPONSEADDRESS, &msg->responseAddress);
}
if (msg->hasChangeRequest)
{
ortp_debug("stun: Encoding SA_CHANGEREQUEST: %i\n", msg->changeRequest.value );
ptr = encodeAtrChangeRequest(ptr, &msg->changeRequest);
}
if (msg->hasSourceAddress)
{
ortp_debug("stun: Encoding SA_SOURCEADDRESS: %s\n", ipaddr(&msg->sourceAddress.ipv4) );
ptr = encodeAtrAddress4(ptr, SA_SOURCEADDRESS, &msg->sourceAddress);
}
if (msg->hasChangedAddress)
{
ortp_debug("stun: Encoding SA_CHANGEDADDRESS: %s\n", ipaddr(&msg->changedAddress.ipv4) );
ptr = encodeAtrAddress4(ptr, SA_CHANGEDADDRESS, &msg->changedAddress);
}
if (msg->hasUsername)
{
ortp_debug("stun: Encoding SA_USERNAME: %s\n", msg->username.value );
ptr = encodeAtrString(ptr, SA_USERNAME, &msg->username);
}
//if (msg->hasPassword)
//{
// ortp_debug("stun: Encoding SA_PASSWORD: %s\n", msg->password.value );
// ptr = encodeAtrString(ptr, SA_PASSWORD, &msg->password);
//}
if (msg->hasErrorCode)
{
ortp_debug("stun: Encoding SA_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)
{
ortp_debug("stun: Encoding SA_UNKNOWNATTRIBUTE: ???");
ptr = encodeAtrUnknown(ptr, &msg->unknownAttributes);
}
if (msg->hasReflectedFrom)
{
ortp_debug("stun: Encoding SA_REFLECTEDFROM: %s\n", ipaddr(&msg->reflectedFrom.ipv4) );
ptr = encodeAtrAddress4(ptr, SA_REFLECTEDFROM, &msg->reflectedFrom);
}
if (msg->hasNonce)
{
ortp_debug("stun: Encoding SA_NONCE: %s\n", msg->nonceName.value );
ptr = encodeAtrString(ptr, SA_NONCE, &msg->nonceName);
}
if (msg->hasRealm)
{
ortp_debug("stun: Encoding SA_REALM: %s\n", msg->realmName.value );
ptr = encodeAtrString(ptr, SA_REALM, &msg->realmName);
}
if (msg->hasXorMappedAddress)
{
ortp_debug("stun: Encoding SA_XORMAPPEDADDRESS: %s\n", ipaddr(&msg->xorMappedAddress.ipv4) );
ptr = encodeAtrAddress4 (ptr, SA_XORMAPPEDADDRESS, &msg->xorMappedAddress);
}
if (msg->hasSoftware)
{
ortp_debug("stun: Encoding SA_SOFTWARE: %s\n", msg->softwareName.value );
ptr = encodeAtrString(ptr, SA_SOFTWARE, &msg->softwareName);
}
if (msg->hasMessageIntegrity
&&password!=NULL && password->sizeValue > 0
&&msg->username.sizeValue>0
&&msg->realmName.sizeValue>0)
{
StunAtrIntegrity integrity;
//ortp_debug("stun: HMAC with password: %s\n", password->value );
encode16(lengthp, (UInt16)(ptr - buf - sizeof(StunMsgHdr)+24));
computeHmac_longterm(integrity.hash, buf, (int)(ptr-buf) ,
msg->username.value, msg->realmName.value, password->value);
ptr = encodeAtrIntegrity(ptr, &integrity);
}
else if (msg->hasMessageIntegrity
&&password!=NULL && password->sizeValue > 0
&&msg->username.sizeValue>0)
{
StunAtrIntegrity integrity;
//ortp_debug("stun: HMAC with password: %s\n", password->value );
encode16(lengthp, (UInt16)(ptr - buf - sizeof(StunMsgHdr)+24));
computeHmac_shortterm(integrity.hash, buf, (int)(ptr-buf) ,
password->value);
ptr = encodeAtrIntegrity(ptr, &integrity);
}
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_t 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(HAVE_DEV_RANDOM)
{
fd_set fdSet;
int maxFd=0;
struct timeval tv;
int e;
int fd=open("/dev/random",O_RDONLY);
if (fd<0)
{
ortp_message("stun: Failed to open random device\n");
return random();
}
FD_ZERO(&fdSet);
FD_SET(fd,&fdSet);
maxFd=fd+1;
tv.tv_sec = 0;
tv.tv_usec = 500;
e = select( maxFd, &fdSet, NULL,NULL, &tv );
if (e <= 0)
{
ortp_error("stun: Failed to get data from random device\n");
closesocket(fd);
return random();
}
read(fd,&tick,sizeof(tick));
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_longterm(char* hmac, const char* input, int length,
const char *username, const char *realm, const char *password)
{
strncpy(hmac,"hmac-not-implemented",20);
}
static void
computeHmac_shortterm(char* hmac, const char* input, int length, const char* key)
{
strncpy(hmac,"hmac-not-implemented",20);
}
#else
#include
#include
static void
computeHmac_longterm(char* hmac, const char* input, int length,
const char *username, const char *realm, const char *password)
{
unsigned int resultSize=0;
unsigned char HA1[16];
char HA1_text[1024];
snprintf(HA1_text, sizeof(HA1_text), "%s:%s:%s", username, realm, password);
MD5((unsigned char *)HA1_text, strlen(HA1_text), HA1);
HMAC(EVP_sha1(),
HA1, 16,
(const unsigned char*) input, length,
(unsigned char*)hmac, &resultSize);
}
static void
computeHmac_shortterm(char* hmac, const char* input, int length, const char* key)
{
unsigned int resultSize=0;
HMAC(EVP_sha1(),
key, strlen(key),
(const unsigned char*) input, length,
(unsigned char*)hmac, &resultSize);
}
#endif
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_t
stunParseHostName( const 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();*/
/* ortp_message("stun: 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();
ortp_message("stun: 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_t
stunParseServerName( const char* name, StunAddress4 *addr)
{
/* assert(name); */
/* TODO - put in DNS SRV stuff. */
bool_t 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 = (STUN_METHOD_BINDING | STUN_ERR_RESP);
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
#if 0
static void
stunCreateSharedSecretResponse(const StunMessage *request, const StunAddress4 *source, StunMessage *response)
{
response->msgHdr.msgType = SharedSecretResponseMsg;
response->msgHdr.tr_id = request->msgHdr.tr_id;
response->hasUsername = TRUE;
stunCreateUserName( source, &response->username);
response->hasPassword = TRUE;
stunCreatePassword( &response->username, &response->password);
}
#endif
/* This funtion takes a single message sent to a stun server, parses
and constructs an apropriate repsonse - returns TRUE if message is
valid */
bool_t
stunServerProcessMsg( char* buf,
unsigned int bufLen,
StunAddress4 *from,
StunAddress4 *myAddr,
StunAddress4 *altAddr,
StunMessage *resp,
StunAddress4 *destination,
StunAtrString *hmacPassword,
bool_t* changePort,
bool_t* changeIp)
{
int i;
StunMessage req;
StunAddress4 mapped;
StunAddress4 respondTo;
UInt32 flags;
bool_t 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);
if (!ok) /* Complete garbage, drop it on the floor */
{
ortp_error("stun: Request did not parse");
return FALSE;
}
//ortp_debug("stun: Request parsed ok");
mapped = req.mappedAddress.ipv4;
respondTo = req.responseAddress.ipv4;
flags = req.changeRequest.value;
if (req.msgHdr.msgType==(STUN_METHOD_BINDING|STUN_REQUEST))
{
if (!req.hasMessageIntegrity)
{
//ortp_debug("stun: BindRequest does not contain SA_MESSAGEINTEGRITY");
if (0) /* !jf! mustAuthenticate */
{
ortp_error("stun: Received BindRequest with no SA_MESSAGEINTEGRITY. Sending 401.");
stunCreateErrorResponse(resp, 4, 1, "Missing SA_MESSAGEINTEGRITY");
return TRUE;
}
}
else
{
if (!req.hasUsername)
{
ortp_error("stun: No UserName. Send 432.");
stunCreateErrorResponse(resp, 4, 32, "No UserName and contains SA_MESSAGEINTEGRITY");
return TRUE;
}
else
{
//ortp_debug("stun: 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];
//ortp_debug("stun: Validating SA_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)
{
ortp_error("stun: SA_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
{
ortp_error("stun: 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;
//ortp_debug("stun: Request is valid:\n");
//ortp_debug("stun: \t flags= %i\n", flags );
//ortp_debug("stun: \t changeIp= %i\n", *changeIp );
//ortp_debug("stun: \t changePort=%i\n", *changePort );
//ortp_debug("stun: \t from= %i\n", from->addr );
//ortp_debug("stun: \t respond to= %i\n", respondTo.addr );
//ortp_debug("stun: \t mapped= %i\n", mapped.addr );
/* form the outgoing message */
resp->msgHdr.msgType = (STUN_METHOD_BINDING | STUN_SUCCESS_RESP);
resp->msgHdr.magic_cookie = ntohl(req.msgHdr.magic_cookie);
for (i=0; i<12; i++ )
{
resp->msgHdr.tr_id.octet[i] = req.msgHdr.tr_id.octet[i];
}
if (1) /* do xorMapped address or not */
{
UInt32 cookie = 0x2112A442;
UInt16 cookie16;
resp->hasXorMappedAddress = TRUE;
cookie16 = ((UInt8*)&cookie)[0]<<8 | ((UInt8*)&cookie)[1];
resp->xorMappedAddress.ipv4.port = mapped.port^cookie16;
resp->xorMappedAddress.ipv4.addr = mapped.addr^cookie;
}
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 ( 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[] = "oRTP " STUN_VERSION; /* must pad to mult of 4 */
resp->hasSoftware = TRUE;
/* assert( sizeof(serverName) < STUN_MAX_STRING ); */
/* cerr << "sizeof serverName is " << sizeof(serverName) ); */
/* assert( sizeof(serverName)%4 == 0 ); */
memcpy( resp->softwareName.value, serverName, sizeof(serverName));
resp->softwareName.sizeValue = sizeof(serverName);
}
#if 0
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 );
}
#endif
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
{
ortp_error("stun: Unknown or unsupported request ");
return FALSE;
}
/* assert(0); */
return FALSE;
}
bool_t
stunInitServer(StunServerInfo *info, const StunAddress4 *myAddr, const StunAddress4 *altAddr, int startMediaPort)
{
/* 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)) == INVALID_SOCKET)
{
ortp_error("stun: Can't open %i\n", myAddr->addr );
stunStopServer(info);
return FALSE;
}
if ((info->altPortFd = openPort(altAddr->port,myAddr->addr)) == INVALID_SOCKET)
{
ortp_error("stun: Can't open %i\n", myAddr->addr );
stunStopServer(info);
return FALSE;
}
info->altIpFd = INVALID_SOCKET;
if ( altAddr->addr != 0 )
{
if ((info->altIpFd = openPort( myAddr->port, altAddr->addr)) == INVALID_SOCKET)
{
ortp_error("stun: Can't open %i\n", altAddr->addr );
stunStopServer(info);
return FALSE;
}
}
info->altIpPortFd = INVALID_SOCKET;
if ( altAddr->addr != 0 )
{ if ((info->altIpPortFd = openPort(altAddr->port, altAddr->addr)) == INVALID_SOCKET)
{
ortp_error("stun: Can't open %i\n", altAddr->addr );
stunStopServer(info);
return FALSE;
}
}
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;
}
}
}
}
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_t changePort, bool_t changeIp, unsigned int id )
{
int i;
/* assert( msg ); */
memset( msg , 0 , sizeof(*msg) );
msg->msgHdr.msgType = (STUN_METHOD_BINDING|STUN_REQUEST);
msg->msgHdr.magic_cookie = 0x2112A442;
for ( i=0; i<12; i=i+4 )
{
/* assert(i+3<16); */
int r = stunRand();
msg->msgHdr.tr_id.octet[i+0]= r>>0;
msg->msgHdr.tr_id.octet[i+1]= r>>8;
msg->msgHdr.tr_id.octet[i+2]= r>>16;
msg->msgHdr.tr_id.octet[i+3]= r>>24;
}
if ( id != 0 )
{
msg->msgHdr.tr_id.octet[0] = id;
}
if (changePort==TRUE || changeIp==TRUE)
{
msg->hasChangeRequest = TRUE;
msg->changeRequest.value =(changeIp?ChangeIpFlag:0) |
(changePort?ChangePortFlag:0);
}
if ( username!=NULL && 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 )
{
/* assert( dest.addr != 0 ); */
/* assert( dest.port != 0 ); */
bool_t changePort=FALSE;
bool_t changeIP=FALSE;
bool_t 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:
ortp_error("stun: 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 );
//ortp_debug("stun: About to send msg of len %i to %s\n", len, ipaddr(dest) );
sendMessage( myFd, buf, len, dest->addr, dest->port );
/* 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
}
#if 0
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);
}
#endif
int
stunTest( StunAddress4 *dest, int testNum, 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_t ok;
if (sAddr)
{
interfaceIp = sAddr->addr;
if ( sAddr->port != 0 )
{
port = sAddr->port;
}
}
myFd = openPort(port,interfaceIp);
if ( myFd == INVALID_SOCKET)
return -1;
username.sizeValue = 0;
password.sizeValue = 0;
#if 0
stunGetUserNameAndPassword( dest, &username, &password );
#endif
stunSendTest( myFd, dest, &username, &password, testNum );
ok = getMessage( myFd,
msg,
&msgLen,
&from.addr,
&from.port );
closesocket(myFd);
if (!ok)
return -1;
memset(&resp, 0, sizeof(StunMessage));
//ortp_debug("stun: Got a response");
ok = stunParseMessage( msg,msgLen, &resp );
//ortp_debug("stun: \t ok=%i\n", ok );
//ortp_debug("stun: \t SA_MAPPEDADDRESS=%i\n", resp.mappedAddress.ipv4.addr );
//ortp_debug("stun: \t SA_CHANGEDADDRESS=%i\n", resp.changedAddress.ipv4.addr );
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_t* preservePort, /* if set, is return for if NAT preservers ports or not */
bool_t* 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_t respTestI=FALSE;
bool_t isNat=TRUE;
StunAddress4 testIchangedAddr;
StunAddress4 testImappedAddr;
bool_t respTestI2=FALSE;
bool_t mappedIpSame = TRUE;
StunAddress4 testI2mappedAddr;
/* StunAddress4 testI2dest=dest; */
StunAddress4 testI2dest;
bool_t respTestII=FALSE;
bool_t respTestIII=FALSE;
bool_t respTestHairpin=FALSE;
StunAtrString username;
StunAtrString password;
int count=0;
UInt64 second_started;
UInt64 second_elapsed;
Socket s;
if ( hairpin )
{
*hairpin = FALSE;
}
if ( port == 0 )
{
port = randomPort();
}
if (sAddr)
{
interfaceIp = sAddr->addr;
}
myFd1 = openPort(port,interfaceIp);
myFd2 = openPort(port+1,interfaceIp);
if ( ( myFd1 == INVALID_SOCKET) || ( myFd2 == INVALID_SOCKET) )
{
ortp_error("stun: 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;
#if 0
stunGetUserNameAndPassword( dest, username, password );
#endif
/* stunSendTest( myFd1, dest, username, password, 1 ); */
second_started = stunGetSystemTimeSecs();
second_elapsed = 1;
while ( count < 3 && 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)
ortp_error("stun: Error %i %s in select\n", e, strerror(e));
#else
ortp_error("stun: 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 );
}
if ( (!respTestI2) && respTestI )
{
/* check the address to send to if valid */
if ( ( testI2dest.addr != 0 ) &&
( testI2dest.port != 0 ) )
{
stunSendTest( myFd1, &testI2dest, &username, &password, 10 );
}
}
if ( !respTestII )
{
stunSendTest( myFd2, dest, &username, &password, 2 );
}
if ( !respTestIII )
{
stunSendTest( myFd2, dest, &username, &password, 3 );
}
if ( respTestI && (!respTestHairpin) )
{
if ( ( testImappedAddr.addr != 0 ) &&
( testImappedAddr.port != 0 ) )
{
stunSendTest( myFd1, &testImappedAddr, &username, &password, 11 );
}
}
}
else
{
int i;
/* 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 );
memset(&resp, 0, sizeof(StunMessage));
stunParseMessage( msg,msgLen, &resp );
//ortp_debug("stun: Received message of type %i id=%i\n",
//resp.msgHdr.msgType,
//(int)(resp.msgHdr.tr_id.octet[0]) );
switch( resp.msgHdr.tr_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 );
if ( s != INVALID_SOCKET )
{
isNat = FALSE;
/* cerr << "binding worked"); */
}
else
{
isNat = TRUE;
/* cerr << "binding failed"); */
}
closesocket(s); /* AMD */
//ortp_debug("stun: test I = %i\n", respTestI );
//ortp_debug("stun: test II = %i\n", respTestII );
//ortp_debug("stun: test III = %i\n", respTestIII );
//ortp_debug("stun: test I(2) = %i\n", respTestI2 );
ortp_debug("stun: is nat = %i\n", isNat);
ortp_debug("stun: 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 )
{
/* 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_t ok;
StunAddress4 mappedAddr;
if ( port == 0 )
{
port = randomPort();
}
if ( srcAddr )
{
interfaceIp = srcAddr->addr;
}
myFd = openPort(port,interfaceIp);
if (myFd == INVALID_SOCKET)
{
return myFd;
}
username.sizeValue = 0;
password.sizeValue = 0;
#if 0
stunGetUserNameAndPassword( dest, username, password );
#endif
stunSendTest(myFd, dest, &username, &password, 1 );
getMessage( myFd, msg, &msgLen, &from.addr, &from.port );
memset(&resp, 0, sizeof(StunMessage));
ok = stunParseMessage( msg, msgLen, &resp );
if (!ok)
{
closesocket(myFd);
return -1;
}
if (resp.hasXorMappedAddress==TRUE)
{
UInt32 cookie = 0x2112A442;
UInt16 cookie16 = 0x2112A442 >> 16;
mappedAddr.port = resp.xorMappedAddress.ipv4.port^cookie16;
mappedAddr.addr = resp.xorMappedAddress.ipv4.addr^cookie;
}
else
mappedAddr = resp.mappedAddress.ipv4;
/*
ortp_message("stun: --- stunOpenSocket --- ");
ortp_message("stun: \treq id=" << req.id );
ortp_message("stun: \tresp id=" << id );
ortp_message("stun: \tmappedAddr=" << mappedAddr );
*/
*mapAddr = mappedAddr;
return myFd;
}
bool_t
stunOpenSocketPair(StunAddress4 *dest,
StunAddress4* mapAddr_rtp,
StunAddress4* mapAddr_rtcp,
int* fd1, int* fd2,
int port, StunAddress4* srcAddr )
{
/* 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;
#if 0
stunGetUserNameAndPassword( dest, username, password );
#endif
for( i=0; i> 16;
mappedAddr[i].port = resp.xorMappedAddress.ipv4.port^cookie16;
mappedAddr[i].addr = resp.xorMappedAddress.ipv4.addr^cookie;
}
else
mappedAddr[i] = resp.mappedAddress.ipv4;
}
//ortp_debug("stun: --- stunOpenSocketPair --- \n");
for( i=0; isizeValue>0
&& password!=NULL && password->sizeValue>0
&& resp->hasRealm==TRUE
&& resp->hasNonce==TRUE)
{
req.hasUsername = TRUE;
memcpy( req.username.value, username->value, username->sizeValue );
req.username.sizeValue = username->sizeValue;
req.hasNonce = TRUE;
memcpy( &req.nonceName, &resp->nonceName, sizeof(resp->nonceName));
req.hasRealm = TRUE;
memcpy( &req.realmName, &resp->realmName, sizeof(resp->realmName));
req.hasMessageIntegrity = TRUE;
}
len = stunEncodeMessage( &req, buf, len, password );
ortp_debug("stun: About to send msg of len %i to %s\n", len, ipaddr(dest) );
sendMessage( myFd, buf, len, dest->addr, dest->port);
/* 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
}
bool_t
turnAllocateSocketPair(StunAddress4 *dest,
StunAddress4* mapAddr_rtp,
StunAddress4* mapAddr_rtcp,
int* fd1, int* fd2,
int port, StunAddress4* srcAddr)
{
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;
}
}
snprintf(username.value, sizeof(username.value), "antisip");
username.sizeValue = strlen(username.value);
snprintf(password.value, sizeof(password.value), "exosip");
password.sizeValue = strlen(password.value);
for( i=0; i> 16;
mappedAddr[i].port = resp.xorRelayedAddress.ipv4.port^cookie16;
mappedAddr[i].addr = resp.xorRelayedAddress.ipv4.addr^cookie;
}
else
{
for( i=0; i