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