Tested using C&C Red Alert 2; selection of interface and network communication worked as expected. IPX traffic correctly sent to the selected interface only, as confirmed by **tcpdump(8)**.
![ra2-on-linux-with-multiple-ipx-interfaces](/uploads/7755db6a7b82a21050623d3ccd9ec37e/ra2-on-linux-with-multiple-ipx-interfaces.png)
-- v5: ws2_32: Fully implement socket options IPX_ADDRESS and IPX_MAX_ADAPTER_NUM on Linux
From: 王昊然 whr@rivoreo.one
Move implementation of these 2 options into ntdll, and add 2 ioctl codes for calling from ws2_32.
Query configured IPX interfaces by reading /proc/net/ipx/interface of Linux procfs, enabling applications to iterate multiple IPX interfaces. This can be useful when a node have multiple IPX interfaces, and application need to receive/send packets from/to a non-primary IPX interface. --- dlls/ntdll/unix/socket.c | 141 +++++++++++++++++++++++++++++++++++++++ dlls/ws2_32/socket.c | 37 +++------- include/wine/afd.h | 2 + 3 files changed, 151 insertions(+), 29 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 53292246648..2f95cc54185 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -78,6 +78,7 @@ #include "ws2tcpip.h" #include "wsipx.h" #include "af_irda.h" +#include "wsnwlink.h" #include "wine/afd.h"
#include "unix_private.h" @@ -2426,6 +2427,146 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc return do_setsockopt( handle, io, 0, SO_DEFAULT_HEADERS, &value, sizeof(value) ); } #endif + case IOCTL_AFD_WINE_GET_IPX_ADDRESS: + { + const IPX_ADDRESS_DATA *in_data; + IPX_ADDRESS_DATA *out_data; +#ifdef __linux__ + FILE *f; + int c; + int nlines; +#else + union unix_sockaddr addr; + struct WS_sockaddr_ipx ws_addr; + socklen_t namelen; +#endif + + /* + * On a Win2000 system with one network card there are usually + * three ipx devices one with a speed of 28.8kbps, 10Mbps and 100Mbps. + * Using this call you can then retrieve info about this all. + * In case of Linux it is a bit different. Usually you have + * only "one" device active and further it is not possible to + * query things like the linkspeed. + */ + in_data = in_buffer; + out_data = out_buffer; + + TRACE("IOCTL_AFD_WINE_GET_IPX_ADDRESS: adapternum = %d\n", in_data->adapternum); + if (in_data->adapternum < 0) + { + status = STATUS_INVALID_PARAMETER; + break; + } + +#ifdef __linux__ + f = fopen("/proc/net/ipx/interface", "r"); + if (!f) + { + f = fopen("/proc/net/ipx_interface", "r"); + if (!f) + { + /* IPX isn't supported by this kernel */ + status = STATUS_INVALID_PARAMETER; + break; + } + } + + status = STATUS_INVALID_PARAMETER; + nlines = 0; /* Count lines from 0 to align with in_data->adapternum */ + while ((c = fgetc(f)) != EOF) + { + /* Skip until next line */ + if (c != '\n') continue; + + /* Because the first line is a header, comparing the line + * number with requested adapter number before increasing it + */ + if (nlines++ == in_data->adapternum) + { + int nmatches; + nmatches = fscanf(f, "%02hhX%02hhX%02hhX%02hhX %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX", + out_data->netnum, out_data->netnum+1, out_data->netnum+2, out_data->netnum+3, + out_data->nodenum, out_data->nodenum+1, out_data->nodenum+2, + out_data->nodenum+3, out_data->nodenum+4, out_data->nodenum+5); + switch (nmatches) + { + case 10: + status = STATUS_SUCCESS; + break; + case EOF: + /* Interface not found */ + status = STATUS_INVALID_PARAMETER; + break; + default: + ERR("IOCTL_AFD_WINE_GET_IPX_ADDRESS: Unexpected format of /proc/net/ipx/interface"); + status = STATUS_INVALID_ADDRESS; + break; + } + break; + } + } + fclose(f); + if (status != STATUS_SUCCESS) break; +#else + FIXME("IOCTL_AFD_WINE_GET_IPX_ADDRESS\n"); + if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL ))) + return status; + namelen = sizeof(struct sockaddr_ipx); + memset( &addr.ipx, 0, sizeof(struct sockaddr_ipx) ); + if (getsockname( fd, (struct sockaddr *)&addr.ipx, &namelen ) < 0) + { + status = sock_errno_to_status( errno ); + break; + } + sockaddr_from_unix( &addr, (struct WS_sockaddr *)&ws_addr, sizeof(ws_addr) ); + memcpy(out_data->nodenum, ws_addr.sa_nodenum, sizeof(out_data->nodenum)); + memcpy(out_data->netnum, ws_addr.sa_netnum, sizeof(out_data->netnum)); +#endif + out_data->wan = FALSE; /* We are not on a wan for now .. */ + out_data->status = FALSE; /* Since we are not on a wan, the wan link isn't up */ + out_data->maxpkt = 1467; /* This value is the default one, at least on Win2k/WinXP */ + out_data->linkspeed = 100000; /* Set the line speed in 100bit/s to 10 Mbit; + * note 1MB = 1000kB in this case */ + status = STATUS_SUCCESS; + break; + } + + case IOCTL_AFD_WINE_GET_IPX_MAX_ADAPTER_NUM: + { + int *count = out_buffer; +#ifdef __linux__ + FILE *f; + int c; + f = fopen("/proc/net/ipx/interface", "r"); + if (!f) + { + f = fopen("/proc/net/ipx_interface", "r"); + if (!f) + { + *count = 0; + status = STATUS_SUCCESS; + break; + } + } + *count = -1; /* Initial value -1 for skipping the header line */ + while ((c = fgetc(f)) != EOF) + { + if (c == '\n') (*count)++; + } + fclose(f); + if (*count < 0) + { + *count = 0; + } + TRACE("IOCTL_AFD_WINE_GET_IPX_MAX_ADAPTER_NUM: *count = %d\n", *count); +#else + FIXME("IOCTL_AFD_WINE_GET_IPX_MAX_ADAPTER_NUM\n"); + *count = 1; /* As noted under IPX_ADDRESS we have just one card. */ +#endif + status = STATUS_SUCCESS; + break; + } #endif
#ifdef HAS_IRDA diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index eb84558cbac..aec4c11504e 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1862,40 +1862,19 @@ int WINAPI getsockopt( SOCKET s, int level, int optname, char *optval, int *optl
case NSPROTO_IPX: { - struct sockaddr_ipx addr; - IPX_ADDRESS_DATA *data; - int namelen; + IO_STATUS_BLOCK io; + NTSTATUS status; switch(optname) { case IPX_ADDRESS: - /* - * On a Win2000 system with one network card there are usually - * three ipx devices one with a speed of 28.8kbps, 10Mbps and 100Mbps. - * Using this call you can then retrieve info about this all. - * In case of Linux it is a bit different. Usually you have - * only "one" device active and further it is not possible to - * query things like the linkspeed. - */ - FIXME("IPX_ADDRESS\n"); - namelen = sizeof(struct sockaddr_ipx); - memset( &addr, 0, sizeof(struct sockaddr_ipx) ); - getsockname( s, (struct sockaddr *)&addr, &namelen ); - - data = (IPX_ADDRESS_DATA*)optval; - memcpy(data->nodenum,addr.sa_nodenum,sizeof(data->nodenum)); - memcpy(data->netnum,addr.sa_netnum,sizeof(data->netnum)); - data->adapternum = 0; - data->wan = FALSE; /* We are not on a wan for now .. */ - data->status = FALSE; /* Since we are not on a wan, the wan link isn't up */ - data->maxpkt = 1467; /* This value is the default one, at least on Win2k/WinXP */ - data->linkspeed = 100000; /* Set the line speed in 100bit/s to 10 Mbit; - * note 1MB = 1000kB in this case */ - return 0; + status = NtDeviceIoControlFile( (HANDLE)s, NULL, NULL, NULL, &io, + IOCTL_AFD_WINE_GET_IPX_ADDRESS, optval, *optlen, optval, *optlen ); + if (status == STATUS_SUCCESS) *optlen = io.Information; + SetLastError( NtStatusToWSAError( status ) ); + return status == STATUS_SUCCESS ? 0 : SOCKET_ERROR;
case IPX_MAX_ADAPTER_NUM: - FIXME("IPX_MAX_ADAPTER_NUM\n"); - *(int*)optval = 1; /* As noted under IPX_ADDRESS we have just one card. */ - return 0; + return server_getsockopt( s, IOCTL_AFD_WINE_GET_IPX_MAX_ADAPTER_NUM, optval, optlen );
case IPX_PTYPE: return server_getsockopt( s, IOCTL_AFD_WINE_GET_IPX_PTYPE, optval, optlen ); diff --git a/include/wine/afd.h b/include/wine/afd.h index aba559ebd8a..ba82ac95930 100644 --- a/include/wine/afd.h +++ b/include/wine/afd.h @@ -291,6 +291,8 @@ C_ASSERT( sizeof(struct afd_get_events_params) == 56 ); #define IOCTL_AFD_WINE_SET_TCP_KEEPCNT WINE_AFD_IOC(302) #define IOCTL_AFD_WINE_GET_TCP_KEEPINTVL WINE_AFD_IOC(303) #define IOCTL_AFD_WINE_SET_TCP_KEEPINTVL WINE_AFD_IOC(304) +#define IOCTL_AFD_WINE_GET_IPX_ADDRESS WINE_AFD_IOC(305) +#define IOCTL_AFD_WINE_GET_IPX_MAX_ADAPTER_NUM WINE_AFD_IOC(306)
struct afd_iovec {
On Tue Aug 6 13:52:50 2024 +0000, Haoran Wang wrote:
My attempts to rewrite this loop always ended up the same original code... So instead I tried to make it to be more clear.
Hm, did my suggestion not end up working?
FWIW, I believe pretty strongly that readable and idiomatic code is worth the higher line count, or less "optimized" loops, at least in cases like this where optimization isn't important.
Can you please fix the authorship of the commit to be your full name (and it should also match the name you use for Gitlab)?
Basically, if your names don't match (exactly) then our scripts consider the patch to be written by someone else, and if you submit other people's patches you need to explicitly approve them. I think either Hanzi or Latin is fine, but I believe you need to either change one or the other so they're consistent, or else explicitly approve the patch.
On Fri Aug 9 17:42:50 2024 +0000, Elizabeth Figura wrote:
Can you please fix the authorship of the commit to be your full name
(and it should also match the name you use for Gitlab)? Basically, if your names don't match (exactly) then our scripts consider the patch to be written by someone else, and if you submit other people's patches you need to explicitly approve them. I think either Hanzi or Latin is fine, but I believe you need to either change one or the other so they're consistent, or else explicitly approve the patch.
I have updated my profile at Wine gitlab.