Module: wine Branch: master Commit: 9c5e380cfe7b9d44d86dac539398c04cd22b8282 URL: https://gitlab.winehq.org/wine/wine/-/commit/9c5e380cfe7b9d44d86dac539398c04...
Author: Paul Gofman pgofman@codeweavers.com Date: Wed Jan 11 19:19:17 2023 -0600
ws2_32: Provide same address order from gethostbyname() on consequent calls.
---
dlls/ws2_32/Makefile.in | 1 + dlls/ws2_32/unixlib.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+)
diff --git a/dlls/ws2_32/Makefile.in b/dlls/ws2_32/Makefile.in index 13937dc6ee7..fc13323b930 100644 --- a/dlls/ws2_32/Makefile.in +++ b/dlls/ws2_32/Makefile.in @@ -3,6 +3,7 @@ MODULE = ws2_32.dll UNIXLIB = ws2_32.so IMPORTLIB = ws2_32 DELAYIMPORTS = dnsapi advapi32 iphlpapi user32 +UNIX_LIBS = $(PTHREAD_LIBS)
C_SRCS = \ async.c \ diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c index 0625c5c72ce..f013e1ac3d0 100644 --- a/dlls/ws2_32/unixlib.c +++ b/dlls/ws2_32/unixlib.c @@ -167,6 +167,51 @@ static const int ip_protocol_map[][2] =
#undef MAP
+static pthread_once_t hash_init_once = PTHREAD_ONCE_INIT; +static BYTE byte_hash[256]; + +static void init_hash(void) +{ + unsigned i, index; + NTSTATUS status; + BYTE *buf, tmp; + ULONG buf_len; + + for (i = 0; i < sizeof(byte_hash); ++i) + byte_hash[i] = i; + + buf_len = sizeof(SYSTEM_INTERRUPT_INFORMATION) * NtCurrentTeb()->Peb->NumberOfProcessors; + if (!(buf = malloc( buf_len ))) + { + ERR( "No memory.\n" ); + return; + } + + for (i = 0; i < sizeof(byte_hash) - 1; ++i) + { + if (!(i % buf_len) && (status = NtQuerySystemInformation( SystemInterruptInformation, buf, + buf_len, &buf_len ))) + { + ERR( "Failed to get random bytes.\n" ); + free( buf ); + return; + } + index = i + buf[i % buf_len] % (sizeof(byte_hash) - i); + tmp = byte_hash[index]; + byte_hash[index] = byte_hash[i]; + byte_hash[i] = tmp; + } + free( buf ); +} + +static void hash_random( BYTE *d, const BYTE *s, unsigned int len ) +{ + unsigned int i; + + for (i = 0; i < len; ++i) + d[i] = byte_hash[s[i]]; +} + static int addrinfo_flags_from_unix( int flags ) { int ws_flags = 0; @@ -889,6 +934,44 @@ static NTSTATUS unix_gethostbyaddr( void *args ) #endif }
+static int compare_addrs_hashed( const void *a1, const void *a2, int addr_len ) +{ + char a1_hashed[16], a2_hashed[16]; + + assert( addr_len <= sizeof(a1_hashed) ); + hash_random( (BYTE *)a1_hashed, a1, addr_len ); + hash_random( (BYTE *)a2_hashed, a2, addr_len ); + return memcmp( a1_hashed, a2_hashed, addr_len ); +} + +static void sort_addrs_hashed( struct hostent *host ) +{ + /* On Unix gethostbyname() may return IP addresses in random order on each call. On Windows the order of + * IP addresses is not determined as well but it is the same on consequent calls (changes after network + * resets and probably DNS timeout expiration). + * Life is Strange Remastered depends on gethostbyname() returning IP addresses in the same order to reuse + * the established TLS connection and avoid timeouts that happen in game when establishing multiple extra TLS + * connections. + * Just sorting the addresses would break server load balancing provided by gethostbyname(), so randomize the + * sort once per process. */ + unsigned int i, j; + char *tmp; + + pthread_once( &hash_init_once, init_hash ); + + for (i = 0; host->h_addr_list[i]; ++i) + { + for (j = i + 1; host->h_addr_list[j]; ++j) + { + if (compare_addrs_hashed( host->h_addr_list[j], host->h_addr_list[i], host->h_length ) < 0) + { + tmp = host->h_addr_list[j]; + host->h_addr_list[j] = host->h_addr_list[i]; + host->h_addr_list[i] = tmp; + } + } + } +}
#ifdef HAVE_LINUX_GETHOSTBYNAME_R_6 static NTSTATUS unix_gethostbyname( void *args ) @@ -915,9 +998,14 @@ static NTSTATUS unix_gethostbyname( void *args ) }
if (!unix_host) + { ret = (locerr < 0 ? errno_from_unix( errno ) : host_errno_from_unix( locerr )); + } else + { + sort_addrs_hashed( unix_host ); ret = hostent_from_unix( unix_host, params->host, params->size ); + }
free( unix_buffer ); return ret; @@ -938,6 +1026,7 @@ static NTSTATUS unix_gethostbyname( void *args ) return ret; }
+ sort_addrs_hashed( unix_host ); ret = hostent_from_unix( unix_host, params->host, params->size );
pthread_mutex_unlock( &host_mutex );