Installing firewall exception rules programmatically

Writing installers for applications requires a substantial effort, even when using tools like InstallShield. My challenge for today was to write as little code as possible (because I’m lazy) to configure Windows Firewall exception rules so that the game I’m working on wouldn’t “pop” a dialog box asking the user to configure firewall settings — this freaks out users and is generally considered a bad thing.

Of course I relied upon Google to find a solution, but boy did I have a tough time finding code to do the job. Many folks run external commands like “netsh” to configure firewall exceptions, but this sucks because the netsh.exe command has different syntax in different versions of Windows. Yuck!

Here’s my solution in visual basic (cscript.exe), which has the virtue of being installed on every modern (XP+) Windows computer in the world.

'configure-firewall.vbs
'Sets Windows firewall permissions for a specific application
'Grants full inbound/outbound access for TCP/UDP
'by Patrick Wyatt 12/22/2011
'MIT License - do with as you will; no warranty

option explicit

'**************************************
const NET_FW_IP_PROTOCOL_TCP  = 6
const NET_FW_IP_PROTOCOL_UDP  = 17

const NET_FW_RULE_DIR_IN  = 1
const NET_FW_RULE_DIR_OUT = 2

'**************************************
sub UpdateFirewallRule (addRules, policy, groupName, ruleName, exePath, netProtocol, direction)
  'Prettify the rule name
  dim name
  name = ruleName
  name = name & " - Allow"
  if netProtocol = NET_FW_IP_PROTOCOL_TCP then
    name = name & " TCP"
  elseif netProtocol = NET_FW_IP_PROTOCOL_UDP then
    name = name & " UDP"
  end if
  if direction = NET_FW_RULE_DIR_IN then
    name = name & " IN"
  elseif direction = NET_FW_RULE_DIR_OUT then
    name = name & " OUT"
  end if

  'Set rule parameters
  dim rule
  set rule              = CreateObject("HNetCfg.FwRule")
  rule.Enabled          = true
  rule.Grouping         = groupName
  rule.Name             = name
  rule.ApplicationName  = exePath
  rule.Protocol         = netProtocol
  rule.Direction        = direction

  'Remove old rule
  if addRules < 0 then
    Wscript.echo "  Removing rule '" & name
  end if

  'Always remove old rule to prevent duplicates
  policy.Rules.Remove rule.name

  if Err.Number <> 0 then
    Wscript.Echo "  Removing rule '" & name & "'failed with error '" & Err.Description & "' (" & Err.Number & ")"
    Wscript.Quit 1
  end if

  'Add new rule
  if addRules > 0 then
    Wscript.echo "  Adding rule '" & name
    policy.Rules.Add rule
    if Err.Number <> 0 then
      Wscript.Echo "  Adding rule '" & name & "'failed with error '" & Err.Description & "' (" & Err.Number & ")"
      Wscript.Echo "  did you remember to run this script as administrator?"
      Wscript.Quit 1
    end if
  end if

end sub

'**************************************
sub DisplayRules (policy, groupName)
  dim RulesObject
  set RulesObject = policy.Rules

  Wscript.echo "Displaying firewall rules"

  dim Rule
  for each Rule in RulesObject
    if Rule.Grouping = groupName then
      Wscript.Echo "  Rule: " & Rule.Name
    end if
  next

  Wscript.echo ""

end sub

'**************************************
'Main program
  dim command, groupName, ruleName, exePath
  command   = Wscript.Arguments.Named("Command")
  groupName = Wscript.Arguments.Named("GroupName")
  ruleName  = Wscript.Arguments.Named("RuleName")
  exePath   = Wscript.Arguments.Named("ExePath")

  Wscript.echo "Firewall rule update arguments:"
  Wscript.echo "  Command:  " & command
  Wscript.echo "  Grouping: " & groupName
  Wscript.echo "  RuleName: " & ruleName
  Wscript.echo "  ExePath:  " & exePath
  Wscript.echo ""

  'Parse command line
  dim addRules
  if command = "install" then
    Wscript.echo "Installing firewall rules"
    addRules = 1
  elseif command = "remove" then
    Wscript.echo "Removing firewall rules"
    addRules = -1
  elseif command = "display" then
    addRules = 0
  else
    Wscript.echo "Unknown command: " & command
    Wscript.Quit 1
  end if

  dim policy
  set policy = CreateObject("HNetCfg.FwPolicy2")

  if addRules <> 0 then
    UpdateFirewallRule addRules, policy, groupName, ruleName, exePath, NET_FW_IP_PROTOCOL_TCP, NET_FW_RULE_DIR_IN
    UpdateFirewallRule addRules, policy, groupName, ruleName, exePath, NET_FW_IP_PROTOCOL_TCP, NET_FW_RULE_DIR_OUT
    UpdateFirewallRule addRules, policy, groupName, ruleName, exePath, NET_FW_IP_PROTOCOL_UDP, NET_FW_RULE_DIR_IN
    UpdateFirewallRule addRules, policy, groupName, ruleName, exePath, NET_FW_IP_PROTOCOL_UDP, NET_FW_RULE_DIR_OUT
    Wscript.echo ""
  end if

  DisplayRules policy, groupName

  Wscript.echo "Success"
  Wscript.Quit 0

And here’s a batch file to call this script. Now in real life you wouldn’t use a batch file; you would call the configure-firewall.vbs script directly from your installer script. That’s one of the reasons that the command-line validation of my script doesn’t do much error checking — it’s not for end-users, it’s for you programmer-types.

::sample-firewall-configuration.bat
@echo off
SETLOCAL EnableExtensions

if "%1" == "" (
  echo Usage:
  echo   %0 display
  echo   %0 install
  echo   %0 remove
  exit /B 1
)

%SystemRoot%\System32\cscript.exe //nologo configure-firewall.vbs /GroupName:"!GroupNameHere" /RuleName:"!RuleNameHere" /ExePath:"C:\Windows\notepad.exe" /Command:%1

Use the code as you see fit!

Your new() is not my new()

One of the problems I’ve experienced using third-party DLLs is the way that they handle new and delete for C++ classes, which can lead to memory leaks or even memory corruption. This was a particular problem when developing the Guild Wars model and animation exporter for 3ds Max, since 3ds Max uses plugins extensively, many written by different teams without the same coding standards as the original authors.

Here’s an example class API defined in a header file:

// FoozleDll.h
class CFoozle { ... };
CFoozle * MakeFoozle (CBarzle * bar);

And the associated CPP file:

// FoozleDll.cpp
CFoozle * MakeFoozle (CBarzle * bar) {
    return new CFoozle(bar);
}

In the application code we’re supposed to create and later delete this object, so here goes:

// Main.cpp
void ProcessData (CBarzle * bar, iostream * outfile) {
    CFoozle * foo = MakeFoozle(bar);
    foo->WriteResults(outfile);
    delete foo;
}

That all looks pretty straightforward, but what isn’t immediately obvious is that the call to new() is made in the context of the Foozle DLL file, and the delete() call is done in the context of the application, which might be using an entirely different memory manager.

Since the DLL was compiled separately, it might link to the release version of the C runtime library, where new() calls malloc() behind the scenes. But the call to delete() occurs in the application code, which could link to the Debug library, which calls _free_dbg() instead of free(). When the application releases the memory via the call to delete it is calling the wrong memory manager, which leads to problems like the inability of the application to coalesce adjacent free memory blocks (memory leakage) or random memory corruption.

The correct solution is that a module which allocates an object should also free the object:

// FoozleDll.h
class CFoozle {
public:
    ...

    // This function must not be implemented in the header or the
    // linker will build the code to call the application delete()
    // function instead of the library delete() function.
    void DeleteThis ();

private:
    ~CFoozle (); // private so it can only be called by DeleteThis();
};

And the implementation:

// FoozleDll.cpp
void CFoozle::DeleteThis () {
    delete this;  // called from the right "delete context"
}

By calling delete() from within the same compilation unit, we can ensure that the compiler will generate a call to the correct delete function.

Incidentally, this same type of problem can occur in C, where a DLL function returns (for example), the result of strdup(), and the application is expected to call free() on the resulting string.

C++: so powerful, so easy to break things.

Using transaction rate-limiting to improve service reliability

I develop and publish multiplayer games for a living, and have discovered some useful solutions for running reliable online services. The one I’m writing about today is how to implement reasonable usage limits so that services are less likely to be abused by hackers.

Y’see, hackers find ways to manipulate games by simulating the behavior of human players, but in ways that exploit bugs and misfeatures; for example, by performing tasks faster than humans could ever hope to do. One example is a “speed hack”, where a player is able to perform actions like swinging a sword more rapidly than should be possible according to the rules of the game. If you’ve ever seen a monster go from full health to instant death in a flurry of blows you’ve likely seen a speed-hack.

Now honestly a speed hack doesn’t sound like such a bad thing; before I was a game programmer I speed-hacked games myself, and thought it was more fun than playing! And I also had to “slow-hack” several older games (including one I developed — Warcraft) to make them playable on fast computers, because the programmers (me included) had forgotten that computers keep getting faster every year.

But then there are professional cyber-criminals, who steal accounts, use speed-hacks to generate lots of game-gold, and sell it for real money to other players, something known as Real Money Trading, or RMT for short. And with such hacks it becomes impossible for honest players to keep up. So what’s to be done?

The code I’ve shared below implements rate-limiting useful for preventing many forms of speed hacking. I’ve used similar code for login rate-limiting, to prevent hackers from brute-force cracking account passwords. It can be used to moderate online chat so that one person can’t “flood” a channel with messages. It’s great for transaction rate-limiting to ensure that no one person can overwhelm a server with requests. And in fact I’ve used it to successfully mitigate distributed denial of service (DDOS) attacks, which I’ll detail in a future article.

Here’s how to use the rate-limiter:

// Using these values a player can attempt to login once
// every 30 seconds, but with as many as ten login attempts
// in a burst. While this sounds like a lot many players
// forget their passwords and need a number of attempts to
// remember it, which I discovered by analyzing log files.
// They should try LastPass, which is an awesome solution
// to this problem.
const unsigned LOGIN_COST_MS      = 30 * 1000;
const unsigned MAX_LOGIN_COST_MS  = 10 * LOGIN_COST_MS;

ErrorCode CPlayer::PlayerLogin () {
    if (!m_rateLimiter.AddTime(LOGIN_COST_MS, MAX_LOGIN_COST_MS))
        return ERROR_LOGIN_RATE_LIMIT;
    ... login code here
}

In any event, here’s the code, which is surprisingly trivial considering how powerful it is. The algorithm is a modified form of the “Leaky Bucket Algorithm as a Meter“, but uses the passage of time instead of incrementing a counter to perform its magic.

RateLimiter.h definitions:

class CRateLimiter {
public:
    CRateLimiter ();
    bool AddTime (unsigned costMs, unsigned maxCostMs);

private:
    unsigned m_timeMs;
};

RateLimiter.cpp implementation:

CRateLimiter::CRateLimiter ()
:   m_timeMs(PlatformTimeMs())
{}

bool CRateLimiter::AddTime (unsigned costMs, unsigned maxCostMs) {
    ASSERT(costMs < maxCostMs);

    // Reset rate-limiter time value if it has expired
    // - handles integer overflow safely
    unsigned currTimeMs = PlatformTimeMs();
    if ((int) (currTimeMs - m_timeMs) > 0)
        m_timeMs = currTimeMs;

    // Has the user accrued too much time-cost?
    // - handles integer overflow safely
    unsigned newTimeMs = m_timeMs + costMs;
    if ((int) (newTimeMs - currTimeMs) >= (int) maxCostMs)
        return false;

    m_timeMs = newTimeMs;
    return true;
}

And somewhere you’ll have to define the PlatformTimeMs function:

unsigned PlatformTimeMs () {
#if defined(_WINDOWS_)
    return GetTickCount();
#else
    #error Your implementation here
   // something like clock_gettime(CLOCK_MONOTONIC, ...) for Unix/Linux
#endif
}

I hope you’ll find this code useful for your project, and would enjoy hearing about the novel purposes you find for it.

A better way to update SQL stored procedures

A common pattern to manage SQL stored procedures is to drop the current procedure and recreate it. Unfortunately, this doesn’t work if you’re trying to run a high-availability service. Here’s the (broken) drop+create pattern:

-- Delete the stored procedure if it already exists
if exists (
  select * from sys.objects where object_id = OBJECT_ID(N'p_MyProc')
  and type = N'P'
) then
  drop procedure p_MyProc
end

-- Now create it again
create procedure p_MyProc as begin
  -- awesome code here
end

-- And set permissions
grant execute on p_MyProc to SomeRole

There’s nothing wrong with this code. Just kick all your users off the server, switch to single user mode, execute the code above and it will work fine.

Oh, what’s that? You’re running a service that needs to be highly available, and you can’t take a maintenance period every time you want to change code?

Using the drop+create method there is obviously a small window of time when the stored procedure does not exist. And as we know from examples like the Seattle monorail crash (see notes at the end of this article), any system that is designed with a built-in flaw will eventually fail because of that flaw.

Here is a better solution:

-- if the stored procedure does not exist then create a placeholder
if not exists (
  select * from sys.objects where object_id = OBJECT_ID(N' p_MyProc')
  and type = N'P'
) then
  create procedure p_MyProc as RAISERROR ('MyProc not defined', 16, 1);
  grant execute on p_MyProc to SomeRole
end

-- update stored proc
alter procedure p_MyProc as begin
  -- awesome code here
end

This ensures that the stored procedure always exists, and (because SQL is transactional) it is possible to have one caller finishing a call to the old version of the stored procedure while the new version is added and called.

I’ve used this trick successfully for the development of Guild Wars using SQL Server 2000, and later SQL Server 2005, and have not encountered any problems using this technique, even on servers running sustained load of 3000+ transactions per second. In fact the standard operating procedure for our database updates was to update all stored procedures (several hundred of them) every time we performed a deployment.

I assume that similar tricks will work for MySQL and PostgreSQL, and would love to hear from users of those platforms about their experiences.

Update (11/5/2011):

It turns out that MySQL doesn’t support atomic updates of stored procedures; apparently this is a long-standing bug, first filed in 2005 and still not fixed (http://bugs.mysql.com/bug.php?id=9588). And PostgreSQL and Oracle both do properly support this feature with a different SQL syntax: “CREATE OR REPLACE PROCEDURE”.

Notes:

In case you’re wondering about the reference to the Seattle monorail crash, which is a great example of design failure, here’s a bit more information. The original Seattle monorail was built for 1962 World’s Fair, and had two sets of tracks so that two trains could operate side-by-side. The tracks were shortened in the 1988 to end at the newly constructed Westlake Center mall. The new design allowed for an automatic passenger-loading ramp to extend to the trains, but led to the tracks being too close together for two trains to be there at the same time. As you might imagine, this design eventually failed, though it took seventeen years (http://seattletimes.nwsource.com/html/localnews/2002650818_monorail28m.html). Of course, when you’re running several thousand SQL transactions per second, such failures are all the more likely.