Search code examples
c#httpmono

HttpListener: The requested address is not valid in this context


When creating an HttpListener object using

var server = new HttpListener();
server.Prefixes.Add("http://*:8080/");
server.Start();

everything works fine. However, when I use

var server = new HttpListener();
server.Prefixes.Add("http://demindiro.com:8080/");
server.Start();

it throws System.Net.Sockets.SocketException (0x80004005): The requested address is not valid in this context (full stacktrace below).

After googling the exception, it was rather clear it had something to do with the address used. After digging in the Mono source code for HttpListener and EndPointManager I determined the issue probably lies within this section of code (at GetEPListener):

static EndPointListener GetEPListener (string host, int port, HttpListener listener, bool secure)
{
    IPAddress addr;
    if (host == "*")
        addr = IPAddress.Any;
    else if (IPAddress.TryParse(host, out addr) == false){
        try {
#pragma warning disable 618
            IPHostEntry iphost = Dns.GetHostByName(host);
#pragma warning restore 618
            if (iphost != null)
                addr = iphost.AddressList[0]; // <---
            else
                addr = IPAddress.Any;
        } catch {
            addr = IPAddress.Any;
        }
        // ...    
    }

It occurs to me that in almost all cases ÌPAddress.Any is used except when it can associate an external IP address with the given host name.

However, I can only assume this was intentional because it is rather explicitly written and it seems HttpListener works just fine for other developers, so at this point, I'm clueless as to what is going wrong here.


Stacktrace:

Unhandled Exception:
System.Net.Sockets.SocketException (0x80004005): The requested address is not valid in this context
  at System.Net.Sockets.Socket.Bind (System.Net.EndPoint localEP) [0x00043] in <50d80b08c1a5449282b22aedf03ce925>:0 
  at System.Net.EndPointListener..ctor (System.Net.HttpListener listener, System.Net.IPAddress addr, System.Int32 port, System.Boolean secure) [0x00047] in <50d80b08c1a5449282b22aedf03ce925>:0 
  at System.Net.EndPointManager.GetEPListener (System.String host, System.Int32 port, System.Net.HttpListener listener, System.Boolean secure) [0x0009d] in <50d80b08c1a5449282b22aedf03ce925>:0 
  at System.Net.EndPointManager.AddPrefixInternal (System.String p, System.Net.HttpListener listener) [0x0005e] in <50d80b08c1a5449282b22aedf03ce925>:0 
  at System.Net.EndPointManager.AddListener (System.Net.HttpListener listener) [0x0009c] in <50d80b08c1a5449282b22aedf03ce925>:0 
  at System.Net.HttpListener.Start () [0x0000f] in <50d80b08c1a5449282b22aedf03ce925>:0 
  at Playground.Playground.StartHttpListener () [0x00016] in <d51cc6c047ee47c9a05c5e174876cbec>:0 
  at Playground.Playground.Main (System.String[] args) [0x00000] in <d51cc6c047ee47c9a05c5e174876cbec>:0 
[ERROR] FATAL UNHANDLED EXCEPTION: System.Net.Sockets.SocketException (0x80004005): The requested address is not valid in this context
  at System.Net.Sockets.Socket.Bind (System.Net.EndPoint localEP) [0x00043] in <50d80b08c1a5449282b22aedf03ce925>:0 
  at System.Net.EndPointListener..ctor (System.Net.HttpListener listener, System.Net.IPAddress addr, System.Int32 port, System.Boolean secure) [0x00047] in <50d80b08c1a5449282b22aedf03ce925>:0 
  at System.Net.EndPointManager.GetEPListener (System.String host, System.Int32 port, System.Net.HttpListener listener, System.Boolean secure) [0x0009d] in <50d80b08c1a5449282b22aedf03ce925>:0 
  at System.Net.EndPointManager.AddPrefixInternal (System.String p, System.Net.HttpListener listener) [0x0005e] in <50d80b08c1a5449282b22aedf03ce925>:0 
  at System.Net.EndPointManager.AddListener (System.Net.HttpListener listener) [0x0009c] in <50d80b08c1a5449282b22aedf03ce925>:0 
  at System.Net.HttpListener.Start () [0x0000f] in <50d80b08c1a5449282b22aedf03ce925>:0 
  at Playground.Playground.StartHttpListener () [0x00016] in <d51cc6c047ee47c9a05c5e174876cbec>:0 
  at Playground.Playground.Main (System.String[] args) [0x00000] in <d51cc6c047ee47c9a05c5e174876cbec>:0

PS: I explicitly want to use demindiro.com, not *.

PS2: I know there are many topics concerning this particular exception, but none seem to concern HttpListener.Start() throwing this exception.


UPDATE I've googled my issue again but this time I looked specifically for TcpListener and I found this answer: https://stackoverflow.com/a/17092670/7327379

The TcpListener can only be bound to a local IP Address of the computer that runs it. So the IP you're specifying isn't an IP of the local machine. Your public IP isn't the same IP as your local machine, especially if you're using some kind of NAT.

If I recall correctly, it's common to just do IPAddress.Any as your IP to initialise the listener.

So if I get this straight, this means that to bind to my external IP, I somehow have to go from this

Server <-- local IP --> Modem <-- external IP --> The Internet

to this

Server <-- external IP --> The Internet

Is this correct? If yes, how do you connect a server to the internet without a modem? Or what else should I do?


Solution

  • You can visualise your network something like below

    local machine -> local interface 
                  -> interface 1 (Private IP) <-> External IP
                  -> interface 2 (Private IP2) <-> External IP
    

    Now in most cases your VM in the cloud will have 1 network interface and one local interface. Local interfaces is the loopback interface and the interface 1 will have a Private IP.

    Now when you use below in your code

    server.Prefixes.Add("http://*:8080/");
    

    It means bind to all interfaces on your machine. Which means if you do

    curl http://localhost:8080
    

    or

    curl http://<privateIP>:8080
    

    Both would work.

    Now if you map example.com to the externalIP run below command it will work as well

    curl http://example.com:8080
    

    This is because your example.com maps to <extrenalIP>, which will forward all traffic to the <privateIP>. Now when you use below in your . code

    server.Prefixes.Add("http://example.com:8080/");
    

    It translate to

    server.Prefixes.Add("http://<externalIP>:8080/");
    

    And this not a valid IP in the context, only the 127.0.0.1 and the <PrivateIP> of the machine are valid. So if you have multiple interfaces then only you should worry about not using *. Now if you have multiple interfaces and your a specific interface to listen for your domain then you will make a entry of the same in your /etc/hosts file like below

    <PrivateIP> example.com
    

    And then you can use

    server.Prefixes.Add("http://example.com:8080/");
    

    Now if your concern is that someone can point to your machine with a different host name and you don't want to server to respond in such a case. Then you will have to check for the Host header in your code and reject the request based on the same. Or you can use a webserver like nginx and only respond to request directed towards example.com