File 0002-ws2_32-Add-support-for-AF_UNIX-sockets.patch of Package wine
From 754c049f36666e899440a79ccfafc77a2e8a72a6 Mon Sep 17 00:00:00 2001
From: Ralf Habacker <ralf.habacker@freenet.de>
Date: Tue, 3 Jun 2025 08:41:18 +0200
Subject: [PATCH 2/9] ws2_32: Add support for AF_UNIX sockets.
This commit additionally modifies wineserver's sock_ioctl to handle the
provided pathname by changing directories and then returning after the
native call. This is NOT threadsafe, but wineserver is not
multithreaded.
---
dlls/ntdll/unix/socket.c | 4 +
dlls/ws2_32/socket.c | 83 +++++++++++++++++--
dlls/ws2_32/ws2_32_private.h | 13 +++
server/sock.c | 152 ++++++++++++++++++++++++++++++++---
4 files changed, 238 insertions(+), 14 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c
index 5156bbfee10..1f484283757 100644
--- a/dlls/ntdll/unix/socket.c
+++ b/dlls/ntdll/unix/socket.c
@@ -67,6 +67,8 @@
# define HAS_IRDA
#endif
+#include <sys/un.h>
+
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
@@ -78,6 +80,7 @@
#include "ws2tcpip.h"
#include "wsipx.h"
#include "af_irda.h"
+#include "afunix.h"
#include "wine/afd.h"
#include "unix_private.h"
@@ -106,6 +109,7 @@ union unix_sockaddr
#ifdef HAS_IRDA
struct sockaddr_irda irda;
#endif
+ struct sockaddr_un un;
};
struct async_recv_ioctl
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index d4223e93bd1..4af9f23b538 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -182,6 +182,19 @@ static const WSAPROTOCOL_INFOW supported_protocols[] =
.iProtocol = BTHPROTO_RFCOMM,
.szProtocol = L"MSAFD RfComm [Bluetooth]",
},
+ {
+ .dwServiceFlags1 = XP1_GUARANTEED_DELIVERY | XP1_GUARANTEED_ORDER | XP1_IFS_HANDLES,
+ .dwProviderFlags = PFL_MATCHES_PROTOCOL_ZERO,
+ .ProviderId = {0xa00943d9, 0x9c2e, 0x4633, {0x9b, 0x59, 0x00, 0x57, 0xa3, 0x16, 0x09, 0x94}},
+ .dwCatalogEntryId = 1007,
+ .ProtocolChain.ChainLen = 1,
+ .iVersion = 2,
+ .iAddressFamily = AF_UNIX,
+ .iMaxSockAddr = sizeof(struct sockaddr_un),
+ .iMinSockAddr = offsetof(struct sockaddr_un, sun_path),
+ .iSocketType = SOCK_STREAM,
+ .szProtocol = L"AF_UNIX",
+ },
};
DECLARE_CRITICAL_SECTION(cs_socket_list);
@@ -252,6 +265,11 @@ const char *debugstr_sockaddr( const struct sockaddr *a )
bth_addr.rgBytes[1], bth_addr.rgBytes[0], wine_dbgstr_guid( &addr->serviceClassId ),
addr->port );
}
+ case AF_UNIX:
+ {
+ return wine_dbg_sprintf("{ family AF_UNIX, path %s }",
+ ((const SOCKADDR_UN *)a)->sun_path);
+ }
default:
return wine_dbg_sprintf("{ family %d }", a->sa_family);
}
@@ -1150,6 +1168,9 @@ int WINAPI bind( SOCKET s, const struct sockaddr *addr, int len )
IO_STATUS_BLOCK io;
HANDLE sync_event;
NTSTATUS status;
+ const int bind_len = len;
+ char *unix_path = NULL;
+ int unix_varargs_size = 0;
TRACE( "socket %#Ix, addr %s, len %d\n", s, debugstr_sockaddr(addr), len );
@@ -1199,6 +1220,15 @@ int WINAPI bind( SOCKET s, const struct sockaddr *addr, int len )
return -1;
}
break;
+
+ case AF_UNIX:
+ if (len < offsetof(struct sockaddr_un, sun_path))
+ {
+ SetLastError( WSAEFAULT );
+ return -1;
+ }
+ break;
+
default:
FIXME( "unknown protocol %u\n", addr->sa_family );
SetLastError( WSAEAFNOSUPPORT );
@@ -1207,7 +1237,29 @@ int WINAPI bind( SOCKET s, const struct sockaddr *addr, int len )
if (!(sync_event = get_sync_event())) return -1;
- params = malloc( sizeof(int) + len );
+ if (addr->sa_family == AF_UNIX && *addr->sa_data)
+ {
+ struct sockaddr_un sun = { 0 };
+ WCHAR *sun_pathW;
+ memcpy(&sun, addr, len);
+ if (strlen( sun.sun_path ))
+ {
+ sun_pathW = strdupAtoW( sun.sun_path );
+ unix_path = wine_get_unix_file_name( sun_pathW );
+ free( sun_pathW );
+ if (!unix_path)
+ return SOCKET_ERROR;
+ }
+ else
+ {
+ unix_path = malloc(1);
+ *unix_path = '\0';
+ }
+ len = sizeof(sun);
+ unix_varargs_size = strlen( unix_path );
+ }
+
+ params = malloc( sizeof(int) + len + unix_varargs_size );
ret_addr = malloc( len );
if (!params || !ret_addr)
{
@@ -1217,10 +1269,14 @@ int WINAPI bind( SOCKET s, const struct sockaddr *addr, int len )
return -1;
}
params->unknown = 0;
- memcpy( ¶ms->addr, addr, len );
+ if (addr->sa_family == AF_UNIX)
+ memset( ¶ms->addr, 0, len );
+ memcpy( ¶ms->addr, addr, bind_len );
+ if (unix_path)
+ memcpy( (char *)¶ms->addr + len, unix_path, unix_varargs_size );
status = NtDeviceIoControlFile( (HANDLE)s, sync_event, NULL, NULL, &io, IOCTL_AFD_BIND,
- params, sizeof(int) + len, ret_addr, len );
+ params, sizeof(int) + len + unix_varargs_size, ret_addr, len );
if (status == STATUS_PENDING)
{
if (WaitForSingleObject( sync_event, INFINITE ) == WAIT_FAILED)
@@ -1233,6 +1289,7 @@ int WINAPI bind( SOCKET s, const struct sockaddr *addr, int len )
free( params );
free( ret_addr );
+ free( unix_path );
SetLastError( NtStatusToWSAError( status ) );
return status ? -1 : 0;
@@ -1273,11 +1330,24 @@ int WINAPI connect( SOCKET s, const struct sockaddr *addr, int len )
HANDLE sync_event;
NTSTATUS status;
+ char *unix_path = NULL;
+ int unix_varargs_size = 0;
+
TRACE( "socket %#Ix, addr %s, len %d\n", s, debugstr_sockaddr(addr), len );
if (!(sync_event = get_sync_event())) return -1;
- if (!(params = malloc( sizeof(*params) + len )))
+ if (addr->sa_family == AF_UNIX && *addr->sa_data)
+ {
+ WCHAR *sun_pathW = strdupAtoW(addr->sa_data);
+ unix_path = wine_get_unix_file_name(sun_pathW);
+ free(sun_pathW);
+ if (!unix_path)
+ return SOCKET_ERROR;
+ unix_varargs_size = strlen(unix_path);
+ }
+
+ if (!(params = malloc( sizeof(*params) + len + unix_varargs_size )))
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return -1;
@@ -1285,10 +1355,13 @@ int WINAPI connect( SOCKET s, const struct sockaddr *addr, int len )
params->addr_len = len;
params->synchronous = TRUE;
memcpy( params + 1, addr, len );
+ if (unix_path)
+ memcpy( (char *)(params + 1) + len, unix_path, unix_varargs_size );
status = NtDeviceIoControlFile( (HANDLE)s, sync_event, NULL, NULL, &io, IOCTL_AFD_WINE_CONNECT,
- params, sizeof(*params) + len, NULL, 0 );
+ params, sizeof(*params) + len + unix_varargs_size, NULL, 0 );
free( params );
+ free( unix_path );
if (status == STATUS_PENDING)
{
if (wait_event_alertable( sync_event ) == WAIT_FAILED) return -1;
diff --git a/dlls/ws2_32/ws2_32_private.h b/dlls/ws2_32/ws2_32_private.h
index 9a4d0794151..af99716efa7 100644
--- a/dlls/ws2_32/ws2_32_private.h
+++ b/dlls/ws2_32/ws2_32_private.h
@@ -47,6 +47,7 @@
#include "mstcpip.h"
#include "af_irda.h"
#include "winnt.h"
+#include "afunix.h"
#define USE_WC_PREFIX /* For CMSG_DATA */
#include "iphlpapi.h"
#include "ip2string.h"
@@ -74,6 +75,18 @@ static inline char *strdupWtoA( const WCHAR *str )
return ret;
}
+static inline WCHAR *strdupAtoW( const char *str )
+{
+ WCHAR *ret = NULL;
+ if (str)
+ {
+ DWORD len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
+ if ((ret = malloc( len * sizeof(WCHAR) )))
+ MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
+ }
+ return ret;
+}
+
static const char magic_loopback_addr[] = {127, 12, 34, 56};
const char *debugstr_sockaddr( const struct sockaddr *addr );
diff --git a/server/sock.c b/server/sock.c
index 7ea708be8af..c3727b51d65 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -95,6 +95,8 @@
# endif
#endif
+#include <sys/un.h>
+
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
@@ -106,6 +108,7 @@
#include "tcpmib.h"
#include "wsipx.h"
#include "af_irda.h"
+#include "afunix.h"
#include "bthsdpdef.h"
#include "bluetoothapis.h"
#include "bthdef.h"
@@ -147,6 +150,7 @@ union win_sockaddr
struct WS_sockaddr_in6 in6;
struct WS_sockaddr_ipx ipx;
SOCKADDR_IRDA irda;
+ struct WS_sockaddr_un un;
};
union unix_sockaddr
@@ -163,6 +167,7 @@ union unix_sockaddr
#ifdef HAS_BLUETOOTH
struct sockaddr_rc rfcomm;
#endif
+ struct sockaddr_un un;
};
static struct list poll_list = LIST_INIT( poll_list );
@@ -744,6 +749,9 @@ static socklen_t get_unix_sockaddr_any( union unix_sockaddr *uaddr, int ws_famil
uaddr->irda.sir_family = AF_IRDA;
return sizeof(uaddr->irda);
#endif
+ case WS_AF_UNIX:
+ uaddr->un.sun_family = AF_UNIX;
+ return sizeof(uaddr->un);
default:
return 0;
}
@@ -1847,6 +1855,7 @@ static int get_unix_family( int family )
#ifdef AF_BLUETOOTH
case WS_AF_BTH: return AF_BLUETOOTH;
#endif
+ case WS_AF_UNIX: return AF_UNIX;
case WS_AF_UNSPEC: return AF_UNSPEC;
default: return -1;
}
@@ -2656,8 +2665,13 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
if (listen( unix_fd, params->backlog ) < 0)
{
- set_error( sock_get_ntstatus( errno ) );
- return;
+ /* Due to the way we handle the Windows AF_UNIX bind edge case, we also need to
+ * ignore listen's error. */
+ if (!(errno == EINVAL && sock->family == WS_AF_UNIX && !*sock->addr.un.sun_path))
+ {
+ set_error( sock_get_ntstatus( errno ) );
+ return;
+ }
}
sock->state = SOCK_LISTENING;
@@ -2727,7 +2741,55 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
break;
}
- unix_len = sockaddr_to_unix( addr, params->addr_len, &unix_addr );
+ if (sock->family == WS_AF_UNIX)
+ {
+ if (*addr->sa_data)
+ {
+ int unix_path_len = get_req_data_size() - sizeof(*params) - params->addr_len;
+ char *unix_path;
+ char *base_name;
+
+ if (!(unix_path = mem_alloc( unix_path_len + 1 )))
+ return;
+
+ memcpy( unix_path, (char *)(params + 1) + params->addr_len, unix_path_len );
+ unix_path[unix_path_len] = '\0';
+
+ base_name = strrchr(unix_path, '/');
+ if (base_name)
+ {
+ if (base_name != unix_path)
+ (++base_name)[-1] = '\0';
+ }
+ else
+ base_name = unix_path;
+
+ if (chdir( unix_path ) == -1)
+ {
+ set_error( sock_get_ntstatus( errno ) );
+ free( unix_path );
+ return;
+ }
+
+ send_len -= unix_path_len;
+ unix_len = sizeof(unix_addr.un);
+ memset( &unix_addr.un, 0, sizeof(unix_addr.un) );
+ unix_addr.un.sun_family = AF_UNIX;
+ memcpy( unix_addr.un.sun_path, base_name, strlen( base_name ) );
+ free( unix_path );
+ }
+ else
+ {
+ /* Contrary to documentation, Windows does not currently support abstract Unix
+ * sockets. connect() throws WSAEINVAL if sun_family is AF_UNIX and sun_path
+ * begins with '\0', even though bind() will succeed. */
+ set_win32_error( WSAEINVAL );
+ return;
+ }
+ }
+ else
+ unix_len = sockaddr_to_unix( addr, params->addr_len, &unix_addr );
+
if (!unix_len)
{
set_error( STATUS_INVALID_ADDRESS );
@@ -2774,6 +2836,9 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
return;
}
+ if (sock->family == WS_AF_UNIX && *addr->sa_data)
+ fchdir(server_dir_fd);
+
/* a connected or connecting socket can no longer be accepted into */
allow_fd_caching( sock->fd );
@@ -3023,6 +3088,7 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
data_size_t in_size;
socklen_t unix_len;
int v6only = 1;
+ int unix_path_len = 0;
/* the ioctl is METHOD_NEITHER, so ntdll gives us the output buffer as
* input */
@@ -3032,8 +3098,10 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
return;
}
in_size = get_req_data_size() - get_reply_max_size();
+ if (params->addr.sa_family == WS_AF_UNIX)
+ unix_path_len = in_size - sizeof(params->unknown) - sizeof(struct WS_sockaddr_un);
if (in_size < offsetof(struct afd_bind_params, addr.sa_data)
- || get_reply_max_size() < in_size - sizeof(int))
+ || get_reply_max_size() < in_size - sizeof(int) - unix_path_len)
{
set_error( STATUS_INVALID_PARAMETER );
return;
@@ -3045,7 +3113,47 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
return;
}
- unix_len = sockaddr_to_unix( ¶ms->addr, in_size - sizeof(int), &unix_addr );
+ if (sock->family == WS_AF_UNIX)
+ {
+ if (*params->addr.sa_data)
+ {
+ char *unix_path;
+ char *base_name;
+
+ if (!(unix_path = mem_alloc( unix_path_len + 1 )))
+ return;
+
+ memcpy( unix_path, (char *)(¶ms->addr) + sizeof(struct WS_sockaddr_un), unix_path_len );
+ unix_path[unix_path_len] = '\0';
+
+ base_name = strrchr(unix_path, '/');
+ if (base_name)
+ {
+ if (base_name != unix_path)
+ (++base_name)[-1] = '\0';
+ }
+ else
+ base_name = unix_path;
+
+ if (chdir( unix_path ) == -1)
+ {
+ free( unix_path );
+ set_error( sock_get_ntstatus( errno ) );
+ return;
+ }
+
+ memset( &unix_addr.un, 0, sizeof(unix_addr.un) );
+ memcpy( unix_addr.un.sun_path, base_name, strlen( base_name ) );
+ free( unix_path );
+ }
+ else
+ memset(unix_addr.un.sun_path, 0, sizeof(unix_addr.un.sun_path));
+ unix_addr.un.sun_family = AF_UNIX;
+ unix_len = sizeof(unix_addr.un);
+ }
+ else
+ unix_len = sockaddr_to_unix( ¶ms->addr, in_size - sizeof(int), &unix_addr );
+
if (!unix_len)
{
set_error( STATUS_INVALID_ADDRESS );
@@ -3123,8 +3231,16 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
if (errno == EADDRINUSE && sock->reuseaddr)
errno = EACCES;
- set_error( sock_get_ntstatus( errno ) );
- return;
+ /* Windows' AF_UNIX implementation has an edge case allowing for a socket to bind to
+ * an empty path. Linux doesn't, so it throws EINVAL. We check for this situation
+ * here and avoid early-exiting if it's the case. */
+ if (!(errno == EINVAL && sock->family == WS_AF_UNIX && !*params->addr.sa_data))
+ {
+ set_error( sock_get_ntstatus( errno ) );
+ if (sock->family == WS_AF_UNIX && *params->addr.sa_data)
+ fchdir(server_dir_fd);
+ return;
+ }
}
sock->bound = 1;
@@ -3136,13 +3252,23 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
* actual unix address */
if (bind_addr.addr.sa_family == AF_INET)
bind_addr.in.sin_addr = unix_addr.in.sin_addr;
- sock->addr_len = sockaddr_from_unix( &bind_addr, &sock->addr.addr, sizeof(sock->addr) );
+ if (bind_addr.addr.sa_family == AF_UNIX)
+ {
+ sock->addr.un.sun_family = WS_AF_UNIX;
+ memcpy(sock->addr.un.sun_path, params->addr.sa_data, sizeof(sock->addr.un.sun_path));
+ sock->addr_len = sizeof(sock->addr.un);
+ }
+ else
+ sock->addr_len = sockaddr_from_unix( &bind_addr, &sock->addr.addr, sizeof(sock->addr) );
}
update_addr_usage( sock, &bind_addr, v6only );
if (get_reply_max_size() >= sock->addr_len)
set_reply_data( &sock->addr, sock->addr_len );
+
+ if (sock->family == WS_AF_UNIX && *params->addr.sa_data)
+ fchdir(server_dir_fd);
return;
}
@@ -3159,7 +3285,15 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
return;
}
- set_reply_data( &sock->addr, sock->addr_len );
+ if (sock->family == WS_AF_UNIX)
+ {
+ if (*sock->addr.un.sun_path)
+ set_reply_data( &sock->addr, sizeof(sock->addr.un.sun_family) + strlen(sock->addr.un.sun_path) + 1 );
+ else
+ set_reply_data( &sock->addr, sizeof(sock->addr.un) );
+ }
+ else
+ set_reply_data( &sock->addr, sock->addr_len );
return;
case IOCTL_AFD_WINE_GETPEERNAME:
--
2.50.0