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