Follow-up of !2786, which appears to have been abandoned.
Depends on !8182, !8575, !8578
# Execute minimal test case
1. checkout wine into ~/src/wine 2. in dlls/ws2_32/tests/sock.c wrap all calls to test functions after `Init()` until `test_afunix()` with #if 0 ... #endif 3. build into ~/src/wine-build 4. run ``` (cd ~/src/wine-build; \ WINEPREFIX=~/src/wine/.wine \ ../wine/tools/runtest -q -P wine -T . -M ws2_32.dll \ -p dlls/ws2_32/tests/i386-windows/ws2_32_test.exe sock) ``` or without the wrapper and some imported wine debug channels enabled ``` (cd ~/src/wine-build; WINEDEBUG=+file,+winsock \ WINEDLLOVERRIDES=';ws2_32.dll=b' \ WINETEST_PLATFORM=wine \ ./wine dlls/ws2_32/tests/i386-windows/ws2_32_test.exe sock) ```
# How to debug wineserver
Since `wineserver` runs in the background, simple calls to printf() will show nothing when the test case is executed. A workaround is to open a file and call fprintf() to write debug messages to this file, which can then be inspected.
The wineserver man page mentions that there is limited support to control wineserver debugging by using `WINEDEBUG=+server` ``` -d[n], --debug[=n] Set the debug level to n. 0 means no debugging information, 1 is the normal level, and 2 is for extra verbose debugging. If n is not specified, the default is 1. The debug output will be sent to stderr. wine(1) will automatically enable normal level debugging when starting wineserver if the +server option is set in the WINEDEBUG variable. ``` However, higher levels probably cannot be set in this way.
# Problems with synchronized log output
- The Windows test binary prints test failures to stdout (line-buffered or fully buffered). - Wine/Wineserver debug prints to stderr (unbuffered). When merged into a single stream (stderr+stdout), stdout lines get fragmented and interleaved between Wine/Wineserver trace messages., which looks like this:
``` 012c:trace:file:NtWriteFile (0xd,(nil),(nil),(nil),0x6afd40,0x4190bc,0x00000012,(nil),(nil)) [?25lsock.c[?25h012c:trace:file:NtWriteFile = SUCCESS (18) 0124:trace:file:NtDeviceIoControlFile (0x10,(nil),(nil),(nil),0x74ddd0,0x00504000,(nil),0x00000000,0x74ddcc,0x00000004)xts={} ) ```
Unfortunately, synchronized logs are important for analyzing processes.
# How to debug a wine test case
At https://gitlab.winehq.org/wine/wine/-/blob/master/tools/runtest#L151 there is mentioned a environment variable name `WINETEST_WRAPPER`, with which it may be possible to run gdb to debug test cases
``` (cd ~/src/wine-build; make -j10 ;\ WINETEST_WRAPPER="gdb --args" \ WINEPREFIX=~/src/wine/.wine \ ../wine/tools/runtest -P wine -T . -M ws2_32.dll \ -p dlls/ws2_32/tests/i386-windows/ws2_32_test.exe sock) Reading symbols from ./wine... (gdb) set follow-fork-mode child (gdb) r current directory: 'Z:\home\ralf.habacker\src\wine-build' sock.c:14624: Test failed: test_afunix.sock: wrong attr ffffffff sock.c:14658: Test failed: test_afunix.sock: wrong family 0 sock.c:14693: Test failed: test_afunix.sock: wrong family 0 ... ``` or without the wrapper and some important debug channels enabled ``` (cd ~/src/wine-build; \ make -j10; \ WINEDEBUG=+file,+winsock \ WINEDLLOVERRIDES=';ws2_32.dll=b' \ WINETEST_PLATFORM=wine \ gdb --args ./wine dlls/ws2_32/tests/i386-windows/ws2_32_test.exe sock) ```
With this you can debug the wine application, but unfortunally not the windows api.
# How to debug a wine test case (windows api)
1. After building wine install the binaries into a temporary location
``` make -C ~/src/wine-build install DESTDIR=~/src/wine-install ```
2. run
``` (cd ../wine-build; \ WINEPREFIX=~/src/wine/.wine \ WINEDLLOVERRIDES=';ws2_32.dll=b' \ WINETEST_PLATFORM=wine \ ./wine programs/winedbg/i386-windows/winedbg.exe --gdb dlls/ws2_32/tests/i386-windows/ws2_32_test.exe sock) ```
Now let's look at the loaded shared libraries
``` Wine-gdb> info sharedlibrary From To Syms Read Shared Object Library 0xf7b39000 0xf7bff6e0 Yes /home/user/src/wine-build/dlls/ntdll/ntdll.so 0x00401000 0x00478e2c Yes /home/user/src/wine-build/dlls/ws2_32/tests/i386-windows/ws2_32_test.exe 0x7bcc1000 0x7bd69280 Yes /home/user/src/wine/.wine/drive_c/windows/system32/ntdll.dll 0x7bb31000 0x7bb91594 Yes /home/user/src/wine/.wine/drive_c/windows/system32/kernel32.dll 0x7b5b1000 0x7b841f38 Yes /home/user/src/wine/.wine/drive_c/windows/system32/kernelbase.dll 0x77fd1000 0x77ff89d8 Yes /home/user/src/wine/.wine/drive_c/windows/system32/iphlpapi.dll 0x7b4b1000 0x7b4ebcb8 Yes /home/user/src/wine/.wine/drive_c/windows/system32/advapi32.dll 0x7b1e1000 0x7b280014 Yes /home/user/src/wine/.wine/drive_c/windows/system32/msvcrt.dll 0x7b151000 0x7b16de40 Yes /home/user/src/wine/.wine/drive_c/windows/system32/sechost.dll 0x7adf1000 0x7aec2690 Yes /home/user/src/wine/.wine/drive_c/windows/system32/ucrtbase.dll 0x77f71000 0x77f85a80 Yes /home/user/src/wine/.wine/drive_c/windows/system32/dnsapi.dll 0x77f31000 0x77f3b240 Yes /home/user/src/wine/.wine/drive_c/windows/system32/nsi.dll 0x7ad41000 0x7ad6653c Yes /home/user/src/wine/.wine/drive_c/windows/system32/ws2_32.dll 0x7a111000 0x7a2cc55c Yes /home/user/src/wine/.wine/drive_c/windows/system32/user32.dll 0x7a631000 0x7a6ae67c Yes /home/user/src/wine/.wine/drive_c/windows/system32/gdi32.dll 0x7a0b1000 0x7a0e48e8 Yes /home/user/src/wine/.wine/drive_c/windows/system32/win32u.dll 0x78121000 0x7813de90 Yes /home/user/src/wine/.wine/drive_c/windows/system32/imm32.dll ```
It is visible, that the unix variant of the shared libraries (ntdll.so) are read from the build dir and also the test case.
The Windows part (*.dll) is loaded from the wine prefix, so that these must be copied from the build directory initially and whenever changes are made.
With this is possible to set breakpoints
``` Wine-gdb> b GetFileAttributesW@4 Breakpoint 1 at 0x7b5d3725: file ../wine/dlls/kernelbase/file.c, line 1665. Wine-gdb> c Continuing.
Breakpoint 1, GetFileAttributesW@4 ( name=0x40fcfa <test_afunix+1434> L"\xec83\x8304\xfff8\x940f\x89c0\x247c\xf08\xc0b6\x44c7\x424\x1bbf\103\x489\xe824\x52e6\001\x44c7", <incomplete sequence \x824>) at ../wine/dlls/kernelbase/file.c:1665 1665 TRACE( "%s\n", debugstr_w(name) ); ```
# Followup
By adding `WINESYSTEMDLLPATH=/home/user/src/wine-install/usr/local/lib/wine/i386-windows/` to the command line, some shared libraries are loaded directly from the build directory, so they do not need to be copied.
``` From To Syms Read Shared Object Library 0xf7eb2000 0xf7f786e0 Yes /home/user/src/wine-build/dlls/ntdll/ntdll.so 0x00401000 0x00478e2c Yes /home/user/src/wine-build/dlls/ws2_32/tests/i386-windows/ws2_32_test.exe 0x7bcc1000 0x7bd69280 Yes /home/user/src/wine/.wine/drive_c/windows/system32/ntdll.dll 0x7bb31000 0x7bb91594 Yes /home/user/src/wine/.wine/drive_c/windows/system32/kernel32.dll 0x7b5b1000 0x7b841f38 Yes /home/user/src/wine/.wine/drive_c/windows/system32/kernelbase.dll 0x77fd1000 0x77ff89d8 Yes /home/user/src/wine/.wine/drive_c/windows/system32/iphlpapi.dll 0x7b4b1000 0x7b4ebcb8 Yes /home/user/src/wine-install/usr/local/lib/wine/i386-windows/advapi32.dll 0x7b1e1000 0x7b280014 Yes /home/user/src/wine-install/usr/local/lib/wine/i386-windows/msvcrt.dll 0x7b151000 0x7b16de40 Yes /home/user/src/wine-install/usr/local/lib/wine/i386-windows/sechost.dll 0x7adf1000 0x7aec2690 Yes /home/user/src/wine-install/usr/local/lib/wine/i386-windows/ucrtbase.dll 0x77f71000 0x77f85a80 Yes /home/user/src/wine/.wine/drive_c/windows/system32/dnsapi.dll 0x77f31000 0x77f3b240 Yes /home/user/src/wine/.wine/drive_c/windows/system32/nsi.dll 0x7ad41000 0x7ad6653c Yes /home/user/src/wine-install/usr/local/lib/wine/i386-windows/ws2_32.dll 0x7a111000 0x7a2cc55c Yes /home/user/src/wine-install/usr/local/lib/wine/i386-windows/user32.dll 0x7a631000 0x7a6ae67c Yes /home/user/src/wine-install/usr/local/lib/wine/i386-windows/gdi32.dll 0x79e31000 0x79e648e8 Yes /home/user/src/wine-install/usr/local/lib/wine/i386-windows/win32u.dll 0x78121000 0x7813de90 Yes /home/user/src/wine/.wine/drive_c/windows/system32/imm32.dll ```
Why this does not apply to all system libraries is currently unclear.
-- v46: lookup_unix_name(): fix bug not terminating wide string correctly ws2_32: Add note in bind() for AF_UNIX sockets ws2_32/tests: In tests for AF_UNIX sockets print actual directory used server: Fix getsockname() and accept() on AF_UNIX sockets. server: Introduce error when attempting to create a SOCK_DGRAM AF_UNIX socket. ws2_32: Add support for AF_UNIX sockets. server: Allow for deletion of socket files.
From: Ally Sommers dropbear.sh@gmail.com
Deleting the socket file is a common pattern with AF_UNIX sockets, and is analogous to unbinding.
Ugliness removed by Ralf Habacker. --- server/fd.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-)
diff --git a/server/fd.c b/server/fd.c index f70bec354a3..3f526eb8340 100644 --- a/server/fd.c +++ b/server/fd.c @@ -1987,20 +1987,42 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam { /* check for trailing slash on file path */ if ((errno == ENOENT || (errno == ENOTDIR && !(options & FILE_DIRECTORY_FILE))) && name[strlen(name) - 1] == '/') + { set_error( STATUS_OBJECT_NAME_INVALID ); + goto error; + } + + if (stat( name, &st )) + { + file_set_error(); + goto error; + } + + /* 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) && S_ISSOCK(st.st_mode) && (options & FILE_DELETE_ON_CLOSE)) + ; /* no error, go to regular deletion code path */ else + { file_set_error(); - goto error; + goto error; + } } }
fd->nt_name = dup_nt_name( root, nt_name, &fd->nt_namelen ); fd->unix_name = NULL; - fstat( fd->unix_fd, &st ); + /* st was set from the file name if the file could not be opened */ + if (fd->unix_fd != -1) + fstat( fd->unix_fd, &st ); *mode = st.st_mode;
/* only bother with an inode for normal files and directories */ - if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) + if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISSOCK(st.st_mode)) { unsigned int err; struct inode *inode = get_inode( st.st_dev, st.st_ino, fd->unix_fd ); @@ -2062,7 +2084,10 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam set_error( STATUS_OBJECT_NAME_COLLISION ); goto error; } - ftruncate( fd->unix_fd, 0 ); + if (fd->unix_fd != -1) + ftruncate( fd->unix_fd, 0 ); + else + truncate( fd->unix_name, 0 ); } } else /* special file */
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/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 dd9fe27d0ab..7aa6a580db6 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 7785d3c7706..2b8316cfcfa 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; } @@ -2666,8 +2675,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; @@ -2737,7 +2751,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 ); @@ -2784,6 +2846,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 );
@@ -3033,6 +3098,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 */ @@ -3042,8 +3108,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; @@ -3055,7 +3123,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 ); @@ -3133,8 +3241,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; @@ -3146,13 +3262,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; }
@@ -3169,7 +3295,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:
From: Ally Sommers dropbear.sh@gmail.com
--- server/sock.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/server/sock.c b/server/sock.c index 2b8316cfcfa..f56fcae3876 100644 --- a/server/sock.c +++ b/server/sock.c @@ -1956,6 +1956,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 if (sockfd == -1 && errno == EPERM && unix_type == SOCK_RAW
From: Ally Sommers dropbear.sh@gmail.com
--- server/sock.c | 72 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 12 deletions(-)
diff --git a/server/sock.c b/server/sock.c index f56fcae3876..191160562f6 100644 --- a/server/sock.c +++ b/server/sock.c @@ -2155,11 +2155,32 @@ 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) ); + } + if (!getpeername( acceptfd, &unix_addr.addr, &unix_len )) - acceptsock->peer_addr_len = sockaddr_from_unix( &unix_addr, - &acceptsock->peer_addr.addr, - sizeof(acceptsock->peer_addr) ); + { + if (sock->family == WS_AF_UNIX) + { + acceptsock->peer_addr_len = sizeof( sock->peer_addr.un ); + acceptsock->peer_addr.un = sock->peer_addr.un; + } + else + { + acceptsock->peer_addr_len = sockaddr_from_unix( &unix_addr, + &acceptsock->peer_addr.addr, + sizeof(acceptsock->peer_addr) ); + } + } } }
@@ -2219,11 +2240,31 @@ 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) ); + } if (!getpeername( get_unix_fd( newfd ), &unix_addr.addr, &unix_len )) - acceptsock->peer_addr_len = sockaddr_from_unix( &unix_addr, - &acceptsock->peer_addr.addr, - sizeof(acceptsock->peer_addr) ); + { + if (sock->family == WS_AF_UNIX) + { + acceptsock->peer_addr_len = sizeof( sock->peer_addr.un ); + acceptsock->peer_addr.un = sock->peer_addr.un; + } + else + { + acceptsock->peer_addr_len = sockaddr_from_unix( &unix_addr, + &acceptsock->peer_addr.addr, + sizeof(acceptsock->peer_addr) ); + } + } }
clear_error(); @@ -2859,10 +2900,17 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) allow_fd_caching( sock->fd );
unix_len = sizeof(unix_addr); - getsockname( unix_fd, &unix_addr.addr, &unix_len ); - sock->addr_len = sockaddr_from_unix( &unix_addr, &sock->addr.addr, sizeof(sock->addr) ); - sock->peer_addr_len = sockaddr_from_unix( &peer_addr, &sock->peer_addr.addr, sizeof(sock->peer_addr)); - + if (sock->family == WS_AF_UNIX) + { + sock->peer_addr.un = *(struct WS_sockaddr_un *)addr; + sock->peer_addr_len = sizeof(struct WS_sockaddr_un); + } + else + { + getsockname( unix_fd, &unix_addr.addr, &unix_len ); + sock->addr_len = sockaddr_from_unix( &unix_addr, &sock->addr.addr, sizeof(sock->addr) ); + sock->peer_addr_len = sockaddr_from_unix( &peer_addr, &sock->peer_addr.addr, sizeof(sock->peer_addr)); + } sock->bound = 1;
if (!ret)
From: Ralf Habacker ralf.habacker@freenet.de
--- dlls/ws2_32/tests/sock.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 883fe0c3021..064d537db19 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -14746,6 +14746,11 @@ static void test_afunix(void) ULONG one = 1; int ret;
+ char currentDir[MAX_PATH+1]; + ret = GetCurrentDirectoryA(sizeof(currentDir)-1, currentDir); + ok(ret, "Could not get current directory: %lu\n", GetLastError()); + winetest_printf("current directory: '%s'\n", currentDir); + /* Test connection and send/recv */ listener = socket(AF_UNIX, SOCK_STREAM, 0); if (listener == INVALID_SOCKET && GetLastError() == WSAEAFNOSUPPORT)
From: Ralf Habacker ralf.habacker@freenet.de
--- dlls/ws2_32/socket.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 4af9f23b538..397097cde73 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1239,6 +1239,8 @@ int WINAPI bind( SOCKET s, const struct sockaddr *addr, int len )
if (addr->sa_family == AF_UNIX && *addr->sa_data) { + /* The corresponding unix path is appended to a buffer with + * the structure sockaddr_un and can have a length of <= PATH_MAX */ struct sockaddr_un sun = { 0 }; WCHAR *sun_pathW; memcpy(&sun, addr, len);
From: Ralf Habacker ralf.habacker@freenet.de
--- dlls/ntdll/unix/file.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index bf05bc67422..49ea3b2cbf8 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -3611,8 +3611,11 @@ static NTSTATUS lookup_unix_name( int root_fd, OBJECT_ATTRIBUTES *attr, UNICODE_ { int reparse_fd;
- memcpy( reparse_name, name, (end - name) * sizeof(WCHAR) ); - reparse_name[end - name] = '?'; + size_t nlen = end - name; + WCHAR* reparse_name = malloc((nlen + 2) * sizeof(WCHAR)); // +1 for '?' +1 for '\0' + memcpy(reparse_name, name, nlen * sizeof(WCHAR)); + reparse_name[nlen] = '?'; + reparse_name[nlen + 1] = 0; // terminate
if (!name_len && open_reparse) {