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