Changed LICENSE. Now referenced to web site and file on project root directory
[anna.git] / source / diameter / codec / Message.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
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 //
7
8
9 // Local
10 #include <anna/diameter/codec/Message.hpp>
11 #include <anna/diameter/codec/Format.hpp>
12
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/stack/Avp.hpp>
20 #include <anna/diameter/stack/Format.hpp>
21 #include <anna/diameter/stack/Dictionary.hpp>
22 #include <anna/diameter/stack/Engine.hpp>
23 #include <anna/core/functions.hpp>
24 #include <anna/core/util/RegularExpression.hpp>
25 #include <anna/diameter/helpers/base/defines.hpp>
26
27 #include <anna/core/tracing/Logger.hpp>
28 #include <anna/core/functions.hpp>
29 #include <anna/core/tracing/TraceMethod.hpp>
30 #include <anna/xml/xml.hpp>
31
32 // STL
33 #include <string>
34 #include <vector>
35
36
37 using namespace anna;
38 using namespace anna::diameter::codec;
39
40
41 //static
42 namespace anna {
43
44 namespace diameter {
45
46 namespace codec {
47 const int Message::HeaderLength(20);
48 const U8 Message::RBitMask(0x80);
49 const U8 Message::PBitMask(0x40);
50 const U8 Message::EBitMask(0x20);
51 const U8 Message::TBitMask(0x10);
52 }
53 }
54 }
55
56 //------------------------------------------------------------------------------
57 //----------------------------------------------------------- Message::Message()
58 //------------------------------------------------------------------------------
59 Message::Message() : a_forCode(true) {
60   initialize();
61 }
62
63
64 //------------------------------------------------------------------------------
65 //----------------------------------------------------------- Message::Message()
66 //------------------------------------------------------------------------------
67 Message::Message(CommandId id) : a_forCode(true) {
68   initialize();
69   setId(id);
70 }
71
72
73 //------------------------------------------------------------------------------
74 //---------------------------------------------------------- Message::~Message()
75 //------------------------------------------------------------------------------
76 Message::~Message() {
77   clear();
78 }
79
80
81 //------------------------------------------------------------------------------
82 //--------------------------------------------------------- Message::getEngine()
83 //------------------------------------------------------------------------------
84 Engine * Message::getEngine() const throw(anna::RuntimeException) {
85   return a_engine ? a_engine : (a_engine = anna::functions::component <Engine> (ANNA_FILE_LOCATION));
86 }
87
88
89 //------------------------------------------------------------------------------
90 //-------------------------------------------------------- Message::initialize()
91 //------------------------------------------------------------------------------
92 void Message::initialize() throw() {
93   a_engine = NULL;
94   a_version = 1;
95   a_id = CommandId(0, false);
96   a_flags = 0x00;
97   a_applicationId = 0;
98   a_hopByHop = 0;
99   a_endToEnd = 0;
100   //a_avps.clear();
101   a_insertionPositionForChilds = 0;
102   //a_finds.clear();
103 }
104
105
106 //------------------------------------------------------------------------------
107 //------------------------------------------------------------- Message::clear()
108 //------------------------------------------------------------------------------
109 void Message::clear() throw(anna::RuntimeException) {
110   for(avp_iterator it = avp_begin(); it != avp_end(); it++) { /*avp(it)->clear(); */getEngine()->releaseAvp(Avp::avp(it)); }
111
112   a_avps.clear();
113   a_forCode.clear();
114   // Cache system:
115   a_finds.clear();
116   // Initialize:
117   initialize();
118 }
119
120
121 //------------------------------------------------------------------------------
122 //----------------------------------------------------------- Message::flagsOK()
123 //------------------------------------------------------------------------------
124 bool Message::flagsOK(int &rc) const throw() {
125   // Dictionary stack command:
126   const stack::Command *stackCommand = getStackCommand();
127
128   if(!stackCommand) {
129     anna::Logger::error("Impossible to decide if flags are correct because stack command is not identified. Assume flags ok", ANNA_FILE_LOCATION);
130     //rc = helpers::base::AVPVALUES__Result_Code::?????;
131     return true;
132   };
133
134   // DIAMETER_INVALID_HDR_BITS 3008
135   //
136   //      A request was received whose bits in the Diameter header were set
137   //      either to an invalid combination or to a value that is
138   //      inconsistent with the Command Code's definition.
139   bool ok = true;
140
141   if(stackCommand->isRequest() != isRequest()) ok = false;  // en teoria es imposible salir por aqui: blindado en la dtd
142
143   if(isRequest() && errorBit()) {
144     anna::Logger::error("E(rror) bit is not allowed at diameter requests", ANNA_FILE_LOCATION);
145     ok = false;
146   }
147
148   if(isAnswer() && potentiallyReTransmittedMessageBit()) {
149     anna::Logger::error("T(Potentially re-transmitted message) bit is not allowed at diameter answers", ANNA_FILE_LOCATION);
150     ok = false;
151   }
152
153   if(!ok) {
154     rc = helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_HDR_BITS;
155     return false;
156   }
157
158   // DIAMETER_INVALID_BIT_IN_HEADER 5013
159   //
160   //      This error is returned when a reserved bit in the Diameter header
161   //      is set to one (1) or the bits in the Diameter header are set
162   //      incorrectly.
163   if((a_flags & 0x0f) != 0x00) {
164     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);
165     rc = helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_BIT_IN_HEADER;
166     return false;
167   }
168
169   return true;
170 }
171
172
173 //------------------------------------------------------------------------------
174 //------------------------------------------------------------- Message::setId()
175 //------------------------------------------------------------------------------
176 void Message::setId(CommandId id, bool _clear) throw(anna::RuntimeException) {
177   // Clear class content:
178   if(_clear) clear();
179
180   // Id assignment:
181   a_id = id;
182   // Dictionary stack command:
183   const stack::Command *stackCommand = getStackCommand(); // based on dictionary and a_id
184
185   // Dictionary flags for known types:
186   if(stackCommand) {
187     if(stackCommand->isRequest()) a_flags |= RBitMask;
188   } else {
189     if(a_id.second) a_flags |= RBitMask; else a_flags &= (~RBitMask);
190   }
191 }
192
193
194 //------------------------------------------------------------------------------
195 //------------------------------------------------------------- Message::setId()
196 //------------------------------------------------------------------------------
197 void Message::setId(const char *name) throw(anna::RuntimeException) {
198   setId(getEngine()->commandIdForName(name));
199 }
200
201
202 //------------------------------------------------------------------------------
203 //-------------------------------------------------- Message::setApplicationId()
204 //------------------------------------------------------------------------------
205 void Message::setApplicationId(U32 aid) throw() {
206   a_applicationId = aid;
207
208   // Default behaviour:
209   if (!getEngine()->hasSelectStackWithApplicationId()) return;
210
211   // Adapts for Application-ID stack identifier:
212   getEngine()->setDictionary(aid);
213 }
214
215
216 //------------------------------------------------------------------------------
217 //------------------------------------------------------------ Message::addAvp()
218 //------------------------------------------------------------------------------
219 Avp * Message::addAvp(const char *name) throw(anna::RuntimeException) {
220   return addAvp(getEngine()->avpIdForName(name));
221 }
222
223
224 //------------------------------------------------------------------------------
225 //--------------------------------------------------------- Message::removeAvp()
226 //------------------------------------------------------------------------------
227 bool Message::removeAvp(const char *name, int ocurrence) throw(anna::RuntimeException) {
228   return removeAvp(getEngine()->avpIdForName(name), ocurrence);
229 }
230
231
232 //------------------------------------------------------------------------------
233 //----------------------------------------------------------- Message::_getAvp()
234 //------------------------------------------------------------------------------
235 const Avp * Message::_getAvp(const char *name, int ocurrence, anna::Exception::Mode::_v emode) const throw(anna::RuntimeException) {
236   return getAvp(getEngine()->avpIdForName(name), ocurrence, emode);
237 }
238
239
240 //------------------------------------------------------------------------------
241 //---------------------------------------------------------- Message::countAvp()
242 //------------------------------------------------------------------------------
243 int Message::countAvp(const char *name) const throw(anna::RuntimeException) {
244   return countAvp(getEngine()->avpIdForName(name));
245 }
246
247
248 //------------------------------------------------------------------------------
249 //--------------------------------------------------------- Message::getLength()
250 //------------------------------------------------------------------------------
251 U24 Message::getLength() const throw() {
252   U24 result;
253   // Header length:
254   result = HeaderLength;
255
256   for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) result += 4 * REQUIRED_WORDS(Avp::avp(it)->getLength());
257
258   return result;
259 }
260
261
262 //------------------------------------------------------------------------------
263 //------------------------------------------------------------ Message::decode()
264 //------------------------------------------------------------------------------
265 void Message::decode(const anna::DataBlock &db, Message *ptrAnswer) throw(anna::RuntimeException) {
266   // Trace
267   LOGDEBUG(
268     anna::xml::Node root("Message::decode");
269     std::string trace = "DataBlock to decode:\n";
270     trace += db.asString();
271     anna::Logger::debug(trace, ANNA_FILE_LOCATION);
272   );
273   clear();
274   // EXCEPTION MANAGEMENT IN THIS METHOD
275   // ===================================
276   // DECODE PHASE
277   // If an error ocurred, decoding will stop launching exception but we will catch it and go on with validation because perhaps
278   //  the achieved message could be valid against all odds. Only fatal errors cause direct decoding exception (length problems
279   //  at header).
280   //
281   // VALIDATION PHASE
282   // Launch exception on first validation error (validateAll == false), or log warning reporting all validation errors when
283   //  complete validation is desired (validateAll == true, engine default) launching a final exception like "the decoded message is invalid".
284   // OAM
285   OamModule &oamModule = OamModule::instantiate();
286
287   if(db.getSize() < HeaderLength) {
288     oamModule.activateAlarm(OamModule::Alarm::MessageDecode__NotEnoughBytesToCoverMessageHeaderLength);
289     oamModule.count(OamModule::Counter::MessageDecode__NotEnoughBytesToCoverMessageHeaderLength);
290     // DIAMETER_INVALID_MESSAGE_LENGTH; // no podr� construir un answer fiable, as� que no registro el result-code
291     throw anna::RuntimeException("Not enough bytes to cover message header length (20 bytes)", ANNA_FILE_LOCATION);
292   }
293
294   const char * buffer = db.getData();
295
296   //         0                   1                   2                   3
297   //         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
298   //        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
299   //        |    Version    |                 Message Length                |
300   //        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
301   //        | command flags |                  Command-Code                 |
302   //        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
303   //        |                         Application-ID                        |
304   //        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
305   //        |                      Hop-by-Hop Identifier                    |
306   //        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
307   //        |                      End-to-End Identifier                    |
308   //        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
309   //        |  AVPs ...
310   //        +-+-+-+-+-+-+-+-+-+-+-+-+-
311   // Header fields:
312   a_version = (S8)buffer[0];
313
314   U24 length = DECODE3BYTES_INDX_VALUETYPE(buffer, 1, U24); // total length including header fields
315
316   a_flags = (S8)buffer[4];
317
318   U24 code = DECODE3BYTES_INDX_VALUETYPE(buffer, 5, U24);
319
320   a_id = CommandId(code, requestBit() /* based on a_flags */);
321
322   setApplicationId(DECODE4BYTES_INDX_VALUETYPE(buffer, 8, U32)); // centralize set, because it could be used for stack selection.
323
324   a_hopByHop = DECODE4BYTES_INDX_VALUETYPE(buffer, 12, U32);
325
326   a_endToEnd = DECODE4BYTES_INDX_VALUETYPE(buffer, 16, U32);
327
328   // Only build answer for a request:
329   Message *answer = isRequest() ? ptrAnswer : NULL;
330
331   if(answer) answer->setHeaderToAnswer(*this);
332
333   // Length check:
334   if(db.getSize() < length) {
335     oamModule.activateAlarm(OamModule::Alarm::MessageDecode__NotEnoughBytesToCoverMessageLength);
336     oamModule.count(OamModule::Counter::MessageDecode__NotEnoughBytesToCoverMessageLength);
337
338     if(answer) answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_MESSAGE_LENGTH);
339
340     throw anna::RuntimeException("Not enough bytes to cover message length", ANNA_FILE_LOCATION);
341   }
342
343   // Message identifier
344   // Flags could have been updated regarding dictionary, but during decoding we must respect buffer received:
345   a_flags = (S8)buffer[4];
346   // Avps start position:
347   const S8 * startData = buffer + HeaderLength;
348   U24 dataBytes = length - HeaderLength;
349
350   if(dataBytes < 8 /* minimum avp */) {
351     LOGDEBUG(anna::Logger::debug("Message empty (without avps)", ANNA_FILE_LOCATION));
352     return;
353   }
354
355   int avpPos = 0;
356   Avp* avp;
357   anna::DataBlock db_aux;
358
359   // Parent information:
360   parent_t parent;
361   parent.setMessage(a_id);
362
363
364   while(avpPos < dataBytes) {
365     try {
366       avp =  getEngine()->allocateAvp();
367       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) */);
368       avp -> decode(db_aux, parent, answer);
369     } catch(anna::RuntimeException &ex) {
370       getEngine()->releaseAvp(avp);
371       LOGWARNING(
372         anna::Logger::warning(ex.getText(), ANNA_FILE_LOCATION);
373         anna::Logger::warning("Although a decoding error was found, validation could be checked because message could be enough for the application", ANNA_FILE_LOCATION);
374       );
375       break;
376     }
377
378     addChild(avp);
379     avpPos += 4 * REQUIRED_WORDS(avp->getLength());
380   }
381
382   // Post-Fixing
383   Engine::FixMode::_v fmode = getEngine()->getFixMode();
384
385   if((fmode == Engine::FixMode::AfterDecoding) || (fmode == Engine::FixMode::Always)) fix();
386
387   // Trace
388   LOGDEBUG(
389     std::string trace = "Message decoded:\n";
390     trace += asXMLString();
391     anna::Logger::debug(trace, ANNA_FILE_LOCATION);
392   );
393   // Post-Validation
394   Engine::ValidationMode::_v vmode = getEngine()->getValidationMode();
395
396   if((vmode == Engine::ValidationMode::AfterDecoding) || (vmode == Engine::ValidationMode::Always))
397     if(!valid(answer))
398       throw anna::RuntimeException("The decoded message is invalid. See previous report on warning-level traces", ANNA_FILE_LOCATION);
399 }
400
401
402 //------------------------------------------------------------------------------
403 //--------------------------------------------------- Message::getStackCommand()
404 //------------------------------------------------------------------------------
405 const anna::diameter::stack::Command *Message::getStackCommand(CommandId id) const throw(anna::RuntimeException) {
406   const stack::Dictionary * dictionary = getEngine()->getDictionary();
407   return (dictionary ? (dictionary->getCommand(id)) : NULL);
408 }
409
410
411 //------------------------------------------------------------------------------
412 //----------------------------------------------------- Message::setResultCode()
413 //------------------------------------------------------------------------------
414 void Message::setResultCode(int rc) throw(anna::RuntimeException) {
415   if(isRequest()) return;
416
417   // Add Result-Code if not yet added. Even if validation depth is set to 'Complete',
418   // the Result-Code value will be the first found during the message analysis:
419   Avp *resultCodeAvp = getAvp(helpers::base::AVPID__Result_Code, 1, anna::Exception::Mode::Ignore);
420
421   if(!resultCodeAvp)
422     addAvp(helpers::base::AVPID__Result_Code)->getUnsigned32()->setValue(rc);
423   else if(resultCodeAvp->getUnsigned32()->getValue() == helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS)  // Only success could be replaced
424     resultCodeAvp->getUnsigned32()->setValue(rc);
425
426   // Error bit:
427   setErrorBit(rc >= 3001 && rc <= 3010 /* protocol errors */);
428 }
429
430
431 //------------------------------------------------------------------------------
432 //----------------------------------------------------- Message::getResultCode()
433 //------------------------------------------------------------------------------
434 int Message::getResultCode() const throw() {
435   if(isAnswer()) {
436     const Avp *resultCodeAvp = getAvp(helpers::base::AVPID__Result_Code, 1, anna::Exception::Mode::Ignore);
437
438     if(resultCodeAvp)
439       return resultCodeAvp->getUnsigned32()->getValue();
440   }
441
442   return -1;
443 }
444
445
446 //------------------------------------------------------------------------------
447 //------------------------------------------------------ Message::setFailedAvp()
448 //------------------------------------------------------------------------------
449 void Message::setFailedAvp(const parent_t &parent, AvpId wrong, const char *wrongName) throw(anna::RuntimeException) {
450
451   if(isRequest()) return;
452
453 // RFC 6733:
454 //
455 //  7.5.  Failed-AVP AVP
456 //
457 //     The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
458 //     debugging information in cases where a request is rejected or not
459 //     fully processed due to erroneous information in a specific AVP.  The
460 //     value of the Result-Code AVP will provide information on the reason
461 //     for the Failed-AVP AVP.  A Diameter answer message SHOULD contain an
462 //     instance of the Failed-AVP AVP that corresponds to the error
463 //     indicated by the Result-Code AVP.  For practical purposes, this
464 //     Failed-AVP would typically refer to the first AVP processing error
465 //     that a Diameter node encounters.
466
467   // Although the Failed-AVP definition has cardinality 1* and Failed-AVP itself is defined in
468   // most of the command codes as *[Failed-AVP], i think this is not a deliberate ambiguity.
469   // Probably the RFC wants to give freedom to the application layer, but it is recommended to
470   // have only one child (wrong avp) inside a unique message Failed-AVP to ease the Result-Code
471   // correspondence. Anyway, this behaviour could be easily  opened commenting condition block (*).
472   Avp *theFailedAvp = getAvp(helpers::base::AVPID__Failed_AVP, 1, anna::Exception::Mode::Ignore);
473   if (theFailedAvp) {
474         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));
475     return;
476   }
477
478   // Section 7.5 RFC 6733: A Diameter message SHOULD contain one Failed-AVP AVP
479   theFailedAvp = addAvp(helpers::base::AVPID__Failed_AVP);
480   Avp *leaf = theFailedAvp;
481
482   LOGDEBUG(
483     std::string msg = "Adding to Failed-AVP, the wrong avp ";
484     msg += wrongName ? wrongName : (anna::diameter::functions::avpIdAsPairString(wrong));
485     msg += " found inside ";
486     msg += parent.asString();
487
488     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
489   );
490
491   std::vector<AvpId>::const_iterator it;
492   for(it = parent.AvpsId.begin(); it != parent.AvpsId.end(); it++)
493         leaf = leaf->addAvp(*it);
494
495   leaf->addAvp(wrong);
496 }
497
498
499 //------------------------------------------------------------------------------
500 //----------------------------------------------- Message::setStandardToAnswer()
501 //------------------------------------------------------------------------------
502 void Message::setStandardToAnswer(const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode) throw() {
503   if(!request.getId().second) return;
504
505   // Message header:
506   setHeaderToAnswer(request);
507   // Session-Id if exists:
508   const Avp *reqSessionId = request.getAvp(helpers::base::AVPID__Session_Id, 1, anna::Exception::Mode::Ignore);
509
510   if(reqSessionId)
511         if(!getAvp(helpers::base::AVPID__Session_Id, 1, anna::Exception::Mode::Ignore))
512       addAvp(helpers::base::AVPID__Session_Id)->getUTF8String()->setValue(reqSessionId->getUTF8String()->getValue());
513
514   // Origin-Host & Realm
515   if(!getAvp(helpers::base::AVPID__Origin_Host, 1, anna::Exception::Mode::Ignore))
516     addAvp(helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->setValue(originHost);
517
518   if(!getAvp(helpers::base::AVPID__Origin_Realm, 1, anna::Exception::Mode::Ignore))
519     addAvp(helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->setValue(originRealm);
520
521   // Proxy-Info AVPs if exist, in the same order:
522   for(const_avp_iterator it = request.avp_begin(); it != request.avp_end(); it++)
523     if((*it).second->getId() == helpers::base::AVPID__Proxy_Info)
524       addAvp((*it).second);
525
526   // If no Result-Code was added is because all was ok, then application could detect another problem:
527   setResultCode(resultCode);
528   // Fix:
529   fix();
530   LOGDEBUG(
531     std::string msg = "Completed answer:\n";
532     msg += asXMLString();
533     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
534   );
535 }
536
537
538 //------------------------------------------------------------------------------
539 //--------------------------------------------------------------- Message::fix()
540 //------------------------------------------------------------------------------
541 void Message::fix() throw() {
542   // Dictionary stack command:
543   const stack::Command *stackCommand = getStackCommand();
544
545   if(!stackCommand) {
546     LOGDEBUG(anna::Logger::debug("No dictionary command reference found. Cannot fix.", ANNA_FILE_LOCATION));
547     // Try to get to the first place the Session-ID (if exists) because...
548     // RFC 6733: All messages pertaining to a specific session MUST include only one Session-Id AVP ...
549     //           ... When present, the Session-Id SHOULD appear immediately following the Diameter header
550     avp_iterator it = Avp::avp_find(a_avps, helpers::base::AVPID__Session_Id, 1 /* one and only */);
551
552     if(it != avp_end()) {
553       int sessionPos = (*it).first;
554       Avp *first = (*avp_begin()).second;
555       a_avps[0] = (*it).second; // Session-Id
556       a_avps[sessionPos] = first;
557       LOGDEBUG(anna::Logger::debug("Session-Id has been manually fixed to the first place", ANNA_FILE_LOCATION));
558     }
559
560     return;
561   }
562
563   Avp::fix(a_avps, (find_container&)a_finds, a_insertionPositionForChilds, stackCommand->avprule_begin(), stackCommand->avprule_end());
564 }
565
566
567 //------------------------------------------------------------------------------
568 //------------------------------------------------------------- Message::valid()
569 //------------------------------------------------------------------------------
570 bool Message::valid(Message *ptrAnswer) const throw(anna::RuntimeException) {
571   // OAM
572   OamModule &oamModule = OamModule::instantiate();
573   // Dictionary stack command:
574   const stack::Command *stackCommand = getStackCommand();
575   // Only build answer for a request:
576   Message *answer = isRequest() ? ptrAnswer : NULL;
577
578   if(answer) answer->setHeaderToAnswer(*this);
579
580   if(!stackCommand) {
581     // OAM
582     std::string me = anna::diameter::functions::commandIdAsPairString(a_id);
583     oamModule.activateAlarm(OamModule::Alarm::MessageValidation__UnknownOperation__s__UnableToValidate, STRING_WITH_QUOTATION_MARKS__C_STR(me));
584     oamModule.count(OamModule::Counter::MessageValidation__UnknownOperationUnableToValidate);
585
586     if(answer) answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_COMMAND_UNSUPPORTED);
587
588     getEngine()->validationAnomaly(anna::functions::asString("Unknown operation %s. Unable to validate", STRING_WITH_QUOTATION_MARKS__C_STR(me)));
589     return false;
590   }
591
592   // Parent information:
593   parent_t me;
594   me.setMessage(a_id, stackCommand->getName().c_str());
595
596   //////////////////////////////
597   // Flags coherence checking //
598   //////////////////////////////
599   int rc;
600   bool result = flagsOK(rc);
601
602   if(!result) {
603     // OAM & Depth management
604     oamModule.activateAlarm(OamModule::Alarm::MessageValidation__Operation__s__HaveIncoherentFlags__d__, STRING_WITH_QUOTATION_MARKS__C_STR(me.asString()), (int)a_flags);
605     oamModule.count(OamModule::Counter::MessageValidation__OperationHaveIncoherentFlags);
606
607     if(answer) answer->setResultCode(rc);
608
609     getEngine()->validationAnomaly(anna::functions::asString("Operation %s have incoherent flags (%d)", STRING_WITH_QUOTATION_MARKS__C_STR(me.asString()), (int)a_flags));
610   }
611
612   ////////////////////
613   // Level checking //
614   ////////////////////
615   result = Avp::validLevel(a_avps, stackCommand->avprule_begin(), stackCommand->avprule_end(), getEngine(), me, answer) && result;
616
617   ////////////////////////
618   // Childrens checking //
619   ////////////////////////
620   for(const_avp_iterator it = avp_begin(); it != avp_end(); it++)
621     result = ((*it).second->valid(me, answer)) && result;
622
623   return result;
624 }
625
626
627 //------------------------------------------------------------------------------
628 //-------------------------------------------------------------- Message::code()
629 //------------------------------------------------------------------------------
630 const anna::DataBlock & Message::code() throw(anna::RuntimeException) {
631   // Pre-Validation
632   Engine::ValidationMode::_v vmode = getEngine()->getValidationMode();
633
634   if((vmode == Engine::ValidationMode::BeforeEncoding) || (vmode == Engine::ValidationMode::Always)) {
635     if(!valid())
636       throw anna::RuntimeException("Try to encode an invalid message. See previous report on warning-level traces", ANNA_FILE_LOCATION);
637   }
638
639   // Pre-Fixing
640   Engine::FixMode::_v fmode = getEngine()->getFixMode();
641
642   if((fmode == Engine::FixMode::BeforeEncoding) || (fmode == Engine::FixMode::Always)) fix();
643
644   // Trace
645   LOGDEBUG(
646     std::string trace = "Message to code:\n";
647     trace += asXMLString();
648     anna::Logger::debug(trace, ANNA_FILE_LOCATION);
649   );
650   // Memory allocation
651   U24 length = getLength();
652   a_forCode.clear();
653   a_forCode.allocate(length);
654   char* buffer = (char*)a_forCode.getData();
655   // Version and length
656   buffer[0] = (S8) a_version;
657   buffer[1] = (S8)(length >> 16);
658   buffer[2] = (S8)(length >> 8);
659   buffer[3] = (S8) length;
660   // Flags and code
661   buffer[4] = (S8) a_flags;
662   buffer[5] = (S8)(a_id.first >> 16);
663   buffer[6] = (S8)(a_id.first >> 8);
664   buffer[7] = (S8) a_id.first;
665   // Application Id
666   buffer[8] = (S8)(a_applicationId >> 24);
667   buffer[9] = (S8)(a_applicationId >> 16);
668   buffer[10] = (S8)(a_applicationId >> 8);
669   buffer[11] = (S8) a_applicationId;
670   // Hob by hop
671   buffer[12] = (S8)(a_hopByHop >> 24);
672   buffer[13] = (S8)(a_hopByHop >> 16);
673   buffer[14] = (S8)(a_hopByHop >> 8);
674   buffer[15] = (S8) a_hopByHop;
675   // End to end
676   buffer[16] = (S8)(a_endToEnd >> 24);
677   buffer[17] = (S8)(a_endToEnd >> 16);
678   buffer[18] = (S8)(a_endToEnd >> 8);
679   buffer[19] = (S8) a_endToEnd;
680   // Data start position:
681   int startDataPos = HeaderLength;
682
683   if(startDataPos == length) {
684     LOGDEBUG(anna::Logger::debug("There is no Avps to encode (only-header message)", ANNA_FILE_LOCATION));
685   } else {
686     // Data part:
687     S8 * dataPart = buffer + startDataPos;
688     int dataBytes; // not used but could be useful for checking (length - startDataPos)
689     // Each avp encoding will remain padding octets depending on format. In order to
690     //  code avps colection (each avp will be multiple of 4) we clean the entire buffer
691     //  to ensure padding (easier than custom-made for each format):
692     memset(dataPart, 0, length - startDataPos); // no estoy seguro de que el clear del DataBlock haga esto...
693
694     for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) {
695       Avp::avp(it)->code(dataPart, dataBytes);
696       dataPart = dataPart + 4 * REQUIRED_WORDS(dataBytes);
697     }
698   }
699
700   // Trace
701   LOGDEBUG(
702     std::string trace = "DataBlock encoded:\n";
703     trace += a_forCode.asString();
704 //      trace += "\nAs continuous hexadecimal string:\n";
705 //      trace += anna::functions::asHexString(a_forCode);
706     anna::Logger::debug(trace, ANNA_FILE_LOCATION);
707   );
708   return a_forCode;
709 }
710
711
712 //------------------------------------------------------------------------------
713 //----------------------------------------------------- Message::fromXMLString()
714 //------------------------------------------------------------------------------
715 void Message::fromXMLString(const std::string &xmlString) throw(anna::RuntimeException) {
716   LOGDEBUG(anna::Logger::debug("Reading diameter message from xml string representation", ANNA_FILE_LOCATION));
717   anna::xml::DocumentMemory xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
718   const anna::xml::Node *rootNode;
719   xmlDocument.initialize(xmlString.c_str());
720   rootNode = xmlDocument.parse(getEngine()->getDTD()); // Parsing: fail here if xml violates dtd
721   LOGDEBUG(anna::Logger::debug("Read OK from XML string representation", ANNA_FILE_LOCATION));
722   fromXML(rootNode);
723 }
724
725
726 //------------------------------------------------------------------------------
727 //----------------------------------------------------------- Message::fromXML()
728 //------------------------------------------------------------------------------
729 void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeException) {
730   // <!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>
731   const anna::xml::Attribute *version, *name, *code, *flags, *pbit, *ebit, *tbit, *appid, *hbh, *ete;
732   version = messageNode->getAttribute("version", false /* no exception */);
733   name = messageNode->getAttribute("name", false /* no exception */);
734   code = messageNode->getAttribute("code", false /* no exception */);
735   flags = messageNode->getAttribute("flags", false /* no exception */);
736   pbit = messageNode->getAttribute("p-bit", false /* no exception */);
737   ebit = messageNode->getAttribute("e-bit", false /* no exception */);
738   tbit = messageNode->getAttribute("t-bit", false /* no exception */);
739   appid = messageNode->getAttribute("application-id"); // required
740   hbh = messageNode->getAttribute("hop-by-hop-id", false /* no exception */);
741   ete = messageNode->getAttribute("end-by-end-id", false /* no exception */);
742   int i_aux;
743   unsigned int u_aux;
744
745   // Clear the message
746   clear();
747
748   if(version) {
749     i_aux = version->getIntegerValue();
750
751     if(i_aux < 0 || i_aux > 256) {
752       std::string msg = "Error processing avp <version '"; msg += version->getValue();
753       msg += "': out of range [0,256]";
754       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
755     }
756
757     a_version = i_aux;
758   }
759
760   // Application-id
761   // This is called before any operation which needs to know about the stack elements (this could set the dictionary)
762   setApplicationId(appid->getIntegerValue());
763
764   // Dictionary
765   const stack::Dictionary * dictionary = getEngine()->getDictionary();
766   const stack::Command *stackCommand = NULL;
767
768   // Compact mode
769   if(name) {
770     if(!dictionary) {
771       std::string msg = "Error processing command <name '"; msg += name->getValue();
772       msg += "'>: no dictionary available. Load one or use <'code' + 'flags'> instead of <'name'>";
773       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
774     }
775
776     stackCommand = dictionary->getCommand(name->getValue());
777
778     if(!stackCommand) {
779       std::string msg = "Error processing command <name '"; msg += name->getValue();
780       msg += "'>: no command found for this identifier at available dictionary";
781       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
782     }
783   }
784
785   // Check attributes exclusiveness
786   if(name) {  // compact mode
787     if(code || flags) {
788       std::string msg = "Error processing command <name '"; msg += name->getValue();
789       msg += "'>: message attributes <'code' + 'flags'> are not allowed if <'name'> is provided";
790       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
791     }
792
793     setId(stackCommand->getId(), false /* don't clear */);
794     // 'P', 'E' and 'T' flags:
795     bool activateP = pbit ? (pbit->getValue() == "yes") : false;
796     bool activateE = ebit ? (ebit->getValue() == "yes") : false;
797     bool activateT = tbit ? (tbit->getValue() == "yes") : false;
798
799     if(activateP) a_flags |= PBitMask;
800
801     if(activateE) a_flags |= EBitMask;
802
803     if(activateT) a_flags |= TBitMask;
804   } else {
805     std::string s_code = code ? code->getValue() : "?";
806     std::string s_flags = flags ? flags->getValue() : "?";
807
808     if(!code || !flags) {
809       std::string msg = "Error processing command <code '"; msg += s_code;
810       msg += "' + flags '"; msg += s_flags;
811       msg += "'>: both must be provided if <'name'> don't";
812       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
813     }
814
815     if(pbit || ebit || tbit) {
816       std::string msg = "Error processing command <code '"; msg += s_code;
817       msg += "' + flags '"; msg += s_flags;
818       msg += "'>: neither of 'p-bit', 'e-bit' or 't-bit' fields are allowed if those are present";
819       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
820     }
821
822     // Code check
823     i_aux = code->getIntegerValue();
824
825     if(i_aux < 0) {
826       std::string msg = "Error processing command <code '"; msg += s_code;
827       msg += "': negative values are not allowed";
828       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
829     }
830
831     U24 u_code = i_aux;
832     // Flags check
833     i_aux = flags->getIntegerValue();
834
835     if(i_aux < 0 || i_aux > 256) {
836       std::string msg = "Error processing command <flags '"; msg += s_flags;
837       msg += "': out of range [0,256]";
838       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
839     }
840
841     a_flags = i_aux;
842     int flagsBCK = a_flags;
843     // Final assignments
844     a_id = CommandId(u_code, requestBit() /* based on a_flags */);
845     // Flags could have been updated regarding dictionary, but during parsing we must respect xml file:
846     a_flags = flagsBCK;
847   }
848
849   // Hob-by-hop-id
850   if(hbh) {
851     u_aux = hbh->getIntegerValue();
852
853     /*
854     if(u_aux < 0) {
855       std::string msg = "Error processing command <hop-by-hop-id '"; msg += hbh->getValue();
856       msg += "': negative values are not allowed";
857       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
858     }
859     */
860   } else u_aux = 0;
861
862   setHopByHop(u_aux);
863
864   // End-to-end-id
865   if(ete) {
866     u_aux = ete->getIntegerValue();
867
868     /*
869     if(u_aux < 0) {
870       std::string msg = "Error processing command <end-to-end-id '"; msg += ete->getValue();
871       msg += "': negative values are not allowed";
872       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
873     }
874     */
875   } else u_aux = 0;
876
877   setEndToEnd(u_aux);
878   // Childrens
879   Avp *avp;
880
881   for(anna::xml::Node::const_child_iterator it = messageNode->child_begin(); it != messageNode->child_end(); it++) {
882     std::string nodeName = (*it)->getName();
883
884     if(nodeName != "avp") {
885       std::string msg = "Unsupported message child node name '"; msg += nodeName;
886       msg += "': use 'avp'";
887       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
888     }
889
890     try {
891       avp =  getEngine()->allocateAvp();
892       avp -> fromXML(*it);
893     } catch(anna::RuntimeException &ex) {
894       getEngine()->releaseAvp(avp);
895       throw ex;
896     }
897
898     addAvp(avp);
899   }
900 }
901
902
903 //------------------------------------------------------------------------------
904 //----------------------------------------------------------- Message::loadXML()
905 //------------------------------------------------------------------------------
906 void Message::loadXML(const std::string & xmlPathFile) throw(anna::RuntimeException) {
907   LOGDEBUG(
908     std::string trace = "Loading diameter message from file '";
909     trace += xmlPathFile;
910     trace += "'";
911     anna::Logger::debug(trace, ANNA_FILE_LOCATION);
912   );
913   anna::xml::DocumentFile xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
914   const anna::xml::Node *rootNode;
915   xmlDocument.initialize(xmlPathFile.c_str()); // fail here is i/o error
916   rootNode = xmlDocument.parse(getEngine()->getDTD()); // Parsing: fail here if xml violates dtd
917   LOGDEBUG(
918     std::string trace = "Loaded XML file (";
919     trace += xmlPathFile;
920     trace += "):\n";
921     trace += anna::xml::Compiler().apply(rootNode);
922     anna::Logger::debug(trace, ANNA_FILE_LOCATION);
923   );
924   fromXML(rootNode);
925 }
926
927
928 //------------------------------------------------------------------------------
929 //------------------------------------------------------------- Message::asXML()
930 //------------------------------------------------------------------------------
931 anna::xml::Node* Message::asXML(anna::xml::Node* parent) const throw() {
932   // <!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>
933   anna::xml::Node* result = parent->createChild("message");
934   // Dictionary stack command:
935   const stack::Command *stackCommand = getStackCommand();
936   bool compactMode = stackCommand /*&& flagsOK()*/;
937   result->createAttribute("version", anna::functions::asString((int)a_version));
938
939   if(compactMode) {
940     result->createAttribute("name", stackCommand->getName());
941
942     if(proxiableBit()) result->createAttribute("p-bit", "yes");
943
944     if(errorBit()) result->createAttribute("e-bit", "yes");
945
946     if(potentiallyReTransmittedMessageBit()) result->createAttribute("t-bit", "yes");
947   } else {
948     result->createAttribute("code", a_id.first);
949     result->createAttribute("flags", (int)a_flags);
950   }
951
952   result->createAttribute("application-id", anna::functions::asString(a_applicationId));
953   result->createAttribute("hop-by-hop-id", anna::functions::asString(a_hopByHop));
954   result->createAttribute("end-by-end-id", anna::functions::asString(a_endToEnd));
955
956   // Avps:
957   for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) {
958     Avp::avp(it)->asXML(result);
959   }
960
961   return result;
962 }
963
964
965 //------------------------------------------------------------------------------
966 //------------------------------------------------------- Message::asXMLString()
967 //------------------------------------------------------------------------------
968 std::string Message::asXMLString() const throw() {
969   anna::xml::Node root("root");
970   return anna::xml::Compiler().apply(asXML(&root));
971 }
972
973
974 //------------------------------------------------------------------------------
975 //------------------------------------------------------------ Message::isLike()
976 //------------------------------------------------------------------------------
977 bool Message::isLike(const std::string &pattern) const throw() {
978   anna::RegularExpression re(pattern);
979   return re.isLike(asXMLString());
980 }