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