Base protocol codec for comm::Engine. Supported retransmissions
authorEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Mon, 13 Jul 2015 02:34:30 +0000 (04:34 +0200)
committerEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Mon, 13 Jul 2015 02:34:30 +0000 (04:34 +0200)
28 files changed:
example/diameter/launcher/DEPLOY_clientAndServer.sh [new file with mode: 0755]
example/diameter/launcher/Launcher.cpp
example/diameter/launcher/Launcher.hpp
example/diameter/launcher/MyDiameterEngine.hpp
example/diameter/launcher/MyDiameterEntity.cpp
example/diameter/launcher/MyLocalServer.cpp
include/anna/diameter.comm/ClientSession.hpp
include/anna/diameter.comm/Engine.hpp
include/anna/diameter.comm/Entity.hpp
include/anna/diameter.comm/LocalServer.hpp
include/anna/diameter.comm/Message.hpp
include/anna/diameter.comm/Server.hpp
include/anna/diameter.comm/ServerSession.hpp
include/anna/diameter.comm/Session.hpp
include/anna/diameter/app/dcca/Message.hpp
include/anna/diameter/codec/EngineImpl.hpp
include/anna/diameter/codec/Message.hpp
include/anna/diameter/codec/functions.hpp
source/diameter.comm/ClientSession.cpp
source/diameter.comm/Engine.cpp
source/diameter.comm/Entity.cpp
source/diameter.comm/LocalServer.cpp
source/diameter.comm/Message.cpp
source/diameter.comm/Server.cpp
source/diameter.comm/ServerSession.cpp
source/diameter.comm/Session.cpp
source/diameter/codec/Message.cpp
source/diameter/codec/functions.cpp

diff --git a/example/diameter/launcher/DEPLOY_clientAndServer.sh b/example/diameter/launcher/DEPLOY_clientAndServer.sh
new file mode 100755 (executable)
index 0000000..c8bf3e6
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+#############
+# VARIABLES #
+#############
+DEPLOY_SCR=`dirname $0`/DEPLOY.sh
+
+#############
+# FUNCTIONS #
+#############
+usage() {
+  echo
+  echo "Usage: $0 <empty directory>"
+  echo
+  exit 1
+}
+
+#############
+# EXECUTION #
+#############
+DIR=$1
+[ "$DIR" = "" ] && usage
+[ -d $DIR ] && usage
+
+$DEPLOY_SCR b $DIR/client
+$DEPLOY_SCR b $DIR/server
+
+cd $DIR/client
+echo c | ./configure.sh
+
+cd - >/dev/null
+
+cd $DIR/server
+echo s | ./configure.sh
+
+echo
+echo "Done!"
+echo
+
index 710ebfe..05422f4 100644 (file)
@@ -58,7 +58,14 @@ void Launcher::releaseCommMessage(anna::diameter::comm::Message *msg) throw() {
   a_commMessages.release(msg);
 }
 
-void Launcher::baseProtocolSetupAsClient(anna::diameter::codec::Engine *codecEngine) throw(anna::RuntimeException) {
+void Launcher::baseProtocolSetupAsClient() throw(anna::RuntimeException) {
+
+  anna::diameter::codec::Engine *codecEngine;
+
+  codecEngine = getCodecEngine();
+  // XXXXXXXXXXXXXXXXXXXX codecEngine = a_myDiameterEngine->getBaseProtocolCodecEngine();
+
+
   // Build CER
   //   <CER> ::= < Diameter Header: 257, REQ >
   //             { Origin-Host } 264 diameterIdentity
@@ -339,7 +346,7 @@ throw(anna::RuntimeException) {
   std::string dictionaryParameter = cl.getValue("dictionary");
   lst.apply(dictionaryParameter, ",");
 
-  if(lst.size() >= 1) {  // always true (at least one, because -dictionary is mandatory)
+  if(lst.size() >= 1) {  // always true (at least one, because --dictionary is mandatory)
     anna::Tokenizer::const_iterator tok_min(lst.begin());
     anna::Tokenizer::const_iterator tok_max(lst.end());
     anna::Tokenizer::const_iterator tok_iter;
@@ -517,6 +524,10 @@ throw(anna::RuntimeException) {
 
   getCodecEngine()->ignoreFlagsOnValidation(cl.exists("ignoreFlags")); // XXXXXXXXXXXXXXXXXXXXXXX
 
+  // Base protocol for internal use (CEA, DWA, DPA and tracing:
+  a_myDiameterEngine->setBaseProtocolCodecEngine(getCodecEngine());
+
+
   // Diameter Server:
   if(cl.exists("diameterServer"))
     startDiameterServer(cl.exists("diameterServerSessions") ? cl.getIntegerValue("diameterServerSessions") : 1);
@@ -566,7 +577,7 @@ throw(anna::RuntimeException) {
     int entityServerSessions = cl.exists("entityServerSessions") ? cl.getIntegerValue("entityServerSessions") : 1;
 
     if(entityServerSessions > 0) {
-      baseProtocolSetupAsClient(getCodecEngine()); // Same CER/CEA, DWR/DWA for all diameter servers XXXXXXXXXXXXXXXXXXXXXXXXX
+      baseProtocolSetupAsClient();
       anna::socket_v servers = anna::functions::getSocketVectorFromString(cl.getValue("entity"));
       a_myDiameterEngine->setNumberOfClientSessionsPerServer(entityServerSessions);
       a_entity = (MyDiameterEntity*)(a_myDiameterEngine->createEntity(servers, "Launcher diameter entity"));
@@ -1303,6 +1314,9 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
 
     if(!entity) throw anna::RuntimeException("No entity configured to send the message", ANNA_FILE_LOCATION);
     anna::diameter::comm::Message *msg = createCommMessage();
+//xxxxxxxxxxxxx
+    msg->setRetries(4);
+    msg->setOnExpiry(anna::diameter::comm::Message::OnExpiry::Retransmit);
 
     if((opType == "sendxml") || (opType == "sendxml2e")) {
       codecMsg.loadXML(param1);
@@ -1635,10 +1649,12 @@ int MyDiameterEntity::readSocketId(const anna::diameter::comm::Message* message,
 
       if(sessionBasedModelsType == "SessionIdOptionalPart") return (atoi(optional.c_str()) % maxClientSessions);
     }
-    //case anna::diameter::helpers::dcca::ChargingContext::SMS:
-    //case anna::diameter::helpers::dcca::ChargingContext::MMS:
-    //default:
-    //   return -1; // IEC model and Unknown traffic types
+
+    case anna::diameter::helpers::dcca::ChargingContext::SMS:
+    case anna::diameter::helpers::dcca::ChargingContext::MMS:
+    case anna::diameter::helpers::dcca::ChargingContext::Unknown:
+    default:
+       return -1;
     }
   } catch(anna::RuntimeException &ex) {
     LOGDEBUG(
index b1bf52a..d7e4bba 100644 (file)
@@ -78,7 +78,7 @@ public:
   anna::diameter::codec::Engine *getCodecEngine() const throw() { return a_codecEngine; } // XXXXXXXXXXXXXXXXX El del nodo de trabajo
   MyCommunicator *getCommunicator() throw() { return a_communicator; }
   MyDiameterEngine* getMyDiameterEngine() const throw() { return (a_myDiameterEngine); }
-  void baseProtocolSetupAsClient(anna::diameter::codec::Engine *codecEngine) throw(anna::RuntimeException);
+  void baseProtocolSetupAsClient() throw(anna::RuntimeException);
   MyDiameterEntity *getEntity() throw() { return a_entity; }
   MyLocalServer* getDiameterLocalServer() throw() { return a_diameterLocalServer; }
   void eventOperation(const std::string &, std::string &) throw(anna::RuntimeException);
index 4606edc..13aae6c 100644 (file)
@@ -20,7 +20,7 @@
 class MyDiameterEngine : public anna::diameter::comm::Engine {
 public:
 
-  MyDiameterEngine(const char *className = "MyDiameterEngine") : Engine(className) {;}
+  MyDiameterEngine(const char *className = "MyDiameterEngine") : Engine(className, NULL /* we will assign the base protocol codec engine later*/) {;}
 
 // Default implementation is enough
 //   void readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw() {;} // DPA is not replied
index 702ec56..ae4a3db 100644 (file)
@@ -78,7 +78,7 @@ throw(anna::RuntimeException) {
   if(localServer && (cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) /* don't forward CER */) {
     try {
       anna::diameter::comm::Message *msg = my_app.createCommMessage();
-      msg->updateEndToEnd(false); // end-to-end will be kept
+      msg->forwardEndToEnd(); // end-to-end will be kept
       msg->setBody(message);
       msg->setRequestClientSessionKey(clientSession->getKey());
       bool success = localServer->send(msg);
@@ -159,7 +159,7 @@ throw(anna::RuntimeException) {
     if(localServer && (request_cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) /* don't forward CEA */) {
       try {
         anna::diameter::comm::Message *msg = my_app.createCommMessage();
-        msg->updateEndToEnd(false); // end-to-end will be kept
+        msg->forwardEndToEnd(); // end-to-end will be kept
         msg->setBody(*message);
         bool success = localServer->send(msg, request->getRequestServerSessionKey());
         my_app.releaseCommMessage(msg);
index 91e95f5..599917f 100644 (file)
@@ -48,7 +48,7 @@ throw(anna::RuntimeException) {
   anna::diameter::comm::Entity *entity = my_app.getEntity();
   if(!programmed && entity) {  // forward condition (no programmed answer + entity available)
     anna::diameter::comm::Message *msg = my_app.createCommMessage();
-    msg->updateEndToEnd(false); // end-to-end will be kept
+    msg->forwardEndToEnd(); // end-to-end will be kept
     msg->setBody(message);
     msg->setRequestServerSessionKey(serverSession->getKey());
     bool success = entity->send(msg, cl.exists("balance"));
@@ -179,7 +179,7 @@ throw(anna::RuntimeException) {
 
       try {
         anna::diameter::comm::Message *msg = my_app.createCommMessage();
-        msg->updateEndToEnd(false); // end-to-end will be kept
+        msg->forwardEndToEnd(); // end-to-end will be kept
         msg->setBody(*message);
 
         // Metodo 1:
index 93490cb..0b4b437 100644 (file)
@@ -232,6 +232,13 @@ private:
   */
   void eventPeerShutdown() throw();
 
+  /**
+     Handler about a request retransmission over the session.
+
+     \param request Message retransmitted
+  */
+  void eventRequestRetransmission(Message *request) throw();
+
   /**
      Handler for diameter server (client-session) responses
 
index d1c276e..609c459 100644 (file)
@@ -94,6 +94,27 @@ class LocalServer;
 class Engine : public anna::app::Component {
 public:
 
+  /**
+   * Sets the base protocol codec engine used internally
+   *
+   * @param baseProtocolCodecEngine This will be used internally during invokation of @readCEA, @readDPA and @readDWA on servers,
+   * and also used during base protocol messages tracing (if debug traces are enabled). You could provide NULL but you must be
+   * sure that neither of the former situations are going to happen or an exception will be thrown. It is recommended to register
+   * a codec engine pointed to a base protocol stack (you can use the files 'avps_ietf.xml' and 'commands_baseProtocol.xml'
+   * located on ANNA suite project under 'source/diameter/stack/setups', or perhaps you can create your own dictionary from
+   * file or directly with the dictionay creation API. Even you can use a greater dictionary (application dictionary), the
+   * only condition is that must contain the resources to build base protocol messages. You could provide this in engine constructor,
+   * but don't forget it.
+  */
+  void setBaseProtocolCodecEngine(codec::Engine *baseProtocolCodecEngine) throw() { a_baseProtocolCodecEngine = baseProtocolCodecEngine; }
+
+  /**
+   * Gets the base protocol codec engine used internally
+   *
+   * @see setBaseProtocolCodecEngine
+   */
+  codec::Engine * getBaseProtocolCodecEngine() const throw() { return a_baseProtocolCodecEngine; }
+
   /**
      Diameter application node realm name (used to be the site domain name).
 
@@ -613,9 +634,18 @@ public:
 protected:
   /**
      Constructor.
+
      @param className Component class name
+     @param baseProtocolCodecEngine This will be used internally during invokation of @readCEA, @readDPA and @readDWA on servers,
+     and also used during base protocol messages tracing (if debug traces are enabled). You could provide NULL but you must be
+     sure that neither of the former situations are going to happen or an exception will be thrown. It is recommended to register
+     a codec engine pointed to a base protocol stack (you can use the files 'avps_ietf.xml' and 'commands_baseProtocol.xml'
+     located on ANNA suite project under 'source/diameter/stack/setups', or perhaps you can create your own dictionary from
+     file or directly with the dictionay creation API. Even you can use a greater dictionary (application dictionary), the
+     only condition is that must contain the resources to build base protocol messages. You could use @setBaseProtocolCodecEngine
+     to set this reference later; don't forget it.
   */
-  Engine(const char *className);
+  Engine(const char *className, codec::Engine *baseProtocolCodecEngine);
 
 
   // INTERNAL CREATORS AND CLOSE METHODS
@@ -668,6 +698,9 @@ protected:
 
 private:
 
+  // Internal use: tracing and readCEA/DPA/DWA
+  codec::Engine *a_baseProtocolCodecEngine;
+
   std::string a_realm;
   std::string a_host;
   bool a_autoBind;
index 2335d82..d0da8c5 100644 (file)
@@ -216,7 +216,7 @@ public:
 //   */
 
 // OJO: en el caso estandard, no se prueban todas las sessiones de un servidor si tiene mas de una, luego la alarma
-//      generada en caso de error, presupone que las sessiones no usadas, también darían error, lo cual no tiene porque
+//      generada en caso de error, presupone que las sessiones no usadas, tambi�n dar�an error, lo cual no tiene porque
 //      ser cierto. En condiciones normales, los servidores tienen una session, con lo que lo anterior es cierto y el
 //      la practica es lo mas normal.
 
@@ -421,6 +421,14 @@ protected:
   */
   virtual void eventPeerShutdown(const ClientSession* clientSession) throw();
 
+  /**
+     Handler about a request retransmission over the session.
+     Default implementation traces warning event
+     \param clientSession ClientSession from which retransmission happened
+     \param request Retransmitted request message
+  */
+  virtual void eventRequestRetransmission(const ClientSession* clientSession, Message *request) throw();
+
   /**
      Handler for diameter server (server) responses
 
index f8f0079..9d733d4 100644 (file)
@@ -359,6 +359,14 @@ protected:
   */
   virtual void eventPeerShutdown(const ServerSession* serverSession) throw();
 
+  /**
+     Handler about a request retransmission over the server-session.
+     Default implementation traces warning event
+     \param serverSession ServerSession from which retransmission happened
+     \param request Retransmitted request message
+  */
+  virtual void eventRequestRetransmission(const ServerSession* serverSession, Message *request) throw();
+
   /**
      Handler for diameter client responses
 
index b5cb459..9fc265a 100644 (file)
@@ -48,7 +48,7 @@ public:
   /**
    * Define las acciones a realizar en caso de que el temporizador de la petici�n expire.
    */
-  struct OnExpiry { enum _v { Abandon, Ignore }; };
+  struct OnExpiry { enum _v { Abandon, Ignore, Retransmit }; };
 
   /**
      Constructor.
@@ -112,19 +112,26 @@ public:
   // http://diameter-protocol.blogspot.com.es/2011/05/diameter-message-structure-and-message.html
 
   /**
-   * In general, diameter agents CANNOT modify the end-to-end value during sending the message to the peer.
-   * That 'true' value stands for intermediate agents and also for retransmissions (must keep end-to-end
-   * during 4 minutes even upon reboots). False is used for new request created from end points (originators)
-   * as diameter clients.
+   * In general, diameter nodes will sequence the End-To-End value when sending new requests.
+   * A 'false' value stands for intermediate agents (must keep end-to-end during 4 minutes even upon reboots).
    */
-  bool updateEndToEnd() const throw() { return a_updateEndToEnd; }
+  bool endToEndSequenced() const throw() { return a_endToEndSequenced; }
 
   /**
-   * In general, diameter agents CANNOT modify the end-to-end value during sending the message to the peer.
-   * The appropiate behaviour must be configured before sending the message. By default, the diameter::comm
-   * message will sequence the end-to-end increasing the initial value created during session establishment.
+   * By default, the diameter::comm message will sequence the end-to-end increasing the initial value created
+   * during session establishment. Anyway you could change this behaviour with this method.
+   *
+   * @see sequenceEndToEnd
+   */
+  void forwardEndToEnd() throw() { a_endToEndSequenced = false; }
+
+  /**
+   * By default, the diameter::comm message will sequence the end-to-end increasing the initial value created
+   * during session establishment. Anyway you could set again this behaviour with this method.
+   *
+   * @see forwardEndToEnd
    */
-  void updateEndToEnd(bool update) throw() { a_updateEndToEnd = update; }
+  void sequenceEndToEnd() throw() { a_endToEndSequenced = true; }
 
 
   // Statistics
@@ -147,12 +154,12 @@ public:
 
   /** Initializes class information */
   void initialize() throw() {
-    a_retries = 0;
+    a_retries = 1;
     a_requestServerSessionKey = -1; // means unknown/unset
     a_requestClientSessionKey = ""; // means unknown/unset
     a_requestHopByHop = 0;
     a_requestEndToEnd = 0;
-    a_updateEndToEnd = true;
+    a_endToEndSequenced = true;
   }
 
 
@@ -176,7 +183,7 @@ private:
   std::string a_requestClientSessionKey;    // idem for request which was received from servers
   HopByHop a_requestHopByHop; // application backup for hop-by-hop in order to restore on answer receive
   EndToEnd a_requestEndToEnd; // application backup for end-to-end in order to restore on answer receive
-  bool a_updateEndToEnd; // end-to-end will be updated
+  bool a_endToEndSequenced; // end-to-end will be sequenced by default (true)
 
   void send(ClientSession&) const throw(anna::RuntimeException);
   void send(ServerSession&) const throw(anna::RuntimeException);
index f7c3831..15c29de 100644 (file)
@@ -281,6 +281,13 @@ protected:
   */
   virtual void eventPeerShutdown(const ClientSession*) throw();
 
+  /**
+     Handler about a request retransmission over the session.
+
+     \param request Message retransmitted
+  */
+  virtual void eventRequestRetransmission(const ClientSession*, Message *request) throw();
+
   /**
      Handler for diameter server (client-session) responses
 
index 5581a4b..5b30204 100644 (file)
@@ -169,6 +169,13 @@ private:
   */
   void eventPeerShutdown() throw();
 
+  /**
+     Handler about a request retransmission over the session.
+
+     \param request Message retransmitted
+  */
+  void eventRequestRetransmission(Message *request) throw();
+
   /**
      Handler for diameter client responses
 
index c1b9bbb..b23f8c0 100644 (file)
@@ -87,16 +87,16 @@ public:
 
 
       // Cierre de iniciativa local:
-      // 1. Envio DPR al PCRF y me pongo en estado 'WaitingDPA'. En este estado no habrá keep-alive DWR/DWA.
+      // 1. Envio DPR al PCRF y me pongo en estado 'WaitingDPA'. En este estado no habr keep-alive DWR/DWA.
       // 2. No dejo pasar nuevas peticiones (BLOCK-SEND).
       // 3. Cierro al recibir el DPA.
       // 4. Si expira el DPA, tambien cierro.
       WaitingDPA, /**< After requesting DPR to server, send is blocked over the session: when DPA arrives (or answer expires) the session is closed */
 
       // Cierre de iniciativa remota:
-      // 1. Recibo DPR del PCRF y me pongo en estado 'Disconnecting'. En este estado no habrá keep-alive DWR/DWA.
+      // 1. Recibo DPR del PCRF y me pongo en estado 'Disconnecting'. En este estado no habr keep-alive DWR/DWA.
       // 2. No dejo pasar nuevas peticiones (BLOCK-SEND).
-      // 3. Espero cursar las peticiones pendientes (a más tardar, será una expiracion Tx desde la recepcion del DPR).
+      // 3. Espero cursar las peticiones pendientes (a m�s tardar, ser� una expiracion Tx desde la recepcion del DPR).
       // 4. Envio DPA y activo un temporizador de cierre local (2*Tx) como proteccion (por si el servidor no cierra).
       Disconnecting, /**< After receiving DPR from server, send is blocked over the session: when no pending requests, DPA is sent to the server who will close connection */
 
@@ -190,7 +190,7 @@ public:
 //
 // En caso de enviar una peticion se activara automaticamente un temporizador. Si este llegara a caducar
 // se cancelara la busqueda y se invocara al metodo Session::eventResponse indicado que se ha producido
-// un error de temporización. La duracion del temporizador sera la establecida por
+// un error de temporizacin. La duracion del temporizador sera la establecida por
 // diameter::comm::TimerManager::setTimeout o el valor defecto.
 //
 // \param message Mensaje a enviar al servidor diameter con el que estamos conectados.
@@ -200,8 +200,8 @@ public:
   const Response* send(const Message& message) throw(anna::RuntimeException) { return send(&message); }
 
 // Desconecta del extremo remoto
-// Se notifica la terminación de cada una de las peticiones pendientes invocando al método Session::eventResponse
-// \warning Después de invocar a este método habría que volver a iniciar una sesion.
+// Se notifica la terminaci�n de cada una de las peticiones pendientes invocando al m�todo Session::eventResponse
+// \warning Despu�s de invocar a este m�todo habr�a que volver a iniciar una sesion.
   virtual bool unbind(bool forceDisconnect /* se usa en timer, para el actionTimer del tipo SessionUnbind, etc. */ = false) throw(anna::RuntimeException) = 0;
   // returns true if done at call time (no pendings or ignore pendings, except Disconnecting state by mean DPR/DPA)
 
@@ -328,6 +328,13 @@ protected:
   */
   virtual void eventPeerShutdown() throw() = 0;
 
+  /**
+     Handler about a request retransmission over the session.
+
+     \param request Message retransmitted
+  */
+  virtual void eventRequestRetransmission(Message *request) throw() = 0;
+
   /**
      Handler for diameter session responses
 
@@ -365,8 +372,8 @@ protected:
   */
   virtual void receive(const anna::comm::Message& message) throw(anna::RuntimeException) = 0;
 //PROTOCOL ERRORS
-//The errors at the protocol level are reported in response messages that contain the \93E\94 bit and the error code in the AVP result-Code (various errors having been produced only the first one of them is reported). Examples of these errors are:
-//An unrecognized AVP with the \93M\94 bit is received.
+//The errors at the protocol level are reported in response messages that contain the �E� bit and the error code in the AVP result-Code (various errors having been produced only the first one of them is reported). Examples of these errors are:
+//An unrecognized AVP with the �M� bit is received.
 //An AVP is received with an unrecognized value (in the AVP failed-AVP indicates the attribute that the error caused).
 //An mandatory AVP is not received.
 //Length of operation incorrect.
index a0455d2..006e23a 100644 (file)
@@ -84,7 +84,7 @@ public:
       const anna::diameter::codec::Avp * sid;
       int pos = 1;
 
-      while(sid = getAvp(helpers::dcca::AVPID__Subscription_Id, pos++))
+      while((sid = getAvp(helpers::dcca::AVPID__Subscription_Id, pos++)))
       if(subscriptionIdType == sid->getAvp(helpers::dcca::AVPID__Subscription_Id_Type)->getEnumerated()->getValue())
         return sid->getAvp(helpers::dcca::AVPID__Subscription_Id_Data)->getUTF8String();
         return NULL;
@@ -230,7 +230,7 @@ public:
       const anna::diameter::codec::Avp * uei;
       int pos = 1;
 
-      while(uei = getAvp(helpers::dcca::AVPID__User_Equipment_Info, pos++))
+      while((uei = getAvp(helpers::dcca::AVPID__User_Equipment_Info, pos++)))
       if(userEquipmentInfoType == uei->getAvp(helpers::dcca::AVPID__User_Equipment_Info_Type)->getEnumerated()->getValue())
         return uei->getAvp(helpers::dcca::AVPID__User_Equipment_Info_Value)->getOctetString();
         return NULL;
index 753d15e..bb7c6f5 100644 (file)
@@ -238,6 +238,7 @@ public:
   *
   * @warning do not activate in case of multithreaded applications.
   * @param enable Activates/deactivates the stack selection from the Application-Id value within the message header.
+  *               False by default.
   */
   void selectStackWithApplicationId (bool enable) throw() { a_selectStackWithApplicationId = enable; }
 
@@ -247,7 +248,7 @@ public:
     Gets the currently configured behaviour regarding stack selection for multistack codec engines in mono thread
     applications.
 
-    @return True if selection is done with the Application-Id. False if no selection is performed (user responsibility).
+    @return True if selection is done with the Application-Id. False (default) if no selection is performed (user responsibility).
   */
   bool hasSelectStackWithApplicationId (void) throw() { return a_selectStackWithApplicationId; }
 
index 449a49a..6ec6e3a 100644 (file)
@@ -272,7 +272,7 @@ public:
 
      @param aid Application-id.
   */
-  void setApplicationId(U32 aid) throw();
+  void setApplicationId(U32 aid) throw(anna::RuntimeException);
 
   /**
      Sets the message hop-by-hop
index df544e0..770fcc1 100644 (file)
@@ -68,13 +68,19 @@ struct functions {
   static HopByHop getHopByHop(const anna::DataBlock &) throw(anna::RuntimeException);
   static EndToEnd getEndToEnd(const anna::DataBlock &) throw(anna::RuntimeException);
 
+  static bool requestBit(const anna::DataBlock &) throw(anna::RuntimeException);
+  static bool proxiableBit(const anna::DataBlock &) throw(anna::RuntimeException);
+  static bool errorBit(const anna::DataBlock &) throw(anna::RuntimeException);
+  static bool potentiallyReTransmittedMessageBit(const anna::DataBlock &) throw(anna::RuntimeException);
+
   static bool isRequest(const CommandId & cid) throw() { return (cid.second); }
-  static bool isRequest(const anna::DataBlock &) throw(anna::RuntimeException);
+  static bool isRequest(const anna::DataBlock & db) throw(anna::RuntimeException) { return requestBit(db); }
 
   static bool isAnswer(const CommandId & cid) throw() { return (!isRequest(cid)); }
   static bool isAnswer(const anna::DataBlock & db) throw(anna::RuntimeException) { return (!isRequest(db)); }
 
 
+
   /**
   * Decodes a Command Header. This helper cannot check boundaries. start pointer must be a valid command context.
   *
@@ -127,6 +133,7 @@ struct functions {
   // modifiers
   static void setHopByHop(anna::DataBlock &, HopByHop) throw(anna::RuntimeException);
   static void setEndToEnd(anna::DataBlock &, EndToEnd) throw(anna::RuntimeException);
+  static void setPotentiallyReTransmittedMessageBit(const anna::DataBlock & db, bool activate = true) throw(anna::RuntimeException);
 };
 
 
index 7974ae7..5a7ca94 100644 (file)
 #include <stdlib.h> // rand()
 #include <time.h>
 
+// XXXXXXXXXX
+#include <iostream>
+
+
 
 
 using namespace std;
@@ -302,12 +306,18 @@ const Response* ClientSession::send(const Message* message) throw(anna::RuntimeE
     countSendings(cid, aid, true /* send ok */);
     // Trace non-application messages:
     LOGDEBUG(
-
       if((cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) ||
          (cid == helpers::base::COMMANDID__Device_Watchdog_Request) ||
-    (cid == helpers::base::COMMANDID__Disconnect_Peer_Request)) {
-    anna::Logger::debug("Sent DataBlock to XML representation:", ANNA_FILE_LOCATION);
-      try { anna::diameter::codec::Message msg; msg.decode(message->getBody()); /* decode to be traced */ } catch(anna::RuntimeException&) {;}
+         (cid == helpers::base::COMMANDID__Disconnect_Peer_Request)) {
+      anna::Logger::debug("Sent DataBlock to XML representation:", ANNA_FILE_LOCATION);
+      try {
+        anna::diameter::codec::Message msg(a_engine->getBaseProtocolCodecEngine()); msg.decode(message->getBody()); /* decode to be traced */
+      }
+      catch(anna::RuntimeException &ex) {
+        std::string msg = ex.getText();
+        msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages full tracing";
+        anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+      }
     }
     );
 
@@ -442,6 +452,11 @@ void ClientSession::eventPeerShutdown() throw() {
   a_parent->eventPeerShutdown(this);
 }
 
+void ClientSession::eventRequestRetransmission(Message *request) throw() {
+  // Inform father server:
+  a_parent->eventRequestRetransmission(this, request);
+}
+
 void ClientSession::eventResponse(const Response& response) throw(anna::RuntimeException) {
   // Inform father server:
   a_parent->eventResponse(response);
@@ -482,8 +497,17 @@ throw(anna::RuntimeException) {
     msg += anna::diameter::functions::commandIdAsPairString(cid);
     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
 
-    if((cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) || (cid.first == helpers::base::COMMANDID__Device_Watchdog_Request.first))
-  try { anna::diameter::codec::Message dmsg; dmsg.decode(db); /* decode to be traced */ } catch(anna::RuntimeException&) {;}
+    if( (cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) ||
+        (cid.first == helpers::base::COMMANDID__Device_Watchdog_Request.first)) {
+      try {
+        anna::diameter::codec::Message dmsg(a_engine->getBaseProtocolCodecEngine()); dmsg.decode(db); /* decode to be traced */
+      }
+      catch(anna::RuntimeException &ex) {
+        std::string msg = ex.getText();
+        msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages full tracing";
+        anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+      }
+    }
 );
   // Main counters:
   OamModule &oamModule = OamModule::instantiate();
index 53de6ac..4028991 100644 (file)
@@ -37,8 +37,9 @@ using namespace std;
 using namespace anna::diameter::comm;
 
 
-Engine::Engine(const char *className) :
+Engine::Engine(const char *className, codec::Engine *baseProtocolCodecEngine) :
   anna::app::Component(className),
+  a_baseProtocolCodecEngine(baseProtocolCodecEngine),
   a_autoBind(true),
   a_availableForEntities(false),
   a_availableForLocalServers(false),
@@ -939,10 +940,10 @@ void Engine::readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw()
   //        [Error-Message].................................(281,0)
   //       *[Failed-AVP]....................................(279,0)
   try {
-    anna::diameter::codec::Message diameterDPA;
-    anna::diameter::codec::Avp avpRC;
-    anna::diameter::codec::Avp avpOH;
-    anna::diameter::codec::Avp avpOR;
+    anna::diameter::codec::Message diameterDPA(getBaseProtocolCodecEngine());
+    anna::diameter::codec::Avp avpRC(getBaseProtocolCodecEngine());
+    anna::diameter::codec::Avp avpOH(getBaseProtocolCodecEngine());
+    anna::diameter::codec::Avp avpOR(getBaseProtocolCodecEngine());
     // Message header
     diameterDPA.setId(anna::diameter::helpers::base::COMMANDID__Disconnect_Peer_Answer);
     diameterDPA.setVersion(1);
@@ -967,7 +968,10 @@ void Engine::readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw()
     // Encode
     dpa = diameterDPA.code();
   } catch(anna::RuntimeException &ex) {
-    ex.trace();
+    std::string msg = ex.getText();
+    msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages encoding (unable to answer with DPA)";
+    anna::Logger::error(msg, ANNA_FILE_LOCATION);
+    //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
   }
 }
 
@@ -993,10 +997,10 @@ void Engine::readCEA(anna::DataBlock &cea, const anna::DataBlock & cer) throw()
   //        [Firmware-Revision].............................(267,0)
   //       *[AVP]...........................................(0,0)
   try {
-    anna::diameter::codec::Message diameterCEA;
-    anna::diameter::codec::Avp avpRC;
-    anna::diameter::codec::Avp avpOH;
-    anna::diameter::codec::Avp avpOR;
+    anna::diameter::codec::Message diameterCEA(getBaseProtocolCodecEngine());
+    anna::diameter::codec::Avp avpRC(getBaseProtocolCodecEngine());
+    anna::diameter::codec::Avp avpOH(getBaseProtocolCodecEngine());
+    anna::diameter::codec::Avp avpOR(getBaseProtocolCodecEngine());
     // Message header
     diameterCEA.setId(anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Answer);
     diameterCEA.setVersion(1);
@@ -1030,7 +1034,10 @@ void Engine::readCEA(anna::DataBlock &cea, const anna::DataBlock & cer) throw()
     // Encode
     cea = diameterCEA.code();
   } catch(anna::RuntimeException &ex) {
-    ex.trace();
+    std::string msg = ex.getText();
+    msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages encoding (unable to answer with CEA)";
+    anna::Logger::error(msg, ANNA_FILE_LOCATION);
+    //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
   }
 }
 
@@ -1046,10 +1053,10 @@ void Engine::readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) throw()
   //       *[Failed-AVP]....................................(279,0)
   //        [Origin-State-Id]...............................(278,0)
   try {
-    anna::diameter::codec::Message diameterDWA;
-    anna::diameter::codec::Avp avpRC;
-    anna::diameter::codec::Avp avpOH;
-    anna::diameter::codec::Avp avpOR;
+    anna::diameter::codec::Message diameterDWA(getBaseProtocolCodecEngine());
+    anna::diameter::codec::Avp avpRC(getBaseProtocolCodecEngine());
+    anna::diameter::codec::Avp avpOH(getBaseProtocolCodecEngine());
+    anna::diameter::codec::Avp avpOR(getBaseProtocolCodecEngine());
     // Message header
     diameterDWA.setId(anna::diameter::helpers::base::COMMANDID__Device_Watchdog_Answer);
     diameterDWA.setVersion(1);
@@ -1074,7 +1081,10 @@ void Engine::readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) throw()
     // Encode
     dwa = diameterDWA.code();
   } catch(anna::RuntimeException &ex) {
-    ex.trace();
+    std::string msg = ex.getText();
+    msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages encoding (unable to answer with DWA)";
+    anna::Logger::error(msg, ANNA_FILE_LOCATION);
+    //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
   }
 }
 
index 67916c7..25a879c 100644 (file)
@@ -320,6 +320,14 @@ void Entity::eventPeerShutdown(const ClientSession* clientSession) throw() {
   );
 }
 
+void Entity::eventRequestRetransmission(const ClientSession* clientSession, Message *request) throw() {
+  LOGWARNING(
+    std::string msg(clientSession->asString());
+    msg += " | eventRequestRetransmission";
+    anna::Logger::warning(msg, ANNA_FILE_LOCATION);
+  );
+}
+
 std::string Entity::asString() const throw() {
   std::string result("diameter::comm::Entity { ");
   std::string realm = a_engine->getRealm();
index 293cc83..985e4a8 100644 (file)
@@ -277,7 +277,7 @@ ServerSession *LocalServer::createServerSession(const anna::comm::ClientSocket &
   result->setClientSocket((anna::comm::ClientSocket*)(&clientSocket));
   result->a_parent = this;
   result->a_socketId = key; // de momento...
-  result->initializeSequences(); // después de asignar el LocalServer y el socketId (*)
+  result->initializeSequences(); // despus de asignar el LocalServer y el socketId (*)
   // (*) Las secuencias se basan en la semilla:    srand(::time(NULL) + anna::functions::exclusiveHash(anna::functions::asString("%s:%d|%d", getAddress().c_str(), getPort(), a_socketId)));
   result->a_engine = a_engine;
   a_serverSessions.insert(serverSession_value_type(key, result));
@@ -329,7 +329,7 @@ throw(anna::RuntimeException) {
   //   #9  0x000000000048d288 in anna::diameter::comm::ServerSession::finalize (this=0xc37a00) at comm.db/diameter.comm.ServerSession.cc:510
   //   #10 0x0000000000494e4f in anna::diameter::comm::ServerSessionReceiver::eventBreakLocalConnection (this=0xc119c0, clientSocket=@0xb0ea00)
   // SOLUCION: no borrar aqui, marcar como "deprecated". Este estado no se necesita realmente puesto que nadie volvera a usar este recurso.
-  // Pero simplemente se podria usar para purgar mediante temporizacion (entonces sí se haría el erase)
+  // Pero simplemente se podria usar para purgar mediante temporizacion (entonces s� se har�a el erase)
   serverSession->a_deprecated = true;
   // WE WILL ERASE AT createServerSession
   a_deliveryIterator = serverSession_begin();
@@ -532,6 +532,14 @@ void LocalServer::eventPeerShutdown(const ServerSession* serverSession) throw()
   );
 }
 
+void LocalServer::eventRequestRetransmission(const ServerSession* serverSession, Message *request) throw() {
+  LOGWARNING(
+    std::string msg(serverSession->asString());
+    msg += " | eventRequestRetransmission";
+    anna::Logger::warning(msg, ANNA_FILE_LOCATION);
+  );
+}
+
 std::string LocalServer::asString() const throw() {
   std::string result("diameter::comm::LocalServer { ");
   result += "Description: ";
index 88cbe91..a07de1a 100644 (file)
@@ -27,7 +27,7 @@ using namespace anna::diameter::comm;
 
 const char* Message::asText(const OnExpiry::_v rc)
 throw() {
-  static const char* text [] = { "Abandon", "Ignore" };
+  static const char* text [] = { "Abandon", "Ignore", "Retransmit" };
   return text [rc];
 }
 
@@ -61,6 +61,7 @@ throw() {
 ////   a_onExpiry = OnExpiry::Ignore;
 //}
 
+
 bool Message::fixRequestSequence(HopByHop hbh, EndToEnd ete) throw() {
   setRequestHopByHop(getHopByHop()); // original request hop-by-hop (backup)
   setRequestEndToEnd(getEndToEnd()); // original request end-to-end (backup)
@@ -71,7 +72,7 @@ bool Message::fixRequestSequence(HopByHop hbh, EndToEnd ete) throw() {
     result = true;
   }
 
-  if(a_updateEndToEnd) {
+  if(a_endToEndSequenced) {
     if(ete != getRequestEndToEnd()) {
       codec::functions::setEndToEnd((anna::DataBlock&)getBody(), ete);
       result = true;
@@ -85,7 +86,7 @@ bool Message::fixRequestSequence(HopByHop hbh, EndToEnd ete) throw() {
     msg += " (original) -> ";
     msg += anna::functions::asString(hbh);
     msg += " (session)";
-    msg += a_updateEndToEnd ? " | End to end: " : " | End to end [end-to-end unchanged]: ";
+    msg += a_endToEndSequenced ? " | End to end: " : " | End to end [end-to-end unchanged]: ";
     msg += anna::functions::asString(getRequestEndToEnd());
     msg += " (original) -> ";
     msg += anna::functions::asString(ete);
index 4b96c5e..a751d48 100644 (file)
@@ -315,27 +315,32 @@ anna::xml::Node* Server::asXML(anna::xml::Node* parent) const throw() {
 
 
 void Server::eventPeerShutdown(const ClientSession *clientSession) throw() {
-  // Inform father server:
+  // Inform father entity:
   a_parent->eventPeerShutdown(clientSession);
 }
 
+void Server::eventRequestRetransmission(const ClientSession* clientSession, Message *request) throw() {
+  // Inform father entity:
+  a_parent->eventRequestRetransmission(clientSession, request);
+}
+
 void Server::eventResponse(const Response& response) throw(anna::RuntimeException) {
-  // Inform father server:
+  // Inform father entity:
   a_parent->eventResponse(response);
 }
 
 void Server::eventRequest(ClientSession *clientSession, const anna::DataBlock & request) throw(anna::RuntimeException) {
-  // Inform father server:
+  // Inform father entity:
   a_parent->eventRequest(clientSession, request);
 }
 
 void Server::eventUnknownResponse(ClientSession *clientSession, const anna::DataBlock & response) throw(anna::RuntimeException) {
-  // Inform father server:
+  // Inform father entity:
   a_parent->eventUnknownResponse(clientSession, response);
 }
 
 void Server::eventDPA(ClientSession *clientSession, const anna::DataBlock & response) throw(anna::RuntimeException) {
-  // Inform father server:
+  // Inform father entity:
   a_parent->eventDPA(clientSession, response);
 }
 
index 3c493d0..a615c78 100644 (file)
@@ -215,11 +215,18 @@ const Response* ServerSession::send(const Message* message) throw(anna::RuntimeE
     // Trace non-application messages:
     LOGDEBUG(
 
-      if((cid == helpers::base::COMMANDID__Device_Watchdog_Request) ||
-    (cid == helpers::base::COMMANDID__Disconnect_Peer_Request)) {
-    anna::Logger::debug("Sent DataBlock to XML representation:", ANNA_FILE_LOCATION);
-      try { anna::diameter::codec::Message msg; msg.decode(message->getBody()); /* decode to be traced */ } catch(anna::RuntimeException&) {;}
-    }
+      if( (cid == helpers::base::COMMANDID__Device_Watchdog_Request) ||
+          (cid == helpers::base::COMMANDID__Disconnect_Peer_Request)) {
+        anna::Logger::debug("Sent DataBlock to XML representation:", ANNA_FILE_LOCATION);
+        try {
+          anna::diameter::codec::Message msg(a_engine->getBaseProtocolCodecEngine()); msg.decode(message->getBody()); /* decode to be traced */
+        }
+        catch(anna::RuntimeException &ex) {
+          std::string msg = ex.getText();
+          msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages full tracing";
+          anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+        }
+      }
     );
 
     // Restore sequences:
@@ -306,6 +313,11 @@ void ServerSession::eventPeerShutdown() throw() {
   a_parent->eventPeerShutdown(this);
 }
 
+void ServerSession::eventRequestRetransmission(Message *request) throw() {
+  // Inform father server:
+  a_parent->eventRequestRetransmission(this, request);
+}
+
 void ServerSession::eventResponse(const Response& response) throw(anna::RuntimeException) {
   // Inform father server:
   a_parent->eventResponse(response);
@@ -344,9 +356,18 @@ throw(anna::RuntimeException) {
     msg += anna::diameter::functions::commandIdAsPairString(cid);
     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
 
-    if((cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) || (cid.first == helpers::base::COMMANDID__Device_Watchdog_Request.first))
-  try { anna::diameter::codec::Message dmsg; dmsg.decode(db); /* decode to be traced */ } catch(anna::RuntimeException&) {;}
-);
+    if( (cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) ||
+        (cid.first == helpers::base::COMMANDID__Device_Watchdog_Request.first)) {
+      try {
+        anna::diameter::codec::Message dmsg(a_engine->getBaseProtocolCodecEngine()); dmsg.decode(db); /* decode to be traced */
+      }
+      catch(anna::RuntimeException &ex) {
+        std::string msg = ex.getText();
+        msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages full tracing";
+        anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+      }
+    }
+  );
   // Main counters:
   OamModule &oamModule = OamModule::instantiate();
   oamModule.count(isRequest ? OamModule::Counter::RequestReceived : OamModule::Counter::AnswerReceived);
@@ -571,7 +592,7 @@ throw(anna::RuntimeException) {
 
   if(cea.isEmpty()) {
     LOGDEBUG(anna::Logger::debug("Empty CEA message. Remote client never will bound this connection at application level", ANNA_FILE_LOCATION));
-    LOGWARNING(anna::Logger::warning("Discarding received CER without sending CEA (consider to send CEA with Result-Code DIAMETER_UNKNOWN_PEER)", ANNA_FILE_LOCATION));
+    LOGWARNING(anna::Logger::warning("Discarding received CER: cannot send empty CEA (consider to send CEA with Result-Code DIAMETER_UNKNOWN_PEER)", ANNA_FILE_LOCATION));
     return;
   }
 
index fada503..051cbce 100644 (file)
@@ -238,6 +238,7 @@ void Session::expireResponse(diameter::comm::Response* response)
 throw() {
   LOGMETHOD(anna::TraceMethod traceMethod("anna::diameter::comm::Session", "expireResponse", ANNA_FILE_LOCATION));
   bool doUnbind = false;
+  bool doRetransmission = false;
 
   // Quitar el OnExpiry: no tiene sentido habiendo keep-alive (DWR)
   if(response->getClassCode() != ClassCode::Bind) {
@@ -245,9 +246,13 @@ throw() {
       a_onDisconnect = OnDisconnect::IgnorePendings; // Abandon is not graceful
       doUnbind = true;
     }
+    else if(response->getRequest()->getOnExpiry() == Message::OnExpiry::Retransmit) {
+      doRetransmission = true;
+    }
   } else
     doUnbind = true; // (*)
 
+
   try {
     response->setMessage(NULL);
     eventResponse(*response);
@@ -263,6 +268,25 @@ throw() {
     setState(State::Bound);
   }
 
+  if(doRetransmission) {
+    diameter::comm::Message *request = const_cast<Message*>(response->getRequest());
+    eventRequestRetransmission(request);
+
+    int retries = request->getRetries();
+    if (retries > 0) {
+      retries--;
+      request->setRetries(retries);
+      LOGDEBUG
+      (
+        //std::string msg(asString());
+        std::string msg = anna::functions::asString("Retransmission initiated for request with HopByHop: %u; remaining %d retries", response->getHopByHop(), retries);
+        anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+      );
+      diameter::codec::functions::setPotentiallyReTransmittedMessageBit(*request);
+      send(request);
+    }
+  }
+
   response_erase(response);
 
   if(doUnbind) unbind();
@@ -275,7 +299,7 @@ void Session::finalize() throw() {
   cancelActionTimer(); // Action timer
   eventPeerShutdown();
 ///////////////////////////////////////////////////////////////////////
-// Notificar la finalización de las respuestas pendientes de recibir //
+// Notificar la finalizacin de las respuestas pendientes de recibir //
 ///////////////////////////////////////////////////////////////////////
 // RFC 3588 - 5.5.4.  Failover and Failback Procedures
 //
index 1d41374..b9c4fbd 100644 (file)
@@ -205,7 +205,7 @@ void Message::setId(const char *name) throw(anna::RuntimeException) {
 //------------------------------------------------------------------------------
 //-------------------------------------------------- Message::setApplicationId()
 //------------------------------------------------------------------------------
-void Message::setApplicationId(U32 aid) throw() {
+void Message::setApplicationId(U32 aid) throw(anna::RuntimeException) {
   a_applicationId = aid;
 
   // Default behaviour:
index 3e8ce1f..a58372d 100644 (file)
@@ -80,14 +80,34 @@ anna::diameter::CommandId functions::getCommandId(const anna::DataBlock & db) th
   return (anna::diameter::CommandId(code, (flags & Message::RBitMask) != 0x00));
 }
 
-
-bool functions::isRequest(const anna::DataBlock & db) throw(anna::RuntimeException) {
+bool functions::requestBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
   if(db.getSize() < Message::HeaderLength)
     throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
 
   return (((db.getData())[4] & Message::RBitMask) != 0x00);
 }
 
+bool functions::proxiableBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
+  if(db.getSize() < Message::HeaderLength)
+    throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
+
+  return (((db.getData())[4] & Message::PBitMask) != 0x00);
+}
+
+bool functions::errorBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
+  if(db.getSize() < Message::HeaderLength)
+    throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
+
+  return (((db.getData())[4] & Message::EBitMask) != 0x00);
+}
+
+bool functions::potentiallyReTransmittedMessageBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
+  if(db.getSize() < Message::HeaderLength)
+    throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
+
+  return (((db.getData())[4] & Message::TBitMask) != 0x00);
+}
+
 anna::diameter::ApplicationId functions::getApplicationId(const anna::DataBlock & db) throw(anna::RuntimeException) {
   if(db.getSize() < Message::HeaderLength)
     throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
@@ -279,4 +299,15 @@ void functions::setEndToEnd(anna::DataBlock & db, diameter::EndToEnd ete) throw(
   memcpy((char *)(db.getData() + 16), source, 4);
 }
 
+void functions::setPotentiallyReTransmittedMessageBit(const anna::DataBlock & db, bool activate) throw(anna::RuntimeException) {
+  if(db.getSize() < Message::HeaderLength) {
+    throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
+  }
+
+  static char flags[1];
+  flags[0] = *(db.getData() + 4);
+  if(activate) flags[0] |= Message::TBitMask; else flags[0] &= (~Message::TBitMask);
+  memcpy((char *)(db.getData() + 4), flags, 1);
+}
+