/*
  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
 * <http://www.vovida.org/>.
 *
 */

#ifndef _WIN32_WCE

#include <errno.h>

#endif


#include <assert.h>


#if defined(WIN32) || defined(_WIN32_WCE)

#include <winsock2.h>

#include <stdlib.h>

/* #include <io.h> */
#include <time.h>

#include <ctype.h> /*for isdigit() */

#else


#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <sys/ioctl.h>

#include <sys/socket.h>

#include <sys/time.h>

#include <sys/types.h> 

#include <arpa/inet.h>

#include <fcntl.h>

#include <netdb.h>

#include <netinet/in.h>

#include <arpa/nameser.h>

#include <resolv.h>

#include <net/if.h>


#endif



#define NOSSL

/*
  #if defined(__sparc__) || defined(WIN32)
  #define NOSSL
  #endif
  #define NOSSL
*/

#include "ortp/stun_udp.h"

#include "ortp/stun.h"


static char *ipaddr(const StunAddress4 *addr)
{
   static char tmp[512];
   struct in_addr inaddr;
   char *atmp;
   inaddr.s_addr = htonl(addr->addr);
   atmp = (char *)inet_ntoa(inaddr);
   
   snprintf(tmp, 512, "%s:%i", atmp, addr->port);
   return tmp;
}

static void
computeHmac(char* hmac, const char* input, int length, const char* key, int keySize);

static bool 
stunParseAtrAddress( char* body, unsigned int hdrLen,  StunAtrAddress4 *result )
{
   if ( hdrLen != 8 )
   {
      printf("hdrLen wrong for Address\n");
      return false;
   }
   result->pad = *body++;
   result->family = *body++;
   if (result->family == IPv4Family)
   {
      UInt16 nport;
      UInt32 naddr;
      memcpy(&nport, body, 2); body+=2;
      result->ipv4.port = ntohs(nport);

      memcpy(&naddr, body, 4); body+=4;
      result->ipv4.addr = ntohl(naddr);
      return true;
   }
   else if (result->family == IPv6Family)
   {
      printf("ipv6 not supported\n");
   }
   else
   {
      printf("bad address family: %i\n", result->family);
   }
	
   return false;
}

static bool 
stunParseAtrChangeRequest( char* body, unsigned int hdrLen,  StunAtrChangeRequest *result )
{
   if ( hdrLen != 4 )
   {
     /* printf("hdr length = %i expecting %i\n",hdrLen, sizeof(result)); */
		
      printf("Incorrect size for ChangeRequest");
      return false;
   }
   else
   {
      memcpy(&result->value, body, 4);
      result->value = ntohl(result->value);
      return true;
   }
}

static bool 
stunParseAtrError( char* body, unsigned int hdrLen,  StunAtrError *result )
{
   if ( hdrLen >= sizeof(result) )
   {
      printf("head on Error too large");
      return false;
   }
   else
   {
      memcpy(&result->pad, body, 2); body+=2;
      result->pad = ntohs(result->pad);
      result->errorClass = *body++;
      result->number = *body++;
		
      result->sizeReason = hdrLen - 4;
      memcpy(&result->reason, body, result->sizeReason);
      result->reason[result->sizeReason] = 0;
      return true;
   }
}

static bool 
stunParseAtrUnknown( char* body, unsigned int hdrLen,  StunAtrUnknown *result )
{
   if ( hdrLen >= sizeof(result) )
   {
      return false;
   }
   else
   {
      int i;
      if (hdrLen % 4 != 0) return false;
      result->numAttributes = hdrLen / 4;
      for (i=0; i<result->numAttributes; i++)
      {
         memcpy(&result->attrType[i], body, 2); body+=2;
         result->attrType[i] = ntohs(result->attrType[i]);
      }
      return true;
   }
}


static bool 
stunParseAtrString( char* body, unsigned int hdrLen,  StunAtrString *result )
{
   if ( hdrLen >= STUN_MAX_STRING )
   {
      printf("String is too large");
      return false;
   }
   else
   {
      if (hdrLen % 4 != 0)
      {
         printf("Bad length string %i\n", hdrLen);
         return false;
      }
		
      result->sizeValue = hdrLen;
      memcpy(&result->value, body, hdrLen);
      result->value[hdrLen] = 0;
      return true;
   }
}


static bool 
stunParseAtrIntegrity( char* body, unsigned int hdrLen,  StunAtrIntegrity *result )
{
   if ( hdrLen != 20)
   {
      printf("MessageIntegrity must be 20 bytes");
      return false;
   }
   else
   {
      memcpy(&result->hash, body, hdrLen);
      return true;
   }
}


bool
stunParseMessage( char* buf, unsigned int bufLen, StunMessage *msg, bool verbose)
{
   char* body;
   unsigned int size;
   if (verbose) printf("Received stun message: %i bytes\n", bufLen);
   memset(msg, 0, sizeof(msg));
	
   if (sizeof(StunMsgHdr) > bufLen)
   {
      printf("Bad message\n");
      return false;
   }

   memcpy(&msg->msgHdr, buf, sizeof(StunMsgHdr));
   msg->msgHdr.msgType = ntohs(msg->msgHdr.msgType);
   msg->msgHdr.msgLength = ntohs(msg->msgHdr.msgLength);

   if (msg->msgHdr.msgLength + sizeof(StunMsgHdr) != bufLen)
   {
      printf("Message header length doesn't match message size: %i - %i\n", msg->msgHdr.msgLength, bufLen);
      return false;
   }

   body = buf + sizeof(StunMsgHdr);
   size = msg->msgHdr.msgLength;
	
   /*printf("bytes after header = %i\n", size); */
	
   while ( size > 0 )
   {
      /* !jf! should check that there are enough bytes left in the buffer */
		
      StunAtrHdr* attr = (StunAtrHdr*)body; /*reinterpret_cast<StunAtrHdr*>(body);*/
		
      unsigned int attrLen = ntohs(attr->length);
      int atrType = ntohs(attr->type);
		
      /*if (verbose) printf("Found attribute type=" << AttrNames[atrType] << " length=" << attrLen << endl;*/
      if ( attrLen+4 > size ) 
      {
         printf("claims attribute is larger than size of message (attribute type=%i)\n", atrType);
         return false;
      }
		
      body += 4; /* skip the length and type in attribute header */
      size -= 4;
		
      if (atrType == MappedAddress)
      {
            msg->hasMappedAddress = true;
            if ( stunParseAtrAddress(  body,  attrLen,  &msg->mappedAddress )== false )
            {
               printf("problem parsing MappedAddress\n");
               return false;
            }
            else
            {
               if (verbose) printf("MappedAddress = %s\n", ipaddr(&msg->mappedAddress.ipv4));
            }
					
      }
      else if (atrType == ResponseAddress)
      {
            msg->hasResponseAddress = true;
            if ( stunParseAtrAddress(  body,  attrLen,  &msg->responseAddress )== false )
            {
               printf("problem parsing ResponseAddress");
               return false;
            }
            else
            {
               if (verbose) printf("ResponseAddress = %s\n", ipaddr(&msg->responseAddress.ipv4));
            }
      }
      else if (atrType == ChangeRequest)
      {
            msg->hasChangeRequest = true;
            if (stunParseAtrChangeRequest( body, attrLen, &msg->changeRequest) == false)
            {
               printf("problem parsing ChangeRequest\n");
               return false;
            }
            else
            {
               if (verbose) printf("ChangeRequest = %i\n", msg->changeRequest.value);
            }
      }
      else if (atrType == SourceAddress)
      {
            msg->hasSourceAddress = true;
            if ( stunParseAtrAddress(  body,  attrLen,  &msg->sourceAddress )== false )
            {
               printf("problem parsing SourceAddress\n");
               return false;
            }
            else
            {
               if (verbose) printf("SourceAddress = %s\n", ipaddr(&msg->sourceAddress.ipv4) );
            }
      }
      else if (atrType == ChangedAddress)
      {
            msg->hasChangedAddress = true;
            if ( stunParseAtrAddress(  body,  attrLen,  &msg->changedAddress )== false )
            {
               printf("problem parsing ChangedAddress\n");
               return false;
            }
            else
            {
               if (verbose) printf("ChangedAddress = %s\n", ipaddr(&msg->changedAddress.ipv4));
            }
      }
      else if (atrType == Username)
      {
            msg->hasUsername = true;
            if (stunParseAtrString( body, attrLen, &msg->username) == false)
            {
               printf("problem parsing Username");
               return false;
            }
            else
            {
               if (verbose) printf("Username = %s\n", msg->username.value );
            }					
      }
      else if (atrType == Password)
      {
            msg->hasPassword = true;
            if (stunParseAtrString( body, attrLen, &msg->password) == false)
            {
               printf("problem parsing Password");
               return false;
            }
            else
            {
               if (verbose) printf("Password = %s\n", msg->password.value );
            }
      }
      else if (atrType == MessageIntegrity)
      {
            msg->hasMessageIntegrity = true;
            if (stunParseAtrIntegrity( body, attrLen, &msg->messageIntegrity) == false)
            {
               printf("problem parsing MessageIntegrity");
               return false;
            }
            else
            {
               /*if (verbose) printf("MessageIntegrity = " << msg->messageIntegrity.hash ); */
            }
					
            /* read the current HMAC
               look up the password given the user of given the transaction id 
               compute the HMAC on the buffer
               decide if they match or not */
      }
      else if (atrType == ErrorCode)
      {
            msg->hasErrorCode = true;
            if (stunParseAtrError(body, attrLen, &msg->errorCode) == false)
            {
               printf("problem parsing ErrorCode");
               return false;
            }
            else
            {
               if (verbose) printf("ErrorCode = %i %i %s\n",
                                   msg->errorCode.errorClass ,
                                   msg->errorCode.number ,
                                   msg->errorCode.reason );
            }
					 
      }
      else if (atrType == UnknownAttribute)
      {
           msg->hasUnknownAttributes = true;
            if (stunParseAtrUnknown(body, attrLen, &msg->unknownAttributes) == false)
            {
               printf("problem parsing UnknownAttribute");
               return false;
            }
      }
      else if (atrType == ReflectedFrom)
      {
            msg->hasReflectedFrom = true;
            if ( stunParseAtrAddress(  body,  attrLen,  &msg->reflectedFrom ) == false )
            {
               printf("problem parsing ReflectedFrom");
               return false;
            }
      }
      else if (atrType == XorMappedAddress)
      { 
           msg->hasXorMappedAddress = true;
            if ( stunParseAtrAddress(  body,  attrLen,  &msg->xorMappedAddress ) == false )
            {
               printf("problem parsing XorMappedAddress");
               return false;
            }
            else
            {
               if (verbose) printf("XorMappedAddress = %s\n", ipaddr(&msg->mappedAddress.ipv4) );
            }
      }
      else if (atrType == XorOnly)
      {
            msg->xorOnly = true;
            if (verbose) 
            {
               printf("xorOnly = true");
            }
      }
      else if (atrType == ServerName)
      {
            msg->hasServerName = true;
            if (stunParseAtrString( body, attrLen, &msg->serverName) == false)
            {
               printf("problem parsing ServerName");
               return false;
            }
            else
            {
               if (verbose) printf("ServerName = %s\n", msg->serverName.value );
            }
      }
      else if (atrType == SecondaryAddress)
      {
            msg->hasSecondaryAddress = true;
            if ( stunParseAtrAddress(  body,  attrLen,  &msg->secondaryAddress ) == false )
            {
               printf("problem parsing secondaryAddress");
               return false;
            }
            else
            {
               if (verbose) printf("SecondaryAddress = %s\n", ipaddr(&msg->secondaryAddress.ipv4) );
            }
      }
      else
      {
            if (verbose) printf("Unknown attribute: %i\n", atrType );
            if ( atrType <= 0x7FFF ) 
            {
               return false;
            }
      }
		
      body += attrLen;
      size -= attrLen;
   }
    
   return true;
}


static char* 
encode16(char* buf, UInt16 data)
{
   UInt16 ndata = htons(data);
   /*memcpy(buf, reinterpret_cast<void*>(&ndata), sizeof(UInt16)); */
   memcpy(buf, &ndata, sizeof(UInt16));
   return buf + sizeof(UInt16);
}

static char* 
encode32(char* buf, UInt32 data)
{
   UInt32 ndata = htonl(data);
   /*memcpy(buf, reinterpret_cast<void*>(&ndata), sizeof(UInt32));*/
   memcpy(buf, &ndata, sizeof(UInt32));
   return buf + sizeof(UInt32);
}


static char* 
encode(char* buf, const char* data, unsigned int length)
{
   memcpy(buf, data, length);
   return buf + length;
}


static char* 
encodeAtrAddress4(char* ptr, UInt16 type, const StunAtrAddress4 *atr)
{
   ptr = encode16(ptr, type);
   ptr = encode16(ptr, 8);
   *ptr++ = atr->pad;
   *ptr++ = IPv4Family;
   ptr = encode16(ptr, atr->ipv4.port);
   ptr = encode32(ptr, atr->ipv4.addr);
	
   return ptr;
}

static char* 
encodeAtrChangeRequest(char* ptr, const StunAtrChangeRequest *atr)
{
   ptr = encode16(ptr, ChangeRequest);
   ptr = encode16(ptr, 4);
   ptr = encode32(ptr, atr->value);
   return ptr;
}

static char* 
encodeAtrError(char* ptr, const StunAtrError *atr)
{
   ptr = encode16(ptr, ErrorCode);
   ptr = encode16(ptr, 6 + atr->sizeReason);
   ptr = encode16(ptr, atr->pad);
   *ptr++ = atr->errorClass;
   *ptr++ = atr->number;
   ptr = encode(ptr, atr->reason, atr->sizeReason);
   return ptr;
}


static char* 
encodeAtrUnknown(char* ptr, const StunAtrUnknown *atr)
{
   int i;
   ptr = encode16(ptr, UnknownAttribute);
   ptr = encode16(ptr, 2+2*atr->numAttributes);
   for (i=0; i<atr->numAttributes; i++)
   {
      ptr = encode16(ptr, atr->attrType[i]);
   }
   return ptr;
}


static char* 
encodeXorOnly(char* ptr)
{
   ptr = encode16(ptr, XorOnly );
   return ptr;
}


static char* 
encodeAtrString(char* ptr, UInt16 type, const StunAtrString *atr)
{
   /*assert(atr->sizeValue % 4 == 0);*/
	
   ptr = encode16(ptr, type);
   ptr = encode16(ptr, atr->sizeValue);
   ptr = encode(ptr, atr->value, atr->sizeValue);
   return ptr;
}


static char* 
encodeAtrIntegrity(char* ptr, const StunAtrIntegrity *atr)
{
   ptr = encode16(ptr, MessageIntegrity);
   ptr = encode16(ptr, 20);
   ptr = encode(ptr, atr->hash, sizeof(atr->hash));
   return ptr;
}


unsigned int
stunEncodeMessage( const StunMessage *msg, 
                   char* buf, 
                   unsigned int bufLen, 
                   const StunAtrString *password, 
                   bool verbose)
{
   /*assert(bufLen >= sizeof(StunMsgHdr));*/
   char* ptr = buf;
   char* lengthp;
   ptr = encode16(ptr, msg->msgHdr.msgType);
   lengthp = ptr;
   ptr = encode16(ptr, 0);
   /*ptr = encode(ptr, reinterpret_cast<const char*>(msg->msgHdr.id.octet), sizeof(msg->msgHdr.id));*/
   ptr = encode(ptr, (const char*)msg->msgHdr.id.octet, sizeof(msg->msgHdr.id));
	
   if (verbose) printf("Encoding stun message: ");
   if (msg->hasMappedAddress)
   {
      if (verbose) printf("Encoding MappedAddress: %s\n", ipaddr(&msg->mappedAddress.ipv4) );
      ptr = encodeAtrAddress4 (ptr, MappedAddress, &msg->mappedAddress);
   }
   if (msg->hasResponseAddress)
   {
      if (verbose) printf("Encoding ResponseAddress: %s\n", ipaddr(&msg->responseAddress.ipv4) );
      ptr = encodeAtrAddress4(ptr, ResponseAddress, &msg->responseAddress);
   }
   if (msg->hasChangeRequest)
   {
      if (verbose) printf("Encoding ChangeRequest: %i\n", msg->changeRequest.value );
      ptr = encodeAtrChangeRequest(ptr, &msg->changeRequest);
   }
   if (msg->hasSourceAddress)
   {
      if (verbose) printf("Encoding SourceAddress: %s\n", ipaddr(&msg->sourceAddress.ipv4) );
      ptr = encodeAtrAddress4(ptr, SourceAddress, &msg->sourceAddress);
   }
   if (msg->hasChangedAddress)
   {
      if (verbose) printf("Encoding ChangedAddress: %s\n", ipaddr(&msg->changedAddress.ipv4) );
      ptr = encodeAtrAddress4(ptr, ChangedAddress, &msg->changedAddress);
   }
   if (msg->hasUsername)
   {
      if (verbose) printf("Encoding Username: %s\n", msg->username.value );
      ptr = encodeAtrString(ptr, Username, &msg->username);
   }
   if (msg->hasPassword)
   {
      if (verbose) printf("Encoding Password: %s\n", msg->password.value );
      ptr = encodeAtrString(ptr, Password, &msg->password);
   }
   if (msg->hasErrorCode)
   {
      if (verbose) printf("Encoding ErrorCode: class=%i number=%i reason=%s\n" 
                          , msg->errorCode.errorClass 
                          , msg->errorCode.number
                          , msg->errorCode.reason );
      
      ptr = encodeAtrError(ptr, &msg->errorCode);
   }
   if (msg->hasUnknownAttributes)
   {
      if (verbose) printf("Encoding UnknownAttribute: ???");
      ptr = encodeAtrUnknown(ptr, &msg->unknownAttributes);
   }
   if (msg->hasReflectedFrom)
   {
      if (verbose) printf("Encoding ReflectedFrom: %s\n", ipaddr(&msg->reflectedFrom.ipv4) );
      ptr = encodeAtrAddress4(ptr, ReflectedFrom, &msg->reflectedFrom);
   }
   if (msg->hasXorMappedAddress)
   {
      if (verbose) printf("Encoding XorMappedAddress: %s\n", ipaddr(&msg->xorMappedAddress.ipv4) );
      ptr = encodeAtrAddress4 (ptr, XorMappedAddress, &msg->xorMappedAddress);
   }
   if (msg->xorOnly)
   {
      if (verbose) printf("Encoding xorOnly: ");
      ptr = encodeXorOnly( ptr );
   }
   if (msg->hasServerName)
   {
      if (verbose) printf("Encoding ServerName: %s\n", msg->serverName.value );
      ptr = encodeAtrString(ptr, ServerName, &msg->serverName);
   }
   if (msg->hasSecondaryAddress)
   {
      if (verbose) printf("Encoding SecondaryAddress: %s\n", ipaddr(&msg->secondaryAddress.ipv4) );
      ptr = encodeAtrAddress4 (ptr, SecondaryAddress, &msg->secondaryAddress);
   }

   if (password->sizeValue > 0)
   {
      StunAtrIntegrity integrity;
      if (verbose) printf("HMAC with password: %s\n", password->value );

      computeHmac(integrity.hash, buf, (int)(ptr-buf) , password->value, password->sizeValue);
      ptr = encodeAtrIntegrity(ptr, &integrity);
   }
   if (verbose) printf("\n");
	
   encode16(lengthp, (UInt16)(ptr - buf - sizeof(StunMsgHdr)));
   return (int)(ptr - buf);
}

int 
stunRand(void)
{
   /* return 32 bits of random stuff */
   /* assert( sizeof(int) == 4 ); */
   static bool init=false;
   if ( !init )
   { 
      UInt64 tick;
      int seed;
      init = true;

#if defined(_WIN32_WCE)

      tick = GetTickCount ();
#elif defined(_MSC_VER)

      {
      volatile unsigned int lowtick=0,hightick=0;
      __asm
         {
            rdtsc 
               mov lowtick, eax
               mov hightick, edx
               }
      tick = hightick;
      tick <<= 32;
      tick |= lowtick;
      }
#elif defined(__GNUC__) && ( defined(__i686__) || defined(__i386__) )

      asm("rdtsc" : "=A" (tick));
#elif defined(__GNUC__) && defined(__amd64__)

      asm("rdtsc" : "=A" (tick));
#elif defined (__SUNPRO_CC) && defined( __sparc__ )	

      tick = gethrtime();
#elif defined(__MACH__) 

      {
	int fd=open("/dev/random",O_RDONLY);
	read(fd,&tick,sizeof(tick));
	closesocket(fd);
      }
#elif defined(__linux) || defined (__FreeBSD__) 

      {
 	fd_set fdSet;
	int maxFd=0;
	struct timeval tv;
	int e;

        int fd=open("/dev/random",O_RDONLY);

	if (fd<0)
	{
	    printf("Failed to open random device\n");
	    return random();
	}
        FD_ZERO(&fdSet);
        FD_SET(fd,&fdSet);
        maxFd=fd+1;

	printf("random device opened\n");
	tv.tv_sec = 0;
	tv.tv_usec = 500;

	e = select( maxFd, &fdSet, NULL,NULL, &tv );
	if (e <= 0)
	{
           printf("Failed to get data from random device\n");
           closesocket(fd);
	   return random();
	}
	read(fd,&tick,sizeof(tick));
	printf("random device returned data\n");
	closesocket(fd);
      }
#else

#     error Need some way to seed the random number generator 

#endif 

      seed = (int)(tick);
#if	defined(_WIN32) || defined(_WIN32_WCE)

      srand(seed);
#else

      srandom(seed);
#endif

   }
	
#if	defined(_WIN32) || defined(_WIN32_WCE)

   /* assert( RAND_MAX == 0x7fff ); */
   {
       int r1 = rand();
       int r2 = rand();
       int ret = (r1<<16) + r2;
	
       return ret;
   }
#else

   return random(); 
#endif

}


/* return a random number to use as a port */
static int
randomPort()
{
   int min=0x4000;
   int max=0x7FFF;
	
   int ret = stunRand();
   ret = ret|min;
   ret = ret&max;
	
   return ret;
}


#ifdef NOSSL

static void
computeHmac(char* hmac, const char* input, int length, const char* key, int sizeKey)
{
   strncpy(hmac,"hmac-not-implemented",20);
}
#else

#include <openssl/hmac.h>


static void
computeHmac(char* hmac, const char* input, int length, const char* key, int sizeKey)
{
   unsigned int resultSize=0;
   HMAC(EVP_sha1(), 
        key, sizeKey, 
        (const unsigned char*) input, length, 
        (unsigned char*)hmac, &resultSize);
   /*
     HMAC(EVP_sha1(), 
        key, sizeKey, 
        reinterpret_cast<const unsigned char*>(input), length, 
        reinterpret_cast<unsigned char*>(hmac), &resultSize);
	assert(resultSize == 20);
   */
}
#endif



static void
toHex(const char* buffer, int bufferSize, char* output) 
{
   int i;
   static char hexmap[] = "0123456789abcdef";
	
   const char* p = buffer;
   char* r = output;
   for (i=0; i < bufferSize; i++)
   {
      unsigned char temp = *p++;
		
      int hi = (temp & 0xf0)>>4;
      int low = (temp & 0xf);
		
      *r++ = hexmap[hi];
      *r++ = hexmap[low];
   }
   *r = 0;
}

void
stunCreateUserName(const StunAddress4* source, StunAtrString* username)
{
   UInt64 time = stunGetSystemTimeSecs();
   UInt64 lotime;
   char buffer[1024];
   char hmac[20];
   char key[] = "Jason";
   char hmacHex[41];
   int l;

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

   sprintf(buffer,
           "%08x:%08x:%08x:", 
           (UInt32)(source->addr),
           (UInt32)(stunRand()),
           (UInt32)(lotime));
   /*assert( strlen(buffer) < 1024 ); */
	
   /*assert(strlen(buffer) + 41 < STUN_MAX_STRING); */
   
   computeHmac(hmac, buffer, strlen(buffer), key, strlen(key) );
   toHex(hmac, 20, hmacHex );
   hmacHex[40] =0;
	
   strcat(buffer,hmacHex);
	
   l = strlen(buffer);
   /* assert( l+1 < STUN_MAX_STRING );*/
   /* assert( l%4 == 0 ); */
   
   username->sizeValue = l;
   memcpy(username->value,buffer,l);
   username->value[l]=0;
	
   /* if (verbose) printf("computed username=%s\n", username.value ); */
}

void
stunCreatePassword(const StunAtrString *username, StunAtrString* password)
{
   char hmac[20];
   char key[] = "Fluffy";
   /* char buffer[STUN_MAX_STRING]; */
   computeHmac(hmac, username->value, strlen(username->value), key, strlen(key));
   toHex(hmac, 20, password->value);
   password->sizeValue = 40;
   password->value[40]=0;
	
   /* printf("password=%s\n", password->value ); */
}


UInt64
stunGetSystemTimeSecs(void)
{
   UInt64 time=0;
#if	defined(_WIN32) || defined(_WIN32_WCE)

   SYSTEMTIME t;
   /*  CJ TODO - this probably has bug on wrap around every 24 hours */
   GetSystemTime( &t );
   time = (t.wHour*60+t.wMinute)*60+t.wSecond; 
#else

   struct timeval now;
   gettimeofday( &now , NULL );
   /* assert( now ); */
   time = now.tv_sec;
#endif

   return time;
}


/* returns true if it scucceeded */
bool 
stunParseHostName( char* peerName,
                   UInt32* ip,
                   UInt16* portVal,
                   UInt16 defaultPort )
{
   struct in_addr sin_addr;
    
   char host[512];
   char* port = NULL;
   int portNum = defaultPort;
   char* sep;
   struct hostent* h;

   strncpy(host,peerName,512);
   host[512-1]='\0';
	
   /* pull out the port part if present. */
   sep = strchr(host,':');
	
   if ( sep == NULL )
   {
      portNum = defaultPort;
   }
   else
   {
      char* endPtr=NULL;
      *sep = '\0';
      port = sep + 1;
      /* set port part */
      
		
      portNum = strtol(port,&endPtr,10);
		
      if ( endPtr != NULL )
      {
         if ( *endPtr != '\0' )
         {
            portNum = defaultPort;
         }
      }
   }
    
   if ( portNum < 1024 ) return false;
   if ( portNum >= 0xFFFF ) return false;
	
   /* figure out the host part */
	
#if	defined(_WIN32) || defined(_WIN32_WCE)

   /* assert( strlen(host) >= 1 ); */
   if ( isdigit( host[0] ) )
   {
      /* assume it is a ip address */
      unsigned long a = inet_addr(host);
      /* cerr << "a=0x" << hex << a << dec ); */
		
      *ip = ntohl( a );
   }
   else
   {
      /* assume it is a host name */
      h = gethostbyname( host );
		
      if ( h == NULL )
      {
         /*int err = getErrno();*/

         /* printf("error was %i\n", err); */
         /* std::cerr << "error was " << err << std::endl; */
         /* assert( err != WSANOTINITIALISED ); */
			
         *ip = ntohl( 0x7F000001L );
			
         return false;
      }
      else
      {
         sin_addr = *(struct in_addr*)h->h_addr;
         *ip = ntohl( sin_addr.s_addr );
      }
   }
	
#else

   h = gethostbyname( host );
   if ( h == NULL )
   {
      /* 
	 int err = getErrno();
	 printf("error was %i\n", err);
      */
      *ip = ntohl( 0x7F000001L );
      return false;
   }
   else
   {
      sin_addr = *(struct in_addr*)h->h_addr;
      *ip = ntohl( sin_addr.s_addr );
   }
#endif

	
   *portVal = portNum;
	
   return true;
}


bool
stunParseServerName( char* name, StunAddress4 *addr)
{
   /* assert(name); */
	
   /* TODO - put in DNS SRV stuff. */
	
   bool ret = stunParseHostName( name, &addr->addr, &addr->port, 3478); 
   if ( ret != true ) 
   {
       addr->port=0xFFFF;
   }	
   return ret;
}


static void
stunCreateErrorResponse(StunMessage *response, int cl, int number, const char* msg)
{
   response->msgHdr.msgType = BindErrorResponseMsg;
   response->hasErrorCode = true;
   response->errorCode.errorClass = cl;
   response->errorCode.number = number;
   strcpy(response->errorCode.reason, msg);
}

#if 0

static void
stunCreateSharedSecretErrorResponse(StunMessage& response, int cl, int number, const char* msg)
{
   response.msgHdr.msgType = SharedSecretErrorResponseMsg;
   response.hasErrorCode = true;
   response.errorCode.errorClass = cl;
   response.errorCode.number = number;
   strcpy(response.errorCode.reason, msg);
}
#endif


static void
stunCreateSharedSecretResponse(const StunMessage *request, const StunAddress4 *source, StunMessage *response)
{
   response->msgHdr.msgType = SharedSecretResponseMsg;
   response->msgHdr.id = request->msgHdr.id;
	
   response->hasUsername = true;
   stunCreateUserName( source, &response->username);
	
   response->hasPassword = true;
   stunCreatePassword( &response->username, &response->password);
}


/* This funtion takes a single message sent to a stun server, parses
   and constructs an apropriate repsonse - returns true if message is
   valid */
bool
stunServerProcessMsg( char* buf,
                      unsigned int bufLen,
                      StunAddress4 *from, 
                      StunAddress4 *secondary,
                      StunAddress4 *myAddr,
                      StunAddress4 *altAddr, 
                      StunMessage *resp,
                      StunAddress4 *destination,
                      StunAtrString *hmacPassword,
                      bool* changePort,
                      bool* changeIp,
                      bool verbose)
{
   int i;
   StunMessage req;
   StunAddress4 mapped;
   StunAddress4 respondTo;
   UInt32 flags;
   bool ok;
   /* set up information for default response */
	
   memset( &req, 0 , sizeof(req) );
   memset( resp, 0 , sizeof(*resp) );
	
   *changeIp = false;
   *changePort = false;
	
   ok = stunParseMessage( buf,bufLen, &req, verbose);
   
   if (!ok)      /* Complete garbage, drop it on the floor */
   {
      if (verbose) printf("Request did not parse");
      return false;
   }
   if (verbose) printf("Request parsed ok");
	
   mapped = req.mappedAddress.ipv4;
   respondTo = req.responseAddress.ipv4;
   flags = req.changeRequest.value;
	
   if (req.msgHdr.msgType==SharedSecretRequestMsg)
   {
         if(verbose) printf("Received SharedSecretRequestMsg on udp. send error 433.");
         /* !cj! - should fix so you know if this came over TLS or UDP */
         stunCreateSharedSecretResponse(&req, from, resp);
         /* stunCreateSharedSecretErrorResponse(*resp, 4, 33, "this request must be over TLS"); */
         return true;
			
   }
   else if (req.msgHdr.msgType==BindRequestMsg)
   {
         if (!req.hasMessageIntegrity)
         {
            if (verbose) printf("BindRequest does not contain MessageIntegrity");
				
            if (0) /* !jf! mustAuthenticate */
            {
               if(verbose) printf("Received BindRequest with no MessageIntegrity. Sending 401.");
               stunCreateErrorResponse(resp, 4, 1, "Missing MessageIntegrity");
               return true;
            }
         }
         else
         {
            if (!req.hasUsername)
            {
               if (verbose) printf("No UserName. Send 432.");
               stunCreateErrorResponse(resp, 4, 32, "No UserName and contains MessageIntegrity");
               return true;
            }
            else
            {
               if (verbose) printf("Validating username: %s", req.username.value );
               /* !jf! could retrieve associated password from provisioning here */
               if (strcmp(req.username.value, "test") == 0)
               {
                  if (0)
                  {
                     /* !jf! if the credentials are stale */
                     stunCreateErrorResponse(resp, 4, 30, "Stale credentials on BindRequest");
                     return true;
                  }
                  else
                  {
                     unsigned char hmac[20];
                     if (verbose) printf("Validating MessageIntegrity");
                     /* need access to shared secret */

#ifndef NOSSL

                     {
                        unsigned int hmacSize=20;

                        HMAC(EVP_sha1(), 
                             "1234", 4, 
                             (const unsigned char*) buf, bufLen-20-4, 
                             hmac, &hmacSize);
                        /*HMAC(EVP_sha1(), 
                             "1234", 4, 
                             reinterpret_cast<const unsigned char*>(buf), bufLen-20-4, 
                             hmac, &hmacSize);
                        assert(hmacSize == 20);
			*/
                     }
#endif

							
                     if (memcmp(buf, hmac, 20) != 0)
                     {
                        if (verbose) printf("MessageIntegrity is bad. Sending ");
                        stunCreateErrorResponse(resp, 4, 3, "Unknown username. Try test with password 1234");
                        return true;
                     }
							
                     /* need to compute this later after message is filled in */
                     resp->hasMessageIntegrity = true;
                     /* assert(req.hasUsername); */
                     resp->hasUsername = true;
                     resp->username = req.username; /* copy username in */
                  }
               }
               else
               {
                  if (verbose) printf("Invalid username: %s Send 430", req.username.value); 
               }
            }
         }
			
         /* TODO !jf! should check for unknown attributes here and send 420 listing the
            unknown attributes. */
			
         if ( respondTo.port == 0 )
         {
            /* respondTo = from; */
            memcpy(&respondTo, from, sizeof(StunAddress4));
         }
         if ( mapped.port == 0 ) 
         {
            /* mapped = from; */
            memcpy(&mapped, from, sizeof(StunAddress4));
         }

         *changeIp   = ( flags & ChangeIpFlag )?true:false;
         *changePort = ( flags & ChangePortFlag )?true:false;
			
         if (verbose)
         {
            printf("Request is valid:\n");
            printf("\t flags= %i\n", flags );
            printf("\t changeIp= %i\n", *changeIp );
            printf("\t changePort=%i\n", *changePort );
            printf("\t from= %i\n", from->addr );
            printf("\t respond to= %i\n", respondTo.addr );
            printf("\t mapped= %i\n", mapped.addr );
         }
				
         /* form the outgoing message */
         resp->msgHdr.msgType = BindResponseMsg;
         for (i=0; i<16; i++ )
         {
            resp->msgHdr.id.octet[i] = req.msgHdr.id.octet[i];
         }
		
         if ( req.xorOnly == false )
         {
            resp->hasMappedAddress = true;
            resp->mappedAddress.ipv4.port = mapped.port;
            resp->mappedAddress.ipv4.addr = mapped.addr;
         }

         if (1) /* do xorMapped address or not */
         {
            UInt16 id16;
            UInt32 id32;
            resp->hasXorMappedAddress = true;
            id16 = req.msgHdr.id.octet[7]<<8 
               | req.msgHdr.id.octet[6];
            id32 = req.msgHdr.id.octet[7]<<24 
               |  req.msgHdr.id.octet[6]<<16 
               |  req.msgHdr.id.octet[5]<<8 
               | req.msgHdr.id.octet[4];
            resp->xorMappedAddress.ipv4.port = mapped.port^id16;
            resp->xorMappedAddress.ipv4.addr = mapped.addr^id32;
         }
         
         resp->hasSourceAddress = true;
         resp->sourceAddress.ipv4.port = (*changePort) ? altAddr->port : myAddr->port;
         resp->sourceAddress.ipv4.addr = (*changeIp)   ? altAddr->addr : myAddr->addr;
			
         resp->hasChangedAddress = true;
         resp->changedAddress.ipv4.port = altAddr->port;
         resp->changedAddress.ipv4.addr = altAddr->addr;
	
         if ( secondary->port != 0 )
         {
            resp->hasSecondaryAddress = true;
            resp->secondaryAddress.ipv4.port = secondary->port;
            resp->secondaryAddress.ipv4.addr = secondary->addr;
         }
         
         if ( req.hasUsername && req.username.sizeValue > 0 ) 
         {
            /* copy username in */
            resp->hasUsername = true;
            /* assert( req.username.sizeValue % 4 == 0 ); */
            /* assert( req.username.sizeValue < STUN_MAX_STRING ); */
            memcpy( resp->username.value, req.username.value, req.username.sizeValue );
            resp->username.sizeValue = req.username.sizeValue;
         }
		
         if (1) /* add ServerName */
         {
            const char serverName[] = "Vovida.org " STUN_VERSION; /* must pad to mult of 4 */
            resp->hasServerName = true;
            
            /* assert( sizeof(serverName) < STUN_MAX_STRING ); */
            /* cerr << "sizeof serverName is "  << sizeof(serverName) ); */
            /* assert( sizeof(serverName)%4 == 0 ); */
            memcpy( resp->serverName.value, serverName, sizeof(serverName));
            resp->serverName.sizeValue = sizeof(serverName);
         }
         
         if ( req.hasMessageIntegrity & req.hasUsername )  
         {
            /* this creates the password that will be used in the HMAC when then */
            /* messages is sent */
            stunCreatePassword( &req.username, hmacPassword );
         }
				
         if (req.hasUsername && (req.username.sizeValue > 64 ) )
         {
            UInt32 source;
            /* assert( sizeof(int) == sizeof(UInt32) ); */
					
            sscanf(req.username.value, "%x", &source);
            resp->hasReflectedFrom = true;
            resp->reflectedFrom.ipv4.port = 0;
            resp->reflectedFrom.ipv4.addr = source;
         }
				
         destination->port = respondTo.port;
         destination->addr = respondTo.addr;
			
         return true;		
   }
   else
   {
         if (verbose) printf("Unknown or unsupported request ");
         return false;
   }
	
   /* assert(0); */
   return false;
}

bool
stunInitServer(StunServerInfo *info, const StunAddress4 *myAddr, const StunAddress4 *altAddr, int startMediaPort, bool verbose )
{
   /* assert( myAddr.port != 0 ); */
   /* assert( altAddr.port!= 0 ); */
   /* assert( myAddr.addr  != 0 ); */
   /* assert( altAddr.addr != 0 ); */
	
   /* info->myAddr = myAddr; */
   info->myAddr.port = myAddr->port;
   info->myAddr.addr = myAddr->addr;

   /* info->altAddr = altAddr; */
   info->altAddr.port = altAddr->port;
   info->altAddr.addr = altAddr->addr;
	
   info->myFd = INVALID_SOCKET;
   info->altPortFd = INVALID_SOCKET;
   info->altIpFd = INVALID_SOCKET;
   info->altIpPortFd = INVALID_SOCKET;

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

      for (i=0; 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)) == INVALID_SOCKET)
   {
      printf("Can't open %i\n", myAddr->addr );
      stunStopServer(info);

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

   if ((info->altPortFd = openPort(altAddr->port,myAddr->addr,verbose)) == INVALID_SOCKET)
   {
      printf("Can't open %i\n", myAddr->addr );
      stunStopServer(info);
      return false;
   }
   /* if (verbose) printf("Opened " << myAddr->addr << ":" << altAddr->port << " --> " << info->altPortFd ); */
   
   
   info->altIpFd = INVALID_SOCKET;
   if (  altAddr->addr != 0 )
   {
      if ((info->altIpFd = openPort( myAddr->port, altAddr->addr,verbose)) == INVALID_SOCKET)
      {
         printf("Can't open %i\n", altAddr->addr );
         stunStopServer(info);
         return false;
      }
      /* if (verbose) printf("Opened " << altAddr->addr << ":" << myAddr->port << " --> " << info->altIpFd ); */
   }
   
   info->altIpPortFd = INVALID_SOCKET;
   if (  altAddr->addr != 0 )
   {  if ((info->altIpPortFd = openPort(altAddr->port, altAddr->addr,verbose)) == INVALID_SOCKET)
      {
         printf("Can't open %i\n", altAddr->addr );
         stunStopServer(info);
         return false;
      }
   /* if (verbose) printf("Opened " << altAddr->addr << ":" << altAddr->port << " --> " << info->altIpPortFd ); */
   }
   
   return true;
}

void
stunStopServer(StunServerInfo *info)
{
   if (info->myFd > 0) closesocket(info->myFd);
   if (info->altPortFd > 0) closesocket(info->altPortFd);
   if (info->altIpFd > 0) closesocket(info->altIpFd);
   if (info->altIpPortFd > 0) closesocket(info->altIpPortFd);
   
   if (info->relay)
   {
      int i;
      for (i=0; i<MAX_MEDIA_RELAYS; ++i)
      {
         StunMediaRelay* relay = &info->relays[i];
         if (relay->fd)
         {
            closesocket(relay->fd);
            relay->fd = 0;
         }
      }
   }
}

#if 0 /* no usefull here */


bool
stunServerProcess(StunServerInfo *info, bool verbose)
{
   char msg[STUN_MAX_MESSAGE_SIZE];
   int msgLen = sizeof(msg);
   	
   bool ok = false;
   bool recvAltIp =false;
   bool recvAltPort = false;
	
   fd_set fdSet; 
#if	defined(_WIN32) || defined(_WIN32_WCE)

   unsigned int maxFd=0;
#else

   int maxFd=0;
#endif

   struct timeval tv;
   int e;

   FD_ZERO(&fdSet); 
   FD_SET(info->myFd,&fdSet); 
   if ( info->myFd >= maxFd ) maxFd=info->myFd+1;
   FD_SET(info->altPortFd,&fdSet); 
   if ( info->altPortFd >= maxFd ) maxFd=info->altPortFd+1;

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

   if (info->relay)
   {
      int i;
      for (i=0; 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 != INVALID_SOCKET )
   {
      FD_SET(info->altIpFd,&fdSet);
      if (info->altIpFd>=maxFd) maxFd=info->altIpFd+1;
   }
   if ( info->altIpPortFd != INVALID_SOCKET )
   {
      FD_SET(info->altIpPortFd,&fdSet);
      if (info->altIpPortFd>=maxFd) maxFd=info->altIpPortFd+1;
   }
   
   tv.tv_sec = 0;
   tv.tv_usec = 1000;
	
   e = select( maxFd, &fdSet, NULL,NULL, &tv );
   if (e < 0)
   {
      int err = getErrno();
#if !defined(_WIN32_WCE)

      printf("Error on select: %s\n",  strerror(err) );
#else

      printf("Error on select: %i\n",  err );
#endif

   }
   else if (e >= 0)
   {
      StunAddress4 from;
      int relayPort = 0;

      bool changePort = false;
      bool changeIp = false;
		
      StunMessage resp;
      StunAddress4 dest;
      StunAtrString hmacPassword;  

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

      hmacPassword.sizeValue = 0;
      secondary.port = 0;
      secondary.addr = 0;

      /* do the media relaying */
      if (info->relay)
      {
         time_t now;
         int i;
#if !defined(_WIN32_WCE)

         now = time(0);
#else

         DWORD timemillis = GetTickCount();
         now = timemillis/1000;
#endif

         for (i=0; 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 ) printf("Relay packet on %i from %i -> %i",
                                           relay->fd,
                                           rtpFrom.addr,
                                           relay->destination.addr 
                                         );
                  }
               }
               else if (now > relay->expireTime)
               {
                  closesocket(relay->fd);
                  relay->fd = 0;
               }
            }
         }
      }
      
     
      if (FD_ISSET(info->myFd,&fdSet))
      {
         if (verbose) printf("received on A1:P1");
         recvAltIp = false;
         recvAltPort = false;
         ok = getMessage( info->myFd, msg, &msgLen, &from.addr, &from.port,verbose );
      }
      else if (FD_ISSET(info->altPortFd, &fdSet))
      {
         if (verbose) printf("received on A1:P2");
         recvAltIp = false;
         recvAltPort = true;
         ok = getMessage( info->altPortFd, msg, &msgLen, &from.addr, &from.port,verbose );
      }
      else if ( (info->altIpFd!=INVALID_SOCKET) && FD_ISSET(info->altIpFd,&fdSet))
      {
         if (verbose) printf("received on A2:P1");
         recvAltIp = true;
         recvAltPort = false;
         ok = getMessage( info->altIpFd, msg, &msgLen, &from.addr, &from.port ,verbose);
      }
      else if ( (info->altIpPortFd!=INVALID_SOCKET) && FD_ISSET(info->altIpPortFd, &fdSet))
      {
         if (verbose) printf("received on A2:P2");
         recvAltIp = true;
         recvAltPort = true;
         ok = getMessage( info->altIpPortFd, msg, &msgLen, &from.addr, &from.port,verbose );
      }
      else
      {
         return true;
      }

      if (info->relay)
      {
         int i;
         for (i=0; 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)
         {
            int i;
            for (i=0; i<MAX_MEDIA_RELAYS; ++i)
            {
               StunMediaRelay* relay = &info->relays[i];
               if (relay->fd == 0)
               {
                  if ( verbose ) printf("Open relay port %i\n", relay->relayPort );
                  relay->fd = openPort(relay->relayPort, info->myAddr.addr, verbose);
                  relay->destination.addr = from.addr;
                  relay->destination.port = from.port;
                  relay->expireTime = time(0) + MEDIA_RELAY_TIMEOUT;
                  relayPort = relay->relayPort;
                  break;
               }
            }
         }
      }
         
      if ( !ok ) 
      {
         if ( verbose ) printf("Get message did not return a valid message\n");
         return true;
      }
		
      if ( verbose ) printf("Got a request (len=%i) from %i", msgLen, from.addr);
		
      if ( msgLen <= 0 )
      {
         return true;
      }
		
      if (info->relay && relayPort)
      {
         secondary = from;
         
         from.addr = info->myAddr.addr;
         from.port = relayPort;
      }
      
      ok = stunServerProcessMsg( msg, msgLen, &from, &secondary,
                                 recvAltIp ? &info->altAddr : &info->myAddr,
                                 recvAltIp ? &info->myAddr : &info->altAddr, 
                                 &resp,
                                 &dest,
                                 &hmacPassword,
                                 &changePort,
                                 &changeIp,
                                 verbose );
		
      if ( !ok )
      {
         if ( verbose ) printf("Failed to parse message");
         return true;
      }
		
      len = stunEncodeMessage( &resp, buf, len, &hmacPassword,verbose );
		
      if ( dest.addr == 0 )  ok=false;
      if ( dest.port == 0 ) ok=false;
		
      if ( ok )
      {
         /* assert( dest.addr != 0 ); */
         /* assert( dest.port != 0 ); */
			
         Socket sendFd;
			
         bool sendAltIp   = recvAltIp;   /* send on the received IP address */
         bool sendAltPort = recvAltPort; /* send on the received port */
			
         if ( changeIp )   sendAltIp   = !sendAltIp; /* if need to change IP, then flip logic */
         if ( changePort ) sendAltPort = !sendAltPort; /* if need to change port, then flip logic */
			
         if ( !sendAltPort )
         {
            if ( !sendAltIp )
            {
               sendFd = info->myFd;
            }
            else
            {
               sendFd = info->altIpFd;
            }
         }
         else
         {
            if ( !sendAltIp )
            {
               sendFd = info->altPortFd;
            }
            else
            {
               sendFd = info->altIpPortFd;
            }
         }
	
         if ( sendFd != INVALID_SOCKET )
         {
            sendMessage( sendFd, buf, len, dest.addr, dest.port, verbose );
         }
      }
   }
   
   return true;
}
#endif


int 
stunFindLocalInterfaces(UInt32* addresses,int maxRet)
{
#if defined(WIN32) || defined(_WIN32_WCE) || defined(__sparc__)

   return 0;
#else

   struct ifconf ifc;
   int e;

   int s = socket( AF_INET, SOCK_DGRAM, 0 );
   int len = 100 * sizeof(struct ifreq);
   
   char buf[ 100 * sizeof(struct ifreq) ];
   char *ptr;
   int tl;
   int count=0;

   ifc.ifc_len = len;
   ifc.ifc_buf = buf;
	
   e = ioctl(s,SIOCGIFCONF,&ifc);
   ptr = buf;
   tl = ifc.ifc_len;
   
   while ( (tl > 0) && ( count < maxRet) )
   {
      struct ifreq* ifr = (struct ifreq *)ptr;
      struct ifreq ifr2;
      struct sockaddr a;
      struct sockaddr_in* addr;
   
      UInt32 ai;
      int si = sizeof(ifr->ifr_name) + sizeof(struct sockaddr);
      tl -= si;
      ptr += si;
      /* char* name = ifr->ifr_ifrn.ifrn_name; */
      /* cerr << "name = " << name ); */
      
      ifr2 = *ifr;
      
      e = ioctl(s,SIOCGIFADDR,&ifr2);
      if ( e == -1 )
      {
         break;
      }
      
      /* cerr << "ioctl addr e = " << e ; */
      
      a = ifr2.ifr_addr;
      addr = (struct sockaddr_in*) &a;
      
      ai = ntohl( addr->sin_addr.s_addr );
      if ((int)((ai>>24)&0xFF) != 127)
      {
         addresses[count++] = ai;
      }
		
   }
   
   closesocket(s);
   
   return count;
#endif

}


void
stunBuildReqSimple( StunMessage* msg,
                    const StunAtrString *username,
                    bool changePort, bool changeIp, unsigned int id )
{
   int i;
   /* assert( msg ); */
   memset( msg , 0 , sizeof(*msg) );
	
   msg->msgHdr.msgType = BindRequestMsg;
	
   for ( i=0; i<16; i=i+4 )
   {
      /* assert(i+3<16); */
      int r = stunRand();
      msg->msgHdr.id.octet[i+0]= r>>0;
      msg->msgHdr.id.octet[i+1]= r>>8;
      msg->msgHdr.id.octet[i+2]= r>>16;
      msg->msgHdr.id.octet[i+3]= r>>24;
   }
	
   if ( id != 0 )
   {
      msg->msgHdr.id.octet[0] = id; 
   }
	
   msg->hasChangeRequest = true;
   msg->changeRequest.value =(changeIp?ChangeIpFlag:0) | 
      (changePort?ChangePortFlag:0);
	
   if ( username->sizeValue > 0 )
   {
      msg->hasUsername = true;
      /* msg->username = username; */
      memcpy(&msg->username, username, sizeof(StunAtrString));
   }
}


static void 
stunSendTest( Socket myFd, StunAddress4 *dest, 
              const StunAtrString *username, const StunAtrString *password, 
              int testNum, bool verbose )
{ 
   /* assert( dest.addr != 0 ); */
   /* assert( dest.port != 0 ); */
	
   bool changePort=false;
   bool changeIP=false;
   bool discard=false;

   StunMessage req;
   char buf[STUN_MAX_MESSAGE_SIZE];
   int len = STUN_MAX_MESSAGE_SIZE;
   
   switch (testNum)
   {
      case 1:
      case 10:
      case 11:
         break;
      case 2:
         /* changePort=true; */
         changeIP=true;
         break;
      case 3:
         changePort=true;
         break;
      case 4:
         changeIP=true;
         break;
      case 5:
         discard=true;
         break;
      default:
         printf("Test %i is unkown\n", testNum);
         return ; /* error */
   }
   
   memset(&req, 0, sizeof(StunMessage));
	
   stunBuildReqSimple( &req, username, 
                       changePort , changeIP , 
                       testNum );
	
   len = stunEncodeMessage( &req, buf, len, password,verbose );
	
   if ( verbose )
   {
      printf("About to send msg of len %i to %s\n", len, ipaddr(dest) );
   }
	
   sendMessage( myFd, buf, len, dest->addr, dest->port, verbose );
	
   /* add some delay so the packets don't get sent too quickly */
#if defined(_WIN32_WCE) 

   Sleep (10);
#elif defined(WIN32)/* !cj! TODO - should fix this up in windows */

   {
       clock_t now = clock();
       assert( CLOCKS_PER_SEC == 1000 );
       while ( clock() <= now+10 ) { };
   }
#else

		 usleep(10*1000);
#endif


}


void 
stunGetUserNameAndPassword(  const StunAddress4 *dest, 
                             StunAtrString* username,
                             StunAtrString* password)
{ 
   /* !cj! This is totally bogus - need to make TLS connection to dest and get a */
   /* username and password to use */
   stunCreateUserName(dest, username);
   stunCreatePassword(username, password);
}


int 
stunTest( StunAddress4 *dest, int testNum, bool verbose, StunAddress4* sAddr , StunAddress4 *sMappedAddr, StunAddress4* sChangedAddr)
{
   /* assert( dest.addr != 0 ); */
   /* assert( dest.port != 0 ); */
	
   int port = randomPort();
   UInt32 interfaceIp=0;
   Socket myFd;
   StunAtrString username;
   StunAtrString password;
   char msg[STUN_MAX_MESSAGE_SIZE];
   int msgLen = STUN_MAX_MESSAGE_SIZE;
   StunAddress4 from;
   StunMessage resp;
   bool ok;

   if (sAddr)
   {
      interfaceIp = sAddr->addr;
      if ( sAddr->port != 0 )
      {
        port = sAddr->port;
      }
   }
   myFd = openPort(port,interfaceIp,verbose);
   if ( myFd == INVALID_SOCKET)
       return -1;
   
   username.sizeValue = 0;
   password.sizeValue = 0;
	
#ifdef USE_TLS

   stunGetUserNameAndPassword( dest, &username, &password );
#endif

	
   stunSendTest( myFd, dest, &username, &password, testNum, verbose );
   
   ok = getMessage( myFd,
               msg,
               &msgLen,
               &from.addr,
               &from.port,verbose );
   closesocket(myFd);
   if (!ok)
       return -1;

   memset(&resp, 0, sizeof(StunMessage));
	
   if ( verbose ) printf("Got a response");
   ok = stunParseMessage( msg,msgLen, &resp,verbose );
	
   if ( verbose )
   {
      printf("\t ok=%i\n", ok );
#if defined(WIN32) || defined(_WIN32_WCE)

      printf("\t id=%u\n", *(unsigned int*)&resp.msgHdr.id );
#endif

      printf("\t mappedAddr=%i\n", resp.mappedAddress.ipv4.addr );
      printf("\t changedAddr=%i\n", resp.changedAddress.ipv4.addr );
      printf("\n");
   }
	
   if (sAddr)
   {
       sAddr->port = port;
   }

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

   if (sChangedAddr)
   {
      sChangedAddr->port = resp.changedAddress.ipv4.port;
      sChangedAddr->addr = resp.changedAddress.ipv4.addr;
   }

   if (ok)
       return 0;
   else
       return -1;
}


NatType
stunNatType( StunAddress4 *dest, 
             bool verbose,
             bool* preservePort, /* if set, is return for if NAT preservers ports or not */
             bool* hairpin,  /* if set, is the return for if NAT will hairpin packets */
             int port, /* port to use for the test, 0 to choose random port */
             StunAddress4* sAddr /* NIC to use */
   )
{ 
   /* assert( dest.addr != 0 ); */
   /* assert( dest.port != 0 ); */
   UInt32 interfaceIp=0;
   Socket myFd1;
   Socket myFd2;

   bool respTestI=false;
   bool isNat=true;
   StunAddress4 testIchangedAddr;
   StunAddress4 testImappedAddr;
   bool respTestI2=false; 
   bool mappedIpSame = true;
   StunAddress4 testI2mappedAddr;
   /* StunAddress4 testI2dest=dest; */
   StunAddress4 testI2dest;
   bool respTestII=false;
   bool respTestIII=false;
   bool respTestHairpin=false;
   StunAtrString username;
   StunAtrString password;
   int count=0;
   int second_started;
   int second_elapsed;
   Socket s;

   if ( hairpin ) 
   {
      *hairpin = false;
   }
	
   if ( port == 0 )
   {
      port = randomPort();
   }

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

   if ( ( myFd1 == INVALID_SOCKET) || ( myFd2 == INVALID_SOCKET) )
   {
      printf("Some problem opening port/interface to send on\n");
      return StunTypeFailure; 
   }

   /* assert( myFd1 != INVALID_SOCKET ); */
   /* assert( myFd2 != INVALID_SOCKET ); */
   
   memcpy(&testI2dest, dest, sizeof(StunAddress4));

   memset(&testImappedAddr,0,sizeof(testImappedAddr));
   
   username.sizeValue = 0;
   password.sizeValue = 0;
	
#ifdef USE_TLS 

   stunGetUserNameAndPassword( dest, username, password );
#endif

	
   /* stunSendTest( myFd1, dest, username, password, 1, verbose ); */

   
   second_started = stunGetSystemTimeSecs();
   second_elapsed = 1;

   while ( count < 7 && second_elapsed < 5)
   {
      struct timeval tv;
      fd_set fdSet; 
      int err;
      int e;

#if defined(WIN32) || defined(_WIN32_WCE)

      unsigned int fdSetSize;
#else

      int fdSetSize;
#endif


      second_elapsed = stunGetSystemTimeSecs() - second_started ;

      FD_ZERO(&fdSet); fdSetSize=0;
      FD_SET(myFd1,&fdSet); fdSetSize = (myFd1+1>fdSetSize) ? myFd1+1 : fdSetSize;
      FD_SET(myFd2,&fdSet); fdSetSize = (myFd2+1>fdSetSize) ? myFd2+1 : fdSetSize;
      tv.tv_sec=0;
      tv.tv_usec=500*1000; /* 150 ms */
      if ( count == 0 ) tv.tv_usec=0;
		
      err = select(fdSetSize, &fdSet, NULL, NULL, &tv);
      e = getErrno();
      if ( err == SOCKET_ERROR )
      {
         /* error occured */
#if !defined(_WIN32_WCE)

         printf("Error %i %s in select\n", e, strerror(e));
#else

         printf("Error %i in select\n", e);
#endif

		 closesocket(myFd1); /* AMD */
         closesocket(myFd2); /* AMD */
         return StunTypeFailure;
     }
      else if ( err == 0 )
      {
         /* timeout occured */
         count++;
			
         if ( !respTestI ) 
         {
            stunSendTest( myFd1, dest, &username, &password, 1 ,verbose );
         }         
			
         if ( (!respTestI2) && respTestI ) 
         {
            /* check the address to send to if valid */
            if (  ( testI2dest.addr != 0 ) &&
                  ( testI2dest.port != 0 ) )
            {
               stunSendTest( myFd1, &testI2dest, &username, &password, 10  ,verbose);
            }
         }
			
         if ( !respTestII )
         {
            stunSendTest( myFd2, dest, &username, &password, 2 ,verbose );
         }
			
         if ( !respTestIII )
         {
            stunSendTest( myFd2, dest, &username, &password, 3 ,verbose );
         }
			
         if ( respTestI && (!respTestHairpin) )
         {
            if (  ( testImappedAddr.addr != 0 ) &&
                  ( testImappedAddr.port != 0 ) )
            {
               stunSendTest( myFd1, &testImappedAddr, &username, &password, 11 ,verbose );
            }
         }
      }
      else
      {
         int i;
         /* if (verbose) printf("-----------------------------------------"); */
         /* assert( err>0 ); */
         /* data is avialbe on some fd */
			
         for ( i=0; i<2; i++)
         {
            Socket myFd;
            if ( i==0 ) 
            {
               myFd=myFd1;
            }
            else
            {
               myFd=myFd2;
            }
				
            if ( myFd!=INVALID_SOCKET ) 
            {					
               if ( FD_ISSET(myFd,&fdSet) )
               {
                  char msg[STUN_MAX_MESSAGE_SIZE];
                  int msgLen = sizeof(msg);
                  						
                  StunAddress4 from;
                  StunMessage resp;

                  getMessage( myFd,
                              msg,
                              &msgLen,
                              &from.addr,
                              &from.port,verbose );
                  
                  memset(&resp, 0, sizeof(StunMessage));
						
                  stunParseMessage( msg,msgLen, &resp,verbose );
						
                  if ( verbose )
                  {
                     printf("Received message of type %i id=%i\n",
                            resp.msgHdr.msgType,
                            (int)(resp.msgHdr.id.octet[0]) );
                  }
						
                  switch( resp.msgHdr.id.octet[0] )
                  {
                     case 1:
                     {
                        if ( !respTestI )
                        {
									
                           testIchangedAddr.addr = resp.changedAddress.ipv4.addr;
                           testIchangedAddr.port = resp.changedAddress.ipv4.port;
                           testImappedAddr.addr = resp.mappedAddress.ipv4.addr;
                           testImappedAddr.port = resp.mappedAddress.ipv4.port;
									
                           if ( preservePort )
                           {
                              *preservePort = ( testImappedAddr.port == port );
                           }								
									
                           testI2dest.addr = resp.changedAddress.ipv4.addr;
									
                           if (sAddr)
                           {
                              sAddr->port = testImappedAddr.port;
                              sAddr->addr = testImappedAddr.addr;
                           }
									
                           count = 0;
                        }		
                        respTestI=true;
                     }
                     break;
                     case 2:
                     {  
                        respTestII=true;
                     }
                     break;
                     case 3:
                     {
                        respTestIII=true;
                     }
                     break;
                     case 10:
                     {
                        if ( !respTestI2 )
                        {
                           testI2mappedAddr.addr = resp.mappedAddress.ipv4.addr;
                           testI2mappedAddr.port = resp.mappedAddress.ipv4.port;
								
                           mappedIpSame = false;
                           if ( (testI2mappedAddr.addr  == testImappedAddr.addr ) &&
                                (testI2mappedAddr.port == testImappedAddr.port ))
                           { 
                              mappedIpSame = true;
                           }
								
							
                        }
                        respTestI2=true;
                     }
                     break;
                     case 11:
                     {
							
                        if ( hairpin ) 
                        {
                           *hairpin = true;
                        }
                        respTestHairpin = true;
                     }
                     break;
                  }
               }
            }
         }
      }
   }
	
   closesocket(myFd1); /* AMD */
   closesocket(myFd2); /* AMD */

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

   closesocket(s); /* AMD */
	
   if (verbose)
   {
      printf("test I = %i\n", respTestI );
      printf("test II = %i\n", respTestII );
      printf("test III = %i\n", respTestIII );
      printf("test I(2) = %i\n", respTestI2 );
      printf("is nat  = %i\n", isNat);
      printf("mapped IP same = %i\n", mappedIpSame );
   }
	
   /* implement logic flow chart from draft RFC */
   if ( respTestI )
   {
      if ( isNat )
      {
         if (respTestII)
         {
            return StunTypeConeNat;
         }
         else
         {
            if ( mappedIpSame )
            {
               if ( respTestIII )
               {
                  return StunTypeRestrictedNat;
               }
               else
               {
                  return StunTypePortRestrictedNat;
               }
            }
            else
            {
               return StunTypeSymNat;
            }
         }
      }
      else
      {
         if (respTestII)
         {
            return StunTypeOpen;
         }
         else
         {
            return StunTypeSymFirewall;
         }
      }
   }
   else
   {
      return StunTypeBlocked;
   }
	
   return StunTypeUnknown;
}

int
stunOpenSocket( StunAddress4 *dest, StunAddress4* mapAddr, 
                int port, StunAddress4* srcAddr, 
                bool verbose )
{
   /* assert( dest.addr != 0 ); */
   /* assert( dest.port != 0 ); */
   /* assert( mapAddr );*/
   unsigned int interfaceIp = 0;
   Socket myFd;
   char msg[STUN_MAX_MESSAGE_SIZE];
   int msgLen = sizeof(msg);
	
   StunAtrString username;
   StunAtrString password;

   StunAddress4 from;
   StunMessage resp;
   bool ok;
   StunAddress4 mappedAddr;

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

   if ( srcAddr )
   {
      interfaceIp = srcAddr->addr;
   }
   
   myFd = openPort(port,interfaceIp,verbose);
   if (myFd == INVALID_SOCKET)
   {
      return myFd;
   }
   
   username.sizeValue = 0;
   password.sizeValue = 0;
	
#ifdef USE_TLS

   stunGetUserNameAndPassword( dest, username, password );
#endif

	
   stunSendTest(myFd, dest, &username, &password, 1, 0/*false*/ );
	
   getMessage( myFd, msg, &msgLen, &from.addr, &from.port,verbose );

   memset(&resp, 0, sizeof(StunMessage));
	
   ok = stunParseMessage( msg, msgLen, &resp,verbose );
   if (!ok)
   {
       closesocket(myFd);
       return -1;
   }
	
   mappedAddr = resp.mappedAddress.ipv4;
	
   /*
     printf("--- stunOpenSocket --- ");
     printf("\treq  id=" << req.id );
     printf("\tresp id=" << id );
     printf("\tmappedAddr=" << mappedAddr );
   */

   *mapAddr = mappedAddr;
	
   return myFd;
}


bool
stunOpenSocketPair(StunAddress4 *dest,
                   StunAddress4* mapAddr_rtp, 
                   StunAddress4* mapAddr_rtcp, 
                   int* fd1, int* fd2, 
                   int port, StunAddress4* srcAddr, 
                   bool verbose )
{
   /* assert( dest.addr!= 0 ); */
   /* assert( dest.port != 0 ); */
   /* assert( mapAddr ); */
   
   const int NUM=2;
   char msg[STUN_MAX_MESSAGE_SIZE];
   int msgLen =sizeof(msg);
	
   StunAddress4 from;
   int fd[2/*NUM*/];
   int i;
	
   unsigned int interfaceIp = 0;
	
   StunAtrString username;
   StunAtrString password;
	
   StunAddress4 mappedAddr[2/*NUM*/];

   if ( port == 0 )
   {
      port = randomPort();
   }
	
   *fd1=-1;
   *fd2=-1;
	
   if ( srcAddr )
   {
      interfaceIp = srcAddr->addr;
   }

   for( i=0; i<NUM; i++)
   {
      fd[i] = openPort( (port == 0) ? 0 : (port + i), 
                        interfaceIp, verbose);
      if (fd[i] < 0) 
      {
         while (i > 0)
         {
            closesocket(fd[--i]);
         }
         return false;
      }
   }
	
   username.sizeValue = 0;
   password.sizeValue = 0;
	
#ifdef USE_TLS

   stunGetUserNameAndPassword( dest, username, password );
#endif

	
   for( i=0; i<NUM; i++)
   {
      stunSendTest(fd[i], dest, &username, &password, 1/*testNum*/, verbose );
   }
   
   for( i=0; i<NUM; i++)
   {
      StunMessage resp;
      bool ok;
      msgLen = sizeof(msg)/sizeof(*msg);
      getMessage( fd[i],
                  msg,
                  &msgLen,
                  &from.addr,
                  &from.port ,verbose);
      
      memset(&resp, 0, sizeof(StunMessage));
		
      ok = stunParseMessage( msg, msgLen, &resp, verbose );
      if (!ok) 
      {  
          for( i=0; i<NUM; i++)
          {
              closesocket(fd[i]);
          }
         return false;
      }
	  
      mappedAddr[i] = resp.mappedAddress.ipv4;
   }
	
   if (verbose)
   {
      printf("--- stunOpenSocketPair --- \n");
      for( i=0; i<NUM; i++)
      {
         printf("\t mappedAddr=%s\n", ipaddr(&mappedAddr[i]) );
      }
   }
	
#if 0

   if ( mappedAddr[0].port %2 == 0 )
   {
      if (  mappedAddr[0].port+1 ==  mappedAddr[1].port )
      {
         *mapAddr = mappedAddr[0];
         *fd1 = fd[0];
         *fd2 = fd[1];
         closesocket( 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];
         closesocket( fd[0] );
         return true;
      }
   }
#else

    *mapAddr_rtp = mappedAddr[0];
    *mapAddr_rtcp = mappedAddr[1];
    *fd1 = fd[0];
    *fd2 = fd[1];
#endif


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

/* Local Variables:
   mode:c
   c-file-style:"ellemtel"
   c-file-offsets:((case-label . +))
   indent-tabs-mode:nil
   End:
*/



syntax highlighted by Code2HTML, v. 0.9.1