// 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 to 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