This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Commando/NAT.cpp

3690 lines
138 KiB
C++
Raw Permalink Normal View History

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** 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, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : Command & Conquer *
* *
* $Archive:: /Commando/Code/Commando/NAT.cpp $*
* *
* $Author:: Steve_t $*
* *
* $Modtime:: 3/26/02 3:04p $*
* *
* $Revision:: 37 $*
* *
* *
*---------------------------------------------------------------------------------------------*
* *
* *
*---------------------------------------------------------------------------------------------*
* *
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
** Disable warning about exception handling not being enabled.
*/
#pragma warning(disable : 4530)
#include "always.h"
#include <windows.h>
#include "systimer.h"
#include <snmp.h>
#include <stddef.h>
#include <process.h>
#include "crandom.h"
#include "except.h"
#include "nat.h"
#include "natsock.h"
#include "nataddr.h"
#include "slavemaster.h"
#include "gamedata.h"
/*
** Set this flag in the debugger to cause the client to fail to connect.
*/
//bool CrapFlag = false;
/*
** Single instance of firewall helper class.
*/
FirewallHelperClass FirewallHelper;
/***********************************************************************************************
* FirewallHelperClass::FirewallHelperClass -- Constructor *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/15/01 5:03PM ST : Created *
*=============================================================================================*/
FirewallHelperClass::FirewallHelperClass(void)
{
Behavior = FIREWALL_TYPE_UNKNOWN;
LastBehavior = FIREWALL_TYPE_UNKNOWN;
SourcePortAllocationDelta = 0;
LastSourcePortAllocationDelta = 0;
NumManglerServers = 0;
CurrentManglerServer = -1;
SourcePortPool = 0;
ThreadActive = false;
ThreadState = THREAD_IDLE;
ThreadEvent = INVALID_HANDLE_VALUE;
NATThreadMutex = CreateMutex(NULL, false, NULL);
NATDataMutex = CreateMutex(NULL, false, NULL);
ThreadHandle = INVALID_HANDLE_VALUE;
QueueNotifyPtr = NULL;
SuccessFlagPtr = NULL;
CancelPlayer[0] = 0;
SendDelay = false;
Confidence = 0;
}
/***********************************************************************************************
* FirewallHelperClass::Startup -- Start up the firewall thread *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/31/2001 12:35PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Startup(void)
{
/*
** Start the firewall thread.
*/
WWDEBUG_SAY(("FirewallHelper: Starting firewall thread\n"));
ThreadState = THREAD_IDLE;
ThreadEvent = INVALID_HANDLE_VALUE;
ThreadActive = true;
//ThreadHandle = CreateThread(NULL, 128*1024, &NAT_Thread_Start, this, 0, &ThreadID);
ThreadHandle = (HANDLE)_beginthreadex(NULL, 128*1024, &NAT_Thread_Start, this, 0, (unsigned int*)&ThreadID);
fw_assert(ThreadHandle != NULL);
}
/***********************************************************************************************
* FirewallHelperClass::Shutdown -- Shut down the firewall thread *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/31/2001 12:35PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Shutdown(void)
{
if (ThreadActive) {
WWDEBUG_SAY(("FirewallHelper: Stopping firewall thread\n"));
/*
** Signal the thread to go away.
*/
ThreadActive = false;
/*
** Wait for the thread to go away.
*/
int deadlock = WaitForSingleObject(NATThreadMutex, 5 * 1000);
if (deadlock == WAIT_TIMEOUT) {
WWDEBUG_SAY(("FirewallHelperClass - Timeout waiting for firewall thread mutex\n"));
fw_assert(deadlock != WAIT_TIMEOUT);
} else {
ReleaseMutex(NATThreadMutex);
}
}
// Release the thread
if (INVALID_HANDLE_VALUE != ThreadHandle) {
CloseHandle(ThreadHandle);
ThreadHandle = INVALID_HANDLE_VALUE;
}
}
/***********************************************************************************************
* FirewallHelperClass::Set_Firewall_Info -- Set firewall info from the game *
* *
* *
* *
* INPUT: last_behavior - how the firewall behaved last time we tried it *
* last_delta - the firewalls last known good port delta *
* port_pool - base port number to use when allocating temporary ports *
* send_delay - value of send delay flag used for netgear firewalls. *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/20/2001 12:16PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Set_Firewall_Info(unsigned long last_behavior, int last_delta, unsigned short port_pool, bool send_delay, int confidence)
{
if (port_pool) {
SourcePortPool = port_pool;
} else {
if (SourcePortPool == 0) {
SourcePortPool = PORT_POOL_MIN + FreeRandom.Get_Int(0, 32768);
}
}
LastBehavior = (FirewallBehaviorType) last_behavior;
LastSourcePortAllocationDelta = last_delta;
SendDelay = send_delay;
Confidence = confidence;
}
/***********************************************************************************************
* FirewallHelperClass::Set_Firewall_Info -- Set firewall info from the game *
* *
* *
* *
* INPUT: last_behavior - how the firewall behaved last time we tried it *
* last_delta - the firewalls last known good port delta *
* port_pool - base port number to use when allocating temporary ports *
* send_delay - value of send delay flag used for netgear firewalls. *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/20/2001 12:16PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Get_Firewall_Info(unsigned long &last_behavior, int &last_delta, unsigned short &port_pool, bool &send_delay, int &confidence) const
{
port_pool = SourcePortPool;
last_behavior = (unsigned long) LastBehavior;
last_delta = LastSourcePortAllocationDelta;
send_delay = SendDelay;
confidence = Confidence;
}
/***********************************************************************************************
* *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/7/2001 2:15PM ST : Created *
*=============================================================================================*/
FirewallHelperClass::~FirewallHelperClass(void)
{
Shutdown();
CloseHandle(NATThreadMutex);
CloseHandle(NATDataMutex);
}
/***********************************************************************************************
* FirewallHelperClass::NAT_Thread_Start -- Thread start for NAT thread *
* *
* *
* *
* INPUT: This ptr *
* *
* OUTPUT: Undefined *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/7/2001 2:39PM ST : Created *
*=============================================================================================*/
unsigned int __stdcall FirewallHelperClass::NAT_Thread_Start(void *thisptr)
{
unsigned int thread_exit_code = 0;
Register_Thread_ID(GetCurrentThreadId(), "Firewall thread");
__try {
thread_exit_code = ((FirewallHelperClass*)thisptr)->NAT_Thread_Main_Loop();
} __except(Exception_Handler(GetExceptionCode(), GetExceptionInformation())) {};
Unregister_Thread_ID(GetCurrentThreadId(), "Firewall thread");
return(thread_exit_code);
}
/***********************************************************************************************
* FirewallHelperClass::NAT_Thread_Main_Loop -- Main loop for NAT thread *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/7/2001 2:42PM ST : Created *
*=============================================================================================*/
unsigned long FirewallHelperClass::NAT_Thread_Main_Loop(void)
{
/*
** Take ownership of the thread mutex.
*/
int deadlock = WaitForSingleObject(NATThreadMutex, 10 * 1000);
if (deadlock == WAIT_TIMEOUT) {
WWDEBUG_SAY(("FirewallHelperClass - Timeout waiting for thread mutex\n"));
fw_assert(deadlock != WAIT_TIMEOUT);
}
/*
** Thread main loop.
*/
while (ThreadActive) {
/*
** Always do some sleeping here since this thread is a relatively low priority compared to stuff like running the
** game engine.
** If we are not doing anything much then sleep for longer.
*/
if (ThreadState == THREAD_IDLE && ThreadQueue.Count() == 0) {
Sleep(50);
} else {
Sleep(0);
}
/*
** Check for incoming private game options packets.
*/
Process_Game_Options();
/*
** Lock data access while we process each loop.
*/
ThreadLockClass locker(this);
switch (ThreadState) {
/*
** Idle state, no activity required.
*/
case THREAD_IDLE:
/*
** See if there is a pending thread action to execute.
*/
if (ThreadQueue.Count()) {
ThreadState = ThreadQueue[0].ThreadAction;
ThreadEvent = ThreadQueue[0].ThreadEvent;
//delete ThreadQueue[0];
ThreadQueue.Delete(0);
} else {
if (WOLNATInterface.Am_I_Server()) {
if (ClientQueue.Count()) {
WWDEBUG_SAY(("Client with no thread action!\n"));
Add_Thread_Action(THREAD_CONNECT_FIREWALL, INVALID_HANDLE_VALUE);
}
}
}
break;
/*
** Time to connect to another player.
*/
case THREAD_CONNECT_FIREWALL:
{
int connected = Negotiate_Port();
if (connected == FW_RESULT_SUCCEEDED && !WOLNATInterface.Am_I_Server()) {
WOLNATInterface.Set_Server_Negotiated_Address(&PlayersFirewallAddress);
}
Set_Client_Success(connected);
ThreadState = THREAD_CONNECT_FIREWALL_DONE;
break;
}
/*
** Connection done, signal as required.
*/
case THREAD_CONNECT_FIREWALL_DONE:
Set_Thread_Event();
Send_Queue_States();
ThreadState = THREAD_IDLE;
break;
/*
** Detect the firewall settings.
*/
case THREAD_DETECT_FIREWALL:
Behavior = Detect_Firewall_Behavior();
WWDEBUG_SAY(("FirewallHelper: Behavior is = %08x\n", (unsigned long)Behavior));
ThreadState = THREAD_DETECT_FIREWALL_DONE;
Set_Thread_Event();
break;
/*
** Detection done, signal as required.
*/
case THREAD_DETECT_FIREWALL_DONE:
Set_Thread_Event();
ThreadState = THREAD_IDLE;
break;
/*
** Get the local chat connection address.
*/
case THREAD_GET_LOCAL_ADDRESS:
Get_Local_Chat_Connection_Address();
ThreadState = THREAD_IDLE;
break;
/*
** We finished getting the local chat connection address.
*/
case THREAD_GET_LOCAL_ADDRESS_DONE:
ThreadState = THREAD_IDLE;
Set_Thread_Event();
break;
default:
fw_assert(false);
break;
}
}
ReleaseMutex(NATThreadMutex);
return(0);
}
/***********************************************************************************************
* FirewallHelperClass::Reset -- Causes a re-detect of the firewall *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/29/01 1:04AM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Reset(void)
{
ThreadLockClass locker(this);
Behavior = FIREWALL_TYPE_UNKNOWN;
}
/***********************************************************************************************
* FirewallHelperClass::Reset_Server -- Reset server side variables *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/10/2001 3:23PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Reset_Server(void)
{
ConnectionHistory.Delete_All();
MangledPortHistory.Delete_All();
}
/***********************************************************************************************
* FirewallHelperClass::Detect_Firewall -- See what our firewall is up to *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/15/01 6:47PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Detect_Firewall(HANDLE event)
{
ThreadLockClass locker(this);
if (Behavior == FIREWALL_TYPE_UNKNOWN) {
Add_Thread_Action(THREAD_DETECT_FIREWALL, event);
} else {
SetEvent(event);
}
}
/***********************************************************************************************
* FirewallHelperClass::Connected_To_WWOnline_Server *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/7/2001 10:02PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Connected_To_WWOnline_Server(void)
{
/*
** Don't need to go through this again if we already got the local chat connection address.
*/
if (!LocalChatConnectionAddress.Is_Valid()) {
/*
** Get the local chat connection address.
*/
ThreadLockClass locker(this);
Add_Thread_Action(THREAD_GET_LOCAL_ADDRESS, INVALID_HANDLE_VALUE);
}
}
/***********************************************************************************************
* FHC::Get_Next_Temporary_Source_Port -- Get a throwaway source port for temporary use *
* *
* *
* *
* INPUT: number of ports in sequence to skip *
* *
* OUTPUT: port number *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/15/01 12:06PM ST : Created *
*=============================================================================================*/
unsigned short FirewallHelperClass::Get_Next_Temporary_Source_Port(int skip)
{
unsigned short return_port = (unsigned short) SourcePortPool;
/*
** Try max 256 ports until we find one we can bind to a socket.
*/
int tries = 256;
if (skip == 0) {
skip = 1;
}
while (tries--) {
SourcePortPool += skip;
return_port = (unsigned short) SourcePortPool;
if (SourcePortPool > PORT_POOL_MAX) {
SourcePortPool = PORT_POOL_MIN;
}
/*
** Validate the port by trying to bind it to a socket.
*/
SocketHandlerClass *temp = new SocketHandlerClass;
bool result = temp->Open(return_port, 0);
delete temp;
if (result) {
return(return_port);
}
}
return(return_port);
}
/***********************************************************************************************
* FHC::Send_To_Mangler -- Send to the mangler from the specified socket handler *
* *
* *
* *
* INPUT: Address of mangler server *
* Socket handler to use when sending *
* ID to insert into the packet *
* blitzme flag (obsolete, must be false) *
* *
* OUTPUT: True if sent OK *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/15/01 12:47PM ST : Created *
*=============================================================================================*/
bool FirewallHelperClass::Send_To_Mangler(IPAddressClass *address, SocketHandlerClass *socket_handler, unsigned long packet_id, bool blitzme)
{
/*
** Build the packet to send out.
*/
unsigned char packet_buf[512];
int packet_size = Build_Mangler_Packet(packet_buf, socket_handler->Get_Incoming_Port(), packet_id, blitzme);
WWDEBUG_SAY(("FirewallHelper: Sending from port %d to %s\n", (unsigned int)(socket_handler->Get_Incoming_Port()), address->As_String()));
/*
** Send it.
*/
socket_handler->Write(packet_buf, packet_size, address, address->Get_Port());
return(true);
}
/***********************************************************************************************
* FHC::Get_Mangler_Response -- Get the manglers response to a specific query *
* *
* *
* *
* INPUT: Packet id of packet we are looking for *
* Ptr to socket handler class to query for packets *
* Timeout, 0=default *
* service_all - true if all sockets should be serviced *
* *
* OUTPUT: Port the mangler saw this packet come from. *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/15/01 12:51PM ST : Created *
*=============================================================================================*/
unsigned short FirewallHelperClass::Get_Mangler_Response(unsigned long packet_id, SocketHandlerClass *socket_handler, int time, bool all_service)
{
/*
** Locals.
*/
int peek_packet = 0;
CnCPacketType *packet;
unsigned char packet_buf[1024];
int packet_size = sizeof(packet_buf);
IPAddressClass address;
unsigned long id;
unsigned long timeout = TIMEGETTIME();
unsigned char temp_address[4];
unsigned short temp_port;
/*
** Asserts.
*/
fw_assert(socket_handler != NULL);
if (time) {
timeout += (unsigned long) time;
} else {
timeout += (unsigned long) TIMER_SECOND;
}
while (timeout > TIMEGETTIME()) {
if (all_service) {
SocketHandlerClass::Service_All();
} else {
socket_handler->Service();
}
/*
** Get a copy of the global packet we want to examine. This doesn't remove it from the queue.
*/
int result = socket_handler->Peek(packet_buf, packet_size, &temp_address, &temp_port, peek_packet);
/*
** Check if we got a packet from the mangler.
*/
if (result) {
packet = (CnCPacketType*)packet_buf;
if (packet->Packet.Command == NET_MANGLER_RESPONSE) {
WWDEBUG_SAY(("FirewallHelper: Got NET_MANGLER_RESPONSE packet\n"));
/*
** We got a mangler packet, see if it's the one we are looking for.
*/
#ifdef WWDEBUG
int original_port = packet->Packet.ManglerData.OriginalPortNumber;
#endif //WWDEBUG
id = packet->GHeader.Header.PacketID;
if (packet_id == id) {
/*
** Looks good, lets get the mangled port number out.
*/
unsigned short mangled_port = packet->Packet.ManglerData.MangledPortNumber;
WWDEBUG_SAY(("FirewallHelper: Mangler is seeing packets from port %d as coming from port %d\n", (unsigned int)original_port, (unsigned int)mangled_port));
/*
** Now the mangled address. This should never change.
*/
ExternalAddress.Set_Address(packet->Packet.ManglerData.MangledAddress);
WWDEBUG_SAY(("FirewallHelper: Mangler is seeing our packets coming from address %s\n", ExternalAddress.As_String()));
/*
** Remove the packet from the queue.
*/
result = socket_handler->Read(packet_buf, packet_size, &temp_address, &temp_port, peek_packet);
fw_assert(result != NULL);
return(mangled_port);
}
}
peek_packet++;
} else {
peek_packet = 0;
}
Sleep(0);
}
return(0);
}
/***********************************************************************************************
* FHC::Detect_Firewall_Behavior -- What is that wacky firewall doing to our packet headers? *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Firewall behavior *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/15/01 12:30PM ST : Created *
*=============================================================================================*/
FirewallHelperClass::FirewallBehaviorType FirewallHelperClass::Detect_Firewall_Behavior(void)
{
unsigned short mangler_port = 4321;
char temp_mangler_name[128];
unsigned long packet_id = 0x7f000000;
/*
** Well, we are going to need some manglers.
*/
static char _manglername[5][80] = {
"mangler1.westwood.com",
"mangler2.westwood.com",
"mangler3.westwood.com",
"mangler.westwood.com",
""
};
unsigned char mangler_addresses[4][4];
int num_mangler_addresses = 0;
FirewallBehaviorType behavior = FIREWALL_TYPE_SIMPLE;
IPAddressClass manglers[4];
unsigned long timeout;
int mangler_index_offset = 0;
unsigned short source_ports[NUM_TEST_PORTS];
unsigned short mangled_ports[NUM_TEST_PORTS];
SocketHandlerClass *port_sockets[NUM_TEST_PORTS];
int delta = 0;
bool save_firewall_type = true;
bool looks_good = true;
/*
** Figure out which mangler to use this time. Pick randomly from the available manglers.
*/
NumManglerServers = WOLNATInterface.Get_Num_Mangler_Servers();
if (CurrentManglerServer == -1) {
CurrentManglerServer = FreeRandom.Get_Int(0, NumManglerServers-1);
WWDEBUG_SAY(("FirewallHelper - Using mangler server %d\n", CurrentManglerServer));
}
/*
** If the user specified a particular port to use then we act as if there is no firewall.
*/
if (WOLNATInterface.Get_Force_Port()) {
behavior = FIREWALL_TYPE_SIMPLE;
WWDEBUG_SAY(("FirewallHelper: Source port %d specified by user\n", WOLNATInterface.Get_Force_Port()));
if (SendDelay) {
unsigned long addbehavior = FIREWALL_TYPE_NETGEAR_BUG;
addbehavior |= (unsigned long)behavior;
behavior = (FirewallBehaviorType) addbehavior;
WWDEBUG_SAY(("FirewallHelper: Netgear bug specified by command line or SendDelay flag\n"));
}
return(behavior);
}
/*
** Slave servers inherit their firewall info from the master server.
*/
if (SlaveMaster.Am_I_Slave()) {
behavior = LastBehavior;
SourcePortAllocationDelta = LastSourcePortAllocationDelta;
ExternalAddress.Set_Address(WOLNATInterface.Get_Reg_External_IP(), WOLNATInterface.Get_Reg_External_Port());
return(behavior);
}
timeout = TIMEGETTIME() + (5*TIMER_SECOND);
WWDEBUG_SAY(("FirewallHelper: About to call gethostbyname for the mangler address\n"));
int namenum = 0;
/*
** Convert the list of mangler servers to numeric IP addresses.
**
*/
do {
char *mangler_name_ptr = &_manglername[namenum][0];
/*
** Use the wolapi supplied mangler info if available.
*/
bool got_name = WOLNATInterface.Get_Mangler_Name_By_Index(namenum, temp_mangler_name);
if (got_name) {
mangler_name_ptr = temp_mangler_name;
unsigned short servserv_port = WOLNATInterface.Get_Mangler_Port_By_Index(namenum);
fw_assert(servserv_port != 0);
if (servserv_port) {
mangler_port = servserv_port;
}
WWDEBUG_SAY(("FirewallHelper - Using mangler server from servserv - address is %s ; %d\n", mangler_name_ptr, mangler_port));
} else {
WWDEBUG_SAY(("FirewallHelper - Using default mangler name for mangler %d\n", namenum));
}
namenum++;
if (strlen(mangler_name_ptr) == 0) {
break;
}
/*
** Do the lookup.
*/
char temp_name[256];
strcpy(temp_name, mangler_name_ptr);
struct hostent *host_info = gethostbyname(temp_name);
if (!host_info) {
WWDEBUG_SAY(("FirewallHelper: gethostbyname failed! Error code %d\n", WSAGetLastError()));
break;
}
/*
** See if we already have that address in the list.
*/
bool found = false;
for (int i=0 ; i<num_mangler_addresses ; i++) {
if (memcmp(mangler_addresses[i], &host_info->h_addr_list[0][0], 4) == 0) {
found = true;
break;
}
}
/*
** Add the address in if we didn't find it.
*/
if (!found) {
int m = num_mangler_addresses++;
memcpy(&mangler_addresses[m][0], &host_info->h_addr_list[0][0], 4);
WWDEBUG_SAY(("FirewallHelper: Found mangler address at %d.%d.%d.%d\n", mangler_addresses[m][0], mangler_addresses[m][1], mangler_addresses[m][2], mangler_addresses[m][3]));
}
} while (num_mangler_addresses < 4 && TIMEGETTIME() < timeout);
/*
** We need a certain number of manglers to be able to do the detection.
*/
fw_assert(num_mangler_addresses > 2);
if (num_mangler_addresses < 3) {
WWDEBUG_SAY(("FirewallHelper: Not enough manglers - returning default behavior\n"));
if (Confidence > 0) {
behavior = LastBehavior;
SourcePortAllocationDelta = LastSourcePortAllocationDelta;
}
return(behavior);
}
/*
** Convert the mangler addresses to IPAddressClass format.
*/
for (int i=0 ; i<num_mangler_addresses ; i++) {
unsigned char addr[4];
memcpy(addr, &mangler_addresses[i][0], 4);
manglers[i].Set_Address(addr, mangler_port);
}
/*
** OK, we have our manglers.
**
** First test, see if there is any port mangling at all.
**
**
**
*/
/*
** Get a spare port number and create a new socket to bind it to.
*/
unsigned short port = Get_Next_Temporary_Source_Port(0);
SocketHandlerClass socket;
if (!socket.Open(port, 4321)) {
WWDEBUG_SAY(("FirewallHelper: Unable to open temp source port - returning default behavior\n"));
return(behavior);
}
/*
** Send to the mangler from this port until we get a response.
*/
timeout = TIMEGETTIME() + TIMER_SECOND * 6;
unsigned short mangled_port = 0;
while (TIMEGETTIME() < timeout && mangled_port == 0) {
Send_To_Mangler(&manglers[0], &socket, packet_id);
mangled_port = Get_Mangler_Response(packet_id, &socket);
}
if (mangled_port == 0) {
/*
** No response from mangler 0. That's bad. I guess we could try the other manglers.
** We already tried the first one so skip that. Don't use the last one either since that will be needed later to detect
** port per ip behavior.
*/
for (int m=1 ; m<num_mangler_addresses - 1 ; m++) {
mangler_index_offset = m;
timeout = TIMEGETTIME() + TIMER_SECOND * 3;
while (TIMEGETTIME() < timeout && mangled_port == 0) {
Send_To_Mangler(&manglers[mangler_index_offset], &socket, packet_id);
mangled_port = Get_Mangler_Response(packet_id, &socket);
}
}
}
/*
** See if we got no response or a non-mangled response.
*/
if (mangled_port == 0 || mangled_port == port) {
if (mangled_port == port) {
SourcePortAllocationDelta = 0;
LastSourcePortAllocationDelta = 0;
LastBehavior = FIREWALL_TYPE_SIMPLE;
Confidence = 0;
} else {
WWASSERT(mangled_port == 0);
if (Confidence) {
SourcePortAllocationDelta = LastSourcePortAllocationDelta;
return(LastBehavior);
}
}
return(FIREWALL_TYPE_SIMPLE);
}
/*
**
** Second test. See if the ports are mangled differently for different destination IPs.
**
** We can use the spare socket from the last test and send to a different mangler.
**
*/
/*
** Send to the mangler from this port until we get a response.
*/
timeout = TIMEGETTIME() + (TIMER_SECOND * 6);
unsigned short second_ip_mangled_port = 0;
while (TIMEGETTIME() < timeout && second_ip_mangled_port == 0) {
Send_To_Mangler(&manglers[mangler_index_offset + 1], &socket, packet_id+1);
second_ip_mangled_port = Get_Mangler_Response(packet_id+1, &socket);
};
/*
** We are done with this socket/port
*/
socket.Close();
/*
** See if we got no response or a non-mangled response.
*/
if (second_ip_mangled_port == 0 || second_ip_mangled_port == port) {
WWDEBUG_SAY(("FirewallHelper: No response or response is unmangled - returning simple firewall behavior\n"));
return(FIREWALL_TYPE_SIMPLE);
}
/*
** Got a mangled port. Is it the same as the last one (mangles the same regardless of destination IP).
*/
if (mangled_port == second_ip_mangled_port) {
behavior = FIREWALL_TYPE_DUMB_MANGLING;
WWDEBUG_SAY(("FirewallHelper: Source ports mangled the same way regardless of destination IP\n"));
} else {
behavior = FIREWALL_TYPE_SMART_MANGLING;
WWDEBUG_SAY(("FirewallHelper: Source ports mangled differently per destination IP\n"));
}
/*
** Third test.
**
** This test tries to detect a pattern in the ports allocated by the NAT.
** We use several source ports for this one.
**
*/
/*
** Try this whole thing a max of 3 times.
*/
int try_again;
for (try_again = 0 ; try_again < 3 ; try_again++) {
memset(source_ports, 0, sizeof(source_ports));
memset(mangled_ports, 0, sizeof(source_ports));
memset(port_sockets, 0, sizeof(port_sockets));
/*
** Open a socket for each source port.
** We should use a non-linear set of source ports so we can detect the NAT32 relative offset
** case.
*/
bool port_open_failed = false;
for (i=0 ; i<NUM_TEST_PORTS ; i++) {
source_ports[i] = Get_Next_Temporary_Source_Port(i);
port_sockets[i] = new SocketHandlerClass;
if (!port_sockets[i]->Open(source_ports[i], 4321)) {
port_open_failed = true;
/*
** Close any spare ports we allocated already before we bail.
*/
for (int j=0 ; j<i ; j++) {
if (port_sockets[j]) {
delete port_sockets[j];
port_sockets[j] = NULL;
}
}
WWDEBUG_SAY(("FirewallHelper: Unable to open all source ports for allocation pattern test - returning default behavior\n"));
break; //return(behavior);
}
}
if (port_open_failed) {
continue;
}
/*
** OK, lets get some numbers from the mangler.
**
** Keep sending from each port until we get a response to all the sends. There's a implied
** delay between initial sends due to the timeout in Get_Mangler_Response.
*/
int num_responses = 0;
packet_id = packet_id + 10;
timeout = TIMEGETTIME() + (TIMER_SECOND * 12);
while (TIMEGETTIME() < timeout && num_responses < NUM_TEST_PORTS) {
for (i=0 ; i<NUM_TEST_PORTS ; i++) {
if (mangled_ports[i] == 0) {
Send_To_Mangler(&manglers[mangler_index_offset], port_sockets[i], packet_id+i);
mangled_ports[i] = Get_Mangler_Response(packet_id+i, port_sockets[i], 0, true);
if (mangled_ports[i] != 0) {
num_responses++;
}
}
}
}
/*
** Close down those sockets - we are finished with them.
*/
for (int j=0 ; j<i ; j++) {
if (port_sockets[j]) {
delete port_sockets[j];
port_sockets[j] = NULL;
}
}
/*
** We need at least 4 responses to be sure of the port allocation scheme.
*/
if (num_responses < 4) {
if (LastSourcePortAllocationDelta != 0 && (int)LastBehavior > (int)FIREWALL_TYPE_SIMPLE) {
/*
** If the delta we got last time we played looks good then use that.
*/
SourcePortAllocationDelta = LastSourcePortAllocationDelta;
WWDEBUG_SAY(("FirewallHelper: Unable to verify responses from 4 mangler servers - returning default behavior\n"));
return(behavior);
}
/*
** Try again.
*/
continue;
}
bool relative_delta = false;
delta = Get_NAT_Port_Allocation_Scheme(num_responses, source_ports, mangled_ports, relative_delta, looks_good);
if (delta) {
/*
** Hey, we got it!
*/
unsigned long addbehavior = 0;
if (relative_delta) {
addbehavior = (unsigned long)FIREWALL_TYPE_RELATIVE_PORT_ALLOCATION;
} else {
addbehavior = (unsigned long)FIREWALL_TYPE_SIMPLE_PORT_ALLOCATION;
}
addbehavior |= (unsigned long)behavior;
behavior = (FirewallBehaviorType) addbehavior;
SourcePortAllocationDelta = delta;
if (!looks_good) {
save_firewall_type = false;
}
break;
} else {
if (LastSourcePortAllocationDelta != 0 && (int)LastBehavior > (int)FIREWALL_TYPE_SIMPLE) {
/*
** If the delta we got last time we played looks good then use that.
*/
SourcePortAllocationDelta = LastSourcePortAllocationDelta;
behavior = LastBehavior;
save_firewall_type = false;
break;
}
}
}
/*
** See if we screwed up.
*/
if (try_again == 3) {
/*
** We know it mangles differently per IP but we don't know how. Make something up.
*/
SourcePortAllocationDelta = 1;
save_firewall_type = false;
}
/*
** Test to see if the NAT mangles differently per destination port at the same IP.
**
**
*/
if ((behavior & FIREWALL_TYPE_SMART_MANGLING) != 0) {
if ((behavior & FIREWALL_TYPE_SIMPLE_PORT_ALLOCATION) != 0) {
WWDEBUG_SAY(("FirewallHelper: Testing to see if the NAT mangles differently per destination port at the same IP\n"));
/*
** We need 2 source ports for this.
*/
unsigned short port1 = Get_Next_Temporary_Source_Port(0);
SocketHandlerClass socket1;
if (!socket1.Open(port1, 4321)) {
return(behavior);
}
unsigned short port2 = Get_Next_Temporary_Source_Port(0);
SocketHandlerClass socket2;
if (!socket2.Open(port2, 4321)) {
return(behavior);
}
/*
** Get a reference port.
*/
timeout = TIMEGETTIME() + (TIMER_SECOND * 4);
mangled_port = 0;
packet_id = packet_id + 10;
/*
** Wait for a response.
*/
while (TIMEGETTIME() < timeout && mangled_port == 0) {
Send_To_Mangler(&manglers[mangler_index_offset], &socket1, packet_id);
mangled_port = Get_Mangler_Response(packet_id, &socket1);
}
if (mangled_port == 0) {
return(behavior);
}
/*
** Send out to a different port at that IP.
** We won't get a response for this.
*/
IPAddressClass addr = manglers[mangler_index_offset];
addr.Set_Port(addr.Get_Port() + 1);
Send_To_Mangler(&addr, &socket1, packet_id);
Send_To_Mangler(&addr, &socket1, packet_id);
Send_To_Mangler(&addr, &socket1, packet_id);
/*
** We can't get a response from a different destination port so the only way to detect
** this behavior is to check the next mangled port allocation to see if it's double
** what we would normally expect.
*/
packet_id++;
unsigned short new_mangled_port = 0;
timeout = TIMEGETTIME() + (TIMER_SECOND * 4);
while (TIMEGETTIME() < timeout && new_mangled_port == 0) {
Send_To_Mangler(&manglers[mangler_index_offset], &socket2, packet_id);
new_mangled_port = Get_Mangler_Response(packet_id, &socket2);
}
if (new_mangled_port != 0) {
if (new_mangled_port != mangled_port + SourcePortAllocationDelta) {
WWDEBUG_SAY(("FirewallHelper: NAT uses different source ports for different destination ports\n"));
unsigned long addbehavior = 0;
addbehavior = (unsigned long)FIREWALL_TYPE_DESTINATION_PORT_DELTA;
addbehavior |= (unsigned long)behavior;
behavior = (FirewallBehaviorType) addbehavior;
} else {
fw_assert(new_mangled_port == mangled_port + SourcePortAllocationDelta);
if (new_mangled_port == mangled_port + SourcePortAllocationDelta) {
WWDEBUG_SAY(("FirewallHelper: NAT uses the same source port for different destination ports\n"));
} else {
WWDEBUG_SAY(("FirewallHelper: Unable to complete destination port mangling test\n"));
fw_assert(false);
}
}
}
} else {
/*
** NAT32 uses different mangled source ports for different destination ports.
*/
unsigned long addbehavior = 0;
addbehavior = (unsigned long)FIREWALL_TYPE_DESTINATION_PORT_DELTA;
addbehavior |= (unsigned long)behavior;
behavior = (FirewallBehaviorType) addbehavior;
}
}
/*
** See if the user specified a netgear firewall - that will save us the trouble of detecting it.
*/
if (SendDelay) {
unsigned long addbehavior = FIREWALL_TYPE_NETGEAR_BUG;
addbehavior |= (unsigned long)behavior;
behavior = (FirewallBehaviorType) addbehavior;
WWDEBUG_SAY(("FirewallHelper: Netgear bug specified by command line or SendDelay flag\n"));
}
/*
** Remember this firewall type for next time.
*/
if (save_firewall_type) {
LastBehavior = behavior;
LastSourcePortAllocationDelta = SourcePortAllocationDelta;
Confidence = 0;
} else {
/*
** If we have high confidence in the last settings we detected then we might want to use those in preference.
*/
if (LastBehavior == behavior && LastSourcePortAllocationDelta != SourcePortAllocationDelta) {
if (Confidence > 0) {
/*
** Only do this max 3 times before accepting the new delta.
*/
Confidence = (Confidence / 3) - 1;
SourcePortAllocationDelta = LastSourcePortAllocationDelta;
}
}
}
return(behavior);
}
/***********************************************************************************************
* FHC::Get_NAT_Port_Allocation_Scheme -- Find out how a NAT is allocating ports *
* *
* *
* *
* INPUT: Number of ports we should analyze *
* List of original port numbers *
* List of mangled port numbers *
* relative_delta (out) Is the delta relative to the original port number? *
* looks_good (out) Do all the values point to the same delta? *
* *
* OUTPUT: Port allocation delta *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/15/01 4:45PM ST : Created *
*=============================================================================================*/
int FirewallHelperClass::Get_NAT_Port_Allocation_Scheme(int num_ports, unsigned short *original_ports, unsigned short *mangled_ports, bool &relative_delta, bool &looks_good)
{
fw_assert(num_ports > 3);
WWDEBUG_SAY(("FirewallHelper: Looking for port allocation pattern in original_ports %d, %d, %d, %d\n", original_ports[0], original_ports[1], original_ports[2], original_ports[3]));
/*
** Sort the mangled ports into order - should be easier to detect patterns.
** Stupid bubble sort will do. original_ports may be out of oder after the sort.
*/
for (int x=0 ; x<num_ports ; x++) {
for (int y=0 ; y<num_ports-1 ; y++) {
if (mangled_ports[y] > mangled_ports[y+1]) {
int temp = mangled_ports[y];
mangled_ports[y] = mangled_ports[y+1];
mangled_ports[y+1] = temp;
temp = original_ports[y];
original_ports[y] = original_ports[y+1];
original_ports[y+1] = temp;
}
}
}
/*
** Now start looking for patterns in the port numbers. Possible patterns include.
**
** Incremental. Port numbers are allocated incrementally.
** Every 'n' ports. NAT adds 'n' port numbers when allocating ports.
**
** Also, schemes may be absolute or relative to the original port number.
*/
/*
** 1. Check for absolute sequential allocation.
*/
if (mangled_ports[1] - mangled_ports[0] == 1) {
if (mangled_ports[2] - mangled_ports[1] == 1) {
if (mangled_ports[3] - mangled_ports[2] == 1) {
WWDEBUG_SAY(("FirewallHelper: Incremental port allocation detected\n"));
relative_delta = false;
looks_good = true;
return(1);
}
}
}
/*
** 2. Check for semi sequential.
*/
if (mangled_ports[1] - mangled_ports[0] == 2) {
if (mangled_ports[2] - mangled_ports[1] == 2) {
if (mangled_ports[3] - mangled_ports[2] == 2) {
WWDEBUG_SAY(("FirewallHelper: Semi-incremental port allocation detected\n"));
relative_delta = false;
looks_good = true;
return(2);
}
}
}
int diff1 = mangled_ports[1] - mangled_ports[0];
int diff2 = mangled_ports[2] - mangled_ports[1];
int diff3 = mangled_ports[3] - mangled_ports[2];
/*
** 3. Check for absolute scheme skipping 'n' ports.
*/
if (diff1 == diff2 && diff2 == diff3) {
WWDEBUG_SAY(("FirewallHelper: Looks good for absolute allocation sequence delta of %d\n", diff1));
relative_delta = false;
looks_good = true;
return(diff1);
}
if (diff1 == diff2) {
WWDEBUG_SAY(("FirewallHelper: Probable absolute allocation sequence delta of %d\n", diff1));
relative_delta = false;
looks_good = false;
return(diff1);
}
if (diff2 == diff3) {
WWDEBUG_SAY(("FirewallHelper: Probable absolute allocation sequence delta of %d\n", diff2));
relative_delta = false;
looks_good = false;
return(diff2);
}
/*
** Insert more tests here if we can think of any!!!!!
*/
/*
** 4. Check for relative scheme skipping 'n' ports. NAT32 behaves this way, it skips 100 ports
** each time.
*/
for (int i=0 ; i<num_ports ; i++) {
mangled_ports[i] -= original_ports[i];
}
diff1 = mangled_ports[1] - mangled_ports[0];
diff2 = mangled_ports[2] - mangled_ports[1];
diff3 = mangled_ports[3] - mangled_ports[2];
/*
** Look for a linear pattern.
*/
if (diff1 == diff2 && diff2 == diff3) {
/*
** Return a -ve result to indicate that port mangling is relative.
*/
WWDEBUG_SAY(("FirewallHelper: Looks good for a relative port range delta of %d\n", diff1));
relative_delta = true;
looks_good = true;
return(diff1);
}
/*
** Look for a broken pattern. Maybe the NAT skipped a whole range.
*/
if (diff1 == diff2 || diff1 == diff3) {
WWDEBUG_SAY(("FirewallHelper: Detected probable broken relative port range delta of %d\n", diff1));
relative_delta = true;
looks_good = false;
return(diff1);
}
if (diff2 == diff3) {
WWDEBUG_SAY(("FirewallHelper: Detected probable broken relative port range delta of %d\n", diff2));
relative_delta = true;
looks_good = false;
return(diff2);
}
/*
** Aw hell, I don't know what it is.
*/
looks_good = false;
relative_delta = false;
return(0);
}
/***********************************************************************************************
* FHC::Get_Firewall_Hardness -- How hard is it to connect to this firewall *
* *
* *
* *
* INPUT: Firewall behavior *
* *
* OUTPUT: Hardness *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/16/01 11:43AM ST : Created *
*=============================================================================================*/
int FirewallHelperClass::Get_Firewall_Hardness(FirewallBehaviorType behavior)
{
int hardness = 0;
unsigned long fw = (unsigned long) behavior;
if (((unsigned long)FIREWALL_TYPE_SIMPLE & fw) != 0) {
hardness++;
}
if (((unsigned long)FIREWALL_TYPE_DUMB_MANGLING & fw) != 0) {
hardness += 2;
}
if (((unsigned long)FIREWALL_TYPE_SMART_MANGLING & fw) != 0) {
hardness += 3;
}
if (((unsigned long)FIREWALL_TYPE_NETGEAR_BUG & fw) != 0) {
hardness += 10;
}
if (((unsigned long)FIREWALL_TYPE_SIMPLE_PORT_ALLOCATION & fw) != 0) {
hardness += 1;
}
if (((unsigned long)FIREWALL_TYPE_RELATIVE_PORT_ALLOCATION & fw) != 0) {
hardness += 2;
}
return(hardness);
}
/***********************************************************************************************
* FHC::Get_Firewall_Retries -- How many retries is it likely to take before we connect? *
* *
* *
* *
* INPUT: Firewall behavior *
* *
* OUTPUT: Hardness *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/16/01 11:43AM ST : Created *
*=============================================================================================*/
int FirewallHelperClass::Get_Firewall_Retries(FirewallBehaviorType behavior)
{
int retries = 2;
unsigned long fw = (unsigned long) behavior;
if (((unsigned long)FIREWALL_TYPE_SIMPLE & fw) != 0) {
retries++;
}
if (((unsigned long)FIREWALL_TYPE_DUMB_MANGLING & fw) != 0) {
retries += 1;
}
if (((unsigned long)FIREWALL_TYPE_SMART_MANGLING & fw) != 0) {
retries += 1;
}
if (((unsigned long)FIREWALL_TYPE_NETGEAR_BUG & fw) != 0) {
//retries += 10;
}
if (((unsigned long)FIREWALL_TYPE_SIMPLE_PORT_ALLOCATION & fw) != 0) {
//retries += 1;
}
if (((unsigned long)FIREWALL_TYPE_RELATIVE_PORT_ALLOCATION & fw) != 0) {
retries += 5;
}
return(retries);
}
/***********************************************************************************************
* FHC::Get_Next_Mangled_Source_Port -- How will the firewall mangle our next source port *
* *
* *
* *
* INPUT: Source port that we will use *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/16/01 3:30PM ST : Created *
*=============================================================================================*/
unsigned short FirewallHelperClass::Get_Next_Mangled_Source_Port(unsigned short source_port)
{
/*
** Locals.
*/
static unsigned long _packet_id = 0x7f100000;
unsigned long timeout;
int return_port = source_port;
SocketHandlerClass socket;
/*
** If our firewall is stupid then just return the source port.
*/
unsigned long fw = (unsigned long) Behavior;
if (fw == 0) {
return(source_port);
}
if (((unsigned long)FIREWALL_TYPE_SIMPLE & fw) != 0) {
return(source_port);
}
/*
** If our NAT uses the same mangled source port regardless of the dest IP then we can use any previous connection to a different IP.
*/
if (WOLNATInterface.Am_I_Server() && ((unsigned long)FIREWALL_TYPE_DUMB_MANGLING & fw) != 0) {
for (int h=0 ; h<ConnectionHistory.Count() ; h++) {
/*
** Make sure it isn't my address (should never be, even with players from behind the same firewall).
*/
if (!ConnectionHistory[h].Is_IP_Equal(ExternalAddress)) {
WWDEBUG_SAY(("FirewallHelper - same port regardless of dest ip - using port %d from connection to %s ; %d\n", (unsigned long) MangledPortHistory[h], ConnectionHistory[h].As_String()));
return(MangledPortHistory[h]);
}
}
}
/*
** Get the address of a mangler server.
*/
unsigned short mangler_port = 4321;
char mangler_name[256];
strcpy(mangler_name, "mangler2.westwood.com");
unsigned char maddress[4];
if (NumManglerServers > 0) {
fw_assert(CurrentManglerServer >= 0 && CurrentManglerServer < NumManglerServers);
bool got_name = WOLNATInterface.Get_Mangler_Name_By_Index(CurrentManglerServer, mangler_name);
if (got_name) {
unsigned short servserv_port = WOLNATInterface.Get_Mangler_Port_By_Index(CurrentManglerServer);
fw_assert(servserv_port != 0);
if (servserv_port) {
mangler_port = servserv_port;
}
WWDEBUG_SAY(("FirewallHelper - Using mangler server from servserv - address is %s ; %d\n", mangler_name, mangler_port));
} else {
WWDEBUG_SAY(("FirewallHelper - Using default mangler name for mangler %d\n", CurrentManglerServer));
}
}
/*
** Get the address to send the packets to.
*/
//WWDEBUG_SAY (("Firewall helper - About to call gethostbyname for %s\n", mangler_name));
struct hostent *host_info = gethostbyname(mangler_name);
if (!host_info) {
WWDEBUG_SAY(("FirewallHelper - gethostbyname failed! Error code %d\n", WSAGetLastError()));
return(source_port);
}
memcpy(maddress, &host_info->h_addr_list[0][0], 4);
//WWDEBUG_SAY(("FirewallHelper: Mangler address is %d.%d.%d.%d ; %d\n", maddress[0], maddress[1], maddress[2], maddress[3], (int)mangler_port));
IPAddressClass mangler_address;
mangler_address.Set_Address(maddress, mangler_port);
//WWDEBUG_SAY(("FirewallHelper: Mangler address is %s\n", mangler_address.As_String()));
//WWDEBUG_SAY(("FirewallHelper: fw = %08x\n", fw));
/*
** Send to the mangler to establish a reference port.
*/
unsigned short port = Get_Next_Temporary_Source_Port(0);
fw_assert(port != source_port);
if (!socket.Open(port, 4321)) {
return(source_port);
}
WWDEBUG_SAY(("FirewallHelper - Getting mangling for temp source port %d\n", (int)port));
/*
** Send to the mangler from this port until we get a response.
*/
timeout = TIMEGETTIME() + (TIMER_SECOND * 3);
unsigned short mangled_port = 0;
while (TIMEGETTIME() < timeout && mangled_port == 0) {
Send_To_Mangler(&mangler_address, &socket, _packet_id);
mangled_port = Get_Mangler_Response(_packet_id, &socket, TIMER_SECOND / 2);
if (mangled_port != 0) {
WWDEBUG_SAY(("FirewallHelper - Mangled port = %d\n", (int)mangled_port));
}
}
_packet_id++;
fw_assert(mangled_port != 0 && mangled_port != source_port);
if (mangled_port == 0) {
/*
** We failed to get a response from the mangler, try a different one next retry.
*/
CurrentManglerServer++;
if (CurrentManglerServer >= NumManglerServers) {
CurrentManglerServer = 0;
}
WWDEBUG_SAY(("FirewallHelper: Couldn't find mangled port, returning source port\n"));
return(source_port);
}
/*
** Our new reference port is 'mangled port'. If we don't care about IP then we are done.
*/
if (((unsigned long)FIREWALL_TYPE_DUMB_MANGLING & fw) != 0) {
WWDEBUG_SAY(("FirewallHelper - Dumb firewall, returning next mangled port as %d\n", mangled_port));
return(mangled_port);
}
/*
** Apply our known delta to the mangled port.
*/
if (((unsigned long)FIREWALL_TYPE_SIMPLE_PORT_ALLOCATION & fw) != 0) {
/*
** Simple port allocation.
*/
return_port = (int)mangled_port + SourcePortAllocationDelta;
} else {
/*
** Rats. It's a relative mangler. This is much harder. Damn NAT32 guy.
*/
if (SourcePortAllocationDelta == 100) {
/*
** Special NAT32 section.
**
** NAT32 mangles source UDP port by adding 1700 + 100 * internal table index
**
** To get the current index we need to take the mangled port and subtract 1700 and the source port
*/
return_port = mangled_port - port;
return_port -= 1700;
return_port += SourcePortAllocationDelta;
return_port += source_port;
return_port += 1700;
} else {
WWASSERT(SourcePortAllocationDelta != 0);
if (SourcePortAllocationDelta == 0) {
/*
** This should never happen....
*/
return_port = mangled_port;
} else {
return_port = mangled_port / SourcePortAllocationDelta;
return_port = return_port * SourcePortAllocationDelta;
return_port += (source_port % SourcePortAllocationDelta);
return_port += SourcePortAllocationDelta;
}
}
}
/*
** This bit is probably doomed.
*/
if (return_port > 65535) {
return_port -= 65535;
}
if (return_port < 1024) {
return_port += 1024;
}
WWDEBUG_SAY(("FirewallHelper - Returning next mangled port as %d\n", return_port));
return(unsigned short(return_port));
}
/***********************************************************************************************
* NatterClass::Build_Mangler_Packet -- Build a packet to send to the mangler. *
* *
* *
* *
* INPUT: Buffer to build packet in *
* Port to set into the packets Port area *
* Packet ID. *
* Blitzme flag (shouldn't be used) *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/6/2001 1:11PM ST : Created *
*=============================================================================================*/
int FirewallHelperClass::Build_Mangler_Packet(unsigned char *buffer, unsigned short port, unsigned long packet_id, bool blitzme)
{
/*
** Asserts.
*/
fw_assert(buffer != NULL);
fw_assert(blitzme == false);
/*
** Need the buffer to exist.
*/
if (buffer == NULL) {
return(0);
}
/*
** Cast the buffer to a CnCPacketType.
*/
CnCPacketType *packet = (CnCPacketType*)buffer;
/*
** Clear out the buffer prior to setting fields.
*/
memset(packet, 0, sizeof(CnCPacketType));
/*
** Set the required Global Header firelds.
*/
packet->GHeader.Header.MagicNumber = GLOBAL_MAGICNUM;
packet->GHeader.Header.Code = PACKET_DATA_NOACK;
packet->GHeader.Header.ForwardTo = 0;
packet->GHeader.Header.PacketID = packet_id;
packet->GHeader.ProductID = COMMAND_AND_CONQUER_RA2;
/*
** Set the Global Packet fields.
*/
packet->Packet.Command = NET_MANGLER_REQUEST;
strcpy(packet->Packet.Name, "ren");
packet->Packet.ManglerData.MangledPortNumber = port;
packet->Packet.ManglerData.OriginalPortNumber = port;
packet->Packet.ManglerData.BlitzMe = 0;
/*
** Blitzme field is obsolete and should never be used.
*/
if (blitzme) {
packet->Packet.ManglerData.BlitzMe = 1;
}
/*
** Return success.
*/
return(sizeof(CnCPacketType));
}
/***********************************************************************************************
* FirewallHelperClass::Add_Thread_Action -- Add a new action to the thread control FIFO *
* *
* *
* *
* INPUT: Thread action *
* Notification event *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/9/2001 9:06PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Add_Thread_Action(int thread_action, HANDLE thread_event)
{
ThreadLockClass locker(this);
ThreadActionClass thread;
thread.ThreadAction = thread_action;
thread.ThreadEvent = thread_event;
ThreadQueue.Add(thread);
}
/***********************************************************************************************
* FirewallHelperClass::Set_Thread_Event -- Set the event associated with action completion *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/9/2001 9:09PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Set_Thread_Event(void)
{
ThreadLockClass locker(this);
if (ThreadEvent != INVALID_HANDLE_VALUE) {
SetEvent(ThreadEvent);
ThreadEvent = INVALID_HANDLE_VALUE;
}
}
/***********************************************************************************************
* FirewallHelperClass::Talk_To_New_Player -- A new player wants to negotiate a NAT port *
* *
* *
* *
* INPUT: User struct for player *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/7/2001 8:31PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Talk_To_New_Player(WOL::User *user)
{
/*
** We are only reading here - no need to get exclusive access.
*/
//ThreadLockClass locker(this);
WWASSERT(PTheGameData != NULL);
/*
** Fill in an invitation options packet to send to the guest.
*/
WOLNATInterfaceClass::PrivateGameOptionsStruct options;
strcpy(options.NATOptionsPrefix, "NAT:");
options.Option = WOLNATInterfaceClass::OPTION_INVITE_PORT_NEGOTIATION;
sprintf(options.OptionData.Invitation.LocalIP, "%08x,", (unsigned long) LocalChatConnectionAddress.Get_Address());
sprintf(options.OptionData.Invitation.LocalPort, "%04x,", The_Game()->Get_Port());
sprintf(options.OptionData.Invitation.ExternalIP, "%08x,", (unsigned long) ExternalAddress.Get_Address());
sprintf(options.OptionData.Invitation.FirewallType, "%08x,", Get_Raw_Firewall_Behavior());
sprintf(options.OptionData.Invitation.Queued, "%04x", ClientQueue.Count());
/*
** Send it.
*/
WOLNATInterface.Send_Private_Game_Options(user, (char*)&options);
}
/***********************************************************************************************
* FirewallHelperClass::Process_Game_Options -- Process private game options packets *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/9/2001 10:01PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Process_Game_Options(void)
{
/*
** Locals.
*/
WOL::User user;
char options_buffer[OPTIONS_STAGING_BUFFER_SIZE];
unsigned long addr_ip = 0;
unsigned short addr_port = 0;
unsigned long firewall = 0;
ThreadLockClass locker(this);
/*
** See if there are any pending game options packets.
*/
if (WOLNATInterface.Get_Private_Game_Options(&user, options_buffer, OPTIONS_STAGING_BUFFER_SIZE)) {
/*
** Cast the options buffer to a more manageable type.
*/
WOLNATInterfaceClass::PrivateGameOptionsStruct *options = (WOLNATInterfaceClass::PrivateGameOptionsStruct*) options_buffer;
/*
** Get the option type.
*/
switch (options->Option) {
/*
** This option represents a game server inviting us to negotiate a connection through firewalls. It will contain his
** local IP and port. This is a client side message only.
*/
case WOLNATInterfaceClass::OPTION_INVITE_PORT_NEGOTIATION:
WWDEBUG_SAY(("FirewallHelper - Got OPTION_INVITE_PORT_NEGOTIATION\n"));
fw_assert(!WOLNATInterface.Am_I_Server());
if (!WOLNATInterface.Am_I_Server()) {
/*
** Pull out the players info from the packet.
*/
sscanf(options->OptionData.Invitation.LocalIP, "%08x", &addr_ip);
sscanf(options->OptionData.Invitation.LocalPort, "%04hx", &addr_port);
PlayersLocalAddress.Set_Address(addr_ip, addr_port);
sscanf(options->OptionData.Invitation.ExternalIP, "%08x", &addr_ip);
PlayersExternalAddress.Set_Address(addr_ip, addr_port);
sscanf(options->OptionData.Invitation.FirewallType, "%08x", (unsigned long*)(&PlayersFirewallType));
sscanf(options->OptionData.Invitation.Queued, "%04x", &QueuedPlayers);
strcpy(PlayersName, (char*)user.name);
PlayerAsUser = user;
if (QueueNotifyPtr) {
*QueueNotifyPtr = QueuedPlayers;
}
WWDEBUG_SAY(("FirewallHelper - Received port negotiation invitation. %d players in the queue ahead of me\n", QueuedPlayers));
WWDEBUG_SAY(("FirewallHelper - Server is %s. Local addr = %s, ", PlayersName, PlayersLocalAddress.As_String()));
WWDEBUG_SAY(("external addr = %s, firewall = %08x\n", PlayersExternalAddress.As_String(), (unsigned long) PlayersFirewallType));
/*
** Wait for queue notification.
*/
if (QueuedPlayers == 0) {
QueuedPlayers = -1;
}
/*
** Respond to the server with my info.
*/
ClientPort = WOLNATInterface.Get_Next_Client_Port();
WOLNATInterfaceClass::PrivateGameOptionsStruct send_options;
strcpy(send_options.NATOptionsPrefix, "NAT:");
send_options.Option = WOLNATInterfaceClass::OPTION_ACCEPT_PORT_NEGOTIATION_INVITATION;
sprintf(send_options.OptionData.Accept.LocalIP, "%08x,", (unsigned long) LocalChatConnectionAddress.Get_Address());
sprintf(send_options.OptionData.Accept.LocalPort, "%04x,", ClientPort);
sprintf(send_options.OptionData.Accept.ExternalIP, "%08x,", (unsigned long) ExternalAddress.Get_Address());
sprintf(send_options.OptionData.Accept.FirewallType, "%08x", Get_Raw_Firewall_Behavior());
/*
** Send it.
*/
WOLNATInterface.Send_Private_Game_Options(&user, (char*)&send_options);
/*
** Go into connect mode.
** Don't do this until we get a confirmation of our queue position.
*/
Add_Thread_Action(THREAD_CONNECT_FIREWALL, INVALID_HANDLE_VALUE);
}
break;
/*
** This option represents a game client accepting our invitation to negotiate a connection through firewalls. It
** will contain his local IP and port. This is a server side message only.
*/
case WOLNATInterfaceClass::OPTION_ACCEPT_PORT_NEGOTIATION_INVITATION:
WWDEBUG_SAY(("FirewallHelper - Got OPTION_ACCEPT_PORT_NEGOTIATION_INVITATION\n"));
fw_assert(WOLNATInterface.Am_I_Server());
if (WOLNATInterface.Am_I_Server()) {
/*
** Pull out the players info from the packet.
*/
unsigned long ext_ip = 0;
sscanf(options->OptionData.Accept.LocalIP, "%08x", &addr_ip);
sscanf(options->OptionData.Accept.LocalPort, "%04hx", &addr_port);
sscanf(options->OptionData.Accept.ExternalIP, "%08x", &ext_ip);
sscanf(options->OptionData.Accept.FirewallType, "%08x", &firewall);
/*
** Create a queue entry for the player.
*/
ClientStruct *client = new ClientStruct;
strcpy(client->Name, (char*)user.name);
client->LocalAddress.Set_Address(addr_ip, addr_port);
client->ExternalAddress.Set_Address(ext_ip, addr_port);
client->FirewallType = (FirewallBehaviorType) firewall;
client->User = user;
/*
** Make sure this client isn't in the client list already (Should never happen but...)
*/
if (Remove_Player_From_Negotiation_Queue(client->Name)) {
WWDEBUG_SAY(("FirewallHelper OPTION_ACCEPT_PORT_NEGOTIATION_INVITATION - Removed %s from ClientQueue\n", client->Name));
}
/*
** Add this player to the negotiation queue.
*/
ClientQueue.Add(client);
WWDEBUG_SAY(("FirewallHelper - Got client accept from %s. Local addr = %s,", client->Name, client->LocalAddress.As_String()));
WWDEBUG_SAY((" external addr = %s, firewall = %08x\n", client->ExternalAddress.As_String(), (unsigned long) firewall));
Add_Thread_Action(THREAD_CONNECT_FIREWALL, INVALID_HANDLE_VALUE);
}
break;
/*
** Handle connection result message.
**
*/
case WOLNATInterfaceClass::OPTION_CONNECTION_RESULT:
{
int result = options->OptionData.ConnectionResult.Result[0] - 'a';
WWDEBUG_SAY(("FirewallHelper - Got OPTION_CONNECTION_RESULT %d from %s\n", result, options->OptionData.ConnectionResult.Name));
if (stricmp(PlayersName, options->OptionData.ConnectionResult.Name) == 0) {
PlayersConnectionResult = result;
unsigned long port;
sscanf(options->OptionData.ConnectionResult.Port, "%04x", &port);
PlayersConnectionResultPort = (unsigned short) port;
if (WOLNATInterface.Am_I_Server()) {
LastOptionsFromClient = TIMEGETTIME();
}
}
break;
}
/*
** Other player is telling us what port to use when talking to him.
*/
case WOLNATInterfaceClass::OPTION_PORT_NOTIFICATION:
{
WWDEBUG_SAY(("FirewallHelper - Got OPTION_PORT_NOTIFICATION from %s\n", options->OptionData.Port.Name));
unsigned long port;
sscanf(options->OptionData.Port.MangledPort, "%04x", &port);
WWDEBUG_SAY(("FirewallHelper - Port is %d\n", port));
//fw_assert(port >= 1024 && port < 65536);
//if (port >= 1024 && port < 65536) {
if (stricmp(PlayersName, options->OptionData.Port.Name) == 0) {
PlayersMangledPort = (unsigned short) port;
if (WOLNATInterface.Am_I_Server()) {
LastOptionsFromClient = TIMEGETTIME();
}
}
//}
break;
}
/*
** Server is telling us how close to the front of the queue we are.
*/
case WOLNATInterfaceClass::OPTION_QUEUE_STATE:
{
WWDEBUG_SAY(("FirewallHelper - Got OPTION_QUEUE_STATE\n"));
int queue;
sscanf(options->OptionData.QueueState.Position, "%02x", &queue);
WWDEBUG_SAY(("FirewallHelper - Queue position is %d\n", queue));
QueuedPlayers = queue;
if (QueueNotifyPtr) {
*QueueNotifyPtr = queue;
}
//if (QueuedPlayers == 0) {
// if (ThreadState != THREAD_DETECT_FIREWALL) {
// Add_Thread_Action(THREAD_CONNECT_FIREWALL, INVALID_HANDLE_VALUE);
// }
//}
break;
}
/*
** Client has cancelled out of the game channel.
*/
case WOLNATInterfaceClass::OPTION_ABORT_NEGOTIATION:
WWDEBUG_SAY(("FirewallHelper - Got OPTION_ABORT_NEGOTIATION from %s\n", (char*)user.name));
{
ThreadLockClass locker(this);
if (WOLNATInterface.Am_I_Server()) {
if (stricmp((char*)user.name, PlayersName) == 0) {
strcpy(CancelPlayer, (char*)user.name);
}
}
/*
** Remove this player from the client queue if he is in there.
*/
if (Remove_Player_From_Negotiation_Queue((char*)user.name)) {
WWDEBUG_SAY(("FirewallHelper OPTION_ABORT_NEGOTIATION - Removed %s from ClientQueue\n", (char*)user.name));
}
break;
}
/*
** Client has joined a channel and is available for negotiation.
*/
case WOLNATInterfaceClass::OPTION_CLIENT_IN_CHANNEL:
WWDEBUG_SAY(("FirewallHelper - Got OPTION_CLIENT_IN_CHANNEL from %s\n", (char*)user.name));
if (WOLNATInterface.Am_I_Server()) {
Talk_To_New_Player(&user);
}
break;
default:
fw_assert(false);
break;
}
}
}
/***********************************************************************************************
* FirewallHelperClass::Cleanup_Client_Queue -- Remove pending entries from client queue *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: ONLY CALL THIS FROM THE MAIN THREAD *
* *
* HISTORY: *
* 3/22/2002 4:09PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Cleanup_Client_Queue(void)
{
/*
** This is the main thread removing entries from the client queue that it wasn't able to remove before due to thread
** contention.
*/
fw_assert(Get_Main_Thread_ID() == GetCurrentThreadId());
while (ClientQueueRemoveList.Count()) {
if (Remove_Player_From_Negotiation_Queue_If_Mutex_Available(ClientQueueRemoveList[0]->Name)) {
delete ClientQueueRemoveList[0];
ClientQueueRemoveList.Delete(0);
} else {
break;
}
}
}
/***********************************************************************************************
* FirewallHelperClass::Remove_Player_From_Negotiation_Queue_If_Mutex_Available - What it says *
* *
* *
* *
* *
* INPUT: Player name *
* *
* OUTPUT: True if mutex was available *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/22/2002 3:41PM ST : Created *
*=============================================================================================*/
bool FirewallHelperClass::Remove_Player_From_Negotiation_Queue_If_Mutex_Available(char *player_name)
{
ThreadLockClass locker(this, 0);
/*
** If we can grab the mutex then remove the item immediately, otherwise add it to a list and return.
*/
if (locker.WaitResult == WAIT_OBJECT_0) {
Remove_Player_From_Negotiation_Queue(player_name);
return(true);
} else {
/*
** Just add the name to a list that will be checked later when it's safe to remove stuff from the client queue.
** This list must only ever be accessed from the main thread.
*/
fw_assert(Get_Main_Thread_ID() == GetCurrentThreadId());
for (int i=0 ; i<ClientQueueRemoveList.Count() ; i++) {
if (stricmp(ClientQueueRemoveList[i]->Name, player_name) == 0) {
return(false);
}
}
ClientStruct *clientptr = new ClientStruct;
strcpy(clientptr->Name, player_name);
ClientQueueRemoveList.Add(clientptr);
}
return(false);
}
/***********************************************************************************************
* FirewallHelperClass::Remove_Player_From_Negotiation_Queue - What it says *
* *
* *
* *
* INPUT: Player name *
* *
* OUTPUT: True if player was in queue *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/3/2002 10:20PM ST : Created *
*=============================================================================================*/
bool FirewallHelperClass::Remove_Player_From_Negotiation_Queue(char *player_name)
{
fw_assert(WOLNATInterface.Am_I_Server());
if (ClientQueue.Count() == 0) {
return(false);
}
ThreadLockClass locker(this);
ClientStruct *clientptr = NULL;
bool retcode = false;
for (int i=0 ; i<ClientQueue.Count() ; i++) {
clientptr = ClientQueue[i];
if (stricmp(clientptr->Name, player_name) == 0) {
WWDEBUG_SAY(("FirewallHelper - Removed %s from ClientQueue\n", clientptr->Name));
delete clientptr;
ClientQueue.Delete(i);
i--;
retcode = true;
}
}
return(retcode);
}
/***********************************************************************************************
* FirewallHelperClass::Send_My_Port -- Send my mangled port number to the other guy *
* *
* *
* *
* INPUT: Port *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/13/2001 11:37AM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Send_My_Port(unsigned short port)
{
ThreadLockClass locker(this);
WWDEBUG_SAY(("FirewallHelper - Sending my port number (%d) to %s\n", (unsigned int)port, PlayersName));
/*
** Fill in an port notification options packet to send to the guest.
*/
WOLNATInterfaceClass::PrivateGameOptionsStruct options;
strcpy(options.NATOptionsPrefix, "NAT:");
options.Option = WOLNATInterfaceClass::OPTION_PORT_NOTIFICATION;
sprintf(options.OptionData.Port.MangledPort, "%04x,", port);
char my_name[64];
WOLNATInterface.Get_My_Name(my_name);
strcpy(options.OptionData.Port.Name, my_name);
/*
** Send it.
*/
WOLNATInterface.Send_Private_Game_Options(&PlayerAsUser, (char*)&options);
}
/***********************************************************************************************
* FirewallHelperClass::Send_Connection_Result -- Send connection result game option *
* *
* *
* *
* INPUT: Result enum *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/13/2001 2:17PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Send_Connection_Result(int result, unsigned short port)
{
ThreadLockClass locker(this);
/*
** Fill in a connection result options packet to send to the other player.
*/
WOLNATInterfaceClass::PrivateGameOptionsStruct options;
strcpy(options.NATOptionsPrefix, "NAT:");
options.Option = WOLNATInterfaceClass::OPTION_CONNECTION_RESULT;
options.OptionData.ConnectionResult.Result[0] = 'a' + result;
options.OptionData.ConnectionResult.Result[1] = ',';
sprintf(options.OptionData.ConnectionResult.Port, "%04x,", (unsigned long) port);
char my_name[64];
WOLNATInterface.Get_My_Name(my_name);
strcpy(options.OptionData.ConnectionResult.Name, my_name);
/*
** Send it.
*/
WOLNATInterface.Send_Private_Game_Options(&PlayerAsUser, (char*)&options);
}
/***********************************************************************************************
* FirewallHelperClass::Send_Connection_Result -- Send connection result game option *
* *
* *
* *
* INPUT: Result enum *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/13/2001 2:17PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Send_Queue_States(void)
{
if (WOLNATInterface.Am_I_Server()) {
if (ClientQueue.Count()) {
ThreadLockClass locker(this);
/*
** Fill in a queue state options packet to send to the other players.
*/
WOLNATInterfaceClass::PrivateGameOptionsStruct options;
strcpy(options.NATOptionsPrefix, "NAT:");
options.Option = WOLNATInterfaceClass::OPTION_QUEUE_STATE;
char my_name[64];
WOLNATInterface.Get_My_Name(my_name);
strcpy(options.OptionData.ConnectionResult.Name, my_name);
/*
** Send updates to all queued players.
*/
for (int i=0 ; i<ClientQueue.Count() ; i++) {
sprintf(options.OptionData.QueueState.Position, "%02x", i);
ClientStruct *client = ClientQueue[i];
fw_assert(client != NULL);
if (client) {
/*
** Send it.
*/
WOLNATInterface.Send_Private_Game_Options(&client->User, (char*)&options);
}
}
}
}
}
/***********************************************************************************************
* FirewallHelperClass::Set_Client_Connect_Event -- Set client event completion info *
* *
* *
* *
* INPUT: Event *
* Ptr to success flag *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/14/2001 10:56PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Set_Client_Connect_Event(HANDLE thread_event, HANDLE cancel_event, int *flag_ptr, int *queue_ptr)
{
ThreadLockClass locker(this);
fw_assert(!WOLNATInterface.Am_I_Server());
if (!WOLNATInterface.Am_I_Server()) {
SuccessFlagPtr = flag_ptr;
ClientConnectEvent = thread_event;
ClientCancelEvent = cancel_event;
QueueNotifyPtr = queue_ptr;
}
}
/***********************************************************************************************
* FirewallHelperClass::Set_Client_Success -- Set the client connect return status *
* *
* *
* *
* INPUT: Success - Did the client get a good port and IP for the server *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/14/2001 10:59PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Set_Client_Success(int success)
{
if (!WOLNATInterface.Am_I_Server()) {
if (SuccessFlagPtr) {
*SuccessFlagPtr = success;
}
SuccessFlagPtr = NULL;
if (QueueNotifyPtr) {
*QueueNotifyPtr = 0;
}
QueueNotifyPtr = NULL;
if (ClientConnectEvent != INVALID_HANDLE_VALUE) {
SetEvent(ClientConnectEvent);
}
ClientConnectEvent = INVALID_HANDLE_VALUE;
ClientCancelEvent = INVALID_HANDLE_VALUE;
}
}
/***********************************************************************************************
* FirewallHelperClass::Client_Cancelled -- Checks to see if the player hit cancel. *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: True if player cancelled *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/17/2001 12:04PM ST : Created *
*=============================================================================================*/
bool FirewallHelperClass::Client_Cancelled(void)
{
if (!WOLNATInterface.Am_I_Server()) {
ThreadLockClass locker(this);
if (ClientCancelEvent != INVALID_HANDLE_VALUE) {
int result = WaitForSingleObject(ClientCancelEvent, 0);
if (result == WAIT_OBJECT_0) {
WWDEBUG_SAY(("FirewallHelper - Client cancelled\n"));
return(true);
}
}
}
return(false);
}
/***********************************************************************************************
* FirewallHelperClass::Remote_Client_Cancelled -- Did the remote client hit cancel? *
* *
* *
* *
* INPUT: True if our client cancelled *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: Requres PlayersName variable to be correctly set up *
* Will only return true once for each cancellation *
* *
* HISTORY: *
* 8/19/2001 9:26PM ST : Created *
*=============================================================================================*/
bool FirewallHelperClass::Remote_Client_Cancelled(void)
{
ThreadLockClass locker(this);
if (WOLNATInterface.Am_I_Server() && CancelPlayer[0] != 0) {
if (stricmp(CancelPlayer, PlayersName) == 0) {
CancelPlayer[0] = 0;
return(true);
}
}
return(false);
}
/***********************************************************************************************
* FirewallHelperClass::Send_Cancel_Notification -- Send abort to host *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/19/2001 9:32PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::Send_Cancel_Notification(void)
{
fw_assert(!WOLNATInterface.Am_I_Server());
if (!WOLNATInterface.Am_I_Server()) {
ThreadLockClass locker(this);
WWDEBUG_SAY(("FirewallHelper - Sending my cancellation notice to the server\n"));
/*
** Fill in an abort notification options packet to send to the host.
*/
WOLNATInterfaceClass::PrivateGameOptionsStruct options;
strcpy(options.NATOptionsPrefix, "NAT:");
options.Option = WOLNATInterfaceClass::OPTION_ABORT_NEGOTIATION;
options.OptionData.QuitTalking.Nothing = 0;
/*
** Send it.
*/
WOLNATInterface.Send_Private_Game_Options(&PlayerAsUser, (char*)&options);
}
}
/***********************************************************************************************
* FirewallHelperClass::Negotiate_Port -- Option communications with one other player *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: True if we heard from the other player directly *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/7/2001 8:54PM ST : Created *
*=============================================================================================*/
int FirewallHelperClass::Negotiate_Port(void)
{
int retries = 5;
unsigned long timeout;
unsigned long resend_timer;
bool mangling = true;
bool sharing_a_nat = false;
int result;
IPAddressClass address;
bool gotit = false;
SocketHandlerClass *socket = NULL;
char receive_packet[512];
char my_name[256];
my_name[0] = 0;
bool server = WOLNATInterface.Am_I_Server();
bool got_name = WOLNATInterface.Get_My_Name(my_name);
fw_assert(got_name);
if (!got_name) {
WWDEBUG_SAY(("FirewallHelper - Failed to get user log on name\n"));
return(FW_RESULT_FAILED);
}
PlayersMangledPort = 0;
PlayersConnectionResult = -1;
PlayersConnectionResultPort = 0;
/*
** On the server side, we need to find out who we are supposed to be connecting to.
** We will use the game comms to send and receive on the server side since we already have a socket bound to the port
** we want to use.
*/
if (server) {
if (ClientQueue.Count() == 0) {
return(FW_RESULT_FAILED);
}
/*
** Update everyones queue state.
*/
Send_Queue_States();
ClientStruct *client = ClientQueue[0];
PlayersLocalAddress = client->LocalAddress;
PlayersExternalAddress = client->ExternalAddress;
PlayersFirewallType = client->FirewallType;
PlayerAsUser = client->User;
strcpy(PlayersName, client->Name);
LastOptionsFromClient = TIMEGETTIME();
delete client;
ClientQueue.Delete(0);
WWDEBUG_SAY(("FirewallHelper - Removed next player %s from client queue\n", PlayersName));
if (Remote_Client_Cancelled()) {
return(FW_RESULT_FAILED);
}
CancelPlayer[0] = 0;
} else {
/*
** On the client side, we need to open a new socket using the client side port number. Use any port number for the
** second parameter since it's the default send port and will always be overridden when we send anyway.
*/
socket = new SocketHandlerClass;
socket->Service_Never();
if (!socket->Open(ClientPort, PlayersLocalAddress.Get_Port())) {
WWDEBUG_SAY(("FirewallHelperClass: Failed to open socket for client\n"));
fw_assert(false);
delete socket;
return(FW_RESULT_FAILED);
}
WOLNATInterface.Set_Service_Socket_Handler(socket);
/*
** If we aren't sure of the queue size then wait until we are.
*/
if (QueuedPlayers == -1) {
timeout = TIMEGETTIME() + (8 * TIMER_SECOND);
while (TIMEGETTIME() < timeout && QueuedPlayers == -1) {
Process_Game_Options();
Sleep(5);
/*
** See if the user cancelled.
*/
if (Client_Cancelled()) {
Send_Cancel_Notification();
WOLNATInterface.Set_Service_Socket_Handler(NULL);
delete socket;
return(FW_RESULT_CANCELLED);
}
}
}
if (QueuedPlayers == -1) {
/*
** This should never happen right....?
*/
if (socket) {
WOLNATInterface.Set_Service_Socket_Handler(NULL);
delete socket;
}
return(FW_RESULT_FAILED);
}
/*
** If there are players queued ahead of us, wait til we get to the front of the queue.
*/
timeout = TIMEGETTIME() + (QueuedPlayers * 32 * TIMER_SECOND);
while (TIMEGETTIME() < timeout && QueuedPlayers != 0) {
Process_Game_Options();
Sleep(5);
/*
** See if the user cancelled.
*/
if (Client_Cancelled()) {
Send_Cancel_Notification();
WOLNATInterface.Set_Service_Socket_Handler(NULL);
delete socket;
return(FW_RESULT_CANCELLED);
}
}
}
PlayersFirewallAddress = PlayersExternalAddress;
PlayersFirewallAddress.Set_Port(PlayersLocalAddress.Get_Port());
WWDEBUG_SAY(("FirewallHelper - Negotiating port with player %s, local address = %s,", PlayersName, PlayersLocalAddress.As_String()));
WWDEBUG_SAY((" ext address = %s, firewall = %08x\n", PlayersExternalAddress.As_String(), PlayersFirewallType));
/*
** What will our mangled port be if we try to talk to this player?
**
** If they are behind the same firewall then there will be no mangling.
**
*/
if (PlayersExternalAddress.Is_IP_Equal(ExternalAddress)) {
mangling = false;
sharing_a_nat = true;
PlayersFirewallAddress = PlayersLocalAddress;
WWDEBUG_SAY(("Both external IPs match - we are behind the same firewall\n"));
}
/*
** Maybe neither of our firewalls use port mangling. If so, then we can use the default port number.
*/
if (mangling) {
if (Behavior == 0 || (Behavior & FIREWALL_TYPE_SIMPLE) != 0) {
if (PlayersFirewallType == 0 || (PlayersFirewallType & FIREWALL_TYPE_SIMPLE) != 0) {
/*
** Ok, we both have crap firewalls or modems or somesuch. Anyway, this should be easy.
*/
mangling = false;
}
}
}
int trying = 0;
unsigned short last_send_port = 0;
unsigned short my_last_mangled_port = 0;
unsigned short verified_mangled_port = 0;
bool used_old_port = false;
do {
Sleep(0);
/*
** See if the user cancelled.
*/
if (Client_Cancelled()) {
Send_Cancel_Notification();
WOLNATInterface.Set_Service_Socket_Handler(NULL);
delete socket;
return(FW_RESULT_CANCELLED);
}
if (Remote_Client_Cancelled()) {
return(FW_RESULT_FAILED);
}
/*
** If we haven't got any kind of options at all from the client for 10 seconds then give up.
*/
if (trying > 0 && server && (TIMEGETTIME() - LastOptionsFromClient > TIMER_SECOND * 10)) {
break;
}
if (!mangling) {
/*
** Use their unmangled port number. Nothing fancy needed here.
*/
PlayersMangledPort = PlayersLocalAddress.Get_Port();
} else {
unsigned short mangled_port = 0;
/*
** If our firewall uses the same source port regardless of destination port and we have
** already connected to someone at this address (two players behind the same NAT) then
** we have to try the same source port we are already talking to the other guy on.
**
** If that's not working out then try a different port after two attempts.
*/
if (server && trying < 2) {
if ((Behavior & FirewallHelperClass::FIREWALL_TYPE_DESTINATION_PORT_DELTA) == 0) {
WWDEBUG_SAY(("FirewallHelper: No destination port delta - looking for earlier connections at the same address\n"));
for (int r = 0 ; r < ConnectionHistory.Count() ; r++) {
if (ConnectionHistory[r].Is_IP_Equal(PlayersExternalAddress)) {
mangled_port = MangledPortHistory[r];
used_old_port = true;
WWDEBUG_SAY(("FirewallHelper - Found old connection port number to use - port number %d\n", mangled_port));
break;
}
}
}
}
/*
** If this isn't the first try then we might already know what the port will be since it can't change between tries.
*/
if (mangled_port == 0) {
if ((Behavior & FirewallHelperClass::FIREWALL_TYPE_DESTINATION_PORT_DELTA) == 0) {
if (trying > 0) {
if (!used_old_port) {
mangled_port = my_last_mangled_port;
WWDEBUG_SAY(("FirewallHelper - No dest port delta - using last port number %d\n", mangled_port));
} else {
used_old_port = false;
}
}
}
}
if (mangled_port == 0) {
/*
** Work out what my port will be.
*/
unsigned short base_port = 0;
if (server) {
base_port = WOLNATInterface.Get_Port_As_Server();
} else {
/*
** Client only. Roll onto the next port and try that.
*/
if (trying > 0) {
WOLNATInterface.Set_Service_Socket_Handler(NULL);
Sleep(100);
socket->Close();
for (int cp=0 ; cp < 2048 ; cp++) {
ClientPort = WOLNATInterface.Get_Next_Client_Port();
if (socket->Open(ClientPort, PlayersLocalAddress.Get_Port())) {
break;
} else {
socket->Close();
ClientPort = 0;
}
}
fw_assert(ClientPort != 0);
WOLNATInterface.Set_Service_Socket_Handler(socket);
}
if (ClientPort != 0) {
base_port = ClientPort;
}
}
/*
** Get my mangled port. This can take up to 4 seconds.
*/
mangled_port = FirewallHelper.Get_Next_Mangled_Source_Port(base_port);
}
/*
** Send my mangled port to the other chap.
*/
fw_assert(mangled_port != 0);
Send_My_Port(mangled_port);
my_last_mangled_port = mangled_port;
}
/*
** Wait until we know what port to send to. Allow plenty of time for slow chat server response.
** Players are more likely to be in 'sync' with each other after the first try.
*/
timeout = TIMEGETTIME() + (6 * TIMER_SECOND);
if (trying > 0) {
timeout = timeout - (3*TIMER_SECOND);
}
while (TIMEGETTIME() < timeout && ((mangling == true && PlayersMangledPort == last_send_port) || (mangling == false && PlayersMangledPort == 0))) {
Process_Game_Options();
Sleep(5);
/*
** See if the user cancelled.
*/
if (Client_Cancelled()) {
Send_Cancel_Notification();
WOLNATInterface.Set_Service_Socket_Handler(NULL);
delete socket;
return(FW_RESULT_CANCELLED);
}
if (Remote_Client_Cancelled()) {
return(FW_RESULT_FAILED);
}
}
if (verified_mangled_port != 0) {
PlayersMangledPort = verified_mangled_port;
}
last_send_port = PlayersMangledPort;
PlayersFirewallAddress.Set_Port(PlayersMangledPort);
if (PlayersMangledPort == 0) {
WWDEBUG_SAY(("FirewallHelper: PlayersMangledPort = 0 - skipping packet send\n"));
}
if (PlayersMangledPort != 0) {
/*
** OK, we know what port to send to. Let dooooo it.
*/
/*
** Build the port probe packet to send.
*/
char packet[256];
strcpy(packet, my_name);
/*
** If we are a netgear and talking to a non netgear then pause before sending.
**
** This is needed when playing a netgear against a linksys. If the netgear sends first
** then the linksys can respond with a ICMP port unreachable message in the case that
** it already has an open port table mapping with a pre-existing player.
**
** When it gets an ICMP port unreachable, the netgear invalidates any existing port mappings.
**
** The delay has to be long enough for our port number to be passed through the chat server
** to the other guy and for him to start sending.
*/
if (Is_Netgear() && !Is_Netgear(PlayersFirewallType)) {
WWDEBUG_SAY(("FirewallHelper: Doing the netgear sleep thing\n"));
Sleep(TIMER_SECOND * 4);
}
/*
** Send the packet to the other player. If a NAT mangles an outgoing port number then it will be noted
** as the packet comes in.
*/
WWDEBUG_SAY(("FirewallHelper: Sending PACKETTYPE_FIREWALL_PROBE packet to %s at port %d\n", PlayersName, PlayersMangledPort));
timeout = TIMEGETTIME() + (TIMER_SECOND * 6);
resend_timer = 0;
do {
if (resend_timer < TIMEGETTIME()) {
//if (!CrapFlag)
WOLNATInterface.Send_Game_Format_Packet_To(&PlayersFirewallAddress, packet, strlen(packet)+1, socket);
resend_timer = TIMEGETTIME() + (TIMER_SECOND / 2);
}
Sleep(5);
Process_Game_Options();
/*
** See if the user cancelled.
*/
if (Client_Cancelled()) {
Send_Cancel_Notification();
WOLNATInterface.Set_Service_Socket_Handler(NULL);
delete socket;
return(FW_RESULT_CANCELLED);
}
if (Remote_Client_Cancelled()) {
return(FW_RESULT_FAILED);
}
//if (!gotit) {
/*
** See if a port probe came in from this player.
*/
IPAddressClass receive_address;
result = WOLNATInterface.Get_Packet(receive_packet, sizeof(receive_packet), receive_address);
/*
** Check if we got a packet from this game.
*/
if (result) {
WWDEBUG_SAY(("FirewallHelper: Got a packet from player %s at address %s\n", receive_packet, receive_address.As_String()));
/*
** Is it from the guy we are trying to talk to?
*/
if (stricmp(receive_packet, PlayersName) == 0) {
WWDEBUG_SAY(("FirewallHelper: Packet is from other guy\n"));
/*
** Send a connection result game option.
*/
Send_Connection_Result(CONNRESULT_CONNECTED, receive_address.Get_Port());
/*
** Note the address the packet came in on.
*/
PlayersFirewallAddress = receive_address;
verified_mangled_port = receive_address.Get_Port();
/*
** Got it.
*/
gotit = true;
}
}
//}
/*
** If the other player got our packet and we got theirs then we are finished.
*/
if (gotit && PlayersConnectionResult == CONNRESULT_CONNECTED) {
WWDEBUG_SAY(("FirewallHelper: Got connection result - my packet must have got through\n"));
break;
}
/*
** If the other guy has started re-trying then we might as well do that too.
*/
if (PlayersConnectionResult >= CONNRESULT_TRY1 && PlayersConnectionResult < CONNRESULT_CONNECTED) {
int their_try = PlayersConnectionResult - CONNRESULT_TRY1;
if (their_try > trying) {
WWDEBUG_SAY(("FirewallHelper: Other player is trying again - their_try = %d\n", their_try));
trying = their_try - 1;
break;
}
}
} while(TIMEGETTIME() < timeout);
}
if (gotit && PlayersConnectionResult == CONNRESULT_CONNECTED) {
break;
}
if (trying >= retries) {
break;
}
/*
** Well, that didn't go too well, let's try again.
*/
trying++;
Send_Connection_Result(CONNRESULT_TRY1 + trying, 0);
WWDEBUG_SAY(("FirewallHelper: Trying again - trying = %d\n", trying));
} while(true);
if (socket) {
WOLNATInterface.Set_Service_Socket_Handler(NULL);
delete socket;
}
if (gotit && PlayersConnectionResult == CONNRESULT_CONNECTED) {
WWDEBUG_SAY(("FirewallHelper: Port negotiation successful! Correct address is %s\n", PlayersFirewallAddress.As_String()));
WWDEBUG_SAY(("Player saw our port as %d\n", (unsigned long)PlayersConnectionResultPort));
/*
** Well it worked so we have more confidence in our firewall settings.
*/
Confidence++;
/*
** Save the port number in case we have to connect to this guy again.
*/
if (server) {
unsigned short history_port = PlayersConnectionResultPort;
if (history_port == 0) {
history_port = my_last_mangled_port;
}
bool found_history = false;
for (int h=0 ; h<ConnectionHistory.Count() ; h++) {
if (ConnectionHistory[h].Is_IP_Equal(PlayersExternalAddress)) {
MangledPortHistory[h] = history_port;
found_history = true;
break;
}
}
if (!found_history) {
ConnectionHistory.Add(PlayersExternalAddress);
MangledPortHistory.Add(history_port);
}
fw_assert(ConnectionHistory.Count() == MangledPortHistory.Count());
}
return(FW_RESULT_SUCCEEDED);
} else {
WWDEBUG_SAY(("FirewallHelper: *** Port negotiation failed! ***\n"));
Confidence--;
if (Confidence < 0) {
Confidence = 0;
}
}
return(FW_RESULT_FAILED);
}
/*
** Function definitions for the MIB-II entry points.
*/
BOOL (__stdcall *SnmpExtensionInitPtr)(IN DWORD dwUpTimeReference, OUT HANDLE *phSubagentTrapEvent, OUT AsnObjectIdentifier *pFirstSupportedRegion);
BOOL (__stdcall *SnmpExtensionQueryPtr)(IN BYTE bPduType, IN OUT RFC1157VarBindList *pVarBindList, OUT AsnInteger32 *pErrorStatus, OUT AsnInteger32 *pErrorIndex);
LPVOID (__stdcall *SnmpUtilMemAllocPtr)(IN DWORD bytes);
VOID (__stdcall *SnmpUtilMemFreePtr)(IN LPVOID pMem);
typedef struct tConnInfoStruct {
unsigned int State;
unsigned long LocalIP;
unsigned short LocalPort;
unsigned long RemoteIP;
unsigned short RemotePort;
} ConnInfoStruct;
/***********************************************************************************************
* Get_Local_Chat_Connection_Address -- Which address are we using to talk to the chat server? *
* *
* *
* *
* INPUT: Ptr to address to return local address * *
* *
* OUTPUT: True if success *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 10/27/00 3:24PM ST : Created *
*=============================================================================================*/
bool FirewallHelperClass::Get_Local_Chat_Connection_Address(void)
{
/*
** Local defines.
*/
enum {
CLOSED = 1,
LISTENING,
SYN_SENT,
SEN_RECEIVED,
ESTABLISHED,
FIN_WAIT,
FIN_WAIT2,
CLOSE_WAIT,
LAST_ACK,
CLOSING,
TIME_WAIT,
DELETE_TCB
};
enum {
tcpConnState = 1,
tcpConnLocalAddress,
tcpConnLocalPort,
tcpConnRemAddress,
tcpConnRemPort
};
/*
** Locals.
*/
char server_name[128];
unsigned char server_address[4];
unsigned char remote_address[4];
HANDLE trap_handle;
AsnObjectIdentifier first_supported_region;
DynamicVectorClass<ConnInfoStruct*> connection_list;
int last_field;
int index;
AsnInteger error_status;
AsnInteger error_index;
int conn_entry_type_index;
int conn_entry_type;
bool found;
unsigned int server_port = 0;
IPAddressClass my_address;
/*
** Statics.
*/
static char _conn_state[][32] = {
"?",
"CLOSED",
"LISTENING",
"SYN_SENT",
"SEN_RECEIVED",
"ESTABLISHED",
"FIN_WAIT",
"FIN_WAIT2",
"CLOSE_WAIT",
"LAST_ACK",
"CLOSING",
"TIME_WAIT",
"DELETE_TCB"
};
/*
** If we already did this then there's no need to do it again.
*/
if (LocalChatConnectionAddress.Is_Valid()) {
return(true);
}
WWDEBUG_SAY(("FirewallHelper - Finding local address used to talk to the chat server\n"));
/*
** Get the name of the current server.
*/
WOLNATInterface.Get_Current_Server_ConnData(server_name, sizeof(server_name));
if (strlen(server_name) == 0) {
return(false);
}
/*
** the conndata field will look something like
**
** TCP;ra2chat.westwood.com;7000
*/
fw_assert(strnicmp((char*)server_name, "TCP;", 4) == 0);
char *server_name_ptr = &server_name[4];
char *semi_colon_ptr = strchr(server_name_ptr, ';');
if (semi_colon_ptr) {
*semi_colon_ptr = 0;
sscanf(semi_colon_ptr+1, "%d", &server_port);
} else {
WWDEBUG_SAY(("FirewallHelper - Failed to parse server name\n"));
return(false);
}
WWDEBUG_SAY(("FirewallHelper - Current chat server name is %s\n", server_name_ptr));
WWDEBUG_SAY(("FirewallHelper - Chat server port is %d\n", server_port));
/*
** Get the address of the chat server.
*/
WWDEBUG_SAY(("FirewallHelper - About to call gethostbyname\n"));
struct hostent *host_info = gethostbyname(server_name_ptr);
if (!host_info) {
WWDEBUG_SAY(("FirewallHelper - gethostbyname failed! Error code %d\n", WSAGetLastError()));
return(false);
}
memcpy(server_address, &host_info->h_addr_list[0][0], 4);
unsigned long temp = *((unsigned long*)(&server_address[0]));
temp = ntohl(temp);
*((unsigned long*)(&server_address[0])) = temp;
WWDEBUG_SAY(("FirewallHelper - Host address is %d.%d.%d.%d\n", server_address[3], server_address[2], server_address[1], server_address[0]));
/*
** Load the MIB-II SNMP DLL.
*/
WWDEBUG_SAY(("FirewallHelper - About to load INETMIB1.DLL\n"));
HINSTANCE mib_ii_dll = LoadLibrary("inetmib1.dll");
if (mib_ii_dll == NULL) {
WWDEBUG_SAY(("FirewallHelper - Failed to load INETMIB1.DLL\n"));
return(false);
}
WWDEBUG_SAY(("FirewallHelper - About to load SNMPAPI.DLL\n"));
HINSTANCE snmpapi_dll = LoadLibrary("snmpapi.dll");
if (snmpapi_dll == NULL) {
WWDEBUG_SAY(("FirewallHelper - Failed to load SNMPAPI.DLL\n"));
FreeLibrary(mib_ii_dll);
return(false);
}
/*
** Get the function pointers into the .dll
*/
SnmpExtensionInitPtr = (int (__stdcall *)(unsigned long,void ** ,AsnObjectIdentifier *)) GetProcAddress(mib_ii_dll, "SnmpExtensionInit");
SnmpExtensionQueryPtr = (int (__stdcall *)(unsigned char,SnmpVarBindList *,long *,long *)) GetProcAddress(mib_ii_dll, "SnmpExtensionQuery");
SnmpUtilMemAllocPtr = (void *(__stdcall *)(unsigned long)) GetProcAddress(snmpapi_dll, "SnmpUtilMemAlloc");
SnmpUtilMemFreePtr = (void (__stdcall *)(void *)) GetProcAddress(snmpapi_dll, "SnmpUtilMemFree");
if (SnmpExtensionInitPtr == NULL || SnmpExtensionQueryPtr == NULL || SnmpUtilMemAllocPtr == NULL || SnmpUtilMemFreePtr == NULL) {
WWDEBUG_SAY(("FirewallHelper - Failed to get proc addresses for linked functions\n"));
FreeLibrary(snmpapi_dll);
FreeLibrary(mib_ii_dll);
return(false);
}
RFC1157VarBindList *bind_list_ptr = (RFC1157VarBindList *) SnmpUtilMemAllocPtr(sizeof(RFC1157VarBindList) + 8192);
RFC1157VarBind *bind_ptr = (RFC1157VarBind *) SnmpUtilMemAllocPtr(sizeof(RFC1157VarBind) + 128);
/*
** OK, here we go. Try to initialise the .dll
*/
WWDEBUG_SAY(("FirewallHelper - About to init INETMIB1.DLL\n"));
int ok = SnmpExtensionInitPtr(GetCurrentTime(), &trap_handle, &first_supported_region);
if (!ok) {
/*
** Aw crap.
*/
WWDEBUG_SAY(("FirewallHelper - Failed to init the .dll\n"));
SnmpUtilMemFreePtr(bind_list_ptr);
SnmpUtilMemFreePtr(bind_ptr);
FreeLibrary(snmpapi_dll);
FreeLibrary(mib_ii_dll);
return(false);
}
/*
** Name of mib_ii object we want to query. See RFC 1213.
**
** iso.org.dod.internet.mgmt.mib-2.tcp.tcpConnTable.TcpConnEntry.tcpConnState
** 1 3 6 1 2 1 6 13 1 1
*/
unsigned int mib_ii_name[] = {1,3,6,1,2,1,6,13,1,1};
unsigned int *mib_ii_name_ptr = (unsigned int *) SnmpUtilMemAllocPtr(sizeof(mib_ii_name) + 1024);
memcpy(mib_ii_name_ptr, mib_ii_name, sizeof(mib_ii_name));
/*
** Get the index of the conn entry data.
*/
conn_entry_type_index = ARRAY_SIZE(mib_ii_name) - 1;
/*
** Set up the bind list.
*/
bind_ptr->name.idLength = ARRAY_SIZE(mib_ii_name);
bind_ptr->name.ids = mib_ii_name_ptr;
bind_list_ptr->list = bind_ptr;
bind_list_ptr->len = 1;
/*
** We start with the tcpConnLocalAddress field.
*/
last_field = 1;
/*
** First connection.
*/
index = 0;
/*
** Suck out that tcp connection info....
*/
while (true) {
if (!SnmpExtensionQueryPtr(ASN_RFC1157_GETNEXTREQUEST, bind_list_ptr, &error_status, &error_index)) {
WWDEBUG_SAY(("FirewallHelper - SnmpExtensionQuery returned false\n"));
SnmpUtilMemFreePtr(bind_list_ptr);
SnmpUtilMemFreePtr(bind_ptr);
FreeLibrary(snmpapi_dll);
FreeLibrary(mib_ii_dll);
return(false);
}
/*
** If this is something new we aren't looking for then we are done.
*/
if (bind_ptr->name.idLength < ARRAY_SIZE(mib_ii_name)) {
break;
}
/*
** Get the type of info we are looking at. See RFC1213.
**
** 1 = tcpConnState
** 2 = tcpConnLocalAddress
** 3 = tcpConnLocalPort
** 4 = tcpConnRemAddress
** 5 = tcpConnRemPort
**
** tcpConnState is one of the following...
**
** 1 closed
** 2 listen
** 3 synSent
** 4 synReceived
** 5 established
** 6 finWait1
** 7 finWait2
** 8 closeWait
** 9 lastAck
** 10 closing
** 11 timeWait
** 12 deleteTCB
*/
conn_entry_type = bind_ptr->name.ids[conn_entry_type_index];
if (last_field != conn_entry_type) {
index = 0;
last_field = conn_entry_type;
}
switch (conn_entry_type) {
/*
** 1. First field in the entry. Need to create a new connection info struct
** here to store this connection in.
*/
case tcpConnState:
{
ConnInfoStruct *new_conn = new ConnInfoStruct;
new_conn->State = bind_ptr->value.asnValue.number;
connection_list.Add(new_conn);
break;
}
/*
** 2. Local address field.
*/
case tcpConnLocalAddress:
fw_assert(index < connection_list.Count());
connection_list[index]->LocalIP = *((unsigned long*)bind_ptr->value.asnValue.address.stream);
index++;
break;
/*
** 3. Local port field.
*/
case tcpConnLocalPort:
fw_assert(index < connection_list.Count());
connection_list[index]->LocalPort = bind_ptr->value.asnValue.number;
index++;
break;
/*
** 4. Remote address field.
*/
case tcpConnRemAddress:
fw_assert(index < connection_list.Count());
connection_list[index]->RemoteIP = *((unsigned long*)bind_ptr->value.asnValue.address.stream);
index++;
break;
/*
** 5. Remote port field.
*/
case tcpConnRemPort:
fw_assert(index < connection_list.Count());
connection_list[index]->RemotePort = bind_ptr->value.asnValue.number;
index++;
break;
}
}
SnmpUtilMemFreePtr(bind_list_ptr);
SnmpUtilMemFreePtr(bind_ptr);
//SnmpUtilMemFreePtr(mib_ii_name_ptr); // Don't free this - the SnmpExtensionQueryPtr call frees it apparently
WWDEBUG_SAY(("FirewallHelper - Got %d connections in list, parsing...\n", connection_list.Count()));
/*
** Right, we got the lot. Lets see if any of them have the same address as the chat
** server we think we are talking to.
*/
found = false;
while (connection_list.Count()) {
ConnInfoStruct *connection = connection_list[0];
temp = ntohl(connection->RemoteIP);
memcpy(remote_address, (unsigned char*)&temp, 4);
/*
** See if this connection has the same address as our server.
*/
if (!found && memcmp(remote_address, server_address, 4) == 0) {
WWDEBUG_SAY(("FirewallHelper - Found connection with same remote address as server\n"));
if (server_port == 0 || server_port == (unsigned int)connection->RemotePort) {
WWDEBUG_SAY(("FirewallHelper - Connection has same port\n"));
/*
** Make sure the connection is current.
*/
if (connection->State == ESTABLISHED) {
WWDEBUG_SAY(("FirewallHelper - Connection is ESTABLISHED\n"));
my_address.Set_Address((unsigned char*)&connection->LocalIP, connection->LocalPort);
found = true;
} else {
WWDEBUG_SAY(("FirewallHelper - Connection is not ESTABLISHED - skipping\n"));
}
} else {
WWDEBUG_SAY(("FirewallHelper - Connection has different port. Port is %d, looking for %d\n", connection->RemotePort, server_port));
}
}
/*
** Free the memory.
*/
delete connection_list[0];
connection_list.Delete(0);
}
if (found) {
WWDEBUG_SAY(("FirewallHelper - Using address %s to talk to chat server\n", my_address.As_String()));
LocalChatConnectionAddress = my_address;
}
FreeLibrary(snmpapi_dll);
FreeLibrary(mib_ii_dll);
ThreadLockClass locker(this);
ThreadState = THREAD_GET_LOCAL_ADDRESS_DONE;
return(found);
}
/***********************************************************************************************
* FirewallHelperClass::Get_Local_Address -- Get local chat address ip as a long *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Local IP - 0 if unknown *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 10/11/2001 4:30PM ST : Created *
*=============================================================================================*/
unsigned long FirewallHelperClass::Get_Local_Address(void)
{
unsigned long ip = 0;
if (LocalChatConnectionAddress.Is_Valid()) {
ip = LocalChatConnectionAddress.Get_Address();
ip = htonl(ip);
}
return(ip);
}
/***********************************************************************************************
* FirewallHelperClass::Get_Raw_Firewall_Behavior -- Get firewall behavior bits *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Firewall behavior *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/3/2001 8:50PM ST : Created *
*=============================================================================================*/
unsigned short FirewallHelperClass::Get_Raw_Firewall_Behavior(void)
{
unsigned short behave = (unsigned short)Behavior;
/*
** If we are forcing a particular port to be used then set the behavior type to be as if there was no firewall there at all.
*/
if (WOLNATInterface.Get_Force_Port() != 0) {
behave = (unsigned short) FIREWALL_TYPE_SIMPLE;
}
return(behave);
}