ws2_32: Move getaddrinfo() support to a new Unix library.

Signed-off-by: Zebediah Figura <zfigura@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zebediah Figura 2021-08-03 23:53:33 -05:00 committed by Alexandre Julliard
parent c07da78a2e
commit e58405000c
6 changed files with 673 additions and 430 deletions

View file

@ -7,6 +7,7 @@ EXTRALIBS = $(POLL_LIBS)
C_SRCS = \
async.c \
protocol.c \
socket.c
socket.c \
unixlib.c
RC_SRCS = version.rc

View file

@ -33,21 +33,6 @@ DECLARE_CRITICAL_SECTION(csWSgetXXXbyYYY);
#define MAP_OPTION(opt) { WS_##opt, opt }
static const int ws_aiflag_map[][2] =
{
MAP_OPTION( AI_PASSIVE ),
MAP_OPTION( AI_CANONNAME ),
MAP_OPTION( AI_NUMERICHOST ),
#ifdef AI_NUMERICSERV
MAP_OPTION( AI_NUMERICSERV ),
#endif
#ifdef AI_V4MAPPED
MAP_OPTION( AI_V4MAPPED ),
#endif
MAP_OPTION( AI_ALL ),
MAP_OPTION( AI_ADDRCONFIG ),
};
static const int ws_eai_map[][2] =
{
MAP_OPTION( EAI_AGAIN ),
@ -82,21 +67,6 @@ static const int ws_af_map[][2] =
{FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO},
};
static const int ws_proto_map[][2] =
{
MAP_OPTION( IPPROTO_IP ),
MAP_OPTION( IPPROTO_TCP ),
MAP_OPTION( IPPROTO_UDP ),
MAP_OPTION( IPPROTO_IPV6 ),
MAP_OPTION( IPPROTO_ICMP ),
MAP_OPTION( IPPROTO_IGMP ),
MAP_OPTION( IPPROTO_RAW ),
{WS_IPPROTO_IPV4, IPPROTO_IPIP},
{FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO},
};
#define IS_IPX_PROTO(X) ((X) >= WS_NSPROTO_IPX && (X) <= WS_NSPROTO_IPX + 255)
static int convert_af_w2u( int family )
{
unsigned int i;
@ -123,78 +93,6 @@ static int convert_af_u2w( int family )
return -1;
}
static int convert_proto_w2u( int protocol )
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(ws_proto_map); i++)
{
if (ws_proto_map[i][0] == protocol)
return ws_proto_map[i][1];
}
if (IS_IPX_PROTO(protocol))
return protocol;
FIXME( "unhandled Windows socket protocol %d\n", protocol );
return -1;
}
static int convert_proto_u2w( int protocol )
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(ws_proto_map); i++)
{
if (ws_proto_map[i][1] == protocol)
return ws_proto_map[i][0];
}
/* if value is inside IPX range just return it - the kernel simply
* echoes the value used in the socket() function */
if (IS_IPX_PROTO(protocol))
return protocol;
FIXME("unhandled UNIX socket protocol %d\n", protocol);
return -1;
}
static int convert_aiflag_w2u( int winflags )
{
unsigned int i;
int unixflags = 0;
for (i = 0; i < ARRAY_SIZE(ws_aiflag_map); i++)
{
if (ws_aiflag_map[i][0] & winflags)
{
unixflags |= ws_aiflag_map[i][1];
winflags &= ~ws_aiflag_map[i][0];
}
}
if (winflags)
FIXME( "Unhandled windows AI_xxx flags 0x%x\n", winflags );
return unixflags;
}
static int convert_aiflag_u2w( int unixflags )
{
unsigned int i;
int winflags = 0;
for (i = 0; i < ARRAY_SIZE(ws_aiflag_map); i++)
{
if (ws_aiflag_map[i][1] & unixflags)
{
winflags |= ws_aiflag_map[i][0];
unixflags &= ~ws_aiflag_map[i][1];
}
}
if (unixflags)
WARN( "Unhandled UNIX AI_xxx flags 0x%x\n", unixflags );
return winflags;
}
int convert_eai_u2w( int unixret )
{
int i;
@ -232,62 +130,37 @@ static char *get_fqdn(void)
return ret;
}
static BOOL addrinfo_in_list( const struct WS_addrinfo *list, const struct WS_addrinfo *ai )
{
const struct WS_addrinfo *cursor = list;
while (cursor)
{
if (ai->ai_flags == cursor->ai_flags &&
ai->ai_family == cursor->ai_family &&
ai->ai_socktype == cursor->ai_socktype &&
ai->ai_protocol == cursor->ai_protocol &&
ai->ai_addrlen == cursor->ai_addrlen &&
!memcmp(ai->ai_addr, cursor->ai_addr, ai->ai_addrlen) &&
((ai->ai_canonname && cursor->ai_canonname && !strcmp(ai->ai_canonname, cursor->ai_canonname))
|| (!ai->ai_canonname && !cursor->ai_canonname)))
{
return TRUE;
}
cursor = cursor->ai_next;
}
return FALSE;
}
/***********************************************************************
* getaddrinfo (ws2_32.@)
*/
int WINAPI WS_getaddrinfo( const char *nodename, const char *servname,
const struct WS_addrinfo *hints, struct WS_addrinfo **res )
int WINAPI WS_getaddrinfo( const char *node, const char *service,
const struct WS_addrinfo *hints, struct WS_addrinfo **info )
{
#ifdef HAVE_GETADDRINFO
struct addrinfo *unixaires = NULL;
int result;
struct addrinfo unixhints, *punixhints = NULL;
char *nodev6 = NULL, *fqdn = NULL;
const char *node;
int ret;
*res = NULL;
if (!nodename && !servname)
TRACE( "node %s, service %s, hints %p\n", debugstr_a(node), debugstr_a(service), hints );
*info = NULL;
if (!node && !service)
{
SetLastError(WSAHOST_NOT_FOUND);
SetLastError( WSAHOST_NOT_FOUND );
return WSAHOST_NOT_FOUND;
}
if (!nodename)
node = NULL;
else if (!nodename[0])
if (node)
{
if (!(fqdn = get_fqdn())) return WSA_NOT_ENOUGH_MEMORY;
node = fqdn;
}
else
{
node = nodename;
/* Check for [ipv6] or [ipv6]:portnumber, which are supported by Windows */
if (!hints || hints->ai_family == WS_AF_UNSPEC || hints->ai_family == WS_AF_INET6)
if (!node[0])
{
if (!(fqdn = get_fqdn())) return WSA_NOT_ENOUGH_MEMORY;
node = fqdn;
}
else if (!hints || hints->ai_family == WS_AF_UNSPEC || hints->ai_family == WS_AF_INET6)
{
/* [ipv6] or [ipv6]:portnumber are supported by Windows */
char *close_bracket;
if (node[0] == '[' && (close_bracket = strchr(node + 1, ']')))
@ -300,50 +173,9 @@ int WINAPI WS_getaddrinfo( const char *nodename, const char *servname,
}
}
/* servname tweak required by OSX and BSD kernels */
if (servname && !servname[0]) servname = "0";
ret = unix_funcs->getaddrinfo( node, service, hints, info );
if (hints)
{
punixhints = &unixhints;
memset( &unixhints, 0, sizeof(unixhints) );
punixhints->ai_flags = convert_aiflag_w2u( hints->ai_flags );
/* zero is a wildcard, no need to convert */
if (hints->ai_family)
punixhints->ai_family = convert_af_w2u( hints->ai_family );
if (hints->ai_socktype)
punixhints->ai_socktype = convert_socktype_w2u( hints->ai_socktype );
if (hints->ai_protocol)
punixhints->ai_protocol = max( convert_proto_w2u( hints->ai_protocol ), 0 );
if (punixhints->ai_socktype < 0)
{
SetLastError( WSAESOCKTNOSUPPORT );
HeapFree( GetProcessHeap(), 0, fqdn );
HeapFree( GetProcessHeap(), 0, nodev6 );
return -1;
}
/* windows allows invalid combinations of socket type and protocol, unix does not.
* fix the parameters here to make getaddrinfo call always work */
if (punixhints->ai_protocol == IPPROTO_TCP
&& punixhints->ai_socktype != SOCK_STREAM
&& punixhints->ai_socktype != SOCK_SEQPACKET)
punixhints->ai_socktype = 0;
else if (punixhints->ai_protocol == IPPROTO_UDP && punixhints->ai_socktype != SOCK_DGRAM)
punixhints->ai_socktype = 0;
else if (IS_IPX_PROTO(punixhints->ai_protocol) && punixhints->ai_socktype != SOCK_DGRAM)
punixhints->ai_socktype = 0;
else if (punixhints->ai_protocol == IPPROTO_IPV6)
punixhints->ai_protocol = 0;
}
/* getaddrinfo(3) is thread safe, no need to wrap in CS */
result = getaddrinfo( node, servname, punixhints, &unixaires );
if (result && (!hints || !(hints->ai_flags & WS_AI_NUMERICHOST)) && node)
if (ret && (!hints || !(hints->ai_flags & WS_AI_NUMERICHOST)) && node)
{
if (!fqdn && !(fqdn = get_fqdn()))
{
@ -356,115 +188,33 @@ int WINAPI WS_getaddrinfo( const char *nodename, const char *servname,
* by sending a NULL host and avoid sending a NULL servname too because that
* is invalid */
ERR_(winediag)( "Failed to resolve your host name IP\n" );
result = getaddrinfo( NULL, servname ? servname : "0", punixhints, &unixaires );
if (!result && punixhints && (punixhints->ai_flags & AI_CANONNAME) && unixaires && !unixaires->ai_canonname)
ret = unix_funcs->getaddrinfo( NULL, service, hints, info );
if (!ret && hints && (hints->ai_flags & WS_AI_CANONNAME) && *info && !(*info)->ai_canonname)
{
freeaddrinfo( unixaires );
result = EAI_NONAME;
WS_freeaddrinfo( *info );
*info = NULL;
return EAI_NONAME;
}
}
}
TRACE( "%s, %s %p -> %p %d\n", debugstr_a(nodename), debugstr_a(servname), hints, res, result );
HeapFree( GetProcessHeap(), 0, fqdn );
HeapFree( GetProcessHeap(), 0, nodev6 );
if (!result)
if (!ret && TRACE_ON(winsock))
{
struct addrinfo *xuai = unixaires;
struct WS_addrinfo **xai = res;
struct WS_addrinfo *ai;
*xai = NULL;
while (xuai)
for (ai = *info; ai != NULL; ai = ai->ai_next)
{
struct WS_addrinfo *ai = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct WS_addrinfo) );
SIZE_T len;
if (!ai)
goto outofmem;
ai->ai_flags = convert_aiflag_u2w( xuai->ai_flags );
ai->ai_family = convert_af_u2w( xuai->ai_family );
/* copy whatever was sent in the hints */
if (hints)
{
ai->ai_socktype = hints->ai_socktype;
ai->ai_protocol = hints->ai_protocol;
}
else
{
ai->ai_socktype = convert_socktype_u2w( xuai->ai_socktype );
ai->ai_protocol = convert_proto_u2w( xuai->ai_protocol );
}
if (xuai->ai_canonname)
{
TRACE( "canon name - %s\n", debugstr_a(xuai->ai_canonname) );
ai->ai_canonname = HeapAlloc( GetProcessHeap(), 0, strlen( xuai->ai_canonname ) + 1 );
if (!ai->ai_canonname)
goto outofmem;
strcpy( ai->ai_canonname, xuai->ai_canonname );
}
len = xuai->ai_addrlen;
ai->ai_addr = HeapAlloc( GetProcessHeap(), 0, len );
if (!ai->ai_addr)
goto outofmem;
ai->ai_addrlen = len;
do
{
int winlen = ai->ai_addrlen;
if (!ws_sockaddr_u2ws( xuai->ai_addr, ai->ai_addr, &winlen ))
{
ai->ai_addrlen = winlen;
break;
}
len *= 2;
ai->ai_addr = HeapReAlloc( GetProcessHeap(), 0, ai->ai_addr, len );
if (!ai->ai_addr)
goto outofmem;
ai->ai_addrlen = len;
} while (1);
if (addrinfo_in_list( *res, ai ))
{
HeapFree( GetProcessHeap(), 0, ai->ai_canonname );
HeapFree( GetProcessHeap(), 0, ai->ai_addr );
HeapFree( GetProcessHeap(), 0, ai );
}
else
{
*xai = ai;
xai = &ai->ai_next;
}
xuai = xuai->ai_next;
}
freeaddrinfo( unixaires );
if (TRACE_ON(winsock))
{
struct WS_addrinfo *ai = *res;
while (ai)
{
TRACE( "=> %p, flags %#x, family %d, type %d, protocol %d, len %ld, name %s, addr %s\n",
ai, ai->ai_flags, ai->ai_family, ai->ai_socktype, ai->ai_protocol, ai->ai_addrlen,
ai->ai_canonname, debugstr_sockaddr(ai->ai_addr) );
ai = ai->ai_next;
}
TRACE( "=> %p, flags %#x, family %d, type %d, protocol %d, len %ld, name %s, addr %s\n",
ai, ai->ai_flags, ai->ai_family, ai->ai_socktype, ai->ai_protocol, ai->ai_addrlen,
ai->ai_canonname, debugstr_sockaddr(ai->ai_addr) );
}
}
else
result = convert_eai_u2w( result );
SetLastError( result );
return result;
outofmem:
if (*res) WS_freeaddrinfo( *res );
if (unixaires) freeaddrinfo( unixaires );
return WSA_NOT_ENOUGH_MEMORY;
#else
FIXME( "getaddrinfo() failed, not found during build time.\n" );
return EAI_FAIL;
#endif
SetLastError( ret );
return ret;
}

View file

@ -41,6 +41,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(winsock);
WINE_DECLARE_DEBUG_CHANNEL(winediag);
const struct unix_funcs *unix_funcs = NULL;
static const WSAPROTOCOL_INFOW supported_protocols[] =
{
{
@ -426,14 +428,6 @@ static int ws_protocol_info(SOCKET s, int unicode, WSAPROTOCOL_INFOW *buffer, in
#define MAP_OPTION(opt) { WS_##opt, opt }
static const int ws_socktype_map[][2] =
{
MAP_OPTION( SOCK_DGRAM ),
MAP_OPTION( SOCK_STREAM ),
MAP_OPTION( SOCK_RAW ),
{FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO},
};
UINT sock_get_error( int err )
{
switch(err)
@ -626,38 +620,20 @@ static HANDLE get_sync_event(void)
return data->sync_event;
}
/***********************************************************************
* DllMain (WS2_32.init)
*/
BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved )
{
if (reason == DLL_THREAD_DETACH)
switch (reason)
{
case DLL_PROCESS_ATTACH:
return !__wine_init_unix_lib( instance, reason, NULL, &unix_funcs );
case DLL_THREAD_DETACH:
free_per_thread_data();
}
return TRUE;
}
int
convert_socktype_w2u(int windowssocktype) {
unsigned int i;
for (i = 0; i < ARRAY_SIZE(ws_socktype_map); i++)
if (ws_socktype_map[i][0] == windowssocktype)
return ws_socktype_map[i][1];
FIXME("unhandled Windows socket type %d\n", windowssocktype);
return -1;
}
int
convert_socktype_u2w(int unixsocktype) {
unsigned int i;
for (i = 0; i < ARRAY_SIZE(ws_socktype_map); i++)
if (ws_socktype_map[i][1] == unixsocktype)
return ws_socktype_map[i][0];
FIXME("unhandled UNIX socket type %d\n", unixsocktype);
return -1;
}
/***********************************************************************
* WSAStartup (WS2_32.115)
@ -858,112 +834,6 @@ unsigned int ws_sockaddr_ws2u( const struct WS_sockaddr *wsaddr, int wsaddrlen,
return uaddrlen;
}
/* Returns 0 if successful, -1 if the buffer is too small */
int ws_sockaddr_u2ws(const struct sockaddr *uaddr, struct WS_sockaddr *wsaddr, int *wsaddrlen)
{
int res;
switch(uaddr->sa_family)
{
#ifdef HAS_IPX
case AF_IPX:
{
const struct sockaddr_ipx* uipx=(const struct sockaddr_ipx*)uaddr;
struct WS_sockaddr_ipx* wsipx=(struct WS_sockaddr_ipx*)wsaddr;
res=-1;
switch (*wsaddrlen) /* how much can we copy? */
{
default:
res=0; /* enough */
*wsaddrlen = sizeof(*wsipx);
wsipx->sa_socket=uipx->sipx_port;
/* fall through */
case 13:
case 12:
memcpy(wsipx->sa_nodenum,uipx->sipx_node,sizeof(wsipx->sa_nodenum));
/* fall through */
case 11:
case 10:
case 9:
case 8:
case 7:
case 6:
memcpy(wsipx->sa_netnum,&uipx->sipx_network,sizeof(wsipx->sa_netnum));
/* fall through */
case 5:
case 4:
case 3:
case 2:
wsipx->sa_family=WS_AF_IPX;
/* fall through */
case 1:
case 0:
/* way too small */
break;
}
}
break;
#endif
#ifdef HAS_IRDA
case AF_IRDA: {
const struct sockaddr_irda *uin = (const struct sockaddr_irda *)uaddr;
SOCKADDR_IRDA *win = (SOCKADDR_IRDA *)wsaddr;
if (*wsaddrlen < sizeof(SOCKADDR_IRDA))
return -1;
win->irdaAddressFamily = WS_AF_IRDA;
memcpy( win->irdaDeviceID, &uin->sir_addr, sizeof(win->irdaDeviceID) );
if (uin->sir_lsap_sel != LSAP_ANY)
sprintf( win->irdaServiceName, "LSAP-SEL%u", uin->sir_lsap_sel );
else
memcpy( win->irdaServiceName, uin->sir_name,
sizeof(win->irdaServiceName) );
return 0;
}
#endif
case AF_INET6: {
const struct sockaddr_in6* uin6 = (const struct sockaddr_in6*)uaddr;
struct WS_sockaddr_in6 *win6 = (struct WS_sockaddr_in6 *)wsaddr;
if (*wsaddrlen < sizeof(struct WS_sockaddr_in6))
return -1;
win6->sin6_family = WS_AF_INET6;
win6->sin6_port = uin6->sin6_port;
win6->sin6_flowinfo = uin6->sin6_flowinfo;
memcpy(&win6->sin6_addr, &uin6->sin6_addr, 16); /* 16 bytes = 128 address bits */
#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
win6->sin6_scope_id = uin6->sin6_scope_id;
#else
win6->sin6_scope_id = 0;
#endif
*wsaddrlen = sizeof(struct WS_sockaddr_in6);
return 0;
}
case AF_INET: {
const struct sockaddr_in* uin = (const struct sockaddr_in*)uaddr;
struct WS_sockaddr_in* win = (struct WS_sockaddr_in*)wsaddr;
if (*wsaddrlen < sizeof(struct WS_sockaddr_in))
return -1;
win->sin_family = WS_AF_INET;
win->sin_port = uin->sin_port;
memcpy(&win->sin_addr,&uin->sin_addr,4); /* 4 bytes = 32 address bits */
memset(win->sin_zero, 0, 8); /* Make sure the null padding is null */
*wsaddrlen = sizeof(struct WS_sockaddr_in);
return 0;
}
case AF_UNSPEC: {
memset(wsaddr,0,*wsaddrlen);
return 0;
}
default:
FIXME("Unknown address family %d\n", uaddr->sa_family);
return -1;
}
return res;
}
static INT WS_DuplicateSocket(BOOL unicode, SOCKET s, DWORD dwProcessId, LPWSAPROTOCOL_INFOW lpProtocolInfo)
{
HANDLE hProcess;

View file

@ -1740,7 +1740,7 @@ static void test_GetAddrInfoW(void)
result = NULL;
SetLastError(0xdeadbeef);
ret = GetAddrInfoW(localhost, NULL, &hint, &result);
todo_wine_if (hinttests[i].error) ok(ret == hinttests[i].error, "test %d: wrong ret %d\n", i, ret);
ok(ret == hinttests[i].error, "test %d: wrong ret %d\n", i, ret);
if (!ret)
{
for (p = result; p; p = p->ai_next)
@ -2293,7 +2293,7 @@ static void test_getaddrinfo(void)
result = NULL;
SetLastError(0xdeadbeef);
ret = getaddrinfo("localhost", NULL, &hint, &result);
todo_wine_if (hinttests[i].error) ok(ret == hinttests[i].error, "test %d: wrong ret %d\n", i, ret);
ok(ret == hinttests[i].error, "test %d: wrong ret %d\n", i, ret);
if (!ret)
{
for (p = result; p; p = p->ai_next)

618
dlls/ws2_32/unixlib.c Normal file
View file

@ -0,0 +1,618 @@
/*
* Unix library functions
*
* Copyright (C) 1993,1994,1996,1997 John Brezak, Erik Bos, Alex Korobka.
* Copyright (C) 2001 Stefan Leichter
* Copyright (C) 2004 Hans Leidekker
* Copyright (C) 2005 Marcus Meissner
* Copyright (C) 2006-2008 Kai Blin
*
* 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
*/
#if 0
#pragma makedep unix
#endif
#include "config.h"
#include <errno.h>
#include <stdarg.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_NETIPX_IPX_H
# include <netipx/ipx.h>
#elif defined(HAVE_LINUX_IPX_H)
# ifdef HAVE_ASM_TYPES_H
# include <asm/types.h>
# endif
# ifdef HAVE_LINUX_TYPES_H
# include <linux/types.h>
# endif
# include <linux/ipx.h>
#endif
#if defined(SOL_IPX) || defined(SO_DEFAULT_HEADERS)
# define HAS_IPX
#endif
#ifdef HAVE_LINUX_IRDA_H
# ifdef HAVE_LINUX_TYPES_H
# include <linux/types.h>
# endif
# include <linux/irda.h>
# define HAS_IRDA
#endif
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winerror.h"
#include "winternl.h"
#include "winsock2.h"
#include "ws2tcpip.h"
#include "wsipx.h"
#include "af_irda.h"
#include "wine/debug.h"
#include "ws2_32_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(winsock);
#define MAP(x) {WS_ ## x, x}
static const int addrinfo_flag_map[][2] =
{
MAP( AI_PASSIVE ),
MAP( AI_CANONNAME ),
MAP( AI_NUMERICHOST ),
#ifdef AI_NUMERICSERV
MAP( AI_NUMERICSERV ),
#endif
#ifdef AI_V4MAPPED
MAP( AI_V4MAPPED ),
#endif
MAP( AI_ALL ),
MAP( AI_ADDRCONFIG ),
};
static const int family_map[][2] =
{
MAP( AF_UNSPEC ),
MAP( AF_INET ),
MAP( AF_INET6 ),
#ifdef AF_IPX
MAP( AF_IPX ),
#endif
#ifdef AF_IRDA
MAP( AF_IRDA ),
#endif
};
static const int socktype_map[][2] =
{
MAP( SOCK_STREAM ),
MAP( SOCK_DGRAM ),
MAP( SOCK_RAW ),
};
static const int ip_protocol_map[][2] =
{
MAP( IPPROTO_IP ),
MAP( IPPROTO_TCP ),
MAP( IPPROTO_UDP ),
MAP( IPPROTO_IPV6 ),
MAP( IPPROTO_ICMP ),
MAP( IPPROTO_IGMP ),
MAP( IPPROTO_RAW ),
{WS_IPPROTO_IPV4, IPPROTO_IPIP},
};
#undef MAP
static int addrinfo_flags_from_unix( int flags )
{
int ws_flags = 0;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(addrinfo_flag_map); ++i)
{
if (flags & addrinfo_flag_map[i][1])
{
ws_flags |= addrinfo_flag_map[i][0];
flags &= ~addrinfo_flag_map[i][1];
}
}
if (flags)
FIXME( "unhandled flags %#x\n", flags );
return ws_flags;
}
static int addrinfo_flags_to_unix( int flags )
{
int unix_flags = 0;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(addrinfo_flag_map); ++i)
{
if (flags & addrinfo_flag_map[i][0])
{
unix_flags |= addrinfo_flag_map[i][1];
flags &= ~addrinfo_flag_map[i][0];
}
}
if (flags)
FIXME( "unhandled flags %#x\n", flags );
return unix_flags;
}
static int family_from_unix( int family )
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(family_map); ++i)
{
if (family == family_map[i][1])
return family_map[i][0];
}
FIXME( "unhandled family %u\n", family );
return -1;
}
static int family_to_unix( int family )
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(family_map); ++i)
{
if (family == family_map[i][0])
return family_map[i][1];
}
FIXME( "unhandled family %u\n", family );
return -1;
}
static int socktype_from_unix( int type )
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(socktype_map); ++i)
{
if (type == socktype_map[i][1])
return socktype_map[i][0];
}
FIXME( "unhandled type %u\n", type );
return -1;
}
static int socktype_to_unix( int type )
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(socktype_map); ++i)
{
if (type == socktype_map[i][0])
return socktype_map[i][1];
}
FIXME( "unhandled type %u\n", type );
return -1;
}
static int protocol_from_unix( int protocol )
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(ip_protocol_map); ++i)
{
if (protocol == ip_protocol_map[i][1])
return ip_protocol_map[i][0];
}
if (protocol >= WS_NSPROTO_IPX && protocol <= WS_NSPROTO_IPX + 255)
return protocol;
FIXME( "unhandled protocol %u\n", protocol );
return -1;
}
static int protocol_to_unix( int protocol )
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(ip_protocol_map); ++i)
{
if (protocol == ip_protocol_map[i][0])
return ip_protocol_map[i][1];
}
if (protocol >= WS_NSPROTO_IPX && protocol <= WS_NSPROTO_IPX + 255)
return protocol;
FIXME( "unhandled protocol %u\n", protocol );
return -1;
}
static unsigned int errno_from_unix( int err )
{
switch (err)
{
case EINTR: return WSAEINTR;
case EBADF: return WSAEBADF;
case EPERM:
case EACCES: return WSAEACCES;
case EFAULT: return WSAEFAULT;
case EINVAL: return WSAEINVAL;
case EMFILE: return WSAEMFILE;
case EINPROGRESS:
case EWOULDBLOCK: return WSAEWOULDBLOCK;
case EALREADY: return WSAEALREADY;
case ENOTSOCK: return WSAENOTSOCK;
case EDESTADDRREQ: return WSAEDESTADDRREQ;
case EMSGSIZE: return WSAEMSGSIZE;
case EPROTOTYPE: return WSAEPROTOTYPE;
case ENOPROTOOPT: return WSAENOPROTOOPT;
case EPROTONOSUPPORT: return WSAEPROTONOSUPPORT;
case ESOCKTNOSUPPORT: return WSAESOCKTNOSUPPORT;
case EOPNOTSUPP: return WSAEOPNOTSUPP;
case EPFNOSUPPORT: return WSAEPFNOSUPPORT;
case EAFNOSUPPORT: return WSAEAFNOSUPPORT;
case EADDRINUSE: return WSAEADDRINUSE;
case EADDRNOTAVAIL: return WSAEADDRNOTAVAIL;
case ENETDOWN: return WSAENETDOWN;
case ENETUNREACH: return WSAENETUNREACH;
case ENETRESET: return WSAENETRESET;
case ECONNABORTED: return WSAECONNABORTED;
case EPIPE:
case ECONNRESET: return WSAECONNRESET;
case ENOBUFS: return WSAENOBUFS;
case EISCONN: return WSAEISCONN;
case ENOTCONN: return WSAENOTCONN;
case ESHUTDOWN: return WSAESHUTDOWN;
case ETOOMANYREFS: return WSAETOOMANYREFS;
case ETIMEDOUT: return WSAETIMEDOUT;
case ECONNREFUSED: return WSAECONNREFUSED;
case ELOOP: return WSAELOOP;
case ENAMETOOLONG: return WSAENAMETOOLONG;
case EHOSTDOWN: return WSAEHOSTDOWN;
case EHOSTUNREACH: return WSAEHOSTUNREACH;
case ENOTEMPTY: return WSAENOTEMPTY;
#ifdef EPROCLIM
case EPROCLIM: return WSAEPROCLIM;
#endif
#ifdef EUSERS
case EUSERS: return WSAEUSERS;
#endif
#ifdef EDQUOT
case EDQUOT: return WSAEDQUOT;
#endif
#ifdef ESTALE
case ESTALE: return WSAESTALE;
#endif
#ifdef EREMOTE
case EREMOTE: return WSAEREMOTE;
#endif
default:
FIXME( "unknown error: %s", strerror( err ) );
return WSAEFAULT;
}
}
static int addrinfo_err_from_unix( int err )
{
switch (err)
{
case EAI_AGAIN: return WS_EAI_AGAIN;
case EAI_BADFLAGS: return WS_EAI_BADFLAGS;
case EAI_FAIL: return WS_EAI_FAIL;
case EAI_FAMILY: return WS_EAI_FAMILY;
case EAI_MEMORY: return WS_EAI_MEMORY;
/* EAI_NODATA is deprecated, but still used by Windows and Linux. We map
* the newer EAI_NONAME to EAI_NODATA for now until Windows changes too. */
#ifdef EAI_NODATA
case EAI_NODATA: return WS_EAI_NODATA;
#endif
#ifdef EAI_NONAME
case EAI_NONAME: return WS_EAI_NODATA;
#endif
case EAI_SERVICE: return WS_EAI_SERVICE;
case EAI_SOCKTYPE: return WS_EAI_SOCKTYPE;
case EAI_SYSTEM:
/* some broken versions of glibc return EAI_SYSTEM and set errno to
* 0 instead of returning EAI_NONAME */
return errno ? errno_from_unix( errno ) : WS_EAI_NONAME;
default:
FIXME( "unhandled error %d\n", err );
return err;
}
}
union unix_sockaddr
{
struct sockaddr addr;
struct sockaddr_in in;
struct sockaddr_in6 in6;
#ifdef HAS_IPX
struct sockaddr_ipx ipx;
#endif
#ifdef HAS_IRDA
struct sockaddr_irda irda;
#endif
};
/* different from the version in ntdll and server; it does not return failure if
* given a short buffer */
static int sockaddr_from_unix( const union unix_sockaddr *uaddr, struct WS_sockaddr *wsaddr, socklen_t wsaddrlen )
{
memset( wsaddr, 0, wsaddrlen );
switch (uaddr->addr.sa_family)
{
case AF_INET:
{
struct WS_sockaddr_in win = {0};
if (wsaddrlen >= sizeof(win))
{
win.sin_family = WS_AF_INET;
win.sin_port = uaddr->in.sin_port;
memcpy( &win.sin_addr, &uaddr->in.sin_addr, sizeof(win.sin_addr) );
memcpy( wsaddr, &win, sizeof(win) );
}
return sizeof(win);
}
case AF_INET6:
{
struct WS_sockaddr_in6 win = {0};
if (wsaddrlen >= sizeof(win))
{
win.sin6_family = WS_AF_INET6;
win.sin6_port = uaddr->in6.sin6_port;
win.sin6_flowinfo = uaddr->in6.sin6_flowinfo;
memcpy( &win.sin6_addr, &uaddr->in6.sin6_addr, sizeof(win.sin6_addr) );
#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
win.sin6_scope_id = uaddr->in6.sin6_scope_id;
#endif
memcpy( wsaddr, &win, sizeof(win) );
}
return sizeof(win);
}
#ifdef HAS_IPX
case AF_IPX:
{
struct WS_sockaddr_ipx win = {0};
if (wsaddrlen >= sizeof(win))
{
win.sa_family = WS_AF_IPX;
memcpy( win.sa_netnum, &uaddr->ipx.sipx_network, sizeof(win.sa_netnum) );
memcpy( win.sa_nodenum, &uaddr->ipx.sipx_node, sizeof(win.sa_nodenum) );
win.sa_socket = uaddr->ipx.sipx_port;
memcpy( wsaddr, &win, sizeof(win) );
}
return sizeof(win);
}
#endif
#ifdef HAS_IRDA
case AF_IRDA:
{
SOCKADDR_IRDA win;
if (wsaddrlen >= sizeof(win))
{
win.irdaAddressFamily = WS_AF_IRDA;
memcpy( win.irdaDeviceID, &uaddr->irda.sir_addr, sizeof(win.irdaDeviceID) );
if (uaddr->irda.sir_lsap_sel != LSAP_ANY)
snprintf( win.irdaServiceName, sizeof(win.irdaServiceName), "LSAP-SEL%u", uaddr->irda.sir_lsap_sel );
else
memcpy( win.irdaServiceName, uaddr->irda.sir_name, sizeof(win.irdaServiceName) );
memcpy( wsaddr, &win, sizeof(win) );
}
return sizeof(win);
}
#endif
case AF_UNSPEC:
return 0;
default:
FIXME( "unknown address family %d\n", uaddr->addr.sa_family );
return 0;
}
}
static BOOL addrinfo_in_list( const struct WS_addrinfo *list, const struct WS_addrinfo *ai )
{
const struct WS_addrinfo *cursor = list;
while (cursor)
{
if (ai->ai_flags == cursor->ai_flags &&
ai->ai_family == cursor->ai_family &&
ai->ai_socktype == cursor->ai_socktype &&
ai->ai_protocol == cursor->ai_protocol &&
ai->ai_addrlen == cursor->ai_addrlen &&
!memcmp( ai->ai_addr, cursor->ai_addr, ai->ai_addrlen ) &&
((ai->ai_canonname && cursor->ai_canonname && !strcmp( ai->ai_canonname, cursor->ai_canonname ))
|| (!ai->ai_canonname && !cursor->ai_canonname)))
{
return TRUE;
}
cursor = cursor->ai_next;
}
return FALSE;
}
static int CDECL unix_getaddrinfo( const char *node, const char *service,
const struct WS_addrinfo *hints, struct WS_addrinfo **info )
{
#ifdef HAVE_GETADDRINFO
struct addrinfo unix_hints = {0};
struct addrinfo *unix_info, *src;
struct WS_addrinfo *dst, *prev = NULL;
int ret;
*info = NULL;
/* servname tweak required by OSX and BSD kernels */
if (service && !service[0]) service = "0";
if (hints)
{
unix_hints.ai_flags = addrinfo_flags_to_unix( hints->ai_flags );
if (hints->ai_family)
unix_hints.ai_family = family_to_unix( hints->ai_family );
if (hints->ai_socktype)
{
if ((unix_hints.ai_socktype = socktype_to_unix( hints->ai_socktype )) < 0)
return WSAESOCKTNOSUPPORT;
}
if (hints->ai_protocol)
unix_hints.ai_protocol = max( protocol_to_unix( hints->ai_protocol ), 0 );
/* Windows allows some invalid combinations */
if (unix_hints.ai_protocol == IPPROTO_TCP
&& unix_hints.ai_socktype != SOCK_STREAM
&& unix_hints.ai_socktype != SOCK_SEQPACKET)
{
WARN( "ignoring invalid type %u for TCP\n", unix_hints.ai_socktype );
unix_hints.ai_socktype = 0;
}
else if (unix_hints.ai_protocol == IPPROTO_UDP && unix_hints.ai_socktype != SOCK_DGRAM)
{
WARN( "ignoring invalid type %u for UDP\n", unix_hints.ai_socktype );
unix_hints.ai_socktype = 0;
}
else if (unix_hints.ai_protocol >= WS_NSPROTO_IPX && unix_hints.ai_protocol <= WS_NSPROTO_IPX + 255
&& unix_hints.ai_socktype != SOCK_DGRAM)
{
WARN( "ignoring invalid type %u for IPX\n", unix_hints.ai_socktype );
unix_hints.ai_socktype = 0;
}
else if (unix_hints.ai_protocol == IPPROTO_IPV6)
{
WARN( "ignoring protocol IPv6\n" );
unix_hints.ai_protocol = 0;
}
}
ret = getaddrinfo( node, service, hints ? &unix_hints : NULL, &unix_info );
if (ret)
return addrinfo_err_from_unix( ret );
*info = NULL;
for (src = unix_info; src != NULL; src = src->ai_next)
{
if (!(dst = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*dst) )))
goto fail;
dst->ai_flags = addrinfo_flags_from_unix( src->ai_flags );
dst->ai_family = family_from_unix( src->ai_family );
if (hints)
{
dst->ai_socktype = hints->ai_socktype;
dst->ai_protocol = hints->ai_protocol;
}
else
{
dst->ai_socktype = socktype_from_unix( src->ai_socktype );
dst->ai_protocol = protocol_from_unix( src->ai_protocol );
}
if (src->ai_canonname)
{
if (!(dst->ai_canonname = RtlAllocateHeap( GetProcessHeap(), 0, strlen( src->ai_canonname ) + 1 )))
{
RtlFreeHeap( GetProcessHeap(), 0, dst );
goto fail;
}
strcpy( dst->ai_canonname, src->ai_canonname );
}
dst->ai_addrlen = sockaddr_from_unix( (const union unix_sockaddr *)src->ai_addr, NULL, 0 );
if (!(dst->ai_addr = RtlAllocateHeap( GetProcessHeap(), 0, dst->ai_addrlen )))
{
RtlFreeHeap( GetProcessHeap(), 0, dst->ai_canonname );
RtlFreeHeap( GetProcessHeap(), 0, dst );
goto fail;
}
sockaddr_from_unix( (const union unix_sockaddr *)src->ai_addr, dst->ai_addr, dst->ai_addrlen );
if (addrinfo_in_list( *info, dst ))
{
RtlFreeHeap( GetProcessHeap(), 0, dst->ai_canonname );
RtlFreeHeap( GetProcessHeap(), 0, dst->ai_addr );
RtlFreeHeap( GetProcessHeap(), 0, dst );
}
else
{
if (prev)
prev->ai_next = dst;
else
*info = dst;
prev = dst;
}
}
freeaddrinfo( unix_info );
return 0;
fail:
dst = *info;
while (dst)
{
struct WS_addrinfo *next;
RtlFreeHeap( GetProcessHeap(), 0, dst->ai_canonname );
RtlFreeHeap( GetProcessHeap(), 0, dst->ai_addr );
next = dst->ai_next;
RtlFreeHeap( GetProcessHeap(), 0, dst );
dst = next;
}
return WS_EAI_MEMORY;
#else
FIXME( "getaddrinfo() not found during build time\n" );
return WS_EAI_FAIL;
#endif
}
static const struct unix_funcs funcs =
{
unix_getaddrinfo,
};
NTSTATUS CDECL __wine_init_unix_lib( HMODULE module, DWORD reason, const void *ptr_in, void *ptr_out )
{
if (reason != DLL_PROCESS_ATTACH) return STATUS_SUCCESS;
*(const struct unix_funcs **)ptr_out = &funcs;
return STATUS_SUCCESS;
}

View file

@ -172,10 +172,6 @@ union generic_unix_sockaddr
};
int convert_eai_u2w( int ret ) DECLSPEC_HIDDEN;
int convert_socktype_u2w( int type ) DECLSPEC_HIDDEN;
int convert_socktype_w2u( int type ) DECLSPEC_HIDDEN;
int ws_sockaddr_u2ws( const struct sockaddr *unix_addr, struct WS_sockaddr *win_addr,
int *win_addr_len ) DECLSPEC_HIDDEN;
unsigned int ws_sockaddr_ws2u( const struct WS_sockaddr *win_addr, int win_addr_len,
union generic_unix_sockaddr *unix_addr ) DECLSPEC_HIDDEN;
@ -200,4 +196,12 @@ extern int num_startup;
struct per_thread_data *get_per_thread_data(void) DECLSPEC_HIDDEN;
struct unix_funcs
{
int (CDECL *getaddrinfo)( const char *node, const char *service,
const struct WS(addrinfo) *hints, struct WS(addrinfo) **info );
};
extern const struct unix_funcs *unix_funcs;
#endif