Webserver+3d print

Dependents:   Nucleo

cyclone_tcp/snmp/snmp_agent_misc.c

Committer:
Sergunb
Date:
2017-02-04
Revision:
0:8918a71cdbe9

File content as of revision 0:8918a71cdbe9:

/**
 * @file snmp_agent_misc.c
 * @brief SNMP agent (miscellaneous functions)
 *
 * @section License
 *
 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
 *
 * This file is part of CycloneTCP Open.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 1.7.6
 **/

//Switch to the appropriate trace level
#define TRACE_LEVEL SNMP_TRACE_LEVEL

//Dependencies
#include <limits.h>
#include "core/net.h"
#include "snmp/snmp_agent.h"
#include "snmp/snmp_agent_misc.h"
#include "mibs/mib2_module.h"
#include "crypto.h"
#include "asn1.h"
#include "oid.h"
#include "debug.h"

//Check TCP/IP stack configuration
#if (SNMP_AGENT_SUPPORT == ENABLED)


/**
 * @brief Lock MIB bases
 **/

void snmpLockMib(SnmpAgentContext *context)
{
   uint_t i;

   //Loop through MIBs
   for(i = 0; i < context->mibModuleCount; i++)
   {
      //Lock access to MIB base
      if(context->mibModule[i]->lock != NULL)
      {
         context->mibModule[i]->lock();
      }
   }
}


/**
 * @brief Unlock MIB bases
 **/

void snmpUnlockMib(SnmpAgentContext *context)
{
   uint_t i;

   //Loop through MIBs
   for(i = 0; i < context->mibModuleCount; i++)
   {
      //Unlock access to MIB base
      if(context->mibModule[i]->unlock != NULL)
      {
         context->mibModule[i]->unlock();
      }
   }
}


/**
 * @brief Initialize a GetResponse-PDU
 * @param[in] context Pointer to the SNMP agent context
 * @return Error code
 **/

error_t snmpInitResponse(SnmpAgentContext *context)
{
   error_t error;

   //Initialize SNMP message
   snmpInitMessage(&context->response);

   //SNMP version identifier
   context->response.version = context->request.version;

#if (SNMP_V1_SUPPORT == ENABLED || SNMP_V2C_SUPPORT == ENABLED)
   //Community name
   context->response.community = context->request.community;
   context->response.communityLen = context->request.communityLen;
#endif

#if (SNMP_V3_SUPPORT == ENABLED)
   //Message identifier
   context->response.msgId = context->request.msgId;
   //Maximum message size supported by the sender
   context->response.msgMaxSize = SNMP_MAX_MSG_SIZE;

   //Bit fields which control processing of the message
   context->response.msgFlags = context->request.msgFlags &
      (SNMP_MSG_FLAG_AUTH | SNMP_MSG_FLAG_PRIV);

   //Security model used by the sender
   context->response.msgSecurityModel = context->request.msgSecurityModel;

   //Authoritative engine identifier
   context->response.msgAuthEngineId = context->contextEngine;
   context->response.msgAuthEngineIdLen = context->contextEngineLen;

   //Number of times the SNMP engine has rebooted
   context->response.msgAuthEngineBoots = context->engineBoots;
   //Number of seconds since last reboot
   context->response.msgAuthEngineTime = context->engineTime;

   //User name
   context->response.msgUserName = context->request.msgUserName;
   context->response.msgUserNameLen = context->request.msgUserNameLen;

   //Authentication parameters
   context->response.msgAuthParameters = NULL;
   context->response.msgAuthParametersLen = context->request.msgAuthParametersLen;

   //Privacy parameters
   context->response.msgPrivParameters = context->privParameters;
   context->response.msgPrivParametersLen = context->request.msgPrivParametersLen;

   //Context engine identifier
   context->response.contextEngineId = context->contextEngine;
   context->response.contextEngineIdLen = context->contextEngineLen;

   //Context name
   context->response.contextName = context->request.contextName;
   context->response.contextNameLen = context->request.contextNameLen;
#endif

   //PDU type
   context->response.pduType = SNMP_PDU_GET_RESPONSE;
   //Request identifier
   context->response.requestId = context->request.requestId;

   //Make room for the message header at the beginning of the buffer
   error = snmpComputeMessageOverhead(&context->response);
   //Return status code
   return error;
}


/**
 * @brief Refresh SNMP engine time
 * @param[in] context Pointer to the SNMP agent context
 **/

void snmpRefreshEngineTime(SnmpAgentContext *context)
{
#if (SNMP_V3_SUPPORT == ENABLED)
   systime_t delta;
   int32_t newEngineTime;

   //Number of seconds elapsed since the last call
   delta = (osGetSystemTime() - context->systemTime) / 1000;
   //Increment SNMP engine time
   newEngineTime = context->engineTime + delta;

   //Check whether the SNMP engine time has rolled over
   if(newEngineTime < context->engineTime)
   {
      //If snmpEngineTime ever reaches its maximum value (2147483647), then
      //snmpEngineBoots is incremented as if the SNMP engine has re-booted
      //and snmpEngineTime is reset to zero and starts incrementing again
      context->engineBoots++;
      context->engineTime = 0;
   }
   else
   {
      //Update SNMP engine time
      context->engineTime = newEngineTime;
   }

   //Save timestamp
   context->systemTime += delta * 1000;
#endif
}


/**
 * @brief Replay protection
 * @param[in] context Pointer to the SNMP agent context
 * @param[in,out] message Pointer to the incoming SNMP message
 * @return Error code
 **/

error_t snmpCheckEngineTime(SnmpAgentContext *context, SnmpMessage *message)
{
   error_t error = NO_ERROR;

#if (SNMP_V3_SUPPORT == ENABLED)
   //If any of the following conditions is true, then the message is
   //considered to be outside of the time window
   if(context->engineBoots == INT32_MAX)
   {
      //The local value of snmpEngineBoots is 2147483647
      error = ERROR_NOT_IN_TIME_WINDOW;
   }
   else if(context->engineBoots != message->msgAuthEngineBoots)
   {
      //The value of the msgAuthoritativeEngineBoots field differs from
      //the local value of snmpEngineBoots
      error = ERROR_NOT_IN_TIME_WINDOW;
   }
   else if((context->engineTime - message->msgAuthEngineTime) > SNMP_TIME_WINDOW ||
      (message->msgAuthEngineTime - context->engineTime) > SNMP_TIME_WINDOW)
   {
      //The value of the msgAuthoritativeEngineTime field differs from the
      //local notion of snmpEngineTime by more than +/- 150 seconds
      error = ERROR_NOT_IN_TIME_WINDOW;
   }
#endif

   //If the message is considered to be outside of the time window then an
   //error indication (notInTimeWindow) is returned to the calling module
   return error;
}


/**
 * @brief Find user in the local configuration datastore
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] name Pointer to the user name
 * @param[in] length Length of the user name
 * @return Security profile corresponding to the specified user name
 **/

SnmpUserInfo *snmpFindUser(SnmpAgentContext *context,
   const char_t *name, size_t length)
{
   uint_t i;
   SnmpUserInfo *entry;

   //Initialize pointer
   entry = NULL;

   //Sanity check
   if(name != NULL)
   {
      //Loop through the local configuration datastore
      for(i = 0; i < SNMP_AGENT_MAX_USER_COUNT; i++)
      {
         //Compare user names
         if(strlen(context->userTable[i].name) == length)
         {
            if(!strncmp(context->userTable[i].name, name, length))
            {
               //A matching entry has been found
               entry = &context->userTable[i];
               //We are done
               break;
            }
         }
      }
   }

   //Return the security profile that corresponds to the specified user name
   return entry;
}


/**
 * @brief Parse variable binding
 * @param[in] p Input stream where to read the variable binding
 * @param[in] length Number of bytes available in the input stream
 * @param[out] var Variable binding
 * @param[out] consumed Total number of bytes that have been consumed
 * @return Error code
 **/

error_t snmpParseVarBinding(const uint8_t *p,
   size_t length, SnmpVarBind *var, size_t *consumed)
{
   error_t error;
   Asn1Tag tag;

   //The variable binding is encapsulated within a sequence
   error = asn1ReadTag(p, length, &tag);
   //Failed to decode ASN.1 tag?
   if(error)
      return error;

   //Enforce encoding, class and type
   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
   //The tag does not match the criteria?
   if(error)
      return error;

   //Total number of bytes that have been consumed
   *consumed = tag.totalLength;

   //Point to the first item of the sequence
   p = tag.value;
   length = tag.length;

   //Read object name
   error = asn1ReadTag(p, length, &tag);
   //Failed to decode ASN.1 tag?
   if(error)
      return error;

   //Enforce encoding, class and type
   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
   //The tag does not match the criteria?
   if(error)
      return error;

   //Save object identifier
   var->oid = tag.value;
   var->oidLen = tag.length;

   //Point to the next item
   p += tag.totalLength;
   length -= tag.totalLength;

   //Read object value
   error = asn1ReadTag(p, length, &tag);
   //Failed to decode ASN.1 tag?
   if(error)
      return error;

   //Make sure that the tag is valid
   if(tag.constructed)
      return ERROR_INVALID_TAG;

   //Save object class
   var->objClass = tag.objClass;
   //Save object type
   var->objType = tag.objType;
   //Save object value
   var->value = tag.value;
   var->valueLen = tag.length;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Write variable binding
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] var Variable binding
 * @return Error code
 **/

error_t snmpWriteVarBinding(SnmpAgentContext *context, const SnmpVarBind *var)
{
   error_t error;
   size_t m;
   size_t n;
   uint8_t *p;
   Asn1Tag tag;

   //The object's name is encoded in ASN.1 format
   tag.constructed = FALSE;
   tag.objClass = ASN1_CLASS_UNIVERSAL;
   tag.objType = ASN1_TYPE_OBJECT_IDENTIFIER;
   tag.length = var->oidLen;
   tag.value = var->oid;

   //Calculate the total length of the ASN.1 tag
   error = asn1WriteTag(&tag, FALSE, NULL, &m);
   //Any error to report?
   if(error)
      return error;

   //The object's value is encoded in ASN.1 format
   tag.constructed = FALSE;
   tag.objClass = var->objClass;
   tag.objType = var->objType;
   tag.length = var->valueLen;
   tag.value = var->value;

   //Calculate the total length of the ASN.1 tag
   error = asn1WriteTag(&tag, FALSE, NULL, &n);
   //Any error to report?
   if(error)
      return error;

   //The variable binding is encapsulated within a sequence
   tag.constructed = TRUE;
   tag.objClass = ASN1_CLASS_UNIVERSAL;
   tag.objType = ASN1_TYPE_SEQUENCE;
   tag.length = m + n;
   tag.value = NULL;

   //The first pass computes the total length of the sequence
   error = asn1WriteTag(&tag, FALSE, NULL, NULL);
   //Any error to report?
   if(error)
      return error;

   //Make sure the buffer is large enough to hold the whole sequence
   if((context->response.varBindListLen + tag.totalLength) >
      context->response.varBindListMaxLen)
   {
      //Report an error
      return ERROR_BUFFER_OVERFLOW;
   }

   //The second pass encodes the sequence in reverse order
   p = context->response.varBindList + context->response.varBindListLen +
      tag.totalLength;

   //Encode the object's value using ASN.1
   tag.constructed = FALSE;
   tag.objClass = var->objClass;
   tag.objType = var->objType;
   tag.length = var->valueLen;
   tag.value = var->value;

   //Write the corresponding ASN.1 tag
   error = asn1WriteTag(&tag, TRUE, p, &m);
   //Any error to report?
   if(error)
      return error;

   //Move backward
   p -= m;

   //Encode the object's name using ASN.1
   tag.constructed = FALSE;
   tag.objClass = ASN1_CLASS_UNIVERSAL;
   tag.objType = ASN1_TYPE_OBJECT_IDENTIFIER;
   tag.length = var->oidLen;
   tag.value = var->oid;

   //Write the corresponding ASN.1 tag
   error = asn1WriteTag(&tag, TRUE, p, &n);
   //Any error to report?
   if(error)
      return error;

   //Move backward
   p -= n;

   //The variable binding is encapsulated within a sequence
   tag.constructed = TRUE;
   tag.objClass = ASN1_CLASS_UNIVERSAL;
   tag.objType = ASN1_TYPE_SEQUENCE;
   tag.length = m + n;
   tag.value = NULL;

   //Write the corresponding ASN.1 tag
   error = asn1WriteTag(&tag, TRUE, p, NULL);
   //Any error to report?
   if(error)
      return error;

   //Update the length of the list
   context->response.varBindListLen += tag.totalLength;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Copy the list of variable bindings
 * @param[in] context Pointer to the SNMP agent context
 * @return Error code
 **/

error_t snmpCopyVarBindingList(SnmpAgentContext *context)
{
   //Sanity check
   if(context->request.varBindListLen > context->response.varBindListMaxLen)
      return ERROR_BUFFER_OVERFLOW;

   //Copy the list of variable bindings to the response buffer
   memcpy(context->response.varBindList, context->request.varBindList,
      context->request.varBindListLen);

   //Save the length of the list
   context->response.varBindListLen = context->request.varBindListLen;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Assign object value
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] var Variable binding
 * @param[in] commit This flag tells whether the changes should be
 *   committed to the MIB base
 * @return Error code
 **/

error_t snmpSetObjectValue(SnmpAgentContext *context, SnmpVarBind *var, bool_t commit)
{
   error_t error;
   size_t n;
   MibVariant *value;
   const MibObject *object;

   //Search the MIB for the specified object
   error = snmpFindMibObject(context, var->oid, var->oidLen, &object);
   //Cannot found the specified object?
   if(error)
      return error;

   //Debug message
   TRACE_INFO("  %s\r\n", object->name);

   //Make sure the specified object is available for set operations
   if(object->access != MIB_ACCESS_WRITE_ONLY &&
      object->access != MIB_ACCESS_READ_WRITE)
   {
      //Report an error
      return ERROR_NOT_WRITABLE;
   }

   //Check class
   if(var->objClass != object->objClass)
      return ERROR_WRONG_TYPE;
   //Check type
   if(var->objType != object->objType)
      return ERROR_WRONG_TYPE;

   //Point to the object value
   value = (MibVariant *) var->value;
   //Get the length of the object value
   n = var->valueLen;

   //Check object class
   if(object->objClass == ASN1_CLASS_UNIVERSAL)
   {
      //Check object type
      if(object->objType == ASN1_TYPE_INTEGER)
      {
         int32_t val;

         //Integer objects use ASN.1 encoding rules
         error = snmpDecodeInt32(var->value, n, &val);
         //Conversion failed?
         if(error)
            return ERROR_WRONG_ENCODING;

         //Point to the scratch buffer
         value = (MibVariant *) context->response.buffer;
         //Save resulting value
         value->integer = val;
         //Integer size
         n = sizeof(int32_t);
      }
   }
   else if(object->objClass == ASN1_CLASS_APPLICATION)
   {
      //Check object type
      if(object->objType == MIB_TYPE_IP_ADDRESS)
      {
         //IpAddress objects have fixed size
         if(n != object->valueSize)
            return ERROR_WRONG_LENGTH;
      }
      else if(object->objType == MIB_TYPE_COUNTER32 ||
         object->objType == MIB_TYPE_GAUGE32 ||
         object->objType == MIB_TYPE_TIME_TICKS)
      {
         uint32_t val;

         //Counter32, Gauge32 and TimeTicks objects use ASN.1 encoding rules
         error = snmpDecodeUnsignedInt32(var->value, n, &val);
         //Conversion failed?
         if(error)
            return ERROR_WRONG_ENCODING;

         //Point to the scratch buffer
         value = (MibVariant *) context->response.buffer;
         //Save resulting value
         value->counter32 = val;
         //Integer size
         n = sizeof(uint32_t);
      }
      else if(object->objType == MIB_TYPE_COUNTER64)
      {
         uint64_t val;

         //Counter64 objects use ASN.1 encoding rules
         error = snmpDecodeUnsignedInt64(var->value, n, &val);
         //Conversion failed?
         if(error)
            return ERROR_WRONG_ENCODING;

         //Point to the scratch buffer
         value = (MibVariant *) context->response.buffer;
         //Save resulting value
         value->counter64 = val;
         //Integer size
         n = sizeof(uint64_t);
      }
   }

   //Objects can be assigned a value using a callback function
   if(object->setValue != NULL)
   {
      //Check whether the changes shall be committed to the MIB base
      if(commit)
      {
         //Invoke callback function to assign object value
         error = object->setValue(object, var->oid, var->oidLen, value, n);
      }
      else
      {
         //Successful write operation
         error = NO_ERROR;
      }
   }
   //Simple scalar objects can also be attached to a variable
   else if(object->value != NULL)
   {
      //Check the length of the object
      if(n <= object->valueSize)
      {
         //Check whether the changes shall be committed to the MIB base
         if(commit)
         {
            //Record the length of the object value
            if(object->valueLen != NULL)
               *object->valueLen = n;

            //Set object value
            memcpy(object->value, value, n);
         }

         //Successful write operation
         error = NO_ERROR;
      }
      else
      {
         //Invalid length
         error = ERROR_WRONG_LENGTH;
      }
   }
   else
   {
      //Report an error
      error = ERROR_WRITE_FAILED;
   }

   //Return status code
   return error;
}


/**
 * @brief Retrieve object value
 * @param[in] context Pointer to the SNMP agent context
 * @param[out] var Variable binding
 * @return Error code
 **/

error_t snmpGetObjectValue(SnmpAgentContext *context, SnmpVarBind *var)
{
   error_t error;
   size_t n;
   MibVariant *value;
   const MibObject *object;

   //Search the MIB for the specified object
   error = snmpFindMibObject(context, var->oid, var->oidLen, &object);
   //Cannot found the specified object?
   if(error)
      return error;

   //Debug message
   TRACE_INFO("  %s\r\n", object->name);

   //Make sure the specified object is available for get operations
   if(object->access != MIB_ACCESS_READ_ONLY &&
      object->access != MIB_ACCESS_READ_WRITE)
   {
      //Report an error
      return ERROR_ACCESS_DENIED;
   }

   //Buffer where to store the object value
   value = (MibVariant *) (context->response.varBindList +
      context->response.varBindListLen + context->response.oidLen);

   //Number of bytes available in the buffer
   n = context->response.varBindListMaxLen -
      (context->response.varBindListLen + context->response.oidLen);

   //Check object class
   if(object->objClass == ASN1_CLASS_UNIVERSAL)
   {
      //Check object type
      if(object->objType == ASN1_TYPE_INTEGER)
      {
         //Make sure the buffer is large enough
         if(n < object->valueSize)
            return ERROR_BUFFER_OVERFLOW;

         //Integer objects have fixed size
         n = object->valueSize;
      }
   }
   else if(object->objClass == ASN1_CLASS_APPLICATION)
   {
      //Check object type
      if(object->objType == MIB_TYPE_IP_ADDRESS ||
         object->objType == MIB_TYPE_COUNTER32 ||
         object->objType == MIB_TYPE_GAUGE32 ||
         object->objType == MIB_TYPE_TIME_TICKS ||
         object->objType == MIB_TYPE_COUNTER64)
      {
         //Make sure the buffer is large enough
         if(n < object->valueSize)
            return ERROR_BUFFER_OVERFLOW;

         //IpAddress, Counter32, Gauge32, TimeTicks and
         //Counter64 objects have fixed size
         n = object->valueSize;
      }
   }

   //Object value can be retrieved using a callback function
   if(object->getValue != NULL)
   {
      //Invoke callback function to retrieve object value
      error = object->getValue(object, var->oid, var->oidLen, value, &n);
   }
   //Simple scalar objects can also be attached to a variable
   else if(object->value != NULL)
   {
      //Get the length of the object value
      if(object->valueLen != NULL)
         n = *object->valueLen;

      //Retrieve object value
      memcpy(value, object->value, n);
      //Successful read operation
      error = NO_ERROR;
   }
   else
   {
      //Report an error
      error = ERROR_READ_FAILED;
   }

   //Unable to retrieve object value?
   if(error)
      return error;

   //Check object class
   if(object->objClass == ASN1_CLASS_UNIVERSAL)
   {
      //Check object type
      if(object->objType == ASN1_TYPE_INTEGER)
      {
         //Encode Integer objects using ASN.1 rules
         error = snmpEncodeInt32(value->integer, (uint8_t *) value, &n);
      }
      else
      {
         //No conversion required for OctetString and ObjectIdentifier objects
         error = NO_ERROR;
      }
   }
   else if(object->objClass == ASN1_CLASS_APPLICATION)
   {
      //Check object type
      if(object->objType == MIB_TYPE_COUNTER32 ||
         object->objType == MIB_TYPE_GAUGE32 ||
         object->objType == MIB_TYPE_TIME_TICKS)
      {
         //Encode Counter32, Gauge32 and TimeTicks objects using ASN.1 rules
         error = snmpEncodeUnsignedInt32(value->counter32, (uint8_t *) value, &n);
      }
      else if(object->objType == MIB_TYPE_COUNTER64)
      {
         //Encode Counter64 objects using ASN.1 rules
         error = snmpEncodeUnsignedInt64(value->counter64, (uint8_t *) value, &n);
      }
      else
      {
         //No conversion required for Opaque objects
         error = NO_ERROR;
      }
   }

   //Save object class and type
   var->objClass = object->objClass;
   var->objType = object->objType;

   //Save object value
   var->value = (uint8_t *) value;
   var->valueLen = n;

   //Return status code
   return error;
}


/**
 * @brief Search MIBs for the next object
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] var Variable binding
 * @return Error pointer
 **/

error_t snmpGetNextObject(SnmpAgentContext *context, SnmpVarBind *var)
{
   error_t error;
   uint_t i;
   uint_t j;
   size_t nextOidLen;
   uint8_t *nextOid;
   const MibObject *object;

   //Buffer where to store the next object identifier
   nextOid = context->response.varBindList + context->response.varBindListLen;

   //Loop through MIBs
   for(i = 0; i < context->mibModuleCount; i++)
   {
      //Point the first object of the MIB
      object = context->mibModule[i]->objects;

      //Loop through objects
      for(j = 0; j < context->mibModule[i]->numObjects; j++)
      {
         //Scalar or tabular object?
         if(object->getNext == NULL)
         {
            //Take in account the instance sub-identifier to determine
            //the length of the OID
            nextOidLen = object->oidLen + 1;

            //Make sure the buffer is large enough to hold the entire OID
            if((context->response.varBindListLen + nextOidLen) >
               context->response.varBindListMaxLen)
            {
               //Report an error
               return ERROR_BUFFER_OVERFLOW;
            }

            //Copy object identifier
            memcpy(nextOid, object->oid, object->oidLen);
            //Append instance sub-identifier
            nextOid[nextOidLen - 1] = 0;

            //Perform lexicographical comparison
            if(oidComp(var->oid, var->oidLen, nextOid, nextOidLen) < 0)
            {
               //Replace the original OID with the name of the next object
               var->oid = nextOid;
               var->oidLen = nextOidLen;

               //Save the length of the OID
               context->response.oidLen = nextOidLen;

               //The specified OID lexicographically precedes the name
               //of the current object
               return NO_ERROR;
            }
         }
         else
         {
            //Maximum acceptable size of the OID
            nextOidLen = context->response.varBindListMaxLen -
               context->response.varBindListLen;

            //Search the MIB for the next object
            error = object->getNext(object, var->oid, var->oidLen, nextOid, &nextOidLen);

            //Check status code
            if(error == NO_ERROR)
            {
               //Replace the original OID with the name of the next object
               var->oid = nextOid;
               var->oidLen = nextOidLen;

               //Save the length of the OID
               context->response.oidLen = nextOidLen;

               //The specified OID lexicographically precedes the name
               //of the current object
               return NO_ERROR;
            }
            if(error != ERROR_OBJECT_NOT_FOUND)
            {
               //Exit immediately
               return error;
            }
         }

         //Point to the next object in the MIB
         object++;
      }
   }

   //The specified OID does not lexicographically precede the
   //name of some object
   return ERROR_OBJECT_NOT_FOUND;
}


/**
 * @brief Search MIBs for the given object
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] oid Object identifier
 * @param[in] oidLen Length of the OID
 * @param[out] object Pointer the MIB object descriptor
 * @return Error code
 **/

error_t snmpFindMibObject(SnmpAgentContext *context,
   const uint8_t *oid, size_t oidLen, const MibObject **object)
{
   error_t error;
   uint_t i;
   uint_t j;
   const MibObject *p;

   //Initialize status code
   error = ERROR_OBJECT_NOT_FOUND;

   //Loop through MIBs
   for(i = 0; i < context->mibModuleCount; i++)
   {
      //Point the first object of the MIB
      p = context->mibModule[i]->objects;

      //Loop through objects
      for(j = 0; j < context->mibModule[i]->numObjects; j++)
      {
         //Check the length of the OID
         if(oidLen > p->oidLen)
         {
            //Compare object names
            if(!memcmp(oid, p->oid, p->oidLen))
            {
               //Scalar object?
               if(p->getNext == NULL)
               {
                  //The instance sub-identifier shall be 0 for scalar objects
                  if(oidLen == (p->oidLen + 1) && oid[oidLen - 1] == 0)
                  {
                     //Return a pointer to the matching object
                     *object = p;
                     //No error to report
                     error = NO_ERROR;
                  }
                  else
                  {
                     //No such instance...
                     error = ERROR_INSTANCE_NOT_FOUND;
                  }
               }
               //Tabular object?
               else
               {
                  //Return a pointer to the matching object
                  *object = p;
                  //No error to report
                  error = NO_ERROR;
               }

               //Exit immediately
               break;
            }
         }

         //Point to the next object in the MIB
         p++;
      }
   }

   //Return status code
   return error;
}


/**
 * @brief Translate status code
 * @param[in,out] message Pointer to the outgoing SNMP message
 * @param[in] status Status code
 * @param[in] index Index of the variable binding in the list that caused an exception
 * @return error code
 **/

error_t snmpTranslateStatusCode(SnmpMessage *message, error_t status, uint_t index)
{
   //SNMPv1 version?
   if(message->version == SNMP_VERSION_1)
   {
      //Set error-status and error-index fields
      switch(status)
      {
      case NO_ERROR:
         //Return noError status code
         message->errorStatus = SNMP_ERROR_NONE;
         message->errorIndex = 0;
         break;

      case ERROR_OBJECT_NOT_FOUND:
      case ERROR_INSTANCE_NOT_FOUND:
      case ERROR_ACCESS_DENIED:
         //Return noSuchName status code
         message->errorStatus = SNMP_ERROR_NO_SUCH_NAME;
         message->errorIndex = index;

         //Total number of SNMP PDUs which were generated by the SNMP protocol
         //entity and for which the value of the error-status field is noSuchName
         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutNoSuchNames, 1);
         break;

      case ERROR_WRONG_TYPE:
      case ERROR_WRONG_LENGTH:
      case ERROR_WRONG_ENCODING:
      case ERROR_WRONG_VALUE:
         //Return badValue status code
         message->errorStatus = SNMP_ERROR_BAD_VALUE;
         message->errorIndex = index;

         //Total number of SNMP PDUs which were generated by the SNMP protocol
         //entity and for which the value of the error-status field is badValue
         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutBadValues, 1);
         break;

      case ERROR_READ_FAILED:
      case ERROR_WRITE_FAILED:
      case ERROR_NOT_WRITABLE:
         //Return genError status code
         message->errorStatus = SNMP_ERROR_GENERIC;
         message->errorIndex = index;

         //Total number of SNMP PDUs which were generated by the SNMP protocol
         //entity and for which the value of the error-status field is genError
         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutGenErrs, 1);
         break;

      case ERROR_BUFFER_OVERFLOW:
         //Return tooBig status code
         message->errorStatus = SNMP_ERROR_TOO_BIG;
         message->errorIndex = 0;

         //Total number of SNMP PDUs which were generated by the SNMP protocol
         //entity and for which the value of the error-status field is tooBig
         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutTooBigs, 1);
         break;

      default:
         //If the parsing of the request fails, the SNMP agent discards
         //the message and performs no further actions
         return status;
      }
   }
   //SNMPv2c or SNMPv3 version?
   else
   {
      //Set error-status and error-index fields
      switch(status)
      {
      case NO_ERROR:
         //Return noError status code
         message->errorStatus = SNMP_ERROR_NONE;
         message->errorIndex = 0;
         break;

      case ERROR_OBJECT_NOT_FOUND:
      case ERROR_INSTANCE_NOT_FOUND:
      case ERROR_ACCESS_DENIED:
         //Return noAccess status code
         message->errorStatus = SNMP_ERROR_NO_ACCESS;
         message->errorIndex = index;
         break;

      case ERROR_WRONG_TYPE:
         //Return wrongType status code
         message->errorStatus = SNMP_ERROR_WRONG_TYPE;
         message->errorIndex = index;
         break;

      case ERROR_WRONG_LENGTH:
         //Return wrongLength status code
         message->errorStatus = SNMP_ERROR_WRONG_LENGTH;
         message->errorIndex = index;
         break;

      case ERROR_WRONG_ENCODING:
         //Return wrongEncoding status code
         message->errorStatus = SNMP_ERROR_WRONG_ENCODING;
         message->errorIndex = index;
         break;

      case ERROR_WRONG_VALUE:
         //Return wrongValue status code
         message->errorStatus = SNMP_ERROR_WRONG_VALUE;
         message->errorIndex = index;
         break;

      case ERROR_READ_FAILED:
      case ERROR_WRITE_FAILED:
         //Return genError status code
         message->errorStatus = SNMP_ERROR_GENERIC;
         message->errorIndex = index;

         //Total number of SNMP PDUs which were generated by the SNMP protocol
         //entity and for which the value of the error-status field is genError
         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutGenErrs, 1);
         break;

      case ERROR_NOT_WRITABLE:
         //Return notWritable status code
         message->errorStatus = SNMP_ERROR_NOT_WRITABLE;
         message->errorIndex = index;
         break;

      case ERROR_BUFFER_OVERFLOW:
         //Return tooBig status code
         message->errorStatus = SNMP_ERROR_TOO_BIG;
         message->errorIndex = 0;

         //Total number of SNMP PDUs which were generated by the SNMP protocol
         //entity and for which the value of the error-status field is tooBig
         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutTooBigs, 1);
         break;

      default:
         //If the parsing of the request fails, the SNMP agent discards
         //the message and performs no further actions
         return status;
      }
   }

   //Successful processing
   return NO_ERROR;
}

#endif