1 // ANNA - Anna is Not Nothingness Anymore //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
9 #include <anna/diameter.comm/Engine.hpp>
11 #include <anna/core/tracing/Logger.hpp>
12 #include <anna/core/tracing/TraceMethod.hpp>
13 #include <anna/xml/Node.hpp>
14 #include <anna/comm/Network.hpp>
15 #include <anna/comm/Host.hpp>
16 #include <anna/comm/ClientSocket.hpp>
18 #include <anna/diameter.comm/Transport.hpp>
19 #include <anna/diameter.comm/Engine.hpp>
20 #include <anna/diameter.comm/Entity.hpp>
21 #include <anna/diameter.comm/Server.hpp>
22 #include <anna/diameter.comm/ClientSession.hpp>
23 #include <anna/diameter.comm/LocalServer.hpp>
24 #include <anna/core/functions.hpp>
25 #include <anna/diameter/internal/sccs.hpp>
26 #include <anna/diameter.comm/OamModule.hpp>
27 #include <anna/diameter/codec/functions.hpp>
28 #include <anna/diameter/helpers/base/functions.hpp>
29 #include <anna/diameter/helpers/helpers.hpp>
30 #include <anna/diameter/codec/Message.hpp>
31 #include <anna/diameter/codec/Avp.hpp>
37 using namespace anna::diameter::comm;
40 Engine::Engine(const char *className, codec::Engine *baseProtocolCodecEngine) :
41 anna::app::Component(className),
42 a_baseProtocolCodecEngine(baseProtocolCodecEngine),
44 a_availableForEntities(false),
45 a_availableForLocalServers(false),
50 a_watchdogPeriod(ClientSession::DefaultWatchdogPeriod),
51 a_maxConnectionDelay(anna::comm::ClientSocket::DefaultMaxConnectionDelay /* 200 ms*/),
52 a_numberOfClientSessionsPerServer(1) {
53 anna::diameter::sccs::activate();
54 a_realm = anna::functions::getDomainname();
55 a_host = anna::functions::getHostname();
58 Server* Engine::allocateServer() throw() { return a_serversRecycler.create(); }
59 void Engine::releaseServer(Server *server) throw() { a_serversRecycler.release(server); }
60 ClientSession* Engine::allocateClientSession() throw() { return a_clientSessionsRecycler.create(); }
61 void Engine::releaseClientSession(ClientSession *clientSession) throw() { a_clientSessionsRecycler.release(clientSession); }
64 void Engine::setClientCERandDWR(const anna::DataBlock & cer, const anna::DataBlock & dwr) throw(anna::RuntimeException) {
65 if(codec::functions::getCommandId(cer) != helpers::base::COMMANDID__Capabilities_Exchange_Request) {
66 throw anna::RuntimeException("The message provided as 'CER' is not a Capabilities-Exchange-Request", ANNA_FILE_LOCATION);
69 if(codec::functions::getCommandId(dwr) != helpers::base::COMMANDID__Device_Watchdog_Request) {
70 throw anna::RuntimeException("The message provided as 'DWR' is not a Device-Watchdog-Request", ANNA_FILE_LOCATION);
77 void Engine::setClientCERandDWR(const std::string & cer, const std::string & dwr) throw(anna::RuntimeException) {
79 // Check for base protocol codec engine:
80 if (!getBaseProtocolCodecEngine())
81 throw anna::RuntimeException("Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow base protocol messages encoding, or use setClientCERandDWR(const anna::DataBlock & cer, const anna::DataBlock & dwr) which expect externally encoded messages", ANNA_FILE_LOCATION);
84 // <CER> ::= < Diameter Header: 257, REQ >
85 // { Origin-Host } 264 diameterIdentity
86 // { Origin-Realm } 296 idem
87 // 1* { Host-IP-Address } 257, address
88 // { Vendor-Id } 266 Unsigned32
89 // { Product-Name } 269 UTF8String
90 // [Origin-State-Id] 278 Unsigned32
91 // * [ Supported-Vendor-Id ] 265 Unsigned32
92 // * [ Auth-Application-Id ] 258 Unsigned32
93 // * [Acct-Application-Id] 259 Unsigned32
94 anna::diameter::codec::Message diameterCER(getBaseProtocolCodecEngine());
95 int applicationId = 0 /*anna::diameter::helpers::APPID__3GPP_Rx*/; // Unsigned32
96 std::string OH = getHost();
97 std::string OR = getRealm();
98 std::string hostIP = anna::functions::getHostnameIP(); // Address
99 int vendorId = anna::diameter::helpers::VENDORID__tgpp; // Unsigned32
100 std::string productName = "ANNA Diameter Client"; // UTF8String
101 bool encodeDefault = false;
105 diameterCER.loadXML(cer);
106 } catch(anna::RuntimeException &ex) {
108 encodeDefault = true;
109 LOGWARNING(anna::Logger::warning("CER file not found or unable to parse. Encoding harcoded default version ...", ANNA_FILE_LOCATION));
113 encodeDefault = true;
117 diameterCER.setId(anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request);
118 diameterCER.setApplicationId(applicationId);
119 diameterCER.addAvp(anna::diameter::helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->setValue(OH);
120 diameterCER.addAvp(anna::diameter::helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->setValue(OR);
121 diameterCER.addAvp(anna::diameter::helpers::base::AVPID__Host_IP_Address)->getAddress()->fromPrintableString(hostIP.c_str()); // supported by Address class, anyway is better to provide "1|<ip address>"
122 diameterCER.addAvp(anna::diameter::helpers::base::AVPID__Vendor_Id)->getUnsigned32()->setValue(vendorId);
123 diameterCER.addAvp(anna::diameter::helpers::base::AVPID__Product_Name)->getUTF8String()->setValue(productName);
124 diameterCER.addAvp(anna::diameter::helpers::base::AVPID__Auth_Application_Id)->getUnsigned32()->setValue(applicationId);
128 // <DWR> ::= < Diameter Header: 280, REQ >
131 anna::diameter::codec::Message diameterDWR(getBaseProtocolCodecEngine());
132 encodeDefault = false;
136 diameterDWR.loadXML(dwr);
137 } catch(anna::RuntimeException &ex) {
139 encodeDefault = true;
140 LOGWARNING(anna::Logger::warning("DWR file not found or unable to parse. Encoding harcoded default version ...", ANNA_FILE_LOCATION));
144 encodeDefault = true;
148 diameterDWR.setId(anna::diameter::helpers::base::COMMANDID__Device_Watchdog_Request);
149 diameterDWR.setApplicationId(applicationId);
150 diameterDWR.addAvp(anna::diameter::helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->setValue(OH);
151 diameterDWR.addAvp(anna::diameter::helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->setValue(OR);
154 // Assignment for internal encoded versions:
155 setClientCERandDWR(diameterCER.code(), diameterDWR.code());
158 void Engine::setWatchdogPeriod(const anna::Millisecond & wp) throw(anna::RuntimeException) {
159 if(wp < ClientSession::DefaultWatchdogPeriod) {
160 throw anna::RuntimeException(anna::functions::asString("Please set watchdog period over %s", ClientSession::DefaultWatchdogPeriod.asString().c_str()), ANNA_FILE_LOCATION);
163 a_watchdogPeriod = wp;
166 void Engine::checkEntityCollision(const socket_v &v) throw(anna::RuntimeException) {
167 socket_v::const_iterator it;
168 socket_v::const_iterator it_min(v.begin());
169 socket_v::const_iterator it_max(v.end());
171 for(it = it_min; it != it_max; it++) {
172 server_iterator ii = server_find(*it);
174 if(ii != server_end())
175 throw anna::RuntimeException("diameter::comm::Engine::checkEntityCollision: Server is already reserved by a former created entity. Use another", ANNA_FILE_LOCATION);
178 // Check repetitions:
179 std::map < socket_t, int/*dummy*/ > auxMap;
181 for(it = it_min; it != it_max; it++) auxMap[(*it)] = 0;
183 if(auxMap.size() != v.size())
184 throw anna::RuntimeException("diameter::comm::Engine::checkEntityCollision: Provided addresses list (sockets) must have all items different", ANNA_FILE_LOCATION);
187 Entity* Engine::createEntity(const socket_v & socketList, const std::string &description)
188 throw(anna::RuntimeException) {
189 Entity* result(NULL);
190 anna::Guard guard(this, "anna::diameter::comm::Engine::createEntity");
192 if(socketList.size() == 0)
193 throw anna::RuntimeException("diameter::comm::Engine::createEntity Address/Port server list provided is empty", ANNA_FILE_LOCATION);
195 // Proteccion antes de reservar memoria para una entidad (allocateEntity):
196 checkEntityCollision(socketList);
198 if((result = allocateEntity()) == NULL)
199 throw anna::RuntimeException("diameter::comm::Engine::allocateEntity returns NULL (perhaps virtual method was not implemented)", ANNA_FILE_LOCATION);
202 result->initialize(); // warning: recycler does not initialize its objects and at least, is important to empty servers vector.
203 // Assignments (it could be done at allocate):
204 result->setEngine(this); // lo podia haber asignado en el allocateEntity (no importa)
205 result->setMaxServers(socketList.size());
206 result->setDescription(description);
207 entity_key key(getEntityKey(socketList));
208 result->a_socketListLiteral = key;
209 // Create associated servers:
210 socket_v::const_iterator it;
211 socket_v::const_iterator it_min(socketList.begin());
212 socket_v::const_iterator it_max(socketList.end());
215 for(it = it_min; it != it_max; it++) {
216 result->addServer(*it);
218 if(count == 1) result->a_primarySocketLiteral = anna::functions::socketLiteralAsString((*it).first, (*it).second);
220 if(count == 2) result->a_secondarySocketLiteral = anna::functions::socketLiteralAsString((*it).first, (*it).second);
225 a_entities.insert(entity_value_type(key, result));
227 string msg("diameter::comm::Engine::createEntity | ");
228 msg += result->asString();
229 msg += anna::functions::asText(" | AutoBind: ", a_autoBind);
230 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
236 LocalServer *Engine::createLocalServer(const std::string & addr, int port, int maxConnections, const anna::Millisecond & allowedInactivityTime, int category, const std::string & description)
237 throw(anna::RuntimeException) {
238 LocalServer* result(NULL);
239 anna::Guard guard(this, "anna::diameter::comm::Engine::createLocalServer");
240 // Proteccion antes de reservar memoria para un LocalServer
241 socket_t key(addr, port);
243 if(a_localServers.find(key) != a_localServers.end())
244 throw anna::RuntimeException("LocalServer is already reserved by a former created access point. Cannot create again", ANNA_FILE_LOCATION);
246 if((result = allocateLocalServer()) == NULL)
247 throw anna::RuntimeException("diameter::comm::Engine::allocateLocalServer returns NULL (perhaps virtual method was not implemented)", ANNA_FILE_LOCATION);
249 result->setEngine(this); // lo podia haber asignado en el allocateLocalServer (no importa)
251 result->setCategory(category);
252 result->setDescription(description);
253 result->setAllowedInactivityTime(allowedInactivityTime);
254 result->initializeStatisticConcepts();
255 // Los saco con metodos virtuales readXXX del motor:
256 // if ((a_cea.isEmpty()) || (a_dwa.isEmpty()))
257 // throw anna::RuntimeException("Must define valid CEA and DWA messages by mean setCEAandDWA()", ANNA_FILE_LOCATION);
258 a_localServers.insert(localServer_value_type(key, result));
260 string msg("diameter::comm::Engine::createLocalServer | ");
261 msg += result->asString();
262 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
265 // /*if (a_autoListen) */result->enable(); // creates server socket
266 result->setMaxConnections(maxConnections); // (*) this enables the listen port ... or not
271 Entity* Engine::createEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, const std::string &description)
272 throw(anna::RuntimeException) {
274 dualList.push_back(socket_t(addr1, port1));
275 dualList.push_back(socket_t(addr2, port2));
276 return (createEntity(dualList, description));
280 Server* Engine::createServer(Entity *entity, const socket_t & socket)
281 throw(anna::RuntimeException) {
282 Server* result(NULL);
283 anna::Guard guard(this, "anna::diameter::comm::Engine::createServer");
285 if((result = allocateServer()) == NULL)
286 throw anna::RuntimeException("diameter::comm::Engine::allocateServer returns NULL", ANNA_FILE_LOCATION);
289 result->initialize(); // warning: recycler does not initialize its objects and at least, is important to empty client-sessions vector.
290 // Assignments (it could be done at allocate):
291 result->a_parent = entity;
292 result->a_socket = socket;
293 result->setMaxClientSessions(a_numberOfClientSessionsPerServer /* engine */);
294 result->a_engine = this;
295 result->initializeStatisticConcepts();
297 for(int k = 0; k < a_numberOfClientSessionsPerServer; k++)
298 result->addClientSession(k);
300 a_servers.insert(server_value_type(socket, result));
301 // LOGDEBUG( Lo comento, porque ya se tracea en el createEntity
302 // string msg("diameter::comm::Engine::resolveServer | ");
303 // msg += result->asString();
304 // msg += anna::functions::asText(" | AutoBind: ", a_autoBind);
305 // anna::Logger::debug(msg, ANNA_FILE_LOCATION);
312 ClientSession* Engine::createClientSession(Server *server, int socketId)
313 throw(anna::RuntimeException) {
314 ClientSession* result(NULL);
315 anna::Guard guard(this, "anna::diameter::comm::Engine::createClientSession");
317 if((result = allocateClientSession()) == NULL)
318 throw anna::RuntimeException("diameter::comm::Engine::allocateClientSession returns NULL", ANNA_FILE_LOCATION);
321 result->initialize(); // warning: recycler does not initialize its objects and at least...
322 // Assignments (it could be done at allocate):
324 if((a_cer.isEmpty()) || (a_dwr.isEmpty()))
325 throw anna::RuntimeException("Must define valid CER and DWR messages by mean setCERandDWR()", ANNA_FILE_LOCATION);
327 result->a_cer.setBody(a_cer);
328 result->a_dwr.setBody(a_dwr);
329 result->setWatchdogPeriod(a_watchdogPeriod);
330 result->a_parent = server;
331 result->a_socketId = socketId;
332 result->initializeSequences(); // despu�s de asignar el server y el socketId (*)
333 // (*) Las secuencias se basan en la semilla: srand(::time(NULL) + anna::functions::exclusiveHash(anna::functions::asString("%s:%d|%d", getAddress().c_str(), getPort(), a_socketId)));
334 result->a_engine = this;
335 clientSession_key key = ClientSession::getKey(server->getAddress(), server->getPort(), socketId);
336 a_clientSessions.insert(clientSession_value_type(key, result));
337 // LOGDEBUG( Lo comento, porque ya se tracea en el createEntity
338 // string msg("diameter::comm::Engine::createClientSession | ");
339 // msg += result->asString();
340 // msg += anna::functions::asText(" | AutoBind: ", a_autoBind);
341 // anna::Logger::debug(msg, ANNA_FILE_LOCATION);
344 anna::comm::Network& network = anna::comm::Network::instantiate();
345 result->a_server = network.resolveServer(server->getAddress().c_str(), server->getPort(), true /* autoRecovery */,
346 result->a_receiverFactory, &anna::diameter::comm::Transport::getFactory(),
347 anna::comm::Network::Port::Multiple, anna::comm::Network::DoConnect::No /* (*) */);
348 // Delay time on tcp connect:
349 result->a_server->setMaxConnectionDelay(a_maxConnectionDelay); // (*)
352 if(a_autoBind) result->bind();
358 bool Engine::broadcastEntities(const Message* message) throw(anna::RuntimeException) {
359 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "broadcastEntities", ANNA_FILE_LOCATION));
363 for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
365 ok = entity(it)->broadcast(message);
367 if(!ok) allok = false;
368 } catch(anna::RuntimeException &ex) {
377 bool Engine::broadcastLocalServers(const Message* message) throw(anna::RuntimeException) {
378 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "broadcastLocalServers", ANNA_FILE_LOCATION));
382 for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++) {
384 ok = localServer(it)->broadcast(message);
386 if(!ok) allok = false;
387 } catch(anna::RuntimeException &ex) {
396 bool Engine::bind() throw(anna::RuntimeException) {
397 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "bind", ANNA_FILE_LOCATION));
398 bool result = true; // all OK return
400 for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
403 } catch(anna::RuntimeException &ex) {
412 ClientSession* Engine::findClientSession(const std::string & addr, int port, int socketId, anna::Exception::Mode::_v emode)
413 throw(anna::RuntimeException) {
414 return findClientSession(ClientSession::getKey(addr, port, socketId), emode);
417 ClientSession* Engine::findClientSession(const std::string & key, anna::Exception::Mode::_v emode)
418 throw(anna::RuntimeException) {
419 anna::Guard guard(this, "anna::diameter::comm::Engine::findClientSession");
420 clientSession_iterator ii = clientSession_find(key);
422 if(ii != clientSession_end())
423 return clientSession(ii);
425 if(emode != anna::Exception::Mode::Ignore) {
426 string msg("diameter::comm::Engine::findClientSession | [addr:port|socketId] = ");
428 msg += " | ClientSession not found";
429 anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
431 if(emode == anna::Exception::Mode::Throw)
441 Server* Engine::findServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
442 throw(anna::RuntimeException) {
443 anna::Guard guard(this, "anna::diameter::comm::Engine::findServer");
444 server_iterator ii = server_find(server_key(addr, port));
446 if(ii != server_end())
449 if(emode != anna::Exception::Mode::Ignore) {
450 string msg("diameter::comm::Engine::findServer | addr: ");
452 msg += anna::functions::asText(" | port: ", port);
453 msg += " | Server not found";
454 anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
456 if(emode == anna::Exception::Mode::Throw)
465 Entity* Engine::findEntity(const socket_v & socketList, anna::Exception::Mode::_v emode)
466 throw(anna::RuntimeException) {
467 anna::Guard guard(this, "anna::diameter::comm::Engine::findEntity");
468 entity_key key(getEntityKey(socketList));
469 entity_iterator ii = entity_find(key);
471 if(ii != entity_end())
474 if(emode != anna::Exception::Mode::Ignore) {
475 string msg("diameter::comm::Engine::findEntity | socket list: ");
477 msg += " | Entity not found";
478 anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
480 if(emode == anna::Exception::Mode::Throw)
489 Entity* Engine::findEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, anna::Exception::Mode::_v emode)
490 throw(anna::RuntimeException) {
492 dualList.push_back(socket_t(addr1, port1));
493 dualList.push_back(socket_t(addr2, port2));
494 return (findEntity(dualList, emode));
498 //Entity* Engine::findEntity(int category, anna::Exception::Mode::_v emode)
499 //throw(anna::RuntimeException) {
503 // for (entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
504 // entity = entity(it);
505 // if (entity->getCategory() == category) return entity;
512 LocalServer* Engine::findLocalServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
513 throw(anna::RuntimeException) {
514 anna::Guard guard(this, "anna::diameter::comm::Engine::findLocalServer");
515 socket_t key(addr, port);
516 localServer_iterator ii = localServer_find(key);
518 if(ii != localServer_end())
519 return localServer(ii);
521 if(emode != anna::Exception::Mode::Ignore) {
522 string msg("diameter::comm::Engine::findLocalServer | addr: ");
524 msg += anna::functions::asText(" | port: ", port);
525 msg += " | LocalServer not found";
526 anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
528 if(emode == anna::Exception::Mode::Throw)
538 ServerSession* Engine::findServerSession(int socketId, anna::Exception::Mode::_v emode) throw(anna::RuntimeException) {
539 anna::Guard guard(this, "anna::diameter::comm::Engine::findServerSession");
540 ServerSession *result;
542 // Search at each local server:
543 for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++) {
544 result = localServer(it)->findServerSession(socketId, anna::Exception::Mode::Ignore);
546 if(result) return result;
549 if(emode != anna::Exception::Mode::Ignore) {
550 string msg("diameter::comm::Engine::findServerSession | socketId: ");
551 msg += anna::functions::asString(socketId);
552 msg += " | ServerSession not found";
553 anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
555 if(emode == anna::Exception::Mode::Throw)
565 void Engine::closeClientSession(ClientSession* clientSession, bool destroy)
566 throw(anna::RuntimeException) {
567 if(clientSession == NULL)
571 string msg("diameter::comm::Engine::closeClientSession | ");
572 msg += clientSession->asString();
573 msg += " | Destroy: ";
574 msg += (destroy ? "yes" : "no");
575 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
577 anna::Guard guard(this, "anna::diameter::comm::Engine::closeClientSession");
578 clientSession_iterator ii = clientSession_find(clientSession->getKey());
580 if(ii == clientSession_end())
584 clientSession->setState(ClientSession::State::Closing);
586 if(destroy) clientSession->setAutoRecovery(false);
588 clientSession->unbind(destroy /* destroy needs to perform immediate close */);
592 releaseClientSession(clientSession);
593 } catch(anna::RuntimeException& ex) {
597 a_clientSessions.erase(ii);
603 void Engine::closeServer(Server* server, bool destroy)
604 throw(anna::RuntimeException) {
609 string msg("diameter::comm::Engine::closeServer | ");
610 msg += server->asString();
611 msg += " | Destroy: ";
612 msg += (destroy ? "yes" : "no");
613 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
615 anna::Guard guard(this, "anna::diameter::comm::Engine::closeServer");
616 server_iterator ii = server_find(server->a_socket);
618 if(ii == server_end())
622 server->close(destroy);
626 releaseServer(server);
627 } catch(anna::RuntimeException& ex) {
635 void Engine::closeEntity(Entity* entity, bool destroy)
636 throw(anna::RuntimeException) {
641 string msg("diameter::comm::Engine::closeEntity | ");
642 msg += entity->asString();
643 msg += " | Destroy: ";
644 msg += (destroy ? "yes" : "no");
645 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
647 anna::Guard guard(this, "anna::diameter::comm::Engine::closeEntity");
648 entity_iterator ii = entity_find(entity->a_socketListLiteral);
650 if(ii == entity_end())
654 entity->close(destroy);
658 if(!entity->idle()) { entity->setDeprecated(true); return; }
660 releaseEntity(entity);
661 } catch(anna::RuntimeException& ex) {
665 a_entities.erase(ii);
670 void Engine::closeLocalServer(LocalServer* localServer, bool destroy)
671 throw(anna::RuntimeException) {
672 if(localServer == NULL)
676 string msg("diameter::comm::Engine::closeLocalServer | ");
677 msg += localServer->asString();
678 msg += " | Destroy: ";
679 msg += (destroy ? "yes" : "no");
680 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
682 anna::Guard guard(this, "anna::diameter::comm::Engine::closeLocalServer");
683 localServer_iterator ii = localServer_find(localServer->getKey());
685 if(ii == localServer_end())
689 localServer->close();
693 releaseLocalServer(localServer);
694 } catch(anna::RuntimeException& ex) {
698 a_localServers.erase(ii);
703 void Engine::closeEntities(bool destroy) throw(anna::RuntimeException) {
704 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "closeEntities", ANNA_FILE_LOCATION));
705 anna::Guard guard(this, "anna::diameter::comm::Engine::closeEntities");
707 for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
708 closeEntity(entity(it), destroy);
711 void Engine::closeLocalServers(bool destroy) throw(anna::RuntimeException) {
712 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "closeLocalServers", ANNA_FILE_LOCATION));
713 anna::Guard guard(this, "anna::diameter::comm::Engine::closeLocalServers");
715 for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
716 closeLocalServer(localServer(it), destroy);
719 void Engine::eraseDeprecatedIdleEntities() throw() {
720 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "eraseDeprecatedIdleEntities", ANNA_FILE_LOCATION));
723 for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
726 if(et->isDeprecated() && et->idle()) closeEntity(et, true /* destroy */);
730 int Engine::getOTARequestsForEntities() const throw() {
733 for(const_entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
734 result += entity(it)->getOTARequests();
739 int Engine::getOTARequestsForLocalServers() const throw() {
742 for(const_localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
743 result += localServer(it)->getOTARequests();
749 void Engine::setRealm(const std::string & name) throw() {
750 a_realm = ((name != "") ? name : anna::functions::getDomainname());
754 void Engine::setHost(const std::string & name) throw() {
755 a_host = ((name != "") ? name : anna::functions::getHostname());
760 void Engine::raiseAutoRecovery(bool autoRecovery) throw(anna::RuntimeException) {
761 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "raiseAutoRecovery", ANNA_FILE_LOCATION));
763 for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
764 entity(it)->raiseAutoRecovery(autoRecovery);
767 void Engine::do_stop()
769 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "do_stop", ANNA_FILE_LOCATION));
770 close(true /* destroy */);
773 std::string Engine::asString(void) const throw() {
775 trace = "\n================================";
776 trace += "\nDiameter comm Engine information";
777 trace += "\n================================";
778 trace += "\nAutoBind: ";
779 trace += a_autoBind ? "yes" : "no";
780 trace += "\nMaxConnectionDelay: ";
781 trace += a_maxConnectionDelay.asString();
782 trace += "\nAvailable for entities: ";
783 trace += a_availableForEntities ? "yes" : "no";
784 trace += "\nAvailable for local servers: ";
785 trace += a_availableForLocalServers ? "yes" : "no";
786 trace += "\nOTA requests: ";
787 trace += anna::functions::asString("%d%s", getOTARequests(), idle() ? " (idle)" : "");
788 trace += "\nOTA requests for entities: ";
789 trace += anna::functions::asString("%d%s", getOTARequestsForEntities(), idleForEntities() ? " (idle)" : "");
790 trace += "\nOTA requests for local servers: ";
791 trace += anna::functions::asString("%d%s", getOTARequestsForLocalServers(), idleForLocalServers() ? " (idle)" : "");
793 trace += "\nNumber of entities: ";
794 trace += anna::functions::asString(a_entities.size());
796 for(const_entity_iterator it = entity_begin(); it != entity_end(); it++) {
798 trace += entity(it)->asString();
802 trace += "\nNumber of LocalServers: ";
803 trace += anna::functions::asString(a_localServers.size());
805 for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++) {
807 trace += localServer(it)->asString();
814 anna::xml::Node* Engine::asXML(anna::xml::Node* parent) const
816 parent = anna::app::Component::asXML(parent);
817 anna::xml::Node* result = parent->createChild("diameter.comm.Engine");
818 result->createAttribute("AutoBind", a_autoBind ? "yes" : "no");
819 result->createAttribute("MaxConnectionDelay", a_maxConnectionDelay.asString());
820 result->createAttribute("AvailableForEntities", a_availableForEntities ? "yes" : "no");
821 result->createAttribute("AvailableForLocalServers", a_availableForLocalServers ? "yes" : "no");
822 result->createAttribute("OTArequests", anna::functions::asString("%d%s", getOTARequests(), idle() ? " (idle)" : ""));
823 result->createAttribute("OTArequestsForEntities", anna::functions::asString("%d%s", getOTARequestsForEntities(), idleForEntities() ? " (idle)" : ""));
824 result->createAttribute("OTArequestsForLocalServers", anna::functions::asString("%d%s", getOTARequestsForLocalServers(), idleForLocalServers() ? " (idle)" : ""));
825 result->createAttribute("NumberOfEntities", a_entities.size());
826 anna::xml::Node* entities = result->createChild("Engine.Entities");
828 for(const_entity_iterator it = entity_begin(); it != entity_end(); it++)
829 entity(it)->asXML(entities);
831 result->createAttribute("NumberOfLocalServers", a_localServers.size());
832 anna::xml::Node* localServers = result->createChild("Engine.LocalServers");
834 for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
835 localServer(it)->asXML(localServers);
840 Engine::clientSession_iterator Engine::clientSession_find(const clientSession_key &key) throw() {
841 return a_clientSessions.find(key);
844 Engine::server_iterator Engine::server_find(const server_key &key) throw() {
845 return a_servers.find(key);
848 Engine::entity_iterator Engine::entity_find(const entity_key &key) throw() {
849 return a_entities.find(key);
852 Engine::localServer_iterator Engine::localServer_find(const socket_t &key) throw() {
853 return a_localServers.find(key);
856 Engine::entity_key Engine::getEntityKey(const std::string & addr1, int port1, const std::string & addr2, int port2) const throw() {
858 dualList.push_back(socket_t(addr1, port1));
859 dualList.push_back(socket_t(addr2, port2));
860 return (getEntityKey(dualList));
863 Engine::entity_key Engine::getEntityKey(const socket_v &v) const throw() {
865 socket_v::const_iterator it;
866 socket_v::const_iterator it_min(v.begin());
867 socket_v::const_iterator it_max(v.end());
869 for(it = it_min; it != it_max; it++) {
870 result += anna::functions::socketLiteralAsString((*it).first, (*it).second);
874 result.erase(result.size() - 1, 1); // remove last space
875 //return anna::functions::exclusiveHash(result);
880 void Engine::availabilityLostForEntities() throw() {
881 a_availableForEntities = false;
883 std::string msg = "diameter::comm::Engine { Realm: ";
885 msg += " } has lost its availability for entities";
886 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
889 OamModule &oamModule = OamModule::instantiate();
890 oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverEntitiesForEngineWithClassName__s__, getClassName());
891 oamModule.count(OamModule::Counter::LostAvailabilityOverEngineForEntities);
893 availabilityLostForEntities(this);
897 void Engine::availabilityRecoveredForEntities() throw() {
898 a_availableForEntities = true;
900 std::string msg = "diameter::comm::Engine { Realm: ";
902 msg += " } has recovered its availability for entities";
903 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
906 OamModule &oamModule = OamModule::instantiate();
907 oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverEntitiesForEngineWithClassName__s__, getClassName());
908 oamModule.count(OamModule::Counter::RecoveredAvailabilityOverEngineForEntities);
910 availabilityRecoveredForEntities(this);
914 void Engine::availabilityLostForLocalServers() throw() {
915 a_availableForLocalServers = false;
917 std::string msg = "diameter::comm::Engine { Realm: ";
919 msg += " } has lost its availability for local servers";
920 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
923 OamModule &oamModule = OamModule::instantiate();
924 oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverLocalServersForEngineWithClassName__s__, getClassName());
925 oamModule.count(OamModule::Counter::LostAvailabilityOverEngineForLocalServers);
927 availabilityLostForLocalServers(this);
931 void Engine::availabilityRecoveredForLocalServers() throw() {
932 a_availableForLocalServers = true;
934 std::string msg = "diameter::comm::Engine { Realm: ";
936 msg += " } has recovered its availability for local servers";
937 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
940 OamModule &oamModule = OamModule::instantiate();
941 oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverLocalServersForEngineWithClassName__s__, getClassName());
942 oamModule.count(OamModule::Counter::RecoveredAvailabilityOverEngineForLocalServers);
944 availabilityRecoveredForLocalServers(this);
948 bool Engine::refreshAvailabilityForEntities() throw() {
950 if(a_availableForEntities) { // check not-bound state for all client-sessions:
953 for(const_entity_iterator it = entity_begin(); it != entity_end(); it++)
954 if(entity(it)->isAvailable()) { isolate = false; break; }
957 availabilityLostForEntities();
964 // Here not available
965 for(const_entity_iterator it = entity_begin(); it != entity_end(); it++)
966 if(entity(it)->isAvailable()) {
967 availabilityRecoveredForEntities();
974 bool Engine::refreshAvailabilityForLocalServers() throw() {
976 if(a_availableForLocalServers) { // check not-bound state for all client-sessions:
979 for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
980 if(localServer(it)->isAvailable()) { isolate = false; break; }
983 availabilityLostForLocalServers();
990 // Here not available
991 for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
992 if(localServer(it)->isAvailable()) {
993 availabilityRecoveredForLocalServers();
1001 void Engine::readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw() {
1002 // Default DPA implementation:
1004 // 'Disconnect-Peer-Answer' (282,answer)
1005 // {Result-Code}...................................(268,0)
1006 // {Origin-Host}...................................(264,0)
1007 // {Origin-Realm}..................................(296,0)
1008 // [Error-Message].................................(281,0)
1009 // *[Failed-AVP]....................................(279,0)
1011 anna::diameter::codec::Message diameterDPA(getBaseProtocolCodecEngine());
1012 anna::diameter::codec::Avp avpRC(getBaseProtocolCodecEngine());
1013 anna::diameter::codec::Avp avpOH(getBaseProtocolCodecEngine());
1014 anna::diameter::codec::Avp avpOR(getBaseProtocolCodecEngine());
1016 diameterDPA.setId(anna::diameter::helpers::base::COMMANDID__Disconnect_Peer_Answer);
1017 diameterDPA.setVersion(1);
1018 diameterDPA.setApplicationId(codec::functions::getApplicationId(dpr));
1019 diameterDPA.setHopByHop(codec::functions::getHopByHop(dpr));
1020 diameterDPA.setEndToEnd(codec::functions::getEndToEnd(dpr));
1022 avpRC.setId(anna::diameter::helpers::base::AVPID__Result_Code);
1023 avpRC.setMandatoryBit();
1024 avpRC.getUnsigned32()->setValue(helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS);
1026 avpOH.setId(anna::diameter::helpers::base::AVPID__Origin_Host);
1027 avpOH.setMandatoryBit();
1028 avpOH.getDiameterIdentity()->fromPrintableString(a_host.c_str());
1030 avpOR.setId(anna::diameter::helpers::base::AVPID__Origin_Realm);
1031 avpOR.setMandatoryBit();
1032 avpOR.getDiameterIdentity()->fromPrintableString(a_realm.c_str());
1033 diameterDPA.addAvp(&avpRC);
1034 diameterDPA.addAvp(&avpOH);
1035 diameterDPA.addAvp(&avpOR);
1037 dpa = diameterDPA.code();
1038 } catch(anna::RuntimeException &ex) {
1039 std::string msg = ex.getText();
1040 msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages encoding (unable to answer with DPA)";
1041 anna::Logger::error(msg, ANNA_FILE_LOCATION);
1042 //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
1047 void Engine::readCEA(anna::DataBlock &cea, const anna::DataBlock & cer) throw() {
1048 // Default CEA implementation:
1050 // 'Capabilities-Exchange-Answer' (257,answer)
1051 // {Result-Code}...................................(268,0)
1052 // {Origin-Host}...................................(264,0)
1053 // {Origin-Realm}..................................(296,0)
1054 // 1*{Host-IP-Address}...............................(257,0)
1055 // {Vendor-Id}.....................................(266,0)
1056 // {Product-Name}..................................(269,0)
1057 // [Origin-State-Id]...............................(278,0)
1058 // [Error-Message].................................(281,0)
1059 // *[Failed-AVP]....................................(279,0)
1060 // *[Supported-Vendor-Id]...........................(265,0)
1061 // *[Auth-Application-Id]...........................(258,0)
1062 // *[Inband-Security-Id]............................(299,0)
1063 // *[Acct-Application-Id]...........................(259,0)
1064 // [Vendor-Specific-Application-Id]................(260,0)
1065 // [Firmware-Revision].............................(267,0)
1066 // *[AVP]...........................................(0,0)
1068 anna::diameter::codec::Message diameterCEA(getBaseProtocolCodecEngine());
1069 anna::diameter::codec::Avp avpRC(getBaseProtocolCodecEngine());
1070 anna::diameter::codec::Avp avpOH(getBaseProtocolCodecEngine());
1071 anna::diameter::codec::Avp avpOR(getBaseProtocolCodecEngine());
1073 diameterCEA.setId(anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Answer);
1074 diameterCEA.setVersion(1);
1075 diameterCEA.setApplicationId(codec::functions::getApplicationId(cer));
1076 diameterCEA.setHopByHop(codec::functions::getHopByHop(cer));
1077 diameterCEA.setEndToEnd(codec::functions::getEndToEnd(cer));
1079 avpRC.setId(anna::diameter::helpers::base::AVPID__Result_Code);
1080 avpRC.setMandatoryBit();
1081 avpRC.getUnsigned32()->setValue(helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS); // re-implementations could analyze CER to accept or not
1083 avpOH.setId(anna::diameter::helpers::base::AVPID__Origin_Host);
1084 avpOH.setMandatoryBit();
1085 avpOH.getDiameterIdentity()->fromPrintableString(a_host.c_str());
1087 avpOR.setId(anna::diameter::helpers::base::AVPID__Origin_Realm);
1088 avpOR.setMandatoryBit();
1089 avpOR.getDiameterIdentity()->fromPrintableString(a_realm.c_str());
1090 diameterCEA.addAvp(&avpRC);
1091 diameterCEA.addAvp(&avpOH);
1092 diameterCEA.addAvp(&avpOR);
1094 std::string hostIP = anna::functions::getHostnameIP(); // Address
1095 diameterCEA.addAvp(anna::diameter::helpers::base::AVPID__Host_IP_Address)->getAddress()->fromPrintableString(hostIP.c_str());
1097 int vendorId = anna::diameter::helpers::VENDORID__tgpp; // Unsigned32
1098 diameterCEA.addAvp(anna::diameter::helpers::base::AVPID__Vendor_Id)->getUnsigned32()->setValue(vendorId);
1100 std::string productName = "OCS Diameter Server"; // UTF8String
1101 diameterCEA.addAvp(anna::diameter::helpers::base::AVPID__Product_Name)->getUTF8String()->setValue(productName);
1103 cea = diameterCEA.code();
1104 } catch(anna::RuntimeException &ex) {
1105 std::string msg = ex.getText();
1106 msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages encoding (unable to answer with CEA)";
1107 anna::Logger::error(msg, ANNA_FILE_LOCATION);
1108 //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
1113 void Engine::readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) throw() {
1114 // Default DWA implementation:
1116 // 'Device-Watchdog-Answer' (280,answer)
1117 // {Result-Code}...................................(268,0)
1118 // {Origin-Host}...................................(264,0)
1119 // {Origin-Realm}..................................(296,0)
1120 // [Error-Message].................................(281,0)
1121 // *[Failed-AVP]....................................(279,0)
1122 // [Origin-State-Id]...............................(278,0)
1124 anna::diameter::codec::Message diameterDWA(getBaseProtocolCodecEngine());
1125 anna::diameter::codec::Avp avpRC(getBaseProtocolCodecEngine());
1126 anna::diameter::codec::Avp avpOH(getBaseProtocolCodecEngine());
1127 anna::diameter::codec::Avp avpOR(getBaseProtocolCodecEngine());
1129 diameterDWA.setId(anna::diameter::helpers::base::COMMANDID__Device_Watchdog_Answer);
1130 diameterDWA.setVersion(1);
1131 diameterDWA.setApplicationId(codec::functions::getApplicationId(dwr));
1132 diameterDWA.setHopByHop(codec::functions::getHopByHop(dwr));
1133 diameterDWA.setEndToEnd(codec::functions::getEndToEnd(dwr));
1135 avpRC.setId(anna::diameter::helpers::base::AVPID__Result_Code);
1136 avpRC.setMandatoryBit();
1137 avpRC.getUnsigned32()->setValue(helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS);
1139 avpOH.setId(anna::diameter::helpers::base::AVPID__Origin_Host);
1140 avpOH.setMandatoryBit();
1141 avpOH.getDiameterIdentity()->fromPrintableString(a_host.c_str());
1143 avpOR.setId(anna::diameter::helpers::base::AVPID__Origin_Realm);
1144 avpOR.setMandatoryBit();
1145 avpOR.getDiameterIdentity()->fromPrintableString(a_realm.c_str());
1146 diameterDWA.addAvp(&avpRC);
1147 diameterDWA.addAvp(&avpOH);
1148 diameterDWA.addAvp(&avpOR);
1150 dwa = diameterDWA.code();
1151 } catch(anna::RuntimeException &ex) {
1152 std::string msg = ex.getText();
1153 msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages encoding (unable to answer with DWA)";
1154 anna::Logger::error(msg, ANNA_FILE_LOCATION);
1155 //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
1159 void Engine::resetStatistics() throw() {
1160 for(server_iterator it = server_begin(), maxii = server_end(); it != maxii; it ++)
1161 server(it)->resetStatistics();
1163 for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
1164 localServer(it)->resetStatistics();
1167 void Engine::do_initialize() throw(RuntimeException) {
1168 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "do_initialize", ANNA_FILE_LOCATION));
1169 LOGDEBUG(anna::Logger::debug("Nothing special done on component initialization", ANNA_FILE_LOCATION));
1172 void Engine::lazyInitialize() throw(RuntimeException) {
1173 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "lazyInitialize", ANNA_FILE_LOCATION));
1174 anna::app::Component::initialize(); // this will invoke do_initialize