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