/* ** Command & Conquer Generals(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 . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: NAT.h ///////////////////////////////////////////////////////////////////////////////// // Author: Bryan Cleveland April 2002 // Desc: Resolves NAT'd IPs and port numbers for the other players in a game. /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once #ifndef __NAT_H #define __NAT_H #include "Lib\BaseType.h" #include "GameNetwork/NetworkInterface.h" #include "GameNetwork/FirewallHelper.h" class Transport; class GameSlot; enum NATStateType { NATSTATE_IDLE, NATSTATE_DOCONNECTIONPATHS, NATSTATE_WAITFORSTATS, NATSTATE_DONE, NATSTATE_FAILED }; enum NATConnectionState { NATCONNECTIONSTATE_NOSTATE, NATCONNECTIONSTATE_WAITINGTOBEGIN, // NATCONNECTIONSTATE_NETGEARDELAY, NATCONNECTIONSTATE_WAITINGFORMANGLERRESPONSE, NATCONNECTIONSTATE_WAITINGFORMANGLEDPORT, NATCONNECTIONSTATE_WAITINGFORRESPONSE, NATCONNECTIONSTATE_DONE, NATCONNECTIONSTATE_FAILED }; struct ConnectionNodeType { FirewallHelperClass::tFirewallBehaviorType m_behavior; ///< the NAT/Firewall behavior of this node. UnsignedInt m_slotIndex; ///< the player list index of this node. }; class NAT { public: NAT(); virtual ~NAT(); NATStateType update(); void attachSlotList(GameSlot **slotList, Int localSlot, UnsignedInt localIP); void establishConnectionPaths(); Int getSlotPort(Int slot); Transport * getTransport(); ///< return the newly created Transport layer that has all the connections and whatnot. // Notification messages from GameSpy void processGlobalMessage(Int slotNum, const char *options); protected: NATConnectionState connectionUpdate(); ///< the update function for the connections. void sendMangledSourcePort(); ///< starts the process to get the next mangled source port. void processManglerResponse(UnsignedShort mangledPort); Bool allConnectionsDoneThisRound(); Bool allConnectionsDone(); void generatePortNumbers(GameSlot **slotList, Int localSlot); ///< generate all of the slots' port numbers to be used. void doThisConnectionRound(); ///< compute who will connect with who for this round. void setConnectionState(Int nodeNumber, NATConnectionState state); ///< central point for changing a connection's state. void sendAProbe(UnsignedInt ip, UnsignedShort port, Int fromNode); ///< send a "PROBE" packet to this IP and port. void notifyTargetOfProbe(GameSlot *targetSlot); void notifyUsersOfConnectionDone(Int nodeIndex); void notifyUsersOfConnectionFailed(Int nodeIndex); void sendMangledPortNumberToTarget(UnsignedShort mangledPort, GameSlot *targetSlot); void probed(Int nodeNumber); void gotMangledPort(Int nodeNumber, UnsignedShort mangledPort); void gotInternalAddress(Int nodeNumber, UnsignedInt address); void connectionComplete(Int slotIndex); void connectionFailed(Int slotIndex); Transport *m_transport; GameSlot **m_slotList; NATStateType m_NATState; Int m_localNodeNumber; ///< The node number of the local player. Int m_targetNodeNumber; ///< The node number of the player we are connecting to this round. UnsignedInt m_localIP; ///< The IP of the local computer. UnsignedInt m_numNodes; ///< The number of players we have to connect together. UnsignedInt m_connectionRound; ///< The "round" of connections we are currently on. Int m_numRetries; Int m_maxNumRetriesAllowed; UnsignedShort m_packetID; UnsignedShort m_spareSocketPort; time_t m_manglerRetryTime; Int m_manglerRetries; UnsignedShort m_previousSourcePort; Bool m_beenProbed; ///< have I been notified that I've been probed this round? UnsignedInt m_manglerAddress; time_t m_timeTillNextSend; ///< The number of milliseconds till we send to the other guy's port again. NATConnectionState m_connectionStates[MAX_SLOTS]; ///< connection states for this round for all the nodes. ConnectionNodeType m_connectionNodes[MAX_SLOTS]; ///< info regarding the nodes that are being connected. UnsignedShort m_sourcePorts[MAX_SLOTS]; ///< the source ports that the other players communicate to us on. Bool m_myConnections[MAX_SLOTS]; ///< keeps track of all the nodes I've connected to. For keepalive. time_t m_nextKeepaliveTime; ///< the next time we will send out our keepalive packets. static Int m_connectionPairs[MAX_SLOTS-1][MAX_SLOTS-1][MAX_SLOTS]; Int m_connectionPairIndex; UnsignedShort m_startingPortNumber; ///< the starting port number for this game. The slots all get port numbers with their port numbers based on this number. ///< this is done so that games that are played right after each other with the same players in the same ///< slot order will not use the old source port allocation scheme in case their NAT ///< hasn't timed out that connection. time_t m_nextPortSendTime; ///< Last time we sent our mangled port number to our target this round. time_t m_timeoutTime; ///< the time at which we will time out waiting for the other player's port number. time_t m_roundTimeout; ///< the time at which we will time out this connection round. static Int m_timeBetweenRetries; // 1 second between retries sounds good to me. static time_t m_manglerRetryTimeInterval; // sounds good to me. static Int m_maxAllowedManglerRetries; // works for me. static time_t m_keepaliveInterval; // 15 seconds between keepalive packets seems good. static time_t m_timeToWaitForPort; // wait for ten seconds for the other player's port number. static time_t m_timeForRoundTimeout; // wait for at most ten seconds for each connection round to finish. }; extern NAT *TheNAT; #endif // #ifndef __NAT_H