// ----------------------------------------------------------------------------
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ----------------------------------------------------------------------------

//////////////////////////////////////////////////////////////////////////////
//
//
//
#include <winlean.h>
#include <winsock.h>
#include <iphlpapi.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "socksr.h"
#include "hashtab.h"

#define ARRAYOF(x)      (sizeof(x)/sizeof(x[0]))
#define ASSERT(a)       assert(a)

//////////////////////////////////////////////////////////////////////////////
//

// Linked list of networks.  Sorted from low to high by address.
static CNetwork *s_pNetworks = NULL;

// Get all of the available networks.  Returns true if the list of networks
// has changed.
// ISSUE: Currently only adds, doesn't remove.
static int __cdecl cmpIpAddrRow(const void *fvp, const void *svp)
{
    const PMIB_IPADDRROW fp = (const PMIB_IPADDRROW)fvp;
    const PMIB_IPADDRROW sp = (const PMIB_IPADDRROW)svp;

    if (fp->dwAddr < sp->dwAddr) {
        return -1;
    }
    else if (fp->dwAddr > sp->dwAddr) {
        return 1;
    }
    return 0;
}

static int __cdecl cmpIpForwardRow(const void *fvp, const void *svp)
{
    const PMIB_IPFORWARDROW fp = (const PMIB_IPFORWARDROW)fvp;
    const PMIB_IPFORWARDROW sp = (const PMIB_IPFORWARDROW)svp;

    if (fp->dwForwardIfIndex < sp->dwForwardIfIndex) {
        return -1;
    }
    else if (fp->dwForwardIfIndex > sp->dwForwardIfIndex) {
        return 1;
    }
    if (fp->dwForwardNextHop < sp->dwForwardNextHop) {
        return -1;
    }
    else if (fp->dwForwardNextHop > sp->dwForwardNextHop) {
        return 1;
    }
    return 0;
}

PCSTR SocketToString(UINT32 dwAddr)
{
    static CHAR rszAddrs[16][32];
    static int nAddrs = 0;

    CHAR *pszAddr = rszAddrs[nAddrs];
    nAddrs = (nAddrs + 1) % ARRAYOF(rszAddrs);

    sprintf(pszAddr, "%d.%d.%d.%d",
            (dwAddr >> 24) & 0xff,
            (dwAddr >> 16) & 0xff,
            (dwAddr >>  8) & 0xff,
            (dwAddr >>  0) & 0xff);
    return pszAddr;
}


BOOL SocketLoadNetworks(CNetwork ** ppNetworks)
{
#if 0
    PMIB_IFTABLE pIfTable = NULL;
#endif
    PMIB_IPADDRTABLE pIpAddrTable = NULL;
    PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
#if 0
    ULONG cbIfTable = 0;
#endif
    ULONG cbIpAddrTable = 0;
    ULONG cbIpForwardTable = 0;
#if 0
    ULONG nIfTable = 0;
#endif
    ULONG nIpAddrTable = 0;
    ULONG nIpForwardTable = 0;
    DWORD err = 0;
    BOOL fChanged = FALSE;

    // Retrieve the address and forwarding tables.
    //
    for (;;) {
        err = GetIpAddrTable(pIpAddrTable, &cbIpAddrTable, FALSE);
        if (err == NO_ERROR) {
            break;
        }
        if (pIpAddrTable != NULL) {
            delete[] (PUINT8)pIpAddrTable;
            pIpAddrTable = NULL;
        }
        if (err != ERROR_INSUFFICIENT_BUFFER) {
            break;
        }

        pIpAddrTable = (PMIB_IPADDRTABLE)new UINT8 [cbIpAddrTable];
    }

    for (;;) {
        err = GetIpForwardTable(pIpForwardTable, &cbIpForwardTable, FALSE);
        if (err == NO_ERROR) {
            break;
        }
        if (pIpForwardTable != NULL) {
            delete[] (PUINT8)pIpForwardTable;
            pIpForwardTable = NULL;
        }
        if (err != ERROR_INSUFFICIENT_BUFFER) {
            break;
        }

        pIpForwardTable = (PMIB_IPFORWARDTABLE)new UINT8 [cbIpForwardTable];
    }

#if 0
    for (;;) {
        err = GetIfTable(pIfTable, &cbIfTable, TRUE);
        if (err == NO_ERROR) {
            break;
        }
        if (pIfTable != NULL) {
            delete[] (PUINT8)pIfTable;
            pIfTable = NULL;
        }
        if (err != ERROR_INSUFFICIENT_BUFFER) {
            break;
        }

        pIfTable = (PMIB_IFTABLE)new UINT8 [cbIfTable];
        nIfTable = pIfTable->dwNumEntries;
    }
#endif

    // Sort the tables.
    //
    if (pIpAddrTable) {
        for (DWORD n = 0; n < pIpAddrTable->dwNumEntries; n++) {
            pIpAddrTable->table[n].dwAddr
                = ntohl(pIpAddrTable->table[n].dwAddr);
            pIpAddrTable->table[n].dwMask
                = ntohl(pIpAddrTable->table[n].dwMask);
            pIpAddrTable->table[n].dwBCastAddr
                = ntohl(pIpAddrTable->table[n].dwBCastAddr);
        }
        qsort(pIpAddrTable->table,
              pIpAddrTable->dwNumEntries,
              sizeof(pIpAddrTable->table[0]),
              cmpIpAddrRow);
        nIpAddrTable = pIpAddrTable->dwNumEntries;
    }

    if (pIpForwardTable) {
        for (DWORD n = 0; n < pIpForwardTable->dwNumEntries; n++) {
            pIpForwardTable->table[n].dwForwardDest
                = ntohl(pIpForwardTable->table[n].dwForwardDest);
            pIpForwardTable->table[n].dwForwardMask
                = ntohl(pIpForwardTable->table[n].dwForwardMask);
            pIpForwardTable->table[n].dwForwardNextHop
                = ntohl(pIpForwardTable->table[n].dwForwardNextHop);
        }
        qsort(pIpForwardTable->table,
              pIpForwardTable->dwNumEntries,
              sizeof(pIpForwardTable->table[0]),
              cmpIpForwardRow);
        nIpForwardTable = pIpForwardTable->dwNumEntries;
    }

#if 0
    if (pIfTable) {
        for (DWORD n = 0; n < nIfTable; n++) {
            printf("%7d. %ls\n",
                   pIfTable->table[n].dwIndex,
                   pIfTable->table[n].wszName);
        }
    }
#endif

    // Normalize the list, delete any fDeleted entries.
    //
    CNetwork **pNetworkLast = &s_pNetworks;
    CNetwork *pNetwork = s_pNetworks;

    while (pNetwork != NULL) {
        if (pNetwork->fDeleted) {
            *pNetworkLast = pNetwork->pNext;

            delete pNetwork;
            pNetwork = *pNetworkLast;
        }
        else {
            pNetwork->fCreated = FALSE;
            pNetwork->fDeleted = FALSE;
            pNetwork->fChanged = FALSE;

            pNetworkLast = &pNetwork->pNext;
            pNetwork = pNetwork->pNext;
        }
    }

    // Walk through the tables, updating the persistent linked list.
    //
    pNetworkLast = &s_pNetworks;
    pNetwork = s_pNetworks;

#if 0
    printf("---------------------------------------------------------------------------\n");
    for (DWORD n = 0; n < nIpAddrTable; n++) {
        printf("                 %5d: %-15.15s -%15.15s\n",
               pIpAddrTable->table[n].dwIndex,
               SocketToString(pIpAddrTable->table[n].dwAddr),
               SocketToString(pIpAddrTable->table[n].dwMask));
    }
#endif

    for (DWORD n = 0; n < nIpAddrTable;) {
        UINT32 nAddr = 0;
        UINT32 nMask = 0;
        UINT32 nCast = 0;
        UINT32 nGate = 0;

        if (pIpAddrTable->table[n].dwAddr == 0 ||
            (pIpAddrTable->table[n].dwAddr & 0xff000000) == 0x7f000000) {

            // Drop 0 networks and 127.*.*.*, the local loopback.
            n++;
            continue;
        }

        nAddr = pIpAddrTable->table[n].dwAddr;
        nMask = pIpAddrTable->table[n].dwMask;
        nCast = (nMask & nAddr) | ~nMask;

        for (DWORD f = 0; f < nIpForwardTable; f++) {
            if (pIpForwardTable->table[f].dwForwardIfIndex
                == pIpAddrTable->table[n].dwIndex &&
                pIpForwardTable->table[f].dwForwardNextHop
                != pIpAddrTable->table[n].dwAddr
               ) {

                nGate = pIpForwardTable->table[f].dwForwardNextHop;
                break;
            }
        }

#if 0
        MIB_IFROW interface;
        memset(&interface, 0, sizeof(interface));
        interface.dwIndex = pIpAddrTable->table[n].dwIndex;

        err = GetIfEntry(&interface);
        if (err == NO_ERROR) {
            printf("Interface %d: %ls %s %04x%04x MTU=%d\n",
                   interface.dwIndex, interface.wszName, interface.wszName,
                   interface.wszName[0], interface.wszName[1],
                   interface.dwMtu);
            printf("    %.*s\n",
                   interface.dwDescrLen, interface.bDescr);
        }
#endif

        // At this point, we have a valid external address
        //
        if (pNetwork && pNetwork->nAddr == nAddr) {
            // The network was known...
            // Update the flags, and move both list and table to the
            // next network.

#if 0
            printf("Existed: %s\n", SocketToString(nAddr));
#endif
            // If it was previously deleted, then it has been re-created.
            pNetwork->fCreated = pNetwork->fDeleted;
            pNetwork->fChanged = pNetwork->fDeleted;
            pNetwork->fDeleted = FALSE;

            if (pNetwork->nMask != nMask ||
                pNetwork->nCast != nCast ||
                pNetwork->nGate != nGate) {

                pNetwork->nMask = nMask;
                pNetwork->nCast = nCast;
                pNetwork->nGate = nGate;

                pNetwork->fChanged = TRUE;
            }

            if (pNetwork->fCreated ||
                pNetwork->fDeleted ||
                pNetwork->fChanged) {

                fChanged = TRUE;
            }

            pNetworkLast = &pNetwork->pNext;
            pNetwork = pNetwork->pNext;
            n++;
            continue;
        }
        else if (pNetwork == NULL || pNetwork->nAddr > nAddr) {
            // The table network is new...

#if 0
            printf("Created: %s\n", SocketToString(nAddr));
#endif
            CNetwork *pNew = new CNetwork;
            if (pNew != NULL) {
                pNew->pNext = pNetwork;
                pNew->nAddr = nAddr;
                pNew->nMask = nMask;
                pNew->nCast = nCast;
                pNew->nGate = nGate;
                pNew->fCreated = TRUE;
                pNew->fDeleted = FALSE;
                pNew->fChanged = FALSE;
                fChanged = TRUE;

                *pNetworkLast = pNew;
                pNetworkLast = &pNew->pNext;
            }
#if 0
            else {
                printf("Couldn't create: %s\n", SocketToString(nAddr));
            }
#endif
            n++;
            continue;
        }
        else if (pNetwork->nAddr < nAddr) {
            // The list network was deleted...

#if 0
            printf("Deleted: %s\n", SocketToString(nAddr));
#endif
            pNetwork->fCreated = FALSE;
            pNetwork->fDeleted = TRUE;
            pNetwork->fChanged = FALSE;
            fChanged = TRUE;

            pNetworkLast = &pNetwork->pNext;
            pNetwork = pNetwork->pNext;
            continue;
        }
        else {
            ASSERT(!"Untested case.");
        }
    }

    while (pNetwork != NULL) {
        // The list network was deleted...

#if 0
        printf("Deleted: %s\n", SocketToString(nAddr));
#endif
        pNetwork->fCreated = FALSE;
        pNetwork->fDeleted = TRUE;
        pNetwork->fChanged = FALSE;
        fChanged = TRUE;

        pNetworkLast = &pNetwork->pNext;
        pNetwork = pNetwork->pNext;
        continue;
    }

#if 0
    printf("------------------------------------------------------------------------------\n");
    for (pNetwork = s_pNetworks; pNetwork != NULL; pNetwork = pNetwork->pNext) {
        printf("    %-15.15s: %-15.15s %-15.15s %-15.15s %s%s%s\n",
               SocketToString(pNetwork->nAddr),
               SocketToString(pNetwork->nMask),
               SocketToString(pNetwork->nCast),
               SocketToString(pNetwork->nGate),
               pNetwork->fCreated ? "Created " : "",
               pNetwork->fDeleted ? "Deleted " : "",
               pNetwork->fChanged ? "Changed " : "");
    }
#endif


    if (pIpAddrTable != NULL) {
        delete[] (PUINT8)pIpAddrTable;
        pIpAddrTable = NULL;
    }
    if (pIpForwardTable != NULL) {
        delete[] (PUINT8)pIpForwardTable;
        pIpForwardTable = NULL;
    }
    if (ppNetworks) {
        *ppNetworks = s_pNetworks;
    }
    return FALSE;
}


//////////////////////////////////////////////////////////////////////////////
//

struct CSocketSource : public ISocketSource
{
    CSocketSource(ISocketSink *pSink);
    ~CSocketSource();

    // ISocketSource
  public:
    virtual UINT    SocketSend(UINT32 nAddr, UINT16 nPort, PBYTE pbData, UINT cbData);
    virtual UINT    SocketClose(UINT dwError);

  public:
    VOID OnRecv();

    SOCKET          m_nSocket;
    ISocketSink *   m_pSink;
};

//////////////////////////////////////////////////////////////////////////////
//
static HWND             s_hWnd;                         // Host Window
static UINT             s_nMsg;                         // Host Event
static CHashTable32<CSocketSource*> s_Sockets;

//////////////////////////////////////////////////////////////////////////////
//
CSocketSource::CSocketSource(ISocketSink *pSink)
    : m_nSocket(0),
      m_pSink(pSink)
{
}

CSocketSource::~CSocketSource()
{
    if (m_pSink)
    {
        ISocketSink *pSink = m_pSink;
        m_pSink = NULL;
        pSink->OnSocketClose(0);
    }
    if (m_nSocket)
    {
        closesocket(m_nSocket);
        m_nSocket = 0;
    }
}

UINT CSocketSource::SocketSend(UINT32 nAddr, UINT16 nPort, PBYTE pbData, UINT cbData)
{
    // Fill out server socket's address information.
    SOCKADDR_IN server_sin;
    server_sin.sin_family = AF_INET;
    server_sin.sin_port = htons(nPort);
    server_sin.sin_addr.s_addr = htonl(nAddr);

    int iRes = sendto(m_nSocket, (const char *)pbData, cbData,
                      0, (struct sockaddr FAR *) &server_sin, sizeof(server_sin));
    return (SOCKET_ERROR == iRes) ? WSAGetLastError() : 0;
}

VOID CSocketSource::OnRecv()
{
    SOCKADDR_IN from;
    int fromlen = sizeof(from);

    BYTE rbData[2048];
    int iRes = recvfrom(m_nSocket, (char *)rbData, sizeof(rbData), 0,
                        (struct sockaddr *)&from, &fromlen);
    int nErr = (iRes == SOCKET_ERROR) ? WSAGetLastError() : 0;

    if (nErr == WSAECONNRESET || nErr == WSAEWOULDBLOCK) {
        // Lost a packet.  Don't worry about it.
    }
    else {
        m_pSink->OnSocketRecv(nErr,
                              ntohl(from.sin_addr.s_addr),
                              ntohs(from.sin_port),
                              rbData,
                              iRes);
    }
}

UINT CSocketSource::SocketClose(UINT dwError)
{
    if (m_pSink)
    {
        ISocketSink *pSink = m_pSink;
        m_pSink = NULL;

        s_Sockets.Delete(m_nSocket);
        pSink->OnSocketClose(dwError);
        delete this;
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////////
//
VOID SocketInit(HWND hWnd, UINT nMsg)
{
    s_hWnd = hWnd;
    s_nMsg = nMsg;
}

LRESULT SocketEvent(WPARAM wParam, LPARAM lParam)
{
    WORD wSelectError = HIWORD(lParam);
    WORD wSelectEvent = LOWORD(lParam);
    DWORD dwSocket = wParam;

    (void)wSelectError;

    CSocketSource *pSource = s_Sockets.Find(dwSocket);
    if (pSource == NULL) {
        return 0;
    }

    if (wSelectEvent & FD_READ) {
        pSource->OnRecv();
    }
    return 0;
}

UINT SocketCreate(UINT32 nLocalAddr, UINT16 wLocalPort, ISocketSink *pSink)
{
    SOCKET s = INVALID_SOCKET;
    CSocketSource *pSource = NULL;
    UINT nError;
    int iRes;

    if (pSink == NULL) {
        nError = (UINT)E_INVALIDARG;
      Error:
        if (pSource) {
            delete pSource;
            pSource = NULL;
        }
        if (s != INVALID_SOCKET) {
            closesocket(s);
            s = INVALID_SOCKET;
        }
        return nError;
    }

    if ((pSource = new CSocketSource(pSink)) == NULL) {
        nError = (UINT)E_OUTOFMEMORY;
        goto Error;
    }

    // Create a datagram window socket.
    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (INVALID_SOCKET == s) {
      SocketError:
        nError = WSAGetLastError();
        goto Error;
    }

    // allow broadcasts on this socket
    BOOL optBOOL = TRUE;
    iRes = setsockopt(s, SOL_SOCKET, SO_BROADCAST,
                      (char *)&optBOOL, sizeof(optBOOL));
    if (0 != iRes) {
        goto SocketError;
    }

    // Fill out the client socket's address information.
    SOCKADDR_IN client_sin;
    client_sin.sin_family = AF_INET;
    client_sin.sin_port = htons(wLocalPort);
    client_sin.sin_addr.s_addr = htonl(nLocalAddr);

    // Associate the client socket's address with the socket, Sock.
    iRes = bind(s, (LPSOCKADDR)&client_sin, sizeof(client_sin));
    if (0 != iRes) {
        goto SocketError;
    }

    // Set the socket as NON-BLOCKING.
    DWORD ioctlVal = TRUE;
    iRes = ioctlsocket(s, FIONBIO, &ioctlVal);
    if (SOCKET_ERROR == iRes) {
        goto SocketError;
    }

    // Set windows for async event..
    iRes = WSAAsyncSelect(s, s_hWnd, s_nMsg,
                          FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT);
    if (SOCKET_ERROR == iRes) {
        goto SocketError;
    }

    pSource->m_nSocket = s;

#if 0
    {
        // Fill out server socket's address information.
        SOCKADDR_IN server_sin;
        server_sin.sin_family = AF_INET;
        server_sin.sin_port = htons(4011);
        server_sin.sin_addr.s_addr = 0xffffffff;

        sendto(s, "OpenPort", 8,
               0, (struct sockaddr FAR *) &server_sin, sizeof(server_sin));
    }
#endif

    s_Sockets.Insert(s, pSource);

    pSink->OnSocketCreate(pSource);
    return 0;
}

//////////////////////////////////////////////////////////////////////////////
//
static inline UINT64 MakeId(UINT32 nAddr, UINT16 nPort) {
    return (UINT64)nAddr | ((UINT64)nPort << 32);
}

struct CSocketFactory : public ISocketSink,
                        public ISessionFactorySource
{
    friend struct CSocketSession;

    struct CSocketSession : public ISessionSource
    {
        CSocketSession(CSocketFactory *pOwner,
                       UINT32 nPeerAddr,
                       UINT16 nPeerPort)
            : m_pOwner(pOwner),
              m_nPeerAddr(nPeerAddr),
              m_nPeerPort(nPeerPort),
              m_pSink(NULL)
        {
        }
        ~CSocketSession()
        {
            if (m_pSink)
            {
                ISessionSink *pSink = m_pSink;
                m_pSink = NULL;
                pSink->OnSessionClose(0);
            }
        }

        // ISessionSource
      public:
        virtual UINT    SessionSend(PBYTE pbData, UINT cbData)
        {
            return m_pOwner->SocketSend(m_nPeerAddr, m_nPeerPort, pbData, cbData);
        }

        virtual UINT    SessionClose(UINT dwError)
        {
            if (m_pSink) {
                ISessionSink *pSink = m_pSink;
                m_pSink = NULL;

                m_pOwner->m_Sessions.Delete(MakeId(m_nPeerAddr, m_nPeerPort));
                pSink->OnSessionClose(dwError);
            }
            return 0;
        }

      public:
        VOID OnRecv(UINT dwError, PBYTE pbData, UINT cbData)
        {
            if (m_pSink)
            {
                m_pSink->OnSessionRecv(dwError, pbData, cbData);
            }
        }

        CSocketFactory *m_pOwner;
        UINT32          m_nPeerAddr;
        UINT16          m_nPeerPort;
        ISessionSink *  m_pSink;
    };

    //
  public:
    CSocketFactory(ISessionFactorySink *pSink)
        : m_pSource(NULL),
          m_pSink(pSink)
    {
    }

    ~CSocketFactory()
    {
        DoFactoryClose(0);
    }

    BOOL DoFactoryClose(UINT dwError)
    {
        if (m_pSink)
        {
            ISessionFactorySink *pSink = m_pSink;
            m_pSink = NULL;

            CSocketSession *pSession;
            UINT64 nKey;

            for (INT nIt = 0; (pSession = m_Sessions.Enumerate(nKey, nIt)) != NULL;) {
                pSession->SessionClose(dwError);
                pSession = NULL;
                // Sessions delete themselves from the m_Sessions.
            }
            pSink->OnFactoryClose(dwError);

            if (m_pSource) {
                ISocketSource *pSource = m_pSource;
                m_pSource = NULL;

                pSource->SocketClose(0);
            }
            return TRUE;
        }
        return FALSE;
    }

    // ISessionFactorySource
  public:
    virtual UINT    FactorySend(UINT32 nAddr, UINT16 nPort,
                                PBYTE pbData, UINT cbData)
    {
        return m_pSource->SocketSend(nAddr, nPort, pbData, cbData);
    }

    virtual UINT    FactoryClose()
    {
        if (DoFactoryClose(0)) {
            delete this;
        }
        return 0;
    }

    virtual UINT    FactorySessionCreate(UINT32 nPeerAddr,
                                         UINT16 nPeerPort,
                                         ISessionSink **ppSink,
                                         PVOID pvContext)
    {
        // Are we shutting down?
        if (m_pSink == NULL) {
            return (UINT)E_PENDING;
        }
        if (ppSink == NULL) {
            return (UINT)E_INVALIDARG;
        }

        CSocketSession *pSession = m_Sessions.Find(MakeId(nPeerAddr, nPeerPort));
        if (m_Sessions.Find(MakeId(nPeerAddr, nPeerPort))) {
            pSession->SessionClose((UINT)E_ABORT);
            pSession = NULL;

            printf("FactorySessionCreate: closed existing session.\n");

            // return (UINT)E_ABORT;
        }

        pSession = new CSocketSession(this, nPeerAddr, nPeerPort);
        if (pSession == NULL) {
            return (UINT)E_OUTOFMEMORY;
        }

        m_pSink->OnSessionCreate(nPeerAddr, nPeerPort, pSession, ppSink, pvContext);
        if (*ppSink == NULL) {
            delete pSession;
            return 1;
        }
        pSession->m_pSink = *ppSink;

        m_Sessions.Insert(MakeId(nPeerAddr, nPeerPort), pSession);
        (*ppSink)->OnSessionCreate(pSession, pvContext);

        return 0;
    }

    // ISocketSink
  public:
    virtual VOID    OnSocketCreate(ISocketSource *pSource)
    {
        m_pSource = pSource;
        m_pSink->OnFactoryCreate(this);
    }

    virtual VOID    OnSocketClose(UINT dwError)
    {
        if (DoFactoryClose(dwError)) {
            delete this;
        }
    }

    virtual VOID    OnSocketRecv(UINT dwError,
                                 UINT32 nAddr, UINT16 nPort,
                                 PBYTE pbData, UINT cbData)
    {
        CSocketSession *pSession = m_Sessions.Find(MakeId(nAddr, nPort));
        if (pSession) {
            pSession->OnRecv(dwError, pbData, cbData);
        }
        else {
            m_pSink->OnFactoryRecv(dwError, nAddr, nPort, pbData, cbData);
        }
    }

    UINT    SocketSend(UINT32 nAddr, UINT16 nPort, PBYTE pbData, UINT cbData)
    {
        return m_pSource->SocketSend(nAddr, nPort, pbData, cbData);
    }

  public:
    ISessionFactorySink *           m_pSink;
    ISocketSource *                 m_pSource;
    CHashTable64<CSocketSession*>   m_Sessions;
};

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////// UDP Sessions.
//////////////////////////////////////////////////////////////////////////////

UINT SessionFactoryCreate(UINT32 nLocalAddr, UINT16 nLocalPort,
                          ISessionFactorySink *pFactorySink)
{
    CSocketFactory *pFactory = new CSocketFactory(pFactorySink);
    if (pFactory == NULL) {
        return (UINT)E_OUTOFMEMORY;
    }

    UINT nError = SocketCreate(nLocalAddr, nLocalPort, pFactory);

    if (nError != 0) {
        delete pFactory;
    }
    return nError;
}

//
///////////////////////////////////////////////////////////////// End of File.
