Classes | Defines | Typedefs | Enumerations | Functions | Variables

stun.h File Reference

#include <iostream>
#include <time.h>

Go to the source code of this file.

Classes

struct  UInt128
struct  StunMsgHdr
struct  StunAtrHdr
struct  StunAddress4
struct  StunAtrAddress4
struct  StunAtrChangeRequest
struct  StunAtrError
struct  StunAtrUnknown
struct  StunAtrString
struct  StunAtrIntegrity
struct  StunMessage
struct  StunMediaRelay
struct  StunServerInfo

Defines

#define STUN_VERSION   "0.96"
#define STUN_MAX_STRING   256
#define STUN_MAX_UNKNOWN_ATTRIBUTES   8
#define STUN_MAX_MESSAGE_SIZE   2048
#define STUN_PORT   3478
#define MAX_MEDIA_RELAYS   500
#define MAX_RTP_MSG_SIZE   1500
#define MEDIA_RELAY_TIMEOUT   3*60

Typedefs

typedef unsigned char UInt8
typedef unsigned short UInt16
typedef unsigned int UInt32
typedef unsigned long long UInt64
typedef int Socket

Enumerations

enum  StunHmacStatus {
  HmacUnkown = 0, HmacOK, HmacBadUserName, HmacUnkownUserName,
  HmacFailed
}
enum  NatType {
  StunTypeUnknown = 0, StunTypeFailure, StunTypeOpen, StunTypeBlocked,
  StunTypeIndependentFilter, StunTypeDependentFilter, StunTypePortDependedFilter, StunTypeDependentMapping,
  StunTypeFirewall
}

Functions

bool stunParseMessage (char *buf, unsigned int bufLen, StunMessage &message, bool verbose)
void stunBuildReqSimple (StunMessage *msg, const StunAtrString &username, bool changePort, bool changeIp, unsigned int id=0)
unsigned int stunEncodeMessage (const StunMessage &message, char *buf, unsigned int bufLen, const StunAtrString &password, bool verbose)
void stunCreateUserName (const StunAddress4 &addr, StunAtrString *username)
void stunGetUserNameAndPassword (const StunAddress4 &dest, StunAtrString *username, StunAtrString *password)
void stunCreatePassword (const StunAtrString &username, StunAtrString *password)
int stunRand ()
UInt64 stunGetSystemTimeSecs ()
bool stunParseServerName (char *serverName, StunAddress4 &stunServerAddr)
 find the IP address of a the specified stun server - return false is fails parse
bool stunParseHostName (char *peerName, UInt32 &ip, UInt16 &portVal, UInt16 defaultPort)
bool stunInitServer (StunServerInfo &info, const StunAddress4 &myAddr, const StunAddress4 &altAddr, int startMediaPort, bool verbose)
 return true if all is OK Create a media relay and do the STERN thing if startMediaPort is non-zero
void stunStopServer (StunServerInfo &info)
bool stunServerProcess (StunServerInfo &info, bool verbose)
 return true if all is OK
int stunFindLocalInterfaces (UInt32 *addresses, int maxSize)
 returns number of address found - take array or addres
void stunTest (StunAddress4 &dest, int testNum, bool verbose, StunAddress4 *srcAddr=0)
NatType stunNatType (StunAddress4 &dest, bool verbose, bool *preservePort=0, bool *hairpin=0, int port=0, StunAddress4 *sAddr=0)
std::ostream & operator<< (std::ostream &strm, const StunAddress4 &addr)
 prints a StunAddress
std::ostream & operator<< (std::ostream &strm, const UInt128 &)
bool stunServerProcessMsg (char *buf, unsigned int bufLen, StunAddress4 &from, StunAddress4 &myAddr, StunAddress4 &altAddr, StunMessage *resp, StunAddress4 *destination, StunAtrString *hmacPassword, bool *changePort, bool *changeIp, bool verbose)
int stunOpenSocket (StunAddress4 &dest, StunAddress4 *mappedAddr, int port=0, StunAddress4 *srcAddr=0, bool verbose=false)
bool stunOpenSocketPair (StunAddress4 &dest, StunAddress4 *mappedAddr, int *fd1, int *fd2, int srcPort=0, StunAddress4 *srcAddr=0, bool verbose=false)
int stunRandomPort ()
 return a random number to use as a port

Variables

const UInt8 IPv4Family = 0x01
 define a structure to hold a stun address
const UInt8 IPv6Family = 0x02
const UInt32 ChangeIpFlag = 0x04
const UInt32 ChangePortFlag = 0x02
const UInt16 MappedAddress = 0x0001
const UInt16 ResponseAddress = 0x0002
const UInt16 ChangeRequest = 0x0003
const UInt16 SourceAddress = 0x0004
const UInt16 ChangedAddress = 0x0005
const UInt16 Username = 0x0006
const UInt16 Password = 0x0007
const UInt16 MessageIntegrity = 0x0008
const UInt16 ErrorCode = 0x0009
const UInt16 UnknownAttribute = 0x000A
const UInt16 ReflectedFrom = 0x000B
const UInt16 XorMappedAddress = 0x8020
const UInt16 XorOnly = 0x0021
const UInt16 ServerName = 0x8022
const UInt16 SecondaryAddress = 0x8050
const UInt16 BindRequestMsg = 0x0001
const UInt16 BindResponseMsg = 0x0101
const UInt16 BindErrorResponseMsg = 0x0111
const UInt16 SharedSecretRequestMsg = 0x0002
const UInt16 SharedSecretResponseMsg = 0x0102
const UInt16 SharedSecretErrorResponseMsg = 0x0112

Define Documentation

#define MAX_MEDIA_RELAYS   500

Definition at line 204 of file stun.h.

#define MAX_RTP_MSG_SIZE   1500

Definition at line 205 of file stun.h.

#define MEDIA_RELAY_TIMEOUT   3*60

Definition at line 206 of file stun.h.

#define STUN_MAX_MESSAGE_SIZE   2048

Definition at line 12 of file stun.h.

#define STUN_MAX_STRING   256

Definition at line 10 of file stun.h.

Referenced by stunCreateUserName(), stunParseAtrString(), and stunServerProcessMsg().

#define STUN_MAX_UNKNOWN_ATTRIBUTES   8

Definition at line 11 of file stun.h.

#define STUN_PORT   3478

Definition at line 14 of file stun.h.

#define STUN_VERSION   "0.96"

Definition at line 8 of file stun.h.


Typedef Documentation

typedef int Socket

Definition at line 201 of file stun.h.

typedef unsigned short UInt16

Definition at line 18 of file stun.h.

typedef unsigned int UInt32

Definition at line 19 of file stun.h.

typedef unsigned long long UInt64

Definition at line 23 of file stun.h.

typedef unsigned char UInt8

Definition at line 17 of file stun.h.


Enumeration Type Documentation

enum NatType
Enumerator:
StunTypeUnknown 
StunTypeFailure 
StunTypeOpen 
StunTypeBlocked 
StunTypeIndependentFilter 
StunTypeDependentFilter 
StunTypePortDependedFilter 
StunTypeDependentMapping 
StunTypeFirewall 

Definition at line 178 of file stun.h.

{
   StunTypeUnknown=0,
   StunTypeFailure,
   StunTypeOpen,
   StunTypeBlocked,

   StunTypeIndependentFilter,
   StunTypeDependentFilter,
   StunTypePortDependedFilter,
   StunTypeDependentMapping,

   //StunTypeConeNat,
   //StunTypeRestrictedNat,
   //StunTypePortRestrictedNat,
   //StunTypeSymNat,
   
   StunTypeFirewall,
} NatType;

Enumerator:
HmacUnkown 
HmacOK 
HmacBadUserName 
HmacUnkownUserName 
HmacFailed 

Definition at line 118 of file stun.h.

{
   HmacUnkown=0,
   HmacOK,
   HmacBadUserName,
   HmacUnkownUserName,
   HmacFailed,
} StunHmacStatus;


Function Documentation

std::ostream& operator<< ( std::ostream &  strm,
const StunAddress4 addr 
)

prints a StunAddress

std::ostream& operator<< ( std::ostream &  strm,
const UInt128  
)
void stunBuildReqSimple ( StunMessage msg,
const StunAtrString username,
bool  changePort,
bool  changeIp,
unsigned int  id = 0 
)

Definition at line 1697 of file stun.cc.

Referenced by stunSendTest().

{
   assert( msg );
   memset( msg , 0 , sizeof(*msg) );

   msg->msgHdr.msgType = BindRequestMsg;

   for ( int 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;
   }
}

void stunCreatePassword ( const StunAtrString username,
StunAtrString password 
)

Definition at line 802 of file stun.cc.

Referenced by stunCreateSharedSecretResponse(), stunGetUserNameAndPassword(), and stunServerProcessMsg().

{
   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;

   //clog << "password=" << password->value << endl;
}

void stunCreateUserName ( const StunAddress4 addr,
StunAtrString username 
)

Definition at line 764 of file stun.cc.

Referenced by stunCreateSharedSecretResponse(), and stunGetUserNameAndPassword().

{
   UInt64 time = stunGetSystemTimeSecs();
   time -= (time % 20*60);
   //UInt64 hitime = time >> 32;
   UInt64 lotime = time & 0xFFFFFFFF;

   char buffer[1024];
   sprintf(buffer,
           "%08x:%08x:%08x:",
           UInt32(source.addr),
           UInt32(stunRand()),
           UInt32(lotime));
   assert( strlen(buffer) < 1024 );

   assert(strlen(buffer) + 41 < STUN_MAX_STRING);

   char hmac[20];
   char key[] = "Jason";
   computeHmac(hmac, buffer, strlen(buffer), key, strlen(key) );
   char hmacHex[41];
   toHex(hmac, 20, hmacHex );
   hmacHex[40] =0;

   strcat(buffer,hmacHex);

   int 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) clog << "computed username=" << username.value << endl;
}

unsigned int stunEncodeMessage ( const StunMessage message,
char *  buf,
unsigned int  bufLen,
const StunAtrString password,
bool  verbose 
)

Definition at line 544 of file stun.cc.

Referenced by stunSendTest(), and stunServerProcess().

{
   assert(bufLen >= sizeof(StunMsgHdr));
   char* ptr = buf;

   ptr = encode16(ptr, msg.msgHdr.msgType);
   char* lengthp = ptr;
   ptr = encode16(ptr, 0);
   ptr = encode(ptr, reinterpret_cast<const char*>(msg.msgHdr.id.octet), sizeof(msg.msgHdr.id));

   if (verbose) clog << "Encoding stun message: " << endl;
   if (msg.hasMappedAddress)
   {
      if (verbose) clog << "Encoding MappedAddress: " << msg.mappedAddress.ipv4 << endl;
      ptr = encodeAtrAddress4 (ptr, MappedAddress, msg.mappedAddress);
   }
   if (msg.hasResponseAddress)
   {
      if (verbose) clog << "Encoding ResponseAddress: " << msg.responseAddress.ipv4 << endl;
      ptr = encodeAtrAddress4(ptr, ResponseAddress, msg.responseAddress);
   }
   if (msg.hasChangeRequest)
   {
      if (verbose) clog << "Encoding ChangeRequest: " << msg.changeRequest.value << endl;
      ptr = encodeAtrChangeRequest(ptr, msg.changeRequest);
   }
   if (msg.hasSourceAddress)
   {
      if (verbose) clog << "Encoding SourceAddress: " << msg.sourceAddress.ipv4 << endl;
      ptr = encodeAtrAddress4(ptr, SourceAddress, msg.sourceAddress);
   }
   if (msg.hasChangedAddress)
   {
      if (verbose) clog << "Encoding ChangedAddress: " << msg.changedAddress.ipv4 << endl;
      ptr = encodeAtrAddress4(ptr, ChangedAddress, msg.changedAddress);
   }
   if (msg.hasUsername)
   {
      if (verbose) clog << "Encoding Username: " << msg.username.value << endl;
      ptr = encodeAtrString(ptr, Username, msg.username);
   }
   if (msg.hasPassword)
   {
      if (verbose) clog << "Encoding Password: " << msg.password.value << endl;
      ptr = encodeAtrString(ptr, Password, msg.password);
   }
   if (msg.hasErrorCode)
   {
      if (verbose) clog << "Encoding ErrorCode: class="
                        << int(msg.errorCode.errorClass)
                        << " number=" << int(msg.errorCode.number)
                        << " reason="
                        << msg.errorCode.reason
                        << endl;

      ptr = encodeAtrError(ptr, msg.errorCode);
   }
   if (msg.hasUnknownAttributes)
   {
      if (verbose) clog << "Encoding UnknownAttribute: ???" << endl;
      ptr = encodeAtrUnknown(ptr, msg.unknownAttributes);
   }
   if (msg.hasReflectedFrom)
   {
      if (verbose) clog << "Encoding ReflectedFrom: " << msg.reflectedFrom.ipv4 << endl;
      ptr = encodeAtrAddress4(ptr, ReflectedFrom, msg.reflectedFrom);
   }
   if (msg.hasXorMappedAddress)
   {
      if (verbose) clog << "Encoding XorMappedAddress: " << msg.xorMappedAddress.ipv4 << endl;
      ptr = encodeAtrAddress4 (ptr, XorMappedAddress, msg.xorMappedAddress);
   }
   if (msg.xorOnly)
   {
      if (verbose) clog << "Encoding xorOnly: " << endl;
      ptr = encodeXorOnly( ptr );
   }
   if (msg.hasServerName)
   {
      if (verbose) clog << "Encoding ServerName: " << msg.serverName.value << endl;
      ptr = encodeAtrString(ptr, ServerName, msg.serverName);
   }
   if (msg.hasSecondaryAddress)
   {
      if (verbose) clog << "Encoding SecondaryAddress: " << msg.secondaryAddress.ipv4 << endl;
      ptr = encodeAtrAddress4 (ptr, SecondaryAddress, msg.secondaryAddress);
   }

   if (password.sizeValue > 0)
   {
      if (verbose) clog << "HMAC with password: " << password.value << endl;

      StunAtrIntegrity integrity;
      computeHmac(integrity.hash, buf, int(ptr-buf) , password.value, password.sizeValue);
      ptr = encodeAtrIntegrity(ptr, integrity);
   }
   if (verbose) clog << endl;

   encode16(lengthp, UInt16(ptr - buf - sizeof(StunMsgHdr)));
   return int(ptr - buf);
}

int stunFindLocalInterfaces ( UInt32 addresses,
int  maxSize 
)

returns number of address found - take array or addres

Definition at line 1630 of file stun.cc.

{
#if defined(WIN32) || defined(__sparc__)
   return 0;
#else
   struct ifconf ifc;

   int s = socket( AF_INET, SOCK_DGRAM, 0 );
   int len = 100 * sizeof(struct ifreq);

   char buf[ len ];

   ifc.ifc_len = len;
   ifc.ifc_buf = buf;

   int e = ioctl(s,SIOCGIFCONF,&ifc);
   char *ptr = buf;
   int tl = ifc.ifc_len;
   int count=0;

   while ( (tl > 0) && ( count < maxRet) )
   {
      struct ifreq* ifr = (struct ifreq *)ptr;

      int si = sizeof(ifr->ifr_name) + sizeof(struct sockaddr);
      tl -= si;
      ptr += si;
      //char* name = ifr->ifr_ifrn.ifrn_name;
      //cerr << "name = " << name << endl;

      struct ifreq ifr2;
      ifr2 = *ifr;

      e = ioctl(s,SIOCGIFADDR,&ifr2);
      if ( e == -1 )
      {
         break;
      }

      //cerr << "ioctl addr e = " << e << endl;

      struct sockaddr a = ifr2.ifr_addr;
      struct sockaddr_in* addr = (struct sockaddr_in*) &a;

      UInt32 ai = ntohl( addr->sin_addr.s_addr );
      if (int((ai>>24)&0xFF) != 127)
      {
         addresses[count++] = ai;
      }

#if 0
      cerr << "Detected interface "
           << int((ai>>24)&0xFF) << "."
           << int((ai>>16)&0xFF) << "."
           << int((ai>> 8)&0xFF) << "."
           << int((ai    )&0xFF) << endl;
#endif
   }

   stunclosesocket(s);

   return count;
#endif
}

UInt64 stunGetSystemTimeSecs (  ) 

Definition at line 817 of file stun.cc.

Referenced by stunCreateUserName().

{
   UInt64 time=0;
#if defined(WIN32)
   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;
}

void stunGetUserNameAndPassword ( const StunAddress4 dest,
StunAtrString username,
StunAtrString password 
)

Definition at line 1801 of file stun.cc.

Referenced by stunNatType(), stunOpenSocket(), stunOpenSocketPair(), and stunTest().

{
   // !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);
}

bool stunInitServer ( StunServerInfo info,
const StunAddress4 myAddr,
const StunAddress4 altAddr,
int  startMediaPort,
bool  verbose 
)

return true if all is OK Create a media relay and do the STERN thing if startMediaPort is non-zero

Definition at line 1249 of file stun.cc.

{
   assert( myAddr.port != 0 );
   assert( altAddr.port!= 0 );
   assert( myAddr.addr  != 0 );
   //assert( altAddr.addr != 0 );

   info.myAddr = myAddr;
   info.altAddr = altAddr;

   info.myFd = STUN_INVALID_SOCKET;
   info.altPortFd = STUN_INVALID_SOCKET;
   info.altIpFd = STUN_INVALID_SOCKET;
   info.altIpPortFd = STUN_INVALID_SOCKET;

   memset(info.relays, 0, sizeof(info.relays));
   if (startMediaPort > 0)
   {
      info.relay = true;

      for (int i=0; i<MAX_MEDIA_RELAYS; ++i)
      {
         StunMediaRelay* relay = &info.relays[i];
         relay->relayPort = startMediaPort+i;
         relay->fd = 0;
         relay->expireTime = 0;
      }
   }
   else
   {
      info.relay = false;
   }

   if ((info.myFd = openPort(myAddr.port, myAddr.addr,verbose)) == STUN_INVALID_SOCKET)
   {
      clog << "Can't open " << myAddr << endl;
      stunStopServer(info);

      return false;
   }
   //if (verbose) clog << "Opened " << myAddr.addr << ":" << myAddr.port << " --> " << info.myFd << endl;

   if ((info.altPortFd = openPort(altAddr.port,myAddr.addr,verbose)) == STUN_INVALID_SOCKET)
   {
      clog << "Can't open " << myAddr << endl;
      stunStopServer(info);
      return false;
   }
   //if (verbose) clog << "Opened " << myAddr.addr << ":" << altAddr.port << " --> " << info.altPortFd << endl;


   info.altIpFd = STUN_INVALID_SOCKET;
   if (  altAddr.addr != 0 )
   {
      if ((info.altIpFd = openPort( myAddr.port, altAddr.addr,verbose)) == STUN_INVALID_SOCKET)
      {
         clog << "Can't open " << altAddr << endl;
         stunStopServer(info);
         return false;
      }
      //if (verbose) clog << "Opened " << altAddr.addr << ":" << myAddr.port << " --> " << info.altIpFd << endl;;
   }

   info.altIpPortFd = STUN_INVALID_SOCKET;
   if (  altAddr.addr != 0 )
   {  if ((info.altIpPortFd = openPort(altAddr.port, altAddr.addr,verbose)) == STUN_INVALID_SOCKET)
      {
         clog << "Can't open " << altAddr << endl;
         stunStopServer(info);
         return false;
      }
      //if (verbose) clog << "Opened " << altAddr.addr << ":" << altAddr.port << " --> " << info.altIpPortFd << endl;;
   }

   return true;
}

NatType stunNatType ( StunAddress4 dest,
bool  verbose,
bool *  preservePort = 0,
bool *  hairpin = 0,
int  port = 0,
StunAddress4 sAddr = 0 
)

Definition at line 1876 of file stun.cc.

Referenced by SingleHostUnderlayConfigurator::initializeUnderlay().

{
   assert( dest.addr != 0 );
   assert( dest.port != 0 );

   if ( hairpin )
   {
      *hairpin = false;
   }

   if ( port == 0 )
   {
      port = stunRandomPort();
   }
   UInt32 interfaceIp=0;
   if (sAddr)
   {
      interfaceIp = sAddr->addr;
   }
   Socket myFd1 = openPort(port,interfaceIp,verbose);
   Socket myFd2 = openPort(port+1,interfaceIp,verbose);

   if ( ( myFd1 == STUN_INVALID_SOCKET) || ( myFd2 == STUN_INVALID_SOCKET) )
   {
        cerr << "Some problem opening port/interface to send on" << endl;
       return StunTypeFailure;
   }

   assert( myFd1 != STUN_INVALID_SOCKET );
   assert( myFd2 != STUN_INVALID_SOCKET );

   bool respTestI=false;
   bool isNat=true;
   StunAddress4 testIchangedAddr;
   StunAddress4 testImappedAddr;
   bool respTestI2=false;
   bool mappedIpSame = true;
   StunAddress4 testI2mappedAddr;
   StunAddress4 testI2dest=dest;
   bool respTestII=false;
   bool respTestIII=false;

   bool respTestHairpin=false;
   bool respTestPreservePort=false;

   memset(&testImappedAddr,0,sizeof(testImappedAddr));

   StunAtrString username;
   StunAtrString password;

   username.sizeValue = 0;
   password.sizeValue = 0;

#ifdef USE_TLS
   stunGetUserNameAndPassword( dest, username, password );
#endif

   int count=0;
   while ( count < 7 )
   {
      struct timeval tv;
      fd_set fdSet;
#ifdef WIN32
      unsigned int fdSetSize;
#else
      int fdSetSize;
#endif
      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=150*1000; // 150 ms
      if ( count == 0 ) tv.tv_usec=0;

      int  err = select(fdSetSize, &fdSet, NULL, NULL, &tv);
      int e = getErrno();
      if ( err == STUN_SOCKET_ERROR )
      {
         // error occured
         cerr << "Error " << e << " " << strerror(e) << " in select" << endl;
        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
      {
         //if (verbose) clog << "-----------------------------------------" << endl;
         assert( err>0 );
         // data is avialbe on some fd

         for ( int i=0; i<2; i++)
         {
            Socket myFd;
            if ( i==0 )
            {
               myFd=myFd1;
            }
            else
            {
               myFd=myFd2;
            }

            if ( myFd!=STUN_INVALID_SOCKET )
            {
               if ( FD_ISSET(myFd,&fdSet) )
               {
                  char msg[STUN_MAX_MESSAGE_SIZE];
                  int msgLen = sizeof(msg);

                  StunAddress4 from;

                  getMessage( myFd,
                              msg,
                              &msgLen,
                              &from.addr,
                              &from.port,verbose );

                  StunMessage resp;
                  memset(&resp, 0, sizeof(StunMessage));

                  stunParseMessage( msg,msgLen, resp,verbose );

                  if ( verbose )
                  {
                     clog << "Received message of type " << resp.msgHdr.msgType
                          << "  id=" << (int)(resp.msgHdr.id.octet[0]) << endl;
                  }

                  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;

                           respTestPreservePort = ( testImappedAddr.port == port );
                           if ( preservePort )
                           {
                              *preservePort = respTestPreservePort;
                           }

                           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;
                  }
               }
            }
         }
      }
   }

   // see if we can bind to this address
   //cerr << "try binding to " << testImappedAddr << endl;
   Socket s = openPort( 0/*use ephemeral*/, testImappedAddr.addr, false );
   if ( s != STUN_INVALID_SOCKET )
   {
      stunclosesocket(s);
      isNat = false;
      //cerr << "binding worked" << endl;
   }
   else
   {
      isNat = true;
      //cerr << "binding failed" << endl;
   }

   if (verbose)
   {
      clog << "test I = " << respTestI << endl;
      clog << "test II = " << respTestII << endl;
      clog << "test III = " << respTestIII << endl;
      clog << "test I(2) = " << respTestI2 << endl;
      clog << "is nat  = " << isNat <<endl;
      clog << "mapped IP same = " << mappedIpSame << endl;
      clog << "hairpin = " << respTestHairpin << endl;
      clog << "preserver port = " << respTestPreservePort << endl;
   }

#if 0
   // 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;
   }
#else
   if ( respTestI ) // not blocked
   {
      if ( isNat )
      {
         if ( mappedIpSame )
         {
            if (respTestII)
            {
               return StunTypeIndependentFilter;
            }
            else
            {
               if ( respTestIII )
               {
                  return StunTypeDependentFilter;
               }
               else
               {
                  return StunTypePortDependedFilter;
               }
            }
         }
         else // mappedIp is not same
         {
            return StunTypeDependentMapping;
         }
      }
      else  // isNat is false
      {
         if (respTestII)
         {
            return StunTypeOpen;
         }
         else
         {
            return StunTypeFirewall;
         }
      }
   }
   else
   {
      return StunTypeBlocked;
   }
#endif

   return StunTypeUnknown;
}

int stunOpenSocket ( StunAddress4 dest,
StunAddress4 mappedAddr,
int  port = 0,
StunAddress4 srcAddr = 0,
bool  verbose = false 
)

Definition at line 2247 of file stun.cc.

{
   assert( dest.addr != 0 );
   assert( dest.port != 0 );
   assert( mapAddr );

   if ( port == 0 )
   {
      port = stunRandomPort();
   }
   unsigned int interfaceIp = 0;
   if ( srcAddr )
   {
      interfaceIp = srcAddr->addr;
   }

   Socket myFd = openPort(port,interfaceIp,verbose);
   if (myFd == STUN_INVALID_SOCKET)
   {
      return myFd;
   }

   char msg[STUN_MAX_MESSAGE_SIZE];
   int msgLen = sizeof(msg);

   StunAtrString username;
   StunAtrString password;

   username.sizeValue = 0;
   password.sizeValue = 0;

#ifdef USE_TLS
   stunGetUserNameAndPassword( dest, username, password );
#endif

   stunSendTest(myFd, dest, username, password, 1, 0/*false*/ );

   StunAddress4 from;

   getMessage( myFd, msg, &msgLen, &from.addr, &from.port,verbose );

   StunMessage resp;
   memset(&resp, 0, sizeof(StunMessage));

   bool ok = stunParseMessage( msg, msgLen, resp,verbose );
   if (!ok)
   {
      return -1;
   }

   StunAddress4 mappedAddr = resp.mappedAddress.ipv4;
   StunAddress4 changedAddr = resp.changedAddress.ipv4;

   //clog << "--- stunOpenSocket --- " << endl;
   //clog << "\treq  id=" << req.id << endl;
   //clog << "\tresp id=" << id << endl;
   //clog << "\tmappedAddr=" << mappedAddr << endl;

   *mapAddr = mappedAddr;

   return myFd;
}

bool stunOpenSocketPair ( StunAddress4 dest,
StunAddress4 mappedAddr,
int *  fd1,
int *  fd2,
int  srcPort = 0,
StunAddress4 srcAddr = 0,
bool  verbose = false 
)

Definition at line 2314 of file stun.cc.

{
   assert( dest.addr!= 0 );
   assert( dest.port != 0 );
   assert( mapAddr );

   const int NUM=3;

   if ( port == 0 )
   {
      port = stunRandomPort();
   }

   *fd1=-1;
   *fd2=-1;

   char msg[STUN_MAX_MESSAGE_SIZE];
   int msgLen =sizeof(msg);

   StunAddress4 from;
   int fd[NUM];
   int i;

   unsigned int interfaceIp = 0;
   if ( srcAddr )
   {
      interfaceIp = srcAddr->addr;
   }

   for( i=0; i<NUM; i++)
   {
      fd[i] = openPort( (port == 0) ? 0 : (port + i),
                        interfaceIp, verbose);
      if (fd[i] < 0)
      {
         while (i > 0)
         {
            stunclosesocket(fd[--i]);
         }
         return false;
      }
   }

   StunAtrString username;
   StunAtrString password;

   username.sizeValue = 0;
   password.sizeValue = 0;

#ifdef USE_TLS
   stunGetUserNameAndPassword( dest, username, password );
#endif

   for( i=0; i<NUM; i++)
   {
      stunSendTest(fd[i], dest, username, password, 1/*testNum*/, verbose );
   }

   StunAddress4 mappedAddr[NUM];
   for( i=0; i<NUM; i++)
   {
      msgLen = sizeof(msg)/sizeof(*msg);
      getMessage( fd[i],
                  msg,
                  &msgLen,
                  &from.addr,
                  &from.port ,verbose);

      StunMessage resp;
      memset(&resp, 0, sizeof(StunMessage));

      bool ok = stunParseMessage( msg, msgLen, resp, verbose );
      if (!ok)
      {
         return false;
      }

      mappedAddr[i] = resp.mappedAddress.ipv4;
      StunAddress4 changedAddr = resp.changedAddress.ipv4;
   }

   if (verbose)
   {
      clog << "--- stunOpenSocketPair --- " << endl;
      for( i=0; i<NUM; i++)
      {
         clog << "\t mappedAddr=" << mappedAddr[i] << endl;
      }
   }

   if ( mappedAddr[0].port %2 == 0 )
   {
      if (  mappedAddr[0].port+1 ==  mappedAddr[1].port )
      {
         *mapAddr = mappedAddr[0];
         *fd1 = fd[0];
         *fd2 = fd[1];
         stunclosesocket( fd[2] );
         return true;
      }
   }
   else
   {
      if (( mappedAddr[1].port %2 == 0 )
          && (  mappedAddr[1].port+1 ==  mappedAddr[2].port ))
      {
         *mapAddr = mappedAddr[1];
         *fd1 = fd[1];
         *fd2 = fd[2];
         stunclosesocket( fd[0] );
         return true;
      }
   }

   // something failed, close all and return error
   for( i=0; i<NUM; i++)
   {
      stunclosesocket( fd[i] );
   }

   return false;
}

bool stunParseHostName ( char *  peerName,
UInt32 ip,
UInt16 portVal,
UInt16  defaultPort 
)

Definition at line 863 of file stun.cc.

Referenced by stunParseServerName().

{
   in_addr sin_addr;

   char host[512];
   strncpy(host,peerName,512);
   host[512-1]='\0';
   char* port = NULL;

   int portNum = defaultPort;

   // pull out the port part if present.
   char* sep = strchr(host,':');

   if ( sep == NULL )
   {
      portNum = defaultPort;
   }
   else
   {
      *sep = '\0';
      port = sep + 1;
      // set port part

      char* endPtr=NULL;

      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
   struct hostent* h;

#ifdef WIN32
   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 << endl;

      ip = ntohl( a );
   }
   else
   {
      // assume it is a host name
      h = gethostbyname( host );

      if ( h == NULL )
      {
         int err = getErrno();
         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();
      std::cerr << "error was " << err << std::endl;
      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 stunParseMessage ( char *  buf,
unsigned int  bufLen,
StunMessage message,
bool  verbose 
)

Definition at line 183 of file stun.cc.

Referenced by stunNatType(), stunOpenSocket(), stunOpenSocketPair(), stunServerProcessMsg(), and stunTest().

{
   if (verbose) clog << "Received stun message: " << bufLen << " bytes" << endl;
   memset(&msg, 0, sizeof(msg));

   if (sizeof(StunMsgHdr) > bufLen)
   {
      clog << "Bad message" << endl;
      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)
   {
      clog << "Message header length doesn't match message size: "
           << msg.msgHdr.msgLength << " - " << bufLen << endl;
      return false;
   }

   char* body = buf + sizeof(StunMsgHdr);
   unsigned int size = msg.msgHdr.msgLength;

   //clog << "bytes after header = " << size << endl;

   while ( size > 0 )
   {
      // !jf! should check that there are enough bytes left in the buffer

      StunAtrHdr* attr = reinterpret_cast<StunAtrHdr*>(body);

      unsigned int attrLen = ntohs(attr->length);
      int atrType = ntohs(attr->type);

      //if (verbose) clog << "Found attribute type=" << AttrNames[atrType] << " length=" << attrLen << endl;
      if ( attrLen+4 > size )
      {
         clog << "claims attribute is larger than size of message "
              <<"(attribute type="<<atrType<<")"<< endl;
         return false;
      }

      body += 4; // skip the length and type in attribute header
      size -= 4;

      switch ( atrType )
      {
         case MappedAddress:
            msg.hasMappedAddress = true;
            if ( stunParseAtrAddress(  body,  attrLen,  msg.mappedAddress )== false )
            {
               clog << "problem parsing MappedAddress" << endl;
               return false;
            }
            else
            {
               if (verbose) clog << "MappedAddress = " << msg.mappedAddress.ipv4 << endl;
            }

            break;

         case ResponseAddress:
            msg.hasResponseAddress = true;
            if ( stunParseAtrAddress(  body,  attrLen,  msg.responseAddress )== false )
            {
               clog << "problem parsing ResponseAddress" << endl;
               return false;
            }
            else
            {
               if (verbose) clog << "ResponseAddress = " << msg.responseAddress.ipv4 << endl;
            }
            break;

         case ChangeRequest:
            msg.hasChangeRequest = true;
            if (stunParseAtrChangeRequest( body, attrLen, msg.changeRequest) == false)
            {
               clog << "problem parsing ChangeRequest" << endl;
               return false;
            }
            else
            {
               if (verbose) clog << "ChangeRequest = " << msg.changeRequest.value << endl;
            }
            break;

         case SourceAddress:
            msg.hasSourceAddress = true;
            if ( stunParseAtrAddress(  body,  attrLen,  msg.sourceAddress )== false )
            {
               clog << "problem parsing SourceAddress" << endl;
               return false;
            }
            else
            {
               if (verbose) clog << "SourceAddress = " << msg.sourceAddress.ipv4 << endl;
            }
            break;

         case ChangedAddress:
            msg.hasChangedAddress = true;
            if ( stunParseAtrAddress(  body,  attrLen,  msg.changedAddress )== false )
            {
               clog << "problem parsing ChangedAddress" << endl;
               return false;
            }
            else
            {
               if (verbose) clog << "ChangedAddress = " << msg.changedAddress.ipv4 << endl;
            }
            break;

         case Username:
            msg.hasUsername = true;
            if (stunParseAtrString( body, attrLen, msg.username) == false)
            {
               clog << "problem parsing Username" << endl;
               return false;
            }
            else
            {
               if (verbose) clog << "Username = " << msg.username.value << endl;
            }

            break;

         case Password:
            msg.hasPassword = true;
            if (stunParseAtrString( body, attrLen, msg.password) == false)
            {
               clog << "problem parsing Password" << endl;
               return false;
            }
            else
            {
               if (verbose) clog << "Password = " << msg.password.value << endl;
            }
            break;

         case MessageIntegrity:
            msg.hasMessageIntegrity = true;
            if (stunParseAtrIntegrity( body, attrLen, msg.messageIntegrity) == false)
            {
               clog << "problem parsing MessageIntegrity" << endl;
               return false;
            }
            else
            {
               //if (verbose) clog << "MessageIntegrity = " << msg.messageIntegrity.hash << endl;
            }

            // 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
            break;

         case ErrorCode:
            msg.hasErrorCode = true;
            if (stunParseAtrError(body, attrLen, msg.errorCode) == false)
            {
               clog << "problem parsing ErrorCode" << endl;
               return false;
            }
            else
            {
               if (verbose) clog << "ErrorCode = " << int(msg.errorCode.errorClass)
                                 << " " << int(msg.errorCode.number)
                                 << " " << msg.errorCode.reason << endl;
            }

            break;

         case UnknownAttribute:
            msg.hasUnknownAttributes = true;
            if (stunParseAtrUnknown(body, attrLen, msg.unknownAttributes) == false)
            {
               clog << "problem parsing UnknownAttribute" << endl;
               return false;
            }
            break;

         case ReflectedFrom:
            msg.hasReflectedFrom = true;
            if ( stunParseAtrAddress(  body,  attrLen,  msg.reflectedFrom ) == false )
            {
               clog << "problem parsing ReflectedFrom" << endl;
               return false;
            }
            break;

         case XorMappedAddress:
            msg.hasXorMappedAddress = true;
            if ( stunParseAtrAddress(  body,  attrLen,  msg.xorMappedAddress ) == false )
            {
               clog << "problem parsing XorMappedAddress" << endl;
               return false;
            }
            else
            {
               if (verbose) clog << "XorMappedAddress = " << msg.mappedAddress.ipv4 << endl;
            }
            break;

         case XorOnly:
            msg.xorOnly = true;
            if (verbose)
            {
               clog << "xorOnly = true" << endl;
            }
            break;

         case ServerName:
            msg.hasServerName = true;
            if (stunParseAtrString( body, attrLen, msg.serverName) == false)
            {
               clog << "problem parsing ServerName" << endl;
               return false;
            }
            else
            {
               if (verbose) clog << "ServerName = " << msg.serverName.value << endl;
            }
            break;

         case SecondaryAddress:
            msg.hasSecondaryAddress = true;
            if ( stunParseAtrAddress(  body,  attrLen,  msg.secondaryAddress ) == false )
            {
               clog << "problem parsing secondaryAddress" << endl;
               return false;
            }
            else
            {
               if (verbose) clog << "SecondaryAddress = " << msg.secondaryAddress.ipv4 << endl;
            }
            break;

         default:
            if (verbose) clog << "Unknown attribute: " << atrType << endl;
            if ( atrType <= 0x7FFF )
            {
               return false;
            }
      }

      body += attrLen;
      size -= attrLen;
   }

   return true;
}

bool stunParseServerName ( char *  serverName,
StunAddress4 stunServerAddr 
)

find the IP address of a the specified stun server - return false is fails parse

Definition at line 964 of file stun.cc.

Referenced by SingleHostUnderlayConfigurator::initializeUnderlay().

{
   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;
}

int stunRand (  ) 

Definition at line 651 of file stun.cc.

Referenced by stunBuildReqSimple(), stunCreateUserName(), and stunRandomPort().

{
   // return 32 bits of random stuff
   assert( sizeof(int) == 4 );
   static bool init=false;
   if ( !init )
   {
      init = true;

      UInt64 tick;

#if defined(MSVC_WIN32)
      volatile unsigned int lowtick=0,hightick=0;
      __asm
         {
            rdtsc
               mov lowtick, eax
               mov hightick, edx
               }
      tick = hightick;
      tick <<= 32;
      tick |= lowtick;
#elif (defined(WIN32) || defined(__GNUC__)) && ( defined(__i686__) || defined(__i386__) || defined(__x86_64__) )
      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));
      stunclosesocket(fd);
#else
#     error Need some way to seed the random number generator
#endif
      int seed = int(tick);
#ifdef WIN32
      srand(seed);
#else
      srandom(seed);
#endif
   }

#ifdef WIN32
   assert( RAND_MAX == 0x7fff );
   int r1 = rand();
   int r2 = rand();

   int ret = (r1<<16) + r2;

   return ret;
#else
   return random();
#endif
}

int stunRandomPort (  ) 

return a random number to use as a port

Definition at line 708 of file stun.cc.

Referenced by stunNatType(), stunOpenSocket(), stunOpenSocketPair(), and stunTest().

{
   int min=0x4000;
   int max=0x7FFF;

   int ret = stunRand();
   ret = ret|min;
   ret = ret&max;

   return ret;
}

bool stunServerProcess ( StunServerInfo info,
bool  verbose 
)

return true if all is OK

Definition at line 1351 of file stun.cc.

{
   char msg[STUN_MAX_MESSAGE_SIZE];
   int msgLen = sizeof(msg);

   bool ok = false;
   bool recvAltIp =false;
   bool recvAltPort = false;

   fd_set fdSet;
   Socket maxFd=0;

   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 != STUN_INVALID_SOCKET )
   {
      FD_SET(info.altIpFd,&fdSet);
      if (info.altIpFd>=maxFd) maxFd=info.altIpFd+1;
   }
   if ( info.altIpPortFd != STUN_INVALID_SOCKET )
   {
      FD_SET(info.altIpPortFd,&fdSet);
      if (info.altIpPortFd>=maxFd) maxFd=info.altIpPortFd+1;
   }

   if (info.relay)
   {
      for (int i=0; i<MAX_MEDIA_RELAYS; ++i)
      {
         StunMediaRelay* relay = &info.relays[i];
         if (relay->fd)
         {
            FD_SET(relay->fd, &fdSet);
            if (relay->fd >= maxFd)
                        {
                                maxFd=relay->fd+1;
                        }
         }
      }
   }

   if ( info.altIpFd != STUN_INVALID_SOCKET )
   {
      FD_SET(info.altIpFd,&fdSet);
      if (info.altIpFd>=maxFd) maxFd=info.altIpFd+1;
   }
   if ( info.altIpPortFd != STUN_INVALID_SOCKET )
   {
      FD_SET(info.altIpPortFd,&fdSet);
      if (info.altIpPortFd>=maxFd) maxFd=info.altIpPortFd+1;
   }

   struct timeval tv;
   tv.tv_sec = 0;
   tv.tv_usec = 1000;

   int e = select( maxFd, &fdSet, NULL,NULL, &tv );
   if (e < 0)
   {
      int err = getErrno();
      clog << "Error on select: " << strerror(err) << endl;
   }
   else if (e >= 0)
   {
      StunAddress4 from;

      // do the media relaying
      if (info.relay)
      {
         time_t now = time(0);
         for (int i=0; i<MAX_MEDIA_RELAYS; ++i)
         {
            StunMediaRelay* relay = &info.relays[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 ) clog << "Relay packet on "
                                         << relay->fd
                                         << " from " << rtpFrom
                                         << " -> " << relay->destination
                                         << endl;
                  }
               }
               else if (now > relay->expireTime)
               {
                  stunclosesocket(relay->fd);
                  relay->fd = 0;
               }
            }
         }
      }


      if (FD_ISSET(info.myFd,&fdSet))
      {
         if (verbose) clog << "received on A1:P1" << endl;
         recvAltIp = false;
         recvAltPort = false;
         ok = getMessage( info.myFd, msg, &msgLen, &from.addr, &from.port,verbose );
      }
      else if (FD_ISSET(info.altPortFd, &fdSet))
      {
         if (verbose) clog << "received on A1:P2" << endl;
         recvAltIp = false;
         recvAltPort = true;
         ok = getMessage( info.altPortFd, msg, &msgLen, &from.addr, &from.port,verbose );
      }
      else if ( (info.altIpFd!=STUN_INVALID_SOCKET) && FD_ISSET(info.altIpFd,&fdSet))
      {
         if (verbose) clog << "received on A2:P1" << endl;
         recvAltIp = true;
         recvAltPort = false;
         ok = getMessage( info.altIpFd, msg, &msgLen, &from.addr, &from.port ,verbose);
      }
      else if ( (info.altIpPortFd!=STUN_INVALID_SOCKET) && FD_ISSET(info.altIpPortFd, &fdSet))
      {
         if (verbose) clog << "received on A2:P2" << endl;
         recvAltIp = true;
         recvAltPort = true;
         ok = getMessage( info.altIpPortFd, msg, &msgLen, &from.addr, &from.port,verbose );
      }
      else
      {
         return true;
      }

      int relayPort = 0;
      if (info.relay)
      {
         for (int i=0; i<MAX_MEDIA_RELAYS; ++i)
         {
            StunMediaRelay* relay = &info.relays[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)
         {
            for (int i=0; i<MAX_MEDIA_RELAYS; ++i)
            {
               StunMediaRelay* relay = &info.relays[i];
               if (relay->fd == 0)
               {
                  if ( verbose ) clog << "Open relay port " << relay->relayPort << endl;

                  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 ) clog << "Get message did not return a valid message" <<endl;
         return true;
      }

      if ( verbose ) clog << "Got a request (len=" << msgLen << ") from " << from << endl;

      if ( msgLen <= 0 )
      {
         return true;
      }

      bool changePort = false;
      bool changeIp = false;

      StunMessage resp;
      StunAddress4 dest;
      StunAtrString hmacPassword;
      hmacPassword.sizeValue = 0;

      StunAddress4 secondary;
      secondary.port = 0;
      secondary.addr = 0;

      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 ) clog << "Failed to parse message" << endl;
         return true;
      }

      char buf[STUN_MAX_MESSAGE_SIZE];
      int len = sizeof(buf);

      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 != STUN_INVALID_SOCKET )
         {
            sendMessage( sendFd, buf, len, dest.addr, dest.port, verbose );
         }
      }
   }

   return true;
}

bool stunServerProcessMsg ( char *  buf,
unsigned int  bufLen,
StunAddress4 from,
StunAddress4 myAddr,
StunAddress4 altAddr,
StunMessage resp,
StunAddress4 destination,
StunAtrString hmacPassword,
bool *  changePort,
bool *  changeIp,
bool  verbose 
)
void stunStopServer ( StunServerInfo info  ) 

Definition at line 1328 of file stun.cc.

Referenced by stunInitServer().

{
   if (info.myFd > 0) stunclosesocket(info.myFd);
   if (info.altPortFd > 0) stunclosesocket(info.altPortFd);
   if (info.altIpFd > 0) stunclosesocket(info.altIpFd);
   if (info.altIpPortFd > 0) stunclosesocket(info.altIpPortFd);

   if (info.relay)
   {
      for (int i=0; i<MAX_MEDIA_RELAYS; ++i)
      {
         StunMediaRelay* relay = &info.relays[i];
         if (relay->fd)
         {
            stunclosesocket(relay->fd);
            relay->fd = 0;
         }
      }
   }
}

void stunTest ( StunAddress4 dest,
int  testNum,
bool  verbose,
StunAddress4 srcAddr = 0 
)

Definition at line 1813 of file stun.cc.

{
   assert( dest.addr != 0 );
   assert( dest.port != 0 );

   int port = stunRandomPort();
   UInt32 interfaceIp=0;
   if (sAddr)
   {
      interfaceIp = sAddr->addr;
      if ( sAddr->port != 0 )
      {
        port = sAddr->port;
      }
   }
   Socket myFd = openPort(port,interfaceIp,verbose);

   StunAtrString username;
   StunAtrString password;

   username.sizeValue = 0;
   password.sizeValue = 0;

#ifdef USE_TLS
   stunGetUserNameAndPassword( dest, username, password );
#endif

   stunSendTest( myFd, dest, username, password, testNum, verbose );

   char msg[STUN_MAX_MESSAGE_SIZE];
   int msgLen = STUN_MAX_MESSAGE_SIZE;

   StunAddress4 from;
   getMessage( myFd,
               msg,
               &msgLen,
               &from.addr,
               &from.port,verbose );

   StunMessage resp;
   memset(&resp, 0, sizeof(StunMessage));

   if ( verbose ) clog << "Got a response" << endl;
   bool ok = stunParseMessage( msg,msgLen, resp,verbose );

   if ( verbose )
   {
      clog << "\t ok=" << ok << endl;
      clog << "\t id=" << resp.msgHdr.id << endl;
      clog << "\t mappedAddr=" << resp.mappedAddress.ipv4 << endl;
      clog << "\t changedAddr=" << resp.changedAddress.ipv4 << endl;
      clog << endl;
   }

   if (sAddr)
   {
      sAddr->port = resp.mappedAddress.ipv4.port;
      sAddr->addr = resp.mappedAddress.ipv4.addr;
   }
}


Variable Documentation

const UInt16 BindErrorResponseMsg = 0x0111

Definition at line 55 of file stun.h.

Referenced by stunCreateErrorResponse().

const UInt16 BindRequestMsg = 0x0001

Definition at line 53 of file stun.h.

Referenced by stunBuildReqSimple(), and stunServerProcessMsg().

const UInt16 BindResponseMsg = 0x0101

Definition at line 54 of file stun.h.

Referenced by stunServerProcessMsg().

const UInt16 ChangedAddress = 0x0005

Definition at line 40 of file stun.h.

Referenced by stunEncodeMessage(), and stunParseMessage().

const UInt32 ChangeIpFlag = 0x04

Definition at line 32 of file stun.h.

Referenced by stunBuildReqSimple(), and stunServerProcessMsg().

const UInt32 ChangePortFlag = 0x02

Definition at line 33 of file stun.h.

Referenced by stunBuildReqSimple(), and stunServerProcessMsg().

const UInt16 ChangeRequest = 0x0003

Definition at line 38 of file stun.h.

Referenced by encodeAtrChangeRequest(), and stunParseMessage().

const UInt16 ErrorCode = 0x0009

Definition at line 44 of file stun.h.

Referenced by encodeAtrError(), and stunParseMessage().

const UInt8 IPv4Family = 0x01

define a structure to hold a stun address

Definition at line 28 of file stun.h.

Referenced by encodeAtrAddress4(), and stunParseAtrAddress().

const UInt8 IPv6Family = 0x02

Definition at line 29 of file stun.h.

Referenced by stunParseAtrAddress().

const UInt16 MappedAddress = 0x0001

Definition at line 36 of file stun.h.

Referenced by stunEncodeMessage(), and stunParseMessage().

const UInt16 MessageIntegrity = 0x0008

Definition at line 43 of file stun.h.

Referenced by encodeAtrIntegrity(), and stunParseMessage().

const UInt16 Password = 0x0007

Definition at line 42 of file stun.h.

Referenced by stunEncodeMessage(), and stunParseMessage().

const UInt16 ReflectedFrom = 0x000B

Definition at line 46 of file stun.h.

Referenced by stunEncodeMessage(), and stunParseMessage().

const UInt16 ResponseAddress = 0x0002

Definition at line 37 of file stun.h.

Referenced by stunEncodeMessage(), and stunParseMessage().

const UInt16 SecondaryAddress = 0x8050

Definition at line 50 of file stun.h.

Referenced by stunEncodeMessage(), and stunParseMessage().

const UInt16 ServerName = 0x8022

Definition at line 49 of file stun.h.

Referenced by stunEncodeMessage(), and stunParseMessage().

Definition at line 58 of file stun.h.

Definition at line 56 of file stun.h.

Referenced by stunServerProcessMsg().

Definition at line 57 of file stun.h.

Referenced by stunCreateSharedSecretResponse().

const UInt16 SourceAddress = 0x0004

Definition at line 39 of file stun.h.

Referenced by stunEncodeMessage(), and stunParseMessage().

const UInt16 UnknownAttribute = 0x000A

Definition at line 45 of file stun.h.

Referenced by encodeAtrUnknown(), and stunParseMessage().

const UInt16 Username = 0x0006

Definition at line 41 of file stun.h.

Referenced by stunEncodeMessage(), and stunParseMessage().

const UInt16 XorMappedAddress = 0x8020

Definition at line 47 of file stun.h.

Referenced by stunEncodeMessage(), and stunParseMessage().

const UInt16 XorOnly = 0x0021

Definition at line 48 of file stun.h.

Referenced by encodeXorOnly(), and stunParseMessage().