|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGateKeeperLib/Private/plNglGateKeeper.cpp
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include "../Pch.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
// This file excluded from pre-compiled header because it is auto-generated by the build server.
|
|
|
|
#include "pnNetBase/pnNbGateKeeperKey.hpp"
|
|
|
|
|
|
|
|
namespace Ngl { namespace GateKeeper {
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Private
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
struct CliGkConn : AtomicRef {
|
|
|
|
CliGkConn ();
|
|
|
|
~CliGkConn ();
|
|
|
|
|
|
|
|
// Reconnection
|
|
|
|
AsyncTimer * reconnectTimer;
|
|
|
|
unsigned reconnectStartMs;
|
|
|
|
|
|
|
|
// Ping
|
|
|
|
AsyncTimer * pingTimer;
|
|
|
|
unsigned pingSendTimeMs;
|
|
|
|
unsigned lastHeardTimeMs;
|
|
|
|
|
|
|
|
// This function should be called during object construction
|
|
|
|
// to initiate connection attempts to the remote host whenever
|
|
|
|
// the socket is disconnected.
|
|
|
|
void AutoReconnect ();
|
|
|
|
bool AutoReconnectEnabled ();
|
|
|
|
void StopAutoReconnect (); // call before destruction
|
|
|
|
void StartAutoReconnect ();
|
|
|
|
void TimerReconnect ();
|
|
|
|
|
|
|
|
// ping
|
|
|
|
void AutoPing ();
|
|
|
|
void StopAutoPing ();
|
|
|
|
void TimerPing ();
|
|
|
|
|
|
|
|
void Send (const unsigned_ptr fields[], unsigned count);
|
|
|
|
|
|
|
|
CCritSect critsect;
|
|
|
|
LINK(CliGkConn) link;
|
|
|
|
AsyncSocket sock;
|
|
|
|
NetCli * cli;
|
|
|
|
wchar name[MAX_PATH];
|
|
|
|
NetAddress addr;
|
|
|
|
Uuid token;
|
|
|
|
unsigned seq;
|
|
|
|
unsigned serverChallenge;
|
|
|
|
AsyncCancelId cancelId;
|
|
|
|
bool abandoned;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
// PingRequestTrans
|
|
|
|
//============================================================================
|
|
|
|
struct PingRequestTrans : NetGateKeeperTrans {
|
|
|
|
FNetCliGateKeeperPingRequestCallback m_callback;
|
|
|
|
void * m_param;
|
|
|
|
unsigned m_pingAtMs;
|
|
|
|
unsigned m_replyAtMs;
|
|
|
|
ARRAY(byte) m_payload;
|
|
|
|
|
|
|
|
PingRequestTrans (
|
|
|
|
FNetCliGateKeeperPingRequestCallback callback,
|
|
|
|
void * param,
|
|
|
|
unsigned pingAtMs,
|
|
|
|
unsigned payloadBytes,
|
|
|
|
const void * payload
|
|
|
|
);
|
|
|
|
|
|
|
|
bool Send ();
|
|
|
|
void Post ();
|
|
|
|
bool Recv (
|
|
|
|
const byte msg[],
|
|
|
|
unsigned bytes
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
// FileSrvIpAddressRequestTrans
|
|
|
|
//============================================================================
|
|
|
|
struct FileSrvIpAddressRequestTrans : NetGateKeeperTrans {
|
|
|
|
FNetCliGateKeeperFileSrvIpAddressRequestCallback m_callback;
|
|
|
|
void * m_param;
|
|
|
|
wchar m_addr[64];
|
|
|
|
bool m_isPatcher;
|
|
|
|
|
|
|
|
FileSrvIpAddressRequestTrans (
|
|
|
|
FNetCliGateKeeperFileSrvIpAddressRequestCallback callback,
|
|
|
|
void * param,
|
|
|
|
bool isPatcher
|
|
|
|
);
|
|
|
|
|
|
|
|
bool Send ();
|
|
|
|
void Post ();
|
|
|
|
bool Recv (
|
|
|
|
const byte msg[],
|
|
|
|
unsigned bytes
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
// AuthSrvIpAddressRequestTrans
|
|
|
|
//============================================================================
|
|
|
|
struct AuthSrvIpAddressRequestTrans : NetGateKeeperTrans {
|
|
|
|
FNetCliGateKeeperAuthSrvIpAddressRequestCallback m_callback;
|
|
|
|
void * m_param;
|
|
|
|
wchar m_addr[64];
|
|
|
|
|
|
|
|
AuthSrvIpAddressRequestTrans (
|
|
|
|
FNetCliGateKeeperAuthSrvIpAddressRequestCallback callback,
|
|
|
|
void * param
|
|
|
|
);
|
|
|
|
|
|
|
|
bool Send ();
|
|
|
|
void Post ();
|
|
|
|
bool Recv (
|
|
|
|
const byte msg[],
|
|
|
|
unsigned bytes
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Private data
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
enum {
|
|
|
|
kPerfConnCount,
|
|
|
|
kPingDisabled,
|
|
|
|
kAutoReconnectDisabled,
|
|
|
|
kNumPerf
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool s_running;
|
|
|
|
static CCritSect s_critsect;
|
|
|
|
static LISTDECL(CliGkConn, link) s_conns;
|
|
|
|
static CliGkConn * s_active;
|
|
|
|
|
|
|
|
static long s_perf[kNumPerf];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Internal functions
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
static unsigned GetNonZeroTimeMs () {
|
|
|
|
if (unsigned ms = TimeGetMs())
|
|
|
|
return ms;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static CliGkConn * GetConnIncRef_CS (const char tag[]) {
|
|
|
|
if (CliGkConn * conn = s_active) {
|
|
|
|
conn->IncRef(tag);
|
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static CliGkConn * GetConnIncRef (const char tag[]) {
|
|
|
|
CliGkConn * conn;
|
|
|
|
s_critsect.Enter();
|
|
|
|
{
|
|
|
|
conn = GetConnIncRef_CS(tag);
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static void UnlinkAndAbandonConn_CS (CliGkConn * conn) {
|
|
|
|
s_conns.Unlink(conn);
|
|
|
|
conn->abandoned = true;
|
|
|
|
|
|
|
|
conn->StopAutoReconnect();
|
|
|
|
|
|
|
|
if (conn->cancelId) {
|
|
|
|
AsyncSocketConnectCancel(nil, conn->cancelId);
|
|
|
|
conn->cancelId = 0;
|
|
|
|
}
|
|
|
|
else if (conn->sock) {
|
|
|
|
AsyncSocketDisconnect(conn->sock, true);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
conn->DecRef("Lifetime");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static void SendClientRegisterRequest (CliGkConn * conn) {
|
|
|
|
/* const unsigned_ptr msg[] = {
|
|
|
|
kCli2GateKeeper_ClientRegisterRequest,
|
|
|
|
BuildId(),
|
|
|
|
};
|
|
|
|
|
|
|
|
conn->Send(msg, arrsize(msg));*/
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static bool ConnEncrypt (ENetError error, void * param) {
|
|
|
|
CliGkConn * conn = (CliGkConn *) param;
|
|
|
|
|
|
|
|
if (IS_NET_SUCCESS(error)) {
|
|
|
|
|
|
|
|
//SendClientRegisterRequest(conn);
|
|
|
|
|
|
|
|
if (!s_perf[kPingDisabled])
|
|
|
|
conn->AutoPing();
|
|
|
|
|
|
|
|
s_critsect.Enter();
|
|
|
|
{
|
|
|
|
SWAP(s_active, conn);
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
|
|
|
|
// AuthConnectedNotifyTrans * trans = NEW(AuthConnectedNotifyTrans);
|
|
|
|
// NetTransSend(trans);
|
|
|
|
}
|
|
|
|
|
|
|
|
return IS_NET_SUCCESS(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static void NotifyConnSocketConnect (CliGkConn * conn) {
|
|
|
|
|
|
|
|
conn->TransferRef("Connecting", "Connected");
|
|
|
|
conn->cli = NetCliConnectAccept(
|
|
|
|
conn->sock,
|
|
|
|
kNetProtocolCli2GateKeeper,
|
|
|
|
false,
|
|
|
|
ConnEncrypt,
|
|
|
|
0,
|
|
|
|
nil,
|
|
|
|
conn
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static void CheckedReconnect (CliGkConn * conn, ENetError error) {
|
|
|
|
|
|
|
|
unsigned disconnectedMs = GetNonZeroTimeMs() - conn->lastHeardTimeMs;
|
|
|
|
|
|
|
|
// no auto-reconnect or haven't heard from the server in a while?
|
|
|
|
if (!conn->AutoReconnectEnabled() || (int)disconnectedMs >= (int)kDisconnectedTimeoutMs) {
|
|
|
|
// Cancel all transactions in progress on this connection.
|
|
|
|
NetTransCancelByConnId(conn->seq, kNetErrTimeout);
|
|
|
|
// conn is dead.
|
|
|
|
conn->DecRef("Lifetime");
|
|
|
|
ReportNetError(kNetProtocolCli2GateKeeper, error);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (conn->token != kNilGuid)
|
|
|
|
// previously encrypted; reconnect quickly
|
|
|
|
conn->reconnectStartMs = GetNonZeroTimeMs() + 500;
|
|
|
|
else
|
|
|
|
// never encrypted; reconnect slowly
|
|
|
|
conn->reconnectStartMs = GetNonZeroTimeMs() + kMaxReconnectIntervalMs;
|
|
|
|
|
|
|
|
// clean up the socket and start reconnect
|
|
|
|
if (conn->cli)
|
|
|
|
NetCliDelete(conn->cli, true);
|
|
|
|
conn->cli = nil;
|
|
|
|
conn->sock = nil;
|
|
|
|
|
|
|
|
conn->StartAutoReconnect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static void NotifyConnSocketConnectFailed (CliGkConn * conn) {
|
|
|
|
|
|
|
|
s_critsect.Enter();
|
|
|
|
{
|
|
|
|
conn->cancelId = 0;
|
|
|
|
s_conns.Unlink(conn);
|
|
|
|
|
|
|
|
if (conn == s_active)
|
|
|
|
s_active = nil;
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
|
|
|
|
CheckedReconnect(conn, kNetErrConnectFailed);
|
|
|
|
|
|
|
|
conn->DecRef("Connecting");
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static void NotifyConnSocketDisconnect (CliGkConn * conn) {
|
|
|
|
|
|
|
|
conn->StopAutoPing();
|
|
|
|
|
|
|
|
s_critsect.Enter();
|
|
|
|
{
|
|
|
|
conn->cancelId = 0;
|
|
|
|
s_conns.Unlink(conn);
|
|
|
|
|
|
|
|
if (conn == s_active)
|
|
|
|
s_active = nil;
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
|
|
|
|
// Cancel all transactions in process on this connection.
|
|
|
|
NetTransCancelByConnId(conn->seq, kNetErrTimeout);
|
|
|
|
conn->DecRef("Connected");
|
|
|
|
conn->DecRef("Lifetime");
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static bool NotifyConnSocketRead (CliGkConn * conn, AsyncNotifySocketRead * read) {
|
|
|
|
// TODO: Only dispatch messages from the active auth server
|
|
|
|
conn->lastHeardTimeMs = GetNonZeroTimeMs();
|
|
|
|
bool result = NetCliDispatch(conn->cli, read->buffer, read->bytes, conn);
|
|
|
|
read->bytesProcessed += read->bytes;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static bool SocketNotifyCallback (
|
|
|
|
AsyncSocket sock,
|
|
|
|
EAsyncNotifySocket code,
|
|
|
|
AsyncNotifySocket * notify,
|
|
|
|
void ** userState
|
|
|
|
) {
|
|
|
|
bool result = true;
|
|
|
|
CliGkConn * conn;
|
|
|
|
|
|
|
|
switch (code) {
|
|
|
|
case kNotifySocketConnectSuccess:
|
|
|
|
conn = (CliGkConn *) notify->param;
|
|
|
|
*userState = conn;
|
|
|
|
bool abandoned;
|
|
|
|
s_critsect.Enter();
|
|
|
|
{
|
|
|
|
conn->sock = sock;
|
|
|
|
conn->cancelId = 0;
|
|
|
|
abandoned = conn->abandoned;
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
if (abandoned)
|
|
|
|
AsyncSocketDisconnect(sock, true);
|
|
|
|
else
|
|
|
|
NotifyConnSocketConnect(conn);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kNotifySocketConnectFailed:
|
|
|
|
conn = (CliGkConn *) notify->param;
|
|
|
|
NotifyConnSocketConnectFailed(conn);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kNotifySocketDisconnect:
|
|
|
|
conn = (CliGkConn *) *userState;
|
|
|
|
NotifyConnSocketDisconnect(conn);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kNotifySocketRead:
|
|
|
|
conn = (CliGkConn *) *userState;
|
|
|
|
result = NotifyConnSocketRead(conn, (AsyncNotifySocketRead *) notify);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static void Connect (
|
|
|
|
CliGkConn * conn
|
|
|
|
) {
|
|
|
|
ASSERT(s_running);
|
|
|
|
|
|
|
|
conn->pingSendTimeMs = 0;
|
|
|
|
|
|
|
|
s_critsect.Enter();
|
|
|
|
{
|
|
|
|
while (CliGkConn * oldConn = s_conns.Head()) {
|
|
|
|
if (oldConn != conn)
|
|
|
|
UnlinkAndAbandonConn_CS(oldConn);
|
|
|
|
else
|
|
|
|
s_conns.Unlink(oldConn);
|
|
|
|
}
|
|
|
|
s_conns.Link(conn);
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
|
|
|
|
Cli2GateKeeper_Connect connect;
|
|
|
|
connect.hdr.connType = kConnTypeCliToGateKeeper;
|
|
|
|
connect.hdr.hdrBytes = sizeof(connect.hdr);
|
|
|
|
connect.hdr.buildId = BuildId();
|
|
|
|
connect.hdr.buildType = BuildType();
|
|
|
|
connect.hdr.branchId = BranchId();
|
|
|
|
connect.hdr.productId = ProductId();
|
|
|
|
connect.data.token = conn->token;
|
|
|
|
connect.data.dataBytes = sizeof(connect.data);
|
|
|
|
|
|
|
|
AsyncSocketConnect(
|
|
|
|
&conn->cancelId,
|
|
|
|
conn->addr,
|
|
|
|
SocketNotifyCallback,
|
|
|
|
conn,
|
|
|
|
&connect,
|
|
|
|
sizeof(connect),
|
|
|
|
0,
|
|
|
|
0
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static void Connect (
|
|
|
|
const wchar name[],
|
|
|
|
const NetAddress & addr
|
|
|
|
) {
|
|
|
|
ASSERT(s_running);
|
|
|
|
|
|
|
|
CliGkConn * conn = NEWZERO(CliGkConn);
|
|
|
|
conn->addr = addr;
|
|
|
|
conn->seq = ConnNextSequence();
|
|
|
|
conn->lastHeardTimeMs = GetNonZeroTimeMs(); // used in connect timeout, and ping timeout
|
|
|
|
StrCopy(conn->name, name, arrsize(conn->name));
|
|
|
|
|
|
|
|
conn->IncRef("Lifetime");
|
|
|
|
conn->AutoReconnect();
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static void AsyncLookupCallback (
|
|
|
|
void * param,
|
|
|
|
const wchar name[],
|
|
|
|
unsigned addrCount,
|
|
|
|
const NetAddress addrs[]
|
|
|
|
) {
|
|
|
|
if (!addrCount) {
|
|
|
|
ReportNetError(kNetProtocolCli2GateKeeper, kNetErrNameLookupFailed);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < addrCount; ++i) {
|
|
|
|
Connect(name, addrs[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* CliGkConn
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
static unsigned CliGkConnTimerDestroyed (void * param) {
|
|
|
|
CliGkConn * conn = (CliGkConn *) param;
|
|
|
|
conn->DecRef("TimerDestroyed");
|
|
|
|
return kAsyncTimeInfinite;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
static unsigned CliGkConnReconnectTimerProc (void * param) {
|
|
|
|
((CliGkConn *) param)->TimerReconnect();
|
|
|
|
return kAsyncTimeInfinite;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
static unsigned CliGkConnPingTimerProc (void * param) {
|
|
|
|
((CliGkConn *) param)->TimerPing();
|
|
|
|
return kPingIntervalMs;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
CliGkConn::CliGkConn () {
|
|
|
|
AtomicAdd(&s_perf[kPerfConnCount], 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
CliGkConn::~CliGkConn () {
|
|
|
|
if (cli)
|
|
|
|
NetCliDelete(cli, true);
|
|
|
|
AtomicAdd(&s_perf[kPerfConnCount], -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CliGkConn::TimerReconnect () {
|
|
|
|
ASSERT(!sock);
|
|
|
|
ASSERT(!cancelId);
|
|
|
|
|
|
|
|
if (!s_running) {
|
|
|
|
s_critsect.Enter();
|
|
|
|
UnlinkAndAbandonConn_CS(this);
|
|
|
|
s_critsect.Leave();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
IncRef("Connecting");
|
|
|
|
|
|
|
|
// Remember the time we started the reconnect attempt, guarding against
|
|
|
|
// TimeGetMs() returning zero (unlikely), as a value of zero indicates
|
|
|
|
// a first-time connect condition to StartAutoReconnect()
|
|
|
|
reconnectStartMs = GetNonZeroTimeMs();
|
|
|
|
|
|
|
|
Connect(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
// This function is called when after a disconnect to start a new connection
|
|
|
|
void CliGkConn::StartAutoReconnect () {
|
|
|
|
critsect.Enter();
|
|
|
|
if (reconnectTimer && !s_perf[kAutoReconnectDisabled]) {
|
|
|
|
// Make reconnect attempts at regular intervals. If the last attempt
|
|
|
|
// took more than the specified max interval time then reconnect
|
|
|
|
// immediately; otherwise wait until the time interval is up again
|
|
|
|
// then reconnect.
|
|
|
|
unsigned remainingMs = 0;
|
|
|
|
if (reconnectStartMs) {
|
|
|
|
remainingMs = reconnectStartMs - GetNonZeroTimeMs();
|
|
|
|
if ((signed)remainingMs < 0)
|
|
|
|
remainingMs = 0;
|
|
|
|
LogMsg(kLogPerf, L"GateKeeper auto-reconnecting in %u ms", remainingMs);
|
|
|
|
}
|
|
|
|
AsyncTimerUpdate(reconnectTimer, remainingMs);
|
|
|
|
}
|
|
|
|
critsect.Leave();
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
// This function should be called during object construction
|
|
|
|
// to initiate connection attempts to the remote host whenever
|
|
|
|
// the socket is disconnected.
|
|
|
|
void CliGkConn::AutoReconnect () {
|
|
|
|
|
|
|
|
ASSERT(!reconnectTimer);
|
|
|
|
IncRef("ReconnectTimer");
|
|
|
|
critsect.Enter();
|
|
|
|
{
|
|
|
|
AsyncTimerCreate(
|
|
|
|
&reconnectTimer,
|
|
|
|
CliGkConnReconnectTimerProc,
|
|
|
|
0, // immediate callback
|
|
|
|
this
|
|
|
|
);
|
|
|
|
}
|
|
|
|
critsect.Leave();
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CliGkConn::StopAutoReconnect () {
|
|
|
|
critsect.Enter();
|
|
|
|
{
|
|
|
|
if (AsyncTimer * timer = reconnectTimer) {
|
|
|
|
reconnectTimer = nil;
|
|
|
|
AsyncTimerDeleteCallback(timer, CliGkConnTimerDestroyed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
critsect.Leave();
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
bool CliGkConn::AutoReconnectEnabled () {
|
|
|
|
|
|
|
|
return (reconnectTimer != nil) && !s_perf[kAutoReconnectDisabled];
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CliGkConn::AutoPing () {
|
|
|
|
ASSERT(!pingTimer);
|
|
|
|
IncRef("PingTimer");
|
|
|
|
critsect.Enter();
|
|
|
|
{
|
|
|
|
AsyncTimerCreate(
|
|
|
|
&pingTimer,
|
|
|
|
CliGkConnPingTimerProc,
|
|
|
|
sock ? 0 : kAsyncTimeInfinite,
|
|
|
|
this
|
|
|
|
);
|
|
|
|
}
|
|
|
|
critsect.Leave();
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CliGkConn::StopAutoPing () {
|
|
|
|
critsect.Enter();
|
|
|
|
{
|
|
|
|
if (AsyncTimer * timer = pingTimer) {
|
|
|
|
pingTimer = nil;
|
|
|
|
AsyncTimerDeleteCallback(timer, CliGkConnTimerDestroyed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
critsect.Leave();
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CliGkConn::TimerPing () {
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// if the time difference between when we last sent a ping and when we last
|
|
|
|
// heard from the server is >= 3x the ping interval, the socket is stale.
|
|
|
|
if (pingSendTimeMs && abs(int(pingSendTimeMs - lastHeardTimeMs)) >= kPingTimeoutMs) {
|
|
|
|
// ping timed out, disconnect the socket
|
|
|
|
AsyncSocketDisconnect(sock, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
// Send a ping request
|
|
|
|
pingSendTimeMs = GetNonZeroTimeMs();
|
|
|
|
|
|
|
|
const unsigned_ptr msg[] = {
|
|
|
|
kCli2GateKeeper_PingRequest,
|
|
|
|
pingSendTimeMs,
|
|
|
|
0, // not a transaction
|
|
|
|
0, // no payload
|
|
|
|
nil
|
|
|
|
};
|
|
|
|
|
|
|
|
//Send(msg, arrsize(msg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CliGkConn::Send (const unsigned_ptr fields[], unsigned count) {
|
|
|
|
critsect.Enter();
|
|
|
|
{
|
|
|
|
NetCliSend(cli, fields, count);
|
|
|
|
NetCliFlush(cli);
|
|
|
|
}
|
|
|
|
critsect.Leave();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Cli2GateKeeper message handlers
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static bool Recv_PingReply (
|
|
|
|
const byte msg[],
|
|
|
|
unsigned bytes,
|
|
|
|
void *
|
|
|
|
) {
|
|
|
|
const GateKeeper2Cli_PingReply & reply = *(const GateKeeper2Cli_PingReply *)msg;
|
|
|
|
|
|
|
|
if (reply.transId)
|
|
|
|
NetTransRecv(reply.transId, msg, bytes);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static bool Recv_FileSrvIpAddressReply (
|
|
|
|
const byte msg[],
|
|
|
|
unsigned bytes,
|
|
|
|
void *
|
|
|
|
) {
|
|
|
|
const GateKeeper2Cli_FileSrvIpAddressReply & reply = *(const GateKeeper2Cli_FileSrvIpAddressReply *)msg;
|
|
|
|
|
|
|
|
if (reply.transId)
|
|
|
|
NetTransRecv(reply.transId, msg, bytes);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static bool Recv_AuthSrvIpAddressReply (
|
|
|
|
const byte msg[],
|
|
|
|
unsigned bytes,
|
|
|
|
void *
|
|
|
|
) {
|
|
|
|
const GateKeeper2Cli_AuthSrvIpAddressReply & reply = *(const GateKeeper2Cli_AuthSrvIpAddressReply *)msg;
|
|
|
|
|
|
|
|
if (reply.transId)
|
|
|
|
NetTransRecv(reply.transId, msg, bytes);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Cli2Auth protocol
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
#define MSG(s) kNetMsg_Cli2GateKeeper_##s
|
|
|
|
static NetMsgInitSend s_send[] = {
|
|
|
|
{ MSG(PingRequest) },
|
|
|
|
{ MSG(FileSrvIpAddressRequest) },
|
|
|
|
{ MSG(AuthSrvIpAddressRequest) },
|
|
|
|
};
|
|
|
|
#undef MSG
|
|
|
|
|
|
|
|
#define MSG(s) kNetMsg_GateKeeper2Cli_##s, Recv_##s
|
|
|
|
static NetMsgInitRecv s_recv[] = {
|
|
|
|
{ MSG(PingReply) },
|
|
|
|
{ MSG(FileSrvIpAddressReply) },
|
|
|
|
};
|
|
|
|
#undef MSG
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* PingRequestTrans
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
PingRequestTrans::PingRequestTrans (
|
|
|
|
FNetCliGateKeeperPingRequestCallback callback,
|
|
|
|
void * param,
|
|
|
|
unsigned pingAtMs,
|
|
|
|
unsigned payloadBytes,
|
|
|
|
const void * payload
|
|
|
|
) : NetGateKeeperTrans(kPingRequestTrans)
|
|
|
|
, m_callback(callback)
|
|
|
|
, m_param(param)
|
|
|
|
, m_pingAtMs(pingAtMs)
|
|
|
|
{
|
|
|
|
m_payload.Set((const byte *)payload, payloadBytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
bool PingRequestTrans::Send () {
|
|
|
|
|
|
|
|
if (!AcquireConn())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const unsigned_ptr msg[] = {
|
|
|
|
kCli2GateKeeper_PingRequest,
|
|
|
|
m_pingAtMs,
|
|
|
|
m_transId,
|
|
|
|
m_payload.Count(),
|
|
|
|
(unsigned_ptr) m_payload.Ptr(),
|
|
|
|
};
|
|
|
|
|
|
|
|
m_conn->Send(msg, arrsize(msg));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void PingRequestTrans::Post () {
|
|
|
|
|
|
|
|
m_callback(
|
|
|
|
m_result,
|
|
|
|
m_param,
|
|
|
|
m_pingAtMs,
|
|
|
|
m_replyAtMs,
|
|
|
|
m_payload.Count(),
|
|
|
|
m_payload.Ptr()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
bool PingRequestTrans::Recv (
|
|
|
|
const byte msg[],
|
|
|
|
unsigned bytes
|
|
|
|
) {
|
|
|
|
const GateKeeper2Cli_PingReply & reply = *(const GateKeeper2Cli_PingReply *)msg;
|
|
|
|
|
|
|
|
m_payload.Set(reply.payload, reply.payloadBytes);
|
|
|
|
m_replyAtMs = TimeGetMs();
|
|
|
|
m_result = kNetSuccess;
|
|
|
|
m_state = kTransStateComplete;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* FileSrvIpAddressRequestTrans
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
FileSrvIpAddressRequestTrans::FileSrvIpAddressRequestTrans (
|
|
|
|
FNetCliGateKeeperFileSrvIpAddressRequestCallback callback,
|
|
|
|
void * param,
|
|
|
|
bool isPatcher
|
|
|
|
) : NetGateKeeperTrans(kGkFileSrvIpAddressRequestTrans)
|
|
|
|
, m_callback(callback)
|
|
|
|
, m_param(param)
|
|
|
|
, m_isPatcher(isPatcher)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
bool FileSrvIpAddressRequestTrans::Send () {
|
|
|
|
|
|
|
|
if (!AcquireConn())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
const unsigned_ptr msg[] = {
|
|
|
|
kCli2GateKeeper_FileSrvIpAddressRequest,
|
|
|
|
m_transId,
|
|
|
|
m_isPatcher == true ? 1 : 0
|
|
|
|
};
|
|
|
|
|
|
|
|
m_conn->Send(msg, arrsize(msg));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void FileSrvIpAddressRequestTrans::Post () {
|
|
|
|
|
|
|
|
m_callback(
|
|
|
|
m_result,
|
|
|
|
m_param,
|
|
|
|
m_addr
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
bool FileSrvIpAddressRequestTrans::Recv (
|
|
|
|
const byte msg[],
|
|
|
|
unsigned bytes
|
|
|
|
) {
|
|
|
|
const GateKeeper2Cli_FileSrvIpAddressReply & reply = *(const GateKeeper2Cli_FileSrvIpAddressReply *)msg;
|
|
|
|
|
|
|
|
|
|
|
|
m_result = kNetSuccess;
|
|
|
|
m_state = kTransStateComplete;
|
|
|
|
StrCopy(m_addr, reply.address, 64);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* AuthSrvIpAddressRequestTrans
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
AuthSrvIpAddressRequestTrans::AuthSrvIpAddressRequestTrans (
|
|
|
|
FNetCliGateKeeperFileSrvIpAddressRequestCallback callback,
|
|
|
|
void * param
|
|
|
|
) : NetGateKeeperTrans(kGkAuthSrvIpAddressRequestTrans)
|
|
|
|
, m_callback(callback)
|
|
|
|
, m_param(param)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
bool AuthSrvIpAddressRequestTrans::Send () {
|
|
|
|
|
|
|
|
if (!AcquireConn())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const unsigned_ptr msg[] = {
|
|
|
|
kCli2GateKeeper_AuthSrvIpAddressRequest,
|
|
|
|
m_transId,
|
|
|
|
};
|
|
|
|
|
|
|
|
m_conn->Send(msg, arrsize(msg));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void AuthSrvIpAddressRequestTrans::Post () {
|
|
|
|
|
|
|
|
m_callback(
|
|
|
|
m_result,
|
|
|
|
m_param,
|
|
|
|
m_addr
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
bool AuthSrvIpAddressRequestTrans::Recv (
|
|
|
|
const byte msg[],
|
|
|
|
unsigned bytes
|
|
|
|
) {
|
|
|
|
const GateKeeper2Cli_AuthSrvIpAddressReply & reply = *(const GateKeeper2Cli_AuthSrvIpAddressReply *)msg;
|
|
|
|
|
|
|
|
m_result = kNetSuccess;
|
|
|
|
m_state = kTransStateComplete;
|
|
|
|
StrCopy(m_addr, reply.address, 64);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} using namespace GateKeeper;
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* NetGateKeeperTrans
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
NetGateKeeperTrans::NetGateKeeperTrans (ETransType transType)
|
|
|
|
: NetTrans(kNetProtocolCli2GateKeeper, transType)
|
|
|
|
, m_conn(nil)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
NetGateKeeperTrans::~NetGateKeeperTrans () {
|
|
|
|
ReleaseConn();
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
bool NetGateKeeperTrans::AcquireConn () {
|
|
|
|
if (!m_conn)
|
|
|
|
m_conn = GetConnIncRef("AcquireConn");
|
|
|
|
return m_conn != nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void NetGateKeeperTrans::ReleaseConn () {
|
|
|
|
if (m_conn) {
|
|
|
|
m_conn->DecRef("AcquireConn");
|
|
|
|
m_conn = nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Protected functions
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void GateKeeperInitialize () {
|
|
|
|
s_running = true;
|
|
|
|
NetMsgProtocolRegister(
|
|
|
|
kNetProtocolCli2GateKeeper,
|
|
|
|
false,
|
|
|
|
s_send, arrsize(s_send),
|
|
|
|
s_recv, arrsize(s_recv),
|
|
|
|
kDhGValue,
|
|
|
|
BigNum(sizeof(kDhXData), kDhXData),
|
|
|
|
BigNum(sizeof(kDhNData), kDhNData)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void GateKeeperDestroy (bool wait) {
|
|
|
|
s_running = false;
|
|
|
|
|
|
|
|
NetTransCancelByProtocol(
|
|
|
|
kNetProtocolCli2GateKeeper,
|
|
|
|
kNetErrRemoteShutdown
|
|
|
|
);
|
|
|
|
NetMsgProtocolDestroy(
|
|
|
|
kNetProtocolCli2GateKeeper,
|
|
|
|
false
|
|
|
|
);
|
|
|
|
|
|
|
|
s_critsect.Enter();
|
|
|
|
{
|
|
|
|
while (CliGkConn * conn = s_conns.Head())
|
|
|
|
UnlinkAndAbandonConn_CS(conn);
|
|
|
|
s_active = nil;
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
|
|
|
|
if (!wait)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (s_perf[kPerfConnCount]) {
|
|
|
|
NetTransUpdate();
|
|
|
|
AsyncSleep(10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
bool GateKeeperQueryConnected () {
|
|
|
|
|
|
|
|
bool result;
|
|
|
|
s_critsect.Enter();
|
|
|
|
{
|
|
|
|
if (nil != (result = s_active))
|
|
|
|
result &= (nil != s_active->cli);
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
unsigned GateKeeperGetConnId () {
|
|
|
|
unsigned connId;
|
|
|
|
s_critsect.Enter();
|
|
|
|
connId = (s_active) ? s_active->seq : 0;
|
|
|
|
s_critsect.Leave();
|
|
|
|
return connId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} using namespace Ngl;
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Exported functions
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void NetCliGateKeeperStartConnect (
|
|
|
|
const wchar * gateKeeperAddrList[],
|
|
|
|
unsigned gateKeeperAddrCount
|
|
|
|
) {
|
|
|
|
gateKeeperAddrCount = min(gateKeeperAddrCount, 1);
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < gateKeeperAddrCount; ++i) {
|
|
|
|
// Do we need to lookup the address?
|
|
|
|
const wchar * name = gateKeeperAddrList[i];
|
|
|
|
while (unsigned ch = *name) {
|
|
|
|
++name;
|
|
|
|
if (!(isdigit(ch) || ch == L'.' || ch == L':')) {
|
|
|
|
AsyncCancelId cancelId;
|
|
|
|
AsyncAddressLookupName(
|
|
|
|
&cancelId,
|
|
|
|
AsyncLookupCallback,
|
|
|
|
gateKeeperAddrList[i],
|
|
|
|
kNetDefaultClientPort,
|
|
|
|
nil
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!name[0]) {
|
|
|
|
NetAddress addr;
|
|
|
|
NetAddressFromString(&addr, gateKeeperAddrList[i], kNetDefaultClientPort);
|
|
|
|
Connect(gateKeeperAddrList[i], addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void NetCliGateKeeperDisconnect () {
|
|
|
|
s_critsect.Enter();
|
|
|
|
{
|
|
|
|
while (CliGkConn * conn = s_conns.Head())
|
|
|
|
UnlinkAndAbandonConn_CS(conn);
|
|
|
|
s_active = nil;
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void NetCliGateKeeperPingRequest (
|
|
|
|
unsigned pingTimeMs,
|
|
|
|
unsigned payloadBytes,
|
|
|
|
const void * payload,
|
|
|
|
FNetCliGateKeeperPingRequestCallback callback,
|
|
|
|
void * param
|
|
|
|
) {
|
|
|
|
PingRequestTrans * trans = NEW(PingRequestTrans)(
|
|
|
|
callback,
|
|
|
|
param,
|
|
|
|
pingTimeMs,
|
|
|
|
payloadBytes,
|
|
|
|
payload
|
|
|
|
);
|
|
|
|
NetTransSend(trans);
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void NetCliGateKeeperFileSrvIpAddressRequest (
|
|
|
|
FNetCliGateKeeperFileSrvIpAddressRequestCallback callback,
|
|
|
|
void * param,
|
|
|
|
bool isPatcher
|
|
|
|
) {
|
|
|
|
FileSrvIpAddressRequestTrans * trans = NEW(FileSrvIpAddressRequestTrans)(callback, param, isPatcher);
|
|
|
|
NetTransSend(trans);
|
|
|
|
}
|