/* * Protocol-level socket 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 */ #include "config.h" #include "ws2_32_private.h" WINE_DEFAULT_DEBUG_CHANNEL(winsock); WINE_DECLARE_DEBUG_CHANNEL(winediag); 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 ), MAP_OPTION( EAI_BADFLAGS ), MAP_OPTION( EAI_FAIL ), MAP_OPTION( EAI_FAMILY ), MAP_OPTION( EAI_MEMORY ), /* Note: 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 MAP_OPTION( EAI_NODATA ), #endif #ifdef EAI_NONAME { WS_EAI_NODATA, EAI_NONAME }, #endif MAP_OPTION( EAI_SERVICE ), MAP_OPTION( EAI_SOCKTYPE ), { 0, 0 } }; static const int ws_af_map[][2] = { MAP_OPTION( AF_UNSPEC ), MAP_OPTION( AF_INET ), MAP_OPTION( AF_INET6 ), #ifdef HAS_IPX MAP_OPTION( AF_IPX ), #endif #ifdef AF_IRDA MAP_OPTION( AF_IRDA ), #endif {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; for (i = 0; i < ARRAY_SIZE(ws_af_map); i++) { if (ws_af_map[i][0] == family) return ws_af_map[i][1]; } FIXME( "unhandled Windows address family %d\n", family ); return -1; } static int convert_af_u2w( int family ) { unsigned int i; for (i = 0; i < ARRAY_SIZE(ws_af_map); i++) { if (ws_af_map[i][1] == family) return ws_af_map[i][0]; } FIXME( "unhandled UNIX address family %d\n", 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; if (!unixret) return 0; for (i = 0; ws_eai_map[i][0]; i++) { if (ws_eai_map[i][1] == unixret) return ws_eai_map[i][0]; } if (unixret == EAI_SYSTEM) /* There are broken versions of glibc which return EAI_SYSTEM * and set errno to 0 instead of returning EAI_NONAME. */ return errno ? sock_get_error( errno ) : WS_EAI_NONAME; FIXME("Unhandled unix EAI_xxx ret %d\n", unixret); return unixret; } static char *get_fqdn(void) { char *ret; DWORD size = 0; GetComputerNameExA( ComputerNamePhysicalDnsFullyQualified, NULL, &size ); if (GetLastError() != ERROR_MORE_DATA) return NULL; if (!(ret = HeapAlloc( GetProcessHeap(), 0, size ))) return NULL; if (!GetComputerNameExA( ComputerNamePhysicalDnsFullyQualified, ret, &size )) { HeapFree( GetProcessHeap(), 0, ret ); return NULL; } 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 ) { #ifdef HAVE_GETADDRINFO struct addrinfo *unixaires = NULL; int result; struct addrinfo unixhints, *punixhints = NULL; char *nodev6 = NULL, *fqdn = NULL; const char *node; *res = NULL; if (!nodename && !servname) { SetLastError(WSAHOST_NOT_FOUND); return WSAHOST_NOT_FOUND; } if (!nodename) node = NULL; else if (!nodename[0]) { 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) { char *close_bracket; if (node[0] == '[' && (close_bracket = strchr(node + 1, ']'))) { nodev6 = HeapAlloc( GetProcessHeap(), 0, close_bracket - node ); if (!nodev6) return WSA_NOT_ENOUGH_MEMORY; lstrcpynA( nodev6, node + 1, close_bracket - node ); node = nodev6; } } } /* servname tweak required by OSX and BSD kernels */ if (servname && !servname[0]) servname = "0"; 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 (!fqdn && !(fqdn = get_fqdn())) { HeapFree( GetProcessHeap(), 0, nodev6 ); return WSA_NOT_ENOUGH_MEMORY; } if (!strcmp( fqdn, node ) || (!strncmp( fqdn, node, strlen( node ) ) && fqdn[strlen( node )] == '.')) { /* If it didn't work it means the host name IP is not in /etc/hosts, try again * 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) { freeaddrinfo( unixaires ); result = 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) { struct addrinfo *xuai = unixaires; struct WS_addrinfo **xai = res; *xai = NULL; while (xuai) { 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; } } } 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 } static ADDRINFOEXW *addrinfo_AtoW( const struct WS_addrinfo *ai ) { ADDRINFOEXW *ret; if (!(ret = HeapAlloc( GetProcessHeap(), 0, sizeof(ADDRINFOEXW) ))) return NULL; ret->ai_flags = ai->ai_flags; ret->ai_family = ai->ai_family; ret->ai_socktype = ai->ai_socktype; ret->ai_protocol = ai->ai_protocol; ret->ai_addrlen = ai->ai_addrlen; ret->ai_canonname = NULL; ret->ai_addr = NULL; ret->ai_blob = NULL; ret->ai_bloblen = 0; ret->ai_provider = NULL; ret->ai_next = NULL; if (ai->ai_canonname) { int len = MultiByteToWideChar( CP_ACP, 0, ai->ai_canonname, -1, NULL, 0 ); if (!(ret->ai_canonname = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) { HeapFree( GetProcessHeap(), 0, ret ); return NULL; } MultiByteToWideChar( CP_ACP, 0, ai->ai_canonname, -1, ret->ai_canonname, len ); } if (ai->ai_addr) { if (!(ret->ai_addr = HeapAlloc( GetProcessHeap(), 0, ai->ai_addrlen ))) { HeapFree( GetProcessHeap(), 0, ret->ai_canonname ); HeapFree( GetProcessHeap(), 0, ret ); return NULL; } memcpy( ret->ai_addr, ai->ai_addr, ai->ai_addrlen ); } return ret; } static ADDRINFOEXW *addrinfo_list_AtoW( const struct WS_addrinfo *info ) { ADDRINFOEXW *ret, *infoW; if (!(ret = infoW = addrinfo_AtoW( info ))) return NULL; while (info->ai_next) { if (!(infoW->ai_next = addrinfo_AtoW( info->ai_next ))) { FreeAddrInfoExW( ret ); return NULL; } infoW = infoW->ai_next; info = info->ai_next; } return ret; } static struct WS_addrinfo *addrinfo_WtoA( const struct WS_addrinfoW *ai ) { struct WS_addrinfo *ret; if (!(ret = HeapAlloc( GetProcessHeap(), 0, sizeof(struct WS_addrinfo) ))) return NULL; ret->ai_flags = ai->ai_flags; ret->ai_family = ai->ai_family; ret->ai_socktype = ai->ai_socktype; ret->ai_protocol = ai->ai_protocol; ret->ai_addrlen = ai->ai_addrlen; ret->ai_canonname = NULL; ret->ai_addr = NULL; ret->ai_next = NULL; if (ai->ai_canonname) { int len = WideCharToMultiByte( CP_ACP, 0, ai->ai_canonname, -1, NULL, 0, NULL, NULL ); if (!(ret->ai_canonname = HeapAlloc( GetProcessHeap(), 0, len ))) { HeapFree( GetProcessHeap(), 0, ret ); return NULL; } WideCharToMultiByte( CP_ACP, 0, ai->ai_canonname, -1, ret->ai_canonname, len, NULL, NULL ); } if (ai->ai_addr) { if (!(ret->ai_addr = HeapAlloc( GetProcessHeap(), 0, sizeof(struct WS_sockaddr) ))) { HeapFree( GetProcessHeap(), 0, ret->ai_canonname ); HeapFree( GetProcessHeap(), 0, ret ); return NULL; } memcpy( ret->ai_addr, ai->ai_addr, sizeof(struct WS_sockaddr) ); } return ret; } struct getaddrinfo_args { OVERLAPPED *overlapped; LPLOOKUPSERVICE_COMPLETION_ROUTINE completion_routine; ADDRINFOEXW **result; char *nodename; char *servname; struct WS_addrinfo *hints; }; static void WINAPI getaddrinfo_callback(TP_CALLBACK_INSTANCE *instance, void *context) { struct getaddrinfo_args *args = context; OVERLAPPED *overlapped = args->overlapped; HANDLE event = overlapped->hEvent; LPLOOKUPSERVICE_COMPLETION_ROUTINE completion_routine = args->completion_routine; struct WS_addrinfo *res; int ret; ret = WS_getaddrinfo( args->nodename, args->servname, args->hints, &res ); if (res) { *args->result = addrinfo_list_AtoW(res); overlapped->u.Pointer = args->result; WS_freeaddrinfo(res); } HeapFree( GetProcessHeap(), 0, args->nodename ); HeapFree( GetProcessHeap(), 0, args->servname ); HeapFree( GetProcessHeap(), 0, args ); overlapped->Internal = ret; if (completion_routine) completion_routine( ret, 0, overlapped ); if (event) SetEvent( event ); } static int WS_getaddrinfoW( const WCHAR *nodename, const WCHAR *servname, const struct WS_addrinfo *hints, ADDRINFOEXW **res, OVERLAPPED *overlapped, LPLOOKUPSERVICE_COMPLETION_ROUTINE completion_routine ) { int ret = EAI_MEMORY, len, i; char *nodenameA = NULL, *servnameA = NULL; struct WS_addrinfo *resA; WCHAR *local_nodenameW = (WCHAR *)nodename; *res = NULL; if (nodename) { /* Is this an IDN? Most likely if any char is above the Ascii table, this * is the simplest validation possible, further validation will be done by * the native getaddrinfo() */ for (i = 0; nodename[i]; i++) { if (nodename[i] > 'z') break; } if (nodename[i]) { if (hints && (hints->ai_flags & WS_AI_DISABLE_IDN_ENCODING)) { /* Name requires conversion but it was disabled */ ret = WSAHOST_NOT_FOUND; SetLastError( ret ); goto end; } len = IdnToAscii( 0, nodename, -1, NULL, 0 ); if (!len) { ERR("Failed to convert %s to punycode\n", debugstr_w(nodename)); ret = EAI_FAIL; goto end; } if (!(local_nodenameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) goto end; IdnToAscii( 0, nodename, -1, local_nodenameW, len ); } } if (local_nodenameW) { len = WideCharToMultiByte( CP_ACP, 0, local_nodenameW, -1, NULL, 0, NULL, NULL ); if (!(nodenameA = HeapAlloc( GetProcessHeap(), 0, len ))) goto end; WideCharToMultiByte( CP_ACP, 0, local_nodenameW, -1, nodenameA, len, NULL, NULL ); } if (servname) { len = WideCharToMultiByte( CP_ACP, 0, servname, -1, NULL, 0, NULL, NULL ); if (!(servnameA = HeapAlloc( GetProcessHeap(), 0, len ))) goto end; WideCharToMultiByte( CP_ACP, 0, servname, -1, servnameA, len, NULL, NULL ); } if (overlapped) { struct getaddrinfo_args *args; if (overlapped->hEvent && completion_routine) { ret = WSAEINVAL; goto end; } if (!(args = HeapAlloc( GetProcessHeap(), 0, sizeof(*args) + sizeof(*args->hints) ))) goto end; args->overlapped = overlapped; args->completion_routine = completion_routine; args->result = res; args->nodename = nodenameA; args->servname = servnameA; if (hints) { args->hints = (struct WS_addrinfo *)(args + 1); args->hints->ai_flags = hints->ai_flags; args->hints->ai_family = hints->ai_family; args->hints->ai_socktype = hints->ai_socktype; args->hints->ai_protocol = hints->ai_protocol; } else args->hints = NULL; overlapped->Internal = WSAEINPROGRESS; if (!TrySubmitThreadpoolCallback( getaddrinfo_callback, args, NULL )) { HeapFree( GetProcessHeap(), 0, args ); ret = GetLastError(); goto end; } if (local_nodenameW != nodename) HeapFree( GetProcessHeap(), 0, local_nodenameW ); SetLastError( ERROR_IO_PENDING ); return ERROR_IO_PENDING; } ret = WS_getaddrinfo( nodenameA, servnameA, hints, &resA ); if (!ret) { *res = addrinfo_list_AtoW( resA ); WS_freeaddrinfo( resA ); } end: if (local_nodenameW != nodename) HeapFree( GetProcessHeap(), 0, local_nodenameW ); HeapFree( GetProcessHeap(), 0, nodenameA ); HeapFree( GetProcessHeap(), 0, servnameA ); return ret; } /*********************************************************************** * GetAddrInfoExW (ws2_32.@) */ int WINAPI GetAddrInfoExW( const WCHAR *name, const WCHAR *servname, DWORD namespace, GUID *namespace_id, const ADDRINFOEXW *hints, ADDRINFOEXW **result, struct WS_timeval *timeout, OVERLAPPED *overlapped, LPLOOKUPSERVICE_COMPLETION_ROUTINE completion_routine, HANDLE *handle ) { int ret; TRACE( "name %s, servname %s, namespace %u, namespace_id %s)\n", debugstr_w(name), debugstr_w(servname), namespace, debugstr_guid(namespace_id) ); if (namespace != NS_DNS) FIXME( "Unsupported namespace %u\n", namespace ); if (namespace_id) FIXME( "Unsupported namespace_id %s\n", debugstr_guid(namespace_id) ); if (timeout) FIXME( "Unsupported timeout\n" ); if (handle) FIXME( "Unsupported cancel handle\n" ); ret = WS_getaddrinfoW( name, servname, (struct WS_addrinfo *)hints, result, overlapped, completion_routine ); if (ret) return ret; if (handle) *handle = (HANDLE)0xdeadbeef; return 0; } /*********************************************************************** * GetAddrInfoExOverlappedResult (ws2_32.@) */ int WINAPI GetAddrInfoExOverlappedResult( OVERLAPPED *overlapped ) { TRACE( "(%p)\n", overlapped ); return overlapped->Internal; } /*********************************************************************** * GetAddrInfoExCancel (ws2_32.@) */ int WINAPI GetAddrInfoExCancel( HANDLE *handle ) { FIXME( "(%p)\n", handle ); return WSA_INVALID_HANDLE; } /*********************************************************************** * GetAddrInfoW (ws2_32.@) */ int WINAPI GetAddrInfoW(const WCHAR *nodename, const WCHAR *servname, const ADDRINFOW *hints, ADDRINFOW **res) { struct WS_addrinfo *hintsA = NULL; ADDRINFOEXW *resex; int ret = EAI_MEMORY; TRACE( "nodename %s, servname %s, hints %p, result %p\n", debugstr_w(nodename), debugstr_w(servname), hints, res ); *res = NULL; if (hints) hintsA = addrinfo_WtoA( hints ); ret = WS_getaddrinfoW( nodename, servname, hintsA, &resex, NULL, NULL ); WS_freeaddrinfo( hintsA ); if (ret) return ret; if (resex) { /* ADDRINFOEXW has a layout compatible with ADDRINFOW except for the * ai_next field, so we may convert it in place */ *res = (ADDRINFOW *)resex; do { ((ADDRINFOW *)resex)->ai_next = (ADDRINFOW *)resex->ai_next; resex = resex->ai_next; } while (resex); } return 0; } /*********************************************************************** * freeaddrinfo (ws2_32.@) */ void WINAPI WS_freeaddrinfo( struct WS_addrinfo *res ) { while (res) { struct WS_addrinfo *next; HeapFree( GetProcessHeap(), 0, res->ai_canonname ); HeapFree( GetProcessHeap(), 0, res->ai_addr ); next = res->ai_next; HeapFree( GetProcessHeap(), 0, res ); res = next; } } /*********************************************************************** * FreeAddrInfoW (ws2_32.@) */ void WINAPI FreeAddrInfoW( ADDRINFOW *ai ) { while (ai) { ADDRINFOW *next; HeapFree( GetProcessHeap(), 0, ai->ai_canonname ); HeapFree( GetProcessHeap(), 0, ai->ai_addr ); next = ai->ai_next; HeapFree( GetProcessHeap(), 0, ai ); ai = next; } } /*********************************************************************** * FreeAddrInfoEx (ws2_32.@) */ void WINAPI FreeAddrInfoEx( ADDRINFOEXA *ai ) { TRACE( "(%p)\n", ai ); while (ai) { ADDRINFOEXA *next; HeapFree( GetProcessHeap(), 0, ai->ai_canonname ); HeapFree( GetProcessHeap(), 0, ai->ai_addr ); next = ai->ai_next; HeapFree( GetProcessHeap(), 0, ai ); ai = next; } } /*********************************************************************** * FreeAddrInfoExW (ws2_32.@) */ void WINAPI FreeAddrInfoExW( ADDRINFOEXW *ai ) { TRACE( "(%p)\n", ai ); while (ai) { ADDRINFOEXW *next; HeapFree( GetProcessHeap(), 0, ai->ai_canonname ); HeapFree( GetProcessHeap(), 0, ai->ai_addr ); next = ai->ai_next; HeapFree( GetProcessHeap(), 0, ai ); ai = next; } } static const int ws_niflag_map[][2] = { MAP_OPTION( NI_NOFQDN ), MAP_OPTION( NI_NUMERICHOST ), MAP_OPTION( NI_NAMEREQD ), MAP_OPTION( NI_NUMERICSERV ), MAP_OPTION( NI_DGRAM ), }; static int convert_niflag_w2u( int winflags ) { unsigned int i; int unixflags = 0; for (i = 0; i < ARRAY_SIZE(ws_niflag_map); i++) { if (ws_niflag_map[i][0] & winflags) { unixflags |= ws_niflag_map[i][1]; winflags &= ~ws_niflag_map[i][0]; } } if (winflags) FIXME("Unhandled windows NI_xxx flags 0x%x\n", winflags); return unixflags; } /*********************************************************************** * getnameinfo (ws2_32.@) */ int WINAPI WS_getnameinfo( const SOCKADDR *addr, WS_socklen_t addr_len, char *host, DWORD host_len, char *serv, DWORD serv_len, int flags ) { #ifdef HAVE_GETNAMEINFO int ret; union generic_unix_sockaddr uaddr; unsigned int uaddr_len; TRACE( "addr %s, addr_len %d, host %p, host_len %u, serv %p, serv_len %d, flags %#x\n", debugstr_sockaddr(addr), addr_len, host, host_len, serv, serv_len, flags ); uaddr_len = ws_sockaddr_ws2u( addr, addr_len, &uaddr ); if (!uaddr_len) { SetLastError( WSAEFAULT ); return WSA_NOT_ENOUGH_MEMORY; } ret = getnameinfo( &uaddr.addr, uaddr_len, host, host_len, serv, serv_len, convert_niflag_w2u(flags) ); return convert_eai_u2w( ret ); #else FIXME( "getnameinfo() failed, not found during buildtime.\n" ); return EAI_FAIL; #endif } /*********************************************************************** * GetNameInfoW (ws2_32.@) */ int WINAPI GetNameInfoW( const SOCKADDR *addr, WS_socklen_t addr_len, WCHAR *host, DWORD host_len, WCHAR *serv, DWORD serv_len, int flags ) { int ret; char *hostA = NULL, *servA = NULL; if (host && (!(hostA = HeapAlloc( GetProcessHeap(), 0, host_len )))) return EAI_MEMORY; if (serv && (!(servA = HeapAlloc( GetProcessHeap(), 0, serv_len )))) { HeapFree( GetProcessHeap(), 0, hostA ); return EAI_MEMORY; } ret = WS_getnameinfo( addr, addr_len, hostA, host_len, servA, serv_len, flags ); if (!ret) { if (host) MultiByteToWideChar( CP_ACP, 0, hostA, -1, host, host_len ); if (serv) MultiByteToWideChar( CP_ACP, 0, servA, -1, serv, serv_len ); } HeapFree( GetProcessHeap(), 0, hostA ); HeapFree( GetProcessHeap(), 0, servA ); return ret; } static UINT host_errno_from_unix( int err ) { WARN( "%d\n", err ); switch (err) { case HOST_NOT_FOUND: return WSAHOST_NOT_FOUND; case TRY_AGAIN: return WSATRY_AGAIN; case NO_RECOVERY: return WSANO_RECOVERY; case NO_DATA: return WSANO_DATA; case ENOBUFS: return WSAENOBUFS; case 0: return 0; default: WARN( "Unknown h_errno %d!\n", err ); return WSAEOPNOTSUPP; } } static struct WS_hostent *get_hostent_buffer( unsigned int size ) { struct per_thread_data *data = get_per_thread_data(); if (data->he_buffer) { if (data->he_len >= size) return data->he_buffer; HeapFree( GetProcessHeap(), 0, data->he_buffer ); } data->he_buffer = HeapAlloc( GetProcessHeap(), 0, (data->he_len = size) ); if (!data->he_buffer) SetLastError(WSAENOBUFS); return data->he_buffer; } /* create a hostent entry * * Creates the entry with enough memory for the name, aliases * addresses, and the address pointers. Also copies the name * and sets up all the pointers. * * NOTE: The alias and address lists must be allocated with room * for the NULL item terminating the list. This is true even if * the list has no items ("aliases" and "addresses" must be * at least "1", a truly empty list is invalid). */ static struct WS_hostent *create_hostent( char *name, int alias_count, int aliases_size, int address_count, int address_length ) { struct WS_hostent *p_to; char *p; unsigned int size = sizeof(struct WS_hostent), i; size += strlen(name) + 1; size += alias_count * sizeof(char *); size += aliases_size; size += address_count * sizeof(char *); size += (address_count - 1) * address_length; if (!(p_to = get_hostent_buffer( size ))) return NULL; memset( p_to, 0, size ); /* Use the memory in the same way winsock does. * First set the pointer for aliases, second set the pointers for addresses. * Third fill the addresses indexes, fourth jump aliases names size. * Fifth fill the hostname. * NOTE: This method is valid for OS versions >= XP. */ p = (char *)(p_to + 1); p_to->h_aliases = (char **)p; p += alias_count * sizeof(char *); p_to->h_addr_list = (char **)p; p += address_count * sizeof(char *); for (i = 0, address_count--; i < address_count; i++, p += address_length) p_to->h_addr_list[i] = p; /* h_aliases must be filled in manually because we don't know each string * size. Leave these pointers NULL (already set to NULL by memset earlier). */ p += aliases_size; p_to->h_name = p; strcpy( p, name ); return p_to; } static struct WS_hostent *hostent_from_unix( const struct hostent *p_he ) { int i, addresses = 0, alias_size = 0; struct WS_hostent *p_to; char *p; for (i = 0; p_he->h_aliases[i]; i++) alias_size += strlen( p_he->h_aliases[i] ) + 1; while (p_he->h_addr_list[addresses]) addresses++; p_to = create_hostent( p_he->h_name, i + 1, alias_size, addresses + 1, p_he->h_length ); if (!p_to) return NULL; p_to->h_addrtype = convert_af_u2w( p_he->h_addrtype ); p_to->h_length = p_he->h_length; for (i = 0, p = p_to->h_addr_list[0]; p_he->h_addr_list[i]; i++, p += p_to->h_length) memcpy( p, p_he->h_addr_list[i], p_to->h_length ); /* Fill the aliases after the IP data */ for (i = 0; p_he->h_aliases[i]; i++) { p_to->h_aliases[i] = p; strcpy( p, p_he->h_aliases[i] ); p += strlen(p) + 1; } return p_to; } /*********************************************************************** * gethostbyaddr (ws2_32.51) */ struct WS_hostent * WINAPI WS_gethostbyaddr( const char *addr, int len, int type ) { struct WS_hostent *retval = NULL; struct hostent *host; int unixtype = convert_af_w2u(type); const char *paddr = addr; unsigned long loopback; #ifdef HAVE_LINUX_GETHOSTBYNAME_R_6 char *extrabuf; int ebufsize = 1024; struct hostent hostentry; int locerr = ENOBUFS; #endif /* convert back the magic loopback address if necessary */ if (unixtype == AF_INET && len == 4 && !memcmp( addr, magic_loopback_addr, 4 )) { loopback = htonl( INADDR_LOOPBACK ); paddr = (char *)&loopback; } #ifdef HAVE_LINUX_GETHOSTBYNAME_R_6 host = NULL; extrabuf = HeapAlloc( GetProcessHeap(), 0, ebufsize ); while (extrabuf) { int res = gethostbyaddr_r( paddr, len, unixtype, &hostentry, extrabuf, ebufsize, &host, &locerr ); if (res != ERANGE) break; ebufsize *= 2; extrabuf = HeapReAlloc( GetProcessHeap(), 0, extrabuf, ebufsize ); } if (host) retval = hostent_from_unix( host ); else SetLastError( (locerr < 0) ? sock_get_error( errno ) : host_errno_from_unix( locerr ) ); HeapFree( GetProcessHeap(), 0, extrabuf ); #else EnterCriticalSection( &csWSgetXXXbyYYY ); host = gethostbyaddr( paddr, len, unixtype ); if (host) retval = hostent_from_unix( host ); else SetLastError( (h_errno < 0) ? sock_get_error( errno ) : host_errno_from_unix( h_errno ) ); LeaveCriticalSection( &csWSgetXXXbyYYY ); #endif TRACE( "ptr %p, len %d, type %d ret %p\n", addr, len, type, retval ); return retval; } struct route { struct in_addr addr; IF_INDEX interface; DWORD metric, default_route; }; static int compare_routes_by_metric_asc( const void *left, const void *right ) { const struct route *a = left, *b = right; if (a->default_route && b->default_route) return a->default_route - b->default_route; if (a->default_route && !b->default_route) return -1; if (b->default_route && !a->default_route) return 1; return a->metric - b->metric; } /* Returns the list of local IP addresses by going through the network * adapters and using the local routing table to sort the addresses * from highest routing priority to lowest routing priority. This * functionality is inferred from the description for obtaining local * IP addresses given in the Knowledge Base Article Q160215. * * Please note that the returned hostent is only freed when the thread * closes and is replaced if another hostent is requested. */ static struct WS_hostent *get_local_ips( char *hostname ) { int numroutes = 0, i, j, default_routes = 0; IP_ADAPTER_INFO *adapters = NULL, *k; struct WS_hostent *hostlist = NULL; MIB_IPFORWARDTABLE *routes = NULL; struct route *route_addrs = NULL; DWORD adap_size, route_size, n; /* Obtain the size of the adapter list and routing table, also allocate memory */ if (GetAdaptersInfo( NULL, &adap_size ) != ERROR_BUFFER_OVERFLOW) return NULL; if (GetIpForwardTable( NULL, &route_size, FALSE ) != ERROR_INSUFFICIENT_BUFFER) return NULL; adapters = HeapAlloc( GetProcessHeap(), 0, adap_size ); routes = HeapAlloc( GetProcessHeap(), 0, route_size ); if (!adapters || !routes) goto cleanup; /* Obtain the adapter list and the full routing table */ if (GetAdaptersInfo( adapters, &adap_size ) != NO_ERROR) goto cleanup; if (GetIpForwardTable( routes, &route_size, FALSE ) != NO_ERROR) goto cleanup; /* Store the interface associated with each route */ for (n = 0; n < routes->dwNumEntries; n++) { IF_INDEX ifindex; DWORD ifmetric, ifdefault = 0; BOOL exists = FALSE; /* Check if this is a default route (there may be more than one) */ if (!routes->table[n].dwForwardDest) ifdefault = ++default_routes; else if (routes->table[n].u1.ForwardType != MIB_IPROUTE_TYPE_DIRECT) continue; ifindex = routes->table[n].dwForwardIfIndex; ifmetric = routes->table[n].dwForwardMetric1; /* Only store the lowest valued metric for an interface */ for (j = 0; j < numroutes; j++) { if (route_addrs[j].interface == ifindex) { if (route_addrs[j].metric > ifmetric) route_addrs[j].metric = ifmetric; exists = TRUE; } } if (exists) continue; route_addrs = heap_realloc( route_addrs, (numroutes + 1) * sizeof(struct route) ); if (!route_addrs) goto cleanup; route_addrs[numroutes].interface = ifindex; route_addrs[numroutes].metric = ifmetric; route_addrs[numroutes].default_route = ifdefault; /* If no IP is found in the next step (for whatever reason) * then fall back to the magic loopback address. */ memcpy( &route_addrs[numroutes].addr.s_addr, magic_loopback_addr, 4 ); numroutes++; } if (numroutes == 0) goto cleanup; /* No routes, fall back to the Magic IP */ /* Find the IP address associated with each found interface */ for (i = 0; i < numroutes; i++) { for (k = adapters; k != NULL; k = k->Next) { char *ip = k->IpAddressList.IpAddress.String; if (route_addrs[i].interface == k->Index) route_addrs[i].addr.s_addr = inet_addr(ip); } } /* Allocate a hostent and enough memory for all the IPs, * including the NULL at the end of the list. */ hostlist = create_hostent( hostname, 1, 0, numroutes+1, sizeof(struct in_addr) ); if (hostlist == NULL) goto cleanup; hostlist->h_addr_list[numroutes] = NULL; hostlist->h_aliases[0] = NULL; hostlist->h_addrtype = AF_INET; hostlist->h_length = sizeof(struct in_addr); /* Reorder the entries before placing them in the host list. Windows expects * the IP list in order from highest priority to lowest (the critical thing * is that most applications expect the first IP to be the default route). */ if (numroutes > 1) qsort( route_addrs, numroutes, sizeof(struct route), compare_routes_by_metric_asc ); for (i = 0; i < numroutes; i++) *(struct in_addr *)hostlist->h_addr_list[i] = route_addrs[i].addr; cleanup: HeapFree( GetProcessHeap(), 0, route_addrs ); HeapFree( GetProcessHeap(), 0, adapters ); HeapFree( GetProcessHeap(), 0, routes ); return hostlist; } /*********************************************************************** * gethostbyname (ws2_32.52) */ struct WS_hostent * WINAPI WS_gethostbyname( const char *name ) { struct WS_hostent *retval = NULL; struct hostent *host; #ifdef HAVE_LINUX_GETHOSTBYNAME_R_6 char *extrabuf; int ebufsize = 1024; struct hostent hostentry; int locerr = ENOBUFS; #endif char hostname[100]; if (!num_startup) { SetLastError( WSANOTINITIALISED ); return NULL; } if (gethostname( hostname, 100 ) == -1) { SetLastError( WSAENOBUFS ); return retval; } if (!name || !name[0]) name = hostname; /* If the hostname of the local machine is requested then return the * complete list of local IP addresses */ if (!strcmp( name, hostname )) retval = get_local_ips( hostname ); /* If any other hostname was requested (or the routing table lookup failed) * then return the IP found by the host OS */ if (!retval) { #ifdef HAVE_LINUX_GETHOSTBYNAME_R_6 host = NULL; extrabuf = HeapAlloc( GetProcessHeap(), 0, ebufsize ); while (extrabuf) { int res = gethostbyname_r( name, &hostentry, extrabuf, ebufsize, &host, &locerr ); if (res != ERANGE) break; ebufsize *= 2; extrabuf = HeapReAlloc( GetProcessHeap(), 0, extrabuf, ebufsize ); } if (!host) SetLastError( (locerr < 0) ? sock_get_error( errno ) : host_errno_from_unix( locerr ) ); #else EnterCriticalSection( &csWSgetXXXbyYYY ); host = gethostbyname( name ); if (!host) SetLastError( (h_errno < 0) ? sock_get_error( errno ) : host_errno_from_unix( h_errno ) ); #endif if (host) retval = hostent_from_unix( host ); #ifdef HAVE_LINUX_GETHOSTBYNAME_R_6 HeapFree( GetProcessHeap(), 0, extrabuf ); #else LeaveCriticalSection( &csWSgetXXXbyYYY ); #endif } if (retval && retval->h_addr_list[0][0] == 127 && strcmp( name, "localhost" )) { /* hostname != "localhost" but has loopback address. replace by our * special address.*/ memcpy( retval->h_addr_list[0], magic_loopback_addr, 4 ); } TRACE( "%s ret %p\n", debugstr_a(name), retval ); return retval; } /*********************************************************************** * gethostname (ws2_32.57) */ int WINAPI WS_gethostname( char *name, int namelen ) { char buf[256]; int len; TRACE( "name %p, len %d\n", name, namelen ); if (!name) { SetLastError( WSAEFAULT ); return -1; } if (gethostname( buf, sizeof(buf) ) != 0) { SetLastError( sock_get_error( errno ) ); return -1; } TRACE( "<- %s\n", debugstr_a(buf) ); len = strlen( buf ); if (len > 15) WARN( "Windows supports NetBIOS name length up to 15 bytes!\n" ); if (namelen <= len) { SetLastError( WSAEFAULT ); return -1; } strcpy( name, buf ); return 0; } /*********************************************************************** * GetHostNameW (ws2_32.@) */ int WINAPI GetHostNameW( WCHAR *name, int namelen ) { char buf[256]; TRACE( "name %p, len %d\n", name, namelen ); if (!name) { SetLastError( WSAEFAULT ); return -1; } if (gethostname( buf, sizeof(buf) )) { SetLastError( sock_get_error( errno ) ); return -1; } if (MultiByteToWideChar( CP_ACP, 0, buf, -1, NULL, 0 ) > namelen) { SetLastError( WSAEFAULT ); return -1; } MultiByteToWideChar( CP_ACP, 0, buf, -1, name, namelen ); return 0; } static int list_size( char **list, int item_size ) { int i, size = 0; if (list) { for (i = 0; list[i]; i++) size += (item_size ? item_size : strlen(list[i]) + 1); size += (i + 1) * sizeof(char *); } return size; } static int list_dup( char **src, char **dst, int item_size ) { char *p; int i; for (i = 0; src[i]; i++) ; p = (char *)(dst + i + 1); for (i = 0; src[i]; i++) { int count = item_size ? item_size : strlen(src[i]) + 1; memcpy( p, src[i], count ); dst[i] = p; p += count; } dst[i] = NULL; return p - (char *)dst; } static const struct { int prot; const char *names[3]; } protocols[] = { { 0, {"ip", "IP"}}, { 1, {"icmp", "ICMP"}}, { 3, {"ggp", "GGP"}}, { 6, {"tcp", "TCP"}}, { 8, {"egp", "EGP"}}, {12, {"pup", "PUP"}}, {17, {"udp", "UDP"}}, {20, {"hmp", "HMP"}}, {22, {"xns-idp", "XNS-IDP"}}, {27, {"rdp", "RDP"}}, {41, {"ipv6", "IPv6"}}, {43, {"ipv6-route", "IPv6-Route"}}, {44, {"ipv6-frag", "IPv6-Frag"}}, {50, {"esp", "ESP"}}, {51, {"ah", "AH"}}, {58, {"ipv6-icmp", "IPv6-ICMP"}}, {59, {"ipv6-nonxt", "IPv6-NoNxt"}}, {60, {"ipv6-opts", "IPv6-Opts"}}, {66, {"rvd", "RVD"}}, }; static struct WS_protoent *get_protoent_buffer( unsigned int size ) { struct per_thread_data *data = get_per_thread_data(); if (data->pe_buffer) { if (data->pe_len >= size) return data->pe_buffer; HeapFree( GetProcessHeap(), 0, data->pe_buffer ); } data->pe_len = size; data->pe_buffer = HeapAlloc( GetProcessHeap(), 0, size ); if (!data->pe_buffer) SetLastError( WSAENOBUFS ); return data->pe_buffer; } static struct WS_protoent *create_protoent( const char *name, char **aliases, int prot ) { struct WS_protoent *ret; unsigned int size = sizeof(*ret) + strlen( name ) + sizeof(char *) + list_size( aliases, 0 ); if (!(ret = get_protoent_buffer( size ))) return NULL; ret->p_proto = prot; ret->p_name = (char *)(ret + 1); strcpy( ret->p_name, name ); ret->p_aliases = (char **)ret->p_name + strlen( name ) / sizeof(char *) + 1; list_dup( aliases, ret->p_aliases, 0 ); return ret; } /*********************************************************************** * getprotobyname (ws2_32.53) */ struct WS_protoent * WINAPI WS_getprotobyname( const char *name ) { struct WS_protoent *retval = NULL; unsigned int i; for (i = 0; i < ARRAY_SIZE(protocols); i++) { if (!_strnicmp( protocols[i].names[0], name, -1 )) { retval = create_protoent( protocols[i].names[0], (char **)protocols[i].names + 1, protocols[i].prot ); break; } } if (!retval) { WARN( "protocol %s not found\n", debugstr_a(name) ); SetLastError( WSANO_DATA ); } TRACE( "%s ret %p\n", debugstr_a(name), retval ); return retval; } /*********************************************************************** * getprotobynumber (ws2_32.54) */ struct WS_protoent * WINAPI WS_getprotobynumber( int number ) { struct WS_protoent *retval = NULL; unsigned int i; for (i = 0; i < ARRAY_SIZE(protocols); i++) { if (protocols[i].prot == number) { retval = create_protoent( protocols[i].names[0], (char **)protocols[i].names + 1, protocols[i].prot ); break; } } if (!retval) { WARN( "protocol %d not found\n", number ); SetLastError( WSANO_DATA ); } TRACE( "%d ret %p\n", number, retval ); return retval; } static char *strdup_lower( const char *str ) { char *ret = HeapAlloc( GetProcessHeap(), 0, strlen(str) + 1 ); int i; if (ret) { for (i = 0; str[i]; i++) ret[i] = tolower( str[i] ); ret[i] = 0; } else SetLastError( WSAENOBUFS ); return ret; } static struct WS_servent *get_servent_buffer( int size ) { struct per_thread_data *data = get_per_thread_data(); if (data->se_buffer) { if (data->se_len >= size) return data->se_buffer; HeapFree( GetProcessHeap(), 0, data->se_buffer ); } data->se_len = size; data->se_buffer = HeapAlloc( GetProcessHeap(), 0, size ); if (!data->se_buffer) SetLastError( WSAENOBUFS ); return data->se_buffer; } static struct WS_servent *servent_from_unix( const struct servent *p_se ) { char *p; struct WS_servent *p_to; int size = (sizeof(*p_se) + strlen(p_se->s_proto) + 1 + strlen(p_se->s_name) + 1 + list_size(p_se->s_aliases, 0)); if (!(p_to = get_servent_buffer( size ))) return NULL; p_to->s_port = p_se->s_port; p = (char *)(p_to + 1); p_to->s_name = p; strcpy( p, p_se->s_name ); p += strlen(p) + 1; p_to->s_proto = p; strcpy( p, p_se->s_proto ); p += strlen(p) + 1; p_to->s_aliases = (char **)p; list_dup( p_se->s_aliases, p_to->s_aliases, 0 ); return p_to; } /*********************************************************************** * getservbyname (ws2_32.55) */ struct WS_servent * WINAPI WS_getservbyname( const char *name, const char *proto ) { struct WS_servent *retval = NULL; struct servent *serv; char *name_str; char *proto_str = NULL; if (!(name_str = strdup_lower( name ))) return NULL; if (proto && *proto) { if (!(proto_str = strdup_lower( proto ))) { HeapFree( GetProcessHeap(), 0, name_str ); return NULL; } } EnterCriticalSection( &csWSgetXXXbyYYY ); serv = getservbyname( name_str, proto_str ); if (serv) retval = servent_from_unix( serv ); else SetLastError( WSANO_DATA ); LeaveCriticalSection( &csWSgetXXXbyYYY ); HeapFree( GetProcessHeap(), 0, proto_str ); HeapFree( GetProcessHeap(), 0, name_str ); TRACE( "%s, %s ret %p\n", debugstr_a(name), debugstr_a(proto), retval ); return retval; } /*********************************************************************** * getservbyport (ws2_32.56) */ struct WS_servent * WINAPI WS_getservbyport( int port, const char *proto ) { struct WS_servent *retval = NULL; #ifdef HAVE_GETSERVBYPORT struct servent *serv; char *proto_str = NULL; if (proto && *proto) { if (!(proto_str = strdup_lower( proto ))) return NULL; } EnterCriticalSection( &csWSgetXXXbyYYY ); if ((serv = getservbyport( port, proto_str ))) retval = servent_from_unix( serv ); else SetLastError( WSANO_DATA ); LeaveCriticalSection( &csWSgetXXXbyYYY ); HeapFree( GetProcessHeap(), 0, proto_str ); #endif TRACE( "%d (i.e. port %d), %s ret %p\n", port, (int)ntohl(port), debugstr_a(proto), retval ); return retval; } /*********************************************************************** * inet_ntoa (ws2_32.12) */ char * WINAPI WS_inet_ntoa( struct WS_in_addr in ) { unsigned int long_ip = ntohl( in.WS_s_addr ); struct per_thread_data *data = get_per_thread_data(); sprintf( data->ntoa_buffer, "%u.%u.%u.%u", (long_ip >> 24) & 0xff, (long_ip >> 16) & 0xff, (long_ip >> 8) & 0xff, long_ip & 0xff ); return data->ntoa_buffer; } /*********************************************************************** * inet_ntop (ws2_32.@) */ const char * WINAPI WS_inet_ntop( int family, void *addr, char *buffer, SIZE_T len ) { NTSTATUS status; ULONG size = min( len, (ULONG)-1 ); TRACE( "family %d, addr %p, buffer %p, len %ld\n", family, addr, buffer, len ); if (!buffer) { SetLastError( STATUS_INVALID_PARAMETER ); return NULL; } switch (family) { case WS_AF_INET: { status = RtlIpv4AddressToStringExA( (IN_ADDR *)addr, 0, buffer, &size ); break; } case WS_AF_INET6: { status = RtlIpv6AddressToStringExA( (IN6_ADDR *)addr, 0, 0, buffer, &size ); break; } default: SetLastError( WSAEAFNOSUPPORT ); return NULL; } if (status == STATUS_SUCCESS) return buffer; SetLastError( STATUS_INVALID_PARAMETER ); return NULL; } /*********************************************************************** * inet_pton (ws2_32.@) */ int WINAPI WS_inet_pton( int family, const char *addr, void *buffer ) { NTSTATUS status; const char *terminator; TRACE( "family %d, addr %s, buffer %p\n", family, debugstr_a(addr), buffer ); if (!addr || !buffer) { SetLastError( WSAEFAULT ); return -1; } switch (family) { case WS_AF_INET: status = RtlIpv4StringToAddressA(addr, TRUE, &terminator, buffer); break; case WS_AF_INET6: status = RtlIpv6StringToAddressA(addr, &terminator, buffer); break; default: SetLastError( WSAEAFNOSUPPORT ); return -1; } return (status == STATUS_SUCCESS && *terminator == 0); } /*********************************************************************** * InetPtonW (ws2_32.@) */ int WINAPI InetPtonW( int family, const WCHAR *addr, void *buffer ) { char *addrA; int len; INT ret; TRACE( "family %d, addr %s, buffer %p\n", family, debugstr_w(addr), buffer ); if (!addr) { SetLastError(WSAEFAULT); return SOCKET_ERROR; } len = WideCharToMultiByte( CP_ACP, 0, addr, -1, NULL, 0, NULL, NULL ); if (!(addrA = HeapAlloc( GetProcessHeap(), 0, len ))) { SetLastError( WSA_NOT_ENOUGH_MEMORY ); return -1; } WideCharToMultiByte( CP_ACP, 0, addr, -1, addrA, len, NULL, NULL ); ret = WS_inet_pton( family, addrA, buffer ); if (!ret) SetLastError( WSAEINVAL ); HeapFree( GetProcessHeap(), 0, addrA ); return ret; } /*********************************************************************** * InetNtopW (ws2_32.@) */ const WCHAR * WINAPI InetNtopW( int family, void *addr, WCHAR *buffer, SIZE_T len ) { char bufferA[WS_INET6_ADDRSTRLEN]; PWSTR ret = NULL; TRACE( "family %d, addr %p, buffer %p, len %ld\n", family, addr, buffer, len ); if (WS_inet_ntop( family, addr, bufferA, sizeof(bufferA) )) { if (MultiByteToWideChar( CP_ACP, 0, bufferA, -1, buffer, len )) ret = buffer; else SetLastError( ERROR_INVALID_PARAMETER ); } return ret; } /*********************************************************************** * WSAStringToAddressA (ws2_32.@) */ int WINAPI WSAStringToAddressA( char *string, int family, WSAPROTOCOL_INFOA *protocol_info, struct WS_sockaddr *addr, int *addr_len ) { NTSTATUS status; TRACE( "string %s, family %u\n", debugstr_a(string), family ); if (!addr || !addr_len) return -1; if (!string) { SetLastError( WSAEINVAL ); return -1; } if (protocol_info) FIXME( "ignoring protocol_info\n" ); switch (family) { case WS_AF_INET: { struct WS_sockaddr_in *addr4 = (struct WS_sockaddr_in *)addr; if (*addr_len < sizeof(struct WS_sockaddr_in)) { *addr_len = sizeof(struct WS_sockaddr_in); SetLastError( WSAEFAULT ); return -1; } memset( addr, 0, sizeof(struct WS_sockaddr_in) ); status = RtlIpv4StringToAddressExA( string, FALSE, &addr4->sin_addr, &addr4->sin_port ); if (status != STATUS_SUCCESS) { SetLastError( WSAEINVAL ); return -1; } addr4->sin_family = WS_AF_INET; *addr_len = sizeof(struct WS_sockaddr_in); return 0; } case WS_AF_INET6: { struct WS_sockaddr_in6 *addr6 = (struct WS_sockaddr_in6 *)addr; if (*addr_len < sizeof(struct WS_sockaddr_in6)) { *addr_len = sizeof(struct WS_sockaddr_in6); SetLastError( WSAEFAULT ); return -1; } memset( addr, 0, sizeof(struct WS_sockaddr_in6) ); status = RtlIpv6StringToAddressExA( string, &addr6->sin6_addr, &addr6->sin6_scope_id, &addr6->sin6_port ); if (status != STATUS_SUCCESS) { SetLastError( WSAEINVAL ); return -1; } addr6->sin6_family = WS_AF_INET6; *addr_len = sizeof(struct WS_sockaddr_in6); return 0; } default: /* According to MSDN, only AF_INET and AF_INET6 are supported. */ TRACE( "Unsupported address family specified: %d.\n", family ); SetLastError( WSAEINVAL ); return -1; } } /*********************************************************************** * WSAStringToAddressW (ws2_32.@) */ int WINAPI WSAStringToAddressW( WCHAR *string, int family, WSAPROTOCOL_INFOW *protocol_info, struct WS_sockaddr *addr, int *addr_len ) { WSAPROTOCOL_INFOA infoA; WSAPROTOCOL_INFOA *protocol_infoA = NULL; int sizeA, ret; char *stringA; TRACE( "string %s, family %u\n", debugstr_w(string), family ); if (!addr || !addr_len) return -1; if (protocol_info) { protocol_infoA = &infoA; memcpy( protocol_infoA, protocol_info, FIELD_OFFSET( WSAPROTOCOL_INFOA, szProtocol ) ); if (!WideCharToMultiByte( CP_ACP, 0, protocol_info->szProtocol, -1, protocol_infoA->szProtocol, sizeof(protocol_infoA->szProtocol), NULL, NULL )) { SetLastError( WSAEINVAL ); return -1; } } if (!string) { SetLastError( WSAEINVAL ); return -1; } sizeA = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL ); if (!(stringA = HeapAlloc( GetProcessHeap(), 0, sizeA ))) { SetLastError( WSA_NOT_ENOUGH_MEMORY ); return -1; } WideCharToMultiByte( CP_ACP, 0, string, -1, stringA, sizeA, NULL, NULL ); ret = WSAStringToAddressA( stringA, family, protocol_infoA, addr, addr_len ); HeapFree( GetProcessHeap(), 0, stringA ); return ret; } /*********************************************************************** * WSAAddressToStringA (ws2_32.@) */ int WINAPI WSAAddressToStringA( struct WS_sockaddr *addr, DWORD addr_len, WSAPROTOCOL_INFOA *info, char *string, DWORD *string_len ) { char buffer[54]; /* 32 digits + 7':' + '[' + '%" + 5 digits + ']:' + 5 digits + '\0' */ DWORD size; TRACE( "addr %s\n", debugstr_sockaddr(addr) ); if (!addr) return SOCKET_ERROR; if (!string || !string_len) return SOCKET_ERROR; switch (addr->sa_family) { case WS_AF_INET: { const struct WS_sockaddr_in *addr4 = (const struct WS_sockaddr_in *)addr; unsigned int long_ip = ntohl( addr4->sin_addr.WS_s_addr ); char *p; if (addr_len < sizeof(struct WS_sockaddr_in)) return -1; sprintf( buffer, "%u.%u.%u.%u:%u", (long_ip >> 24) & 0xff, (long_ip >> 16) & 0xff, (long_ip >> 8) & 0xff, long_ip & 0xff, ntohs( addr4->sin_port ) ); p = strchr( buffer, ':' ); if (!addr4->sin_port) *p = 0; break; } case WS_AF_INET6: { struct WS_sockaddr_in6 *addr6 = (struct WS_sockaddr_in6 *)addr; size_t len; buffer[0] = 0; if (addr_len < sizeof(struct WS_sockaddr_in6)) return -1; if (addr6->sin6_port) strcpy( buffer, "[" ); len = strlen( buffer ); if (!WS_inet_ntop( WS_AF_INET6, &addr6->sin6_addr, &buffer[len], sizeof(buffer) - len )) { SetLastError( WSAEINVAL ); return -1; } if (addr6->sin6_scope_id) sprintf( buffer + strlen( buffer ), "%%%u", addr6->sin6_scope_id ); if (addr6->sin6_port) sprintf( buffer + strlen( buffer ), "]:%u", ntohs( addr6->sin6_port ) ); break; } default: SetLastError( WSAEINVAL ); return -1; } size = strlen( buffer ) + 1; if (*string_len < size) { *string_len = size; SetLastError( WSAEFAULT ); return -1; } TRACE( "=> %s, %u bytes\n", debugstr_a(buffer), size ); *string_len = size; strcpy( string, buffer ); return 0; } /*********************************************************************** * WSAAddressToStringW (ws2_32.@) */ int WINAPI WSAAddressToStringW( struct WS_sockaddr *addr, DWORD addr_len, WSAPROTOCOL_INFOW *info, WCHAR *string, DWORD *string_len ) { INT ret; char buf[54]; /* 32 digits + 7':' + '[' + '%" + 5 digits + ']:' + 5 digits + '\0' */ TRACE( "(%p, %d, %p, %p, %p)\n", addr, addr_len, info, string, string_len ); if ((ret = WSAAddressToStringA( addr, addr_len, NULL, buf, string_len ))) return ret; MultiByteToWideChar( CP_ACP, 0, buf, *string_len, string, *string_len ); TRACE( "=> %s, %u chars\n", debugstr_w(string), *string_len ); return 0; } /*********************************************************************** * inet_addr (ws2_32.11) */ WS_u_long WINAPI WS_inet_addr( const char *cp ) { if (!cp) return INADDR_NONE; return inet_addr( cp ); } /*********************************************************************** * htonl (ws2_32.8) */ WS_u_long WINAPI WS_htonl( WS_u_long hostlong ) { return htonl( hostlong ); } /*********************************************************************** * htons (ws2_32.9) */ WS_u_short WINAPI WS_htons( WS_u_short hostshort ) { return htons( hostshort ); } /*********************************************************************** * WSAHtonl (ws2_32.@) */ int WINAPI WSAHtonl( SOCKET s, WS_u_long hostlong, WS_u_long *netlong ) { if (netlong) { *netlong = htonl( hostlong ); return 0; } SetLastError( WSAEFAULT ); return -1; } /*********************************************************************** * WSAHtons (ws2_32.@) */ int WINAPI WSAHtons( SOCKET s, WS_u_short hostshort, WS_u_short *netshort ) { if (netshort) { *netshort = htons( hostshort ); return 0; } SetLastError( WSAEFAULT ); return -1; } /*********************************************************************** * ntohl (ws2_32.14) */ WS_u_long WINAPI WS_ntohl( WS_u_long netlong ) { return ntohl( netlong ); } /*********************************************************************** * ntohs (ws2_32.15) */ WS_u_short WINAPI WS_ntohs( WS_u_short netshort ) { return ntohs( netshort ); } /*********************************************************************** * WSANtohl (ws2_32.@) */ int WINAPI WSANtohl( SOCKET s, WS_u_long netlong, WS_u_long *hostlong ) { if (!hostlong) return WSAEFAULT; *hostlong = ntohl( netlong ); return 0; } /*********************************************************************** * WSANtohs (ws2_32.@) */ int WINAPI WSANtohs( SOCKET s, WS_u_short netshort, WS_u_short *hostshort ) { if (!hostshort) return WSAEFAULT; *hostshort = ntohs( netshort ); return 0; } /*********************************************************************** * WSAInstallServiceClassA (ws2_32.@) */ int WINAPI WSAInstallServiceClassA( WSASERVICECLASSINFOA *info ) { FIXME( "Request to install service %s\n", debugstr_a(info->lpszServiceClassName) ); SetLastError( WSAEACCES ); return -1; } /*********************************************************************** * WSAInstallServiceClassW (ws2_32.@) */ int WINAPI WSAInstallServiceClassW( WSASERVICECLASSINFOW *info ) { FIXME( "Request to install service %s\n", debugstr_w(info->lpszServiceClassName) ); SetLastError( WSAEACCES ); return -1; } /*********************************************************************** * WSARemoveServiceClass (ws2_32.@) */ int WINAPI WSARemoveServiceClass( GUID *info ) { FIXME( "Request to remove service %s\n", debugstr_guid(info) ); SetLastError( WSATYPE_NOT_FOUND ); return -1; } /*********************************************************************** * WSAGetServiceClassInfoA (ws2_32.@) */ int WINAPI WSAGetServiceClassInfoA( GUID *provider, GUID *service, DWORD *len, WSASERVICECLASSINFOA *info ) { FIXME( "(%s %s %p %p) Stub!\n", debugstr_guid(provider), debugstr_guid(service), len, info ); SetLastError( WSA_NOT_ENOUGH_MEMORY ); return -1; } /*********************************************************************** * WSAGetServiceClassInfoW (ws2_32.@) */ int WINAPI WSAGetServiceClassInfoW( GUID *provider, GUID *service, DWORD *len, WSASERVICECLASSINFOW *info ) { FIXME( "(%s %s %p %p) Stub!\n", debugstr_guid(provider), debugstr_guid(service), len, info ); SetLastError( WSA_NOT_ENOUGH_MEMORY ); return -1; } /*********************************************************************** * WSAGetServiceClassNameByClassIdA (ws2_32.@) */ int WINAPI WSAGetServiceClassNameByClassIdA( GUID *class, char *service, DWORD *len ) { FIXME( "(%s %p %p) Stub!\n", debugstr_guid(class), service, len ); SetLastError( WSA_NOT_ENOUGH_MEMORY ); return -1; } /*********************************************************************** * WSAGetServiceClassNameByClassIdW (ws2_32.@) */ int WINAPI WSAGetServiceClassNameByClassIdW( GUID *class, WCHAR *service, DWORD *len ) { FIXME( "(%s %p %p) Stub!\n", debugstr_guid(class), service, len ); SetLastError( WSA_NOT_ENOUGH_MEMORY ); return -1; } /*********************************************************************** * WSALookupServiceBeginA (ws2_32.@) */ int WINAPI WSALookupServiceBeginA( WSAQUERYSETA *query, DWORD flags, HANDLE *lookup ) { FIXME( "(%p 0x%08x %p) Stub!\n", query, flags, lookup ); SetLastError( WSA_NOT_ENOUGH_MEMORY ); return -1; } /*********************************************************************** * WSALookupServiceBeginW (ws2_32.@) */ int WINAPI WSALookupServiceBeginW( WSAQUERYSETW *query, DWORD flags, HANDLE *lookup ) { FIXME( "(%p 0x%08x %p) Stub!\n", query, flags, lookup ); SetLastError( WSA_NOT_ENOUGH_MEMORY ); return -1; } /*********************************************************************** * WSALookupServiceEnd (ws2_32.@) */ int WINAPI WSALookupServiceEnd( HANDLE lookup ) { FIXME("(%p) Stub!\n", lookup ); return 0; } /*********************************************************************** * WSALookupServiceNextA (ws2_32.@) */ int WINAPI WSALookupServiceNextA( HANDLE lookup, DWORD flags, DWORD *len, WSAQUERYSETA *results ) { FIXME( "(%p 0x%08x %p %p) Stub!\n", lookup, flags, len, results ); SetLastError( WSA_E_NO_MORE ); return -1; } /*********************************************************************** * WSALookupServiceNextW (ws2_32.@) */ int WINAPI WSALookupServiceNextW( HANDLE lookup, DWORD flags, DWORD *len, WSAQUERYSETW *results ) { FIXME( "(%p 0x%08x %p %p) Stub!\n", lookup, flags, len, results ); SetLastError( WSA_E_NO_MORE ); return -1; } /*********************************************************************** * WSASetServiceA (ws2_32.@) */ int WINAPI WSASetServiceA( WSAQUERYSETA *query, WSAESETSERVICEOP operation, DWORD flags ) { FIXME( "(%p 0x%08x 0x%08x) Stub!\n", query, operation, flags ); return 0; } /*********************************************************************** * WSASetServiceW (ws2_32.@) */ int WINAPI WSASetServiceW( WSAQUERYSETW *query, WSAESETSERVICEOP operation, DWORD flags ) { FIXME( "(%p 0x%08x 0x%08x) Stub!\n", query, operation, flags ); return 0; } /*********************************************************************** * WSAEnumNameSpaceProvidersA (ws2_32.@) */ int WINAPI WSAEnumNameSpaceProvidersA( DWORD *len, WSANAMESPACE_INFOA *buffer ) { FIXME( "(%p %p) Stub!\n", len, buffer ); return 0; } /*********************************************************************** * WSAEnumNameSpaceProvidersW (ws2_32.@) */ int WINAPI WSAEnumNameSpaceProvidersW( DWORD *len, WSANAMESPACE_INFOW *buffer ) { FIXME( "(%p %p) Stub!\n", len, buffer ); return 0; } /*********************************************************************** * WSAProviderConfigChange (ws2_32.@) */ int WINAPI WSAProviderConfigChange( HANDLE *handle, OVERLAPPED *overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completion ) { FIXME( "(%p %p %p) Stub!\n", handle, overlapped, completion ); return -1; } /*********************************************************************** * WSANSPIoctl (ws2_32.@) */ int WINAPI WSANSPIoctl( HANDLE lookup, DWORD code, void *in_buffer, DWORD in_size, void *out_buffer, DWORD out_size, DWORD *ret_size, WSACOMPLETION *completion ) { FIXME( "(%p, 0x%08x, %p, 0x%08x, %p, 0x%08x, %p, %p) Stub!\n", lookup, code, in_buffer, in_size, out_buffer, out_size, ret_size, completion ); SetLastError( WSA_NOT_ENOUGH_MEMORY ); return -1; } /*********************************************************************** * WSCEnableNSProvider (ws2_32.@) */ int WINAPI WSCEnableNSProvider( GUID *provider, BOOL enable ) { FIXME( "(%s 0x%08x) Stub!\n", debugstr_guid(provider), enable ); return 0; } /*********************************************************************** * WSCGetProviderInfo (ws2_32.@) */ int WINAPI WSCGetProviderInfo( GUID *provider, WSC_PROVIDER_INFO_TYPE info_type, BYTE *info, size_t *len, DWORD flags, int *errcode ) { FIXME( "(%s 0x%08x %p %p 0x%08x %p) Stub!\n", debugstr_guid(provider), info_type, info, len, flags, errcode ); if (!errcode) return -1; if (!provider) { *errcode = WSAEFAULT; return -1; } *errcode = WSANO_RECOVERY; return -1; } /*********************************************************************** * WSCGetProviderPath (ws2_32.@) */ int WINAPI WSCGetProviderPath( GUID *provider, WCHAR *path, int *len, int *errcode ) { FIXME( "(%s %p %p %p) Stub!\n", debugstr_guid(provider), path, len, errcode ); if (!provider || !len) { if (errcode) *errcode = WSAEFAULT; return -1; } if (*len <= 0) { if (errcode) *errcode = WSAEINVAL; return -1; } return 0; } /*********************************************************************** * WSCInstallNameSpace (ws2_32.@) */ int WINAPI WSCInstallNameSpace( WCHAR *identifier, WCHAR *path, DWORD namespace, DWORD version, GUID *provider ) { FIXME( "(%s %s 0x%08x 0x%08x %s) Stub!\n", debugstr_w(identifier), debugstr_w(path), namespace, version, debugstr_guid(provider) ); return 0; } /*********************************************************************** * WSCUnInstallNameSpace (ws2_32.@) */ int WINAPI WSCUnInstallNameSpace( GUID *provider ) { FIXME( "(%s) Stub!\n", debugstr_guid(provider) ); return NO_ERROR; } /*********************************************************************** * WSCWriteProviderOrder (ws2_32.@) */ int WINAPI WSCWriteProviderOrder( DWORD *entry, DWORD number ) { FIXME( "(%p 0x%08x) Stub!\n", entry, number ); return 0; } /*********************************************************************** * WSCInstallProvider (ws2_32.@) */ int WINAPI WSCInstallProvider( GUID *provider, const WCHAR *path, WSAPROTOCOL_INFOW *protocol_info, DWORD count, int *err ) { FIXME( "(%s, %s, %p, %d, %p): stub !\n", debugstr_guid(provider), debugstr_w(path), protocol_info, count, err ); *err = 0; return 0; } /*********************************************************************** * WSCDeinstallProvider (ws2_32.@) */ int WINAPI WSCDeinstallProvider( GUID *provider, int *err ) { FIXME( "(%s, %p): stub !\n", debugstr_guid(provider), err ); *err = 0; return 0; } /*********************************************************************** * WSCSetApplicationCategory (ws2_32.@) */ int WINAPI WSCSetApplicationCategory( const WCHAR *path, DWORD len, const WCHAR *extra, DWORD extralen, DWORD lspcat, DWORD *prev_lspcat, int *err ) { FIXME( "(%s %d %s %d %d %p) Stub!\n", debugstr_w(path), len, debugstr_w(extra), extralen, lspcat, prev_lspcat ); return 0; } /*********************************************************************** * WSCEnumProtocols (ws2_32.@) */ int WINAPI WSCEnumProtocols( int *protocols, WSAPROTOCOL_INFOW *info, DWORD *len, int *err ) { int ret = WSAEnumProtocolsW( protocols, info, len ); if (ret == SOCKET_ERROR) *err = WSAENOBUFS; return ret; }