Multistack launcher
[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_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);
126 }
127
128
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);
134   return a_dictionary;
135 }
136
137
138 //------------------------------------------------------------------------------
139 //------------------------------------------------------ EngineImpl::createAvp()
140 //------------------------------------------------------------------------------
141 Avp* EngineImpl::createAvp(const AvpId *id) throw(anna::RuntimeException) {
142   Avp *result(NULL);
143   anna::Guard guard(this, "diameter::codec::EngineImpl::createAvp");
144
145   if((result = allocateAvp()) == NULL)
146     throw anna::RuntimeException("diameter::codec::EngineImpl::allocateAvp returns NULL", ANNA_FILE_LOCATION);
147
148   // Sets engine
149   result->setEngine((Engine*)this);
150
151   //result->clear(); better clear this at releaseAvp(), see class-help implementation example
152   if(id) result->setId(*id);
153
154   return result;
155 }
156
157
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");
164
165   if((result = allocateMessage()) == NULL)
166     throw anna::RuntimeException("diameter::codec::EngineImpl::allocateMessage returns NULL", ANNA_FILE_LOCATION);
167
168   // Sets engine
169   result->setEngine((Engine*)this);
170
171   //result->clear(); better clear this at releaseMessage(), see class-help implementation example
172   if(id) result->setId(*id);
173
174   return result;
175 }
176
177
178 //------------------------------------------------------------------------------
179 //-------------------------------------------------- EngineImpl::createMessage()
180 //------------------------------------------------------------------------------
181 Message *EngineImpl::createMessage(const std::string & xmlPathFile) throw(anna::RuntimeException) {
182   Message *result = createMessage();
183   result->loadXML(xmlPathFile);
184   return result;
185 }
186
187
188
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>";
203   return result;
204 }
205
206 //------------------------------------------------------------------------------
207 //---------------------------------------------------------- EngineImpl::asXML()
208 //------------------------------------------------------------------------------
209 anna::xml::Node* EngineImpl::asXML(anna::xml::Node* parent) const
210 throw() {
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");
219
220   if(a_dictionary) a_dictionary->asXML(result);
221
222   return result;
223 }
224
225 //------------------------------------------------------------------------------
226 //--------------------------------------------------------- EngineImpl::asText()
227 //------------------------------------------------------------------------------
228 const char* EngineImpl::asText(const ValidationDepth::_v vd)
229 throw() {
230   static const char* text [] = { "Complete", "FirstError" };
231   return text [vd];
232 }
233
234 //------------------------------------------------------------------------------
235 //--------------------------------------------------------- EngineImpl::asText()
236 //------------------------------------------------------------------------------
237 const char* EngineImpl::asText(const ValidationMode::_v vm)
238 throw() {
239   static const char* text [] = { "BeforeEncoding", "AfterDecoding", "Always", "Never" };
240   return text [vm];
241 }
242
243 //------------------------------------------------------------------------------
244 //--------------------------------------------------------- EngineImpl::asText()
245 //------------------------------------------------------------------------------
246 const char* EngineImpl::asText(const FixMode::_v fm)
247 throw() {
248   static const char* text [] = { "BeforeEncoding", "AfterDecoding", "Always", "Never" };
249   return text [fm];
250 }
251
252 //------------------------------------------------------------------------------
253 //---------------------------------------------- EngineImpl::validationAnomaly()
254 //------------------------------------------------------------------------------
255 void EngineImpl::validationAnomaly(const std::string & description) const throw(anna::RuntimeException) {
256   std::string msg = "Validation error: ";
257
258   if(a_validationDepth == ValidationDepth::FirstError)
259     throw anna::RuntimeException(msg + description, ANNA_FILE_LOCATION);
260
261   LOGWARNING(anna::Logger::warning(msg + description, ANNA_FILE_LOCATION));
262 }
263
264
265 //------------------------------------------------------------------------------
266 //--------------------------------------------------- EngineImpl::avpIdForName()
267 //------------------------------------------------------------------------------
268 anna::diameter::AvpId EngineImpl::avpIdForName(const char * name) throw(anna::RuntimeException) {
269   if(!name)
270     throw anna::RuntimeException("Provided NULL Avp Logical Name", ANNA_FILE_LOCATION);
271
272   if(!a_dictionary) {
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);
276   }
277
278   const stack::Avp * stackAvp = a_dictionary->getAvp(name);
279
280   if(!stackAvp) {
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);
284   }
285
286   return (stackAvp->getId());
287 }
288
289
290 //------------------------------------------------------------------------------
291 //----------------------------------------------- EngineImpl::commandIdForName()
292 //------------------------------------------------------------------------------
293 anna::diameter::CommandId EngineImpl::commandIdForName(const char * name) throw(anna::RuntimeException) {
294   if(!name)
295     throw anna::RuntimeException("Provided NULL Command Logical Name", ANNA_FILE_LOCATION);
296
297   if(!a_dictionary) {
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);
301   }
302
303   const stack::Command * stackCommand = a_dictionary->getCommand(name);
304
305   if(!stackCommand) {
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);
309   }
310
311   return (stackCommand->getId());
312 }
313