1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // https://bitbucket.org/testillano/anna
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
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
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.
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.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
38 #include <anna/diameter/stack/Dictionary.hpp>
39 #include <anna/diameter/stack/Engine.hpp>
41 #include <anna/config/defines.hpp>
42 #include <anna/core/tracing/Logger.hpp>
43 #include <anna/xml/xml.hpp>
44 #include <anna/core/functions.hpp>
46 #include <anna/config/defines.hpp>
47 #include <anna/core/functions.hpp>
48 #include <anna/diameter/codec/Format.hpp>
54 using namespace anna::diameter::stack;
58 //------------------------------------------------------------------------------
59 //---------------------------------------------------------------------- #define
60 //------------------------------------------------------------------------------
61 #define ITEM_OVERWRITE(item_n,item,pool,poolNames)\
63 std::string trace = "\n Updated ";\
66 trace += anna::functions::tab(found->asString(), 6);\
67 trace += "\n New content:\n";\
68 trace += anna::functions::tab(item.asString(), 6);\
70 anna::Logger::notice(trace, ANNA_FILE_LOCATION);\
72 pool.erase(pool.find(found->getId()));\
73 poolNames.erase(poolNames.find(found->getName()));
76 //------------------------------------------------------------------------------
77 //----------------------------------------------------- Dictionary::Dictionary()
78 //------------------------------------------------------------------------------
79 Dictionary::Dictionary(void) {
80 a_dtd = (Engine::instantiate()).getDictionariesDTD();
81 a_allowUpdates = false;
85 //------------------------------------------------------------------------------
86 //----------------------------------------------------- Dictionary::initialize()
87 //------------------------------------------------------------------------------
88 void Dictionary::initialize() throw() {
93 a_vendorNames.clear();
95 a_commandNames.clear();
96 a_dtd = (Engine::instantiate()).getDictionariesDTD();
97 // RFC3588 Diameter Formats harcoding:
98 // Basic diameter types
99 Format OctetString(this), Integer32(this), Integer64(this), Unsigned32(this), Unsigned64(this), Float32(this), Float64(this), Grouped(this);
100 OctetString.setRFC3588(codec::Format::OctetString);
101 Integer32.setRFC3588(codec::Format::Integer32);
102 Integer64.setRFC3588(codec::Format::Integer64);
103 Unsigned32.setRFC3588(codec::Format::Unsigned32);
104 Unsigned64.setRFC3588(codec::Format::Unsigned64);
105 Float32.setRFC3588(codec::Format::Float32);
106 Float64.setRFC3588(codec::Format::Float64);
107 Grouped.setRFC3588(codec::Format::Grouped);
108 addFormat(OctetString);
109 addFormat(Integer32);
110 addFormat(Integer64);
111 addFormat(Unsigned32);
112 addFormat(Unsigned64);
116 // Derived diameter types
117 Format Address(this), Time(this), UTF8String(this), DiameterIdentity(this), DiameterURI(this), Enumerated(this), IPFilterRule(this), QoSFilterRule(this);
118 Address.setRFC3588(codec::Format::Address); Address.setParentName(OctetString.getName());
119 Time.setRFC3588(codec::Format::Time); Time.setParentName(OctetString.getName());
120 UTF8String.setRFC3588(codec::Format::UTF8String); UTF8String.setParentName(OctetString.getName());
121 DiameterIdentity.setRFC3588(codec::Format::DiameterIdentity); DiameterIdentity.setParentName(OctetString.getName());
122 DiameterURI.setRFC3588(codec::Format::DiameterURI); DiameterURI.setParentName(OctetString.getName());
123 Enumerated.setRFC3588(codec::Format::Enumerated); Enumerated.setParentName(Integer32.getName());
124 IPFilterRule.setRFC3588(codec::Format::IPFilterRule); IPFilterRule.setParentName(OctetString.getName());
125 QoSFilterRule.setRFC3588(codec::Format::QoSFilterRule); QoSFilterRule.setParentName(OctetString.getName());
128 addFormat(UTF8String);
129 addFormat(DiameterIdentity);
130 addFormat(DiameterURI);
131 addFormat(Enumerated);
132 addFormat(IPFilterRule);
133 addFormat(QoSFilterRule);
134 // Generic AVP hardcoding:
135 Avp genericAvp(this);
136 genericAvp.setCode(0);
137 genericAvp.setVendorId(0/*Vendor::Code::Ietf*/);
138 genericAvp.setName("AVP");
140 Any.setRFC3588(codec::Format::Any);
141 addFormat(Any, true /*reserved*/);
142 genericAvp.setFormatName(Any.getName());
143 genericAvp.setVbit(Avp::FlagRule::mustnot);
144 genericAvp.setMbit(Avp::FlagRule::may);
145 genericAvp.setPbit(Avp::FlagRule::may);
146 genericAvp.setMayEncrypt(false);
151 //------------------------------------------------------------------------------
152 //------------------------------------------------------ Dictionary::addFormat()
153 //------------------------------------------------------------------------------
154 void Dictionary::addFormat(const Format & format, bool reserved) throw(anna::RuntimeException) {
155 if(!reserved && format.isReserved()) {
156 std::string s_ex = anna::functions::asString("Format type '%s' is reserved for internal use", format.getName().c_str());
157 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
160 const Format * found;
162 if(found = getFormat(format.getName())) {
163 if(!a_allowUpdates) {
164 std::string s_ex = "Cannot add a format with an existing type name: ";
165 //s_ex += format.getName();
166 s_ex += format.asString();
168 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
172 std::string trace = "\n Updated format:\n";
173 trace += anna::functions::tab(found->asString(), 6);
174 trace += "\n New content:\n";
175 trace += anna::functions::tab(format.asString(), 6);
177 anna::Logger::notice(trace, ANNA_FILE_LOCATION);
181 a_formats[format.getName()] = format;
185 //------------------------------------------------------------------------------
186 //------------------------------------------------------ Dictionary::addVendor()
187 //------------------------------------------------------------------------------
188 void Dictionary::addVendor(const Vendor & vendor) throw(anna::RuntimeException) {
189 const Vendor * found;
191 if(found = getVendor(vendor.getId())) {
192 if(!a_allowUpdates) {
193 std::string s_ex = "Cannot add a vendor with an existing code: ";
194 s_ex += vendor.asString();
195 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
198 ITEM_OVERWRITE("vendor", vendor, a_vendors, a_vendorNames);
201 if(found = getVendor(vendor.getName())) {
202 if(!a_allowUpdates) {
203 std::string s_ex = "Cannot add a vendor with an existing name: ";
204 s_ex += vendor.asString();
205 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
208 ITEM_OVERWRITE("vendor", vendor, a_vendors, a_vendorNames);
211 a_vendors[vendor.getId()] = vendor;
212 a_vendorNames[vendor.getName()] = getVendor(vendor.getId());
216 //------------------------------------------------------------------------------
217 //--------------------------------------------------------- Dictionary::addAvp()
218 //------------------------------------------------------------------------------
219 void Dictionary::addAvp(const Avp & avp) throw(anna::RuntimeException) {
222 if(found = getAvp(avp.getId())) {
223 if(!a_allowUpdates) {
224 std::string s_ex = "Cannot add an avp with an existing identifier (code,vendor):\n";
225 s_ex += avp.asString();
226 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
229 ITEM_OVERWRITE("avp", avp, a_avps, a_avpNames);
232 if(found = getAvp(avp.getName())) {
233 if(!a_allowUpdates) {
234 std::string s_ex = "Cannot add an avp with an existing name:\n";
235 s_ex += avp.asString();
236 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
239 ITEM_OVERWRITE("avp", avp, a_avps, a_avpNames);
242 a_avps[avp.getId()] = avp;
243 a_avpNames[avp.getName()] = getAvp(avp.getId());
247 //------------------------------------------------------------------------------
248 //----------------------------------------------------- Dictionary::addCommand()
249 //------------------------------------------------------------------------------
250 void Dictionary::addCommand(const Command & command) throw(anna::RuntimeException) {
251 const Command * found;
253 if(found = getCommand(command.getId())) {
254 if(!a_allowUpdates) {
255 std::string s_ex = "Cannot add a command with an existing identifier (code,request):\n";
256 s_ex += command.asString();
257 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
260 ITEM_OVERWRITE("command", command, a_commands, a_commandNames);
263 if(found = getCommand(command.getName())) {
264 if(!a_allowUpdates) {
265 std::string s_ex = "Cannot add a command with an existing name:\n";
266 s_ex += command.asString();
267 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
270 ITEM_OVERWRITE("command", command, a_commands, a_commandNames);
273 a_commands[command.getId()] = command;
274 a_commandNames[command.getName()] = getCommand(command.getId());
280 //------------------------------------------------------------------------------
281 //------------------------------------------------------ Dictionary::getFormat()
282 //------------------------------------------------------------------------------
283 const Format * Dictionary::getFormat(const std::string & formatName) const throw() {
284 const_format_iterator it = a_formats.find(formatName);
286 if(it != format_end()) return ((const Format *) & ((*it).second));
292 //------------------------------------------------------------------------------
293 //------------------------------------------------------ Dictionary::getVendor()
294 //------------------------------------------------------------------------------
295 const Vendor * Dictionary::getVendor(S32 vendorId) const throw() {
296 const_vendor_iterator it = a_vendors.find(vendorId);
298 if(it != vendor_end()) return ((const Vendor *) & ((*it).second));
304 //------------------------------------------------------------------------------
305 //------------------------------------------------------ Dictionary::getVendor()
306 //------------------------------------------------------------------------------
307 const Vendor * Dictionary::getVendor(const std::string & vendorName) const throw() {
308 const_vendorNames_iterator v_it = a_vendorNames.find(vendorName);
310 if(v_it != a_vendorNames.end()) return ((*v_it).second);
316 //------------------------------------------------------------------------------
317 //--------------------------------------------------------- Dictionary::getAvp()
318 //------------------------------------------------------------------------------
319 const Avp * Dictionary::getAvp(const AvpId & avpId) const throw() {
320 const_avp_iterator it = a_avps.find(avpId);
322 if(it != avp_end()) return ((const Avp *) & ((*it).second));
328 //------------------------------------------------------------------------------
329 //--------------------------------------------------------- Dictionary::getAvp()
330 //------------------------------------------------------------------------------
331 const Avp * Dictionary::getAvp(const std::string & avpName) const throw() {
332 const_avpNames_iterator a_it = a_avpNames.find(avpName);
334 if(a_it != a_avpNames.end()) return ((*a_it).second);
340 //------------------------------------------------------------------------------
341 //----------------------------------------------------- Dictionary::getCommand()
342 //------------------------------------------------------------------------------
343 const Command * Dictionary::getCommand(const CommandId & commandId) const throw() {
344 const_command_iterator it = a_commands.find(commandId);
346 if(it != command_end()) return ((const Command *) & ((*it).second));
352 //------------------------------------------------------------------------------
353 //----------------------------------------------------- Dictionary::getCommand()
354 //------------------------------------------------------------------------------
355 const Command * Dictionary::getCommand(const std::string & commandName) const throw() {
356 const_commandNames_iterator c_it = a_commandNames.find(commandName);
358 if(c_it != a_commandNames.end()) return ((*c_it).second);
364 //------------------------------------------------------------------------------
365 //------------------------------------------------------- Dictionary::asString()
366 //------------------------------------------------------------------------------
367 std::string Dictionary::asString(void) const throw() {
368 std::string trace, title;
370 anna::functions::TextHighlightMode::_v thm = anna::functions::TextHighlightMode::LeftAndRightline;
371 trace += anna::functions::highlight("Name", thm);
372 trace += a_name; trace += "\n";
373 const_format_iterator f_it;
374 const_vendor_iterator v_it;
375 const_avp_iterator a_it;
376 const_command_iterator c_it;
380 //title = "Formats (";
381 //title += anna::functions::entriesAsString(format_size());
383 trace += anna::functions::highlight(title, thm);
385 for(f_it = format_begin(); f_it != format_end(); f_it++) {
386 if((*f_it).second.isReserved()) continue;
388 if((*f_it).second.isRFC3588())
389 trace += "(Diameter RFC-3588 type) ";
391 trace += "(Application-specific type) ";
393 trace += (*f_it).second.asString();
400 title += anna::functions::entriesAsString(vendor_size());
402 trace += anna::functions::highlight(title, thm);
404 for(v_it = vendor_begin(); v_it != vendor_end(); v_it++) {
405 trace += (*v_it).second.asString();
412 title += anna::functions::entriesAsString(avp_size());
414 trace += anna::functions::highlight(title, thm);
416 for(a_it = avp_begin(); a_it != avp_end(); a_it++) {
417 trace += (*a_it).second.asString();
423 title = "Commands (";
424 title += anna::functions::entriesAsString(command_size());
426 trace += anna::functions::highlight(title, thm);
428 for(c_it = command_begin(); c_it != command_end(); c_it++) {
429 trace += (*c_it).second.asString();
438 //------------------------------------------------------------------------------
439 //---------------------------------------------------------- Dictionary::asXML()
440 //------------------------------------------------------------------------------
441 anna::xml::Node* Dictionary::asXML(anna::xml::Node* parent) const throw() {
442 // <!ELEMENT dictionary (format*, vendor*, avp*, command*)>
443 // <!ATTLIST dictionary name CDATA #REQUIRED>
444 anna::xml::Node* result = parent->createChild("dictionary");
445 result->createAttribute("name", a_name);
448 for(const_format_iterator it = format_begin(); it != format_end(); it++) {
449 if((*it).second.isReserved()) continue;
451 if((*it).second.isRFC3588()) continue;
453 (*it).second.asXML(result);
457 for(const_vendor_iterator it = vendor_begin(); it != vendor_end(); it++)
458 (*it).second.asXML(result);
461 for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) {
462 if((*it).second.getFormat()->isAny()) continue; // Generic AVP not shown
464 (*it).second.asXML(result);
468 for(const_command_iterator it = command_begin(); it != command_end(); it++)
469 (*it).second.asXML(result);
476 //------------------------------------------------------------------------------
477 //---------------------------------------------------- Dictionary::asXMLString()
478 //------------------------------------------------------------------------------
479 std::string Dictionary::asXMLString() const throw() {
480 anna::xml::Node root("root");
481 return anna::xml::Compiler().apply(asXML(&root));
486 ////------------------------------------------------------------------------------
487 ////----------------------------------------- Dictionary::checkUniqueIdentifiers()
488 ////------------------------------------------------------------------------------
489 //void Dictionary::checkUniqueIdentifiers(const anna::xml::Node *rootNode) const throw(anna::RuntimeException) {
490 // std::map < std::string/*xml ref*/, int/*dummy*/ > formats;
491 // std::map < std::string/*xml ref*/, int/*dummy*/ > vendors;
492 // std::map < std::string/*xml ref*/, int/*dummy*/ > avps;
493 // std::map < std::string/*xml ref*/, int/*dummy*/ > commands;
494 // std::string nodeName, ref;
496 // for (anna::xml::Node::const_child_iterator it = rootNode->child_begin(); it != rootNode->child_end(); it++) {
497 // nodeName = (*it)->getName();
499 // if (nodeName == "format") {
500 // ref = (*it)->getAttribute("name")->getValue();
501 // int refs_i = formats.size(); formats[ref] = 0;
503 // if (formats.size() == refs_i) {
504 // std::string s_ex = anna::functions::asString("Repeated format name reference '%s' not allowed within the same xml dictionary", ref.c_str());
505 // throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
507 // } else if (nodeName == "vendor") {
508 // ref = (*it)->getAttribute("name")->getValue();
509 // int refs_i = vendors.size(); vendors[ref] = 0;
511 // if (vendors.size() == refs_i) {
512 // std::string s_ex = anna::functions::asString("Repeated vendor name reference '%s' not allowed within the same xml dictionary", ref.c_str());
513 // throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
515 // } else if (nodeName == "avp") {
516 // ref = (*it)->getAttribute("name")->getValue();
517 // int refs_i = avps.size(); avps[ref] = 0;
519 // if (avps.size() == refs_i) {
520 // std::string s_ex = anna::functions::asString("Repeated avp name reference '%s' not allowed within the same xml dictionary", ref.c_str());
521 // throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
523 // } else if (nodeName == "command") {
524 // ref = (*it)->getAttribute("name")->getValue();
525 // int refs_i = commands.size(); commands[ref] = 0;
527 // if (commands.size() == refs_i) {
528 // std::string s_ex = anna::functions::asString("Repeated command name reference '%s' not allowed within the same xml dictionary", ref.c_str());
529 // throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
536 //------------------------------------------------------------------------------
537 //------------------------------------------------- Dictionary::extractFormats()
538 //------------------------------------------------------------------------------
539 void Dictionary::extractFormats(const anna::xml::Node *rootNode) throw(anna::RuntimeException) {
542 for(anna::xml::Node::const_child_iterator it = rootNode->child_begin(); it != rootNode->child_end(); it++) {
543 std::string nodeName = (*it)->getName();
545 if(nodeName == "format") {
547 aux.initialize(this);
549 const char * type = (*it)->getAttribute("name")->getCStringValue(); // mandatory
550 const char * parentType = (*it)->getAttribute("parent-type")->getCStringValue(); // mandatory
553 aux.setParentName(parentType /* never empty, basics registered at initialization */);
561 //------------------------------------------------------------------------------
562 //------------------------------------------------- Dictionary::extractVendors()
563 //------------------------------------------------------------------------------
564 void Dictionary::extractVendors(const anna::xml::Node *rootNode) throw(anna::RuntimeException) {
567 for(anna::xml::Node::const_child_iterator it = rootNode->child_begin(); it != rootNode->child_end(); it++) {
568 std::string nodeName = (*it)->getName();
570 if(nodeName == "vendor") {
572 const char * name = (*it)->getAttribute("name")->getCStringValue(); // mandatory
573 S32 code = (*it)->getAttribute("code")->getIntegerValue(); // mandatory
584 //------------------------------------------------------------------------------
585 //---------------------------------------------------- Dictionary::extractAvps()
586 //------------------------------------------------------------------------------
587 void Dictionary::extractAvps(const anna::xml::Node *rootNode) throw(anna::RuntimeException) {
589 const anna::xml::Node *singleNode, *groupedNode;
591 for(anna::xml::Node::const_child_iterator it = rootNode->child_begin(); it != rootNode->child_end(); it++) {
592 std::string nodeName = (*it)->getName();
594 if(nodeName == "avp") {
596 auxAvp.initialize(this);
598 const char * name = (*it)->getAttribute("name")->getCStringValue(); // mandatory
599 S32 code = (*it)->getAttribute("code")->getIntegerValue(); // mandatory
600 const anna::xml::Attribute *vendor_name = (*it)->getAttribute("vendor-name", anna::Exception::Mode::Ignore); // implied
601 S32 vendorCode = 0; /* IETF by default */
604 const char * c_name = vendor_name->getCStringValue();
605 auxAvp.setVendorName(c_name);
606 const_vendorNames_iterator v_it = a_vendorNames.find(c_name);
608 if(v_it == a_vendorNames.end()) {
609 std::string s_ex = anna::functions::asString("Vendor '%s', referenced at '%s' avp definition, not found at xml", c_name, name);
610 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
613 vendorCode = ((*v_it).second)->getId();
617 auxAvp.setCode(code);
618 auxAvp.setVendorId(vendorCode);
619 auxAvp.setName(name);
620 // libxml2 doesn't support default attribute value retrieving. All default values will be replaced by #IMPLIED sintax:
621 const anna::xml::Attribute *v_bit, *m_bit, *p_bit, *may_encript;
622 v_bit = (*it)->getAttribute("v-bit", anna::Exception::Mode::Ignore);
623 m_bit = (*it)->getAttribute("m-bit", anna::Exception::Mode::Ignore);
624 p_bit = (*it)->getAttribute("p-bit", anna::Exception::Mode::Ignore);
625 may_encript = (*it)->getAttribute("may-encrypt", anna::Exception::Mode::Ignore);
626 auxAvp.setVbit(v_bit ? (Avp::FlagRule::asEnum(v_bit->getCStringValue())) : (Avp::FlagRule::mustnot));
627 auxAvp.setMbit(m_bit ? (Avp::FlagRule::asEnum(m_bit->getCStringValue())) : (Avp::FlagRule::may));
628 auxAvp.setPbit(p_bit ? (Avp::FlagRule::asEnum(p_bit->getCStringValue())) : (Avp::FlagRule::may));
629 auxAvp.setMayEncrypt(may_encript ? ((may_encript->getValue()) == "yes") : false);
631 // Check vendor specific bit:
632 if(vendorCode && (auxAvp.getVbit() == Avp::FlagRule::mustnot)) {
633 std::string s_ex = anna::functions::asString("Flag rules for vendor specific bit (mustnot) at '%s' avp definicion, are incompatible with non-zeroed vendor id %s", name, getVendor(vendorCode)->asString().c_str());
634 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
637 if(!vendorCode && (auxAvp.getVbit() == Avp::FlagRule::must)) {
638 std::string s_ex = anna::functions::asString("Flag rules for vendor specific bit (must) at '%s' avp definicion, are incompatible with zeroed vendor id %s", name, getVendor(vendorCode)->asString().c_str());
639 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
642 // Single or grouped:
643 singleNode = (*it)->find("single", anna::Exception::Mode::Ignore);
644 groupedNode = (*it)->find("grouped", anna::Exception::Mode::Ignore);
648 const char * formatName = singleNode->getAttribute("format-name")->getCStringValue(); // mandatory
649 const anna::xml::Attribute *_enum = singleNode->getAttribute("enum", anna::Exception::Mode::Ignore); // implied
651 const Format *format = getFormat(formatName);
654 std::string s_ex = anna::functions::asString("Format '%s', referenced at '%s' avp definition, not found at dictionary (neither xml nor RFC 3588 diameter format types)", formatName, name);
655 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
658 auxAvp.setFormatName(formatName);
661 const char *enums = _enum->getCStringValue();
663 if(!format->isEnumerated()) {
664 std::string s_ex = anna::functions::asString("Enumerated literal '%s' is not allowed for '%s' avp format", enums, formatName);
665 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
668 auxAvp.setEnums(enums);
671 for(anna::xml::Node::const_child_iterator l_it = singleNode->child_begin(); l_it != singleNode->child_end(); l_it++) {
673 std::string data = (*l_it)->getAttribute("data")->getValue(); // mandatory
674 std::string alias = (*l_it)->getAttribute("alias")->getValue(); // mandatory
676 auxAvp.addLabel(data, alias);
680 auxAvp.setFormatName(codec::Format::asText(codec::Format::Grouped));
681 // Wait for avprule insertion, because we need complete avp reference pool (*)
689 // (*) Avp rules adding:
690 for(anna::xml::Node::const_child_iterator it = rootNode->child_begin(); it != rootNode->child_end(); it++) {
691 std::string nodeName = (*it)->getName();
693 if(nodeName == "avp") {
695 const char * gid = (*it)->getAttribute("name")->getCStringValue(); // mandatory
697 const_avpNames_iterator a_it = a_avpNames.find(gid);
698 Avp * gavp = (Avp *)((*a_it).second);
700 if(!gavp) continue; // it could be mising (a redefinition could have removed it)
702 const Format *format = gavp->getFormat();
705 if(format->isGrouped()) {
706 AvpRule auxAvpRule(this);
707 groupedNode = (*it)->find("grouped");
709 for(anna::xml::Node::const_child_iterator r_it = groupedNode->child_begin(); r_it != groupedNode->child_end(); r_it++) {
711 std::string id = (*r_it)->getAttribute("id")->getValue(); // mandatory
712 std::string type = (*r_it)->getAttribute("type")->getValue(); // mandatory
713 const anna::xml::Attribute *qual = (*r_it)->getAttribute("qual", anna::Exception::Mode::Ignore); // implied
715 const Avp * avp = getAvp(id);
718 std::string s_ex = anna::functions::asString("Avp '%s', referenced at avp rule definition within grouped avp '%s', not found at xml", id.c_str(), gid);
719 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
722 auxAvpRule.setAvpName(id);
723 auxAvpRule.setPresence(AvpRule::Presence::asEnum(type));
724 auxAvpRule.setQual(qual ? (qual->getValue()) : "");
725 gavp->addAvpRule(auxAvpRule);
731 // Check avp loops between grouped avps:
733 // In order to avoid loops, we could force to define grouped avps which children
734 // had been previously defined at xml file. In this way, is imposible to get a loop:
737 // A = grouped of B,C,D -> error, B unknown
738 // B = grouped of A,F -> with former definition, would become a loop
740 // But this supposes a restriction at xml configuration (specific order).
741 // The other way is an internal check: a grouped AVP won't have descendants within
742 // its ascendants. Then we will check all grouped avps in this way:
744 // 1. Searching for another grouped avps which are parents for this avp.
745 // 2. If these are children (even this avp(*)) at avp definition, then a loop is detected.
747 // Example 1: (1) Analyzing 'A', found parent 'B' / (2) 'B' is already children of 'A'
755 // (*) Example 2: (1) Analyzing 'A', found parent 'A' / (2) 'A' is already children of 'A'
761 for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) {
762 const Avp & avp = (*it).second;
764 if(!((avp.getFormat())->isGrouped())) continue;
766 for(const_avp_iterator it_p = avp_begin(); it_p != avp_end(); it_p++) {
767 const Avp & avp_p = (*it_p).second;
769 if(!((avp_p.getFormat())->isGrouped())) continue;
771 if(avp_p.isChild(avp.getId())) {
772 if(avp.isChild(avp_p.getId())) {
776 s_ex = anna::functions::asString("Loop detected between grouped avps '%s' and '%s'", avp.getName().c_str(), avp_p.getName().c_str());
778 s_ex = anna::functions::asString("Loop within grouped avp '%s': cannot contain itself !!", avp.getName().c_str());
780 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
781 } // parent is children of (ref): loop !
784 } // search grouped avps (ref)
788 //------------------------------------------------------------------------------
789 //------------------------------------------------ Dictionary::extractCommands()
790 //------------------------------------------------------------------------------
791 void Dictionary::extractCommands(const anna::xml::Node *rootNode) throw(anna::RuntimeException) {
794 // (*) Avp rules adding:
795 for(anna::xml::Node::const_child_iterator it = rootNode->child_begin(); it != rootNode->child_end(); it++) {
796 std::string nodeName = (*it)->getName();
798 if(nodeName == "command") {
800 auxCommand.initialize(this);
802 const char * name = (*it)->getAttribute("name")->getCStringValue(); // mandatory
803 U24 code = (*it)->getAttribute("code")->getIntegerValue(); // mandatory
804 std::string type = (*it)->getAttribute("type")->getValue(); // mandatory
806 auxCommand.setCode(code);
807 auxCommand.setName(name);
808 auxCommand.setRequest(type == "Request");
809 AvpRule auxAvpRule(this);
811 for(anna::xml::Node::const_child_iterator r_it = (*it)->child_begin(); r_it != (*it)->child_end(); r_it++) {
813 std::string id = (*r_it)->getAttribute("id")->getValue(); // mandatory
814 std::string type = (*r_it)->getAttribute("type")->getValue(); // mandatory
815 const anna::xml::Attribute *qual = (*r_it)->getAttribute("qual", anna::Exception::Mode::Ignore); // implied
817 const Avp * avp = getAvp(id);
820 std::string s_ex = anna::functions::asString("Avp '%s', referenced at avp rule definition within command '%s', not found at xml", id.c_str(), name);
821 throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
824 auxAvpRule.setAvpName(id);
825 auxAvpRule.setPresence(AvpRule::Presence::asEnum(type));
826 auxAvpRule.setQual(qual ? (qual->getValue()) : "");
827 auxCommand.addAvpRule(auxAvpRule);
831 addCommand(auxCommand);
837 //------------------------------------------------------------------------------
838 //----------------------------------------------------------- Dictionary::load()
839 //------------------------------------------------------------------------------
840 void Dictionary::load(const std::string & xmlPathFile) throw(anna::RuntimeException) {
841 if(xmlPathFile == "")
842 throw anna::RuntimeException("Empty xml path file provided", ANNA_FILE_LOCATION);
845 std::string trace = "Loading diameter dictionary from '";
846 trace += xmlPathFile;
848 anna::Logger::debug(trace, ANNA_FILE_LOCATION);
852 anna::xml::DocumentFile xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
853 const anna::xml::Node *rootNode;
854 xmlDocument.initialize(xmlPathFile.c_str()); // fail here is i/o error
855 rootNode = xmlDocument.parse(*a_dtd); // Parsing: fail here if xml violates dtd
856 a_name = rootNode->getAttribute("name")->getValue();
857 //checkUniqueIdentifiers(rootNode); // Check unique id within xml, for vendor, avp and command nodes:
858 extractFormats(rootNode); // Add formats:
859 extractVendors(rootNode); // Add vendors:
860 extractAvps(rootNode); // Add avps:
861 extractCommands(rootNode); // Add commands:
862 } catch(anna::RuntimeException& ex) {
868 LOGDEBUG(anna::Logger::debug(asString(), ANNA_FILE_LOCATION));