1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // http://redmine.teslayout.com/projects/anna-suite
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 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.
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_selectStackWithApplicationId(false),
149 a_fixMode(FixMode::BeforeEncoding) {
150 anna::diameter::sccs::activate();
151 anna::xml::functions::initialize();
152 a_dtd.initialize(MessageDTD);
156 //------------------------------------------------------------------------------
157 //-------------------------------------------------- EngineImpl::setDictionary()
158 //------------------------------------------------------------------------------
159 const anna::diameter::stack::Dictionary *EngineImpl::setDictionary(unsigned int stackId) throw() {
160 a_dictionary = (stack::Engine::instantiate()).getDictionary(stackId);
165 //------------------------------------------------------------------------------
166 //------------------------------------------------------ EngineImpl::createAvp()
167 //------------------------------------------------------------------------------
168 Avp* EngineImpl::createAvp(const AvpId *id) throw(anna::RuntimeException) {
170 anna::Guard guard(this, "diameter::codec::EngineImpl::createAvp");
172 if((result = allocateAvp()) == NULL)
173 throw anna::RuntimeException("diameter::codec::EngineImpl::allocateAvp returns NULL", ANNA_FILE_LOCATION);
175 //result->clear(); better clear this at releaseAvp(), see class-help implementation example
176 if(id) result->setId(*id);
182 //------------------------------------------------------------------------------
183 //-------------------------------------------------- EngineImpl::createMessage()
184 //------------------------------------------------------------------------------
185 Message* EngineImpl::createMessage(const CommandId *id) throw(anna::RuntimeException) {
186 Message *result(NULL);
187 anna::Guard guard(this, "diameter::codec::EngineImpl::createMessage");
189 if((result = allocateMessage()) == NULL)
190 throw anna::RuntimeException("diameter::codec::EngineImpl::allocateMessage returns NULL", ANNA_FILE_LOCATION);
192 //result->clear(); better clear this at releaseMessage(), see class-help implementation example
193 if(id) result->setId(*id);
199 //------------------------------------------------------------------------------
200 //-------------------------------------------------- EngineImpl::createMessage()
201 //------------------------------------------------------------------------------
202 Message *EngineImpl::createMessage(const std::string & xmlPathFile) throw(anna::RuntimeException) {
203 Message *result = createMessage();
204 result->loadXML(xmlPathFile);
210 std::string EngineImpl::asString(void) const throw() {
211 std::string result = anna::Component::asString();
212 result += "\nValidationDepth: ";
213 result += asText(a_validationDepth);
214 result += "\nValidationMode: ";
215 result += asText(a_validationMode);
216 result += "\nIgnore flags: ";
217 result += a_ignoreFlags ? "yes" : "no";
218 result += "\nFixMode: ";
219 result += asText(a_fixMode);
220 result += "\nActivated Dictionary: ";
221 result += a_dictionary ? (a_dictionary->getName()) : "<null>";
225 //------------------------------------------------------------------------------
226 //---------------------------------------------------------- EngineImpl::asXML()
227 //------------------------------------------------------------------------------
228 anna::xml::Node* EngineImpl::asXML(anna::xml::Node* parent) const
230 parent = anna::Component::asXML(parent);
231 anna::xml::Node* result = parent->createChild("diameter.codec.EngineImpl");
232 result->createAttribute("ValidationDepth", asText(a_validationDepth));
233 result->createAttribute("ValidationMode", asText(a_validationMode));
234 result->createAttribute("IgnoreFlags", a_ignoreFlags ? "yes" : "no");
235 result->createAttribute("FixMode", asText(a_fixMode));
236 anna::xml::Node* dictionary = result->createChild("EngineImpl.ActivatedDictionary");
238 if(a_dictionary) a_dictionary->asXML(result);
243 //------------------------------------------------------------------------------
244 //--------------------------------------------------------- EngineImpl::asText()
245 //------------------------------------------------------------------------------
246 const char* EngineImpl::asText(const ValidationDepth::_v vd)
248 static const char* text [] = { "Complete", "FirstError" };
252 //------------------------------------------------------------------------------
253 //--------------------------------------------------------- EngineImpl::asText()
254 //------------------------------------------------------------------------------
255 const char* EngineImpl::asText(const ValidationMode::_v vm)
257 static const char* text [] = { "BeforeEncoding", "AfterDecoding", "Always", "Never" };
261 //------------------------------------------------------------------------------
262 //--------------------------------------------------------- EngineImpl::asText()
263 //------------------------------------------------------------------------------
264 const char* EngineImpl::asText(const FixMode::_v fm)
266 static const char* text [] = { "BeforeEncoding", "AfterDecoding", "Always", "Never" };
270 //------------------------------------------------------------------------------
271 //---------------------------------------------- EngineImpl::validationAnomaly()
272 //------------------------------------------------------------------------------
273 void EngineImpl::validationAnomaly(const std::string & description) const throw(anna::RuntimeException) {
274 std::string msg = "Validation error: ";
276 if(a_validationDepth == ValidationDepth::FirstError)
277 throw anna::RuntimeException(msg + description, ANNA_FILE_LOCATION);
279 LOGWARNING(anna::Logger::warning(msg + description, ANNA_FILE_LOCATION));
283 //------------------------------------------------------------------------------
284 //--------------------------------------------------- EngineImpl::avpIdForName()
285 //------------------------------------------------------------------------------
286 anna::diameter::AvpId EngineImpl::avpIdForName(const char * name) throw(anna::RuntimeException) {
288 throw anna::RuntimeException("Provided NULL Avp Logical Name", ANNA_FILE_LOCATION);
291 std::string msg = "Cannot find identifier '"; msg += name;
292 msg += "' for the avp: no dictionary available";
293 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
296 const stack::Avp * stackAvp = a_dictionary->getAvp(name);
299 std::string msg = "Cannot find identifier '"; msg += name;
300 msg += "' for the avp at the available dictionary";
301 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
304 return (stackAvp->getId());
308 //------------------------------------------------------------------------------
309 //----------------------------------------------- EngineImpl::commandIdForName()
310 //------------------------------------------------------------------------------
311 anna::diameter::CommandId EngineImpl::commandIdForName(const char * name) throw(anna::RuntimeException) {
313 throw anna::RuntimeException("Provided NULL Command Logical Name", ANNA_FILE_LOCATION);
316 std::string msg = "Cannot find identifier '"; msg += name;
317 msg += "' for the command: no dictionary available";
318 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
321 const stack::Command * stackCommand = a_dictionary->getCommand(name);
324 std::string msg = "Cannot find identifier '"; msg += name;
325 msg += "' for the command at the available dictionary";
326 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
329 return (stackCommand->getId());