//  Beta release only.
//
//  Copyright(c) Ivyware Pty Ltd  2002-2009
//               MELBOURNE, VICTORIA, AUSTRALIA, 3130
//
//
//  P2PeerConWsa implementation
//  NOTES: Manages Microsoft WSA connections
//

#include "StdAfx.h"
#include "MSWSock.h"

#include "P2PeerConWsa.h"
#include "P2PeerioDmx.h"
#include "P2Pwin32.h"

///////////////////////////////////////////////////////////////////////
//  Constructors, destructor and factories

const __time64_t
iLoginTimeoutSeconds = 20;             // Login sequences must be
                                       //   processed within this time

P2PeerConWsa::P2PeerConWsa ( )
{
    // Firstly
    RenderThisSafe ( );
}

//
//  Parameters:  P2PaddrSTR strThisP2Paddr
//               Identification address for this end
//
//               P2PaddrSTR strThatP2Paddr
//               Identification address for that, or the other end
//
//               DWORD oSocket
//               Socket upon which connection accepted
//
P2PeerConWsa::P2PeerConWsa ( P2PaddrSTR pThatP2PaddrSTR
                           , SOCKET   oSocket )
            : P2PeerCon ( pThatP2PaddrSTR )
{
    // Firstly
    RenderThisSafe ( );
    m_oSocket        =         oSocket;
    m_hFile          = (HANDLE)oSocket;

    m_eP2PeerConMode = P2PeerCon_Accept;
}

//
//               LPCTSTR lpszIPaddress
//               Connection address
//
//               short nIPort
//               Connection port.
//
P2PeerConWsa::P2PeerConWsa ( P2PaddrSTR pThatP2PaddrSTR
                           , LPCTSTR lpszIPaddress, short nIPort )
            : P2PeerCon ( pThatP2PaddrSTR )
{
    // Firstly
    RenderThisSafe ( );
    m_sIPaddress     = lpszIPaddress;
    m_nIPort         =    nIPort;
    m_eP2PeerConMode = P2PeerCon_CLIENT;
}

//
//               short nIPort
//               Listening port
//
P2PeerConWsa::P2PeerConWsa ( P2PaddrSTR pThatP2PaddrSTR
                           , short nIPort )
            : P2PeerCon ( pThatP2PaddrSTR )
{
    // Firstly
    RenderThisSafe ( );
    m_nIPort         =    nIPort;
    m_eP2PeerConMode = P2PeerCon_SERVICE;
}

P2PeerConWsa::~P2PeerConWsa ( )
{
    // Resource collection
    Drop ( 0 );
}

void
P2PeerConWsa::RenderThisSafe ( )
{
    // Properties
    m_nIPort        = 0;

    m_oSocket       = INVALID_SOCKET;
    m_oSocketAccept = INVALID_SOCKET;
}

P2PeerConWsa*
P2PeerConWsa::ServiceFactory ( P2PaddrSTR strP2PaddrThat
                             , short nIPort )
{
    // Instanciate
    P2PeerConWsa *pCon = new P2PeerConWsa ( );

    // Attributes
    pCon -> m_oThatP2Paddr   = strP2PaddrThat;
    pCon -> m_oP2Padomain    = strP2PaddrThat;
    pCon -> m_eP2PeerConMode = P2PeerCon_SERVICE;
    pCon -> m_nIPort         = nIPort;

    // Protocol
    // NOTES: Instance is cloned for accepted connections
    pCon -> SetP2Peerio ( new P2Peerio( ) );

    // Done
    return pCon;
}

P2PeerConWsa*
P2PeerConWsa::ClientFactory ( P2PaddrSTR strP2PaddrThat
                            , LPCTSTR lpszIPaddress, short nIPort )
{
    // Instanciate
    P2PeerConWsa *pCon = new P2PeerConWsa ( );

    // Attributes
    pCon -> m_oThatP2Paddr   = strP2PaddrThat;
    pCon -> m_eP2PeerConMode = P2PeerCon_CLIENT;
    pCon -> m_sIPaddress     = lpszIPaddress;
    pCon -> m_nIPort         = nIPort;

    // Protocol
    pCon -> SetP2Peerio ( new P2Peerio( ) );

    // Done
    return pCon;
}

//
//  Description: Splits listening connection into listening and
//               accepted P2PeerCon'nections
//               NOTES: Derived classes must delegate
//
//
//  Parameters:  P2PeerCon **pConListen = 0
//               Previously manufactured listening instance
//
//               P2PeerCon **pConAccept = 0
//               Previously manufactured accept instance
//
P2PeerCon*
P2PeerConWsa::AcceptSpawn ( P2PeerCon *pConSpawn )
{
    // To be sure, to be sure
    if ( m_oSocketAccept == INVALID_SOCKET )
      return 0;

    // Instanciation etc
    if ( pConSpawn == NULL )
      pConSpawn = new P2PeerConWsa ( );

    // Settlement
    // NOTES: Accept removes the allocated socket
    P2PeerConWsa *pConSpawnWsa = (P2PeerConWsa *)pConSpawn;
    P2PeerCon::AcceptSpawn ( pConSpawn );
    pConSpawnWsa -> m_oSocket =         m_oSocketAccept;
    pConSpawnWsa -> m_hFile   = (HANDLE)m_oSocketAccept;
                                        m_oSocketAccept = INVALID_SOCKET;

    // Done
    // NOTES: Spawned object is free floating
    return pConSpawn;
}

///////////////////////////////////////////////////////////////////////
//  IOCP Integration
//

//
//  Description: Processes IO completion status
//               NOTES:
//
//
//  Parameters:  DWORD dwError
//               Error code associated with operation
//               
//               DWORD dbBytes
//               Bytes transferred during completed I/O operation.
// 
//               OVERLAPPEDcon pOVERLAPPEDcon
//               Address of OVERLAPPEDcon structure that was specified
//               for the I/O operation
//
bool
P2PeerConWsa::On_QueuedCompletionStatus ( DWORD dwError
                                        , DWORD dwBytes
                                        , OVERLAPPEDcon *pOVERLAPPEDcon )
{
    // Pre-processing
    HRESULT hr = dwError;
    if ( hr == S_OK )
      hr = pOVERLAPPEDcon -> hr;

    // Because this is all problematic we
    try
    {
      // Accept
      // NOTES: Perform On_P2PeerCon_ACCEPT() notification.  At which
      //        point the intercepting handler will initiate state
      //        changes.
      //      : Perform On_P2PeerCon_LISTEN() notification.  At which
      //        point the intercepting handler will listen for another
      //        connection
      if ( pOVERLAPPEDcon == m_pOVERLAPPEDaccept )
      {
        // Success
        // NOTES: Honour expectation that the On_P2PeerCon_ACCEPT()
        //        processed before the ON_P2PeerCon_LISTEN()
        //        notification        releaseOVERLAPPED ( pOVERLAPPEDcon );
        releaseOVERLAPPED ( pOVERLAPPEDcon );
        if ( hr )
          EVERR->Module (__FUNCTION__"[%s-%s]"
                        , GetP2PaddrHub().c_str()
                        , (P2PaddrSTR)m_oThatP2Paddr )
               ->Message("Overlapped accept failed")
               ->HResult( hr ) -> Throw();

        // Accept the new connection
        // NOTES: Depending upon machine load several connections
        //        may be waiting to be accepted
        if ( m_oSocketAccept != INVALID_SOCKET )
        {
          LPSOCKADDR pSockaddrLocal;
          int        iSizeLocal = 0;
          LPSOCKADDR pSockaddrRemote;
          int        iSizeRemote = 0;
          GetAcceptExSockaddrs ( pOVERLAPPEDcon->pBuffer
                               , 0//pOVERLAPPEDcon->dwBytesMax -16 -16
                               , 16
                               , 16
                               ,&pSockaddrLocal, &iSizeLocal
                               ,&pSockaddrRemote, &iSizeRemote );

          if ( setsockopt( m_oSocketAccept, SOL_SOCKET
                         , SO_UPDATE_ACCEPT_CONTEXT
                         , (char *)&m_oSocket, sizeof(m_oSocket) ) )
          {
            EVERR->Module (__FUNCTION__"(%s-%s)"
                          , GetP2PaddrHub().c_str()
                          , m_oThatP2Paddr.c_str() )
                 ->Message("setsockopt() failed")
                 ->HResult( WSAGetLastError() )->Throw();
          }

          // Configure non-blocking mode
          DWORD dwArg = 1;
          if ( ioctlsocket ( m_oSocketAccept, FIONBIO, &dwArg ) )
            EVERR->Module  (__FUNCTION__"(%s-%s)"
                           , GetP2PaddrHub().c_str()
                           , m_oThatP2Paddr.c_str() )
                 ->Message ("ioctlsocket(FIONBIO) failed")
                 ->Group("WSA")->HResult(WSAGetLastError() )->Throw();
          // Split into listening, usually this, and accepted P2PeerCon
          // NOTES: The underlying P2PeerCon objects have different styles
          //        according to the derived type.
          //P2PeerCon *pConAccept = 0, *pConListen = 0;
          //AcceptSplit ( &pConListen, &pConAccept );

          // ON_P2PeerOLD_ACCEPT() notification
          // NOTES: Accepted P2PeerCon is spawned within the handler
          PostP2Pmsg ( GetP2Paddress(), CN_P2PeerCon, P2P_Accept
                     , this, 0, m_hCPortP2PumpID );

          // ON_P2PeerCon_LISTEN() notification
          // NOTES: Listen for new connection
          //PostP2Pmsg ( pConListen->GetP2Paddress(), CN_P2PeerCon, P2P_Listen
          //           , pConListen, 0 );
        }

        // Observe expectations
        /*else
          EVERR->Module (__FUNCTION__"[%s-%s]"
                        , GetP2PaddrHub().c_str()
                        , m_oThatP2Paddr.c_str() )
               ->Message("Overlapped accept without assigned socket")
               ->Throw ( );*/
      }

      // Connect
      // NOTES: Perform On_P2PeerWsa_CONNECT() notification.  At which
      //        point the intercepting handler will initiate the
      //        appropriate state changes.
      else if ( pOVERLAPPEDcon == m_pOVERLAPPEDconnect )
      {
        releaseOVERLAPPED ( pOVERLAPPEDcon );
        if ( hr )
          EVERR->Module  (__FUNCTION__"(%s-%s)"
                         , GetP2PaddrHub().c_str()
                         , m_oThatP2Paddr.c_str() )
               ->Message ("Overlapped ConnectEx failed")
               ->HResult ( hr ) -> Throw();

        // Retrieve socket options
        if ( setsockopt( m_oSocket, SOL_SOCKET
                       , SO_UPDATE_CONNECT_CONTEXT
                       , NULL, 0 ) )
          EVERR->Module (__FUNCTION__"(%s-%s)"
                        , GetP2PaddrHub().c_str()
                        , m_oThatP2Paddr.c_str() )
               ->Message ("setsockopt() failed")
               ->HResult ( WSAGetLastError() )->Throw();

        // ON_P2PeerCon_CONNECT() notification
        // NOTES: Send() and Recv() buffers required beyond this point
        if ( m_pOVERLAPPEDsend == NULL )
          m_pOVERLAPPEDsend = MakeOVERLAPPED (  );
        if ( m_pOVERLAPPEDrecv == NULL )
          m_pOVERLAPPEDrecv = MakeOVERLAPPED ( m_pP2Peerio->GetMaxRecvSize() );

        PostP2Pmsg  ( m_oThatP2Paddr, CN_P2PeerCon, P2P_Connect
                    , this, (P2PeerMsg *)0, m_hCPortP2PumpID );
      }

      // Delegate
      else
        P2PeerCon::On_QueuedCompletionStatus ( dwError, dwBytes, pOVERLAPPEDcon );
    }

    // Exceptions
    // NOTES: Notification will eventually be routed through to
    //        ON_P2PeerCon_CLOSE() handler. At which point Restart()
    //        may be used on non accepted sockets or this object
    //        conDROP'ed
    //      : ON_P2PeerCon_CLOSE() handler to which this posted object
    //        is routed MUST cancel the attached event.  OnClose()
    //        contains such default processing.
    //      : P2P_Close notification MUST be performed once per
    //        attached event. Subsequent P2P_Close notifications are
    //        blocked until the original event is cancelled.
    catch ( P2Pevent *pEVENT )
    {
      Drop ( pEVENT->Isolate() );
    }

    // Tidy up and
    return true;
}

//
//  Description: Drops the encapsulated connection and performs the
//               appropriate ON_P2PeerCon_CLOSE() notification
//               NOTES: Should an EVENT already be attached the passed
//                      EVENT is simply cancelled.
//
//
//  Parameters:  P2Pevent *pEVENT
//               Precipitating event.  May be NULL
//               NOTES: Control assumed over life cycle.
//
void
P2PeerConWsa::Drop ( P2Pevent *pEVENT )
{
    // Firstly drop socket
    // NOTES: Cancels all outstanding overlapped IO
    if ( m_oSocket != INVALID_SOCKET )
    {
      if (   closesocket(m_oSocket) &&
           !pEVENT                     )
        pEVENT =
         EVERR->Module  (__FUNCTION__"(%s-%s)"
                        , GetP2PaddrHub().c_str()
                        , m_oThatP2Paddr.c_str() )
              ->Message ("closesocket(connect) failed" )
              ->Advice  ("Bug (SNHappen)" )
              ->HResult ( GetLastError() );
      m_hFile      = 0;
      m_hFileCPort = 0;
      m_oSocket    = INVALID_SOCKET;
    }

    // Secondly drop accept socket
    // NOTES: Cancels all outstanding overlapped IO
    if ( m_oSocketAccept != INVALID_SOCKET )
    {
      if (   closesocket(m_oSocketAccept) &&
           !pEVENT                           )
        pEVENT =
         EVERR->Module  (__FUNCTION__"(%s-%s)"
                        , GetP2PaddrHub().c_str()
                        , m_oThatP2Paddr.c_str() )
              ->Message ("closesocket(accept) failed" )
              ->Advice  ("Bug (SNHappen)" )
              ->HResult ( GetLastError() );
      m_oSocketAccept = INVALID_SOCKET;
    }

    // Always delegate
    P2PeerCon::Drop ( pEVENT );
}

///////////////////////////////////////////////////////////////////////
//  Connection state management 
//  NOTES: Referenced from P2PeerCon_MAP handlers only

//
//  Description: Initiates listening state processing for object.
//               NOTES: Referenced from On_P2PeerCon_STARTUP handlers
//                      for listening type objects.
//
//
//  Returns:     bool
//               Previous listening status
//                 true... Already listening
//                 false.. Not listening
//
bool
P2PeerConWsa::Listen ( )
{
    // Introduce locals
    ASSERT(m_nIPort>100);

    // To be sure, to be sure
    if ( m_oSocket != INVALID_SOCKET )
      return true;

    // Because this is all problematic, we
    try
    {
      // Create socket
      m_oSocket = WSASocket ( AF_INET, SOCK_STREAM, IPPROTO_IP
                            , 0//&oWsaProtocolInfo
                            , 0
                            , WSA_FLAG_OVERLAPPED );
      if ( m_oSocket == INVALID_SOCKET )
        EVERR->Module  (__FUNCTION__"(%s)"
                       , GetP2PaddrHub().c_str() )
             ->Message ("WSASocket() failed" )
             ->Advice  ("No free sockets?" )
             ->Advice  ("Network problem?)")
             ->Group("WSA")->HResult(WSAGetLastError() )->Throw();

      // Bind socket
      sockaddr_in oSockAddr;
      oSockAddr.sin_family = AF_INET;
      oSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
      oSockAddr.sin_port   = htons(m_nIPort);

      if ( bind ( m_oSocket
                , (PSOCKADDR)&oSockAddr, sizeof(oSockAddr) ) )
        EVERR->MODULE
             ->Message ("Bind(%i) failed", m_nIPort )
             ->Advice  ("Network problem?" )
             ->Group("WSA")->HResult(WSAGetLastError() )->Throw();

      // Associate with IO Completion Port
      // NOTES: All subsequent notifications received via queued
      //        IO Completion Packets
      CreateIOCP ( (HANDLE)m_oSocket );
      //m_hFileCPort = CreateIoCompletionPort ( (HANDLE)m_oSocket
      //                                      , m_hCPort
      //                                      ,(ULONG_PTR)(P2PeerCon *)this
      //                                      , 0 );
      //if ( m_hFileCPort == 0 )
      //  EVERR->Module  (__FUNCTION__"(%s-%s)"
      //                 , EncodeP2PeerID(m_oThisP2Paddr)
      //                 , EncodeP2PeerID(m_oThatP2Paddr) )
      //       ->Message ("CreateIoCompletionPort() failed")
      //       ->WSAGROUP( WSAGetLastError() )->Throw();

      // Listen
      if ( listen ( m_oSocket, 32/*m_xConnections*/ ) )
        EVERR->MODULE
             ->Message ("listen() failed" )
             ->Advice  ("Network problem? (SNHappen)" )
             ->Group("WSA")->HResult(WSAGetLastError() )
             ->Throw();

      // On_P2PeerCon_LISTEN() notification
      PostP2Pmsg ( m_oThatP2Paddr, CN_P2PeerCon, P2P_Listen
                 , this, (P2PeerMsg *)0, m_hCPortP2PumpID );
    }

    // Exceptions
    // NOTES: Perform P2PeerCon_CLOSE notification
    catch ( P2Pevent *pEVENT )
    {
      Drop ( pEVENT->Isolate() );
    }

    // Tidy up and
    return false;
}

//
//  Description: Completes listening state processing for object.
//               NOTES: Referenced from On_P2PeerCon_LISTEN handlers
//                      for listening type objects.
//
void
P2PeerConWsa::OnListen ( )
{
    // Always delegate
    P2PeerCon::OnListen();
}

//
//  Description: Initiates accept facility for object.
//               NOTES: Referenced from On_P2PeerCon_LISTEN handlers
//                      for listening type objects.
//                    : Accepted connections are processed via
//                      ON_P2PeerOLD_ACCEPT handlers
//
//
//  Returns:     bool
//
bool
P2PeerConWsa::Accept ( )
{
    // Always delegate
    P2PeerCon::Accept ( );

    // To be sure, to be sure
    if ( m_oSocketAccept != INVALID_SOCKET )
      return true;

    // Because this is all problematic, we
    try
    {
      // To be sure, to be sure
      if ( GetMode() != P2PeerCon_SERVICE )
        EVERR->Module (__FUNCTION__"[%s-%s]"
                      , GetP2PaddrHub().c_str()
                      , m_oThatP2Paddr.c_str() )
             ->Message("Operation only valid for listening type sockets" )
             ->Advice ("Bug (SNHappen)" )
             ->Throw();

      // To be sure, to be sure
      if ( m_oSocket == INVALID_SOCKET )
        EVERR->Module (__FUNCTION__"[%s-%s]"
                      , GetP2PaddrHub().c_str()
                      , m_oThatP2Paddr.c_str() )
             ->Message("Listening socket has not been assigned" )
             ->Advice ("Bug (SNHappen)" )
             ->Throw();

      // To be sure, to be sure
      if (   m_oSocketAccept != INVALID_SOCKET ||
           ( m_pOVERLAPPEDaccept          &&
             m_pOVERLAPPEDaccept->bQueued    )     )
        EVERR->Module (__FUNCTION__"[%s-%s]"
                      , GetP2PaddrHub().c_str()
                      , m_oThatP2Paddr.c_str() )
             ->Message("Duplicate accepts attempted on single socket" )
             ->Advice ("Bug (SNHappen)" )
             ->Throw();

      // Create socket
      // NOTES: Pre-allocated socket is required to accept connection
      m_oSocketAccept = WSASocket ( AF_INET, SOCK_STREAM, IPPROTO_TCP //changed from IPPROTO_IP for Webwerver
                                  , 0//&oWsaProtocolInfo
                                  , 0
                                  , WSA_FLAG_OVERLAPPED );
      if ( m_oSocketAccept == INVALID_SOCKET )
        EVERR->Module  (__FUNCTION__"(%s)"
                       , GetP2PaddrHub().c_str() )
             ->Message ("WSASocket() failed" )
             ->Advice  ("No free sockets?" )
             ->Advice  ("Network problem?" )
             ->Group("WSA")->HResult(WSAGetLastError() )->Throw();

      // Configure non-blocking mode
      DWORD dwArg = 1;
      if ( ioctlsocket ( m_oSocketAccept, FIONBIO, &dwArg ) )
        EVERR->Module  (__FUNCTION__"(%s-%s)"
                       , GetP2PaddrHub().c_str()
                       , m_oThatP2Paddr.c_str() )
             ->Message ("ioctlsocket(FIONBIO) failed")
             ->Group("WSA")->HResult(WSAGetLastError() )->Throw();

      // Accept
      if ( m_pOVERLAPPEDaccept == NULL )
        m_pOVERLAPPEDaccept = MakeOVERLAPPED ( MAX_P2Psize+16+16 );
      //DWORD dwBytesReceived = 0;
      prepareOVERLAPPED ( m_pOVERLAPPEDaccept );
      AcceptEx ( m_oSocket
               , m_oSocketAccept
               , m_pOVERLAPPEDaccept->pBuffer // lpOutputBuffer,
               , 0//m_pOVERLAPPEDaccept->dwBytesMax - 16 - 16//DWORD dwReceiveDataLength,
               , sizeof(SOCKADDR_STORAGE) + 16//DWORD dwLocalAddressLength,
               , sizeof(SOCKADDR_STORAGE) + 16//DWORD dwRemoteAddressLength,
               ,&m_pOVERLAPPEDaccept->dwBytes
               , (OVERLAPPED *)m_pOVERLAPPEDaccept );
      if ( WSAGetLastError() != ERROR_IO_PENDING )
      {
        releaseOVERLAPPED ( m_pOVERLAPPEDaccept );
        EVERR->MODULE
             ->Message ("AcceptEx() failed" )
             ->Advice  ("Network problem?" )
             ->Advice  ("Firewall configuration" )
             ->Group("WSA")->HResult(WSAGetLastError() )->Throw();
      }
    }

    // Exceptions
    // NOTES: Perform P2PeerCon_CLOSE notification
    catch ( P2Pevent *pEVENT )
    {
      Drop ( pEVENT->Isolate() );
    }

    // Tidy up and
    return false;
}

//
//  Description: Initiates connection processing for object.
//               NOTES: Referenced from On_P2PeerCon_STARTUP handlers
//                      for connection type objects.
//
//
//  Returns:
//
bool
P2PeerConWsa::Connect ( )
{
    // Always delegate
    P2PeerCon::Connect ( );

    // Introduce locals
    ASSERT(m_nIPort>100);

    // To be sure, to be sure
    if ( m_oSocket != INVALID_SOCKET )
      return true;

    // Because this is all problematic, we
    try
    {
      // Create socket
      // NOTES: Opportunity to define our own protocol here
      //      : WSA_FLAG_OVERLAPPED is mandatory when using IO
      //        completion ports
      m_oSocket = WSASocket ( AF_INET, SOCK_STREAM, IPPROTO_TCP //changed from IPPROTO_IP for Webwerver
                            , 0//&oWsaProtocolInfo
                            , 0
                            , WSA_FLAG_OVERLAPPED );
      if ( m_oSocket == INVALID_SOCKET )
        EVERR->Module  (__FUNCTION__"(%s-%s)"
                       , GetP2PaddrHub().c_str()
                       , m_oThatP2Paddr.c_str() )
             ->Message ("WSAsocket() failed")
             ->Group("WSA")->HResult(WSAGetLastError() )->Throw();

      // Associate with IO Completion Port
      // NOTES: All subsequent notifications received via queued
      //        IO Completion Packets
      CreateIOCP ( (HANDLE)m_oSocket );
      //m_hFileCPort = CreateIoCompletionPort ( (HANDLE)m_oSocket
      //                                      , m_hCPort
      //                                      ,(ULONG_PTR)(P2PeerCon *)this
      //                                      , 0 );
      //if ( m_hFileCPort == 0 )
      //  EVERR->Module  (__FUNCTION__"(%s-%s)"
      //                 , EncodeP2PeerID(m_oThisP2Paddr)
      //                 , EncodeP2PeerID(m_oThatP2Paddr) )
      //       ->Message ("CreateIoCompletionPort() failed")
      //       ->WSAGROUP( WSAGetLastError() )->Throw();
      //m_hFile = (HANDLE)m_oSocket;

      // Configure non-blocking mode
      DWORD dwArg = 1;
      if ( ioctlsocket ( m_oSocket, FIONBIO, &dwArg ) )
        EVERR->Module  (__FUNCTION__"(%s-%s)"
                       , GetP2PaddrHub().c_str()
                       , m_oThatP2Paddr.c_str() )
             ->Message ("ioctlsocket(FIONBIO) failed")
             ->Group("WSA")->HResult(WSAGetLastError() )->Throw();

      // Resolve host name
      // NOTES: Empty address flags self connection
      CString csIpAddress = m_sIPaddress;
      if ( csIpAddress.IsEmpty() )
      {
        char szIpAddress[512];
        if ( gethostname( szIpAddress, sizeof(szIpAddress) ) )
          EVERR->Module  (__FUNCTION__"(%s-%s)"
                         , GetP2PaddrHub().c_str()
                         , m_oThatP2Paddr.c_str() )
               ->Message("gethostname() failed" )
               ->Advice ("Local host has no name")
               ->Group("WSA")->HResult(WSAGetLastError())->Throw();
        csIpAddress = szIpAddress;
      }

      // Prepare address details
      struct sockaddr_in oSockaddr;
      memset ( &oSockaddr, 0, sizeof(oSockaddr) );
      oSockaddr.sin_family      = AF_INET;
      oSockaddr.sin_port        = htons(m_nIPort);
      oSockaddr.sin_addr.s_addr = inet_addr( (LPCTSTR)csIpAddress ); //127.0.0.1 is this machine

      if ( oSockaddr.sin_addr.s_addr == INADDR_NONE )
      {
        struct hostent *pHostent = NULL;

        pHostent = gethostbyname ( (LPCTSTR)csIpAddress );
        if ( pHostent == NULL )
          EVERR->Module  (__FUNCTION__"(%s-%s)"
                         , GetP2PaddrHub().c_str()
                         , m_oThatP2Paddr.c_str() )
               ->Message ("gethostbyname(%s) failed", m_sIPaddress )
               ->Advice  ("Unable to resolve server" )
               ->Group("WSA")->HResult(WSAGetLastError())->Throw();

        CopyMemory ( &oSockaddr.sin_addr, pHostent->h_addr_list[0]
                   , pHostent->h_length );
      }

      // Overlapped preparation
      if ( m_pOVERLAPPEDconnect == NULL )
        m_pOVERLAPPEDconnect = MakeOVERLAPPED ( 0 );

      // ConnectEx implementation
      // NOTES: Notification occurs via IO Completion Port
      //      : Operation must return ERROR_IO_PENDING state
      DWORD dwBytes = 0;
      prepareOVERLAPPED ( m_pOVERLAPPEDconnect );
      ConnectEx ( m_oSocket
                ,(struct sockaddr *)&oSockaddr, sizeof(oSockaddr)
                , NULL, 0, &dwBytes
                , m_pOVERLAPPEDconnect );
      if ( WSAGetLastError() != ERROR_IO_PENDING )
      {
        releaseOVERLAPPED ( m_pOVERLAPPEDconnect );
        EVERR->Module (__FUNCTION__"(%s-%s)"
                      , GetP2PaddrHub().c_str()
                      , m_oThatP2Paddr.c_str() )
             ->Message ("ConnectEx() failed\n" )
             ->Group("WSA")->HResult(WSAGetLastError() )->Throw();
      }
    }

    // Exceptions
    // NOTES: Perform P2PeerCon_CLOSE notification
    catch ( P2Pevent *pEVENT )
    {
      Drop ( pEVENT->Isolate() );
    }

    // Tidy up and
    return false;
}

void
P2PeerConWsa::Close  ( )
{
    // Delegate
    P2PeerCon::Close ( );
}

//
//  Performs default ON_P2PeerCon_CLOSE() handler processing
//  NOTES: Non-accepted connections may be Restart()'ed
//       : Usually performed in the ON_P2PeerCon_CLOSE() handlers
//
//
//  Returns:     conRESULT
//               Connection handler result code
//
conRESULT
P2PeerConWsa::OnClose ( )
{
    // Primary resource recovery
    // NOTES: Sockets are a little different and need to be tidied
    //        up before delegation to the base class
    if ( m_oSocket != INVALID_SOCKET )
    {
      closesocket ( m_oSocket );
      m_oSocket    = INVALID_SOCKET;
      m_hFile      = 0;                // P2PeerCon attribute
      m_hFileCPort = 0;                // P2PeerCon attribute
    }

    // Seconday resource recovery
    if ( m_oSocketAccept != INVALID_SOCKET )
    {
      closesocket ( m_oSocketAccept );
      m_oSocketAccept = INVALID_SOCKET;
    }

    // Always delegate
    // NOTES: Accepted connections MUST always be destroyed and any 
    //        form of restart blocked
    if ( GetMode() == P2PeerCon_Accept )
      Destroy ( );
    return P2PeerCon::OnClose();
}

///////////////////////////////////////////////////////////////////////
//  Utilities

//
//  Summarises connection failure
//  NOTES: Summarises those errors likely to have resulted
//         from the remote connection dropping out.
//
//
//  Parameters:  HRESULT hr
//               Precipitating error as returned from WriteFile(),
//               ReadFile()
//
//  Returns:     bool
//               Connection failure summary
//                 true... Remote failure
//                 false.. Local failure
bool
P2PeerConWsa::HasDroppedOut ( HRESULT hr )
{
    // Default action
    if ( hr == ERROR_NETNAME_DELETED )
      return true;
    return false;
}

///////////////////////////////////////////////////////////////////////
//  System
//  NOTES: Win32 extensions and packaging

typedef struct
{
    SOCKET               oSocket;
    struct sockaddr FAR  name;
    int                  namelen;
    HANDLE               hCPort;
    OVERLAPPEDcon       *pOVERLAPPEDcon;
    DWORD               dwCompletionKey;
    P2PeerConWsa        *pThis;
} ConnectExThreadData;

//
//  Windows ConnectEx() implementation
//  NOTES: Tidies up convoluted implementation
//
//
//  Parameters:  SOCKET oSocket
//               Unconnected, unbound socket.
//               NOTES: Win32 implementation requires socket to be
//                      bound to address provided
//
//               const struct sockaddr FAR *name
//               Name of the socket to which to connect
//
//               int namelen
//               Length of name, in bytes.
//
//               PVOID lpSendBuffer
//               Pointer to the buffer to be transferred upon
//               connection establishment. This parameter is optional
//
//               DWORD dwSendDataLength
//               Size of data in lpSendBuffer.
//
//               LPDWORD lpdwBytesSent
//               Number of bytes sent from lpSendBuffer.
//
//               LPOVERLAPPED lpOverlapped
//               An OVERLAPPED structure used to process the request.
//               The lpOverlapped parameter must be specified, and
//               cannot be NULL
//
//  Returns:     BOOL
//                 TRUE... Connection made
//                 FALSE.. Use WSAGetLastError() to retrieve code
//                         Refer Windows API for further details
BOOL
P2PeerConWsa::ConnectEx ( SOCKET oSocket
                        , const struct sockaddr FAR *name
                        , int namelen
                        , PVOID lpSendBuffer OPTIONAL
                        , DWORD dwSendDataLength
                        , LPDWORD lpdwBytesSent
                        , LPOVERLAPPED lpOverlapped )
{
    // Assume XP implementation available
    ASSERT(oSocket==m_oSocket);
    LPFN_CONNECTEX pfnConnectEx = NULL;
    GUID           gufn = WSAID_CONNECTEX;
    DWORD          dwBytes = 0;
    if ( WSAIoctl ( oSocket
                  , SIO_GET_EXTENSION_FUNCTION_POINTER
                  , &gufn, sizeof(gufn)
                  , &pfnConnectEx, sizeof(pfnConnectEx)
                  , &dwBytes, NULL, NULL ) )
    {
      EVERR->Module  (__FUNCTION__"(%s-%s)"
                     , GetP2PaddrHub().c_str()
                     , m_oThatP2Paddr.c_str() )
           ->Message ("WSAIoctl(%s) failed", m_sIPaddress )
           ->Advice  ("ConnectEx requires XP or better" )
           ->Group("WSA")->HResult(WSAGetLastError())->Display();
      return FALSE;
    }

    // Bind socket to address
    if ( bind(oSocket,name,namelen) == SOCKET_ERROR )
    {
      EVERR->Module (__FUNCTION__"(%s-%s)"
                    , GetP2PaddrHub().c_str()
                    , m_oThatP2Paddr.c_str() )
           ->Message ("bind() failed")
           ->Group("WSA")->HResult(WSAGetLastError() )->Display();
      return FALSE;
    }

    // ConnectEx implementation
    // NOTES: Notification occurs via IO Completion Port
    dwBytes = 0;
    ASSERT(dwSendDataLength==0);
    ASSERT(lpOverlapped==&m_pOVERLAPPEDconnect->oOverlapped);
    if (    pfnConnectEx &&
         !(*pfnConnectEx)( oSocket
                         , name, namelen
                         , lpSendBuffer, dwSendDataLength
                         , lpdwBytesSent
                         , lpOverlapped ) )
      return FALSE;

    // Tidy up, and
    return TRUE;
}

//
//  Description: Windows ConnectEx() implementation
//               NOTES: Provides alternative ConnectEx implementation
//
//
//  Parameters:  SOCKET oSocket
//               Unconnected, unbound socket.
//               NOTES: Win32 implementation requires socket to be
//                      bound to address provided
//
//               const struct sockaddr FAR *name
//               Name of the socket to which to connect
//
//               int namelen
//               Length of name, in bytes.
//
//               PVOID lpSendBuffer
//               Pointer to the buffer to be transferred upon
//               connection establishment. This parameter is optional
//
//               DWORD dwSendDataLength
//               Size of data in lpSendBuffer.
//
//               LPDWORD lpdwBytesSent
//               Number of bytes sent from lpSendBuffer.
//
//               LPOVERLAPPED lpOverlapped
//               An OVERLAPPED structure used to process the request.
//               The lpOverlapped parameter must be specified, and
//               cannot be NULL
//
//  Returns:     BOOL
//                 TRUE... Connection made
//                 FALSE.. Use WSAGetLastError() to retrieve code
//                         Refer Windows API for further details
BOOL
P2PeerConWsa::ConnectEx ( SOCKET oSocket
                        , const struct sockaddr FAR *name
                        , int namelen
                        , PVOID lpSendBuffer OPTIONAL
                        , DWORD dwSendDataLength
                        , LPDWORD lpdwBytesSent
                        , OVERLAPPEDcon *pOVERLAPPEDcon )
{
    // Preparation
    // NOTES: Must persist across life cycle of ConnectExThread
    ConnectExThreadData *pData = new ConnectExThreadData;
    pData -> dwCompletionKey = m_dwCompletionKey;
    pData -> hCPort          = m_hCPort;
    memcpy ( &pData->name, name, namelen );
    pData->  namelen         = namelen;
    pData -> oSocket         = oSocket;
    pData -> pOVERLAPPEDcon  = pOVERLAPPEDcon;
    pData -> pThis           = this;

    // Threaded emulation
    DWORD nThreadID;
    if ( CreateThread ( 0
                      , 0
                      , P2PeerConWsa::ConnectExThread
                      , pData
                      , 0
                      ,&nThreadID ) == NULL )
    {
      delete pData;                    // Garbage collection
      return FALSE;                    // Failed
    }

    // Tidy up, and
    // NOTES: Emulate expected and documented win32 response
    WSASetLastError ( ERROR_IO_PENDING );
    return FALSE;
}

//
//  Description: ConnectEx implementation thread
//               NOTES: Emulates ConnectEx implementation.  Overcomes
//                      problem whereby a bind() must be performed prior
//                      to the ConnectEx() call.  Works OK for remote
//                      connections but gives WSAEADDRINUSE for local.
//                    : Thread drops out upon the completion of the
//                      connect() call.
//
//
//  Parameters:  void *pvData
//               Thread data passed via CreateThread()
//
//  Returns:     DWORD
//               Completion code
//
DWORD WINAPI
P2PeerConWsa::ConnectExThread ( void *pvData )
{
    // Locals
    HRESULT                hr  = S_OK;
    ConnectExThreadData *pData = (ConnectExThreadData *)pvData;
    P2PeerConWsa           *pThis = pData -> pThis;

    // Because this is all problematic, we
    try
    {
      // Disable non-blocking mode
      // NOTES: Sockets must be configured as blocking.  But our
      //        ConnectEx() implementation requires blocking
      DWORD dwDisable = 0;               // Disable non-blocking mode
      if ( hr == S_OK                                         &&
           ioctlsocket ( pData->oSocket, FIONBIO, &dwDisable )    )
        hr = WSAGetLastError();

      // Perform connection
      // NOTES: May take a considerable period of time.  During which
      //        attempt to delete this object may be made.
      //      : Errors are expected here and as such are passed back
      //        via the IO Completion Port
      if ( hr == S_OK                                    &&
           connect ( pData->oSocket
                   , (struct sockaddr *)&pData->name
                   ,                     pData->namelen )    )
        hr = GetLastError();

      // Enable non-blocking mode
      // NOTES: Sockets must be configured as blocking.  But our
      //        ConnectEx() implementation requires blocking
      DWORD dwEnable = 1;                // Enable non-blocking mode
      if ( hr == S_OK                                         &&
           ioctlsocket ( pData->oSocket, FIONBIO, &dwEnable )    )
        hr = WSAGetLastError();

      // Post IO Completion Port 
      // NOTES: Confirm completion port still exists.  Object may be
      //        in the process of being deleted.
      //      : Simply report errors
      pData -> pOVERLAPPEDcon -> hr = hr;
      OVERLAPPED *pOVERLAPPED = (OVERLAPPED *)pData->pOVERLAPPEDcon;
      if ( !PostQueuedCompletionStatus ( pData->hCPort
                                       , 0 // bytes transferred
                                       , pData->dwCompletionKey 
                                       , pOVERLAPPED )  )
      {
        pThis -> releaseOVERLAPPED(pData->pOVERLAPPEDcon);
        EVERR->Module (__FUNCTION__"(%s-%s)"
                      , (P2PaddrSTR)pThis->GetP2PaddrHub()
                      , (P2PaddrSTR)pThis->GetP2Paddress() )
             ->Message ("PostQueuedCompletionStatus() failed")
             ->HResult ( GetLastError() )->Display()->Throw();
      }
    }

    // Exceptions
    // NOTES: Simply tidy up and, ignore
    catch ( P2Pevent *pEVENT )
    {
      pThis -> Drop ( pEVENT->Isolate() );
      ASSERT(!pData->pOVERLAPPEDcon->bQueued);
    }
    catch ( ... )
    {
      ASSERT(!pData->pOVERLAPPEDcon->bQueued);
    }

    // Tidy up, and
    delete pData;
    return 0;
}

///////////////////////////////////////////////////////////////////////
//  Troubleshooting

void
P2PeerConWsa::AssertValid ( ) const
{
    // Firstly delegate
    __super::AssertValid ( );

    // TODO: Additional validation
}

P3PmsgNode
P2PeerConWsa::Serialise ( LPCTSTR lpszVar )
{
    // Locals
    bool bDsc = true;

    // Create a placeholder for receipt of P2PeerConWsa details
    // NOTES: This will be passed by value back up the stack
    P3PmsgNode oNodeVar ( P3PmsgField(lpszVar,P3PmsgData(m_nP2PconID)) );
    if ( lpszVar == 0 || strlen(lpszVar) <= 0 )
      (P3PmsgField&)oNodeVar = P3PmsgName ( _T("{P2PeerConWsa}") );
    else
      oNodeVar.r_data() = P3PmsgData ( _T("{P2PeerConWsa}") );

    // Append our state to node
    P3PmsgField_SERIALISE ( oNodeVar, _T("IPaddress"), (LPCTSTR)m_sIPaddress, bDsc
                          , _T("Allocated IP connection address") );
    P3PmsgField_SERIALISE ( oNodeVar, _T("IPort"), m_nIPort, bDsc
                          , _T("Allocated IP port") );

    // Tidy up and
    oNodeVar += __super::Serialise ( 0 );
    return oNodeVar;
}

///////////////////////////////////////////////////////////////////////
//  Properties
