90da697f820c0d57db70ec3d7842fd54ec8a3ba8
[anna.git] / source / diameter.comm / ServerSession.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // http://redmine.teslayout.com/projects/anna-suite
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     *  Neither the name of the copyright holder nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #include <anna/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 void ServerSession::eventDPA(const anna::DataBlock& response) throw(anna::RuntimeException) {
350   // Inform father server:
351   a_parent->eventDPA(this, response);
352 }
353
354 //------------------------------------------------------------------------------------------
355 // Se invoca desde el diameter::comm::Receiver
356 //------------------------------------------------------------------------------------------
357 void ServerSession::receive(const anna::comm::Message& message)
358 throw(anna::RuntimeException) {
359   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "receive", ANNA_FILE_LOCATION));
360   // Activity:
361   updateIncomingActivityTime();
362   activateTimer();
363   // Command id:
364   const anna::DataBlock & db = message.getBody();
365   diameter::CommandId cid = codec::functions::getCommandId(db);
366   bool isRequest = cid.second;
367   LOGDEBUG(
368     std::string msg = "Received diameter message: ";
369     msg += anna::diameter::functions::commandIdAsPairString(cid);
370     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
371
372     if((cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) || (cid.first == helpers::base::COMMANDID__Device_Watchdog_Request.first))
373   try { anna::diameter::codec::Message dmsg; dmsg.decode(db); /* decode to be traced */ } catch(anna::RuntimeException&) {;}
374 );
375   // Main counters:
376   OamModule &oamModule = OamModule::instantiate();
377   oamModule.count(isRequest ? OamModule::Counter::RequestReceived : OamModule::Counter::AnswerReceived);
378   oamModule.count(isRequest ? OamModule::Counter::RequestReceivedOnServerSession : OamModule::Counter::AnswerReceivedOnServerSession);
379   // Statistic (size)
380   a_parent->updateReceivedMessageSizeStatisticConcept(message.getSize()); // only on reception (application could manage sent sizes)
381
382   if(isRequest) {
383     // Si recibo un request, el message solo tiene fiable el DataBlock. Como por defecto se construye como ApplicationMessage,
384     // el unico caso que no cuadraria seria la recepcion de un CER. Lo que hacemos es NO PROGRESAR NUNCA un CER (*).
385     // El DWR sin embargo, si podriamos progresarlo al ser de aplicacion, pero no les sirve para nada (**).
386     /////////////////////////////
387     // Here received a request //
388     /////////////////////////////
389
390     // Received CER
391     if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
392       oamModule.count(OamModule::Counter::CERReceived);
393
394       if(a_state == State::Bound) {
395         LOGWARNING(anna::Logger::warning("Received another CER over already bound connection. Anyway, will be replied with CEA", ANNA_FILE_LOCATION));
396       }
397
398       a_cer.setBody(db);
399       sendCEA();
400       //activateTimer(); // Ya se invoca al inicio de este metodo ::receive
401       //bool changes = a_parent->refreshAvailability();
402       return; // (*)
403     }
404     // Received DWR
405     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
406       oamModule.count(OamModule::Counter::DWRReceived);
407       a_dwr.setBody(db);
408       sendDWA();
409       return; // (**)
410     }
411     // Received DPR
412     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
413       oamModule.count(OamModule::Counter::DPRReceived);
414
415       if(a_state == State::Bound) {
416         a_dpr.setBody(db);
417         setState(State::Disconnecting);
418         LOGWARNING(anna::Logger::warning("DPR has been received from peer (diameter client)", ANNA_FILE_LOCATION));
419         // Ignore pending on server sessions:
420         /*if (getOTARequests() == 0) */sendDPA();
421         return; // DPR won't be informed because virtual readDPA is available for this
422       }
423     }
424
425     try {
426       eventRequest(db);
427     } catch(anna::RuntimeException& ex) {
428       ex.trace();
429     }
430
431     return;
432   }
433
434   /////////////////////////////
435   // Here received an answer //
436   /////////////////////////////
437   bool doUnbind = false;
438   int resultCode = 0;
439
440   try {
441     resultCode = helpers::base::functions::getResultCode(db);
442   } catch(anna::RuntimeException& ex) {
443     ex.trace();
444   }
445
446   // Received DPA
447   if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) {
448     oamModule.count(OamModule::Counter::DPAReceived);
449
450     if(a_state == State::WaitingDPA) {
451       if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) {
452         LOGWARNING(anna::Logger::warning("Received DPA with non-success Result-Code. Ignoring and recovering Bound state", ANNA_FILE_LOCATION));
453         setState(State::Bound);
454       } else {
455         LOGWARNING(anna::Logger::warning("Received DPA With Result-Code = DIAMETER_SUCCESS. Disconnect now.", ANNA_FILE_LOCATION));
456         doUnbind = true;
457       }
458     }
459
460     eventDPA(db);
461
462   } else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) {  // non usual (server should not send DWR's)
463     oamModule.count(OamModule::Counter::DWAReceived);
464   }
465
466   HopByHop hopByHop = codec::functions::getHopByHop(db); // context identification
467   Response* response = response_find(hopByHop);
468
469   // Out-of-context responses:
470   if(!response) {
471     // OAM
472     oamModule.count(OamModule::Counter::AnswerReceivedUnknown);
473     oamModule.count(OamModule::Counter::AnswerReceivedOnServerSessionUnknown);
474     oamModule.activateAlarm(OamModule::Alarm::AnswerReceivedOnServerSessionUnknown);
475     eventUnknownResponse(db);
476     string msg(asString());
477     msg += anna::functions::asString(" | Response received from client, for non registered context (HopByHop: %u)", hopByHop);
478     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
479   }
480
481   response->setResultCode(Response::ResultCode::Success);
482   response->cancelTimer();
483   LOGDEBUG(
484     string msg("diameter::comm::ServerSession::receive | ");
485     msg += asString();
486     msg += " | Received answer";
487     msg += response->asString();
488     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
489   );
490   // Statistics
491   anna::Millisecond current = (anna::Millisecond)anna::functions::millisecond();
492   anna::Millisecond request = response->getRequest()->getRequestTimestampMs();
493   anna::Millisecond timeToAnswerMs = current - request;
494   a_parent->updateProcessingTimeStatisticConcept(timeToAnswerMs);
495   LOGDEBUG
496   (
497     std::string msg = "This diameter request context lasted ";
498     msg += anna::functions::asString(timeToAnswerMs);
499     msg += " milliseconds at diameter client (included network time)";
500     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
501   );
502   // Progress origin for tracking purposes on asyncronous boxes with both diameter interfaces (entities and clients)
503   Message * requestMessage = const_cast<Message*>(response->getRequest());
504   requestMessage->setRequestClientSessionKey(response->getRequest()->getRequestClientSessionKey()); // "" means unkown/unset
505
506   if(cid != helpers::base::COMMANDID__Disconnect_Peer_Answer) {
507     // don't progress DPA: unbind is automatically performed and not open to any application decision
508     try {
509       response->setMessage(&db);
510       // Restore received datablock
511       LOGDEBUG(
512         string msg("diameter::comm::ClientSession::receive | Restore answer to original request sequences (hop-by-hop = ");
513         msg += anna::functions::asString(response->getRequest()->getRequestHopByHop());
514         msg += ", end-to-end = ";
515         msg += anna::functions::asString(response->getRequest()->getRequestEndToEnd());
516         msg += ")";
517         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
518       );
519       diameter::codec::functions::setHopByHop((anna::DataBlock&)db, response->getRequest()->getRequestHopByHop());
520       diameter::codec::functions::setEndToEnd((anna::DataBlock&)db, response->getRequest()->getRequestEndToEnd());
521       eventResponse(*response);
522     } catch(anna::RuntimeException& ex) {
523       ex.trace();
524     }
525   }
526
527   response_erase(response);
528
529   // Unbind trigger
530   if(doUnbind)
531     unbind(true /* always immediate */);
532 }
533
534 void ServerSession::finalize() throw() {
535   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "finalize", ANNA_FILE_LOCATION));
536   // Configuration overiddings
537   setOnDisconnect(OnDisconnect::IgnorePendings);
538   notifyOrphansOnExpiration(false);
539   // session will be idle after 'Session::finalize'
540   // (anyway at 99% of the cases, server session would be idle because is not usual to send request from diameter servers)
541   // Closing state won't be used on server-sessions
542   Session::finalize(); // sets closed state (unbind at closeServerSession won't repeat client socket close, because returns at the beginning)
543   // stops timers
544
545   // "delete this" indirecto (seria mas elegante borrar las server sessions deprecated mediante un temporizador de purgado)
546   try {
547     if(idle()) getParent()->closeServerSession(this);  // http://www.parashift.com/c++-faq-lite/delete-this.html
548   } catch(anna::RuntimeException& ex) {
549     ex.trace();
550   }
551
552   // Inform father local server (availability changes):
553   bool changes = getParent()->refreshAvailability();
554   // OAM
555   bool multipleConnections = (getParent()->getMaxConnections() > 1);
556   std::string socket = anna::functions::socketLiteralAsString(getAddress(), getPort());
557   OamModule &oamModule = OamModule::instantiate();
558
559   if(multipleConnections) {
560     oamModule.activateAlarm(OamModule::Alarm::LostConnectionForServerSessionAtLocalServer__s__ServerSessionId__d__, socket.c_str(), getSocketId());
561     oamModule.count(OamModule::Counter::LostConnectionForServerSession);
562   } else {
563     oamModule.activateAlarm(OamModule::Alarm::LostConnectionForServerSessionAtLocalServer__s__, socket.c_str());
564     oamModule.count(OamModule::Counter::LostConnectionForServerSession);
565   }
566 }
567
568
569
570 void ServerSession::sendCEA()
571 throw(anna::RuntimeException) {
572   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "sendCEA", ANNA_FILE_LOCATION));
573   anna::DataBlock cea(true);
574   a_engine->readCEA(cea, a_cer.getBody()); // Asume that CEA is valid ...
575   // If one peer sends a CER message to another Peer and receiver does not have support for
576   //
577   //  1) any common application then it must return the CEA with Result-Code Avp set to DIAMETER_NO_COMMON_APPLICATION
578   //     and should disconnect the transport layer connection (automatically done by diameter::comm module).
579   //  2) no common security mechanism then it must return the CEA with Result-Code Avp set to DIAMETER_NO_COMMON_SECURITY
580   //     and should disconnect the transport layer connection (automatically done by diameter::comm module).
581   //  3) if CER is received from any unknown peer then receiver should discard the message, or send the CEA with the
582   //     Result-Code Avp set to DIAMETER_UNKNOWN_PEER.
583
584   if(cea.isEmpty()) {
585     LOGDEBUG(anna::Logger::debug("Empty CEA message. Remote client never will bound this connection at application level", ANNA_FILE_LOCATION));
586     LOGWARNING(anna::Logger::warning("Discarding received CER without sending CEA (consider to send CEA with Result-Code DIAMETER_UNKNOWN_PEER)", ANNA_FILE_LOCATION));
587     return;
588   }
589
590   Message msgCea;
591   msgCea.setBody(cea);
592   send(&msgCea);
593   // Here, CEA has been sent (no exception). Analize CEA Result-Code chosen:
594   int resultCode = 0;
595
596   try {
597     resultCode = helpers::base::functions::getResultCode(cea);
598   } catch(anna::RuntimeException& ex) {
599     ex.trace();
600   }
601
602   switch(resultCode) {
603   case helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS:
604     setState(State::Bound);
605     break;
606   case helpers::base::AVPVALUES__Result_Code::DIAMETER_NO_COMMON_APPLICATION:
607     LOGWARNING(anna::Logger::warning("DIAMETER_NO_COMMON_APPLICATION CEA Result-Code implies unbinding connection", ANNA_FILE_LOCATION));
608     unbind(true /* always immediate */); // no delegamos en un planning o similar
609     // Realmente el cliente diameter tambien deberia cerrar la conexion al recibir este Result-Code
610     break;
611   case helpers::base::AVPVALUES__Result_Code::DIAMETER_NO_COMMON_SECURITY:
612     LOGWARNING(anna::Logger::warning("DIAMETER_NO_COMMON_SECURITY CEA Result-Code implies unbinding connection", ANNA_FILE_LOCATION));
613     unbind(true /* always immediate */); // no delegamos en un planning o similar
614     // Realmente el cliente diameter tambien deberia cerrar la conexion al recibir este Result-Code
615     break;
616     // ...
617     // ...
618   }
619 }
620
621 void ServerSession::sendDWA()
622 throw(anna::RuntimeException) {
623   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "sendDWA", ANNA_FILE_LOCATION));
624   anna::DataBlock dwa(true);
625   a_engine->readDWA(dwa, a_dwr.getBody()); // Asume that DWA is valid ...
626
627   if(dwa.isEmpty())
628     throw anna::RuntimeException("This diameter agent defines an empty DWA message. Remote client never will validate this connection health", ANNA_FILE_LOCATION);
629
630   Message msgDwa;
631   msgDwa.setBody(dwa);
632   send(&msgDwa);
633 }
634
635
636 //-------------------------------------------------------------------------
637 // Se invoca desde diameter::comm::Timer
638 //-------------------------------------------------------------------------
639 void ServerSession::expireResponse(diameter::comm::Response* response)
640 throw() {
641   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expireResponse", ANNA_FILE_LOCATION));
642   Session::expireResponse(response);
643   // OAM
644   OamModule &oamModule = OamModule::instantiate();
645   oamModule.count(OamModule::Counter::RequestSentExpired);
646   oamModule.count(OamModule::Counter::RequestSentOnServerSessionExpired);
647   oamModule.activateAlarm(OamModule::Alarm::RequestSentOnServerSessionExpired);
648 //   // "delete this" indirecto (seria mas elegante borrar las server sessions deprecated mediante un temporizador de purgado)
649 //   if (idle()) a_parent->eraseServerSession(*a_clientSocket); // http://www.parashift.com/c++-faq-lite/delete-this.html
650 }
651
652 std::string ServerSession::asString() const
653 throw() {
654   string result = Session::asString();
655   result += " | Parent Local Server: ";
656   result += anna::functions::socketLiteralAsString(getAddress(), getPort());
657   result += " | Client Socket: ";
658   result += a_clientSocket->asString();
659   // Diferente del timeout de ApplicationMessage:
660   result += " | Allowed inactivity time: ";
661   result += getTimeout().asString();
662   result += " | Deprecated: ";
663   result += (a_deprecated ? "yes" : "no");
664   return result += " }";
665 }
666
667 anna::xml::Node* ServerSession::asXML(anna::xml::Node* parent) const
668 throw() {
669   anna::xml::Node* result = Session::asXML(parent);
670   parent->createChild("diameter.comm.ServerSession");
671   result->createAttribute("ParentLocalServer", anna::functions::socketLiteralAsString(getAddress(), getPort()));
672   result->createAttribute("ClientSocket", a_clientSocket->asString());
673   // Diferente del timeout de ApplicationMessage:
674   result->createAttribute("AllowedInactivityTime", getTimeout().asString());
675   result->createAttribute("Deprecated", a_deprecated ? "yes" : "no");
676   return result;
677 }
678
679
680 //------------------------------------------------------------------------------
681 //------------------------------------------------------ ServerSession::expire()
682 //------------------------------------------------------------------------------
683 void ServerSession::expire(anna::timex::Engine *timeController) throw(anna::RuntimeException) {
684   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expire (inactivity check timer)", ANNA_FILE_LOCATION));
685   LOGWARNING(anna::Logger::warning("Detecting anomaly (too inactivity time) over server session. Resetting", ANNA_FILE_LOCATION));
686   // OAM
687   bool multipleConnections = (getParent()->getMaxConnections() > 1);
688   std::string socket = anna::functions::socketLiteralAsString(getAddress(), getPort());
689   OamModule &oamModule = OamModule::instantiate();
690
691   if(multipleConnections) {
692     oamModule.activateAlarm(OamModule::Alarm::UnbindConnectionForServerSessionAtLocalServer__s__ServerSessionId__d__DueToInactivityTimeAnomaly, socket.c_str(), getSocketId());
693     oamModule.count(OamModule::Counter::UnbindConnectionForServerSessionDueToInactivityTimeAnomaly);
694   } else {
695     oamModule.activateAlarm(OamModule::Alarm::UnbindConnectionForServerSessionAtLocalServer__s__DueToInactivityTimeAnomaly, socket.c_str());
696     oamModule.count(OamModule::Counter::UnbindConnectionForServerSessionDueToInactivityTimeAnomaly);
697   }
698
699   unbind(true /* always immediate */); // no delegamos en un planning o similar
700 }
701
702 void ServerSession::setAllowedInactivityTime(const anna::Millisecond & allowedInactivityTime) throw() {
703   setTimeout(allowedInactivityTime);
704 }
705
706 //------------------------------------------------------------------------------
707 //---------------------------------- ServerSession::updateIncomingActivityTime()
708 //------------------------------------------------------------------------------
709 void ServerSession::updateIncomingActivityTime() throw() {
710   Session::updateIncomingActivityTime();
711   a_parent->updateIncomingActivityTime();
712 }
713
714
715 //------------------------------------------------------------------------------
716 //---------------------------------- ServerSession::updateOutgoingActivityTime()
717 //------------------------------------------------------------------------------
718 void ServerSession::updateOutgoingActivityTime(void) throw() {
719   Session::updateOutgoingActivityTime();
720   a_parent->updateOutgoingActivityTime();
721 }
722
723
724 //------------------------------------------------------------------------------
725 //----------------------------------------------- ServerSession::countSendings()
726 //------------------------------------------------------------------------------
727 void ServerSession::countSendings(const diameter::CommandId & cid, bool ok)throw() {
728   OamModule &oamModule = OamModule::instantiate();
729   bool isRequest = cid.second;
730
731   if(ok) {
732     // Main counters:
733     oamModule.count(isRequest ? OamModule::Counter::RequestSentOK : OamModule::Counter::AnswerSentOK);
734     oamModule.count(isRequest ? OamModule::Counter::RequestSentOnServerSessionOK : OamModule::Counter::AnswerSentOnServerSessionOK);
735
736     if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) oamModule.count(OamModule::Counter::CEASentOK);
737     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) oamModule.count(OamModule::Counter::DWASentOK);
738     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentOK);  // not usual
739     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentOK);
740     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentOK);
741   } else {
742     // Main counters:
743     oamModule.count(isRequest ? OamModule::Counter::RequestSentNOK : OamModule::Counter::AnswerSentNOK);
744     oamModule.count(isRequest ? OamModule::Counter::RequestSentOnServerSessionNOK : OamModule::Counter::RequestSentOnServerSessionNOK);
745
746     if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) oamModule.count(OamModule::Counter::CEASentNOK);
747     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) oamModule.count(OamModule::Counter::DWASentNOK);
748     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentNOK);  // not usual
749     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentNOK);
750     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentNOK);
751   }
752 }
753
754