-// ANNA - Anna is Not Nothingness Anymore
-//
-// (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
-//
-// http://redmine.teslayout.com/projects/anna-suite
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Authors: eduardo.ramos.testillano@gmail.com
-// cisco.tierra@gmail.com
+// ANNA - Anna is Not Nothingness Anymore //
+// //
+// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
+// //
+// See project site at http://redmine.teslayout.com/projects/anna-suite //
+// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
#ifndef anna_diameter_codec_functions_hpp
#define anna_diameter_codec_functions_hpp
-// Local
+// Project
#include <anna/diameter/defines.hpp>
-
#include <anna/core/RuntimeException.hpp>
+#include <anna/xml/xml.hpp>
// STL
#include <string>
namespace codec {
+static const char *MessageDTD = "\
+<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
+<!-- Diameter message DTD -->\n\
+\n\
+<!ELEMENT message (avp*)>\n\
+<!ELEMENT avp (avp*)>\n\
+\n\
+<!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-to-end-id CDATA #IMPLIED>\n\
+<!--\n\
+ version: Diameter version. Sets '1' by default\n\
+ name: Command name within working stack (dictionary identifier)\n\
+ p-bit: (P)roxiable bit flag (yes, no). By default is 'no'\n\
+ e-bit: (E)rror bit flag (yes, no). By default is 'no'\n\
+ t-bit: Potentially re-(T)ransmitted bit flag (yes, no). By default is 'no'\n\
+\n\
+ In order to get more coding capabilities, command code and flags could be established instead of former fields,\n\
+ but neither of them are allowed if the other are present (and vice versa):\n\
+\n\
+ code: Command code\n\
+ 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\
+\n\
+\n\
+ application-id: Message application id\n\
+ hop-by-hop-id: Message hop by hop id. Sets '0' by default\n\
+ end-to-end-id: Message end to end id. Sets '0' by default\n\
+-->\n\
+\n\
+<!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\
+<!--\n\
+ name: Avp name within working stack (dictionary identifier)\n\
+\n\
+ In order to get more coding capabilities, avp code, vendor-id and flags could be established instead of former avp name,\n\
+ but neither of them are allowed if 'name' is provided (and vice versa):\n\
+\n\
+ code: Avp code\n\
+ vendor-code: Avp vendor code\n\
+ 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\
+ 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\
+ 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\
+\n\
+\n\
+ data: Natural string representation for avp data. Specially applicable with numbers and printable strings, but also\n\
+ useful for certain formats which could be easily understandable in such friendly/smart representation. We will\n\
+ achieve different human-readable strings depending on data format:\n\
+\n\
+ [ OctetString ] (if printable, but not recommended)\n\
+ [ Integer32, Integer64, Unsigned32, Unsigned64, Float32, Float64 ] (normal number representation)\n\
+ [ Time ] (NTP timestamp, normal number representation)\n\
+ [ Address ] ('<type (IANA Address Family Number)>|<value>' representation; i.e. '1|192.168.0.1'(IPv4), '8|34616279266'(E164), etc.\n\
+ Type (and pipe) field could be avoided for IPv4 and IPv6 address types (a light parse checking is done: one colon for\n\
+ IPv6, one dot for IPv4). Internal engine always includes type on data field, which is also recommended for inputs.\n\
+ Currently, only IPv4, IPv6 and E164 address types have a known printable presentation, anyway using printable format\n\
+ for another types will encode the address value directly as E164 does)\n\
+ [ UTF8String, DiameterIdentity, DiameterURI ] (printable)\n\
+ [ IPFilterRule, QoSFilterRule ] (uses ASCII charset, printable)\n\
+\n\
+ New application formats must define specific natural representation for internal raw data\n\
+\n\
+ hex-data: Hexadecimal octet sequence representation (i.e. 'af012fb3', with even number of digits). Suitable for whatever kind\n\
+ of diameter format, but mandatory for non printable information. OctetString usually transport non human-readable\n\
+ data and should better be encoded within this field although being printable. Unknown avps (which fails identifying\n\
+ provided name or code/vendor-code) must always use this representation.\n\
+\n\
+ Xml representation for decoded messages shows natural content except for 'OctetString' format and unknown avps. Anyway, when printable,\n\
+ OctetString could show such information at data field apart from hex-data, because many implementations use this format to transport\n\
+ readable-string data. In general, one of the data fields is mandatory except for 'Grouped' type (its data is another level of avps).\n\
+ Application-specific formats must decide the way to represent its contents, being recommended to use a natural representation if possible,\n\
+ because xml is read by humans with testing and monitoring purposes.\n\
+-->\n\
+\n\
+";
+
+
+
+
// Used for alarms, tracing and Failed-AVP construction:
typedef struct parent {
- // Used on decoding:
- anna::diameter::CommandId MessageId;
- std::string MessageName;
+ // Used on decoding:
+ anna::diameter::CommandId MessageId;
+ std::string MessageName;
- std::vector<anna::diameter::AvpId> AvpsId;
- std::vector<std::string> AvpsName;
+ std::vector<anna::diameter::AvpId> AvpsId;
+ std::vector<std::string> AvpsName;
- void setMessage(const anna::diameter::CommandId & mid, const char *mname = NULL /* well known in validation */) throw();
- void addAvp(const anna::diameter::AvpId & aid, const char *aname = NULL /* well known in validation */) throw();
- std::string asString() const throw();
+ void setMessage(const anna::diameter::CommandId & mid, const char *mname = NULL /* well known in validation */) throw();
+ void addAvp(const anna::diameter::AvpId & aid, const char *aname = NULL /* well known in validation */) throw();
+ std::string asString() const throw();
} parent_t;
static HopByHop getHopByHop(const anna::DataBlock &) throw(anna::RuntimeException);
static EndToEnd getEndToEnd(const anna::DataBlock &) throw(anna::RuntimeException);
+ static bool requestBit(const anna::DataBlock &) throw(anna::RuntimeException);
+ static bool proxiableBit(const anna::DataBlock &) throw(anna::RuntimeException);
+ static bool errorBit(const anna::DataBlock &) throw(anna::RuntimeException);
+ static bool potentiallyReTransmittedMessageBit(const anna::DataBlock &) throw(anna::RuntimeException);
+
static bool isRequest(const CommandId & cid) throw() { return (cid.second); }
- static bool isRequest(const anna::DataBlock &) throw(anna::RuntimeException);
+ static bool isRequest(const anna::DataBlock & db) throw(anna::RuntimeException) { return requestBit(db); }
static bool isAnswer(const CommandId & cid) throw() { return (!isRequest(cid)); }
static bool isAnswer(const anna::DataBlock & db) throw(anna::RuntimeException) { return (!isRequest(db)); }
+
/**
- * Decodes a Command Header. This helper cannot check boundaries. start pointer must be a valid command context.
- *
- * @param start Must be a valid command start (point to the command version byte).
- * @param version Diameter version.
- * @param length Message length.
- * @param flags Command flags.
- * @param id Command identification (code, request<true,false>).
- * @param appId Application-ID.
- * @param hbh Hop-by-Hop Identifier.
- * @param ete End-to-End Identifier.
- */
+ * Decodes a Command Header. This helper cannot check boundaries. start pointer must be a valid command context.
+ *
+ * @param start Must be a valid command start (point to the command version byte).
+ * @param version Diameter version.
+ * @param length Message length.
+ * @param flags Command flags.
+ * @param id Command identification (code, request<true,false>).
+ * @param appId Application-ID.
+ * @param hbh Hop-by-Hop Identifier.
+ * @param ete End-to-End Identifier.
+ */
static void decodeCommandHeader(const char *start, char & version, U24 & length, char & flags, CommandId & id, int & appId, int & hbh, int & ete) throw(anna::RuntimeException);
/**
- * Decodes an AVP. This helper cannot check boundaries. start pointer must be a valid avp context.
- *
- * @param start Must be a valid avp start (point to the 32-bits avp code word).
- * @param id Avp identification (code, vendorId).
- * @param flags Avp flags byte.
- * @param length Avp length (includes code, flags, length itself, vendorId if exists and data length).
- * @param data Avp data part.
- */
+ * Decodes an AVP. This helper cannot check boundaries. start pointer must be a valid avp context.
+ *
+ * @param start Must be a valid avp start (point to the 32-bits avp code word).
+ * @param id Avp identification (code, vendorId).
+ * @param flags Avp flags byte.
+ * @param length Avp length (includes code, flags, length itself, vendorId if exists and data length).
+ * @param data Avp data part.
+ */
static void decodeAVP(const char *start, AvpId & id, char & flags, int & length, std::string & data) throw(anna::RuntimeException);
/**
- * Gets the next AVP pointer reference starting from a first-avp datablock. It could be the first avp within
- * a command, or within an grouped avp.
- *
- * @param avpsDB AVPs set as datablock
- * @param start Point to start the search. Must be a valid avp start (point to the 32-bits avp code word).
- *
- * @return Pointer to the next AVP found. NULL if no more.
- */
- static const char * nextAVP(const anna::DataBlock & avpsDB, const char *start) throw(anna::RuntimeException);
+ * Gets the next AVP pointer reference starting from a first-avp data block. It could be the first avp within
+ * a command, or within an grouped avp.
+ *
+ * @param avpsDB AVP data block buffer pointer
+ * @param avpsLen AVP data block buffer length
+ * @param start Point to start the search. Must be a valid avp start (point to the 32-bits avp code word).
+ *
+ * @return Pointer to the next AVP found. NULL if no more.
+ */
+ static const char * nextAVP(const char *avpsDB, int avpsLen, const char *start) throw(anna::RuntimeException);
+
+ // /**
+ // * Gets the next AVP pointer reference starting from a first-avp datablock. It could be the first avp within
+ // * a command, or within an grouped avp.
+ // *
+ // * @param avpsDB AVPs set as datablock
+ // * @param start Point to start the search. Must be a valid avp start (point to the 32-bits avp code word).
+ // *
+ // * @return Pointer to the next AVP found. NULL if no more.
+ // */
+ // static const char * nextAVP(const anna::DataBlock & avpsDB, const char *start) throw(anna::RuntimeException);
/**
- * Gets the next AVP pointer reference within an AVPs set datablock with a certain AVP identification.
- *
- * @param avpsDB AVPs set as datablock
- * @param id Avp identification (code, vendorId).
- * @param n Ocurrence number (first avp, second avp, etc.)
- *
- * @return Pointer to first AVP found with identification provided. NULL if not found.
- */
- static const char * findAVP(const anna::DataBlock & avpsDB, const AvpId & id, int n = 1) throw(anna::RuntimeException);
+ * Gets the next AVP pointer reference within an AVPs set data block with a certain AVP identification.
+ *
+ * @param avpsDB AVP data block buffer pointer
+ * @param avpsLen AVP data block buffer length
+ * @param id Avp identification (code, vendorId).
+ * @param n Ocurrence number (first avp, second avp, etc.). 1 by default.
+ *
+ * @return Pointer to first AVP found with identification provided. NULL if not found.
+ */
+ static const char *findAVP(const char *avpsDB, int avpsLen, const diameter::AvpId & id, int n = 1) throw(anna::RuntimeException);
+
+ // /**
+ // * Gets the next AVP pointer reference within an AVPs set datablock with a certain AVP identification.
+ // *
+ // * @param avpsDB AVPs set as datablock
+ // * @param id Avp identification (code, vendorId).
+ // * @param n Ocurrence number (first avp, second avp, etc.). 1 by default.
+ // *
+ // * @return Pointer to first AVP found with identification provided. NULL if not found.
+ // */
+ // static const char * findAVP(const anna::DataBlock & avpsDB, const AvpId & id, int n = 1) throw(anna::RuntimeException);
// modifiers
static void setHopByHop(anna::DataBlock &, HopByHop) throw(anna::RuntimeException);
static void setEndToEnd(anna::DataBlock &, EndToEnd) throw(anna::RuntimeException);
+ static void setPotentiallyReTransmittedMessageBit(const anna::DataBlock & db, bool activate = true) throw(anna::RuntimeException);
+
+
+ /**
+ Interpret a xml file in order to create a memory xml document.
+ The xml file is based on this message DTD:
+
+ <pre>
+ <!ELEMENT message (avp*)>
+ <!ELEMENT avp (avp*)>
+
+ <!ATTLIST message version CDATA #IMPLIED name CDATA #IMPLIED code CDATA #IMPLIED flags CDATA #IMPLIED application-id CDATA #REQUIRED hop-by-hop-id CDATA #IMPLIED end-to-end-id CDATA #IMPLIED>
+ <!--
+ version: Diameter version. Sets '1' by default
+ name: Command name within working stack (dictionary identifier)
+
+ In order to get more coding capabilities, command code and flags could be established instead of former command name,
+ but neither of them are allowed if 'name' is provided (and vice versa):
+
+ code: Command code
+ 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
+
+
+ application-id: Message application id
+ hop-by-hop-id: Message hop by hop id. Sets '0' by default
+ end-to-end-id: Message end to end id. Sets '0' by default
+ -->
+
+ <!ATTLIST avp name CDATA #IMPLIED code CDATA #IMPLIED vendor-code CDATA #IMPLIED flags CDATA #IMPLIED data CDATA #IMPLIED hex-data CDATA #IMPLIED>
+ <!--
+ name: Avp name within working stack (dictionary identifier)
+
+ In order to get more coding capabilities, avp code, vendor-id and flags could be established instead of former avp name,
+ but neither of them are allowed if 'name' is provided (and vice versa):
+
+ code: Avp code
+ vendor-code: Avp vendor code
+ 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)
+
+
+ data: Natural string representation for avp data. Specially applicable with numbers and printable strings, but also
+ useful for certain formats which could be easily understandable in such friendly/smart representation. We will
+ achieve different human-readable strings depending on data format:
+
+ [ OctetString ] (if printable, but not recommended)
+ [ Integer32, Integer64, Unsigned32, Unsigned64, Float32, Float64 ] (normal number representation)
+ [ Time ] (NTP timestamp, normal number representation)
+ [ Address ] (auto detects IPv4 or IPv6 address version, then only ip address is specified: IPv4 with dots, IPv6 with colons)
+ [ UTF8String, DiameterIdentity, DiameterURI ] (printable)
+ [ IPFilterRule, QoSFilterRule ] (uses ASCII charset, printable)
+
+ New application formats must define specific natural representation for internal raw data
+
+ hex-data: Hexadecimal octet sequence representation (i.e. 'af012fb3', with even number of digits). Suitable for whatever kind
+ of diameter format, but mandatory for non printable information. OctetString usually transport non human-readable
+ data and should better be encoded within this field although being printable. Unknown avps (which fails identifying
+ provided name or code/vendor-code) must always use this representation.
+
+ Xml representation for decoded messages shows natural content except for 'OctetString' format and unknown avps. Anyway, when printable,
+ OctetString could show such information at data field apart from hex-data, because many implementations use this format to transport
+ readable-string data. In general, one of the data fields is mandatory except for 'Grouped' type (its data is another level of avps).
+ Application-specific formats must decide the way to represent its contents, being recommended to use a natural representation if possible,
+ because xml is read by humans with testing and monitoring purposes.
+ -->
+ </pre>
+
+ @param xmlDocument XML document allocated by the user of the function (anna::xml::DocumentMemory xmlDocument)
+ @param xmlPathFile Complete path file to the xml document which represents the diameter message
+ @see messageXmlDocumentFromXmlString
+
+ @warning Whatever you will do with the xml document, will be only valid inside the scope of such xml document.
+ For example, you could load the document to be decoded over a codec Message by mean #Message::fromXML (using
+ the xml document #getRootNode) during document lifetime. After that, it could be destroyed.
+ */
+ static void messageXmlDocumentFromXmlFile(anna::xml::DocumentFile &xmlDocument, const std::string & xmlPathFile) throw(anna::RuntimeException);
+
+ /**
+ Interpret xml string representation in order to create a memory xml document.
+ DTD validation is used in the same way that #messageXmlDocumentFromXmlFile does.
+
+ @param xmlDocument XML document allocated by the user of the function (anna::xml::DocumentMemory xmlDocument)
+ @param xmlString XML string representation of the diameter message
+ @see messageXmlDocumentFromXmlFile
+
+ @warning Whatever you will do with the xml document, will be only valid inside the scope of such xml document.
+ For example, you could load the document to be decoded over a codec Message by mean #Message::fromXML (using
+ the xml document #getRootNode) during document lifetime. After that, it could be destroyed.
+ */
+ static void messageXmlDocumentFromXmlString(anna::xml::DocumentFile &xmlDocument, const std::string &xmlString) throw(anna::RuntimeException);
};