Changed LICENSE. Now referenced to web site and file on project root directory
[anna.git] / source / diameter / codec / EngineImpl.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
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 //
7
8
9 // Local
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>
18
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>
24
25
26
27 namespace anna {
28 namespace diameter {
29 namespace codec {
30
31 const char *MessageDTD = "\
32 <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
33 <!-- Diameter message DTD -->\n\
34 \n\
35 <!ELEMENT message (avp*)>\n\
36 <!ELEMENT avp (avp*)>\n\
37 \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\
39 <!--\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\
45 \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\
48 \n\
49    code:    Command code\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\
51 \n\
52 \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\
56 -->\n\
57 \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\
59 <!--\n\
60    name:   Avp name within working stack (dictionary identifier)\n\
61 \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\
64 \n\
65    code:          Avp code\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\
70 \n\
71 \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\
75 \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\
86 \n\
87                      New application formats must define specific natural representation for internal raw data\n\
88 \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\
93 \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\
99 -->\n\
100 \n\
101 ";
102
103
104 }
105 }
106 }
107
108 using namespace anna::diameter::codec;
109
110
111 //------------------------------------------------------------------------------
112 //----------------------------------------------------- EngineImpl::EngineImpl()
113 //------------------------------------------------------------------------------
114 EngineImpl::EngineImpl(const char* className) :
115   anna::Component(className),
116   a_dictionary(NULL),
117   a_validationDepth(ValidationDepth::FirstError),
118   a_validationMode(ValidationMode::AfterDecoding),
119   a_ignoreFlags(false),
120   a_selectStackWithApplicationId(false),
121   a_fixMode(FixMode::BeforeEncoding) {
122   anna::diameter::sccs::activate();
123   anna::xml::functions::initialize();
124   a_dtd.initialize(MessageDTD);
125 }
126
127
128 //------------------------------------------------------------------------------
129 //-------------------------------------------------- EngineImpl::setDictionary()
130 //------------------------------------------------------------------------------
131 const anna::diameter::stack::Dictionary *EngineImpl::setDictionary(unsigned int stackId) throw() {
132   a_dictionary = (stack::Engine::instantiate()).getDictionary(stackId);
133   return a_dictionary;
134 }
135
136
137 //------------------------------------------------------------------------------
138 //------------------------------------------------------ EngineImpl::createAvp()
139 //------------------------------------------------------------------------------
140 Avp* EngineImpl::createAvp(const AvpId *id) throw(anna::RuntimeException) {
141   Avp *result(NULL);
142   anna::Guard guard(this, "diameter::codec::EngineImpl::createAvp");
143
144   if((result = allocateAvp()) == NULL)
145     throw anna::RuntimeException("diameter::codec::EngineImpl::allocateAvp returns NULL", ANNA_FILE_LOCATION);
146
147   //result->clear(); better clear this at releaseAvp(), see class-help implementation example
148   if(id) result->setId(*id);
149
150   return result;
151 }
152
153
154 //------------------------------------------------------------------------------
155 //-------------------------------------------------- EngineImpl::createMessage()
156 //------------------------------------------------------------------------------
157 Message* EngineImpl::createMessage(const CommandId *id) throw(anna::RuntimeException) {
158   Message *result(NULL);
159   anna::Guard guard(this, "diameter::codec::EngineImpl::createMessage");
160
161   if((result = allocateMessage()) == NULL)
162     throw anna::RuntimeException("diameter::codec::EngineImpl::allocateMessage returns NULL", ANNA_FILE_LOCATION);
163
164   //result->clear(); better clear this at releaseMessage(), see class-help implementation example
165   if(id) result->setId(*id);
166
167   return result;
168 }
169
170
171 //------------------------------------------------------------------------------
172 //-------------------------------------------------- EngineImpl::createMessage()
173 //------------------------------------------------------------------------------
174 Message *EngineImpl::createMessage(const std::string & xmlPathFile) throw(anna::RuntimeException) {
175   Message *result = createMessage();
176   result->loadXML(xmlPathFile);
177   return result;
178 }
179
180
181
182 std::string EngineImpl::asString(void) const throw() {
183   std::string result = anna::Component::asString();
184   result += "\nValidationDepth: ";
185   result += asText(a_validationDepth);
186   result += "\nValidationMode: ";
187   result += asText(a_validationMode);
188   result += "\nIgnore flags: ";
189   result += a_ignoreFlags ? "yes" : "no";
190   result += "\nFixMode: ";
191   result += asText(a_fixMode);
192   result += "\nActivated Dictionary: ";
193   result += a_dictionary ? (a_dictionary->getName()) : "<null>";
194   return result;
195 }
196
197 //------------------------------------------------------------------------------
198 //---------------------------------------------------------- EngineImpl::asXML()
199 //------------------------------------------------------------------------------
200 anna::xml::Node* EngineImpl::asXML(anna::xml::Node* parent) const
201 throw() {
202   parent = anna::Component::asXML(parent);
203   anna::xml::Node* result = parent->createChild("diameter.codec.EngineImpl");
204   result->createAttribute("ValidationDepth", asText(a_validationDepth));
205   result->createAttribute("ValidationMode", asText(a_validationMode));
206   result->createAttribute("IgnoreFlags", a_ignoreFlags ? "yes" : "no");
207   result->createAttribute("FixMode", asText(a_fixMode));
208   anna::xml::Node* dictionary = result->createChild("EngineImpl.ActivatedDictionary");
209
210   if(a_dictionary) a_dictionary->asXML(result);
211
212   return result;
213 }
214
215 //------------------------------------------------------------------------------
216 //--------------------------------------------------------- EngineImpl::asText()
217 //------------------------------------------------------------------------------
218 const char* EngineImpl::asText(const ValidationDepth::_v vd)
219 throw() {
220   static const char* text [] = { "Complete", "FirstError" };
221   return text [vd];
222 }
223
224 //------------------------------------------------------------------------------
225 //--------------------------------------------------------- EngineImpl::asText()
226 //------------------------------------------------------------------------------
227 const char* EngineImpl::asText(const ValidationMode::_v vm)
228 throw() {
229   static const char* text [] = { "BeforeEncoding", "AfterDecoding", "Always", "Never" };
230   return text [vm];
231 }
232
233 //------------------------------------------------------------------------------
234 //--------------------------------------------------------- EngineImpl::asText()
235 //------------------------------------------------------------------------------
236 const char* EngineImpl::asText(const FixMode::_v fm)
237 throw() {
238   static const char* text [] = { "BeforeEncoding", "AfterDecoding", "Always", "Never" };
239   return text [fm];
240 }
241
242 //------------------------------------------------------------------------------
243 //---------------------------------------------- EngineImpl::validationAnomaly()
244 //------------------------------------------------------------------------------
245 void EngineImpl::validationAnomaly(const std::string & description) const throw(anna::RuntimeException) {
246   std::string msg = "Validation error: ";
247
248   if(a_validationDepth == ValidationDepth::FirstError)
249     throw anna::RuntimeException(msg + description, ANNA_FILE_LOCATION);
250
251   LOGWARNING(anna::Logger::warning(msg + description, ANNA_FILE_LOCATION));
252 }
253
254
255 //------------------------------------------------------------------------------
256 //--------------------------------------------------- EngineImpl::avpIdForName()
257 //------------------------------------------------------------------------------
258 anna::diameter::AvpId EngineImpl::avpIdForName(const char * name) throw(anna::RuntimeException) {
259   if(!name)
260     throw anna::RuntimeException("Provided NULL Avp Logical Name", ANNA_FILE_LOCATION);
261
262   if(!a_dictionary) {
263     std::string msg = "Cannot find identifier '"; msg += name;
264     msg += "' for the avp: no dictionary available";
265     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
266   }
267
268   const stack::Avp * stackAvp = a_dictionary->getAvp(name);
269
270   if(!stackAvp) {
271     std::string msg = "Cannot find identifier '"; msg += name;
272     msg += "' for the avp at the available dictionary";
273     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
274   }
275
276   return (stackAvp->getId());
277 }
278
279
280 //------------------------------------------------------------------------------
281 //----------------------------------------------- EngineImpl::commandIdForName()
282 //------------------------------------------------------------------------------
283 anna::diameter::CommandId EngineImpl::commandIdForName(const char * name) throw(anna::RuntimeException) {
284   if(!name)
285     throw anna::RuntimeException("Provided NULL Command Logical Name", ANNA_FILE_LOCATION);
286
287   if(!a_dictionary) {
288     std::string msg = "Cannot find identifier '"; msg += name;
289     msg += "' for the command: no dictionary available";
290     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
291   }
292
293   const stack::Command * stackCommand = a_dictionary->getCommand(name);
294
295   if(!stackCommand) {
296     std::string msg = "Cannot find identifier '"; msg += name;
297     msg += "' for the command at the available dictionary";
298     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
299   }
300
301   return (stackCommand->getId());
302 }
303