733 lines
21 KiB
C++
733 lines
21 KiB
C++
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//===========================================================================//
|
|
|
|
#include "mm_framework.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#ifdef _X360
|
|
|
|
#include "tier0/memdbgoff.h"
|
|
|
|
#include "fmtstr.h"
|
|
#include "protocol.h"
|
|
#include "proto_oob.h"
|
|
#include "netmessages.h"
|
|
|
|
#ifndef NETMSG_TYPE_BITS
|
|
#define NETMSG_TYPE_BITS 6
|
|
#endif
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
|
|
ConVar mm_net_channel_timeout( "mm_net_channel_timeout", "100", FCVAR_DEVELOPMENTONLY );
|
|
|
|
|
|
//
|
|
// Network manager for Xbox 360 network layer
|
|
//
|
|
|
|
CX360NetworkMgr::CX360NetworkMgr( IX360NetworkEvents *pListener, INetSupport::NetworkSocket_t eSocket ) :
|
|
m_pListener( pListener ),
|
|
m_eSocket( eSocket ),
|
|
m_arrConnections( DefLessFunc( XUID ) ),
|
|
m_eState( STATE_IDLE ),
|
|
m_eUpdateResult( UPDATE_SUCCESS )
|
|
{
|
|
}
|
|
|
|
void CX360NetworkMgr::SetListener( IX360NetworkEvents *pListener )
|
|
{
|
|
if ( m_eState == STATE_UPDATING )
|
|
{
|
|
DevMsg( "CX360NetworkMgr::SetListener during network update...\n" );
|
|
if ( m_eUpdateResult == UPDATE_SUCCESS )
|
|
m_eUpdateResult = UPDATE_LISTENER_CHANGED;
|
|
}
|
|
|
|
// We allow to set listener, we will just return the change
|
|
// from inside the update loop
|
|
m_pListener = pListener;
|
|
}
|
|
|
|
bool CX360NetworkMgr::IsUpdating() const
|
|
{
|
|
return m_eState != STATE_IDLE;
|
|
}
|
|
|
|
CX360NetworkMgr::UpdateResult_t CX360NetworkMgr::Update()
|
|
{
|
|
if ( !m_pListener )
|
|
return UPDATE_SUCCESS;
|
|
|
|
Assert( m_eState == STATE_IDLE );
|
|
if ( m_eState != STATE_IDLE )
|
|
{
|
|
DevWarning( "CX360NetworkMgr::Update when not idle! (state = %d)\n", m_eState );
|
|
return UPDATE_SUCCESS;
|
|
}
|
|
|
|
m_eState = STATE_UPDATING;
|
|
m_eUpdateResult = UPDATE_SUCCESS;
|
|
|
|
g_pMatchExtensions->GetINetSupport()->ProcessSocket( m_eSocket, this );
|
|
|
|
//
|
|
// Transmit all our active channels
|
|
//
|
|
for ( int idx = m_arrConnections.FirstInorder();
|
|
idx != m_arrConnections.InvalidIndex(); )
|
|
{
|
|
XUID xuidKey = m_arrConnections.Key( idx );
|
|
ConnectionInfo_t const ci = m_arrConnections.Element( idx ); // get a copy
|
|
idx = m_arrConnections.NextInorder( idx );
|
|
|
|
if ( !xuidKey && m_arrConnections.Count() > 1 )
|
|
// skip host designation record if there are other records
|
|
// there should be an alias for host too
|
|
continue;
|
|
|
|
// If it is a secure connection in LOST state, then shut it down right away
|
|
if ( ci.m_xnaddr.inaOnline.s_addr )
|
|
{
|
|
if ( g_pMatchExtensions->GetIXOnline()->XNetGetConnectStatus( ci.m_inaddr ) == XNET_CONNECT_STATUS_LOST )
|
|
{
|
|
DevWarning( "CX360NetworkMgr::Update - Net channel %p is LOST!\n", ci.m_pNetChannel );
|
|
ConnectionPeerClose( ci.m_xuid );
|
|
|
|
// Notify listener
|
|
if ( m_pListener )
|
|
{
|
|
m_pListener->OnX360NetDisconnected( ci.m_xuid );
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( ci.m_pNetChannel->IsTimedOut() )
|
|
{
|
|
DevWarning( "CX360NetworkMgr::Update - Net channel %p is timed out (%.1f s)!\n",
|
|
ci.m_pNetChannel, mm_net_channel_timeout.GetFloat() );
|
|
ConnectionPeerClose( ci.m_xuid );
|
|
|
|
// Notify listener
|
|
if ( m_pListener )
|
|
{
|
|
m_pListener->OnX360NetDisconnected( ci.m_xuid );
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
ci.m_pNetChannel->Transmit();
|
|
}
|
|
|
|
bool bSelfDestroy = ( m_eState == STATE_DESTROY_DEFERRED );
|
|
m_eState = STATE_IDLE;
|
|
|
|
if ( bSelfDestroy )
|
|
{
|
|
Destroy();
|
|
return UPDATE_DESTROYED;
|
|
}
|
|
|
|
return m_eUpdateResult;
|
|
}
|
|
|
|
void CX360NetworkMgr::Destroy()
|
|
{
|
|
// If we are being destroyed in the middle of update loop, then defer the actual destruction
|
|
if ( m_eState == STATE_UPDATING )
|
|
{
|
|
DevMsg( "CX360NetworkMgr::Destroy is deferred...\n" );
|
|
m_pListener = NULL;
|
|
m_eState = STATE_DESTROY_DEFERRED;
|
|
return;
|
|
}
|
|
|
|
Assert( m_eState == STATE_IDLE );
|
|
DevMsg( "CX360NetworkMgr::Destroy is deleting this object [%p].\n", this );
|
|
|
|
// Shutdown all the connections with peers
|
|
while ( m_arrConnections.Count() > 0 )
|
|
{
|
|
ConnectionInfo_t const &ci = m_arrConnections.Element( m_arrConnections.FirstInorder() );
|
|
XUID xuidRemote = ci.m_xuid;
|
|
ConnectionPeerClose( xuidRemote );
|
|
}
|
|
|
|
delete this;
|
|
}
|
|
|
|
void CX360NetworkMgr::DebugPrint()
|
|
{
|
|
DevMsg( "CX360NetworkMgr\n" );
|
|
DevMsg( " state: %d\n", m_eState );
|
|
DevMsg( " connections: %d\n", m_arrConnections.Count() );
|
|
for ( int k = m_arrConnections.FirstInorder(), j = 0;
|
|
k != m_arrConnections.InvalidIndex();
|
|
++j, k = m_arrConnections.NextInorder( k ) )
|
|
{
|
|
ConnectionInfo_t const *ci;
|
|
ci = &m_arrConnections.Element( k );
|
|
DevMsg( " connection%d: %llx [%llx] = channel %p\n", j,
|
|
m_arrConnections.Key( k ),
|
|
ci->m_xuid, ci->m_pNetChannel );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Declaration of the X360 network message
|
|
//
|
|
|
|
class CX360NetworkMessageBase : public CNetMessage
|
|
{
|
|
public:
|
|
virtual bool ReadFromBuffer( bf_read &buffer ) { Assert( 0 ); return false; }
|
|
virtual bool WriteToBuffer( bf_write &buffer ) const { Assert( 0 ); return false; }
|
|
virtual const char *ToString() const { return GetName(); }
|
|
|
|
virtual int GetType() const { return 20; } // should fit in 5 bits
|
|
virtual const char *GetName() const { return "CX360NetworkMessage";}
|
|
virtual size_t GetSize() const { return sizeof( *this ); }
|
|
};
|
|
|
|
class CX360NetworkMessageSend : public CX360NetworkMessageBase
|
|
{
|
|
public:
|
|
explicit CX360NetworkMessageSend( KeyValues *pData ) : m_pData( pData ) {}
|
|
|
|
virtual bool WriteToBuffer( bf_write &buffer ) const;
|
|
|
|
public:
|
|
KeyValues *m_pData;
|
|
};
|
|
|
|
class CX360NetworkMessageRecv : public CX360NetworkMessageBase
|
|
{
|
|
public:
|
|
explicit CX360NetworkMessageRecv( CX360NetworkMgr *pMgr ) : m_pMgr( pMgr ), m_pData( NULL ) {}
|
|
~CX360NetworkMessageRecv();
|
|
|
|
virtual bool ReadFromBuffer( bf_read &buffer );
|
|
virtual bool Process();
|
|
|
|
public:
|
|
CX360NetworkMgr *m_pMgr;
|
|
KeyValues *m_pData;
|
|
|
|
struct GrowStorage_t
|
|
{
|
|
GrowStorage_t() : m_nBytes( 0 ), m_pbData( NULL ) {}
|
|
~GrowStorage_t() { delete [] m_pbData; m_nBytes = 0; m_pbData = NULL; }
|
|
void Grow( int nBytes )
|
|
{
|
|
if ( m_nBytes < nBytes )
|
|
{
|
|
delete [] m_pbData;
|
|
m_pbData = new unsigned char[ m_nBytes = nBytes ];
|
|
}
|
|
}
|
|
|
|
int m_nBytes;
|
|
unsigned char *m_pbData;
|
|
};
|
|
|
|
GrowStorage_t m_gsKeyValues;
|
|
GrowStorage_t m_gsBinaryData;
|
|
};
|
|
|
|
bool CX360NetworkMessageSend::WriteToBuffer( bf_write &buffer ) const
|
|
{
|
|
buffer.WriteUBitLong( GetType(), NETMSG_TYPE_BITS );
|
|
|
|
buffer.WriteLong( g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() );
|
|
|
|
CUtlBuffer bufData;
|
|
bufData.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
|
|
m_pData->WriteAsBinary( bufData );
|
|
|
|
buffer.WriteLong( bufData.TellMaxPut() );
|
|
buffer.WriteBytes( bufData.Base(), bufData.TellMaxPut() );
|
|
|
|
if ( KeyValues *kvBinary = m_pData->FindKey( "binary" ) )
|
|
{
|
|
void *pvData = kvBinary->GetPtr( "ptr", NULL );
|
|
int nSize = kvBinary->GetInt( "size", 0 );
|
|
|
|
if ( pvData && nSize )
|
|
{
|
|
buffer.WriteLong( nSize );
|
|
if ( !buffer.WriteBytes( pvData, nSize ) )
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
buffer.WriteLong( 0 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
buffer.WriteLong( 0 );
|
|
}
|
|
|
|
return !buffer.IsOverflowed();
|
|
}
|
|
|
|
bool CX360NetworkMessageRecv::ReadFromBuffer( bf_read &buffer )
|
|
{
|
|
if ( buffer.ReadLong() != g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() )
|
|
return false;
|
|
|
|
int nDataLen = buffer.ReadLong();
|
|
if ( nDataLen <= 0 )
|
|
return false;
|
|
|
|
m_gsKeyValues.Grow( nDataLen );
|
|
if ( !buffer.ReadBytes( m_gsKeyValues.m_pbData, nDataLen ) )
|
|
return false;
|
|
|
|
if ( !m_pData )
|
|
m_pData = new KeyValues( "" );
|
|
else
|
|
m_pData->Clear();
|
|
|
|
CUtlBuffer bufData( m_gsKeyValues.m_pbData, nDataLen, CUtlBuffer::READ_ONLY );
|
|
bufData.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
|
|
if ( !m_pData->ReadAsBinary( bufData ) )
|
|
{
|
|
m_pData->Clear();
|
|
return false;
|
|
}
|
|
|
|
int nBinaryBytes = buffer.ReadLong();
|
|
if ( nBinaryBytes > 0 )
|
|
{
|
|
m_gsBinaryData.Grow( nBinaryBytes );
|
|
if ( !buffer.ReadBytes( m_gsBinaryData.m_pbData, nBinaryBytes ) )
|
|
return false;
|
|
|
|
if ( KeyValues *kvPtr = m_pData->FindKey( "binary/ptr" ) )
|
|
kvPtr->SetPtr( "", m_gsBinaryData.m_pbData );
|
|
}
|
|
|
|
return !buffer.IsOverflowed();
|
|
}
|
|
|
|
CX360NetworkMessageRecv::~CX360NetworkMessageRecv()
|
|
{
|
|
if ( m_pData )
|
|
m_pData->deleteThis();
|
|
m_pData = NULL;
|
|
}
|
|
|
|
bool CX360NetworkMessageRecv::Process()
|
|
{
|
|
m_pMgr->OnConnectionMessage( m_pData );
|
|
return true;
|
|
}
|
|
|
|
void CX360NetworkMgr::ConnectionMessageHandler_t::ConnectionStart( INetChannel *chan )
|
|
{
|
|
m_pChannel = chan;
|
|
|
|
CX360NetworkMessageRecv *pMsg = new CX360NetworkMessageRecv( m_pMgr );
|
|
m_pChannel->RegisterMessage( pMsg );
|
|
}
|
|
|
|
void CX360NetworkMgr::ConnectionPeerSendMessage( KeyValues *pMsg )
|
|
{
|
|
DevMsg( 2, "[NET] -> ConnectionPeerSendMessage\n" );
|
|
KeyValuesDumpAsDevMsg( pMsg, 1, 2 );
|
|
|
|
for ( int idx = m_arrConnections.FirstInorder();
|
|
idx != m_arrConnections.InvalidIndex();
|
|
idx = m_arrConnections.NextInorder( idx ) )
|
|
{
|
|
XUID xuidKey = m_arrConnections.Key( idx );
|
|
ConnectionInfo_t const &ci = m_arrConnections.Element( idx );
|
|
if ( !xuidKey && m_arrConnections.Count() > 1 )
|
|
// skip host designation record if there are other records
|
|
// there should be an alias for host too
|
|
continue;
|
|
|
|
CX360NetworkMessageSend msg( pMsg );
|
|
bool bVoice = !Q_stricmp( pMsg->GetName(), "SysSession::Voice" ); // marks the message as voice-channel-VDP-data
|
|
|
|
ci.m_pNetChannel->SendNetMsg( msg, msg.IsReliable(), bVoice );
|
|
ci.m_pNetChannel->Transmit();
|
|
|
|
DevMsg( 2, " -> %llx (%p : %s)\n", ci.m_xuid, ci.m_pNetChannel,
|
|
ci.m_pNetChannel ? ci.m_pNetChannel->GetRemoteAddress().ToString() : "" );
|
|
}
|
|
}
|
|
|
|
char const * CX360NetworkMgr::ConnectionPeerGetAddress( XUID xuidRemote )
|
|
{
|
|
int idxConn = m_arrConnections.Find( xuidRemote );
|
|
if ( idxConn == m_arrConnections.InvalidIndex() )
|
|
return NULL;
|
|
|
|
ConnectionInfo_t const &ci = m_arrConnections.Element( idxConn );
|
|
if ( !ci.m_pNetChannel )
|
|
return NULL;
|
|
|
|
return ci.m_pNetChannel->GetRemoteAddress().ToString( true );
|
|
}
|
|
|
|
//
|
|
// Network communication implementation
|
|
//
|
|
|
|
void CX360NetworkMgr::ConnectionPeerSendConnectionless( XUID xuidRemote, KeyValues *pMsg )
|
|
{
|
|
// Client should have started an active connection
|
|
int idxConn = m_arrConnections.Find( xuidRemote );
|
|
if ( idxConn == m_arrConnections.InvalidIndex() )
|
|
{
|
|
DevWarning( "CX360NetworkMgr::ConnectionPeerSendConnectionless( xuidRemote = %llx ) XUID is not registered!\n",
|
|
xuidRemote );
|
|
Assert( 0 );
|
|
return;
|
|
}
|
|
|
|
DevMsg( 2, "[NET] -> ConnectionPeerSendConnectionless( %llx )\n", xuidRemote );
|
|
KeyValuesDumpAsDevMsg( pMsg, 1, 2 );
|
|
|
|
ConnectionInfo_t const &ci = m_arrConnections.Element( idxConn );
|
|
|
|
g_pConnectionlessLanMgr->SendPacket( pMsg, ci.m_pNetChannel->GetRemoteAddress().ToString(),
|
|
m_eSocket );
|
|
}
|
|
|
|
bool CX360NetworkMgr::ProcessConnectionlessPacket( netpacket_t *packet )
|
|
{
|
|
Assert( m_pListener );
|
|
|
|
// Unpack key values
|
|
if ( KeyValues *pMsg = g_pConnectionlessLanMgr->UnpackPacket( packet ) )
|
|
{
|
|
KeyValues::AutoDelete autodelete( pMsg );
|
|
|
|
DevMsg( 2, "[NET] <- OnX360NetConnectionlessPacket( %s )\n", packet->from.ToString() );
|
|
KeyValuesDumpAsDevMsg( pMsg, 1, 2 );
|
|
|
|
if ( m_pListener && m_pListener->OnX360NetConnectionlessPacket( packet, pMsg ) )
|
|
return true;
|
|
}
|
|
|
|
// Otherwise this is an unwanted packet, prevent any more packets from this peer
|
|
ConnectionPeerClose( packet );
|
|
return false;
|
|
}
|
|
|
|
void CX360NetworkMgr::OnConnectionMessage( KeyValues *pMsg )
|
|
{
|
|
Assert( m_pListener );
|
|
|
|
DevMsg( 2, "[NET] <- OnConnectionMessage\n" );
|
|
KeyValuesDumpAsDevMsg( pMsg, 1, 2 );
|
|
|
|
if ( m_pListener )
|
|
{
|
|
m_pListener->OnX360NetPacket( pMsg );
|
|
}
|
|
}
|
|
|
|
void CX360NetworkMgr::OnConnectionClosing( INetChannel *pNetChannel )
|
|
{
|
|
// This is called in several cases:
|
|
// - we called pNetChannel->Shutdown (we wouldn't find this channel then)
|
|
// - remote side shut down the channel (we should still find this channel)
|
|
// - we crashed while pumping buffered messages (we should still find this channel)
|
|
|
|
ConnectionInfo_t const *ci = NULL;
|
|
for ( int idx = m_arrConnections.FirstInorder();
|
|
!ci && idx != m_arrConnections.InvalidIndex();
|
|
idx = m_arrConnections.NextInorder( idx ) )
|
|
{
|
|
ConnectionInfo_t const &record = m_arrConnections.Element( idx );
|
|
if ( record.m_pNetChannel == pNetChannel )
|
|
ci = &record;
|
|
}
|
|
|
|
if ( !ci )
|
|
{
|
|
// This is a notification from inside Shutdown initiated by us, ignore
|
|
return;
|
|
}
|
|
|
|
// Otherwise something went wrong with the connection and we
|
|
// have no other option, but close it and release secure association
|
|
XUID xuidRemote = ci->m_xuid;
|
|
DevMsg( "CX360NetworkMgr::OnConnectionClosing( xuidRemote = %llx, ip = %s )\n",
|
|
xuidRemote, pNetChannel->GetRemoteAddress().ToString( true ) );
|
|
ConnectionPeerClose( xuidRemote );
|
|
|
|
// Notify listener
|
|
if ( m_pListener )
|
|
{
|
|
m_pListener->OnX360NetDisconnected( xuidRemote );
|
|
}
|
|
}
|
|
|
|
bool CX360NetworkMgr::ConnectionPeerOpenPassive( XUID xuidRemote, netpacket_t *pktIncoming, XNKID *pxnkidSession /*= NULL*/ )
|
|
{
|
|
// Check if we already have the corresponding connection open, close it if it is open
|
|
if ( m_arrConnections.Find( xuidRemote ) != m_arrConnections.InvalidIndex() )
|
|
{
|
|
DevWarning( "CX360NetworkMgr::ConnectionPeerOpenPassive( xuidRemote = %llx, ip = %s ) attempted on a duplicate XUID!\n",
|
|
xuidRemote, pktIncoming->from.ToString() );
|
|
|
|
ConnectionPeerClose( xuidRemote );
|
|
|
|
// Notify listener
|
|
if ( m_pListener )
|
|
{
|
|
m_pListener->OnX360NetDisconnected( xuidRemote );
|
|
}
|
|
|
|
DevWarning( "CX360NetworkMgr::ConnectionPeerOpenPassive proceeding after stale duplicate connection closed...\n" );
|
|
}
|
|
|
|
//
|
|
// XNetInAddrToXnAddr
|
|
//
|
|
XNADDR xnaddrRemote = {0};
|
|
XNKID xnkidRemote = {0};
|
|
IN_ADDR inaddrRemote = {0};
|
|
inaddrRemote.s_addr = pktIncoming->from.GetIPNetworkByteOrder();
|
|
|
|
if ( pxnkidSession )
|
|
xnkidRemote = *pxnkidSession;
|
|
|
|
if ( pxnkidSession ) // if ( pxnkidSession && !XNetXnKidIsSystemLink( pxnkidSession ) )
|
|
{
|
|
if ( int err = g_pMatchExtensions->GetIXOnline()->XNetInAddrToXnAddr( inaddrRemote, &xnaddrRemote, &xnkidRemote ) )
|
|
{
|
|
DevWarning( "CX360NetworkMgr::ConnectionPeerOpenPassive( xuidRemote = %llx, ip = %s ) failed to resolve XNADDR ( code 0x%08X )\n",
|
|
xuidRemote, pktIncoming->from.ToString(), err );
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
xnaddrRemote.ina = inaddrRemote;
|
|
xnaddrRemote.wPortOnline = pktIncoming->from.GetPort();
|
|
}
|
|
|
|
if ( pxnkidSession )
|
|
*pxnkidSession = xnkidRemote;
|
|
|
|
//
|
|
// Register the connection internally
|
|
//
|
|
ConnectionMessageHandler_t *pHandler = new ConnectionMessageHandler_t( this, NULL );
|
|
|
|
INetChannel *pChannel = g_pMatchExtensions->GetINetSupport()->CreateChannel(
|
|
m_eSocket, pktIncoming->from,
|
|
CFmtStr( "MM:%llx", xuidRemote ), pHandler );
|
|
|
|
ConnectionInfo_t ci;
|
|
ci.m_xuid = xuidRemote;
|
|
ci.m_inaddr = inaddrRemote;
|
|
ci.m_xnaddr = xnaddrRemote;
|
|
ci.m_pNetChannel = pChannel;
|
|
ci.m_pHandler = pHandler;
|
|
|
|
m_arrConnections.Insert( xuidRemote, ci );
|
|
|
|
DevMsg( "CX360NetworkMgr::ConnectionPeerOpenPassive( xuidRemote = %llx, ip = %s ) succeeded (pNetChannel = %p).\n",
|
|
xuidRemote, pktIncoming->from.ToString(), pChannel );
|
|
return true;
|
|
}
|
|
|
|
bool CX360NetworkMgr::ConnectionPeerOpenActive( XUID xuidRemote, XSESSION_INFO const &xRemote )
|
|
{
|
|
// Check if we already have the corresponding connection open
|
|
if ( m_arrConnections.Find( xuidRemote ) != m_arrConnections.InvalidIndex() )
|
|
{
|
|
DevWarning( "CX360NetworkMgr::ConnectionPeerOpenActive( xuidRemote = %llx ) attempted on a duplicate XUID!\n",
|
|
xuidRemote );
|
|
Assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// XNetXnAddrToInAddr
|
|
//
|
|
XNADDR xnaddrRemote = xRemote.hostAddress;
|
|
IN_ADDR inaddrRemote;
|
|
if ( 1 ) // if ( !XNetXnKidIsSystemLink( &xRemote.sessionID ) )
|
|
{
|
|
if ( int err = g_pMatchExtensions->GetIXOnline()->XNetXnAddrToInAddr( &xRemote.hostAddress, &xRemote.sessionID, &inaddrRemote ) )
|
|
{
|
|
DevWarning( "CX360NetworkMgr::ConnectionPeerOpenActive( xuidRemote = %llx ) failed to resolve XNADDR ( code 0x%08X )\n",
|
|
xuidRemote, err );
|
|
return false;
|
|
}
|
|
|
|
// Initiate secure connection and key exchange
|
|
if ( int err = g_pMatchExtensions->GetIXOnline()->XNetConnect( inaddrRemote ) )
|
|
{
|
|
DevWarning( "CX360NetworkMgr::ConnectionPeerOpenActive( xuidRemote = %llx ) failed to start key exchange ( code 0x%08X )\n",
|
|
xuidRemote, err );
|
|
g_pMatchExtensions->GetIXOnline()->XNetUnregisterInAddr( inaddrRemote ); // need to unregister inaddr, otherwise we will leak a secure association
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
inaddrRemote = xnaddrRemote.ina;
|
|
xnaddrRemote.inaOnline.s_addr = 0;
|
|
}
|
|
|
|
//
|
|
// Register the connection internally
|
|
//
|
|
netadr_t inetAddr;
|
|
inetAddr.SetType( NA_IP );
|
|
inetAddr.SetIPAndPort( inaddrRemote.s_addr, 0 );
|
|
|
|
ConnectionMessageHandler_t *pHandler = new ConnectionMessageHandler_t( this, NULL );
|
|
|
|
INetChannel *pChannel = g_pMatchExtensions->GetINetSupport()->CreateChannel(
|
|
m_eSocket, inetAddr,
|
|
CFmtStr( "MM:XNKID:%llx", ( const uint64 & ) xRemote.sessionID ), pHandler );
|
|
|
|
ConnectionInfo_t ci;
|
|
ci.m_xuid = xuidRemote;
|
|
ci.m_inaddr = inaddrRemote;
|
|
ci.m_xnaddr = xnaddrRemote;
|
|
ci.m_pNetChannel = pChannel;
|
|
ci.m_pHandler = pHandler;
|
|
|
|
m_arrConnections.Insert( xuidRemote, ci );
|
|
|
|
DevMsg( "CX360NetworkMgr::ConnectionPeerOpenActive( xuidRemote = %llx, xnkid = %llx, ip = %s ) succeeded (pNetChannel = %p), connection %s.\n",
|
|
xuidRemote, ( const uint64 & ) xRemote.sessionID, inetAddr.ToString( true ), pChannel,
|
|
XNetXnKidIsSystemLink( &xRemote.sessionID ) ? "LAN" : "SecureXnet" );
|
|
|
|
// Caller is required to transmit a first connectionless packet on net channel address
|
|
// to poke through secure handshaking
|
|
pChannel->SetTimeout( mm_net_channel_timeout.GetFloat() );
|
|
// pChannel->Transmit();
|
|
|
|
return true;
|
|
}
|
|
|
|
void CX360NetworkMgr::ConnectionPeerUpdateXuid( XUID xuidRemoteOld, XUID xuidRemoteNew )
|
|
{
|
|
Assert( !xuidRemoteOld != !xuidRemoteNew ); // either of the XUIDs must be NULL
|
|
|
|
int idxOldRecord = m_arrConnections.Find( xuidRemoteOld );
|
|
Assert( idxOldRecord != m_arrConnections.InvalidIndex() );
|
|
|
|
int idxNewRecord = m_arrConnections.Find( xuidRemoteNew );
|
|
// Assert( idxNewRecord == m_arrConnections.InvalidIndex() );
|
|
|
|
if ( idxOldRecord != m_arrConnections.InvalidIndex() &&
|
|
idxNewRecord == m_arrConnections.InvalidIndex() )
|
|
{
|
|
// Adding an alias to a peer network connection:
|
|
// - either found out a host xuid after anonymous connect by NULL xuid
|
|
// - or another client migrated to become a new host and adding a NULL xuid alias for that client
|
|
ConnectionInfo_t &ci = m_arrConnections.Element( idxOldRecord );
|
|
ci.m_xuid = xuidRemoteNew ? xuidRemoteNew : xuidRemoteOld;
|
|
|
|
ConnectionInfo_t const ciAlias = ci; // need to keep a copy on the stack in case insert reallocates memory
|
|
m_arrConnections.Insert( xuidRemoteNew, ciAlias );
|
|
|
|
DevMsg( "CX360NetworkMgr::ConnectionPeerUpdateXuid: xuidRemote = %llx + %llx, ip = %s, pNetChannel = %p.\n",
|
|
xuidRemoteOld, xuidRemoteNew, ciAlias.m_pNetChannel->GetRemoteAddress().ToString(), ciAlias.m_pNetChannel );
|
|
}
|
|
else if ( idxOldRecord != m_arrConnections.InvalidIndex() &&
|
|
idxNewRecord != m_arrConnections.InvalidIndex() &&
|
|
!xuidRemoteNew )
|
|
{
|
|
// Handling a case when client migrated to become a new host and needs a NULL xuid alias,
|
|
// but the old host is still registered in network manager with a NULL xuid alias:
|
|
// just overwrite the existing NULL alias to point to the new host
|
|
ConnectionInfo_t &ciOld = m_arrConnections.Element( idxOldRecord );
|
|
ConnectionInfo_t &ciNew = m_arrConnections.Element( idxNewRecord );
|
|
|
|
DevMsg( "CX360NetworkMgr::ConnectionPeerUpdateXuid: xuidRemote = %llx + %llx, ip = %s, pNetChannel = %p (alias override for host).\n",
|
|
xuidRemoteOld, xuidRemoteNew, ciOld.m_pNetChannel->GetRemoteAddress().ToString(), ciOld.m_pNetChannel );
|
|
|
|
ciNew = ciOld;
|
|
}
|
|
else
|
|
{
|
|
DevWarning( "CX360NetworkMgr::ConnectionPeerUpdateXuid: ERROR: xuidRemote = %llx (%s) + %llx (%s)!\n",
|
|
xuidRemoteOld, ( ( idxOldRecord != m_arrConnections.InvalidIndex() ) ? "valid" : "missing" ),
|
|
xuidRemoteNew, ( ( idxNewRecord != m_arrConnections.InvalidIndex() ) ? "valid" : "missing" ) );
|
|
Assert( 0 );
|
|
}
|
|
}
|
|
|
|
void CX360NetworkMgr::ConnectionPeerClose( netpacket_t *pktIncoming )
|
|
{
|
|
IN_ADDR inaddrRemote;
|
|
inaddrRemote.s_addr = pktIncoming->from.GetIPNetworkByteOrder();
|
|
|
|
g_pMatchExtensions->GetIXOnline()->XNetUnregisterInAddr( inaddrRemote );
|
|
}
|
|
|
|
void CX360NetworkMgr::ConnectionPeerClose( XUID xuidRemote )
|
|
{
|
|
//
|
|
// Find the connection record
|
|
//
|
|
int idx = m_arrConnections.Find( xuidRemote );
|
|
if ( !m_arrConnections.IsValidIndex( idx ) )
|
|
{
|
|
DevWarning( "CX360NetworkMgr::ConnectionPeerClose( xuidRemote = %llx ) failed: not registered!\n",
|
|
xuidRemote );
|
|
Assert( 0 );
|
|
return;
|
|
}
|
|
|
|
ConnectionInfo_t const ci = m_arrConnections[ idx ]; // get a copy
|
|
|
|
//
|
|
// Remove the connection record and host identifier if it refers to the same machine
|
|
//
|
|
m_arrConnections.RemoveAt( idx );
|
|
|
|
Assert( ci.m_xuid == xuidRemote || !ci.m_xuid || !xuidRemote ); // NULL record identifies host and only it can be duplicated
|
|
m_arrConnections.Remove( ci.m_xuid );
|
|
|
|
int idxHost = m_arrConnections.Find( 0ull );
|
|
if ( m_arrConnections.IsValidIndex( idxHost ) )
|
|
{
|
|
ConnectionInfo_t const &ciHost = m_arrConnections.Element( idxHost );
|
|
if ( ciHost.m_xuid == xuidRemote )
|
|
m_arrConnections.RemoveAt( idxHost );
|
|
}
|
|
|
|
//
|
|
// Cleanup resources associated with the connection
|
|
//
|
|
netadr_t inetAddr = ci.m_pNetChannel->GetRemoteAddress();
|
|
ci.m_pNetChannel->Shutdown( "" );
|
|
delete ci.m_pHandler;
|
|
|
|
if ( ci.m_xnaddr.inaOnline.s_addr )
|
|
{
|
|
g_pMatchExtensions->GetIXOnline()->XNetUnregisterInAddr( ci.m_inaddr );
|
|
}
|
|
|
|
DevMsg( "CX360NetworkMgr::ConnectionPeerClose: xuidRemote = %llx, ip = %s, pNetChannel = %p.\n",
|
|
ci.m_xuid, inetAddr.ToString(), ci.m_pNetChannel );
|
|
}
|
|
|
|
#endif
|