ANNA Suite
2020b
Multipurpose development suite for Telco applications
|
#include <Message.hpp>
Public Member Functions | |
Message (Engine *engine=NULL) | |
Message (CommandId id, Engine *engine=NULL) | |
void | setEngine (Engine *engine) |
virtual | ~Message () |
void | setId (CommandId id) noexcept(false) |
void | setId (const char *name) noexcept(false) |
void | setVersion (U8 version) |
void | setProxiableBit (bool activate=true) |
void | setErrorBit (bool activate=true) |
void | setPotentiallyReTransmittedMessageBit (bool activate=true) |
void | setApplicationId (U32 aid) noexcept(false) |
void | setHopByHop (U32 hbh) |
void | setEndToEnd (U32 ete) |
void | setHeaderToAnswer (const Message &request) noexcept(false) |
void | setStandardToAnswer (const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode=helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) noexcept(false) |
void | setResultCode (int rc=helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) noexcept(false) |
int | getResultCode () const |
Avp * | addAvp (AvpId id) noexcept(false) |
Avp * | addAvp (const char *name) noexcept(false) |
Avp * | addAvp (Avp *avp) |
bool | removeAvp (AvpId id, int ocurrence=1) noexcept(false) |
bool | removeAvp (const char *name, int ocurrence=1) noexcept(false) |
virtual void | clear (bool resetEngine=true) noexcept(false) |
void | decode (const anna::DataBlock &db, Message *ptrAnswer=NULL) noexcept(false) |
void | fix () |
bool | valid (Message *ptrAnswer=NULL) const noexcept(false) |
void | fromXML (const anna::xml::Node *messageNode) noexcept(false) |
void | loadXMLFile (const std::string &xmlPathFile) noexcept(false) |
void | loadXMLString (const std::string &xmlString) noexcept(false) |
const CommandId & | getId () const |
U8 | getVersion () const |
bool | isRequest () const |
bool | isAnswer () const |
const U32 & | getApplicationId () const |
const U32 & | getHopByHop () const |
const U32 & | getEndToEnd () const |
const anna::diameter::stack::Command * | getStackCommand () const noexcept(false) |
bool | requestBit () const |
bool | proxiableBit () const |
bool | errorBit () const |
bool | potentiallyReTransmittedMessageBit () const |
const Avp * | getAvp (AvpId id, int ocurrence=1, anna::Exception::Mode::_v emode=anna::Exception::Mode::Throw) const noexcept(false) |
Avp * | getAvp (AvpId id, int ocurrence=1, anna::Exception::Mode::_v emode=anna::Exception::Mode::Throw) noexcept(false) |
const Avp * | getAvp (const char *name, int ocurrence=1, anna::Exception::Mode::_v emode=anna::Exception::Mode::Throw) const noexcept(false) |
Avp * | getAvp (const char *name, int ocurrence=1, anna::Exception::Mode::_v emode=anna::Exception::Mode::Throw) noexcept(false) |
int | countAvp (AvpId id) const |
int | countAvp (const char *name) const noexcept(false) |
int | countChilds () const |
const anna::DataBlock & | code () noexcept(false) |
anna::xml::Node * | asXML (anna::xml::Node *parent) const |
std::string | asXMLString (bool normalize=false) const |
bool | isLike (const std::string &pattern) const |
Static Public Attributes | |
static const int | HeaderLength |
static const U8 | RBitMask |
static const U8 | PBitMask |
static const U8 | EBitMask |
static const U8 | TBitMask |
Protected Member Functions | |
virtual Engine * | getEngine () const noexcept(false) |
virtual void | initialize () |
Protected Attributes | |
Engine * | a_engine |
Friends | |
class | Avp |
bool | operator== (const Message &m1, const Message &m2) |
Diameter message generic container
RFC 3588 Diameter Based Protocol September 2003 3. Diameter Header
A summary of the Diameter header format is shown below. The fields are transmitted in network byte order.
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Version | Message Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | command flags | Command-Code | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Application-ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Hop-by-Hop Identifier | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | End-to-End Identifier | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | AVPs ... +-+-+-+-+-+-+-+-+-+-+-+-+-
anna::diameter::codec::Message::Message | ( | Engine * | engine = NULL | ) |
Default constructor
engine | Codec engine used |
Identified constructor
id | Command identifier as pair (code,request-indicator). |
engine | Codec engine used |
|
virtual |
Destructor
Adds an avp child providing its identifier and reserve internal memory it.
id | Avp identifier as pair (code,vendor-id). |
|
noexcept |
Adds an avp child providing a persistent pointer (must be maintained by application). It is not allowed to add an avp with no codec engine configured, neither if the engine is not the same.
avp | Avp external pointer. If NULL provided, nothing is done and NULL returned. Also NULL returned for bad engine configuration. |
anna::xml::Node* anna::diameter::codec::Message::asXML | ( | anna::xml::Node * | parent | ) | const |
Class xml representation
parent | Parent XML node on which hold this instance information. |
std::string anna::diameter::codec::Message::asXMLString | ( | bool | normalize = false | ) | const |
Class xml string representation
normalize | Optional normalization which sorts attribute names and removes newlines in the xml representation in order to ease regexp matching. |
|
virtualnoexcept |
Clears and initializes Message class information. Application must clear auxiliary message objects before adding Avps in a new context if the same object is reused. Application don't need to clear a message object before decode operation (decode invokes clear before any other task). Any reimplementation must first invoke base class method.
resetEngine | Sets to NULL the codec engine (true, default) or respect its current value (false). If you are going to reuse the message instance it is better to clear all the information (default) to manage different stacks, because if you don't initialize the engine to NULL, the second use of the message will keep the same engine deduced from the first decoding/loading operation, which could be wrong if the second message belongs to a different application identifier. |
|
noexcept |
Encodes datablock with the class content. In case that validation is enabled (codec::Engine::ValidationMode) an exception will be launched in a moment which depends on validation depth (codec::Engine::ValidationDepth). If you want to see validation errors but go on with encoding, you should try/catch valid() procedure out of code.
|
inline |
Counts the number of ocurrences of Avps (first level) with the identifier provided
id | Avp identifier (pair code + vendor-id). |
|
noexcept |
|
inline |
Counts the number of children
id | Avp identifier (pair code + vendor-id). |
|
noexcept |
Decodes buffer provided over class content. If an error ocurred, decoding will stop launching exception (fatal error) or a warning trace (perhaps the achieved message is valid against all odds then validation will go on). In case that validation is enabled (codec::Engine::ValidationMode) an exception will be launched in a moment which depends on validation depth (codec::Engine::ValidationDepth).
You could decode multiple times over the same object. A basic cleanup is done respecting the codec engine.
db | buffer data block processed. Before decoding, the whole message instance will be cleared (no need to invoke clear before decode). |
ptrAnswer | Answer set by application (could be empty or not), who is responsible for its memory reservation, and automatically built regarding standard. If message analyzed realizes to be an answer, internal reference becomes NULL because no answer is built for answers. By default, automatic answer is not built. |
|
inline |
Returns E bit activation state
void anna::diameter::codec::Message::fix | ( | ) |
Fix childrens content regarding dictionary avp positions. Message could remain invalid because of possible fixed/mandatory avps. This is useful to give flexibility to the application during message construction before encoding or representing the data. Is not recommended to fix a recently decoded message because possible validation problems will be hidden.
|
noexcept |
Interpret xml data in order to dump over the class content. You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
messageNode | Message root node obtained from ::xmlFileTo |
|
inline |
|
inlinenoexcept |
Access content for internal Avps. Exception mode allows different combinations like cascade access:
try { message->getAvp(anna::diameter::helpers::base::AVP__Multiple_Services_Credit_Control, anna::Exception::Mode::Throw) ->getAvp(anna::diameter::helpers::base::AVP__Rating_Group, anna::Exception::Mode::Throw); } catch(anna::RuntimeException) {;}
Or step access:
const Avp *mscc = message->getAvp(anna::diameter::helpers::base::AVP__Multiple_Services_Credit_Control); const Avp *rg; if (mscc) rg = mscc->getAvp(anna::diameter::helpers::base::AVP__Rating_Group);
Replacing procedures becomes easy because an Avp can be searched and its pointer reconfigured by mean setId and data part setters. Deleting procedures must use removeAvp. Access is internally cached to speed up the search operations. This cache is reset after calling fix or removeAvp methods.
id | Avp identifier (pair code + vendor-id). |
ocurrence | Order of appearance for the searched avp. Zero position is rejected, but negative values could be used to reverse access positions: i.e. -1 is the last ocurrence, -2 is the second to last (penultimate), etc. |
emode | Excepcion mode handling: Ignore (no action is taken), Throw (excepcion when missing avp), Trace (trace situation as warning). |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
|
inline |
|
protectedvirtualnoexcept |
Codec Engine getter: avoids have to create base engine when using its child
|
inline |
|
inline |
Gets Message identifier as pair (code, request indicator).
int anna::diameter::codec::Message::getResultCode | ( | ) | const |
Gets the Result-Code AVP value from an answer message (for requests, returns -1). If missing, -1 value is returned.
|
inlinenoexcept |
Gets stack command (dictionary command reference).
|
inline |
Gets the command version. By default, messages initializes with value 1.
|
protectedvirtual |
Initializes Message class information. Any reimplementation must first invoke base class method.
Reimplemented in anna::diameter::codec::tme::Message.
|
inline |
Gets Message answer indicator.
bool anna::diameter::codec::Message::isLike | ( | const std::string & | pattern | ) | const |
Matchs a regular expression (string pattern) regarding xml string serialization for this message. The message xml representation is internally normalized (attribute names are sort and newlines are removed) in order to ease regexp matching.
You could use simple regular expressions. For example, the pattern '<avp data="(.)*32251@3gpp.org" name="Service-Context-Id">' detects PS charging contexts because of data suffix specification '32251' for that AVP. The pattern '<message(.)* name="Capabilities-Exchange-Request"' detects a CER message. And so on. @3gp p.org
It would seems strange or 'creative' to use regular expressions within an hex string representation, but anyway you could also do such kind of things to check non-printable data parts within the message: for example, the pattern '<avp hex-data="0a[A-Fa-f0-9]{2}0a0a" name="Framed-IP-Address">' matchs IP addresses for '10.x.10.10' where x = [0..255].
Normally only printable 'data' fields are used for matching issues.
Now imagine 'message.xml' containing this avp:
... <avp name="Subscription-Id"> <avp alias="END_USER_E164" data="0" name="Subscription-Id-Type"> <avp data="616[0-9]{6}" name="Subscription-Id-Data"> </avp>' ...
You could also extract AVP xml normalized representation in this way:
anna::diameter::codec::Message myMessage; myMessage.loadXMLFile("message.xml"); std::string subscriptionId = myMessage.getAvp("Subscription-Id")->getAvp("Subscription-Id-Type")->asXMLString(true); // Former is '<avp data="616[0-9]{6}" name="Subscription-Id-Data">'
And then use to match incoming messages:
bool match = incomingMessage.isLike(subscriptionId);
Using a complex pattern (many avps, grouped ones) is possible, indeed testing ADML engine supports 'waitfe/fc-xml' operations which load entire diameter messages to be used as a whole regular expression (hop-by-hop, end-to-end and Origin-State-Id avp is automatically replaced by '[0-9]+' to make possible the comparison).
Those operations makes all the work, but if you use the API, you may take into account:
|
inline |
Gets Message request indicator.
|
noexcept |
Interpret a xml file in order to create a diameter message You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
xmlPathFile | Complete path file to the xml document which represents the diameter message |
|
noexcept |
Interpret a xml string in order to create a diameter message You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
xmlString | xml representation of the diameter message |
|
inline |
Returns T bit activation state
|
inline |
Returns P bit activation state
|
inlinenoexcept |
Removes an Avp within message (first level) and free resources.
id | Avp identifier (pair code + vendor-id). |
ocurrence | Order of appearance for the searched avp. Zero value means remove all avps with provided identifier at first level (no recursiveness would be allowed in the API in order to avoid unexpected behaviour). Negative values could be used to reverse access positions: i.e. -1 is the last ocurrence, -2 is the second to last (penultimate), etc. |
|
noexcept |
|
inline |
Returns R bit activation state
|
noexcept |
Sets the message application id.
The codec engine could be configured to force a stack selection based in this field value: see #selectStackWithApplicationId. In multistack applications (in case of being monothread), you only have to take care about how to apply this method: the thing is that you must not interleave message builds which belongs to different stacks. For example, you could think about setting the message header for message A using stack A. Then, start to add the message header fields for a second message B using another stack B. Following you would add the message A avps, but then, the stack is not going to be automatically changed (this is only done through this method). The result could be unexpected when adding/encoding messages with a dictionary which does not correspond.
aid | Application-id. |
|
inline |
void anna::diameter::codec::Message::setEngine | ( | Engine * | engine | ) |
Sets the codec engine.
Once assigned (here or at constructor), this method SHALL NOT be used anymore. Also, the associated dictionary SHOULD NOT BE CHANGED through the engine, unless you know what are you doing. Setting a new different engine with different stack, even same engine where the stack has been dynamically changed, could cause a bad behaviour depending on the changes: in general, if the dictionary grows, nothing bad will happen, but if you remove or modified some elements which were processed with a certain format, will be interpreted as 'unknown' with the new dictionary, and then some problems may occur. If you add elements (vendors, avps, messages) is not a problem.
IMPORTANT NOTES: 1) if you want to reuse the message, as a recommendation, you should clear the message. In that way, next operation will adjust automatically the needed engine. 2) if you have dedicated message objects for each interface (application id), then you could set the corresponding engine on constructor (or setEngine), and forget about clear. The needed cleanup will be done automatically from decoding and xml loading procedures, and initialized engine will be kept along message operations.
|
inline |
Sets/unsets E bit activation. Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored. This flag MUST NOT be set in request messages (in this case, it will be ignored).
activate | Activates/deactivates the bit. True by default. |
|
inlinenoexcept |
Sets header to be an answer regarding provided request message code. Internally, updates command identifier (indeed request flag), promotes version, application identifier, hop-by-hop and end-to-end fields.
request | Message to be answered. |
|
inline |
|
noexcept |
Sets the command identifier and clear the former content.
id | Command identifier as pair (code,request-indicator). |
|
noexcept |
|
inline |
Sets/unsets T bit activation. Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored. This flag MUST NOT be set in answer messages (in this case, it will be ignored).
activate | Activates/deactivates the bit. True by default. |
|
inline |
Sets/unsets P bit activation. Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored.
activate | Activates/deactivates the bit. True by default. |
|
noexcept |
Sets a Result-Code AVP over an answer message (for requests, do nothing). If Result-Code AVP doesn't exists, is added and then filled with the value provided. If Result-Code AVP already exists, value detected is replaced if was DIAMETER_SUCCESS (non success codes are unchanged). When provided value corresponds to an protocol error, that is to say within range [3001,3010], message (E)rror bit is automatically activated.
This method is internally used during decode and/or valid procedures in order to build automatic answers, but application could call this for set another Result-Code no detected by these methods within its category or for other one (application layer). These are the Result-Codes implemented (detected) by ANNA::diameter::codec:
Protocol Errors:
DIAMETER_COMMAND_UNSUPPORTED DIAMETER_INVALID_HDR_BITS DIAMETER_INVALID_AVP_BITS
Permanent Failures:
DIAMETER_AVP_UNSUPPORTED (F) DIAMETER_INVALID_AVP_VALUE (F) DIAMETER_MISSING_AVP (F) DIAMETER_AVP_NOT_ALLOWED (F) DIAMETER_AVP_OCCURS_TOO_MANY_TIMES (F) DIAMETER_INVALID_BIT_IN_HEADER DIAMETER_INVALID_AVP_LENGTH (F) DIAMETER_INVALID_MESSAGE_LENGTH
(F) Generates Failed-AVP (also DIAMETER_CONTRADICTING_AVPS and DIAMETER_INVALID_AVP_BIT_COMBO values does, but these are not managed by anna::diameter::codec).
rc | Result-Code value. DIAMETER_SUCCESS by default. |
|
noexcept |
Standard minimum-answer building from requests. Adds Session-Id (mirrored from request if present), Origin-Host and Origin-Realm (which could be configured, extracted from optional Destination AVPs, etc.), and all the Proxy-Info AVPs (added in same order as appear on the request). Of course, answer message header is built from request information through setHeaderToAnswer. Finally, message is fixed regarding dictionary elements order (fix).
Summing up, as RFC 6733 Section 6.2, says:
6.2. Diameter Answer Processing
When a request is locally processed, the following procedures MUST be applied to create the associated answer, in addition to any additional procedures that MAY be discussed in the Diameter application defining the command:
o The same Hop-by-Hop Identifier in the request is used in the answer.
o The local host's identity is encoded in the Origin-Host AVP.
o The Destination-Host and Destination-Realm AVPs MUST NOT be present in the answer message.
o The Result-Code AVP is added with its value indicating success or failure.
o If the Session-Id is present in the request, it MUST be included in the answer.
o Any Proxy-Info AVPs in the request MUST be added to the answer message, in the same order they were present in the request.
o The 'P' bit is set to the same value as the one in the request.
o The same End-to-End identifier in the request is used in the answer.
Note that the error messages (see Section 7) are also subjected to the above processing rules.
Regarding errors, is recommended to use this over automatic answer built at decode and/or valid procedures, which would had added Result-Code and/or Failed-AVP AVPs if proceed, but be aware of DIAMETER_COMMAND_UNSUPPORTED Result-Code, because becomes impossible to fix (Session-Id SHOULD appear immediately following the Diameter header, and fix do this manually even if no information about the command structure is known, but perhaps another fixed AVPs could not comply... use getResultCode to find out this situation before using setStandardToAnswer). Anyway, application could add another Failed-AVP content no detected internally, for example: DIAMETER_CONTRADICTING_AVPS or DIAMETER_INVALID_AVP_BIT_COMBO). Also, application could add more Failed-AVP avps with other wrong avps, or accumulate wrong avps inside the one and only Failed-AVP managed by the stack. The standard is open to add multiple avps inside Failed-AVP or multiple Failed-AVP avps with single or multiple avps inside. This depends on application criteria regarding other nodes. However, internally the Anna::diameter stack only provides one Failed-AVP with the first wrong avp found, as RFC 6733 says in section 7.5.
If application decoding and/or validation operations are ok, user may search for other problems and put the appropiate Result-Code. For example, DIAMETER_TOO_BUSY (3004) depends on congestion issues at business layer and cannot be decided with the only message information automatically (not all the Result-Code values are taken into account, only those which correspond to anomalies managed by anna::diameter::codec). Application Result-Codes could be provided in this prototype, being DIAMETER_SUCCESS the default value if missing.
request | Message to be answered. |
originHost | Mandatory Origin-Host diameter identity value provided by application. If answer has already an Origin-Host, this will be ignored. |
originRealm | Mandatory Origin-Realm diameter identity value provided by application. If answer has already an Origin-Realm, this will be ignored. |
resultCode | Result-Code value assigned by application. If non-success value is already assigned, this will be ignored. DIAMETER_SUCCESS is provided by default. |
|
inline |
|
noexcept |
Validates the message regarding dictionary rules like enumerated range, flags coherence, mandatory and fixed types, cardinality qualifiers, etc.
ptrAnswer | Answer set by application (could be empty or not), who is responsible for its memory reservation, and automatically built regarding standard. If message analyzed realizes to be an answer, internal reference becomes NULL because no answer is built for answers. By default, automatic answer is not built. |
|
friend |
|
static |
|
static |
|
static |
|
static |
|
static |