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