Changed LICENSE. Now referenced to web site and file on project root directory
[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
9 #include <anna/diameter.comm/Engine.hpp>
10
11 #include <anna/core/tracing/Logger.hpp>
12 #include <anna/core/tracing/TraceMethod.hpp>
13 #include <anna/xml/Node.hpp>
14 #include <anna/comm/Network.hpp>
15 #include <anna/comm/Host.hpp>
16 #include <anna/comm/ClientSocket.hpp>
17
18 #include <anna/diameter.comm/Transport.hpp>
19 #include <anna/diameter.comm/Engine.hpp>
20 #include <anna/diameter.comm/Entity.hpp>
21 #include <anna/diameter.comm/Server.hpp>
22 #include <anna/diameter.comm/ClientSession.hpp>
23 #include <anna/diameter.comm/LocalServer.hpp>
24 #include <anna/core/functions.hpp>
25 #include <anna/diameter/internal/sccs.hpp>
26 #include <anna/diameter.comm/OamModule.hpp>
27 #include <anna/diameter/codec/functions.hpp>
28 #include <anna/diameter/helpers/base/functions.hpp>
29 #include <anna/diameter/helpers/helpers.hpp>
30 #include <anna/diameter/codec/Message.hpp>
31 #include <anna/diameter/codec/Avp.hpp>
32
33 // STD
34 #include <map>
35
36 using namespace std;
37 using namespace anna::diameter::comm;
38
39
40 Engine::Engine() :
41   anna::app::Component(getClassName()),
42   a_autoBind(true),
43   a_availableForEntities(false),
44   a_availableForLocalServers(false),
45   a_cer(true),
46   a_dwr(true),
47 //      a_cea(true),
48 //      a_dwa(true),
49   a_watchdogPeriod(ClientSession::DefaultWatchdogPeriod),
50   a_maxConnectionDelay(anna::comm::ClientSocket::DefaultMaxConnectionDelay /* 200 ms*/),
51   a_numberOfClientSessionsPerServer(1),
52   a_freezeEndToEndOnSending(false) {
53   anna::diameter::sccs::activate();
54   a_realm = anna::functions::getDomainname();
55   a_host = anna::functions::getHostname();
56 }
57
58 Server* Engine::allocateServer() throw() { return a_serversRecycler.create(); }
59 void Engine::releaseServer(Server *server) throw() { a_serversRecycler.release(server); }
60 ClientSession* Engine::allocateClientSession() throw() { return a_clientSessionsRecycler.create(); }
61 void Engine::releaseClientSession(ClientSession *clientSession) throw() { a_clientSessionsRecycler.release(clientSession); }
62
63
64 void Engine::setCERandDWR(const anna::DataBlock & cer, const anna::DataBlock & dwr) throw(anna::RuntimeException) {
65   if(codec::functions::getCommandId(cer) != helpers::base::COMMANDID__Capabilities_Exchange_Request) {
66     throw anna::RuntimeException("The message provided as 'CER' is not a Capabilities-Exchange-Request", ANNA_FILE_LOCATION);
67   }
68
69   if(codec::functions::getCommandId(dwr) != helpers::base::COMMANDID__Device_Watchdog_Request) {
70     throw anna::RuntimeException("The message provided as 'DWR' is not a Device-Watchdog-Request", ANNA_FILE_LOCATION);
71   }
72
73   a_cer = cer;
74   a_dwr = dwr;
75 }
76
77 //void Engine::setCEAandDWA(const anna::DataBlock & cea, const anna::DataBlock & dwa) throw(anna::RuntimeException) {
78 //   if (codec::functions::getCommandId(cea) != helpers::base::COMMANDID__Capabilities_Exchange_Answer) {
79 //      throw anna::RuntimeException("The message provided as 'CEA' is not a Capabilities-Exchange-Answer", ANNA_FILE_LOCATION);
80 //   }
81 //
82 //   if (codec::functions::getCommandId(dwa) != helpers::base::COMMANDID__Device_Watchdog_Answer) {
83 //      throw anna::RuntimeException("The message provided as 'DWA' is not a Device-Watchdog-Answer", ANNA_FILE_LOCATION);
84 //   }
85 //
86 //   a_cea = cea;
87 //   a_dwa = dwa;
88 //}
89
90 void Engine::setWatchdogPeriod(const anna::Millisecond & wp) throw(anna::RuntimeException) {
91   if(wp < ClientSession::DefaultWatchdogPeriod) {
92     throw anna::RuntimeException(anna::functions::asString("Please set watchdog period over %s", ClientSession::DefaultWatchdogPeriod.asString().c_str()), ANNA_FILE_LOCATION);
93   }
94
95   a_watchdogPeriod = wp;
96 }
97
98 void Engine::checkEntityCollision(const socket_v &v) throw(anna::RuntimeException) {
99   socket_v::const_iterator it;
100   socket_v::const_iterator it_min(v.begin());
101   socket_v::const_iterator it_max(v.end());
102
103   for(it = it_min; it != it_max; it++) {
104     server_iterator ii = server_find(*it);
105
106     if(ii != server_end())
107       throw anna::RuntimeException("diameter::comm::Engine::checkEntityCollision: Server is already reserved by a former created entity. Use another", ANNA_FILE_LOCATION);
108   }
109
110   // Check repetitions:
111   std::map < socket_t, int/*dummy*/ > auxMap;
112
113   for(it = it_min; it != it_max; it++) auxMap[(*it)] = 0;
114
115   if(auxMap.size() != v.size())
116     throw anna::RuntimeException("diameter::comm::Engine::checkEntityCollision: Provided addresses list (sockets) must have all items different", ANNA_FILE_LOCATION);
117 }
118
119 Entity* Engine::createEntity(const socket_v & socketList, const std::string &description)
120 throw(anna::RuntimeException) {
121   Entity* result(NULL);
122   anna::Guard guard(this, "anna::diameter::comm::Engine::createEntity");
123
124   if(socketList.size() == 0)
125     throw anna::RuntimeException("diameter::comm::Engine::createEntity Address/Port server list provided is empty", ANNA_FILE_LOCATION);
126
127   // Proteccion antes de reservar memoria para una entidad (allocateEntity):
128   checkEntityCollision(socketList);
129
130   if((result = allocateEntity()) == NULL)
131     throw anna::RuntimeException("diameter::comm::Engine::allocateEntity returns NULL (perhaps virtual method was not implemented)", ANNA_FILE_LOCATION);
132
133   // Initialize:
134   result->initialize(); // warning: recycler does not initialize its objects and at least, is important to empty servers vector.
135   // Assignments (it could be done at allocate):
136   result->setEngine(this); // lo podia haber asignado en el allocateEntity (no importa)
137   result->setMaxServers(socketList.size());
138   result->setDescription(description);
139   entity_key key(getEntityKey(socketList));
140   result->a_socketListLiteral = key;
141   // Create associated servers:
142   socket_v::const_iterator it;
143   socket_v::const_iterator it_min(socketList.begin());
144   socket_v::const_iterator it_max(socketList.end());
145   int count = 1;
146
147   for(it = it_min; it != it_max; it++) {
148     result->addServer(*it);
149
150     if(count == 1) result->a_primarySocketLiteral = anna::functions::socketLiteralAsString((*it).first, (*it).second);
151
152     if(count == 2) result->a_secondarySocketLiteral = anna::functions::socketLiteralAsString((*it).first, (*it).second);
153
154     count++;
155   }
156
157   a_entities.insert(entity_value_type(key, result));
158   LOGDEBUG(
159     string msg("diameter::comm::Engine::createEntity | ");
160     msg += result->asString();
161     msg += anna::functions::asText(" | AutoBind: ", a_autoBind);
162     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
163   );
164   return result;
165 }
166
167
168 LocalServer *Engine::createLocalServer(const std::string & addr, int port, int maxConnections, const anna::Millisecond & allowedInactivityTime, int category, const std::string & description)
169 throw(anna::RuntimeException) {
170   LocalServer* result(NULL);
171   anna::Guard guard(this, "anna::diameter::comm::Engine::createLocalServer");
172   // Proteccion antes de reservar memoria para un LocalServer
173   socket_t key(addr, port);
174
175   if(a_localServers.find(key) != a_localServers.end())
176     throw anna::RuntimeException("LocalServer is already reserved by a former created access point. Cannot create again", ANNA_FILE_LOCATION);
177
178   if((result = allocateLocalServer()) == NULL)
179     throw anna::RuntimeException("diameter::comm::Engine::allocateLocalServer returns NULL (perhaps virtual method was not implemented)", ANNA_FILE_LOCATION);
180
181   result->setEngine(this); // lo podia haber asignado en el allocateLocalServer (no importa)
182   result->setKey(key);
183   result->setCategory(category);
184   result->setDescription(description);
185   result->setAllowedInactivityTime(allowedInactivityTime);
186   result->initializeStatisticConcepts();
187 // Los saco con metodos virtuales readXXX del motor:
188 //   if ((a_cea.isEmpty()) || (a_dwa.isEmpty()))
189 //      throw anna::RuntimeException("Must define valid CEA and DWA messages by mean setCEAandDWA()", ANNA_FILE_LOCATION);
190   a_localServers.insert(localServer_value_type(key, result));
191   LOGDEBUG(
192     string msg("diameter::comm::Engine::createLocalServer | ");
193     msg += result->asString();
194     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
195   );
196 //   // Listen: (*)
197 //   /*if (a_autoListen) */result->enable(); // creates server socket
198   result->setMaxConnections(maxConnections); // (*) this enables the listen port ... or not
199   return result;
200 }
201
202
203 Entity* Engine::createEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, const std::string &description)
204 throw(anna::RuntimeException) {
205   socket_v dualList;
206   dualList.push_back(socket_t(addr1, port1));
207   dualList.push_back(socket_t(addr2, port2));
208   return (createEntity(dualList, description));
209 }
210
211
212 Server* Engine::createServer(Entity *entity, const socket_t & socket)
213 throw(anna::RuntimeException) {
214   Server* result(NULL);
215   anna::Guard guard(this, "anna::diameter::comm::Engine::createServer");
216
217   if((result = allocateServer()) == NULL)
218     throw anna::RuntimeException("diameter::comm::Engine::allocateServer returns NULL", ANNA_FILE_LOCATION);
219
220   // Initialize:
221   result->initialize(); // warning: recycler does not initialize its objects and at least, is important to empty client-sessions vector.
222   // Assignments (it could be done at allocate):
223   result->a_parent = entity;
224   result->a_socket = socket;
225   result->setMaxClientSessions(a_numberOfClientSessionsPerServer /* engine */);
226   result->a_engine = this;
227   result->initializeStatisticConcepts();
228
229   for(int k = 0; k < a_numberOfClientSessionsPerServer; k++)
230     result->addClientSession(k);
231
232   a_servers.insert(server_value_type(socket, result));
233 //      LOGDEBUG( Lo comento, porque ya se tracea en el createEntity
234 //         string msg("diameter::comm::Engine::resolveServer | ");
235 //         msg += result->asString();
236 //         msg += anna::functions::asText(" | AutoBind: ", a_autoBind);
237 //         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
238 //      );
239   return result;
240 }
241
242
243 // Lohacemos privado
244 ClientSession* Engine::createClientSession(Server *server, int socketId)
245 throw(anna::RuntimeException) {
246   ClientSession* result(NULL);
247   anna::Guard guard(this, "anna::diameter::comm::Engine::createClientSession");
248
249   if((result = allocateClientSession()) == NULL)
250     throw anna::RuntimeException("diameter::comm::Engine::allocateClientSession returns NULL", ANNA_FILE_LOCATION);
251
252   // Initialize:
253   result->initialize(); // warning: recycler does not initialize its objects and at least...
254   // Assignments (it could be done at allocate):
255
256   if((a_cer.isEmpty()) || (a_dwr.isEmpty()))
257     throw anna::RuntimeException("Must define valid CER and DWR messages by mean setCERandDWR()", ANNA_FILE_LOCATION);
258
259   result->a_cer.setBody(a_cer);
260   result->a_dwr.setBody(a_dwr);
261   result->setWatchdogPeriod(a_watchdogPeriod);
262   result->a_parent = server;
263   result->a_socketId = socketId;
264   result->initializeSequences(); // después de asignar el server y el socketId (*)
265   // (*) 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)));
266   result->a_engine = this;
267   clientSession_key key = ClientSession::getKey(server->getAddress(), server->getPort(), socketId);
268   a_clientSessions.insert(clientSession_value_type(key, result));
269 //      LOGDEBUG( Lo comento, porque ya se tracea en el createEntity
270 //         string msg("diameter::comm::Engine::createClientSession | ");
271 //         msg += result->asString();
272 //         msg += anna::functions::asText(" | AutoBind: ", a_autoBind);
273 //         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
274 //      );
275   // Creation:
276   anna::comm::Network& network = anna::comm::Network::instantiate();
277   result->a_server = network.resolveServer(server->getAddress().c_str(), server->getPort(), true /* autoRecovery */,
278                      result->a_receiverFactory, &anna::diameter::comm::Transport::getFactory(),
279                      anna::comm::Network::Port::Multiple, anna::comm::Network::DoConnect::No /* (*) */);
280   // Delay time on tcp connect:
281   result->a_server->setMaxConnectionDelay(a_maxConnectionDelay); // (*)
282
283   // Bind:
284   if(a_autoBind) result->bind();
285
286   return result;
287 }
288
289
290 bool Engine::broadcastEntities(const Message* message) throw(anna::RuntimeException) {
291   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "broadcastEntities", ANNA_FILE_LOCATION));
292   bool allok = true;
293   bool ok;
294
295   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
296     try {
297       ok = entity(it)->broadcast(message);
298
299       if(!ok) allok = false;
300     } catch(anna::RuntimeException &ex) {
301       ex.trace();
302       allok = false;
303     }
304   }
305
306   return allok;
307 }
308
309 bool Engine::broadcastLocalServers(const Message* message) throw(anna::RuntimeException) {
310   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "broadcastLocalServers", ANNA_FILE_LOCATION));
311   bool allok = true;
312   bool ok;
313
314   for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++) {
315     try {
316       ok = localServer(it)->broadcast(message);
317
318       if(!ok) allok = false;
319     } catch(anna::RuntimeException &ex) {
320       ex.trace();
321       allok = false;
322     }
323   }
324
325   return allok;
326 }
327
328 bool Engine::bind() throw(anna::RuntimeException) {
329   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "bind", ANNA_FILE_LOCATION));
330   bool result = true; // all OK return
331
332   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
333     try {
334       entity(it)->bind();
335     } catch(anna::RuntimeException &ex) {
336       ex.trace();
337       result = false;
338     }
339   }
340
341   return result;
342 }
343
344 ClientSession* Engine::findClientSession(const std::string & addr, int port, int socketId, anna::Exception::Mode::_v emode)
345 throw(anna::RuntimeException) {
346   return findClientSession(ClientSession::getKey(addr, port, socketId), emode);
347 }
348
349 ClientSession* Engine::findClientSession(const std::string & key, anna::Exception::Mode::_v emode)
350 throw(anna::RuntimeException) {
351   anna::Guard guard(this, "anna::diameter::comm::Engine::findClientSession");
352   clientSession_iterator ii = clientSession_find(key);
353
354   if(ii != clientSession_end())
355     return clientSession(ii);
356
357   if(emode != anna::Exception::Mode::Ignore) {
358     string msg("diameter::comm::Engine::findClientSession | [addr:port|socketId] = ");
359     msg += key;
360     msg += " | ClientSession not found";
361     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
362
363     if(emode == anna::Exception::Mode::Throw)
364       throw ex;
365
366     ex.trace();
367   }
368
369   return NULL;
370 }
371
372
373 Server* Engine::findServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
374 throw(anna::RuntimeException) {
375   anna::Guard guard(this, "anna::diameter::comm::Engine::findServer");
376   server_iterator ii = server_find(server_key(addr, port));
377
378   if(ii != server_end())
379     return server(ii);
380
381   if(emode != anna::Exception::Mode::Ignore) {
382     string msg("diameter::comm::Engine::findServer | addr: ");
383     msg += addr;
384     msg += anna::functions::asText(" | port: ", port);
385     msg += " | Server not found";
386     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
387
388     if(emode == anna::Exception::Mode::Throw)
389       throw ex;
390
391     ex.trace();
392   }
393
394   return NULL;
395 }
396
397 Entity* Engine::findEntity(const socket_v & socketList, anna::Exception::Mode::_v emode)
398 throw(anna::RuntimeException) {
399   anna::Guard guard(this, "anna::diameter::comm::Engine::findEntity");
400   entity_key key(getEntityKey(socketList));
401   entity_iterator ii = entity_find(key);
402
403   if(ii != entity_end())
404     return entity(ii);
405
406   if(emode != anna::Exception::Mode::Ignore) {
407     string msg("diameter::comm::Engine::findEntity | socket list: ");
408     msg += key;
409     msg += " | Entity not found";
410     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
411
412     if(emode == anna::Exception::Mode::Throw)
413       throw ex;
414
415     ex.trace();
416   }
417
418   return NULL;
419 }
420
421 Entity* Engine::findEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, anna::Exception::Mode::_v emode)
422 throw(anna::RuntimeException) {
423   socket_v dualList;
424   dualList.push_back(socket_t(addr1, port1));
425   dualList.push_back(socket_t(addr2, port2));
426   return (findEntity(dualList, emode));
427 }
428
429
430 //Entity* Engine::findEntity(int category, anna::Exception::Mode::_v emode)
431 //throw(anna::RuntimeException) {
432 //
433 //   Entity *entity;
434 //
435 //   for (entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
436 //      entity = entity(it);
437 //      if (entity->getCategory() == category) return entity;
438 //   }
439 //
440 //   return NULL;
441 //}
442
443
444 LocalServer* Engine::findLocalServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
445 throw(anna::RuntimeException) {
446   anna::Guard guard(this, "anna::diameter::comm::Engine::findLocalServer");
447   socket_t key(addr, port);
448   localServer_iterator ii = localServer_find(key);
449
450   if(ii != localServer_end())
451     return localServer(ii);
452
453   if(emode != anna::Exception::Mode::Ignore) {
454     string msg("diameter::comm::Engine::findLocalServer | addr: ");
455     msg += addr;
456     msg += anna::functions::asText(" | port: ", port);
457     msg += " | LocalServer not found";
458     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
459
460     if(emode == anna::Exception::Mode::Throw)
461       throw ex;
462
463     ex.trace();
464   }
465
466   return NULL;
467 }
468
469
470 ServerSession* Engine::findServerSession(int socketId, anna::Exception::Mode::_v emode) throw(anna::RuntimeException) {
471   anna::Guard guard(this, "anna::diameter::comm::Engine::findServerSession");
472   ServerSession *result;
473
474   // Search at each local server:
475   for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++) {
476     result = localServer(it)->findServerSession(socketId, anna::Exception::Mode::Ignore);
477
478     if(result) return result;
479   }
480
481   if(emode != anna::Exception::Mode::Ignore) {
482     string msg("diameter::comm::Engine::findServerSession | socketId: ");
483     msg += anna::functions::asString(socketId);
484     msg += " | ServerSession not found";
485     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
486
487     if(emode == anna::Exception::Mode::Throw)
488       throw ex;
489
490     ex.trace();
491   }
492
493   return NULL;
494 }
495
496
497 void Engine::closeClientSession(ClientSession* clientSession, bool destroy)
498 throw(anna::RuntimeException) {
499   if(clientSession == NULL)
500     return;
501
502   LOGDEBUG(
503     string msg("diameter::comm::Engine::closeClientSession | ");
504     msg += clientSession->asString();
505     msg += " | Destroy: ";
506     msg += (destroy ? "yes" : "no");
507     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
508   );
509   anna::Guard guard(this, "anna::diameter::comm::Engine::closeClientSession");
510   clientSession_iterator ii = clientSession_find(clientSession->getKey());
511
512   if(ii == clientSession_end())
513     return;
514
515   try {
516     clientSession->setState(ClientSession::State::Closing);
517
518     if(destroy) clientSession->setAutoRecovery(false);
519
520     clientSession->unbind(destroy /* destroy needs to perform immediate close */);
521
522     if(!destroy) return;
523
524     releaseClientSession(clientSession);
525   } catch(anna::RuntimeException& ex) {
526     ex.trace();
527   }
528
529   a_clientSessions.erase(ii);
530 }
531
532
533
534
535 void Engine::closeServer(Server* server, bool destroy)
536 throw(anna::RuntimeException) {
537   if(server == NULL)
538     return;
539
540   LOGDEBUG(
541     string msg("diameter::comm::Engine::closeServer | ");
542     msg += server->asString();
543     msg += " | Destroy: ";
544     msg += (destroy ? "yes" : "no");
545     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
546   );
547   anna::Guard guard(this, "anna::diameter::comm::Engine::closeServer");
548   server_iterator ii = server_find(server->a_socket);
549
550   if(ii == server_end())
551     return;
552
553   try {
554     server->close(destroy);
555
556     if(!destroy) return;
557
558     releaseServer(server);
559   } catch(anna::RuntimeException& ex) {
560     ex.trace();
561   }
562
563   a_servers.erase(ii);
564 }
565
566
567 void Engine::closeEntity(Entity* entity, bool destroy)
568 throw(anna::RuntimeException) {
569   if(entity == NULL)
570     return;
571
572   LOGDEBUG(
573     string msg("diameter::comm::Engine::closeEntity | ");
574     msg += entity->asString();
575     msg += " | Destroy: ";
576     msg += (destroy ? "yes" : "no");
577     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
578   );
579   anna::Guard guard(this, "anna::diameter::comm::Engine::closeEntity");
580   entity_iterator ii = entity_find(entity->a_socketListLiteral);
581
582   if(ii == entity_end())
583     return;
584
585   try {
586     entity->close(destroy);
587
588     if(!destroy) return;
589
590     if(!entity->idle()) { entity->setDeprecated(true); return; }
591
592     releaseEntity(entity);
593   } catch(anna::RuntimeException& ex) {
594     ex.trace();
595   }
596
597   a_entities.erase(ii);
598 }
599
600
601
602 void Engine::closeLocalServer(LocalServer* localServer, bool destroy)
603 throw(anna::RuntimeException) {
604   if(localServer == NULL)
605     return;
606
607   LOGDEBUG(
608     string msg("diameter::comm::Engine::closeLocalServer | ");
609     msg += localServer->asString();
610     msg += " | Destroy: ";
611     msg += (destroy ? "yes" : "no");
612     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
613   );
614   anna::Guard guard(this, "anna::diameter::comm::Engine::closeLocalServer");
615   localServer_iterator ii = localServer_find(localServer->getKey());
616
617   if(ii == localServer_end())
618     return;
619
620   try {
621     localServer->close();
622
623     if(!destroy) return;
624
625     releaseLocalServer(localServer);
626   } catch(anna::RuntimeException& ex) {
627     ex.trace();
628   }
629
630   a_localServers.erase(ii);
631 }
632
633
634
635 void Engine::closeEntities(bool destroy) throw(anna::RuntimeException) {
636   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "closeEntities", ANNA_FILE_LOCATION));
637   anna::Guard guard(this, "anna::diameter::comm::Engine::closeEntities");
638
639   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
640     closeEntity(entity(it), destroy);
641 }
642
643 void Engine::closeLocalServers(bool destroy) throw(anna::RuntimeException) {
644   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "closeLocalServers", ANNA_FILE_LOCATION));
645   anna::Guard guard(this, "anna::diameter::comm::Engine::closeLocalServers");
646
647   for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
648     closeLocalServer(localServer(it), destroy);
649 }
650
651 void Engine::eraseDeprecatedIdleEntities() throw() {
652   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "eraseDeprecatedIdleEntities", ANNA_FILE_LOCATION));
653   Entity *et;
654
655   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
656     et = entity(it);
657
658     if(et->isDeprecated() && et->idle()) closeEntity(et, true /* destroy */);
659   }
660 }
661
662 int Engine::getOTARequestsForEntities() const throw() {
663   int result = 0;
664
665   for(const_entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
666     result += entity(it)->getOTARequests();
667
668   return result;
669 }
670
671 int Engine::getOTARequestsForLocalServers() const throw() {
672   int result = 0;
673
674   for(const_localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
675     result += localServer(it)->getOTARequests();
676
677   return result;
678 }
679
680
681 void Engine::setRealm(const std::string & name) throw() {
682   a_realm = ((name != "") ? name : anna::functions::getDomainname());
683 }
684
685
686 void Engine::setHost(const std::string & name) throw() {
687   a_host = ((name != "") ? name : anna::functions::getHostname());
688 }
689
690
691
692 void Engine::raiseAutoRecovery(bool autoRecovery) throw(anna::RuntimeException) {
693   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "raiseAutoRecovery", ANNA_FILE_LOCATION));
694
695   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
696     entity(it)->raiseAutoRecovery(autoRecovery);
697 }
698
699 void Engine::do_stop()
700 throw() {
701   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "do_stop", ANNA_FILE_LOCATION));
702   close(true /* destroy */);
703 }
704
705 std::string Engine::asString(void) const throw() {
706   std::string trace;
707   trace =  "\n================================";
708   trace += "\nDiameter comm Engine information";
709   trace += "\n================================";
710   trace += "\nAutoBind: ";
711   trace += a_autoBind ? "yes" : "no";
712   trace += "\nMaxConnectionDelay: ";
713   trace += a_maxConnectionDelay.asString();
714   trace += "\nAvailable for entities: ";
715   trace += a_availableForEntities ? "yes" : "no";
716   trace += "\nAvailable for local servers: ";
717   trace += a_availableForLocalServers ? "yes" : "no";
718   trace += "\nOTA requests: ";
719   trace += anna::functions::asString("%d%s", getOTARequests(), idle() ? " (idle)" : "");
720   trace += "\nOTA requests for entities: ";
721   trace += anna::functions::asString("%d%s", getOTARequestsForEntities(), idleForEntities() ? " (idle)" : "");
722   trace += "\nOTA requests for local servers: ";
723   trace += anna::functions::asString("%d%s", getOTARequestsForLocalServers(), idleForLocalServers() ? " (idle)" : "");
724   // Entities
725   trace += "\nNumber of entities: ";
726   trace += anna::functions::asString(a_entities.size());
727
728   for(const_entity_iterator it = entity_begin(); it != entity_end(); it++) {
729     trace += "\n";
730     trace += entity(it)->asString();
731   }
732
733   // Server sockets
734   trace += "\nNumber of LocalServers: ";
735   trace += anna::functions::asString(a_localServers.size());
736
737   for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++) {
738     trace += "\n";
739     trace += localServer(it)->asString();
740   }
741
742   return trace;
743 }
744
745
746 anna::xml::Node* Engine::asXML(anna::xml::Node* parent) const
747 throw() {
748   parent = anna::app::Component::asXML(parent);
749   anna::xml::Node* result = parent->createChild("diameter.comm.Engine");
750   result->createAttribute("AutoBind", a_autoBind ? "yes" : "no");
751   result->createAttribute("MaxConnectionDelay", a_maxConnectionDelay.asString());
752   result->createAttribute("AvailableForEntities", a_availableForEntities ? "yes" : "no");
753   result->createAttribute("AvailableForLocalServers", a_availableForLocalServers ? "yes" : "no");
754   result->createAttribute("OTArequests", anna::functions::asString("%d%s", getOTARequests(), idle() ? " (idle)" : ""));
755   result->createAttribute("OTArequestsForEntities", anna::functions::asString("%d%s", getOTARequestsForEntities(), idleForEntities() ? " (idle)" : ""));
756   result->createAttribute("OTArequestsForLocalServers", anna::functions::asString("%d%s", getOTARequestsForLocalServers(), idleForLocalServers() ? " (idle)" : ""));
757   result->createAttribute("NumberOfEntities", a_entities.size());
758   anna::xml::Node* entities = result->createChild("Engine.Entities");
759
760   for(const_entity_iterator it = entity_begin(); it != entity_end(); it++)
761     entity(it)->asXML(entities);
762
763   result->createAttribute("NumberOfLocalServers", a_localServers.size());
764   anna::xml::Node* localServers = result->createChild("Engine.LocalServers");
765
766   for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
767     localServer(it)->asXML(localServers);
768
769   return result;
770 }
771
772 Engine::clientSession_iterator Engine::clientSession_find(const clientSession_key &key) throw() {
773   return a_clientSessions.find(key);
774 }
775
776 Engine::server_iterator Engine::server_find(const server_key &key) throw() {
777   return a_servers.find(key);
778 }
779
780 Engine::entity_iterator Engine::entity_find(const entity_key &key) throw() {
781   return a_entities.find(key);
782 }
783
784 Engine::localServer_iterator Engine::localServer_find(const socket_t &key) throw() {
785   return a_localServers.find(key);
786 }
787
788 Engine::entity_key Engine::getEntityKey(const std::string & addr1, int port1, const std::string & addr2, int port2) const throw() {
789   socket_v dualList;
790   dualList.push_back(socket_t(addr1, port1));
791   dualList.push_back(socket_t(addr2, port2));
792   return (getEntityKey(dualList));
793 }
794
795 Engine::entity_key Engine::getEntityKey(const socket_v &v) const throw() {
796   std::string result;
797   socket_v::const_iterator it;
798   socket_v::const_iterator it_min(v.begin());
799   socket_v::const_iterator it_max(v.end());
800
801   for(it = it_min; it != it_max; it++) {
802     result += anna::functions::socketLiteralAsString((*it).first, (*it).second);
803     result += " ";
804   }
805
806   result.erase(result.size() - 1, 1);  // remove last space
807   //return anna::functions::exclusiveHash(result);
808   return result;
809 }
810
811
812 void Engine::availabilityLostForEntities() throw() {
813   a_availableForEntities = false;
814   LOGDEBUG(
815     std::string msg = "diameter::comm::Engine { Realm: ";
816     msg += getRealm();
817     msg += " } has lost its availability for entities";
818     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
819   );
820   // OAM
821   OamModule &oamModule = OamModule::instantiate();
822   oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverEntitiesForEngineWithClassName__s__, getClassName());
823   oamModule.count(OamModule::Counter::LostAvailabilityOverEngineForEntities);
824   // Virtual
825   availabilityLostForEntities(this);
826 }
827
828
829 void Engine::availabilityRecoveredForEntities() throw() {
830   a_availableForEntities = true;
831   LOGDEBUG(
832     std::string msg = "diameter::comm::Engine { Realm: ";
833     msg += getRealm();
834     msg += " } has recovered its availability for entities";
835     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
836   );
837   // OAM
838   OamModule &oamModule = OamModule::instantiate();
839   oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverEntitiesForEngineWithClassName__s__, getClassName());
840   oamModule.count(OamModule::Counter::RecoveredAvailabilityOverEngineForEntities);
841   // Virtual
842   availabilityRecoveredForEntities(this);
843 }
844
845
846 void Engine::availabilityLostForLocalServers() throw() {
847   a_availableForLocalServers = false;
848   LOGDEBUG(
849     std::string msg = "diameter::comm::Engine { Realm: ";
850     msg += getRealm();
851     msg += " } has lost its availability for local servers";
852     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
853   );
854   // OAM
855   OamModule &oamModule = OamModule::instantiate();
856   oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverLocalServersForEngineWithClassName__s__, getClassName());
857   oamModule.count(OamModule::Counter::LostAvailabilityOverEngineForLocalServers);
858   // Virtual
859   availabilityLostForLocalServers(this);
860 }
861
862
863 void Engine::availabilityRecoveredForLocalServers() throw() {
864   a_availableForLocalServers = true;
865   LOGDEBUG(
866     std::string msg = "diameter::comm::Engine { Realm: ";
867     msg += getRealm();
868     msg += " } has recovered its availability for local servers";
869     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
870   );
871   // OAM
872   OamModule &oamModule = OamModule::instantiate();
873   oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverLocalServersForEngineWithClassName__s__, getClassName());
874   oamModule.count(OamModule::Counter::RecoveredAvailabilityOverEngineForLocalServers);
875   // Virtual
876   availabilityRecoveredForLocalServers(this);
877 }
878
879
880 bool Engine::refreshAvailabilityForEntities() throw() {
881   // Here available
882   if(a_availableForEntities) {  // check not-bound state for all client-sessions:
883     bool isolate = true;
884
885     for(const_entity_iterator it = entity_begin(); it != entity_end(); it++)
886       if(entity(it)->isAvailable()) { isolate = false; break; }
887
888     if(isolate) {
889       availabilityLostForEntities();
890       return true;
891     }
892
893     return false;
894   }
895
896   // Here not available
897   for(const_entity_iterator it = entity_begin(); it != entity_end(); it++)
898     if(entity(it)->isAvailable()) {
899       availabilityRecoveredForEntities();
900       return true;
901     }
902
903   return false;
904 }
905
906 bool Engine::refreshAvailabilityForLocalServers() throw() {
907   // Here available
908   if(a_availableForLocalServers) {  // check not-bound state for all client-sessions:
909     bool isolate = true;
910
911     for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
912       if(localServer(it)->isAvailable()) { isolate = false; break; }
913
914     if(isolate) {
915       availabilityLostForLocalServers();
916       return true;
917     }
918
919     return false;
920   }
921
922   // Here not available
923   for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
924     if(localServer(it)->isAvailable()) {
925       availabilityRecoveredForLocalServers();
926       return true;
927     }
928
929   return false;
930 }
931
932
933 void Engine::readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw() {
934   // Default DPA implementation:
935   //
936   //   'Disconnect-Peer-Answer' (282,answer)
937   //        {Result-Code}...................................(268,0)
938   //        {Origin-Host}...................................(264,0)
939   //        {Origin-Realm}..................................(296,0)
940   //        [Error-Message].................................(281,0)
941   //       *[Failed-AVP]....................................(279,0)
942   try {
943     anna::diameter::codec::Message diameterDPA;
944     anna::diameter::codec::Avp avpRC;
945     anna::diameter::codec::Avp avpOH;
946     anna::diameter::codec::Avp avpOR;
947     // Message header
948     diameterDPA.setId(anna::diameter::helpers::base::COMMANDID__Disconnect_Peer_Answer);
949     diameterDPA.setVersion(1);
950     diameterDPA.setApplicationId(codec::functions::getApplicationId(dpr));
951     diameterDPA.setHopByHop(codec::functions::getHopByHop(dpr));
952     diameterDPA.setEndToEnd(codec::functions::getEndToEnd(dpr));
953     // Result-Code
954     avpRC.setId(anna::diameter::helpers::base::AVPID__Result_Code);
955     avpRC.setMandatoryBit();
956     avpRC.getUnsigned32()->setValue(helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS);
957     // Origin-Host
958     avpOH.setId(anna::diameter::helpers::base::AVPID__Origin_Host);
959     avpOH.setMandatoryBit();
960     avpOH.getDiameterIdentity()->fromPrintableString(a_host.c_str());
961     // Origin-Realm
962     avpOR.setId(anna::diameter::helpers::base::AVPID__Origin_Realm);
963     avpOR.setMandatoryBit();
964     avpOR.getDiameterIdentity()->fromPrintableString(a_realm.c_str());
965     diameterDPA.addAvp(&avpRC);
966     diameterDPA.addAvp(&avpOH);
967     diameterDPA.addAvp(&avpOR);
968     // Encode
969     dpa = diameterDPA.code();
970   } catch(anna::RuntimeException &ex) {
971     ex.trace();
972   }
973 }
974
975
976 void Engine::readCEA(anna::DataBlock &cea, const anna::DataBlock & cer) throw() {
977   // Default CEA implementation:
978   //
979   //   'Capabilities-Exchange-Answer' (257,answer)
980   //        {Result-Code}...................................(268,0)
981   //        {Origin-Host}...................................(264,0)
982   //        {Origin-Realm}..................................(296,0)
983   //      1*{Host-IP-Address}...............................(257,0)
984   //        {Vendor-Id}.....................................(266,0)
985   //        {Product-Name}..................................(269,0)
986   //        [Origin-State-Id]...............................(278,0)
987   //        [Error-Message].................................(281,0)
988   //       *[Failed-AVP]....................................(279,0)
989   //       *[Supported-Vendor-Id]...........................(265,0)
990   //       *[Auth-Application-Id]...........................(258,0)
991   //       *[Inband-Security-Id]............................(299,0)
992   //       *[Acct-Application-Id]...........................(259,0)
993   //        [Vendor-Specific-Application-Id]................(260,0)
994   //        [Firmware-Revision].............................(267,0)
995   //       *[AVP]...........................................(0,0)
996   try {
997     anna::diameter::codec::Message diameterCEA;
998     anna::diameter::codec::Avp avpRC;
999     anna::diameter::codec::Avp avpOH;
1000     anna::diameter::codec::Avp avpOR;
1001     // Message header
1002     diameterCEA.setId(anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Answer);
1003     diameterCEA.setVersion(1);
1004     diameterCEA.setApplicationId(codec::functions::getApplicationId(cer));
1005     diameterCEA.setHopByHop(codec::functions::getHopByHop(cer));
1006     diameterCEA.setEndToEnd(codec::functions::getEndToEnd(cer));
1007     // Result-Code
1008     avpRC.setId(anna::diameter::helpers::base::AVPID__Result_Code);
1009     avpRC.setMandatoryBit();
1010     avpRC.getUnsigned32()->setValue(helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS); // re-implementations could analyze CER to accept or not
1011     // Origin-Host
1012     avpOH.setId(anna::diameter::helpers::base::AVPID__Origin_Host);
1013     avpOH.setMandatoryBit();
1014     avpOH.getDiameterIdentity()->fromPrintableString(a_host.c_str());
1015     // Origin-Realm
1016     avpOR.setId(anna::diameter::helpers::base::AVPID__Origin_Realm);
1017     avpOR.setMandatoryBit();
1018     avpOR.getDiameterIdentity()->fromPrintableString(a_realm.c_str());
1019     diameterCEA.addAvp(&avpRC);
1020     diameterCEA.addAvp(&avpOH);
1021     diameterCEA.addAvp(&avpOR);
1022     // Host-IP-Address
1023     std::string hostIP = anna::functions::getHostnameIP(); // Address
1024     diameterCEA.addAvp(anna::diameter::helpers::base::AVPID__Host_IP_Address)->getAddress()->fromPrintableString(hostIP.c_str());
1025     // Vendor-Id
1026     int vendorId = anna::diameter::helpers::VENDORID__tgpp; // Unsigned32
1027     diameterCEA.addAvp(anna::diameter::helpers::base::AVPID__Vendor_Id)->getUnsigned32()->setValue(vendorId);
1028     // Product-Name
1029     std::string productName = "OCS Diameter Server"; // UTF8String
1030     diameterCEA.addAvp(anna::diameter::helpers::base::AVPID__Product_Name)->getUTF8String()->setValue(productName);
1031     // Encode
1032     cea = diameterCEA.code();
1033   } catch(anna::RuntimeException &ex) {
1034     ex.trace();
1035   }
1036 }
1037
1038
1039 void Engine::readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) throw() {
1040   // Default DWA implementation:
1041   //
1042   //   'Device-Watchdog-Answer' (280,answer)
1043   //        {Result-Code}...................................(268,0)
1044   //        {Origin-Host}...................................(264,0)
1045   //        {Origin-Realm}..................................(296,0)
1046   //        [Error-Message].................................(281,0)
1047   //       *[Failed-AVP]....................................(279,0)
1048   //        [Origin-State-Id]...............................(278,0)
1049   try {
1050     anna::diameter::codec::Message diameterDWA;
1051     anna::diameter::codec::Avp avpRC;
1052     anna::diameter::codec::Avp avpOH;
1053     anna::diameter::codec::Avp avpOR;
1054     // Message header
1055     diameterDWA.setId(anna::diameter::helpers::base::COMMANDID__Device_Watchdog_Answer);
1056     diameterDWA.setVersion(1);
1057     diameterDWA.setApplicationId(codec::functions::getApplicationId(dwr));
1058     diameterDWA.setHopByHop(codec::functions::getHopByHop(dwr));
1059     diameterDWA.setEndToEnd(codec::functions::getEndToEnd(dwr));
1060     // Result-Code
1061     avpRC.setId(anna::diameter::helpers::base::AVPID__Result_Code);
1062     avpRC.setMandatoryBit();
1063     avpRC.getUnsigned32()->setValue(helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS);
1064     // Origin-Host
1065     avpOH.setId(anna::diameter::helpers::base::AVPID__Origin_Host);
1066     avpOH.setMandatoryBit();
1067     avpOH.getDiameterIdentity()->fromPrintableString(a_host.c_str());
1068     // Origin-Realm
1069     avpOR.setId(anna::diameter::helpers::base::AVPID__Origin_Realm);
1070     avpOR.setMandatoryBit();
1071     avpOR.getDiameterIdentity()->fromPrintableString(a_realm.c_str());
1072     diameterDWA.addAvp(&avpRC);
1073     diameterDWA.addAvp(&avpOH);
1074     diameterDWA.addAvp(&avpOR);
1075     // Encode
1076     dwa = diameterDWA.code();
1077   } catch(anna::RuntimeException &ex) {
1078     ex.trace();
1079   }
1080 }
1081
1082 void Engine::resetStatistics() throw() {
1083   for(server_iterator it = server_begin(), maxii = server_end(); it != maxii; it ++)
1084     server(it)->resetStatistics();
1085
1086   for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
1087     localServer(it)->resetStatistics();
1088 }
1089
1090