Base protocol codec for comm::Engine. Supported retransmissions
[anna.git] / source / diameter / codec / functions.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 // Local
10 #include <anna/diameter/codec/functions.hpp>
11 #include <anna/diameter/codec/Message.hpp>
12 #include <anna/diameter/codec/Avp.hpp>
13 #include <anna/diameter/defines.hpp>
14 #include <anna/diameter/functions.hpp>
15 #include <anna/config/defines.hpp> // general types, decoding helpers (DECODE[2/3/4]BYTES_INDX_VALUETYPE), etc.
16
17 #include <anna/core/DataBlock.hpp>
18 #include <anna/core/functions.hpp>
19 #include <anna/core/tracing/Logger.hpp>
20
21 // STL
22 #include <string>
23
24
25 using namespace anna::diameter::codec;
26
27
28
29
30 // Parent struct helper /////////////////////////////////////////////////////////////////////////////
31 void parent::setMessage(const anna::diameter::CommandId & mid, const char *mname) throw() {
32   MessageId = mid;
33   if (mname) {
34     MessageName = mname;
35   }
36   else {
37         MessageName = "Message";
38     MessageName += anna::diameter::functions::commandIdAsPairString(mid);
39   }
40 }
41
42 void parent::addAvp(const anna::diameter::AvpId & aid, const char *aname) throw() {
43   AvpsId.push_back(aid);
44   std::string name;
45   if (aname) {
46         name = aname;
47   }
48   else {
49     name = "Avp";
50         name += anna::diameter::functions::avpIdAsPairString(aid);
51   }
52   AvpsName.push_back(name);
53 }
54
55 std::string parent::asString() const throw() { // "<command><avp 1>-><avp 2>->...-><avp N>"
56   std::string result = MessageName;
57   for (std::vector<std::string>::const_iterator it = AvpsName.begin(); it != AvpsName.end(); it++) {
58         result += "->";
59         result += (*it);
60   }
61
62   return result;
63 }
64 /////////////////////////////////////////////////////////////////////////////////////////////////////
65
66
67
68
69 // getters
70 anna::diameter::CommandId functions::getCommandId(const anna::DataBlock & db) throw(anna::RuntimeException) {
71   if(db.getSize() < Message::HeaderLength)
72     throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
73
74   const char * data = db.getData();
75   U8 flags =  data[4];
76   U24 code = DECODE3BYTES_INDX_VALUETYPE(data, 5, U24);
77 //   U24 code = (((U24)data[5] << 16) & 0xFF0000) +
78 //              (((U24)data[6] << 8)  & 0x00FF00) +
79 //              (((U24)data[7]) & 0x0000FF);
80   return (anna::diameter::CommandId(code, (flags & Message::RBitMask) != 0x00));
81 }
82
83 bool functions::requestBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
84   if(db.getSize() < Message::HeaderLength)
85     throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
86
87   return (((db.getData())[4] & Message::RBitMask) != 0x00);
88 }
89
90 bool functions::proxiableBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
91   if(db.getSize() < Message::HeaderLength)
92     throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
93
94   return (((db.getData())[4] & Message::PBitMask) != 0x00);
95 }
96
97 bool functions::errorBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
98   if(db.getSize() < Message::HeaderLength)
99     throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
100
101   return (((db.getData())[4] & Message::EBitMask) != 0x00);
102 }
103
104 bool functions::potentiallyReTransmittedMessageBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
105   if(db.getSize() < Message::HeaderLength)
106     throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
107
108   return (((db.getData())[4] & Message::TBitMask) != 0x00);
109 }
110
111 anna::diameter::ApplicationId functions::getApplicationId(const anna::DataBlock & db) throw(anna::RuntimeException) {
112   if(db.getSize() < Message::HeaderLength)
113     throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
114
115   const char * appidPtr = db.getData() + 8;
116   anna::diameter::ApplicationId result = DECODE4BYTES_INDX_VALUETYPE(appidPtr, 0, U32);
117 //   anna::diameter::ApplicationId result = (((U32)appidPtr[0] << 24) & 0xFF000000) +
118 //                               (((U32)appidPtr[1] << 16) & 0x00FF0000) +
119 //                               (((U32)appidPtr[2] << 8) & 0x0000FF00) +
120 //                               (((U32)appidPtr[3]) & 0x000000FF);
121   return result;
122 }
123
124 anna::diameter::HopByHop functions::getHopByHop(const anna::DataBlock & db) throw(anna::RuntimeException) {
125   if(db.getSize() < Message::HeaderLength)
126     throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
127
128   const char * hbhPtr = db.getData() + 12;
129   anna::diameter::HopByHop result = DECODE4BYTES_INDX_VALUETYPE(hbhPtr, 0, U32);
130 //   anna::diameter::HopByHop result = (((U32)hbhPtr[0] << 24) & 0xFF000000) +
131 //                               (((U32)hbhPtr[1] << 16) & 0x00FF0000) +
132 //                               (((U32)hbhPtr[2] << 8) & 0x0000FF00) +
133 //                               (((U32)hbhPtr[3]) & 0x000000FF);
134   return result;
135 }
136
137 anna::diameter::EndToEnd functions::getEndToEnd(const anna::DataBlock & db) throw(anna::RuntimeException) {
138   if(db.getSize() < Message::HeaderLength)
139     throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
140
141   const char * etePtr = db.getData() + 16;
142   anna::diameter::EndToEnd result = DECODE4BYTES_INDX_VALUETYPE(etePtr, 0, U32);
143 //   anna::diameter::EndToEnd result = (((U32)etePtr[0] << 24) & 0xFF000000) +
144 //                               (((U32)etePtr[1] << 16) & 0x00FF0000) +
145 //                               (((U32)etePtr[2] << 8) & 0x0000FF00) +
146 //                               (((U32)etePtr[3]) & 0x000000FF);
147   return result;
148 }
149
150 void functions::decodeCommandHeader(const char *start, char & version, U24 & length, char & flags, diameter::CommandId & id, int & appId, int & hbh, int & ete) throw(anna::RuntimeException) {
151   if(start == NULL)
152     throw anna::RuntimeException("NULL provided start pointer", ANNA_FILE_LOCATION);
153
154   //      0                   1                   2                   3
155   //      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
156   //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
157   //      |    Version    |                 Message Length                |
158   //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
159   //      | command flags |                  Command-Code                 |
160   //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
161   //      |                         Application-ID                        |
162   //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
163   //      |                      Hop-by-Hop Identifier                    |
164   //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
165   //      |                      End-to-End Identifier                    |
166   //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
167   //      |  AVPs ...
168   //      +-+-+-+-+-+-+-+-+-+-+-+-+-
169   version = start[0]; // Version
170   //length = (start[1] << 16) + (start[2] << 8) + start[3]; // Message Length
171   length = DECODE3BYTES_INDX_VALUETYPE(start, 1, U24);
172   flags = start[4]; // command flags
173   //id.first = (start[5] << 16) + (start[6] << 8) + start[7]; // Command-Code
174   id.first = DECODE3BYTES_INDX_VALUETYPE(start, 5, U24);
175   id.second = ((flags & Message::RBitMask) != 0x00); // Command-Code is a request
176   //appId = (start[8] << 24) + (start[9] << 16) + (start[10] << 8) + start[11]; // Application-ID
177   appId = DECODE4BYTES_INDX_VALUETYPE(start, 8, U32);
178   //hbh = (start[12] << 24) + (start[13] << 16) + (start[14] << 8) + start[15]; // Hop-by-Hop Identifier
179   hbh = DECODE4BYTES_INDX_VALUETYPE(start, 12, U32);
180   //ete = (start[16] << 24) + (start[17] << 16) + (start[18] << 8) + start[19]; // End-to-End Identifier
181   ete = DECODE4BYTES_INDX_VALUETYPE(start, 16, U32);
182 }
183
184 void functions::decodeAVP(const char *start, diameter::AvpId & id, char & flags, int & length, std::string & data) throw(anna::RuntimeException) {
185   if(start == NULL)
186     throw anna::RuntimeException("NULL provided start pointer", ANNA_FILE_LOCATION);
187
188   //       0                   1                   2                   3
189   //       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
190   //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
191   //      |                           AVP Code                            |
192   //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
193   //      |   AVP Flags   |                  AVP Length                   |
194   //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
195   //      |                        Vendor-ID (opt)                        |
196   //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
197   //      |    Data ...
198   //      +-+-+-+-+-+-+-+-+
199   //id.first = (start[0] << 24) + (start[1] << 16) + (start[2] << 8) + start[3]; // AVP Code
200   id.first = DECODE4BYTES_INDX_VALUETYPE(start, 0, S32);
201   flags = start[4]; // AVP Flags
202   //length = (start[5] << 16) + (start[6] << 8) + start[7]; // AVP Length
203   length = DECODE3BYTES_INDX_VALUETYPE(start, 5, U24);
204   bool vendorSpecific = ((flags & Avp::VBitMask) != 0x00); // AVP Code is vendor-specific
205   //id.second = (vendorSpecific ? ((start[8] << 24) + (start[9] << 16) + (start[10] << 8) + start[11]) : 0 /* IETF */); // Vendor-ID (opt)
206   id.second = (vendorSpecific ? (DECODE4BYTES_INDX_VALUETYPE(start, 8, S32)) : 0 /* IETF */); // Vendor-ID (opt)
207   int dataLength = (vendorSpecific ? (length - 12) : (length - 8)); // avp data part length
208   const char *dataPointer = (vendorSpecific ? (start + 12) : (start + 8)); // pointer to data part
209   data.assign(dataPointer, dataLength);
210   LOGLOCAL3(
211     std::string msg = anna::functions::asString("decodedAVP id (%d,%d), length %d, data length %d, data part 0x%s",
212                       id.first, id.second, length, dataLength, anna::functions::asHexString(anna::DataBlock(dataPointer, dataLength)).c_str());
213     anna::Logger::write(anna::Logger::Local3, msg, ANNA_FILE_LOCATION);
214   );
215 }
216
217 const char * functions::nextAVP(const anna::DataBlock & avpsDB, const char *start) throw(anna::RuntimeException) {
218   if(start == NULL)
219     throw anna::RuntimeException("NULL provided start pointer", ANNA_FILE_LOCATION);
220
221   const char *result;
222 //   LOGDEBUG(
223 //      std::string msg("DataBlock provided to 'nextAVP'");
224 //      msg += avpsDB.asString();
225 //      anna::Logger::debug(msg, ANNA_FILE_LOCATION);
226 //   );
227   //int avpLength = (start[5] << 16) + (start[6] << 8) + start[7]; // AVP Length
228   int avpLength = DECODE3BYTES_INDX_VALUETYPE(start, 5, int);
229   result = start + 4 * REQUIRED_WORDS(avpLength);
230   const char * first = avpsDB.getData();
231   int offset = (result - first);
232
233   if(offset > (avpsDB.getSize() - 1))
234     //throw anna::RuntimeException("Start pointer out of boundaries for DataBlock", ANNA_FILE_LOCATION);
235     return NULL; // (*)
236
237   return result;
238 }
239
240 const char * functions::findAVP(const anna::DataBlock & avpsDB, const diameter::AvpId & id, int n) throw(anna::RuntimeException) {
241   const char * result = avpsDB.getData(); // first avp
242   int positives = 0;
243   // Decoded avp information:
244   diameter::AvpId _id;
245   char _flags;
246   int _length;
247   std::string _data;
248   decodeAVP(result, _id, _flags, _length, _data);
249
250   if(_id == id) positives++;
251
252   while((_id != id) || (positives != n)) {  // next search if not found or not ocurrence number reached
253     result = nextAVP(avpsDB, result);
254
255     if(result == NULL) {  // (*)
256       LOGDEBUG(
257         std::string msg = "AVP ";
258         msg += anna::diameter::functions::avpIdAsPairString(id);
259         msg += " not found at DataBlock";
260         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
261       );
262       return NULL;
263     }
264
265     decodeAVP(result, _id, _flags, _length, _data);
266
267     if(_id == id) positives++;
268   }
269
270   return result;
271 }
272
273
274 // modifiers
275 void functions::setHopByHop(anna::DataBlock & db, diameter::HopByHop hbh) throw(anna::RuntimeException) {
276   if(db.getSize() < Message::HeaderLength) {
277     throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
278   }
279
280   static char source[4];
281   source[0] = (char)(hbh >> 24);
282   source[1] = (char)(hbh >> 16);
283   source[2] = (char)(hbh >> 8);
284   source[3] = (char)hbh;
285   memcpy((char*)(db.getData() + 12), source, 4);
286 }
287
288
289 void functions::setEndToEnd(anna::DataBlock & db, diameter::EndToEnd ete) throw(anna::RuntimeException) {
290   if(db.getSize() < Message::HeaderLength) {
291     throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
292   }
293
294   static char source[4];
295   source[0] = (char)(ete >> 24);
296   source[1] = (char)(ete >> 16);
297   source[2] = (char)(ete >> 8);
298   source[3] = (char)ete;
299   memcpy((char *)(db.getData() + 16), source, 4);
300 }
301
302 void functions::setPotentiallyReTransmittedMessageBit(const anna::DataBlock & db, bool activate) throw(anna::RuntimeException) {
303   if(db.getSize() < Message::HeaderLength) {
304     throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
305   }
306
307   static char flags[1];
308   flags[0] = *(db.getData() + 4);
309   if(activate) flags[0] |= Message::TBitMask; else flags[0] &= (~Message::TBitMask);
310   memcpy((char *)(db.getData() + 4), flags, 1);
311 }
312
313