1be14702ae734307d77b8582c20327e10259bdf7
[anna.git] / source / diameter.comm / ServerSession.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/core/functions.hpp>
10 #include <anna/diameter/defines.hpp>
11 #include <anna/diameter/functions.hpp>
12 #include <anna/diameter/helpers/helpers.hpp>
13 #include <anna/diameter/codec/functions.hpp>
14 #include <anna/diameter/codec/Message.hpp>
15 #include <anna/diameter/helpers/base/functions.hpp>
16 #include <anna/time/functions.hpp>
17
18 // Local
19 #include <anna/diameter.comm/ServerSession.hpp>
20 #include <anna/diameter.comm/Engine.hpp>
21 #include <anna/diameter.comm/Entity.hpp>
22 #include <anna/diameter.comm/Response.hpp>
23 #include <anna/diameter.comm/Message.hpp>
24 #include <anna/diameter.comm/OamModule.hpp>
25 #include <anna/diameter.comm/TimerManager.hpp>
26 #include <anna/diameter.comm/Timer.hpp>
27 #include <anna/diameter.comm/LocalServer.hpp>
28 #include <anna/diameter.comm/ServerSessionReceiver.hpp>
29 #include <anna/diameter.comm/ReceiverFactoryImpl.hpp>
30
31 #include <anna/app/functions.hpp>
32 #include <anna/comm/ClientSocket.hpp>
33 #include <anna/core/functions.hpp>
34 #include <anna/core/DataBlock.hpp>
35 #include <anna/core/tracing/Logger.hpp>
36 #include <anna/core/tracing/TraceMethod.hpp>
37 #include <anna/xml/Node.hpp>
38 #include <anna/timex/Engine.hpp>
39
40 // Standard
41 #include <stdlib.h> // rand()
42 #include <time.h>
43
44
45
46 using namespace std;
47 using namespace anna::diameter;
48 using namespace anna::diameter::comm;
49
50 //static
51 const anna::Millisecond ServerSession::DefaultAllowedInactivityTime(90000); // Inactivity timeout
52
53
54 ServerSession::ServerSession() : Session("diameter::comm::ServerSession", "Diameter Inactivity Detection Timer"),
55   a_receiverFactory(this),
56   a_cer(ClassCode::Bind),
57   a_dwr(ClassCode::ApplicationMessage) // realmente no es necesario, los Message son por defecto de aplicacion
58 { initialize(); }
59
60 void ServerSession::initialize() throw() {
61   Session::initialize();
62   a_parent = NULL;
63   a_clientSocket = NULL;
64   a_deprecated = false; // selected for remove (server session was lost due to event break connection)
65 }
66
67 //ServerSession::~ServerSession() {;}
68
69 void ServerSession::setClientSocket(anna::comm::ClientSocket *clientSocket) throw() {
70   a_clientSocket = clientSocket;
71   a_clientSocket->setReceiverFactory(a_receiverFactory);
72 }
73
74
75 const std::string& ServerSession::getAddress() const throw() {
76   return a_parent->getKey().first;
77 }
78
79 int ServerSession::getPort() const throw() {
80   return a_parent->getKey().second;
81 }
82
83
84 const Response* ServerSession::send(const Message* message) throw(anna::RuntimeException) {
85   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "send", ANNA_FILE_LOCATION));
86
87   if(!message)
88     throw anna::RuntimeException("Cannot send a NULL message", ANNA_FILE_LOCATION);
89
90   // Command id:
91   bool isRequest;
92   diameter::CommandId cid = message->getCommandId(isRequest);
93   LOGDEBUG(
94     std::string msg = "Sending diameter message: ";
95     msg += anna::diameter::functions::commandIdAsPairString(cid);
96     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
97   );
98
99   // Checkings:
100   if(a_deprecated) {
101     string msg(asString());
102     msg += " | ServerSession is deprecated and will be erased";
103     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
104   }
105
106   if((a_state == State::Closed) && (cid != helpers::base::COMMANDID__Capabilities_Exchange_Answer)) {
107     string msg(asString());
108     msg += " | ServerSession is not bound";
109     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
110   }
111
112   // CER/DWR are not sent from server-side:
113   if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
114     string msg(asString());
115     msg += " | Trying to send CER: unexpected message from server-side";
116     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
117   }
118
119   if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
120     string msg(asString());
121     msg += " | Trying to send DWR: unexpected message from server-side";
122     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
123   }
124
125   // Check states:
126   /*if (cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) {
127      if (a_state != State::Closed) {
128         string msg(asString());
129         msg += " | Discarding CEA on not closed state";
130         throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
131      }
132
133   } else*/ if(cid ==  helpers::base::COMMANDID__Device_Watchdog_Answer) {
134     if(a_state == State::WaitingDPA) {
135       string msg(asString());
136       msg += " | DWA is not sent on 'WaitingDPA' state";
137       //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
138       LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
139       return NULL;
140     }
141
142     if(a_state == State::Disconnecting) {
143       string msg(asString());
144       msg += " | DWA is not sent on 'Disconnecting' state";
145       //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
146       LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
147       return NULL;
148     }
149   } else if(cid ==  helpers::base::COMMANDID__Disconnect_Peer_Request) {
150     if(a_state == State::WaitingDPA) {
151       string msg(asString());
152       msg += " | Still waiting for DPR ack (DPA)";
153       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
154     }
155
156     if(a_state == State::Disconnecting) {
157       string msg(asString());
158       msg += " | Server disconnection has already been initiated";
159       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
160     }
161   } else {
162     if((a_state == State::WaitingDPA) || (a_state == State::Disconnecting)) {
163       if(cid != helpers::base::COMMANDID__Disconnect_Peer_Answer) {
164         LOGDEBUG(
165           string msg("diameter::comm::ServerSession::send | ");
166           msg += asString();
167           msg += " | Sents (request or answer) blocked to diameter client (disconnection in progress). Discarding ...";
168           anna::Logger::debug(msg, ANNA_FILE_LOCATION);
169         );
170         return NULL;
171       }
172     }
173   }
174
175   // Trace send operation:
176   LOGDEBUG(
177     string msg("diameter::comm::ServerSession::send | ");
178     msg += asString();
179     msg += " | ";
180     msg += message->asString();
181     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
182   );
183   bool fixed = false; // answers cannot be fixed
184   Message * message_nc = const_cast<Message*>(message);
185
186   if(isRequest) {
187     // Fixing indicator:
188     fixed = message_nc->fixRequestSequence(a_nextHopByHop, a_nextEndToEnd);
189     message_nc->updateRequestTimestampMs(); // statistics purposes (processing time for request type)
190   }
191
192   // Send message
193   try {
194     message->send(*this);
195
196     // Next hop by hop & end to end identifiers:
197     if(isRequest) generateNextSequences();
198
199     //   Transaction state
200     //         The Diameter protocol requires that agents maintain transaction
201     //         state, which is used for failover purposes.  Transaction state
202     //         implies that upon forwarding a request, the Hop-by-Hop identifier
203     //         is saved; the field is replaced with a locally unique identifier,
204     //         which is restored to its original value when the corresponding
205     //         answer is received.  The request's state is released upon receipt
206     //         of the answer.  A stateless agent is one that only maintains
207     //         transaction state.
208     //
209     updateOutgoingActivityTime();
210     // OAM
211     countSendings(cid, true /* send ok */);
212     // Trace non-application messages:
213     LOGDEBUG(
214
215       if((cid == helpers::base::COMMANDID__Device_Watchdog_Request) ||
216     (cid == helpers::base::COMMANDID__Disconnect_Peer_Request)) {
217     anna::Logger::debug("Sent DataBlock to XML representation:", ANNA_FILE_LOCATION);
218       try { anna::diameter::codec::Message msg; msg.decode(message->getBody()); /* decode to be traced */ } catch(anna::RuntimeException&) {;}
219     }
220     );
221
222     // Restore sequences:
223     if(fixed) message_nc->restoreSequencesAfterFix();  // restore to application sequences after fix
224   } catch(anna::RuntimeException&) {
225     if(fixed) message_nc->restoreSequencesAfterFix();  // restore to application sequences after fix
226
227     // OAM
228     countSendings(cid, false /* send no ok */);
229     throw;
230   }
231
232   // Renew states:
233   /*if (cid ==  helpers::base::COMMANDID__Capabilities_Exchange_Answer) {
234      setState(State::Bound); // Done at sendCEA if proceed
235
236   } else */if(cid ==  helpers::base::COMMANDID__Disconnect_Peer_Request) {
237     LOGWARNING(anna::Logger::warning("DPR has been sent to the peer (diameter client)", ANNA_FILE_LOCATION));
238     setState(State::WaitingDPA);
239   }
240
241   // Answers are not temporized:
242   if(!isRequest) return NULL;
243
244   // Request will have context responses:
245   Response* result(NULL);
246   result = Response::instance(message->getClassCode(), a_nextHopByHop - 1 /* current request sent to client */);
247   result->setRequest(message);
248   response_add(result);
249   return result;
250 }
251
252
253
254 bool ServerSession::unbind(bool forceDisconnect) throw(anna::RuntimeException) {
255   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "unbind", ANNA_FILE_LOCATION));
256
257   if(a_state == State::Closed)
258     return false;
259
260   // Client socket:
261   anna::comm::ClientSocket * cs = a_clientSocket;
262   //anna::comm::ClientSocket * cs = const_cast<anna::comm::ClientSocket*>(a_clientSocket);
263
264   if(forceDisconnect) {
265     LOGDEBUG(anna::Logger::debug("Immediate disconnection (forceDisconnect)", ANNA_FILE_LOCATION));
266
267     if(cs) cs->requestClose();  // this will invoke finalize()
268
269     return true;
270   }
271
272 //   if (a_state == State::Disconnecting) {
273 //      LOGDEBUG(
274 //         string msg("diameter::comm::ServerSession::unbind | ");
275 //         msg += asString();
276 //         msg += " | Disconnection already in progress !";
277 //         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
278 //      );
279 //      return false;
280 //   }
281 //
282 //
283 //   if (a_onDisconnect == OnDisconnect::IgnorePendings) {
284 //      LOGDEBUG(anna::Logger::debug("Immediate disconnection (IgnorePendings)", ANNA_FILE_LOCATION));
285 //
286 //      if (cs) cs->requestClose(); // this will invoke finalize()
287 //
288 //      return true;
289 //   }
290 //
291 //   if (getOTARequests() == 0) { // No pendings
292 //      LOGDEBUG(anna::Logger::debug("No pending answers. Perform client-session close.", ANNA_FILE_LOCATION));
293 //
294 //      if (cs) cs->requestClose(); // this will invoke finalize()
295 //
296 //      return true;
297 //   }
298   return false;
299 }
300
301 void ServerSession::eventPeerShutdown() throw() {
302   // Inform father server:
303   a_parent->eventPeerShutdown(this);
304 }
305
306 void ServerSession::eventResponse(const Response& response) throw(anna::RuntimeException) {
307   // Inform father server:
308   a_parent->eventResponse(response);
309 }
310
311 void ServerSession::eventRequest(const anna::DataBlock &request) throw(anna::RuntimeException) {
312   // Inform father server:
313   a_parent->eventRequest(this, request);
314 }
315
316 void ServerSession::eventUnknownResponse(const anna::DataBlock& response) throw(anna::RuntimeException) {
317   // Inform father server:
318   a_parent->eventUnknownResponse(this, response);
319 }
320
321 void ServerSession::eventDPA(const anna::DataBlock& response) throw(anna::RuntimeException) {
322   // Inform father server:
323   a_parent->eventDPA(this, response);
324 }
325
326 //------------------------------------------------------------------------------------------
327 // Se invoca desde el diameter::comm::Receiver
328 //------------------------------------------------------------------------------------------
329 void ServerSession::receive(const anna::comm::Message& message)
330 throw(anna::RuntimeException) {
331   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "receive", ANNA_FILE_LOCATION));
332   // Activity:
333   updateIncomingActivityTime();
334   activateTimer();
335   // Command id:
336   const anna::DataBlock & db = message.getBody();
337   diameter::CommandId cid = codec::functions::getCommandId(db);
338   bool isRequest = cid.second;
339   LOGDEBUG(
340     std::string msg = "Received diameter message: ";
341     msg += anna::diameter::functions::commandIdAsPairString(cid);
342     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
343
344     if((cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) || (cid.first == helpers::base::COMMANDID__Device_Watchdog_Request.first))
345   try { anna::diameter::codec::Message dmsg; dmsg.decode(db); /* decode to be traced */ } catch(anna::RuntimeException&) {;}
346 );
347   // Main counters:
348   OamModule &oamModule = OamModule::instantiate();
349   oamModule.count(isRequest ? OamModule::Counter::RequestReceived : OamModule::Counter::AnswerReceived);
350   oamModule.count(isRequest ? OamModule::Counter::RequestReceivedOnServerSession : OamModule::Counter::AnswerReceivedOnServerSession);
351   // Statistic (size)
352   a_parent->updateReceivedMessageSizeStatisticConcept(message.getSize()); // only on reception (application could manage sent sizes)
353
354   if(isRequest) {
355     // Si recibo un request, el message solo tiene fiable el DataBlock. Como por defecto se construye como ApplicationMessage,
356     // el unico caso que no cuadraria seria la recepcion de un CER. Lo que hacemos es NO PROGRESAR NUNCA un CER (*).
357     // El DWR sin embargo, si podriamos progresarlo al ser de aplicacion, pero no les sirve para nada (**).
358     /////////////////////////////
359     // Here received a request //
360     /////////////////////////////
361
362     // Received CER
363     if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
364       oamModule.count(OamModule::Counter::CERReceived);
365
366       if(a_state == State::Bound) {
367         LOGWARNING(anna::Logger::warning("Received another CER over already bound connection. Anyway, will be replied with CEA", ANNA_FILE_LOCATION));
368       }
369
370       a_cer.setBody(db);
371       sendCEA();
372       //activateTimer(); // Ya se invoca al inicio de este metodo ::receive
373       //bool changes = a_parent->refreshAvailability();
374       return; // (*)
375     }
376     // Received DWR
377     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
378       oamModule.count(OamModule::Counter::DWRReceived);
379       a_dwr.setBody(db);
380       sendDWA();
381       return; // (**)
382     }
383     // Received DPR
384     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
385       oamModule.count(OamModule::Counter::DPRReceived);
386
387       if(a_state == State::Bound) {
388         a_dpr.setBody(db);
389         setState(State::Disconnecting);
390         LOGWARNING(anna::Logger::warning("DPR has been received from peer (diameter client)", ANNA_FILE_LOCATION));
391         // Ignore pending on server sessions:
392         /*if (getOTARequests() == 0) */sendDPA();
393         return; // DPR won't be informed because virtual readDPA is available for this
394       }
395     }
396
397     try {
398       eventRequest(db);
399     } catch(anna::RuntimeException& ex) {
400       ex.trace();
401     }
402
403     return;
404   }
405
406   /////////////////////////////
407   // Here received an answer //
408   /////////////////////////////
409   bool doUnbind = false;
410   int resultCode = 0;
411
412   try {
413     resultCode = helpers::base::functions::getResultCode(db);
414   } catch(anna::RuntimeException& ex) {
415     ex.trace();
416   }
417
418   // Received DPA
419   if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) {
420     oamModule.count(OamModule::Counter::DPAReceived);
421
422     if(a_state == State::WaitingDPA) {
423       if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) {
424         LOGWARNING(anna::Logger::warning("Received DPA with non-success Result-Code. Ignoring and recovering Bound state", ANNA_FILE_LOCATION));
425         setState(State::Bound);
426       } else {
427         LOGWARNING(anna::Logger::warning("Received DPA With Result-Code = DIAMETER_SUCCESS. Disconnect now.", ANNA_FILE_LOCATION));
428         doUnbind = true;
429       }
430     }
431
432     eventDPA(db);
433
434   } else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) {  // non usual (server should not send DWR's)
435     oamModule.count(OamModule::Counter::DWAReceived);
436   }
437
438   HopByHop hopByHop = codec::functions::getHopByHop(db); // context identification
439   Response* response = response_find(hopByHop);
440
441   // Out-of-context responses:
442   if(!response) {
443     // OAM
444     oamModule.count(OamModule::Counter::AnswerReceivedUnknown);
445     oamModule.count(OamModule::Counter::AnswerReceivedOnServerSessionUnknown);
446     oamModule.activateAlarm(OamModule::Alarm::AnswerReceivedOnServerSessionUnknown);
447     eventUnknownResponse(db);
448     string msg(asString());
449     msg += anna::functions::asString(" | Response received from client, for non registered context (HopByHop: %u)", hopByHop);
450     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
451   }
452
453   response->setResultCode(Response::ResultCode::Success);
454   response->cancelTimer();
455   LOGDEBUG(
456     string msg("diameter::comm::ServerSession::receive | ");
457     msg += asString();
458     msg += " | Received answer";
459     msg += response->asString();
460     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
461   );
462   // Statistics
463   anna::Millisecond current = (anna::Millisecond)anna::functions::millisecond();
464   anna::Millisecond request = response->getRequest()->getRequestTimestampMs();
465   anna::Millisecond timeToAnswerMs = current - request;
466   a_parent->updateProcessingTimeStatisticConcept(timeToAnswerMs);
467   LOGDEBUG
468   (
469     std::string msg = "This diameter request context lasted ";
470     msg += anna::functions::asString(timeToAnswerMs);
471     msg += " milliseconds at diameter client (included network time)";
472     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
473   );
474   // Progress origin for tracking purposes on asyncronous boxes with both diameter interfaces (entities and clients)
475   Message * requestMessage = const_cast<Message*>(response->getRequest());
476   requestMessage->setRequestClientSessionKey(response->getRequest()->getRequestClientSessionKey()); // "" means unkown/unset
477
478   if(cid != helpers::base::COMMANDID__Disconnect_Peer_Answer) {
479     // don't progress DPA: unbind is automatically performed and not open to any application decision
480     try {
481       response->setMessage(&db);
482       // Restore received datablock
483       LOGDEBUG(
484         string msg("diameter::comm::ClientSession::receive | Restore answer to original request sequences (hop-by-hop = ");
485         msg += anna::functions::asString(response->getRequest()->getRequestHopByHop());
486         msg += ", end-to-end = ";
487         msg += anna::functions::asString(response->getRequest()->getRequestEndToEnd());
488         msg += ")";
489         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
490       );
491       diameter::codec::functions::setHopByHop((anna::DataBlock&)db, response->getRequest()->getRequestHopByHop());
492       diameter::codec::functions::setEndToEnd((anna::DataBlock&)db, response->getRequest()->getRequestEndToEnd());
493       eventResponse(*response);
494     } catch(anna::RuntimeException& ex) {
495       ex.trace();
496     }
497   }
498
499   response_erase(response);
500
501   // Unbind trigger
502   if(doUnbind)
503     unbind(true /* always immediate */);
504 }
505
506 void ServerSession::finalize() throw() {
507   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "finalize", ANNA_FILE_LOCATION));
508   // Configuration overiddings
509   setOnDisconnect(OnDisconnect::IgnorePendings);
510   notifyOrphansOnExpiration(false);
511   // session will be idle after 'Session::finalize'
512   // (anyway at 99% of the cases, server session would be idle because is not usual to send request from diameter servers)
513   // Closing state won't be used on server-sessions
514   Session::finalize(); // sets closed state (unbind at closeServerSession won't repeat client socket close, because returns at the beginning)
515   // stops timers
516
517   // "delete this" indirecto (seria mas elegante borrar las server sessions deprecated mediante un temporizador de purgado)
518   try {
519     if(idle()) getParent()->closeServerSession(this);  // http://www.parashift.com/c++-faq-lite/delete-this.html
520   } catch(anna::RuntimeException& ex) {
521     ex.trace();
522   }
523
524   // Inform father local server (availability changes):
525   bool changes = getParent()->refreshAvailability();
526   // OAM
527   bool multipleConnections = (getParent()->getMaxConnections() > 1);
528   std::string socket = anna::functions::socketLiteralAsString(getAddress(), getPort());
529   OamModule &oamModule = OamModule::instantiate();
530
531   if(multipleConnections) {
532     oamModule.activateAlarm(OamModule::Alarm::LostConnectionForServerSessionAtLocalServer__s__ServerSessionId__d__, socket.c_str(), getSocketId());
533     oamModule.count(OamModule::Counter::LostConnectionForServerSession);
534   } else {
535     oamModule.activateAlarm(OamModule::Alarm::LostConnectionForServerSessionAtLocalServer__s__, socket.c_str());
536     oamModule.count(OamModule::Counter::LostConnectionForServerSession);
537   }
538 }
539
540
541
542 void ServerSession::sendCEA()
543 throw(anna::RuntimeException) {
544   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "sendCEA", ANNA_FILE_LOCATION));
545   anna::DataBlock cea(true);
546   a_engine->readCEA(cea, a_cer.getBody()); // Asume that CEA is valid ...
547   // If one peer sends a CER message to another Peer and receiver does not have support for
548   //
549   //  1) any common application then it must return the CEA with Result-Code Avp set to DIAMETER_NO_COMMON_APPLICATION
550   //     and should disconnect the transport layer connection (automatically done by diameter::comm module).
551   //  2) no common security mechanism then it must return the CEA with Result-Code Avp set to DIAMETER_NO_COMMON_SECURITY
552   //     and should disconnect the transport layer connection (automatically done by diameter::comm module).
553   //  3) if CER is received from any unknown peer then receiver should discard the message, or send the CEA with the
554   //     Result-Code Avp set to DIAMETER_UNKNOWN_PEER.
555
556   if(cea.isEmpty()) {
557     LOGDEBUG(anna::Logger::debug("Empty CEA message. Remote client never will bound this connection at application level", ANNA_FILE_LOCATION));
558     LOGWARNING(anna::Logger::warning("Discarding received CER without sending CEA (consider to send CEA with Result-Code DIAMETER_UNKNOWN_PEER)", ANNA_FILE_LOCATION));
559     return;
560   }
561
562   Message msgCea;
563   msgCea.setBody(cea);
564   send(&msgCea);
565   // Here, CEA has been sent (no exception). Analize CEA Result-Code chosen:
566   int resultCode = 0;
567
568   try {
569     resultCode = helpers::base::functions::getResultCode(cea);
570   } catch(anna::RuntimeException& ex) {
571     ex.trace();
572   }
573
574   switch(resultCode) {
575   case helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS:
576     setState(State::Bound);
577     break;
578   case helpers::base::AVPVALUES__Result_Code::DIAMETER_NO_COMMON_APPLICATION:
579     LOGWARNING(anna::Logger::warning("DIAMETER_NO_COMMON_APPLICATION CEA Result-Code implies unbinding connection", ANNA_FILE_LOCATION));
580     unbind(true /* always immediate */); // no delegamos en un planning o similar
581     // Realmente el cliente diameter tambien deberia cerrar la conexion al recibir este Result-Code
582     break;
583   case helpers::base::AVPVALUES__Result_Code::DIAMETER_NO_COMMON_SECURITY:
584     LOGWARNING(anna::Logger::warning("DIAMETER_NO_COMMON_SECURITY CEA Result-Code implies unbinding connection", ANNA_FILE_LOCATION));
585     unbind(true /* always immediate */); // no delegamos en un planning o similar
586     // Realmente el cliente diameter tambien deberia cerrar la conexion al recibir este Result-Code
587     break;
588     // ...
589     // ...
590   }
591 }
592
593 void ServerSession::sendDWA()
594 throw(anna::RuntimeException) {
595   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "sendDWA", ANNA_FILE_LOCATION));
596   anna::DataBlock dwa(true);
597   a_engine->readDWA(dwa, a_dwr.getBody()); // Asume that DWA is valid ...
598
599   if(dwa.isEmpty())
600     throw anna::RuntimeException("This diameter agent defines an empty DWA message. Remote client never will validate this connection health", ANNA_FILE_LOCATION);
601
602   Message msgDwa;
603   msgDwa.setBody(dwa);
604   send(&msgDwa);
605 }
606
607
608 //-------------------------------------------------------------------------
609 // Se invoca desde diameter::comm::Timer
610 //-------------------------------------------------------------------------
611 void ServerSession::expireResponse(diameter::comm::Response* response)
612 throw() {
613   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expireResponse", ANNA_FILE_LOCATION));
614   Session::expireResponse(response);
615   // OAM
616   OamModule &oamModule = OamModule::instantiate();
617   oamModule.count(OamModule::Counter::RequestSentExpired);
618   oamModule.count(OamModule::Counter::RequestSentOnServerSessionExpired);
619   oamModule.activateAlarm(OamModule::Alarm::RequestSentOnServerSessionExpired);
620 //   // "delete this" indirecto (seria mas elegante borrar las server sessions deprecated mediante un temporizador de purgado)
621 //   if (idle()) a_parent->eraseServerSession(*a_clientSocket); // http://www.parashift.com/c++-faq-lite/delete-this.html
622 }
623
624 std::string ServerSession::asString() const
625 throw() {
626   string result = Session::asString();
627   result += " | Parent Local Server: ";
628   result += anna::functions::socketLiteralAsString(getAddress(), getPort());
629   result += " | Client Socket: ";
630   result += a_clientSocket->asString();
631   // Diferente del timeout de ApplicationMessage:
632   result += " | Allowed inactivity time: ";
633   result += getTimeout().asString();
634   result += " | Deprecated: ";
635   result += (a_deprecated ? "yes" : "no");
636   return result += " }";
637 }
638
639 anna::xml::Node* ServerSession::asXML(anna::xml::Node* parent) const
640 throw() {
641   anna::xml::Node* result = Session::asXML(parent);
642   parent->createChild("diameter.comm.ServerSession");
643   result->createAttribute("ParentLocalServer", anna::functions::socketLiteralAsString(getAddress(), getPort()));
644   result->createAttribute("ClientSocket", a_clientSocket->asString());
645   // Diferente del timeout de ApplicationMessage:
646   result->createAttribute("AllowedInactivityTime", getTimeout().asString());
647   result->createAttribute("Deprecated", a_deprecated ? "yes" : "no");
648   return result;
649 }
650
651
652 //------------------------------------------------------------------------------
653 //------------------------------------------------------ ServerSession::expire()
654 //------------------------------------------------------------------------------
655 void ServerSession::expire(anna::timex::Engine *timeController) throw(anna::RuntimeException) {
656   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expire (inactivity check timer)", ANNA_FILE_LOCATION));
657   LOGWARNING(anna::Logger::warning("Detecting anomaly (too inactivity time) over server session. Resetting", ANNA_FILE_LOCATION));
658   // OAM
659   bool multipleConnections = (getParent()->getMaxConnections() > 1);
660   std::string socket = anna::functions::socketLiteralAsString(getAddress(), getPort());
661   OamModule &oamModule = OamModule::instantiate();
662
663   if(multipleConnections) {
664     oamModule.activateAlarm(OamModule::Alarm::UnbindConnectionForServerSessionAtLocalServer__s__ServerSessionId__d__DueToInactivityTimeAnomaly, socket.c_str(), getSocketId());
665     oamModule.count(OamModule::Counter::UnbindConnectionForServerSessionDueToInactivityTimeAnomaly);
666   } else {
667     oamModule.activateAlarm(OamModule::Alarm::UnbindConnectionForServerSessionAtLocalServer__s__DueToInactivityTimeAnomaly, socket.c_str());
668     oamModule.count(OamModule::Counter::UnbindConnectionForServerSessionDueToInactivityTimeAnomaly);
669   }
670
671   unbind(true /* always immediate */); // no delegamos en un planning o similar
672 }
673
674 void ServerSession::setAllowedInactivityTime(const anna::Millisecond & allowedInactivityTime) throw() {
675   setTimeout(allowedInactivityTime);
676 }
677
678 //------------------------------------------------------------------------------
679 //---------------------------------- ServerSession::updateIncomingActivityTime()
680 //------------------------------------------------------------------------------
681 void ServerSession::updateIncomingActivityTime() throw() {
682   Session::updateIncomingActivityTime();
683   a_parent->updateIncomingActivityTime();
684 }
685
686
687 //------------------------------------------------------------------------------
688 //---------------------------------- ServerSession::updateOutgoingActivityTime()
689 //------------------------------------------------------------------------------
690 void ServerSession::updateOutgoingActivityTime(void) throw() {
691   Session::updateOutgoingActivityTime();
692   a_parent->updateOutgoingActivityTime();
693 }
694
695
696 //------------------------------------------------------------------------------
697 //----------------------------------------------- ServerSession::countSendings()
698 //------------------------------------------------------------------------------
699 void ServerSession::countSendings(const diameter::CommandId & cid, bool ok)throw() {
700   OamModule &oamModule = OamModule::instantiate();
701   bool isRequest = cid.second;
702
703   if(ok) {
704     // Main counters:
705     oamModule.count(isRequest ? OamModule::Counter::RequestSentOK : OamModule::Counter::AnswerSentOK);
706     oamModule.count(isRequest ? OamModule::Counter::RequestSentOnServerSessionOK : OamModule::Counter::AnswerSentOnServerSessionOK);
707
708     if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) oamModule.count(OamModule::Counter::CEASentOK);
709     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) oamModule.count(OamModule::Counter::DWASentOK);
710     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentOK);  // not usual
711     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentOK);
712     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentOK);
713   } else {
714     // Main counters:
715     oamModule.count(isRequest ? OamModule::Counter::RequestSentNOK : OamModule::Counter::AnswerSentNOK);
716     oamModule.count(isRequest ? OamModule::Counter::RequestSentOnServerSessionNOK : OamModule::Counter::RequestSentOnServerSessionNOK);
717
718     if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) oamModule.count(OamModule::Counter::CEASentNOK);
719     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) oamModule.count(OamModule::Counter::DWASentNOK);
720     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentNOK);  // not usual
721     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentNOK);
722     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentNOK);
723   }
724 }
725
726