text: /*
   *  gennt.c v0.14  May 15, 1996 
   *
   ****************************************************************************
   *                                                                          *
   *      (C) Copyright 1995 DIGITAL EQUIPMENT CORPORATION                    *
   *                                                                          *
   *      This  software  is  an  unpublished work protected under the        *
   *      the copyright laws of the  United  States  of  America,  all        *
   *      rights reserved.                                                    *
   *                                                                          *
   *      In the event this software is licensed for use by the United        *
   *      States Government, all use, duplication or disclosure by the        *
   *      United States Government is subject to restrictions  as  set        *
   *      forth in either subparagraph  (c)(1)(ii)  of the  Rights  in        *
   *      Technical  Data  And  Computer  Software  Clause  at   DFARS        *
   *      252.227-7013, or the Commercial Computer Software Restricted        *
   *      Rights Clause at FAR 52.221-19, whichever is applicable.            *
   *                                                                          *
   ****************************************************************************
   *
   *  Facility:
   *
   *    SNMP Extension Agent
   *
   *  Abstract:
   *
   *    This module contains the code for dealing with the generic logic for
   *    processing the SNMP request.  It is table driven.  No user modification
   *    should be done.
   *
   *  Functions:
   *
   *    ResolveVarBind()
   *    FindClass()
   *    ResolveGetNext()
   *
   *  Author:
   *    Miriam Amos Nihart, Kathy Faust
   *
   *  Date:
   *      2/17/95
   *
   *  Revision History:
   *  6/22/95  krw0001  FindClass - modify to stop checking for valid variable - we only care about valid
   *                              class.
   *                              Rewrite ResolveGetNext
   *  6/26/95  ags      FindClass - stop checking for valid variable
   *                              Rewrite ResolveGetNext
   *  7/31/95  ags      SNMP_oidfree works with CRTDLL.lib, hence use them.
   *  2/14/96  ags   v0.11    one fix for the getnext bug found by Cindy
   *  3/19/96  kff   v0.12    modified for trap support
   *  4/19/96  ags   v0.13    Modified to get rid of trap.c in case of no traps.
   *  5/15/96  cs    v0.14    Modified FindClass in the backward walkthru to tighten
   *                      up the verification
   */
  
  
  #include <windows.h>
  #include <malloc.h>
  #include <stdio.h>
  
  #include <snmp.h>
  
  #include "mib.h"
  #include "mib_xtrn.h"
  #include "smint.h"
  
  extern DWORD dwTimeZero ;
  
  UINT
  SnmpUtilOidMatch(AsnObjectIdentifier *pOid1, AsnObjectIdentifier *pOid2)
  {
      unsigned long int nScan = min(pOid1->idLength, pOid2->idLength);
      unsigned long int i;
  
      for (i = 0; i < nScan; i++)
      {
          if (pOid1->ids[i] != pOid2->ids[i])
              break;
      }
  
      return i;
  }
  
  
  
  /*
   *  ResolveVarBind
   *
   *    Resolves a single variable binding.  Modifies the variable value pair
   *    on a GET or a GET-NEXT.
   *
   *  Arguments:
   *
   *    VarBind                    pointer to the variable value pair
   *    PduAction                  type of request - get, set, or getnext
   *
   *  Return Codes:
   *
   *    Standard PDU error codes.
   *
   */
  
  UINT
  ResolveVarBind( IN OUT RFC1157VarBind *VarBind , // Variable Binding to resolve
                  IN UINT PduAction )              // Action specified in PDU
  
  {
      unsigned long int cindex ;  // index into the class info table
      unsigned long int vindex ;  // index into the class's var table
      UINT instance_array[ MAX_STRING_LEN ] ;
      UINT status ;
      UINT result ;               // SNMP PDU error status
      AsnObjectIdentifier instance ;
      InstanceName native_instance ;
  
      instance.ids = instance_array ;
  
      // Determine which class the VarBind is for
  
      status = FindClass( VarBind, &cindex ) ;
      if ( status )
      {
          if ( PduAction != MIB_ACTION_GETNEXT )
          {
              // Check for valid variable as this is a get or set
  
              CHECK_VARIABLE( VarBind, cindex, vindex, status ) ;
              if ( !status )
                  return SNMP_ERRORSTATUS_NOSUCHNAME ;
  
              // Check for valid instance
  
              status = ( *class_info[ cindex ].FindInstance )
                       ( (ObjectIdentifier *)&(VarBind->name) ,
                         (ObjectIdentifier *)&instance ) ;
              if ( status != SNMP_ERRORSTATUS_NOERROR )
                  return status ;
  
              // Check for access
  
              CHECK_ACCESS( cindex, vindex, PduAction, status ) ;
              if ( !status )
              {
                  if ( PduAction == MIB_ACTION_SET )
                      return SNMP_ERRORSTATUS_NOTWRITABLE ;
                  else
                      return SNMP_ERRORSTATUS_GENERR ;
              }
  
              // Ok to do the get or set
  
              if ( PduAction == MIB_ACTION_GET )
              {
                  status = ( *class_info[ cindex ].ConvertInstance )
                           ( (ObjectIdentifier *)&instance, &native_instance ) ;
                  if ( status == FAILURE )
                      return SNMP_ERRORSTATUS_GENERR ;
  
                  result = ( *class_info[ cindex ].variable[ vindex].SMIGet )
                           ( VarBind , cindex, vindex, &native_instance ) ;
              }
              else
              {
                  if ( VarBind->value.asnType !=
                       class_info[ cindex ].variable[ vindex ].type )
                      return SNMP_ERRORSTATUS_BADVALUE ;
  
                  status = ( *class_info[ cindex ].ConvertInstance )
                           ( (ObjectIdentifier *)&instance, &native_instance ) ;
                  if ( status == FAILURE )
                      return SNMP_ERRORSTATUS_GENERR ;
  
                  result = ( *class_info[ cindex ].variable[ vindex ].SMISet )
                           ( VarBind, cindex, vindex, &native_instance ) ;
              }
          }
          else  // This is a GETNEXT
          {
              //
              //  Call ResolveGetNext() to determine which class, variable, and
              //  instance to do a Get on.
              //
  
              status = ResolveGetNext( VarBind, &cindex, &vindex, &instance ) ;
              if ( status == SUCCESS )
              {
                  status = ( *class_info[ cindex ].ConvertInstance )
                           ( (ObjectIdentifier *)&instance, &native_instance ) ;
                  if ( status == FAILURE )
                      return SNMP_ERRORSTATUS_GENERR ;
  
                  result = ( *class_info[ cindex ].variable[ vindex ].SMIGet )
                           ( VarBind, cindex, vindex, &native_instance ) ;
              }
              else
                  return SNMP_ERRORSTATUS_NOSUCHNAME ;
          }
  
      }
      else
      {
          //
          // No class found, but its a GETNEXT.. we need to find the class that has the longest
          // with the requested oid and forward the request to it
          //
  
          if (PduAction == MIB_ACTION_GETNEXT)
          {
              unsigned long int ci;               // index into the class info table
              unsigned long int nLongestMatch;    // max number of ids that matched between names
              unsigned long int nCurrentMatch;    // matching number of IDs at the current iteration
  
              // scan the class_info table, relying on the fact that the table is ordered
              // ordered ascendingly on the class OID.
              for (ci = 0, nLongestMatch = 0; ci < CLASS_TABLE_MAX; ci++)
              {
                  // pick up the number of matching ids between the VarBind and the class name..
                  nCurrentMatch = SnmpUtilOidMatch(&VarBind->name, class_info[ci].oid);
  
                  // store in cindex the first class with the highest match number
                  if (nCurrentMatch > nLongestMatch)
                  {
                      cindex = ci;
                      nLongestMatch = nCurrentMatch;
                  }
              }
  
  
              // only if VarBind name is longer than the match number we need to look
              // for an even better match
              if (VarBind->name.idLength > nLongestMatch)
              {
                  for (;cindex < CLASS_TABLE_MAX; cindex++)
                  {
                      // make sure we don't go over the range with the longest Match
                      if (SnmpUtilOidMatch(&VarBind->name, class_info[cindex].oid) != nLongestMatch)
                          break;
  
                      // if the class matches entirely into the VarBind name, check if the first ID
                      // that follows in VarBind name is inside the range supported by the class
                      if (class_info[cindex].oid->idLength == nLongestMatch)
                      {
                          // this is a hack - we rely the var_index is always 1 more than number of ids in
                          // the class_info name. Since VarBind has already a name longer than nLongestMatch
                          // no buffer overrun happens here.
                          // if the VarBind name is in the right range, then we found the class - just break the loop
                          // (don't forget, var_index is '1' based)
                          if(VarBind->name.ids[class_info[cindex].var_index - 1] <= class_info[cindex].max_index)
                              break;
                      }
                      else
                      {
                          // the VarBind name is longer than the IDs that match, the class_info name is the same
                          // the first ID that follows in both names can't be equal, so we can break the loop if
                          // the VarBind name is just in front of it.
                          if (VarBind->name.ids[nLongestMatch] < class_info[cindex].oid->ids[nLongestMatch])
                              break;
                      }
  
                  }
              }
  
              if (cindex < CLASS_TABLE_MAX )
                     vindex = class_info[cindex].min_index ;
              else
                     return SNMP_ERRORSTATUS_NOSUCHNAME ;
  
              SNMP_oidfree( &VarBind->name ) ;
              if (! SNMP_oidcpy( &VarBind->name ,
                           class_info[ cindex ].variable[ vindex ].oid ) )
              {
                  return SNMP_ERRORSTATUS_GENERR ;
              }
              status = ResolveGetNext( VarBind, &cindex, &vindex, &instance ) ;
              if ( status == SUCCESS )
              {
                  status = ( *class_info[ cindex ].ConvertInstance )
                           ( (ObjectIdentifier *)&instance, &native_instance ) ;
                  if ( status == FAILURE )
                      return SNMP_ERRORSTATUS_GENERR ;
  
                  result = ( *class_info[ cindex ].variable[ vindex ].SMIGet )
                           ( VarBind, cindex, vindex, &native_instance ) ;
              }
              else
              {
                  return SNMP_ERRORSTATUS_NOSUCHNAME ;
              }
          }
          else
              return SNMP_ERRORSTATUS_NOSUCHNAME ;
      }
  
      ( *class_info[ cindex ].FreeInstance )( &native_instance ) ;
      return result ;
  
  } /* end of ResolveVarBind() */
  
  
  
  /*
   *  FindClass
   *
   *    This routine determines the class by walking the class_info table
   *    backwards and comparing the class oids.  The table is walked
   *    backwards because it assumes that the classes are listed in
   *    increasing order.  For example,
   *
   *    Group Name              Group Identifier
   *
   *    group1                  1.3.6.1.4.1.36.2.78
   *    table1                  1.3.6.1.4.1.36.2.78.9
   *    table2                  1.3.6.1.4.1.36.2.78.10
   *
   *    We need to look for the longest exact match on the oid thus we
   *    walk the table backwards.
   *
   *  Arguments:
   *
   *     VarBind                 Variable value pair
   *     class                   Index into the class_info
   *
   *  Return Codes:
   *
   *     SUCCESS                 Class is valid, return index into class_info
   *     FAILURE                 Invalid class
   *
   */
  
  UINT
  FindClass( IN RFC1157VarBind *VarBind ,
             IN OUT UINT *cindex )
  {
      int index ;
      UINT status, vindex ;
      UINT length ;
  
      for ( index = CLASS_TABLE_MAX - 1 ; index >= 0 ; index-- )
      {
          if ( class_info[ index ].table )
              // skip over the entry code -- kkf, why?
  //            length = class_info[ index ].var_index - 2 ;
              length = class_info[ index ].var_index - 1 ;
          else
              length = class_info[ index ].var_index - 1 ;
          status = SNMP_oidncmp( &VarBind->name ,
                                 class_info[ index ].oid ,
                                 length ) ;
  
          // if the oid don't match the class or it is shorter than the
          // class go on to the next one.
          // If the oid requested is shorter than the class we can't stop
          // otherwise we'll point to a wrong (longest match) class.
          if (status != 0 ||
              VarBind->name.idLength < class_info[ index ].var_index)
              continue;
  
          vindex = VarBind->name.ids[ class_info[ index ].var_index - 1 ] ;
          // cs - added the vindex verification to make sure that the varbind
          // oid definitely belongs in this class (fixed partial table oids)
          
          if ( vindex >= class_info[ index ].min_index &&
               vindex <= class_info[ index ].max_index)
          {
              *cindex = index ;
              return SUCCESS ;
          }
      }
  
      //  Failed to match by walking list backwards (longest match)
      //  so OID supplied is shorter than expected (e.g., partial OID supplied)
      //  Try matching by forward walking...
      for (index = 0; index < CLASS_TABLE_MAX; index++ ) {
          status = SNMP_oidncmp( &VarBind->name ,
                                 class_info[ index ].oid ,
                                 VarBind->name.idLength ) ;
          if ( status == 0 ) {
              *cindex = index ;
              return SUCCESS ;
          }
      }
  
      return FAILURE ;
  
  } /* end of FindClass() */
  
  
  
  /*
   *   ResolveGetNext
   *
   *     Determines the class, the variable and the instance that the
   *     GetNext request is to be performed on.  This is a recursive
   *     routine.  The input arguments VarBind and class may be modified
   *     as part of the resolution.
   *
   *     The rules for getnext are:
   *       1. No instance and no variable specified so return the first
   *          variable for the first instance.
   *       2. No instance specified but a variable is specified so return
   *          the variable for the first instance.
   *       3. An instance and a variable are specified
   *              Follow 3a,4b for  Non Tables
   *              Follow 3b, 4b, 5b for  Tables
   *
   *       3a.Return the next variable for the instance.
   *       4a.An instance and a variable are specified but the variable is the
   *          last variable in the group so return the first variable for the
   *          next group.
   *          If there is no next group return FAILURE.
   *
   *       3b. Return the variable for the next instance ( walk down the column).
   *       4b. Reached the bottom of the column, start at the top of next column.
   *       5b. An instance and a variable are specified but it is the last 
   *          variable and the last instace so roll to the next group (class).
   *          If there is no next group return FAILURE.
   *
   *  Arguments:
   *
   *     VarBind                 Variable value pair
   *     cindex                  Index into the class_info
   *     vindex                  address to specify variable for the get
   *
   *  Return Codes:
   *
   *     SUCCESS                 Able to resolve the request to a class, variable
   *                             and instance
   *     FAILURE                 Unable to resolve the request within this  MIB
   *
   */
  
  UINT
  ResolveGetNext( IN OUT RFC1157VarBind *VarBind ,
                  IN OUT UINT *cindex ,
                  IN OUT UINT *vindex ,
                  OUT AsnObjectIdentifier *instance )
                  {
      UINT status ;
      access_mode_t tmpAccess ;
  
      
      /*
       * We've come in with a pointer to the class, to start with
       * Do we have a variable specified?
       */
      
      *vindex = 0 ;
      if (VarBind->name.idLength < class_info[ *cindex ].var_index )  {
          /*
           * No variable specified. so pick the first variable (if it exists)
           * to start the search for a valid variable.
           * If not roll over to the next class.
           * Instnace is 0 for non Tables, and the first instance for Tables.
           */
  
          if ( class_info[ *cindex ].min_index <= class_info[ *cindex ].max_index)   {
  
              *vindex = class_info[ *cindex ].min_index ;
              goto StartSearchAt;
          } else  {
              goto BumpClass;
          }
  
      } else {
          /*
           * Yes, a variable is specified.
           * If it is below min_index, start testing for a valid variable at the min_index.
           * If it is ablove max_index roll over to the next class.
           * If we change the variable, Instance is reset to the first (or the only) Instance.
           */
          *vindex = VarBind->name.ids[ class_info[ *cindex ].var_index - 1 ] ;
          
          if ( *vindex < class_info[ *cindex ].min_index) {
              *vindex = class_info[ *cindex ].min_index ;
              goto StartSearchAt;
          }
          
          if ( *vindex > class_info[ *cindex ].max_index)
              goto BumpClass;
          /*
           * A valid variable for this class is specified. Table & NonTable are treated
           * differently.
           * In case of Non Tables:
           *      if instance is specified, we start the serach for a valid variable at the
           *              next variable.
           *      if no instnace is specified, we start search at the specified variable.
           *
           * In case of Tables:
           * We may have
           *      a. No Instance              start at the 1st Instance
           *      b. Partial instance         start at the 1st Instance
           *      c. Invalid instance         start at the 1st Instance
           *      d. Valid Instance           start at the next Instance
           * All these cases will be handled by the FindNextInstance
           * Hence first check that access of the given vaiable, if it is readable
           * get the Next Instance. If not start the search for a valid variable at the next
           * variable.
           */
  
          if ( class_info[ *cindex ].table == NON_TABLE ) {
              /* test for the Instance */
              if ( VarBind->name.idLength > class_info[ *cindex ].var_index)
                  (*vindex)++ ;
  
              goto StartSearchAt;
          } else {
              /* Start Table case */
              tmpAccess =  class_info[ *cindex ].variable[ *vindex ].access_mode ;
              if ( ( tmpAccess == NSM_READ_ONLY ) || (tmpAccess == NSM_READ_WRITE) ) {
                  /*
                   * readable Variable,  walk down the column
                   */
                  status = ( *class_info[ *cindex ].FindNextInstance )
                          ( (ObjectIdentifier *)&(VarBind->name) ,
                          (ObjectIdentifier *)instance ) ;
              
                  if (status == SNMP_ERRORSTATUS_NOERROR) {           
                      SNMP_oidfree ( &VarBind->name ) ;
                      if (! SNMP_oidcpy ( &VarBind->name,
                                  class_info[*cindex ].variable[*vindex].oid ) )
                      {
                          return FAILURE;
                      }
                      if (!SNMP_oidappend ( &VarBind->name, instance ))
                      {
                          return FAILURE;
                      }
                      return SUCCESS ;                   
                      /* we are all done   */
                  }
              }
              /*
               * Either at end of the column, or variable specified is non Readable.
               * Hence we need to move to the next column,
               * This means we start at the 1st instnace.
               */
              (*vindex)++ ;
              goto StartSearchAt;
              /* End Table case */
          }
          /* end of variable specified case */
      }
  StartSearchAt:
      /*
       * We have a start variable in *vindex.
       * At this point we are moving to the next column in case of a Table
       * Hence if we can't find an NextInstance ( empty Table), move to the
       * next class.
       */
       status = FAILURE;
       while ( *vindex <= class_info[ *cindex ].max_index)  {
  
          tmpAccess =  class_info[ *cindex ].variable[ *vindex ].access_mode ;
          if ( ( tmpAccess == NSM_READ_ONLY ) || (tmpAccess == NSM_READ_WRITE) ) {
              status = SUCCESS;
              break;
          } else  {
              (*vindex)++;
          }
       }
  
       if ( status == SUCCESS) {
          /*
           * we hava a valid variable, get the instance
           */
          SNMP_oidfree ( &VarBind->name ) ;
          if (!SNMP_oidcpy ( &VarBind->name, class_info[ *cindex ].variable[*vindex ].oid ))
          {
              return FAILURE;
          }
          if ( class_info[ *cindex ].table == NON_TABLE) {
          
              instance->ids[ 0 ] = 0 ;
              instance->idLength = 1 ;
              if (!SNMP_oidappend ( &VarBind->name, instance ))
              {
                  return FAILURE;
              }
              return SUCCESS ;
  
          } else {
  
              status = ( *class_info[ *cindex ].FindNextInstance )
                          ( (ObjectIdentifier *)&(VarBind->name) ,
                          (ObjectIdentifier *)instance ) ;
              
              if (status == SNMP_ERRORSTATUS_NOERROR) {           
                  if (!SNMP_oidappend ( &VarBind->name, instance ))
                  {
                      return FAILURE;
                  }
                  return SUCCESS ;
              }
          }
       }
  
  /*
   * Come here to move on to the next class
   */
  
  BumpClass:
      {
          (*cindex)++ ;
          if ( *cindex >= CLASS_TABLE_MAX)
              return FAILURE ;
          SNMP_oidfree( &VarBind->name );
          if (!SNMP_oidcpy ( &VarBind->name, class_info[ *cindex ].oid ))
          {
              return FAILURE;
          }
          status = ResolveGetNext( VarBind, cindex, vindex, instance) ;
          return status ;
      }
      
      // This oughtn't to happen
      return FAILURE ;
  } /* end of ResolveGetNext() */
  
  
  #ifndef TRAPS
  //
  // If there are no traps, TrapInit() is still needed. 
  // If there are traps, all this code appears in 
  // generated file trap.c
  //
  
  UINT number_of_traps = 0 ;
  
  trap_t
      trap_info[] = {
          { NULL, 0, 0, 0, NULL }
  } ;
  
  extern
  trap_t trap_info[] ;
  
  extern
  UINT number_of_traps ;
  
  extern HANDLE hEnabledTraps ;
  extern HANDLE hTrapQMutex ;
  
  /*
   *  TrapInit
   *
   *    This routine initializes the trap handle.
   *
   *  Arguments:
   *
   *    hPollForTrapEvent    handle for traps - this is used to coordinate
   *                         between the Extendible Agent and this Extension
   *                         Agent.
   *                             - NULL indicates no traps
   *                             - value from CreateEvent() indicates traps
   *                               are implemented and the Extendible agent
   *                               must poll for them
   *
   *  Return Code:
   *
   *    SUCCESS     Successful initialization
   *    FAILURE     Unable to initialize
   *
   |=========================================================================
   | There are no Traps associated with the HostMIB.  Consequently this
   | routine is taken over and used to create a handle to a timer rather
   | than an event.
   |
   | We want to be entered at "SnmpExtensionTrap()" (in "HOSTMIB.C") on
   | a periodic interval.  When entered, we won't really do any trap processing,
   | instead we'll refresh the cached information associated with SNMP
   | attribute "hrProcessorLoad" (in "HRPROCES.C") thru a call to function
   | "hrProcessLoad_Refresh()" (also in "HRPROCES.C").
   |
   | So the contents of this standard function is replaced.  (Note that the
   | "hTrapQMutex" is no longer created).
   */
  
  VOID
  TrapInit( IN OUT HANDLE *hPollForTrapEvent )
  {
  #if 0
      // The default value for traps is NULL indicating NO traps.
  
      *hPollForTrapEvent = NULL ;
      hTrapQMutex = NULL ;
  
      // Call to CreateEvent uses the default security descriptor (therefore
      // the handle is not inheritable), flags auto reset (no call to ResetEvent()
      // required), flags no signal to be sent at the initial state, and does
      // not specify a name for this event.
      //
      // If the CreateEvent() fails the value returned is NULL so traps
      // are not enabled.  Otherwise the setting of this event with will cause
      // the Extendible Agent to call this Extension Agent's SnmpExtensionTrap
      // routine to collect any traps.
  
      *hPollForTrapEvent = CreateEvent( NULL ,   // Address of security attrib
                                        FALSE ,  // Flag for manual-reset event
                                        FALSE ,  // Flag for initial state 
                                        NULL ) ; // Address of event-object name
  
      //
      // Save the handle in a global variable for use later in setting a trap.
      //
  
      hEnabledTraps = *hPollForTrapEvent ;
  
      //
      //  Create Mutex for assuring single thread access to enque/dequeue on trap_q
      hTrapQMutex = CreateMutex( NULL,  // Address of security attrib
                                 FALSE, // Mutex is not initially owned
                     NULL ) ; // Mutex is unnamed
  
      return ;
  #endif
  /*
  |========================
  | Special HostMIB code:
  */
  LARGE_INTEGER   due_time;       /* When the timer first goes off */
  LONG            period;         /* Frequency: every minute       */
  BOOL            waitable;       /* Status from SetWaitable()     */
  
  
      *hPollForTrapEvent = NULL ;
  
      /* Attempt the creation of a waitable timer . . . */
      *hPollForTrapEvent = CreateWaitableTimer(NULL,      // Security
                                               FALSE,     // = Auto-resetting
                                               NULL       // = No name
                                               );
  
      /*
      | Set a negative due time to mean "relative": We want it to go off
      | in 30 seconds.  Ticks are 100 ns or 1/10th of a millionth of a second.
      |
      */
      due_time.QuadPart = 10000000 * (-30);
  
      /*
      | Set the period in milliseconds to 1 minute.
      */
      period = 1000 * 60;
  
      /* If we actually managed to create it, start it */
      if (*hPollForTrapEvent != NULL) {
  
          waitable = 
              SetWaitableTimer(*hPollForTrapEvent,    // Handle to timer
                               &due_time,             // "Due Time" to go off
                               period,                // Length of period in ms.
                               NULL,                  // no completion routine
                               NULL,                  // no arg to comp. routine
                               FALSE                  // no power-resume in NT
                               );
          }
  
  } /* end of TrapInit() */
  
  #endif /* #ifndef TRAPS */
  