Updated license
[anna.git] / source / diameter / stack / Engine.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
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
16 // distribution.
17 //     * Neither the name of Google Inc. 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.
20 //
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.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 // Local
38 #include <anna/diameter/stack/Engine.hpp>
39
40 #include <anna/xml/xml.hpp>
41 #include <anna/core/functions.hpp>
42 #include <anna/core/tracing/Logger.hpp>
43
44 #include <anna/core/functions.hpp>
45
46 // Standard
47 #include <stdarg.h>
48
49
50
51 // libxml2 Parser doesn't support default attribute value retrieving:
52 // <!ATTLIST avp name CDATA #REQUIRED code CDATA #REQUIRED vendor-name CDATA #IMPLIED may-encrypt (yes | no) \"no\" v-bit (must | may | shouldnot | mustnot) \"mustnot\" m-bit (must | may | shouldnot | mustnot) \"may\" p-bit (must | may | shouldnot | mustnot) \"may\">\n\
53 // This dtd sintax will be replaced by #IMPLIED attributes.
54
55 namespace anna {
56 namespace diameter {
57 namespace stack {
58
59 const char *StackDTD = "\
60 <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
61 <!-- Diameter dictionary DTD -->\n\
62 \n\
63 <!-- <!ELEMENT dictionary (format*, vendor+, avp+, command+)> allowed empty dictionary load: diameter base protocol would be harcoded -->\n\
64 <!ELEMENT dictionary (format*, vendor*, avp*, command*)>\n\
65 <!ATTLIST dictionary name CDATA #REQUIRED>\n\
66 <!--\n\
67    Dictionary definitions\n\
68 \n\
69    name: Dictionary name\n\
70 -->\n\
71 \n\
72 <!ELEMENT format EMPTY>\n\
73 <!ATTLIST format name CDATA #REQUIRED parent-type ( OctetString | Integer32 | Integer64 | Unsigned32 | Unsigned64 | Float32 | Float64 ) #REQUIRED>\n\
74 <!--\n\
75    Format record\n\
76 \n\
77    name:        Format type unique name\n\
78    parent-type: Format parent type within diameter basics: OctetString, Integer32, Integer64, Unsigned32, Unsigned64, Float32, Float64\n\
79 -->\n\
80 \n\
81 <!ELEMENT vendor EMPTY>\n\
82 <!ATTLIST vendor name CDATA #REQUIRED code CDATA #REQUIRED>\n\
83 <!--\n\
84    Vendor record\n\
85 \n\
86    name: Vendor unique name\n\
87    code: Vendor numeric code\n\
88 -->\n\
89 \n\
90 <!ELEMENT avp (single | grouped)>\n\
91 <!ATTLIST avp name CDATA #REQUIRED code CDATA #REQUIRED vendor-name CDATA #IMPLIED may-encrypt (yes | no) #IMPLIED v-bit (must | may | shouldnot | mustnot) #IMPLIED m-bit (must | may | shouldnot | mustnot) #IMPLIED p-bit (must | may | shouldnot | mustnot) #IMPLIED>\n\
92 <!--\n\
93    Avp record\n\
94 \n\
95    name:          Avp unique name\n\
96    code:          Avp numeric code\n\
97    vendor-name:   Avp vendor name with vendor-id value. Gets IETF (zero value) when missing\n\
98    may-encrypt:   Avp data may be encrypted (yes, no). By default is 'no'\n\
99    v-bit:         Avp vendor-specific bit rule (must, may, shouldnot, mustnot). By default is 'mustnot'\n\
100    m-bit:         Avp mandatory bit rule (must, may, shouldnot, mustnot). By default is 'may'\n\
101    p-bit:         Avp protected (encryption) bit rule (must, may, shouldnot, mustnot). By default is 'may'\n\
102 -->\n\
103 \n\
104 <!ELEMENT single (label*)>\n\
105 <!ATTLIST single format-name CDATA #REQUIRED enum CDATA #IMPLIED>\n\
106 <!--\n\
107    Non-grouped avp struct\n\
108 \n\
109    format-name: Avp format type name (RFC3588: 'OctetString, Integer32, Integer64, Unsigned32, Unsigned64, Float32, Float64, Address, Time, UTF8String, DiameterIdentity, DiameterURI, Enumerated, IPFilterRule, QoSFilterRule' or application format data type names)\n\
110    enum:        Enum values restriction for enumerated-format avp: '[i][,[j]][,[m-n]...]'\n\
111 -->\n\
112 \n\
113 <!ELEMENT label EMPTY>\n\
114 <!ATTLIST label data CDATA #REQUIRED alias CDATA #REQUIRED>\n\
115 <!--\n\
116    Mostly used with enumerated-type avps\n\
117 \n\
118    data:    Specific avp data\n\
119    alias:   Data-linked name\n\
120 -->\n\
121 \n\
122 <!ELEMENT grouped (avprule+)>\n\
123 <!--\n\
124    Grouped avp struct\n\
125 -->\n\
126 \n\
127 <!ELEMENT avprule EMPTY>\n\
128 <!ATTLIST avprule id CDATA #REQUIRED type (Fixed | Mandatory | Optional) #REQUIRED qual CDATA #IMPLIED>\n\
129 <!--\n\
130    Avp rule definition\n\
131 \n\
132    id:   Avp name reference. Generic AVP is harcoded with 'AVP' name\n\
133    type: Avp type (Fixed, Mandatory, Optional)\n\
134    qual: Avp qualifier: *:0-inf; x*:x-inf; *x:0-x; x*y:x-y; missing:1(Fixed, Mandatory), 0-1(Optional)\n\
135 -->\n\
136 \n\
137 <!ELEMENT command (avprule*)>\n\
138 <!ATTLIST command name CDATA #REQUIRED code CDATA #REQUIRED type (Request | Answer) #REQUIRED>\n\
139 <!--\n\
140    Command record\n\
141 \n\
142    name:       Command unique name\n\
143    code:       Command numeric code\n\
144    type:       Command type (Request, Answer)\n\
145 -->\n\
146 \n\
147 ";
148
149 }
150 }
151 }
152
153
154 //------------------------------------------------------------------------------
155 //------------------------------------------------------------- Engine::Engine()
156 //------------------------------------------------------------------------------
157 anna::diameter::stack::Engine::Engine(void) {
158   anna::xml::functions::initialize();
159   a_dtd.initialize(StackDTD);
160 }
161
162
163 //------------------------------------------------------------------------------
164 //------------------------------------------------------ Engine::getDictionary()
165 //------------------------------------------------------------------------------
166 const anna::diameter::stack::Dictionary * anna::diameter::stack::Engine::getDictionary(int stackId) const throw() {
167   const Dictionary * result = NULL;
168   const_stack_iterator it = a_stacks.find(stackId);
169
170   if(it != stack_end()) result = (*it).second;
171
172   return result;
173 }
174
175
176 //------------------------------------------------------------------------------
177 //----------------------------------------------------------- Engine::asString()
178 //------------------------------------------------------------------------------
179 std::string anna::diameter::stack::Engine::asString(void) const throw() {
180   std::string trace;
181   int stackId;
182
183   if(isEmpty()) {
184     trace = "No diameter dictionaries found";
185   } else {
186     int numberOfStacks = stack_size();
187     trace = ((numberOfStacks > 1) ? "Multi-stack " : "Mono-stack ");
188     trace += "configuration, ";
189     trace += anna::functions::entriesAsString(numberOfStacks);
190     trace += ":\n";
191     trace += "\n";
192
193     for(const_stack_iterator it = stack_begin(); it != stack_end(); it++) {
194       std::string title = "Diameter stack id = ";
195       title += anna::functions::asString((*it).first);
196       trace += anna::functions::highlightJustify(title);
197       trace += (*it).second->asString(); trace += "\n";
198     }
199   }
200
201   trace += "\n";
202   return (trace);
203 }
204
205
206 //------------------------------------------------------------------------------
207 //--------------------------------------------------- Engine::createDictionary()
208 //------------------------------------------------------------------------------
209 anna::diameter::stack::Dictionary *  anna::diameter::stack::Engine::createDictionary(int stackId, const std::string & xmlPathFile) throw(anna::RuntimeException) {
210   Dictionary * result = const_cast<Dictionary *>(getDictionary(stackId));
211
212   if(result) {  // if exists, launch exception
213     throw anna::RuntimeException("Such provided stack id has already been created. Removes it before call this method", ANNA_FILE_LOCATION);
214   } else { // new stack
215     a_stacks[stackId] = new Dictionary(); // no need for singleton destructor
216     const_stack_iterator it = a_stacks.find(stackId);
217     result = (Dictionary *)(*it).second;
218   }
219
220   if(xmlPathFile != "") {
221     try {
222       result->load(xmlPathFile);
223     } catch(anna::RuntimeException& ex) {
224       ex.trace();
225       throw ex;
226     }
227   }
228
229   return result;
230 }
231
232
233 void anna::diameter::stack::Engine::loadDictionary(const std::vector<int> & stacks, const std::string & xmlPathFile) throw(anna::RuntimeException) {
234   std::vector<int>::const_iterator it;
235   Dictionary *d;
236
237   if(xmlPathFile == "")
238     throw anna::RuntimeException("Empty xml path file provided", ANNA_FILE_LOCATION);
239
240   for(it = stacks.begin(); it != stacks.end(); it++) {
241     d = const_cast<Dictionary*>(getDictionary(*it));
242
243     if(d) d->load(xmlPathFile);
244     else {
245       LOGWARNING(
246         std::string trace = "Cannot load dictionary '";
247         trace += xmlPathFile;
248         trace += "' over stack id '";
249         trace += anna::functions::asString(*it);
250         trace += "' because it doesn't exists";
251         anna::Logger::warning(trace, ANNA_FILE_LOCATION);
252       );
253     }
254   }
255 }
256
257 void anna::diameter::stack::Engine::loadDictionary(const std::string & xmlPathFile) throw(anna::RuntimeException) {
258   Dictionary *d;
259
260   if(xmlPathFile == "")
261     throw anna::RuntimeException("Empty xml path file provided", ANNA_FILE_LOCATION);
262
263   for(const_stack_iterator it = stack_begin(); it != stack_end(); it++) {
264     d = const_cast<Dictionary*>(getDictionary((*it).first));
265     d->load(xmlPathFile);
266   }
267 }
268
269
270 //------------------------------------------------------------------------------
271 //-------------------------------------------------------- Engine::removeStack()
272 //------------------------------------------------------------------------------
273 void anna::diameter::stack::Engine::removeStack(int stackId) throw() {
274   stack_iterator it = a_stacks.find(stackId);
275
276   if(it != stack_end()) {  // if exists, clear
277     //(*it).second->clear();
278     a_stacks.erase(it);
279   } else { // new stack
280     LOGWARNING(
281       std::string trace = "Cannot remove stack id '";
282       trace += anna::functions::asString(stackId);
283       trace += "' because it doesn't exists";
284       anna::Logger::warning(trace, ANNA_FILE_LOCATION);
285     );
286   }
287 }