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
170 * Identified constructor
171 * @param id Command identifier as pair (code,request-indicator).
173 Message(CommandId id);
177 static const int HeaderLength;
188 // T(Potentially re-transmitted message)
189 // r(eserved) - these flag bits are reserved for future use, and
190 // MUST be set to zero, and ignored by the receiver.
191 static const U8 RBitMask;
192 static const U8 PBitMask;
193 static const U8 EBitMask;
194 static const U8 TBitMask;
201 // Virtual destructors are useful when you can delete an instance of a derived class through a pointer to base class:
202 // 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.
203 // My current solution: virtualizing method 'clear'
206 // To sum up, always make base classes' destructors virtual when they're meant to be manipulated polymorphically.
211 Sets the command identifier and clear the former content.
213 @param id Command identifier as pair (code,request-indicator).
214 @param _clear Message will be cleared when updating the command identifier (default behaviour).
216 void setId(CommandId id, bool _clear = true) throw(anna::RuntimeException);
219 Same as #setId but providing dictionary logical name for Avp searched
221 void setId(const char *name) throw(anna::RuntimeException);
224 Sets the command version. By default, messages initializes with value 1.
226 @param version Version provided
228 void setVersion(U8 version) throw() { a_version = version; }
231 Sets/unsets P bit activation.
232 Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored.
234 @param activate Activates/deactivates the bit. True by default.
236 void setProxiableBit(bool activate = true) throw() { if(activate) a_flags |= PBitMask; else a_flags &= (~PBitMask); }
239 Sets/unsets E bit activation.
240 Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored.
242 @param activate Activates/deactivates the bit. True by default.
244 void setErrorBit(bool activate = true) throw() { if(activate) a_flags |= EBitMask; else a_flags &= (~EBitMask); }
247 Sets/unsets T bit activation.
248 Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored.
250 @param activate Activates/deactivates the bit. True by default.
252 void setPotentiallyReTransmittedMessageBit(bool activate = true) throw() { if(activate) a_flags |= TBitMask; else a_flags &= (~TBitMask); }
255 Sets the message application id.
257 The codec engine could be configured to force a stack selection based in this field value: see #selectStackWithApplicationId.
258 In multistack applications (which also shall be monothreaded), you only have to take care about how to apply this method: the thing
259 is that you must not interleave message builds which belongs to different stacks. For example, you could think about setting the
260 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.
261 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
262 method). The result could be unexpected when adding/encoding messages with a dictionary which does not correspond.
264 @warning do not interleave build/encode operations between different messages which uses different stacks over the same codec engine.
265 It seems common sense, but it is not bad to advice about this.
267 @param aid Application-id.
269 void setApplicationId(U32 aid) throw();
272 Sets the message hop-by-hop
273 @param hbh Hop-by-hop identifier.
275 void setHopByHop(U32 hbh) throw() { a_hopByHop = hbh; }
278 Sets the message end-to-end
279 @param ete End-to-end identifier.
281 void setEndToEnd(U32 ete) throw() { a_endToEnd = ete; }
285 Sets header to be an answer regarding provided request message code.
286 Internally, updates command identifier (indeed request flag), promotes version, application identifier, hop-by-hop and end-to-end fields.
288 @param request Message to be answered.
290 @warning Request provided must be a request, in other case method do nothing.
292 void setHeaderToAnswer(const Message &request) throw() {
293 if(!request.getId().second) return;
295 setId(CommandId(request.getId().first, !request.getId().second), false /* don't clear */);
296 setVersion(request.getVersion());
297 setApplicationId(request.getApplicationId());
298 setHopByHop(request.getHopByHop()); // The same Hop-by-Hop Identifier in the request is used in the answer (RFC 6733 Section 6.2).
299 setEndToEnd(request.getEndToEnd()); // The same End-to-End Identifier in the request is used in the answer (RFC 6733 Section 6.2).
300 setProxiableBit(request.proxiableBit()); // The 'P' bit is set to the same value as the one in the request (RFC 6733 Section 6.2).
305 Standard minimum-answer building from requests. Adds Session-Id (mirrored from request if present), Origin-Host and Origin-Realm
306 (which could be configured, extracted from optional Destination AVPs, etc.), and all the Proxy-Info AVPs (added in same order as
307 appear on the request). Of course, answer message header is built from request information through #setHeaderToAnswer. Finally,
308 message is fixed regarding dictionary elements order (#fix).
310 Summing up, as RFC 6733 Section 6.2, says:
314 6.2. Diameter Answer Processing
316 When a request is locally processed, the following procedures MUST be
317 applied to create the associated answer, in addition to any
318 additional procedures that MAY be discussed in the Diameter
319 application defining the command:
321 o The same Hop-by-Hop Identifier in the request is used in the
324 o The local host's identity is encoded in the Origin-Host AVP.
326 o The Destination-Host and Destination-Realm AVPs MUST NOT be
327 present in the answer message.
329 o The Result-Code AVP is added with its value indicating success or
332 o If the Session-Id is present in the request, it MUST be included
335 o Any Proxy-Info AVPs in the request MUST be added to the answer
336 message, in the same order they were present in the request.
338 o The 'P' bit is set to the same value as the one in the request.
340 o The same End-to-End identifier in the request is used in the
343 Note that the error messages (see Section 7) are also subjected to
344 the above processing rules.
346 Regarding errors, is recommended to use this over automatic answer built at #decode and/or #valid procedures, which would had added
347 Result-Code and/or Failed-AVP AVPs if proceed, but be aware of DIAMETER_COMMAND_UNSUPPORTED Result-Code, because becomes impossible
348 to fix (Session-Id SHOULD appear immediately following the Diameter header, and #fix do this manually even if no information about
349 the command structure is known, but perhaps another fixed AVPs could not comply... use #getResultCode to find out this situation before
350 using #setStandardToAnswer). Anyway, application could add another Failed-AVP content no detected internally, for example:
351 DIAMETER_CONTRADICTING_AVPS or DIAMETER_INVALID_AVP_BIT_COMBO). Also, application could add more Failed-AVP avps with other
352 wrong avps, or accumulate wrong avps inside the one and only Failed-AVP managed by the stack. The standard is open to add multiple
353 avps inside Failed-AVP or multiple Failed-AVP avps with single or multiple avps inside. This depends on application criteria regarding
354 other nodes. However, internally the Anna::diameter stack only provides one Failed-AVP with the first wrong avp found, as RFC 6733 says
357 If application decoding and/or validation operations are ok, user may search for other problems and put the appropiate Result-Code.
358 For example, DIAMETER_TOO_BUSY (3004) depends on congestion issues at business layer and cannot be decided with the only message
359 information automatically (not all the Result-Code values are taken into account, only those which correspond to anomalies managed
360 by anna::diameter::codec). Application Result-Codes could be provided in this prototype, being DIAMETER_SUCCESS the default value if missing.
363 @param request Message to be answered.
364 @param originHost Mandatory Origin-Host diameter identity value provided by application. If answer has already an Origin-Host, this will be ignored.
365 @param originRealm Mandatory Origin-Realm diameter identity value provided by application. If answer has already an Origin-Realm, this will be ignored.
366 @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.
368 @warning Request provided must be a request, in other case method do nothing.
370 void setStandardToAnswer(const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode = helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) throw();
374 Sets a Result-Code AVP over an answer message (for requests, do nothing).
375 If Result-Code AVP doesn't exists, is added and then filled with the value provided.
376 If Result-Code AVP already exists, value detected is replaced if was DIAMETER_SUCCESS (non success codes are unchanged).
377 When provided value corresponds to an protocol error, that is to say within range [3001,3010], message (E)rror bit is
378 automatically activated.
380 This method is internally used during #decode and/or #valid procedures in order to build automatic answers, but application
381 could call this for set another Result-Code no detected by these methods within its category or for other one (application
382 layer). These are the Result-Codes implemented (detected) by ANNA::diameter::codec:
387 DIAMETER_COMMAND_UNSUPPORTED
388 DIAMETER_INVALID_HDR_BITS
389 DIAMETER_INVALID_AVP_BITS
393 DIAMETER_AVP_UNSUPPORTED (F)
394 DIAMETER_INVALID_AVP_VALUE (F)
395 DIAMETER_MISSING_AVP (F)
396 DIAMETER_AVP_NOT_ALLOWED (F)
397 DIAMETER_AVP_OCCURS_TOO_MANY_TIMES (F)
398 DIAMETER_INVALID_BIT_IN_HEADER
399 DIAMETER_INVALID_AVP_LENGTH (F)
400 DIAMETER_INVALID_MESSAGE_LENGTH
402 (F) Generates Failed-AVP (also DIAMETER_CONTRADICTING_AVPS and DIAMETER_INVALID_AVP_BIT_COMBO
403 values does, but these are not managed by anna::diameter::codec).
406 @param rc Result-Code value. DIAMETER_SUCCESS by default.
408 void setResultCode(int rc = helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) throw(anna::RuntimeException);
412 Gets the Result-Code AVP value from an answer message (for requests, returns -1).
413 If missing, -1 value is returned.
415 @return Result-Code value for answers, -1 for request and answers without Result-Code AVP inside
417 int getResultCode() const throw();
421 Adds an avp child providing its identifier and reserve internal memory it.
423 @param id Avp identifier as pair (code,vendor-id).
425 @return Pointer to the new created avp.
427 Avp * addAvp(AvpId id) throw(anna::RuntimeException) { return Avp::addAvp(a_avps, a_insertionPositionForChilds, id, getEngine()); }
431 Same as #addAvp but providing dictionary logical name for Avp searched
433 Avp * addAvp(const char *name) throw(anna::RuntimeException);
437 Adds an avp child providing a persistent pointer (must be maintained by application).
439 @param avp Avp external pointer. If NULL provided, nothing is done and NULL returned.
441 @return Pointer to the added avp (again).
443 Avp * addAvp(Avp * avp) throw() { if(!avp) return NULL; addChild(avp); return avp; }
447 Removes an Avp within message (first level) and free resources.
449 @param id Avp identifier (pair code + vendor-id).
450 @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).
451 Negative values could be used to reverse access positions: i.e. -1 is the last ocurrence, -2 is the second to last (penultimate), etc.
453 @return Returns true if something was removed. False in other cases (including i.e. when this message is empty).
455 bool removeAvp(AvpId id, int ocurrence = 1) throw(anna::RuntimeException) { return Avp::removeAvp(a_avps, (find_container&)a_finds, id, ocurrence, getEngine()); }
459 Same as #removeAvp but providing dictionary logical name for Avp searched
461 bool removeAvp(const char *name, int ocurrence = 1) throw(anna::RuntimeException);
465 * Clears and initializes Message class information.
466 * Application must clear auxiliary message objects before adding Avps in a new context.
467 * Application don't need to clear a message object before decode operation (decode invokes #clear before any other task).
468 * Any reimplementation must first invoke base class method.
470 virtual void clear() throw(anna::RuntimeException);
473 Decodes buffer provided over class content. If an error ocurred, decoding will stop launching exception (fatal error) or a warning trace (perhaps the achieved
474 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
475 in a moment which depends on validation depth (codec::Engine::ValidationDepth).
477 @param db buffer data block processed. Before decoding, the whole message instance will be cleared (no need to invoke #clear before #decode).
478 @param ptrAnswer Answer set by application (could be empty or not), who is responsible for its memory reservation,
479 and automatically built regarding standard. If message analyzed realizes to be an answer, internal reference becomes
480 NULL because no answer is built for answers. By default, automatic answer is not built.
482 void decode(const anna::DataBlock &db, Message *ptrAnswer = NULL) throw(anna::RuntimeException);
485 Fix childrens content regarding dictionary avp positions.
486 Message could remain invalid because of possible fixed/mandatory avps.
487 This is useful to give flexibility to the application during message construction before encoding or representing the data.
488 Is not recommended to fix a recently decoded message because possible validation problems will be hidden.
493 Validates the message regarding dictionary rules like enumerated range, flags coherence, mandatory and fixed types, cardinality qualifiers, etc.
494 @return Boolean indicating validation result
495 @param ptrAnswer Answer set by application (could be empty or not), who is responsible for its memory reservation,
496 and automatically built regarding standard. If message analyzed realizes to be an answer, internal reference becomes
497 NULL because no answer is built for answers. By default, automatic answer is not built.
499 bool valid(Message *ptrAnswer = NULL) const throw(anna::RuntimeException);
503 Interpret xml data in order to dump over the class content.
504 \param messageNode Message root node
506 void fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeException);
509 Interpret xml string representation in order to dump over the class content.
510 DTD validation is used in the same way that #loadXML does.
511 \param xmlString XML string representation with relevant information for this instance
513 void fromXMLString(const std::string &xmlString) throw(anna::RuntimeException);
516 Loads an xml file based on this message DTD (could be accumulative, no initialization will be performed by this method).
519 <!ELEMENT message (avp*)>
520 <!ELEMENT avp (avp*)>
522 <!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-by-end-id CDATA #IMPLIED>
524 version: Diameter version. Sets '1' by default
525 name: Command name within working stack (dictionary identifier)
527 In order to get more coding capabilities, command code and flags could be established instead of former command name,
528 but neither of them are allowed if 'name' is provided (and vice versa):
531 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
534 application-id: Message application id
535 hop-by-hop-id: Message hop by hop id. Sets '0' by default
536 end-by-end-id: Message end by end id. Sets '0' by default
539 <!ATTLIST avp name CDATA #IMPLIED code CDATA #IMPLIED vendor-code CDATA #IMPLIED flags CDATA #IMPLIED data CDATA #IMPLIED hex-data CDATA #IMPLIED>
541 name: Avp name within working stack (dictionary identifier)
543 In order to get more coding capabilities, avp code, vendor-id and flags could be established instead of former avp name,
544 but neither of them are allowed if 'name' is provided (and vice versa):
547 vendor-code: Avp vendor code
548 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)
551 data: Natural string representation for avp data. Specially applicable with numbers and printable strings, but also
552 useful for certain formats which could be easily understandable in such friendly/smart representation. We will
553 achieve different human-readable strings depending on data format:
555 [ OctetString ] (if printable, but not recommended)
556 [ Integer32, Integer64, Unsigned32, Unsigned64, Float32, Float64 ] (normal number representation)
557 [ Time ] (NTP timestamp, normal number representation)
558 [ Address ] (auto detects IPv4 or IPv6 address version, then only ip address is specified: IPv4 with dots, IPv6 with colons)
559 [ UTF8String, DiameterIdentity, DiameterURI ] (printable)
560 [ IPFilterRule, QoSFilterRule ] (uses ASCII charset, printable)
562 New application formats must define specific natural representation for internal raw data
564 hex-data: Hexadecimal octet sequence representation (i.e. 'af012fb3', with even number of digits). Suitable for whatever kind
565 of diameter format, but mandatory for non printable information. OctetString usually transport non human-readable
566 data and should better be encoded within this field although being printable. Unknown avps (which fails identifying
567 provided name or code/vendor-code) must always use this representation.
569 Xml representation for decoded messages shows natural content except for 'OctetString' format and unknown avps. Anyway, when printable,
570 OctetString could show such information at data field apart from hex-data, because many implementations use this format to transport
571 readable-string data. In general, one of the data fields is mandatory except for 'Grouped' type (its data is another level of avps).
572 Application-specific formats must decide the way to represent its contents, being recommended to use a natural representation if possible,
573 because xml is read by humans with testing and monitoring purposes.
577 @param xmlPathFile Complete path file to the xml document which represents the diameter message
580 void loadXML(const std::string & xmlPathFile) throw(anna::RuntimeException);
587 Gets Message identifier as pair (code, request indicator).
589 const CommandId & getId() const throw() { return a_id; }
592 Gets the command version. By default, messages initializes with value 1.
594 @return version Message version
596 U8 getVersion() const throw() { return a_version; }
599 Gets Message request indicator.
601 bool isRequest() const throw() { return a_id.second; }
604 Gets Message answer indicator.
606 bool isAnswer() const throw() { return !isRequest(); }
609 Gets the message application id
610 @return aid Application-id.
612 const U32 & getApplicationId() const throw() { return a_applicationId; }
615 Gets the message hop-by-hop
616 @return hbh Hop-by-hop identifier.
618 const U32 & getHopByHop() const throw() { return a_hopByHop; }
621 Gets the message end-to-end
622 @return ete End-to-end identifier.
624 const U32 & getEndToEnd() const throw() { return a_endToEnd; }
627 Gets stack command (dictionary command reference).
629 const anna::diameter::stack::Command *getStackCommand() const throw(anna::RuntimeException) { return getStackCommand(a_id); }
631 /** Returns R bit activation state */
632 bool requestBit() const throw() { return ((a_flags & RBitMask) != 0x00); }
634 /** Returns P bit activation state */
635 bool proxiableBit() const throw() { return ((a_flags & PBitMask) != 0x00); }
637 /** Returns E bit activation state */
638 bool errorBit() const throw() { return ((a_flags & EBitMask) != 0x00); }
640 /** Returns T bit activation state */
641 bool potentiallyReTransmittedMessageBit() const throw() { return ((a_flags & TBitMask) != 0x00); }
645 Access content for internal Avps. Exception mode allows different combinations like cascade access:
649 message->getAvp(anna::diameter::helpers::base::AVP__Multiple_Services_Credit_Control, anna::Exception::Mode::Throw)
650 ->getAvp(anna::diameter::helpers::base::AVP__Rating_Group, anna::Exception::Mode::Throw);
652 catch(anna::RuntimeException) {;}
658 const Avp *mscc = message->getAvp(anna::diameter::helpers::base::AVP__Multiple_Services_Credit_Control);
660 if (mscc) rg = mscc->getAvp(anna::diameter::helpers::base::AVP__Rating_Group);
663 Replacing procedures becomes easy because an Avp can be searched and its pointer reconfigured by mean #setId and data part setters.
664 Deleting procedures must use #removeAvp.
665 Access is internally cached to speed up the search operations. This cache is reset after calling #fix or #removeAvp methods.
667 @param id Avp identifier (pair code + vendor-id).
668 @param ocurrence Order of appearance for the searched avp. Zero position is rejected, but negative values could be used to reverse
669 access positions: i.e. -1 is the last ocurrence, -2 is the second to last (penultimate), etc.
670 @param emode Excepcion mode handling: Ignore (no action is taken), Throw (excepcion when missing avp), Trace (trace situation as warning).
672 const Avp* getAvp(AvpId id, int ocurrence = 1, anna::Exception::Mode::_v emode = anna::Exception::Mode::Throw) const throw(anna::RuntimeException) {
673 return Avp::getAvp(a_avps, (find_container&)a_finds, id, ocurrence, getEngine(), emode);
676 Avp* getAvp(AvpId id, int ocurrence = 1, anna::Exception::Mode::_v emode = anna::Exception::Mode::Throw) throw(anna::RuntimeException) {
677 return const_cast<Avp*>(Avp::getAvp(a_avps, (find_container&)a_finds, id, ocurrence, getEngine(), emode));
682 Same as #getAvp but providing dictionary logical name for Avp searched
684 const Avp* getAvp(const char *name, int ocurrence = 1, anna::Exception::Mode::_v emode = anna::Exception::Mode::Throw) const throw(anna::RuntimeException) {
685 return _getAvp(name, ocurrence, emode);
688 Avp* getAvp(const char *name, int ocurrence = 1, anna::Exception::Mode::_v emode = anna::Exception::Mode::Throw) throw(anna::RuntimeException) {
689 return const_cast<Avp*>(_getAvp(name, ocurrence, emode));
695 Counts the number of ocurrences of Avps (first level) with the identifier provided
697 @param id Avp identifier (pair code + vendor-id).
699 int countAvp(AvpId id) const throw() { return Avp::countAvp(a_avps, id); }
702 Same as #countAvp but providing dictionary logical name for Avp searched
704 int countAvp(const char *name) const throw(anna::RuntimeException);
707 Counts the number of children
709 @param id Avp identifier (pair code + vendor-id).
711 int countChilds() const throw() { return Avp::countChilds(a_avps); }
714 Encodes datablock with the class content. In case that validation is enabled (codec::Engine::ValidationMode) an exception will be launched
715 in a moment which depends on validation depth (codec::Engine::ValidationDepth). If you want to see validation errors but go on with encoding,
716 you should try/catch #valid() procedure out of #code.
718 @return DataBlock encoded (internal memory used)
720 const anna::DataBlock & code() throw(anna::RuntimeException);
723 Class xml representation
724 \param parent Parent XML node on which hold this instance information.
725 \return XML document with relevant information for this instance.
727 anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
730 Class xml string representation
731 \return XML string representation with relevant information for this instance.
733 std::string asXMLString() const throw();
736 Comparison operator by mean serialization
738 @param m1 Instance 1 for Message class
739 @param m2 Instance 2 for Message class
741 @return Comparison result
743 friend bool operator == (const Message & m1, const Message & m2) throw() { return (m1.asXMLString() == m2.asXMLString()); }
746 Match a regular expression (string pattern) regarding xml string serialization for this message.
747 Using a complex pattern (many avps, grouped ones) it could be necessary to fix the message before
748 using the method in order to perform a more controlled comparison. In the same way, flags could be
749 ignored to simplify message xml presentation.
750 This powerful tool could be used to program traffic analysis and decide future behaviour (routing,
756 The pattern '<avp name="Service-Context-Id" data="(.)*32251@3gpp.org"/>' detects PS charging contexts
757 because of data suffix specification '32251@3gpp.org' for that AVP.
759 The pattern '<message version="1" name="Capabilities-Exchange-Request"' detects a CER message.
761 The pattern (string including carriage returns):
763 '<avp name="Subscription-Id">
764 <avp name="Subscription-Id-Type" data="0" alias="END_USER_E164"/>
765 <avp name="Subscription-Id-Data" data="606000106"/>
768 detects MSISDN (not IMSI) equal to 606000106
770 It would seems strange or 'creative' to use regular expressions within an hex string representation,
771 but anyway you could also do such kind of things to check non-printable data parts within the message:
772 for example, the pattern '<avp name="Framed-IP-Address" hex-data="0a[A-Fa-f0-9][A-Fa-f0-9]0a0a"/>'
773 matchs IP addresses for '10.x.10.10' where x = [0..255].
775 Note that string pattern could also be generated via #loadXML and then #asXML, that is to say, you
776 could get patterns through xml files which act as conditional triggers over message. In that case,
777 it is not possible to specify regular expressions within xml 'hex-data' fields because parser will fail
778 during hexadecimal read. Normally only printable 'data' fields are used for matching issues.
780 For example, imagine a 'pattern.xml' file like:
781 <message version="1" name="Credit-Control-Request" application-id="16777236" hop-by-hop-id="0" end-by-end-id="0">
782 <avp name="Subscription-Id">
783 <avp name="Subscription-Id-Type" data="0" alias="END_USER_E164"/>
784 <avp name="Subscription-Id-Data" data="616[0-9]{6,6}"/>
790 anna::diameter::codec::Message patternMessage;
791 patternMessage.loadXML("pattern.xml");
792 std::string pattern = patternMessage.getAvp("Subscription-Id")->getAvp("Subscription-Id-Type")->asXMLString();
793 // Former is '<avp name="Subscription-Id-Data" data="616[0-9]{6,6}"/>'
794 bool match = incomingMessage.isLike(pattern);
796 Then, messages having MSISDN numbers starting with '616' will match the pattern.
797 Note, that any other message codes (and not only Credit-Control-Request ones), could pass the test...
798 You could also build that string manually:
801 std::string pattern = "<avp name=\"Subscription-Id\">\n";
802 pattern += ANNA_XML_COMPILER_TAB; pattern += "<avp name=\"Subscription-Id-Type\" data=\"0\" alias=\"END_USER_E164\"/>\n"
803 pattern += ANNA_XML_COMPILER_TAB; pattern += "<avp name=\"Subscription-Id-Data\" data=\"616[0-9]{6,6}\"/>"
806 std::string pattern = "name=\"Subscription-Id\"(.)*name=\"Subscription-Id-Type\" data=\"0\"(.)*name=\"Subscription-Id-Data\" data=\"616[0-9]{6,6}\"";
809 \return Returns the match result
811 bool isLike(const std::string &pattern) const throw();
814 //friend class Engine;