Search code examples
c#asp.net-coresocketsport

How to resolve "SocketException" access denied?


I am trying to make my ASP.NET Core (.NET 6.0) application bind to a fixed port that is 49913. I have set this up using the launchSettings.json (for debugging) and appsettings.json (for release) as well:

launchSettings.json:

{
  "profiles": {
    "Controller": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": false,
      "applicationUrl": "https://[::1]:49913",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http2"
    },
    "Endpoints": {
      "Https": {
        "Url": "https://[::1]:49913"
      }
    }
  }
}

But when I try to start the application, I'm getting a SocketException with error code 10013 (access denied). But I don't understand what's denying the access.

  • In CMD, netstat -aon does not list this port (or any of the 499XX range) as in-use
  • 49913 is well within the range of dynamic ports by any definition (most notably 49152-65535 as per RFC 6335)
  • A quick Google search turns up no result on other programs that might use that port apart from one page claiming it's used by "xsan", but a) this is a Mac program (and I'm on Windows), and b) I think this information was scraped from Apple's support site which merely claims xsan uses any dynamic port (and Wikipedia suggests it tends to use 63146 [citation needed]).
  • Even IANA's port number registry does not feature this port at all for anything

I tried a different port (namely 59913), just to see what would happen, and it works fine. Then I tried 49910, and it failed again with the same error (10013 access denied).

What's different about 49913 & Co. to make it fail? What can I do to find out what's up with that particular port range?


Solution

  • With the help of some links in the comments I was able to figure it out.

    Various different bits of the question and answers here suggest the problem is related to some virtualization feature of Windows, namely HyperV (which is used for the Windows Sandbox for example) or WSL.

    The problem appears to be that these features use NAT, utilizing the winnat service. This service reserves some port ranges for the NAT usage, apparently in blocks of 100 consecutive ports.

    One can verify that using the command netsh int ipv4 show excludedportrange protocol=tcp in a regular cmd shell (no elevation needed). This is what the output might look like (translated from German, numbers are faked example values):

    Excluded port ranges for the protocol "tcp"
    
    Start port    End port
    ----------    --------
          1453        1453
          5357        5357
          6900        6900
          7564        7564
         50000       50059     *
         54267       54366
         54467       54566
         54869       54968
         55069       55168
         59013       59112
         59113       59212
         59230       59329
         59330       59429
         59696       59795
         59796       59895
         59896       59995
         59996       60095
         62249       62348
         62349       62448
         62449       62548
    
    * - Managed port exclusions.
    

    By the looks of it, this list grows over time; frankly, I'm not sure if that's a technical necessity I am failing to understand, or a "resource leak" (where the "resource" are port reservations). Anyway, some answers suggest that restarting the winnat service fixed their problem, like so (elevated shell required):

    net stop winnat
    net start winnat
    

    Doing so will release most of the reserved ports back to the pool. If there is some program that needs a specific port (Docker for example), then you can start that in between those two commands, and it should work.

    However, restarting winnat comes at a "cost": afterwards, programs like Windows Sandbox cannot connect to the Internet any longer, even if you [re]started those programs after the restart of winnat. To fix this, you need to reboot your computer. Beware however that this will mean winnat will reserve some blocks again, albeit probably different ones than before.

    Obviously, this is just a short term solution. Without further actions taken, any particular port may by chance end up in a range of reserved ports again at some point in the future.

    To make sure this doesn't happen and the desired port remains free to use in the future, you can reserve that port (or a port range) yourself using these commands (elevated shell required):

    netsh int ipv4 add excludedportrange protocol=tcp startport=49913 numberofports=1 (only reserve 49913)

    netsh int ipv4 add excludedportrange protocol=tcp startport=49900 numberofports=100 (reserves the whole range 49900-49999)

    This will mean these ports are reserved, but not used. Despite them being reserved, your program can still bind to them.

    In case it's needed, using delete instead of add removes an excluded port range again from that list. The newly added range will appear as "Managed port exclusion" (with a * behind it).


    The alternative

    If you looked up the excluded port ranges and it did not list a range that included your desired but blocked port, then there might be a problem with the setting which ports can be bound to.

    The dynamic port range wasn't always the range it is today, and to adapt to the latest change, Windows adjusted/updated the rules for its dynamic port range some time back. Some users however claimed that this automatic update hadn't worked for them, so they needed to manually adjust it. You can check what the configured dynamic port range is on your system using this command:

    netsh int ipv4 show dynamic protocol=tcp (no elevation needed)

    A correct example output looks like this:

    Protocol tcp Dynamic Port Range
    ---------------------------------
    Start Port      : 49152
    Number of Ports : 16384
    

    If that doesn't match your output, it may still look like this:

    Protocol tcp Dynamic Port Range
    ---------------------------------
    Start Port      : 1024
    Number of Ports : 64511
    

    You can use this command to fix it (elevated shell required):

    netsh int ipv4 set dynamic tcp start=49152 num=16384