| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708 |
- /*
- ** 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
|