Windows 10 [received support](https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/) for AF_UNIX sockets in Insider Build 17063. This merge request adds basic support for AF_UNIX sockets to ws2_32 and wineserver.
Of particular note is the difficulty in handling `sun_path`. Most of the functions that allow for translating Windows paths to Unix paths are not accessible from ws2_32. I considered the following options: * Pass the Windows path to wineserver and do the conversion there. * This is, as far as I can tell, not possible without major rearchitecting. wineserver does not have functions to translate Windows paths to Unix paths, for obvious reasons. * Obtain the current working directory of the requesting process and temporarily change directories to there. * This only handles relative paths and fails for absolute paths, UNC paths, etc. * Conditionally change directories based on whether the path is relative or not. * This is error-prone and wineserver does not have the requisite functions to do this cleanly.
I ultimately decided to pass the translated Unix path to wineserver, which changes directories to `dirname(path)`. It then provides `bind` and `connect` with `basename(path)`. This is not threadsafe, but wineserver is not (currently) multithreaded.
Abstract sockets are supported by this patch.
-- v36: ws2_32/tests: Add test for AF_UNIX sockets fix. server: Fix getsockname() and accept() on AF_UNIX sockets. server: Introduce error when attempting to connect() to abstract AF_UNIX sockets. server: Allow for deletion of socket files. ws2_32: Add support for AF_UNIX sockets. ntdll/unix: Add support for AF_UNIX sockets to multiple functions. ws2_32: Add afunix.h header.
From: Ally Sommers dropbear.sh@gmail.com
This header is needed for support of AF_UNIX sockets. --- include/Makefile.in | 1 + include/afunix.h | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 include/afunix.h
diff --git a/include/Makefile.in b/include/Makefile.in index 48f43dfa372..9d3a5b45f3d 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -11,6 +11,7 @@ SOURCES = \ adshlp.h \ advpub.h \ af_irda.h \ + afunix.h \ amaudio.h \ amsi.idl \ amstream.idl \ diff --git a/include/afunix.h b/include/afunix.h new file mode 100644 index 00000000000..8118dda271b --- /dev/null +++ b/include/afunix.h @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Ally Sommers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef _WS2AFUNIX_ +#define _WS2AFUNIX_ + +#include "windef.h" +#include "ws2def.h" + +#ifdef USE_WS_PREFIX +# define WS(x) WS_##x +#else +# define WS(x) x +#endif + +#define UNIX_PATH_MAX 108 + +typedef struct WS(sockaddr_un) { + USHORT sun_family; + char sun_path[UNIX_PATH_MAX]; +} SOCKADDR_UN, *PSOCKADDR_UN; + +#endif /* _WS2AFUNIX_ */ \ No newline at end of file
From: Ally Sommers dropbear.sh@gmail.com
--- dlls/ntdll/unix/socket.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 80105eda0c9..b989b4866f8 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -65,6 +65,8 @@ # define HAS_IRDA #endif
+#include <sys/un.h> + #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" @@ -76,6 +78,7 @@ #include "ws2tcpip.h" #include "wsipx.h" #include "af_irda.h" +#include "afunix.h" #include "wine/afd.h"
#include "unix_private.h" @@ -104,6 +107,7 @@ union unix_sockaddr #ifdef HAS_IRDA struct sockaddr_irda irda; #endif + struct sockaddr_un un; };
struct async_recv_ioctl @@ -1929,6 +1933,13 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc break; }
+ if (unix_addr.addr.sa_family == AF_UNIX) + { + FIXME( "getpeername called on AF_UNIX socket\n" ); + status = STATUS_SUCCESS; + break; + } + len = sockaddr_from_unix( &unix_addr, out_buffer, out_size ); if (out_size < len) {
From: Ally Sommers dropbear.sh@gmail.com
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/ws2_32/socket.c | 69 +++++++++++++++++++++++++++--- dlls/ws2_32/ws2_32_private.h | 14 ++++++ server/sock.c | 83 +++++++++++++++++++++++++++++++++++- 3 files changed, 159 insertions(+), 7 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 5d647d12699..ec72171a900 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -167,6 +167,19 @@ static const WSAPROTOCOL_INFOW supported_protocols[] = .dwMessageSize = UINT_MAX, .szProtocol = L"SPX II", }, + { + .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); @@ -226,6 +239,11 @@ const char *debugstr_sockaddr( const struct sockaddr *a ) addr, ((const SOCKADDR_IRDA *)a)->irdaServiceName); } + 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); } @@ -1106,6 +1124,9 @@ int WINAPI bind( SOCKET s, const struct sockaddr *addr, int len ) HANDLE sync_event; NTSTATUS status;
+ char *unix_path = NULL; + int unix_path_len = 0; + TRACE( "socket %#Ix, addr %s\n", s, debugstr_sockaddr(addr) );
if (!addr) @@ -1148,6 +1169,14 @@ int WINAPI bind( SOCKET s, const struct sockaddr *addr, int len ) } break;
+ case AF_UNIX: + if (len < sizeof(struct sockaddr_un)) + { + SetLastError( WSAEFAULT ); + return -1; + } + break; + default: FIXME( "unknown protocol %u\n", addr->sa_family ); SetLastError( WSAEAFNOSUPPORT ); @@ -1156,8 +1185,18 @@ int WINAPI bind( SOCKET s, const struct sockaddr *addr, int len )
if (!(sync_event = get_sync_event())) return -1;
- params = malloc( sizeof(int) + len ); - ret_addr = malloc( 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_path_len = strlen(unix_path) + 1; + } + + params = malloc( sizeof(int) + len + unix_path_len ); + ret_addr = malloc( len + unix_path_len ); if (!params || !ret_addr) { free( params ); @@ -1167,9 +1206,11 @@ int WINAPI bind( SOCKET s, const struct sockaddr *addr, int len ) } params->unknown = 0; memcpy( ¶ms->addr, addr, len ); + if (unix_path) + memcpy( (char *)¶ms->addr + len, unix_path, unix_path_len );
status = NtDeviceIoControlFile( (HANDLE)s, sync_event, NULL, NULL, &io, IOCTL_AFD_BIND, - params, sizeof(int) + len, ret_addr, len ); + params, sizeof(int) + len + unix_path_len, ret_addr, len + unix_path_len ); if (status == STATUS_PENDING) { if (WaitForSingleObject( sync_event, INFINITE ) == WAIT_FAILED) @@ -1182,6 +1223,8 @@ int WINAPI bind( SOCKET s, const struct sockaddr *addr, int len )
free( params ); free( ret_addr ); + if (unix_path) + free( unix_path );
SetLastError( NtStatusToWSAError( status ) ); return status ? -1 : 0; @@ -1222,11 +1265,24 @@ int WINAPI connect( SOCKET s, const struct sockaddr *addr, int len ) HANDLE sync_event; NTSTATUS status;
+ char *unix_path = NULL; + int unix_path_len = 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_path_len = strlen(unix_path) + 1; + } + + if (!(params = malloc( sizeof(*params) + len + unix_path_len ))) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return -1; @@ -1234,10 +1290,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_len) + memcpy( (char *)(params + 1) + len, unix_path, unix_path_len );
status = NtDeviceIoControlFile( (HANDLE)s, sync_event, NULL, NULL, &io, IOCTL_AFD_WINE_CONNECT, - params, sizeof(*params) + len, NULL, 0 ); + params, sizeof(*params) + len + unix_path_len, 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 6ebcd6837ae..481be1a170e 100644 --- a/dlls/ws2_32/ws2_32_private.h +++ b/dlls/ws2_32/ws2_32_private.h @@ -44,6 +44,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" @@ -71,6 +72,19 @@ 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 ) DECLSPEC_HIDDEN; diff --git a/server/sock.c b/server/sock.c index 34be6ea22ef..d1ab7ad7cd8 100644 --- a/server/sock.c +++ b/server/sock.c @@ -53,6 +53,7 @@ #include <time.h> #include <unistd.h> #include <limits.h> +#include <libgen.h> #ifdef HAVE_LINUX_FILTER_H # include <linux/filter.h> #endif @@ -82,6 +83,8 @@ # define HAS_IRDA #endif
+#include <sys/un.h> + #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" @@ -92,6 +95,7 @@ #include "ws2tcpip.h" #include "wsipx.h" #include "af_irda.h" +#include "afunix.h" #include "wine/afd.h" #include "wine/rbtree.h"
@@ -115,6 +119,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 @@ -128,6 +133,7 @@ union unix_sockaddr #ifdef HAS_IRDA struct sockaddr_irda irda; #endif + struct sockaddr_un un; };
static struct list poll_list = LIST_INIT( poll_list ); @@ -545,6 +551,17 @@ static int sockaddr_from_unix( const union unix_sockaddr *uaddr, struct WS_socka } #endif
+ case AF_UNIX: + { + struct WS_sockaddr_un win = {0}; + + if (wsaddrlen < sizeof(win)) return -1; + win.sun_family = WS_AF_UNIX; + memcpy( win.sun_path, uaddr->un.sun_path, sizeof(win.sun_path) ); + memcpy( wsaddr, &win, sizeof(win) ); + return sizeof(win); + } + case AF_UNSPEC: return 0;
@@ -645,6 +662,17 @@ static socklen_t sockaddr_to_unix( const struct WS_sockaddr *wsaddr, int wsaddrl return sizeof(uaddr->in6); }
+ case WS_AF_UNIX: + { + struct WS_sockaddr_un win = {0}; + + if (wsaddrlen < sizeof(win.sun_family) + strlen( wsaddr->sa_data ) + 1) return 0; + memcpy( &win, wsaddr, sizeof(win) ); + uaddr->un.sun_family = AF_UNIX; + memcpy( uaddr->un.sun_path, win.sun_path, sizeof(win.sun_path) ); + return sizeof(uaddr->un); + } + default: return 0; } @@ -671,6 +699,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; } @@ -1756,6 +1787,7 @@ static int get_unix_family( int family ) #ifdef AF_IRDA case WS_AF_IRDA: return AF_IRDA; #endif + case WS_AF_UNIX: return AF_UNIX; case WS_AF_UNSPEC: return AF_UNSPEC; default: return -1; } @@ -2606,6 +2638,20 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) if (unix_addr.addr.sa_family == AF_INET && !memcmp( &unix_addr.in.sin_addr, magic_loopback_addr, 4 )) unix_addr.in.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
+ if (sock->family == WS_AF_UNIX && *((struct WS_sockaddr_un *)&addr)->sun_path) + { + char *unix_path = (char *)(params + 1) + params->addr_len; + char unix_path_copy[PATH_MAX]; + send_len -= strlen( unix_path ) + 1; + strcpy(unix_path_copy, unix_path); + if (chdir( dirname( unix_path_copy ) ) == -1) + { + set_error( sock_get_ntstatus( errno ) ); + return; + } + memcpy( unix_addr.un.sun_path, basename( unix_path ), sizeof(unix_addr.un.sun_path) ); + } + ret = connect( unix_fd, &unix_addr.addr, unix_len ); if (ret < 0 && errno == ECONNABORTED) { @@ -2618,6 +2664,9 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) ret = connect( unix_fd, &unix_addr.addr, unix_len ); }
+ if (sock->family == WS_AF_UNIX && *((struct WS_sockaddr_un *)&addr)->sun_path) + fchdir(server_dir_fd); + if (ret < 0 && errno != EINPROGRESS) { set_error( sock_get_ntstatus( errno ) ); @@ -2877,7 +2926,7 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) case IOCTL_AFD_BIND: { const struct afd_bind_params *params = get_req_data(); - union unix_sockaddr unix_addr, bind_addr; + union unix_sockaddr unix_addr, bind_addr, win_addr; data_size_t in_size; socklen_t unix_len; int v6only = 1; @@ -2942,12 +2991,29 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) if (check_addr_usage( sock, &bind_addr, v6only )) return;
+ if (sock->family == WS_AF_UNIX && *params->addr.sa_data) + { + char *unix_path = (char *)¶ms->addr + sizeof(unix_addr.un); + char unix_path_copy[PATH_MAX]; + strcpy(unix_path_copy, unix_path); + if (chdir( dirname( unix_path_copy ) ) == -1) + { + set_error( sock_get_ntstatus( errno ) ); + return; + } + + win_addr.un = bind_addr.un; + memcpy( bind_addr.un.sun_path, basename( unix_path ), sizeof(bind_addr.un.sun_path) ); + } + if (bind( unix_fd, &bind_addr.addr, unix_len ) < 0) { if (errno == EADDRINUSE && sock->reuseaddr) errno = EACCES;
set_error( sock_get_ntstatus( errno ) ); + if (sock->family == WS_AF_UNIX && *params->addr.sa_data) + fchdir(server_dir_fd); return; }
@@ -2960,6 +3026,8 @@ 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; + if (bind_addr.addr.sa_family == AF_UNIX) + bind_addr = win_addr; sock->addr_len = sockaddr_from_unix( &bind_addr, &sock->addr.addr, sizeof(sock->addr) ); }
@@ -2967,6 +3035,9 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
if (get_reply_max_size() >= sock->addr_len) set_reply_data( &sock->addr, sock->addr_len ); + + if (sock->family == WS_AF_UNIX && *(char *)(params + 1)) + fchdir(server_dir_fd); return; }
@@ -2983,7 +3054,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_DEFER:
From: Ally Sommers dropbear.sh@gmail.com
Deleting the socket file is a common pattern with AF_UNIX sockets, and is analogous to unbinding. --- server/fd.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/server/fd.c b/server/fd.c index 0b0e91ebfbb..33414f47a24 100644 --- a/server/fd.c +++ b/server/fd.c @@ -1971,6 +1971,24 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam fd->unix_fd = open( name, O_RDONLY | (flags & ~(O_TRUNC | O_CREAT | O_EXCL)), *mode ); }
+ /* POSIX requires that open(2) throws EOPNOTSUPP when `path` is a UNIX + * socket. *BSD throws EOPNOTSUPP in this case and the additional case of + * O_SHLOCK or O_EXLOCK being passed when `path` resides on a filesystem + * without lock support. + * + * Contrary to POSIX, Linux returns ENXIO in this case, so we also check + * that error code here. */ + if ((errno == EOPNOTSUPP || errno == ENXIO) && !stat( name, &st ) && S_ISSOCK(st.st_mode)) + { + if ((access & DELETE) && unlink( name )) + { + file_set_error(); + goto error; + } + else + fd->unix_fd = open( name, O_CREAT | O_RDWR, S_IRWXU ); + } + if (fd->unix_fd == -1) { /* check for trailing slash on file path */
From: Ally Sommers dropbear.sh@gmail.com
This also introduces an error when attempting to create an AF_UNIX socket of type SOCK_DGRAM. --- server/sock.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-)
diff --git a/server/sock.c b/server/sock.c index d1ab7ad7cd8..59b2e84b926 100644 --- a/server/sock.c +++ b/server/sock.c @@ -1880,6 +1880,12 @@ static int init_socket( struct sock *sock, int family, int type, int protocol ) return -1; }
+ if (unix_family == AF_UNIX && unix_type == SOCK_DGRAM) + { + set_win32_error(WSAEAFNOSUPPORT); + return -1; + } + sockfd = socket( unix_family, unix_type, unix_protocol );
#ifdef linux @@ -2638,18 +2644,29 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) if (unix_addr.addr.sa_family == AF_INET && !memcmp( &unix_addr.in.sin_addr, magic_loopback_addr, 4 )) unix_addr.in.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
- if (sock->family == WS_AF_UNIX && *((struct WS_sockaddr_un *)&addr)->sun_path) + if (sock->family == WS_AF_UNIX) { - char *unix_path = (char *)(params + 1) + params->addr_len; - char unix_path_copy[PATH_MAX]; - send_len -= strlen( unix_path ) + 1; - strcpy(unix_path_copy, unix_path); - if (chdir( dirname( unix_path_copy ) ) == -1) + if (*((struct WS_sockaddr_un *)&addr)->sun_path) { - set_error( sock_get_ntstatus( errno ) ); + char *unix_path = (char *)(params + 1) + params->addr_len; + char unix_path_copy[PATH_MAX]; + send_len -= strlen( unix_path ) + 1; + strcpy(unix_path_copy, unix_path); + if (chdir( dirname( unix_path_copy ) ) == -1) + { + set_error( sock_get_ntstatus( errno ) ); + return; + } + memcpy( unix_addr.un.sun_path, basename( unix_path ), sizeof(unix_addr.un.sun_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; } - memcpy( unix_addr.un.sun_path, basename( unix_path ), sizeof(unix_addr.un.sun_path) ); }
ret = connect( unix_fd, &unix_addr.addr, unix_len );
From: Ally Sommers dropbear.sh@gmail.com
--- server/sock.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-)
diff --git a/server/sock.c b/server/sock.c index 59b2e84b926..6aecb846b71 100644 --- a/server/sock.c +++ b/server/sock.c @@ -2057,7 +2057,19 @@ static struct sock *accept_socket( struct sock *sock ) } unix_len = sizeof(unix_addr); if (!getsockname( acceptfd, &unix_addr.addr, &unix_len )) - acceptsock->addr_len = sockaddr_from_unix( &unix_addr, &acceptsock->addr.addr, sizeof(acceptsock->addr) ); + { + if (sock->family == WS_AF_UNIX) + { + acceptsock->addr_len = sock->addr_len; + acceptsock->addr.un = sock->addr.un; + } + else + { + acceptsock->addr_len = sockaddr_from_unix( &unix_addr, + &acceptsock->addr.addr, + sizeof(acceptsock->addr) ); + } + } } clear_error(); sock->pending_events &= ~AFD_POLL_ACCEPT; @@ -2113,7 +2125,19 @@ static int accept_into_socket( struct sock *sock, struct sock *acceptsock )
unix_len = sizeof(unix_addr); if (!getsockname( get_unix_fd( newfd ), &unix_addr.addr, &unix_len )) - acceptsock->addr_len = sockaddr_from_unix( &unix_addr, &acceptsock->addr.addr, sizeof(acceptsock->addr) ); + { + if (sock->family == WS_AF_UNIX) + { + acceptsock->addr_len = sock->addr_len; + acceptsock->addr.un = sock->addr.un; + } + else + { + acceptsock->addr_len = sockaddr_from_unix( &unix_addr, + &acceptsock->addr.addr, + sizeof(acceptsock->addr) ); + } + }
clear_error(); sock->pending_events &= ~AFD_POLL_ACCEPT; @@ -2582,7 +2606,7 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) { const struct afd_connect_params *params = get_req_data(); const struct WS_sockaddr *addr; - union unix_sockaddr unix_addr; + union unix_sockaddr unix_addr, win_addr; struct connect_req *req; socklen_t unix_len; int send_len, ret; @@ -2644,6 +2668,7 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) if (unix_addr.addr.sa_family == AF_INET && !memcmp( &unix_addr.in.sin_addr, magic_loopback_addr, 4 )) unix_addr.in.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
+ memcpy( &win_addr, &unix_addr, sizeof(unix_addr) ); if (sock->family == WS_AF_UNIX) { if (*((struct WS_sockaddr_un *)&addr)->sun_path) @@ -2657,6 +2682,7 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) set_error( sock_get_ntstatus( errno ) ); return; } + win_addr.un = unix_addr.un; memcpy( unix_addr.un.sun_path, basename( unix_path ), sizeof(unix_addr.un.sun_path) ); } else @@ -2695,7 +2721,10 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
unix_len = sizeof(unix_addr); if (!getsockname( unix_fd, &unix_addr.addr, &unix_len )) - sock->addr_len = sockaddr_from_unix( &unix_addr, &sock->addr.addr, sizeof(sock->addr) ); + { + if (sock->family != WS_AF_UNIX) + sock->addr_len = sockaddr_from_unix( &unix_addr, &sock->addr.addr, sizeof(sock->addr) ); + } sock->bound = 1;
if (!ret)
From: Ally Sommers dropbear.sh@gmail.com
--- dlls/ws2_32/tests/sock.c | 255 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index e6ab5065cfe..365c808e39f 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -29,6 +29,7 @@ #include <iphlpapi.h> #include <ws2tcpip.h> #include <wsipx.h> +#include <afunix.h> #include <wsnwlink.h> #include <mswsock.h> #include <mstcpip.h> @@ -13818,6 +13819,259 @@ static void test_tcp_sendto_recvfrom(void) closesocket(server); }
+static void test_afunix(void) +{ + SOCKET listener, client, server = 0; + SOCKADDR_UN addr = { AF_UNIX, "test_afunix.sock" }; + char serverBuf[] = "ws2_32/AF_UNIX socket test"; + char clientBuf[sizeof(serverBuf)] = { 0 }; + + char paths[4][sizeof(addr.sun_path)] = { "./tmp.sock", "../tmp.sock" }; + char dosPath[sizeof(addr.sun_path)]; + WCHAR dosWidePath[sizeof(addr.sun_path)]; + UNICODE_STRING ntPath = { 0 }; + SOCKADDR_UN outAddr = { 0 }; + int outAddrSize = 0; + ULONG zero = 0; + ULONG one = 1; + int ret; + + /* Test connection and send/recv */ + listener = socket(AF_UNIX, SOCK_STREAM, 0); + if (listener == INVALID_SOCKET && GetLastError() == WSAEAFNOSUPPORT) + { + win_skip("AF_UNIX sockets are unsupported, skipping...\n"); + return; + } + + ok(listener != INVALID_SOCKET, "Could not create Unix socket: %lu\n", + GetLastError()); + + ret = bind(listener, (SOCKADDR *)&addr, sizeof(SOCKADDR_UN)); + ok(!ret, "Could not bind Unix socket: %lu\n", GetLastError()); + + ret = listen(listener, 1); + ok(!ret, "Could not listen on Unix socket: %lu\n", GetLastError()); + + client = socket(AF_UNIX, SOCK_STREAM, 0); + ok(client != INVALID_SOCKET, "Failed to create second Unix socket: %lu\n", + GetLastError()); + + ret = ioctlsocket(client, FIONBIO, &one); + ok(!ret, "Could not set AF_UNIX socket to nonblocking: %lu; skipping connection\n", GetLastError()); + if (!ret) + { + ret = connect(client, (SOCKADDR *)&addr, sizeof(addr)); + ok(!ret || (ret == SOCKET_ERROR && GetLastError() == WSAEWOULDBLOCK), + "Error when connecting to Unix socket: %lu\n", + GetLastError()); + server = accept(listener, NULL, NULL); + ok(server != INVALID_SOCKET, "Could not accept Unix socket connection: %lu\n", + GetLastError()); + ret = ioctlsocket(client, FIONBIO, &zero); + ok(!ret, "Could not set AF_UNIX socket to blocking: %lu\n", GetLastError()); + } + + ret = send(server, serverBuf, sizeof(serverBuf), 0); + ok(ret == sizeof(serverBuf), "Incorrect return value from send: %d\n", ret); + ret = recv(client, clientBuf, sizeof(serverBuf), 0); + ok(ret == sizeof(serverBuf), "Incorrect return value from recv: %d\n", ret); + ok(!memcmp(serverBuf, clientBuf, sizeof(serverBuf)), "Data mismatch over Unix socket\n"); + + memset(clientBuf, 0, sizeof(clientBuf)); + + ret = sendto(server, serverBuf, sizeof(serverBuf), 0, NULL, 0); + ok(ret == sizeof(serverBuf), "Incorrect return value from sendto: %d\n", ret); + ret = recvfrom(client, clientBuf, sizeof(serverBuf), 0, NULL, 0); + ok(ret == sizeof(serverBuf), "Incorrect return value from recvfrom: %d\n", ret); + ok(!memcmp(serverBuf, clientBuf, sizeof(serverBuf)), "Data mismatch over Unix socket\n"); + + memset(serverBuf, 0, sizeof(serverBuf)); + + ret = send(client, clientBuf, sizeof(clientBuf), 0); + ok(ret == sizeof(clientBuf), "Incorrect return value from send: %d\n", ret); + ret = recv(server, serverBuf, sizeof(clientBuf), 0); + ok(ret == sizeof(serverBuf), "Incorrect return value from recv: %d\n", ret); + ok(!memcmp(serverBuf, clientBuf, sizeof(clientBuf)), "Data mismatch over Unix socket\n"); + + memset(serverBuf, 0, sizeof(serverBuf)); + + ret = sendto(client, clientBuf, sizeof(clientBuf), 0, NULL, 0); + ok(ret == sizeof(clientBuf), "Incorrect return value from sendto: %d\n", ret); + ret = recvfrom(server, serverBuf, sizeof(clientBuf), 0, NULL, 0); + ok(ret == sizeof(serverBuf), "Incorrect return value from recvfrom: %d\n", ret); + ok(!memcmp(serverBuf, clientBuf, sizeof(clientBuf)), "Data mismatch over Unix socket\n"); + + closesocket(listener); + closesocket(client); + closesocket(server); + + /* Test socket file deletion */ + ret = DeleteFileA("test_afunix.sock"); + ok(ret, "DeleteFileA on socket file failed: %lu\n", GetLastError()); + ok(GetFileAttributesA("test_afunix.sock") == INVALID_FILE_ATTRIBUTES && + GetLastError() == ERROR_FILE_NOT_FOUND, + "Failed to delete socket file at path '%s'\n", + addr.sun_path); + return; + + /* Test failure modes */ + listener = socket(AF_UNIX, SOCK_STREAM, 0); + ok(listener != INVALID_SOCKET, "Could not create Unix socket: %lu\n", + GetLastError()); + ret = bind(listener, (SOCKADDR *)&addr, sizeof(SOCKADDR_UN)); + ok(!ret, "Could not bind Unix socket to path '%s': %lu\n", addr.sun_path, GetLastError()); + closesocket(listener); + listener = socket(AF_UNIX, SOCK_STREAM, 0); + ok(listener != INVALID_SOCKET, "Could not create Unix socket: %lu\n", + GetLastError()); + ret = bind(listener, (SOCKADDR *)&addr, sizeof(SOCKADDR_UN)); + ok(ret && GetLastError() == WSAEADDRINUSE, + "Bound Unix socket to path '%s' despite existing socket file: %lu\n", + addr.sun_path, + GetLastError()); + closesocket(listener); + ret = DeleteFileA(addr.sun_path); + ok(ret, "DeleteFileA on socket file failed: %lu\n", GetLastError()); + ok(GetFileAttributesA("test_afunix.sock") == INVALID_FILE_ATTRIBUTES && + GetLastError() == ERROR_FILE_NOT_FOUND, + "Failed to delete socket file at path '%s'\n", + addr.sun_path); + + /* Test different path types (relative, NT, etc.) */ + GetTempPathA(sizeof(paths[0]) - 1, paths[2]); + strcat(paths[2], "tmp.sock"); + MultiByteToWideChar(CP_ACP, 0, paths[2], -1, dosWidePath, sizeof(dosPath)); + RtlDosPathNameToNtPathName_U(dosWidePath, &ntPath, NULL, NULL); + RtlUnicodeToMultiByteN(paths[3], sizeof(addr.sun_path) - 1, NULL, ntPath.Buffer, ntPath.Length); + + for (int i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) + { + memcpy(addr.sun_path, paths[i], sizeof(paths[i])); + + listener = socket(AF_UNIX, SOCK_STREAM, 0); + ok(listener != INVALID_SOCKET, "Could not create Unix socket: %lu\n", + GetLastError()); + + ret = bind(listener, (SOCKADDR *)&addr, sizeof(SOCKADDR_UN)); + ok(!ret, "Could not bind Unix socket to path '%s': %lu\n", addr.sun_path, GetLastError()); + + ret = listen(listener, 1); + ok(!ret, "Could not listen on Unix socket: %lu\n", GetLastError()); + + client = socket(AF_UNIX, SOCK_STREAM, 0); + ok(client != INVALID_SOCKET, "Failed to create second Unix socket: %lu\n", + GetLastError()); + + ret = ioctlsocket(client, FIONBIO, &one); + ok(!ret, "Could not set AF_UNIX socket to nonblocking: %lu; skipping connection\n", GetLastError()); + if (!ret) + { + ret = connect(client, (SOCKADDR *)&addr, sizeof(addr)); + ok(!ret || (ret == SOCKET_ERROR && GetLastError() == WSAEWOULDBLOCK), + "Error when connecting to Unix socket: %lu\n", + GetLastError()); + server = accept(listener, NULL, NULL); + ok(server != INVALID_SOCKET, "Could not accept Unix socket connection: %lu\n", + GetLastError()); + ret = ioctlsocket(client, FIONBIO, &zero); + ok(!ret, "Could not set AF_UNIX socket to blocking: %lu\n", GetLastError()); + } + + memset(&outAddr, 0, sizeof(outAddr)); + outAddrSize = sizeof(outAddr); + ret = getsockname(listener, (SOCKADDR *)&outAddr, &outAddrSize); + ok(!ret, "Could not get info on Unix socket: %lu\n", GetLastError()); + ok(!memcmp(addr.sun_path, outAddr.sun_path, sizeof(addr.sun_path)), + "getsockname returned incorrect path '%s' for provided path '%s'\n", + outAddr.sun_path, + addr.sun_path); + ok(outAddrSize == sizeof(outAddr.sun_family) + strlen(outAddr.sun_path) + 1, + "getsockname returned incorrect size '%d' for provided path '%s'\n", + outAddrSize, + addr.sun_path); + + memset(&outAddr, 0, sizeof(outAddr)); + outAddrSize = sizeof(outAddr); + ret = getsockname(client, (SOCKADDR *)&outAddr, &outAddrSize); + ok(!ret, "Could not get info on Unix socket: %lu\n", GetLastError()); + ok(!memcmp((char[108]){0}, outAddr.sun_path, sizeof(addr.sun_path)), + "getsockname returned incorrect path '%s' for provided path '%s'\n", + outAddr.sun_path, + addr.sun_path); + ok(outAddrSize == sizeof(outAddr), + "getsockname returned incorrect size '%d' for provided path '%s'\n", + outAddrSize, + addr.sun_path); + + memset(&outAddr, 0, sizeof(outAddr)); + outAddrSize = sizeof(outAddr); + ret = getsockname(server, (SOCKADDR *)&outAddr, &outAddrSize); + ok(!ret, "Could not get info on Unix socket: %lu\n", GetLastError()); + ok(!memcmp(addr.sun_path, outAddr.sun_path, sizeof(addr.sun_path)), + "getsockname returned incorrect path '%s' for provided path '%s'\n", + outAddr.sun_path, + addr.sun_path); + ok(outAddrSize == sizeof(outAddr.sun_family) + strlen(outAddr.sun_path) + 1, + "getsockname returned incorrect size '%d' for provided path '%s'\n", + outAddrSize, + addr.sun_path); + + memset(&outAddr, 0, sizeof(outAddr)); + outAddrSize = sizeof(outAddr); + ret = getpeername(listener, (SOCKADDR *)&outAddr, &outAddrSize); + ok(ret == -1, "Got info on Unix socket: %lu\n", GetLastError()); + ok(GetLastError() == WSAENOTCONN, + "Incorrect error returned from getpeername on Unix socket: %ld\n", + GetLastError()); + ok(!memcmp((char[108]){0}, outAddr.sun_path, sizeof(addr.sun_path)), + "getpeername returned incorrect path '%s' for provided path '%s'\n", + outAddr.sun_path, + addr.sun_path); + ok(outAddrSize == sizeof(outAddr), + "getpeername returned incorrect size '%d' for provided path '%s'\n", + outAddrSize, + addr.sun_path); + + memset(&outAddr, 0, sizeof(outAddr)); + outAddrSize = sizeof(outAddr); + ret = getpeername(client, (SOCKADDR *)&outAddr, &outAddrSize); + todo_wine ok(!ret, "Could not get info on Unix socket: %lu\n", GetLastError()); + todo_wine ok(!memcmp(addr.sun_path, outAddr.sun_path, sizeof(addr.sun_path)), + "getpeername returned incorrect path '%s' for provided path '%s'\n", + outAddr.sun_path, + addr.sun_path); + ok(outAddrSize == sizeof(outAddr), + "getpeername returned incorrect size '%d' for provided path '%s'\n", + outAddrSize, + addr.sun_path); + + memset(&outAddr, 0, sizeof(outAddr)); + outAddrSize = sizeof(outAddr); + ret = getpeername(server, (SOCKADDR *)&outAddr, &outAddrSize); + todo_wine ok(!ret, "Could not get info on Unix socket: %lu\n", GetLastError()); + ok(!memcmp((char[108]){0}, outAddr.sun_path, sizeof(addr.sun_path)), + "getpeername returned incorrect path '%s' for provided path '%s'\n", + outAddr.sun_path, + addr.sun_path); + ok(outAddrSize == sizeof(outAddr), + "getpeername returned incorrect size '%d' for provided path '%s'\n", + outAddrSize, + addr.sun_path); + + closesocket(listener); + closesocket(client); + closesocket(server); + + ret = DeleteFileA(addr.sun_path); + ok(ret, "DeleteFileA on socket file failed: %lu\n", GetLastError()); + ok(GetFileAttributesA(addr.sun_path) == INVALID_FILE_ATTRIBUTES && + GetLastError() == ERROR_FILE_NOT_FOUND, + "Failed to delete socket file at path '%s'\n", + addr.sun_path); + } +} + START_TEST( sock ) { int i; @@ -13900,6 +14154,7 @@ START_TEST( sock ) test_icmp(); test_connect_udp(); test_tcp_sendto_recvfrom(); + test_afunix();
/* this is an io heavy test, do it at the end so the kernel doesn't start dropping packets */ test_send();
Zebediah Figura (@zfigura) commented about dlls/ws2_32/socket.c:
} break;
case AF_UNIX:
if (len < sizeof(struct sockaddr_un))
{
SetLastError( WSAEFAULT );
return -1;
}
This is still questionable, and needs tests.
Zebediah Figura (@zfigura) commented about dlls/ws2_32/socket.c:
free( params ); free( ret_addr );
- if (unix_path)
free( unix_path );
This if is redundant.
Zebediah Figura (@zfigura) commented about server/sock.c:
}
#endif
- case AF_UNIX:
- {
struct WS_sockaddr_un win = {0};
if (wsaddrlen < sizeof(win)) return -1;
win.sun_family = WS_AF_UNIX;
memcpy( win.sun_path, uaddr->un.sun_path, sizeof(win.sun_path) );
memcpy( wsaddr, &win, sizeof(win) );
return sizeof(win);
- }
As with ntdll, this looks wrong. I don't think we want to be calling sockaddr_from_unix() or sockaddr_to_unix() at all for AF_UNIX sockets.
On Thu Jul 6 15:52:57 2023 +0000, Zebediah Figura wrote:
This if is redundant.
My bad; I thought I had removed this in an earlier revision but I must have messed up when amending.