microsoft / WSL

Issues found on WSL
https://docs.microsoft.com/windows/wsl
MIT License
17.42k stars 822 forks source link

AF_UNIX abstract on Windows do not work #4240

Open amoldeshpande opened 5 years ago

amoldeshpande commented 5 years ago
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <afunix.h>
#include <stdio.h>

typedef SOCKET fd_t;
#define INVALID_FD INVALID_SOCKET

int socketpair(int domain, int type, int protocol, fd_t sv[2])

{
    SOCKET fd0 = INVALID_FD;
    SOCKET fd1 = INVALID_FD;
    SOCKET fdListen = INVALID_FD;
    struct sockaddr_un sa;
    int  len = 0;
    static int counter = 666;

    memset(&sa, 0, sizeof sa);
    sa.sun_family = AF_UNIX;
    _snprintf_s(sa.sun_path, ARRAYSIZE(sa.sun_path),_TRUNCATE, " ./xyx-%d-%d", GetCurrentProcessId(),counter++);
    len = sizeof(sa);
    sa.sun_path[0] = 0;

    if (domain != AF_UNIX || type != SOCK_STREAM || protocol != PF_UNSPEC)
    {
        return -1;
    }
    fdListen = socket(domain, type, protocol);// WSASocketW(domain, type, protocol, NULL, 0, 0);
    if (fdListen == INVALID_SOCKET)
    {
        goto Error;
    }
    printf("before bind address %s\n",sa.sun_path+1);
    if (bind(fdListen, (struct sockaddr *)&sa, len) == SOCKET_ERROR) {
        goto Error;
    }
    if (listen(fdListen, 5) == SOCKET_ERROR) {
        goto Error;
    }
    fd0 = socket(domain, type, protocol);
    if (fd0 == INVALID_SOCKET)
    {
        goto Error;
    }
    len = sizeof(sa);
    getsockname(fdListen, (struct sockaddr*)&sa, &len);
    printf("bound address %s\n",sa.sun_path+1);
    if (connect(fd0, (const struct sockaddr*)&sa, len) == SOCKET_ERROR)
    {
        fprintf(stderr,"connect error %d\n",WSAGetLastError());
        goto Error;
    }
    {
        int addrLen = sizeof(sa);;
        fd1 = accept(fdListen, (struct sockaddr*) & sa, &addrLen);
    }
    if (fd1 == SOCKET_ERROR)
    {
        goto Error;
    }

    sv[0] = fd0;
    sv[1] = fd1;
    closesocket(fdListen);
    printf("success\n");
    return 0;
Error:
    if (fd0 != INVALID_FD)
    {
        closesocket(fd0);
    }
    if (fd1 != INVALID_FD)
    {
        closesocket(fd1);
    }
    if (fdListen != INVALID_FD)
    {
        closesocket(fdListen);
    }
    return -1;
}
int main(int argc, char **argv) {

    fd_t x[2];
    WSADATA wsd;
    WSAStartup(WINSOCK_VERSION,&wsd);
    socketpair(AF_UNIX,SOCK_STREAM,0,x);
}
therealkenc commented 5 years ago

I almost posted this one myself sometime last year after having burned an evening trying to use abstract names. [The announcement blog doesn't say they aren't supported, although no socketpair() makes for a unsubtle hint.] I ended up not posting after I came to the uncomfortable conclusion the ask isn't strictly speaking WSL actionable (it is a win32 feature). Happy to hear I'm not the only one who tried tho.

Heads up we lost named AF_UNIX Windows interop in WSL2 (for now anyway). But even if we get that back 🙏, the fact that WSL2 is not in the same network namespace (nvm mount namespace) as win32 speaks to why abstract names is hard.

For time being, the way in and out of WSL (notwithstanding extreme unsupported measures) is through interop stdin/stdout/stderr and AF_INET.

amoldeshpande commented 5 years ago

Thanks @therealkenc , but the blog post does explicitly state "The second category is the ‘abstract’ socket address where the first character in ‘sun_path’ is a null byte. Windows implementation of AF_UNIX socket can also accept abstract addresses".

I am not really interested in interop with WSL, and while I don't see why they direct support for Winsock here, I hope it reaches someone who cares.

There's some real strangeness going on in this "feature" because I can run the code from https://gist.github.com/mattn/6b6bd66ff15e95ab0b241a578023e1ed and it will succeed outside a debugger but not within windbg. But that's an issue for another day.

I guess AF_INET on loopback is always an option for me in my specific use-case.

therealkenc commented 5 years ago

but the blog post does explicitly state

Indeed, it does; thanks. I knew there must have been a reason I spent so long trying to make it work.

I am not really interested in interop with WSL, and while I don't see why they direct support for Winsock here, I hope it reaches someone who cares.

I actually don't know the right place to reach someone who might care. I do hope someone does. Windows SDK forum maybe? [Noting that I didn't post there at the time either, and punted AF_INET pretty quick.]

My scenario at the time was interop with WSL though. If you are pure-play win32, I guess SOP would be win32 pipes.

Closing with tag question, since WSL is not the use case. Appreciate the post though, since it's a pretty good question regardless.

amoldeshpande commented 5 years ago

Well, the blog post explicitly points to this issue tracker as a place to provide feedback for AF_UNIX on Windows, and the forums have always been beyond useless. So, I'd like it left open for a real answer if that's possible. thanks.

benhillis commented 5 years ago

Let's leave this open, it was our team that did the AF_UNIX work.

jabedude commented 5 years ago

Heads up we lost named AF_UNIX Windows interop in WSL2 (for now anyway). But even if we get that back 🙏, the fact that WSL2 is not in the same network namespace (nvm mount namespace) as win32 speaks to why abstract names is hard.

@therealkenc what do you mean by that?

Also, how is WSL connecting to the 9P server to share the Linux filesystem now? I thought it was using a Unix socket in /run/WSL/_interop.

therealkenc commented 5 years ago

what do you mean by that?

I mean, we lost named AF_UNIX Windows interop in WSL2 (for now anyway). [edit, correction: that's assuming we ever had AF_UNIX Windows interop. It looks like not; I incorrectly assumed otherwise. The OP ask is abstract sockets in Windows.]

Also, how is WSL connecting to the 9P server to share the Linux filesystem now?

Wouldn't know. Probably a hyper-v socket, but that's only a guess.

I thought it was using a Unix socket in /run/WSL/_interop.

That path is not on 9p. Quick look shows /run/WSL/*_interop is accessed by /init not a Windows process.

amoldeshpande commented 5 years ago

can we please not bring WSL into this ? I know this is a WSL issue tracker, but I specifically filed a Windows bug. If you have similar WSL issues, please separate them. thanks.

heaven-hq commented 5 years ago

I also encountered this problem, AF_UNIX abstract on Windows, but connect always returns EINVAL, Is there any solution at the moment?

sigiesec commented 5 years ago

I also have problems with named AF_UNIX sockets, bind & listen & select work, but when I call accept in a response to a successful read select, I mysteriously get a WSAENOTSOCK error. I was not able to find any example code for AF_UNIX sockets on Windows. Do you have some working examples (named and/or abstract)?

This doesn't look like the right place to report this, since it is unrelated to WSL, but I am also missing a better feedback channel.

Myriachan commented 4 years ago

I have this problem as well: non-abstract AF_UNIX sockets work fine, but connect fails with WSAEINVAL with abstract AF_UNIX sockets.

The entire reason I am doing this is so that I can implement my own socketpair so I can interrupt a select/WSAPoll loop. So if you implement socketpair in WinSock, that'd work, too.

By the way, if you do implement socketpair in Win32, please add something equivalent to Linux's SOCK_CLOEXEC to it, which would atomically set the socket as non-inheritable. (Compare WSASocket's WSA_FLAG_NO_HANDLE_INHERIT.)

Myriachan commented 4 years ago

This feature is simply not implemented. The AfUnixTlConnect function of afunix.sys checks whether the address is abstract, and if it is, does a trace log of [0x%08x] connect to abstract address then returns STATUS_INVALID_PARAMETER, which becomes WSAEINVAL in WinSock.

jabedude commented 4 years ago

Why does this blog post from Microsoft claim that "Windows implementation of AF_UNIX socket can also accept abstract addresses" then? And why is @microsoft deleting comments?

amoldeshpande commented 4 years ago

well, that comment was just me ranting, so it definitely deserved to be deleted :) Nothing useful was lost in that deletion.

luisfavila commented 4 years ago

Will this ever be worked on?

emmenlau commented 4 years ago

Oh and maybe someone can correct the original blog post at https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/? I've lost a day's work due to this (know) limitation :-(

sunilmut commented 4 years ago

Windows implementation of AF_UNIX does not support abstract sockets (as already established here so far) and we have very less desire to implement it since it lacks the Windows security model (abstract unix sockets cannot be secured).

The blog post is incorrect and we take responsibility for that (we will correct it). And, we do apologize for the confusion caused here.

emmenlau commented 4 years ago

Thanks @sunilmut !

Myriachan commented 4 years ago

@sunilmut The entire reason I tried to use abstract sockets was so that I could interrupt select. The only decent way to interrupt select is to send a byte to one of the sockets it's using. But this requires a loopback socket, which without a socketpair API can only be created by binding a socket to a localhost port and connecting to it. I didn't want to create an actual TCP port, so I used AF_UNIX instead. I wanted to use abstract sockets so that there wasn't an object in the filesystem sticking around.

As for the security issue, the way I got around the problem of other processes connecting to my socket in the timing window was to use SIO_AF_UNIX_GETPEERPID to verify that I am talking to myself. So even without the ability to set ACLs on abstract sockets, it would've been secure.

All this could be avoided with a socketpair API in WinSock.

(Yes, you can queue a user-mode APC to a select thread, but there's no well-defined method to cause the select to abort and return from that APC.)

Zingam commented 4 years ago

@sunilmut So there are no abstract sockets in Windows and I wasted a couple of days and there are also no docs about AF_SOCKET here: https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-start-page-2

Very nice! Maybe the doc issues could be rectified too?

dlenski commented 2 years ago

If anyone needs an AF_UNIX-based implementation of socketpair() for Windows, we needed it in OpenConnect and I figured out how to make it work: https://gitlab.com/openconnect/openconnect/-/merge_requests/320/diffs

The caveat, of course, is that you need a real filesystem path for the socket. :face_with_head_bandage:

dlenski commented 2 years ago

Hey @microsoft!

Do you respect developers who are willing/able/required to write software for Windows?

If so, you should stop wasting everyone's time by correcting the blog post to reflect the fact that abstract sockets are not actually supported… at all… which is completely the opposite of what it explicitly states. It appears I'm just one of many developers who've wasted hours trying to make them work on Windows before discovering this thread. :cursing_face:

@sunilmut, you wrote more than a year and a half ago that it would get updated https://github.com/microsoft/WSL/issues/4240#issuecomment-620805115

Also, pretty much everyone who wants AF_UNIX sockets on Win32 wants them for the same reason: to implement socketpair without needing either to use either…

  1. Filesystem paths for the sockets (messy and easy to break due to permissions issues, collisions)
  2. Local IPv4 sockets (surprisingly, also very easy to break simply by running route /f, which will nuke the 127.0.0.0/8 route, and make it very hard to restore; see openconnect issue #228 for examples of this)

… so why not just implement socketpair properly and give everyone what they actually want?

davidebeatrici commented 2 years ago

Adding support for AF_UNIX was a huge step forward, as it allows to interrupt a WSAPoll() call without the need to create TCP/IP sockets (reserving a port and leaving it exposed).

However, in order to achieve a proper socketpair() implementation, abstract sockets are required.

dlenski commented 2 years ago

However, in order to achieve a proper socketpair() implementation, abstract sockets are required.

@davidebeatrici, agreed, but do check out the "just about good enough" version that I've implemented for OpenConnect: https://gitlab.com/openconnect/openconnect/-/merge_requests/320/diffs

It works around the issue of missing abstract sockets by trying, in order…

  1. Unix socket with filename {64-bit CPU ticks in hex}-{Process ID}.$$$ in %TMP%
  2. Unix socket with same filename in %WINDIR%\Temp
  3. Unix socket with same filename in C:\Temp
  4. Unix socket with same filename in current directory
  5. AF_INET :sob:

About as robust as I think we can do given the (frankly infuriating) limitations of Windows’ incomplete AF_UNIX implementation.

davidebeatrici commented 2 years ago

Your solution is indeed excellent and probably the best right now.

The only thing I would do differently is use GetTempFileName() to generate the filename. The function should guarantee a unique string, the only limitation is the lack of a custom prefix (it's always .TMP).

dlenski commented 2 years ago

The only thing I would do differently is use GetTempFileNameA() to generate the filename. The function should guarantee a unique string, the only limitation is the lack of a custom prefix (it's always .TMP).

Thanks! That's a great addition. Will include that when I try to contribute it back upstream to https://github.com/ncm/selectable-socketpair/blob/master/socketpair.c

davidebeatrici commented 2 years ago

Sure, no problem!

davidebeatrici commented 2 years ago

Is the socket file not deleted when closesocket() is called?

dlenski commented 2 years ago

Is the socket file not deleted when closesocket() is called?

It is not, in the Windows 10 implementation. My temp directory is littered with size-0 files that were Unix domain sockets.

davidebeatrici commented 2 years ago

Solution: call DeleteFile() right after connect().

GMC254 commented 2 years ago

Oh and maybe someone can correct the original blog post at https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/? I've lost a day's work due to this (know) limitation :-(

That misleading blogpost! Lost 2 days.

ioquatix commented 2 years ago

Please implement correct support for socketpair. The documentation and lack of support is truely frustrating and time consuming.

halifir commented 10 months ago

btw The unnamed AF_UNIX socket does not work as well.

Windows implementation of AF_UNIX does not support abstract sockets since it lacks the Windows security model (abstract unix sockets cannot be secured).

it would be nice of the unnamed AF_UNIX socket may be changed to a working state. I dont see an security problem, because the used internal name (handle) can be calculated by the members of microsoft themself. no one can do it better and more secure than a microsoft member.

To get more secured sockets for internal communication it would be great to get the unnamed AF_UNIX sockets working. The other way to open two sockets on a arbritayr ports is always less secure. So to "not implement" abstract or unnamed sockets to avoid security problems is not a solution, because there is no documented known secure solution available on windows, isnt it?

Source: https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/?nsl_bypass_cache=1691bb3186187e54c37d7a6cd3e209af#comments "Lastly, ‘unnamed’ sockets, where the socket is bound to a pathname with no name. This is also supported on Windows unix socket implementation."

But unnamed sockets with length "2" (just sun_family=AF_UNIX not path) lead into the same not available error message.

RivenSkaye commented 9 months ago

As we've hit 2024, the blog post was not yet rectified image There's a comment beneath it from someone whose name I don't see in this entire thread. Can someone please pick up the work of either fixing the blog post, or getting abstract sockets to work upstream rather than with robust workarounds?

nathnial commented 3 months ago

where to put this afunix.h file.

halifir commented 3 months ago

The file afunix.h is part of visual studio / windows kits / wsdk. If not beside winsock2.h or stdio.h on your disk, you may need sdk (Software development kit) / visual studio updates.

The file afunix.h is an include file to get header definitions in your code. You should not deliver this file.

ioquatix commented 3 months ago

@keith-horton @CatalinFetoiu @OneBlue folks are you able to address this issue and the incorrect blog post? This is causing a lot of confusion, and it's been ~5 years since the issue was reported.