Improvements
[anna.git] / example / diameter / launcher / Procedure.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 // Standard
9 #include <iostream>
10 #include <string>
11 //#include <cstdlib>
12
13 // Project
14 #include <anna/core/util/Tokenizer.hpp>
15 #include <anna/core/Exception.hpp>
16 #include <TestManager.hpp>
17 #include <anna/diameter/codec/Message.hpp>
18 #include <anna/diameter/codec/Avp.hpp>
19 #include <anna/diameter/helpers/dcca/functions.hpp>
20 #include <anna/diameter/helpers/dcca/functions.hpp>
21
22 // Local
23 #include "Procedure.hpp"
24 #include "Launcher.hpp"
25
26 namespace {
27
28    void usage (std::string &response) {
29      response += "\n\nInvalid arguments. Provide these ones:";
30      response += "\n";
31      response += "\n<test timeout ms (0: no timeout step)><initial sequence>|<final sequence>|<digits>|<CCR-I xml file>[|CCR-T xml file]";
32      response += "\n";
33      response += "\nSequences are parsed when needed, over AVPs or internal values:";
34      response += "\n";
35      response += "\nSession-Id: <DiameterIdentity>;<high 32 bits>;<low 32 bits>[;<optional value>]";
36      response += "\n            \\_fixed_/\\_digits_/                            \\_fixed_/\\_digits_/";
37      response += "\n";
38      response += "\nFor example, imagine a 13-digits diameter identity, and a 10-digits optional part.";
39      response += "\nThis procedure will sequence the range for 8 digits in this way:";
40      response += "\n";
41      response += "\n111111<7-digit-sequence>;BAT004;esmdx0900.gxrel10plusrealm.com;901<7-digit-sequence>";
42      response += "\n";
43      response += "\nThe same will be done in MSISDN and IMSI (Subscription-Data AVPs).";
44      response += "\nFramed-IP-Address will be sequenced with a direct correspondence to hex value.";
45      response += "\n";
46      response += "\nThen, you could provide these arguments: \"5000|2000000|9000000|7|CCR-I.xml|CCR-T.xml\"";
47      response += "\n";
48    }
49 }
50
51 void Procedure::execute(const std::string &args, std::string &response)  throw(anna::RuntimeException) {
52
53   response = "Dynamic procedure failed to process '"; response += args; response += "': ";
54
55   anna::Tokenizer targs;
56   targs.apply(args, "|");
57
58   if (targs.size() < 5) {
59     usage(response);
60     return;
61   }
62
63   anna::Tokenizer::const_iterator tok_it = targs.begin();
64
65   std::string seq_i = anna::Tokenizer::data(tok_it); tok_it++;
66   std::string seq_f = anna::Tokenizer::data(tok_it); tok_it++;
67   std::string timeout = anna::Tokenizer::data(tok_it); tok_it++;
68   std::string digits = anna::Tokenizer::data(tok_it); tok_it++;
69   std::string ccr_i = anna::Tokenizer::data(tok_it); tok_it++;
70   std::string ccr_t = ((tok_it != targs.end()) ? anna::Tokenizer::data(tok_it):"");
71   bool haveTermination = (ccr_t != "");
72
73   // Test cases cycles:
74   int i_timeout = std::atoi(timeout.c_str());
75   unsigned int ll_seq_i = std::atol(seq_i.c_str());
76   unsigned int ll_seq_f = std::atol(seq_f.c_str());
77   unsigned int ll_seq_size = ll_seq_f - ll_seq_i + 1;
78   int i_digits = std::atoi(digits.c_str());
79
80   if (ll_seq_i > ll_seq_f) {
81     response += "<final sequence> must be greater or equal than <initial sequence>";
82     return;
83   }
84
85   if (seq_f.size() > i_digits) {
86     response += "<final sequence> must be lesser than number of <digits>";
87     return;
88   }
89
90   unsigned int ll_seq, ll_index;
91   anna::Millisecond timeoutMS(i_timeout);
92
93   // Load xml messages:
94   anna::diameter::codec::Message ccri, ccrt;
95   anna::diameter::codec::Avp *ccri_sessionId, *ccrt_sessionId, *ccri_framedIPAddress, *ccrt_framedIPAddress, *ccri_msisdn, *ccri_imsi;
96   anna::diameter::codec::Avp *si1, *si2, *sidata1, *sidata2, *sitype1, *sitype2;
97
98   try {
99
100     ///////// CCR-Initial:
101     ccri.loadXML(ccr_i);
102
103     // Session-Id & Framed-Ip-Address AVPs
104     ccri_sessionId = ccri.getAvp("Session-Id");
105     ccri_framedIPAddress = ccri.getAvp("Framed-IP-Address");
106
107     // Subscription-Id AVPs
108     if (ccri.countAvp("Subscription-Id") != 2) {
109       response += "Both Subscription-Id MSISDN & IMSI Avps must be present in the CCR-Initial provided !";
110       return;
111     }
112
113     si1 = ccri.getAvp("Subscription-Id", 1, anna::Exception::Mode::Ignore);
114     si2 = ccri.getAvp("Subscription-Id", 2, anna::Exception::Mode::Ignore);
115
116     if (!si1 || !si2) {
117       response += "Cannot found Subscription-Id MSISDN & IMSI Avps !" ;
118       return;
119     }
120
121     sidata1 = si1->getAvp("Subscription-Id-Data");
122     sidata2 = si2->getAvp("Subscription-Id-Data");
123     sitype1 = si1->getAvp("Subscription-Id-Type");
124     //sitype2 = si2->getAvp("Subscription-Id-Type");
125
126     if (sitype1->getEnumerated()->getValue() == anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164) {
127       ccri_msisdn = sidata1;
128       ccri_imsi = sidata2;
129     }
130     else {
131       ccri_msisdn = sidata2;
132       ccri_imsi = sidata1;
133     }
134
135     ///////// CCR-Termination:
136     if (haveTermination) {
137       ccrt.loadXML(ccr_t);
138
139       // Session-Id & Framed-Ip-Address AVPs
140       ccrt_sessionId = ccrt.getAvp("Session-Id");
141       ccrt_framedIPAddress = ccrt.getAvp("Framed-IP-Address");
142     }
143   }
144   catch(anna::RuntimeException &ex) {
145     ex.trace();
146   }
147
148   // Prepare session-id string:
149   std::string sessionId = ccri_sessionId->getUTF8String()->getValue();
150   std::size_t last_semicolon = sessionId.rfind(";");
151   anna::Tokenizer tsessionid;
152   tsessionid.apply(sessionId, ";");
153
154   if (tsessionid.size() < 4) {
155     response += "Session-Id must be in form '<a>;<b>;<c>;<d>'.\n\n";
156     usage(response);
157     return;
158   }
159
160   tok_it = tsessionid.begin();
161   std::string d_identity = anna::Tokenizer::data(tok_it); tok_it++; tok_it++; tok_it++;
162   std::string o_part = anna::Tokenizer::data(tok_it);
163   int d_identity_len = d_identity.size();
164   int left_di = d_identity_len - i_digits;
165   if (left_di < 0) {
166     response += "Session-Id diameter identity length is lower than selected number of digits ";
167     response += anna::functions::asString("(%d < %d).\n\n", d_identity_len, i_digits);
168     usage(response);
169     return;
170   }
171   int o_part_len = o_part.size();
172   int left_op = o_part_len - i_digits;
173   if (left_op < 0) {
174     response += "Session-Id optional part length is lower than selected number of digits ";
175     response += anna::functions::asString("(%d < %d).\n\n", o_part_len, i_digits);
176     usage(response);
177     return;
178   }
179
180   // Idem for MSISDN & IMSI Subscription-Data:
181   std::string msisdn = ccri_msisdn->getUTF8String()->getValue();
182   int msisdn_len = msisdn.size();
183   int left_msisdn = msisdn_len - i_digits;
184   if (left_msisdn < 0) {
185     response += "MSISDN Subscription-Data length is lower than selected number of digits ";
186     response += anna::functions::asString("(%d < %d).\n\n", left_msisdn, i_digits);
187     usage(response);
188     return;
189   }
190   std::string imsi = ccri_imsi->getUTF8String()->getValue();
191   int imsi_len = imsi.size();
192   int left_imsi = imsi_len - i_digits;
193   if (left_imsi < 0) {
194     response += "IMSI Subscription-Data length is lower than selected number of digits ";
195     response += anna::functions::asString("(%d < %d).\n\n", left_imsi, i_digits);
196     usage(response);
197     return;
198   }
199
200
201   // Format strings:
202   char ndigit_format[32];
203   sprintf(ndigit_format, "%s%d%s", "%0", i_digits, "u");
204
205   //response += "\nSession-Id original: "; response += sessionId;
206   //response += "\nMSISDN original:     "; response += msisdn;
207   //response += "\nIMSI original:       "; response += imsi;
208
209   // TestManager:
210   TestManager &testManager = TestManager::instantiate();
211   char cad_aux[16];
212   char cad_framed[16];
213   TestCase *tc;
214   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
215   OriginHost *oh = my_app.getWorkingNode();
216
217   for (ll_index = 0; ll_index < ll_seq_size; ll_index++) {
218
219     // Calculate next values ////////////////////////////////////////////////////////////
220     ll_seq = ll_seq_i + ll_index;
221     sprintf(cad_aux, ndigit_format, ll_seq);
222     sprintf(cad_framed, "%08x", ll_seq);
223
224     // Activity indicator:
225     if (ll_seq % 10000 == 0) std::cout << ".";
226
227     sessionId.replace(left_di, i_digits, cad_aux);
228     sessionId.replace(last_semicolon + left_op + 1, i_digits, cad_aux);
229
230     msisdn.replace(left_msisdn, i_digits, cad_aux);
231
232     imsi.replace(left_imsi, i_digits, cad_aux);
233
234     //response += "\nSession-Id modified: "; response += sessionId;
235     //response += "\nMSISDN modified:     "; response += msisdn;
236     //response += "\nIMSI modified:       "; response += imsi;
237     //response += "\nFramedIP modified:   "; response += cad_framed;
238
239     // Update diameter messages /////////////////////////////////////////////////////////
240     ccri_sessionId->getUTF8String()->setValue(sessionId);
241     ccri_framedIPAddress->getOctetString()->fromHexString(cad_framed);
242     ccri_msisdn->getUTF8String()->setValue(msisdn);
243     ccri_imsi->getUTF8String()->setValue(imsi);
244
245     if (haveTermination) {
246       ccrt_sessionId->getUTF8String()->setValue(sessionId);
247       ccrt_framedIPAddress->getOctetString()->fromHexString(cad_framed);
248     }
249
250     // Create testcase:
251     tc = testManager.getTestCase(ll_index+1);
252     //    test|__TESTID__|timeout|6000
253     //    test|__TESTID__|sendxml2e|ccr-i.xml
254     //    test|__TESTID__|waitfe|272|0|||11111__SEQ8__;BAT004;esmdx0900.gxrel10plusrealm.com;90__SEQ8__|2001
255     //    test|__TESTID__|sendxml2e|ccr-t.xml
256     //    test|__TESTID__|waitfe|272|0|||11111__SEQ8__;BAT004;esmdx0900.gxrel10plusrealm.com;90__SEQ8__|2001
257
258     if (i_timeout != 0) {
259       // Step 1: timeout 5000 ms:
260       tc->addTimeout(timeoutMS);
261     }
262
263     // Step 2: sendxml2e: CCR-Initial
264     tc->addSendxml2e(ccri.code(), oh, -1 /* 'wait for request' step number for answers */);
265
266     // Step 3: waitfe: CCA with same session id
267     // PARAM: 1     2            3      4          5           6             7           8          9       10         11
268     //             wait<fe/fc>|[code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
269     tc->addWait(true /* from entity */, "272", "0", "", "", sessionId, "2001", "", "", "");
270
271     if (haveTermination) {
272       // Step 4: sendxml2e: CCR-Termination
273       tc->addSendxml2e(ccrt.code(), oh, -1 /* 'wait for request' step number for answers */);
274
275       // Step 5: waitfe: CCA with same session id
276       tc->addWait(true /* from entity */, "272", "0", "", "", sessionId, "2001", "", "", "");
277     }
278   } // loop
279
280   response = "Completed provision for pid "; response += anna::functions::asString(a_app->getPid()); response += "; range [";
281   response += seq_i; response += ", "; response += seq_f; response += "]; scenary: ";
282   response += "CCR-Initial"; if (haveTermination) response += " + CCR-Termination";
283 }
284