5e78ab32429407fa79a01c001fdfc9c60b5aad39
[anna.git] / example / diameter / launcher / MyDiameterEntity.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 // Project
10 #include <anna/core/core.hpp>
11 #include <anna/diameter/functions.hpp>
12 #include <anna/time/functions.hpp>
13 #include <anna/diameter.comm/Response.hpp>
14 #include <anna/diameter.comm/ClientSession.hpp>
15 #include <anna/diameter/helpers/base/functions.hpp>
16 #include <anna/diameter/helpers/dcca/functions.hpp>
17
18 // Process
19 #include <MyDiameterEngine.hpp>
20 #include <MyDiameterEntity.hpp>
21 #include <MyLocalServer.hpp>
22 #include <Launcher.hpp>
23 #include <OriginHost.hpp>
24 #include <TestManager.hpp>
25
26
27 void MyDiameterEntity::eventRequestRetransmission(const anna::diameter::comm::ClientSession* clientSession, anna::diameter::comm::Message *request) throw() {
28
29   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventRequestRetransmission", ANNA_FILE_LOCATION));
30
31   // Base class:
32   Entity::eventRequestRetransmission(clientSession, request); // warning trace
33
34   // Performance stats:
35   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
36   OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost());
37   // CommandId:
38   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(request->getBody());
39   LOGDEBUG
40   (
41     std::string msg = "Request retransmitted: ";
42     msg += anna::diameter::functions::commandIdAsPairString(cid);
43     msg += " | DiameterServer: ";
44     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
45     msg += " | EventTime: ";
46     msg += anna::time::functions::currentTimeAsString();
47     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
48   );
49
50   // Write retransmission
51   if(my_node->logEnabled()) my_node->writeLogFile(request->getBody(), "retry", clientSession->asString());
52 }
53
54
55 int MyDiameterEntity::readSocketId(const anna::diameter::comm::Message* message, int maxClientSessions) const throw() {
56
57   if(a_sessionBasedModelsType == "RoundRobin") return -1;  // IEC also would return -1
58
59   try {
60     // Service-Context-Id:
61     anna::diameter::helpers::dcca::ChargingContext::_v chargingContext;
62     std::string scid = anna::diameter::helpers::dcca::functions::getServiceContextId(message->getBody(), chargingContext);
63
64     switch(chargingContext) {
65     case anna::diameter::helpers::dcca::ChargingContext::Data:
66     case anna::diameter::helpers::dcca::ChargingContext::Voice:
67     case anna::diameter::helpers::dcca::ChargingContext::Content: {
68       // Session-Id: '<DiameterIdentity>;<high 32 bits>;<low 32 bits>[;<optional value>="">]'
69       std::string sid = anna::diameter::helpers::base::functions::getSessionId(message->getBody());
70       std::string diameterIdentity, optional;
71       anna::U32 high, low;
72       anna::diameter::helpers::base::functions::decodeSessionId(sid, diameterIdentity, high, low /* context-teid */, optional);
73
74       if(a_sessionBasedModelsType == "SessionIdLowPart") return (low % maxClientSessions);
75
76       if(a_sessionBasedModelsType == "SessionIdHighPart") return (high % maxClientSessions);
77
78       if(a_sessionBasedModelsType == "SessionIdOptionalPart") return (atoi(optional.c_str()) % maxClientSessions);
79     }
80     case anna::diameter::helpers::dcca::ChargingContext::SMS:
81     case anna::diameter::helpers::dcca::ChargingContext::MMS:
82     case anna::diameter::helpers::dcca::ChargingContext::Unknown:
83     default:
84        return -1; // IEC model and Unknown traffic types
85     }
86   } catch(anna::RuntimeException &ex) {
87     LOGDEBUG(
88       std::string msg = ex.getText();
89       msg += " | Round-robin between sessions will be used to send";
90       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
91     );
92   }
93
94   return -1;
95 }
96
97 void MyDiameterEntity::eventRequest(anna::diameter::comm::ClientSession *clientSession, const anna::DataBlock &message)
98 throw(anna::RuntimeException) {
99   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventRequest", ANNA_FILE_LOCATION));
100   // Performance stats:
101   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
102   OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost());
103
104   // CommandId:
105   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
106   LOGDEBUG
107   (
108     std::string msg = "Request received: ";
109     msg += anna::diameter::functions::commandIdAsPairString(cid);
110     msg += " | DiameterServer: ";
111     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
112     msg += " | EventTime: ";
113     msg += anna::time::functions::currentTimeAsString();
114     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
115   );
116
117   // Write reception
118   if(my_node->logEnabled()) my_node->writeLogFile(message, "recvfe", clientSession->asString());
119
120   // Lookup reacting answers list:
121   int code = cid.first;
122   anna::diameter::codec::Message *answer_message = a_reactingAnswers.getMessage(code);
123   if (answer_message) {
124     // Prepare answer:
125     my_app.getCommunicator()->prepareAnswer(answer_message, message);
126     anna::diameter::comm::Message *msg;
127
128     try {
129       msg = my_node->createCommMessage();
130       msg->setBody(answer_message->code());
131       /* response = NULL =*/clientSession->send(msg);
132
133       if(my_node->logEnabled()) my_node->writeLogFile(*answer_message, "sent2e", clientSession->asString());
134     } catch(anna::RuntimeException &ex) {
135       ex.trace();
136
137       if(my_node->logEnabled()) my_node->writeLogFile(*answer_message, "send2eError", clientSession->asString());
138     }
139
140     // release msg
141     my_node->releaseCommMessage(msg);
142
143     // Pop front the reacting answer:
144     a_reactingAnswers.nextMessage(code);
145     return;
146   }
147
148   LOGDEBUG
149   (
150     std::string msg = "No answers programmed (maybe sold out) for request coming from entity: ";
151     msg += anna::diameter::functions::commandIdAsPairString(cid);
152     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
153   );
154
155   // not found: forward to client (if exists)
156   // Forward to client:
157   MyLocalServer *localServer = my_node->getDiameterServer();
158
159   if(localServer && (cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) /* don't forward CER */) {
160     try {
161       anna::diameter::comm::Message *msg = my_node->createCommMessage();
162       msg->forwardEndToEnd(); // end-to-end will be kept
163       msg->setBody(message);
164       msg->setRequestClientSessionKey(clientSession->getKey());
165       bool success = localServer->send(msg);
166
167       // Detailed log:
168       if(my_node->logEnabled()) {
169         anna::diameter::comm::ServerSession *usedServerSession = localServer->getLastUsedResource();
170         std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // esto no deberia ocurrir
171         my_node->writeLogFile(message, (success ? "fwd2c" : "fwd2cError"), detail);
172       }
173     } catch(anna::RuntimeException &ex) {
174       ex.trace();
175     }
176   }
177
178   // Testing:
179   TestManager::instantiate().receiveMessage(message, my_node, clientSession);
180 }
181
182 void MyDiameterEntity::eventResponse(const anna::diameter::comm::Response &response)
183 throw(anna::RuntimeException) {
184   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventResponse", ANNA_FILE_LOCATION));
185   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
186   OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost());
187   anna::diameter::comm::ClassCode::_v code = response.getClassCode();
188   anna::diameter::comm::Response::ResultCode::_v result = response.getResultCode();
189   anna::diameter::comm::Message* request = const_cast<anna::diameter::comm::Message*>(response.getRequest());
190   const anna::DataBlock* message = response.getMessage();
191   const anna::diameter::comm::ClientSession *clientSession = static_cast<const anna::diameter::comm::ClientSession *>(response.getSession());
192   bool isBindResponse = (code == anna::diameter::comm::ClassCode::Bind);
193   bool isApplicationMessage = (code == anna::diameter::comm::ClassCode::ApplicationMessage);
194   bool contextExpired = (result == anna::diameter::comm::Response::ResultCode::Timeout);
195   bool isUnavailable = (result == anna::diameter::comm::Response::ResultCode::DiameterUnavailable);
196   bool isOK = (result == anna::diameter::comm::Response::ResultCode::Success);
197
198   // CommandId:
199   anna::diameter::CommandId request_cid = request->getCommandId();
200   LOGDEBUG
201   (
202     std::string msg = "Response received for original diameter request: ";
203     msg += anna::diameter::functions::commandIdAsPairString(request_cid);
204     msg += " | Response: ";
205     msg += response.asString();
206     msg += " | DiameterServer: ";
207     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
208     msg += " | EventTime: ";
209     msg += anna::time::functions::currentTimeAsString();
210     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
211   );
212
213   if(isUnavailable) {
214     //if (isApplicationMessage)
215     LOGWARNING(anna::Logger::warning("Diameter entity unavailable for Diameter Request", ANNA_FILE_LOCATION));
216   }
217
218   if(contextExpired) {
219     //if (isApplicationMessage)
220     LOGWARNING(anna::Logger::warning("Context Expired for Diameter Request which was sent to the entity", ANNA_FILE_LOCATION));
221
222     if(request_cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) {  // don't trace CEA
223       if(my_node->logEnabled()) my_node->writeLogFile(*request, "req2e-expired", clientSession->asString());
224     }
225   }
226
227   if(isOK) {
228     LOGDEBUG(
229       std::string msg = "Received response for diameter message:  ";
230       msg += anna::diameter::functions::commandIdAsPairString(request_cid);
231       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
232     );
233
234     // Write reception
235     if(request_cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) {  // don't trace CEA
236       if(my_node->logEnabled()) {
237         my_node->writeLogFile(*message, "recvfe", clientSession->asString());
238       }
239     }
240
241     // Forward to client:
242     MyLocalServer *localServer = my_node->getDiameterServer();
243
244     if(localServer && (request_cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) /* don't forward CEA */) {
245       anna::diameter::comm::Message *msg;
246
247       try {
248         msg = my_node->createCommMessage();
249         msg->forwardEndToEnd(); // end-to-end will be kept
250         msg->setBody(*message);
251         bool success = localServer->send(msg, request->getRequestServerSessionKey());
252
253         // Detailed log:
254         anna::diameter::comm::ServerSession *usedServerSession = my_node->getMyDiameterEngine()->findServerSession(request->getRequestServerSessionKey());
255         std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // esto no deberia ocurrir
256
257         if(my_node->logEnabled()) {
258           my_node->writeLogFile(*message, (success ? "fwd2c" : "fwd2cError"), detail);
259         }
260       } catch(anna::RuntimeException &ex) {
261         ex.trace();
262       }
263
264       // release msgs
265       my_node->releaseCommMessage(msg);
266       my_node->releaseCommMessage(request);
267     }
268   }
269
270   // Triggering burst:
271   if(isOK || contextExpired) my_node->sendBurstMessage();
272
273   // Testing:
274   TestManager::instantiate().receiveMessage(*message, my_node, clientSession);
275 }
276
277 void MyDiameterEntity::eventUnknownResponse(anna::diameter::comm::ClientSession *clientSession, const anna::DataBlock &message)
278 throw(anna::RuntimeException) {
279   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventUnknownResponse", ANNA_FILE_LOCATION));
280   // Performance stats:
281   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
282   OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost());
283   // CommandId:
284   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
285   LOGDEBUG
286   (
287     std::string msg = "Out-of-context response received from entity: ";
288     msg += anna::diameter::functions::commandIdAsPairString(cid);
289     msg += " | DiameterServer: ";
290     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
291     msg += " | EventTime: ";
292     msg += anna::time::functions::currentTimeAsString();
293     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
294   );
295
296   // Write reception
297   if(my_node->logEnabled()) my_node->writeLogFile(message, "recvfe-ans-unknown", clientSession->asString());
298 }
299
300 void MyDiameterEntity::eventDPA(anna::diameter::comm::ClientSession *clientSession, const anna::DataBlock &message)
301 throw(anna::RuntimeException) {
302   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventDPA", ANNA_FILE_LOCATION));
303   // Performance stats:
304   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
305   OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost());
306   // CommandId:
307   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
308   LOGDEBUG
309   (
310     std::string msg = "Disconnect-Peer-Answer received from entity: ";
311     msg += anna::diameter::functions::commandIdAsPairString(cid);
312     msg += " | DiameterServer: ";
313     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
314     msg += " | EventTime: ";
315     msg += anna::time::functions::currentTimeAsString();
316     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
317   );
318
319   // Write reception
320   if(my_node->logEnabled()) my_node->writeLogFile(message, "recvfe", clientSession->asString());
321
322   // Testing:
323   TestManager::instantiate().receiveMessage(message, my_node, clientSession);
324 }