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

Attempt to use AF_UNIX sockets instead of AF_INET on Windows 10+ #8

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions dummy_headers/afunix.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#define UNIX_PATH_MAX 108
struct sockaddr_un
{
ADDRESS_FAMILY sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* pathname */
} SOCKADDR_UN, *PSOCKADDR_UN;;
144 changes: 108 additions & 36 deletions socketpair.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <string.h>
#include <inttypes.h>

#ifdef WIN32
# include <ws2tcpip.h> /* socklen_t, et al (MSVC20xx) */
# include <windows.h>
# include <io.h>
# include <afunix.h>
#else
# include <sys/types.h>
# include <sys/socket.h>
Expand All @@ -73,12 +75,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
int dumb_socketpair(SOCKET socks[2], int make_overlapped)
{
union {
struct sockaddr_un unaddr;
struct sockaddr_in inaddr;
struct sockaddr addr;
} a;
SOCKET listener;
int e;
socklen_t addrlen = sizeof(a.inaddr);
int e, ii;
int domain = AF_UNIX;
socklen_t addrlen = sizeof(a.unaddr);
DWORD flags = (make_overlapped ? WSA_FLAG_OVERLAPPED : 0);
int reuse = 1;

Expand All @@ -88,52 +92,120 @@ int dumb_socketpair(SOCKET socks[2], int make_overlapped)
}
socks[0] = socks[1] = -1;

listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == -1)
return SOCKET_ERROR;

memset(&a, 0, sizeof(a));
a.inaddr.sin_family = AF_INET;
a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
a.inaddr.sin_port = 0;

for (;;) {
if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
(char*) &reuse, (socklen_t) sizeof(reuse)) == -1)
break;
if (bind(listener, &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR)
break;
/* AF_UNIX/SOCK_STREAM became available in Windows 10
* ( https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows )
*
* We will attempt to use AF_UNIX, but fallback to using AF_INET if
* setting up AF_UNIX socket fails in any other way, which it surely will
* on earlier versions of Windows.
*/
for (ii = 0; ii < 2; ii++) {
listener = socket(domain, SOCK_STREAM, domain == AF_INET ? IPPROTO_TCP : 0);
if (listener == INVALID_SOCKET)
goto fallback;

memset(&a, 0, sizeof(a));
if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR)
break;
// win32 getsockname may only set the port number, p=0.0005.
// ( http://msdn.microsoft.com/library/ms738543.aspx ):
a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
a.inaddr.sin_family = AF_INET;
if (domain == AF_UNIX) {
/* Abstract sockets (filesystem-independent) don't work, contrary to
* the claims of the aforementioned blog post:
* ( https://github.com/microsoft/WSL/issues/4240#issuecomment-549663217 )
*
* So we must use a named path, and that comes with all the attendant
* problems of permissions and collisions. Trying various temporary
* directories and putting high-res time and PID in the filename, that
* seems like a less-bad option.
*/
LARGE_INTEGER ticks;
DWORD n;
int bind_try = 0;

for (;;) {
switch (bind_try++) {
case 0:
/* "The returned string ends with a backslash" */
n = GetTempPath(UNIX_PATH_MAX, a.unaddr.sun_path);
break;
case 1:
/* Heckuva job with API consistency, Microsoft! Reversed argument order, and
* "This path does not end with a backslash unless the Windows directory is the root directory.."
*/
n = GetWindowsDirectory(a.unaddr.sun_path, UNIX_PATH_MAX);
n += snprintf(a.unaddr.sun_path + n, UNIX_PATH_MAX - n, "\\Temp\\");
break;
case 2:
n = snprintf(a.unaddr.sun_path, UNIX_PATH_MAX, "C:\\Temp\\");
break;
case 3:
n = 0; /* Current directory */
break;
case 4:
goto fallback;
}

/* GetTempFileName could be used here.
* ( https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettempfilenamea )
* However it only adds 16 bits of time-based random bits,
* fails if there isn't room for a 14-character filename, and
* seems to offers no other apparent advantages. So we will
* use high-res timer ticks and PID for filename.
*/
QueryPerformanceCounter(&ticks);
snprintf(a.unaddr.sun_path + n, UNIX_PATH_MAX - n,
"%"PRIx64"-%"PRId32".$$$", ticks.QuadPart, GetCurrentProcessId());
a.unaddr.sun_family = AF_UNIX;

if (bind(listener, &a.addr, addrlen) != SOCKET_ERROR)
break;
}
} else {
a.inaddr.sin_family = AF_INET;
a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
a.inaddr.sin_port = 0;

if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
(char *) &reuse, (socklen_t) sizeof(reuse)) == -1)
goto fallback;;

if (bind(listener, &a.addr, addrlen) == SOCKET_ERROR)
goto fallback;

memset(&a, 0, sizeof(a));
if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR)
goto fallback;

// win32 getsockname may only set the port number, p=0.0005.
// ( https://docs.microsoft.com/windows/win32/api/winsock/nf-winsock-getsockname ):
a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
a.inaddr.sin_family = AF_INET;
}

if (listen(listener, 1) == SOCKET_ERROR)
break;
goto fallback;

socks[0] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, flags);
if (socks[0] == -1)
break;
if (connect(socks[0], &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR)
break;
socks[0] = WSASocket(domain, SOCK_STREAM, 0, NULL, 0, flags);
if (socks[0] == INVALID_SOCKET)
goto fallback;
if (connect(socks[0], &a.addr, addrlen) == SOCKET_ERROR)
goto fallback;

socks[1] = accept(listener, NULL, NULL);
if (socks[1] == -1)
break;
if (socks[1] == INVALID_SOCKET)
goto fallback;

closesocket(listener);
return 0;

fallback:
domain = AF_INET;
addrlen = sizeof(a.inaddr);

e = WSAGetLastError();
closesocket(listener);
closesocket(socks[0]);
closesocket(socks[1]);
WSASetLastError(e);
}

e = WSAGetLastError();
closesocket(listener);
closesocket(socks[0]);
closesocket(socks[1]);
WSASetLastError(e);
socks[0] = socks[1] = -1;
return SOCKET_ERROR;
}
Expand Down