New feature to allow to register components with different names for same class:...
[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   // Sets engine
148   result->setEngine((Engine*)this);
149
150   //result->clear(); better clear this at releaseAvp(), see class-help implementation example
151   if(id) result->setId(*id);
152
153   return result;
154 }
155
156
157 //------------------------------------------------------------------------------
158 //-------------------------------------------------- EngineImpl::createMessage()
159 //------------------------------------------------------------------------------
160 Message* EngineImpl::createMessage(const CommandId *id) throw(anna::RuntimeException) {
161   Message *result(NULL);
162   anna::Guard guard(this, "diameter::codec::EngineImpl::createMessage");
163
164   if((result = allocateMessage()) == NULL)
165     throw anna::RuntimeException("diameter::codec::EngineImpl::allocateMessage returns NULL", ANNA_FILE_LOCATION);
166
167   // Sets engine
168   result->setEngine((Engine*)this);
169
170   //result->clear(); better clear this at releaseMessage(), see class-help implementation example
171   if(id) result->setId(*id);
172
173   return result;
174 }
175
176
177 //------------------------------------------------------------------------------
178 //-------------------------------------------------- EngineImpl::createMessage()
179 //------------------------------------------------------------------------------
180 Message *EngineImpl::createMessage(const std::string & xmlPathFile) throw(anna::RuntimeException) {
181   Message *result = createMessage();
182   result->loadXML(xmlPathFile);
183   return result;
184 }
185
186
187
188 std::string EngineImpl::asString(void) const throw() {
189   std::string result = anna::Component::asString();
190   result += "\nValidationDepth: ";
191   result += asText(a_validationDepth);
192   result += "\nValidationMode: ";
193   result += asText(a_validationMode);
194   result += "\nIgnore flags: ";
195   result += a_ignoreFlags ? "yes" : "no";
196   result += "\nFixMode: ";
197   result += asText(a_fixMode);
198   result += "\nActivated Dictionary: ";
199   result += a_dictionary ? (a_dictionary->getName()) : "<null>";
200   return result;
201 }
202
203 //------------------------------------------------------------------------------
204 //---------------------------------------------------------- EngineImpl::asXML()
205 //------------------------------------------------------------------------------
206 anna::xml::Node* EngineImpl::asXML(anna::xml::Node* parent) const
207 throw() {
208   parent = anna::Component::asXML(parent);
209   anna::xml::Node* result = parent->createChild("diameter.codec.EngineImpl");
210   result->createAttribute("ValidationDepth", asText(a_validationDepth));
211   result->createAttribute("ValidationMode", asText(a_validationMode));
212   result->createAttribute("IgnoreFlags", a_ignoreFlags ? "yes" : "no");
213   result->createAttribute("FixMode", asText(a_fixMode));
214   anna::xml::Node* dictionary = result->createChild("EngineImpl.ActivatedDictionary");
215
216   if(a_dictionary) a_dictionary->asXML(result);
217
218   return result;
219 }
220
221 //------------------------------------------------------------------------------
222 //--------------------------------------------------------- EngineImpl::asText()
223 //------------------------------------------------------------------------------
224 const char* EngineImpl::asText(const ValidationDepth::_v vd)
225 throw() {
226   static const char* text [] = { "Complete", "FirstError" };
227   return text [vd];
228 }
229
230 //------------------------------------------------------------------------------
231 //--------------------------------------------------------- EngineImpl::asText()
232 //------------------------------------------------------------------------------
233 const char* EngineImpl::asText(const ValidationMode::_v vm)
234 throw() {
235   static const char* text [] = { "BeforeEncoding", "AfterDecoding", "Always", "Never" };
236   return text [vm];
237 }
238
239 //------------------------------------------------------------------------------
240 //--------------------------------------------------------- EngineImpl::asText()
241 //------------------------------------------------------------------------------
242 const char* EngineImpl::asText(const FixMode::_v fm)
243 throw() {
244   static const char* text [] = { "BeforeEncoding", "AfterDecoding", "Always", "Never" };
245   return text [fm];
246 }
247
248 //------------------------------------------------------------------------------
249 //---------------------------------------------- EngineImpl::validationAnomaly()
250 //------------------------------------------------------------------------------
251 void EngineImpl::validationAnomaly(const std::string & description) const throw(anna::RuntimeException) {
252   std::string msg = "Validation error: ";
253
254   if(a_validationDepth == ValidationDepth::FirstError)
255     throw anna::RuntimeException(msg + description, ANNA_FILE_LOCATION);
256
257   LOGWARNING(anna::Logger::warning(msg + description, ANNA_FILE_LOCATION));
258 }
259
260
261 //------------------------------------------------------------------------------
262 //--------------------------------------------------- EngineImpl::avpIdForName()
263 //------------------------------------------------------------------------------
264 anna::diameter::AvpId EngineImpl::avpIdForName(const char * name) throw(anna::RuntimeException) {
265   if(!name)
266     throw anna::RuntimeException("Provided NULL Avp Logical Name", ANNA_FILE_LOCATION);
267
268   if(!a_dictionary) {
269     std::string msg = "Cannot find identifier '"; msg += name;
270     msg += "' for the avp: no dictionary available";
271     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
272   }
273
274   const stack::Avp * stackAvp = a_dictionary->getAvp(name);
275
276   if(!stackAvp) {
277     std::string msg = "Cannot find identifier '"; msg += name;
278     msg += "' for the avp at the available dictionary";
279     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
280   }
281
282   return (stackAvp->getId());
283 }
284
285
286 //------------------------------------------------------------------------------
287 //----------------------------------------------- EngineImpl::commandIdForName()
288 //------------------------------------------------------------------------------
289 anna::diameter::CommandId EngineImpl::commandIdForName(const char * name) throw(anna::RuntimeException) {
290   if(!name)
291     throw anna::RuntimeException("Provided NULL Command Logical Name", ANNA_FILE_LOCATION);
292
293   if(!a_dictionary) {
294     std::string msg = "Cannot find identifier '"; msg += name;
295     msg += "' for the command: no dictionary available";
296     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
297   }
298
299   const stack::Command * stackCommand = a_dictionary->getCommand(name);
300
301   if(!stackCommand) {
302     std::string msg = "Cannot find identifier '"; msg += name;
303     msg += "' for the command at the available dictionary";
304     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
305   }
306
307   return (stackCommand->getId());
308 }
309