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