// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdnet.h"
#include "nel/net/module_gateway.h"
#include "nel/net/module.h"
#include "nel/net/module_manager.h"
#include "nel/net/module_socket.h"
#include "nel/net/module_message.h"
#include "nel/net/callback_client.h"
#include "nel/net/callback_server.h"
using namespace std;
using namespace NLMISC;
namespace NLNET
{
struct TSecurityDataDesc
{
TSecurityData *SecurityData;
TSecurityDataDesc()
: SecurityData(NULL)
{
}
void serial(CMemStream &s)
{
if (!s.isReading())
{
// write mode
TSecurityData *sd = SecurityData;
uint32 nbSecBlock = 0;
sint32 tagCountPos = s.reserve(4);
while (sd != NULL)
{
if (sd->DataTag == 0xff)
{
TUnknownSecurityData *usd = safe_cast(sd);
s.serial(usd->RealDataTag);
}
else
{
s.serial(sd->DataTag);
}
// reserve a place to store the size of the next element
sint32 pos = s.reserve(4);
s.serial(*sd);
// store the size
uint32 size = s.getPos()-pos-4;
s.poke(size, pos);
nbSecBlock++;
sd = sd->NextItem;
}
// store the number of item
s.poke(nbSecBlock, tagCountPos);
}
else
{
nlassert(SecurityData == NULL);
TSecurityData **pLastSd = &SecurityData;
// read mode
uint32 nbSecBlock;
s.serial(nbSecBlock);
if (nbSecBlock == 0)
SecurityData = NULL;
for (uint i=0; iserial(s);
}
else
{
sd->serial(s);
}
}
catch(EStreamOverflow e)
{
// FAILED to read the security block, rewind to old pos and serial as unknow
nlwarning("Error while reading stream for security data type %u", dataTag);
s.seek(pos, NLMISC::IStream::begin);
sd = new TUnknownSecurityData(dataTag, blockSize);
sd->serial(s);
}
*pLastSd = sd;
pLastSd = &(sd->NextItem);
}
}
}
};
/// Sub message for module description
struct TModuleDescCodec
{
TModuleId ModuleProxyId;
uint32 ModuleDistance;
string ModuleFullName;
string ModuleClass;
string ModuleManifest;
TSecurityDataDesc SecDesc;
TModuleDescCodec()
{
}
TModuleDescCodec(IModuleProxy *proxy)
{
ModuleProxyId = proxy->getModuleProxyId();
ModuleDistance = proxy->getModuleDistance()+1;
ModuleFullName = proxy->getModuleName();
ModuleClass = proxy->getModuleClassName();
ModuleManifest = proxy->getModuleManifest();
SecDesc.SecurityData = const_cast(proxy->getFirstSecurityData());
}
void serial(NLMISC::CMemStream &s)
{
s.serial(ModuleProxyId);
s.serial(ModuleDistance);
s.serial(ModuleFullName);
s.serial(ModuleManifest);
s.serial(ModuleClass);
s.serial(SecDesc);
}
};
/// message for module distance update
struct TModuleDistanceChangeMsg
{
TModuleId ModuleId;
uint32 NewDistance;
void serial(NLMISC::IStream &s)
{
s.serial(ModuleId);
s.serial(NewDistance);
}
};
/// message for module security update
struct TModuleSecurityChangeMsg
{
TModuleId ModuleId;
TSecurityDataDesc SecDesc;
void serial(NLMISC::CMemStream &s)
{
s.serial(ModuleId);
s.serial(SecDesc);
}
};
/// Message for module removing
struct TModuleRemMsg
{
vector RemovedModules;
void serial(NLMISC::IStream &s)
{
s.serialCont(RemovedModules);
}
};
/// Message for module operation
struct TModuleOperationMsg
{
TModuleId ModuleId;
string OperationName;
CMessage MessageBody;
void serial(NLMISC::IStream &s)
{
s.serial(ModuleId);
s.serial(OperationName);
s.serial(MessageBody);
}
};
/// message waiting next update for local dispatching
struct TLocalMessage
{
TModuleId SenderProxyId;
TModuleId AddresseProxyId;
CMessage Message;
};
/** Register the gateway in the module manager gateway registry
*/
void CModuleGateway::registerGateway()
{
IModuleManager::getInstance().registerModuleGateway(this);
}
/** Unregister the gateway in the module manager gateway registry
*/
void CModuleGateway::unregisterGateway()
{
IModuleManager::getInstance().unregisterModuleGateway(this);
}
/** The standard gateway that interconnect module
* across process.
*/
class CStandardGateway :
public CModuleBase,
public CModuleGateway,
public CModuleSocket
{
typedef map TModuleProxies;
/// Module proxies managed by this gateway. The map key is the module proxy id
TModuleProxies _ModuleProxies;
typedef CTwinMap TNamedProxyIdx;
/// Index of name to proxy id
TNamedProxyIdx _NameToProxyIdx;
/// A structure to hold foreign proxy information
struct TKnownModuleInfo
{
TModuleId ForeignProxyId;
CGatewayRoute *Route;
uint32 ModuleDistance;
TStringId ModuleClassId;
};
typedef multimap TKnownModuleInfos;
/** List of known foreign module info.
*/
TKnownModuleInfos _KnownModules;
typedef map TLocalModuleIndex;
/// Translation table to find module proxies for locally plugged module
/// The map key is the local module id, the data is the associated proxy id
TLocalModuleIndex _LocalModuleIndex;
typedef map TTransportList;
/// the list of active transport
TTransportList _Transports;
typedef set TRouteList;
// the list of available routes
TRouteList _Routes;
/// The security plug-in (if any)
CGatewaySecurity *_SecurityPlugin;
/// Ping counter for debug purpose
uint32 _PingCounter;
typedef std::list TLocalMessageList;
/// List of local message waiting dispatching at next update
TLocalMessageList _LocalMessages;
public:
CStandardGateway()
: _SecurityPlugin(NULL),
_PingCounter(0)
{
}
~CStandardGateway()
{
// we need to unplug any plugged module
while (!_PluggedModules.getAToBMap().empty())
{
_PluggedModules.getAToBMap().begin()->second->unplugModule(this);
}
// delete all transport
while (!_Transports.empty())
{
deleteTransport(_Transports.begin()->first);
}
// delete security plug-in
if (_SecurityPlugin != NULL)
removeSecurityPlugin();
// must be done before the other destructors are called
unregisterSocket();
unregisterGateway();
}
CModuleProxy *getModuleProxy(TModuleId proxyId)
{
TModuleProxies::iterator it(_ModuleProxies.find(proxyId));
if (it == _ModuleProxies.end())
return NULL;
return static_cast(it->second.getPtr());
}
/***********************************************************
** Gateway methods
***********************************************************/
virtual const std::string &getGatewayName() const
{
return getModuleName();
}
virtual const std::string &getFullyQualifiedGatewayName() const
{
return getModuleFullyQualifiedName();
}
/// Create and bind to this gateway a new transport
virtual void createTransport(const std::string &transportClass, const std::string &instanceName)
{
if (_Transports.find(instanceName) != _Transports.end())
{
nlwarning("A transport with the name '%s' already exist in this gateway", instanceName.c_str());
return;
}
IGatewayTransport::TCtorParam param;
param.Gateway = this;
IGatewayTransport *transport = NLMISC_GET_FACTORY(IGatewayTransport, std::string).createObject(transportClass, param);
if (transport == NULL)
{
nlwarning("Failed to create a transport with the class '%s'", transportClass.c_str());
return;
}
// Store the transport
// TTransportInfo *ti = new TTransportInfo(transport);
_Transports.insert(make_pair(instanceName, transport));
// _TransportPtrIdx.insert(make_pair(transport, ti));
nldebug("NETL6: Gateway transport %s (%s) created", instanceName.c_str(), transportClass.c_str());
}
/// Delete a transport (this will close any open route)
virtual void deleteTransport(const std::string &instanceName)
{
TTransportList::iterator it(_Transports.find(instanceName));
if (it == _Transports.end())
{
nlwarning("Unknown transport named '%s'", instanceName.c_str());
return;
}
nldebug("NETL6: Gateway transport '%s' deleted", instanceName.c_str());
// delete the transport
IGatewayTransport *transport = it->second;
// nlassert(_TransportPtrIdx.find(transport) != _TransportPtrIdx.end());
// _TransportPtrIdx.erase(transport);
delete transport;
// delete it->second;
_Transports.erase(it);
}
/// Activate/stop peer invisible mode on a transport
virtual void setTransportPeerInvisible(const std::string &transportInstanceName, bool peerInvisible)
{
TTransportList::iterator it(_Transports.find(transportInstanceName));
if (it == _Transports.end())
{
nlwarning("Unknown transport named '%s'", transportInstanceName.c_str());
return;
}
IGatewayTransport *transport= it->second;
if (peerInvisible == transport->PeerInvisible)
// nothing more to do
return;
// set the mode
transport->PeerInvisible = peerInvisible;
nldebug("NETL6: Gateway transport %s peer invisible mode %s", transportInstanceName.c_str(), peerInvisible? "ON" : "OFF");
// For each route of this transport, we need to disclose/undisclose peer modules
TRouteList::iterator first(_Routes.begin()), last(_Routes.end());
for (; first != last; ++first)
{
CGatewayRoute *route = *first;
if (route->getTransport() == transport)
{
// this route need to be filtered
TModuleProxies::iterator first(_ModuleProxies.begin()), last(_ModuleProxies.end());
for (; first != last; ++first)
{
IModuleProxy *proxy = first->second;
if (proxy->getGatewayRoute() != NULL
&& proxy->getGatewayRoute() != route
&& proxy->getGatewayRoute()->getTransport() == transport)
{
// this module is on the same transport, but another route, remove/add it from the
// route
if (peerInvisible)
undiscloseModuleToRoute(route, proxy);
else
{
// check firewall rules
if (!route->getTransport()->Firewalled)
discloseModuleToRoute(route, proxy);
}
}
}
}
}
}
/// Activate/stop firewalling mode on a transport
virtual void setTransportFirewallMode(const std::string &transportInstanceName, bool firewalled)
throw (EGatewayFirewallBreak)
{
TTransportList::iterator it(_Transports.find(transportInstanceName));
if (it == _Transports.end())
{
nlwarning("Unknown transport named '%s'", transportInstanceName.c_str());
return;
}
IGatewayTransport *transport = it->second;
if (firewalled == transport->Firewalled)
// nothing to do
return;
if (firewalled && transport->getRouteCount() != 0)
throw EGatewayFirewallBreak();
/// set the firewall mode
transport->Firewalled = firewalled;
nldebug("NETL6: Gateway transport %s firewall mode %s", transportInstanceName.c_str(), firewalled? "ON" : "OFF");
if (firewalled == false)
{
// we need to disclose all module not disclosed yet
TRouteList::iterator first(_Routes.begin()), last(_Routes.end());
for (; first != last; ++first)
{
CGatewayRoute *route = *first;
if (route->getTransport() == transport)
{
// this route need to be unfiltered
TModuleProxies::iterator first(_ModuleProxies.begin()), last(_ModuleProxies.end());
for (; first != last; ++first)
{
IModuleProxy *proxy = first->second;
if (proxy->getGatewayRoute() == NULL || (proxy->getGatewayRoute() != route ))
{
// this module is on another route, disclose it if needed
if (route->FirewallDisclosed.find(proxy->getModuleProxyId()) == route->FirewallDisclosed.end())
discloseModuleToRoute(route, proxy);
}
}
}
// clear the firewall disclosed table
route->FirewallDisclosed.clear();
}
}
}
/// Send a command to a transport
virtual void transportCommand(const TParsedCommandLine &commandLine)
{
for (uint i=1; iParamName;
TTransportList::const_iterator it(_Transports.find(transportName));
if (it == _Transports.end())
{
nlwarning("Unknown transport named '%s', ignoring command.", transportName.c_str());
}
else if (subParam->SubParams.empty())
{
nlwarning("Can't find sub param list for transport '%s' command", transportName.c_str());
}
else
{
nldebug("NETL6: Gateway transport %s, sending command '%s'", transportName.c_str(), commandLine.toString().c_str());
// ok, we have a valid transport, send the command
IGatewayTransport *transport = it->second;
if (!transport->onCommand(*subParam))
return;
}
}
}
virtual IGatewayTransport *getGatewayTransport(const std::string &transportName) const
{
TTransportList::const_iterator it(_Transports.find(transportName));
if (it == _Transports.end())
return NULL;
else
return it->second;
}
virtual uint32 getTransportCount() const
{
return (uint32)_Transports.size();
}
virtual uint32 getRouteCount() const
{
return (uint32)_Routes.size();
}
virtual uint32 getReceivedPingCount() const
{
return _PingCounter;
}
virtual void onRouteAdded(CGatewayRoute *route)
{
nlassert(route != NULL);
// Remember the new route
nlassert(_Routes.find(route) == _Routes.end());
_Routes.insert(route);
// a new route is available, disclose known modules
{
TModuleProxies::iterator first(_ModuleProxies.begin()), last(_ModuleProxies.end());
for (; first != last; ++first)
{
IModuleProxy *modProx = first->second;
// only transmit module desc coming from other routes
// and other transport if peer invisible
if (isModuleProxyVisible(modProx, route))
{
discloseModuleToRoute(route, modProx);
}
}
}
}
/// A route is removed by a transport
virtual void onRouteRemoved(CGatewayRoute *route)
{
nlassert(route != NULL);
nlassert(_Routes.find(route) != _Routes.end());
// we need to remove all the proxy that come from this route
// CGatewayRoute::TForeignToLocalIdx::TAToBMap::const_iterator first(route->ForeignToLocalIdx.getAToBMap().begin()), last(route->ForeignToLocalIdx.getAToBMap().end());
// for (; first != last; ++first)
while (!route->ForeignToLocalIdx.getAToBMap().empty())
{
removeForeignModule(route, route->ForeignToLocalIdx.getAToBMap().begin()->first);
// TModuleId localProxyId = first->second;
// TModuleProxies::iterator it(_ModuleProxies.find(localProxyId));
// nlassert(it != _ModuleProxies.end());
//
// IModuleProxy *modProx = it->second;
//
// // trigger an event in the gateway
// onRemoveModuleProxy(modProx);
//
// // remove proxy record from the proxy list
// _ModuleProxies.erase(it);
// _NameToProxyIdx.removeWithB(modProx);
//
// // Release the proxy object
// IModuleManager::getInstance().releaseModuleProxy(modProx->getModuleProxyId());
}
// cleanup the translation table
// route->ForeignToLocalIdx.clear();
// clear the route tracker
_Routes.erase(route);
// cleanup route state
route->ForeignToLocalIdx.clear();
route->PendingEvents.clear();
route->FirewallDisclosed.clear();
route->NextMessageType = CModuleMessageHeaderCodec::mt_invalid;
route->NextSenderProxyId = 0;
route->NextAddresseeProxyId = 0;
}
/// A transport have received a message
virtual void onReceiveMessage(CGatewayRoute *from, const CMessage &msgin)
{
H_AUTO(CModuleGetaway_onReceiveMessage);
// dispatch the message
if (from->NextMessageType != CModuleMessageHeaderCodec::mt_invalid)
{
// this message must be dispatched to a module
onReceiveModuleMessage(from, msgin);
}
// Not a module message, dispatch the gateway message
else if (msgin.getName() == "MOD_OP")
{
onReceiveModuleMessageHeader(from, msgin);
}
else if (msgin.getName() == "MOD_UPD")
{
onReceiveModuleUpdate(from, msgin);
}
// else if (msgin.getName() == "MOD_ADD")
// {
// onReceiveModuleAdd(from, msgin);
// }
// else if (msgin.getName() == "MOD_REM")
// {
// onReceiveModuleRemove(from, msgin);
// }
// else if (msgin.getName() == "MOD_DST_UPD")
// {
// onReceiveModuleDistanceUpdate(from, msgin);
// }
}
/***********************************/
/* security plug-in management*/
/***********************************/
/** create a security plug-in.
* There must be no security plug-in currently created.
*/
virtual void createSecurityPlugin(const std::string &className)
{
if (_SecurityPlugin != NULL)
{
nlwarning("NLNETL5 : CStandardGateway::createSecurityPlugin : plug-in already created ");
return;
}
CGatewaySecurity::TCtorParam params;
params.Gateway = this;
CGatewaySecurity *gs = NLMISC_GET_FACTORY(CGatewaySecurity, std::string).createObject(className, params);
if (gs == NULL)
{
nlwarning("NLNETL5 : CStandardGateway::createSecurityPlugin : can't create a security plug-in for class '%s'", className.c_str());
return;
}
// store the security plug-in
_SecurityPlugin = gs;
// update security for all existing proxies
TModuleProxies::iterator first(_ModuleProxies.begin()), last(_ModuleProxies.end());
for (; first != last; ++first)
{
IModuleProxy *proxy = first->second;
_SecurityPlugin->onNewProxy(proxy);
}
}
/** Send a command to the security plug-in */
virtual void sendSecurityCommand(const TParsedCommandLine &command)
{
if (_SecurityPlugin != NULL)
{
nlwarning("NLNETL5 : CStandardGateway::sendSecurityCommand : plug-in NOT created ");
return;
}
_SecurityPlugin->onCommand(command);
}
/** Remove the security plug-in.
*/
virtual void removeSecurityPlugin()
{
if (_SecurityPlugin == NULL)
{
nlwarning("NLNETL5 : CStandardGateway::removeSecurityPlugin : plug-in not created");
return;
}
// delete the plug-in (this can remove some security data)
_SecurityPlugin->onDelete();
delete _SecurityPlugin;
_SecurityPlugin = NULL;
}
/** Set a security data block. If a bloc of the same type
* already exist in the list, the new one will replace the
* existing one.
*/
void setSecurityData(IModuleProxy *proxy, TSecurityData *securityData)
{
nlassert(proxy->getModuleGateway() == this);
nlassert(securityData->NextItem == NULL);
CModuleProxy *modProx = dynamic_cast(proxy);
nlassert(modProx != NULL);
// look in the existing security for a matching type and remove it
removeSecurityData(proxy, securityData->DataTag);
// now, store the security data
securityData->NextItem = modProx->_SecurityData;
modProx->_SecurityData = securityData;
}
/** Clear a block of security data
* The block is identified by the data tag
*/
bool removeSecurityData(IModuleProxy *proxy, uint8 dataTag)
{
nlassert(proxy->getModuleGateway() == this);
CModuleProxy *modProx = dynamic_cast(proxy);
nlassert(modProx != NULL);
bool ret = false;
TSecurityData *prevSec = NULL;
TSecurityData *currentSec = modProx->_SecurityData;
while (currentSec != NULL)
{
if (currentSec->DataTag == dataTag)
{
if (prevSec != NULL)
prevSec->NextItem = currentSec->NextItem;
else
modProx->_SecurityData = currentSec->NextItem;
TSecurityData *toDelete = currentSec;
currentSec = currentSec->NextItem;
toDelete->NextItem = NULL;
delete toDelete;
ret = true;
}
else
{
prevSec = currentSec;
currentSec = currentSec->NextItem;
}
}
return ret;
}
void replaceAllSecurityDatas(IModuleProxy *proxy, TSecurityData *securityData)
{
nlassert(proxy->getModuleGateway() == this);
CModuleProxy *modProx = dynamic_cast(proxy);
nlassert(modProx != NULL);
nlassert(modProx->_SecurityData != securityData);
if (modProx->_SecurityData != NULL)
delete modProx->_SecurityData;
modProx->_SecurityData = securityData;
}
/** Ask the gateway to resend the security data.
* The plug-in call this method after having changed
* the security info for a plug-in outside of the
* onNewProxy call.
*/
void forceSecurityUpdate(IModuleProxy *proxy)
{
TRouteList::iterator first(_Routes.begin()), last(_Routes.end());
for (; first != last; ++first)
{
CGatewayRoute *route = *first;
if (isModuleProxyVisible(proxy, route))
{
updateModuleSecurityDataToRoute(route, proxy);
}
}
}
/***********************************/
/* Inter gateway message reception */
/***********************************/
/** A gateway receive module operation */
void onReceiveModuleMessage(CGatewayRoute *from, const CMessage &msgin)
{
H_AUTO(CModuleGetaway_onReceiveModuleMessage);
// clean the message type now, any return path will be safe
from->NextMessageType = CModuleMessageHeaderCodec::mt_invalid;
// Retrieve sender and destination proxy and recall gateway send method
IModuleProxy *senderProxy;
IModuleProxy *addresseeProxy;
TModuleProxies::iterator it;
// sender proxy
it = _ModuleProxies.find(from->NextSenderProxyId);
if (it == _ModuleProxies.end())
{
nlwarning("Can't dispatch the module message, sender proxy %u is not in this gateway", from->NextSenderProxyId);
return;
}
senderProxy = it->second;
// addressee proxy
it = _ModuleProxies.find(from->NextAddresseeProxyId);
if (it == _ModuleProxies.end())
{
nlwarning("Can't dispatch the module message '%s', sender proxy %u is not in this gateway", msgin.getName().c_str(), from->NextAddresseeProxyId);
return;
}
addresseeProxy = it->second;
// give the message to the gateway (either for local dispatch or for forwarding)
sendModuleMessage(senderProxy, addresseeProxy, msgin);
}
// A gateway receive a module message header
void onReceiveModuleMessageHeader(CGatewayRoute *from, const CMessage &msgin)
{
H_AUTO(CModuleGetaway_onReceiveModuleMessageHeader);
if (from->NextMessageType != CModuleMessageHeaderCodec::mt_invalid)
{
// juste warn (but that is VERY BAD)
nlwarning("Receiving a new module message header without having received the previous module message !");
}
// store the message information in the route
CModuleMessageHeaderCodec::decode(
msgin,
from->NextMessageType,
from->NextSenderProxyId,
from->NextAddresseeProxyId);
// translate sender id
const TModuleId *pmoduleId = from->ForeignToLocalIdx.getB(from->NextSenderProxyId);
if (pmoduleId == NULL)
{
nlwarning("The sender proxy %u is unknown in the translation table, can't dispatch the message !", from->NextSenderProxyId);
from->NextMessageType = CModuleMessageHeaderCodec::mt_invalid;
return;
}
from->NextSenderProxyId = *pmoduleId;
// now, wait the message body
}
/** A gateway receive a general update message */
void onReceiveModuleUpdate(CGatewayRoute *from, const CMessage &msgin)
{
H_AUTO(CModuleGetaway_onReceiveModuleUpdate);
while (uint32(msgin.getPos()) != msgin.length())
{
CGatewayRoute::TPendingEventType type = CGatewayRoute::pet_disclose_module;
// msgin.serialShortEnum(type);
nlRead(msgin, serialShortEnum, type);
switch (type)
{
case CGatewayRoute::pet_disclose_module:
onReceiveModuleAdd(from, msgin);
break;
case CGatewayRoute::pet_undisclose_module:
onReceiveModuleRemove(from, msgin);
break;
case CGatewayRoute::pet_update_distance:
onReceiveModuleDistanceUpdate(from, msgin);
break;
case CGatewayRoute::pet_update_security:
onReceiveModuleSecurityUpdate(from, msgin);
break;
default:
// should not append
nlstop;
}
}
}
/** A gateway send new modules information */
void onReceiveModuleAdd(CGatewayRoute *from, const CMessage &msgin)
{
H_AUTO(CModuleGetaway_onReceiveModuleAdd);
TModuleDescCodec modDesc;
nlRead(msgin, serial, modDesc);
// for each received module info
TStringId modNameId = CStringMapper::map(modDesc.ModuleFullName);
/// store the module information
TKnownModuleInfo modInfo;
modInfo.ForeignProxyId = modDesc.ModuleProxyId;
modInfo.ModuleClassId = CStringMapper::map(modDesc.ModuleClass);
modInfo.ModuleDistance = modDesc.ModuleDistance;
modInfo.Route = from;
nldebug("Gateway '%s' : store module info for '%s' (foreign ID %u) @ %u hop",
getGatewayName().c_str(),
modDesc.ModuleFullName.c_str(),
modDesc.ModuleProxyId,
modDesc.ModuleDistance);
// Store module information
_KnownModules.insert(make_pair(modNameId, modInfo));
if (_NameToProxyIdx.getB(modNameId) != NULL)
{
// a proxy for this module already exist,
IModuleProxy *modProx = *(_NameToProxyIdx.getB(modNameId));
// fill the id translation table
// from->ForeignToLocalIdx.insert(make_pair(modDesc.ModuleProxyId, modProx->getModuleProxyId()));
from->ForeignToLocalIdx.add(modDesc.ModuleProxyId, modProx->getModuleProxyId());
// check if this route is better
if (modProx->getModuleDistance() > modInfo.ModuleDistance)
{
// update module distance and swap route
CModuleProxy *proxy = static_cast(modProx);
nldebug("Gateway '%s' : Use a shorter path for '%s' from %u to %u hops",
getGatewayName().c_str(),
modDesc.ModuleFullName.c_str(),
proxy->_Distance,
modInfo.ModuleDistance);
proxy->_Distance = modInfo.ModuleDistance;
proxy->_Route = modInfo.Route;
sendModuleDistanceUpdate(proxy);
}
// update the security if needed
if (modDesc.SecDesc.SecurityData != NULL)
{
CModuleProxy *proxy = static_cast(modProx);
if (_SecurityPlugin != NULL)
{
_SecurityPlugin->onNewSecurityData(from, proxy, modDesc.SecDesc.SecurityData);
}
else
{
if (proxy->_SecurityData != NULL)
delete proxy->_SecurityData;
proxy->_SecurityData = modDesc.SecDesc.SecurityData;
}
}
}
else
{
// we need to create a new proxy
// create a module proxy
IModuleProxy *modProx = IModuleManager::getInstance().createModuleProxy(
this,
from,
modDesc.ModuleDistance,
NULL,
modDesc.ModuleClass,
modDesc.ModuleFullName,
modDesc.ModuleManifest,
modDesc.ModuleProxyId);
// set the module security
CModuleProxy *proxy = static_cast(modProx);
proxy->_SecurityData = modDesc.SecDesc.SecurityData;
// let the security plug-in add/remove security data
if (_SecurityPlugin != NULL)
_SecurityPlugin->onNewProxy(proxy);
// store the proxy in the proxy list
_ModuleProxies.insert(make_pair(modProx->getModuleProxyId(), modProx));
_NameToProxyIdx.add(modNameId, modProx);
// Fill the proxy id translation table for this route
// from->ForeignToLocalIdx.insert(make_pair(modDesc.ModuleProxyId, modProx->getModuleProxyId()));
from->ForeignToLocalIdx.add(modDesc.ModuleProxyId, modProx->getModuleProxyId());
// trigger an event in the gateway
onAddModuleProxy(modProx);
}
}
void onReceiveModuleRemove(CGatewayRoute *from, const CMessage &msgin)
{
H_AUTO(CModuleGetaway_onReceiveModuleRemove);
TModuleId moduleId;
nlRead(msgin, serial, moduleId);
removeForeignModule(from, moduleId);
}
void onReceiveModuleDistanceUpdate(CGatewayRoute *from, const CMessage &msgin)
{
H_AUTO(CModuleGetaway_onReceiveModuleDistanceUpdate);
TModuleId moduleId;
uint32 newDistance;
nlRead(msgin, serial, moduleId);
nlRead(msgin, serial, newDistance);
// translate the module id
const TModuleId *pModuleId = from->ForeignToLocalIdx.getB(moduleId);
if (pModuleId == NULL)
{
nlwarning("Receive a module distance update for foreign module %u, but no translation available", moduleId);
return;
}
TModuleId localId = *pModuleId;
// now, retrieve the module info and update
TModuleProxies::iterator it2(_ModuleProxies.find(localId));
nlassert(it2 != _ModuleProxies.end());
CModuleProxy *proxy = static_cast(it2->second.getPtr());
pair range;
range = _KnownModules.equal_range(proxy->_FullyQualifiedModuleName);
for (; range.first != range.second; ++range.first)
{
TKnownModuleInfo &kmi = range.first->second;
if (kmi.Route == from)
{
// we found the module info, update the data
nldebug("Gateway '%s' : updating distance from %u to %u hop for module '%s'",
getGatewayName().c_str(),
kmi.ModuleDistance,
newDistance,
CStringMapper::unmap(range.first->first).c_str());
kmi.ModuleDistance = newDistance;
break;
}
}
nlassert(range.first != range.second);
// check if the changed module is the one currently in use
if (proxy->_Route == from)
{
// two task : first, if the new distance is greater, look
// in available route for a shorter path,
// second, send a module distance update for this module.
if (proxy->_Distance < newDistance)
{
// look for a shorter path
range = _KnownModules.equal_range(proxy->_FullyQualifiedModuleName);
for (; range.first != range.second; ++range.first)
{
TKnownModuleInfo &kmi = range.first->second;
if (kmi.ModuleDistance < proxy->_Distance)
{
nldebug("Gateway '%s' : proxy '%s' use a new path from %u to %u hop",
getGatewayName().c_str(),
proxy->getModuleName().c_str(),
proxy->_Distance,
kmi.ModuleDistance);
// this path is shorter, use it now
proxy->_Route = kmi.Route;
proxy->_ForeignModuleId = kmi.ForeignProxyId;
proxy->_Distance = kmi.ModuleDistance;
break;
}
}
if (range.first == range.second)
{
// no shorter path found, update the proxy
nldebug("Gateway '%s' : proxy '%s' path distance changed from %u to %u hop",
getGatewayName().c_str(),
proxy->getModuleName().c_str(),
proxy->_Distance,
newDistance);
proxy->_Distance = newDistance;
}
}
else
{
// the new distance is shorter, just update
nldebug("Gateway '%s' : proxy '%s' path distance reduced from %u to %u hop",
getGatewayName().c_str(),
proxy->getModuleName().c_str(),
proxy->_Distance,
newDistance);
proxy->_Distance = newDistance;
}
// send the distance update
sendModuleDistanceUpdate(proxy);
}
}
void onReceiveModuleSecurityUpdate(CGatewayRoute *from, const CMessage &msgin)
{
H_AUTO(CModuleGetaway_onReceiveModuleSecurityUpdate);
// TModuleId foreignModuleId;
// TSecurityData *modSec;
TModuleSecurityChangeMsg secChg;
nlRead(msgin, serial, secChg);
// msgin.serial(foreignModuleId);
// msgin.serialPolyPtr(modSec);
const TModuleId *pModuleId = from->ForeignToLocalIdx.getB(secChg.ModuleId);
if (pModuleId == NULL)
{
nlwarning("LNETL6 : receive module security update for unknown module foreign proxy %u", secChg.ModuleId);
return;
}
TModuleId moduleId = *pModuleId;
CModuleProxy *modProx = getModuleProxy(moduleId);
if (modProx == NULL)
{
nlwarning("LNETL6 : receive module security update for unknown module proxy %u, foreign %u", moduleId, secChg.ModuleId);
return;
}
// allow the security plug-in to affect the data
if( _SecurityPlugin != NULL)
{
// let the plug-in update the security data
_SecurityPlugin->onNewSecurityData(from, modProx, secChg.SecDesc.SecurityData);
}
else
{
// update the security data in the proxy
replaceAllSecurityDatas(modProx, secChg.SecDesc.SecurityData);
}
// warn local module about new security data
{
TPluggedModules::TAToBMap::const_iterator first(_PluggedModules.getAToBMap().begin()), last(_PluggedModules.getAToBMap().end());
for (; first != last; ++first)
{
IModule *module = first->second;
module->onModuleSecurityChange(modProx);
}
}
// update the security to peers
{
TRouteList::iterator first(_Routes.begin()), last(_Routes.end());
for (; first != last; ++first)
{
CGatewayRoute *route = *first;
if (isModuleProxyVisible(modProx, route))
{
updateModuleSecurityDataToRoute(route, modProx);
}
}
}
}
virtual void onAddModuleProxy(IModuleProxy *addedModule)
{
H_AUTO(CModuleGetaway_onAddmoduleProxy);
// disclose module to local modules
discloseModule(addedModule);
// and send module info to any route
// for each route
TRouteList::iterator first(_Routes.begin()), last(_Routes.end());
for (; first != last; ++first)
{
CGatewayRoute *route = *first;
// only send info to other routes
if (isModuleProxyVisible(addedModule, route))
{
discloseModuleToRoute(route, addedModule);
}
}
}
virtual void onRemoveModuleProxy(IModuleProxy *removedModule)
{
H_AUTO(CModuleGetaway_onRemoveModuleProxy);
// for each route
{
// for each route
TRouteList::iterator first(_Routes.begin()), last(_Routes.end());
for (; first != last; ++first)
{
CGatewayRoute *route = *first;
// only send info to other routes
if (isModuleProxyVisible(removedModule, route))
{
undiscloseModuleToRoute(route, removedModule);
}
}
}
// warn any locally plugged module
{
TPluggedModules::TAToBMap::const_iterator first(_PluggedModules.getAToBMap().begin()), last(_PluggedModules.getAToBMap().end());
for (; first != last; ++first)
{
IModule *module = first->second;
if (removedModule->getGatewayRoute() != NULL
|| module->getModuleId() != removedModule->getForeignModuleId())
{
module->_onModuleDown(removedModule);
}
}
}
}
virtual void discloseModule(IModuleProxy *moduleProxy)
throw (EGatewayNotConnected)
{
nlassert(moduleProxy->getModuleGateway() == this);
// warn any plugged module
TPluggedModules::TAToBMap::const_iterator first(_PluggedModules.getAToBMap().begin()), last(_PluggedModules.getAToBMap().end());
for (; first != last; ++first)
{
IModule *module = first->second;
if (moduleProxy->getGatewayRoute() != NULL
|| module->getModuleId() != moduleProxy->getForeignModuleId())
{
module->_onModuleUp(moduleProxy);
}
}
}
virtual IModuleProxy *getPluggedModuleProxy(IModule *pluggedModule)
{
TLocalModuleIndex::iterator it(_LocalModuleIndex.find(pluggedModule->getModuleId()));
if (it == _LocalModuleIndex.end())
return NULL;
else
{
TModuleProxies::iterator it2(_ModuleProxies.find(it->second));
nlassert(it2 != _ModuleProxies.end());
return it2->second;
}
}
virtual uint32 getProxyCount() const
{
return (uint32)_ModuleProxies.size();
}
/// Fill a vector with the list of proxies managed here. The module are filled in ascending proxy id order.
virtual void getModuleProxyList(std::vector &resultList) const
{
TModuleProxies::const_iterator first(_ModuleProxies.begin()), last(_ModuleProxies.end());
for (; first != last; ++first)
{
resultList.push_back(first->second);
}
}
virtual void sendModuleMessage(IModuleProxy *senderProxy, IModuleProxy *addresseeProxy, const NLNET::CMessage &message)
{
H_AUTO(CModuleGetaway_sendModuleMessage);
// manage firewall
if (addresseeProxy->getGatewayRoute()
&& addresseeProxy->getGatewayRoute()->getTransport()->Firewalled)
{
CGatewayRoute *route = addresseeProxy->getGatewayRoute();
// the destination route is firewalled, we need to
// disclose the sender module if it's not already done
if (route->FirewallDisclosed.find(senderProxy->getModuleProxyId()) == route->FirewallDisclosed.end())
{
discloseModuleToRoute(route, senderProxy);
route->FirewallDisclosed.insert(senderProxy->getModuleProxyId());
}
}
// check for visibility rules
if (!isModuleProxyVisible(addresseeProxy, senderProxy->getGatewayRoute()))
{
nlwarning("Module %u '%s' try to send message to %u '%s' but addressee is not visible, message discarded",
senderProxy->getModuleProxyId(),
senderProxy->getModuleName().c_str(),
addresseeProxy->getModuleProxyId(),
addresseeProxy->getModuleName().c_str());
return;
}
if (addresseeProxy->getGatewayRoute() == NULL)
{
// the module is local, just forward the call to the dispatcher
nlassert(senderProxy != NULL);
nlassert(_ModuleProxies.find(senderProxy->getModuleProxyId()) != _ModuleProxies.end());
// invert the message for immediate dispatching if needed
if (!message.isReading())
const_cast(message).invert();
// check if the module support immediate dispatching
TModuleId addresseeModId = addresseeProxy->getForeignModuleId();
const TModulePtr *adrcp = _PluggedModules.getB(addresseeModId);
if (adrcp == NULL)
{
nlwarning("sendModuleMessage : can't find addressee module %u that is not plugged here !", addresseeModId);
return;
}
IModule *addreseeMod = *adrcp;
if (!addreseeMod->isImmediateDispatchingSupported())
{
// dispatch the message at next gateway update
// this provide a coherent behavior between local and distant module message exchange
_LocalMessages.push_back(TLocalMessage());
TLocalMessage &lm = _LocalMessages.back();
lm.SenderProxyId = senderProxy->getModuleProxyId();
lm.AddresseProxyId = addresseeProxy->getModuleProxyId();
nldebug("NETL6 : gateway '%s' : queuing local message '%s' from proxy %u to proxy %u",
getModuleName().c_str(),
message.getName().c_str(),
lm.SenderProxyId,
lm.AddresseProxyId);
if (message.hasLockedSubMessage())
{
lm.Message.assignFromSubMessage(message);
}
else
{
lm.Message = message;
}
}
else
{
// immediate dispatching
dispatchModuleMessage(senderProxy, addresseeProxy, message);
}
}
else
{
// the module is distant, send the message via the route
// create a message for sending
CMessage msgHeader("MOD_OP");
CModuleMessageHeaderCodec::encode(
msgHeader,
CModuleMessageHeaderCodec::mt_oneway,
senderProxy->getModuleProxyId(),
addresseeProxy->getForeignModuleId());
// send any pending module info
sendPendingModuleUpdate(addresseeProxy->getGatewayRoute());
// send the header
addresseeProxy->getGatewayRoute()->sendMessage(msgHeader);
// send the message
addresseeProxy->getGatewayRoute()->sendMessage(message);
}
}
virtual void dispatchModuleMessage(IModuleProxy *senderProxy, IModuleProxy *addresseeProxy, const CMessage &message)
{
H_AUTO(CModuleGetaway_dispatchModuleMessage);
CMessage::TMessageType msgType = message.getType();
// retrieve the address module from the proxy
nlassert(addresseeProxy->getGatewayRoute() == NULL);
// As the addressee is local, the foreign proxy id is the local module id (a bit triky...)
TModuleId addresseeModId = addresseeProxy->getForeignModuleId();
const TModulePtr *adrcp = _PluggedModules.getB(addresseeModId);
if (adrcp == NULL)
{
nlwarning("dispatchModuleMessage : dispatching a message to module %u that is not plugged here !", addresseeModId);
return;
}
IModule *addreseeMod = *adrcp;
// finally, transmit the message to the module
// addreseeMod->onProcessModuleMessage(senderProxy, message);
try
{
addreseeMod->onReceiveModuleMessage(senderProxy, message);
}
catch(...)
{
nlwarning("An exception was thrown while dispatching message '%s' from '%s' to '%s'",
message.getName().c_str(),
senderProxy->getModuleName().c_str(),
addresseeProxy->getModuleName().c_str());
if (msgType == CMessage::Request)
{
// send back an exception message
CMessage except;
except.setType("EXCEPT", CMessage::Except);
senderProxy->sendModuleMessage(addreseeMod, except);
}
}
}
/***********************************************************
** Module methods
***********************************************************/
bool initModule(const TParsedCommandLine &initInfo)
{
bool ret = CModuleBase::initModule(initInfo);
// no options for now
registerSocket();
registerGateway();
return ret;
}
std::string buildModuleManifest() const
{
return string();
}
void onServiceUp(const std::string &/* serviceName */, NLNET::TServiceId /* serviceId */)
{
}
void onServiceDown(const std::string &/* serviceName */, NLNET::TServiceId /* serviceId */)
{
}
void onModuleUpdate()
{
H_AUTO(CModuleGetaway_onModuleUpdate);
// send waiting local messages
while (!_LocalMessages.empty())
{
TLocalMessage &lm = _LocalMessages.front();
IModuleProxy *senderProx = getModuleProxy(lm.SenderProxyId);
IModuleProxy *addresseeProx = getModuleProxy(lm.AddresseProxyId);
if (senderProx == NULL)
{
nlwarning("CStandardGateway : local message dispatching : Failed to retrieve proxy for sender module %u while dispatching message '%s' to %u",
lm.SenderProxyId,
lm.Message.getName().c_str(),
lm.AddresseProxyId);
}
else if (addresseeProx == NULL)
{
nlwarning("CStandardGateway : local message dispatching : Failed to retrieve proxy for addressee module %u while dispatching message '%s' from %u",
lm.AddresseProxyId,
lm.Message.getName().c_str(),
lm.SenderProxyId);
}
else
{
// we can dispatch the message
dispatchModuleMessage(senderProx, addresseeProx, lm.Message);
}
_LocalMessages.pop_front();
}
// send pending module un/disclosure
{
TRouteList::iterator first(_Routes.begin()), last(_Routes.end());
for (; first != last; ++first)
{
CGatewayRoute *route = *first;
sendPendingModuleUpdate(route);
}
}
// update the transports
{
TTransportList::iterator first(_Transports.begin()), last(_Transports.end());
for (; first != last; ++first)
{
IGatewayTransport *transport = first->second;
transport->update();
}
}
}
void onApplicationExit()
{
// delete all transport
while (!_Transports.empty())
{
deleteTransport(_Transports.begin()->first);
}
}
void onModuleUp(IModuleProxy * /* moduleProxy */)
{
}
void onModuleDown(IModuleProxy * /* moduleProxy */)
{
}
bool onProcessModuleMessage(IModuleProxy * /* senderModuleProxy */, const CMessage &message)
{
// simple message for debug and unit testing
if (message.getName() == "DEBUG_MOD_PING")
{
_PingCounter++;
return true;
}
return false;
}
void onModuleSecurityChange(IModuleProxy * /* moduleProxy */)
{
}
void onModuleSocketEvent(IModuleSocket * /* moduleSocket */, TModuleSocketEvent /* eventType */)
{
}
/***********************************************************
** Socket methods
***********************************************************/
const std::string &getSocketName()
{
return getModuleName();
}
void _sendModuleMessage(IModule *senderModule, TModuleId destModuleProxyId, const NLNET::CMessage &message )
throw (EModuleNotReachable, EModuleNotPluggedHere)
{
// the socket implementation already checked that the module is plugged here
// just check that the destination module effectively from here
TLocalModuleIndex::iterator it(_LocalModuleIndex.find(senderModule->getModuleId()));
nlassert(it != _LocalModuleIndex.end());
// get the sender proxy
TModuleProxies::iterator it2(_ModuleProxies.find(it->second));
nlassert(it2 != _ModuleProxies.end());
IModuleProxy *senderProx = it2->second;
// get the addressee proxy
it2 = _ModuleProxies.find(destModuleProxyId);
nlassert(it2 != _ModuleProxies.end());
IModuleProxy *destProx = it2->second;
sendModuleMessage(senderProx, destProx, message);
}
virtual void _broadcastModuleMessage(IModule *senderModule, const NLNET::CMessage &message)
throw (EModuleNotPluggedHere)
{
H_AUTO(CModuleGetaway__broadcastModuleMessage);
// send the message to all proxies (except the sender module)
TLocalModuleIndex::iterator it(_LocalModuleIndex.find(senderModule->getModuleId()));
nlassert(it != _LocalModuleIndex.end());
TModuleProxies::iterator first(_ModuleProxies.begin()), last(_ModuleProxies.end());
for (; first != last; ++first)
{
IModuleProxy *proxy = first->second;
proxy->sendModuleMessage(senderModule, message);
}
}
void onModulePlugged(IModule *pluggedModule)
{
nldebug("NETL6: Gateway %s : plugging module '%s' id=%u",
getModuleName().c_str(),
pluggedModule->getModuleName().c_str(),
pluggedModule->getModuleId());
// A module has just been plugged here, we need to disclose it the the
// other module, and disclose other module to it.
// create a proxy for this module
IModuleProxy *modProx = IModuleManager::getInstance().createModuleProxy(
this,
NULL, // the module is local, so there is no route
0, // the module is local, distance is 0
pluggedModule, // the module is local, so store the module pointer
pluggedModule->getModuleClassName(),
pluggedModule->getModuleFullyQualifiedName(),
pluggedModule->getModuleManifest(),
pluggedModule->getModuleId() // the module is local, foreign id is the module id
);
// and store it in the proxies container
_ModuleProxies.insert(make_pair(modProx->getModuleProxyId(), modProx));
_NameToProxyIdx.add(CStringMapper::map(modProx->getModuleName()), modProx);
// and also in the local module index
_LocalModuleIndex.insert(make_pair(pluggedModule->getModuleId(), modProx->getModuleProxyId()));
// trigger the new module proxy event
onAddModuleProxy(modProx);
// // disclose the new module to other modules
// discloseModule(modProx);
//
// second, disclose already known proxies in the gateway to the plugged module
{
TModuleProxies::iterator first(_ModuleProxies.begin()), last(_ModuleProxies.end());
for (; first != last; ++first)
{
IModuleProxy *modProx = first->second;
// do not send a moduleUp on the module himself !
// either the gateway is non null (distant module), or the
// foreign module id is different of the local module (for local proxy,
// the foreign module id store the local module id).
if (modProx->getGatewayRoute() != NULL || modProx->getForeignModuleId() != pluggedModule->getModuleId())
{
pluggedModule->_onModuleUp(modProx);
}
}
}
}
/// Called just after a module as been effectively unplugged from a socket
void onModuleUnplugged(IModule *unpluggedModule)
{
nldebug("NETL6: Gateway %s : unplugging module '%s' id=%u",
getModuleName().c_str(),
unpluggedModule->getModuleName().c_str(),
unpluggedModule->getModuleId());
// remove the proxy info
TLocalModuleIndex::iterator it(_LocalModuleIndex.find(unpluggedModule->getModuleId()));
nlassert(it != _LocalModuleIndex.end());
TModuleProxies::iterator it2(_ModuleProxies.find(it->second));
nlassert(it2 != _ModuleProxies.end());
IModuleProxy *modProx = it2->second;
// warn the unplugged module that all proxies in this gateway become unavailable
{
TModuleProxies::iterator first(_ModuleProxies.begin()), last(_ModuleProxies.end());
for (; first != last; ++first)
{
IModuleProxy *modProx = first->second;
if (modProx->getGatewayRoute() != NULL
|| modProx->getForeignModuleId() != unpluggedModule->getModuleId())
{
unpluggedModule->_onModuleDown(modProx);
}
}
}
/// the gateway do the rest of the job
onRemoveModuleProxy(modProx);
TModuleId localProxyId = modProx->getModuleProxyId();
// remove reference to the proxy
_ModuleProxies.erase(it2);
_NameToProxyIdx.removeWithB(modProx);
_LocalModuleIndex.erase(it);
// check in the local message queue if some message are to/from
// this module
TLocalMessageList::iterator first(_LocalMessages.begin()), last(_LocalMessages.end());
for (; first != last; ++first)
{
TLocalMessage &lm = *first;
if (lm.AddresseProxyId == localProxyId
|| lm.SenderProxyId == localProxyId)
{
// erase this message !
nlwarning("CStandardGateway : while unplugging module %u from the gateway, locale message '%s' from proxy %u to proxy %u is lost",
unpluggedModule->getModuleId(),
lm.Message.getName().c_str(),
lm.SenderProxyId,
lm.AddresseProxyId);
TLocalMessageList::iterator next = first;
++next;
if (next == last)
{
_LocalMessages.erase(first);
break;
}
else
{
_LocalMessages.erase(first);
first = next;
}
}
}
// release the module proxy
IModuleManager::getInstance().releaseModuleProxy(localProxyId);
}
////////////////////////////////////////////////////
// Gateway internal methods
////////////////////////////////////////////////////
void removeForeignModule(CGatewayRoute *route, TModuleId foreignModuleId)
{
// translate the module id
const TModuleId *pModuleId = route->ForeignToLocalIdx.getB(foreignModuleId);
if (pModuleId == NULL)
{
// oups !
nlwarning("removeForeignModule : unknown foreign module id %u", foreignModuleId);
return;
}
TModuleId proxyId = *pModuleId;
// retrieve the module proxy
TModuleProxies::iterator it2(_ModuleProxies.find(proxyId));
if (it2 == _ModuleProxies.end())
{
// oups !
nlwarning("Gateway '%s' : removeForeignModule : can't find proxy for id %u coming from foreign id %u",
getGatewayName().c_str(),
proxyId,
foreignModuleId);
// still remove the idx
route->ForeignToLocalIdx.removeWithA(foreignModuleId);
return;
}
CModuleProxy *modProx = static_cast(it2->second.getPtr());
// remove module information
pair range;
range = _KnownModules.equal_range(modProx->_FullyQualifiedModuleName);
nlassert(range.first != range.second);
bool found = false;
for (;range.first != range.second; ++range.first)
{
TKnownModuleInfo &kmi = range.first->second;
if (kmi.Route == route)
{
nldebug("Gateway '%s' : removing foreign module info for '%s'",
getGatewayName().c_str(),
CStringMapper::unmap(range.first->first).c_str());
// we have found the info relative to this module
_KnownModules.erase(range.first);
found = true;
break;
}
}
nlassert(found == true);
// NB : stl debug mode don't allow to test with range.first when range;first is erased.
// nlassert(range.first != range.second);
// check if there is another view of this module
// if so, we keep the proxy and, eventually, we update the distance
range = _KnownModules.equal_range(modProx->_FullyQualifiedModuleName);
if (range.first != range.second)
{
// clean the translation table
route->ForeignToLocalIdx.removeWithA(foreignModuleId);
// we keep the proxy, choose the best route
TKnownModuleInfos::iterator best(_KnownModules.end());
for (; range.first != range.second; ++range.first)
{
if (best == _KnownModules.end()
|| best->second.ModuleDistance > range.first->second.ModuleDistance)
best = range.first;
}
nlassert(best != _KnownModules.end());
TKnownModuleInfo &kmi = best->second;
if (modProx->_Route != kmi.Route)
{
// the best route has changed, update the proxy
nldebug("Gateway '%s' : use a new route for module '%s' from %u to %u hop",
getGatewayName().c_str(),
modProx->getModuleName().c_str(),
modProx->_Distance,
kmi.ModuleDistance);
// update the proxy data
modProx->_Route = kmi.Route;
modProx->_ForeignModuleId = kmi.ForeignProxyId;
if (modProx->_Distance != kmi.ModuleDistance)
{
// the distance has changed, update and send the new distance to other gateway
modProx->_Distance = kmi.ModuleDistance;
sendModuleDistanceUpdate(modProx);
}
}
}
else
{
// do not remove proxy for local module from her !
if (modProx->_Route != NULL)
{
// this module is no longer reachable, remove the proxy
// trigger an event in the gateway
onRemoveModuleProxy(modProx);
// remove from the proxy list
_NameToProxyIdx.removeWithB(modProx);
_ModuleProxies.erase(it2);
// release the proxy
IModuleManager::getInstance().releaseModuleProxy(proxyId);
}
// clean the translation table
route->ForeignToLocalIdx.removeWithA(foreignModuleId);
}
}
void sendModuleDistanceUpdate(IModuleProxy *proxy)
{
// in fact, don't send immediately, store update in each
// route and wait the next update or module message sending
// to effectively send the update
// for each route
TRouteList::iterator first(_Routes.begin()), last(_Routes.end());
for (; first != last; ++first)
{
CGatewayRoute *route = *first;
if (isModuleProxyVisible(proxy, route))
{
updateModuleDistanceToRoute(route, proxy);
// // TODO : optimize by batch sending
// TModuleDistanceChangeMsg mdu;
//
// mdu.ModuleId = proxy->getModuleProxyId();
// mdu.NewDistance = proxy->getModuleDistance()+1;
//
// CMessage msg("MOD_DST_UPD");
// msg.serial(mdu);
//
// sendPendingModuleUpdate(route);
// route->sendMessage(msg);
}
}
}
/// Check if a module can be seen by a route
bool isModuleProxyVisible(IModuleProxy *proxy, CGatewayRoute *route)
{
if (route == NULL)
{
// no route, we can see the proxy
return true;
}
// check firewall rules
if (route->getTransport()->Firewalled)
{
if (route->FirewallDisclosed.find(proxy->getModuleProxyId()) == route->FirewallDisclosed.end())
return false;
}
// if the module is local, then, it can be seen
if (proxy->getGatewayRoute() == NULL)
return true;
// if the module is on the same route, it can't be seen (it is seen by the route outbound)
if (proxy->getGatewayRoute() == route)
return false;
IGatewayTransport *transport = route->getTransport();
// if the module is on a different transport, it can be seen
if (proxy->getGatewayRoute()->getTransport() != transport)
{
// we also need to check if this module is known in this route
// CGatewayRoute::TForeignToLocalIdx::iterator it(route->ForeignToLocalIdx.find(proxy->getForeignModuleId()));
if (route->ForeignToLocalIdx.getA(proxy->getModuleProxyId()) != NULL)
// this module is known in this route, so not invisible
return false;
// ok, we can see
return true;
}
// if the transport in not in peer invisible, it can be seen
if (!transport->PeerInvisible)
return true;
// not visible
return false;
}
/// Disclose module information to a gateway route
void discloseModuleToRoute(CGatewayRoute *route, IModuleProxy *proxy)
{
// route->PendingUndisclosure.erase(proxy->getModuleProxyId());
CGatewayRoute::TPendingEvent pe;
pe.EventType = CGatewayRoute::pet_disclose_module;
pe.ModuleId = proxy->getModuleProxyId();
route->PendingEvents.push_back(pe);
// route->PendingDisclosure.insert(proxy);
}
/// Undisclose module information to a gateway route
void undiscloseModuleToRoute(CGatewayRoute *route, IModuleProxy *proxy)
{
// route->PendingDisclosure.erase(proxy);
// route->PendingUndisclosure.insert(proxy->getModuleProxyId());
// route->FirewallDisclosed.erase(proxy->getModuleProxyId());
CGatewayRoute::TPendingEvent pe;
pe.EventType = CGatewayRoute::pet_undisclose_module;
pe.ModuleId = proxy->getModuleProxyId();
route->PendingEvents.push_back(pe);
route->FirewallDisclosed.erase(proxy->getModuleProxyId());
}
/// the distance of a module need to be update to peers
void updateModuleDistanceToRoute(CGatewayRoute *route, IModuleProxy *proxy)
{
CGatewayRoute::TPendingEvent pe;
pe.EventType = CGatewayRoute::pet_update_distance;
pe.ModuleId = proxy->getModuleProxyId();
route->PendingEvents.push_back(pe);
}
/// The security data need to be updated to peers
void updateModuleSecurityDataToRoute(CGatewayRoute *route, IModuleProxy *proxy)
{
CGatewayRoute::TPendingEvent pe;
pe.EventType = CGatewayRoute::pet_update_security;
pe.ModuleId = proxy->getModuleProxyId();
route->PendingEvents.push_back(pe);
}
void sendPendingModuleUpdate(CGatewayRoute *route)
{
if (route->PendingEvents.empty())
return;
CMessage updateMsg("MOD_UPD");
// compil all update in a single message
while (!route->PendingEvents.empty())
{
CGatewayRoute::TPendingEvent &pe = route->PendingEvents.front();
switch (pe.EventType)
{
case CGatewayRoute::pet_disclose_module:
{
IModuleProxy *proxy = getModuleProxy(pe.ModuleId);
if (proxy == NULL)
break;
// store the update type
updateMsg.serialShortEnum(pe.EventType);
// encode the message data
TModuleDescCodec modDesc(proxy);
updateMsg.serial(modDesc);
// modDesc.encode(proxy, updateMsg);
}
break;
case CGatewayRoute::pet_undisclose_module:
{
// store the update type
updateMsg.serialShortEnum(pe.EventType);
// store the module id
updateMsg.serial(pe.ModuleId);
}
break;
case CGatewayRoute::pet_update_distance:
{
IModuleProxy *proxy = getModuleProxy(pe.ModuleId);
if (proxy == NULL)
break;
// store the update type
updateMsg.serialShortEnum(pe.EventType);
// store module ID and distance
updateMsg.serial(pe.ModuleId);
uint32 distance = proxy->getModuleDistance()+1;
updateMsg.serial(distance);
}
break;
case CGatewayRoute::pet_update_security:
{
IModuleProxy *proxy = getModuleProxy(pe.ModuleId);
if (proxy == NULL)
break;
// store the update type
updateMsg.serialShortEnum(pe.EventType);
// store module ID and security data
TModuleSecurityChangeMsg secChg;
secChg.ModuleId = pe.ModuleId;
secChg.SecDesc.SecurityData = const_cast(proxy->getFirstSecurityData());
updateMsg.serial(secChg);
// updateMsg.serial(pe.ModuleId);
// TSecurityData *modSec = const_cast(proxy->getFirstSecurityData());
// updateMsg.serialPolyPtr(modSec);
}
break;
default:
// should not append
nlstop;
}
route->PendingEvents.pop_front();
}
// now send the message
route->sendMessage(updateMsg);
// // send pending module proxy un/disclosure
// if (!route->PendingDisclosure.empty())
// {
// // disclose new module
// TModuleAddMsg message;
// message.Modules.resize(route->PendingDisclosure.size());
//
// std::set::iterator first(route->PendingDisclosure.begin()), last(route->PendingDisclosure.end());
// for (uint i=0; first != last; ++i, ++first)
// {
// TModuleDescMsg &modDesc = message.Modules[i];
// IModuleProxy *addedModule = *first;
//
// modDesc.ModuleProxyId = addedModule->getModuleProxyId();
// modDesc.ModuleClass = addedModule->getModuleClassName();
// modDesc.ModuleFullName = addedModule->getModuleName();
// modDesc.ModuleDistance = addedModule->getModuleDistance()+1;
// }
// route->PendingDisclosure.clear();
//
// CMessage buffer("MOD_ADD");
// buffer.serial(message);
//
// route->sendMessage(buffer);
// }
// if (!route->PendingUndisclosure.empty())
// {
// // disclose new module
// TModuleRemMsg message;
// std::copy(route->PendingUndisclosure.begin(), route->PendingUndisclosure.end(), back_insert_iterator >(message.RemovedModules));
// route->PendingUndisclosure.clear();
//
// CMessage buffer("MOD_REM");
// buffer.serial(message);
//
// route->sendMessage(buffer);
// }
}
void getModuleList(std::vector &resultList)
{
TModuleProxies::iterator first(_ModuleProxies.begin()), last(_ModuleProxies.end());
for (; first != last; ++first)
{
resultList.push_back(first->second);
}
}
NLMISC_COMMAND_HANDLER_TABLE_EXTEND_BEGIN(CStandardGateway, CModuleBase)
NLMISC_COMMAND_HANDLER_ADD(CStandardGateway, dump, "dump various information about the gateway statue", "")
NLMISC_COMMAND_HANDLER_ADD(CStandardGateway, transportListAvailableClass, "list the available transport class", "no param")
NLMISC_COMMAND_HANDLER_ADD(CStandardGateway, transportAdd, "add a new transport to this gateway", " ")
NLMISC_COMMAND_HANDLER_ADD(CStandardGateway, transportOptions, "set a gateway level option on a transport", " ( [PeerInvisible] [Firewalled] )")
NLMISC_COMMAND_HANDLER_ADD(CStandardGateway, transportCmd, "send a command to a transport", "[ ( )]*")
NLMISC_COMMAND_HANDLER_ADD(CStandardGateway, transportRemove, "remove an existing transport instance", "")
NLMISC_COMMAND_HANDLER_ADD(CStandardGateway, securityListAvailableClass, "list the available security class", "no param")
NLMISC_COMMAND_HANDLER_ADD(CStandardGateway, securityCreate, "create a security plug-in", "")
NLMISC_COMMAND_HANDLER_ADD(CStandardGateway, securityCommand, "send a command to the security plug-in", "")
NLMISC_COMMAND_HANDLER_ADD(CStandardGateway, securityRemove, "remove the security plug-in", "no parameter")
NLMISC_COMMAND_HANDLER_TABLE_END
NLMISC_CLASS_COMMAND_DECL(securityRemove)
{
nlunreferenced(rawCommandString);
nlunreferenced(args);
nlunreferenced(quiet);
nlunreferenced(human);
if (_SecurityPlugin == NULL)
{
log.displayNL("No security plug-in !");
return true;
}
removeSecurityPlugin();
return true;
}
NLMISC_CLASS_COMMAND_DECL(securityCommand)
{
nlunreferenced(args);
nlunreferenced(quiet);
nlunreferenced(human);
TParsedCommandLine command;
if (!command.parseParamList(rawCommandString))
{
log.displayNL("Invalid command line");
return false;
}
if (command.SubParams.size() < 2)
{
log.displayNL("Invalid command line");
return false;
}
if (_SecurityPlugin == NULL)
{
log.displayNL("No security plug-in !");
return true;
}
sendSecurityCommand(*command.SubParams[1]);
return true;
}
NLMISC_CLASS_COMMAND_DECL(securityCreate)
{
nlunreferenced(rawCommandString);
nlunreferenced(quiet);
nlunreferenced(human);
if (args.size() != 1)
return false;
if (_SecurityPlugin != NULL)
{
log.displayNL("The gateway already have a security plug-in ! Remove it first");
return true;
}
log.displayNL("Creating a security plug-in '%s' in gateway '%s'",
args[0].c_str(),
getModuleName().c_str());
createSecurityPlugin(args[0]);
return true;
}
NLMISC_CLASS_COMMAND_DECL(securityListAvailableClass)
{
nlunreferenced(rawCommandString);
nlunreferenced(quiet);
nlunreferenced(human);
if (args.size() != 0)
return false;
vector list;
NLMISC_GET_FACTORY(CGatewaySecurity, std::string).fillFactoryList(list);
log.displayNL("List of %u available security class :", list.size());
for (uint i=0; iParamName;
if (_Transports.find(transName) == _Transports.end())
{
log.displayNL("unknown transport '%s'", transName.c_str());
return false;
}
// IGatewayTransport *transport = _Transports.find(transName)->second;
// check for peer invisible
if (cl.SubParams[1]->getParam("PeerInvisible"))
setTransportPeerInvisible(transName, true);
else
setTransportPeerInvisible(transName, false);
// check for firewall mode
if (cl.SubParams[1]->getParam("Firewalled"))
setTransportFirewallMode(transName, true);
else
setTransportFirewallMode(transName, false);
return true;
}
NLMISC_CLASS_COMMAND_DECL(transportAdd)
{
nlunreferenced(rawCommandString);
nlunreferenced(quiet);
nlunreferenced(human);
if (args.size() != 2)
return false;
if (_Transports.find(args[1]) != _Transports.end())
{
log.displayNL("A transport with that name already exist !");
return true;
}
createTransport(args[0], args[1]);
return true;
}
NLMISC_CLASS_COMMAND_DECL(transportListAvailableClass)
{
nlunreferenced(rawCommandString);
nlunreferenced(quiet);
nlunreferenced(human);
if (args.size() != 0)
return false;
vector list;
NLMISC_GET_FACTORY(IGatewayTransport, std::string).fillFactoryList(list);
log.displayNL("List of %u available transport class :", list.size());
for (uint i=0; isecond;
log.displayNL(" ID:%5u : \tName = '%s' \tclass = '%s'",
module->getModuleId(),
module->getModuleName().c_str(),
module->getModuleClassName().c_str());
}
}
log.displayNL("The gateway as %u transport activated :", _Transports.size());
{
TTransportList::iterator first(_Transports.begin()), last(_Transports.end());
for (; first != last; ++first)
{
const string &name = first->first;
IGatewayTransport *transport = first->second;
log.displayNL("Transport '%s' (transport class is '%s') :",
name.c_str(),
transport->getClassName().c_str());
log.displayNL(" * %s", transport->PeerInvisible ? "Peer module are NON visible" : "Peer modules are visible");
log.displayNL(" * %s", transport->Firewalled ? "Firewall ON" : "Firewall OFF");
transport->dump(log);
}
}
log.displayNL("------------------------------");
log.displayNL("------- End of dump ----------");
log.displayNL("------------------------------");
return true;
}
};
// register the module factory
NLNET_REGISTER_MODULE_FACTORY(CStandardGateway, "StandardGateway");
/** Set a security data block. If a bloc of the same type
* already exist in the list, the new one will replace the
* existing one.
*/
void CGatewaySecurity::setSecurityData(IModuleProxy *proxy, TSecurityData *securityData)
{
// forward the call to standard gateway
CStandardGateway *sg = static_cast(_Gateway);
sg->setSecurityData(proxy, securityData);
}
/** Clear a block of security data
* The block is identified by the data tag
*/
bool CGatewaySecurity::removeSecurityData(IModuleProxy *proxy, uint8 dataTag)
{
// forward the call to standard gateway
CStandardGateway *sg = static_cast(_Gateway);
return sg->removeSecurityData(proxy, dataTag);
}
/** Replace the complete set of security data with the new one.
* Security data allocated on the proxy are freed,
*/
void CGatewaySecurity::replaceAllSecurityDatas(IModuleProxy *proxy, TSecurityData *securityData)
{
// forward the call to standard gateway
CStandardGateway *sg = static_cast(_Gateway);
sg->replaceAllSecurityDatas(proxy, securityData);
}
/** Ask the gateway to resend the security data.
* The plug-in call this method after having changed
* the security info for a plug-in outside of the
* onNewProxy call.
*/
void CGatewaySecurity::forceSecurityUpdate(IModuleProxy *proxy)
{
// forward the call to standard gateway
CStandardGateway *sg = static_cast(_Gateway);
sg->forceSecurityUpdate(proxy);
}
void forceGatewayLink()
{
}
} // namespace NLNET