1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // https://bitbucket.org/testillano/anna
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
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
17 // * Neither the name of Google Inc. 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.
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.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
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.
45 #include <anna/core/DataBlock.hpp>
46 #include <anna/core/functions.hpp>
47 #include <anna/core/tracing/Logger.hpp>
53 using namespace anna::diameter::codec;
57 anna::diameter::CommandId functions::getCommandId(const anna::DataBlock & db) throw(anna::RuntimeException) {
58 if(db.getSize() < Message::HeaderLength)
59 throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
61 const char * data = db.getData();
63 U24 code = DECODE3BYTES_INDX_VALUETYPE(data, 5, U24);
64 // U24 code = (((U24)data[5] << 16) & 0xFF0000) +
65 // (((U24)data[6] << 8) & 0x00FF00) +
66 // (((U24)data[7]) & 0x0000FF);
67 return (anna::diameter::CommandId(code, (flags & Message::RBitMask) != 0x00));
71 bool functions::isRequest(const anna::DataBlock & db) throw(anna::RuntimeException) {
72 if(db.getSize() < Message::HeaderLength)
73 throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
75 return (((db.getData())[4] & Message::RBitMask) != 0x00);
78 anna::diameter::ApplicationId functions::getApplicationId(const anna::DataBlock & db) throw(anna::RuntimeException) {
79 if(db.getSize() < Message::HeaderLength)
80 throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
82 const char * appidPtr = db.getData() + 8;
83 anna::diameter::ApplicationId result = DECODE4BYTES_INDX_VALUETYPE(appidPtr, 0, U32);
84 // anna::diameter::ApplicationId result = (((U32)appidPtr[0] << 24) & 0xFF000000) +
85 // (((U32)appidPtr[1] << 16) & 0x00FF0000) +
86 // (((U32)appidPtr[2] << 8) & 0x0000FF00) +
87 // (((U32)appidPtr[3]) & 0x000000FF);
91 anna::diameter::HopByHop functions::getHopByHop(const anna::DataBlock & db) throw(anna::RuntimeException) {
92 if(db.getSize() < Message::HeaderLength)
93 throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
95 const char * hbhPtr = db.getData() + 12;
96 anna::diameter::HopByHop result = DECODE4BYTES_INDX_VALUETYPE(hbhPtr, 0, U32);
97 // anna::diameter::HopByHop result = (((U32)hbhPtr[0] << 24) & 0xFF000000) +
98 // (((U32)hbhPtr[1] << 16) & 0x00FF0000) +
99 // (((U32)hbhPtr[2] << 8) & 0x0000FF00) +
100 // (((U32)hbhPtr[3]) & 0x000000FF);
104 anna::diameter::EndToEnd functions::getEndToEnd(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);
108 const char * etePtr = db.getData() + 16;
109 anna::diameter::EndToEnd result = DECODE4BYTES_INDX_VALUETYPE(etePtr, 0, U32);
110 // anna::diameter::EndToEnd result = (((U32)etePtr[0] << 24) & 0xFF000000) +
111 // (((U32)etePtr[1] << 16) & 0x00FF0000) +
112 // (((U32)etePtr[2] << 8) & 0x0000FF00) +
113 // (((U32)etePtr[3]) & 0x000000FF);
117 void functions::decodeCommandHeader(const char *start, char & version, U24 & length, char & flags, diameter::CommandId & id, int & appId, int & hbh, int & ete) throw(anna::RuntimeException) {
119 throw anna::RuntimeException("NULL provided start pointer", ANNA_FILE_LOCATION);
122 // 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
123 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
124 // | Version | Message Length |
125 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
126 // | command flags | Command-Code |
127 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
128 // | Application-ID |
129 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
130 // | Hop-by-Hop Identifier |
131 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
132 // | End-to-End Identifier |
133 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
135 // +-+-+-+-+-+-+-+-+-+-+-+-+-
136 version = start[0]; // Version
137 //length = (start[1] << 16) + (start[2] << 8) + start[3]; // Message Length
138 length = DECODE3BYTES_INDX_VALUETYPE(start, 1, U24);
139 flags = start[4]; // command flags
140 //id.first = (start[5] << 16) + (start[6] << 8) + start[7]; // Command-Code
141 id.first = DECODE3BYTES_INDX_VALUETYPE(start, 5, U24);
142 id.second = ((flags & Message::RBitMask) != 0x00); // Command-Code is a request
143 //appId = (start[8] << 24) + (start[9] << 16) + (start[10] << 8) + start[11]; // Application-ID
144 appId = DECODE4BYTES_INDX_VALUETYPE(start, 8, U32);
145 //hbh = (start[12] << 24) + (start[13] << 16) + (start[14] << 8) + start[15]; // Hop-by-Hop Identifier
146 hbh = DECODE4BYTES_INDX_VALUETYPE(start, 12, U32);
147 //ete = (start[16] << 24) + (start[17] << 16) + (start[18] << 8) + start[19]; // End-to-End Identifier
148 ete = DECODE4BYTES_INDX_VALUETYPE(start, 16, U32);
151 void functions::decodeAVP(const char *start, diameter::AvpId & id, char & flags, int & length, std::string & data) throw(anna::RuntimeException) {
153 throw anna::RuntimeException("NULL provided start pointer", ANNA_FILE_LOCATION);
156 // 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
157 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
159 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
160 // | AVP Flags | AVP Length |
161 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
162 // | Vendor-ID (opt) |
163 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
166 //id.first = (start[0] << 24) + (start[1] << 16) + (start[2] << 8) + start[3]; // AVP Code
167 id.first = DECODE4BYTES_INDX_VALUETYPE(start, 0, S32);
168 flags = start[4]; // AVP Flags
169 //length = (start[5] << 16) + (start[6] << 8) + start[7]; // AVP Length
170 length = DECODE3BYTES_INDX_VALUETYPE(start, 5, U24);
171 bool vendorSpecific = ((flags & Avp::VBitMask) != 0x00); // AVP Code is vendor-specific
172 //id.second = (vendorSpecific ? ((start[8] << 24) + (start[9] << 16) + (start[10] << 8) + start[11]) : 0 /* IETF */); // Vendor-ID (opt)
173 id.second = (vendorSpecific ? (DECODE4BYTES_INDX_VALUETYPE(start, 8, S32)) : 0 /* IETF */); // Vendor-ID (opt)
174 int dataLength = (vendorSpecific ? (length - 12) : (length - 8)); // avp data part length
175 const char *dataPointer = (vendorSpecific ? (start + 12) : (start + 8)); // pointer to data part
176 data.assign(dataPointer, dataLength);
178 std::string msg = anna::functions::asString("decodedAVP id (%d,%d), length %d, data length %d, data part 0x%s",
179 id.first, id.second, length, dataLength, anna::functions::asHexString(anna::DataBlock(dataPointer, dataLength)).c_str());
180 anna::Logger::write(anna::Logger::Local3, msg, ANNA_FILE_LOCATION);
184 const char * functions::nextAVP(const anna::DataBlock & avpsDB, const char *start) throw(anna::RuntimeException) {
186 throw anna::RuntimeException("NULL provided start pointer", ANNA_FILE_LOCATION);
190 // std::string msg("DataBlock provided to 'nextAVP'");
191 // msg += avpsDB.asString();
192 // anna::Logger::debug(msg, ANNA_FILE_LOCATION);
194 //int avpLength = (start[5] << 16) + (start[6] << 8) + start[7]; // AVP Length
195 int avpLength = DECODE3BYTES_INDX_VALUETYPE(start, 5, int);
196 result = start + 4 * REQUIRED_WORDS(avpLength);
197 const char * first = avpsDB.getData();
198 int offset = (result - first);
200 if(offset > (avpsDB.getSize() - 1))
201 //throw anna::RuntimeException("Start pointer out of boundaries for DataBlock", ANNA_FILE_LOCATION);
207 const char * functions::findAVP(const anna::DataBlock & avpsDB, const diameter::AvpId & id, int n) throw(anna::RuntimeException) {
208 const char * result = avpsDB.getData(); // first avp
210 // Decoded avp information:
215 decodeAVP(result, _id, _flags, _length, _data);
217 if(_id == id) positives++;
219 while((_id != id) || (positives != n)) { // next search if not found or not ocurrence number reached
220 result = nextAVP(avpsDB, result);
222 if(result == NULL) { // (*)
224 std::string msg = "AVP ";
225 msg += anna::diameter::functions::avpIdAsPairString(id);
226 msg += " not found at DataBlock";
227 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
232 decodeAVP(result, _id, _flags, _length, _data);
234 if(_id == id) positives++;
242 void functions::setHopByHop(anna::DataBlock & db, diameter::HopByHop hbh) throw(anna::RuntimeException) {
243 if(db.getSize() < Message::HeaderLength) {
244 throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
247 static char source[4];
248 source[0] = (char)(hbh >> 24);
249 source[1] = (char)(hbh >> 16);
250 source[2] = (char)(hbh >> 8);
251 source[3] = (char)hbh;
252 memcpy((char*)(db.getData() + 12), source, 4);
256 void functions::setEndToEnd(anna::DataBlock & db, diameter::EndToEnd ete) throw(anna::RuntimeException) {
257 if(db.getSize() < Message::HeaderLength) {
258 throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
261 static char source[4];
262 source[0] = (char)(ete >> 24);
263 source[1] = (char)(ete >> 16);
264 source[2] = (char)(ete >> 8);
265 source[3] = (char)ete;
266 memcpy((char *)(db.getData() + 16), source, 4);