1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // https://bitbucket.org/testillano/anna
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
11 // * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
17 // * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
38 #include <anna/diameter/codec/Message.hpp>
39 #include <anna/diameter/codec/Format.hpp>
41 #include <anna/diameter/codec/functions.hpp> // REQUIRED_WORDS
42 #include <anna/config/defines.hpp> // general types, decoding helpers (DECODE[2/3/4]BYTES_INDX_VALUETYPE), etc.
43 #include <anna/diameter/functions.hpp>
44 #include <anna/diameter/codec/functions.hpp> // REQUIRED_WORDS
45 #include <anna/diameter/codec/OamModule.hpp>
46 #include <anna/diameter/codec/Engine.hpp>
47 #include <anna/diameter/stack/Avp.hpp>
48 #include <anna/diameter/stack/Format.hpp>
49 #include <anna/diameter/stack/Dictionary.hpp>
50 #include <anna/diameter/stack/Engine.hpp>
51 #include <anna/core/functions.hpp>
52 #include <anna/core/util/RegularExpression.hpp>
53 #include <anna/diameter/helpers/base/defines.hpp>
55 #include <anna/core/tracing/Logger.hpp>
56 #include <anna/core/functions.hpp>
57 #include <anna/core/tracing/TraceMethod.hpp>
58 #include <anna/xml/xml.hpp>
65 using namespace anna::diameter::codec;
74 const int Message::HeaderLength(20);
75 const U8 Message::RBitMask(0x80);
76 const U8 Message::PBitMask(0x40);
77 const U8 Message::EBitMask(0x20);
78 const U8 Message::TBitMask(0x10);
83 //------------------------------------------------------------------------------
84 //----------------------------------------------------------- Message::Message()
85 //------------------------------------------------------------------------------
86 Message::Message() : a_forCode(true) {
91 //------------------------------------------------------------------------------
92 //----------------------------------------------------------- Message::Message()
93 //------------------------------------------------------------------------------
94 Message::Message(CommandId id) : a_forCode(true) {
100 //------------------------------------------------------------------------------
101 //---------------------------------------------------------- Message::~Message()
102 //------------------------------------------------------------------------------
103 Message::~Message() {
108 //------------------------------------------------------------------------------
109 //--------------------------------------------------------- Message::getEngine()
110 //------------------------------------------------------------------------------
111 Engine * Message::getEngine() const throw(anna::RuntimeException) {
112 return a_engine ? a_engine : (a_engine = anna::functions::component <Engine> (ANNA_FILE_LOCATION));
116 //------------------------------------------------------------------------------
117 //-------------------------------------------------------- Message::initialize()
118 //------------------------------------------------------------------------------
119 void Message::initialize() throw() {
122 a_id = CommandId(0, false);
128 a_insertionPositionForChilds = 0;
133 //------------------------------------------------------------------------------
134 //------------------------------------------------------------- Message::clear()
135 //------------------------------------------------------------------------------
136 void Message::clear() throw(anna::RuntimeException) {
137 for(avp_iterator it = avp_begin(); it != avp_end(); it++) { /*avp(it)->clear(); */getEngine()->releaseAvp(Avp::avp(it)); }
148 //------------------------------------------------------------------------------
149 //----------------------------------------------------------- Message::flagsOK()
150 //------------------------------------------------------------------------------
151 bool Message::flagsOK(int &rc) const throw() {
152 // Dictionary stack command:
153 const stack::Command *stackCommand = getStackCommand();
156 anna::Logger::error("Impossible to decide if flags are correct because stack command is not identified. Assume flags ok", ANNA_FILE_LOCATION);
157 //rc = helpers::base::AVPVALUES__Result_Code::?????;
161 // DIAMETER_INVALID_HDR_BITS 3008
163 // A request was received whose bits in the Diameter header were set
164 // either to an invalid combination or to a value that is
165 // inconsistent with the Command Code's definition.
168 if(stackCommand->isRequest() != isRequest()) ok = false; // en teoria es imposible salir por aqui: blindado en la dtd
170 if(isRequest() && errorBit()) {
171 anna::Logger::error("E(rror) bit is not allowed at diameter requests", ANNA_FILE_LOCATION);
175 if(isAnswer() && potentiallyReTransmittedMessageBit()) {
176 anna::Logger::error("T(Potentially re-transmitted message) bit is not allowed at diameter answers", ANNA_FILE_LOCATION);
181 rc = helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_HDR_BITS;
185 // DIAMETER_INVALID_BIT_IN_HEADER 5013
187 // This error is returned when a reserved bit in the Diameter header
188 // is set to one (1) or the bits in the Diameter header are set
190 if((a_flags & 0x0f) != 0x00) {
191 anna::Logger::error("Any (or more than one) of the reserved message flags bit has been activated. Reserved bits must be null", ANNA_FILE_LOCATION);
192 rc = helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_BIT_IN_HEADER;
200 //------------------------------------------------------------------------------
201 //------------------------------------------------------------- Message::setId()
202 //------------------------------------------------------------------------------
203 void Message::setId(CommandId id, bool _clear) throw(anna::RuntimeException) {
204 // Clear class content:
209 // Dictionary stack command:
210 const stack::Command *stackCommand = getStackCommand(); // based on dictionary and a_id
212 // Dictionary flags for known types:
214 if(stackCommand->isRequest()) a_flags |= RBitMask;
216 if(a_id.second) a_flags |= RBitMask; else a_flags &= (~RBitMask);
221 //------------------------------------------------------------------------------
222 //------------------------------------------------------------- Message::setId()
223 //------------------------------------------------------------------------------
224 void Message::setId(const char *name) throw(anna::RuntimeException) {
225 setId(getEngine()->commandIdForName(name));
229 //------------------------------------------------------------------------------
230 //------------------------------------------------------------ Message::addAvp()
231 //------------------------------------------------------------------------------
232 Avp * Message::addAvp(const char *name) throw(anna::RuntimeException) {
233 return addAvp(getEngine()->avpIdForName(name));
237 //------------------------------------------------------------------------------
238 //--------------------------------------------------------- Message::removeAvp()
239 //------------------------------------------------------------------------------
240 bool Message::removeAvp(const char *name, int ocurrence) throw(anna::RuntimeException) {
241 return removeAvp(getEngine()->avpIdForName(name), ocurrence);
245 //------------------------------------------------------------------------------
246 //----------------------------------------------------------- Message::_getAvp()
247 //------------------------------------------------------------------------------
248 const Avp * Message::_getAvp(const char *name, int ocurrence, anna::Exception::Mode::_v emode) const throw(anna::RuntimeException) {
249 return getAvp(getEngine()->avpIdForName(name), ocurrence, emode);
253 //------------------------------------------------------------------------------
254 //---------------------------------------------------------- Message::countAvp()
255 //------------------------------------------------------------------------------
256 int Message::countAvp(const char *name) const throw(anna::RuntimeException) {
257 return countAvp(getEngine()->avpIdForName(name));
261 //------------------------------------------------------------------------------
262 //--------------------------------------------------------- Message::getLength()
263 //------------------------------------------------------------------------------
264 U24 Message::getLength() const throw() {
267 result = HeaderLength;
269 for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) result += 4 * REQUIRED_WORDS(Avp::avp(it)->getLength());
275 //------------------------------------------------------------------------------
276 //------------------------------------------------------------ Message::decode()
277 //------------------------------------------------------------------------------
278 void Message::decode(const anna::DataBlock &db, Message *ptrAnswer) throw(anna::RuntimeException) {
281 anna::xml::Node root("Message::decode");
282 std::string trace = "DataBlock to decode:\n";
283 trace += db.asString();
284 anna::Logger::debug(trace, ANNA_FILE_LOCATION);
287 // EXCEPTION MANAGEMENT IN THIS METHOD
288 // ===================================
290 // If an error ocurred, decoding will stop launching exception but we will catch it and go on with validation because perhaps
291 // the achieved message could be valid against all odds. Only fatal errors cause direct decoding exception (length problems
295 // Launch exception on first validation error (validateAll == false), or log warning reporting all validation errors when
296 // complete validation is desired (validateAll == true, engine default) launching a final exception like "decoded an invalid message".
298 OamModule &oamModule = OamModule::instantiate();
300 if(db.getSize() < HeaderLength) {
301 oamModule.activateAlarm(OamModule::Alarm::MessageDecode__NotEnoughBytesToCoverMessageHeaderLength);
302 oamModule.count(OamModule::Counter::MessageDecode__NotEnoughBytesToCoverMessageHeaderLength);
303 // DIAMETER_INVALID_MESSAGE_LENGTH; // no podré construir un answer fiable, asà que no registro el result-code
304 throw anna::RuntimeException("Not enough bytes to cover message header length (20 bytes)", ANNA_FILE_LOCATION);
307 const char * buffer = db.getData();
310 // 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
311 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
312 // | Version | Message Length |
313 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
314 // | command flags | Command-Code |
315 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
316 // | Application-ID |
317 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
318 // | Hop-by-Hop Identifier |
319 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
320 // | End-to-End Identifier |
321 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
323 // +-+-+-+-+-+-+-+-+-+-+-+-+-
325 a_version = (S8)buffer[0];
327 U24 length = DECODE3BYTES_INDX_VALUETYPE(buffer, 1, U24); // total length including header fields
329 a_flags = (S8)buffer[4];
331 U24 code = DECODE3BYTES_INDX_VALUETYPE(buffer, 5, U24);
333 setId(CommandId(code, requestBit() /* based on a_flags */));
335 a_applicationId = DECODE4BYTES_INDX_VALUETYPE(buffer, 8, U32);
337 a_hopByHop = DECODE4BYTES_INDX_VALUETYPE(buffer, 12, U32);
339 a_endToEnd = DECODE4BYTES_INDX_VALUETYPE(buffer, 16, U32);
341 // Only build answer for a request:
342 Message *answer = isRequest() ? ptrAnswer : NULL;
344 if(answer) answer->setHeaderToAnswer(*this);
347 if(db.getSize() < length) {
348 oamModule.activateAlarm(OamModule::Alarm::MessageDecode__NotEnoughBytesToCoverMessageLength);
349 oamModule.count(OamModule::Counter::MessageDecode__NotEnoughBytesToCoverMessageLength);
351 if(answer) answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_MESSAGE_LENGTH);
353 throw anna::RuntimeException("Not enough bytes to cover message length", ANNA_FILE_LOCATION);
356 // Message identifier
357 // Flags could have been updated regarding dictionary, but during decoding we must respect buffer received:
358 a_flags = (S8)buffer[4];
359 // Avps start position:
360 const S8 * startData = buffer + HeaderLength;
361 U24 dataBytes = length - HeaderLength;
363 if(dataBytes < 8 /* minimum avp */) {
364 LOGDEBUG(anna::Logger::debug("Message empty (without avps)", ANNA_FILE_LOCATION));
370 anna::DataBlock db_aux;
372 while(avpPos < dataBytes) {
374 avp = getEngine()->allocateAvp();
375 db_aux.assign(startData + avpPos, dataBytes - avpPos /* is valid to pass total length (indeed i don't know the real avp length) because it will be limited and this has deep copy disabled (no memory is reserved) */);
376 avp -> decode(db_aux, answer);
377 } catch(anna::RuntimeException &ex) {
379 answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_AVP_VALUE); // unspecified error ...
380 answer->setNewFailedAvp(avp->getId());
383 getEngine()->releaseAvp(avp);
385 anna::Logger::warning(ex.getText(), ANNA_FILE_LOCATION);
386 anna::Logger::warning("Although a decoding error was found, validation could be checked because message could be enough for the application", ANNA_FILE_LOCATION);
392 avpPos += 4 * REQUIRED_WORDS(avp->getLength());
396 Engine::FixMode::_v fmode = getEngine()->getFixMode();
398 if((fmode == Engine::FixMode::AfterDecoding) || (fmode == Engine::FixMode::Always)) fix();
402 std::string trace = "Message decoded:\n";
403 trace += asXMLString();
404 anna::Logger::debug(trace, ANNA_FILE_LOCATION);
407 Engine::ValidationMode::_v vmode = getEngine()->getValidationMode();
409 if((vmode == Engine::ValidationMode::AfterDecoding) || (vmode == Engine::ValidationMode::Always))
411 throw anna::RuntimeException("Decoded an invalid message. See previous report on warning-level traces", ANNA_FILE_LOCATION);
415 //------------------------------------------------------------------------------
416 //--------------------------------------------------- Message::getStackCommand()
417 //------------------------------------------------------------------------------
418 const anna::diameter::stack::Command *Message::getStackCommand(CommandId id) const throw(anna::RuntimeException) {
419 const stack::Dictionary * dictionary = getEngine()->getDictionary();
420 return (dictionary ? (dictionary->getCommand(id)) : NULL);
424 //------------------------------------------------------------------------------
425 //----------------------------------------------------- Message::setResultCode()
426 //------------------------------------------------------------------------------
427 void Message::setResultCode(int rc) throw(anna::RuntimeException) {
428 if(isRequest()) return;
430 // Add Result-Code if not yet added. Even if validation depth is set to 'Complete',
431 // the Result-Code value will be the first found during the message analysis:
432 Avp *resultCodeAvp = getAvp(helpers::base::AVPID__Result_Code, 1, anna::Exception::Mode::Ignore);
435 addAvp(helpers::base::AVPID__Result_Code)->getUnsigned32()->setValue(rc);
436 else if(resultCodeAvp->getUnsigned32()->getValue() == helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) // Only success could be replaced
437 resultCodeAvp->getUnsigned32()->setValue(rc);
440 setErrorBit(rc >= 3001 && rc <= 3010 /* protocol errors */);
444 //------------------------------------------------------------------------------
445 //----------------------------------------------------- Message::getResultCode()
446 //------------------------------------------------------------------------------
447 int Message::getResultCode() const throw() {
449 const Avp *resultCodeAvp = getAvp(helpers::base::AVPID__Result_Code, 1, anna::Exception::Mode::Ignore);
452 return resultCodeAvp->getUnsigned32()->getValue();
459 //------------------------------------------------------------------------------
460 //------------------------------------------------------ Message::addFailedAVP()
461 //------------------------------------------------------------------------------
462 Avp * Message::addFailedAVP() throw() {
463 Avp *result = getAvp(helpers::base::AVPID__Failed_AVP, 1, anna::Exception::Mode::Ignore);
465 if(!result) result = addAvp(helpers::base::AVPID__Failed_AVP);
471 //------------------------------------------------------------------------------
472 //----------------------------------------------- Message::setStandardToAnswer()
473 //------------------------------------------------------------------------------
474 void Message::setStandardToAnswer(const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode) throw() {
475 if(!request.getId().second) return;
478 setHeaderToAnswer(request);
479 // Session-Id if exists:
480 const Avp *reqSessionId = request.getAvp(helpers::base::AVPID__Session_Id, 1, anna::Exception::Mode::Ignore);
483 addAvp(helpers::base::AVPID__Session_Id)->getUTF8String()->setValue(reqSessionId->getUTF8String()->getValue());
485 // Origin-Host & Realm
486 if(!getAvp(helpers::base::AVPID__Origin_Host, 1, anna::Exception::Mode::Ignore))
487 addAvp(helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->setValue(originHost);
489 if(!getAvp(helpers::base::AVPID__Origin_Realm, 1, anna::Exception::Mode::Ignore))
490 addAvp(helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->setValue(originRealm);
492 // Proxy-Info AVPs if exist, in the same order:
493 for(const_avp_iterator it = request.avp_begin(); it != request.avp_end(); it++)
494 if((*it).second->getId() == helpers::base::AVPID__Proxy_Info)
495 addAvp((*it).second);
497 // If no Result-Code was added is because all was ok, then application could detect another problem:
498 setResultCode(resultCode);
502 std::string msg = "Completed answer:\n";
503 msg += asXMLString();
504 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
509 //------------------------------------------------------------------------------
510 //--------------------------------------------------------------- Message::fix()
511 //------------------------------------------------------------------------------
512 void Message::fix() throw() {
513 // Dictionary stack command:
514 const stack::Command *stackCommand = getStackCommand();
517 LOGDEBUG(anna::Logger::debug("No dictionary command reference found. Cannot fix.", ANNA_FILE_LOCATION));
518 // Try to get to the first place the Session-ID (if exists) because...
519 // RFC 6733: All messages pertaining to a specific session MUST include only one Session-Id AVP ...
520 // ... When present, the Session-Id SHOULD appear immediately following the Diameter header
521 avp_iterator it = Avp::avp_find(a_avps, helpers::base::AVPID__Session_Id, 1 /* one and only */);
523 if(it != avp_end()) {
524 int sessionPos = (*it).first;
525 Avp *first = (*avp_begin()).second;
526 a_avps[0] = (*it).second; // Session-Id
527 a_avps[sessionPos] = first;
528 LOGDEBUG(anna::Logger::debug("Session-Id has been manually fixed to the first place", ANNA_FILE_LOCATION));
534 Avp::fix(a_avps, (find_container&)a_finds, a_insertionPositionForChilds, stackCommand->avprule_begin(), stackCommand->avprule_end());
538 //------------------------------------------------------------------------------
539 //------------------------------------------------------------- Message::valid()
540 //------------------------------------------------------------------------------
541 bool Message::valid(Message *ptrAnswer) const throw(anna::RuntimeException) {
543 OamModule &oamModule = OamModule::instantiate();
544 // Dictionary stack command:
545 const stack::Command *stackCommand = getStackCommand();
547 // Only build answer for a request:
548 Message *answer = isRequest() ? ptrAnswer : NULL;
550 if(answer) answer->setHeaderToAnswer(*this);
554 me = anna::diameter::functions::commandIdAsPairString(a_id);
555 oamModule.activateAlarm(OamModule::Alarm::MessageValidation__UnknownOperation__s__UnableToValidate, STRING_WITH_QUOTATION_MARKS__C_STR(me));
556 oamModule.count(OamModule::Counter::MessageValidation__UnknownOperationUnableToValidate);
558 if(answer) answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_COMMAND_UNSUPPORTED);
560 getEngine()->validationAnomaly(anna::functions::asString("Unknown operation %s. Unable to validate", STRING_WITH_QUOTATION_MARKS__C_STR(me)));
564 me = stackCommand->getName();
565 //////////////////////////////
566 // Flags coherence checking //
567 //////////////////////////////
569 bool result = flagsOK(rc);
572 // OAM & Depth management
573 oamModule.activateAlarm(OamModule::Alarm::MessageValidation__Operation__s__HaveIncoherentFlags__d__, STRING_WITH_QUOTATION_MARKS__C_STR(me), (int)a_flags);
574 oamModule.count(OamModule::Counter::MessageValidation__OperationHaveIncoherentFlags);
576 if(answer) answer->setResultCode(rc);
578 getEngine()->validationAnomaly(anna::functions::asString("Operation %s have incoherent flags (%d)", STRING_WITH_QUOTATION_MARKS__C_STR(me), (int)a_flags));
584 result = Avp::validLevel(a_avps, stackCommand->avprule_begin(), stackCommand->avprule_end(), getEngine(), me, answer) && result;
586 ////////////////////////
587 // Childrens checking //
588 ////////////////////////
589 for(const_avp_iterator it = avp_begin(); it != avp_end(); it++)
590 result = ((*it).second->valid(me, answer)) && result;
596 //------------------------------------------------------------------------------
597 //-------------------------------------------------------------- Message::code()
598 //------------------------------------------------------------------------------
599 const anna::DataBlock & Message::code() throw(anna::RuntimeException) {
601 Engine::ValidationMode::_v vmode = getEngine()->getValidationMode();
603 if((vmode == Engine::ValidationMode::BeforeCoding) || (vmode == Engine::ValidationMode::Always)) {
605 throw anna::RuntimeException("Try to encode an invalid message. See previous report on warning-level traces", ANNA_FILE_LOCATION);
609 Engine::FixMode::_v fmode = getEngine()->getFixMode();
611 if((fmode == Engine::FixMode::BeforeCoding) || (fmode == Engine::FixMode::Always)) fix();
615 std::string trace = "Message to code:\n";
616 trace += asXMLString();
617 anna::Logger::debug(trace, ANNA_FILE_LOCATION);
620 U24 length = getLength();
622 a_forCode.allocate(length);
623 char* buffer = (char*)a_forCode.getData();
624 // Version and length
625 buffer[0] = (S8) a_version;
626 buffer[1] = (S8)(length >> 16);
627 buffer[2] = (S8)(length >> 8);
628 buffer[3] = (S8) length;
630 buffer[4] = (S8) a_flags;
631 buffer[5] = (S8)(a_id.first >> 16);
632 buffer[6] = (S8)(a_id.first >> 8);
633 buffer[7] = (S8) a_id.first;
635 buffer[8] = (S8)(a_applicationId >> 24);
636 buffer[9] = (S8)(a_applicationId >> 16);
637 buffer[10] = (S8)(a_applicationId >> 8);
638 buffer[11] = (S8) a_applicationId;
640 buffer[12] = (S8)(a_hopByHop >> 24);
641 buffer[13] = (S8)(a_hopByHop >> 16);
642 buffer[14] = (S8)(a_hopByHop >> 8);
643 buffer[15] = (S8) a_hopByHop;
645 buffer[16] = (S8)(a_endToEnd >> 24);
646 buffer[17] = (S8)(a_endToEnd >> 16);
647 buffer[18] = (S8)(a_endToEnd >> 8);
648 buffer[19] = (S8) a_endToEnd;
649 // Data start position:
650 int startDataPos = HeaderLength;
652 if(startDataPos == length) {
653 LOGDEBUG(anna::Logger::debug("There is no Avps to encode (only-header message)", ANNA_FILE_LOCATION));
656 S8 * dataPart = buffer + startDataPos;
657 int dataBytes; // not used but could be useful for checking (length - startDataPos)
658 // Each avp encoding will remain padding octets depending on format. In order to
659 // code avps colection (each avp will be multiple of 4) we clean the entire buffer
660 // to ensure padding (easier than custom-made for each format):
661 memset(dataPart, 0, length - startDataPos); // no estoy seguro de que el clear del DataBlock haga esto...
663 for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) {
664 Avp::avp(it)->code(dataPart, dataBytes);
665 dataPart = dataPart + 4 * REQUIRED_WORDS(dataBytes);
671 std::string trace = "DataBlock encoded:\n";
672 trace += a_forCode.asString();
673 // trace += "\nAs continuous hexadecimal string:\n";
674 // trace += anna::functions::asHexString(a_forCode);
675 anna::Logger::debug(trace, ANNA_FILE_LOCATION);
681 //------------------------------------------------------------------------------
682 //----------------------------------------------------- Message::fromXMLString()
683 //------------------------------------------------------------------------------
684 void Message::fromXMLString(const std::string &xmlString) throw(anna::RuntimeException) {
685 LOGDEBUG(anna::Logger::debug("Reading diameter message from xml string representation", ANNA_FILE_LOCATION));
686 anna::xml::DocumentMemory xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
687 const anna::xml::Node *rootNode;
688 xmlDocument.initialize(xmlString.c_str());
689 rootNode = xmlDocument.parse(getEngine()->getDTD()); // Parsing: fail here if xml violates dtd
690 LOGDEBUG(anna::Logger::debug("Read OK from XML string representation", ANNA_FILE_LOCATION));
695 //------------------------------------------------------------------------------
696 //----------------------------------------------------------- Message::fromXML()
697 //------------------------------------------------------------------------------
698 void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeException) {
699 // <!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-by-end-id CDATA #IMPLIED>
700 const anna::xml::Attribute *version, *name, *code, *flags, *pbit, *ebit, *tbit, *appid, *hbh, *ete;
701 version = messageNode->getAttribute("version", false /* no exception */);
702 name = messageNode->getAttribute("name", false /* no exception */);
703 code = messageNode->getAttribute("code", false /* no exception */);
704 flags = messageNode->getAttribute("flags", false /* no exception */);
705 pbit = messageNode->getAttribute("p-bit", false /* no exception */);
706 ebit = messageNode->getAttribute("e-bit", false /* no exception */);
707 tbit = messageNode->getAttribute("t-bit", false /* no exception */);
708 appid = messageNode->getAttribute("application-id"); // required
709 hbh = messageNode->getAttribute("hop-by-hop-id", false /* no exception */);
710 ete = messageNode->getAttribute("end-by-end-id", false /* no exception */);
715 i_aux = version->getIntegerValue();
717 if(i_aux < 0 || i_aux > 256) {
718 std::string msg = "Error processing avp <version '"; msg += version->getValue();
719 msg += "': out of range [0,256]";
720 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
727 const stack::Dictionary * dictionary = getEngine()->getDictionary();
728 const stack::Command *stackCommand = NULL;
733 std::string msg = "Error processing command <name '"; msg += name->getValue();
734 msg += "'>: no dictionary available. Load one or use <'code' + 'flags'> instead of <'name'>";
735 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
738 stackCommand = dictionary->getCommand(name->getValue());
741 std::string msg = "Error processing command <name '"; msg += name->getValue();
742 msg += "'>: no command found for this identifier at available dictionary";
743 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
747 // Check attributes exclusiveness
748 if(name) { // compact mode
750 std::string msg = "Error processing command <name '"; msg += name->getValue();
751 msg += "'>: message attributes <'code' + 'flags'> are not allowed if <'name'> is provided";
752 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
755 setId(stackCommand->getId());
756 // 'P', 'E' and 'T' flags:
757 bool activateP = pbit ? (pbit->getValue() == "yes") : false;
758 bool activateE = ebit ? (ebit->getValue() == "yes") : false;
759 bool activateT = tbit ? (tbit->getValue() == "yes") : false;
761 if(activateP) a_flags |= PBitMask;
763 if(activateE) a_flags |= EBitMask;
765 if(activateT) a_flags |= TBitMask;
767 std::string s_code = code ? code->getValue() : "?";
768 std::string s_flags = flags ? flags->getValue() : "?";
770 if(!code || !flags) {
771 std::string msg = "Error processing command <code '"; msg += s_code;
772 msg += "' + flags '"; msg += s_flags;
773 msg += "'>: both must be provided if <'name'> don't";
774 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
777 if(pbit || ebit || tbit) {
778 std::string msg = "Error processing command <code '"; msg += s_code;
779 msg += "' + flags '"; msg += s_flags;
780 msg += "'>: neither of 'p-bit', 'e-bit' or 't-bit' fields are allowed if those are present";
781 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
785 i_aux = code->getIntegerValue();
788 std::string msg = "Error processing command <code '"; msg += s_code;
789 msg += "': negative values are not allowed";
790 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
795 i_aux = flags->getIntegerValue();
797 if(i_aux < 0 || i_aux > 256) {
798 std::string msg = "Error processing command <flags '"; msg += s_flags;
799 msg += "': out of range [0,256]";
800 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
804 int flagsBCK = a_flags;
806 setId(CommandId(u_code, requestBit() /* based on a_flags */));
807 // Flags could have been updated regarding dictionary, but during parsing we must respect xml file:
812 u_aux = appid->getIntegerValue();
815 std::string msg = "Error processing command <application-id '"; msg += appid->getValue();
816 msg += "': negative values are not allowed";
817 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
820 setApplicationId(u_aux);
824 u_aux = hbh->getIntegerValue();
827 std::string msg = "Error processing command <hop-by-hop-id '"; msg += hbh->getValue();
828 msg += "': negative values are not allowed";
829 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
837 u_aux = ete->getIntegerValue();
840 std::string msg = "Error processing command <end-to-end-id '"; msg += ete->getValue();
841 msg += "': negative values are not allowed";
842 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
850 for(anna::xml::Node::const_child_iterator it = messageNode->child_begin(); it != messageNode->child_end(); it++) {
851 std::string nodeName = (*it)->getName();
853 if(nodeName != "avp") {
854 std::string msg = "Unsupported message child node name '"; msg += nodeName;
855 msg += "': use 'avp'";
856 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
860 avp = getEngine()->allocateAvp();
862 } catch(anna::RuntimeException &ex) {
863 getEngine()->releaseAvp(avp);
872 //------------------------------------------------------------------------------
873 //----------------------------------------------------------- Message::loadXML()
874 //------------------------------------------------------------------------------
875 void Message::loadXML(const std::string & xmlPathFile) throw(anna::RuntimeException) {
877 std::string trace = "Loading diameter message from file '";
878 trace += xmlPathFile;
880 anna::Logger::debug(trace, ANNA_FILE_LOCATION);
882 anna::xml::DocumentFile xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
883 const anna::xml::Node *rootNode;
884 xmlDocument.initialize(xmlPathFile.c_str()); // fail here is i/o error
885 rootNode = xmlDocument.parse(getEngine()->getDTD()); // Parsing: fail here if xml violates dtd
887 std::string trace = "Loaded XML file (";
888 trace += xmlPathFile;
890 trace += anna::xml::Compiler().apply(rootNode);
891 anna::Logger::debug(trace, ANNA_FILE_LOCATION);
897 //------------------------------------------------------------------------------
898 //------------------------------------------------------------- Message::asXML()
899 //------------------------------------------------------------------------------
900 anna::xml::Node* Message::asXML(anna::xml::Node* parent) const throw() {
901 // <!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>
902 anna::xml::Node* result = parent->createChild("message");
903 // Dictionary stack command:
904 const stack::Command *stackCommand = getStackCommand();
905 bool compactMode = stackCommand /*&& flagsOK()*/;
906 result->createAttribute("version", anna::functions::asString((int)a_version));
909 result->createAttribute("name", stackCommand->getName());
911 if(proxiableBit()) result->createAttribute("p-bit", "yes");
913 if(errorBit()) result->createAttribute("e-bit", "yes");
915 if(potentiallyReTransmittedMessageBit()) result->createAttribute("t-bit", "yes");
917 result->createAttribute("code", a_id.first);
918 result->createAttribute("flags", (int)a_flags);
921 result->createAttribute("application-id", anna::functions::asString(a_applicationId));
922 result->createAttribute("hop-by-hop-id", anna::functions::asString(a_hopByHop));
923 result->createAttribute("end-by-end-id", anna::functions::asString(a_endToEnd));
926 for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) {
927 Avp::avp(it)->asXML(result);
934 //------------------------------------------------------------------------------
935 //------------------------------------------------------- Message::asXMLString()
936 //------------------------------------------------------------------------------
937 std::string Message::asXMLString() const throw() {
938 anna::xml::Node root("root");
939 return anna::xml::Compiler().apply(asXML(&root));
943 //------------------------------------------------------------------------------
944 //------------------------------------------------------------ Message::isLike()
945 //------------------------------------------------------------------------------
946 bool Message::isLike(const std::string &pattern) const throw() {
947 anna::RegularExpression re(pattern);
948 return re.isLike(asXMLString());