Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sockets: bind to non-local address succeeds #460

Closed
christoh opened this issue Jun 1, 2016 · 10 comments
Closed

sockets: bind to non-local address succeeds #460

christoh opened this issue Jun 1, 2016 · 10 comments

Comments

@christoh
Copy link

christoh commented Jun 1, 2016

If you use bind() with an invalid IP address, the call succeeds under WSL (as opposed to Linux). Maybe related to #395 but is a different problem.

bindtest.zip (contains a single file bindtest.c):

// compiles under Linux and Windows (should compile under any UNIX-like OS)
// Tested compilers:
//   VS2015          : cl bindtest.c "wsock32.lib"
//   linux gcc       : gcc -o bindtest bindtest.c
//   MingW32-W64 gcc : i686-w64-mingw32-gcc -o bindtest.exe bindtest.c -lws2_32
//   FreeBSD clang   : cc -o bindtest bindtest.c

#include <stdio.h>
#include <stdlib.h>

#ifndef _WIN32
#include <netinet/in.h>
#include <errno.h>
#include <sys/socket.h>
#include <unistd.h>
#define SOCKET int
#define close_socket(x) close(x)
typedef void* sockopt_t;

#else // _WIN32

#include <winsock.h>
#define close_socket(x) closesocket(x)
typedef char* sockopt_t;

#endif // _WIN32

// FreeBSD uses IP_BINDANY or IP_NONLOCALOK instead of IP_FREEBIND
// Use requires root rights in FreeBSD. Linux allows this without capabilities
#if (__FreeBSD__ || __FreeBSD_kernel__) && !defined(IP_FREEBIND)
#if defined(IP_BINDANY)
#define IP_FREEBIND IP_BINDANY
#elif defined(IP_NONLOCALOK)
#define IP_FREEBIND IP_NONLOCALOK
#endif // IP_BINDANY || IP_NONLOCALOK
#endif // __FREEBSD__ || __FreeBSD_kernel__

// returns 0 on failure and !0 on success
int bindtest(int use_ip_freebind, int do_not_bind)
{
    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

#   ifdef IP_FREEBIND
    int socket_option;

    if (use_ip_freebind && setsockopt(s, IPPROTO_IP, IP_FREEBIND, (sockopt_t)&socket_option, sizeof(socket_option)))
    {
        if (errno == EPERM)
        {
            fprintf(stderr, " -> FreeBSD: Please run this program with root rights under FreeBSD\n");
            exit(1);
        }

        fprintf(stderr, " -> WSL or Linux Kernel < 2.4 detected: IP_FREEBIND not supported; will never be fixed in WSL; missing support in kernel protocol stack\n");
    }
#   endif // IP_FREEBIND

    struct sockaddr_in addr = {
            .sin_family = AF_INET,
            .sin_port   = htons(1234),
    };

    addr.sin_addr.s_addr = htonl(0x01020304); // 1.2.3.4

    if (!do_not_bind)
        {
        printf("binding to 1.2.3.4 (port 1234)\n");

        if (bind(s, (struct sockaddr *)&addr, sizeof(addr)))
        {
#           if _WIN32
            if (WSAGetLastError() == WSAEADDRNOTAVAIL) printf(" -> WIN32: bind failed with WSAEADDRNOTAVAIL (correct)\n");
            else fprintf(stderr, " -> WIN32: bind failed with WSAGetLastError() != WSAEADDRNOTAVAIL (wrong): [%u]\n", (unsigned int)WSAGetLastError());
#           else // !_WIN32
            if (errno == EADDRNOTAVAIL) printf(" -> POSIX: bind failed with EADDRNOTAVAIL (correct)\n");
            else fprintf(stderr, " -> POSIX: bind failed with errno != EADDRNOTAVAIL (wrong)\n");
#           endif // !_WIN32
            close_socket(s);
            return 0;
        }

        printf(" -> bind was successful ");
#       ifdef IP_FREEBIND
        printf(use_ip_freebind ? "(correct)\n": "(wrong)\n");
#       else // !IP_FREEBIND
        printf("(wrong)\n");
#       endif // IP_FREEBIND
    }

    printf("listening\n");
    if (listen(s, SOMAXCONN))
    {
#       if _WIN32
        if (WSAGetLastError() == WSAEINVAL) printf(" -> WIN32: listen failed with WSAEINVAL (correct)\n");
        else fprintf(stderr, " -> WIN32: listen failed with WSAGetLastError() != WSAEINVAL (wrong): [%u]\n", (unsigned int)WSAGetLastError());
#       else // !_WIN32
        fprintf(stderr, " -> POSIX: listen failed (wrong)\n");
#       endif // !_WIN32
        close_socket(s);
        return 0;
    }

    printf(" -> listen was successful ");
#   if _WIN32
    printf("wrong)\n");
#   else
    printf(use_ip_freebind || do_not_bind ? "(correct)\n" : "(wrong)\n");
#   endif //!_WIN32
    close_socket(s);
    return !0;
}

int main(int argc, const char *const *argv)
{
#   if _WIN32
    WSADATA wsadata;
    WSAStartup(0x0202, &wsadata);
#   endif // _WIN32

    printf("Starting test 1: Simple bind to non-local IPv4 address 1.2.3.4\n");
    bindtest(0, 0); // Normal bind/listen with SOCK_STREAM

    printf("\nStarting test 2: Same as test 1 but with IP_FREEBIND\n");
#   ifndef IP_FREEBIND
    fprintf(stderr, " -> This OS doesn't support IP_FREEBIND\n");
#   else // IP_FREEBIND
    bindtest(!0, 0);
#   endif // IP_FREEBIND

    printf("\nStarting test 3: listen without bind\n");
    bindtest(0, !0); // listen on a socket without bind

    return 0;
}

Output on Linux:

[root@ubuntu ~] # gcc -o bindtest bindtest.c
[root@ubuntu ~] # ./bindtest
Starting test 1: Simple bind to non-local IPv4 address 1.2.3.4
binding to 1.2.3.4 (port 1234)
 -> POSIX: bind failed with EADDRNOTAVAIL (correct)

Starting test 2: Same as test 1 but with IP_FREEBIND
binding to 1.2.3.4 (port 1234)
 -> bind was successful (correct)
listening
 -> listen was successful (correct)

Starting test 3: listen without bind
listening
 -> listen was successful (correct)
[root@ubuntu ~] #

Output on WSL:

root@INSIDER-PREVIEW:~# gcc -o bindtest bindtest.c
root@INSIDER-PREVIEW:~# ./bindtest
Starting test 1: Simple bind to non-local IPv4 address 1.2.3.4
binding to 1.2.3.4 (port 1234)
 -> bind was successful (wrong)
listening
 -> POSIX: listen failed (wrong)

Starting test 2: Same as test 1 but with IP_FREEBIND
 -> WSL or Linux Kernel < 2.4 detected: IP_FREEBIND not supported; will never be fixed in WSL; missing support in kernel protocol stack
binding to 1.2.3.4 (port 1234)
 -> bind was successful (correct)
listening
 -> POSIX: listen failed (wrong)

Starting test 3: listen without bind
listening
 -> listen was successful (correct)
root@INSIDER-PREVIEW:~#
@sunilmut
Copy link
Member

sunilmut commented Jun 1, 2016

@christoh - Thanks for reporting this. #395 is a different issue. This is something that we are aware of and the fix for it has a dependency on the underlying Windows network stack. We have communicated that but unfortunately its too late for Anniversary update. We should be able to fix this for the next release. Having said that, we are not aware of this issue causing any app compat problems. Are you aware of one?

@christoh
Copy link
Author

christoh commented Jun 1, 2016

@sunilmut: No, I am not aware of any app causing serious problems. Most server apps do a bind followed immediately by listen. Since listen fails after bind incorrectly reported success, errors are handled almost correctly. The only minor issue is that the failure on listen reports EINVAL while bind reports EADDRNOTAVAIL. So the user sees "Invalid argument" instead of "Cannot assign requested address".

So handle this with low priority and focus on /dev/pts and IPV6_V6ONLY ;-)

Keep up the good work and don't worry that it won't be 100 % perfect in the anniversary update. Take your time to keep pressure on the kernel protocol stack people.

BTW, just to make sure: This is not a "WSL does not support IP_FREEBIND" issue. I am stating this because you mention a dependency on the protocol stack.

@sunilmut
Copy link
Member

sunilmut commented Jun 1, 2016

@christoh - Understood (the part about the IP_FREEBIND). This is something that we have known about, but it hasn't given us much pain in terms of compat so far. But, we are looking to fix this in future. Thanks for your understanding and trying out WSL!

@sunilmut
Copy link
Member

With the help of the Windows networking team, we were able to fully implement the IPV6_V6ONLY flag. Hopefully this should resolve a bunch of issues here. This also resolves the issue of binding to non-local address. The fix is still in our dev branch, but should soon hit the release branch.

@sunilmut
Copy link
Member

Just FYI that the IPV6_V6ONLY issue is fixed in 14936 (see release notes)

@christoh
Copy link
Author

Thx. I'm currently on a business trip and will return this weekend. I'll immediately test and close this issue.

@christoh
Copy link
Author

christoh commented Oct 1, 2016

Sorry, still can't close this:

WSL now outputs

Starting test 1: Simple bind to non-local IPv4 address 1.2.3.4
binding to 1.2.3.4 (port 1234)
 -> POSIX: bind failed with errno != EADDRNOTAVAIL (wrong)

Starting test 2: Same as test 1 but with IP_FREEBIND
 -> WSL or Linux Kernel < 2.4 detected: IP_FREEBIND not supported; will never be fixed in WSL; missing support in kernel protocol stack
binding to 1.2.3.4 (port 1234)
 -> POSIX: bind failed with errno != EADDRNOTAVAIL (wrong)

Starting test 3: listen without bind
listening
 -> listen was successful (correct)

bind() to non-local IP address now returrns an error which is good but this should be EADDRNOTAVAIL. Instead WSL returns EINVAL.

@sunilmut
Copy link
Member

sunilmut commented Oct 3, 2016

@christoh - Thanks much for validating this and the report. This has already been fixed in a subsequent check-in, but that change has not made it to the release branch yet.

image

@christoh
Copy link
Author

christoh commented Oct 3, 2016

Good news. Will close asap when it gets into the insider preview

@christoh
Copy link
Author

christoh commented Oct 8, 2016

Works in 14942

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants