Fix local server for multiple applications
[anna.git] / source / diameter.comm / Engine.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
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 //
7
8 // Standard
9 #include <stdlib.h> // rand()
10
11
12 #include <anna/diameter.comm/Engine.hpp>
13 #include <anna/core/tracing/Logger.hpp>
14 #include <anna/core/tracing/TraceMethod.hpp>
15 #include <anna/xml/Node.hpp>
16 #include <anna/comm/Network.hpp>
17 #include <anna/comm/Host.hpp>
18 #include <anna/comm/ClientSocket.hpp>
19 #include <anna/diameter.comm/Transport.hpp>
20 #include <anna/diameter.comm/Engine.hpp>
21 #include <anna/diameter.comm/Entity.hpp>
22 #include <anna/diameter.comm/Server.hpp>
23 #include <anna/diameter.comm/ClientSession.hpp>
24 #include <anna/diameter.comm/LocalServer.hpp>
25 #include <anna/core/functions.hpp>
26 #include <anna/diameter/internal/sccs.hpp>
27 #include <anna/diameter.comm/OamModule.hpp>
28 #include <anna/diameter/codec/functions.hpp>
29 #include <anna/diameter/helpers/base/functions.hpp>
30 #include <anna/diameter/helpers/helpers.hpp>
31 #include <anna/diameter/codec/Message.hpp>
32 #include <anna/diameter/codec/Avp.hpp>
33 #include <anna/diameter.comm/Response.hpp>
34
35 // STD
36 #include <map>
37
38 using namespace std;
39 using namespace anna::diameter;
40
41
42 namespace anna {
43   namespace diameter {
44     namespace stack {
45       class Dictionary;
46     }
47   }
48 }
49
50 comm::Engine::Engine(const char *className, const stack::Dictionary *baseProtocolDictionary) :
51   anna::app::Component(className),
52   a_autoBind(true),
53   a_availableForEntities(false),
54   a_availableForLocalServers(false),
55   a_client_cer(true),
56   a_client_dwr(true),
57 //      a_cea(true),
58 //      a_dwa(true),
59   a_watchdogPeriod(ClientSession::DefaultWatchdogPeriod),
60   a_maxConnectionDelay(anna::comm::ClientSocket::DefaultMaxConnectionDelay /* 200 ms*/),
61   a_numberOfClientSessionsPerServer(1),
62   a_baseProtocolCodecEngine((std::string("baseProtocolCodecEngine_for_") + std::string(className)).c_str(), baseProtocolDictionary)
63 {
64   anna::diameter::sccs::activate();
65   a_originRealm = anna::functions::getDomainname();
66   a_originHost = anna::functions::getHostname();
67   a_ceaPathfile = "";
68
69   // Internal base protocol codec engine:
70   a_baseProtocolCodecEngine.setValidationMode(anna::diameter::codec::Engine::ValidationMode::Always); // default was: after decoding
71 }
72
73
74 void comm::Engine::assertBaseProtocolHealth() const noexcept(false) {
75   if (!getBaseProtocolCodecEngine()->getDictionary())
76     throw anna::RuntimeException("Invalid diameter::comm::Engine object: base protocol dictionary provided on constructor was NULL", ANNA_FILE_LOCATION);
77   // it would be interesting to check and identify certain base protocol elements in the dictionary ...
78   //  but these things will be checked in runtime and will fail if they should.
79 }
80
81
82 comm::Server* comm::Engine::allocateServer() { return a_serversRecycler.create(); }
83 void comm::Engine::releaseServer(Server *server) { a_serversRecycler.release(server); }
84 comm::ClientSession* comm::Engine::allocateClientSession() { return a_clientSessionsRecycler.create(); }
85 void comm::Engine::releaseClientSession(ClientSession *clientSession) { a_clientSessionsRecycler.release(clientSession); }
86
87 void comm::Engine::setClientCER(const anna::DataBlock & cer) noexcept(false) {
88   if(codec::functions::getCommandId(cer) != helpers::base::COMMANDID__Capabilities_Exchange_Request) {
89     throw anna::RuntimeException("The message provided as 'CER' is not a Capabilities-Exchange-Request", ANNA_FILE_LOCATION);
90   }
91
92   a_client_cer = cer;
93 }
94
95 void comm::Engine::setClientDWR(const anna::DataBlock & dwr) noexcept(false) {
96   if(codec::functions::getCommandId(dwr) != helpers::base::COMMANDID__Device_Watchdog_Request) {
97     throw anna::RuntimeException("The message provided as 'DWR' is not a Device-Watchdog-Request", ANNA_FILE_LOCATION);
98   }
99
100   a_client_dwr = dwr;
101 }
102
103 void comm::Engine::setClientCER(const std::string & cerPathfile) noexcept(false) {
104
105   // Check for base protocol codec engine health:
106   assertBaseProtocolHealth();
107
108   anna::diameter::codec::Message diameterCER(getBaseProtocolCodecEngine());
109   try {
110     diameterCER.loadXMLFile(cerPathfile);
111   } catch(anna::RuntimeException &ex) {
112     anna::Logger::error("CER file not found or unable to parse. Nothing done !", ANNA_FILE_LOCATION);
113     return;
114   }
115
116   // Assignment for internal encoded version:
117   setClientCER(diameterCER.code());
118 }
119
120 void comm::Engine::setClientDWR(const std::string & dwrPathfile) noexcept(false) {
121
122   // Check for base protocol codec engine health:
123   assertBaseProtocolHealth();
124
125   anna::diameter::codec::Message diameterDWR(getBaseProtocolCodecEngine());
126   std::string OH = getOriginHostName();
127   std::string OR = getOriginRealmName();
128
129   if (!dwrPathfile.empty()) {
130     try {
131       diameterDWR.loadXMLFile(dwrPathfile);
132     } catch(anna::RuntimeException &ex) {
133       anna::Logger::error("DWR file not found or unable to parse. Nothing done !", ANNA_FILE_LOCATION);
134       return;
135     }
136   }
137
138   // DEFAULT VERSION:
139   // Build DWR
140   //   <DWR>  ::= < Diameter Header: 280, REQ >
141   //              { Origin-Host }
142   //              { Origin-Realm }
143   diameterDWR.setId(anna::diameter::helpers::base::COMMANDID__Device_Watchdog_Request);
144   diameterDWR.setApplicationId(0); // base protocol
145   diameterDWR.addAvp(anna::diameter::helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->setValue(OH);
146   diameterDWR.addAvp(anna::diameter::helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->setValue(OR);
147
148   // Assignment for internal encoded version:
149   setClientDWR(diameterDWR.code());
150 }
151
152 void comm::Engine::setClientCER(const anna::U32 &applicationId) noexcept(false) {
153
154   // Check for base protocol codec engine health:
155   assertBaseProtocolHealth();
156
157   // Build CER
158   //   <CER> ::= < Diameter Header: 257, REQ >
159   //             { Origin-Host } 264 diameterIdentity
160   //             { Origin-Realm } 296 idem
161   //          1* { Host-IP-Address } 257, address
162   //             { Vendor-Id } 266 Unsigned32
163   //             { Product-Name } 269 UTF8String
164   //             [Origin-State-Id] 278 Unsigned32
165   //           * [ Supported-Vendor-Id ]  265 Unsigned32
166   //           * [ Auth-Application-Id ] 258 Unsigned32
167   //           * [Acct-Application-Id]  259 Unsigned32
168   anna::diameter::codec::Message diameterCER(getBaseProtocolCodecEngine());
169   std::string OH = getOriginHostName();
170   std::string OR = getOriginRealmName();
171   std::string hostIP = anna::functions::getHostnameIP(); // Address
172   int vendorId = anna::diameter::helpers::VENDORID__tgpp; // Unsigned32
173   std::string productName = "ANNA Diameter Client"; // UTF8String
174
175   diameterCER.setId(anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request);
176   diameterCER.setApplicationId(0); // base protocol
177   diameterCER.addAvp(anna::diameter::helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->setValue(OH);
178   diameterCER.addAvp(anna::diameter::helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->setValue(OR);
179   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>"
180   diameterCER.addAvp(anna::diameter::helpers::base::AVPID__Vendor_Id)->getUnsigned32()->setValue(vendorId);
181   diameterCER.addAvp(anna::diameter::helpers::base::AVPID__Product_Name)->getUTF8String()->setValue(productName);
182   diameterCER.addAvp(anna::diameter::helpers::base::AVPID__Auth_Application_Id)->getUnsigned32()->setValue(applicationId);
183
184   // Assignment for internal encoded versions:
185   setClientCER(diameterCER.code());
186 }
187
188 void comm::Engine::setWatchdogPeriod(const anna::Millisecond & wp) noexcept(false) {
189   if(wp < ClientSession::DefaultWatchdogPeriod) {
190     throw anna::RuntimeException(anna::functions::asString("Please set watchdog period over %s", ClientSession::DefaultWatchdogPeriod.asString().c_str()), ANNA_FILE_LOCATION);
191   }
192
193   a_watchdogPeriod = wp;
194 }
195
196 void comm::Engine::checkEntityCollision(const socket_v &v) noexcept(false) {
197   socket_v::const_iterator it;
198   socket_v::const_iterator it_min(v.begin());
199   socket_v::const_iterator it_max(v.end());
200
201   for(it = it_min; it != it_max; it++) {
202     server_iterator ii = server_find(*it);
203
204     if(ii != server_end())
205       throw anna::RuntimeException("diameter::comm::Engine::checkEntityCollision: Server is already reserved by a former created entity. Use another", ANNA_FILE_LOCATION);
206   }
207
208   // Check repetitions:
209   std::map < socket_t, int/*dummy*/ > auxMap;
210
211   for(it = it_min; it != it_max; it++) auxMap[(*it)] = 0;
212
213   if(auxMap.size() != v.size())
214     throw anna::RuntimeException("diameter::comm::Engine::checkEntityCollision: Provided addresses list (sockets) must have all items different", ANNA_FILE_LOCATION);
215 }
216
217 comm::Entity* comm::Engine::createEntity(const socket_v & socketList, const std::string &description)
218 noexcept(false) {
219   Entity* result(NULL);
220   anna::Guard guard(this, "anna::diameter::comm::Engine::createEntity");
221
222   if(socketList.size() == 0)
223     throw anna::RuntimeException("diameter::comm::Engine::createEntity Address/Port server list provided is empty", ANNA_FILE_LOCATION);
224
225   // Proteccion antes de reservar memoria para una entidad (allocateEntity):
226   checkEntityCollision(socketList);
227
228   if((result = allocateEntity()) == NULL)
229     throw anna::RuntimeException("diameter::comm::Engine::allocateEntity returns NULL (perhaps virtual method was not implemented)", ANNA_FILE_LOCATION);
230
231   // Initialize:
232   result->initialize(); // warning: recycler does not initialize its objects and at least, is important to empty servers vector.
233   // Assignments (it could be done at allocate):
234   result->setEngine(this); // lo podia haber asignado en el allocateEntity (no importa)
235   result->setMaxServers(socketList.size());
236   result->setDescription(description);
237   entity_key key(getEntityKey(socketList));
238   result->a_socketListLiteral = key;
239   // Create associated servers:
240   socket_v::const_iterator it;
241   socket_v::const_iterator it_min(socketList.begin());
242   socket_v::const_iterator it_max(socketList.end());
243   int count = 1;
244
245   for(it = it_min; it != it_max; it++) {
246     result->addServer(*it);
247
248     if(count == 1) result->a_primarySocketLiteral = anna::functions::socketLiteralAsString((*it).first, (*it).second);
249
250     if(count == 2) result->a_secondarySocketLiteral = anna::functions::socketLiteralAsString((*it).first, (*it).second);
251
252     count++;
253   }
254
255   a_entities.insert(entity_value_type(key, result));
256   LOGDEBUG(
257     string msg("diameter::comm::Engine::createEntity | ");
258     msg += result->asString();
259     msg += anna::functions::asText(" | AutoBind: ", a_autoBind);
260     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
261   );
262   return result;
263 }
264
265
266 comm::LocalServer *comm::Engine::createLocalServer(const std::string & addr, int port, int maxConnections, const anna::Millisecond & allowedInactivityTime, int category, const std::string & description)
267 noexcept(false) {
268   LocalServer* result(NULL);
269   anna::Guard guard(this, "anna::diameter::comm::Engine::createLocalServer");
270   // Proteccion antes de reservar memoria para un LocalServer
271   socket_t key(addr, port);
272
273   if(a_localServers.find(key) != a_localServers.end()) {
274     throw anna::RuntimeException("LocalServer is already reserved by a former created access point. Cannot create again", ANNA_FILE_LOCATION);
275   }
276
277   if((result = allocateLocalServer()) == NULL)
278     throw anna::RuntimeException("diameter::comm::Engine::allocateLocalServer returns NULL (perhaps virtual method was not implemented)", ANNA_FILE_LOCATION);
279
280   result->setEngine(this); // only to refresh availability
281   result->setKey(key);
282   result->setCategory(category);
283   result->setDescription(description);
284   result->setAllowedInactivityTime(allowedInactivityTime);
285   result->initializeStatisticResources();
286 // Los saco con metodos virtuales readXXX del motor:
287 //   if ((a_cea.isEmpty()) || (a_dwa.isEmpty()))
288 //      throw anna::RuntimeException("Must define valid CEA and DWA messages by mean setCEAandDWA()", ANNA_FILE_LOCATION);
289   a_localServers.insert(localServer_value_type(key, result));
290   LOGDEBUG(
291     string msg("diameter::comm::Engine::createLocalServer | ");
292     msg += result->asString();
293     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
294   );
295 //   // Listen: (*)
296 //   /*if (a_autoListen) */result->enable(); // creates server socket
297   // ENABLE THE SERVER: if already open (other comm engine for another origin host in the same address), nothing done. But the reference to the server is doubled along 2 comm engines ...
298   result->setMaxConnections(maxConnections); // (*) this enables the listen port ... or not
299   return result;
300 }
301
302
303 comm::Entity* comm::Engine::createEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, const std::string &description)
304 noexcept(false) {
305   socket_v dualList;
306   dualList.push_back(socket_t(addr1, port1));
307   dualList.push_back(socket_t(addr2, port2));
308   return (createEntity(dualList, description));
309 }
310
311
312 comm::Server* comm::Engine::createServer(Entity *entity, const socket_t & socket)
313 noexcept(false) {
314   Server* result(NULL);
315   anna::Guard guard(this, "anna::diameter::comm::Engine::createServer");
316
317   if((result = allocateServer()) == NULL)
318     throw anna::RuntimeException("diameter::comm::Engine::allocateServer returns NULL", ANNA_FILE_LOCATION);
319
320   // Initialize:
321   result->initialize(); // warning: recycler does not initialize its objects and at least, is important to empty client-sessions vector.
322   // Assignments (it could be done at allocate):
323   result->a_parent = entity;
324   result->a_socket = socket;
325   result->setMaxClientSessions(a_numberOfClientSessionsPerServer /* engine */);
326   result->initializeStatisticResources();
327
328   result->a_engine = this;
329   for(int k = 0; k < a_numberOfClientSessionsPerServer; k++)
330     result->addClientSession(k);
331
332   a_servers.insert(server_value_type(socket, result));
333 //      LOGDEBUG( Lo comento, porque ya se tracea en el createEntity
334 //         string msg("diameter::comm::Engine::resolveServer | ");
335 //         msg += result->asString();
336 //         msg += anna::functions::asText(" | AutoBind: ", a_autoBind);
337 //         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
338 //      );
339   return result;
340 }
341
342
343 // Lohacemos privado
344 comm::ClientSession* comm::Engine::createClientSession(Server *server, int socketId)
345 noexcept(false) {
346   ClientSession* result(NULL);
347   anna::Guard guard(this, "anna::diameter::comm::Engine::createClientSession");
348
349   if((result = allocateClientSession()) == NULL)
350     throw anna::RuntimeException("diameter::comm::Engine::allocateClientSession returns NULL", ANNA_FILE_LOCATION);
351
352   // Initialize:
353   result->initialize(); // warning: recycler does not initialize its objects and at least...
354   // Assignments (it could be done at allocate):
355
356   if((a_client_cer.isEmpty()) || (a_client_dwr.isEmpty()))
357     throw anna::RuntimeException("Must define valid CER and DWR messages by mean setClientCER and setClientDWR()", ANNA_FILE_LOCATION);
358
359   result->a_cer.setBody(a_client_cer);
360   result->a_dwr.setBody(a_client_dwr);
361   result->setWatchdogPeriod(a_watchdogPeriod);
362   result->a_parent = server;
363   result->a_socketId = socketId;
364   result->initializeSequences(); // despues de asignar el server y el socketId (sequences are seed-based by mean exclusive hash)
365   result->a_engine = this;
366   clientSession_key key = ClientSession::getKey(server->getAddress(), server->getPort(), socketId);
367   a_clientSessions.insert(clientSession_value_type(key, result));
368 //      LOGDEBUG( Lo comento, porque ya se tracea en el createEntity
369 //         string msg("diameter::comm::Engine::createClientSession | ");
370 //         msg += result->asString();
371 //         msg += anna::functions::asText(" | AutoBind: ", a_autoBind);
372 //         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
373 //      );
374   // Creation:
375   anna::comm::Network& network = anna::comm::Network::instantiate();
376   result->a_server = network.resolveServer(server->getAddress().c_str(), server->getPort(), true /* autoRecovery */,
377                      result->a_receiverFactory, &anna::diameter::comm::Transport::getFactory(),
378                      anna::comm::Network::Port::Multiple, anna::comm::Network::DoConnect::No /* (*) */);
379   // Delay time on tcp connect:
380   result->a_server->setMaxConnectionDelay(a_maxConnectionDelay); // (*)
381
382   // Bind:
383   if(a_autoBind) result->bind();
384
385   return result;
386 }
387
388
389 bool comm::Engine::broadcastEntities(const Message* message) noexcept(false) {
390   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "broadcastEntities", ANNA_FILE_LOCATION));
391   bool allok = true;
392   bool ok;
393
394   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
395     try {
396       ok = entity(it)->broadcast(message);
397
398       if(!ok) allok = false;
399     } catch(anna::RuntimeException &ex) {
400       ex.trace();
401       allok = false;
402     }
403   }
404
405   return allok;
406 }
407
408 bool comm::Engine::broadcastLocalServers(const Message* message) noexcept(false) {
409   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "broadcastLocalServers", ANNA_FILE_LOCATION));
410   bool allok = true;
411   bool ok;
412
413   for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++) {
414     try {
415       ok = localServer(it)->broadcast(message);
416
417       if(!ok) allok = false;
418     } catch(anna::RuntimeException &ex) {
419       ex.trace();
420       allok = false;
421     }
422   }
423
424   return allok;
425 }
426
427 bool comm::Engine::bind() noexcept(false) {
428   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "bind", ANNA_FILE_LOCATION));
429   bool result = true; // all OK return
430
431   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
432     try {
433       entity(it)->bind();
434     } catch(anna::RuntimeException &ex) {
435       ex.trace();
436       result = false;
437     }
438   }
439
440   return result;
441 }
442
443 comm::ClientSession* comm::Engine::findClientSession(const std::string & addr, int port, int socketId, anna::Exception::Mode::_v emode)
444 noexcept(false) {
445   return findClientSession(ClientSession::getKey(addr, port, socketId), emode);
446 }
447
448 comm::ClientSession* comm::Engine::findClientSession(const std::string & key, anna::Exception::Mode::_v emode)
449 noexcept(false) {
450   anna::Guard guard(this, "anna::diameter::comm::Engine::findClientSession");
451   clientSession_iterator ii = clientSession_find(key);
452
453   if(ii != clientSession_end())
454     return clientSession(ii);
455
456   if(emode != anna::Exception::Mode::Ignore) {
457     string msg("diameter::comm::Engine::findClientSession | [addr:port|socketId] = ");
458     msg += key;
459     msg += " | ClientSession not found";
460     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
461
462     if(emode == anna::Exception::Mode::Throw)
463       throw ex;
464
465     ex.trace();
466   }
467
468   return NULL;
469 }
470
471
472 comm::Server* comm::Engine::findServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
473 noexcept(false) {
474   anna::Guard guard(this, "anna::diameter::comm::Engine::findServer");
475   server_iterator ii = server_find(server_key(addr, port));
476
477   if(ii != server_end())
478     return server(ii);
479
480   if(emode != anna::Exception::Mode::Ignore) {
481     string msg("diameter::comm::Engine::findServer | addr: ");
482     msg += addr;
483     msg += anna::functions::asText(" | port: ", port);
484     msg += " | Server not found";
485     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
486
487     if(emode == anna::Exception::Mode::Throw)
488       throw ex;
489
490     ex.trace();
491   }
492
493   return NULL;
494 }
495
496 comm::Entity* comm::Engine::findEntity(const socket_v & socketList, anna::Exception::Mode::_v emode)
497 noexcept(false) {
498   anna::Guard guard(this, "anna::diameter::comm::Engine::findEntity");
499   entity_key key(getEntityKey(socketList));
500   entity_iterator ii = entity_find(key);
501
502   if(ii != entity_end())
503     return entity(ii);
504
505   if(emode != anna::Exception::Mode::Ignore) {
506     string msg("diameter::comm::Engine::findEntity | socket list: ");
507     msg += key;
508     msg += " | Entity not found";
509     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
510
511     if(emode == anna::Exception::Mode::Throw)
512       throw ex;
513
514     ex.trace();
515   }
516
517   return NULL;
518 }
519
520 comm::Entity* comm::Engine::findEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, anna::Exception::Mode::_v emode)
521 noexcept(false) {
522   socket_v dualList;
523   dualList.push_back(socket_t(addr1, port1));
524   dualList.push_back(socket_t(addr2, port2));
525   return (findEntity(dualList, emode));
526 }
527
528
529 //Entity* Engine::findEntity(int category, anna::Exception::Mode::_v emode)
530 //noexcept(false) {
531 //
532 //   Entity *entity;
533 //
534 //   for (entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
535 //      entity = entity(it);
536 //      if (entity->getCategory() == category) return entity;
537 //   }
538 //
539 //   return NULL;
540 //}
541
542
543 comm::LocalServer* comm::Engine::findLocalServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
544 noexcept(false) {
545   anna::Guard guard(this, "anna::diameter::comm::Engine::findLocalServer");
546   socket_t key(addr, port);
547   localServer_iterator ii = localServer_find(key);
548
549   if(ii != localServer_end())
550     return localServer(ii);
551
552   if(emode != anna::Exception::Mode::Ignore) {
553     string msg("diameter::comm::Engine::findLocalServer | addr: ");
554     msg += addr;
555     msg += anna::functions::asText(" | port: ", port);
556     msg += " | LocalServer not found";
557     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
558
559     if(emode == anna::Exception::Mode::Throw)
560       throw ex;
561
562     ex.trace();
563   }
564
565   return NULL;
566 }
567
568
569 comm::ServerSession* comm::Engine::findServerSession(int socketId, anna::Exception::Mode::_v emode) noexcept(false) {
570   anna::Guard guard(this, "anna::diameter::comm::Engine::findServerSession");
571   ServerSession *result;
572
573   // Search at each local server:
574   for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++) {
575     result = localServer(it)->findServerSession(socketId, anna::Exception::Mode::Ignore);
576
577     if(result) return result;
578   }
579
580   if(emode != anna::Exception::Mode::Ignore) {
581     string msg("diameter::comm::Engine::findServerSession | socketId: ");
582     msg += anna::functions::asString(socketId);
583     msg += " | ServerSession not found";
584     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
585
586     if(emode == anna::Exception::Mode::Throw)
587       throw ex;
588
589     ex.trace();
590   }
591
592   return NULL;
593 }
594
595
596 void comm::Engine::closeClientSession(comm::ClientSession* clientSession, bool destroy)
597 noexcept(false) {
598   if(clientSession == NULL)
599     return;
600
601   LOGDEBUG(
602     string msg("diameter::comm::Engine::closeClientSession | ");
603     msg += clientSession->asString();
604     msg += " | Destroy: ";
605     msg += (destroy ? "yes" : "no");
606     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
607   );
608   anna::Guard guard(this, "anna::diameter::comm::Engine::closeClientSession");
609   clientSession_iterator ii = clientSession_find(clientSession->getKey());
610
611   if(ii == clientSession_end())
612     return;
613
614   try {
615     clientSession->setState(ClientSession::State::Closing);
616
617     if(destroy) clientSession->setAutoRecovery(false);
618
619     clientSession->unbind(destroy /* destroy needs to perform immediate close */);
620
621     if(!destroy) return;
622
623     releaseClientSession(clientSession);
624   } catch(anna::RuntimeException& ex) {
625     ex.trace();
626   }
627
628   a_clientSessions.erase(ii);
629 }
630
631
632
633
634 void comm::Engine::closeServer(comm::Server* server, bool destroy)
635 noexcept(false) {
636   if(server == NULL)
637     return;
638
639   LOGDEBUG(
640     string msg("diameter::comm::Engine::closeServer | ");
641     msg += server->asString();
642     msg += " | Destroy: ";
643     msg += (destroy ? "yes" : "no");
644     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
645   );
646   anna::Guard guard(this, "anna::diameter::comm::Engine::closeServer");
647   server_iterator ii = server_find(server->a_socket);
648
649   if(ii == server_end())
650     return;
651
652   try {
653     server->close(destroy);
654
655     if(!destroy) return;
656
657     releaseServer(server);
658   } catch(anna::RuntimeException& ex) {
659     ex.trace();
660   }
661
662   a_servers.erase(ii);
663 }
664
665
666 void comm::Engine::closeEntity(comm::Entity* entity, bool destroy)
667 noexcept(false) {
668   if(entity == NULL)
669     return;
670
671   LOGDEBUG(
672     string msg("diameter::comm::Engine::closeEntity | ");
673     msg += entity->asString();
674     msg += " | Destroy: ";
675     msg += (destroy ? "yes" : "no");
676     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
677   );
678   anna::Guard guard(this, "anna::diameter::comm::Engine::closeEntity");
679   entity_iterator ii = entity_find(entity->a_socketListLiteral);
680
681   if(ii == entity_end())
682     return;
683
684   try {
685     entity->close(destroy);
686
687     if(!destroy) return;
688
689     if(!entity->idle()) { entity->setDeprecated(true); return; }
690
691     releaseEntity(entity);
692   } catch(anna::RuntimeException& ex) {
693     ex.trace();
694   }
695
696   a_entities.erase(ii);
697 }
698
699
700
701 void comm::Engine::closeLocalServer(comm::LocalServer* localServer, bool destroy)
702 noexcept(false) {
703   if(localServer == NULL)
704     return;
705
706   LOGDEBUG(
707     string msg("diameter::comm::Engine::closeLocalServer | ");
708     msg += localServer->asString();
709     msg += " | Destroy: ";
710     msg += (destroy ? "yes" : "no");
711     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
712   );
713   anna::Guard guard(this, "anna::diameter::comm::Engine::closeLocalServer");
714   localServer_iterator ii = localServer_find(localServer->getKey());
715
716   if(ii == localServer_end())
717     return;
718
719   try {
720     localServer->close();
721
722     if(!destroy) return;
723
724     releaseLocalServer(localServer);
725   } catch(anna::RuntimeException& ex) {
726     ex.trace();
727   }
728
729   a_localServers.erase(ii);
730 }
731
732
733
734 void comm::Engine::closeEntities(bool destroy) noexcept(false) {
735   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "closeEntities", ANNA_FILE_LOCATION));
736   anna::Guard guard(this, "anna::diameter::comm::Engine::closeEntities");
737
738   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
739     closeEntity(entity(it), destroy);
740 }
741
742 void comm::Engine::closeLocalServers(bool destroy) noexcept(false) {
743   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "closeLocalServers", ANNA_FILE_LOCATION));
744   anna::Guard guard(this, "anna::diameter::comm::Engine::closeLocalServers");
745
746   for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
747     closeLocalServer(localServer(it), destroy);
748 }
749
750 void comm::Engine::eraseDeprecatedIdleEntities() {
751   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "eraseDeprecatedIdleEntities", ANNA_FILE_LOCATION));
752   Entity *et;
753
754   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
755     et = entity(it);
756
757     if(et->isDeprecated() && et->idle()) closeEntity(et, true /* destroy */);
758   }
759 }
760
761 int comm::Engine::getOTARequestsForEntities() const {
762   int result = 0;
763
764   for(const_entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
765     result += entity(it)->getOTARequests();
766
767   return result;
768 }
769
770 int comm::Engine::getOTARequestsForLocalServers() const {
771   int result = 0;
772
773   for(const_localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
774     result += localServer(it)->getOTARequests();
775
776   return result;
777 }
778
779
780 void comm::Engine::setOriginRealmName(const std::string & originRealmName) {
781   a_originRealm = ((originRealmName != "") ? originRealmName : anna::functions::getDomainname());
782 }
783
784
785 void comm::Engine::setOriginHostName(const std::string & originHostName) {
786   a_originHost = ((originHostName != "") ? originHostName : anna::functions::getHostname());
787 }
788
789
790
791 void comm::Engine::raiseAutoRecovery(bool autoRecovery) noexcept(false) {
792   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "raiseAutoRecovery", ANNA_FILE_LOCATION));
793
794   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
795     entity(it)->raiseAutoRecovery(autoRecovery);
796 }
797
798 void comm::Engine::do_stop()
799 {
800   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "do_stop", ANNA_FILE_LOCATION));
801   close(true /* destroy */);
802 }
803
804 std::string comm::Engine::asString(void) const {
805   std::string trace;
806   trace =  "\n================================";
807   trace += "\nDiameter comm Engine information";
808   trace += "\n================================";
809   trace += "\nAutoBind: ";
810   trace += a_autoBind ? "yes" : "no";
811   trace += "\nMaxConnectionDelay: ";
812   trace += a_maxConnectionDelay.asString();
813   trace += "\nAvailable for entities: ";
814   trace += a_availableForEntities ? "yes" : "no";
815   trace += "\nAvailable for local servers: ";
816   trace += a_availableForLocalServers ? "yes" : "no";
817   trace += "\nOTA requests: ";
818   trace += anna::functions::asString("%d%s", getOTARequests(), idle() ? " (idle)" : "");
819   trace += "\nOTA requests for entities: ";
820   trace += anna::functions::asString("%d%s", getOTARequestsForEntities(), idleForEntities() ? " (idle)" : "");
821   trace += "\nOTA requests for local servers: ";
822   trace += anna::functions::asString("%d%s", getOTARequestsForLocalServers(), idleForLocalServers() ? " (idle)" : "");
823   // Entities
824   trace += "\nNumber of entities: ";
825   trace += anna::functions::asString(a_entities.size());
826
827   for(const_entity_iterator it = entity_begin(); it != entity_end(); it++) {
828     trace += "\n";
829     trace += entity(it)->asString();
830   }
831
832   // Server sockets
833   trace += "\nNumber of LocalServers: ";
834   trace += anna::functions::asString(a_localServers.size());
835
836   for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++) {
837     trace += "\n";
838     trace += localServer(it)->asString();
839   }
840
841   return trace;
842 }
843
844
845 anna::xml::Node* comm::Engine::asXML(anna::xml::Node* parent) const
846 {
847   parent = anna::app::Component::asXML(parent);
848   anna::xml::Node* result = parent->createChild("diameter.comm.Engine");
849   result->createAttribute("AutoBind", a_autoBind ? "yes" : "no");
850   result->createAttribute("MaxConnectionDelay", a_maxConnectionDelay.asString());
851   result->createAttribute("AvailableForEntities", a_availableForEntities ? "yes" : "no");
852   result->createAttribute("AvailableForLocalServers", a_availableForLocalServers ? "yes" : "no");
853   result->createAttribute("OTArequests", anna::functions::asString("%d%s", getOTARequests(), idle() ? " (idle)" : ""));
854   result->createAttribute("OTArequestsForEntities", anna::functions::asString("%d%s", getOTARequestsForEntities(), idleForEntities() ? " (idle)" : ""));
855   result->createAttribute("OTArequestsForLocalServers", anna::functions::asString("%d%s", getOTARequestsForLocalServers(), idleForLocalServers() ? " (idle)" : ""));
856   result->createAttribute("NumberOfEntities", a_entities.size());
857   anna::xml::Node* entities = result->createChild("Engine.Entities");
858
859   for(const_entity_iterator it = entity_begin(); it != entity_end(); it++)
860     entity(it)->asXML(entities);
861
862   result->createAttribute("NumberOfLocalServers", a_localServers.size());
863   anna::xml::Node* localServers = result->createChild("Engine.LocalServers");
864
865   for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
866     localServer(it)->asXML(localServers);
867
868   // DRA Basics
869   // Aspect:
870   //  <Engine.RemoteRealm Name="afNodeHostRealm.com">
871   //     <Engine.RemoteRealmHost Name="afNodeHostname.afNodeHostRealm.com" ServerSession="localhost:3868|ServerSessionId:4"/>
872   //  </Engine.RemoteRealm>
873   //  <Engine.RemoteRealm Name="ggsnNodeHostRealm.com">
874   //     <Engine.RemoteRealmHost Name="ggsnNodeHostname.ggsnNodeHostRealm.com" ServerSession="localhost:3868|ServerSessionId:6"/>
875   //  </Engine.RemoteRealm>
876   for (dr_dh_server_sessions_it_t drit = a_dr_dh_server_sessions.begin(); drit != a_dr_dh_server_sessions.end(); drit++) {
877     anna::xml::Node* remoteRealm = result->createChild("Engine.RemoteRealm");
878     remoteRealm->createAttribute("Name", drit->first);
879     dh_server_sessions_map_t *dhServerSessions = (dh_server_sessions_map_t *)&(drit->second);
880     for (dh_server_sessions_it_t dhit = dhServerSessions->begin(); dhit != dhServerSessions->end(); dhit++) {
881       anna::xml::Node* remoteRealmHost = remoteRealm->createChild("Engine.RemoteRealmHost");
882       remoteRealmHost->createAttribute("Name", dhit->first);
883       server_sessions_vector_t *serverSessions = (server_sessions_vector_t *)&(dhit->second);
884       for (server_sessions_it_t ssit = serverSessions->begin(); ssit != serverSessions->end(); ssit++) {
885         std::string socket = anna::functions::socketLiteralAsString((*ssit)->getAddress(), (*ssit)->getPort());
886         std::string ss_desc = socket + anna::functions::asString("|ServerSessionId:%d", (*ssit)->getSocketId());
887         remoteRealmHost->createAttribute("ServerSession", ss_desc);
888       }
889     }
890   }
891
892   return result;
893 }
894
895 comm::Engine::clientSession_iterator comm::Engine::clientSession_find(const clientSession_key &key) {
896   return a_clientSessions.find(key);
897 }
898
899 comm::Engine::server_iterator comm::Engine::server_find(const server_key &key) {
900   return a_servers.find(key);
901 }
902
903 comm::Engine::entity_iterator comm::Engine::entity_find(const entity_key &key) {
904   return a_entities.find(key);
905 }
906
907 comm::Engine::localServer_iterator comm::Engine::localServer_find(const socket_t &key) {
908   return a_localServers.find(key);
909 }
910
911 comm::Engine::entity_key comm::Engine::getEntityKey(const std::string & addr1, int port1, const std::string & addr2, int port2) const {
912   socket_v dualList;
913   dualList.push_back(socket_t(addr1, port1));
914   dualList.push_back(socket_t(addr2, port2));
915   return (getEntityKey(dualList));
916 }
917
918 comm::Engine::entity_key comm::Engine::getEntityKey(const socket_v &v) const {
919   std::string result;
920   socket_v::const_iterator it;
921   socket_v::const_iterator it_min(v.begin());
922   socket_v::const_iterator it_max(v.end());
923
924   for(it = it_min; it != it_max; it++) {
925     result += anna::functions::socketLiteralAsString((*it).first, (*it).second);
926     result += " ";
927   }
928
929   result.erase(result.size() - 1, 1);  // remove last space
930   return result;
931 }
932
933
934 void comm::Engine::availabilityLostForEntities() {
935   a_availableForEntities = false;
936   LOGDEBUG(
937     std::string msg = "diameter::comm::Engine { Origin-Realm: ";
938     msg += getOriginRealmName();
939     msg += " | Origin-Host: ";
940     msg += getOriginHostName();
941     msg += " } has lost its availability for entities";
942     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
943   );
944   // OAM
945   OamModule &oamModule = OamModule::instantiate();
946   oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverEntitiesForEngineWithClassName__s__, getClassName());
947   oamModule.count(OamModule::Counter::LostAvailabilityOverEngineForEntities);
948 }
949
950
951 void comm::Engine::availabilityRecoveredForEntities() {
952   a_availableForEntities = true;
953   LOGDEBUG(
954     std::string msg = "diameter::comm::Engine { Origin-Realm: ";
955     msg += getOriginRealmName();
956     msg += " | Origin-Host: ";
957     msg += getOriginHostName();
958     msg += " } has recovered its availability for entities";
959     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
960   );
961   // OAM
962   OamModule &oamModule = OamModule::instantiate();
963   oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverEntitiesForEngineWithClassName__s__, getClassName());
964   oamModule.count(OamModule::Counter::RecoveredAvailabilityOverEngineForEntities);
965 }
966
967
968 void comm::Engine::availabilityLostForLocalServers() {
969   a_availableForLocalServers = false;
970   LOGDEBUG(
971     std::string msg = "diameter::comm::Engine { Origin-Realm: ";
972     msg += getOriginRealmName();
973     msg += " | Origin-Host: ";
974     msg += getOriginHostName();
975     msg += " } has lost its availability for local servers";
976     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
977   );
978   // OAM
979   OamModule &oamModule = OamModule::instantiate();
980   oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverLocalServersForEngineWithClassName__s__, getClassName());
981   oamModule.count(OamModule::Counter::LostAvailabilityOverEngineForLocalServers);
982 }
983
984
985 void comm::Engine::availabilityRecoveredForLocalServers() {
986   a_availableForLocalServers = true;
987   LOGDEBUG(
988     std::string msg = "diameter::comm::Engine { Origin-Realm: ";
989     msg += getOriginRealmName();
990     msg += " | Origin-Host: ";
991     msg += getOriginHostName();
992     msg += " } has recovered its availability for local servers";
993     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
994   );
995   // OAM
996   OamModule &oamModule = OamModule::instantiate();
997   oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverLocalServersForEngineWithClassName__s__, getClassName());
998   oamModule.count(OamModule::Counter::RecoveredAvailabilityOverEngineForLocalServers);
999 }
1000
1001
1002 bool comm::Engine::refreshAvailabilityForEntities() {
1003   // Here available
1004   if(a_availableForEntities) {  // check not-bound state for all client-sessions:
1005     bool isolate = true;
1006
1007     for(const_entity_iterator it = entity_begin(); it != entity_end(); it++)
1008       if(entity(it)->isAvailable()) { isolate = false; break; }
1009
1010     if(isolate) {
1011       availabilityLostForEntities();
1012       return true;
1013     }
1014
1015     return false;
1016   }
1017
1018   // Here not available
1019   for(const_entity_iterator it = entity_begin(); it != entity_end(); it++)
1020     if(entity(it)->isAvailable()) {
1021       availabilityRecoveredForEntities();
1022       return true;
1023     }
1024
1025   return false;
1026 }
1027
1028 bool comm::Engine::refreshAvailabilityForLocalServers() {
1029   // Here available
1030   if(a_availableForLocalServers) {  // check not-bound state for all client-sessions:
1031     bool isolate = true;
1032
1033     for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
1034       if(localServer(it)->isAvailable()) { isolate = false; break; }
1035
1036     if(isolate) {
1037       availabilityLostForLocalServers();
1038       return true;
1039     }
1040
1041     return false;
1042   }
1043
1044   // Here not available
1045   for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
1046     if(localServer(it)->isAvailable()) {
1047       availabilityRecoveredForLocalServers();
1048       return true;
1049     }
1050
1051   return false;
1052 }
1053
1054
1055 void comm::Engine::readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) const {
1056
1057   // Check for base protocol codec engine health:
1058   try {
1059     assertBaseProtocolHealth();
1060   }
1061   catch(anna::RuntimeException &ex) {
1062     ex.trace();
1063     return;
1064   }
1065
1066   // Default DPA implementation:
1067   //
1068   //   'Disconnect-Peer-Answer' (282,answer)
1069   //        {Result-Code}...................................(268,0)
1070   //        {Origin-Host}...................................(264,0)
1071   //        {Origin-Realm}..................................(296,0)
1072   //        [Error-Message].................................(281,0)
1073   //       *[Failed-AVP]....................................(279,0)
1074   try {
1075     anna::diameter::codec::Message diameterDPA(getBaseProtocolCodecEngine());
1076     anna::diameter::codec::Avp avpRC(getBaseProtocolCodecEngine());
1077     anna::diameter::codec::Avp avpOH(getBaseProtocolCodecEngine());
1078     anna::diameter::codec::Avp avpOR(getBaseProtocolCodecEngine());
1079     // Message header
1080     diameterDPA.setId(anna::diameter::helpers::base::COMMANDID__Disconnect_Peer_Answer);
1081     diameterDPA.setVersion(1);
1082     diameterDPA.setApplicationId(codec::functions::getApplicationId(dpr));
1083     diameterDPA.setHopByHop(codec::functions::getHopByHop(dpr));
1084     diameterDPA.setEndToEnd(codec::functions::getEndToEnd(dpr));
1085     // Result-Code
1086     avpRC.setId(anna::diameter::helpers::base::AVPID__Result_Code);
1087     avpRC.setMandatoryBit();
1088     avpRC.getUnsigned32()->setValue(helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS);
1089     // Origin-Host
1090     avpOH.setId(anna::diameter::helpers::base::AVPID__Origin_Host);
1091     avpOH.setMandatoryBit();
1092     avpOH.getDiameterIdentity()->fromPrintableString(a_originHost.c_str());
1093     // Origin-Realm
1094     avpOR.setId(anna::diameter::helpers::base::AVPID__Origin_Realm);
1095     avpOR.setMandatoryBit();
1096     avpOR.getDiameterIdentity()->fromPrintableString(a_originRealm.c_str());
1097     diameterDPA.addAvp(&avpRC);
1098     diameterDPA.addAvp(&avpOH);
1099     diameterDPA.addAvp(&avpOR);
1100     // Encode
1101     dpa = diameterDPA.code();
1102   } catch(anna::RuntimeException &ex) {
1103     std::string msg = ex.getText();
1104     msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages encoding (unable to answer with DPA)";
1105     anna::Logger::error(msg, ANNA_FILE_LOCATION);
1106     //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
1107   }
1108 }
1109
1110
1111 void comm::Engine::readCEA(anna::DataBlock &cea, const anna::DataBlock &cer) const {
1112
1113   // Check for base protocol codec engine health:
1114   assertBaseProtocolHealth();
1115
1116   if (getCEA() != "") {
1117     anna::diameter::codec::Message diameterCEA(getBaseProtocolCodecEngine());
1118
1119     try {
1120       diameterCEA.loadXMLFile(getCEA());
1121       diameterCEA.setHopByHop(anna::diameter::codec::functions::getHopByHop(cer));
1122       diameterCEA.setEndToEnd(anna::diameter::codec::functions::getEndToEnd(cer));
1123       cea = diameterCEA.code();
1124
1125     } catch(anna::RuntimeException &ex) {
1126       ex.trace();
1127       LOGWARNING(anna::Logger::warning("CEA file not found or unable to parse. Encoding harcoded default version ...", ANNA_FILE_LOCATION));
1128       //return anna::diameter::comm::Engine::readCEA(cea, cer);
1129       // will fail with empty cea
1130       }
1131
1132     return;
1133   }
1134
1135   // Default CEA implementation:
1136   //
1137   //   'Capabilities-Exchange-Answer' (257,answer)
1138   //        {Result-Code}...................................(268,0)
1139   //        {Origin-Host}...................................(264,0)
1140   //        {Origin-Realm}..................................(296,0)
1141   //      1*{Host-IP-Address}...............................(257,0)
1142   //        {Vendor-Id}.....................................(266,0)
1143   //        {Product-Name}..................................(269,0)
1144   //        [Origin-State-Id]...............................(278,0)
1145   //        [Error-Message].................................(281,0)
1146   //       *[Failed-AVP]....................................(279,0)
1147   //       *[Supported-Vendor-Id]...........................(265,0)
1148   //       *[Auth-Application-Id]...........................(258,0)
1149   //       *[Inband-Security-Id]............................(299,0)
1150   //       *[Acct-Application-Id]...........................(259,0)
1151   //        [Vendor-Specific-Application-Id]................(260,0)
1152   //        [Firmware-Revision].............................(267,0)
1153   //       *[AVP]...........................................(0,0)
1154   try {
1155     anna::diameter::codec::Message diameterCEA(getBaseProtocolCodecEngine());
1156     anna::diameter::codec::Avp avpRC(getBaseProtocolCodecEngine());
1157     anna::diameter::codec::Avp avpOH(getBaseProtocolCodecEngine());
1158     anna::diameter::codec::Avp avpOR(getBaseProtocolCodecEngine());
1159     // Message header
1160     diameterCEA.setId(anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Answer);
1161     diameterCEA.setVersion(1);
1162     diameterCEA.setApplicationId(codec::functions::getApplicationId(cer));
1163     diameterCEA.setHopByHop(codec::functions::getHopByHop(cer));
1164     diameterCEA.setEndToEnd(codec::functions::getEndToEnd(cer));
1165     // Result-Code
1166     avpRC.setId(anna::diameter::helpers::base::AVPID__Result_Code);
1167     avpRC.setMandatoryBit();
1168     avpRC.getUnsigned32()->setValue(helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS); // re-implementations could analyze CER to accept or not
1169     // Origin-Host
1170     avpOH.setId(anna::diameter::helpers::base::AVPID__Origin_Host);
1171     avpOH.setMandatoryBit();
1172     avpOH.getDiameterIdentity()->fromPrintableString(a_originHost.c_str());
1173     // Origin-Realm
1174     avpOR.setId(anna::diameter::helpers::base::AVPID__Origin_Realm);
1175     avpOR.setMandatoryBit();
1176     avpOR.getDiameterIdentity()->fromPrintableString(a_originRealm.c_str());
1177     diameterCEA.addAvp(&avpRC);
1178     diameterCEA.addAvp(&avpOH);
1179     diameterCEA.addAvp(&avpOR);
1180     // Host-IP-Address
1181     std::string hostIP = anna::functions::getHostnameIP(); // Address
1182     diameterCEA.addAvp(anna::diameter::helpers::base::AVPID__Host_IP_Address)->getAddress()->fromPrintableString(hostIP.c_str());
1183     // Vendor-Id
1184     int vendorId = anna::diameter::helpers::VENDORID__tgpp; // Unsigned32
1185     diameterCEA.addAvp(anna::diameter::helpers::base::AVPID__Vendor_Id)->getUnsigned32()->setValue(vendorId);
1186     // Product-Name
1187     std::string productName = "Diameter Server"; // UTF8String
1188     diameterCEA.addAvp(anna::diameter::helpers::base::AVPID__Product_Name)->getUTF8String()->setValue(productName);
1189     // Encode
1190     cea = diameterCEA.code();
1191   } catch(anna::RuntimeException &ex) {
1192     std::string msg = ex.getText();
1193     msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages encoding (unable to answer with CEA)";
1194     anna::Logger::error(msg, ANNA_FILE_LOCATION);
1195     //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
1196   }
1197 }
1198
1199 void comm::Engine::manageDrDhServerSession(ServerSession *ss, bool register_or_desregister) {
1200
1201   // Decode CER (TODO: use raw buffer helpers)
1202   std::string destinationRealm, destinationHost;
1203   codec::Message codecMsg(getBaseProtocolCodecEngine());
1204   try {
1205     codecMsg.decode(a_client_cer);
1206     destinationRealm = codecMsg.getAvp(anna::diameter::helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->getValue();
1207     destinationHost = codecMsg.getAvp(anna::diameter::helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->getValue();
1208   }
1209   catch(anna::RuntimeException &ex) {
1210     ex.trace();
1211     return;
1212   }
1213
1214   dr_dh_server_sessions_nc_it_t drit = a_dr_dh_server_sessions.find(destinationRealm);
1215   if (drit != a_dr_dh_server_sessions.end()) { // found
1216     dh_server_sessions_map_t *dhServerSessions = (dh_server_sessions_map_t *)&(drit->second);
1217     dh_server_sessions_nc_it_t dhit = dhServerSessions->find(destinationHost);
1218     if (dhit != dhServerSessions->end()) { // found
1219       server_sessions_vector_t *serverSessions = (server_sessions_vector_t *)&(dhit->second);
1220       if (register_or_desregister) { // REGISTER
1221         serverSessions->push_back(ss);
1222       }
1223       else { // DESREGISTER
1224         // Sequential search the specific server session:
1225         for (server_sessions_nc_it_t ssit = serverSessions->begin(); ssit != serverSessions->end(); ssit++) {
1226           if ((*ssit)->getAddress() != ss->getAddress()) continue;
1227           if ((*ssit)->getPort() != ss->getPort()) continue;
1228           if ((*ssit)->getSocketId() != ss->getSocketId()) continue;
1229           serverSessions->erase(ssit);  // if it is the last server session removed in DR-DH path, the XML will show this tree empty
1230                                         // (it could be a hint for past registerings):
1231                                         //          <Engine.RemoteRealm Name="afNodeHostRealm.com">
1232                                         //             <Engine.RemoteRealmHost Name="afNodeHostname.afNodeHostRealm.com"/>
1233                                         //          </Engine.RemoteRealm>
1234                                         //          <Engine.RemoteRealm Name="ggsnNodeHostRealm.com">
1235                                         //             <Engine.RemoteRealmHost Name="ggsnNodeHostname.ggsnNodeHostRealm.com"/>
1236                                         //          </Engine.RemoteRealm>
1237
1238           break;
1239         }
1240       }
1241     }
1242     else {
1243       if (!register_or_desregister) return; // strange (host not found)
1244       server_sessions_vector_t ssVector;
1245       ssVector.push_back(ss);
1246       (*dhServerSessions)[destinationHost] = ssVector;
1247     }
1248   }
1249   else {
1250     if (!register_or_desregister) return; // strange (realm not found)
1251     server_sessions_vector_t ssVector;
1252     ssVector.push_back(ss);
1253     dh_server_sessions_map_t dhServerSessions;
1254     dhServerSessions[destinationHost] = ssVector;
1255     a_dr_dh_server_sessions[destinationRealm] = dhServerSessions;
1256   }
1257 }
1258
1259 void comm::Engine::readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) const {
1260
1261   // Check for base protocol codec engine health:
1262   assertBaseProtocolHealth();
1263
1264   // Default DWA implementation:
1265   //
1266   //   'Device-Watchdog-Answer' (280,answer)
1267   //        {Result-Code}...................................(268,0)
1268   //        {Origin-Host}...................................(264,0)
1269   //        {Origin-Realm}..................................(296,0)
1270   //        [Error-Message].................................(281,0)
1271   //       *[Failed-AVP]....................................(279,0)
1272   //        [Origin-State-Id]...............................(278,0)
1273   try {
1274     anna::diameter::codec::Message diameterDWA(getBaseProtocolCodecEngine());
1275     anna::diameter::codec::Avp avpRC(getBaseProtocolCodecEngine());
1276     anna::diameter::codec::Avp avpOH(getBaseProtocolCodecEngine());
1277     anna::diameter::codec::Avp avpOR(getBaseProtocolCodecEngine());
1278     // Message header
1279     diameterDWA.setId(anna::diameter::helpers::base::COMMANDID__Device_Watchdog_Answer);
1280     diameterDWA.setVersion(1);
1281     diameterDWA.setApplicationId(anna::diameter::codec::functions::getApplicationId(dwr));
1282     diameterDWA.setHopByHop(anna::diameter::codec::functions::getHopByHop(dwr));
1283     diameterDWA.setEndToEnd(anna::diameter::codec::functions::getEndToEnd(dwr));
1284     // Result-Code
1285     avpRC.setId(anna::diameter::helpers::base::AVPID__Result_Code);
1286     avpRC.setMandatoryBit();
1287     avpRC.getUnsigned32()->setValue(helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS);
1288     // Origin-Host
1289     avpOH.setId(anna::diameter::helpers::base::AVPID__Origin_Host);
1290     avpOH.setMandatoryBit();
1291     avpOH.getDiameterIdentity()->fromPrintableString(a_originHost.c_str());
1292     // Origin-Realm
1293     avpOR.setId(anna::diameter::helpers::base::AVPID__Origin_Realm);
1294     avpOR.setMandatoryBit();
1295     avpOR.getDiameterIdentity()->fromPrintableString(a_originRealm.c_str());
1296     diameterDWA.addAvp(&avpRC);
1297     diameterDWA.addAvp(&avpOH);
1298     diameterDWA.addAvp(&avpOR);
1299     // Encode
1300     dwa = diameterDWA.code();
1301   } catch(anna::RuntimeException &ex) {
1302     std::string msg = ex.getText();
1303     msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages encoding (unable to answer with DWA)";
1304     anna::Logger::error(msg, ANNA_FILE_LOCATION);
1305     //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
1306   }
1307 }
1308
1309 void comm::Engine::resetStatistics() {
1310   for(server_iterator it = server_begin(), maxii = server_end(); it != maxii; it ++)
1311     server(it)->resetStatistics();
1312
1313   for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
1314     localServer(it)->resetStatistics();
1315 }
1316
1317 void comm::Engine::do_initialize() noexcept(false) {
1318   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "do_initialize", ANNA_FILE_LOCATION));
1319   LOGDEBUG(anna::Logger::debug("Nothing special done on component initialization", ANNA_FILE_LOCATION));
1320 }
1321
1322 void comm::Engine::lazyInitialize() noexcept(false) {
1323   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "lazyInitialize", ANNA_FILE_LOCATION));
1324   anna::app::Component::initialize(); // this will invoke do_initialize
1325 }
1326
1327 // Not tested yet
1328 const comm::Response* comm::Engine::sendRealmHost(const Message* message, const std::string &destinationRealm, const std::string &destinationHost) noexcept(false) {
1329
1330   if (destinationRealm == "")
1331     throw anna::RuntimeException("Unable to resolve the destination: empty provided Destination-Realm name", ANNA_FILE_LOCATION);
1332
1333   // Get the server sessions which fulfill the restrictions:
1334   dr_dh_server_sessions_it_t drit = a_dr_dh_server_sessions.find(destinationRealm);
1335   if (drit == a_dr_dh_server_sessions.end())
1336     throw anna::RuntimeException(anna::functions::asString("Unable to resolve the destination: Destination-Realm name is not registered (no remote clients have been connected to '%s')", destinationRealm.c_str()), ANNA_FILE_LOCATION);
1337
1338   dh_server_sessions_map_t *dhServerSessions = (dh_server_sessions_map_t *)&(drit->second);
1339   // randomize between all server sessions for all hosts:
1340   dh_server_sessions_nc_it_t dhit;
1341   int hostsN = dhServerSessions->size();
1342   if (hostsN == 0) // avoids next division by cero (rand() % 0)
1343     throw anna::RuntimeException(anna::functions::asString("Unable to resolve the destination: neither Destination-Host currently connected to Destination-Realm '%s'", destinationRealm.c_str()), ANNA_FILE_LOCATION);
1344
1345   if (destinationHost == "") {
1346     // in this case, randomize the host:
1347     dhit = dhServerSessions->begin();
1348     int randomHostIndx = rand() % hostsN; // number between 0 and the number of hosts - 1
1349     std::advance (dhit, randomHostIndx);
1350   }
1351   else {
1352     dhit = dhServerSessions->find(destinationHost);
1353     if (dhit == dhServerSessions->end())
1354       throw anna::RuntimeException(anna::functions::asString("Unable to resolve the destination: Destination-Host '%s' is not registered for Destination-Realm '%s'", destinationHost.c_str(), destinationRealm.c_str()), ANNA_FILE_LOCATION);
1355   }
1356
1357   // Now, randomize the available server sessions:
1358   server_sessions_vector_t *serverSessions = (server_sessions_vector_t *)&(dhit->second);
1359   int serverSessionN = serverSessions->size();
1360   if (serverSessionN == 0) { // avoids next division by cero (rand() % 0)
1361     std::string aux = "";
1362     if (destinationHost != "") { aux = "to Destination-Host '"; aux += destinationHost; aux += "'"; }
1363     std::string msg = anna::functions::asString("Unable to resolve the destination: neither server session currently connected%s within Destination-Realm '%s'", aux.c_str(), destinationRealm.c_str());
1364     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
1365   }
1366
1367   server_sessions_nc_it_t ssit = serverSessions->begin();
1368   int randomServerSessionIndx = rand() % serverSessionN; // number between 0 and the number of server sessions - 1
1369   std::advance (ssit, randomServerSessionIndx);
1370   return (*ssit)->send(message);
1371 }