/*
 * Unit test suite for protocol functions
 *
 * Copyright 2004 Hans Leidekker
 *
 * 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 <stdarg.h>

#include <ntstatus.h>
#define WIN32_NO_STATUS
#include <windef.h>
#include <winbase.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <ws2spi.h>
#include <mswsock.h>
#include <iphlpapi.h>

#include "wine/test.h"

static void (WINAPI *pFreeAddrInfoExW)(ADDRINFOEXW *ai);
static int (WINAPI *pGetAddrInfoExW)(const WCHAR *name, const WCHAR *servname, DWORD namespace,
        GUID *namespace_id, const ADDRINFOEXW *hints, ADDRINFOEXW **result,
        struct timeval *timeout, OVERLAPPED *overlapped,
        LPLOOKUPSERVICE_COMPLETION_ROUTINE completion_routine, HANDLE *handle);
static int   (WINAPI *pGetAddrInfoExOverlappedResult)(OVERLAPPED *overlapped);
static int (WINAPI *pGetHostNameW)(WCHAR *name, int len);
static const char *(WINAPI *p_inet_ntop)(int family, void *addr, char *string, ULONG size);
static const WCHAR *(WINAPI *pInetNtopW)(int family, void *addr, WCHAR *string, ULONG size);
static int (WINAPI *p_inet_pton)(int family, const char *string, void *addr);
static int (WINAPI *pInetPtonW)(int family, WCHAR *string, void *addr);
static int (WINAPI *pWSCGetProviderInfo)(GUID *provider, WSC_PROVIDER_INFO_TYPE type, BYTE *info, size_t *size, DWORD flags, INT *err);

/* TCP and UDP over IP fixed set of service flags */
#define TCPIP_SERVICE_FLAGS (XP1_GUARANTEED_DELIVERY \
                           | XP1_GUARANTEED_ORDER    \
                           | XP1_GRACEFUL_CLOSE      \
                           | XP1_EXPEDITED_DATA      \
                           | XP1_IFS_HANDLES)

#define UDPIP_SERVICE_FLAGS (XP1_CONNECTIONLESS      \
                           | XP1_MESSAGE_ORIENTED    \
                           | XP1_SUPPORT_BROADCAST   \
                           | XP1_SUPPORT_MULTIPOINT  \
                           | XP1_IFS_HANDLES)

static void test_service_flags(int family, int version, int socktype, int protocol, DWORD testflags)
{
    DWORD expectedflags = 0;
    if (socktype == SOCK_STREAM && protocol == IPPROTO_TCP)
        expectedflags = TCPIP_SERVICE_FLAGS;
    if (socktype == SOCK_DGRAM && protocol == IPPROTO_UDP)
        expectedflags = UDPIP_SERVICE_FLAGS;

    /* check if standard TCP and UDP protocols are offering the correct service flags */
    if ((family == AF_INET || family == AF_INET6) && version == 2 && expectedflags)
    {
        /* QOS may or may not be installed */
        testflags &= ~XP1_QOS_SUPPORTED;
        ok(expectedflags == testflags,
           "Incorrect flags, expected 0x%x, received 0x%x\n",
           expectedflags, testflags);
    }
}

static void test_WSAEnumProtocolsA(void)
{
    INT ret, i, j, found;
    DWORD len = 0, error;
    WSAPROTOCOL_INFOA info, *buffer;
    INT ptest[] = {0xdead, IPPROTO_TCP, 0xcafe, IPPROTO_UDP, 0xbeef, 0};

    ret = WSAEnumProtocolsA( NULL, NULL, &len );
    ok( ret == SOCKET_ERROR, "WSAEnumProtocolsA() succeeded unexpectedly\n");
    error = WSAGetLastError();
    ok( error == WSAENOBUFS, "Expected 10055, received %d\n", error);

    len = 0;

    ret = WSAEnumProtocolsA( NULL, &info, &len );
    ok( ret == SOCKET_ERROR, "WSAEnumProtocolsA() succeeded unexpectedly\n");
    error = WSAGetLastError();
    ok( error == WSAENOBUFS, "Expected 10055, received %d\n", error);

    buffer = HeapAlloc( GetProcessHeap(), 0, len );

    if (buffer)
    {
        ret = WSAEnumProtocolsA( NULL, buffer, &len );
        ok( ret != SOCKET_ERROR, "WSAEnumProtocolsA() failed unexpectedly: %d\n",
            WSAGetLastError() );

        for (i = 0; i < ret; i++)
        {
            ok( strlen( buffer[i].szProtocol ), "No protocol name found\n" );
            test_service_flags( buffer[i].iAddressFamily, buffer[i].iVersion,
                                buffer[i].iSocketType, buffer[i].iProtocol,
                                buffer[i].dwServiceFlags1);
        }

        HeapFree( GetProcessHeap(), 0, buffer );
    }

    /* Test invalid protocols in the list */
    ret = WSAEnumProtocolsA( ptest, NULL, &len );
    ok( ret == SOCKET_ERROR, "WSAEnumProtocolsA() succeeded unexpectedly\n");
    error = WSAGetLastError();
    ok( error == WSAENOBUFS || broken(error == WSAEFAULT) /* NT4 */,
       "Expected 10055, received %d\n", error);

    buffer = HeapAlloc( GetProcessHeap(), 0, len );

    if (buffer)
    {
        ret = WSAEnumProtocolsA( ptest, buffer, &len );
        ok( ret != SOCKET_ERROR, "WSAEnumProtocolsA() failed unexpectedly: %d\n",
            WSAGetLastError() );
        ok( ret >= 2, "Expected at least 2 items, received %d\n", ret);

        for (i = found = 0; i < ret; i++)
            for (j = 0; j < ARRAY_SIZE(ptest); j++)
                if (buffer[i].iProtocol == ptest[j])
                {
                    found |= 1 << j;
                    break;
                }
        ok(found == 0x0A, "Expected 2 bits represented as 0xA, received 0x%x\n", found);

        HeapFree( GetProcessHeap(), 0, buffer );
    }
}

static void test_WSAEnumProtocolsW(void)
{
    INT ret, i, j, found;
    DWORD len = 0, error;
    WSAPROTOCOL_INFOW info, *buffer;
    INT ptest[] = {0xdead, IPPROTO_TCP, 0xcafe, IPPROTO_UDP, 0xbeef, 0};

    ret = WSAEnumProtocolsW( NULL, NULL, &len );
    ok( ret == SOCKET_ERROR, "WSAEnumProtocolsW() succeeded unexpectedly\n");
    error = WSAGetLastError();
    ok( error == WSAENOBUFS, "Expected 10055, received %d\n", error);

    len = 0;

    ret = WSAEnumProtocolsW( NULL, &info, &len );
    ok( ret == SOCKET_ERROR, "WSAEnumProtocolsW() succeeded unexpectedly\n");
    error = WSAGetLastError();
    ok( error == WSAENOBUFS, "Expected 10055, received %d\n", error);

    buffer = HeapAlloc( GetProcessHeap(), 0, len );

    if (buffer)
    {
        ret = WSAEnumProtocolsW( NULL, buffer, &len );
        ok( ret != SOCKET_ERROR, "WSAEnumProtocolsW() failed unexpectedly: %d\n",
            WSAGetLastError() );

        for (i = 0; i < ret; i++)
        {
            ok( lstrlenW( buffer[i].szProtocol ), "No protocol name found\n" );
            test_service_flags( buffer[i].iAddressFamily, buffer[i].iVersion,
                                buffer[i].iSocketType, buffer[i].iProtocol,
                                buffer[i].dwServiceFlags1);
        }

        HeapFree( GetProcessHeap(), 0, buffer );
    }

    /* Test invalid protocols in the list */
    ret = WSAEnumProtocolsW( ptest, NULL, &len );
    ok( ret == SOCKET_ERROR, "WSAEnumProtocolsW() succeeded unexpectedly\n");
    error = WSAGetLastError();
    ok( error == WSAENOBUFS || broken(error == WSAEFAULT) /* NT4 */,
       "Expected 10055, received %d\n", error);

    buffer = HeapAlloc( GetProcessHeap(), 0, len );

    if (buffer)
    {
        ret = WSAEnumProtocolsW( ptest, buffer, &len );
        ok( ret != SOCKET_ERROR, "WSAEnumProtocolsW() failed unexpectedly: %d\n",
            WSAGetLastError() );
        ok( ret >= 2, "Expected at least 2 items, received %d\n", ret);

        for (i = found = 0; i < ret; i++)
            for (j = 0; j < ARRAY_SIZE(ptest); j++)
                if (buffer[i].iProtocol == ptest[j])
                {
                    found |= 1 << j;
                    break;
                }
        ok(found == 0x0A, "Expected 2 bits represented as 0xA, received 0x%x\n", found);

        HeapFree( GetProcessHeap(), 0, buffer );
    }
}

struct protocol
{
    int prot;
    const char *names[2];
    BOOL missing_from_xp;
};

static const struct protocol 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" }, TRUE},
    {  43, { "ipv6-route", "IPv6-Route" }, TRUE},
    {  44, { "ipv6-frag", "IPv6-Frag" }, TRUE},
    {  50, { "esp", "ESP" }, TRUE},
    {  51, { "ah", "AH" }, TRUE},
    {  58, { "ipv6-icmp", "IPv6-ICMP" }, TRUE},
    {  59, { "ipv6-nonxt", "IPv6-NoNxt" }, TRUE},
    {  60, { "ipv6-opts", "IPv6-Opts" }, TRUE},
    {  66, { "rvd", "RVD" }},
};

static const struct protocol *find_protocol(int number)
{
    int i;
    for (i = 0; i < ARRAY_SIZE(protocols); i++)
    {
        if (protocols[i].prot == number)
            return &protocols[i];
    }
    return NULL;
}

static void test_getprotobyname(void)
{
    struct protoent *ent;
    char all_caps_name[16];
    int i, j;

    for (i = 0; i < ARRAY_SIZE(protocols); i++)
    {
        for (j = 0; j < ARRAY_SIZE(protocols[0].names); j++)
        {
            ent = getprotobyname(protocols[i].names[j]);
            ok((ent && ent->p_proto == protocols[i].prot) || broken(!ent && protocols[i].missing_from_xp),
               "Expected %s to be protocol number %d, got %d\n",
               wine_dbgstr_a(protocols[i].names[j]), protocols[i].prot, ent ? ent->p_proto : -1);
        }

        for (j = 0; protocols[i].names[0][j]; j++)
            all_caps_name[j] = toupper(protocols[i].names[0][j]);
        all_caps_name[j] = 0;
        ent = getprotobyname(all_caps_name);
        ok((ent && ent->p_proto == protocols[i].prot) || broken(!ent && protocols[i].missing_from_xp),
           "Expected %s to be protocol number %d, got %d\n",
           wine_dbgstr_a(all_caps_name), protocols[i].prot, ent ? ent->p_proto : -1);
    }
}

static void test_getprotobynumber(void)
{
    struct protoent *ent;
    const struct protocol *ref;
    int i;

    for (i = -1; i <= 256; i++)
    {
        ent = getprotobynumber(i);
        ref = find_protocol(i);

        if (!ref)
        {
            ok(!ent, "Expected protocol number %d to be undefined, got %s\n",
               i, wine_dbgstr_a(ent ? ent->p_name : NULL));
            continue;
        }

        ok((ent && ent->p_name && !strcmp(ent->p_name, ref->names[0])) ||
           broken(!ent && ref->missing_from_xp),
           "Expected protocol number %d to be %s, got %s\n",
           i, ref->names[0], wine_dbgstr_a(ent ? ent->p_name : NULL));

        ok((ent && ent->p_aliases && ent->p_aliases[0] &&
            !strcmp(ent->p_aliases[0], ref->names[1])) ||
           broken(!ent && ref->missing_from_xp),
           "Expected protocol number %d alias 0 to be %s, got %s\n",
           i, ref->names[0], wine_dbgstr_a(ent && ent->p_aliases ? ent->p_aliases[0] : NULL));
    }
}

#define NUM_THREADS 3      /* Number of threads to run getservbyname */
#define NUM_QUERIES 250    /* Number of getservbyname queries per thread */

static DWORD WINAPI do_getservbyname( void *param )
{
    struct
    {
        const char *name;
        const char *proto;
        int port;
    } serv[2] =
    {
        {"domain", "udp", 53},
        {"telnet", "tcp", 23},
    };

    HANDLE *starttest = param;
    int i, j;
    struct servent *pserv[2];

    ok( WaitForSingleObject( *starttest, 30 * 1000 ) != WAIT_TIMEOUT,
         "test_getservbyname: timeout waiting for start signal\n" );

    /* ensure that necessary buffer resizes are completed */
    for (j = 0; j < 2; j++)
        pserv[j] = getservbyname( serv[j].name, serv[j].proto );

    for (i = 0; i < NUM_QUERIES / 2; i++)
    {
        for (j = 0; j < 2; j++)
        {
            pserv[j] = getservbyname( serv[j].name, serv[j].proto );
            ok( pserv[j] != NULL || broken(pserv[j] == NULL) /* win8, fixed in win81 */,
                 "getservbyname could not retrieve information for %s: %d\n", serv[j].name, WSAGetLastError() );
            if ( !pserv[j] ) continue;
            ok( pserv[j]->s_port == htons(serv[j].port),
                 "getservbyname returned the wrong port for %s: %d\n", serv[j].name, ntohs(pserv[j]->s_port) );
            ok( !strcmp( pserv[j]->s_proto, serv[j].proto ),
                 "getservbyname returned the wrong protocol for %s: %s\n", serv[j].name, pserv[j]->s_proto );
            ok( !strcmp( pserv[j]->s_name, serv[j].name ),
                 "getservbyname returned the wrong name for %s: %s\n", serv[j].name, pserv[j]->s_name );
        }

        ok( pserv[0] == pserv[1] || broken(pserv[0] != pserv[1]) /* win8, fixed in win81 */,
             "getservbyname: winsock resized servent buffer when not necessary\n" );
    }

    return 0;
}

static void test_getservbyname(void)
{
    int i;
    HANDLE starttest, thread[NUM_THREADS];

    starttest = CreateEventA( NULL, 1, 0, "test_getservbyname_starttest" );

    /* create threads */
    for (i = 0; i < NUM_THREADS; i++)
        thread[i] = CreateThread( NULL, 0, do_getservbyname, &starttest, 0, NULL );

    /* signal threads to start */
    SetEvent( starttest );

    for (i = 0; i < NUM_THREADS; i++)
        WaitForSingleObject( thread[i], 30 * 1000 );
}

static void test_WSALookupService(void)
{
    char buffer[4096], strbuff[128];
    WSAQUERYSETW *qs = NULL;
    HANDLE handle;
    PNLA_BLOB netdata;
    int ret;
    DWORD error, offset, size;

    qs = (WSAQUERYSETW *)buffer;
    memset(qs, 0, sizeof(*qs));

    /* invalid parameter tests */
    ret = WSALookupServiceBeginW(NULL, 0, &handle);
    error = WSAGetLastError();
    ok(ret == SOCKET_ERROR, "WSALookupServiceBeginW should have failed\n");
todo_wine
    ok(error == WSAEFAULT, "expected 10014, got %d\n", error);

    ret = WSALookupServiceBeginW(qs, 0, NULL);
    error = WSAGetLastError();
    ok(ret == SOCKET_ERROR, "WSALookupServiceBeginW should have failed\n");
todo_wine
    ok(error == WSAEFAULT, "expected 10014, got %d\n", error);

    ret = WSALookupServiceBeginW(qs, 0, &handle);
    ok(ret == SOCKET_ERROR, "WSALookupServiceBeginW should have failed\n");
    todo_wine ok(WSAGetLastError() == ERROR_INVALID_PARAMETER
            || broken(WSAGetLastError() == WSASERVICE_NOT_FOUND) /* win10 1809 */,
            "got error %u\n", WSAGetLastError());

    ret = WSALookupServiceEnd(NULL);
    error = WSAGetLastError();
todo_wine
    ok(ret == SOCKET_ERROR, "WSALookupServiceEnd should have failed\n");
todo_wine
    ok(error == ERROR_INVALID_HANDLE, "expected 6, got %d\n", error);

    /* standard network list query */
    qs->dwSize = sizeof(*qs);
    handle = (HANDLE)0xdeadbeef;
    ret = WSALookupServiceBeginW(qs, LUP_RETURN_ALL | LUP_DEEP, &handle);
    error = WSAGetLastError();
    if (ret && error == ERROR_INVALID_PARAMETER)
    {
        win_skip("the current WSALookupServiceBeginW test is not supported in win <= 2000\n");
        return;
    }

todo_wine
    ok(!ret, "WSALookupServiceBeginW failed unexpectedly with error %d\n", error);
todo_wine
    ok(handle != (HANDLE)0xdeadbeef, "Handle was not filled\n");

    offset = 0;
    do
    {
        memset(qs, 0, sizeof(*qs));
        size = sizeof(buffer);

        if (WSALookupServiceNextW(handle, 0, &size, qs) == SOCKET_ERROR)
        {
            ok(WSAGetLastError() == WSA_E_NO_MORE, "got error %u\n", WSAGetLastError());
            break;
        }

        if (winetest_debug <= 1) continue;

        WideCharToMultiByte(CP_ACP, 0, qs->lpszServiceInstanceName, -1,
                            strbuff, sizeof(strbuff), NULL, NULL);
        trace("Network Name: %s\n", strbuff);

        /* network data is written in the blob field */
        if (qs->lpBlob)
        {
            /* each network may have multiple NLA_BLOB information structures */
            do
            {
                netdata = (PNLA_BLOB) &qs->lpBlob->pBlobData[offset];
                switch (netdata->header.type)
                {
                    case NLA_RAW_DATA:
                        trace("\tNLA Data Type: NLA_RAW_DATA\n");
                        break;
                    case NLA_INTERFACE:
                        trace("\tNLA Data Type: NLA_INTERFACE\n");
                        trace("\t\tType: %d\n", netdata->data.interfaceData.dwType);
                        trace("\t\tSpeed: %d\n", netdata->data.interfaceData.dwSpeed);
                        trace("\t\tAdapter Name: %s\n", netdata->data.interfaceData.adapterName);
                        break;
                    case NLA_802_1X_LOCATION:
                        trace("\tNLA Data Type: NLA_802_1X_LOCATION\n");
                        trace("\t\tInformation: %s\n", netdata->data.locationData.information);
                        break;
                    case NLA_CONNECTIVITY:
                        switch (netdata->data.connectivity.type)
                        {
                            case NLA_NETWORK_AD_HOC:
                                trace("\t\tNetwork Type: AD HOC\n");
                                break;
                            case NLA_NETWORK_MANAGED:
                                trace("\t\tNetwork Type: Managed\n");
                                break;
                            case NLA_NETWORK_UNMANAGED:
                                trace("\t\tNetwork Type: Unmanaged\n");
                                break;
                            case NLA_NETWORK_UNKNOWN:
                                trace("\t\tNetwork Type: Unknown\n");
                        }
                        switch (netdata->data.connectivity.internet)
                        {
                            case NLA_INTERNET_NO:
                                trace("\t\tInternet connectivity: No\n");
                                break;
                            case NLA_INTERNET_YES:
                                trace("\t\tInternet connectivity: Yes\n");
                                break;
                            case NLA_INTERNET_UNKNOWN:
                                trace("\t\tInternet connectivity: Unknown\n");
                                break;
                        }
                        break;
                    case NLA_ICS:
                        trace("\tNLA Data Type: NLA_ICS\n");
                        trace("\t\tSpeed: %d\n",
                               netdata->data.ICS.remote.speed);
                        trace("\t\tType: %d\n",
                               netdata->data.ICS.remote.type);
                        trace("\t\tState: %d\n",
                               netdata->data.ICS.remote.state);
                        WideCharToMultiByte(CP_ACP, 0, netdata->data.ICS.remote.machineName, -1,
                            strbuff, sizeof(strbuff), NULL, NULL);
                        trace("\t\tMachine Name: %s\n", strbuff);
                        WideCharToMultiByte(CP_ACP, 0, netdata->data.ICS.remote.sharedAdapterName, -1,
                            strbuff, sizeof(strbuff), NULL, NULL);
                        trace("\t\tShared Adapter Name: %s\n", strbuff);
                        break;
                    default:
                        trace("\tNLA Data Type: Unknown\n");
                        break;
                }
            }
            while (offset);
        }
    }
    while (1);

    ret = WSALookupServiceEnd(handle);
    ok(!ret, "WSALookupServiceEnd failed unexpectedly\n");
}

#define WM_ASYNCCOMPLETE (WM_USER + 100)
static HWND create_async_message_window(void)
{
    static const char class_name[] = "ws2_32 async message window class";

    WNDCLASSEXA wndclass;
    HWND hWnd;

    wndclass.cbSize         = sizeof(wndclass);
    wndclass.style          = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc    = DefWindowProcA;
    wndclass.cbClsExtra     = 0;
    wndclass.cbWndExtra     = 0;
    wndclass.hInstance      = GetModuleHandleA(NULL);
    wndclass.hIcon          = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
    wndclass.hIconSm        = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
    wndclass.hCursor        = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
    wndclass.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
    wndclass.lpszClassName  = class_name;
    wndclass.lpszMenuName   = NULL;

    RegisterClassExA(&wndclass);

    hWnd = CreateWindowA(class_name, "ws2_32 async message window", WS_OVERLAPPEDWINDOW,
                        0, 0, 500, 500, NULL, NULL, GetModuleHandleA(NULL), NULL);
    ok(!!hWnd, "failed to create window\n");

    return hWnd;
}

static void wait_for_async_message(HWND hwnd, HANDLE handle)
{
    BOOL ret;
    MSG msg;

    while ((ret = GetMessageA(&msg, 0, 0, 0)) &&
           !(msg.hwnd == hwnd && msg.message == WM_ASYNCCOMPLETE))
    {
        TranslateMessage(&msg);
        DispatchMessageA(&msg);
    }

    ok(ret, "did not expect WM_QUIT message\n");
    ok(msg.wParam == (WPARAM)handle, "expected wParam = %p, got %lx\n", handle, msg.wParam);
}

static void test_WSAAsyncGetServByPort(void)
{
    HWND hwnd = create_async_message_window();
    HANDLE ret;
    char buffer[MAXGETHOSTSTRUCT];

    /* FIXME: The asynchronous window messages should be tested. */

    /* Parameters are not checked when initiating the asynchronous operation.  */
    ret = WSAAsyncGetServByPort(NULL, 0, 0, NULL, NULL, 0);
    ok(ret != NULL, "WSAAsyncGetServByPort returned NULL\n");

    ret = WSAAsyncGetServByPort(hwnd, WM_ASYNCCOMPLETE, 0, NULL, NULL, 0);
    ok(ret != NULL, "WSAAsyncGetServByPort returned NULL\n");
    wait_for_async_message(hwnd, ret);

    ret = WSAAsyncGetServByPort(hwnd, WM_ASYNCCOMPLETE, htons(80), NULL, NULL, 0);
    ok(ret != NULL, "WSAAsyncGetServByPort returned NULL\n");
    wait_for_async_message(hwnd, ret);

    ret = WSAAsyncGetServByPort(hwnd, WM_ASYNCCOMPLETE, htons(80), NULL, buffer, MAXGETHOSTSTRUCT);
    ok(ret != NULL, "WSAAsyncGetServByPort returned NULL\n");
    wait_for_async_message(hwnd, ret);

    DestroyWindow(hwnd);
}

static void test_WSAAsyncGetServByName(void)
{
    HWND hwnd = create_async_message_window();
    HANDLE ret;
    char buffer[MAXGETHOSTSTRUCT];

    /* FIXME: The asynchronous window messages should be tested. */

    /* Parameters are not checked when initiating the asynchronous operation.  */
    ret = WSAAsyncGetServByName(hwnd, WM_ASYNCCOMPLETE, "", NULL, NULL, 0);
    ok(ret != NULL, "WSAAsyncGetServByName returned NULL\n");
    wait_for_async_message(hwnd, ret);

    ret = WSAAsyncGetServByName(hwnd, WM_ASYNCCOMPLETE, "", "", buffer, MAXGETHOSTSTRUCT);
    ok(ret != NULL, "WSAAsyncGetServByName returned NULL\n");
    wait_for_async_message(hwnd, ret);

    ret = WSAAsyncGetServByName(hwnd, WM_ASYNCCOMPLETE, "http", NULL, NULL, 0);
    ok(ret != NULL, "WSAAsyncGetServByName returned NULL\n");
    wait_for_async_message(hwnd, ret);

    ret = WSAAsyncGetServByName(hwnd, WM_ASYNCCOMPLETE, "http", "tcp", buffer, MAXGETHOSTSTRUCT);
    ok(ret != NULL, "WSAAsyncGetServByName returned NULL\n");
    wait_for_async_message(hwnd, ret);

    DestroyWindow(hwnd);
}

static DWORD WINAPI inet_ntoa_thread_proc(void *param)
{
    ULONG addr;
    const char *str;
    HANDLE *event = param;

    addr = inet_addr("4.3.2.1");
    ok(addr == htonl(0x04030201), "expected 0x04030201, got %08x\n", addr);
    str = inet_ntoa(*(struct in_addr *)&addr);
    ok(!strcmp(str, "4.3.2.1"), "expected 4.3.2.1, got %s\n", str);

    SetEvent(event[0]);
    WaitForSingleObject(event[1], 3000);

    return 0;
}

static void test_inet_ntoa(void)
{
    ULONG addr;
    const char *str;
    HANDLE thread, event[2];
    DWORD tid;

    addr = inet_addr("1.2.3.4");
    ok(addr == htonl(0x01020304), "expected 0x01020304, got %08x\n", addr);
    str = inet_ntoa(*(struct in_addr *)&addr);
    ok(!strcmp(str, "1.2.3.4"), "expected 1.2.3.4, got %s\n", str);

    event[0] = CreateEventW(NULL, TRUE, FALSE, NULL);
    event[1] = CreateEventW(NULL, TRUE, FALSE, NULL);

    thread = CreateThread(NULL, 0, inet_ntoa_thread_proc, event, 0, &tid);
    WaitForSingleObject(event[0], 3000);

    ok(!strcmp(str, "1.2.3.4"), "expected 1.2.3.4, got %s\n", str);

    SetEvent(event[1]);
    WaitForSingleObject(thread, 3000);

    CloseHandle(event[0]);
    CloseHandle(event[1]);
    CloseHandle(thread);
}

static void test_inet_pton(void)
{
    static const struct
    {
        char input[32];
        int ret;
        DWORD addr;
    }
    ipv4_tests[] =
    {
        {"",                       0, 0xdeadbeef},
        {" ",                      0, 0xdeadbeef},
        {"1.1.1.1",                1, 0x01010101},
        {"0.0.0.0",                1, 0x00000000},
        {"127.127.127.255",        1, 0xff7f7f7f},
        {"127.127.127.255:123",    0, 0xff7f7f7f},
        {"127.127.127.256",        0, 0xdeadbeef},
        {"a",                      0, 0xdeadbeef},
        {"1.1.1.0xaA",             0, 0xdeadbeef},
        {"1.1.1.0x",               0, 0xdeadbeef},
        {"1.1.1.010",              0, 0xdeadbeef},
        {"1.1.1.00",               0, 0xdeadbeef},
        {"1.1.1.0a",               0, 0x00010101},
        {"1.1.1.0o10",             0, 0x00010101},
        {"1.1.1.0b10",             0, 0x00010101},
        {"1.1.1.-2",               0, 0xdeadbeef},
        {"1",                      0, 0xdeadbeef},
        {"1.2",                    0, 0xdeadbeef},
        {"1.2.3",                  0, 0xdeadbeef},
        {"203569230",              0, 0xdeadbeef},
        {"3.4.5.6.7",              0, 0xdeadbeef},
        {" 3.4.5.6",               0, 0xdeadbeef},
        {"\t3.4.5.6",              0, 0xdeadbeef},
        {"3.4.5.6 ",               0, 0x06050403},
        {"3. 4.5.6",               0, 0xdeadbeef},
        {"[0.1.2.3]",              0, 0xdeadbeef},
        {"0x00010203",             0, 0xdeadbeef},
        {"0x2134",                 0, 0xdeadbeef},
        {"1234BEEF",               0, 0xdeadbeef},
        {"017700000001",           0, 0xdeadbeef},
        {"0777",                   0, 0xdeadbeef},
        {"2607:f0d0:1002:51::4",   0, 0xdeadbeef},
        {"::177.32.45.20",         0, 0xdeadbeef},
        {"::1/128",                0, 0xdeadbeef},
        {"::1",                    0, 0xdeadbeef},
        {":1",                     0, 0xdeadbeef},
    };

    static const struct
    {
        char input[64];
        int ret;
        unsigned short addr[8];
    }
    ipv6_tests[] =
    {
        {"0000:0000:0000:0000:0000:0000:0000:0000",        1, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"0000:0000:0000:0000:0000:0000:0000:0001",        1, {0, 0, 0, 0, 0, 0, 0, 0x100}},
        {"0:0:0:0:0:0:0:0",                                1, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"0:0:0:0:0:0:0:1",                                1, {0, 0, 0, 0, 0, 0, 0, 0x100}},
        {"0:0:0:0:0:0:0::",                                1, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"0:0:0:0:0:0:13.1.68.3",                          1, {0, 0, 0, 0, 0, 0, 0x10d, 0x344}},
        {"0:0:0:0:0:0::",                                  1, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"0:0:0:0:0::",                                    1, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"0:0:0:0:0:FFFF:129.144.52.38",                   1, {0, 0, 0, 0, 0, 0xffff, 0x9081, 0x2634}},
        {"0::",                                            1, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"0:1:2:3:4:5:6:7",                                1, {0, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700}},
        {"1080:0:0:0:8:800:200c:417a",                     1, {0x8010, 0, 0, 0, 0x800, 0x8, 0x0c20, 0x7a41}},
        {"0:a:b:c:d:e:f::",                                1, {0, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, 0xf00, 0}},
        {"1111:2222:3333:4444:5555:6666:123.123.123.123",  1, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7b7b, 0x7b7b}},
        {"1111:2222:3333:4444:5555:6666:7777:8888",        1, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888}},
        {"1111:2222:3333:4444:0x5555:6666:7777:8888",      0, {0x1111, 0x2222, 0x3333, 0x4444, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:2222:3333:4444:x555:6666:7777:8888",        0, {0x1111, 0x2222, 0x3333, 0x4444, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:2222:3333:4444:0r5555:6666:7777:8888",      0, {0x1111, 0x2222, 0x3333, 0x4444, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:2222:3333:4444:r5555:6666:7777:8888",       0, {0x1111, 0x2222, 0x3333, 0x4444, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:2222:3333:4444:5555:6666:7777::",           1, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0}},
        {"1111:2222:3333:4444:5555:6666::",                1, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0, 0}},
        {"1111:2222:3333:4444:5555:6666::8888",            1, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0, 0x8888}},
        {"1111:2222:3333:4444:5555:6666::7777:8888",       0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0, 0x7777}},
        {"1111:2222:3333:4444:5555:6666:7777::8888",       0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0}},
        {"1111:2222:3333:4444:5555::",                     1, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0, 0}},
        {"1111:2222:3333:4444:5555::123.123.123.123",      1, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0x7b7b, 0x7b7b}},
        {"1111:2222:3333:4444:5555::0x1.123.123.123",      0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0, 0x100}},
        {"1111:2222:3333:4444:5555::0x88",                 0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0, 0x8800}},
        {"1111:2222:3333:4444:5555::0X88",                 0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0, 0x8800}},
        {"1111:2222:3333:4444:5555::0X",                   0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0, 0}},
        {"1111:2222:3333:4444:5555::0X88:7777",            0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0, 0x8800}},
        {"1111:2222:3333:4444:5555::0x8888",               0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0, 0x8888}},
        {"1111:2222:3333:4444:5555::0x80000000",           0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0, 0xffff}},
        {"1111:2222:3333:4444::5555:0x012345678",          0, {0x1111, 0x2222, 0x3333, 0x4444, 0, 0, 0x5555, 0x7856}},
        {"1111:2222:3333:4444::5555:0x123456789",          0, {0x1111, 0x2222, 0x3333, 0x4444, 0, 0, 0x5555, 0xffff}},
        {"1111:2222:3333:4444:5555:6666:0x12345678",       0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0xabab, 0xabab}},
        {"1111:2222:3333:4444:5555:6666:7777:0x80000000",  0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0xffff}},
        {"1111:2222:3333:4444:5555:6666:7777:0x012345678", 0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x7856}},
        {"1111:2222:3333:4444:5555:6666:7777:0x123456789", 0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0xffff}},
        {"111:222:333:444:555:666:777:0x123456789abcdef0", 0, {0x1101, 0x2202, 0x3303, 0x4404, 0x5505, 0x6606, 0x7707, 0xffff}},
        {"1111:2222:3333:4444:5555::08888",                0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0xabab, 0xabab, 0xabab}},
        {"1111:2222:3333:4444:5555::08888::",              0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0xabab, 0xabab, 0xabab}},
        {"1111:2222:3333:4444:5555:6666:7777:fffff:",      0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0xabab}},
        {"1111:2222:3333:4444:5555:6666::fffff:",          0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0xabab, 0xabab}},
        {"1111:2222:3333:4444:5555::fffff",                0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0xabab, 0xabab, 0xabab}},
        {"1111:2222:3333:4444::fffff",                     0, {0x1111, 0x2222, 0x3333, 0x4444, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:2222:3333::fffff",                          0, {0x1111, 0x2222, 0x3333, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:2222:3333:4444:5555::7777:8888",            1, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0x7777, 0x8888}},
        {"1111:2222:3333:4444:5555::8888",                 1, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0, 0x8888}},
        {"1111::",                                         1, {0x1111, 0, 0, 0, 0, 0, 0, 0}},
        {"1111::123.123.123.123",                          1, {0x1111, 0, 0, 0, 0, 0, 0x7b7b, 0x7b7b}},
        {"1111::3333:4444:5555:6666:123.123.123.123",      1, {0x1111, 0, 0x3333, 0x4444, 0x5555, 0x6666, 0x7b7b, 0x7b7b}},
        {"1111::3333:4444:5555:6666:7777:8888",            1, {0x1111, 0, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888}},
        {"1111::4444:5555:6666:123.123.123.123",           1, {0x1111, 0, 0, 0x4444, 0x5555, 0x6666, 0x7b7b, 0x7b7b}},
        {"1111::4444:5555:6666:7777:8888",                 1, {0x1111, 0, 0, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888}},
        {"1111::5555:6666:123.123.123.123",                1, {0x1111, 0, 0, 0, 0x5555, 0x6666, 0x7b7b, 0x7b7b}},
        {"1111::5555:6666:7777:8888",                      1, {0x1111, 0, 0, 0, 0x5555, 0x6666, 0x7777, 0x8888}},
        {"1111::6666:123.123.123.123",                     1, {0x1111, 0, 0, 0, 0, 0x6666, 0x7b7b, 0x7b7b}},
        {"1111::6666:7777:8888",                           1, {0x1111, 0, 0, 0, 0, 0x6666, 0x7777, 0x8888}},
        {"1111::7777:8888",                                1, {0x1111, 0, 0, 0, 0, 0, 0x7777, 0x8888}},
        {"1111::8888",                                     1, {0x1111, 0, 0, 0, 0, 0, 0, 0x8888}},
        {"1:2:3:4:5:6:1.2.3.4",                            1, {0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x201, 0x403}},
        {"1:2:3:4:5:6:7:8",                                1, {0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700, 0x800}},
        {"1:2:3:4:5:6::",                                  1, {0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0, 0}},
        {"1:2:3:4:5:6::8",                                 1, {0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0, 0x800}},
        {"2001:0000:1234:0000:0000:C1C0:ABCD:0876",        1, {0x120, 0, 0x3412, 0, 0, 0xc0c1, 0xcdab, 0x7608}},
        {"2001:0000:4136:e378:8000:63bf:3fff:fdd2",        1, {0x120, 0, 0x3641, 0x78e3, 0x80, 0xbf63, 0xff3f, 0xd2fd}},
        {"2001:0db8:0:0:0:0:1428:57ab",                    1, {0x120, 0xb80d, 0, 0, 0, 0, 0x2814, 0xab57}},
        {"2001:0db8:1234:ffff:ffff:ffff:ffff:ffff",        1, {0x120, 0xb80d, 0x3412, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}},
        {"2001::CE49:7601:2CAD:DFFF:7C94:FFFE",            1, {0x120, 0, 0x49ce, 0x176, 0xad2c, 0xffdf, 0x947c, 0xfeff}},
        {"2001:db8:85a3::8a2e:370:7334",                   1, {0x120, 0xb80d, 0xa385, 0, 0, 0x2e8a, 0x7003, 0x3473}},
        {"3ffe:0b00:0000:0000:0001:0000:0000:000a",        1, {0xfe3f, 0xb, 0, 0, 0x100, 0, 0, 0xa00}},
        {"::",                                             1, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"::%16",                                          0, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"::/16",                                          0, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"::01234",                                        0, {0, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"::0",                                            1, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"::0:0",                                          1, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"::0:0:0",                                        1, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"::0:0:0:0",                                      1, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"::0:0:0:0:0",                                    1, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"::0:0:0:0:0:0",                                  1, {0, 0, 0, 0, 0, 0, 0, 0}},
        /* this one and the next one are incorrectly parsed by windows,
            it adds one zero too many in front, cutting off the last digit. */
        {"::0:0:0:0:0:0:0",                                0, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"::0:a:b:c:d:e:f",                                0, {0, 0, 0, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00}},
        {"::123.123.123.123",                              1, {0, 0, 0, 0, 0, 0, 0x7b7b, 0x7b7b}},
        {"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",        1, {0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}},
        {"':10.0.0.1",                                     0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"-1",                                             0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"02001:0000:1234:0000:0000:C1C0:ABCD:0876",       0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"2001:00000:1234:0000:0000:C1C0:ABCD:0876",       0, {0x120, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"2001:0000:01234:0000:0000:C1C0:ABCD:0876",       0, {0x120, 0, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"2001:0000::01234.0",                             0, {0x120, 0, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"2001:0::b.0",                                    0, {0x120, 0, 0, 0, 0, 0, 0, 0xb00}},
        {"2001::0:b.0",                                    0, {0x120, 0, 0, 0, 0, 0, 0, 0xb00}},
        {"1.2.3.4",                                        0, {0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1.2.3.4:1111::5555",                             0, {0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1.2.3.4::5555",                                  0, {0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"11112222:3333:4444:5555:6666:1.2.3.4",           0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"11112222:3333:4444:5555:6666:7777:8888",         0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111",                                           0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"0x1111",                                         0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:22223333:4444:5555:6666:1.2.3.4",           0, {0x1111, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:22223333:4444:5555:6666:7777:8888",         0, {0x1111, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:123456789:4444:5555:6666:7777:8888",        0, {0x1111, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:1234567890abcdef0:4444:5555:6666:7777:888", 0, {0x1111, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:2222:",                                     0, {0x1111, 0x2222, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:2222:1.2.3.4",                              0, {0x1111, 0x2222, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:2222:3333",                                 0, {0x1111, 0x2222, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111:2222:3333:4444:5555:6666::1.2.3.4",         0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0, 0x100}},
        {"1111:2222:3333:4444:5555:6666:7777:1.2.3.4",     0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x100}},
        {"1111:2222:3333:4444:5555:6666:7777:8888:",       0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888}},
        {"1111:2222:3333:4444:5555:6666:7777:8888:1.2.3.4",0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888}},
        {"1111:2222:3333:4444:5555:6666:7777:8888:9999",   0, {0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888}},
        {"1111:2222:::",                                   0, {0x1111, 0x2222, 0, 0, 0, 0, 0, 0}},
        {"1111::5555:",                                    0, {0x1111, 0x5555, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1111::3333:4444:5555:6666:7777::",               0, {0x1111, 0, 0, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777}},
        {"1111:2222:::4444:5555:6666:1.2.3.4",             0, {0x1111, 0x2222, 0, 0, 0, 0, 0, 0}},
        {"1111::3333::5555:6666:1.2.3.4",                  0, {0x1111, 0, 0, 0, 0, 0, 0, 0x3333}},
        {"12345::6:7:8",                                   0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::001.2.3.4",                                   1, {0x100, 0, 0, 0, 0, 0, 0x201, 0x403}},
        {"1::1.002.3.4",                                   1, {0x100, 0, 0, 0, 0, 0, 0x201, 0x403}},
        {"1::0001.2.3.4",                                  0, {0x100, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.0002.3.4",                                  0, {0x100, 0xab01, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.256.4",                                   0, {0x100, 0x201, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.4294967296.4",                            0, {0x100, 0x201, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.18446744073709551616.4",                  0, {0x100, 0x201, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.3.256",                                   0, {0x100, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.3.4294967296",                            0, {0x100, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.3.18446744073709551616",                  0, {0x100, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.3.300",                                   0, {0x100, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.3.300.",                                  0, {0x100, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2::1",                                      0, {0x100, 0xab01, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.3.4::1",                                  0, {0x100, 0, 0, 0, 0, 0, 0x201, 0x403}},
        {"1::1.",                                          0, {0x100, 0xab01, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2",                                         0, {0x100, 0xab01, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.",                                        0, {0x100, 0x201, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.3",                                       0, {0x100, 0x201, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.3.",                                      0, {0x100, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.3.4",                                     1, {0x100, 0, 0, 0, 0, 0, 0x201, 0x403}},
        {"1::1.2.3.900",                                   0, {0x100, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2.300.4",                                   0, {0x100, 0x201, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.256.3.4",                                   0, {0x100, 0xab01, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.256:3.4",                                   0, {0x100, 0xab01, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1.2a.3.4",                                    0, {0x100, 0xab01, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::256.2.3.4",                                   0, {0x100, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"1::1a.2.3.4",                                    0, {0x100, 0, 0, 0, 0, 0, 0, 0x1a00}},
        {"1::2::3",                                        0, {0x100, 0, 0, 0, 0, 0, 0, 0x200}},
        {"2001:0000:1234: 0000:0000:C1C0:ABCD:0876",       0, {0x120, 0, 0x3412, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"2001:0000:1234:0000:0000:C1C0:ABCD:0876  0",     0, {0x120, 0, 0x3412, 0, 0, 0xc0c1, 0xcdab, 0x7608}},
        {"2001:1:1:1:1:1:255Z255X255Y255",                 0, {0x120, 0x100, 0x100, 0x100, 0x100, 0x100, 0xabab, 0xabab}},
        {"2001::FFD3::57ab",                               0, {0x120, 0, 0, 0, 0, 0, 0, 0xd3ff}},
        {":",                                              0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {":1111:2222:3333:4444:5555:6666:1.2.3.4",         0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {":1111:2222:3333:4444:5555:6666:7777:8888",       0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {":1111::",                                        0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"::-1",                                           0, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"::12345678",                                     0, {0, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"::123456789",                                    0, {0, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"::1234567890abcdef0",                            0, {0, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"::0x80000000",                                   0, {0, 0, 0, 0, 0, 0, 0, 0xffff}},
        {"::0x012345678",                                  0, {0, 0, 0, 0, 0, 0, 0, 0x7856}},
        {"::0x123456789",                                  0, {0, 0, 0, 0, 0, 0, 0, 0xffff}},
        {"::0x1234567890abcdef0",                          0, {0, 0, 0, 0, 0, 0, 0, 0xffff}},
        {"::.",                                            0, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"::..",                                           0, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"::...",                                          0, {0, 0, 0, 0, 0, 0, 0, 0}},
        {"XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4",          0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
        {"[::]",                                           0, {0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab}},
    };

    BYTE buffer[32];
    int i, ret;

    /* inet_ntop and inet_pton became available in Vista and Win2008 */
    if (!p_inet_ntop)
    {
        win_skip("inet_ntop is not available\n");
        return;
    }

    WSASetLastError(0xdeadbeef);
    ret = p_inet_pton(AF_UNSPEC, NULL, buffer);
    ok(ret == -1, "got %d\n", ret);
    ok(WSAGetLastError() == WSAEFAULT, "got error %u\n", WSAGetLastError());

    WSASetLastError(0xdeadbeef);
    ret = p_inet_pton(AF_INET, NULL, buffer);
    ok(ret == -1, "got %d\n", ret);
    ok(WSAGetLastError() == WSAEFAULT, "got error %u\n", WSAGetLastError());

    WSASetLastError(0xdeadbeef);
    ret = pInetPtonW(AF_UNSPEC, NULL, buffer);
    ok(ret == -1, "got %d\n", ret);
    ok(WSAGetLastError() == WSAEFAULT, "got error %u\n", WSAGetLastError());

    WSASetLastError(0xdeadbeef);
    ret = pInetPtonW(AF_INET, NULL, buffer);
    ok(ret == -1, "got %d\n", ret);
    ok(WSAGetLastError() == WSAEFAULT, "got error %u\n", WSAGetLastError());

    WSASetLastError(0xdeadbeef);
    ret = p_inet_pton(AF_UNSPEC, "127.0.0.1", buffer);
    ok(ret == -1, "got %d\n", ret);
    ok(WSAGetLastError() == WSAEAFNOSUPPORT, "got error %u\n", WSAGetLastError());

    WSASetLastError(0xdeadbeef);
    ret = p_inet_pton(AF_UNSPEC, "2607:f0d0:1002:51::4", buffer);
    ok(ret == -1, "got %d\n", ret);
    ok(WSAGetLastError() == WSAEAFNOSUPPORT, "got error %u\n", WSAGetLastError());

    WSASetLastError(0xdeadbeef);
    ret = inet_addr(NULL);
    ok(ret == INADDR_NONE, "got %#x\n", ret);
    ok(WSAGetLastError() == WSAEFAULT, "got error %u\n", WSAGetLastError());

    for (i = 0; i < ARRAY_SIZE(ipv4_tests); ++i)
    {
        WCHAR inputW[32];
        DWORD addr;

        WSASetLastError(0xdeadbeef);
        addr = 0xdeadbeef;
        ret = p_inet_pton(AF_INET, ipv4_tests[i].input, &addr);
        ok(ret == ipv4_tests[i].ret, "%s: got %d\n", debugstr_a(ipv4_tests[i].input), ret);
        ok(WSAGetLastError() == 0xdeadbeef, "%s: got error %u\n",
                debugstr_a(ipv4_tests[i].input), WSAGetLastError());
        ok(addr == ipv4_tests[i].addr, "%s: got addr %#08x\n", debugstr_a(ipv4_tests[i].input), addr);

        MultiByteToWideChar(CP_ACP, 0, ipv4_tests[i].input, -1, inputW, ARRAY_SIZE(inputW));
        WSASetLastError(0xdeadbeef);
        addr = 0xdeadbeef;
        ret = pInetPtonW(AF_INET, inputW, &addr);
        ok(ret == ipv4_tests[i].ret, "%s: got %d\n", debugstr_a(ipv4_tests[i].input), ret);
        ok(WSAGetLastError() == (ret ? 0xdeadbeef : WSAEINVAL), "%s: got error %u\n",
                debugstr_a(ipv4_tests[i].input), WSAGetLastError());
        ok(addr == ipv4_tests[i].addr, "%s: got addr %#08x\n", debugstr_a(ipv4_tests[i].input), addr);

        WSASetLastError(0xdeadbeef);
        addr = inet_addr(ipv4_tests[i].input);
        ok(addr == ipv4_tests[i].ret ? ipv4_tests[i].addr : INADDR_NONE,
                "%s: got addr %#08x\n", debugstr_a(ipv4_tests[i].input), addr);
        ok(WSAGetLastError() == 0xdeadbeef, "%s: got error %u\n",
                debugstr_a(ipv4_tests[i].input), WSAGetLastError());
    }

    for (i = 0; i < ARRAY_SIZE(ipv6_tests); ++i)
    {
        unsigned short addr[8];
        WCHAR inputW[64];

        WSASetLastError(0xdeadbeef);
        memset(addr, 0xab, sizeof(addr));
        ret = p_inet_pton(AF_INET6, ipv6_tests[i].input, addr);
        ok(ret == ipv6_tests[i].ret, "%s: got %d\n", debugstr_a(ipv6_tests[i].input), ret);
        ok(WSAGetLastError() == 0xdeadbeef, "%s: got error %u\n",
                debugstr_a(ipv6_tests[i].input), WSAGetLastError());
        ok(!memcmp(addr, ipv6_tests[i].addr, sizeof(addr)),
                "%s: address didn't match\n", debugstr_a(ipv6_tests[i].input));

        MultiByteToWideChar(CP_ACP, 0, ipv6_tests[i].input, -1, inputW, ARRAY_SIZE(inputW));
        WSASetLastError(0xdeadbeef);
        memset(addr, 0xab, sizeof(addr));
        ret = pInetPtonW(AF_INET6, inputW, addr);
        ok(ret == ipv6_tests[i].ret, "%s: got %d\n", debugstr_a(ipv6_tests[i].input), ret);
        ok(WSAGetLastError() == (ret ? 0xdeadbeef : WSAEINVAL), "%s: got error %u\n",
                debugstr_a(ipv6_tests[i].input), WSAGetLastError());
        ok(!memcmp(addr, ipv6_tests[i].addr, sizeof(addr)),
                "%s: address didn't match\n", debugstr_a(ipv6_tests[i].input));
    }
}

static void test_addr_to_print(void)
{
    char dst[16];
    char dst6[64];
    const char *pdst;
    struct in_addr in;
    struct in6_addr in6;

    u_long addr0_Num = 0x00000000;
    const char *addr0_Str = "0.0.0.0";
    u_long addr1_Num = 0x20201015;
    const char *addr1_Str = "21.16.32.32";
    u_char addr2_Num[16] = {0,0,0,0,0,0,0,0,0,0,0xff,0xfe,0xcC,0x98,0xbd,0x74};
    const char *addr2_Str = "::fffe:cc98:bd74";
    u_char addr3_Num[16] = {0x20,0x30,0xa4,0xb1};
    const char *addr3_Str = "2030:a4b1::";
    u_char addr4_Num[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0xcC,0x98,0xbd,0x74};
    const char *addr4_Str = "::204.152.189.116";

    /* Test IPv4 addresses */
    in.s_addr = addr0_Num;

    pdst = inet_ntoa(*((struct in_addr *)&in.s_addr));
    ok(pdst != NULL, "inet_ntoa failed %s\n", dst);
    ok(!strcmp(pdst, addr0_Str),"Address %s != %s\n", pdst, addr0_Str);

    /* Test that inet_ntoa and inet_ntop return the same value */
    in.S_un.S_addr = addr1_Num;
    pdst = inet_ntoa(*((struct in_addr *)&in.s_addr));
    ok(pdst != NULL, "inet_ntoa failed %s\n", dst);
    ok(!strcmp(pdst, addr1_Str),"Address %s != %s\n", pdst, addr1_Str);

    /* inet_ntop became available in Vista and Win2008 */
    if (!p_inet_ntop)
    {
        win_skip("InetNtop not present, not executing tests\n");
        return;
    }

    /* Second part of test */
    pdst = p_inet_ntop(AF_INET, &in.s_addr, dst, sizeof(dst));
    ok(pdst != NULL, "InetNtop failed %s\n", dst);
    ok(!strcmp(pdst, addr1_Str),"Address %s != %s\n", pdst, addr1_Str);

    /* Test invalid parm conditions */
    pdst = p_inet_ntop(1, (void *)&in.s_addr, dst, sizeof(dst));
    ok(pdst == NULL, "The pointer should not be returned (%p)\n", pdst);
    ok(WSAGetLastError() == WSAEAFNOSUPPORT, "Should be WSAEAFNOSUPPORT\n");

    /* Test Null destination */
    pdst = NULL;
    pdst = p_inet_ntop(AF_INET, &in.s_addr, NULL, sizeof(dst));
    ok(pdst == NULL, "The pointer should not be returned (%p)\n", pdst);
    ok(WSAGetLastError() == STATUS_INVALID_PARAMETER || WSAGetLastError() == WSAEINVAL /* Win7 */,
       "Should be STATUS_INVALID_PARAMETER or WSAEINVAL not 0x%x\n", WSAGetLastError());

    /* Test zero length passed */
    WSASetLastError(0);
    pdst = NULL;
    pdst = p_inet_ntop(AF_INET, &in.s_addr, dst, 0);
    ok(pdst == NULL, "The pointer should not be returned (%p)\n", pdst);
    ok(WSAGetLastError() == STATUS_INVALID_PARAMETER || WSAGetLastError() == WSAEINVAL /* Win7 */,
       "Should be STATUS_INVALID_PARAMETER or WSAEINVAL not 0x%x\n", WSAGetLastError());

    /* Test length one shorter than the address length */
    WSASetLastError(0);
    pdst = NULL;
    pdst = p_inet_ntop(AF_INET, &in.s_addr, dst, 6);
    ok(pdst == NULL, "The pointer should not be returned (%p)\n", pdst);
    ok(WSAGetLastError() == STATUS_INVALID_PARAMETER || WSAGetLastError() == WSAEINVAL /* Win7 */,
       "Should be STATUS_INVALID_PARAMETER or WSAEINVAL not 0x%x\n", WSAGetLastError());

    /* Test longer length is ok */
    WSASetLastError(0);
    pdst = NULL;
    pdst = p_inet_ntop(AF_INET, &in.s_addr, dst, sizeof(dst)+1);
    ok(pdst != NULL, "The pointer should  be returned (%p)\n", pdst);
    ok(!strcmp(pdst, addr1_Str),"Address %s != %s\n", pdst, addr1_Str);

    /* Test the IPv6 addresses */

    /* Test an zero prefixed IPV6 address */
    memcpy(in6.u.Byte, addr2_Num, sizeof(addr2_Num));
    pdst = p_inet_ntop(AF_INET6, &in6.s6_addr, dst6, sizeof(dst6));
    ok(pdst != NULL, "InetNtop failed %s\n", dst6);
    ok(!strcmp(pdst, addr2_Str),"Address %s != %s\n", pdst, addr2_Str);

    /* Test an zero suffixed IPV6 address */
    memcpy(in6.s6_addr, addr3_Num, sizeof(addr3_Num));
    pdst = p_inet_ntop(AF_INET6, &in6.s6_addr, dst6, sizeof(dst6));
    ok(pdst != NULL, "InetNtop failed %s\n", dst6);
    ok(!strcmp(pdst, addr3_Str),"Address %s != %s\n", pdst, addr3_Str);

    /* Test the IPv6 address contains the IPv4 address in IPv4 notation */
    memcpy(in6.s6_addr, addr4_Num, sizeof(addr4_Num));
    pdst = p_inet_ntop(AF_INET6, &in6.s6_addr, dst6, sizeof(dst6));
    ok(pdst != NULL, "InetNtop failed %s\n", dst6);
    ok(!strcmp(pdst, addr4_Str),"Address %s != %s\n", pdst, addr4_Str);

    /* Test invalid parm conditions */
    memcpy(in6.u.Byte, addr2_Num, sizeof(addr2_Num));

    /* Test Null destination */
    pdst = NULL;
    pdst = p_inet_ntop(AF_INET6, &in6.s6_addr, NULL, sizeof(dst6));
    ok(pdst == NULL, "The pointer should not be returned (%p)\n", pdst);
    ok(WSAGetLastError() == STATUS_INVALID_PARAMETER || WSAGetLastError() == WSAEINVAL /* Win7 */,
       "Should be STATUS_INVALID_PARAMETER or WSAEINVAL not 0x%x\n", WSAGetLastError());

    /* Test zero length passed */
    WSASetLastError(0);
    pdst = NULL;
    pdst = p_inet_ntop(AF_INET6, &in6.s6_addr, dst6, 0);
    ok(pdst == NULL, "The pointer should not be returned (%p)\n", pdst);
    ok(WSAGetLastError() == STATUS_INVALID_PARAMETER || WSAGetLastError() == WSAEINVAL /* Win7 */,
       "Should be STATUS_INVALID_PARAMETER or WSAEINVAL not 0x%x\n", WSAGetLastError());

    /* Test length one shorter than the address length */
    WSASetLastError(0);
    pdst = NULL;
    pdst = p_inet_ntop(AF_INET6, &in6.s6_addr, dst6, 16);
    ok(pdst == NULL, "The pointer should not be returned (%p)\n", pdst);
    ok(WSAGetLastError() == STATUS_INVALID_PARAMETER || WSAGetLastError() == WSAEINVAL /* Win7 */,
       "Should be STATUS_INVALID_PARAMETER or WSAEINVAL not 0x%x\n", WSAGetLastError());

    /* Test longer length is ok */
    WSASetLastError(0);
    pdst = NULL;
    pdst = p_inet_ntop(AF_INET6, &in6.s6_addr, dst6, 18);
    ok(pdst != NULL, "The pointer should be returned (%p)\n", pdst);
}

static void test_WSAAddressToString(void)
{
    static struct
    {
        ULONG address;
        USHORT port;
        char output[32];
    }
    ipv4_tests[] =
    {
        { 0, 0, "0.0.0.0" },
        { 0xffffffff, 0, "255.255.255.255" },
        { 0, 0xffff, "0.0.0.0:65535" },
        { 0xffffffff, 0xffff, "255.255.255.255:65535" },
    };
    static struct
    {
        USHORT address[8];
        ULONG scope;
        USHORT port;
        char output[64];
    }
    ipv6_tests[] =
    {
        { { 0, 0, 0, 0, 0, 0, 0, 0x100 }, 0, 0, "::1" },
        { { 0xab20, 0, 0, 0, 0, 0, 0, 0x100 }, 0, 0, "20ab::1" },
        { { 0xab20, 0, 0, 0, 0, 0, 0, 0x120 }, 0, 0xfa81, "[20ab::2001]:33274" },
        { { 0xab20, 0, 0, 0, 0, 0, 0, 0x120 }, 0x1234, 0xfa81, "[20ab::2001%4660]:33274" },
        { { 0xab20, 0, 0, 0, 0, 0, 0, 0x120 }, 0x1234, 0, "20ab::2001%4660" },
    };
    SOCKADDR_IN sockaddr;
    SOCKADDR_IN6 sockaddr6;
    char output[64];
    WCHAR outputW[64], expected_outputW[64];
    SOCKET v6;
    INT ret;
    DWORD len;
    int i, j;

    len = 0;
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_addr.s_addr = 0;
    sockaddr.sin_port = 0;
    WSASetLastError( 0xdeadbeef );
    ret = WSAAddressToStringA( (SOCKADDR *)&sockaddr, sizeof(sockaddr), NULL, output, &len );
    ok( ret == SOCKET_ERROR, "WSAAddressToStringA() returned %d, expected SOCKET_ERROR\n", ret );
    ok( WSAGetLastError() == WSAEFAULT, "WSAAddressToStringA() gave error %d, expected WSAEFAULT\n", WSAGetLastError() );
    ok( len == 8, "WSAAddressToStringA() gave length %d, expected 8\n", len );

    len = 0;
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_addr.s_addr = 0;
    sockaddr.sin_port = 0;
    WSASetLastError( 0xdeadbeef );
    ret = WSAAddressToStringW( (SOCKADDR *)&sockaddr, sizeof(sockaddr), NULL, NULL, &len );
    ok( ret == SOCKET_ERROR, "got %d\n", ret );
    ok( WSAGetLastError() == WSAEFAULT, "got %08x\n", WSAGetLastError() );
    ok( len == 8, "got %u\n", len );

    len = ARRAY_SIZE(outputW);
    memset( outputW, 0, sizeof(outputW) );
    ret = WSAAddressToStringW( (SOCKADDR *)&sockaddr, sizeof(sockaddr), NULL, outputW, &len );
    ok( !ret, "WSAAddressToStringW() returned %d\n", ret );
    ok( len == 8, "got %u\n", len );
    ok( !wcscmp(outputW, L"0.0.0.0"), "got %s\n", wine_dbgstr_w(outputW) );

    for (i = 0; i < 2; i++)
    {
        for (j = 0; j < ARRAY_SIZE(ipv4_tests); j++)
        {
            sockaddr.sin_family = AF_INET;
            sockaddr.sin_addr.s_addr = ipv4_tests[j].address;
            sockaddr.sin_port = ipv4_tests[j].port;

            if (i == 0)
            {
                len = sizeof(output);
                memset(output, 0, len);
                ret = WSAAddressToStringA( (SOCKADDR *)&sockaddr, sizeof(sockaddr), NULL, output, &len );
                ok( !ret, "ipv4_tests[%d] failed unexpectedly: %d\n", j, WSAGetLastError() );
                ok( !strcmp( output, ipv4_tests[j].output ),
                    "ipv4_tests[%d]: got address %s, expected %s\n",
                    j, wine_dbgstr_a(output), wine_dbgstr_a(ipv4_tests[j].output) );
                ok( len == strlen(ipv4_tests[j].output) + 1,
                    "ipv4_tests[%d]: got length %d, expected %d\n",
                    j, len, strlen(ipv4_tests[j].output) + 1 );
            }
            else
            {
                len = sizeof(outputW);
                memset(outputW, 0, len);
                ret = WSAAddressToStringW( (SOCKADDR *)&sockaddr, sizeof(sockaddr), NULL, outputW, &len );
                MultiByteToWideChar( CP_ACP, 0, ipv4_tests[j].output, -1,
                                     expected_outputW, ARRAY_SIZE(expected_outputW) );
                ok( !ret, "ipv4_tests[%d] failed unexpectedly: %d\n", j, WSAGetLastError() );
                ok( !wcscmp( outputW, expected_outputW ),
                    "ipv4_tests[%d]: got address %s, expected %s\n",
                    j, wine_dbgstr_w(outputW), wine_dbgstr_w(expected_outputW) );
                ok( len == wcslen(expected_outputW) + 1,
                    "ipv4_tests[%d]: got length %d, expected %d\n",
                    j, len, wcslen(expected_outputW) + 1 );
            }
        }

        /* check to see if IPv6 is available */
        v6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
        if (v6 == INVALID_SOCKET) {
            skip("Could not create IPv6 socket (LastError: %d; %d expected if IPv6 not available).\n",
                WSAGetLastError(), WSAEAFNOSUPPORT);
            continue;
        }
        closesocket(v6);

        for (j = 0; j < ARRAY_SIZE(ipv6_tests); j++)
        {
            sockaddr6.sin6_family = AF_INET6;
            sockaddr6.sin6_scope_id = ipv6_tests[j].scope;
            sockaddr6.sin6_port = ipv6_tests[j].port;
            memcpy( sockaddr6.sin6_addr.s6_addr, ipv6_tests[j].address, sizeof(ipv6_tests[j].address) );

            if (i == 0)
            {
                len = sizeof(output);
                ret = WSAAddressToStringA( (SOCKADDR *)&sockaddr6, sizeof(sockaddr6), NULL, output, &len );
                ok( !ret, "ipv6_tests[%d] failed unexpectedly: %d\n", j, WSAGetLastError() );
                ok( !strcmp( output, ipv6_tests[j].output ),
                    "ipv6_tests[%d]: gave address %s, expected %s\n",
                    j, wine_dbgstr_a(output), wine_dbgstr_a(ipv6_tests[j].output) );
                ok( len == strlen(ipv6_tests[j].output) + 1,
                    "ipv6_tests[%d]: got length %d, expected %d\n",
                    j, len, strlen(ipv6_tests[j].output) + 1 );
            }
            else
            {
                len = sizeof(outputW);
                ret = WSAAddressToStringW( (SOCKADDR *)&sockaddr6, sizeof(sockaddr6), NULL, outputW, &len );
                MultiByteToWideChar( CP_ACP, 0, ipv6_tests[j].output, -1,
                                     expected_outputW, ARRAY_SIZE(expected_outputW) );
                ok( !ret, "ipv6_tests[%d] failed unexpectedly: %d\n", j, WSAGetLastError() );
                ok( !wcscmp( outputW, expected_outputW ),
                    "ipv6_tests[%d]: got address %s, expected %s\n",
                    j, wine_dbgstr_w(outputW), wine_dbgstr_w(expected_outputW) );
                ok( len == wcslen(expected_outputW) + 1,
                    "ipv6_tests[%d]: got length %d, expected %d\n",
                    j, len, wcslen(expected_outputW) + 1 );
            }
        }
    }
}

static void test_WSAStringToAddress(void)
{
    static struct
    {
        char input[32];
        ULONG address;
        USHORT port;
        int error;
    }
    ipv4_tests[] =
    {
        { "",                       0,          0, WSAEINVAL },
        { " ",                      0,          0, WSAEINVAL },
        { "1.1.1.1",                0x01010101 },
        { "0.0.0.0",                0 },
        { "127.127.127.127",        0x7f7f7f7f },
        { "127.127.127.127:",       0x7f7f7f7f, 0, WSAEINVAL },
        { "127.127.127.127:0",      0x7f7f7f7f, 0, WSAEINVAL },
        { "127.127.127.127:123",    0x7f7f7f7f, 123 },
        { "127.127.127.127:65535",  0x7f7f7f7f, 65535 },
        { "127.127.127.127:65537",  0x7f7f7f7f, 0, WSAEINVAL },
        { "127.127.127.127::1",     0x7f7f7f7f, 0, WSAEINVAL },
        { "127.127.127.127:0xAbCd", 0x7f7f7f7f, 43981 },
        { "127.127.127.127:0XaBcD", 0x7f7f7f7f, 43981 },
        { "127.127.127.127:0x",     0x7f7f7f7f, 0, WSAEINVAL },
        { "127.127.127.127:0x10000",0x7f7f7f7f, 0, WSAEINVAL },
        { "127.127.127.127:010",    0x7f7f7f7f, 8 },
        { "127.127.127.127:007",    0x7f7f7f7f, 7 },
        { "127.127.127.127:08",     0x7f7f7f7f, 0, WSAEINVAL },
        { "127.127.127.127:008",    0x7f7f7f7f, 0, WSAEINVAL },
        { "127.127.127.127:0a",     0x7f7f7f7f, 0, WSAEINVAL },
        { "127.127.127.127:0o10",   0x7f7f7f7f, 0, WSAEINVAL },
        { "127.127.127.127:0b10",   0x7f7f7f7f, 0, WSAEINVAL },
        { "255.255.255.255",        0xffffffff },
        { "255.255.255.255:123",    0xffffffff, 123 },
        { "255.255.255.255:65535",  0xffffffff, 65535 },
        { "255.255.255.256",        0,          0, WSAEINVAL },
        { "a",                      0,          0, WSAEINVAL },
        { "1.1.1.0xaA",             0x010101aa },
        { "1.1.1.0XaA",             0x010101aa },
        { "1.1.1.0x",               0,          0, WSAEINVAL },
        { "1.1.1.0xff",             0x010101ff },
        { "1.1.1.0x100",            0,          0, WSAEINVAL },
        { "1.1.1.010",              0x01010108 },
        { "1.1.1.00",               0x01010100 },
        { "1.1.1.007",              0x01010107 },
        { "1.1.1.08",               0,          0, WSAEINVAL },
        { "1.1.1.008",              0x01010100, 0, WSAEINVAL },
        { "1.1.1.0a",               0x01010100, 0, WSAEINVAL },
        { "1.1.1.0o10",             0x01010100, 0, WSAEINVAL },
        { "1.1.1.0b10",             0x01010100, 0, WSAEINVAL },
        { "1.1.1.-2",               0,          0, WSAEINVAL },
        { "1",                      0x00000001 },
        { "-1",                     0,          0, WSAEINVAL },
        { "1:1",                    0x00000001, 1 },
        { "1.2",                    0x01000002 },
        { "1.2:1",                  0x01000002, 1 },
        { "1000.2000",              0,          0, WSAEINVAL },
        { "1.2.",                   0,          0, WSAEINVAL },
        { "1..2",                   0,          0, WSAEINVAL },
        { "1...2",                  0,          0, WSAEINVAL },
        { "1.2.3",                  0x01020003 },
        { "1.2.3.",                 0,          0, WSAEINVAL },
        { "1.2.3:1",                0x01020003, 1 },
        { "203569230",              0x0c22384e },
        { "203569230:123",          0x0c22384e, 123 },
        { "2001::1",                0x000007d1, 0, WSAEINVAL },
        { "1.223756",               0x01036a0c },
        { "3.4.756",                0x030402f4 },
        { "756.3.4",                0,          0, WSAEINVAL },
        { "3.756.4",                0,          0, WSAEINVAL },
        { "3.4.756.1",              0,          0, WSAEINVAL },
        { "3.4.65536",              0,          0, WSAEINVAL },
        { "3.4.5.6.7",              0,          0, WSAEINVAL },
        { "3.4.5.+6",               0,          0, WSAEINVAL },
        { " 3.4.5.6",               0,          0, WSAEINVAL },
        { "\t3.4.5.6",              0,          0, WSAEINVAL },
        { "3.4.5.6 ",               0x03040506, 0, WSAEINVAL },
        { "3.4.5.6:7 ",             0x03040506, 0, WSAEINVAL },
        { "3.4.5.6: 7",             0x03040506, 0, WSAEINVAL },
        { "3. 4.5.6",               0,          0, WSAEINVAL },
        { ".",                      0,          0, WSAEINVAL },
        { "[0.1.2.3]",              0,          0, WSAEINVAL },
        { "0x00010203",             0x00010203 },
        { "0X00010203",             0x00010203 },
        { "0x00010203:10",          0x00010203, 10 },
        { "0x1234",                 0x00001234 },
        { "0x123456789",            0x23456789 },
        { "0x00010Q03",             0x00000010, 0, WSAEINVAL },
        { "x00010203",              0,          0, WSAEINVAL },
        { "1234BEEF",               0x000004d2, 0, WSAEINVAL },
        { "017700000001",           0x7f000001 },
        { "0777",                   0x000001ff },
        { "0777:10",                0x000001ff, 10 },
        { "::1",                    0,          0, WSAEINVAL },
        { ":1",                     0,          0, WSAEINVAL },
    };
    static struct
    {
        char input[64];
        USHORT address[8];
        USHORT port;
        int error;
    }
    ipv6_tests[] =
    {
        { "::1", { 0, 0, 0, 0, 0, 0, 0, 0x100 } },
        { "[::1]", { 0, 0, 0, 0, 0, 0, 0, 0x100 } },
        { "[::1]:65535", { 0, 0, 0, 0, 0, 0, 0, 0x100 }, 0xffff },
        { "2001::1", { 0x120, 0, 0, 0, 0, 0, 0, 0x100 } },
        { "::1]:65535", { 0, 0, 0, 0, 0, 0, 0, 0x100 }, 0, WSAEINVAL },
        { "001::1", { 0x100, 0, 0, 0, 0, 0, 0, 0x100 } },
        { "::1:2:3:4:5:6:7", { 0, 0, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600 }, 0, WSAEINVAL }, /* Windows bug */
        { "1.2.3.4", { 0x201, 0x3, 0, 0, 0, 0, 0, 0 }, 0, WSAEINVAL },
        { "1:2:3:", { 0x100, 0x200, 0x300, 0, 0, 0, 0 }, 0, WSAEINVAL },
        { "", { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, WSAEINVAL },
    };

    WCHAR inputW[64];
    INT len, ret, expected_len, expected_ret;
    short expected_family;
    SOCKADDR_IN sockaddr;
    SOCKADDR_IN6 sockaddr6;
    int i, j;

    len = 0;
    WSASetLastError( 0 );
    ret = WSAStringToAddressA( ipv4_tests[0].input, AF_INET, NULL, (SOCKADDR *)&sockaddr, &len );
    ok( ret == SOCKET_ERROR, "WSAStringToAddressA() returned %d, expected SOCKET_ERROR\n", ret );
    ok( WSAGetLastError() == WSAEFAULT, "WSAStringToAddress() gave error %d, expected WSAEFAULT\n", WSAGetLastError() );
    ok( len >= sizeof(sockaddr) || broken(len == 0) /* xp */,
        "WSAStringToAddress() gave length %d, expected at least %d\n", len, sizeof(sockaddr) );

    for (i = 0; i < 2; i++)
    {
        for (j = 0; j < ARRAY_SIZE(ipv4_tests); j++)
        {
            len = sizeof(sockaddr) + 10;
            expected_len = ipv4_tests[j].error ? len : sizeof(sockaddr);
            memset( &sockaddr, 0xab, sizeof(sockaddr) );

            WSASetLastError( 0 );
            if (i == 0)
            {
                ret = WSAStringToAddressA( ipv4_tests[j].input, AF_INET, NULL, (SOCKADDR *)&sockaddr, &len );
            }
            else
            {
                MultiByteToWideChar( CP_ACP, 0, ipv4_tests[j].input, -1, inputW, ARRAY_SIZE(inputW) );
                ret = WSAStringToAddressW( inputW, AF_INET, NULL, (SOCKADDR *)&sockaddr, &len );
            }
            expected_ret = ipv4_tests[j].error ? SOCKET_ERROR : 0;
            expected_family = ipv4_tests[j].error ? 0 : AF_INET;
            ok( ret == expected_ret,
                "WSAStringToAddress(%s) returned %d, expected %d\n",
                wine_dbgstr_a( ipv4_tests[j].input ), ret, expected_ret );
            ok( WSAGetLastError() == ipv4_tests[j].error,
                "WSAStringToAddress(%s) gave error %d, expected %d\n",
                wine_dbgstr_a( ipv4_tests[j].input ), WSAGetLastError(), ipv4_tests[j].error );
            ok( sockaddr.sin_family == expected_family,
                "WSAStringToAddress(%s) gave family %d, expected %d\n",
                wine_dbgstr_a( ipv4_tests[j].input ), sockaddr.sin_family, expected_family );
            ok( sockaddr.sin_addr.s_addr == htonl( ipv4_tests[j].address ),
                "WSAStringToAddress(%s) gave address %08x, expected %08x\n",
                wine_dbgstr_a( ipv4_tests[j].input ), sockaddr.sin_addr.s_addr, htonl( ipv4_tests[j].address) );
            ok( sockaddr.sin_port == htons( ipv4_tests[j].port ),
                "WSAStringToAddress(%s) gave port %04x, expected %04x\n",
                wine_dbgstr_a( ipv4_tests[j].input ), sockaddr.sin_port, htons( ipv4_tests[j].port ) );
            ok( len == expected_len,
                "WSAStringToAddress(%s) gave length %d, expected %d\n",
                wine_dbgstr_a( ipv4_tests[j].input ), len, expected_len );
        }

        for (j = 0; j < ARRAY_SIZE(ipv6_tests); j++)
        {
            len = sizeof(sockaddr6) + 10;
            expected_len = ipv6_tests[j].error ? len : sizeof(sockaddr6);
            memset( &sockaddr6, 0xab, sizeof(sockaddr6) );

            WSASetLastError( 0 );
            if (i == 0)
            {
                ret = WSAStringToAddressA( ipv6_tests[j].input, AF_INET6, NULL, (SOCKADDR *)&sockaddr6, &len );
            }
            else
            {
                MultiByteToWideChar( CP_ACP, 0, ipv6_tests[j].input, -1, inputW, ARRAY_SIZE(inputW) );
                ret = WSAStringToAddressW( inputW, AF_INET6, NULL, (SOCKADDR *)&sockaddr6, &len );
            }
            if (j == 0 && ret == SOCKET_ERROR)
            {
                win_skip("IPv6 not supported\n");
                break;
            }
            expected_ret = ipv6_tests[j].error ? SOCKET_ERROR : 0;
            expected_family = ipv6_tests[j].error ? 0 : AF_INET6;
            ok( ret == expected_ret,
                "WSAStringToAddress(%s) returned %d, expected %d\n",
                wine_dbgstr_a( ipv6_tests[j].input ), ret, expected_ret );
            ok( WSAGetLastError() == ipv6_tests[j].error,
                "WSAStringToAddress(%s) gave error %d, expected %d\n",
                wine_dbgstr_a( ipv6_tests[j].input ), WSAGetLastError(), ipv6_tests[j].error );
            ok( sockaddr6.sin6_family == expected_family,
                "WSAStringToAddress(%s) gave family %d, expected %d\n",
                wine_dbgstr_a( ipv4_tests[j].input ), sockaddr6.sin6_family, expected_family );
            ok( memcmp(&sockaddr6.sin6_addr, ipv6_tests[j].address, sizeof(sockaddr6.sin6_addr)) == 0,
                "WSAStringToAddress(%s) gave address %x:%x:%x:%x:%x:%x:%x:%x, expected %x:%x:%x:%x:%x:%x:%x:%x\n",
                wine_dbgstr_a( ipv6_tests[j].input ),
                sockaddr6.sin6_addr.s6_words[0], sockaddr6.sin6_addr.s6_words[1],
                sockaddr6.sin6_addr.s6_words[2], sockaddr6.sin6_addr.s6_words[3],
                sockaddr6.sin6_addr.s6_words[4], sockaddr6.sin6_addr.s6_words[5],
                sockaddr6.sin6_addr.s6_words[6], sockaddr6.sin6_addr.s6_words[7],
                ipv6_tests[j].address[0], ipv6_tests[j].address[1],
                ipv6_tests[j].address[2], ipv6_tests[j].address[3],
                ipv6_tests[j].address[4], ipv6_tests[j].address[5],
                ipv6_tests[j].address[6], ipv6_tests[j].address[7] );
            ok( sockaddr6.sin6_scope_id == 0,
                "WSAStringToAddress(%s) gave scope %d, expected 0\n",
                wine_dbgstr_a( ipv6_tests[j].input ), sockaddr6.sin6_scope_id );
            ok( sockaddr6.sin6_port == ipv6_tests[j].port,
                "WSAStringToAddress(%s) gave port %04x, expected %04x\n",
                wine_dbgstr_a( ipv6_tests[j].input ), sockaddr6.sin6_port, ipv6_tests[j].port );
            ok( sockaddr6.sin6_flowinfo == 0,
                "WSAStringToAddress(%s) gave flowinfo %d, expected 0\n",
                wine_dbgstr_a( ipv6_tests[j].input ), sockaddr6.sin6_flowinfo );
            ok( len == expected_len,
                "WSAStringToAddress(%s) gave length %d, expected %d\n",
                wine_dbgstr_a( ipv6_tests[j].input ), len, expected_len );
        }
    }
}

/* Tests used in both getaddrinfo and GetAddrInfoW */
static const struct addr_hint_tests
{
    int family, socktype, protocol;
    DWORD error;
}
hinttests[] =
{
    {AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0},
    {AF_UNSPEC, SOCK_STREAM, IPPROTO_UDP, 0},
    {AF_UNSPEC, SOCK_STREAM, IPPROTO_IPV6,0},
    {AF_UNSPEC, SOCK_DGRAM,  IPPROTO_TCP, 0},
    {AF_UNSPEC, SOCK_DGRAM,  IPPROTO_UDP, 0},
    {AF_UNSPEC, SOCK_DGRAM,  IPPROTO_IPV6,0},
    {AF_INET,   SOCK_STREAM, IPPROTO_TCP, 0},
    {AF_INET,   SOCK_STREAM, IPPROTO_UDP, 0},
    {AF_INET,   SOCK_STREAM, IPPROTO_IPV6,0},
    {AF_INET,   SOCK_DGRAM,  IPPROTO_TCP, 0},
    {AF_INET,   SOCK_DGRAM,  IPPROTO_UDP, 0},
    {AF_INET,   SOCK_DGRAM,  IPPROTO_IPV6,0},
    {AF_UNSPEC, 0,           IPPROTO_TCP, 0},
    {AF_UNSPEC, 0,           IPPROTO_UDP, 0},
    {AF_UNSPEC, 0,           IPPROTO_IPV6,0},
    {AF_UNSPEC, SOCK_STREAM, 0,           0},
    {AF_UNSPEC, SOCK_DGRAM,  0,           0},
    {AF_INET,   0,           IPPROTO_TCP, 0},
    {AF_INET,   0,           IPPROTO_UDP, 0},
    {AF_INET,   0,           IPPROTO_IPV6,0},
    {AF_INET,   SOCK_STREAM, 0,           0},
    {AF_INET,   SOCK_DGRAM,  0,           0},
    {AF_UNSPEC, 999,         IPPROTO_TCP, WSAESOCKTNOSUPPORT},
    {AF_UNSPEC, 999,         IPPROTO_UDP, WSAESOCKTNOSUPPORT},
    {AF_UNSPEC, 999,         IPPROTO_IPV6,WSAESOCKTNOSUPPORT},
    {AF_INET,   999,         IPPROTO_TCP, WSAESOCKTNOSUPPORT},
    {AF_INET,   999,         IPPROTO_UDP, WSAESOCKTNOSUPPORT},
    {AF_INET,   999,         IPPROTO_IPV6,WSAESOCKTNOSUPPORT},
    {AF_UNSPEC, SOCK_STREAM, 999,         0},
    {AF_UNSPEC, SOCK_STREAM, 999,         0},
    {AF_INET,   SOCK_DGRAM,  999,         0},
    {AF_INET,   SOCK_DGRAM,  999,         0},
};

static void compare_addrinfow(ADDRINFOW *a, ADDRINFOW *b)
{
    for (; a && b; a = a->ai_next, b = b->ai_next)
    {
        ok(a->ai_flags == b->ai_flags,
           "Wrong flags %d != %d\n", a->ai_flags, b->ai_flags);
        ok(a->ai_family == b->ai_family,
           "Wrong family %d != %d\n", a->ai_family, b->ai_family);
        ok(a->ai_socktype == b->ai_socktype,
           "Wrong socktype %d != %d\n", a->ai_socktype, b->ai_socktype);
        ok(a->ai_protocol == b->ai_protocol,
           "Wrong protocol %d != %d\n", a->ai_protocol, b->ai_protocol);
        ok(a->ai_addrlen == b->ai_addrlen,
           "Wrong addrlen %lu != %lu\n", a->ai_addrlen, b->ai_addrlen);
        ok(!memcmp(a->ai_addr, b->ai_addr, min(a->ai_addrlen, b->ai_addrlen)),
           "Wrong address data\n");
        if (a->ai_canonname && b->ai_canonname)
        {
            ok(!lstrcmpW(a->ai_canonname, b->ai_canonname), "Wrong canonical name '%s' != '%s'\n",
               wine_dbgstr_w(a->ai_canonname), wine_dbgstr_w(b->ai_canonname));
        }
        else
            ok(!a->ai_canonname && !b->ai_canonname, "Expected both names absent (%p != %p)\n",
               a->ai_canonname, b->ai_canonname);
    }
    ok(!a && !b, "Expected both addresses null (%p != %p)\n", a, b);
}

static void test_GetAddrInfoW(void)
{
    static const WCHAR port[] = {'8','0',0};
    static const WCHAR empty[] = {0};
    static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
    static const WCHAR nxdomain[] =
        {'n','x','d','o','m','a','i','n','.','c','o','d','e','w','e','a','v','e','r','s','.','c','o','m',0};
    static const WCHAR zero[] = {'0',0};
    int i, ret;
    ADDRINFOW *result, *result2, *p, hint;
    WCHAR name[256];
    DWORD size = ARRAY_SIZE(name);
    /* te su to.winehq.org written in katakana */
    static const WCHAR idn_domain[] =
        {0x30C6,0x30B9,0x30C8,'.','w','i','n','e','h','q','.','o','r','g',0};
    static const WCHAR idn_punycode[] =
        {'x','n','-','-','z','c','k','z','a','h','.','w','i','n','e','h','q','.','o','r','g',0};

    memset(&hint, 0, sizeof(ADDRINFOW));
    name[0] = 0;
    GetComputerNameExW( ComputerNamePhysicalDnsHostname, name, &size );

    result = (ADDRINFOW *)0xdeadbeef;
    WSASetLastError(0xdeadbeef);
    ret = GetAddrInfoW(NULL, NULL, NULL, &result);
    ok(ret == WSAHOST_NOT_FOUND, "got %d expected WSAHOST_NOT_FOUND\n", ret);
    ok(WSAGetLastError() == WSAHOST_NOT_FOUND, "expected 11001, got %d\n", WSAGetLastError());
    ok(result == NULL, "got %p\n", result);

    result = NULL;
    WSASetLastError(0xdeadbeef);
    ret = GetAddrInfoW(empty, NULL, NULL, &result);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    ok(result != NULL, "GetAddrInfoW failed\n");
    ok(WSAGetLastError() == 0, "expected 0, got %d\n", WSAGetLastError());
    FreeAddrInfoW(result);

    result = NULL;
    ret = GetAddrInfoW(NULL, zero, NULL, &result);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    ok(result != NULL, "GetAddrInfoW failed\n");

    result2 = NULL;
    ret = GetAddrInfoW(NULL, empty, NULL, &result2);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    ok(result2 != NULL, "GetAddrInfoW failed\n");
    compare_addrinfow(result, result2);
    FreeAddrInfoW(result);
    FreeAddrInfoW(result2);

    result = NULL;
    ret = GetAddrInfoW(empty, zero, NULL, &result);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    ok(WSAGetLastError() == 0, "expected 0, got %d\n", WSAGetLastError());
    ok(result != NULL, "GetAddrInfoW failed\n");

    result2 = NULL;
    ret = GetAddrInfoW(empty, empty, NULL, &result2);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    ok(result2 != NULL, "GetAddrInfoW failed\n");
    compare_addrinfow(result, result2);
    FreeAddrInfoW(result);
    FreeAddrInfoW(result2);

    result = NULL;
    ret = GetAddrInfoW(localhost, NULL, NULL, &result);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    FreeAddrInfoW(result);

    result = NULL;
    ret = GetAddrInfoW(localhost, empty, NULL, &result);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    FreeAddrInfoW(result);

    result = NULL;
    ret = GetAddrInfoW(localhost, zero, NULL, &result);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    FreeAddrInfoW(result);

    result = NULL;
    ret = GetAddrInfoW(localhost, port, NULL, &result);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    FreeAddrInfoW(result);

    result = NULL;
    ret = GetAddrInfoW(localhost, NULL, &hint, &result);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    FreeAddrInfoW(result);

    result = NULL;
    SetLastError(0xdeadbeef);
    ret = GetAddrInfoW(localhost, port, &hint, &result);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    ok(WSAGetLastError() == 0, "expected 0, got %d\n", WSAGetLastError());
    FreeAddrInfoW(result);

    /* try to get information from the computer name, result is the same
     * as if requesting with an empty host name. */
    ret = GetAddrInfoW(name, NULL, NULL, &result);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    ok(result != NULL, "GetAddrInfoW failed\n");

    ret = GetAddrInfoW(empty, NULL, NULL, &result2);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    ok(result != NULL, "GetAddrInfoW failed\n");
    compare_addrinfow(result, result2);
    FreeAddrInfoW(result);
    FreeAddrInfoW(result2);

    ret = GetAddrInfoW(name, empty, NULL, &result);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    ok(result != NULL, "GetAddrInfoW failed\n");

    ret = GetAddrInfoW(empty, empty, NULL, &result2);
    ok(!ret, "GetAddrInfoW failed with %d\n", WSAGetLastError());
    ok(result != NULL, "GetAddrInfoW failed\n");
    compare_addrinfow(result, result2);
    FreeAddrInfoW(result);
    FreeAddrInfoW(result2);

    result = (ADDRINFOW *)0xdeadbeef;
    WSASetLastError(0xdeadbeef);
    ret = GetAddrInfoW(NULL, NULL, NULL, &result);
    if (ret == 0)
    {
        skip("nxdomain returned success. Broken ISP redirects?\n");
        return;
    }
    ok(ret == WSAHOST_NOT_FOUND, "got %d expected WSAHOST_NOT_FOUND\n", ret);
    ok(WSAGetLastError() == WSAHOST_NOT_FOUND, "expected 11001, got %d\n", WSAGetLastError());
    ok(result == NULL, "got %p\n", result);

    result = (ADDRINFOW *)0xdeadbeef;
    WSASetLastError(0xdeadbeef);
    ret = GetAddrInfoW(nxdomain, NULL, NULL, &result);
    if (ret == 0)
    {
        skip("nxdomain returned success. Broken ISP redirects?\n");
        return;
    }
    ok(ret == WSAHOST_NOT_FOUND, "got %d expected WSAHOST_NOT_FOUND\n", ret);
    ok(WSAGetLastError() == WSAHOST_NOT_FOUND, "expected 11001, got %d\n", WSAGetLastError());
    ok(result == NULL, "got %p\n", result);

    for (i = 0; i < ARRAY_SIZE(hinttests); i++)
    {
        hint.ai_family = hinttests[i].family;
        hint.ai_socktype = hinttests[i].socktype;
        hint.ai_protocol = hinttests[i].protocol;

        result = NULL;
        SetLastError(0xdeadbeef);
        ret = GetAddrInfoW(localhost, NULL, &hint, &result);
        ok(ret == hinttests[i].error, "test %d: wrong ret %d\n", i, ret);
        if (!ret)
        {
            for (p = result; p; p = p->ai_next)
            {
                /* when AF_UNSPEC is used the return will be either AF_INET or AF_INET6 */
                if (hinttests[i].family == AF_UNSPEC)
                    ok(p->ai_family == AF_INET || p->ai_family == AF_INET6,
                       "test %d: expected AF_INET or AF_INET6, got %d\n",
                       i, p->ai_family);
                else
                    ok(p->ai_family == hinttests[i].family,
                       "test %d: expected family %d, got %d\n",
                       i, hinttests[i].family, p->ai_family);

                ok(p->ai_socktype == hinttests[i].socktype,
                   "test %d: expected type %d, got %d\n",
                   i, hinttests[i].socktype, p->ai_socktype);
                ok(p->ai_protocol == hinttests[i].protocol,
                   "test %d: expected protocol %d, got %d\n",
                   i, hinttests[i].protocol, p->ai_protocol);
            }
            FreeAddrInfoW(result);
        }
        else
        {
            ok(WSAGetLastError() == hinttests[i].error, "test %d: wrong error %d\n", i, WSAGetLastError());
        }
    }

    /* Test IDN resolution (Internationalized Domain Names) present since Windows 8 */
    result = NULL;
    ret = GetAddrInfoW(idn_punycode, NULL, NULL, &result);
    ok(!ret, "got %d expected success\n", ret);
    ok(result != NULL, "got %p\n", result);
    FreeAddrInfoW(result);

    hint.ai_family = AF_INET;
    hint.ai_socktype = 0;
    hint.ai_protocol = 0;
    hint.ai_flags = 0;

    result = NULL;
    ret = GetAddrInfoW(idn_punycode, NULL, &hint, &result);
    ok(!ret, "got %d expected success\n", ret);
    ok(result != NULL, "got %p\n", result);

    result2 = NULL;
    ret = GetAddrInfoW(idn_domain, NULL, NULL, &result2);
    if (broken(ret == WSAHOST_NOT_FOUND))
    {
        FreeAddrInfoW(result);
        win_skip("IDN resolution not supported in Win <= 7\n");
        return;
    }

    ok(!ret, "got %d expected success\n", ret);
    ok(result2 != NULL, "got %p\n", result2);
    FreeAddrInfoW(result2);

    hint.ai_family = AF_INET;
    hint.ai_socktype = 0;
    hint.ai_protocol = 0;
    hint.ai_flags = 0;

    result2 = NULL;
    ret = GetAddrInfoW(idn_domain, NULL, &hint, &result2);
    ok(!ret, "got %d expected success\n", ret);
    ok(result2 != NULL, "got %p\n", result2);

    /* ensure manually resolved punycode and unicode hosts result in same data */
    compare_addrinfow(result, result2);

    FreeAddrInfoW(result);
    FreeAddrInfoW(result2);

    hint.ai_family = AF_INET;
    hint.ai_socktype = 0;
    hint.ai_protocol = 0;
    hint.ai_flags = 0;

    result2 = NULL;
    ret = GetAddrInfoW(idn_domain, NULL, &hint, &result2);
    ok(!ret, "got %d expected success\n", ret);
    ok(result2 != NULL, "got %p\n", result2);
    FreeAddrInfoW(result2);

    /* Disable IDN resolution and test again*/
    hint.ai_family = AF_INET;
    hint.ai_socktype = 0;
    hint.ai_protocol = 0;
    hint.ai_flags = AI_DISABLE_IDN_ENCODING;

    SetLastError(0xdeadbeef);
    result2 = NULL;
    ret = GetAddrInfoW(idn_domain, NULL, &hint, &result2);
    ok(ret == WSAHOST_NOT_FOUND, "got %d expected WSAHOST_NOT_FOUND\n", ret);
    ok(WSAGetLastError() == WSAHOST_NOT_FOUND, "expected 11001, got %d\n", WSAGetLastError());
    ok(result2 == NULL, "got %p\n", result2);
}

static struct completion_routine_test
{
    WSAOVERLAPPED  *overlapped;
    DWORD           error;
    ADDRINFOEXW   **result;
    HANDLE          event;
    DWORD           called;
} completion_routine_test;

static void CALLBACK completion_routine(DWORD error, DWORD byte_count, WSAOVERLAPPED *overlapped)
{
    struct completion_routine_test *test = &completion_routine_test;

    ok(error == test->error, "got %u\n", error);
    ok(!byte_count, "got %u\n", byte_count);
    ok(overlapped == test->overlapped, "got %p\n", overlapped);
    ok(overlapped->Internal == test->error, "got %lu\n", overlapped->Internal);
    ok(overlapped->Pointer == test->result, "got %p\n", overlapped->Pointer);
    ok(overlapped->hEvent == NULL, "got %p\n", overlapped->hEvent);

    test->called++;
    SetEvent(test->event);
}

static void test_GetAddrInfoExW(void)
{
    static const WCHAR empty[] = {0};
    static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
    static const WCHAR winehq[] = {'t','e','s','t','.','w','i','n','e','h','q','.','o','r','g',0};
    static const WCHAR nxdomain[] = {'n','x','d','o','m','a','i','n','.','w','i','n','e','h','q','.','o','r','g',0};
    ADDRINFOEXW *result, hints;
    OVERLAPPED overlapped;
    HANDLE event;
    int ret;

    if (!pGetAddrInfoExW || !pGetAddrInfoExOverlappedResult)
    {
        win_skip("GetAddrInfoExW and/or GetAddrInfoExOverlappedResult not present\n");
        return;
    }

    event = WSACreateEvent();

    result = (ADDRINFOEXW *)0xdeadbeef;
    WSASetLastError(0xdeadbeef);
    ret = pGetAddrInfoExW(NULL, NULL, NS_DNS, NULL, NULL, &result, NULL, NULL, NULL, NULL);
    ok(ret == WSAHOST_NOT_FOUND, "got %d expected WSAHOST_NOT_FOUND\n", ret);
    ok(WSAGetLastError() == WSAHOST_NOT_FOUND, "expected 11001, got %d\n", WSAGetLastError());
    ok(result == NULL, "got %p\n", result);

    result = NULL;
    WSASetLastError(0xdeadbeef);
    ret = pGetAddrInfoExW(empty, NULL, NS_DNS, NULL, NULL, &result, NULL, NULL, NULL, NULL);
    ok(!ret, "GetAddrInfoExW failed with %d\n", WSAGetLastError());
    ok(result != NULL, "GetAddrInfoW failed\n");
    ok(WSAGetLastError() == 0, "expected 0, got %d\n", WSAGetLastError());
    pFreeAddrInfoExW(result);

    result = NULL;
    ret = pGetAddrInfoExW(localhost, NULL, NS_DNS, NULL, NULL, &result, NULL, NULL, NULL, NULL);
    ok(!ret, "GetAddrInfoExW failed with %d\n", WSAGetLastError());
    pFreeAddrInfoExW(result);

    ret = pGetAddrInfoExOverlappedResult(NULL);
    ok(ret == WSAEINVAL, "overlapped result is %d\n", ret);

    result = (void *)0xdeadbeef;
    memset(&overlapped, 0xcc, sizeof(overlapped));
    overlapped.hEvent = event;
    ResetEvent(event);
    ret = pGetAddrInfoExW(localhost, NULL, NS_DNS, NULL, NULL, &result, NULL, &overlapped, NULL, NULL);
    ok(ret == ERROR_IO_PENDING, "GetAddrInfoExW failed with %d\n", WSAGetLastError());
    /* result pointer is cleared by GetAddrInfoExW(), but may be set to the
     * actual addrinfo before we can verify that */
    ok(result != (void *)0xdeadbeef, "result was not changed\n");
    ok(WaitForSingleObject(event, 1000) == WAIT_OBJECT_0, "wait failed\n");
    ret = pGetAddrInfoExOverlappedResult(&overlapped);
    ok(!ret, "overlapped result is %d\n", ret);
    pFreeAddrInfoExW(result);

    result = (void *)0xdeadbeef;
    memset(&overlapped, 0xcc, sizeof(overlapped));
    ResetEvent(event);
    overlapped.hEvent = event;
    WSASetLastError(0xdeadbeef);
    ret = pGetAddrInfoExW(winehq, NULL, NS_DNS, NULL, NULL, &result, NULL, &overlapped, NULL, NULL);
    ok(ret == ERROR_IO_PENDING, "GetAddrInfoExW failed with %d\n", WSAGetLastError());
    ok(WSAGetLastError() == ERROR_IO_PENDING, "expected 11001, got %d\n", WSAGetLastError());
    ret = overlapped.Internal;
    ok(ret == WSAEINPROGRESS || ret == ERROR_SUCCESS, "overlapped.Internal = %u\n", ret);
    ok(WaitForSingleObject(event, 1000) == WAIT_OBJECT_0, "wait failed\n");
    ret = pGetAddrInfoExOverlappedResult(&overlapped);
    ok(!ret, "overlapped result is %d\n", ret);
    ok(overlapped.hEvent == event, "hEvent changed %p\n", overlapped.hEvent);
    ok(overlapped.Internal == ERROR_SUCCESS, "overlapped.Internal = %lx\n", overlapped.Internal);
    ok(overlapped.Pointer == &result, "overlapped.Pointer != &result\n");
    ok(result != NULL, "result == NULL\n");
    ok(!result->ai_blob, "ai_blob != NULL\n");
    ok(!result->ai_bloblen, "ai_bloblen != 0\n");
    ok(!result->ai_provider, "ai_provider = %s\n", wine_dbgstr_guid(result->ai_provider));
    pFreeAddrInfoExW(result);

    /* hints */
    result = (void *)0xdeadbeef;
    memset(&overlapped, 0xcc, sizeof(overlapped));
    ResetEvent(event);
    overlapped.hEvent = event;
    WSASetLastError(0xdeadbeef);
    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = AI_ALL | AI_V4MAPPED;
    hints.ai_family = AF_INET6;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_protocol = IPPROTO_UDP;
    ret = pGetAddrInfoExW(winehq, NULL, NS_ALL, NULL, &hints, &result, NULL, &overlapped, NULL, NULL);
    ok(ret == ERROR_IO_PENDING, "GetAddrInfoExW failed with %d\n", WSAGetLastError());
    ok(WSAGetLastError() == ERROR_IO_PENDING, "expected 11001, got %d\n", WSAGetLastError());
    ret = overlapped.Internal;
    ok(ret == WSAEINPROGRESS || ret == ERROR_SUCCESS, "overlapped.Internal = %u\n", ret);
    ok(WaitForSingleObject(event, 1000) == WAIT_OBJECT_0, "wait failed\n");
    ret = pGetAddrInfoExOverlappedResult(&overlapped);
    ok(!ret, "overlapped result is %d\n", ret);
    ok(result != NULL, "result == NULL\n");
    pFreeAddrInfoExW(result);

    result = (void *)0xdeadbeef;
    memset(&overlapped, 0xcc, sizeof(overlapped));
    ResetEvent(event);
    overlapped.hEvent = event;
    ret = pGetAddrInfoExW(NULL, NULL, NS_DNS, NULL, NULL, &result, NULL, &overlapped, NULL, NULL);
    todo_wine
    ok(ret == WSAHOST_NOT_FOUND, "got %d expected WSAHOST_NOT_FOUND\n", ret);
    todo_wine
    ok(WSAGetLastError() == WSAHOST_NOT_FOUND, "expected 11001, got %d\n", WSAGetLastError());
    ok(result == NULL, "got %p\n", result);
    ret = WaitForSingleObject(event, 0);
    todo_wine_if(ret != WAIT_TIMEOUT) /* Remove when abowe todo_wines are fixed */
    ok(ret == WAIT_TIMEOUT, "wait failed\n");

    /* event + completion routine */
    result = (void *)0xdeadbeef;
    memset(&overlapped, 0xcc, sizeof(overlapped));
    overlapped.hEvent = event;
    ResetEvent(event);
    ret = pGetAddrInfoExW(localhost, NULL, NS_DNS, NULL, NULL, &result, NULL, &overlapped, completion_routine, NULL);
    ok(ret == WSAEINVAL, "GetAddrInfoExW failed with %d\n", WSAGetLastError());

    /* completion routine, existing domain */
    result = (void *)0xdeadbeef;
    overlapped.hEvent = NULL;
    completion_routine_test.overlapped = &overlapped;
    completion_routine_test.error = ERROR_SUCCESS;
    completion_routine_test.result = &result;
    completion_routine_test.event = event;
    completion_routine_test.called = 0;
    ResetEvent(event);
    ret = pGetAddrInfoExW(winehq, NULL, NS_DNS, NULL, NULL, &result, NULL, &overlapped, completion_routine, NULL);
    ok(ret == ERROR_IO_PENDING, "GetAddrInfoExW failed with %d\n", WSAGetLastError());
    ok(!result, "result != NULL\n");
    ok(WaitForSingleObject(event, 1000) == WAIT_OBJECT_0, "wait failed\n");
    ret = pGetAddrInfoExOverlappedResult(&overlapped);
    ok(!ret, "overlapped result is %d\n", ret);
    ok(overlapped.hEvent == NULL, "hEvent changed %p\n", overlapped.hEvent);
    ok(overlapped.Internal == ERROR_SUCCESS, "overlapped.Internal = %lx\n", overlapped.Internal);
    ok(overlapped.Pointer == &result, "overlapped.Pointer != &result\n");
    ok(completion_routine_test.called == 1, "got %u\n", completion_routine_test.called);
    pFreeAddrInfoExW(result);

    /* completion routine, non-existing domain */
    result = (void *)0xdeadbeef;
    completion_routine_test.overlapped = &overlapped;
    completion_routine_test.error = WSAHOST_NOT_FOUND;
    completion_routine_test.called = 0;
    ResetEvent(event);
    ret = pGetAddrInfoExW(nxdomain, NULL, NS_DNS, NULL, NULL, &result, NULL, &overlapped, completion_routine, NULL);
    ok(ret == ERROR_IO_PENDING, "GetAddrInfoExW failed with %d\n", WSAGetLastError());
    ok(!result, "result != NULL\n");
    ok(WaitForSingleObject(event, 1000) == WAIT_OBJECT_0, "wait failed\n");
    ret = pGetAddrInfoExOverlappedResult(&overlapped);
    ok(ret == WSAHOST_NOT_FOUND, "overlapped result is %d\n", ret);
    ok(overlapped.hEvent == NULL, "hEvent changed %p\n", overlapped.hEvent);
    ok(overlapped.Internal == WSAHOST_NOT_FOUND, "overlapped.Internal = %lx\n", overlapped.Internal);
    ok(overlapped.Pointer == &result, "overlapped.Pointer != &result\n");
    ok(completion_routine_test.called == 1, "got %u\n", completion_routine_test.called);
    ok(result == NULL, "got %p\n", result);

    WSACloseEvent(event);
}

static void verify_ipv6_addrinfo(ADDRINFOA *result, const char *expect)
{
    SOCKADDR_IN6 *sockaddr6;
    char buffer[256];
    const char *ret;

    ok(result->ai_family == AF_INET6, "ai_family == %d\n", result->ai_family);
    ok(result->ai_addrlen >= sizeof(struct sockaddr_in6), "ai_addrlen == %d\n", (int)result->ai_addrlen);
    ok(result->ai_addr != NULL, "ai_addr == NULL\n");

    sockaddr6 = (SOCKADDR_IN6 *)result->ai_addr;
    ok(sockaddr6->sin6_family == AF_INET6, "ai_addr->sin6_family == %d\n", sockaddr6->sin6_family);
    ok(sockaddr6->sin6_port == 0, "ai_addr->sin6_port == %d\n", sockaddr6->sin6_port);

    memset(buffer, 0, sizeof(buffer));
    ret = p_inet_ntop(AF_INET6, &sockaddr6->sin6_addr, buffer, sizeof(buffer));
    ok(ret != NULL, "inet_ntop failed (%d)\n", WSAGetLastError());
    ok(!strcmp(buffer, expect), "ai_addr->sin6_addr == '%s' (expected '%s')\n", buffer, expect);
}

static void compare_addrinfo(ADDRINFO *a, ADDRINFO *b)
{
    for (; a && b ; a = a->ai_next, b = b->ai_next)
    {
        ok(a->ai_flags == b->ai_flags,
           "Wrong flags %d != %d\n", a->ai_flags, b->ai_flags);
        ok(a->ai_family == b->ai_family,
           "Wrong family %d != %d\n", a->ai_family, b->ai_family);
        ok(a->ai_socktype == b->ai_socktype,
           "Wrong socktype %d != %d\n", a->ai_socktype, b->ai_socktype);
        ok(a->ai_protocol == b->ai_protocol,
           "Wrong protocol %d != %d\n", a->ai_protocol, b->ai_protocol);
        ok(a->ai_addrlen == b->ai_addrlen,
           "Wrong addrlen %lu != %lu\n", a->ai_addrlen, b->ai_addrlen);
        ok(!memcmp(a->ai_addr, b->ai_addr, min(a->ai_addrlen, b->ai_addrlen)),
           "Wrong address data\n");
        if (a->ai_canonname && b->ai_canonname)
        {
            ok(!strcmp(a->ai_canonname, b->ai_canonname), "Wrong canonical name '%s' != '%s'\n",
               a->ai_canonname, b->ai_canonname);
        }
        else
            ok(!a->ai_canonname && !b->ai_canonname, "Expected both names absent (%p != %p)\n",
               a->ai_canonname, b->ai_canonname);
    }
    ok(!a && !b, "Expected both addresses null (%p != %p)\n", a, b);
}

static void test_getaddrinfo(void)
{
    int i, ret;
    ADDRINFOA *result, *result2, *p, hint;
    SOCKADDR_IN *sockaddr;
    CHAR name[256], *ip;
    DWORD size = sizeof(name);

    memset(&hint, 0, sizeof(ADDRINFOA));
    GetComputerNameExA( ComputerNamePhysicalDnsHostname, name, &size );

    result = (ADDRINFOA *)0xdeadbeef;
    WSASetLastError(0xdeadbeef);
    ret = getaddrinfo(NULL, NULL, NULL, &result);
    ok(ret == WSAHOST_NOT_FOUND, "got %d expected WSAHOST_NOT_FOUND\n", ret);
    ok(WSAGetLastError() == WSAHOST_NOT_FOUND, "expected 11001, got %d\n", WSAGetLastError());
    ok(result == NULL, "got %p\n", result);

    result = NULL;
    WSASetLastError(0xdeadbeef);
    ret = getaddrinfo("", NULL, NULL, &result);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    ok(result != NULL, "getaddrinfo failed\n");
    ok(WSAGetLastError() == 0, "expected 0, got %d\n", WSAGetLastError());
    freeaddrinfo(result);

    result = NULL;
    ret = getaddrinfo(NULL, "0", NULL, &result);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    ok(result != NULL, "getaddrinfo failed\n");

    result2 = NULL;
    ret = getaddrinfo(NULL, "", NULL, &result2);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    ok(result2 != NULL, "getaddrinfo failed\n");
    compare_addrinfo(result, result2);
    freeaddrinfo(result);
    freeaddrinfo(result2);

    result = NULL;
    WSASetLastError(0xdeadbeef);
    ret = getaddrinfo("", "0", NULL, &result);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    ok(WSAGetLastError() == 0, "expected 0, got %d\n", WSAGetLastError());
    ok(result != NULL, "getaddrinfo failed\n");

    result2 = NULL;
    ret = getaddrinfo("", "", NULL, &result2);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    ok(result2 != NULL, "getaddrinfo failed\n");
    compare_addrinfo(result, result2);
    freeaddrinfo(result);
    freeaddrinfo(result2);

    result = NULL;
    ret = getaddrinfo("localhost", NULL, NULL, &result);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    freeaddrinfo(result);

    result = NULL;
    ret = getaddrinfo("localhost", "", NULL, &result);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    freeaddrinfo(result);

    result = NULL;
    ret = getaddrinfo("localhost", "0", NULL, &result);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    freeaddrinfo(result);

    result = NULL;
    ret = getaddrinfo("localhost", "80", NULL, &result);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    freeaddrinfo(result);

    result = NULL;
    ret = getaddrinfo("localhost", NULL, &hint, &result);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    freeaddrinfo(result);

    result = NULL;
    WSASetLastError(0xdeadbeef);
    ret = getaddrinfo("localhost", "80", &hint, &result);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    ok(WSAGetLastError() == 0, "expected 0, got %d\n", WSAGetLastError());
    freeaddrinfo(result);

    hint.ai_flags = AI_NUMERICHOST;
    result = (void *)0xdeadbeef;
    ret = getaddrinfo("localhost", "80", &hint, &result);
    ok(ret == WSAHOST_NOT_FOUND, "getaddrinfo failed with %d\n", WSAGetLastError());
    ok(WSAGetLastError() == WSAHOST_NOT_FOUND, "expected WSAHOST_NOT_FOUND, got %d\n", WSAGetLastError());
    ok(!result, "result = %p\n", result);
    hint.ai_flags = 0;

    /* try to get information from the computer name, result is the same
     * as if requesting with an empty host name. */
    ret = getaddrinfo(name, NULL, NULL, &result);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    ok(result != NULL, "GetAddrInfoW failed\n");

    ret = getaddrinfo("", NULL, NULL, &result2);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    ok(result != NULL, "GetAddrInfoW failed\n");
    compare_addrinfo(result, result2);
    freeaddrinfo(result);
    freeaddrinfo(result2);

    ret = getaddrinfo(name, "", NULL, &result);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    ok(result != NULL, "GetAddrInfoW failed\n");

    ret = getaddrinfo("", "", NULL, &result2);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    ok(result != NULL, "GetAddrInfoW failed\n");
    compare_addrinfo(result, result2);
    freeaddrinfo(result);
    freeaddrinfo(result2);

    result = (ADDRINFOA *)0xdeadbeef;
    WSASetLastError(0xdeadbeef);
    ret = getaddrinfo("nxdomain.codeweavers.com", NULL, NULL, &result);
    if (ret == 0)
    {
        skip("nxdomain returned success. Broken ISP redirects?\n");
        return;
    }
    ok(ret == WSAHOST_NOT_FOUND, "got %d expected WSAHOST_NOT_FOUND\n", ret);
    ok(WSAGetLastError() == WSAHOST_NOT_FOUND, "expected 11001, got %d\n", WSAGetLastError());
    ok(result == NULL, "got %p\n", result);

    /* Test IPv4 address conversion */
    result = NULL;
    ret = getaddrinfo("192.168.1.253", NULL, NULL, &result);
    ok(!ret, "getaddrinfo failed with %d\n", ret);
    ok(result->ai_family == AF_INET, "ai_family == %d\n", result->ai_family);
    ok(result->ai_addrlen >= sizeof(struct sockaddr_in), "ai_addrlen == %d\n", (int)result->ai_addrlen);
    ok(result->ai_addr != NULL, "ai_addr == NULL\n");
    sockaddr = (SOCKADDR_IN *)result->ai_addr;
    ok(sockaddr->sin_family == AF_INET, "ai_addr->sin_family == %d\n", sockaddr->sin_family);
    ok(sockaddr->sin_port == 0, "ai_addr->sin_port == %d\n", sockaddr->sin_port);

    ip = inet_ntoa(sockaddr->sin_addr);
    ok(strcmp(ip, "192.168.1.253") == 0, "sockaddr->ai_addr == '%s'\n", ip);
    freeaddrinfo(result);

    /* Test IPv4 address conversion with port */
    result = NULL;
    hint.ai_flags = AI_NUMERICHOST;
    ret = getaddrinfo("192.168.1.253:1024", NULL, &hint, &result);
    hint.ai_flags = 0;
    ok(ret == WSAHOST_NOT_FOUND, "getaddrinfo returned unexpected result: %d\n", ret);
    ok(result == NULL, "expected NULL, got %p\n", result);

    /* Test IPv6 address conversion */
    result = NULL;
    SetLastError(0xdeadbeef);
    ret = getaddrinfo("2a00:2039:dead:beef:cafe::6666", NULL, NULL, &result);

    if (result != NULL)
    {
        ok(!ret, "getaddrinfo failed with %d\n", ret);
        verify_ipv6_addrinfo(result, "2a00:2039:dead:beef:cafe::6666");
        freeaddrinfo(result);

        /* Test IPv6 address conversion with brackets */
        result = NULL;
        ret = getaddrinfo("[beef::cafe]", NULL, NULL, &result);
        ok(!ret, "getaddrinfo failed with %d\n", ret);
        verify_ipv6_addrinfo(result, "beef::cafe");
        freeaddrinfo(result);

        /* Test IPv6 address conversion with brackets and hints */
        memset(&hint, 0, sizeof(ADDRINFOA));
        hint.ai_flags = AI_NUMERICHOST;
        hint.ai_family = AF_INET6;
        result = NULL;
        ret = getaddrinfo("[beef::cafe]", NULL, &hint, &result);
        ok(!ret, "getaddrinfo failed with %d\n", ret);
        verify_ipv6_addrinfo(result, "beef::cafe");
        freeaddrinfo(result);

        memset(&hint, 0, sizeof(ADDRINFOA));
        hint.ai_flags = AI_NUMERICHOST;
        hint.ai_family = AF_INET;
        result = NULL;
        ret = getaddrinfo("[beef::cafe]", NULL, &hint, &result);
        ok(ret == WSAHOST_NOT_FOUND, "getaddrinfo failed with %d\n", ret);

        /* Test IPv6 address conversion with brackets and port */
        result = NULL;
        ret = getaddrinfo("[beef::cafe]:10239", NULL, NULL, &result);
        ok(!ret, "getaddrinfo failed with %d\n", ret);
        verify_ipv6_addrinfo(result, "beef::cafe");
        freeaddrinfo(result);

        /* Test IPv6 address conversion with unmatched brackets */
        result = NULL;
        hint.ai_flags = AI_NUMERICHOST;
        ret = getaddrinfo("[beef::cafe", NULL, &hint, &result);
        ok(ret == WSAHOST_NOT_FOUND, "getaddrinfo failed with %d\n", ret);

        ret = getaddrinfo("beef::cafe]", NULL, &hint, &result);
        ok(ret == WSAHOST_NOT_FOUND, "getaddrinfo failed with %d\n", ret);
    }
    else
    {
        ok(ret == WSAHOST_NOT_FOUND, "getaddrinfo failed with %d\n", ret);
        win_skip("getaddrinfo does not support IPV6\n");
    }

    hint.ai_flags = 0;

    for (i = 0; i < ARRAY_SIZE(hinttests); i++)
    {
        hint.ai_family = hinttests[i].family;
        hint.ai_socktype = hinttests[i].socktype;
        hint.ai_protocol = hinttests[i].protocol;

        result = NULL;
        SetLastError(0xdeadbeef);
        ret = getaddrinfo("localhost", NULL, &hint, &result);
        ok(ret == hinttests[i].error, "test %d: wrong ret %d\n", i, ret);
        if (!ret)
        {
            for (p = result; p; p = p->ai_next)
            {
                /* when AF_UNSPEC is used the return will be either AF_INET or AF_INET6 */
                if (hinttests[i].family == AF_UNSPEC)
                    ok(p->ai_family == AF_INET || p->ai_family == AF_INET6,
                       "test %d: expected AF_INET or AF_INET6, got %d\n",
                       i, p->ai_family);
                else
                    ok(p->ai_family == hinttests[i].family,
                       "test %d: expected family %d, got %d\n",
                       i, hinttests[i].family, p->ai_family);

                ok(p->ai_socktype == hinttests[i].socktype,
                   "test %d: expected type %d, got %d\n",
                   i, hinttests[i].socktype, p->ai_socktype);
                ok(p->ai_protocol == hinttests[i].protocol,
                   "test %d: expected protocol %d, got %d\n",
                   i, hinttests[i].protocol, p->ai_protocol);
            }
            freeaddrinfo(result);
        }
        else
        {
            ok(WSAGetLastError() == hinttests[i].error, "test %d: wrong error %d\n", i, WSAGetLastError());
        }
    }

    memset(&hint, 0, sizeof(hint));
    ret = getaddrinfo(NULL, "nonexistentservice", &hint, &result);
    ok(ret == WSATYPE_NOT_FOUND, "got %d\n", ret);

    result = NULL;
    memset(&hint, 0, sizeof(hint));
    hint.ai_flags    = AI_ALL | AI_DNS_ONLY;
    hint.ai_family   = AF_UNSPEC;
    hint.ai_socktype = SOCK_STREAM;
    hint.ai_protocol = IPPROTO_TCP;
    ret = getaddrinfo("winehq.org", NULL, &hint, &result);
    ok(!ret, "getaddrinfo failed with %d\n", WSAGetLastError());
    ok(!result->ai_flags, "ai_flags == %08x\n", result->ai_flags);
    ok(result->ai_family == AF_INET, "ai_family == %d\n", result->ai_family);
    ok(result->ai_socktype == SOCK_STREAM, "ai_socktype == %d\n", result->ai_socktype);
    ok(result->ai_protocol == IPPROTO_TCP, "ai_protocol == %d\n", result->ai_protocol);
    ok(!result->ai_canonname, "ai_canonname == %s\n", result->ai_canonname);
    ok(result->ai_addrlen == sizeof(struct sockaddr_in), "ai_addrlen == %d\n", (int)result->ai_addrlen);
    ok(result->ai_addr != NULL, "ai_addr == NULL\n");
    sockaddr = (SOCKADDR_IN *)result->ai_addr;
    ok(sockaddr->sin_family == AF_INET, "ai_addr->sin_family == %d\n", sockaddr->sin_family);
    ok(sockaddr->sin_port == 0, "ai_addr->sin_port == %d\n", sockaddr->sin_port);
    freeaddrinfo(result);
}

static void test_dns(void)
{
    struct hostent *h;
    union
    {
        char *chr;
        void *mem;
    } addr;
    char **ptr;
    int count;

    h = gethostbyname("");
    ok(h != NULL, "gethostbyname(\"\") failed with %d\n", h_errno);

    /* Use an address with valid alias names if possible */
    h = gethostbyname("source.winehq.org");
    if (!h)
    {
        skip("Can't test the hostent structure because gethostbyname failed\n");
        return;
    }

    /* The returned struct must be allocated in a very strict way. First we need to
     * count how many aliases there are because they must be located right after
     * the struct hostent size. Knowing the amount of aliases we know the exact
     * location of the first IP returned. Rule valid for >= XP, for older OS's
     * it's somewhat the opposite. */
    addr.mem = h + 1;
    if (h->h_addr_list == addr.mem) /* <= W2K */
    {
        win_skip("Skipping hostent tests since this OS is unsupported\n");
        return;
    }

    ok(h->h_aliases == addr.mem,
       "hostent->h_aliases should be in %p, it is in %p\n", addr.mem, h->h_aliases);

    for (ptr = h->h_aliases, count = 1; *ptr; ptr++) count++;
    addr.chr += sizeof(*ptr) * count;
    ok(h->h_addr_list == addr.mem,
       "hostent->h_addr_list should be in %p, it is in %p\n", addr.mem, h->h_addr_list);

    for (ptr = h->h_addr_list, count = 1; *ptr; ptr++) count++;

    addr.chr += sizeof(*ptr) * count;
    ok(h->h_addr_list[0] == addr.mem,
       "hostent->h_addr_list[0] should be in %p, it is in %p\n", addr.mem, h->h_addr_list[0]);
}

static void test_gethostbyname(void)
{
    struct hostent *he;
    struct in_addr **addr_list;
    char name[256], first_ip[16];
    int ret, i, count;
    MIB_IPFORWARDTABLE *routes = NULL;
    IP_ADAPTER_INFO *adapters = NULL, *k;
    DWORD adap_size = 0, route_size = 0;
    BOOL found_default = FALSE;
    BOOL local_ip = FALSE;

    ret = gethostname(name, sizeof(name));
    ok(ret == 0, "gethostname() call failed: %d\n", WSAGetLastError());

    he = gethostbyname(name);
    ok(he != NULL, "gethostbyname(\"%s\") failed: %d\n", name, WSAGetLastError());
    addr_list = (struct in_addr **)he->h_addr_list;
    strcpy(first_ip, inet_ntoa(*addr_list[0]));

    if (winetest_debug > 1) trace("List of local IPs:\n");
    for (count = 0; addr_list[count] != NULL; count++)
    {
        char *ip = inet_ntoa(*addr_list[count]);
        if (!strcmp(ip, "127.0.0.1"))
            local_ip = TRUE;
        if (winetest_debug > 1) trace("%s\n", ip);
    }

    if (local_ip)
    {
        ok(count == 1, "expected 127.0.0.1 to be the only IP returned\n");
        skip("Only the loopback address is present, skipping tests\n");
        return;
    }

    ret = GetAdaptersInfo(NULL, &adap_size);
    ok(ret  == ERROR_BUFFER_OVERFLOW, "GetAdaptersInfo failed with a different error: %d\n", ret);
    ret = GetIpForwardTable(NULL, &route_size, FALSE);
    ok(ret == ERROR_INSUFFICIENT_BUFFER, "GetIpForwardTable failed with a different error: %d\n", ret);

    adapters = HeapAlloc(GetProcessHeap(), 0, adap_size);
    routes = HeapAlloc(GetProcessHeap(), 0, route_size);

    ret = GetAdaptersInfo(adapters, &adap_size);
    ok(ret  == NO_ERROR, "GetAdaptersInfo failed, error: %d\n", ret);
    ret = GetIpForwardTable(routes, &route_size, FALSE);
    ok(ret == NO_ERROR, "GetIpForwardTable failed, error: %d\n", ret);

    /* This test only has meaning if there is more than one IP configured */
    if (adapters->Next == NULL && count == 1)
    {
        skip("Only one IP is present, skipping tests\n");
        goto cleanup;
    }

    for (i = 0; !found_default && i < routes->dwNumEntries; i++)
    {
        /* default route (ip 0.0.0.0) ? */
        if (routes->table[i].dwForwardDest) continue;

        for (k = adapters; k != NULL; k = k->Next)
        {
            char *ip;

            if (k->Index != routes->table[i].dwForwardIfIndex) continue;

            /* the first IP returned from gethostbyname must be a default route */
            ip = k->IpAddressList.IpAddress.String;
            if (!strcmp(first_ip, ip))
            {
                found_default = TRUE;
                break;
            }
        }
    }
    ok(found_default, "failed to find the first IP from gethostbyname!\n");

cleanup:
    HeapFree(GetProcessHeap(), 0, adapters);
    HeapFree(GetProcessHeap(), 0, routes);
}

static void test_gethostbyname_hack(void)
{
    struct hostent *he;
    char name[256];
    static BYTE loopback[] = {127, 0, 0, 1};
    static BYTE magic_loopback[] = {127, 12, 34, 56};
    int ret;

    ret = gethostname(name, 256);
    ok(ret == 0, "gethostname() call failed: %d\n", WSAGetLastError());

    he = gethostbyname("localhost");
    ok(he != NULL, "gethostbyname(\"localhost\") failed: %d\n", h_errno);
    if (he->h_length != 4)
    {
        skip("h_length is %d, not IPv4, skipping test.\n", he->h_length);
        return;
    }
    ok(!memcmp(he->h_addr_list[0], loopback, he->h_length),
       "gethostbyname(\"localhost\") returned %u.%u.%u.%u\n",
       he->h_addr_list[0][0], he->h_addr_list[0][1], he->h_addr_list[0][2],
       he->h_addr_list[0][3]);

    if (!strcmp(name, "localhost"))
    {
        skip("hostname seems to be \"localhost\", skipping test.\n");
        return;
    }

    he = gethostbyname(name);
    ok(he != NULL, "gethostbyname(\"%s\") failed: %d\n", name, h_errno);
    if (he->h_length != 4)
    {
        skip("h_length is %d, not IPv4, skipping test.\n", he->h_length);
        return;
    }

    if (he->h_addr_list[0][0] == 127)
    {
        ok(memcmp(he->h_addr_list[0], magic_loopback, he->h_length) == 0,
           "gethostbyname(\"%s\") returned %u.%u.%u.%u not 127.12.34.56\n",
           name, he->h_addr_list[0][0], he->h_addr_list[0][1],
           he->h_addr_list[0][2], he->h_addr_list[0][3]);
    }

    gethostbyname("nonexistent.winehq.org");
    /* Don't check for the return value, as some braindead ISPs will kindly
     * resolve nonexistent host names to addresses of the ISP's spam pages. */
}

static void test_gethostname(void)
{
    struct hostent *he;
    char name[256];
    int ret, len;

    WSASetLastError(0xdeadbeef);
    ret = gethostname(NULL, 256);
    ok(ret == -1, "gethostname() returned %d\n", ret);
    ok(WSAGetLastError() == WSAEFAULT, "gethostname with null buffer "
            "failed with %d, expected %d\n", WSAGetLastError(), WSAEFAULT);

    ret = gethostname(name, sizeof(name));
    ok(ret == 0, "gethostname() call failed: %d\n", WSAGetLastError());
    he = gethostbyname(name);
    ok(he != NULL, "gethostbyname(\"%s\") failed: %d\n", name, WSAGetLastError());

    len = strlen(name);
    WSASetLastError(0xdeadbeef);
    strcpy(name, "deadbeef");
    ret = gethostname(name, len);
    ok(ret == -1, "gethostname() returned %d\n", ret);
    ok(!strcmp(name, "deadbeef"), "name changed unexpected!\n");
    ok(WSAGetLastError() == WSAEFAULT, "gethostname with insufficient length "
            "failed with %d, expected %d\n", WSAGetLastError(), WSAEFAULT);

    len++;
    ret = gethostname(name, len);
    ok(ret == 0, "gethostname() call failed: %d\n", WSAGetLastError());
    he = gethostbyname(name);
    ok(he != NULL, "gethostbyname(\"%s\") failed: %d\n", name, WSAGetLastError());
}

static void test_GetHostNameW(void)
{
    WCHAR name[256];
    int ret, len;

    if (!pGetHostNameW)
    {
        win_skip("GetHostNameW() not present\n");
        return;
    }

    WSASetLastError(0xdeadbeef);
    ret = pGetHostNameW(NULL, 256);
    ok(ret == -1, "GetHostNameW() returned %d\n", ret);
    ok(WSAGetLastError() == WSAEFAULT, "GetHostNameW with null buffer "
            "failed with %d, expected %d\n", WSAGetLastError(), WSAEFAULT);

    ret = pGetHostNameW(name, sizeof(name));
    ok(ret == 0, "GetHostNameW() call failed: %d\n", WSAGetLastError());

    len = wcslen(name);
    WSASetLastError(0xdeadbeef);
    wcscpy(name, L"deadbeef");
    ret = pGetHostNameW(name, len);
    ok(ret == -1, "GetHostNameW() returned %d\n", ret);
    ok(!wcscmp(name, L"deadbeef"), "name changed unexpected!\n");
    ok(WSAGetLastError() == WSAEFAULT, "GetHostNameW with insufficient length "
            "failed with %d, expected %d\n", WSAGetLastError(), WSAEFAULT);

    len++;
    ret = pGetHostNameW(name, len);
    ok(ret == 0, "GetHostNameW() call failed: %d\n", WSAGetLastError());
}

static void test_WSAEnumNameSpaceProvidersA(void)
{
    WSANAMESPACE_INFOA *name = NULL;
    DWORD ret, error, len = 0;

    SetLastError(0xdeadbeef);
    ret = WSAEnumNameSpaceProvidersA(&len, name);
    error = WSAGetLastError();
todo_wine
    ok(ret == SOCKET_ERROR, "Expected failure, got %u\n", ret);
todo_wine
    ok(error == WSAEFAULT, "Expected 10014, got %u\n", error);

    /* Invalid parameter tests */
    SetLastError(0xdeadbeef);
    ret = WSAEnumNameSpaceProvidersA(NULL, name);
    error = WSAGetLastError();
todo_wine
    ok(ret == SOCKET_ERROR, "Expected failure, got %u\n", ret);
todo_wine
    ok(error == WSAEFAULT, "Expected 10014, got %u\n", error);

    SetLastError(0xdeadbeef);
    ret = WSAEnumNameSpaceProvidersA(NULL, NULL);
    error = WSAGetLastError();
todo_wine
    ok(ret == SOCKET_ERROR, "Expected failure, got %u\n", ret);
todo_wine
    ok(error == WSAEFAULT, "Expected 10014, got %u\n", error);

    SetLastError(0xdeadbeef);
    ret = WSAEnumNameSpaceProvidersA(&len, NULL);
    error = WSAGetLastError();
todo_wine
    ok(ret == SOCKET_ERROR, "Expected failure, got %u\n", ret);
todo_wine
    ok(error == WSAEFAULT, "Expected 10014, got %u\n", error);

    name = HeapAlloc(GetProcessHeap(), 0, len);

    ret = WSAEnumNameSpaceProvidersA(&len, name);
todo_wine
    ok(ret > 0, "Expected more than zero name space providers\n");

    HeapFree(GetProcessHeap(), 0, name);
}

static void test_WSAEnumNameSpaceProvidersW(void)
{
    WSANAMESPACE_INFOW *name = NULL;
    DWORD ret, error, len = 0, i;

    SetLastError(0xdeadbeef);
    ret = WSAEnumNameSpaceProvidersW(&len, name);
    error = WSAGetLastError();
todo_wine
    ok(ret == SOCKET_ERROR, "Expected failure, got %u\n", ret);
todo_wine
    ok(error == WSAEFAULT, "Expected 10014, got %u\n", error);

    /* Invalid parameter tests */
    SetLastError(0xdeadbeef);
    ret = WSAEnumNameSpaceProvidersW(NULL, name);
    error = WSAGetLastError();
todo_wine
    ok(ret == SOCKET_ERROR, "Expected failure, got %u\n", ret);
todo_wine
    ok(error == WSAEFAULT, "Expected 10014, got %u\n", error);

    SetLastError(0xdeadbeef);
    ret = WSAEnumNameSpaceProvidersW(NULL, NULL);
    error = WSAGetLastError();
todo_wine
    ok(ret == SOCKET_ERROR, "Expected failure, got %u\n", ret);
todo_wine
    ok(error == WSAEFAULT, "Expected 10014, got %u\n", error);

    SetLastError(0xdeadbeef);
    ret = WSAEnumNameSpaceProvidersW(&len, NULL);
    error = WSAGetLastError();
todo_wine
    ok(ret == SOCKET_ERROR, "Expected failure, got %u\n", ret);
todo_wine
    ok(error == WSAEFAULT, "Expected 10014, got %u\n", error);

    name = HeapAlloc(GetProcessHeap(), 0, len);

    ret = WSAEnumNameSpaceProvidersW(&len, name);
todo_wine
    ok(ret > 0, "Expected more than zero name space providers\n");

    if (winetest_debug > 1)
    {
        for (i = 0; i < ret; i++)
        {
            trace("Name space Identifier (%p): %s\n", name[i].lpszIdentifier,
                   wine_dbgstr_w(name[i].lpszIdentifier));
            switch (name[i].dwNameSpace)
            {
                case NS_DNS:
                    trace("\tName space ID: NS_DNS (%u)\n", name[i].dwNameSpace);
                    break;
                case NS_NLA:
                    trace("\tName space ID: NS_NLA (%u)\n", name[i].dwNameSpace);
                    break;
                default:
                    trace("\tName space ID: Unknown (%u)\n", name[i].dwNameSpace);
                    break;
            }
            trace("\tActive:  %d\n", name[i].fActive);
            trace("\tVersion: %d\n", name[i].dwVersion);
        }
    }

    HeapFree(GetProcessHeap(), 0, name);
}

static void test_WSCGetProviderInfo(void)
{
    int ret;
    int errcode;
    GUID provider = {0};
    BYTE info[1];
    size_t len = 0;

    if (!pWSCGetProviderInfo)
    {
        skip("WSCGetProviderInfo is not available.\n");
        return;
    }

    ret = pWSCGetProviderInfo(NULL, -1, NULL, NULL, 0, NULL);
    ok(ret == SOCKET_ERROR, "got %d, expected SOCKET_ERROR\n", ret);

    errcode = 0xdeadbeef;
    ret = pWSCGetProviderInfo(NULL, ProviderInfoLspCategories, info, &len, 0, &errcode);
    ok(ret == SOCKET_ERROR, "got %d, expected SOCKET_ERROR\n", ret);
    ok(errcode == WSAEFAULT, "got %d, expected WSAEFAULT\n", errcode);

    errcode = 0xdeadbeef;
    ret = pWSCGetProviderInfo(&provider, -1, info, &len, 0, &errcode);
    ok(ret == SOCKET_ERROR, "got %d, expected SOCKET_ERROR\n", ret);
    ok(errcode == WSANO_RECOVERY, "got %d, expected WSANO_RECOVERY\n", errcode);

    errcode = 0xdeadbeef;
    ret = pWSCGetProviderInfo(&provider, ProviderInfoLspCategories, NULL, &len, 0, &errcode);
    ok(ret == SOCKET_ERROR, "got %d, expected SOCKET_ERROR\n", ret);
    ok(errcode == WSANO_RECOVERY, "got %d, expected WSANO_RECOVERY\n", errcode);

    errcode = 0xdeadbeef;
    ret = pWSCGetProviderInfo(&provider, ProviderInfoLspCategories, info, NULL, 0, &errcode);
    ok(ret == SOCKET_ERROR, "got %d, expected SOCKET_ERROR\n", ret);
    ok(errcode == WSANO_RECOVERY, "got %d, expected WSANO_RECOVERY\n", errcode);

    errcode = 0xdeadbeef;
    ret = pWSCGetProviderInfo(&provider, ProviderInfoLspCategories, info, &len, 0, &errcode);
    ok(ret == SOCKET_ERROR, "got %d, expected SOCKET_ERROR\n", ret);
    ok(errcode == WSANO_RECOVERY, "got %d, expected WSANO_RECOVERY\n", errcode);
}

static void test_WSCGetProviderPath(void)
{
    GUID provider = {0};
    WCHAR buffer[256];
    INT ret, err, len;

    ret = WSCGetProviderPath(NULL, NULL, NULL, NULL);
    ok(ret == SOCKET_ERROR, "Got unexpected ret %d.\n", ret);

    ret = WSCGetProviderPath(&provider, NULL, NULL, NULL);
    ok(ret == SOCKET_ERROR, "Got unexpected ret %d.\n", ret);

    ret = WSCGetProviderPath(NULL, buffer, NULL, NULL);
    ok(ret == SOCKET_ERROR, "Got unexpected ret %d.\n", ret);

    len = -1;
    ret = WSCGetProviderPath(NULL, NULL, &len, NULL);
    ok(ret == SOCKET_ERROR, "Got unexpected ret %d.\n", ret);
    ok(len == -1, "Got unexpected len %d.\n", len);

    err = 0;
    ret = WSCGetProviderPath(NULL, NULL, NULL, &err);
    ok(ret == SOCKET_ERROR, "Got unexpected ret %d.\n", ret);
    ok(err == WSAEFAULT, "Got unexpected error %d.\n", err);

    err = 0;
    ret = WSCGetProviderPath(&provider, NULL, NULL, &err);
    ok(ret == SOCKET_ERROR, "Got unexpected ret %d.\n", ret);
    ok(err == WSAEFAULT, "Got unexpected error %d.\n", err);

    err = 0;
    len = -1;
    ret = WSCGetProviderPath(&provider, NULL, &len, &err);
    ok(ret == SOCKET_ERROR, "Got unexpected ret %d.\n", ret);
    ok(err == WSAEINVAL, "Got unexpected error %d.\n", err);
    ok(len == -1, "Got unexpected len %d.\n", len);

    err = 0;
    len = 256;
    ret = WSCGetProviderPath(&provider, NULL, &len, &err);
    todo_wine ok(ret == SOCKET_ERROR, "Got unexpected ret %d.\n", ret);
    todo_wine ok(err == WSAEINVAL, "Got unexpected error %d.\n", err);
    ok(len == 256, "Got unexpected len %d.\n", len);

    /* Valid pointers and length but invalid GUID */
    err = 0;
    len = 256;
    ret = WSCGetProviderPath(&provider, buffer, &len, &err);
    todo_wine ok(ret == SOCKET_ERROR, "Got unexpected ret %d.\n", ret);
    todo_wine ok(err == WSAEINVAL, "Got unexpected error %d.\n", err);
    ok(len == 256, "Got unexpected len %d.\n", len);
}

static void test_startup(void)
{
    unsigned int i;
    WSADATA data;
    int ret;

    static const struct
    {
        WORD version;
        WORD ret_version;
    }
    tests[] =
    {
        {MAKEWORD(0, 0), MAKEWORD(2, 2)},
        {MAKEWORD(0, 1), MAKEWORD(2, 2)},
        {MAKEWORD(1, 0), MAKEWORD(1, 0)},
        {MAKEWORD(1, 1), MAKEWORD(1, 1)},
        {MAKEWORD(1, 2), MAKEWORD(1, 1)},
        {MAKEWORD(1, 0xff), MAKEWORD(1, 1)},
        {MAKEWORD(2, 0), MAKEWORD(2, 0)},
        {MAKEWORD(2, 1), MAKEWORD(2, 1)},
        {MAKEWORD(2, 2), MAKEWORD(2, 2)},
        {MAKEWORD(2, 3), MAKEWORD(2, 2)},
        {MAKEWORD(2, 0xff), MAKEWORD(2, 2)},
        {MAKEWORD(3, 0), MAKEWORD(2, 2)},
        {MAKEWORD(0xff, 0), MAKEWORD(2, 2)},
    };

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        winetest_push_context("Version %#x", tests[i].version);

        memset(&data, 0xcc, sizeof(data));
        data.lpVendorInfo = (void *)0xdeadbeef;
        ret = WSAStartup(tests[i].version, &data);
        ok(ret == (LOBYTE(tests[i].version) ? 0 : WSAVERNOTSUPPORTED), "got %d\n", ret);
        ok(data.wVersion == tests[i].ret_version, "got version %#x\n", data.wVersion);
        if (!ret)
        {
            ret = WSAStartup(tests[i].version, &data);
            ok(!ret, "got %d\n", ret);

            WSASetLastError(0xdeadbeef);
            ret = WSACleanup();
            ok(!ret, "got %d\n", ret);
            todo_wine ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError());

            WSASetLastError(0xdeadbeef);
            ret = WSACleanup();
            ok(!ret, "got %d\n", ret);
            todo_wine ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError());
        }
        ok(data.lpVendorInfo == (void *)0xdeadbeef, "got vendor info %p\n", data.lpVendorInfo);
        ok(data.wHighVersion == 0x202, "got maximum version %#x\n", data.wHighVersion);
        ok(!strcmp(data.szDescription, "WinSock 2.0"), "got description %s\n", debugstr_a(data.szDescription));
        ok(!strcmp(data.szSystemStatus, "Running"), "got status %s\n", debugstr_a(data.szSystemStatus));
        ok(data.iMaxSockets == (LOBYTE(tests[i].version) == 1 ? 32767 : 0), "got maximum sockets %u\n", data.iMaxSockets);
        ok(data.iMaxUdpDg == (LOBYTE(tests[i].version) == 1 ? 65467 : 0), "got maximum datagram size %u\n", data.iMaxUdpDg);

        WSASetLastError(0xdeadbeef);
        ret = WSACleanup();
        ok(ret == -1, "got %d\n", ret);
        ok(WSAGetLastError() == WSANOTINITIALISED, "got error %u\n", WSAGetLastError());

        ret = WSAStartup(tests[i].version, NULL);
        ok(ret == (LOBYTE(tests[i].version) ? WSAEFAULT : WSAVERNOTSUPPORTED), "got %d\n", ret);

        winetest_pop_context();
    }
}

START_TEST( protocol )
{
    WSADATA data;
    int ret;

    pFreeAddrInfoExW = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "FreeAddrInfoExW");
    pGetAddrInfoExOverlappedResult = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "GetAddrInfoExOverlappedResult");
    pGetAddrInfoExW = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "GetAddrInfoExW");
    pGetHostNameW = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "GetHostNameW");
    p_inet_ntop = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "inet_ntop");
    pInetNtopW = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "InetNtopW");
    p_inet_pton = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "inet_pton");
    pInetPtonW = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "InetPtonW");
    pWSCGetProviderInfo = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "WSCGetProviderInfo");

    ret = WSAStartup(0x202, &data);
    ok(!ret, "got %d\n", ret);

    test_WSAEnumProtocolsA();
    test_WSAEnumProtocolsW();
    test_getprotobyname();
    test_getprotobynumber();

    test_getservbyname();
    test_WSALookupService();

    test_inet_ntoa();
    test_inet_pton();
    test_addr_to_print();
    test_WSAAddressToString();
    test_WSAStringToAddress();

    test_GetAddrInfoW();
    test_GetAddrInfoExW();
    test_getaddrinfo();

    test_dns();
    test_gethostbyname();
    test_gethostbyname_hack();
    test_gethostname();
    test_GetHostNameW();

    test_WSAEnumNameSpaceProvidersA();
    test_WSAEnumNameSpaceProvidersW();
    test_WSCGetProviderInfo();
    test_WSCGetProviderPath();

    WSACleanup();

    /* These tests are finnicky. If WSAStartup() is ever called with a
     * version below 2.2, it causes getaddrinfo() to behave differently. */

    test_startup();

    /* And if WSAAsyncGetServBy*() is ever called, it somehow causes
     * WSAStartup() to succeed with 0.1 instead of failing. */

    ret = WSAStartup(0x202, &data);
    ok(!ret, "got %d\n", ret);

    test_WSAAsyncGetServByPort();
    test_WSAAsyncGetServByName();
}
