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