// ----------------------------------------------------------------------------
// testxnet.cpp
//
// Copyright (C) Microsoft Corporation
// ----------------------------------------------------------------------------

#include "xnp.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <vlan.h>

// ---------------------------------------------------------------------------------------
// Utilities
// ---------------------------------------------------------------------------------------

#define Verify(x)       do { if (!(x)) DbgBreak(); } while (0)

struct CDiscReq
{
    BYTE    abNonce[8];         // Nonce of the requester
};

struct CDiscRsp : public CDiscReq
{
    XNKID   xnkid;              // The host's key identifier
    XNKEY   xnkey;              // The host's key
    XNADDR  xnaddr;             // The host's address
};

ULONG WSAAPI inet_addr(const char * pch)
{
    return(((CXNet *)0)->inet_addr(pch));
}

// ---------------------------------------------------------------------------------------
// CTestXnIp
// ---------------------------------------------------------------------------------------

class CTestXnIp : public CXnIp
{

public:
    
    virtual void Startup(char * pszXbox, char * pszIp, char * pszMask, char * pszGateway, DWORD dwFlags)
    {
        XNetParams xnp = { sizeof(XNetStartupParams) };
        xnp.cfgFlags = (BYTE)(XNET_STARTUP_MANUAL_CONFIG|dwFlags);
        XNetInitParams xnip = { &xnp, pszXbox };
        Verify(IpInit(&xnip) == 0);

        XNetConfigParams xncp = { 0 };
        xncp.ina.s_addr = pszIp ? inet_addr(pszIp) : 0;
        xncp.inaMask.s_addr = pszMask ? inet_addr(pszMask) : 0;
        xncp.inaGateway.s_addr = pszGateway ? inet_addr(pszGateway) : 0;
        Verify(IpConfig(&xncp, XNET_CONFIG_NORMAL) == 0);

        IpStart();
    }

    virtual void Init()
    {
        Startup(NULL, NULL, NULL, NULL, 0);
    }

    virtual void Term()
    {
        RaiseToDpc();
        IpTerm();
    }

    void Go(HANDLE hEvent1, HANDLE hEvent2)
    {
        Init();
        if (hEvent1) SetEvent(hEvent1);
        if (hEvent2) Verify(WaitForSingleObject(hEvent2, INFINITE) == WAIT_OBJECT_0);
        Test();
        if (hEvent1) SetEvent(hEvent1);
        if (hEvent2) Verify(WaitForSingleObject(hEvent2, INFINITE) == WAIT_OBJECT_0);
        Term();
    }

    void UdpRecv(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen) {};
    void TcpRecv(CPacket * ppkt, CIpHdr * pIpHdr, CTcpHdr * pTcpHdr, UINT cbHdrLen, UINT cbLen) {};
    void SockReset(CIpAddr ipa) {};

    virtual void Test() = 0;
};

#define IPTEST_BEG(_name, pszXbox, pszIp, pszMask, pszGateway, dwFlags) \
    class CTest##_name : public CTestXnIp { \
    virtual void Init() { Startup(pszXbox, pszIp, pszMask, pszGateway, dwFlags); } \
    virtual void Test()
#define IPTEST_END(_name) \
    }; void Test_##_name(HANDLE hEvent1 = 0, HANDLE hEvent2 = 0) \
    { CTest##_name * p = new CTest##_name; p->Go(hEvent1, hEvent2); delete p; }

// ---------------------------------------------------------------------------------------
// CTestXNet
// ---------------------------------------------------------------------------------------

HANDLE  g_hEventXb1ToXb2;
HANDLE  g_hEventXb2ToXb1;
XNADDR  g_xnaddrXb1;
XNADDR  g_xnaddrXb2;
XNKID   g_xnkidXb1ToXb2;
XNKEY   g_xnkeyXb1ToXb2;
BOOL    g_fXb1ToXb2Timeout;
UINT    g_cSecsXb1ToXb2Connect;

class CTestXNet : public CXNet
{
public:
    
    CTestXNet(char * pszXbox = NULL) : CXNet(pszXbox) {}

    virtual void Startup(char * pszIp, char * pszMask, char * pszGateway, DWORD dwFlags)
    {
        XNetStartupParams xnp = { sizeof(XNetStartupParams) };
        xnp.cfgFlags = (BYTE)(XNET_STARTUP_MANUAL_CONFIG|dwFlags);
        Verify(XNetStartup(&xnp) == 0);

        XNetConfigParams xncp = { 0 };
        xncp.ina.s_addr = pszIp ? inet_addr(pszIp) : 0;
        xncp.inaMask.s_addr = pszMask ? inet_addr(pszMask) : 0;
        xncp.inaGateway.s_addr = pszGateway ? inet_addr(pszGateway) : 0;
        Verify(XNetConfig(&xncp, XNET_CONFIG_NORMAL) == 0);

        WSADATA WSAData;
        Verify(WSAStartup(0x0200, &WSAData) == 0);
    }

    virtual void Init()
    {
        Startup(NULL, NULL, NULL, 0);
    }

    virtual void Term()
    {
        WSACleanup();
        XNetCleanup();
    }

    void Go(HANDLE hEvent1, HANDLE hEvent2)
    {
        Init();
        if (hEvent1) SetEvent(hEvent1);
        if (hEvent2) Verify(WaitForSingleObject(hEvent2, INFINITE) == WAIT_OBJECT_0);
        Test();
        if (hEvent1) SetEvent(hEvent1);
        if (hEvent2) Verify(WaitForSingleObject(hEvent2, INFINITE) == WAIT_OBJECT_0);
        Term();
    }

    virtual void Test() = 0;

    SOCKET  SockUdpCreate(CIpPort ipport = 0, BOOL fBroadcast = FALSE);
    void    SockUdpTransmit(SOCKET s, CIpAddr ipaDst, CIpPort ipport, UINT cbMax = UDP_MAXIMUM_MSS);
    void    SockUdpReflect(SOCKET s);
    SOCKET  SockTcpCreate(CIpPort ipport = 0);
    void    SockTcpTransmit(SOCKET s, UINT cbMax = TCP_MAXIMUM_MSS);
    void    SockTcpReflect(SOCKET s);
    void    DiscoveryHost(UINT cClient, XNKID * pxnkid);
    void    DiscoveryClient(XNKID * pxnkid, CIpAddr * pipa);
    void    SgTcpXbToXbClient();
    void    SgTcpXbToXbServer();

};

SOCKET CTestXNet::SockUdpCreate(CIpPort ipport, BOOL fBroadcast)
{
    SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    Verify(s != SOCKET_ERROR);

    if (ipport)
    {
        sockaddr_in sin;
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = 0;
        sin.sin_port = ipport;
        Verify(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
    }

    if (fBroadcast)
    {
        char cBroadcast = 1;
        Verify(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &cBroadcast, 1) == 0);
    }

    return(s);
}

void CTestXNet::SockUdpTransmit(SOCKET s, CIpAddr ipaDst, CIpPort ipport, UINT cbMax)
{
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = ipaDst;
    sin.sin_port = ipport;
    
    BYTE abXmit[1500], abRecv[1500];

    if (cbMax > sizeof(abXmit))
        cbMax = sizeof(abXmit);

    int cb;

    for (cb = 1; cb < (int)cbMax; ++cb)
    {
        XNetRandom(abXmit, cb);
        Verify(sendto(s, (char *)abXmit, cb, 0, (struct sockaddr *)&sin, sizeof(sin)) == cb);
        Verify(recv(s, (char *)abRecv, sizeof(abRecv), 0) == cb);
        Verify(memcmp(abXmit, abRecv, cb) == 0);
        if ((cb % 200) == 0) printf(".");
    }

    for (; cb > 0; --cb)
    {
        XNetRandom(abXmit, cb);
        Verify(sendto(s, (char *)abXmit, cb, 0, (struct sockaddr *)&sin, sizeof(sin)) == cb);
        Verify(recv(s, (char *)abRecv, sizeof(abRecv), 0) == cb);
        Verify(memcmp(abXmit, abRecv, cb) == 0);
        if ((cb % 200) == 0) printf(".");
    }

    Verify(sendto(s, (char *)abXmit, 0, 0, (struct sockaddr *)&sin, sizeof(sin)) == 0);
    Verify(recv(s, (char *)abRecv, sizeof(abRecv), 0) == 0);
}

void CTestXNet::SockUdpReflect(SOCKET s)
{
    BYTE abRecv[1500];

    sockaddr_in sin;
    int cb;

    while (1)
    {
        int slen = sizeof(sin);
        cb = recvfrom(s, (char *)abRecv, sizeof(abRecv), 0, (struct sockaddr *)&sin, &slen);
        Verify(cb != SOCKET_ERROR);
        Verify(sendto(s, (char *)abRecv, cb, 0, (struct sockaddr *)&sin, sizeof(sin)) == cb);
        if (cb == 0)
            break;
    }
}

SOCKET CTestXNet::SockTcpCreate(CIpPort ipport)
{
    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    Verify(s != SOCKET_ERROR);

    if (ipport)
    {
        sockaddr_in sin;
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = 0;
        sin.sin_port = ipport;
        Verify(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
    }

    return(s);
}

void CTestXNet::SockTcpTransmit(SOCKET s, UINT cbMax)
{
    BYTE abXmit[1500], abRecv[1500];

    if (cbMax > sizeof(abXmit))
        cbMax = sizeof(abXmit);

    int cb;

    for (cb = 1; cb < (int)cbMax; ++cb)
    {
        XNetRandom(abXmit, cb);
        Verify(send(s, (char *)abXmit, cb, 0) == cb);
        Verify(recv(s, (char *)abRecv, sizeof(abRecv), 0) == cb);
        Verify(memcmp(abXmit, abRecv, cb) == 0);
        if ((cb % 200) == 0) printf(".");
    }

    for (; cb > 0; --cb)
    {
        XNetRandom(abXmit, cb);
        Verify(send(s, (char *)abXmit, cb, 0) == cb);
        Verify(recv(s, (char *)abRecv, sizeof(abRecv), 0) == cb);
        Verify(memcmp(abXmit, abRecv, cb) == 0);
        if ((cb % 200) == 0) printf(".");
    }

    Verify(shutdown(s, SD_SEND) != SOCKET_ERROR);
}

void CTestXNet::SockTcpReflect(SOCKET s)
{
    BYTE abRecv[1500];
    int cb;

    while (1)
    {
        cb = recv(s, (char *)abRecv, sizeof(abRecv), 0);
        Verify(cb != SOCKET_ERROR);

        if (cb == 0)
        {
            Verify(shutdown(s, SD_SEND) != SOCKET_ERROR);
            break;
        }

        Verify(send(s, (char *)abRecv, cb, 0) == cb);
    }
}

void CTestXNet::DiscoveryHost(UINT cClient, XNKID * pxnkid)
{
    SOCKET s = SockUdpCreate(NTOHS(1234), TRUE);

    CDiscReq dreq;
    CDiscRsp drsp;
    UINT iClient = 0;
    sockaddr_in sin;
    BYTE * pbNonce = (BYTE *)LocalAlloc(LPTR, sizeof(dreq.abNonce) * cClient);
    Verify(pbNonce != NULL);
    XNADDR xnaddr;
    XNKID xnkid;
    XNKEY xnkey;

    Verify(XNetCreateKey(&drsp.xnkid, &drsp.xnkey) == 0);
    Verify(XNetRegisterKey(&drsp.xnkid, &drsp.xnkey) == 0);
    Verify(XNetGetTitleXnAddr(&drsp.xnaddr) != 0);

    XNetRandom(drsp.abNonce, sizeof(drsp.abNonce));

    while (iClient < cClient)
    {
        Verify(recv(s, (char *)&dreq, sizeof(dreq), 0) == sizeof(dreq));

        for (UINT i = 0; i < iClient; ++i)
        {
            if (memcmp(&pbNonce[i * sizeof(dreq.abNonce)], dreq.abNonce, sizeof(dreq.abNonce)) == 0)
                break;
        }

        if (i == iClient)
        {
            memcpy(&pbNonce[i * sizeof(dreq.abNonce)], dreq.abNonce, sizeof(dreq.abNonce));
            iClient += 1;
        }

        memcpy(drsp.abNonce, dreq.abNonce, sizeof(dreq.abNonce));
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = INADDR_BROADCAST;
        sin.sin_port = NTOHS(1234);
        Verify(sendto(s, (char *)&drsp, sizeof(drsp), 0, (struct sockaddr *)&sin, sizeof(sin)) == sizeof(drsp));
    }

    Verify(closesocket(s) == 0);
    LocalFree(pbNonce);
    *pxnkid = drsp.xnkid;
}

void CTestXNet::DiscoveryClient(XNKID * pxnkid, CIpAddr * pipa)
{
    SOCKET s = SockUdpCreate(NTOHS(1234));

    char cBroadcast = 1;
    sockaddr_in sin;
    CDiscReq dreq;
    CDiscRsp drsp;

    XNetRandom(dreq.abNonce, sizeof(dreq.abNonce));

    Verify(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &cBroadcast, 1) == 0);

    while (1)
    {
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = INADDR_BROADCAST;
        sin.sin_port = NTOHS(1234);
        Verify(sendto(s, (char *)&dreq, sizeof(dreq), 0, (struct sockaddr *)&sin, sizeof(sin)) == sizeof(dreq));

        fd_set  fds;
        TIMEVAL tv;
        FD_ZERO(&fds);
        FD_SET(s, &fds);
        tv.tv_sec  = 0;
        tv.tv_usec = 200000;

        INT iResult = select(0, &fds, NULL, NULL, &tv);
        Verify(iResult != SOCKET_ERROR);

        while (1)
        {
            ULONG ul = 0;
            iResult = ioctlsocket(s, FIONREAD, &ul);
            Verify(iResult != SOCKET_ERROR);

            if (ul == 0)
                break;

            Verify(recv(s, (char *)&drsp, sizeof(drsp), 0) == sizeof(drsp));
            if (memcmp(drsp.abNonce, dreq.abNonce, sizeof(drsp.abNonce)) == 0)
                goto done;
        }
    }

done:

    Verify(XNetRegisterKey(&drsp.xnkid, &drsp.xnkey) == 0);
    Verify(XNetXnAddrToInAddr(&drsp.xnaddr, &drsp.xnkid, (IN_ADDR *)pipa) == 0);
    Verify(closesocket(s) == 0);
    *pxnkid = drsp.xnkid;
}

void CTestXNet::SgTcpXbToXbClient()
{
    while (XNetGetTitleXnAddr(&g_xnaddrXb1) == XNET_GET_XNADDR_PENDING)
        Sleep(100);

    IN_ADDR inaVip, inaSg, inaXb2;
    HANDLE hEventLogon;

    inaVip.s_addr = inet_addr("10.0.0.2");
    Verify(XNetServerToInAddr(inaVip, 0, &inaSg) == 0);
    Verify((hEventLogon = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL);
    GetXn()->IpLogon(inaSg.s_addr, NULL, hEventLogon);
    Verify(WaitForSingleObject(hEventLogon, INFINITE) == WAIT_OBJECT_0);
    Verify(GetXn()->IpLogonGetStatus(NULL) == XN_LOGON_STATE_ONLINE);

    Verify(WaitForSingleObject(g_hEventXb1ToXb2, INFINITE) == WAIT_OBJECT_0);

    Verify(XNetRegisterKey(&g_xnkidXb1ToXb2, &g_xnkeyXb1ToXb2) == 0);
    Verify(XNetXnAddrToInAddr(&g_xnaddrXb2, &g_xnkidXb1ToXb2, &inaXb2) == 0);

    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    Verify(s != SOCKET_ERROR);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inaXb2.s_addr;
    sin.sin_port = HTONS(80);
    DWORD dwTick = GetTickCount();
    INT err = connect(s, (struct sockaddr *)&sin, sizeof(sin));
    dwTick = GetTickCount() - dwTick;
    g_cSecsXb1ToXb2Connect = (dwTick + 500) / 1000;

    if (err == SOCKET_ERROR)
    {
        g_fXb1ToXb2Timeout = TRUE;
    }

    SetEvent(g_hEventXb2ToXb1);

    if (!g_fXb1ToXb2Timeout)
    {
        SockTcpTransmit(s, 10);
    }

    Verify(closesocket(s) == 0);

    // don't blow away the SA before the TCP connection has a chance to close gracefully
    Sleep(100);
    
    Verify(XNetUnregisterKey(&g_xnkidXb1ToXb2) == 0);

    GetXn()->IpLogoff();
    Verify(CloseHandle(hEventLogon));
}

void CTestXNet::SgTcpXbToXbServer()
{
    while (XNetGetTitleXnAddr(&g_xnaddrXb2) == XNET_GET_XNADDR_PENDING)
        Sleep(100);

    IN_ADDR inaVip, inaSg, inaXb2;
    HANDLE hEventLogon;

    inaVip.s_addr = inet_addr("10.0.0.2");
    Verify(XNetServerToInAddr(inaVip, 0, &inaSg) == 0);
    Verify((hEventLogon = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL);
    GetXn()->IpLogon(inaSg.s_addr, NULL, hEventLogon);
    Verify(WaitForSingleObject(hEventLogon, INFINITE) == WAIT_OBJECT_0);
    Verify(GetXn()->IpLogonGetStatus(NULL) == XN_LOGON_STATE_ONLINE);

    Verify(XNetCreateKey(&g_xnkidXb1ToXb2, &g_xnkeyXb1ToXb2) == 0);
    g_xnkidXb1ToXb2.ab[0] &= ~XNET_XNKID_MASK;
    g_xnkidXb1ToXb2.ab[0] |=  XNET_XNKID_ONLINE_PEER;
    Verify(XNetRegisterKey(&g_xnkidXb1ToXb2, &g_xnkeyXb1ToXb2) == 0);
    DWORD dw = XNetGetTitleXnAddr(&g_xnaddrXb2);
    Verify(dw != XNET_GET_XNADDR_PENDING);
    Verify(dw & XNET_GET_XNADDR_ONLINE);

    SOCKET s1 = SockTcpCreate(HTONS(80));
    Verify(listen(s1, 1) == 0);

    SetEvent(g_hEventXb1ToXb2);
    Verify(WaitForSingleObject(g_hEventXb2ToXb1, INFINITE) == WAIT_OBJECT_0);

    if (!g_fXb1ToXb2Timeout)
    {
        sockaddr_in sin;
        int slen = sizeof(sin);
        SOCKET s2 = accept(s1, (struct sockaddr *)&sin, &slen);
        Verify(s2 != SOCKET_ERROR);
        Verify(slen == sizeof(sin));
        SockTcpReflect(s2);
        Verify(closesocket(s2) == 0);
    }

    Verify(closesocket(s1) == 0);
    Verify(XNetUnregisterKey(&g_xnkidXb1ToXb2) == 0);

    GetXn()->IpLogoff();
    Verify(CloseHandle(hEventLogon));
}

#define XNETTEST_BEG(_name, pszXbox, pszIp, pszMask, pszGateway, dwFlags) \
    class CTest##_name : public CTestXNet { public: \
    CTest##_name() : CTestXNet(pszXbox) {}; \
    virtual void Init() { Startup(pszIp, pszMask, pszGateway, dwFlags); } \
    virtual void Test()
#define XNETTEST_END(_name) \
    }; void Test_##_name(HANDLE hEvent1 = 0, HANDLE hEvent2 = 0) { CTest##_name * p = new CTest##_name; p->Go(hEvent1, hEvent2); delete p; }

// ---------------------------------------------------------------------------------------
// TWOTHREADTEST
// ---------------------------------------------------------------------------------------

typedef void (*PFNTEST)(HANDLE hEvent1, HANDLE hEvent2);

typedef struct {
    HANDLE      hThread;
    HANDLE      hEvent1;
    HANDLE      hEvent2;
    PFNTEST     pfn;
} TWOTHREADPARAM;

DWORD WINAPI TwoThreadProc(void * pv)
{
    TWOTHREADPARAM * p = (TWOTHREADPARAM *)pv;
    p->pfn(p->hEvent1, p->hEvent2);
    return(0);
}

#define TWOTHREADTEST(_name) \
    void Test_##_name##_run() \
    { \
        g_fXb1ToXb2Timeout = FALSE; \
        Verify((g_hEventXb1ToXb2 = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL); \
        Verify((g_hEventXb2ToXb1 = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL); \
        HANDLE  hThread1 = NULL, hEvent1 = NULL, hThread2 = NULL, hEvent2 = NULL; \
        TWOTHREADPARAM p1, p2; \
        Verify((p1.hEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL); \
        Verify((p1.hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL); \
        p1.pfn = &Test_##_name##_1; \
        p2.hEvent1 = p1.hEvent2; \
        p2.hEvent2 = p1.hEvent1; \
        p2.pfn = &Test_##_name##_2; \
        Verify((p1.hThread = CreateThread(NULL, 0, TwoThreadProc, &p1, 0, NULL)) != NULL); \
        Verify((p2.hThread = CreateThread(NULL, 0, TwoThreadProc, &p2, 0, NULL)) != NULL); \
        Verify(WaitForSingleObject(p1.hThread, INFINITE) == WAIT_OBJECT_0); \
        Verify(WaitForSingleObject(p2.hThread, INFINITE) == WAIT_OBJECT_0); \
        CloseHandle(p1.hEvent1); CloseHandle(p2.hEvent1); \
        CloseHandle(p1.hThread); CloseHandle(p2.hThread); \
        CloseHandle(g_hEventXb1ToXb2); g_hEventXb1ToXb2 = NULL; \
        CloseHandle(g_hEventXb2ToXb1); g_hEventXb2ToXb1 = NULL; \
    } \
    void Test_##_name() \

#define TWOTHREADRUN(_name) Test_##_name##_run()

// Test_inet_addr ------------------------------------------------------------------------

XNETTEST_BEG(inet_addr, "xb1@Lan", "10.0.0.1", "255.0.0.0", NULL, 0)
{
    printf("Testing inet_addr ");
    Verify(inet_addr("004.003.002.020") == 0x10020304);
    Verify(inet_addr("0377.0377.0377.0377") == 0xffffffff);
    Verify(inet_addr("004.003.002.0400") == 0x00020304);
    Verify(inet_addr("004.003.0400.020") == 0x10000304);
    Verify(inet_addr("004.0400.002.020") == 0x10020004);
    Verify(inet_addr("0400.003.002.020") == 0x10020300);
    Verify(inet_addr("0x4.0x3.0x2.0x10") == 0x10020304);
    Verify(inet_addr("0xFF.0xFF.0xFF.0xFF") == 0xffffffff);
    Verify(inet_addr("0x4.0x3.0x2.0x100") == 0x00020304);
    Verify(inet_addr("0x4.0x3.0x100.0x10") == 0x10000304);
    Verify(inet_addr("0x4.0x100.0x2.0x10") == 0x10020004);
    Verify(inet_addr("0x100.0x3.0x2.0x10") == 0x10020300);
    Verify(inet_addr("4.003.2.0x10") == 0x10020304);
    Verify(inet_addr("004.3.0x2.16") == 0x10020304);
    Verify(inet_addr("4.0x3.2.020") == 0x10020304);
    Verify(inet_addr("0x4.3.002.16") == 0x10020304);
    Verify(inet_addr("255.0377.255.0xFF") == 0xffffffff);
    Verify(inet_addr("0377.255.0xFF.255") == 0xffffffff);
    Verify(inet_addr("255.0xFF.255.0377") == 0xffffffff);
    Verify(inet_addr("0xFF.255.0377.255") == 0xffffffff);
    Verify(inet_addr("4.0400.2.0x10") == 0x10020004);
    Verify(inet_addr("004.3.0x100.16") == 0x10000304);
    Verify(inet_addr("256.0x3.2.020") == 0x10020300);
    Verify(inet_addr("0x4.3.002.256") == 0x00020304);
    Verify(inet_addr("4.3.2") == 0x02000304);
    Verify(inet_addr("255.255.255") == 0xff00ffff);
    Verify(inet_addr("4.3.256") == 0x00010304);
    Verify(inet_addr("4.3.65536") == 0x00000304);
    Verify(inet_addr("4.3") == 0x03000004);
    Verify(inet_addr("255.255") == 0xff0000ff);
    Verify(inet_addr("4.256") == 0x00010004);
    Verify(inet_addr("4.16777216") == 0x00000004);
    Verify(inet_addr("4") == 0x04000000);
    Verify(inet_addr("255") == 0xff000000);
    Verify(inet_addr("4294967296") == 0x00000000);
    Verify(inet_addr("4.3.2.16abcdef") == 0xffffffff);
    Verify(inet_addr("4.3.2abcdef.16") == 0xffffffff);
    Verify(inet_addr("4.3abcdef.2.16") == 0xffffffff);
    Verify(inet_addr("4abcdef.3.2.16") == 0xffffffff);
    Verify(inet_addr("4.3.2.abcdef16") == 0xffffffff);
    Verify(inet_addr("4.3.abcdef2.16") == 0xffffffff);
    Verify(inet_addr("4.abcdef3.2.16") == 0xffffffff);
    Verify(inet_addr("abcdef4.3.2.16") == 0xffffffff);
    Verify(inet_addr("4.3.2 .16") == 0x10020304);
    Verify(inet_addr("4.3 .2.16") == 0x10020304);
    Verify(inet_addr("4 .3.2.16") == 0x10020304);
    Verify(inet_addr("4.3.2. 16") == 0x10020304);
    Verify(inet_addr("4.3. 2.16") == 0x10020304);
    Verify(inet_addr("4. 3.2.16") == 0x10020304);
    Verify(inet_addr(" 4.3.2.16") == 0x10020304);
    printf(" [OK]\n");
}
XNETTEST_END(inet_addr);

// Test_XNetRandom -----------------------------------------------------------------------

XNETTEST_BEG(XNetRandom, "xb1@Lan", "10.0.0.1", "255.0.0.0", NULL, 0)
{
    printf("Testing XNetRandom ");

    DWORD dw1, dw2;
    Verify(XNetRandom((BYTE *)&dw1, sizeof(dw1)) == 0);
    Verify(XNetRandom((BYTE *)&dw2, sizeof(dw2)) == 0);
    Verify(dw1 != dw2);

    BYTE ab[13];
    UINT c = 200;
    for (; c > 0; --c)
    {
        Verify(XNetRandom(ab, sizeof(ab)) == 0);
    }

    printf("[OK]\n");
}
XNETTEST_END(XNetRandom);

// Test_XNetRegisterKey ------------------------------------------------------------------

XNETTEST_BEG(XNetRegisterKey, "xb1@Lan", "10.0.0.1", "255.0.0.0", NULL, 0)
{
    printf("Testing XNetRegisterKey ");

    XNKID xnkid1, xnkid2, xnkid3, xnkid4, xnkid5;
    XNKEY xnkey1, xnkey2, xnkey3, xnkey4, xnkey5;

    Verify(XNetCreateKey(&xnkid1, &xnkey1) == 0);
    Verify(XNetCreateKey(&xnkid2, &xnkey2) == 0);
    Verify(XNetCreateKey(&xnkid3, &xnkey3) == 0);
    Verify(XNetCreateKey(&xnkid4, &xnkey4) == 0);
    Verify(XNetCreateKey(&xnkid5, &xnkey5) == 0);

    Verify(XNetRegisterKey(&xnkid1, &xnkey1) == 0);
    Verify(XNetRegisterKey(&xnkid1, &xnkey1) == WSAEALREADY);
    Verify(XNetRegisterKey(&xnkid1, &xnkey2) == WSAEALREADY);
    Verify(XNetRegisterKey(&xnkid2, &xnkey2) == 0);
    Verify(XNetRegisterKey(&xnkid2, &xnkey1) == WSAEALREADY);
    Verify(XNetRegisterKey(&xnkid2, &xnkey2) == WSAEALREADY);
    Verify(XNetUnregisterKey(&xnkid1) == 0);
    Verify(XNetUnregisterKey(&xnkid1) == WSAEINVAL);
    Verify(XNetRegisterKey(&xnkid1, &xnkey1) == 0);
    Verify(XNetRegisterKey(&xnkid1, &xnkey1) == WSAEALREADY);
    Verify(XNetUnregisterKey(&xnkid2) == 0);
    Verify(XNetUnregisterKey(&xnkid2) == WSAEINVAL);
    Verify(XNetUnregisterKey(&xnkid1) == 0);
    Verify(XNetUnregisterKey(&xnkid1) == WSAEINVAL);
    Verify(XNetRegisterKey(&xnkid1, &xnkey1) == 0);
    Verify(XNetRegisterKey(&xnkid2, &xnkey2) == 0);
    Verify(XNetRegisterKey(&xnkid3, &xnkey3) == 0);
    Verify(XNetRegisterKey(&xnkid4, &xnkey4) == 0);
    Verify(XNetRegisterKey(&xnkid5, &xnkey5) == WSAENOMORE);
    Verify(XNetUnregisterKey(&xnkid3) == 0);
    Verify(XNetRegisterKey(&xnkid5, &xnkey5) == 0);
    Verify(XNetUnregisterKey(&xnkid3) == WSAEINVAL);
    Verify(XNetUnregisterKey(&xnkid2) == 0);
    Verify(XNetUnregisterKey(&xnkid4) == 0);
    Verify(XNetUnregisterKey(&xnkid1) == 0);
    Verify(XNetUnregisterKey(&xnkid5) == 0);

    printf("[OK]\n");
}
XNETTEST_END(XNetRegisterKey);

// Test_XNetXnAddrToInAddr ---------------------------------------------------------------

XNETTEST_BEG(XNetXnAddrToInAddr, "xb1@Lan", "10.0.0.1", "255.0.0.0", NULL, 0)
{
    printf("Testing XNetXnAddrToInAddr ");

    XNKID   xnkid1,     xnkid2,     xnkid3,     xnkidT,     xnkidZ;
    XNKEY   xnkey1,     xnkey2,     xnkey3,     xnkeyT;
    XNADDR  xnaddr1,    xnaddr2,    xnaddr3,    xnaddrT,    xnaddrZ;
    CIpAddr ipa1,       ipa2,       ipa3,       ipaT;

    Verify(XNetCreateKey(&xnkid1, &xnkey1) == 0);
    Verify(XNetCreateKey(&xnkid2, &xnkey2) == 0);
    Verify(XNetCreateKey(&xnkid3, &xnkey3) == 0);

    Verify(XNetRegisterKey(&xnkid1, &xnkey1) == 0);
    Verify(XNetRegisterKey(&xnkid2, &xnkey2) == 0);
    Verify(XNetRegisterKey(&xnkid3, &xnkey3) == 0);
    
    Verify(XNetGetTitleXnAddr(&xnaddr1) != 0);
    Verify(XNetXnAddrToInAddr(&xnaddr1, &xnkid1, (IN_ADDR *)&ipa1) == 0);
    Verify(ipa1 == IPADDR_LOOPBACK);

    memset(&xnkidZ, 0, sizeof(xnkidZ));
    Verify(XNetInAddrToXnAddr(*(IN_ADDR *)&ipa1, &xnaddrT, &xnkidT) == 0);
    Verify(memcmp(&xnaddr1, &xnaddrT, sizeof(XNADDR)) == 0);
    Verify(memcmp(&xnkidT, &xnkidZ, sizeof(xnkidZ)) == 0);

    XNADDR  rgxna[32];
    CIpAddr rgipa[32];
    int     i;

    for (i = 0; i < dimensionof(rgxna); ++i)
    {
        XNADDR * pxnaddr = &rgxna[i];
        memset(pxnaddr, 0, sizeof(XNADDR));
        Verify(XNetRandom(pxnaddr->abEnet, sizeof(pxnaddr->abEnet)) == 0);
        pxnaddr->ina.s_addr = 0;
        XNKID * pxnkid = ((i % 3) == 0 ? &xnkid1 : ((i % 3) == 1) ? &xnkid2 : &xnkid3);
        Verify(XNetXnAddrToInAddr(pxnaddr, pxnkid, (IN_ADDR *)&rgipa[i]) == 0);
    }

    memset(&xnaddrT, 0, sizeof(xnaddrT));
    Verify(XNetRandom(xnaddrT.abEnet, sizeof(xnaddrT.abEnet)) == 0);
    Verify(XNetXnAddrToInAddr(&xnaddrT, &xnkid1, (IN_ADDR *)&ipaT) == WSAENOMORE);

    for (i = 0; i < dimensionof(rgxna); ++i)
    {
        XNADDR * pxnaddr = &rgxna[i];
        XNKID * pxnkid = ((i % 3) == 0 ? &xnkid1 : ((i % 3) == 1) ? &xnkid2 : &xnkid3);
        Verify(XNetInAddrToXnAddr(*(IN_ADDR *)&rgipa[i], &xnaddrT, &xnkidT) == 0);
        Verify(memcmp(&xnaddrT, &rgxna[i], sizeof(XNADDR)) == 0);
        Verify(memcmp(&xnkidT, pxnkid, sizeof(XNKID)) == 0);
    }

    Verify(XNetUnregisterKey(&xnkid2) == 0);

    for (i = 0; i < dimensionof(rgxna); ++i)
    {
        XNKID * pxnkid = ((i % 3) == 0 ? &xnkid1 : ((i % 3) == 1) ? &xnkid2 : &xnkid3);
        if ((i % 3) == 1)
        {
            memset(&xnaddrZ, 0, sizeof(XNADDR));
            memcpy(&xnaddrT, &rgxna[i], sizeof(XNADDR));
            memset(&xnkidZ, 0, sizeof(XNKID));
            memcpy(&xnkidT, pxnkid, sizeof(XNKID));
            Verify(XNetInAddrToXnAddr(*(IN_ADDR *)&rgipa[i], &xnaddrT, &xnkidT) == WSAEINVAL);
            Verify(memcmp(&xnaddrT, &xnaddrZ, sizeof(XNADDR)) == 0);
            Verify(memcmp(&xnkidT, &xnkidZ, sizeof(XNKID)) == 0);
        }
        else
        {
            Verify(XNetInAddrToXnAddr(*(IN_ADDR *)&rgipa[i], &xnaddrT, &xnkidT) == 0);
            Verify(memcmp(&xnaddrT, &rgxna[i], sizeof(XNADDR)) == 0);
            Verify(memcmp(&xnkidT, pxnkid, sizeof(XNKID)) == 0);
        }
    }

    Verify(XNetUnregisterKey(&xnkid1) == 0);
    Verify(XNetUnregisterKey(&xnkid3) == 0);

    printf("[OK]\n");
}
XNETTEST_END(XNetXnAddrToInAddr)

// Test_Broadcast ------------------------------------------------------------------------

IPTEST_BEG(Broadcast_1, "xb1@Lan", "10.0.0.1", "255.0.0.0", NULL, XNET_STARTUP_BYPASS_SECURITY)
{
    Verify((_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL);

    _uiFlags = PKTF_TYPE_UDP_ESP|PKTF_CRYPT;

    for (int i = 0; i < 2; ++i)
    {
        Rand(_ab, sizeof(_ab));

        CPacket *   ppkt    = PacketAlloc(0, _uiFlags, sizeof(_ab));
        CUdpHdr *   pUdpHdr = ppkt->GetUdpHdr();
        memcpy(pUdpHdr + 1, _ab, sizeof(_ab));
        pUdpHdr->_ipportDst = HTONS(666);
        pUdpHdr->_ipportSrc = HTONS(666);
        pUdpHdr->_wLen      = HTONS(sizeof(CUdpHdr) + sizeof(_ab));
        IpFillAndXmit(ppkt, IPADDR_BROADCAST, IPPROTOCOL_UDP, NULL);
        Verify(WaitForSingleObject(_hEvent, INFINITE) == WAIT_OBJECT_0);
        _uiFlags = PKTF_TYPE_UDP;
    }
}

virtual void UdpRecv(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen)
{
    Verify(ppkt->IsUdp());
    Verify(pIpHdr->_ipaDst.IsBroadcast());
    Verify(!!ppkt->TestFlags(PKTF_TYPE_ESP) == !!(_uiFlags & PKTF_TYPE_ESP));
    Verify(!!ppkt->TestFlags(PKTF_CRYPT) == !!(_uiFlags & PKTF_CRYPT));
    Verify(pUdpHdr->_ipportDst == HTONS(666));
    Verify(pUdpHdr->_ipportSrc == HTONS(666));
    Verify(NTOHS(pUdpHdr->_wLen) == sizeof(CUdpHdr) + sizeof(_ab));
    Verify(memcmp(pUdpHdr + 1, _ab, sizeof(_ab)) == 0);
    SetEvent(_hEvent);
}

UINT _uiFlags;
HANDLE _hEvent;
BYTE _ab[211];

IPTEST_END(Broadcast_1);

IPTEST_BEG(Broadcast_2, "xb2@Lan", "10.0.0.2", "255.0.0.0", NULL, XNET_STARTUP_BYPASS_SECURITY)
{
    Verify((_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL);

    for (int i = 0; i < 2; ++i)
    {
        Verify(WaitForSingleObject(_hEvent, INFINITE) == WAIT_OBJECT_0);
    }
}

virtual void UdpRecv(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen)
{
    Verify(pIpHdr->_ipaDst.IsBroadcast());
    UINT uiFlags = (ppkt->GetFlags() & (PKTF_TYPE_UDP|PKTF_TYPE_ESP|PKTF_CRYPT));
    CPacket * ppktNew = PacketAlloc(PTAG_CPacket, PKTF_POOLALLOC|uiFlags, cbLen);
    memcpy(ppktNew->GetUdpHdr(), pUdpHdr, sizeof(CUdpHdr) + cbLen);
    IpFillAndXmit(ppktNew, IPADDR_BROADCAST, IPPROTOCOL_UDP, NULL);
    SetEvent(_hEvent);
}

HANDLE _hEvent;

IPTEST_END(Broadcast_2);

TWOTHREADTEST(Broadcast)
{
    printf("Testing Broadcast ");
    TWOTHREADRUN(Broadcast);
    printf("[OK]\n");
}

// Test_Discovery ------------------------------------------------------------------------

#define DISCOVERY_UNICAST       200

IPTEST_BEG(Discovery_1, "xb1@Lan", "10.0.0.1", "255.0.0.0", NULL, 0)
{
    // This is the host of the "game session".  We create a key and register it.

    Verify((_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL);
    Verify(IpCreateKey(&_xnkid, &_xnkey) == 0);
    IpGetXnAddr(&_xnaddr);
    Verify(IpRegisterKey(&_xnkid, &_xnkey) == 0);
    _fListening = TRUE;

    // Now we wait for discovery requests and respond with a discovery response.
    // This event will be signalled as soon as we've responded.

    Verify(WaitForSingleObject(_hEvent, INFINITE) == WAIT_OBJECT_0);
    Verify(_cBroadcast > 0);

    // Now we wait for a stream of unicast UDP packets from the client

    while (_cUnicast < DISCOVERY_UNICAST)
    {
        Verify(WaitForSingleObject(_hEvent, INFINITE) == WAIT_OBJECT_0);
    }

    Verify(IpUnregisterKey(&_xnkid) == 0);
}

virtual void UdpRecv(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen)
{
    if (pIpHdr->_ipaDst.IsBroadcast())
    {
        if (!_fListening)
        {
            TraceSz(Warning, "Dropping discovery request that arrived too early");
            return;
        }

        Verify(cbLen == sizeof(CDiscReq));
        Verify(pUdpHdr->_ipportDst == HTONS(666));
        Verify(pUdpHdr->_ipportSrc == HTONS(666));
        CDiscReq *  pDiscReq    = (CDiscReq *)(pUdpHdr + 1);
        CPacket *   ppktNew     = PacketAlloc(PTAG_CPacket, PKTF_POOLALLOC|PKTF_TYPE_UDP_ESP|PKTF_CRYPT, sizeof(CDiscRsp));
        CUdpHdr *   pUdpHdrNew  = ppktNew->GetUdpHdr();
        pUdpHdrNew->_ipportDst  = HTONS(666);
        pUdpHdrNew->_ipportSrc  = HTONS(666);
        pUdpHdrNew->_wLen       = HTONS(sizeof(CUdpHdr) + sizeof(CDiscRsp));
        CDiscRsp *  pDiscRsp    = (CDiscRsp *)(pUdpHdrNew + 1);
        memcpy(pDiscRsp->abNonce, pDiscReq->abNonce, sizeof(pDiscReq->abNonce));
        pDiscRsp->xnkid         = _xnkid;
        pDiscRsp->xnkey         = _xnkey;
        pDiscRsp->xnaddr        = _xnaddr;
        IpFillAndXmit(ppktNew, IPADDR_BROADCAST, IPPROTOCOL_UDP, NULL);
        _cBroadcast += 1;
        SetEvent(_hEvent);
        return;
    }

    CIpAddr ipaSrc = pIpHdr->_ipaSrc;
    XNKID   xnkid;
    XNADDR  xnaddr;
    Verify(IpInAddrToXnAddr(ipaSrc, &xnaddr, &xnkid) == 0);
    Verify(memcmp(&xnkid, &_xnkid, sizeof(XNKID)) == 0);
    Verify(ppkt->GetEnetHdr()->_eaSrc.IsEqual(xnaddr.abEnet));
    UINT uiFlags = ppkt->GetFlags() & (PKTF_TYPE_UDP|PKTF_TYPE_ESP|PKTF_CRYPT);
    CPacket * ppktNew = PacketAlloc(PTAG_CPacket, PKTF_POOLALLOC|uiFlags, cbLen);
    memcpy(ppktNew->GetUdpHdr(), pUdpHdr, sizeof(CUdpHdr) + cbLen);
    IpFillAndXmit(ppktNew, ipaSrc, IPPROTOCOL_UDP, NULL);
    _cUnicast += 1;
    SetEvent(_hEvent);
}

HANDLE _hEvent;
XNKID _xnkid;
XNKEY _xnkey;
XNADDR _xnaddr;
BOOL _fListening;
UINT _cBroadcast;
UINT _cUnicast;

IPTEST_END(Discovery_1);

IPTEST_BEG(Discovery_2, "xb2@Lan", "10.0.0.2", "255.0.0.0", NULL, 0)
{
    Verify((_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL);

    // Until we get a response, keep broadcasting a CDiscReq packet

    Rand(_abNonce, sizeof(_abNonce));

    while (_cBroadcast == 0)
    {
        CPacket * ppkt = PacketAlloc(0, PKTF_TYPE_UDP_ESP|PKTF_CRYPT, sizeof(CDiscReq));
        CUdpHdr * pUdpHdr = ppkt->GetUdpHdr();
        CDiscReq * pDiscReq = (CDiscReq *)(pUdpHdr + 1);
        pUdpHdr->_ipportDst  = HTONS(666);
        pUdpHdr->_ipportSrc  = HTONS(666);
        pUdpHdr->_wLen       = HTONS(sizeof(CUdpHdr) + sizeof(CDiscReq));
        memcpy(pDiscReq->abNonce, _abNonce, sizeof(_abNonce));
        IpFillAndXmit(ppkt, IPADDR_BROADCAST, IPPROTOCOL_UDP, NULL);
        WaitForSingleObject(_hEvent, 200);
    }

    // Register the host's key

    Verify(IpRegisterKey(&_xnkid, &_xnkey) == 0);

    // Get a secure IP address for the host

    CIpAddr ipaHost;

    Verify(IpXnAddrToInAddr(&_xnaddr, &_xnkid, &ipaHost) == 0);

    // Send Unicast UDP packets

    Rand(_ab, sizeof(_ab));

    UINT i;

    for (i = 0; i < DISCOVERY_UNICAST; ++i)
    {
        UINT uiFlags = PKTF_TYPE_UDP_ESP|PKTF_CRYPT;
        CPacket * ppkt = PacketAlloc(0, uiFlags, sizeof(_ab));
        CUdpHdr * pUdpHdr = ppkt->GetUdpHdr();
        pUdpHdr->_ipportDst  = HTONS(666);
        pUdpHdr->_ipportSrc  = HTONS(666);
        pUdpHdr->_wLen       = HTONS(sizeof(CUdpHdr) + sizeof(_ab));
        memcpy(pUdpHdr + 1, _ab, sizeof(_ab));
        IpFillAndXmit(ppkt, ipaHost, IPPROTOCOL_UDP, NULL);

        // Don't get too far ahead of the other side

        while (_cUnicast + 6 < i)
        {
            Verify(WaitForSingleObject(_hEvent, INFINITE) == WAIT_OBJECT_0);
        }
    }

    // Wait for the remaining packets to be reflected

    while (_cUnicast < i)
    {
        Verify(WaitForSingleObject(_hEvent, INFINITE) == WAIT_OBJECT_0);
    }

    Verify(IpUnregisterKey(&_xnkid) == 0);
}

virtual void UdpRecv(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen)
{
    if (pIpHdr->_ipaDst.IsBroadcast())
    {
        Verify(cbLen == sizeof(CDiscRsp));
        Verify(pUdpHdr->_ipportDst == HTONS(666));
        Verify(pUdpHdr->_ipportSrc == HTONS(666));
        CDiscRsp * pDiscRsp = (CDiscRsp *)(pUdpHdr + 1);
        Verify(memcmp(pDiscRsp->abNonce, _abNonce, sizeof(_abNonce)) == 0);
        _xnkid = pDiscRsp->xnkid;
        _xnkey = pDiscRsp->xnkey;
        _xnaddr = pDiscRsp->xnaddr;
        _cBroadcast += 1;
        SetEvent(_hEvent);
        return;
    }

    Verify(pUdpHdr->_ipportDst == HTONS(666));
    Verify(pUdpHdr->_ipportSrc == HTONS(666));
    Verify(cbLen == sizeof(_ab));
    Verify(memcmp(pUdpHdr + 1, _ab, sizeof(_ab)) == 0);
    _cUnicast += 1;
    SetEvent(_hEvent);
}

HANDLE _hEvent;
XNKID _xnkid;
XNKEY _xnkey;
XNADDR _xnaddr;
BYTE _abNonce[8];
BYTE _ab[592];
UINT _cBroadcast;
UINT _cUnicast;

IPTEST_END(Discovery_2);

TWOTHREADTEST(Discovery)
{
    printf("Testing Discovery ");
    TWOTHREADRUN(Discovery);
    printf("[OK]\n");
}

// Test_Frag -----------------------------------------------------------------------------

#define FRAGALG_FORWARD     0
#define FRAGALG_REVERSE     1
#define FRAGALG_RANDOM      2
#define FRAGALG_OVERLAP     3
#define FRAGALG_COUNT       4

DefineTag(fragTest, 0);

IPTEST_BEG(Frag, "xb1@Lan", "10.0.0.1", "255.0.0.0", NULL, 0)
{
    Verify((_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL);

    for (int iAlg = 0; iAlg < FRAGALG_COUNT; ++iAlg)
    {
        printf("Testing Fragment Reassembler (%s) ",
               iAlg == FRAGALG_FORWARD ? "forward" :
               iAlg == FRAGALG_REVERSE ? "reverse" :
               iAlg == FRAGALG_RANDOM ? "random" : "overlap");

        if (iAlg == FRAGALG_RANDOM)
        {
            printf(" ");
        }

        for (int cbHdrOpt = 0; cbHdrOpt <= (MAXIPHDRLEN-sizeof(CIpHdr)); cbHdrOpt += 4)
        {
            printf(".");

            for (int cbData = 1; cbData < (int)(ENET_DATA_MAXSIZE - sizeof(CIpHdr) - cbHdrOpt - sizeof(CUdpHdr)); )
            {
                int cbChunk = 8;

                while (1)
                {
                    TestFrag(cbHdrOpt, cbData, iAlg, cbChunk);

                    if (cbChunk < 64)
                        cbChunk += 8;
                    else
                        cbChunk += 32;

                    if (cbChunk > (int)((cbData + sizeof(CUdpHdr)) / 2))
                        break;
                }

                if (cbData < 63)
                    cbData += 1;
                else if (cbData < 253)
                    cbData += 51;
                else
                    cbData += 127;
            }
        }

        printf(" [OK]\n");
    }

    CloseHandle(_hEvent);
}

void TestFrag(int cbHdrOpt, int cbData, int iAlg, int cbChunk)
{
    CIpHdr *        pIpHdr;
    CUdpHdr *       pUdpHdr;
    CPseudoHeader   ph;
    UINT            uiChecksum;
    int             iChunk;
    int             cChunk;

    TraceSz4(fragTest, "cbHdrOpt=%d,cbData=%d,iAlg=%d,cbChunk=%d", cbHdrOpt, cbData, iAlg, cbChunk);

    ResetEvent(_hEvent);

    // Create the original packet

    _ppktOrig = PacketAlloc(0, PKTF_TYPE_UDP, cbHdrOpt + cbData);
    Verify(_ppktOrig != NULL);

    pIpHdr              = _ppktOrig->GetIpHdr();
    pIpHdr->SetHdrLen(sizeof(CIpHdr) + cbHdrOpt);
    pIpHdr->_bTos       = 0;
    pIpHdr->_wLen       = HTONS(sizeof(CIpHdr) + cbHdrOpt + sizeof(CUdpHdr) + cbData);
    pIpHdr->_wId        = HTONS(GetNextDgramId());
    pIpHdr->_wFragOff   = 0;
    pIpHdr->_bTtl       = 64;
    pIpHdr->_bProtocol  = IPPROTOCOL_UDP;
    pIpHdr->_ipaSrc     = IPADDR_LOOPBACK;
    pIpHdr->_ipaDst     = IPADDR_LOOPBACK;
    Rand((BYTE *)(pIpHdr + 1), cbHdrOpt);
    pIpHdr->_wChecksum  = 0;
    pIpHdr->_wChecksum  = ~tcpipxsum(0, pIpHdr, sizeof(CIpHdr) + cbHdrOpt);

    _ppktOrig->SetHdrOptLen(cbHdrOpt);

    pUdpHdr             = _ppktOrig->GetUdpHdr();
    pUdpHdr->_ipportSrc = HTONS(666);
    pUdpHdr->_ipportDst = HTONS(666);
    pUdpHdr->_wLen      = HTONS(sizeof(CUdpHdr) + cbData);
    pUdpHdr->_wChecksum = 0;
    Rand((BYTE *)(pUdpHdr + 1), cbData);
    ph._ipaSrc          = pIpHdr->_ipaSrc;
    ph._ipaDst          = pIpHdr->_ipaDst;
    ph._bZero           = 0;
    ph._bProtocol       = IPPROTOCOL_UDP;
    ph._wLen            = HTONS(sizeof(CUdpHdr) + cbData);
    pUdpHdr->_wChecksum = 0;
    uiChecksum          = ~tcpipxsum(tcpipxsum(0, &ph, sizeof(ph)), pUdpHdr, sizeof(CUdpHdr) + cbData);
    pUdpHdr->_wChecksum = uiChecksum - (uiChecksum == 0);

    TraceUdpHdr(fragTest, 0, pUdpHdr, pUdpHdr->GetLen() - sizeof(CUdpHdr));
    TraceSz9(fragTest, "[IP %s %s (%d %d %04X %04X) %d]{%d}[%d]",
             pIpHdr->_ipaDst.Str(), pIpHdr->_ipaSrc.Str(), pIpHdr->_bTos, pIpHdr->_bTtl,
             NTOHS(pIpHdr->_wId), NTOHS(pIpHdr->_wFragOff), pIpHdr->_bProtocol,
             pIpHdr->GetOptLen(), _ppktOrig->GetCb() - pIpHdr->GetHdrLen());

    // Create and send packet fragments

    cbData += sizeof(CUdpHdr);
    cChunk  = (cbData + cbChunk - 1) / cbChunk;

    if (iAlg == FRAGALG_FORWARD)
    {
        for (iChunk = 0; iChunk < cChunk; ++iChunk)
        {
            SendChunk(iChunk, 1, cbChunk, iChunk == (cChunk - 1));
        }
    }
    else if (iAlg == FRAGALG_REVERSE)
    {
        for (iChunk = cChunk - 1; iChunk >= 0; --iChunk)
        {
            SendChunk(iChunk, 1, cbChunk, iChunk == (cChunk - 1));
        }
    }
    else if (iAlg == FRAGALG_RANDOM || iAlg == FRAGALG_OVERLAP)
    {
        BYTE abMap[ENET_DATA_MAXSIZE / 8];
        int cChunkSent = 0;

        memset(abMap, 0, sizeof(abMap));

        while (cChunkSent < cChunk)
        {
            // Randomly select a chunk to send.  If that chunk is already sent,
            // look for the next one that isn't sent yet.

            int iChunkRand = rand() % cChunk;

            while (abMap[iChunkRand])
            {
                iChunkRand += 1;
                if (iChunkRand == cChunk)
                    iChunkRand = 0;
            }

            if (iAlg == FRAGALG_OVERLAP)
            {
                int iChunkBeg = iChunkRand;
                int iChunkEnd = iChunkRand;

                if (iChunkBeg > 0)
                {
                    // Back up a random number of chunks (no more than 4)

                    iChunkBeg -= rand() % (min(4, iChunkBeg));
                    Verify(iChunkBeg >= 0);
                }

                if (iChunkEnd < (cChunk - 1))
                {
                    // Extend the range a random number of chunks (no more than 4)

                    iChunkEnd += rand() % (cChunk - iChunkEnd);
                    Verify(iChunkEnd < cChunk);
                }

                if (iChunkBeg == 0 && iChunkEnd == (cChunk - 1))
                {
                    // Whoops.  Can't send the whole thing in one chunk.

                    if (iChunkBeg != iChunkRand)
                        iChunkBeg += 1;
                    else
                    {
                        Verify(iChunkEnd != iChunkRand);
                        iChunkEnd -= 1;
                    }
                }

                Verify(iChunkBeg >= 0);
                Verify(iChunkEnd < cChunk);

                for (iChunkRand = iChunkBeg; iChunkRand <= iChunkEnd; ++iChunkRand)
                {
                    if (abMap[iChunkRand] == 0)
                    {
                        abMap[iChunkRand] = 1;
                        cChunkSent += 1;
                        Verify(cChunkSent <= cChunk);
                    }
                }

                SendChunk(iChunkBeg, iChunkEnd - iChunkBeg + 1, cbChunk, iChunkEnd == (cChunk - 1));
            }
            else
            {
                abMap[iChunkRand] = 1;
                cChunkSent += 1;
                Verify(cChunkSent <= cChunk);
                SendChunk(iChunkRand, 1, cbChunk, iChunkRand == (cChunk - 1));
            }
        }
    }

    // Wait for the packet to arrive

    Verify(WaitForSingleObject(_hEvent, 1000) == WAIT_OBJECT_0);

    PacketFree(_ppktOrig);
    _ppktOrig = NULL;
}

void SendChunk(int iChunk, int cChunk, int cbChunk, BOOL fLastChunk)
{
    CIpHdr *    pIpHdrOrig   = _ppktOrig->GetIpHdr();
    int         cbHdrOptOrig = pIpHdrOrig->GetOptLen();
    int         cbDataOrig   = _ppktOrig->GetCb() - sizeof(CIpHdr) - cbHdrOptOrig;
    int         cbHdrOpt     = iChunk == 0 ? cbHdrOptOrig : 0;
    int         ibData       = iChunk * cbChunk;
    int         cbData       = cChunk * cbChunk;

    Verify(ibData < cbDataOrig);

    if (ibData + cbData >= cbDataOrig)
    {
        cbData = cbDataOrig - ibData;
        Verify(fLastChunk);
    }

    Verify(cbData > 0);
    Verify(!fLastChunk || iChunk > 0);

    CPacket *   ppkt    = PacketAlloc(0, PKTF_TYPE_IP, cbHdrOpt + cbData);
    Verify(ppkt != NULL);

    CIpHdr *    pIpHdr  = ppkt->GetIpHdr();
    memcpy(pIpHdr, pIpHdrOrig, sizeof(CIpHdr) + cbHdrOpt);
    pIpHdr->SetHdrLen(sizeof(CIpHdr) + cbHdrOpt);
    pIpHdr->_wLen       = NTOHS((WORD)(sizeof(CIpHdr) + cbHdrOpt + cbData));
    pIpHdr->_wFragOff   = NTOHS((WORD)((iChunk * (cbChunk / 8)) | (fLastChunk ? 0 : MORE_FRAGMENTS)));
    pIpHdr->_wChecksum  = 0;
    pIpHdr->_wChecksum  = ~tcpipxsum(0, pIpHdr, pIpHdr->GetHdrLen());
    memcpy((BYTE *)(pIpHdr + 1) + cbHdrOpt, (BYTE *)(pIpHdrOrig + 1) + cbHdrOptOrig + ibData, cbData);

    RaiseToDpc();
    IpXmit(ppkt);
}

void UdpRecv(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen)
{
    Verify(ppkt->GetCb() == _ppktOrig->GetCb());
    Verify(memcmp(ppkt->GetPv(), _ppktOrig->GetPv(), ppkt->GetCb()) == 0);
    SetEvent(_hEvent);
}

CPacket *   _ppktOrig;
HANDLE      _hEvent;

IPTEST_END(Frag);

// Test_SockUdp --------------------------------------------------------------------------

XNETTEST_BEG(SockUdp_1, "xb1@Lan", "10.0.0.1", "255.0.0.0", NULL, 0)
{
    SOCKET s = SockUdpCreate(NTOHS(1567));
    XNKID xnkid;
    DiscoveryHost(1, &xnkid);
    SockUdpReflect(s);
    Verify(closesocket(s) == 0);
    Verify(XNetUnregisterKey(&xnkid) == 0);
}
XNETTEST_END(SockUdp_1);

XNETTEST_BEG(SockUdp_2, "xb2@Lan", "10.0.0.2", "255.0.0.0", NULL, 0)
{
    SOCKET s = SockUdpCreate(CIpPort(NTOHS(6543)));
    XNKID xnkid;
    CIpAddr ipa;
    DiscoveryClient(&xnkid, &ipa);
    SockUdpTransmit(s, ipa, CIpPort(NTOHS(1567)));
    Verify(closesocket(s) == 0);
    Verify(XNetUnregisterKey(&xnkid) == 0);
}
XNETTEST_END(SockUdp_2);

TWOTHREADTEST(SockUdp)
{
    printf("Testing SockUdp ");
    TWOTHREADRUN(SockUdp);
    printf(" [OK]\n");
}

// Test_SockTcp --------------------------------------------------------------------------

XNETTEST_BEG(SockTcp_1, "xb1@Lan", "10.0.0.1", "255.0.0.0", NULL, 0)
{
    SOCKET s1 = SockTcpCreate(NTOHS(1567));
    Verify(listen(s1, 1) == 0);
    XNKID xnkid;
    DiscoveryHost(1, &_xnkid);
    sockaddr_in sin;
    int slen = sizeof(sin);
    SOCKET s2 = accept(s1, (struct sockaddr *)&sin, &slen);
    Verify(s2 != SOCKET_ERROR);
    Verify(slen == sizeof(sin));
    SockTcpReflect(s2);
    Verify(closesocket(s2) == 0);
    Verify(closesocket(s1) == 0);
}

virtual void Term()
{
    Verify(XNetUnregisterKey(&_xnkid) == 0);
    CTestXNet::Term();
}

XNKID _xnkid;

XNETTEST_END(SockTcp_1);

XNETTEST_BEG(SockTcp_2, "xb2@Lan", "10.0.0.2", "255.0.0.0", NULL, 0)
{
    SOCKET s = SockTcpCreate();
    XNKID xnkid;
    CIpAddr ipa;
    DiscoveryClient(&_xnkid, &ipa);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = ipa;
    sin.sin_port = NTOHS(1567);
    Verify(connect(s, (struct sockaddr *)&sin, sizeof(sin)) != SOCKET_ERROR);
    SockTcpTransmit(s);
    Verify(closesocket(s) == 0);
}

virtual void Term()
{
    Verify(XNetUnregisterKey(&_xnkid) == 0);
    CTestXNet::Term();
}

XNKID _xnkid;

XNETTEST_END(SockTcp_2);

TWOTHREADTEST(SockTcp)
{
    printf("Testing SockTcp ");
    TWOTHREADRUN(SockTcp);
    printf(" [OK]\n");
}

// Test_Dhcp -----------------------------------------------------------------------------

#include <pshpack1.h>

struct DhcpMessage
{
    CEnetHdr        _enethdr;           // [ENET]
    CIpHdr          _iphdr;             // [IP]
    CUdpHdr         _udphdr;            // [UDP]
    BYTE            _op;                // message type
    BYTE            _htype;             // hareware address type
    BYTE            _hlen;              // hardware address length
    BYTE            _hops;              // relay hops
    DWORD           _xid;               // transaction ID
    WORD            _secs;              // seconds since address acquisition process began
    WORD            _flags;             // flags
    CIpAddr         _ciaddr;            // client IP address
    CIpAddr         _yiaddr;            // "your" (client) IP address
    CIpAddr         _siaddr;            // server IP address
    CIpAddr         _giaddr;            // relay agent IP address
    BYTE            _chaddr[16];        // client hardware address
    BYTE            _sname[64];         // optional server hostname
    BYTE            _file[128];         // boot filename
    BYTE            _options[256];      // optional parameters (variable length)
};

#include <poppack.h>

BYTE abDhcpResp[] =
{
    0x00,0x50,0xF2,0x00,0x17,0xFC,0x00,0x90,0x27,0xCC,0x74,0x7C,0x08,0x00,0x45,0x10,
    0x01,0x4E,0x00,0x00,0x00,0x00,0x10,0x11,0x9F,0x47,0x0A,0x47,0x00,0x02,0xFF,0xFF,
    0xFF,0xFF,0x00,0x43,0x00,0x44,0x01,0x3A,0xB4,0xF7,0x02,0x01,0x06,0x00,0x39,0x73,
    0xDD,0x20,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x0A,0x47,0xFE,0x63,0x0A,0x47,
    0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x50,0xF2,0x00,0x17,0xFC,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x82,0x53,0x63,0x35,0x01,0x02,0x36,0x04,0x0A,
    0x47,0x00,0x02,0x33,0x04,0x00,0x00,0x46,0x50,0x01,0x04,0xFF,0xFF,0x00,0x00,0x03,
    0x04,0x0A,0x47,0x00,0x01,0x06,0x0C,0x0A,0x47,0x00,0x02,0x0A,0x49,0x00,0x02,0xD1,
    0xF9,0xE0,0x82,0x0F,0x16,0x73,0x61,0x6E,0x6A,0x6F,0x73,0x65,0x2E,0x69,0x6E,0x66,
    0x6F,0x67,0x72,0x61,0x6D,0x65,0x73,0x2E,0x6C,0x6F,0x63,0xFF
};

BYTE abDhcpAck[] =
{
    0x00,0x50,0xF2,0x00,0x17,0xFC,0x00,0x90,0x27,0xCC,0x74,0x7C,0x08,0x00,0x45,0x10,
    0x01,0x4E,0x00,0x00,0x00,0x00,0x10,0x11,0x9F,0x47,0x0A,0x47,0x00,0x02,0xFF,0xFF,
    0xFF,0xFF,0x00,0x43,0x00,0x44,0x01,0x3A,0xB1,0xF6,0x02,0x01,0x06,0x00,0x39,0x73,
    0xDD,0x21,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x0A,0x47,0xFE,0x63,0x0A,0x47,
    0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x50,0xF2,0x00,0x17,0xFC,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x82,0x53,0x63,0x35,0x01,0x05,0x36,0x04,0x0A,
    0x47,0x00,0x02,0x33,0x04,0x00,0x00,0x46,0x50,0x01,0x04,0xFF,0xFF,0x00,0x00,0x03,
    0x04,0x0A,0x47,0x00,0x01,0x06,0x0C,0x0A,0x47,0x00,0x02,0x0A,0x49,0x00,0x02,0xD1,
    0xF9,0xE0,0x82,0x0F,0x16,0x73,0x61,0x6E,0x6A,0x6F,0x73,0x65,0x2E,0x69,0x6E,0x66,
    0x6F,0x67,0x72,0x61,0x6D,0x65,0x73,0x2E,0x6C,0x6F,0x63,0xFF
};

IPTEST_BEG(Dhcp, "xb1@Lan", NULL, NULL, NULL, 0)
{
    printf("Testing Dhcp ");
    XNADDR xnaddr;
    while (IpGetXnAddr(&xnaddr) == XNET_GET_XNADDR_PENDING)
        Sleep(100);
	Verify(xnaddr.ina.s_addr == inet_addr("10.71.254.99"));
    printf("[OK]\n");
}

virtual void EnetXmit(CPacket * ppkt, CIpAddr ipaNext)
{
    DhcpMessage * pdhcpMsg = (DhcpMessage *)ppkt->GetEnetHdr();
    DhcpMessage   dhcpMsg;

    BYTE * pb = abDhcpResp;
    UINT   cb = sizeof(abDhcpResp);

    if (_fGotDiscover)
    {
        pb = abDhcpAck;
        cb = sizeof(abDhcpAck);
    }

    Assert(sizeof(dhcpMsg) >= cb);
    memcpy(&dhcpMsg, pb, cb);

    memcpy(dhcpMsg._chaddr, _ea._ab, sizeof(_ea._ab));
    dhcpMsg._xid = pdhcpMsg->_xid;
    dhcpMsg._enethdr._eaDst = _ea;

    CPseudoHeader ph;
    ph._ipaSrc      = dhcpMsg._iphdr._ipaSrc;
    ph._ipaDst      = dhcpMsg._iphdr._ipaDst;
    ph._bZero       = 0;
    ph._bProtocol   = IPPROTOCOL_UDP;
    ph._wLen        = dhcpMsg._udphdr._wLen;
    dhcpMsg._udphdr._wChecksum = 0;
    UINT uiChecksum = ~tcpipxsum(tcpipxsum(0, &ph, sizeof(ph)), &dhcpMsg._udphdr, NTOHS(ph._wLen));
    dhcpMsg._udphdr._wChecksum = uiChecksum - (uiChecksum == 0);

    NicRecvFrame(&dhcpMsg, cb);

    CXnEnet::EnetXmit(ppkt, 0);

    _fGotDiscover = TRUE;
}

BOOL _fGotDiscover;

IPTEST_END(Dhcp)

// Test_Dns ------------------------------------------------------------------------------

XNETTEST_BEG(Dns, "xb1@Nic/0:FEFFDEADF00D", NULL, NULL, NULL, 0)
{
    printf("Testing Dns ");

    if (!VLanDriver())
    {
        printf("[No VLan]\n");
        return;
    }

    XNADDR xnaddr;
    while (XNetGetTitleXnAddr(&xnaddr) == XNET_GET_XNADDR_PENDING)
        Sleep(100);
    Verify((_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL);
    
    XNDNS * pxndns = NULL;

    Verify(XNetDnsLookup("nosuchhost.foo.bar.com", _hEvent, &pxndns) == 0);
    Verify(WaitForSingleObject(_hEvent, INFINITE) == WAIT_OBJECT_0);
    Verify(pxndns->iStatus == WSAHOST_NOT_FOUND);
    Verify(pxndns->cina == 0);
    Verify(XNetDnsRelease(pxndns) == 0);

    Verify(XNetDnsLookup("products.redmond.corp.microsoft.com", _hEvent, &pxndns) == 0);
    Verify(WaitForSingleObject(_hEvent, INFINITE) == WAIT_OBJECT_0);
    Verify(pxndns->iStatus == 0);
    Verify(pxndns->cina > 0);
    Verify(XNetDnsRelease(pxndns) == 0);

    Verify(XNetDnsLookup("products.redmond.corp.microsoft.com", NULL, &pxndns) == 0);
    while (pxndns->iStatus == WSAEINPROGRESS)
        Sleep(100);
    Verify(pxndns->iStatus == 0);
    Verify(pxndns->cina > 0);
    Verify(XNetDnsRelease(pxndns) == 0);

    CloseHandle(_hEvent);
    printf("[OK]\n");
}

HANDLE _hEvent;

XNETTEST_END(Dns)

// Test_SgUdp ----------------------------------------------------------------------------

XNETTEST_BEG(SgUdp_1, "xb1@Internet", "10.0.0.10", "255.0.0.0", "10.0.0.1", 0)
{
    GetXn()->IpLogon(0, NULL, NULL);
    Sleep(200);
    IN_ADDR inaVip, inaSec;
    inaVip.s_addr = inet_addr("10.0.0.2");
    Verify(XNetServerToInAddr(inaVip, 0, &inaSec) == 0);
    SOCKET s = SockUdpCreate(0, FALSE);
    Verify(s != SOCKET_ERROR);
    SockUdpTransmit(s, inaSec.s_addr, CIpPort(NTOHS(1)));
    Verify(closesocket(s) == 0);
}
XNETTEST_END(SgUdp_1)

XNETTEST_BEG(SgUdp_2, "xb2@Datacenter", "10.0.0.4", "255.0.0.0", "10.0.0.1", XNET_STARTUP_BYPASS_SECURITY)
{
    SOCKET s = SockUdpCreate(HTONS(127));
    SockUdpReflect(s);
    Verify(closesocket(s) == 0);
}
XNETTEST_END(SgUdp_2)

TWOTHREADTEST(SgUdp)
{
    printf("Testing SgUdp ");
    TWOTHREADRUN(SgUdp);
    printf(" [OK]\n");
}

// Test_SgTcp ----------------------------------------------------------------------------

XNETTEST_BEG(SgTcp_1, "xb1@Internet", "10.0.0.11", "255.0.0.0", "10.0.0.1", 0)
{
    GetXn()->IpLogon(0, NULL, NULL);
    Sleep(200);
    IN_ADDR inaVip, inaSec;
    inaVip.s_addr = inet_addr("10.0.0.2");
    Verify(XNetServerToInAddr(inaVip, 0, &inaSec) == 0);
    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    Verify(s != SOCKET_ERROR);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr = inaSec;
    sin.sin_port = HTONS(2);
    Verify(connect(s, (struct sockaddr *)&sin, sizeof(sin)) != SOCKET_ERROR);
    SockTcpTransmit(s);
    Verify(closesocket(s) == 0);
}
XNETTEST_END(SgTcp_1)

XNETTEST_BEG(SgTcp_2, "xb2@Datacenter", "10.0.0.5", "255.0.0.0", "10.0.0.1", XNET_STARTUP_BYPASS_SECURITY)
{
    SOCKET s1 = SockTcpCreate(HTONS(80));
    Verify(listen(s1, 1) == 0);
    sockaddr_in sin;
    int slen = sizeof(sin);
    SOCKET s2 = accept(s1, (struct sockaddr *)&sin, &slen);
    Verify(s2 != SOCKET_ERROR);
    Verify(slen == sizeof(sin));
    SockTcpReflect(s2);
    Verify(closesocket(s2) == 0);
    Verify(closesocket(s1) == 0);
}
XNETTEST_END(SgTcp_2)

TWOTHREADTEST(SgTcp)
{
    printf("Testing SgTcp ");
    TWOTHREADRUN(SgTcp);
    printf(" [OK]\n");
}

// Test_SgRst ----------------------------------------------------------------------------

XNETTEST_BEG(SgRst_1, "xb1@Internet", "10.0.0.11", "255.0.0.0", "10.0.0.1", 0)
{
    GetXn()->IpLogon(0, NULL, NULL);
    Sleep(200);
    IN_ADDR inaVip, inaSec;
    inaVip.s_addr = inet_addr("10.0.0.2");
    Verify(XNetServerToInAddr(inaVip, 0, &inaSec) == 0);
    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    Verify(s != SOCKET_ERROR);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr = inaSec;
    sin.sin_port = HTONS(2);
    Verify(connect(s, (struct sockaddr *)&sin, sizeof(sin)) != SOCKET_ERROR);
    Verify(recv(s, (char *)&s, 1, 0) == 1);
    Verify(XNetUnregisterInAddr(inaSec) == 0);
    Verify(closesocket(s) == 0);
}
XNETTEST_END(SgRst_1)

XNETTEST_BEG(SgRst_2, "xb2@Datacenter", "10.0.0.5", "255.0.0.0", "10.0.0.1", XNET_STARTUP_BYPASS_SECURITY)
{
    SOCKET s1 = SockTcpCreate(HTONS(80));
    Verify(listen(s1, 1) == 0);
    sockaddr_in sin;
    int slen = sizeof(sin);
    SOCKET s2 = accept(s1, (struct sockaddr *)&sin, &slen);
    Verify(s2 != SOCKET_ERROR);
    Verify(slen == sizeof(sin));

    while (1)
    {
        INT err = send(s2, (char *)&s2, 1, 0);

        if (err == SOCKET_ERROR)
        {
            Verify(WSAGetLastError() == WSAECONNRESET);
            break;
        }

        Sleep(100);
    }

    Verify(closesocket(s2) == 0);
    Verify(closesocket(s1) == 0);
}
XNETTEST_END(SgRst_2)

TWOTHREADTEST(SgRst)
{
    printf("Testing SgRst ");
    TWOTHREADRUN(SgRst);
    printf("[OK]\n");
}

// Test_SgAlive --------------------------------------------------------------------------

XNETTEST_BEG(SgAlive_1, "xb1@Internet", "10.0.0.10", "255.0.0.0", "10.0.0.1", 0)
{
    GetXn()->IpLogon(0, NULL, NULL);
    Sleep(200);
    IN_ADDR inaVip, inaSec;
    inaVip.s_addr = inet_addr("10.0.0.2");
    Verify(XNetServerToInAddr(inaVip, 0, &inaSec) == 0);
    SOCKET s = SockUdpCreate(0, FALSE);
    Verify(s != SOCKET_ERROR);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr = inaSec;
    sin.sin_port = NTOHS(3);
    Verify(sendto(s, (char *)&inaVip, sizeof(inaVip), 0, (struct sockaddr *)&sin, sizeof(sin)) == sizeof(inaVip));

    // Sleep for a while to allow keepalives to flow
    Sleep(60 * 1000);

    sin.sin_family = AF_INET;
    sin.sin_addr = inaSec;
    sin.sin_port = NTOHS(3);
    Verify(sendto(s, (char *)&inaVip, sizeof(inaVip), 0, (struct sockaddr *)&sin, sizeof(sin)) == sizeof(inaVip));
    Verify(closesocket(s) == 0);
}
XNETTEST_END(SgAlive_1)

XNETTEST_BEG(SgAlive_2, "xb2@Datacenter", "10.0.0.6", "255.0.0.0", "10.0.0.1", XNET_STARTUP_BYPASS_SECURITY)
{
    SOCKET s = SockUdpCreate(HTONS(123));

    IN_ADDR inaVip;
    sockaddr_in sin;
    int slen = sizeof(sin);
    
    Verify(recvfrom(s, (char *)&inaVip, sizeof(inaVip), 0, (struct sockaddr *)&sin, &slen) == sizeof(inaVip));
    Verify(inaVip.s_addr == inet_addr("10.0.0.2"));

    CSgMsgAuthReq   sgar;

    sgar._wType   = SGMSG_TYPE_AUTHDATA_REQ;
    sgar._cbEnt   = sizeof(CSgMsgAuthReq);
    XNetRandom((BYTE *)&sgar._dwReqNum, sizeof(sgar._dwReqNum));
    sgar._ipaZ    = sin.sin_addr;
    sgar._ipportZ = sin.sin_port;
    
    sin.sin_port = NTOHS(0xFFFF);
    Verify(sendto(s, (char *)&sgar, sizeof(sgar), 0, (struct sockaddr *)&sin, sizeof(sin)) == sizeof(sgar));

    BYTE            ab[CBSGAUTHREPMSG];
    CSgMsgAuthRep * psgap     = (CSgMsgAuthRep *)ab;
    CSgMsgHdr *     psgmh     = (CSgMsgHdr *)(psgap + 1);
    CAuthData *     pAuthData = (CAuthData *)(psgmh + 1);

    slen = sizeof(sin);
    Verify(recvfrom(s, (char *)ab, sizeof(ab), 0, (struct sockaddr *)&sin, &slen) == sizeof(ab));
    Verify(psgap->_wType == SGMSG_TYPE_AUTHDATA_REP);
    Verify(psgap->_cbEnt == sizeof(CSgMsgAuthRep));
    Verify(psgap->_dwReqNum == sgar._dwReqNum);
    Verify(psgap->_ipaZ.s_addr == sgar._ipaZ.s_addr);
    Verify(psgap->_ipaI.s_addr == inet_addr("10.0.0.10"));
    Verify(psgap->_ipportZ == sgar._ipportZ);
    Verify(psgap->_fNotFound == FALSE);
    Verify(psgmh->_wType == SGMSG_TYPE_AUTHDATA);
    Verify(psgmh->_cbEnt == sizeof(CSgMsgHdr) + sizeof(CAuthData));
    Verify(pAuthData->GetCb() == sizeof(CAuthData));

    slen = sizeof(sin);
    Verify(recvfrom(s, (char *)&inaVip, sizeof(inaVip), 0, (struct sockaddr *)&sin, &slen) == sizeof(inaVip));
    Verify(inaVip.s_addr == inet_addr("10.0.0.2"));

    Verify(closesocket(s) == 0);
}
XNETTEST_END(SgAlive_2)

TWOTHREADTEST(SgAlive)
{
    printf("Testing SgAlive ");
    TWOTHREADRUN(SgAlive);
    printf("[OK]\n");
}

// Test_SgMsg ----------------------------------------------------------------------------

XNETTEST_BEG(SgMsg_1, "xb1@Internet", "10.0.0.10", "255.0.0.0", "10.0.0.1", 0)
{
    GetXn()->IpLogon(0, NULL, NULL);
    Sleep(200);
    IN_ADDR inaVip, inaSec;
    inaVip.s_addr = inet_addr("10.0.0.2");
    Verify(XNetServerToInAddr(inaVip, 0, &inaSec) == 0);
    SOCKET s = SockUdpCreate(0, FALSE);
    Verify(s != SOCKET_ERROR);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr = inaSec;
    sin.sin_port = NTOHS(3);
    Verify(sendto(s, (char *)&inaVip, sizeof(inaVip), 0, (struct sockaddr *)&sin, sizeof(sin)) == sizeof(inaVip));
    Verify(closesocket(s) == 0);
}
XNETTEST_END(SgMsg_1)

XNETTEST_BEG(SgMsg_2, "xb2@Datacenter", "10.0.0.6", "255.0.0.0", "10.0.0.1", XNET_STARTUP_BYPASS_SECURITY)
{
    SOCKET s = SockUdpCreate(HTONS(123));

    IN_ADDR inaVip;
    sockaddr_in sin;
    int slen = sizeof(sin);
    
    Verify(recvfrom(s, (char *)&inaVip, sizeof(inaVip), 0, (struct sockaddr *)&sin, &slen) == sizeof(inaVip));
    Verify(inaVip.s_addr == inet_addr("10.0.0.2"));

    CSgMsgAuthReq   sgar;

    sgar._wType   = SGMSG_TYPE_AUTHDATA_REQ;
    sgar._cbEnt   = sizeof(CSgMsgAuthReq);
    XNetRandom((BYTE *)&sgar._dwReqNum, sizeof(sgar._dwReqNum));
    sgar._ipaZ    = sin.sin_addr;
    sgar._ipportZ = sin.sin_port;
    
    sin.sin_port = NTOHS(0xFFFF);
    Verify(sendto(s, (char *)&sgar, sizeof(sgar), 0, (struct sockaddr *)&sin, sizeof(sin)) == sizeof(sgar));

    BYTE            ab[CBSGAUTHREPMSG];
    CSgMsgAuthRep * psgap     = (CSgMsgAuthRep *)ab;
    CSgMsgHdr *     psgmh     = (CSgMsgHdr *)(psgap + 1);
    CAuthData *     pAuthData = (CAuthData *)(psgmh + 1);

    slen = sizeof(sin);
    Verify(recvfrom(s, (char *)ab, sizeof(ab), 0, (struct sockaddr *)&sin, &slen) == sizeof(ab));
    Verify(psgap->_wType == SGMSG_TYPE_AUTHDATA_REP);
    Verify(psgap->_cbEnt == sizeof(CSgMsgAuthRep));
    Verify(psgap->_dwReqNum == sgar._dwReqNum);
    Verify(psgap->_ipaZ.s_addr == sgar._ipaZ.s_addr);
    Verify(psgap->_ipaI.s_addr == inet_addr("10.0.0.10"));
    Verify(psgap->_ipportZ == sgar._ipportZ);
    Verify(psgap->_fNotFound == FALSE);
    Verify(psgmh->_wType == SGMSG_TYPE_AUTHDATA);
    Verify(psgmh->_cbEnt == sizeof(CSgMsgHdr) + sizeof(CAuthData));
    Verify(pAuthData->GetCb() == sizeof(CAuthData));
    Verify(closesocket(s) == 0);
}
XNETTEST_END(SgMsg_2)

TWOTHREADTEST(SgMsg)
{
    printf("Testing SgMsg ");
    TWOTHREADRUN(SgMsg);
    printf("[OK]\n");
}

// Test_SgLogon --------------------------------------------------------------------------

XNETTEST_BEG(SgLogon_1, "xb1@Internet", "10.0.0.10", "255.0.0.0", "10.0.0.1", 0)
{
    IN_ADDR inaVip, inaSec;
    HANDLE hEventLogon = NULL;
    ULONGLONG aqwUserId[4];
    XNKID axnkid[4];
    BYTE abData[32];
    UINT iUser;

    // Let the other thread start up so it doesn't miss our first UDP packet
    Sleep(500);

    XNetRandom((BYTE *)aqwUserId, sizeof(aqwUserId));
    XNetRandom((BYTE *)axnkid, sizeof(axnkid));
    XNetRandom(abData, sizeof(abData));

    inaVip.s_addr = inet_addr("10.0.0.2");
    Verify(XNetServerToInAddr(inaVip, 0, &inaSec) == 0);
    Verify((hEventLogon = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL);
    GetXn()->IpLogon(inaSec.s_addr, aqwUserId, hEventLogon);
    Verify(WaitForSingleObject(hEventLogon, INFINITE) == WAIT_OBJECT_0);
    Verify(GetXn()->IpLogonGetStatus(NULL) == XN_LOGON_STATE_ONLINE);

    XNADDR xnaddr;
    Verify(XNetGetTitleXnAddr(&xnaddr) != XNET_GET_XNADDR_PENDING);

    SOCKET s = SockUdpCreate(0, FALSE);
    Verify(s != SOCKET_ERROR);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr = inaSec;
    sin.sin_port = NTOHS(3);
    Verify(sendto(s, (char *)&inaVip, sizeof(inaVip), 0, (struct sockaddr *)&sin, sizeof(sin)) == sizeof(inaVip));

    printf(".");

    for (iUser = 0; iUser < 4; ++iUser)
    {
        Verify(GetXn()->IpLogonSetPState(0, ~iUser, &axnkid[0], iUser ? (iUser + 1) * 2 : 0, &abData[iUser * 8]));
        printf(".");
    }

    // Sleep for a while to allow keepalives to flow
    for (int i = 0; i < 5; i++)
    {
        Sleep(4 * 1000);
        printf(".");
    }

    sin.sin_family = AF_INET;
    sin.sin_addr = inaSec;
    sin.sin_port = NTOHS(3);
    Verify(sendto(s, (char *)&inaVip, sizeof(inaVip), 0, (struct sockaddr *)&sin, sizeof(sin)) == sizeof(inaVip));
    Verify(closesocket(s) == 0);
    printf(".");

    GetXn()->IpLogoff();
    printf(".");

    CloseHandle(hEventLogon);
}
XNETTEST_END(SgLogon_1)

XNETTEST_BEG(SgLogon_2, "xb2@Datacenter", "10.0.0.6", "255.0.0.0", "10.0.0.1", XNET_STARTUP_BYPASS_SECURITY)
{
    SOCKET s = SockUdpCreate(HTONS(123));

    IN_ADDR inaVip;
    sockaddr_in sin;
    int slen = sizeof(sin);
    
    Verify(recvfrom(s, (char *)&inaVip, sizeof(inaVip), 0, (struct sockaddr *)&sin, &slen) == sizeof(inaVip));
    Verify(inaVip.s_addr == inet_addr("10.0.0.2"));
    printf(".");

    CSgMsgAuthReq   sgar;

    sgar._wType   = SGMSG_TYPE_AUTHDATA_REQ;
    sgar._cbEnt   = sizeof(CSgMsgAuthReq);
    XNetRandom((BYTE *)&sgar._dwReqNum, sizeof(sgar._dwReqNum));
    sgar._ipaZ    = sin.sin_addr;
    sgar._ipportZ = sin.sin_port;
    
    sin.sin_port = NTOHS(0xFFFF);
    Verify(sendto(s, (char *)&sgar, sizeof(sgar), 0, (struct sockaddr *)&sin, sizeof(sin)) == sizeof(sgar));

    BYTE            ab[CBSGAUTHREPMSG];
    CSgMsgAuthRep * psgap     = (CSgMsgAuthRep *)ab;
    CSgMsgHdr *     psgmh     = (CSgMsgHdr *)(psgap + 1);
    CAuthData *     pAuthData = (CAuthData *)(psgmh + 1);

    slen = sizeof(sin);
    Verify(recvfrom(s, (char *)ab, sizeof(ab), 0, (struct sockaddr *)&sin, &slen) == sizeof(ab));
    Verify(psgap->_wType == SGMSG_TYPE_AUTHDATA_REP);
    Verify(psgap->_cbEnt == sizeof(CSgMsgAuthRep));
    Verify(psgap->_dwReqNum == sgar._dwReqNum);
    Verify(psgap->_ipaZ.s_addr == sgar._ipaZ.s_addr);
    Verify(psgap->_ipaI.s_addr == inet_addr("10.0.0.10"));
    Verify(psgap->_ipportZ == sgar._ipportZ);
    Verify(psgap->_fNotFound == FALSE);
    Verify(psgmh->_wType == SGMSG_TYPE_AUTHDATA);
    Verify(psgmh->_cbEnt == sizeof(CSgMsgHdr) + sizeof(CAuthData));
    Verify(pAuthData->GetCb() == sizeof(CAuthData));

    slen = sizeof(sin);
    Verify(recvfrom(s, (char *)&inaVip, sizeof(inaVip), 0, (struct sockaddr *)&sin, &slen) == sizeof(inaVip));
    Verify(inaVip.s_addr == inet_addr("10.0.0.2"));

    Verify(closesocket(s) == 0);
}
XNETTEST_END(SgLogon_2)

TWOTHREADTEST(SgLogon)
{
    printf("Testing SgLogon ");
    TWOTHREADRUN(SgLogon);
    printf(" [OK]\n");
}

// Test_XnSetEventTimer ------------------------------------------------------------------

IPTEST_BEG(XnSetEventTimer, "xb1@Internet", "10.0.0.1", "255.255.0.0", NULL, 0)
{
    printf("Testing XnSetEventTimer ");
    
    CEventTimer et;
    HANDLE hEvent;

    memset(&et, 0, sizeof(et));
    Verify((hEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL);
    DWORD dwTickBeg = GetTickCount();
    IpSetEventTimer(&et, hEvent, 1000);
    IpSetEventTimer(&et, hEvent, 0xFFFFFFFF);
    CloseHandle(hEvent);
    hEvent = NULL;

    for (DWORD dwTimeout = 50; dwTimeout < 1000; dwTimeout += 160)
    {
        for (int i = 0; i < 5; ++i)
        {
            memset(&et, 0, sizeof(et));
            Verify((hEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL);
            DWORD dwTickBeg = GetTickCount();
            IpSetEventTimer(&et, hEvent, dwTimeout);
            Verify(WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0);
            CloseHandle(hEvent);
            hEvent = NULL;
            DWORD dwTickEnd = GetTickCount();
            Verify(dwTickEnd - dwTickBeg > dwTimeout);
            printf(".");
            Sleep(17 * i);
        }
    }

    printf(" [OK]\n");
}
IPTEST_END(XnSetEventTimer);

// Test_NatDhcp --------------------------------------------------------------------------

XNETTEST_BEG(NatDhcp_1, "xb1@Nat1Inner", NULL, NULL, NULL, 0)
{
    XNADDR xnaddr;
    DWORD dw;

    while (1)
    {
        dw = XNetGetTitleXnAddr(&xnaddr);
        if (dw != XNET_GET_XNADDR_PENDING)
            break;
        Sleep(100);
    }

    Verify(dw & XNET_GET_XNADDR_DHCP);
}
XNETTEST_END(NatDhcp_1)

void Test_NatDhcp()
{
    for (UINT iAssign = NAT_ASSIGN_MINIMAL; iAssign <= NAT_ASSIGN_AGGRESSIVE; ++iAssign)
    {
        for (UINT iFilter = NAT_FILTER_NONE; iFilter <= NAT_FILTER_ADDRESS_PORT; ++iFilter)
        {
            printf("Testing NatDhcp %s/%s ", iAssign == NAT_ASSIGN_MINIMAL ? "min" : "agr",
                   iFilter == NAT_FILTER_NONE ? "none" : iFilter == NAT_FILTER_ADDRESS ? "addr" :
                   iFilter == NAT_FILTER_PORT ? "port" : "both");

            NATCREATE natcreate = { 0 };

            strcpy(natcreate._achXboxInner, "Nat@Nat1Inner");
            strcpy(natcreate._achXboxOuter, "Nat@Nat1Outer");

            natcreate._xncpInner.ina.s_addr         = inet_addr("10.0.0.1");
            natcreate._xncpInner.inaMask.s_addr     = inet_addr("255.255.0.0");
            natcreate._xncpOuter.ina.s_addr         = inet_addr("10.0.0.1");
            natcreate._xncpOuter.inaMask.s_addr     = inet_addr("255.255.0.0");
            natcreate._inaBase.s_addr               = inet_addr("10.0.1.0");
            natcreate._inaLast.s_addr               = inet_addr("10.0.254.254");
            natcreate._dwLeaseTime                  = 60 * 60 * 24;
            natcreate._iAssign                      = iAssign;
            natcreate._iFilter                      = iFilter;

            void * pvNat = XNetNatCreate(&natcreate);
            Verify(pvNat != NULL);

            Test_NatDhcp_1();

            XNetNatDelete(pvNat);

            printf("[OK]\n");
        }
    }
}

// Test_NatDns ---------------------------------------------------------------------------

XNETTEST_BEG(NatDns_1, "xb1@Nat1Inner", NULL, NULL, NULL, 0)
{
    XNADDR xnaddr;
    while (XNetGetTitleXnAddr(&xnaddr) == XNET_GET_XNADDR_PENDING)
        Sleep(100);
    Verify((_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL);
    
    XNDNS * pxndns = NULL;

    Verify(XNetDnsLookup("nosuchhost.foo.bar.com", _hEvent, &pxndns) == 0);
    Verify(WaitForSingleObject(_hEvent, INFINITE) == WAIT_OBJECT_0);
    Verify(pxndns->iStatus == WSAHOST_NOT_FOUND);
    Verify(pxndns->cina == 0);
    Verify(XNetDnsRelease(pxndns) == 0);

    Verify(XNetDnsLookup("products.redmond.corp.microsoft.com", _hEvent, &pxndns) == 0);
    Verify(WaitForSingleObject(_hEvent, INFINITE) == WAIT_OBJECT_0);
    Verify(pxndns->iStatus == 0);
    Verify(pxndns->cina > 0);
    Verify(XNetDnsRelease(pxndns) == 0);

    Verify(XNetDnsLookup("products.redmond.corp.microsoft.com", NULL, &pxndns) == 0);
    while (pxndns->iStatus == WSAEINPROGRESS)
        Sleep(100);
    Verify(pxndns->iStatus == 0);
    Verify(pxndns->cina > 0);
    Verify(XNetDnsRelease(pxndns) == 0);

    CloseHandle(_hEvent);
}

HANDLE _hEvent;

XNETTEST_END(NatDns_1)

void Test_NatDns()
{
    for (UINT iAssign = NAT_ASSIGN_MINIMAL; iAssign <= NAT_ASSIGN_AGGRESSIVE; ++iAssign)
    {
        for (UINT iFilter = NAT_FILTER_NONE; iFilter <= NAT_FILTER_ADDRESS_PORT; ++iFilter)
        {
            printf("Testing NatDns %s/%s ", iAssign == NAT_ASSIGN_MINIMAL ? "min" : "agr",
                   iFilter == NAT_FILTER_NONE ? "none" : iFilter == NAT_FILTER_ADDRESS ? "addr" :
                   iFilter == NAT_FILTER_PORT ? "port" : "both");

            if (!VLanDriver())
            {
                printf("[No VLan]\n");
                return;
            }

            NATCREATE natcreate = { 0 };

            strcpy(natcreate._achXboxInner, "Nat1@Nat1Inner");
            strcpy(natcreate._achXboxOuter, "Nat1@Nic/0");

            natcreate._xncpInner.ina.s_addr         = inet_addr("10.0.0.1");
            natcreate._xncpInner.inaMask.s_addr     = inet_addr("255.255.0.0");
            natcreate._inaBase.s_addr               = inet_addr("10.0.1.0");
            natcreate._inaLast.s_addr               = inet_addr("10.0.254.254");
            natcreate._dwLeaseTime                  = 60 * 60 * 24;
            natcreate._iAssign                      = iAssign;
            natcreate._iFilter                      = iFilter;

            void * pvNat = XNetNatCreate(&natcreate);
            Verify(pvNat != NULL);

            Test_NatDns_1();

            XNetNatDelete(pvNat);

            printf("[OK]\n");
        }
    }
}

// Test_NatUdp ---------------------------------------------------------------------------

XNETTEST_BEG(NatUdp_1, "xb1@Nat1Inner", NULL, NULL, NULL, XNET_STARTUP_BYPASS_SECURITY)
{
    XNADDR xnaddr;
    while (XNetGetTitleXnAddr(&xnaddr) == XNET_GET_XNADDR_PENDING)
        Sleep(100);

    SOCKET s = SockUdpCreate(CIpPort(NTOHS(6543)));
    SockUdpTransmit(s, inet_addr("192.168.0.2"), CIpPort(NTOHS(1567)));
    Verify(closesocket(s) == 0);
}
XNETTEST_END(NatUdp_1);

XNETTEST_BEG(NatUdp_2, "xb2@Nat1Outer", "192.168.0.2", "255.255.0.0", NULL, XNET_STARTUP_BYPASS_SECURITY)
{
    SOCKET s = SockUdpCreate(NTOHS(1567));
    SockUdpReflect(s);
    Verify(closesocket(s) == 0);
}
XNETTEST_END(NatUdp_2);

TWOTHREADTEST(NatUdp)
{
    for (UINT iAssign = NAT_ASSIGN_MINIMAL; iAssign <= NAT_ASSIGN_AGGRESSIVE; ++iAssign)
    {
        for (UINT iFilter = NAT_FILTER_NONE; iFilter <= NAT_FILTER_ADDRESS_PORT; ++iFilter)
        {
            printf("Testing NatUdp %s/%s ", iAssign == NAT_ASSIGN_MINIMAL ? "min" : "agr",
                   iFilter == NAT_FILTER_NONE ? "none" : iFilter == NAT_FILTER_ADDRESS ? "addr" :
                   iFilter == NAT_FILTER_PORT ? "port" : "both");

            NATCREATE natcreate = { 0 };

            strcpy(natcreate._achXboxInner, "Nat1@Nat1Inner");
            strcpy(natcreate._achXboxOuter, "Nat1@Nat1Outer");

            natcreate._xncpInner.ina.s_addr         = inet_addr("10.0.0.1");
            natcreate._xncpInner.inaMask.s_addr     = inet_addr("255.255.0.0");
            natcreate._xncpOuter.ina.s_addr         = inet_addr("192.168.0.1");
            natcreate._xncpOuter.inaMask.s_addr     = inet_addr("255.255.0.0");
            natcreate._inaBase.s_addr               = inet_addr("10.0.1.0");
            natcreate._inaLast.s_addr               = inet_addr("10.0.254.254");
            natcreate._dwLeaseTime                  = 60 * 60 * 24;
            natcreate._iAssign                      = iAssign;
            natcreate._iFilter                      = iFilter;

            void * pvNat = XNetNatCreate(&natcreate);
            Verify(pvNat != NULL);

            TWOTHREADRUN(NatUdp);

            XNetNatDelete(pvNat);

            printf(" [OK]\n");
        }
    }
}

// Test_NatTcp ---------------------------------------------------------------------------

XNETTEST_BEG(NatTcp_1, "xb1@Nat1Inner", NULL, NULL, NULL, XNET_STARTUP_BYPASS_SECURITY)
{
    XNADDR xnaddr;
    while (XNetGetTitleXnAddr(&xnaddr) == XNET_GET_XNADDR_PENDING)
        Sleep(100);

    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    Verify(s != SOCKET_ERROR);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr("192.168.0.2");
    sin.sin_port = HTONS(80);
    Verify(connect(s, (struct sockaddr *)&sin, sizeof(sin)) != SOCKET_ERROR);
    SockTcpTransmit(s);
    Verify(closesocket(s) == 0);
}
XNETTEST_END(NatTcp_1)

XNETTEST_BEG(NatTcp_2, "xb2@Nat1Outer", "192.168.0.2", "255.255.0.0", NULL, XNET_STARTUP_BYPASS_SECURITY)
{
    SOCKET s1 = SockTcpCreate(HTONS(80));
    Verify(listen(s1, 1) == 0);
    sockaddr_in sin;
    int slen = sizeof(sin);
    SOCKET s2 = accept(s1, (struct sockaddr *)&sin, &slen);
    Verify(s2 != SOCKET_ERROR);
    Verify(slen == sizeof(sin));
    SockTcpReflect(s2);
    Verify(closesocket(s2) == 0);
    Verify(closesocket(s1) == 0);
}
XNETTEST_END(NatTcp_2)

TWOTHREADTEST(NatTcp)
{
    for (UINT iAssign = NAT_ASSIGN_MINIMAL; iAssign <= NAT_ASSIGN_AGGRESSIVE; ++iAssign)
    {
        for (UINT iFilter = NAT_FILTER_NONE; iFilter <= NAT_FILTER_ADDRESS_PORT; ++iFilter)
        {
            printf("Testing NatTcp %s/%s ", iAssign == NAT_ASSIGN_MINIMAL ? "min" : "agr",
                   iFilter == NAT_FILTER_NONE ? "none" : iFilter == NAT_FILTER_ADDRESS ? "addr" :
                   iFilter == NAT_FILTER_PORT ? "port" : "both");

            NATCREATE natcreate = { 0 };

            strcpy(natcreate._achXboxInner, "Nat1@Nat1Inner");
            strcpy(natcreate._achXboxOuter, "Nat1@Nat1Outer");

            natcreate._xncpInner.ina.s_addr         = inet_addr("10.0.0.1");
            natcreate._xncpInner.inaMask.s_addr     = inet_addr("255.255.0.0");
            natcreate._xncpOuter.ina.s_addr         = inet_addr("192.168.0.1");
            natcreate._xncpOuter.inaMask.s_addr     = inet_addr("255.255.0.0");
            natcreate._inaBase.s_addr               = inet_addr("10.0.1.0");
            natcreate._inaLast.s_addr               = inet_addr("10.0.254.254");
            natcreate._dwLeaseTime                  = 60 * 60 * 24;
            natcreate._iAssign                      = iAssign;
            natcreate._iFilter                      = iFilter;

            void * pvNat = XNetNatCreate(&natcreate);
            Verify(pvNat != NULL);

            TWOTHREADRUN(NatTcp);

            XNetNatDelete(pvNat);

            printf(" [OK]\n");
        }
    }
}

// Test_SgNat1Xb1ToNat2Xb2 ---------------------------------------------------------------

XNETTEST_BEG(SgXb1ToXb2_1, "xb1@Internet", "10.0.0.10", "255.0.0.0", "10.0.0.1", 0)
{
    SgTcpXbToXbClient();
}
XNETTEST_END(SgXb1ToXb2_1)

XNETTEST_BEG(SgXb1ToXb2_2, "xb2@Internet", "10.0.0.11", "255.0.0.0", "10.0.0.1", 0)
{
    SgTcpXbToXbServer();
}
XNETTEST_END(SgXb1ToXb2_2)

TWOTHREADTEST(SgXb1ToXb2)
{
    TWOTHREADRUN(SgXb1ToXb2);
}

XNETTEST_BEG(SgXb1ToNat2Xb2_1, "xb1@Internet", "10.0.0.10", "255.0.0.0", "10.0.0.1", 0)
{
    SgTcpXbToXbClient();
}
XNETTEST_END(SgXb1ToNat2Xb2_1)

XNETTEST_BEG(SgXb1ToNat2Xb2_2, "xb2@Nat2Inner", NULL, NULL, NULL, 0)
{
    SgTcpXbToXbServer();
}
XNETTEST_END(SgXb1ToNat2Xb2_2)

TWOTHREADTEST(SgXb1ToNat2Xb2)
{
    TWOTHREADRUN(SgXb1ToNat2Xb2);
}

XNETTEST_BEG(SgNat1Xb1ToXb2_1, "xb1@Nat1Inner", NULL, NULL, NULL, 0)
{
    SgTcpXbToXbClient();
}
XNETTEST_END(SgNat1Xb1ToXb2_1)

XNETTEST_BEG(SgNat1Xb1ToXb2_2, "xb2@Internet", "10.0.0.10", "255.0.0.0", NULL, 0)
{
    SgTcpXbToXbServer();
}
XNETTEST_END(SgNat1Xb1ToXb2_2)

TWOTHREADTEST(SgNat1Xb1ToXb2)
{
    TWOTHREADRUN(SgNat1Xb1ToXb2);
}

XNETTEST_BEG(SgNat1Xb1ToNat2Xb2_1, "xb1@Nat1Inner", NULL, NULL, NULL, 0)
{
    SgTcpXbToXbClient();
}
XNETTEST_END(SgNat1Xb1ToNat2Xb2_1)

XNETTEST_BEG(SgNat1Xb1ToNat2Xb2_2, "xb2@Nat2Inner", NULL, NULL, NULL, 0)
{
    SgTcpXbToXbServer();
}
XNETTEST_END(SgNat1Xb1ToNat2Xb2_2)

TWOTHREADTEST(SgNat1Xb1ToNat2Xb2)
{
    TWOTHREADRUN(SgNat1Xb1ToNat2Xb2);
}

XNETTEST_BEG(SgNat1Xb1ToNat1Xb2_1, "xb1@Nat1Inner", NULL, NULL, NULL, 0)
{
    SgTcpXbToXbClient();
}
XNETTEST_END(SgNat1Xb1ToNat1Xb2_1)

XNETTEST_BEG(SgNat1Xb1ToNat1Xb2_2, "xb2@Nat1Inner", NULL, NULL, NULL, 0)
{
    SgTcpXbToXbServer();
}
XNETTEST_END(SgNat1Xb1ToNat1Xb2_2)

TWOTHREADTEST(SgNat1Xb1ToNat1Xb2)
{
    TWOTHREADRUN(SgNat1Xb1ToNat1Xb2);
}

void Test_SgNat()
{
    printf("Testing SgNat\n\n");
    printf("         Direct Min/-- Min/A- Min/-P Min/AP Agr/-- Agr/A- Agr/-P Agr/AP\n");

    UINT    aiAssign[] = { NAT_ASSIGN_MINIMAL, NAT_ASSIGN_MINIMAL, NAT_ASSIGN_MINIMAL, NAT_ASSIGN_MINIMAL, NAT_ASSIGN_AGGRESSIVE, NAT_ASSIGN_AGGRESSIVE, NAT_ASSIGN_AGGRESSIVE, NAT_ASSIGN_AGGRESSIVE };
    UINT    aiFilter[] = { NAT_FILTER_NONE, NAT_FILTER_ADDRESS, NAT_FILTER_PORT, NAT_FILTER_ADDRESS_PORT, NAT_FILTER_NONE, NAT_FILTER_ADDRESS, NAT_FILTER_PORT, NAT_FILTER_ADDRESS_PORT };
    UINT    iAssign1, iAssign2, iFilter1, iFilter2;
    void *  pvNat1, * pvNat2;
    UINT    i, j;

    printf("  OneNat ");
    TWOTHREADRUN(SgXb1ToXb2);
    printf(g_fXb1ToXb2Timeout ? "[F:%2d] " : "%6d ", g_cSecsXb1ToXb2Connect);

    for (i = 0; i < dimensionof(aiAssign); ++i)
    {
        iAssign1 = aiAssign[i];
        iFilter1 = aiFilter[i];

        NATCREATE natcreate1 = { 0 };

        strcpy(natcreate1._achXboxOuter, "Nat1@Internet");
        natcreate1._xncpOuter.ina.s_addr         = inet_addr("10.0.0.253");
        natcreate1._xncpOuter.inaMask.s_addr     = inet_addr("255.255.0.0");

        strcpy(natcreate1._achXboxInner, "Nat1@Nat1Inner");
        natcreate1._xncpInner.ina.s_addr         = inet_addr("192.168.0.1");
        natcreate1._xncpInner.inaMask.s_addr     = inet_addr("255.255.0.0");
        natcreate1._inaBase.s_addr               = inet_addr("192.168.0.2");
        natcreate1._inaLast.s_addr               = inet_addr("192.168.254.254");

        natcreate1._dwLeaseTime                  = 60 * 60 * 24;
        natcreate1._iAssign                      = iAssign1;
        natcreate1._iFilter                      = iFilter1;

        pvNat1 = XNetNatCreate(&natcreate1);
        Verify(pvNat1 != NULL);
        TWOTHREADRUN(SgNat1Xb1ToNat1Xb2);
        XNetNatDelete(pvNat1);
        printf(g_fXb1ToXb2Timeout ? "[F:%2d] " : "%6d ", g_cSecsXb1ToXb2Connect);
    }

    printf("\n");
    printf("  Direct ");

    TWOTHREADRUN(SgXb1ToXb2);
    printf(g_fXb1ToXb2Timeout ? "[F:%2d] " : "%6d ", g_cSecsXb1ToXb2Connect);

    for (i = 0; i < dimensionof(aiAssign); ++i)
    {
        iAssign2 = aiAssign[i];
        iFilter2 = aiFilter[i];

        NATCREATE natcreate2 = { 0 };

        strcpy(natcreate2._achXboxOuter, "Nat2@Internet");
        natcreate2._xncpOuter.ina.s_addr         = inet_addr("10.0.0.254");
        natcreate2._xncpOuter.inaMask.s_addr     = inet_addr("255.255.0.0");

        strcpy(natcreate2._achXboxInner, "Nat2@Nat2Inner");
        natcreate2._xncpInner.ina.s_addr         = inet_addr("192.168.0.1");
        natcreate2._xncpInner.inaMask.s_addr     = inet_addr("255.255.0.0");
        natcreate2._inaBase.s_addr               = inet_addr("192.168.0.2");
        natcreate2._inaLast.s_addr               = inet_addr("192.168.254.254");

        natcreate2._dwLeaseTime                  = 60 * 60 * 24;
        natcreate2._iAssign                      = iAssign2;
        natcreate2._iFilter                      = iFilter2;
        natcreate2._iNatPortBase                 = 2000;

        pvNat2 = XNetNatCreate(&natcreate2);
        Verify(pvNat2 != NULL);
        TWOTHREADRUN(SgXb1ToNat2Xb2);
        XNetNatDelete(pvNat2);
        printf(g_fXb1ToXb2Timeout ? "[F:%2d] " : "%6d ", g_cSecsXb1ToXb2Connect);
    }

    printf("\n");

    for (i = 0; i < dimensionof(aiAssign); ++i)
    {
        iAssign1 = aiAssign[i];
        iFilter1 = aiFilter[i];

        printf("  %s/%c%c ", iAssign1 == NAT_ASSIGN_MINIMAL ? "Min" : "Agr",
               (iFilter1 & NAT_FILTER_ADDRESS) ? 'A' : '-', (iFilter1 & NAT_FILTER_PORT) ? 'P' : '-');

        NATCREATE natcreate1 = { 0 };

        strcpy(natcreate1._achXboxOuter, "Nat1@Internet");
        natcreate1._xncpOuter.ina.s_addr         = inet_addr("10.0.0.253");
        natcreate1._xncpOuter.inaMask.s_addr     = inet_addr("255.255.0.0");

        strcpy(natcreate1._achXboxInner, "Nat1@Nat1Inner");
        natcreate1._xncpInner.ina.s_addr         = inet_addr("192.168.0.1");
        natcreate1._xncpInner.inaMask.s_addr     = inet_addr("255.255.0.0");
        natcreate1._inaBase.s_addr               = inet_addr("192.168.0.2");
        natcreate1._inaLast.s_addr               = inet_addr("192.168.254.254");

        natcreate1._dwLeaseTime                  = 60 * 60 * 24;
        natcreate1._iAssign                      = iAssign1;
        natcreate1._iFilter                      = iFilter1;

        pvNat1 = XNetNatCreate(&natcreate1);
        Verify(pvNat1 != NULL);
        TWOTHREADRUN(SgNat1Xb1ToXb2);
        XNetNatDelete(pvNat1);
        printf(g_fXb1ToXb2Timeout ? "[F:%2d] " : "%6d ", g_cSecsXb1ToXb2Connect);

        for (j = 0; j < dimensionof(aiAssign); ++j)
        {
            iAssign2 = aiAssign[j];
            iFilter2 = aiFilter[j];

            NATCREATE natcreate1 = { 0 };

            strcpy(natcreate1._achXboxOuter, "Nat1@Internet");
            natcreate1._xncpOuter.ina.s_addr         = inet_addr("10.0.0.253");
            natcreate1._xncpOuter.inaMask.s_addr     = inet_addr("255.255.0.0");

            strcpy(natcreate1._achXboxInner, "Nat1@Nat1Inner");
            natcreate1._xncpInner.ina.s_addr         = inet_addr("192.168.0.1");
            natcreate1._xncpInner.inaMask.s_addr     = inet_addr("255.255.0.0");
            natcreate1._inaBase.s_addr               = inet_addr("192.168.0.2");
            natcreate1._inaLast.s_addr               = inet_addr("192.168.254.254");

            natcreate1._dwLeaseTime                  = 60 * 60 * 24;
            natcreate1._iAssign                      = iAssign1;
            natcreate1._iFilter                      = iFilter1;

            pvNat1 = XNetNatCreate(&natcreate1);
            Verify(pvNat1 != NULL);

            NATCREATE natcreate2 = { 0 };

            strcpy(natcreate2._achXboxOuter, "Nat2@Internet");
            natcreate2._xncpOuter.ina.s_addr         = inet_addr("10.0.0.254");
            natcreate2._xncpOuter.inaMask.s_addr     = inet_addr("255.255.0.0");

            strcpy(natcreate2._achXboxInner, "Nat2@Nat2Inner");
            natcreate2._xncpInner.ina.s_addr         = inet_addr("192.168.0.1");
            natcreate2._xncpInner.inaMask.s_addr     = inet_addr("255.255.0.0");
            natcreate2._inaBase.s_addr               = inet_addr("192.168.0.2");
            natcreate2._inaLast.s_addr               = inet_addr("192.168.254.254");

            natcreate2._dwLeaseTime                  = 60 * 60 * 24;
            natcreate2._iAssign                      = iAssign2;
            natcreate2._iFilter                      = iFilter2;
            natcreate2._iNatPortBase                 = 2000;

            pvNat2 = XNetNatCreate(&natcreate2);
            Verify(pvNat2 != NULL);
            TWOTHREADRUN(SgNat1Xb1ToNat2Xb2);
            XNetNatDelete(pvNat2);
            XNetNatDelete(pvNat1);

            printf(g_fXb1ToXb2Timeout ? "[F:%2d] " : "%6d ", g_cSecsXb1ToXb2Connect);
        }
            
        printf("\n");
    }

    printf("\n");
}

// ---------------------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------------------

int     g_argc;
char ** g_argv;

BOOL RunTest(char * pszName, ...)
{
    BOOL fByNameOnly = FALSE;

    va_list va;

    va_start(va, pszName);

    while (pszName)
    {
        if (*pszName == '*')
        {
            fByNameOnly = TRUE;
            pszName += 1;
        }
        else
        {
            fByNameOnly = FALSE;
        }

        if (!fByNameOnly && g_argc == 1)
        {
            return(TRUE);
        }

        for (int i = 1; i < g_argc; ++i)
        {
            if (g_argv[i] && lstrcmpiA(g_argv[i], pszName) == 0)
                return(TRUE);
        }

        pszName = va_arg(va, char *);
    }

    return(FALSE);
}

int __cdecl main(int argc, char * argv[])
{
    g_argc = argc;
    g_argv = argv;

    Verify(VLanInit());

    if (RunTest("inet_addr", NULL))                     Test_inet_addr();
    if (RunTest("XNetRandom", NULL))                    Test_XNetRandom();
    if (RunTest("XNetRegisterKey", NULL))               Test_XNetRegisterKey();
    if (RunTest("XNetXnAddrToInAddr", NULL))            Test_XNetXnAddrToInAddr();
    if (RunTest("Broadcast", NULL))                     Test_Broadcast();
    if (RunTest("Discovery", NULL))                     Test_Discovery();
    if (RunTest("Frag", NULL))                          Test_Frag();
    if (RunTest("XnSetEventTimer", NULL))               Test_XnSetEventTimer();
    if (RunTest("SockUdp", NULL))                       Test_SockUdp();
    if (RunTest("SockTcp", NULL))                       Test_SockTcp();
    if (RunTest("Dhcp", NULL))                          Test_Dhcp();
    if (RunTest("Dns", NULL))                           Test_Dns();
    if (RunTest("NatDhcp", "Nat", NULL))                Test_NatDhcp();
    if (RunTest("NatDns", "Nat", NULL))                 Test_NatDns();
    if (RunTest("NatUdp", "Nat", NULL))                 Test_NatUdp();
    if (RunTest("NatTcp", "Nat", NULL))                 Test_NatTcp();
    if (RunTest("*SgAlive", NULL))                      Test_SgAlive();
    if (RunTest("*SgMsg", "*Sg", NULL))                 Test_SgMsg();
    if (RunTest("*SgUdp", "*Sg", NULL))                 Test_SgUdp();
    if (RunTest("*SgTcp", "*Sg", NULL))                 Test_SgTcp();
    if (RunTest("*SgRst", "*Sg", NULL))                 Test_SgRst();
    if (RunTest("*SgNat", "*Sg", NULL))                 Test_SgNat();
    if (RunTest("*SgLogon", "*Sg", NULL))               Test_SgLogon();

    VLanTerm();

    return(0);
}
