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