1 // ANNA - Anna is Not Nothingness Anymore //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
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 //
9 #ifndef anna_diameter_codec_Message_hpp
10 #define anna_diameter_codec_Message_hpp
14 #include <anna/config/defines.hpp>
15 #include <anna/diameter/defines.hpp>
16 #include <anna/diameter/codec/Avp.hpp>
17 #include <anna/diameter/helpers/base/defines.hpp>
19 #include <anna/core/DataBlock.hpp>
20 #include <anna/core/RuntimeException.hpp>
25 //------------------------------------------------------------------------------
26 //---------------------------------------------------------------------- #define
27 //------------------------------------------------------------------------------
49 * Diameter message generic container
51 * RFC 3588 Diameter Based Protocol September 2003
54 * A summary of the Diameter header format is shown below. The fields
55 * are transmitted in network byte order.
58 * 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
59 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60 * | Version | Message Length |
61 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
62 * | command flags | Command-Code |
63 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
66 * | Hop-by-Hop Identifier |
67 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68 * | End-to-End Identifier |
69 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
71 * +-+-+-+-+-+-+-+-+-+-+-+-+-
77 CommandId a_id; // code and request indicator
82 avp_container a_avps; // childrens
83 find_container a_finds; // fast access for message first-level avps
86 int a_insertionPositionForChilds; // used with childrens
87 anna::DataBlock a_forCode;
89 const Avp* _getAvp(const char *name, int ocurrence, anna::Exception::Mode::_v emode) const throw(anna::RuntimeException);
91 // --- Developer notes ---
92 // 'AVP Length' does not include posible data padding. Thanks to this, 'Data Length'
93 // is the difference between 'AVP Length' and sum of code, length, flags and
94 // optionally the vendor-ID (all of them are 32-bit boundary), that is to say:
95 // 8 or 12 (vendor-specific avps).
97 // Grouped avps 'AVP Length' includes own headers plus the total length of all
98 // underlying AVPs, including their headers and padding, then 'AVP Length' is
99 // always multiple of 4 (library will check this), and smae for 'Data Length'
100 // which is an 'whole avp Length with padding' itself.
106 avp_iterator avp_begin() throw() { return a_avps.begin(); }
107 avp_iterator avp_end() throw() { return a_avps.end(); }
108 const_avp_iterator avp_begin() const throw() { return a_avps.begin(); }
109 const_avp_iterator avp_end() const throw() { return a_avps.end(); }
112 * Gets avp total message length.
114 U24 getLength() const throw();
118 bool flagsOK(int &rc) const throw(); // flags coherence regarding dictionary. Only must be called when Message is identified at the dictionary.
119 int addChild(Avp *avp) throw() { return Avp::addChild(a_avps, a_insertionPositionForChilds, avp); }
120 const anna::diameter::stack::Command *getStackCommand(CommandId id) const throw(anna::RuntimeException);
122 void setFailedAvp(const parent_t &parent, AvpId wrong, const char *wrongName = NULL) throw(anna::RuntimeException);
123 // During message decoding and validation, the first wrong avp is stored and all the tracking is managed to find out its
124 // nested path for the case of grouped avps with wrong avps inside. Remember the RFC 6733, section 7.5:
126 // In the case where the offending AVP is embedded within a Grouped AVP,
127 // the Failed-AVP MAY contain the grouped AVP, which in turn contains
128 // the single offending AVP. The same method MAY be employed if the
129 // grouped AVP itself is embedded in yet another grouped AVP and so on.
130 // In this case, the Failed-AVP MAY contain the grouped AVP hierarchy up
131 // to the single offending AVP. This enables the recipient to detect
132 // the location of the offending AVP when embedded in a group.
134 // The first wrong avp found will set the final result code, as the RFC recommends:
136 // The value of the Result-Code AVP will provide information on the reason
137 // for the Failed-AVP AVP. A Diameter answer message SHOULD contain an
138 // instance of the Failed-AVP AVP that corresponds to the error
139 // indicated by the Result-Code AVP. For practical purposes, this
140 // Failed-AVP would typically refer to the first AVP processing error
141 // that a Diameter node encounters.
143 // The message keeps the list (reverse order) of avps hierarchy (in case of grouping) for the final Failed-AVP construction,
144 // which is done at the end of decoding or validation, and only the first wrong avp is stored with its corresponding path.
150 mutable Engine *a_engine;
152 /** Codec Engine getter: avoids have to create base engine when using its child */
153 virtual Engine * getEngine() const throw(anna::RuntimeException);
156 * Initializes Message class information.
157 * Any reimplementation must first invoke base class method.
159 virtual void initialize() throw();
165 * Default constructor
166 * @param engine Codec engine used
168 Message(Engine *engine = NULL);
171 * Identified constructor
172 * @param id Command identifier as pair (code,request-indicator).
173 * @param engine Codec engine used
175 Message(CommandId id, Engine *engine = NULL);
179 * Sets the codec engine.
181 * Once assigned (here or at constructor), this method SHALL NOT be used anymore.
182 * Also, the associated dictionary SHOULD NOT BE CHANGED through the engine,
183 * unless you know what are you doing.
184 * Setting a new different engine with different stack, even same engine where the
185 * stack has been dynamically changed, could cause a bad behaviour depending on the
186 * changes: in general, if the dictionary grows, nothing bad will happen, but if
187 * you remove or modified some elements which were processed with a certain format,
188 * will be interpreted as 'unknown' with the new dictionary, and then some problems
189 * may occur. If you add elements (vendors, avps, messages) is not a problem.
192 * 1) if you want to reuse the message, as a recommendation, you should #clear the
193 * message. In that way, next operation will adjust automatically the needed engine.
194 * 2) if you have dedicated message objects for each interface (application id), then
195 * you could set the corresponding engine on constructor (or setEngine), and forget
196 * about #clear. The needed cleanup will be done automatically from decoding and xml
197 * loading procedures, and initialized engine will be kept along message operations.
199 void setEngine(Engine *engine) throw();
203 static const int HeaderLength;
214 // T(Potentially re-transmitted message)
215 // r(eserved) - these flag bits are reserved for future use, and
216 // MUST be set to zero, and ignored by the receiver.
217 static const U8 RBitMask;
218 static const U8 PBitMask;
219 static const U8 EBitMask;
220 static const U8 TBitMask;
228 // Virtual destructors are useful when you can delete an instance of a derived class through a pointer to base class:
229 // This destructor is not virtual, then a pointer to base class (even pointing to a children one) will invoke this destructor, not the derived one.
230 // My current solution: virtualizing method 'clear'
233 // To sum up, always make base classes' destructors virtual when they're meant to be manipulated polymorphically.
238 Sets the command identifier and clear the former content.
240 @param id Command identifier as pair (code,request-indicator).
242 void setId(CommandId id) throw(anna::RuntimeException);
245 Same as #setId but providing dictionary logical name for Avp searched
247 void setId(const char *name) throw(anna::RuntimeException);
250 Sets the command version. By default, messages initializes with value 1.
252 @param version Version provided
254 void setVersion(U8 version) throw() { a_version = version; }
257 Sets/unsets P bit activation.
258 Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored.
260 @param activate Activates/deactivates the bit. True by default.
262 void setProxiableBit(bool activate = true) throw() { if(activate) a_flags |= PBitMask; else a_flags &= (~PBitMask); }
265 Sets/unsets E bit activation.
266 Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored.
267 This flag MUST NOT be set in request messages (in this case, it will be ignored).
269 @param activate Activates/deactivates the bit. True by default.
271 void setErrorBit(bool activate = true) throw() { if(isRequest()) return; if(activate) a_flags |= EBitMask; else a_flags &= (~EBitMask); }
274 Sets/unsets T bit activation.
275 Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored.
276 This flag MUST NOT be set in answer messages (in this case, it will be ignored).
278 @param activate Activates/deactivates the bit. True by default.
280 void setPotentiallyReTransmittedMessageBit(bool activate = true) throw() { if(isAnswer()) return; if(activate) a_flags |= TBitMask; else a_flags &= (~TBitMask); }
283 Sets the message application id.
285 The codec engine could be configured to force a stack selection based in this field value: see #selectStackWithApplicationId.
286 In multistack applications (in case of being monothread), you only have to take care about how to apply this method: the thing
287 is that you must not interleave message builds which belongs to different stacks. For example, you could think about setting the
288 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.
289 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
290 method). The result could be unexpected when adding/encoding messages with a dictionary which does not correspond.
292 @warning do not interleave build/encode operations between different messages which uses different stacks over the same codec engine.
293 It seems common sense, but it is not bad to advice about this.
295 @param aid Application-id.
297 void setApplicationId(U32 aid) throw(anna::RuntimeException);
300 Sets the message hop-by-hop
301 @param hbh Hop-by-hop identifier.
303 void setHopByHop(U32 hbh) throw() { a_hopByHop = hbh; }
306 Sets the message end-to-end
307 @param ete End-to-end identifier.
309 void setEndToEnd(U32 ete) throw() { a_endToEnd = ete; }
313 Sets header to be an answer regarding provided request message code.
314 Internally, updates command identifier (indeed request flag), promotes version, application identifier, hop-by-hop and end-to-end fields.
316 @param request Message to be answered.
318 @warning Request provided must be a request, in other case method do nothing.
320 void setHeaderToAnswer(const Message &request) throw(anna::RuntimeException) {
321 if(!request.getId().second) return;
323 a_engine = request.getEngine(); // we know this will be
325 setId(CommandId(request.getId().first, !request.getId().second));
326 setVersion(request.getVersion());
327 setApplicationId(request.getApplicationId());
328 setHopByHop(request.getHopByHop()); // The same Hop-by-Hop Identifier in the request is used in the answer (RFC 6733 Section 6.2).
329 setEndToEnd(request.getEndToEnd()); // The same End-to-End Identifier in the request is used in the answer (RFC 6733 Section 6.2).
330 setProxiableBit(request.proxiableBit()); // The 'P' bit is set to the same value as the one in the request (RFC 6733 Section 6.2).
335 Standard minimum-answer building from requests. Adds Session-Id (mirrored from request if present), Origin-Host and Origin-Realm
336 (which could be configured, extracted from optional Destination AVPs, etc.), and all the Proxy-Info AVPs (added in same order as
337 appear on the request). Of course, answer message header is built from request information through #setHeaderToAnswer. Finally,
338 message is fixed regarding dictionary elements order (#fix).
340 Summing up, as RFC 6733 Section 6.2, says:
344 6.2. Diameter Answer Processing
346 When a request is locally processed, the following procedures MUST be
347 applied to create the associated answer, in addition to any
348 additional procedures that MAY be discussed in the Diameter
349 application defining the command:
351 o The same Hop-by-Hop Identifier in the request is used in the
354 o The local host's identity is encoded in the Origin-Host AVP.
356 o The Destination-Host and Destination-Realm AVPs MUST NOT be
357 present in the answer message.
359 o The Result-Code AVP is added with its value indicating success or
362 o If the Session-Id is present in the request, it MUST be included
365 o Any Proxy-Info AVPs in the request MUST be added to the answer
366 message, in the same order they were present in the request.
368 o The 'P' bit is set to the same value as the one in the request.
370 o The same End-to-End identifier in the request is used in the
373 Note that the error messages (see Section 7) are also subjected to
374 the above processing rules.
376 Regarding errors, is recommended to use this over automatic answer built at #decode and/or #valid procedures, which would had added
377 Result-Code and/or Failed-AVP AVPs if proceed, but be aware of DIAMETER_COMMAND_UNSUPPORTED Result-Code, because becomes impossible
378 to fix (Session-Id SHOULD appear immediately following the Diameter header, and #fix do this manually even if no information about
379 the command structure is known, but perhaps another fixed AVPs could not comply... use #getResultCode to find out this situation before
380 using #setStandardToAnswer). Anyway, application could add another Failed-AVP content no detected internally, for example:
381 DIAMETER_CONTRADICTING_AVPS or DIAMETER_INVALID_AVP_BIT_COMBO). Also, application could add more Failed-AVP avps with other
382 wrong avps, or accumulate wrong avps inside the one and only Failed-AVP managed by the stack. The standard is open to add multiple
383 avps inside Failed-AVP or multiple Failed-AVP avps with single or multiple avps inside. This depends on application criteria regarding
384 other nodes. However, internally the Anna::diameter stack only provides one Failed-AVP with the first wrong avp found, as RFC 6733 says
387 If application decoding and/or validation operations are ok, user may search for other problems and put the appropiate Result-Code.
388 For example, DIAMETER_TOO_BUSY (3004) depends on congestion issues at business layer and cannot be decided with the only message
389 information automatically (not all the Result-Code values are taken into account, only those which correspond to anomalies managed
390 by anna::diameter::codec). Application Result-Codes could be provided in this prototype, being DIAMETER_SUCCESS the default value if missing.
393 @param request Message to be answered.
394 @param originHost Mandatory Origin-Host diameter identity value provided by application. If answer has already an Origin-Host, this will be ignored.
395 @param originRealm Mandatory Origin-Realm diameter identity value provided by application. If answer has already an Origin-Realm, this will be ignored.
396 @param resultCode Result-Code value assigned by application. If non-success value is already assigned, this will be ignored. DIAMETER_SUCCESS is provided by default.
398 @warning Request provided must be a request, in other case method do nothing.
400 void setStandardToAnswer(const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode = helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) throw(anna::RuntimeException);
404 Sets a Result-Code AVP over an answer message (for requests, do nothing).
405 If Result-Code AVP doesn't exists, is added and then filled with the value provided.
406 If Result-Code AVP already exists, value detected is replaced if was DIAMETER_SUCCESS (non success codes are unchanged).
407 When provided value corresponds to an protocol error, that is to say within range [3001,3010], message (E)rror bit is
408 automatically activated.
410 This method is internally used during #decode and/or #valid procedures in order to build automatic answers, but application
411 could call this for set another Result-Code no detected by these methods within its category or for other one (application
412 layer). These are the Result-Codes implemented (detected) by ANNA::diameter::codec:
417 DIAMETER_COMMAND_UNSUPPORTED
418 DIAMETER_INVALID_HDR_BITS
419 DIAMETER_INVALID_AVP_BITS
423 DIAMETER_AVP_UNSUPPORTED (F)
424 DIAMETER_INVALID_AVP_VALUE (F)
425 DIAMETER_MISSING_AVP (F)
426 DIAMETER_AVP_NOT_ALLOWED (F)
427 DIAMETER_AVP_OCCURS_TOO_MANY_TIMES (F)
428 DIAMETER_INVALID_BIT_IN_HEADER
429 DIAMETER_INVALID_AVP_LENGTH (F)
430 DIAMETER_INVALID_MESSAGE_LENGTH
432 (F) Generates Failed-AVP (also DIAMETER_CONTRADICTING_AVPS and DIAMETER_INVALID_AVP_BIT_COMBO
433 values does, but these are not managed by anna::diameter::codec).
436 @param rc Result-Code value. DIAMETER_SUCCESS by default.
438 void setResultCode(int rc = helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) throw(anna::RuntimeException);
442 Gets the Result-Code AVP value from an answer message (for requests, returns -1).
443 If missing, -1 value is returned.
445 @return Result-Code value for answers, -1 for request and answers without Result-Code AVP inside
447 int getResultCode() const throw();
451 Adds an avp child providing its identifier and reserve internal memory it.
453 @param id Avp identifier as pair (code,vendor-id).
455 @return Pointer to the new created avp.
457 Avp * addAvp(AvpId id) throw(anna::RuntimeException) { return Avp::addAvp(a_avps, a_insertionPositionForChilds, id, getEngine()); }
461 Same as #addAvp but providing dictionary logical name for Avp searched
463 Avp * addAvp(const char *name) throw(anna::RuntimeException);
467 Adds an avp child providing a persistent pointer (must be maintained by application).
468 It is not allowed to add an avp with no codec engine configured, neither if the engine
471 @param avp Avp external pointer. If NULL provided, nothing is done and NULL returned.
472 Also NULL returned for bad engine configuration.
474 @return Pointer to the added avp (again).
476 Avp * addAvp(Avp * avp) throw();
480 Removes an Avp within message (first level) and free resources.
482 @param id Avp identifier (pair code + vendor-id).
483 @param 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).
484 Negative values could be used to reverse access positions: i.e. -1 is the last ocurrence, -2 is the second to last (penultimate), etc.
486 @return Returns true if something was removed. False in other cases (including i.e. when this message is empty).
488 bool removeAvp(AvpId id, int ocurrence = 1) throw(anna::RuntimeException) { return Avp::removeAvp(a_avps, (find_container&)a_finds, id, ocurrence, getEngine()); }
492 Same as #removeAvp but providing dictionary logical name for Avp searched
494 bool removeAvp(const char *name, int ocurrence = 1) throw(anna::RuntimeException);
498 * Clears and initializes Message class information.
499 * Application must clear auxiliary message objects before adding Avps in a new context if the same object is reused.
500 * Application don't need to clear a message object before decode operation (decode invokes #clear before any other task).
501 * Any reimplementation must first invoke base class method.
503 * @param resetEngine Sets to NULL the codec engine (true, default) or respect its current value (false). If you are going
504 * to reuse the message instance it is better to clear all the information (default) to manage different stacks, because if
505 * you don't initialize the engine to NULL, the second use of the message will keep the same engine deduced from the first
506 * decoding/loading operation, which could be wrong if the second message belongs to a different application identifier.
508 virtual void clear(bool resetEngine = true) throw(anna::RuntimeException);
511 Decodes buffer provided over class content. If an error ocurred, decoding will stop launching exception (fatal error) or a warning trace (perhaps the achieved
512 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
513 in a moment which depends on validation depth (codec::Engine::ValidationDepth).
515 You could decode multiple times over the same object. A basic cleanup is done respecting the codec engine.
517 @param db buffer data block processed. Before decoding, the whole message instance will be cleared (no need to invoke #clear before #decode).
518 @param ptrAnswer Answer set by application (could be empty or not), who is responsible for its memory reservation,
519 and automatically built regarding standard. If message analyzed realizes to be an answer, internal reference becomes
520 NULL because no answer is built for answers. By default, automatic answer is not built.
522 void decode(const anna::DataBlock &db, Message *ptrAnswer = NULL) throw(anna::RuntimeException);
525 Fix childrens content regarding dictionary avp positions.
526 Message could remain invalid because of possible fixed/mandatory avps.
527 This is useful to give flexibility to the application during message construction before encoding or representing the data.
528 Is not recommended to fix a recently decoded message because possible validation problems will be hidden.
533 Validates the message regarding dictionary rules like enumerated range, flags coherence, mandatory and fixed types, cardinality qualifiers, etc.
534 @return Boolean indicating validation result
535 @param ptrAnswer Answer set by application (could be empty or not), who is responsible for its memory reservation,
536 and automatically built regarding standard. If message analyzed realizes to be an answer, internal reference becomes
537 NULL because no answer is built for answers. By default, automatic answer is not built.
539 bool valid(Message *ptrAnswer = NULL) const throw(anna::RuntimeException);
542 Interpret xml data in order to dump over the class content.
543 You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
544 \param messageNode Message root node obtained from @functions::xmlFileTo
546 void fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeException);
549 * Interpret a xml file in order to create a diameter message
550 * You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
552 * @see functions::messageXmlDocumentFromXmlFile
555 * @param xmlPathFile Complete path file to the xml document which represents the diameter message
557 void loadXMLFile(const std::string &xmlPathFile) throw(anna::RuntimeException);
560 * Interpret a xml string in order to create a diameter message
561 * You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
563 * @see functions::messageXmlDocumentFromXmlString
566 * @param xmlString xml representation of the diameter message
568 void loadXMLString(const std::string &xmlString) throw(anna::RuntimeException);
574 Gets Message identifier as pair (code, request indicator).
576 const CommandId & getId() const throw() { return a_id; }
579 Gets the command version. By default, messages initializes with value 1.
581 @return version Message version
583 U8 getVersion() const throw() { return a_version; }
586 Gets Message request indicator.
588 bool isRequest() const throw() { return a_id.second; }
591 Gets Message answer indicator.
593 bool isAnswer() const throw() { return !isRequest(); }
596 Gets the message application id
597 @return aid Application-id.
599 const U32 & getApplicationId() const throw() { return a_applicationId; }
602 Gets the message hop-by-hop
603 @return hbh Hop-by-hop identifier.
605 const U32 & getHopByHop() const throw() { return a_hopByHop; }
608 Gets the message end-to-end
609 @return ete End-to-end identifier.
611 const U32 & getEndToEnd() const throw() { return a_endToEnd; }
614 Gets stack command (dictionary command reference).
616 const anna::diameter::stack::Command *getStackCommand() const throw(anna::RuntimeException) { return getStackCommand(a_id); }
618 /** Returns R bit activation state */
619 bool requestBit() const throw() { return ((a_flags & RBitMask) != 0x00); }
621 /** Returns P bit activation state */
622 bool proxiableBit() const throw() { return ((a_flags & PBitMask) != 0x00); }
624 /** Returns E bit activation state */
625 bool errorBit() const throw() { return ((a_flags & EBitMask) != 0x00); }
627 /** Returns T bit activation state */
628 bool potentiallyReTransmittedMessageBit() const throw() { return ((a_flags & TBitMask) != 0x00); }
632 Access content for internal Avps. Exception mode allows different combinations like cascade access:
636 message->getAvp(anna::diameter::helpers::base::AVP__Multiple_Services_Credit_Control, anna::Exception::Mode::Throw)
637 ->getAvp(anna::diameter::helpers::base::AVP__Rating_Group, anna::Exception::Mode::Throw);
639 catch(anna::RuntimeException) {;}
645 const Avp *mscc = message->getAvp(anna::diameter::helpers::base::AVP__Multiple_Services_Credit_Control);
647 if (mscc) rg = mscc->getAvp(anna::diameter::helpers::base::AVP__Rating_Group);
650 Replacing procedures becomes easy because an Avp can be searched and its pointer reconfigured by mean #setId and data part setters.
651 Deleting procedures must use #removeAvp.
652 Access is internally cached to speed up the search operations. This cache is reset after calling #fix or #removeAvp methods.
654 @param id Avp identifier (pair code + vendor-id).
655 @param ocurrence Order of appearance for the searched avp. Zero position is rejected, but negative values could be used to reverse
656 access positions: i.e. -1 is the last ocurrence, -2 is the second to last (penultimate), etc.
657 @param emode Excepcion mode handling: Ignore (no action is taken), Throw (excepcion when missing avp), Trace (trace situation as warning).
659 const Avp* getAvp(AvpId id, int ocurrence = 1, anna::Exception::Mode::_v emode = anna::Exception::Mode::Throw) const throw(anna::RuntimeException) {
660 return Avp::getAvp(a_avps, (find_container&)a_finds, id, ocurrence, getEngine(), emode);
663 Avp* getAvp(AvpId id, int ocurrence = 1, anna::Exception::Mode::_v emode = anna::Exception::Mode::Throw) throw(anna::RuntimeException) {
664 return const_cast<Avp*>(Avp::getAvp(a_avps, (find_container&)a_finds, id, ocurrence, getEngine(), emode));
669 Same as #getAvp but providing dictionary logical name for Avp searched
671 const Avp* getAvp(const char *name, int ocurrence = 1, anna::Exception::Mode::_v emode = anna::Exception::Mode::Throw) const throw(anna::RuntimeException) {
672 return _getAvp(name, ocurrence, emode);
675 Avp* getAvp(const char *name, int ocurrence = 1, anna::Exception::Mode::_v emode = anna::Exception::Mode::Throw) throw(anna::RuntimeException) {
676 return const_cast<Avp*>(_getAvp(name, ocurrence, emode));
682 Counts the number of ocurrences of Avps (first level) with the identifier provided
684 @param id Avp identifier (pair code + vendor-id).
686 int countAvp(AvpId id) const throw() { return Avp::countAvp(a_avps, id); }
689 Same as #countAvp but providing dictionary logical name for Avp searched
691 int countAvp(const char *name) const throw(anna::RuntimeException);
694 Counts the number of children
696 @param id Avp identifier (pair code + vendor-id).
698 int countChilds() const throw() { return Avp::countChilds(a_avps); }
701 Encodes datablock with the class content. In case that validation is enabled (codec::Engine::ValidationMode) an exception will be launched
702 in a moment which depends on validation depth (codec::Engine::ValidationDepth). If you want to see validation errors but go on with encoding,
703 you should try/catch #valid() procedure out of #code.
705 @return DataBlock encoded (internal memory used)
707 const anna::DataBlock & code() throw(anna::RuntimeException);
710 Class xml representation
711 \param parent Parent XML node on which hold this instance information.
712 \return XML document with relevant information for this instance.
714 anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
717 Class xml string representation
718 @param normalize Optional normalization which sorts attribute names and removes
719 newlines in the xml representation in order to ease regexp matching.
721 \return XML string representation with relevant information for this instance.
723 std::string asXMLString(bool normalize = false) const throw();
726 Comparison operator by mean serialization
728 @param m1 Instance 1 for Message class
729 @param m2 Instance 2 for Message class
731 @return Comparison result
733 friend bool operator == (const Message & m1, const Message & m2) throw() { return (m1.asXMLString() == m2.asXMLString()); }
736 Matchs a regular expression (string pattern) regarding xml string serialization for this message.
737 The message xml representation is internally normalized (attribute names are sort and newlines
738 are removed) in order to ease regexp matching.
740 You could use simple regular expressions.
741 For example, the pattern '<avp data="(.)*32251@3gpp.org" name="Service-Context-Id"/>' detects
742 PS charging contexts because of data suffix specification '32251@3gpp.org' for that AVP.
743 The pattern '<message(.)* name="Capabilities-Exchange-Request"' detects a CER message. And so on.
745 It would seems strange or 'creative' to use regular expressions within an hex string representation,
746 but anyway you could also do such kind of things to check non-printable data parts within the message:
747 for example, the pattern '<avp hex-data="0a[A-Fa-f0-9]{2}0a0a" name="Framed-IP-Address"/>'
748 matchs IP addresses for '10.x.10.10' where x = [0..255].
750 Normally only printable 'data' fields are used for matching issues.
752 Now imagine 'message.xml' containing this avp:
756 <avp name="Subscription-Id">
757 <avp alias="END_USER_E164" data="0" name="Subscription-Id-Type"/>
758 <avp data="616[0-9]{6}" name="Subscription-Id-Data"/>
763 You could also extract AVP xml normalized representation in this way:
766 anna::diameter::codec::Message myMessage;
767 myMessage.loadXMLFile("message.xml");
768 std::string subscriptionId = myMessage.getAvp("Subscription-Id")->getAvp("Subscription-Id-Type")->asXMLString(true);
769 // Former is '<avp data="616[0-9]{6}" name="Subscription-Id-Data"/>'
772 And then use to match incoming messages:
775 bool match = incomingMessage.isLike(subscriptionId);
778 Using a complex pattern (many avps, grouped ones) is possible, indeed testing ADML engine supports 'waitfe/fc-xml'
779 operations which load entire diameter messages to be used as a whole regular expression (hop-by-hop, end-to-end and
780 Origin-State-Id avp is automatically replaced by '[0-9]+' to make possible the comparison).
782 Those operations makes all the work, but if you use the API, you may take into account:
784 - Respect indentation for inner Message xml representation (normally 3 spaces).
785 - Sort alphabetically the attribute names in every xml node.
786 - Remove all the newlines in the xml representation as normalization stage.
787 - Ignore flags and set the fix mode for the message.
789 \return Returns the match result
791 bool isLike(const std::string &pattern) const throw();
794 //friend class Engine;