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.h

708 lines
No EOL
19 KiB
C++

/*
** 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.h $*
* *
* $Author:: Steve_t $*
* *
* $Modtime:: 3/26/02 12:16p $*
* *
* $Revision:: 19 $*
* *
* *
*---------------------------------------------------------------------------------------------*
* *
* *
*---------------------------------------------------------------------------------------------*
* *
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#pragma once
#ifndef NAT_H
#define NAT_H
#include "always.h"
#include "win.h"
#ifdef WWASSERT
#ifndef fw_assert
#define fw_assert WWASSERT
#endif //fw_assert
#else //WWASSERT
#define fw_assert assert
#endif //WWASSERT
#include "nataddr.h"
#include "natter.h"
#include "wolapi.h"
/*
** Number of ports to use when testing port mangling sequences.
*/
#define NUM_TEST_PORTS 4
#ifndef IPAddressClass
class IPAddressClass;
#endif //IPAddressClass
#ifndef SocketHandlerClass
class SocketHandlerClass;
#endif //SocketHandlerClass
/*
**
** Class to help in dealing with firewalls.
**
** Basically detects firewall type and behavior.
**
** In Renegade it also does the firewall port negotiation between server and client.
*/
class FirewallHelperClass {
public:
/*
** Enumeration of firewall behaviors we can detect.
**
** It is assumed that all port mangling firewalls change the mangled source port in response to
** an application source port change.
*/
typedef enum tFirewallBehaviorType {
/*
** Just used as an initialiser.
*/
FIREWALL_TYPE_UNKNOWN = 0,
/*
** This is a simple, non-port translating firewall, or there is no firewall at all.
*/
FIREWALL_TYPE_SIMPLE = 1,
/*
** This is a firewall/NAT with port mangling but it's pretty dumb - it uses the same mangled
** source port regardless of the destination address.
*/
FIREWALL_TYPE_DUMB_MANGLING = 2,
/*
** This is a smarter firewall/NAT with port mangling that uses different mangled source ports
** for different destination IPs.
*/
FIREWALL_TYPE_SMART_MANGLING = 4,
/*
** This is a firewall that exhibits the bug as seen in the Netgear firewalls. A previously good
** source port mapping will change in response to unsolicited traffic from a known IP.
*/
FIREWALL_TYPE_NETGEAR_BUG = 8,
/*
** This firewall has a simple absolute offset port allocation scheme.
*/
FIREWALL_TYPE_SIMPLE_PORT_ALLOCATION = 16,
/*
** This firewall has a relative offset port allocation scheme. For these firewalls, we have to
** subtract the actual source port from the mangled source port to discover the allocation scheme.
** The mangled port number is based in part on the original source port number.
*/
FIREWALL_TYPE_RELATIVE_PORT_ALLOCATION = 32,
/*
** This firewall mangles source ports differently depending on the destination port.
*/
FIREWALL_TYPE_DESTINATION_PORT_DELTA = 64
} FirewallBehaviorType;
/*
** Constructor, destructor.
*/
FirewallHelperClass(void);
~FirewallHelperClass(void);
/*
** Startup, shutdown.
*/
void Startup(void);
void Shutdown(void);
/*
** Detection.
*/
void Detect_Firewall(HANDLE event = INVALID_HANDLE_VALUE);
unsigned short Get_Raw_Firewall_Behavior(void); // {return((unsigned short)Behavior);};
short Get_Source_Port_Allocation_Delta(void) {return(SourcePortAllocationDelta);}
/*
** Query class for behavior.
*/
unsigned short Get_Next_Mangled_Source_Port(unsigned short source_port);
int Get_Firewall_Hardness(FirewallBehaviorType behavior);
int Get_Firewall_Retries(FirewallBehaviorType behavior);
void Set_Source_Port_Pool_Start(int port) {SourcePortPool = port;};
int Get_Source_Port_Pool(void) {return(SourcePortPool);};
/*
** Talking to the manglers.
*/
int Build_Mangler_Packet(unsigned char *buffer, unsigned short port, unsigned long packet_id = 0, bool blitzme = false);
/*
** Port management.
*/
unsigned short Get_Next_Temporary_Source_Port(int skip);
bool Get_Reference_Port(void);
void Reset_Server(void);
unsigned short Get_Client_Bind_Port(void) {return(ClientPort);}; //PlayersFirewallAddress.Get_Port());};
/*
** Firewall info import and export.
*/
void Set_Firewall_Info(unsigned long last_behavior, int last_delta, unsigned short port_pool, bool send_delay, int confidence);
void Get_Firewall_Info(unsigned long &last_behavior, int &last_delta, unsigned short &port_pool, bool &send_delay, int &confidence) const;
void Set_Send_Delay(bool send_delay) {SendDelay = send_delay;};
bool Get_Send_Delay(void) {return(SendDelay);};
/*
** Communications functions.
*/
bool Send_To_Mangler(IPAddressClass *address, SocketHandlerClass *socket_handler, unsigned long packet_id, bool blitzme = false);
unsigned short Get_Mangler_Response(unsigned long packet_id, SocketHandlerClass *socket_handler, int time = 0, bool all_service = false);
/*
** Server connection negotiation functions.
*/
void Connected_To_WWOnline_Server(void);
void Talk_To_New_Player(WOL::User *user);
void Send_My_Port(unsigned short port);
void Set_Client_Connect_Event(HANDLE thread_event, HANDLE cancel_event, int *flag_ptr, int *queue_ptr);
bool Remove_Player_From_Negotiation_Queue(char *player_name);
bool Remove_Player_From_Negotiation_Queue_If_Mutex_Available(char *player_name);
void Cleanup_Client_Queue(void);
/*
** Get the local chat connection address.
*/
bool Get_Local_Chat_Connection_Address(void);
unsigned long Get_Local_Address(void);
IPAddressClass &Get_External_Address(void) {return(ExternalAddress);}
void Set_External_Address(IPAddressClass &addr) {ExternalAddress = addr;}
/*
** Reset.
*/
void Reset(void);
/*
** Query.
*/
bool Is_Busy(void) {return((ThreadState != THREAD_IDLE) ? true : false);}
/*
** Inline behavior query functions.
*/
inline bool Is_NAT(void) {
if (Behavior == FIREWALL_TYPE_UNKNOWN || (Behavior & FIREWALL_TYPE_SIMPLE) != 0) {
return(false);
}
return(true);
};
inline bool Is_NAT(FirewallBehaviorType behavior) {
if (behavior == FIREWALL_TYPE_UNKNOWN || (behavior & FIREWALL_TYPE_SIMPLE) != 0) {
return(false);
}
return(true);
};
inline bool Is_Netgear(FirewallBehaviorType behavior) {
if ((behavior & FIREWALL_TYPE_NETGEAR_BUG) != 0) {
return(true);
}
return(false);
};
inline bool Is_Netgear(void) {
if ((Behavior & FIREWALL_TYPE_NETGEAR_BUG) != 0) {
return(true);
}
return(false);
};
/*
** Exposing the thread ID for the exception handler.
*/
unsigned long Get_Thread_ID(void) {return(ThreadID);};
/*
** Connection results reported back to the dialog wait object.
*/
enum {
FW_RESULT_UNKNOWN,
FW_RESULT_FAILED,
FW_RESULT_SUCCEEDED,
FW_RESULT_CANCELLED
};
private:
/************************************************************************************************************************
**
** Private functions.
**
*/
/*
** Detection.
*/
FirewallBehaviorType Detect_Firewall_Behavior(void);
int Get_NAT_Port_Allocation_Scheme(int num_ports, unsigned short *original_ports, unsigned short *mangled_ports, bool &relative_delta, bool &looks_good);
/*
** Server connection negotiation functions.
*/
int Negotiate_Port(void);
void Send_Connection_Result(int result, unsigned short port);
void Set_Client_Success(int success);
void Send_Queue_States(void);
bool Client_Cancelled(void);
bool Remote_Client_Cancelled(void);
void Send_Cancel_Notification(void);
/*
** Threading.
*/
static unsigned int __stdcall NAT_Thread_Start(void *param);
unsigned long NAT_Thread_Main_Loop(void);
void Add_Thread_Action(int thread_action, HANDLE thread_event);
void Set_Thread_Event(void);
/*
** Game options processing.
*/
void Process_Game_Options(void);
/************************************************************************************************************************
**
** Private data.
**
*/
/*
** How does our firewall behave?
*/
FirewallBehaviorType Behavior;
/*
** How did the firewall behave the last time we ran the game.
*/
FirewallBehaviorType LastBehavior;
/*
** What is the delta in our firewalls NAT port allocation scheme.
*/
int SourcePortAllocationDelta;
/*
** What was the delta the last time we ran?
*/
int LastSourcePortAllocationDelta;
/*
** Source ports used only to discover port allocation patterns.
*/
int SourcePortPool;
/*
** Should we delay before sending (for Netgear bug)?
*/
bool SendDelay;
/*
** How well do the remembered settings work.
*/
int Confidence;
/*
** Mangler server info.
*/
int NumManglerServers;
char ManglerServerAddress[64][128];
int ManglerServerPort[64];
int CurrentManglerServer;
/*
** Local chat connection address.
*/
IPAddressClass LocalChatConnectionAddress;
/*
** Our address as far as chat knows.
*/
IPAddressClass ExternalAddress;
/************************************************************************************************************************
**
** Server/Client connection related private data.
**
**
*/
/*
** Who we are currently trying to talk to.
*/
char PlayersName[64];
/*
** Local address of the player we are trying to talk to.
*/
IPAddressClass PlayersLocalAddress;
/*
** External (mangler view) address of the player we are trying to talk to.
*/
IPAddressClass PlayersExternalAddress;
/*
** Type of firewall that the other player has.
*/
FirewallBehaviorType PlayersFirewallType;
/*
** Teacks what we expect the other player to have his port number mangled to.
*/
unsigned short PlayersMangledPort;
/*
** Other players in WOL::User struct form.
*/
WOL::User PlayerAsUser;
/*
** Our current best guess at the combined mangled port/ip of the other player.
*/
IPAddressClass PlayersFirewallAddress;
/*
** Connection result reported by other player. Used to find out if our packet got through. See enum below.
*/
int PlayersConnectionResult;
/*
** Port that the other player saw our packet actually come from.
*/
unsigned short PlayersConnectionResultPort;
/*
** Enum for connection results.
*/
enum {
CONNRESULT_UNTRIED = -1,
CONNRESULT_FAILED = 0,
CONNRESULT_TRY1,
CONNRESULT_TRY2,
CONNRESULT_TRY3,
CONNRESULT_TRY4,
CONNRESULT_TRY5,
CONNRESULT_TRY6,
CONNRESULT_TRY7,
CONNRESULT_TRY8,
CONNRESULT_TRY9,
CONNRESULT_TRY10,
CONNRESULT_CONNECTED
};
/************************************************************************************************************************
**
** Server side only data.
**
*/
/*
** Client queue (for server only). This is a list of clients waiting to negotiate a port.
*/
typedef struct tClientStruct {
char Name[64];
IPAddressClass LocalAddress;
IPAddressClass ExternalAddress;
FirewallBehaviorType FirewallType;
WOL::User User;
} ClientStruct;
DynamicVectorClass<ClientStruct*> ClientQueue;
/*
** List if names waiting to be removed from the client queue.
*/
DynamicVectorClass<ClientStruct*> ClientQueueRemoveList;
/*
** Connections we have made so far in this game. This includes people who were in the game and left.
*/
DynamicVectorClass<IPAddressClass> ConnectionHistory;
DynamicVectorClass<unsigned short> MangledPortHistory;
/*
** When we last heard from the client.
*/
unsigned long LastOptionsFromClient;
/*
** Name of player who has cancelled out of our game channel before connecting.
*/
char CancelPlayer[64];
/************************************************************************************************************************
**
** Client side only data.
**
*/
/*
** Number of players in the servers queue ahead of us.
*/
int QueuedPlayers;
/*
** Port that the client will use as a basis for negotiation.
*/
unsigned short ClientPort;
/*
** Client connect event notification.
*/
int *SuccessFlagPtr;
HANDLE ClientConnectEvent;
HANDLE ClientCancelEvent;
int *QueueNotifyPtr;
/************************************************************************************************************************
**
** Threading.
**
** This class runs in it's own thread so that firewall negotiation doesn't stall the server when a new client joins.
** It also means that the clients join dialog is serviced while connecting, allowing him to cancel at any time.
**
*/
HANDLE ThreadHandle;
unsigned long ThreadID;
HANDLE NATThreadMutex;
HANDLE NATDataMutex;
bool ThreadActive;
/*
** Thread actions.
*/
enum {
THREAD_IDLE,
THREAD_DETECT_FIREWALL,
THREAD_DETECT_FIREWALL_DONE,
THREAD_CONNECT_FIREWALL,
THREAD_CONNECT_FIREWALL_DONE,
THREAD_GET_LOCAL_ADDRESS,
THREAD_GET_LOCAL_ADDRESS_DONE
};
/*
** Thread state i.e. what it's doing.
*/
int ThreadState;
/*
** Event to be signalled when the current thread action completes.
*/
HANDLE ThreadEvent;
/*
** Thread action FIFO
*/
class ThreadActionClass {
public:
inline bool operator == (ThreadActionClass const &data) {
return(memcmp((void*)this, &data, sizeof(*this)) == 0);
};
inline bool operator != (ThreadActionClass const &data) {
return(memcmp((void*)this, &data, sizeof(*this)) != 0);
};
int ThreadAction;
HANDLE ThreadEvent;
};
DynamicVectorClass<ThreadActionClass> ThreadQueue;
/*
** Thread safety.
*/
class ThreadLockClass
{
public:
/*
** Constructor. Grabs the mutex.
*/
inline ThreadLockClass(FirewallHelperClass *fwptr, unsigned long timeout = 10 * 1000) {
FWPtr = fwptr;
/*
** Just test the mutex if timeout is 0.
*/
WaitResult = WaitForSingleObject(fwptr->NATDataMutex, timeout);
if (timeout != 0 && WaitResult == WAIT_TIMEOUT) {
WWDEBUG_SAY(("FirewallHelper - Timeout waiting for firewall helper data mutex\n"));
fw_assert(WaitResult != WAIT_TIMEOUT);
}
};
FirewallHelperClass *FWPtr;
int WaitResult;
/*
** Destructor, releases the mutex.
*/
inline ~ThreadLockClass(void) {
ReleaseMutex(FWPtr->NATDataMutex);
};
};
friend ThreadLockClass;
/*****************************************************************************************************************
**
**
** C&C Packet format as expected by the mangler. Copied from various places in the RA2 code.
**
*/
public:
/*
** One byte alignment.
*/
#pragma pack(push, 1)
/*
** Misc C&C packet defines.
*/
#define GLOBAL_MAGICNUM 0x1236
#define PACKET_DATA_NOACK 1
#define COMMAND_AND_CONQUER_RA2 0xaa03
#define NET_MANGLER_REQUEST 43
#define NET_MANGLER_RESPONSE 44
#define MPLAYER_NAME_MAX 20
#define SERIAL_MAX 23
/*
** CommHeaderType - Low level packet wrapper.
*/
struct CommHeaderType {
unsigned short MagicNumber; // in, out = GLOBAL_MAGICNUM = 0x1236. Just preserve the incoming value.
char Code; // in, out = PACKET_DATA_NOACK = 1
union {
unsigned char ForwardTo; // = 0
unsigned char ForwardFrom;
};
unsigned long PacketID; // Dont care.
unsigned char ForwardAddress[4];
unsigned short ForwardPort;
};
/*
** GlobalHeaderType - Low level packet wrapper for global channel packets.
*/
struct GlobalHeaderType {
CommHeaderType Header; // See above
unsigned short ProductID; // in, out = COMMAND_AND_CONQUER_RA2 = 0xaa03. Just return the value from the received packet
};
/*
** GlobalPacketType - Payload of global channel packet.
*/
struct GlobalPacketType {
int Command; // in = NET_MANGLER_REQUEST = 43 , out = NET_MANGLER_RESPONSE = 44
char Name[MPLAYER_NAME_MAX]; // in, out = Player name (8 bit) - return same name
char Serial[SERIAL_MAX]; // Not used.
union
{
struct {
unsigned short MangledPortNumber;
unsigned char MangledAddress[4];
unsigned short OriginalPortNumber;
unsigned char BlitzMe;
} ManglerData;
};
};
/*
** CnCPacketType - Header and payload in one structure.
*/
typedef struct tCnCPacketType {
GlobalHeaderType GHeader;
GlobalPacketType Packet;
} CnCPacketType;
#pragma pack(pop)
/*
** End C&C packet format.
****************************************************************************************************************
*/
};
/*
** Single instance of FirewallHelperClass
*/
extern FirewallHelperClass FirewallHelper;
#endif //NAT_H