0

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?

LWChris
  • 3,320
  • 1
  • 22
  • 39
  • 4
    Does 49913 fall into a range in the list provided by `netsh int ipv4 show excludedportrange protocol=tcp`? – ProgrammingLlama Jun 26 '23 at 08:59
  • Indeed, apparently it does, one range appears to be 49894-49993. Where do these come from, what is their purpose, and are they identical on all machines? – LWChris Jun 26 '23 at 09:01
  • There's a suggestion [here](https://github.com/microsoft/WSL/issues/5306) that they might relate to Hyper-V or WSL. – ProgrammingLlama Jun 26 '23 at 09:02
  • I think [this answer](https://stackoverflow.com/a/71190107/3181933) should help you solve it. It has been a long time since I had the issue so I can't 100% remember though. – ProgrammingLlama Jun 26 '23 at 09:03
  • @ProgrammingLlama Thank you all this sure helps a lot understanding and mitigating this issue in the future, especially the part about disabling HyperV and reserving the port range yourself before re-enabling it. – LWChris Jun 26 '23 at 09:14
  • @ProgrammingLlama Would you like to flesh this out into an answer that I can accept, or should I go ahead and do that for you? – LWChris Jul 06 '23 at 14:16
  • I'm currently travelling, so feel free :) – ProgrammingLlama Jul 06 '23 at 14:27

1 Answers1

1

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

LWChris
  • 3,320
  • 1
  • 22
  • 39