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