Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Core/GameEngine/Include/Common/OptionPreferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,7 @@ class OptionPreferences : public UserPreferences
Bool getScreenEdgeScrollEnabledInFullscreenApp() const;
ScreenEdgeScrollMode getScreenEdgeScrollMode() const;
Bool getSendDelay();
Int getFirewallBehavior();
Short getFirewallPortAllocationDelta();
UnsignedShort getFirewallPortOverride();
Bool getFirewallNeedToRefresh();
Bool usesSystemMapDir();
AsciiString getPreferred3DProvider();
AsciiString getSpeakerType();
Expand Down Expand Up @@ -132,4 +129,8 @@ class OptionPreferences : public UserPreferences
Bool getShowMoneyPerMinute() const;

Real getGameWindowTransitionSpeedMultiplier() const;

private:

void setIPAddress(AsciiString key, AsciiString IP);
};
13 changes: 8 additions & 5 deletions Core/GameEngine/Include/GameNetwork/FirewallHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,21 +175,24 @@ class FirewallHelperClass {
FirewallHelperClass();
virtual ~FirewallHelperClass();
Bool detectFirewall();
void detectFirewallBehavior();
UnsignedShort getRawFirewallBehavior() {return((UnsignedShort)m_behavior);}
Short getSourcePortAllocationDelta();
Int getFirewallHardness(FirewallBehaviorType behavior);
Int getFirewallRetries(FirewallBehaviorType behavior);
void setSourcePortPoolStart(Int port) {m_sourcePortPool = port;};
Int getSourcePortPool() {return(m_sourcePortPool);};
void readFirewallBehavior();
void reset();
Bool behaviorDetectionUpdate();

FirewallBehaviorType getFirewallBehavior();
void writeFirewallBehavior();

void flagNeedToRefresh(Bool flag);

Bool isBehaviorDetectionComplete() {return(m_currentState == DETECTIONSTATE_DONE);}
FirewallBehaviorType getLastFirewallBehavior() {return(m_lastBehavior);}
Short getLastSourcePortAllocationDelta() {return((Short)m_lastSourcePortAllocationDelta);}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can all be const


static void getManglerName(Int manglerIndex, Char *nameBuf);
Bool sendToManglerFromPort(UnsignedInt address, UnsignedShort port, UnsignedShort packetID, Bool blitzme = FALSE);
UnsignedShort getManglerResponse(UnsignedShort packetID, Int time = 0);
Expand Down Expand Up @@ -239,12 +242,9 @@ class FirewallHelperClass {
return(FALSE);
};



private:

Int getNATPortAllocationScheme(Int numPorts, UnsignedShort *originalPorts, UnsignedShort *mangledPorts, Bool &relativeDelta, Bool &looksGood);
void detectFirewallBehavior(/*Bool &canRecord*/);
Bool getReferencePort();

SpareSocketStruct * findSpareSocketByPort(UnsignedShort port);
Expand Down Expand Up @@ -305,3 +305,6 @@ class FirewallHelperClass {

extern FirewallHelperClass *TheFirewallHelper;
FirewallHelperClass * createFirewallHelper();

FirewallHelperClass::FirewallBehaviorType getBestKnownFirewallBehavior();
Short getBestKnownSourcePortAllocationDelta();
68 changes: 20 additions & 48 deletions Core/GameEngine/Source/Common/OptionPreferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "GameLogic/ScriptEngine.h"

#include "GameNetwork/IPEnumeration.h"
#include "GameNetwork/FirewallHelper.h"

OptionPreferences::OptionPreferences()
{
Expand Down Expand Up @@ -140,16 +141,27 @@ UnsignedInt OptionPreferences::getLANIPAddress()
return TheGlobalData->m_defaultIP;
}

void OptionPreferences::setIPAddress(AsciiString key, AsciiString IP)
{
if ((*this)[key].compareNoCase(IP) == 0)
return;

(*this)[key] = IP;

if (TheFirewallHelper != nullptr)
TheFirewallHelper->flagNeedToRefresh(TRUE);

@xezon xezon Jul 26, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this class is not intended to set state elsewhere in the program. Can this be implemented differently to keep the focus of this class for writing options?

}

void OptionPreferences::setLANIPAddress(AsciiString IP)
{
(*this)["IPAddress"] = IP;
setIPAddress("IPAddress", IP);
}

void OptionPreferences::setLANIPAddress(UnsignedInt IP)
{
AsciiString tmp;
tmp.format("%d.%d.%d.%d", PRINTF_IP_AS_4_INTS(IP));
(*this)["IPAddress"] = tmp;
AsciiString ipString;
ipString.format("%d.%d.%d.%d", PRINTF_IP_AS_4_INTS(IP));
setLANIPAddress(ipString);
}

UnsignedInt OptionPreferences::getOnlineIPAddress()
Expand All @@ -170,14 +182,14 @@ UnsignedInt OptionPreferences::getOnlineIPAddress()

void OptionPreferences::setOnlineIPAddress(AsciiString IP)
{
(*this)["GameSpyIPAddress"] = IP;
setIPAddress("GameSpyIPAddress", IP);
}

void OptionPreferences::setOnlineIPAddress(UnsignedInt IP)
{
AsciiString tmp;
tmp.format("%d.%d.%d.%d", PRINTF_IP_AS_4_INTS(IP));
(*this)["GameSpyIPAddress"] = tmp;
AsciiString ipString;
ipString.format("%d.%d.%d.%d", PRINTF_IP_AS_4_INTS(IP));
setOnlineIPAddress(ipString);
}

Bool OptionPreferences::getArchiveReplaysEnabled() const
Expand Down Expand Up @@ -463,31 +475,6 @@ Bool OptionPreferences::getSendDelay()
return FALSE;
}

Int OptionPreferences::getFirewallBehavior()
{
OptionPreferences::const_iterator it = find("FirewallBehavior");
if (it == end())
return TheGlobalData->m_firewallBehavior;

Int behavior = atoi(it->second.str());
if (behavior < 0)
{
behavior = 0;
}
return behavior;
}

Short OptionPreferences::getFirewallPortAllocationDelta()
{
OptionPreferences::const_iterator it = find("FirewallPortAllocationDelta");
if (it == end()) {
return TheGlobalData->m_firewallPortAllocationDelta;
}

Short delta = atoi(it->second.str());
return delta;
}

UnsignedShort OptionPreferences::getFirewallPortOverride()
{
OptionPreferences::const_iterator it = find("FirewallPortOverride");
Expand All @@ -501,21 +488,6 @@ UnsignedShort OptionPreferences::getFirewallPortOverride()
return portOverride;
}

Bool OptionPreferences::getFirewallNeedToRefresh()
{
OptionPreferences::const_iterator it = find("FirewallNeedToRefresh");
if (it == end()) {
return FALSE;
}

Bool retval = FALSE;
AsciiString str = it->second;
if (str.compareNoCase("TRUE") == 0) {
retval = TRUE;
}
return retval;
}

AsciiString OptionPreferences::getPreferred3DProvider()
{
OptionPreferences::const_iterator it = find("3DAudioProvider");
Expand Down
153 changes: 69 additions & 84 deletions Core/GameEngine/Source/GameNetwork/FirewallHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,55 @@
#include "GameNetwork/NAT.h"
#include "GameNetwork/udp.h"
#include "GameNetwork/NetworkDefs.h"
#include "GameNetwork/IPEnumeration.h"
#include "GameNetwork/GameSpy/GSConfig.h"


FirewallHelperClass *TheFirewallHelper = nullptr;

FirewallHelperClass * createFirewallHelper()
{
return NEW FirewallHelperClass();
FirewallHelperClass *helper = NEW FirewallHelperClass();
Comment thread
githubawn marked this conversation as resolved.
helper->detectFirewallBehavior();
return helper;
}

FirewallHelperClass::FirewallBehaviorType getBestKnownFirewallBehavior()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All it does is interact with FirewallHelperClass. Can this be member of FirewallHelperClass?

{
if (TheFirewallHelper != nullptr)
{
if (TheFirewallHelper->isBehaviorDetectionComplete()
&& TheFirewallHelper->getFirewallBehavior() != FirewallHelperClass::FIREWALL_TYPE_UNKNOWN)
{
return TheFirewallHelper->getFirewallBehavior();
}

if (TheFirewallHelper->getLastFirewallBehavior() != FirewallHelperClass::FIREWALL_TYPE_UNKNOWN)
{
return TheFirewallHelper->getLastFirewallBehavior();
}
Comment on lines +80 to +83

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Refresh publishes stale NAT state

When the selected IP changes after an earlier probe completed, this fallback publishes the previous interface's NAT behavior while replacement detection is still running. The matching delta fallback is then consumed by NAT port prediction, causing peers to use the wrong traversal strategy or probe the wrong port until connection timeout.

Knowledge Base Used: GameNetwork: Multiplayer Networking

Prompt To Fix With AI
This is a comment left during a code review.
Path: Core/GameEngine/Source/GameNetwork/FirewallHelper.cpp
Line: 80-83

Comment:
**Refresh publishes stale NAT state**

When the selected IP changes after an earlier probe completed, this fallback publishes the previous interface's NAT behavior while replacement detection is still running. The matching delta fallback is then consumed by NAT port prediction, causing peers to use the wrong traversal strategy or probe the wrong port until connection timeout.

**Knowledge Base Used:** [GameNetwork: Multiplayer Networking](https://app.greptile.com/thesuperhackers/-/custom-context/knowledge-base/thesuperhackers/generalsgamecode/-/docs/gamenetwork-multiplayer.md)

How can I resolve this? If you propose a fix, please make it concise.

}

return FirewallHelperClass::FIREWALL_TYPE_UNKNOWN;
Comment thread
greptile-apps[bot] marked this conversation as resolved.
}

Short getBestKnownSourcePortAllocationDelta()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All it does is interact with FirewallHelperClass. Can this be member of FirewallHelperClass?

{
if (TheFirewallHelper != nullptr)
{
if (TheFirewallHelper->isBehaviorDetectionComplete()
&& TheFirewallHelper->getFirewallBehavior() != FirewallHelperClass::FIREWALL_TYPE_UNKNOWN)
{
return TheFirewallHelper->getSourcePortAllocationDelta();
}

if (TheFirewallHelper->getLastFirewallBehavior() != FirewallHelperClass::FIREWALL_TYPE_UNKNOWN)
{
return TheFirewallHelper->getLastSourcePortAllocationDelta();
}
}

return 0;
}


Expand Down Expand Up @@ -176,22 +217,12 @@ void FirewallHelperClass::reset()
*=============================================================================================*/
Bool FirewallHelperClass::detectFirewall()
{
OptionPreferences pref;

OptionPreferences::const_iterator it = pref.find("FirewallNeedToRefresh");
if (it != pref.end()) {
AsciiString str = it->second;
if (str.compareNoCase("TRUE") == 0) {
TheWritableGlobalData->m_firewallBehavior = FIREWALL_TYPE_UNKNOWN;
}
}

if (TheWritableGlobalData->m_firewallBehavior == FIREWALL_TYPE_UNKNOWN) {
if (m_behavior == FIREWALL_TYPE_UNKNOWN) {
detectFirewallBehavior();

return FALSE;
} else {
DEBUG_LOG(("FirewallHelperClass::detectFirewall - firewall behavior already specified as %d, port allocation delta is %d, skipping detection.", TheWritableGlobalData->m_firewallBehavior, TheWritableGlobalData->m_firewallPortAllocationDelta));
DEBUG_LOG(("FirewallHelperClass::detectFirewall - firewall behavior already specified as %d, port allocation delta is %d, skipping detection.", m_behavior, m_sourcePortAllocationDelta));
}

return TRUE;
Expand Down Expand Up @@ -480,44 +511,6 @@ UnsignedShort FirewallHelperClass::getManglerResponse(UnsignedShort packetID, In
return mangled_port;
}




/***********************************************************************************************
* FirewallHelperClass::Write_Firewall_Settings -- Save out firewall settings. *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/22/01 10:23PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::writeFirewallBehavior()
{
OptionPreferences pref;

char num[16];
num[0] = 0;
itoa(TheGlobalData->m_firewallBehavior, num, 10);
AsciiString numstr;
numstr = num;
(pref)["FirewallBehavior"] = numstr;

TheWritableGlobalData->m_firewallPortAllocationDelta = getSourcePortAllocationDelta();
num[0] = 0;
itoa(TheGlobalData->m_firewallPortAllocationDelta, num, 10);
numstr = num;
(pref)["FirewallPortAllocationDelta"] = numstr;

pref.write();
}


/***********************************************************************************************
* FirewallHelperClass::flagNeedToRefresh -- Flag that the next time we log in we need to *
* refresh our firewall settings. *
Expand All @@ -535,38 +528,11 @@ void FirewallHelperClass::writeFirewallBehavior()
*=============================================================================================*/
void FirewallHelperClass::flagNeedToRefresh(Bool flag)
{
Comment thread
greptile-apps[bot] marked this conversation as resolved.
OptionPreferences pref;

(pref)["FirewallNeedToRefresh"] = flag ? "TRUE" : "FALSE";

pref.write();
}


/***********************************************************************************************
* FirewallHelperClass::Read_Firewall_Behavior -- Read in old firewall settings *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/22/01 10:25PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::readFirewallBehavior()
{
#if (0)
m_lastBehavior = (FirewallBehaviorType) ConfigINI.Get_Int("MultiPlayer", "FirewallSettings", FIREWALL_UNKNOWN);
m_lastSourcePortAllocationDelta = ConfigINI.Get_Int("MultiPlayer", "FirewallDelta", 1);
#endif //(0)
if (flag) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This flag argument looks very useless. Can be removed. "Munkee" text above needs updating then.

detectFirewallBehavior();
}
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.



/***********************************************************************************************
* FHC::detectFirewallBehavior -- What is that wacky firewall doing to our packet headers? *
* *
Expand All @@ -581,15 +547,28 @@ void FirewallHelperClass::readFirewallBehavior()
* HISTORY: *
* 3/15/01 12:30PM ST : Created *
*=============================================================================================*/
void FirewallHelperClass::detectFirewallBehavior(/*Bool &canRecord*/)
void FirewallHelperClass::detectFirewallBehavior()
{
m_behavior = FIREWALL_TYPE_SIMPLE;
reset();
m_currentTry = 0;
m_numManglers = 0;
m_numResponses = 0;
m_packetID = 0;
m_timeoutLength = 0;
m_timeoutStart = 0;

if (m_behavior != FIREWALL_TYPE_UNKNOWN)
{
m_lastBehavior = m_behavior;
m_lastSourcePortAllocationDelta = m_sourcePortAllocationDelta;
}

m_sourcePortAllocationDelta = 0;
m_behavior = FIREWALL_TYPE_UNKNOWN;
m_currentState = DETECTIONSTATE_BEGIN;
}

FirewallHelperClass::FirewallBehaviorType FirewallHelperClass::getFirewallBehavior() {
m_currentState = DETECTIONSTATE_IDLE;
return m_behavior;
}

Expand All @@ -608,6 +587,12 @@ Short FirewallHelperClass::getSourcePortAllocationDelta() {
Bool FirewallHelperClass::detectionBeginUpdate() {
// UnsignedShort mangler_port = MANGLER_PORT;
m_packetID = 0x7f00;

if (TheGameSpyConfig == nullptr) {
DEBUG_LOG(("FirewallHelperClass::detectionBeginUpdate - no GameSpy config, skipping detection."));
m_currentState = DETECTIONSTATE_DONE;
return TRUE;
}
//int current_mangler = 0;

/*
Expand Down
Loading
Loading