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 //
10 #include <anna/diameter/codec/Message.hpp>
11 #include <anna/diameter/codec/Format.hpp>
13 #include <anna/diameter/codec/functions.hpp> // REQUIRED_WORDS
14 #include <anna/config/defines.hpp> // general types, decoding helpers (DECODE[2/3/4]BYTES_INDX_VALUETYPE), etc.
15 #include <anna/diameter/functions.hpp>
16 #include <anna/diameter/codec/functions.hpp> // REQUIRED_WORDS
17 #include <anna/diameter/codec/OamModule.hpp>
18 #include <anna/diameter/codec/Engine.hpp>
19 #include <anna/diameter/codec/EngineManager.hpp>
20 #include <anna/diameter/stack/Avp.hpp>
21 #include <anna/diameter/stack/Format.hpp>
22 #include <anna/diameter/stack/Dictionary.hpp>
23 #include <anna/diameter/stack/Engine.hpp>
24 #include <anna/core/functions.hpp>
25 #include <anna/core/util/RegularExpression.hpp>
26 #include <anna/diameter/helpers/base/defines.hpp>
28 #include <anna/core/tracing/Logger.hpp>
29 #include <anna/core/functions.hpp>
30 #include <anna/core/tracing/TraceMethod.hpp>
31 #include <anna/xml/xml.hpp>
39 using namespace anna::diameter::codec;
48 const int Message::HeaderLength(20);
49 const U8 Message::RBitMask(0x80);
50 const U8 Message::PBitMask(0x40);
51 const U8 Message::EBitMask(0x20);
52 const U8 Message::TBitMask(0x10);
57 //------------------------------------------------------------------------------
58 //----------------------------------------------------------- Message::Message()
59 //------------------------------------------------------------------------------
60 Message::Message(Engine *engine) : a_engine(engine), a_forCode(true) {
65 //------------------------------------------------------------------------------
66 //----------------------------------------------------------- Message::Message()
67 //------------------------------------------------------------------------------
68 Message::Message(CommandId id, Engine *engine) : a_engine(engine), a_forCode(true) {
74 //------------------------------------------------------------------------------
75 //---------------------------------------------------------- Message::~Message()
76 //------------------------------------------------------------------------------
82 //------------------------------------------------------------------------------
83 //--------------------------------------------------------- Message::setEngine()
84 //------------------------------------------------------------------------------
85 void Message::setEngine(Engine *engine) throw() {
88 LOGWARNING(anna::Logger::warning("Ignored: you must assign a valid codec engine. If you want to set NULL engine, clear the message", ANNA_FILE_LOCATION));
92 if (a_engine && (engine != a_engine)) {
93 LOGWARNING(anna::Logger::warning("Ignored: it is not a good practice to change the codec engine once assigned. Clear the message first to set the engine again.", ANNA_FILE_LOCATION));
101 //------------------------------------------------------------------------------
102 //--------------------------------------------------------- Message::getEngine()
103 //------------------------------------------------------------------------------
104 Engine * Message::getEngine() const throw(anna::RuntimeException) {
106 throw anna::RuntimeException("Invalid codec engine reference (NULL). Use setEngine() to set the corresponding codec engine", ANNA_FILE_LOCATION);
113 //------------------------------------------------------------------------------
114 //-------------------------------------------------------- Message::initialize()
115 //------------------------------------------------------------------------------
116 void Message::initialize() throw() {
118 a_id = CommandId(0, false);
124 a_insertionPositionForChilds = 0;
129 //------------------------------------------------------------------------------
130 //------------------------------------------------------------- Message::clear()
131 //------------------------------------------------------------------------------
132 void Message::clear(bool resetEngine) throw(anna::RuntimeException) {
133 for(avp_iterator it = avp_begin(); it != avp_end(); it++) { /*avp(it)->clear(); */getEngine()->releaseAvp(Avp::avp(it)); }
141 if (resetEngine) a_engine = NULL;
145 //------------------------------------------------------------------------------
146 //----------------------------------------------------------- Message::flagsOK()
147 //------------------------------------------------------------------------------
148 bool Message::flagsOK(int &rc) const throw() {
149 // Dictionary stack command:
150 const stack::Command *stackCommand = getStackCommand();
153 anna::Logger::error("Impossible to decide if flags are correct because stack command is not identified. Assume flags ok", ANNA_FILE_LOCATION);
154 //rc = helpers::base::AVPVALUES__Result_Code::?????;
158 // DIAMETER_INVALID_HDR_BITS 3008
160 // A request was received whose bits in the Diameter header were set
161 // either to an invalid combination or to a value that is
162 // inconsistent with the Command Code's definition.
165 if(stackCommand->isRequest() != isRequest()) ok = false; // en teoria es imposible salir por aqui: blindado en la dtd
167 if(isRequest() && errorBit()) {
168 anna::Logger::error("E(rror) bit is not allowed at diameter requests", ANNA_FILE_LOCATION);
172 if(isAnswer() && potentiallyReTransmittedMessageBit()) {
173 anna::Logger::error("T(Potentially re-transmitted message) bit is not allowed at diameter answers", ANNA_FILE_LOCATION);
178 rc = helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_HDR_BITS;
182 // DIAMETER_INVALID_BIT_IN_HEADER 5013
184 // This error is returned when a reserved bit in the Diameter header
185 // is set to one (1) or the bits in the Diameter header are set
187 if((a_flags & 0x0f) != 0x00) {
188 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);
189 rc = helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_BIT_IN_HEADER;
197 //------------------------------------------------------------------------------
198 //------------------------------------------------------------- Message::setId()
199 //------------------------------------------------------------------------------
200 void Message::setId(CommandId id) throw(anna::RuntimeException) {
204 // Dictionary stack command:
205 const stack::Command *stackCommand = getStackCommand(); // based on dictionary and a_id
207 // Dictionary flags for known types:
209 if(stackCommand->isRequest()) a_flags |= RBitMask;
211 if(a_id.second) a_flags |= RBitMask; else a_flags &= (~RBitMask);
216 //------------------------------------------------------------------------------
217 //------------------------------------------------------------- Message::setId()
218 //------------------------------------------------------------------------------
219 void Message::setId(const char *name) throw(anna::RuntimeException) {
220 setId(getEngine()->commandIdForName(name));
224 //------------------------------------------------------------------------------
225 //-------------------------------------------------- Message::setApplicationId()
226 //------------------------------------------------------------------------------
227 void Message::setApplicationId(U32 aid) throw(anna::RuntimeException) {
228 a_applicationId = aid;
230 // Automatic engine configuration:
231 if (a_engine) return;
233 // Codec engine manager (a multithreaded application, normally does not achieve this point, because
234 // messages are prepared for each interface with the corresponding codec engine)
235 anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
236 if (em.size() == 0) return;
237 if (em.selectFromApplicationId()) {
238 Engine *monostackEngine = em.getMonoStackCodecEngine();
239 if (monostackEngine) { a_engine = monostackEngine; return; }
240 a_engine = em.getCodecEngine(aid);
245 //------------------------------------------------------------------------------
246 //------------------------------------------------------------ Message::addAvp()
247 //------------------------------------------------------------------------------
248 Avp * Message::addAvp(const char *name) throw(anna::RuntimeException) {
249 return addAvp(getEngine()->avpIdForName(name));
253 //------------------------------------------------------------------------------
254 //------------------------------------------------------------ Message::addAvp()
255 //------------------------------------------------------------------------------
256 Avp * Message::addAvp(Avp * avp) throw() {
257 if(!avp) return NULL;
258 if (avp->getEngine() != getEngine()) return NULL;
264 //------------------------------------------------------------------------------
265 //--------------------------------------------------------- Message::removeAvp()
266 //------------------------------------------------------------------------------
267 bool Message::removeAvp(const char *name, int ocurrence) throw(anna::RuntimeException) {
268 return removeAvp(getEngine()->avpIdForName(name), ocurrence);
272 //------------------------------------------------------------------------------
273 //----------------------------------------------------------- Message::_getAvp()
274 //------------------------------------------------------------------------------
275 const Avp * Message::_getAvp(const char *name, int ocurrence, anna::Exception::Mode::_v emode) const throw(anna::RuntimeException) {
276 return getAvp(getEngine()->avpIdForName(name), ocurrence, emode);
280 //------------------------------------------------------------------------------
281 //---------------------------------------------------------- Message::countAvp()
282 //------------------------------------------------------------------------------
283 int Message::countAvp(const char *name) const throw(anna::RuntimeException) {
284 return countAvp(getEngine()->avpIdForName(name));
288 //------------------------------------------------------------------------------
289 //--------------------------------------------------------- Message::getLength()
290 //------------------------------------------------------------------------------
291 U24 Message::getLength() const throw() {
294 result = HeaderLength;
296 for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) result += 4 * REQUIRED_WORDS(Avp::avp(it)->getLength());
302 //------------------------------------------------------------------------------
303 //------------------------------------------------------------ Message::decode()
304 //------------------------------------------------------------------------------
305 void Message::decode(const anna::DataBlock &db, Message *ptrAnswer) throw(anna::RuntimeException) {
308 anna::xml::Node root("Message::decode");
309 std::string trace = "DataBlock to decode:\n";
310 trace += db.asString();
311 anna::Logger::debug(trace, ANNA_FILE_LOCATION);
313 clear(false /* respect engine */);
314 // EXCEPTION MANAGEMENT IN THIS METHOD
315 // ===================================
317 // If an error ocurred, decoding will stop launching exception but we will catch it and go on with validation because perhaps
318 // the achieved message could be valid against all odds. Only fatal errors cause direct decoding exception (length problems
322 // Launch exception on first validation error (validateAll == false), or log warning reporting all validation errors when
323 // complete validation is desired (validateAll == true, engine default) launching a final exception like "the decoded message is invalid".
325 OamModule &oamModule = OamModule::instantiate();
327 if(db.getSize() < HeaderLength) {
328 oamModule.activateAlarm(OamModule::Alarm::MessageDecode__NotEnoughBytesToCoverMessageHeaderLength);
329 oamModule.count(OamModule::Counter::MessageDecode__NotEnoughBytesToCoverMessageHeaderLength);
330 // DIAMETER_INVALID_MESSAGE_LENGTH; // no podr� construir un answer fiable, as� que no registro el result-code
331 throw anna::RuntimeException("Not enough bytes to cover message header length (20 bytes)", ANNA_FILE_LOCATION);
334 const char * buffer = db.getData();
337 // 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
338 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
339 // | Version | Message Length |
340 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
341 // | command flags | Command-Code |
342 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
343 // | Application-ID |
344 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
345 // | Hop-by-Hop Identifier |
346 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
347 // | End-to-End Identifier |
348 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
350 // +-+-+-+-+-+-+-+-+-+-+-+-+-
352 a_version = (S8)buffer[0];
354 U24 length = DECODE3BYTES_INDX_VALUETYPE(buffer, 1, U24); // total length including header fields
356 a_flags = (S8)buffer[4];
358 U24 code = DECODE3BYTES_INDX_VALUETYPE(buffer, 5, U24);
360 a_id = CommandId(code, requestBit() /* based on a_flags */);
362 setApplicationId(DECODE4BYTES_INDX_VALUETYPE(buffer, 8, U32)); // centralize set, because it could be used for stack selection.
364 a_hopByHop = DECODE4BYTES_INDX_VALUETYPE(buffer, 12, U32);
366 a_endToEnd = DECODE4BYTES_INDX_VALUETYPE(buffer, 16, U32);
368 // Only build answer for a request:
369 Message *answer = isRequest() ? ptrAnswer : NULL;
371 if(answer) answer->setHeaderToAnswer(*this);
374 if(db.getSize() < length) {
375 oamModule.activateAlarm(OamModule::Alarm::MessageDecode__NotEnoughBytesToCoverMessageLength);
376 oamModule.count(OamModule::Counter::MessageDecode__NotEnoughBytesToCoverMessageLength);
378 if(answer) answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_MESSAGE_LENGTH);
380 throw anna::RuntimeException("Not enough bytes to cover message length", ANNA_FILE_LOCATION);
383 // Message identifier
384 // Flags could have been updated regarding dictionary, but during decoding we must respect buffer received:
385 a_flags = (S8)buffer[4];
386 // Avps start position:
387 const S8 * startData = buffer + HeaderLength;
388 U24 dataBytes = length - HeaderLength;
390 if(dataBytes < 8 /* minimum avp */) {
391 LOGDEBUG(anna::Logger::debug("Message empty (without avps)", ANNA_FILE_LOCATION));
397 anna::DataBlock db_aux;
399 // Parent information:
401 parent.setMessage(a_id);
404 while(avpPos < dataBytes) {
406 avp = getEngine()->createAvp(NULL);
407 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) */);
408 avp -> decode(db_aux, parent, answer);
409 } catch(anna::RuntimeException &ex) {
410 getEngine()->releaseAvp(avp);
412 anna::Logger::warning(ex.getText(), ANNA_FILE_LOCATION);
413 anna::Logger::warning("Although a decoding error was found, validation could be checked because message could be enough for the application", ANNA_FILE_LOCATION);
419 avpPos += 4 * REQUIRED_WORDS(avp->getLength());
423 Engine::FixMode::_v fmode = getEngine()->getFixMode();
425 if((fmode == Engine::FixMode::AfterDecoding) || (fmode == Engine::FixMode::Always)) fix();
429 std::string trace = "Message decoded:\n";
430 trace += asXMLString();
431 anna::Logger::debug(trace, ANNA_FILE_LOCATION);
434 Engine::ValidationMode::_v vmode = getEngine()->getValidationMode();
436 if((vmode == Engine::ValidationMode::AfterDecoding) || (vmode == Engine::ValidationMode::Always))
438 throw anna::RuntimeException("The decoded message is invalid. See previous report on warning-level traces", ANNA_FILE_LOCATION);
442 //------------------------------------------------------------------------------
443 //--------------------------------------------------- Message::getStackCommand()
444 //------------------------------------------------------------------------------
445 const anna::diameter::stack::Command *Message::getStackCommand(CommandId id) const throw(anna::RuntimeException) {
446 const stack::Dictionary * dictionary = getEngine()->getDictionary();
447 return (dictionary ? (dictionary->getCommand(id)) : NULL);
451 //------------------------------------------------------------------------------
452 //----------------------------------------------------- Message::setResultCode()
453 //------------------------------------------------------------------------------
454 void Message::setResultCode(int rc) throw(anna::RuntimeException) {
455 if(isRequest()) return;
457 // Add Result-Code if not yet added. Even if validation depth is set to 'Complete',
458 // the Result-Code value will be the first found during the message analysis:
459 Avp *resultCodeAvp = getAvp(helpers::base::AVPID__Result_Code, 1, anna::Exception::Mode::Ignore);
462 addAvp(helpers::base::AVPID__Result_Code)->getUnsigned32()->setValue(rc);
463 else if(resultCodeAvp->getUnsigned32()->getValue() == helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) // Only success could be replaced
464 resultCodeAvp->getUnsigned32()->setValue(rc);
467 setErrorBit(rc >= 3001 && rc <= 3010 /* protocol errors */);
471 //------------------------------------------------------------------------------
472 //----------------------------------------------------- Message::getResultCode()
473 //------------------------------------------------------------------------------
474 int Message::getResultCode() const throw() {
476 const Avp *resultCodeAvp = getAvp(helpers::base::AVPID__Result_Code, 1, anna::Exception::Mode::Ignore);
479 return resultCodeAvp->getUnsigned32()->getValue();
486 //------------------------------------------------------------------------------
487 //------------------------------------------------------ Message::setFailedAvp()
488 //------------------------------------------------------------------------------
489 void Message::setFailedAvp(const parent_t &parent, AvpId wrong, const char *wrongName) throw(anna::RuntimeException) {
491 if(isRequest()) return;
495 // 7.5. Failed-AVP AVP
497 // The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
498 // debugging information in cases where a request is rejected or not
499 // fully processed due to erroneous information in a specific AVP. The
500 // value of the Result-Code AVP will provide information on the reason
501 // for the Failed-AVP AVP. A Diameter answer message SHOULD contain an
502 // instance of the Failed-AVP AVP that corresponds to the error
503 // indicated by the Result-Code AVP. For practical purposes, this
504 // Failed-AVP would typically refer to the first AVP processing error
505 // that a Diameter node encounters.
507 // Although the Failed-AVP definition has cardinality 1* and Failed-AVP itself is defined in
508 // most of the command codes as *[Failed-AVP], i think this is not a deliberate ambiguity.
509 // Probably the RFC wants to give freedom to the application layer, but it is recommended to
510 // have only one child (wrong avp) inside a unique message Failed-AVP to ease the Result-Code
511 // correspondence. Anyway, this behaviour could be easily opened by mean 'setSingleFailedAVP(false)'
512 Avp *theFailedAvp = getAvp(helpers::base::AVPID__Failed_AVP, 1, anna::Exception::Mode::Ignore);
514 if (getEngine()->getSingleFailedAVP()) {
515 LOGDEBUG(anna::Logger::debug("Failed-AVP has already been added. RFC 6733 Section 7.5 recommends to store only the first error found", ANNA_FILE_LOCATION));
520 // Section 7.5 RFC 6733: A Diameter message SHOULD contain one Failed-AVP AVP
521 theFailedAvp = addAvp(helpers::base::AVPID__Failed_AVP);
522 Avp *leaf = theFailedAvp;
525 std::string msg = "Adding to Failed-AVP, the wrong avp ";
526 msg += wrongName ? wrongName : (anna::diameter::functions::avpIdAsPairString(wrong));
527 msg += " found inside ";
528 msg += parent.asString();
530 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
533 std::vector<AvpId>::const_iterator it;
534 for(it = parent.AvpsId.begin(); it != parent.AvpsId.end(); it++)
535 leaf = leaf->addAvp(*it);
541 //------------------------------------------------------------------------------
542 //----------------------------------------------- Message::setStandardToAnswer()
543 //------------------------------------------------------------------------------
544 void Message::setStandardToAnswer(const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode) throw(anna::RuntimeException) {
545 if(!request.getId().second) return;
548 setHeaderToAnswer(request);
549 // Session-Id if exists:
550 const Avp *reqSessionId = request.getAvp(helpers::base::AVPID__Session_Id, 1, anna::Exception::Mode::Ignore);
553 if(!getAvp(helpers::base::AVPID__Session_Id, 1, anna::Exception::Mode::Ignore))
554 addAvp(helpers::base::AVPID__Session_Id)->getUTF8String()->setValue(reqSessionId->getUTF8String()->getValue());
556 // Origin-Host & Realm
557 if(!getAvp(helpers::base::AVPID__Origin_Host, 1, anna::Exception::Mode::Ignore))
558 addAvp(helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->setValue(originHost);
560 if(!getAvp(helpers::base::AVPID__Origin_Realm, 1, anna::Exception::Mode::Ignore))
561 addAvp(helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->setValue(originRealm);
563 // Proxy-Info AVPs if exist, in the same order:
564 for(const_avp_iterator it = request.avp_begin(); it != request.avp_end(); it++)
565 if((*it).second->getId() == helpers::base::AVPID__Proxy_Info)
566 addAvp((*it).second);
568 // If no Result-Code was added is because all was ok, then application could detect another problem:
569 setResultCode(resultCode);
573 std::string msg = "Completed answer:\n";
574 msg += asXMLString();
575 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
580 //------------------------------------------------------------------------------
581 //--------------------------------------------------------------- Message::fix()
582 //------------------------------------------------------------------------------
583 void Message::fix() throw() {
584 // Dictionary stack command:
585 const stack::Command *stackCommand = getStackCommand();
588 LOGDEBUG(anna::Logger::debug("No dictionary command reference found. Cannot fix.", ANNA_FILE_LOCATION));
589 // Try to get to the first place the Session-ID (if exists) because...
590 // RFC 6733: All messages pertaining to a specific session MUST include only one Session-Id AVP ...
591 // ... When present, the Session-Id SHOULD appear immediately following the Diameter header
592 avp_iterator it = Avp::avp_find(a_avps, helpers::base::AVPID__Session_Id, 1 /* one and only */);
594 if(it != avp_end()) {
595 int sessionPos = (*it).first;
596 Avp *first = (*avp_begin()).second;
597 a_avps[0] = (*it).second; // Session-Id
598 a_avps[sessionPos] = first;
599 LOGDEBUG(anna::Logger::debug("Session-Id has been manually fixed to the first place", ANNA_FILE_LOCATION));
605 Avp::fix(a_avps, (find_container&)a_finds, a_insertionPositionForChilds, stackCommand->avprule_begin(), stackCommand->avprule_end());
609 //------------------------------------------------------------------------------
610 //------------------------------------------------------------- Message::valid()
611 //------------------------------------------------------------------------------
612 bool Message::valid(Message *ptrAnswer) const throw(anna::RuntimeException) {
614 OamModule &oamModule = OamModule::instantiate();
615 // Dictionary stack command:
616 const stack::Command *stackCommand = getStackCommand();
617 // Only build answer for a request:
618 Message *answer = isRequest() ? ptrAnswer : NULL;
620 if(answer) answer->setHeaderToAnswer(*this);
624 std::string me = anna::diameter::functions::commandIdAsPairString(a_id);
625 oamModule.activateAlarm(OamModule::Alarm::MessageValidation__UnknownOperation__s__UnableToValidate, STRING_WITH_QUOTATION_MARKS__C_STR(me));
626 oamModule.count(OamModule::Counter::MessageValidation__UnknownOperationUnableToValidate);
628 if(answer) answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_COMMAND_UNSUPPORTED);
630 getEngine()->validationAnomaly(anna::functions::asString("Unknown operation %s. Unable to validate", STRING_WITH_QUOTATION_MARKS__C_STR(me)));
634 // Parent information:
636 me.setMessage(a_id, stackCommand->getName().c_str());
638 //////////////////////////////
639 // Flags coherence checking //
640 //////////////////////////////
642 bool result = flagsOK(rc);
645 // OAM & Depth management
646 oamModule.activateAlarm(OamModule::Alarm::MessageValidation__Operation__s__HaveIncoherentFlags__d__, STRING_WITH_QUOTATION_MARKS__C_STR(me.asString()), (int)a_flags);
647 oamModule.count(OamModule::Counter::MessageValidation__OperationHaveIncoherentFlags);
649 if(answer) answer->setResultCode(rc);
651 getEngine()->validationAnomaly(anna::functions::asString("Operation %s have incoherent flags (%d)", STRING_WITH_QUOTATION_MARKS__C_STR(me.asString()), (int)a_flags));
657 result = Avp::validLevel(a_avps, stackCommand->avprule_begin(), stackCommand->avprule_end(), getEngine(), me, answer) && result;
659 ////////////////////////
660 // Childrens checking //
661 ////////////////////////
662 for(const_avp_iterator it = avp_begin(); it != avp_end(); it++)
663 result = ((*it).second->valid(me, answer)) && result;
669 //------------------------------------------------------------------------------
670 //-------------------------------------------------------------- Message::code()
671 //------------------------------------------------------------------------------
672 const anna::DataBlock & Message::code() throw(anna::RuntimeException) {
674 Engine::ValidationMode::_v vmode = getEngine()->getValidationMode();
676 if((vmode == Engine::ValidationMode::BeforeEncoding) || (vmode == Engine::ValidationMode::Always)) {
678 throw anna::RuntimeException("Try to encode an invalid message. See previous report on warning-level traces", ANNA_FILE_LOCATION);
682 Engine::FixMode::_v fmode = getEngine()->getFixMode();
684 if((fmode == Engine::FixMode::BeforeEncoding) || (fmode == Engine::FixMode::Always)) fix();
688 std::string trace = "Message to code:\n";
689 trace += asXMLString();
690 anna::Logger::debug(trace, ANNA_FILE_LOCATION);
693 U24 length = getLength();
695 a_forCode.allocate(length);
696 char* buffer = (char*)a_forCode.getData();
697 // Version and length
698 buffer[0] = (S8) a_version;
699 buffer[1] = (S8)(length >> 16);
700 buffer[2] = (S8)(length >> 8);
701 buffer[3] = (S8) length;
703 buffer[4] = (S8) a_flags;
704 buffer[5] = (S8)(a_id.first >> 16);
705 buffer[6] = (S8)(a_id.first >> 8);
706 buffer[7] = (S8) a_id.first;
708 buffer[8] = (S8)(a_applicationId >> 24);
709 buffer[9] = (S8)(a_applicationId >> 16);
710 buffer[10] = (S8)(a_applicationId >> 8);
711 buffer[11] = (S8) a_applicationId;
713 buffer[12] = (S8)(a_hopByHop >> 24);
714 buffer[13] = (S8)(a_hopByHop >> 16);
715 buffer[14] = (S8)(a_hopByHop >> 8);
716 buffer[15] = (S8) a_hopByHop;
718 buffer[16] = (S8)(a_endToEnd >> 24);
719 buffer[17] = (S8)(a_endToEnd >> 16);
720 buffer[18] = (S8)(a_endToEnd >> 8);
721 buffer[19] = (S8) a_endToEnd;
722 // Data start position:
723 int startDataPos = HeaderLength;
725 if(startDataPos == length) {
726 LOGDEBUG(anna::Logger::debug("There is no Avps to encode (only-header message)", ANNA_FILE_LOCATION));
729 S8 * dataPart = buffer + startDataPos;
730 int dataBytes; // not used but could be useful for checking (length - startDataPos)
731 // Each avp encoding will remain padding octets depending on format. In order to
732 // code avps colection (each avp will be multiple of 4) we clean the entire buffer
733 // to ensure padding (easier than custom-made for each format):
734 memset(dataPart, 0, length - startDataPos); // no estoy seguro de que el clear del DataBlock haga esto...
736 for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) {
737 Avp::avp(it)->code(dataPart, dataBytes);
738 dataPart = dataPart + 4 * REQUIRED_WORDS(dataBytes);
744 std::string trace = "DataBlock encoded:\n";
745 trace += a_forCode.asString();
746 // trace += "\nAs continuous hexadecimal string:\n";
747 // trace += anna::functions::asHexString(a_forCode);
748 anna::Logger::debug(trace, ANNA_FILE_LOCATION);
753 //------------------------------------------------------------------------------
754 //----------------------------------------------------------- Message::loadXML()
755 //------------------------------------------------------------------------------
756 void Message::loadXML(const std::string &xmlPathFile) throw(anna::RuntimeException) {
758 anna::xml::DocumentFile xmlDocument;
759 anna::diameter::codec::functions::messageXmlDocumentFromXmlFile(xmlDocument, xmlPathFile);
760 fromXML(xmlDocument.getRootNode());
763 //------------------------------------------------------------------------------
764 //----------------------------------------------------------- Message::fromXML()
765 //------------------------------------------------------------------------------
766 void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeException) {
767 // <!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>
768 const anna::xml::Attribute *version, *name, *code, *flags, *pbit, *ebit, *tbit, *appid, *hbh, *ete;
769 version = messageNode->getAttribute("version", false /* no exception */);
770 name = messageNode->getAttribute("name", false /* no exception */);
771 code = messageNode->getAttribute("code", false /* no exception */);
772 flags = messageNode->getAttribute("flags", false /* no exception */);
773 pbit = messageNode->getAttribute("p-bit", false /* no exception */);
774 ebit = messageNode->getAttribute("e-bit", false /* no exception */);
775 tbit = messageNode->getAttribute("t-bit", false /* no exception */);
776 appid = messageNode->getAttribute("application-id"); // required
777 hbh = messageNode->getAttribute("hop-by-hop-id", false /* no exception */);
778 ete = messageNode->getAttribute("end-by-end-id", false /* no exception */);
783 clear(false /* respect engine */);
786 i_aux = version->getIntegerValue();
788 if(i_aux < 0 || i_aux > 256) {
789 std::string msg = "Error processing avp <version '"; msg += version->getValue();
790 msg += "': out of range [0,256]";
791 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
798 // This is called before any operation which needs to know about the stack elements (this could set the dictionary)
799 setApplicationId(appid->getIntegerValue());
802 const stack::Dictionary * dictionary = getEngine()->getDictionary();
803 const stack::Command *stackCommand = NULL;
808 std::string msg = "Error processing command <name '"; msg += name->getValue();
809 msg += "'>: no dictionary available. Load one or use <'code' + 'flags'> instead of <'name'>";
810 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
813 stackCommand = dictionary->getCommand(name->getValue());
816 std::string msg = "Error processing command <name '"; msg += name->getValue();
817 msg += "'>: no command found for this identifier at available dictionary";
818 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
822 // Check attributes exclusiveness
823 if(name) { // compact mode
825 std::string msg = "Error processing command <name '"; msg += name->getValue();
826 msg += "'>: message attributes <'code' + 'flags'> are not allowed if <'name'> is provided";
827 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
830 setId(stackCommand->getId());
832 // 'P', 'E' and 'T' flags:
833 bool activateP = pbit ? (pbit->getValue() == "yes") : false;
834 bool activateE = ebit ? (ebit->getValue() == "yes") : false;
835 bool activateT = tbit ? (tbit->getValue() == "yes") : false;
837 if(activateP) a_flags |= PBitMask;
839 if(activateE) a_flags |= EBitMask;
841 if(activateT) a_flags |= TBitMask;
843 std::string s_code = code ? code->getValue() : "?";
844 std::string s_flags = flags ? flags->getValue() : "?";
846 if(!code || !flags) {
847 std::string msg = "Error processing command <code '"; msg += s_code;
848 msg += "' + flags '"; msg += s_flags;
849 msg += "'>: both must be provided if <'name'> don't";
850 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
853 if(pbit || ebit || tbit) {
854 std::string msg = "Error processing command <code '"; msg += s_code;
855 msg += "' + flags '"; msg += s_flags;
856 msg += "'>: neither of 'p-bit', 'e-bit' or 't-bit' fields are allowed if those are present";
857 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
861 i_aux = code->getIntegerValue();
864 std::string msg = "Error processing command <code '"; msg += s_code;
865 msg += "': negative values are not allowed";
866 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
871 i_aux = flags->getIntegerValue();
873 if(i_aux < 0 || i_aux > 256) {
874 std::string msg = "Error processing command <flags '"; msg += s_flags;
875 msg += "': out of range [0,256]";
876 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
880 int flagsBCK = a_flags;
882 a_id = CommandId(u_code, requestBit() /* based on a_flags */);
883 // Flags could have been updated regarding dictionary, but during parsing we must respect xml file:
889 u_aux = hbh->getIntegerValue();
893 std::string msg = "Error processing command <hop-by-hop-id '"; msg += hbh->getValue();
894 msg += "': negative values are not allowed";
895 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
904 u_aux = ete->getIntegerValue();
908 std::string msg = "Error processing command <end-to-end-id '"; msg += ete->getValue();
909 msg += "': negative values are not allowed";
910 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
919 for(anna::xml::Node::const_child_iterator it = messageNode->child_begin(); it != messageNode->child_end(); it++) {
920 std::string nodeName = (*it)->getName();
922 if(nodeName != "avp") {
923 std::string msg = "Unsupported message child node name '"; msg += nodeName;
924 msg += "': use 'avp'";
925 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
929 avp = getEngine()->createAvp(NULL);
931 } catch(anna::RuntimeException &ex) {
932 getEngine()->releaseAvp(avp);
941 //------------------------------------------------------------------------------
942 //------------------------------------------------------------- Message::asXML()
943 //------------------------------------------------------------------------------
944 anna::xml::Node* Message::asXML(anna::xml::Node* parent) const throw() {
945 // <!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>
946 anna::xml::Node* result = parent->createChild("message");
947 // Dictionary stack command:
948 const stack::Command *stackCommand = getStackCommand();
949 bool compactMode = stackCommand /*&& flagsOK()*/;
950 result->createAttribute("version", anna::functions::asString((int)a_version));
953 result->createAttribute("name", stackCommand->getName());
955 if(proxiableBit()) result->createAttribute("p-bit", "yes");
957 if(errorBit()) result->createAttribute("e-bit", "yes");
959 if(potentiallyReTransmittedMessageBit()) result->createAttribute("t-bit", "yes");
961 result->createAttribute("code", a_id.first);
962 result->createAttribute("flags", (int)a_flags);
965 result->createAttribute("application-id", anna::functions::asString(a_applicationId));
966 result->createAttribute("hop-by-hop-id", anna::functions::asString(a_hopByHop));
967 result->createAttribute("end-by-end-id", anna::functions::asString(a_endToEnd));
970 for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) {
971 Avp::avp(it)->asXML(result);
978 //------------------------------------------------------------------------------
979 //------------------------------------------------------- Message::asXMLString()
980 //------------------------------------------------------------------------------
981 std::string Message::asXMLString() const throw() {
982 anna::xml::Node root("root");
983 return anna::xml::Compiler().apply(asXML(&root));
987 //------------------------------------------------------------------------------
988 //------------------------------------------------------------ Message::isLike()
989 //------------------------------------------------------------------------------
990 bool Message::isLike(const std::string &pattern) const throw() {
991 anna::RegularExpression re(pattern);
992 return re.isLike(asXMLString());