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/EngineImpl.hpp>
39 #include <anna/diameter/codec/Message.hpp>
40 #include <anna/diameter/codec/Avp.hpp>
41 #include <anna/diameter/stack/Engine.hpp>
42 #include <anna/diameter/stack/Dictionary.hpp>
43 #include <anna/diameter/stack/Command.hpp>
44 #include <anna/diameter/internal/sccs.hpp>
45 #include <anna/diameter/defines.hpp>
47 #include <anna/xml/xml.hpp>
48 #include <anna/core/tracing/Logger.hpp>
49 #include <anna/core/tracing/TraceMethod.hpp>
50 #include <anna/core/functions.hpp>
51 #include <anna/core/mt/Guard.hpp>
59 const char *MessageDTD = "\
60 <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
61 <!-- Diameter message DTD -->\n\
63 <!ELEMENT message (avp*)>\n\
64 <!ELEMENT avp (avp*)>\n\
66 <!ATTLIST message version CDATA #IMPLIED name CDATA #IMPLIED code CDATA #IMPLIED flags CDATA #IMPLIED p-bit (yes | no) #IMPLIED e-bit (yes | no) #IMPLIED t-bit (yes | no) #IMPLIED application-id CDATA #REQUIRED hop-by-hop-id CDATA #IMPLIED end-by-end-id CDATA #IMPLIED>\n\
68 version: Diameter version. Sets '1' by default\n\
69 name: Command name within working stack (dictionary identifier)\n\
70 p-bit: (P)roxiable bit flag (yes, no). By default is 'no'\n\
71 e-bit: (E)rror bit flag (yes, no). By default is 'no'\n\
72 t-bit: Potentially re-(T)ransmitted bit flag (yes, no). By default is 'no'\n\
74 In order to get more coding capabilities, command code and flags could be established instead of former fields,\n\
75 but neither of them are allowed if the other are present (and vice versa):\n\
78 flags: Command flags byte value (0-255) where standard bit set for flags is 'RPET rrrr': (R)equest, (P)roxiable, (E)rror, Potentially re-(T)ransmitted message and (r)eserved\n\
81 application-id: Message application id\n\
82 hop-by-hop-id: Message hop by hop id. Sets '0' by default\n\
83 end-by-end-id: Message end by end id. Sets '0' by default\n\
86 <!ATTLIST avp name CDATA #IMPLIED code CDATA #IMPLIED vendor-code CDATA #IMPLIED flags CDATA #IMPLIED data CDATA #IMPLIED hex-data CDATA #IMPLIED alias CDATA #IMPLIED>\n\
88 name: Avp name within working stack (dictionary identifier)\n\
90 In order to get more coding capabilities, avp code, vendor-id and flags could be established instead of former avp name,\n\
91 but neither of them are allowed if 'name' is provided (and vice versa):\n\
94 vendor-code: Avp vendor code\n\
95 flags: Avp flags byte value (0-255) where standard bit set for flags is 'VMPr rrrr': (V)endor-specific, (M)andatory, end to end encry(P)tion and r(eserved)\n\
96 alias: Descriptive/helper field for certain numeric data values. Aliases are defined at diameter dictionary, but are ignored (not checked) at xml message parsing\n\
97 The reason to include it in dtd definition, is because xml messages traced by the diameter codec could add alias field for some Avps\n\
100 data: Natural string representation for avp data. Specially applicable with numbers and printable strings, but also\n\
101 useful for certain formats which could be easily understandable in such friendly/smart representation. We will\n\
102 achieve different human-readable strings depending on data format:\n\
104 [ OctetString ] (if printable, but not recommended)\n\
105 [ Integer32, Integer64, Unsigned32, Unsigned64, Float32, Float64 ] (normal number representation)\n\
106 [ Time ] (NTP timestamp, normal number representation)\n\
107 [ Address ] ('<type (IANA Address Family Number)>|<value>' representation; i.e. '1|192.168.0.1'(IPv4), '8|34616279266'(E164), etc.\n\
108 Type (and pipe) field could be avoided for IPv4 and IPv6 address types (a light parse checking is done: one colon for\n\
109 IPv6, one dot for IPv4). Internal engine always includes type on data field, which is also recommended for inputs.\n\
110 Currently, only IPv4, IPv6 and E164 address types have a known printable presentation, anyway using printable format\n\
111 for another types will encode the address value directly as E164 does)\n\
112 [ UTF8String, DiameterIdentity, DiameterURI ] (printable)\n\
113 [ IPFilterRule, QoSFilterRule ] (uses ASCII charset, printable)\n\
115 New application formats must define specific natural representation for internal raw data\n\
117 hex-data: Hexadecimal octet sequence representation (i.e. 'af012fb3', with even number of digits). Suitable for whatever kind\n\
118 of diameter format, but mandatory for non printable information. OctetString usually transport non human-readable\n\
119 data and should better be encoded within this field although being printable. Unknown avps (which fails identifying\n\
120 provided name or code/vendor-code) must always use this representation.\n\
122 Xml representation for decoded messages shows natural content except for 'OctetString' format and unknown avps. Anyway, when printable,\n\
123 OctetString could show such information at data field apart from hex-data, because many implementations use this format to transport\n\
124 readable-string data. In general, one of the data fields is mandatory except for 'Grouped' type (its data is another level of avps).\n\
125 Application-specific formats must decide the way to represent its contents, being recommended to use a natural representation if possible,\n\
126 because xml is read by humans with testing and monitoring purposes.\n\
136 using namespace anna::diameter::codec;
139 //------------------------------------------------------------------------------
140 //----------------------------------------------------- EngineImpl::EngineImpl()
141 //------------------------------------------------------------------------------
142 EngineImpl::EngineImpl(const char* className) :
143 anna::Component(className),
145 a_validationDepth(ValidationDepth::FirstError),
146 a_validationMode(ValidationMode::AfterDecoding),
147 a_ignoreFlags(false),
148 a_fixMode(FixMode::BeforeCoding) {
149 anna::diameter::sccs::activate();
150 anna::xml::functions::initialize();
151 a_dtd.initialize(MessageDTD);
155 //------------------------------------------------------------------------------
156 //-------------------------------------------------- EngineImpl::setDictionary()
157 //------------------------------------------------------------------------------
158 const anna::diameter::stack::Dictionary *EngineImpl::setDictionary(int stackId) throw() {
159 a_dictionary = (stack::Engine::instantiate()).getDictionary(stackId);
164 //------------------------------------------------------------------------------
165 //------------------------------------------------------ EngineImpl::createAvp()
166 //------------------------------------------------------------------------------
167 Avp* EngineImpl::createAvp(const AvpId *id) throw(anna::RuntimeException) {
169 anna::Guard guard(this, "diameter::codec::EngineImpl::createAvp");
171 if((result = allocateAvp()) == NULL)
172 throw anna::RuntimeException("diameter::codec::EngineImpl::allocateAvp returns NULL", ANNA_FILE_LOCATION);
174 //result->clear(); better clear this at releaseAvp(), see class-help implementation example
175 if(id) result->setId(*id);
181 //------------------------------------------------------------------------------
182 //-------------------------------------------------- EngineImpl::createMessage()
183 //------------------------------------------------------------------------------
184 Message* EngineImpl::createMessage(const CommandId *id) throw(anna::RuntimeException) {
185 Message *result(NULL);
186 anna::Guard guard(this, "diameter::codec::EngineImpl::createMessage");
188 if((result = allocateMessage()) == NULL)
189 throw anna::RuntimeException("diameter::codec::EngineImpl::allocateMessage returns NULL", ANNA_FILE_LOCATION);
191 //result->clear(); better clear this at releaseMessage(), see class-help implementation example
192 if(id) result->setId(*id);
198 //------------------------------------------------------------------------------
199 //-------------------------------------------------- EngineImpl::createMessage()
200 //------------------------------------------------------------------------------
201 Message *EngineImpl::createMessage(const std::string & xmlPathFile) throw(anna::RuntimeException) {
202 Message *result = createMessage();
203 result->loadXML(xmlPathFile);
209 std::string EngineImpl::asString(void) const throw() {
210 std::string result = anna::Component::asString();
211 result += "\nValidationDepth: ";
212 result += asText(a_validationDepth);
213 result += "\nValidationMode: ";
214 result += asText(a_validationMode);
215 result += "\nIgnore flags: ";
216 result += a_ignoreFlags ? "yes" : "no";
217 result += "\nFixMode: ";
218 result += asText(a_fixMode);
219 result += "\nActivated Dictionary: ";
220 result += a_dictionary ? (a_dictionary->getName()) : "<null>";
224 //------------------------------------------------------------------------------
225 //---------------------------------------------------------- EngineImpl::asXML()
226 //------------------------------------------------------------------------------
227 anna::xml::Node* EngineImpl::asXML(anna::xml::Node* parent) const
229 parent = anna::Component::asXML(parent);
230 anna::xml::Node* result = parent->createChild("diameter.codec.EngineImpl");
231 result->createAttribute("ValidationDepth", asText(a_validationDepth));
232 result->createAttribute("ValidationMode", asText(a_validationMode));
233 result->createAttribute("IgnoreFlags", a_ignoreFlags ? "yes" : "no");
234 result->createAttribute("FixMode", asText(a_fixMode));
235 anna::xml::Node* dictionary = result->createChild("EngineImpl.ActivatedDictionary");
237 if(a_dictionary) a_dictionary->asXML(result);
242 //------------------------------------------------------------------------------
243 //--------------------------------------------------------- EngineImpl::asText()
244 //------------------------------------------------------------------------------
245 const char* EngineImpl::asText(const ValidationDepth::_v vd)
247 static const char* text [] = { "Complete", "FirstError" };
251 //------------------------------------------------------------------------------
252 //--------------------------------------------------------- EngineImpl::asText()
253 //------------------------------------------------------------------------------
254 const char* EngineImpl::asText(const ValidationMode::_v vm)
256 static const char* text [] = { "BeforeCoding", "AfterDecoding", "Always", "Never" };
260 //------------------------------------------------------------------------------
261 //--------------------------------------------------------- EngineImpl::asText()
262 //------------------------------------------------------------------------------
263 const char* EngineImpl::asText(const FixMode::_v fm)
265 static const char* text [] = { "BeforeCoding", "AfterDecoding", "Always", "Never" };
269 //------------------------------------------------------------------------------
270 //---------------------------------------------- EngineImpl::validationAnomaly()
271 //------------------------------------------------------------------------------
272 void EngineImpl::validationAnomaly(const std::string & description) const throw(anna::RuntimeException) {
273 std::string msg = "Validation error: ";
275 if(a_validationDepth == ValidationDepth::FirstError)
276 throw anna::RuntimeException(msg + description, ANNA_FILE_LOCATION);
278 LOGWARNING(anna::Logger::warning(msg + description, ANNA_FILE_LOCATION));
282 //------------------------------------------------------------------------------
283 //--------------------------------------------------- EngineImpl::avpIdForName()
284 //------------------------------------------------------------------------------
285 anna::diameter::AvpId EngineImpl::avpIdForName(const char * name) throw(anna::RuntimeException) {
287 throw anna::RuntimeException("Provided NULL Avp Logical Name", ANNA_FILE_LOCATION);
290 std::string msg = "Cannot find identifier '"; msg += name;
291 msg += "' for the avp: no dictionary available";
292 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
295 const stack::Avp * stackAvp = a_dictionary->getAvp(name);
298 std::string msg = "Cannot find identifier '"; msg += name;
299 msg += "' for the avp at the available dictionary";
300 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
303 return (stackAvp->getId());
307 //------------------------------------------------------------------------------
308 //----------------------------------------------- EngineImpl::commandIdForName()
309 //------------------------------------------------------------------------------
310 anna::diameter::CommandId EngineImpl::commandIdForName(const char * name) throw(anna::RuntimeException) {
312 throw anna::RuntimeException("Provided NULL Command Logical Name", ANNA_FILE_LOCATION);
315 std::string msg = "Cannot find identifier '"; msg += name;
316 msg += "' for the command: no dictionary available";
317 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
320 const stack::Command * stackCommand = a_dictionary->getCommand(name);
323 std::string msg = "Cannot find identifier '"; msg += name;
324 msg += "' for the command at the available dictionary";
325 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
328 return (stackCommand->getId());