1 // ANNA - Anna is Not Nothingness Anymore //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
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 //
10 #include <anna/diameter/codec/EngineImpl.hpp>
11 #include <anna/diameter/codec/Message.hpp>
12 #include <anna/diameter/codec/Avp.hpp>
13 #include <anna/diameter/stack/Engine.hpp>
14 #include <anna/diameter/stack/Dictionary.hpp>
15 #include <anna/diameter/stack/Command.hpp>
16 #include <anna/diameter/internal/sccs.hpp>
17 #include <anna/diameter/defines.hpp>
19 #include <anna/xml/xml.hpp>
20 #include <anna/core/tracing/Logger.hpp>
21 #include <anna/core/tracing/TraceMethod.hpp>
22 #include <anna/core/functions.hpp>
23 #include <anna/core/mt/Guard.hpp>
31 const char *MessageDTD = "\
32 <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
33 <!-- Diameter message DTD -->\n\
35 <!ELEMENT message (avp*)>\n\
36 <!ELEMENT avp (avp*)>\n\
38 <!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\
40 version: Diameter version. Sets '1' by default\n\
41 name: Command name within working stack (dictionary identifier)\n\
42 p-bit: (P)roxiable bit flag (yes, no). By default is 'no'\n\
43 e-bit: (E)rror bit flag (yes, no). By default is 'no'\n\
44 t-bit: Potentially re-(T)ransmitted bit flag (yes, no). By default is 'no'\n\
46 In order to get more coding capabilities, command code and flags could be established instead of former fields,\n\
47 but neither of them are allowed if the other are present (and vice versa):\n\
50 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\
53 application-id: Message application id\n\
54 hop-by-hop-id: Message hop by hop id. Sets '0' by default\n\
55 end-by-end-id: Message end by end id. Sets '0' by default\n\
58 <!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\
60 name: Avp name within working stack (dictionary identifier)\n\
62 In order to get more coding capabilities, avp code, vendor-id and flags could be established instead of former avp name,\n\
63 but neither of them are allowed if 'name' is provided (and vice versa):\n\
66 vendor-code: Avp vendor code\n\
67 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\
68 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\
69 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\
72 data: Natural string representation for avp data. Specially applicable with numbers and printable strings, but also\n\
73 useful for certain formats which could be easily understandable in such friendly/smart representation. We will\n\
74 achieve different human-readable strings depending on data format:\n\
76 [ OctetString ] (if printable, but not recommended)\n\
77 [ Integer32, Integer64, Unsigned32, Unsigned64, Float32, Float64 ] (normal number representation)\n\
78 [ Time ] (NTP timestamp, normal number representation)\n\
79 [ Address ] ('<type (IANA Address Family Number)>|<value>' representation; i.e. '1|192.168.0.1'(IPv4), '8|34616279266'(E164), etc.\n\
80 Type (and pipe) field could be avoided for IPv4 and IPv6 address types (a light parse checking is done: one colon for\n\
81 IPv6, one dot for IPv4). Internal engine always includes type on data field, which is also recommended for inputs.\n\
82 Currently, only IPv4, IPv6 and E164 address types have a known printable presentation, anyway using printable format\n\
83 for another types will encode the address value directly as E164 does)\n\
84 [ UTF8String, DiameterIdentity, DiameterURI ] (printable)\n\
85 [ IPFilterRule, QoSFilterRule ] (uses ASCII charset, printable)\n\
87 New application formats must define specific natural representation for internal raw data\n\
89 hex-data: Hexadecimal octet sequence representation (i.e. 'af012fb3', with even number of digits). Suitable for whatever kind\n\
90 of diameter format, but mandatory for non printable information. OctetString usually transport non human-readable\n\
91 data and should better be encoded within this field although being printable. Unknown avps (which fails identifying\n\
92 provided name or code/vendor-code) must always use this representation.\n\
94 Xml representation for decoded messages shows natural content except for 'OctetString' format and unknown avps. Anyway, when printable,\n\
95 OctetString could show such information at data field apart from hex-data, because many implementations use this format to transport\n\
96 readable-string data. In general, one of the data fields is mandatory except for 'Grouped' type (its data is another level of avps).\n\
97 Application-specific formats must decide the way to represent its contents, being recommended to use a natural representation if possible,\n\
98 because xml is read by humans with testing and monitoring purposes.\n\
108 using namespace anna::diameter::codec;
111 //------------------------------------------------------------------------------
112 //----------------------------------------------------- EngineImpl::EngineImpl()
113 //------------------------------------------------------------------------------
114 EngineImpl::EngineImpl(const char* className) :
115 anna::Component(className),
117 a_validationDepth(ValidationDepth::FirstError),
118 a_validationMode(ValidationMode::AfterDecoding),
119 a_singleFailedAVP(true),
120 a_ignoreFlags(false),
121 a_selectStackWithApplicationId(false),
122 a_fixMode(FixMode::BeforeEncoding) {
123 anna::diameter::sccs::activate();
124 anna::xml::functions::initialize();
125 a_dtd.initialize(MessageDTD);
129 //------------------------------------------------------------------------------
130 //-------------------------------------------------- EngineImpl::setDictionary()
131 //------------------------------------------------------------------------------
132 const anna::diameter::stack::Dictionary *EngineImpl::setDictionary(unsigned int stackId) throw() {
133 a_dictionary = (stack::Engine::instantiate()).getDictionary(stackId);
138 //------------------------------------------------------------------------------
139 //------------------------------------------------------ EngineImpl::createAvp()
140 //------------------------------------------------------------------------------
141 Avp* EngineImpl::createAvp(const AvpId *id) throw(anna::RuntimeException) {
143 anna::Guard guard(this, "diameter::codec::EngineImpl::createAvp");
145 if((result = allocateAvp()) == NULL)
146 throw anna::RuntimeException("diameter::codec::EngineImpl::allocateAvp returns NULL", ANNA_FILE_LOCATION);
149 result->setEngine((Engine*)this);
151 //result->clear(); better clear this at releaseAvp(), see class-help implementation example
152 if(id) result->setId(*id);
158 //------------------------------------------------------------------------------
159 //-------------------------------------------------- EngineImpl::createMessage()
160 //------------------------------------------------------------------------------
161 Message* EngineImpl::createMessage(const CommandId *id) throw(anna::RuntimeException) {
162 Message *result(NULL);
163 anna::Guard guard(this, "diameter::codec::EngineImpl::createMessage");
165 if((result = allocateMessage()) == NULL)
166 throw anna::RuntimeException("diameter::codec::EngineImpl::allocateMessage returns NULL", ANNA_FILE_LOCATION);
169 result->setEngine((Engine*)this);
171 //result->clear(); better clear this at releaseMessage(), see class-help implementation example
172 if(id) result->setId(*id);
178 //------------------------------------------------------------------------------
179 //-------------------------------------------------- EngineImpl::createMessage()
180 //------------------------------------------------------------------------------
181 Message *EngineImpl::createMessage(const std::string & xmlPathFile) throw(anna::RuntimeException) {
182 Message *result = createMessage();
183 result->loadXML(xmlPathFile);
189 std::string EngineImpl::asString(void) const throw() {
190 std::string result = anna::Component::asString();
191 result += "\nValidationDepth: ";
192 result += asText(a_validationDepth);
193 result += "\nValidationMode: ";
194 result += asText(a_validationMode);
195 result += "\nSingle Failed-AVP: ";
196 result += a_singleFailedAVP ? "yes" : "no";
197 result += "\nIgnore flags: ";
198 result += a_ignoreFlags ? "yes" : "no";
199 result += "\nFixMode: ";
200 result += asText(a_fixMode);
201 result += "\nActivated Dictionary: ";
202 result += a_dictionary ? (a_dictionary->getName()) : "<null>";
206 //------------------------------------------------------------------------------
207 //---------------------------------------------------------- EngineImpl::asXML()
208 //------------------------------------------------------------------------------
209 anna::xml::Node* EngineImpl::asXML(anna::xml::Node* parent) const
211 parent = anna::Component::asXML(parent);
212 anna::xml::Node* result = parent->createChild("diameter.codec.EngineImpl");
213 result->createAttribute("ValidationDepth", asText(a_validationDepth));
214 result->createAttribute("ValidationMode", asText(a_validationMode));
215 result->createAttribute("SingleFailedAVP", a_singleFailedAVP ? "yes" : "no");
216 result->createAttribute("IgnoreFlags", a_ignoreFlags ? "yes" : "no");
217 result->createAttribute("FixMode", asText(a_fixMode));
218 anna::xml::Node* dictionary = result->createChild("EngineImpl.ActivatedDictionary");
220 if(a_dictionary) a_dictionary->asXML(result);
225 //------------------------------------------------------------------------------
226 //--------------------------------------------------------- EngineImpl::asText()
227 //------------------------------------------------------------------------------
228 const char* EngineImpl::asText(const ValidationDepth::_v vd)
230 static const char* text [] = { "Complete", "FirstError" };
234 //------------------------------------------------------------------------------
235 //--------------------------------------------------------- EngineImpl::asText()
236 //------------------------------------------------------------------------------
237 const char* EngineImpl::asText(const ValidationMode::_v vm)
239 static const char* text [] = { "BeforeEncoding", "AfterDecoding", "Always", "Never" };
243 //------------------------------------------------------------------------------
244 //--------------------------------------------------------- EngineImpl::asText()
245 //------------------------------------------------------------------------------
246 const char* EngineImpl::asText(const FixMode::_v fm)
248 static const char* text [] = { "BeforeEncoding", "AfterDecoding", "Always", "Never" };
252 //------------------------------------------------------------------------------
253 //---------------------------------------------- EngineImpl::validationAnomaly()
254 //------------------------------------------------------------------------------
255 void EngineImpl::validationAnomaly(const std::string & description) const throw(anna::RuntimeException) {
256 std::string msg = "Validation error: ";
258 if(a_validationDepth == ValidationDepth::FirstError)
259 throw anna::RuntimeException(msg + description, ANNA_FILE_LOCATION);
261 LOGWARNING(anna::Logger::warning(msg + description, ANNA_FILE_LOCATION));
265 //------------------------------------------------------------------------------
266 //--------------------------------------------------- EngineImpl::avpIdForName()
267 //------------------------------------------------------------------------------
268 anna::diameter::AvpId EngineImpl::avpIdForName(const char * name) throw(anna::RuntimeException) {
270 throw anna::RuntimeException("Provided NULL Avp Logical Name", ANNA_FILE_LOCATION);
273 std::string msg = "Cannot find identifier '"; msg += name;
274 msg += "' for the avp: no dictionary available";
275 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
278 const stack::Avp * stackAvp = a_dictionary->getAvp(name);
281 std::string msg = "Cannot find identifier '"; msg += name;
282 msg += "' for the avp at the available dictionary";
283 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
286 return (stackAvp->getId());
290 //------------------------------------------------------------------------------
291 //----------------------------------------------- EngineImpl::commandIdForName()
292 //------------------------------------------------------------------------------
293 anna::diameter::CommandId EngineImpl::commandIdForName(const char * name) throw(anna::RuntimeException) {
295 throw anna::RuntimeException("Provided NULL Command Logical Name", ANNA_FILE_LOCATION);
298 std::string msg = "Cannot find identifier '"; msg += name;
299 msg += "' for the command: no dictionary available";
300 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
303 const stack::Command * stackCommand = a_dictionary->getCommand(name);
306 std::string msg = "Cannot find identifier '"; msg += name;
307 msg += "' for the command at the available dictionary";
308 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
311 return (stackCommand->getId());