/* Copyright (c) 2002-2012 Croteam Ltd. 
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation


This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */

#include <Engine/StdH.h>

#include <Engine/Engine.h>
#include <Engine/CurrentVersion.h>
#include <Engine/Entities/Entity.h>
#include <Engine/Base/Shell.h>
#include <Engine/Base/Console.h>
#include <Engine/Base/CTString.h>
#include <Engine/Network/Server.h>
#include <Engine/Network/Network.h>
#include <Engine/Network/SessionState.h>
#include <GameMP/SessionProperties.h>
#include <Engine/GameAgent/GameAgent.h>

#if defined(PLATFORM_WIN32)
#pragma comment(lib, "wsock32.lib")
WSADATA* _wsaData = NULL;
typedef int socklen_t;
#else
#include <fcntl.h>
#include <netdb.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#define INVALID_SOCKET -1
#define SOCKET_ERROR   -1
#define closesocket close
typedef int SOCKET;
typedef struct hostent HOSTENT, *PHOSTENT;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr    SOCKADDR;
#define WSAGetLastError() (INDEX) errno
#define WSACleanup()
#endif

#define MSPORT      28900
#define BUFFSZ      8192
#define BUFFSZSTR   4096

#define PCK         "\\gamename\\%s" \
                    "\\enctype\\%d" \
                    "\\validate\\%s" \
                    "\\final\\" \
                    "\\queryid\\1.1" \
                    "\\list\\cmp" \
                    "\\gamename\\%s" \
                    "\\gamever\\1.05" \
                    "%s%s" \
                    "\\final\\"

#define PCKQUERY    "\\gamename\\%s" \
                    "\\gamever\\%s" \
                    "\\location\\%s" \
                    "\\hostname\\%s" \
                    "\\hostport\\%hu" \
                    "\\mapname\\%s" \
                    "\\gametype\\%s" \
                    "\\activemod\\" \
                    "\\numplayers\\%d" \
                    "\\maxplayers\\%d" \
                    "\\gamemode\\openplaying" \
                    "\\difficulty\\Normal" \
                    "\\friendlyfire\\%d" \
                    "\\weaponsstay\\%d" \
                    "\\ammosstay\\%d" \
                    "\\healthandarmorstays\\%d" \
                    "\\allowhealth\\%d" \
                    "\\allowarmor\\%d" \
                    "\\infinitearmor\\%d" \
                    "\\respawninplace\\%d" \
                    "\\password\\0" \
                    "\\vipplayers\\1"

#define PCKINFO     "\\hostname\\%s" \
                    "\\hostport\\%hu" \
                    "\\mapname\\%s" \
                    "\\gametype\\%s" \
                    "\\numplayers\\%d" \
                    "\\maxplayers\\%d" \
                    "\\gamemode\\openplaying" \
                    "\\final\\" \
                    "\\queryid\\8.1"

#define PCKBASIC    "\\gamename\\%s" \
                    "\\gamever\\%s" \
                    "\\location\\EU" \
                    "\\final\\" \
                    "\\queryid\\1.1"


#define CHK_BUFFSTRLEN if((iLen < 0) || (iLen > BUFFSZSTR)) { \
                        CPrintF("\n" \
                            "Error: the used buffer is smaller than how much needed (%d < %d)\n" \
                            "\n", iLen, BUFFSZSTR); \
                            if(cMsstring) free (cMsstring); \
                            closesocket(_sock); \
                            WSACleanup(); \
                        }

#define CLEANMSSRUFF1       closesocket(_sock); \
                            WSACleanup();

#define CLEANMSSRUFF2       if(cResponse) free (cResponse); \
                            closesocket(_sock); \
                            WSACleanup();

#define SERIOUSSAMKEY       "AKbna4\0"
#define SERIOUSSAMSTR       "serioussamse"

#include <Engine/GameAgent/MSLegacy.h>

SOCKET _socket = INVALID_SOCKET;

sockaddr_in* _sin = NULL;
sockaddr_in* _sinLocal = NULL;
sockaddr_in _sinFrom;

CHAR* _szBuffer = NULL;
CHAR* _szIPPortBuffer = NULL;
LONG   _iIPPortBufferLen = 0;
CHAR* _szIPPortBufferLocal = NULL;
LONG   _iIPPortBufferLocalLen = 0;

BOOL _bServer = FALSE;
BOOL _bInitialized = FALSE;
BOOL _bActivated = FALSE;
BOOL _bActivatedLocal = FALSE;

TIME _tmLastHeartbeat = 0;

CDynamicStackArray<CServerRequest> ga_asrRequests;

//CTString ga_strServer = "master1.croteam.org";
CTString ga_strServer = "master1.42amsterdam.net";
//CTString ga_strMSLegacy = "master1.croteam.org";
CTString ga_strMSLegacy = "42amsterdam.net";

BOOL ga_bMSLegacy = TRUE;
//BOOL ga_bMSLegacy = FALSE;

void _uninitWinsock();
void _initializeWinsock(void)
{
#ifdef PLATFORM_WIN32
  if(_wsaData != NULL && _socket != INVALID_SOCKET) {
    return;
  }

  _wsaData = new WSADATA;
  _socket = INVALID_SOCKET;

  // start WSA
  if(WSAStartup(MAKEWORD(2, 2), _wsaData) != 0) {
    CPrintF("Error initializing winsock!\n");
    _uninitWinsock();
    return;
  }
#endif

  // make the buffer that we'll use for packet reading
  if(_szBuffer != NULL) {
    delete[] _szBuffer;
  }
  _szBuffer = new char[2050];

  // get the host IP
  hostent* phe;
  if(!ga_bMSLegacy) {
    phe = gethostbyname(ga_strServer);
  } else {
    phe = gethostbyname(ga_strMSLegacy);
  }
  // if we couldn't resolve the hostname
  if(phe == NULL) {
    // report and stop
    CPrintF("Couldn't resolve GameAgent server %s.\n", (const char *) ga_strServer);
    _uninitWinsock();
    return;
  }

  // create the socket destination address
  _sin = new sockaddr_in;
  _sin->sin_family = AF_INET;
  _sin->sin_addr.s_addr = *(ULONG*)phe->h_addr_list[0];
  if(!ga_bMSLegacy) {
    _sin->sin_port = htons(9005);
  } else {
    _sin->sin_port = htons(27900);
  }

  // create the socket
  _socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  if (_socket == INVALID_SOCKET) {
    CPrintF("Error creating GameAgent socket!\n");
    _uninitWinsock();
    return;
  }

  // if we're a server
  if(_bServer) {
    // create the local socket source address
    _sinLocal = new sockaddr_in;
    _sinLocal->sin_family = AF_INET;
    _sinLocal->sin_addr.s_addr = inet_addr("0.0.0.0");
    _sinLocal->sin_port = htons(_pShell->GetINDEX("net_iPort") + 1);

    // bind the socket
    bind(_socket, (sockaddr*)_sinLocal, sizeof(*_sinLocal));
  }

  // set the socket to be nonblocking
#ifdef PLATFORM_WIN32
  DWORD dwNonBlocking = 1;
  if(ioctlsocket(_socket, FIONBIO, &dwNonBlocking) != 0) {
    CPrintF("Error setting socket to nonblocking!\n");
    _uninitWinsock();
    return;
  }
#else
  int flags = fcntl(_socket, F_GETFL);
  int failed = flags;
  if (failed != -1) {
      flags |= O_NONBLOCK;
      failed = fcntl(_socket, F_SETFL, flags);
  }

  if (failed == -1) {
    CPrintF("Error setting socket to nonblocking!\n");
    _uninitWinsock();
    return;
  }
#endif
}

void _uninitWinsock()
{
  if (_socket != INVALID_SOCKET) {
    closesocket(_socket);
    _socket = INVALID_SOCKET;
  }
  #ifdef PLATFORM_WIN32
  if(_wsaData != NULL) {
    delete _wsaData;
    _wsaData = NULL;
  }
  #endif
}

void _sendPacketTo(const char* pubBuffer, INDEX iLen, sockaddr_in* sin)
{
  sendto(_socket, pubBuffer, iLen, 0, (sockaddr*)sin, sizeof(sockaddr_in));
}
void _sendPacketTo(const char* szBuffer, sockaddr_in* addsin)
{
  sendto(_socket, szBuffer, strlen(szBuffer), 0, (sockaddr*)addsin, sizeof(sockaddr_in));
}

void _sendPacket(const char* pubBuffer, INDEX iLen)
{
  _initializeWinsock();
  _sendPacketTo(pubBuffer, iLen, _sin);
}
void _sendPacket(const char* szBuffer)
{
  _initializeWinsock();
  _sendPacketTo(szBuffer, _sin);
}

int _recvPacket()
{
  socklen_t fromLength = sizeof(_sinFrom);
  return recvfrom(_socket, _szBuffer, 2048, 0, (sockaddr*)&_sinFrom, &fromLength);
}

CTString _getGameModeName(INDEX iGameMode)
{
  // get function that will provide us the info about gametype
  CShellSymbol *pss = _pShell->GetSymbol("GetGameTypeNameSS", /*bDeclaredOnly=*/ TRUE);

  if(pss == NULL) {
    return "";
  }

  CTString (*pFunc)(INDEX) = (CTString (*)(INDEX))pss->ss_pvValue;
  return pFunc(iGameMode);
}

const CSessionProperties* _getSP()
{
  return ((const CSessionProperties *)_pNetwork->GetSessionProperties());
}

void _sendHeartbeat(INDEX iChallenge)
{
  CTString strPacket;
  if (!ga_bMSLegacy) {
    strPacket.PrintF("0;challenge;%d;players;%d;maxplayers;%d;level;%s;gametype;%s;version;%s;product;%s",
        iChallenge,
        _pNetwork->ga_srvServer.GetPlayersCount(),
        _pNetwork->ga_sesSessionState.ses_ctMaxPlayers,
        (const char *) _pNetwork->ga_World.wo_strName,
        (const char *) _getGameModeName(_getSP()->sp_gmGameMode),
        _SE_VER_STRING,
        (const char *) _pShell->GetString("sam_strGameName"));
  } else {
    strPacket.PrintF("\\heartbeat\\%hu\\gamename\\serioussamse", (_pShell->GetINDEX("net_iPort") + 1));
  }
  _sendPacket(strPacket);
  _tmLastHeartbeat = _pTimer->GetRealTimeTick();
}

static void _setStatus(const CTString &strStatus)
{
  _pNetwork->ga_bEnumerationChange = TRUE;
  _pNetwork->ga_strEnumerationStatus = strStatus;
}

CServerRequest::CServerRequest(void)
{
  Clear();
}
CServerRequest::~CServerRequest(void) { }
void CServerRequest::Clear(void)
{
  sr_ulAddress = 0;
  sr_iPort = 0;
  sr_tmRequestTime = 0;
}

/// Initialize GameAgent.
extern void GameAgent_ServerInit(void)
{
  // join
  _bServer = TRUE;
  _bInitialized = TRUE;

  if (!ga_bMSLegacy) {
    _sendPacket("q");
  } else {
    CTString strPacket;
    strPacket.PrintF("\\heartbeat\\%hu\\gamename\\serioussamse", (_pShell->GetINDEX("net_iPort") + 1));
    _sendPacket(strPacket);
  }
}

/// Let GameAgent know that the server has stopped.
extern void GameAgent_ServerEnd(void)
{
  if (!_bInitialized) {
    return;
  }

  if (ga_bMSLegacy) {
    CTString strPacket;
    strPacket.PrintF("\\heartbeat\\%hu\\gamename\\serioussamse\\statechanged", (_pShell->GetINDEX("net_iPort") + 1));
    _sendPacket(strPacket);
  }

  _uninitWinsock();
  _bInitialized = FALSE;
}

/// GameAgent server update call which responds to enumeration pings and sends pings to masterserver.
extern void GameAgent_ServerUpdate(void)
{
  if((_socket == INVALID_SOCKET) || (!_bInitialized)) {
    return;
  }

  int iLen = _recvPacket();
  if(iLen > 0) {
    if (!ga_bMSLegacy) {
    // check the received packet ID
    switch(_szBuffer[0]) {
    case 1: // server join response
      {
        int iChallenge = *(INDEX*)(_szBuffer + 1);
        // send the challenge
        _sendHeartbeat(iChallenge);
        break;
      }

    case 2: // server status request
      {
        // send the status response
        CTString strPacket;
        strPacket.PrintF("0;players;%d;maxplayers;%d;level;%s;gametype;%s;version;%s;gamename;%s;sessionname;%s",
          _pNetwork->ga_srvServer.GetPlayersCount(),
          _pNetwork->ga_sesSessionState.ses_ctMaxPlayers,
          (const char *) _pNetwork->ga_World.wo_strName,
          (const char *) _getGameModeName(_getSP()->sp_gmGameMode),
          _SE_VER_STRING,
          (const char *) _pShell->GetString("sam_strGameName"),
          (const char *) _pShell->GetString("gam_strSessionName"));
        _sendPacketTo(strPacket, &_sinFrom);
        break;
      }

    case 3: // player status request
      {
        // send the player status response
        CTString strPacket;
        strPacket.PrintF("\x01players\x02%d\x03", _pNetwork->ga_srvServer.GetPlayersCount());
        for(INDEX i=0; i<_pNetwork->ga_srvServer.GetPlayersCount(); i++) {
          CPlayerBuffer &plb = _pNetwork->ga_srvServer.srv_aplbPlayers[i];
          CPlayerTarget &plt = _pNetwork->ga_sesSessionState.ses_apltPlayers[i];
          if(plt.plt_bActive) {
            CTString strPlayer;
            plt.plt_penPlayerEntity->GetGameAgentPlayerInfo(plb.plb_Index, strPlayer);

            // if we don't have enough space left for the next player
            if(strlen(strPacket) + strlen(strPlayer) > 2048) {
              // send the packet
              _sendPacketTo(strPacket, &_sinFrom);
              strPacket = "";
            }

            strPacket += strPlayer;
          }
        }

        strPacket += "\x04";
        _sendPacketTo(strPacket, &_sinFrom);
        break;
      }

    case 4: // ping
      {
        // just send back 1 byte and the amount of players in the server (this could be useful in some cases for external scripts)
        CTString strPacket;
        strPacket.PrintF("\x04%d", _pNetwork->ga_srvServer.GetPlayersCount());
        _sendPacketTo(strPacket, &_sinFrom);
        break;
      }
     }
   } else {

      _szBuffer[iLen] = 0;
      char *sPch1 = NULL, *sPch2 = NULL, *sPch3 = NULL, *sPch4 = NULL;
      sPch1 = strstr(_szBuffer, "\\status\\");
      sPch2 = strstr(_szBuffer, "\\info\\");
      sPch3 = strstr(_szBuffer, "\\basic\\");
      sPch4 = strstr(_szBuffer, "\\players\\");
      if(sPch1) {
        CTString strPacket;
        CTString strLocation;
        strLocation = _pShell->GetString("net_strLocalHost");
        if ( strLocation == ""){
          strLocation = "Heartland";
        }
        strPacket.PrintF( PCKQUERY,
          (const char *) _pShell->GetString("sam_strGameName"),
          _SE_VER_STRING,
          //(const char *) _pShell->GetString("net_strLocalHost"),
          (const char *) strLocation,
          (const char *) _pShell->GetString("gam_strSessionName"),
          _pShell->GetINDEX("net_iPort"),
          (const char *) _pNetwork->ga_World.wo_strName,
          (const char *) _getGameModeName(_getSP()->sp_gmGameMode),
          _pNetwork->ga_srvServer.GetPlayersCount(),
          _pNetwork->ga_sesSessionState.ses_ctMaxPlayers,
          _pShell->GetINDEX("gam_bFriendlyFire"),
          _pShell->GetINDEX("gam_bWeaponsStay"),
          _pShell->GetINDEX("gam_bAmmoStays"),
          _pShell->GetINDEX("gam_bHealthArmorStays"),
          _pShell->GetINDEX("gam_bAllowHealth"),
          _pShell->GetINDEX("gam_bAllowArmor"),
          _pShell->GetINDEX("gam_bInfiniteAmmo"),
          _pShell->GetINDEX("gam_bRespawnInPlace"));

          for(INDEX i=0; i<_pNetwork->ga_srvServer.GetPlayersCount(); i++) {
            CPlayerBuffer &plb = _pNetwork->ga_srvServer.srv_aplbPlayers[i];
            CPlayerTarget &plt = _pNetwork->ga_sesSessionState.ses_apltPlayers[i];
            if(plt.plt_bActive) {
              CTString strPlayer;
              plt.plt_penPlayerEntity->GetMSLegacyPlayerInf(plb.plb_Index, strPlayer);

              // if we don't have enough space left for the next player
              if(strlen(strPacket) + strlen(strPlayer) > 2048) {
                // send the packet
                _sendPacketTo(strPacket, &_sinFrom);
                strPacket = "";
              }
              strPacket += strPlayer;
            }
          }

        strPacket += "\\final\\\\queryid\\333.1";
        _sendPacketTo(strPacket, &_sinFrom);

      } else if (sPch2){

        CTString strPacket;
        strPacket.PrintF( PCKINFO,
          (const char *) _pShell->GetString("gam_strSessionName"),
          _pShell->GetINDEX("net_iPort"),
          (const char *) _pNetwork->ga_World.wo_strName,
          (const char *) _getGameModeName(_getSP()->sp_gmGameMode),
          _pNetwork->ga_srvServer.GetPlayersCount(),
          _pNetwork->ga_sesSessionState.ses_ctMaxPlayers);
        _sendPacketTo(strPacket, &_sinFrom);

      } else if (sPch3){

        CTString strPacket;
        CTString strLocation;
        strLocation = _pShell->GetString("net_strLocalHost");
        if ( strLocation == ""){
          strLocation = "Heartland";
        }
        strPacket.PrintF( PCKBASIC,
          (const char *) _pShell->GetString("sam_strGameName"),
          _SE_VER_STRING,
          //(const char *) _pShell->GetString("net_strLocalHost"));
          (const char *) strLocation);
        _sendPacketTo(strPacket, &_sinFrom);

      } else if (sPch4){

        // send the player status response
        CTString strPacket;
        strPacket = "";
        for(INDEX i=0; i<_pNetwork->ga_srvServer.GetPlayersCount(); i++) {
          CPlayerBuffer &plb = _pNetwork->ga_srvServer.srv_aplbPlayers[i];
          CPlayerTarget &plt = _pNetwork->ga_sesSessionState.ses_apltPlayers[i];
          if(plt.plt_bActive) {
            CTString strPlayer;
            plt.plt_penPlayerEntity->GetMSLegacyPlayerInf(plb.plb_Index, strPlayer);

            // if we don't have enough space left for the next player
            if(strlen(strPacket) + strlen(strPlayer) > 2048) {
              // send the packet
              _sendPacketTo(strPacket, &_sinFrom);
              strPacket = "";
            }

            strPacket += strPlayer;
          }
        }

        strPacket += "\\final\\\\queryid\\6.1";
        _sendPacketTo(strPacket, &_sinFrom);

      } else {
        CPrintF("Unknown query server response!\n");
        return;
      }
   }
 }

 // send a heartbeat every 150 seconds
 if(_pTimer->GetRealTimeTick() - _tmLastHeartbeat >= 150.0f) {
    _sendHeartbeat(0);
 }
}

/// Notify GameAgent that the server state has changed.
extern void GameAgent_ServerStateChanged(void)
{
  if (!_bInitialized) {
    return;
  }
  if (!ga_bMSLegacy) {
    _sendPacket("u");
  } else {
    CTString strPacket;
    strPacket.PrintF("\\heartbeat\\%hu\\gamename\\serioussamse\\statechanged", (_pShell->GetINDEX("net_iPort") + 1));
    _sendPacket(strPacket);
  }
}

/// Request serverlist enumeration.
extern void GameAgent_EnumTrigger(BOOL bInternet)
{

  if ( _pNetwork->ga_bEnumerationChange ) {
    return;
  }
  
  if ( !bInternet && ga_bMSLegacy) {
    // make sure that there are no requests still stuck in buffer
    ga_asrRequests.Clear();
    // we're not a server
    _bServer = FALSE;
    _pNetwork->ga_strEnumerationStatus = ".";

	PHOSTENT _phHostinfo;
	ULONG    _uIP,*_pchIP = &_uIP;
	USHORT   _uPort,*_pchPort = &_uPort;
	LONG     _iLen;
	char     _cName[256],*_pch,_strFinal[8] = {0};

	struct in_addr addr;

    // make the buffer that we'll use for packet reading
    if(_szIPPortBufferLocal != NULL) {
       return;
    }
    _szIPPortBufferLocal = new char[1024];

    #ifdef PLATFORM_WIN32
	// start WSA
	WSADATA  wsaData;
	const WORD _wsaRequested = MAKEWORD( 2, 2 );
    if( WSAStartup(_wsaRequested, &wsaData) != 0) {
		CPrintF("Error initializing winsock!\n");
		if(_szIPPortBufferLocal != NULL) {
			delete[] _szIPPortBufferLocal;
		}
		_szIPPortBufferLocal = NULL;
		_uninitWinsock();
		_bInitialized = FALSE;
		_pNetwork->ga_bEnumerationChange = FALSE;
		_pNetwork->ga_strEnumerationStatus = "";
		WSACleanup();
        return;
    }
    #endif

    _pch = _szIPPortBufferLocal;
	_iLen = 0;
	strcpy(_strFinal,"\\final\\");
	
    if( gethostname ( _cName, sizeof(_cName)) == 0)
	{
		if((_phHostinfo = gethostbyname(_cName)) != NULL)
		{
			int _iCount = 0;
			while(_phHostinfo->h_addr_list[_iCount])
			{
				addr.s_addr = *(u_long *) _phHostinfo->h_addr_list[_iCount];
				_uIP = htonl(addr.s_addr);
				
				for (UINT uPort = 25601; uPort < 25622; ++uPort){
					_uPort = htons(uPort);
					memcpy(_pch,_pchIP,4);
					_pch  +=4;
					_iLen +=4;
					memcpy(_pch,_pchPort,2);
					_pch  +=2;
					_iLen +=2;
				}
				++_iCount;
			}
			memcpy(_pch,_strFinal, 7);
			_pch  +=7;
			_iLen +=7;
			_pch[_iLen] = 0x00;
		}
	}
    _iIPPortBufferLocalLen = _iLen;

    _bActivatedLocal = TRUE;
    _bInitialized = TRUE;
    _initializeWinsock();	
    return;
	
  } else {

  if (!ga_bMSLegacy) {
    // make sure that there are no requests still stuck in buffer
    ga_asrRequests.Clear();
    // we're not a server
    _bServer = FALSE;
    // Initialization
    _bInitialized = TRUE;
    // send enumeration packet to masterserver
    _sendPacket("e");
    _setStatus(".");
  }
  else
  { /* MSLegacy */
    // make sure that there are no requests still stuck in buffer
    ga_asrRequests.Clear();
    // we're not a server
    _bServer = FALSE;
    _pNetwork->ga_strEnumerationStatus = ".";

    struct  sockaddr_in peer;

    SOCKET  _sock               = INVALID_SOCKET;
    u_int   uiMSIP;
    int     iErr,
            iLen,
            iDynsz,
            iEnctype             = 0;
    u_short usMSport             = MSPORT;

    u_char  ucGamekey[]          = {SERIOUSSAMKEY},
            ucGamestr[]          = {SERIOUSSAMSTR},
            *ucSec               = NULL,
            *ucKey               = NULL;

    const char *cFilter             = "";
    const char *cWhere              = "";
    char cMS[128]             = {0};
    char *cResponse           = NULL;
    char *cMsstring           = NULL;
    char *cSec                = NULL;

    strcpy(cMS,ga_strMSLegacy);

    #if PLATFORM_WIN32
    WSADATA wsadata;
    if(WSAStartup(MAKEWORD(2,2), &wsadata) != 0) {
        CPrintF("Error initializing winsock!\n");
        return;
    }
    #endif

/* Open a socket and connect to the Master server */

    peer.sin_addr.s_addr = uiMSIP = resolv(cMS);
    peer.sin_port        = htons(usMSport);
    peer.sin_family      = AF_INET;

    _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(_sock < 0) {
        CPrintF("Error creating TCP socket!\n");
        WSACleanup();
        return;
    }
    if(connect(_sock, (struct sockaddr *)&peer, sizeof(peer)) < 0) {
        CPrintF("Error connecting to TCP socket!\n");
        CLEANMSSRUFF1;
        return;
    }

/* Allocate memory for a buffer and get a pointer to it */

    cResponse = (char*) malloc(BUFFSZSTR + 1);
    if(!cResponse) {
        CPrintF("Error initializing memory buffer!\n");
        CLEANMSSRUFF1;
        return;
    }

/* Reading response from Master Server - returns the string with the secret key */

    iLen = 0;
    iErr = recv(_sock, (char*)cResponse + iLen, BUFFSZSTR - iLen, 0);
    if(iErr < 0) {
        CPrintF("Error reading from TCP socket!\n");
        CLEANMSSRUFF2;
        return;
    }

    iLen += iErr;
    cResponse[iLen] = 0x00;

/* Allocate memory for a buffer and get a pointer to it */

    ucSec = (u_char*) malloc(BUFFSZSTR + 1);
    if(!ucSec) {
        CPrintF("Error initializing memory buffer!\n");
        CLEANMSSRUFF2;
        return;
    }
    memcpy ( ucSec, cResponse,  BUFFSZSTR);
    ucSec[iLen] = 0x00;

/* Geting the secret key from a string */

    cSec = strstr(cResponse, "\\secure\\");
    if(!cSec) {
        CPrintF("Not valid master server response!\n");
        CLEANMSSRUFF2;
        return;
    } else {
        ucSec  += 15;

/* Creating a key for authentication (Validate key) */

        ucKey = gsseckey(ucSec, ucGamekey, iEnctype);
    }
    ucSec -= 15;
    if(cResponse) free (cResponse);
    if(ucSec) free (ucSec);

/* Generate a string for the response (to Master Server) with the specified (Validate ucKey) */

    cMsstring = (char*) malloc(BUFFSZSTR + 1);
    if(!cMsstring) {
        CPrintF("Not valid master server response!\n");
        CLEANMSSRUFF1;
        return;
    }

    iLen = _snprintf(
        cMsstring,
        BUFFSZSTR,
        PCK,
        ucGamestr,
        iEnctype,
        ucKey,
        ucGamestr,
        cWhere,
        cFilter);

/* Check the buffer */

    CHK_BUFFSTRLEN;

/* The string sent to master server */

    if(send(_sock,cMsstring, iLen, 0) < 0){
        CPrintF("Error reading from TCP socket!\n");
        if(cMsstring) free (cMsstring);
        CLEANMSSRUFF1;
        return;
    }
    if(cMsstring) free (cMsstring);

 /* Allocate memory for a buffer and get a pointer to it */

    if(_szIPPortBuffer ) {
        CLEANMSSRUFF1;
        return;
    };

    _szIPPortBuffer = (char*) malloc(BUFFSZ + 1);
    if(!_szIPPortBuffer) {
        CPrintF("Error reading from TCP socket!\n");
        CLEANMSSRUFF1;
        return;
    }
    iDynsz = BUFFSZ;


/* The received encoded data after sending the string (Validate key) */

    iLen = 0;
    while((iErr = recv(_sock, _szIPPortBuffer + iLen, iDynsz - iLen, 0)) > 0) {
        iLen += iErr;
        if(iLen >= iDynsz) {
            iDynsz += BUFFSZ;
            _szIPPortBuffer = (char*)realloc(_szIPPortBuffer, iDynsz);
            if(!_szIPPortBuffer) {
                CPrintF("Error reallocation memory buffer!\n");
                if(_szIPPortBuffer) free (_szIPPortBuffer);
                CLEANMSSRUFF1;
                return;
            }
        }
    }
    CLEANMSSRUFF1;
    _iIPPortBufferLen = iLen;

    _bActivated = TRUE;
    _bInitialized = TRUE;
    _initializeWinsock();
   
    }
  }
}

/// GameAgent client update for enumerations.
extern void GameAgent_EnumUpdate(void)
{
  if((_socket == INVALID_SOCKET) || (!_bInitialized)) {
    return;
  }

  if (!ga_bMSLegacy) {
   int iLen = _recvPacket();
   if(iLen != -1) {
    // null terminate the buffer
    _szBuffer[iLen] = 0;
    switch(_szBuffer[0]) {
    case 's':
      {
        // !!! FIXME: serialize this and byteswap it.  --ryan.
        #pragma pack(1)
        struct sIPPort {
          UBYTE bFirst;
          UBYTE bSecond;
          UBYTE bThird;
          UBYTE bFourth;
          USHORT iPort;
        };
        #pragma pack()

        _pNetwork->ga_strEnumerationStatus = "";

        sIPPort* pServers = (sIPPort*)(_szBuffer + 1);
        while(iLen - ((CHAR*)pServers - _szBuffer) >= sizeof(sIPPort)) {
          sIPPort ip = *pServers;

          CTString strIP;
          strIP.PrintF("%d.%d.%d.%d", ip.bFirst, ip.bSecond, ip.bThird, ip.bFourth);

          sockaddr_in sinServer;
          sinServer.sin_family = AF_INET;
          sinServer.sin_addr.s_addr = inet_addr(strIP);
          sinServer.sin_port = htons(ip.iPort + 1);

          // insert server status request into container
          CServerRequest &sreq = ga_asrRequests.Push();
          sreq.sr_ulAddress = sinServer.sin_addr.s_addr;
          sreq.sr_iPort = sinServer.sin_port;
          sreq.sr_tmRequestTime = _pTimer->GetHighPrecisionTimer().GetMilliseconds();

          // send packet to server
          _sendPacketTo("\x02", &sinServer);

          pServers++;
        }
      }
      break;

    case '0':
      {
        CTString strPlayers;
        CTString strMaxPlayers;
        CTString strLevel;
        CTString strGameType;
        CTString strVersion;
        CTString strGameName;
        CTString strSessionName;

        CHAR* pszPacket = _szBuffer + 2; // we do +2 because the first character is always ';', which we don't care about.

        BOOL bReadValue = FALSE;
        CTString strKey;
        CTString strValue;

        while(*pszPacket != 0) {
          switch(*pszPacket) {
          case ';':
            if(strKey != "sessionname") {
              if(bReadValue) {
                // we're done reading the value, check which key it was
                if(strKey == "players") {
                  strPlayers = strValue;
                } else if(strKey == "maxplayers") {
                  strMaxPlayers = strValue;
                } else if(strKey == "level") {
                  strLevel = strValue;
                } else if(strKey == "gametype") {
                  strGameType = strValue;
                } else if(strKey == "version") {
                  strVersion = strValue;
                } else if(strKey == "gamename") {
                  strGameName = strValue;
                } else {
                  CPrintF("Unknown GameAgent parameter key '%s'!", (const char *) strKey);
                }

                // reset temporary holders
                strKey = "";
                strValue = "";
              }
            }
            bReadValue = !bReadValue;
            break;

          default:
            // read into the value or into the key, depending where we are
            if(bReadValue) {
              strValue.InsertChar(strlen(strValue), *pszPacket);
            } else {
              strKey.InsertChar(strlen(strKey), *pszPacket);
            }
            break;
          }

          // move to next character
          pszPacket++;
        }

        // check if we still have a sessionname to back up
        if(strKey == "sessionname") {
          strSessionName = strValue;
        }

        // insert the server into the serverlist
        CNetworkSession &ns = *new CNetworkSession;
        _pNetwork->ga_lhEnumeratedSessions.AddTail(ns.ns_lnNode);

        long long tmPing = -1;
        // find the request in the request array
        for(INDEX i=0; i<ga_asrRequests.Count(); i++) {
          CServerRequest &req = ga_asrRequests[i];
          if(req.sr_ulAddress == _sinFrom.sin_addr.s_addr && req.sr_iPort == _sinFrom.sin_port) {
            tmPing = _pTimer->GetHighPrecisionTimer().GetMilliseconds() - req.sr_tmRequestTime;
            ga_asrRequests.Delete(&req);
            break;
          }
        }

        if(tmPing == -1) {
          // server status was never requested
          break;
        }

        // add the server to the serverlist
        ns.ns_strSession = strSessionName;
        ns.ns_strAddress = inet_ntoa(_sinFrom.sin_addr) + CTString(":") + CTString(0, "%d", htons(_sinFrom.sin_port) - 1);
        ns.ns_tmPing = (tmPing / 1000.0f);
        ns.ns_strWorld = strLevel;
        ns.ns_ctPlayers = atoi(strPlayers);
        ns.ns_ctMaxPlayers = atoi(strMaxPlayers);
        ns.ns_strGameType = strGameType;
        ns.ns_strMod = strGameName;
        ns.ns_strVer = strVersion;
      }
      break;

    default:
      CPrintF("Unknown enum packet ID %x!\n", _szBuffer[0]);
      break;
    }
  }
 } else {
 #ifndef PLATFORM_WIN32
 //STUBBED("write me");
 /* MSLegacy LinuxPort */
    if(_bActivated) {
        pthread_t  	_hThread;
        int   		_iThreadId;

        _iThreadId = pthread_create(&_hThread, NULL, _MS_Thread, NULL);
        if (_iThreadId == 0) {
            pthread_detach(_hThread);
        }
        _bActivated = FALSE;		
    }
    if(_bActivatedLocal) {
        pthread_t  _hThread;
        int   	   _iThreadId;

        _iThreadId = pthread_create(&_hThread, NULL, _LocalNet_Thread, NULL);
        if (_iThreadId == 0) {
            pthread_detach(_hThread);
        }
        _bActivatedLocal = FALSE;		
    }	 
 #else
 /* MSLegacy */
    if(_bActivated) {
        HANDLE  _hThread;
        DWORD   _dwThreadId;

        _hThread = CreateThread(NULL, 0, _MS_Thread, 0, 0, &_dwThreadId);
        if (_hThread != NULL) {
            CloseHandle(_hThread);
        }
        _bActivated = FALSE;		
    }
    if(_bActivatedLocal) {
        HANDLE  _hThread;
        DWORD   _dwThreadId;

        _hThread = CreateThread(NULL, 0, _LocalNet_Thread, 0, 0, &_dwThreadId);
        if (_hThread != NULL) {
            CloseHandle(_hThread);
        }
        _bActivatedLocal = FALSE;		
    }	
 #endif
 }
}

/// Cancel the GameAgent serverlist enumeration.
extern void GameAgent_EnumCancel(void)
{
  if (_bInitialized) {
    CPrintF("...GameAgent_EnumCancel!\n");
    ga_asrRequests.Clear();
    _uninitWinsock();
  }
}

#ifdef PLATFORM_WIN32
DWORD WINAPI _MS_Thread(LPVOID lpParam) {
#else
void*        _MS_Thread(void *arg) {
#endif

    SOCKET _sockudp = INVALID_SOCKET;
    struct _sIPPort {
        UBYTE bFirst;
        UBYTE bSecond;
        UBYTE bThird;
        UBYTE bFourth;
        USHORT iPort;
      };

    _setStatus("");
    _sockudp = socket(AF_INET, SOCK_DGRAM, 0);
    if (_sockudp == INVALID_SOCKET){
        WSACleanup();
        return 0;
    }

    _sIPPort* pServerIP = (_sIPPort*)(_szIPPortBuffer);
    while(_iIPPortBufferLen >= 6) {
        if(!strncmp((char *)pServerIP, "\\final\\", 7)) {
                break;
            }

        _sIPPort ip = *pServerIP;

        CTString strIP;
        strIP.PrintF("%d.%d.%d.%d", ip.bFirst, ip.bSecond, ip.bThird, ip.bFourth);

        sockaddr_in sinServer;
        sinServer.sin_family = AF_INET;
        sinServer.sin_addr.s_addr = inet_addr(strIP);
        sinServer.sin_port = ip.iPort;

        // insert server status request into container
        CServerRequest &sreq = ga_asrRequests.Push();
        sreq.sr_ulAddress = sinServer.sin_addr.s_addr;
        sreq.sr_iPort = sinServer.sin_port;
        sreq.sr_tmRequestTime = _pTimer->GetHighPrecisionTimer().GetMilliseconds();

        // send packet to server
        sendto(_sockudp,"\\status\\",8,0,
            (sockaddr *) &sinServer, sizeof(sinServer));

        sockaddr_in _sinClient;
        socklen_t _iClientLength = sizeof(_sinClient);

        fd_set readfds_udp;                         // declare a read set
        struct timeval timeout_udp;                 // declare a timeval for our timer
        int iRet = -1;

        FD_ZERO(&readfds_udp);                      // zero out the read set
        FD_SET(_sockudp, &readfds_udp);                // add socket to the read set
        timeout_udp.tv_sec = 0;                     // timeout = 0 seconds
        timeout_udp.tv_usec = 50000;               // timeout += 0.05 seconds
        int _iN = select(_sockudp + 1, &readfds_udp, NULL, NULL, &timeout_udp);
        if (_iN > 0) {
          /** do recvfrom stuff **/
          iRet =  recvfrom(_sockudp, _szBuffer, 2048, 0, (sockaddr*)&_sinClient, &_iClientLength);
          FD_CLR(_sockudp, &readfds_udp);
          if(iRet != -1 && iRet > 100 && iRet != SOCKET_ERROR) {
            // null terminate the buffer
            _szBuffer[iRet] = 0;
            char *sPch = NULL;
            sPch = strstr(_szBuffer, "\\gamename\\serioussamse\\");
            if(!sPch) {
                CPrintF("Unknown query server response!\n");
                return 0;
            } else {

                CTString strPlayers;
                CTString strMaxPlayers;
                CTString strLevel;
                CTString strGameType;
                CTString strVersion;
                CTString strGameName;
                CTString strSessionName;

                CTString strGamePort;
                CTString strServerLocation;
                CTString strGameMode;
                CTString strActiveMod;

                CHAR* pszPacket = _szBuffer + 1; // we do +1 because the first character is always '\', which we don't care about.

                BOOL bReadValue = FALSE;
                CTString strKey;
                CTString strValue;

                while(*pszPacket != 0) {
                switch(*pszPacket) {
                case '\\':
                    if(strKey != "gamemode") {
                      if(bReadValue) {
                        // we're done reading the value, check which key it was
                        if(strKey == "gamename") {
                            strGameName = strValue;
                        } else if(strKey == "gamever") {
                            strVersion = strValue;
                        } else if(strKey == "location") {
                            strServerLocation = strValue;
                        } else if(strKey == "hostname") {
                            strSessionName = strValue;
                        } else if(strKey == "hostport") {
                            strGamePort = strValue;
                        } else if(strKey == "mapname") {
                            strLevel = strValue;
                        } else if(strKey == "gametype") {
                            strGameType = strValue;
                        } else if(strKey == "activemod") {
                            strActiveMod = strValue;
                        } else if(strKey == "numplayers") {
                            strPlayers = strValue;
                        } else if(strKey == "maxplayers") {
                            strMaxPlayers = strValue;
                        } else {
                            //CPrintF("Unknown GameAgent parameter key '%s'!", strKey);
                        }
                        // reset temporary holders
                        strKey = "";
                        strValue = "";
                      }
                    }
                    bReadValue = !bReadValue;
                    break;

                default:
                    // read into the value or into the key, depending where we are
                    if(bReadValue) {
                        strValue.InsertChar(strlen(strValue), *pszPacket);
                    } else {
                        strKey.InsertChar(strlen(strKey), *pszPacket);
                    }
                    break;
                  }
                  // move to next character
                  pszPacket++;
                }

                // check if we still have a maxplayers to back up
                if(strKey == "gamemode") {
                    strGameMode = strValue;
                }
                if(strActiveMod != "") {
                    strGameName = strActiveMod;
                }
				
                long long tmPing = -1;
                // find the request in the request array
                for(INDEX i=0; i<ga_asrRequests.Count(); i++) {
                    CServerRequest &req = ga_asrRequests[i];
                    if(req.sr_ulAddress == _sinClient.sin_addr.s_addr && req.sr_iPort == _sinClient.sin_port) {
                        tmPing = _pTimer->GetHighPrecisionTimer().GetMilliseconds() - req.sr_tmRequestTime;
                        ga_asrRequests.Delete(&req);
                        break;
                    }
                }

                if(tmPing > 0 && tmPing < 2500000) {
				    // insert the server into the serverlist
                    CNetworkSession &ns = *new CNetworkSession;
                    _pNetwork->ga_lhEnumeratedSessions.AddTail(ns.ns_lnNode);
					
                    // add the server to the serverlist
                    ns.ns_strSession = strSessionName;
                    ns.ns_strAddress = inet_ntoa(_sinClient.sin_addr) + CTString(":") + CTString(0, "%d", htons(_sinClient.sin_port) - 1);
                    ns.ns_tmPing = (tmPing / 1000.0f);
                    ns.ns_strWorld = strLevel;
                    ns.ns_ctPlayers = atoi(strPlayers);
                    ns.ns_ctMaxPlayers = atoi(strMaxPlayers);
                    ns.ns_strGameType = strGameType;
                    ns.ns_strMod = strGameName;
                    ns.ns_strVer = strVersion;
                }
            }
          } else {
            // find the request in the request array
            for(INDEX i=0; i<ga_asrRequests.Count(); i++) {
              CServerRequest &req = ga_asrRequests[i];
              if(req.sr_ulAddress == _sinClient.sin_addr.s_addr && req.sr_iPort == _sinClient.sin_port) {
                ga_asrRequests.Delete(&req);
                break;
              }
            }
          }
        }
        pServerIP++;
        _iIPPortBufferLen -= 6;
    }
    if(_szIPPortBuffer) free (_szIPPortBuffer);
    _szIPPortBuffer = NULL;

    closesocket(_sockudp);
    _uninitWinsock();
    _bInitialized = FALSE;
    _pNetwork->ga_bEnumerationChange = FALSE;
    WSACleanup();
    return 0;
}

#ifdef PLATFORM_WIN32
DWORD WINAPI _LocalNet_Thread(LPVOID lpParam) {
#else
void*        _LocalNet_Thread(void *arg) {
#endif

    SOCKET _sockudp = INVALID_SOCKET;
    struct _sIPPort {
        UBYTE bFirst;
        UBYTE bSecond;
        UBYTE bThird;
        UBYTE bFourth;
        USHORT iPort;
      };

    _sockudp = socket(AF_INET, SOCK_DGRAM, 0);
    if (_sockudp == INVALID_SOCKET){
        WSACleanup();
        _pNetwork->ga_strEnumerationStatus = "";
		if(_szIPPortBufferLocal != NULL) {
			delete[] _szIPPortBufferLocal;
		}
		_szIPPortBufferLocal = NULL;		
		return 0;
    }

    _sIPPort* pServerIP = (_sIPPort*)(_szIPPortBufferLocal);
    while(_iIPPortBufferLocalLen >= 6) {
        if(!strncmp((char *)pServerIP, "\\final\\", 7)) {
                break;
            }

        _sIPPort ip = *pServerIP;

        CTString strIP;
        strIP.PrintF("%d.%d.%d.%d", ip.bFourth, ip.bThird, ip.bSecond, ip.bFirst);

        sockaddr_in sinServer;
        sinServer.sin_family = AF_INET;
        sinServer.sin_addr.s_addr = inet_addr(strIP);
        sinServer.sin_port = ip.iPort;

        // insert server status request into container
        CServerRequest &sreq = ga_asrRequests.Push();
        sreq.sr_ulAddress = sinServer.sin_addr.s_addr;
        sreq.sr_iPort = sinServer.sin_port;
        sreq.sr_tmRequestTime = _pTimer->GetHighPrecisionTimer().GetMilliseconds();

        // send packet to server
        sendto(_sockudp,"\\status\\",8,0,
            (sockaddr *) &sinServer, sizeof(sinServer));

        sockaddr_in _sinClient;
        socklen_t _iClientLength = sizeof(_sinClient);

        fd_set readfds_udp;                         // declare a read set
        struct timeval timeout_udp;                 // declare a timeval for our timer
        int iRet = -1;

        FD_ZERO(&readfds_udp);                      // zero out the read set
        FD_SET(_sockudp, &readfds_udp);             // add socket to the read set
        timeout_udp.tv_sec = 0;                     // timeout = 0 seconds
        timeout_udp.tv_usec = 50000;                // timeout += 0.05 seconds
        int _iN = select(_sockudp + 1, &readfds_udp, NULL, NULL, &timeout_udp);
        if (_iN > 0) {
          /** do recvfrom stuff **/
          iRet =  recvfrom(_sockudp, _szBuffer, 2048, 0, (sockaddr*)&_sinClient, &_iClientLength);
          FD_CLR(_sockudp, &readfds_udp);
          if(iRet != -1 && iRet > 100 && iRet != SOCKET_ERROR) {
            // null terminate the buffer
            _szBuffer[iRet] = 0;
            char *sPch = NULL;
            sPch = strstr(_szBuffer, "\\gamename\\serioussamse\\");
            if(!sPch) {
                CPrintF("Unknown query server response!\n");
				if(_szIPPortBufferLocal != NULL) {
					delete[] _szIPPortBufferLocal;
				}
				_szIPPortBufferLocal = NULL;               
				WSACleanup();
				return 0;
            } else {

                CTString strPlayers;
                CTString strMaxPlayers;
                CTString strLevel;
                CTString strGameType;
                CTString strVersion;
                CTString strGameName;
                CTString strSessionName;

                CTString strGamePort;
                CTString strServerLocation;
                CTString strGameMode;
                CTString strActiveMod;

                CHAR* pszPacket = _szBuffer + 1; // we do +1 because the first character is always '\', which we don't care about.

                BOOL bReadValue = FALSE;
                CTString strKey;
                CTString strValue;

                while(*pszPacket != 0) {
                switch(*pszPacket) {
                case '\\':
                    if(strKey != "gamemode") {
                      if(bReadValue) {
                        // we're done reading the value, check which key it was
                        if(strKey == "gamename") {
                            strGameName = strValue;
                        } else if(strKey == "gamever") {
                            strVersion = strValue;
                        } else if(strKey == "location") {
                            strServerLocation = strValue;
                        } else if(strKey == "hostname") {
                            strSessionName = strValue;
                        } else if(strKey == "hostport") {
                            strGamePort = strValue;
                        } else if(strKey == "mapname") {
                            strLevel = strValue;
                        } else if(strKey == "gametype") {
                            strGameType = strValue;
                        } else if(strKey == "activemod") {
                            strActiveMod = strValue;
                        } else if(strKey == "numplayers") {
                            strPlayers = strValue;
                        } else if(strKey == "maxplayers") {
                            strMaxPlayers = strValue;
                        } else {
							//CPrintF("Unknown GameAgent parameter key '%s'!", strKey);
                        }
                        // reset temporary holders
                        strKey = "";
                        strValue = "";
                      }
                    }
                    bReadValue = !bReadValue;
                    break;

                default:
                    // read into the value or into the key, depending where we are
                    if(bReadValue) {
                        strValue.InsertChar(strlen(strValue), *pszPacket);
                    } else {
                        strKey.InsertChar(strlen(strKey), *pszPacket);
                    }
                    break;
                  }
                  // move to next character
                  pszPacket++;
                }

                // check if we still have a maxplayers to back up
                if(strKey == "gamemode") {
                    strGameMode = strValue;
                }
                if(strActiveMod != "") {
                    strGameName = strActiveMod;
                }

                long long tmPing = -1;
                // find the request in the request array
                for(INDEX i=0; i<ga_asrRequests.Count(); i++) {
                    CServerRequest &req = ga_asrRequests[i];
                    if(req.sr_ulAddress == _sinClient.sin_addr.s_addr && req.sr_iPort == _sinClient.sin_port) {
                        tmPing = _pTimer->GetHighPrecisionTimer().GetMilliseconds() - req.sr_tmRequestTime;
                        ga_asrRequests.Delete(&req);
                        break;
                    }
                }

                if(tmPing > 0 && tmPing < 2500000) {
				    // insert the server into the serverlist
                    _pNetwork->ga_strEnumerationStatus = "";
					CNetworkSession &ns = *new CNetworkSession;
                    _pNetwork->ga_lhEnumeratedSessions.AddTail(ns.ns_lnNode);
					
                    // add the server to the serverlist
                    ns.ns_strSession = strSessionName;
                    ns.ns_strAddress = inet_ntoa(_sinClient.sin_addr) + CTString(":") + CTString(0, "%d", htons(_sinClient.sin_port) - 1);
                    ns.ns_tmPing = (tmPing / 1000.0f);
                    ns.ns_strWorld = strLevel;
                    ns.ns_ctPlayers = atoi(strPlayers);
                    ns.ns_ctMaxPlayers = atoi(strMaxPlayers);
                    ns.ns_strGameType = strGameType;
                    ns.ns_strMod = strGameName;
                    ns.ns_strVer = strVersion;
                }
            }
          } else {
            // find the request in the request array
            for(INDEX i=0; i<ga_asrRequests.Count(); i++) {
              CServerRequest &req = ga_asrRequests[i];
              if(req.sr_ulAddress == _sinClient.sin_addr.s_addr && req.sr_iPort == _sinClient.sin_port) {
                ga_asrRequests.Delete(&req);
                break;
              }
            }
          }
        }
        pServerIP++;
        _iIPPortBufferLocalLen -= 6;
    }
	if(_szIPPortBufferLocal != NULL) {
      delete[] _szIPPortBufferLocal;
    }
	_szIPPortBufferLocal = NULL;
	
    closesocket(_sockudp);
    _uninitWinsock();
    _bInitialized = FALSE;
    _pNetwork->ga_bEnumerationChange = FALSE;
	_pNetwork->ga_strEnumerationStatus = "";
    WSACleanup();
    return 0;
}