2

I needed to open a socket from a specific local network card using WinSock. I asked about this and got an answer here. In short, the answer advises that you first bind to the local interface, then call connect.

However, when I do this, I get a WSAEADDRNOTAVAIL (10049) "The requested address is not valid in its context.". Why is this?

Assuming the sample code below is part of an application running on the local box 192.168.1.3 and is attempting to connect to remote server 192.168.1.4. I've checked and double-checked that the local and remote addresses are correct. I can ping both ways (from local to remote and remote to local).

I've tried ports other than 0 for the local; no difference. If I remove the bind before the connect, it then works, but I'm then not able to specify a network interface.

So, any idea why I keep getting WSAEADDRNOTAVAIL ?

addrinfo localhints = {0};
localhints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
localhints.ai_family = AF_INET;
localhints.ai_socktype = SOCK_STREAM;
localhints.ai_protocol = IPPROTO_TCP;

addrinfo *localaddr = NULL;
getaddrinfo("192.168.1.3", "0", &localhints, &localaddr);
bind(s, localaddr->ai_addr, localaddr->ai_addrlen);
freeaddrinfo(localaddr);

addrinfo remotehints = {0};
remotehints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
remotehints.ai_family = AF_INET;
remotehints.ai_socktype = SOCK_STREAM;
remotehints.ai_protocol = IPPROTO_TCP;

addrinfo *remoteaddr = NULL;
getaddrinfo("192.168.1.4", "12345", &remotehints, &remoteaddr);
connect(s, remoteaddr->ai_addr, remoteaddr->ai_addrlen);
freeaddrinfo(remoteaddr);

EDIT: This sample code intentionally has no error checking, so that my intent could be communicated in the most efficient way.

EDIT 2: A bind to 192.168.1.3 causes connect to fail. A bind to 127.0.0.1 works. Yes, I'm 100% sure that 192.168.1.3 is the correct local IP.

EDIT 3: Right! On a whim, I tried the test app on my home PC and it works fine. So, at least the code does work, and the trouble must be related to my work PC.

Community
  • 1
  • 1
His Royal Redness
  • 721
  • 1
  • 9
  • 17
  • It looks like the bind call is of no use. Windows will use the destination IP & routing table to figure out which interface to send the packet on. Read these two articles http://support.microsoft.com/kb/175396 http://technet.microsoft.com/en-us/magazine/2007.09.cableguy.aspx –  Feb 12 '13 at 07:49
  • That is OK. Windows does take the bound IP into account when determining which entries in the routing table to use. It has worked this way for years. Calling `bind()` with a specific IP does ensure that the connection is established using the specific network adapter/interface that the IP belongs to. – Remy Lebeau Feb 12 '13 at 10:57
  • @KapilKapre: If there are duplicate routes to a destination, Windows will indeed work it's magic and select the most efficient route. However, if I want to test the various routes, I'll need to specifically select a route, send a ping request or somesuch and wait for a reply. I'll then switch to the next route in turn and repeat. This is fairly close the the application that I have in mind. – His Royal Redness Feb 12 '13 at 20:28
  • Nope, firewall disabled – His Royal Redness Feb 13 '13 at 20:06

2 Answers2

2

OK, turns out @claptrap was right.

I had disabled my firewall, but unknown to me, our IT department has some other firewally-thingy tightly integrated into the enterprise virus scanner (which I can't uninstall or disable). Once I managed to get IT to temporarily disabled it, everything worked as expected.

His Royal Redness
  • 721
  • 1
  • 9
  • 17
-1

Always check error codes when calling API functions, eg:

addrinfo localhints = {0};
localhints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
localhints.ai_family = AF_INET;
localhints.ai_socktype = SOCK_STREAM;
localhints.ai_protocol = IPPROTO_TCP;

addrinfo *localaddr = NULL;
int ret = getaddrinfo("192.168.1.3", "0", &localhints, &localaddr);
if (ret == 0)
{
    ret = bind(s, localaddr->ai_addr, localaddr->ai_addrlen);
    freeaddrinfo(localaddr);

    if (ret == 0)
    {
        addrinfo remotehints = {0};
        remotehints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
        remotehints.ai_family = AF_INET;
        remotehints.ai_socktype = SOCK_STREAM;
        remotehints.ai_protocol = IPPROTO_TCP;

        addrinfo *remoteaddr = NULL;
        ret = getaddrinfo("192.168.1.4", "12345", &remotehints, &remoteaddr);
        if (ret == 0)
        {
            ret = connect(s, remoteaddr->ai_addr, remoteaddr->ai_addrlen);
            freeaddrinfo(remoteaddr);

            if (ret == 0)
            {
                // connect succeeded...

                // can use getsockname() here to discover which port was actually bound to, if needed.
                // On some OS versions, bind() does not perform the actual binding immediately,
                // connect() does the actual binding instead since it can make more informed choices based on the target address being connected to...
            }
            else
            {
                // connect failed...
            }
        }
        else
        {
            // getaddrinfo() failed
        }
    }
    else
    {
        // bind failed
    }
}
else
{
    // getaddrinfo() failed...
}

How far does the code actually get?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I omitted the error checking in the sample code pasted here for clarity. I have it in place in my test app. The bind call succeeds (returns 0). The connect call returns WSAEADDRNOTAVAIL. If I remove the bind call, the connect call then succeeds. Interestingly, if I bind to 127.0.0.1, both the bind and the connect then succeed – His Royal Redness Feb 12 '13 at 20:20
  • [According to MSDN](http://msdn.microsoft.com/en-us/library/windows/desktop/ms737625.aspx): "If the address member of the structure specified by the name parameter is filled with zeros, connect will return the error WSAEADDRNOTAVAIL." That suggests `getaddrinfo()` is returning an `addrinfo` whose `ai_addr` contains zeros. Have you checked that yet? – Remy Lebeau Feb 12 '13 at 23:07
  • If I print out the ai_addr directly after the connect call, it prints the IP address that I would expect to be there. – His Royal Redness Feb 12 '13 at 23:57
  • And if you print it before calling `connect()`? Did you try using the `sockaddr_in`/`inet_addr()` approach I showed you earlier instead of using `getaddrinfo()`? Does it exhibit the same behavior? For `connect()` to fail with this error, either "192.168.1.3:0" is not available for binding, or "192.168.1.4:12345" is not routable for connecting to. You cannot `bind()` to "127.0.0.1" and then `connect()` to "192.168.1.4", there is no route for it. If you bind to "127.0.0.1", you can only connect to "127.0.0.1". – Remy Lebeau Feb 13 '13 at 00:44
  • I've uploaded my full code [here](http://pastebin.com/c6jUeFSQ). You'll just need to change DEFAULT_HOST and DEFAULT_PORT to point to a valid server. I'm only expecting a small http response (56 bytes to be exact) and I've coded accordingly. Thank you for all your effort thus far – His Royal Redness Feb 13 '13 at 01:21