
/*++ BUILD Version: 0001    // Increment this if a change has global effects

Copyright (c) 1991  Microsoft Corporation

Module Name:

    dhcpmib.c

Abstract:

    DHCP SNMP Extension Agent for Windows NT.

    These files (dhcpmibm.c, dhcpmib.c, and dhcpmib.h) provide an example of 
    how to structure an Extension Agent DLL which works in conjunction with 
    the SNMP Extendible Agent for Windows NT.

    Extensive comments have been included to describe its structure and
    operation.  See also "Microsoft Windows/NT SNMP Programmer's Reference".

Created:

   15-Jan-1994 

Revision History:

        Pradeep Bahl                        1/11/94
--*/



// This Extension Agent implements the DHCP MIB.  It's  
// definition follows here:
//
//

// Necessary includes.

#include <stdio.h>
#include <stdlib.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <winbase.h>                         //for SYSTEMTIME def
#include <rpc.h>

#include "dhcpapi.h"

#include <string.h>
#include <time.h>
#include <search.h>


// Contains definitions for the table structure describing the MIB.  This
// is used in conjunction with winsmib.c where the MIB requests are resolved.

#include "dhcpmib.h"


// If an addition or deletion to the MIB is necessary, there are several
// places in the code that must be checked and possibly changed.
//
// The last field in each MIB entry is used to point to the NEXT
// leaf variable.  If an addition or deletetion is made, these pointers
// may need to be updated to reflect the modification.



#define LOCAL_ADD        L"127.0.0.1"


//
// Used by MIBStat
//
#define TMST(x)        x.wHour,\
                x.wMinute,\
                x.wSecond,\
                x.wMonth,\
                x.wDay,\
                x.wYear

#define PRINTTIME(Var, x)      sprintf(Var, "%02u:%02u:%02u on %02u:%02u:%04u.\n", TMST(x))

//
// All MIB variables in the common group have 1 as their first id
//
#define COMMON_VAL_M(pMib)         ((pMib)->Oid.ids[0] == 1) 

//
// All MIB variables in the scope group have 2 as their first id
//
#define SCOPE_VAL_M(pMib)         ((pMib)->Oid.ids[0] == 2) 

static LPDHCP_MIB_INFO        spMibInfo = NULL;

//
// The prefix to all of the DHCP MIB variables is 1.3.6.1.4.1.311.1.3
//
// The last digit -- 3 is for the DHCP MIB
//

UINT OID_Prefix[] = { 1, 3, 6, 1, 4, 1, 311, 1, 3 };
AsnObjectIdentifier MIB_OidPrefix = { OID_SIZEOF(OID_Prefix), OID_Prefix };
BOOL        fDhcpMibVarsAccessed = FALSE;
        



//
// Definition of the Dhcp MIB  (not used)
//

//UINT MIB_Dhcp[]  = { 3 };

//                        
// OID definitions for MIB
//                        

//
// Definition of group and leaf variables under the wins group
// All leaf variables have a zero appended to their OID to indicate
// that it is the only instance of this variable and it exists.
//

UINT MIB_Parameters[]                        = { 1 }; 
UINT MIB_DhcpStartTime[]                     = { 1, 1, 0 };
UINT MIB_NoOfDiscovers[]                = { 1, 2, 0 };
UINT MIB_NoOfRequests[]                        = { 1, 3, 0 };
UINT MIB_NoOfReleases[]                        = { 1, 4, 0 };
UINT MIB_NoOfOffers[]                        = { 1, 5, 0 };
UINT MIB_NoOfAcks[]                        = { 1, 6, 0 };
UINT MIB_NoOfNacks[]                        = { 1, 7, 0 };
UINT MIB_NoOfDeclines[]                        = { 1, 8, 0 };

//
// Scope table
//
UINT MIB_Scope[]                        = { 2 }; 
UINT MIB_ScopeTable[]                        = { 2, 1};
UINT MIB_ScopeTableEntry[]                = { 2, 1, 1};

//
//                             //
// Storage definitions for MIB //
//                             //
char            MIB_DhcpStartTimeStore[80];
AsnCounter MIB_NoOfDiscoversStore;
AsnCounter MIB_NoOfRequestsStore;
AsnCounter MIB_NoOfReleasesStore;
AsnCounter MIB_NoOfOffersStore;
AsnCounter MIB_NoOfAcksStore;
AsnCounter MIB_NoOfNacksStore;
AsnCounter MIB_NoOfDeclinesStore;





static
UINT 
MIB_Table(
        IN DWORD           Index, 
        IN UINT            Action,
        IN PMIB_ENTRY           pMibPtr,
        IN RFC1157VarBind *VarBind
        );

static
UINT 
ScopeTable(
        IN UINT           Action,
        IN PMIB_ENTRY           pMibPtr,
        IN RFC1157VarBind *VarBind
                );

static
UINT 
MIB_leaf_func(
        IN UINT           Action,
        IN MIB_ENTRY           *MibPtr,
        IN RFC1157VarBind *VarBind
        );


static
UINT 
MIB_Stat(
        IN UINT           Action,
        IN MIB_ENTRY           *MibPtr,
        IN RFC1157VarBind *VarBind
        );
static
DWORD 
GetMibInfo (
        LPWSTR                        DhcpAdd,
        LPDHCP_MIB_INFO                *ppMibInfo
        );
//
// MIB definiton
//

MIB_ENTRY Mib[] = {
//parameters
      { { OID_SIZEOF(MIB_Parameters), MIB_Parameters },
        NULL, ASN_RFC1155_OPAQUE,
        MIB_NOACCESS, NULL, &Mib[1] },

      { { OID_SIZEOF(MIB_DhcpStartTime), MIB_DhcpStartTime },
        &MIB_DhcpStartTimeStore, ASN_RFC1213_DISPSTRING,
        MIB_ACCESS_READ, MIB_Stat, &Mib[2] },

      { { OID_SIZEOF(MIB_NoOfDiscovers), MIB_NoOfDiscovers },
        &MIB_NoOfDiscoversStore, ASN_RFC1155_COUNTER,
        MIB_ACCESS_READ, MIB_Stat, &Mib[3] },

      { { OID_SIZEOF(MIB_NoOfRequests), MIB_NoOfRequests },
        &MIB_NoOfRequestsStore, ASN_RFC1155_COUNTER,
        MIB_ACCESS_READ, MIB_Stat, &Mib[4] },

      { { OID_SIZEOF(MIB_NoOfReleases), MIB_NoOfReleases },
        &MIB_NoOfReleasesStore, ASN_RFC1155_COUNTER,
        MIB_ACCESS_READ, MIB_Stat, &Mib[5] },

      { { OID_SIZEOF(MIB_NoOfOffers), MIB_NoOfOffers },
        &MIB_NoOfOffersStore, ASN_RFC1155_COUNTER,
        MIB_ACCESS_READ, MIB_Stat, &Mib[6] },

      { { OID_SIZEOF(MIB_NoOfAcks), MIB_NoOfAcks },
        &MIB_NoOfAcksStore, ASN_RFC1155_COUNTER,
        MIB_ACCESS_READ, MIB_Stat, &Mib[7] },

      { { OID_SIZEOF(MIB_NoOfNacks), MIB_NoOfNacks },
        &MIB_NoOfNacksStore, ASN_RFC1155_COUNTER,
        MIB_ACCESS_READ, MIB_Stat, &Mib[8] },

      { { OID_SIZEOF(MIB_NoOfDeclines), MIB_NoOfDeclines },
        &MIB_NoOfDeclinesStore, ASN_RFC1155_COUNTER,
        MIB_ACCESS_READ, MIB_Stat, &Mib[9] },

//
// Scope
//
      { { OID_SIZEOF(MIB_Scope), MIB_Scope },
        NULL, ASN_RFC1155_OPAQUE,
        MIB_NOACCESS, NULL, &Mib[10] },


      { { OID_SIZEOF(MIB_ScopeTable), MIB_ScopeTable },
        NULL, ASN_RFC1155_OPAQUE,
        MIB_ACCESS_READ, NULL, &Mib[11] },

      { { OID_SIZEOF(MIB_ScopeTableEntry), MIB_ScopeTableEntry },
        NULL, ASN_SEQUENCE,
        MIB_ACCESS_READ, ScopeTable, NULL }
      };



//
//  defines pertaining to tables
//
#define SCOPE_OIDLEN                 (MIB_PREFIX_LEN + OID_SIZEOF(MIB_ScopeTableEntry))        

#define NO_FLDS_IN_SCOPE_ROW        4
#define SCOPE_TABLE_INDEX        0

#define NUM_TABLES                sizeof(Tables)/sizeof(TAB_INFO_T)

UINT MIB_num_variables = sizeof Mib / sizeof( MIB_ENTRY );

//
// table structure containing the functions to invoke for different actions
// on the table
//
typedef struct _TAB_INFO_T {
        UINT (*ti_get)(
                RFC1157VarBind *VarBind
                     );
        UINT (*ti_getf)(
                RFC1157VarBind *VarBind,
                PMIB_ENTRY        pMibEntry
                     );
        UINT (*ti_getn)(
                RFC1157VarBind *VarBind,
                PMIB_ENTRY        pMibEntry
                    );
        UINT (*ti_set)(
                RFC1157VarBind *VarBind
                    );

        PMIB_ENTRY pMibPtr;
        } TAB_INFO_T, *PTAB_INFO_T;




static
UINT
ScopeGetNext(
       IN RFC1157VarBind *VarBind,
       IN PMIB_ENTRY          pMibPtr
        );


static
UINT
ScopeGet(
       IN RFC1157VarBind *VarBind
);


static
UINT
ScopeGetFirst(
       IN RFC1157VarBind *VarBind,
       IN PMIB_ENTRY          pMibPtr
        );


static
UINT
ScopeMatch(
       IN RFC1157VarBind          *VarBind,
       IN LPDWORD                  pIndex,
       IN LPDWORD                 pField,
       IN UINT                          PduAction,
       IN LPBOOL                 pfFirst
        );

extern
UINT
ScopeFindNext(
        INT                   SubnetIndex        
        );



static
int 
_CRTAPI1
CompareScopes(
        const VOID *pKey1,
        const VOID *pKey2
        );
static
UINT
GetNextVar(
        IN RFC1157VarBind *pVarBind,
        IN PMIB_ENTRY          pMibPtr
);

TAB_INFO_T Tables[] = {
                        ScopeGet, 
                        ScopeGetFirst, 
                        ScopeGetNext, 
                        NULL,
                        &Mib[11]
        };






UINT 
ResolveVarBind(
        IN OUT RFC1157VarBind *VarBind, // Variable Binding to resolve
        IN UINT PduAction               // Action specified in PDU
        )
//
// ResolveVarBind
//    Resolves a single variable binding.  Modifies the variable on a GET
//    or a GET-NEXT.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
{
   MIB_ENTRY            *MibPtr;
   AsnObjectIdentifier  TempOid;
   int                  CompResult;
   UINT                 I;
   UINT                 nResult;
   DWORD TableIndex;
   BOOL  fTableMatch = FALSE;

   //
   // Check the Tables array
   //
   // See if the prefix of the variable matches the prefix of
   // any of the tables
   //
   for (TableIndex = 0; TableIndex < NUM_TABLES; TableIndex++)
   {
        //
           // Construct OID with complete prefix for comparison purposes
        //
           SnmpUtilOidCpy( &TempOid, &MIB_OidPrefix );
           SnmpUtilOidAppend( &TempOid,  &Tables[TableIndex].pMibPtr->Oid );

        //
        // is there a match with the prefix oid of a table entry
        //
        if (
                SnmpUtilOidNCmp(
                            &VarBind->name, 
                             &TempOid,                
                             MIB_PREFIX_LEN + 
                                Tables[TableIndex].pMibPtr->Oid.idLength
                               )  == 0
           )
        {

                //
                // the prefix string of the var. matched the oid
                // of a table.
                //
                MibPtr = Tables[TableIndex].pMibPtr;
                fTableMatch = TRUE;
                break;
        }

           // Free OID memory before checking with another table entry
           SnmpUtilOidFree( &TempOid );
   }
   //
   // There was an exact match with a table entry's prefix. 
   //
   if ( fTableMatch)
   {
        
        if (
                (SnmpUtilOidCmp(
                        &VarBind->name, 
                        &TempOid
                               ) == 0)
           )
           {
           //
           // The oid specified is a prefix of a table entry. if the operation
           // is not GETNEXT, return NOSUCHNAME
           //
           if (PduAction != MIB_GETNEXT) 
           {
                           SnmpUtilOidFree( &TempOid );
                             nResult = SNMP_ERRORSTATUS_NOSUCHNAME;
                              goto Exit;
           }
           else
           {
                UINT           TableEntryIds[1];
                AsnObjectIdentifier TableEntryOid = { 
                                OID_SIZEOF(TableEntryIds), TableEntryIds };
                //
                // Replace var bind name with new name
                //

                //
                // A sequence item oid always starts with a field no.
                // The first item has a field no of 1. 
                //
                TableEntryIds[0] = 1;
                SnmpUtilOidAppend( &VarBind->name, &TableEntryOid);

                //
                // Get the first entry in the table
                //
                PduAction = MIB_GETFIRST;
           }
        }
           SnmpUtilOidFree( &TempOid );
        //
        //  if there was no exact match with a prefix entry, then we
        //  don't touch the PduAction value specified. 
        //
   }
   else
   {
        
      //
      // There was no match with any table entry.  Let us see if there is
      // a match with a group entry, a table, or a leaf variable
      //

      //
      // Search for var bind name in the MIB
      //
      I      = 0;
      MibPtr = NULL;
      while ( MibPtr == NULL && I < MIB_num_variables )
      {

         //
         // Construct OID with complete prefix for comparison purposes
         //
         SnmpUtilOidCpy( &TempOid, &MIB_OidPrefix );
         SnmpUtilOidAppend( &TempOid, &Mib[I].Oid );

         //
         //Check for OID in MIB - On a GET-NEXT the OID does not have to exactly
         // match a variable in the MIB, it must only fall under the MIB root.
         //
         CompResult = SnmpUtilOidCmp( &VarBind->name, &TempOid );

        //
        // If CompResult is negative, the only valid operation is GET_NEXT
        //
        if (  CompResult  < 0)
        {

                //
                // This could be the oid of a leaf (without a 0)
                // or it could be  an invalid oid (in between two valid oids) 
                // The next oid might be that of a group or a table or table
                // entry.  In that case, we do not change the PduAction
                //
                if (PduAction == MIB_GETNEXT)
                {
                       MibPtr = &Mib[I];
                             SnmpUtilOidFree( &VarBind->name );
                       SnmpUtilOidCpy( &VarBind->name, &MIB_OidPrefix );
                       SnmpUtilOidAppend( &VarBind->name, &MibPtr->Oid );
                       if (
                                (MibPtr->Type != ASN_RFC1155_OPAQUE)
                                         &&
                                (MibPtr->Type != ASN_SEQUENCE)
                          )
                       {
                                 PduAction = MIB_GET;
                       }
                }
                else
                {
                  nResult = SNMP_ERRORSTATUS_NOSUCHNAME;
                        SnmpUtilOidFree( &TempOid );
                  goto Exit;
                }

                      SnmpUtilOidFree( &TempOid );
                break;
      }
      else
      {
         //
         // An exact match was found ( a group, table, or leaf).
         //
         if ( CompResult == 0)
         {
            MibPtr = &Mib[I];
         }
      }

      //
      // Free OID memory before checking another variable
      //
      SnmpUtilOidFree( &TempOid );
      I++;
    } // while
   } // end of else

   //
   // if there was a match
   //
   if (fTableMatch || (I < MIB_num_variables))
   {
        //
        // the function will be NULL only if the match was with a group
        // or a sequence (table). If the match was with a table entry
        // (entire VarBind string match or partial string match), we
        // function would be a table function
        //
        if (MibPtr->MibFunc == NULL) 
        {
                if(PduAction != MIB_GETNEXT) 
                {
                              nResult = SNMP_ERRORSTATUS_NOSUCHNAME;
                              goto Exit;
                }
                else
                {
                        //
                        // Get the next variable which allows access 
                        //
                         nResult = GetNextVar(VarBind, MibPtr);
                        goto Exit;
                }
        }
   }
   else
   {
              nResult = SNMP_ERRORSTATUS_NOSUCHNAME;
              goto Exit;
   }

   //
   // Call function to process request.  Each MIB entry has a function pointer
   // that knows how to process its MIB variable.
   //
   nResult = (*MibPtr->MibFunc)( PduAction, MibPtr, VarBind );

Exit:
   return nResult;
} // ResolveVarBind
//
// MIB_leaf_func
//    Performs generic actions on LEAF variables in the MIB.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_leaf_func(
        IN UINT            Action,
        IN MIB_ENTRY            *MibPtr,
        IN RFC1157VarBind  *VarBind
        )

{
   UINT   ErrStat;

   switch ( Action )
   {
      case MIB_GETNEXT:
         //
         // If there is no GET-NEXT pointer, this is the end of this MIB
         //
         if ( MibPtr->MibNext == NULL )
         {
            ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
            goto Exit;
         }
         ErrStat = GetNextVar(VarBind, MibPtr);
         if (ErrStat != SNMP_ERRORSTATUS_NOERROR)
         {
                goto Exit;
         }
         break;

      case MIB_GETFIRST: // fall through 
      case MIB_GET:

         // Make sure that this variable's ACCESS is GET'able
         if ( MibPtr->Access != MIB_ACCESS_READ &&
              MibPtr->Access != MIB_ACCESS_READWRITE )
         {
               ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
               goto Exit;
         }

         // Setup varbind's return value
         VarBind->value.asnType = MibPtr->Type;
         switch ( VarBind->value.asnType )
         {
            case ASN_RFC1155_COUNTER:
               VarBind->value.asnValue.number = *(AsnCounter *)(MibPtr->Storage);
               break;
            case ASN_RFC1155_GAUGE:
            case ASN_INTEGER:
               VarBind->value.asnValue.number = *(AsnInteger *)(MibPtr->Storage);
               break;

            case ASN_RFC1155_IPADDRESS:
                                //
                                // fall through
                                //
                                
            case ASN_OCTETSTRING: 
               if (VarBind->value.asnType == ASN_RFC1155_IPADDRESS)
               {
                               VarBind->value.asnValue.string.length = 4;
               }
               else
               {
                               VarBind->value.asnValue.string.length =
                                 strlen( (LPSTR)MibPtr->Storage );
               }

               if ( NULL == 
                    (VarBind->value.asnValue.string.stream =
                    SnmpUtilMemAlloc(VarBind->value.asnValue.string.length *
                           sizeof(char))) )
               {
                  ErrStat = SNMP_ERRORSTATUS_GENERR;
                  goto Exit;
               }

               memcpy( VarBind->value.asnValue.string.stream,
                       (LPSTR)MibPtr->Storage,
                       VarBind->value.asnValue.string.length );
               VarBind->value.asnValue.string.dynamic = TRUE;
        
               break;

                

            default:
               ErrStat = SNMP_ERRORSTATUS_GENERR;
               goto Exit;
         }

         break;

      case MIB_SET:

         // Make sure that this variable's ACCESS is SET'able
         if ( MibPtr->Access != MIB_ACCESS_READWRITE &&
              MibPtr->Access != MIB_ACCESS_WRITE )
         {
            ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
            goto Exit;
         }

         // Check for proper type before setting
         if ( MibPtr->Type != VarBind->value.asnType )
         {
            ErrStat = SNMP_ERRORSTATUS_BADVALUE;
            goto Exit;
         }

         // Save value in MIB
         switch ( VarBind->value.asnType )
         {
            case ASN_RFC1155_COUNTER:
               *(AsnCounter *)(MibPtr->Storage) = VarBind->value.asnValue.number;
               break;
            case ASN_RFC1155_GAUGE:
            case ASN_INTEGER:
               *(AsnInteger *)(MibPtr->Storage) = VarBind->value.asnValue.number;
               break;

            case ASN_RFC1155_IPADDRESS:
                                //
                                // fall through
                                //
            case ASN_OCTETSTRING:
               // The storage must be adequate to contain the new string
               // including a NULL terminator.
               memcpy( (LPSTR)MibPtr->Storage,
                       VarBind->value.asnValue.string.stream,
                       VarBind->value.asnValue.string.length );

               ((LPSTR)MibPtr->Storage)[VarBind->value.asnValue.string.length] =
                                                                          '\0';
               if ( VarBind->value.asnValue.string.dynamic)
               {
                  SnmpUtilMemFree( VarBind->value.asnValue.string.stream);
               }
               break;

            default:
               ErrStat = SNMP_ERRORSTATUS_GENERR;
               goto Exit;
            }

         break;

      default:
         ErrStat = SNMP_ERRORSTATUS_GENERR;
         goto Exit;
      } // switch

   // Signal no error occurred
   ErrStat = SNMP_ERRORSTATUS_NOERROR;

Exit:
   return ErrStat;
} // MIB_leaf_func

//
// MIB_Stat
//    Performs specific actions on the different MIB variable
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_Stat(
        IN UINT           Action,
        IN MIB_ENTRY           *MibPtr,
        IN RFC1157VarBind *VarBind
        )

{
DWORD                        Status;
UINT                           ErrStat;
SYSTEMTIME                DhcpStartTime;



   switch ( Action )
      {
      case MIB_SET:
                   ErrStat = MIB_leaf_func( Action, MibPtr, VarBind );
                break;
      case MIB_GETNEXT:
                   ErrStat = MIB_leaf_func( Action, MibPtr, VarBind );
                break;
                
      case MIB_GETFIRST:
                //
                // fall through
                //
      case MIB_GET:
        //
        // Call the DhcpStatus function to get the statistics
        //
        Status = GetMibInfo(LOCAL_ADD, &spMibInfo);

        
        if (Status == ERROR_SUCCESS) 
        {
            if (FileTimeToSystemTime( 
                                (FILETIME *)&spMibInfo->ServerStartTime,  
                                &DhcpStartTime
                                   ) == FALSE)
            {
                goto Exit;
            }
           
            if (MibPtr->Storage  == &MIB_DhcpStartTimeStore) 
            {
                PRINTTIME(MIB_DhcpStartTimeStore, DhcpStartTime);
                goto LEAF1;
            }

            if (MibPtr->Storage  == &MIB_NoOfDiscoversStore) 
            {
                MIB_NoOfDiscoversStore =  spMibInfo->Discovers;
                goto LEAF1;
            }
            if (MibPtr->Storage  == &MIB_NoOfRequestsStore) 
            {
                MIB_NoOfRequestsStore =  spMibInfo->Requests;
                goto LEAF1;
            }
            if (MibPtr->Storage  == &MIB_NoOfReleasesStore) 
            {
                MIB_NoOfReleasesStore =  spMibInfo->Releases;
                goto LEAF1;
            }
            if (MibPtr->Storage  == &MIB_NoOfOffersStore) 
            {
                MIB_NoOfOffersStore =  spMibInfo->Offers;
                goto LEAF1;
            }
            if (MibPtr->Storage  == &MIB_NoOfAcksStore) 
            {
                MIB_NoOfAcksStore =  spMibInfo->Acks;
                goto LEAF1;
            }
            if (MibPtr->Storage  == &MIB_NoOfNacksStore) 
            {
                MIB_NoOfNacksStore =  spMibInfo->Naks;
                goto LEAF1;
            }
            if (MibPtr->Storage  == &MIB_NoOfDeclinesStore) 
            {
                MIB_NoOfNacksStore =  spMibInfo->Naks;
                goto LEAF1;
            }
            ErrStat = SNMP_ERRORSTATUS_GENERR;
            goto Exit;
         }
         else
         {
           printf("Error from DhcpStatus = (%d)\n", Status);
           ErrStat = SNMP_ERRORSTATUS_GENERR;
           goto Exit;
         }        
LEAF1:
         // Call the more generic function to perform the action
         ErrStat = MIB_leaf_func( Action, MibPtr, VarBind );

         break;

      default:
         ErrStat = SNMP_ERRORSTATUS_GENERR;
         goto Exit;
      } // switch

Exit:
   return ErrStat;
} // MIB_Stat



DWORD GetMibInfo (
        LPWSTR                        DhcpAdd,
        LPDHCP_MIB_INFO                *ppMibInfo
        )
{
        DWORD  Status = ERROR_SUCCESS;
        if (!fDhcpMibVarsAccessed)
        {
           //
           // The Dhcp server does a single node allocation. So we
           // we need to free only spMibInfo.
           //
           if (spMibInfo != NULL)
           {
#if 0
                if (spMibInfo->ScopeInfo != NULL)
                {
                        DhcpRpcFreeMemory(spMibInfo->ScopeInfo);
                        spMibInfo->ScopeInfo = NULL; 
                }
#endif
                DhcpRpcFreeMemory(spMibInfo);
                spMibInfo            = NULL;
           }
           Status = DhcpGetMibInfo(LOCAL_ADD, &spMibInfo);
           if (Status == ERROR_SUCCESS)
           {
                if (spMibInfo->Scopes > 1)
                {
          ASSERT(spMibInfo->ScopeInfo != NULL);
                  qsort(spMibInfo->ScopeInfo,(size_t)spMibInfo->Scopes,
                        sizeof(SCOPE_MIB_INFO),CompareScopes );
                }
                fDhcpMibVarsAccessed = TRUE;
           }
           else
           {
                fDhcpMibVarsAccessed = FALSE;
           }
        }
        return(Status);
}

int 
_CRTAPI1
CompareScopes(
        const VOID *pKey1,
        const VOID *pKey2
        )

{
        const LPSCOPE_MIB_INFO        pScopeKey1 = (LPSCOPE_MIB_INFO)pKey1;
        const LPSCOPE_MIB_INFO        pScopeKey2 = (LPSCOPE_MIB_INFO)pKey2;

        return(pScopeKey1->Subnet - pScopeKey2->Subnet);
}

UINT
GetNextVar(
        IN RFC1157VarBind *pVarBind,
        IN MIB_ENTRY          *pMibPtr
)
{
       UINT                ErrStat;

       while (pMibPtr != NULL)
       {
         if (pMibPtr->MibNext != NULL)
         {
            //
            // Setup var bind name of NEXT MIB variable
            //
            SnmpUtilOidFree( &pVarBind->name );
            SnmpUtilOidCpy( &pVarBind->name, &MIB_OidPrefix );
            SnmpUtilOidAppend( &pVarBind->name, &pMibPtr->MibNext->Oid );

            //
            // If the func. ptr is  NULL and the type of the mib variable
            // is anything but OPAQUE, call function to process the
            // MIB variable
            //
            if (
                 (pMibPtr->MibNext->MibFunc != NULL) 
                        && 
                 (pMibPtr->MibNext->Type !=  ASN_RFC1155_OPAQUE)
               )
                
            {
                ErrStat = (*pMibPtr->MibNext->MibFunc)( MIB_GETFIRST,
                                                pMibPtr->MibNext, pVarBind );
                if (ErrStat != SNMP_ERRORSTATUS_NOERROR)
                {
                        goto Exit;
                }
                break;
            }
            else
            {
                pMibPtr = pMibPtr->MibNext;        
            }
          }
          else
          {
            ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
            break;
          }
         } 

         if (pMibPtr == NULL)
         {
            ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
         }
Exit:
        return(ErrStat);
}

UINT 
ScopeTable(
        IN UINT           Action,
        IN MIB_ENTRY           *MibPtr,
        IN RFC1157VarBind *VarBind
)
{
        if (Action == MIB_SET)
        {
                return(SNMP_ERRORSTATUS_READONLY);
        }

        //
        // if the length indicates a 0 or partial key, then only the get next 
        // operation is allowed.  The field and the full key
        // have a length of 5
        //
        if (VarBind->name.idLength <= (SCOPE_OIDLEN + 4))
        {
                if ((Action == MIB_GET) || (Action == MIB_SET))
                {
                        return(SNMP_ERRORSTATUS_NOSUCHNAME);
                }
        }
        if (GetMibInfo(LOCAL_ADD, &spMibInfo) != ERROR_SUCCESS)
        {
                if (Action == MIB_GETNEXT)
                {
                        return(GetNextVar(VarBind, MibPtr));
                }
                else
                {
                        return(SNMP_ERRORSTATUS_GENERR);
                }
        }
        return( MIB_Table(SCOPE_TABLE_INDEX, Action, MibPtr, VarBind) );
}

UINT 
MIB_Table(
        IN DWORD            Index,
        IN UINT           Action,
        IN MIB_ENTRY           *MibPtr,
        IN RFC1157VarBind *VarBind
       )
{
        UINT        ErrStat;
        switch(Action)
        {
                case(MIB_GET):
                        ErrStat = (*Tables[Index].ti_get)(VarBind);
                        break;
                        
                case(MIB_GETFIRST):
                        ErrStat = (*Tables[Index].ti_getf)(VarBind, MibPtr);
                        break;

                case(MIB_GETNEXT):
                        ErrStat = (*Tables[Index].ti_getn)(VarBind, MibPtr);
                        break;
                case(MIB_SET):
                        ErrStat = (*Tables[Index].ti_set)(VarBind);
                        break;
                default:
                        ErrStat = SNMP_ERRORSTATUS_GENERR;
                        break;

        }

        return(ErrStat);

}  //MIB_Table



UINT
ScopeGet(
       IN RFC1157VarBind *VarBind
    )
{
        UINT                ErrStat = SNMP_ERRORSTATUS_NOERROR;
        DWORD           Field;
        DWORD                Index;
        LPSCOPE_MIB_INFO pScope = spMibInfo->ScopeInfo;


        ErrStat = ScopeMatch(VarBind, &Index, &Field, MIB_GET, NULL);
             if (ErrStat != SNMP_ERRORSTATUS_NOERROR)
             {
                return(ErrStat);
             }

        switch(Field)
        {
                case 1:                //subnet itself

                        VarBind->value.asnType        = ASN_RFC1155_IPADDRESS;
                               VarBind->value.asnValue.string.length = sizeof(ULONG);
                        
                               if ( NULL == 
                                    (VarBind->value.asnValue.string.stream =
                                    SnmpUtilMemAlloc(VarBind->value.asnValue.string.length
                                   )) )
                          {
                                  ErrStat = SNMP_ERRORSTATUS_GENERR;
                                  goto Exit;
                          }
                        
                        //
                        // SNMP expects the MSB to be in the first byte, MSB-1
                        // to be in the second, ....
                        //
                        VarBind->value.asnValue.string.stream[0] =
                                        (BYTE)((pScope + Index)->Subnet >> 24);
                        VarBind->value.asnValue.string.stream[1] =
                                (BYTE)(((pScope + Index)->Subnet >> 16) & 0xFF);
                        VarBind->value.asnValue.string.stream[2] =
                                (BYTE)(((pScope + Index)->Subnet >> 8) & 0xFF);
                        VarBind->value.asnValue.string.stream[3] =
                                (BYTE)((pScope + Index)->Subnet & 0xFF );
                        VarBind->value.asnValue.address.dynamic = TRUE;
                        break;

                case 2:                // NumAddressesInUse
                        VarBind->value.asnType        = ASN_RFC1155_COUNTER;
                               VarBind->value.asnValue.number = 
                                        (AsnCounter)((pScope + Index)->
                                                        NumAddressesInuse);
                               break;
                case 3:                // NumAddressesFree
                        VarBind->value.asnType        = ASN_RFC1155_COUNTER;
                               VarBind->value.asnValue.number = 
                                        (AsnCounter)((pScope + Index)->
                                                        NumAddressesFree);
                               break;
                case 4:                // NumPendingOffers
                        VarBind->value.asnType        = ASN_RFC1155_COUNTER;
                               VarBind->value.asnValue.number = 
                                        (AsnCounter)((pScope + Index)->
                                                        NumPendingOffers);
                               break;

                default:
                        ErrStat = SNMP_ERRORSTATUS_BADVALUE;
                        break;

        }
Exit:
        return(ErrStat); 
} // ScopeGet 

          
UINT
ScopeGetNext(
       IN RFC1157VarBind *VarBind,
       IN MIB_ENTRY          *MibPtr
          )
{
     DWORD          OidIndex;
     INT            Index;
     DWORD         FieldNo;
     UINT         ErrStat = SNMP_ERRORSTATUS_NOERROR;
     BOOL         fFirst;
     LPSCOPE_MIB_INFO pScope = spMibInfo->ScopeInfo;
        

     //
     // Check if the name passed matches any in the table (i.e. table of
     // of ADD_KEY_T structures.  If there is a match, the address
     // of the ip address key and the matching field's no. are returned 
     //
     ErrStat = ScopeMatch(VarBind,  &Index,  &FieldNo,  MIB_GETNEXT, &fFirst); 
     if (        
                (ErrStat != SNMP_ERRORSTATUS_NOERROR)
                        &&
                (ErrStat != SNMP_ERRORSTATUS_NOSUCHNAME)
        )
     {
                return(GetNextVar(VarBind, MibPtr));
     }

     //
     // We were passed an oid that is less than all oids in the table. Set
     // the Index to -1 so that we retrieve the first record in the table
     //
     if (fFirst)
     {
        Index = -1;
     }
     //
     // Since the operation is GETNEXT, get the next IP address (i.e. one
     // that is lexicographically bigger.  If there is none, we must increment
     // the field value and move back to the lexically first item in the table
     // If the new field value is more than the largest supported, we call
     // the MibFunc of the next MIB entry.
     //
     if ((Index = ScopeFindNext(Index)) < 0) 
     {
          
          //
          // if we were trying to retrieve the second or subsequent record
          // we must increment the field number nd get the first record in 
          // the table.  If we were retrieving the first record, then 
          // we should get the next var.
          //
          if (!fFirst)
          {
            Index = ScopeFindNext(-1);
          }
          else
          {
                return(GetNextVar(VarBind, MibPtr));
          }

          //
          // If either there is no entry in the table or if we have
          // exhausted all fields of the entry, call the function
          // of the next mib entry.
          //
          if (
                (++FieldNo > NO_FLDS_IN_SCOPE_ROW) || (Index < 0)
             )
          {                
                return(GetNextVar(VarBind, MibPtr));
          }
     }
                
     if (VarBind->name.idLength <= (SCOPE_OIDLEN + 4))
     {
         UINT TableEntryIds[5];  //field and subnet mask have a length of 5
         AsnObjectIdentifier  TableEntryOid = {OID_SIZEOF(TableEntryIds),
                                             TableEntryIds };
         SnmpUtilOidFree( &VarBind->name);
         SnmpUtilOidCpy(&VarBind->name, &MIB_OidPrefix);
         SnmpUtilOidAppend(&VarBind->name, &MibPtr->Oid);
         TableEntryIds[0] = (UINT)FieldNo;
         OidIndex                  = 1;
         TableEntryIds[OidIndex++] = (UINT)((pScope + Index)->Subnet >> 24);
         TableEntryIds[OidIndex++] = (UINT)((pScope + Index)->Subnet >> 16 & 0xFF);
         TableEntryIds[OidIndex++] = (UINT)((pScope + Index)->Subnet >> 8 & 0xFF);
         TableEntryIds[OidIndex++] = (UINT)((pScope + Index)->Subnet & 0xFF);
         TableEntryOid.idLength    = OidIndex;
         SnmpUtilOidAppend(&VarBind->name, &TableEntryOid);
     }
     else
     {
        //
        // The fixed part of the objid is corect. Update the rest.
        //
        OidIndex = SCOPE_OIDLEN;
        VarBind->name.ids[OidIndex++] = (UINT)FieldNo;
        VarBind->name.ids[OidIndex++] = (UINT)((pScope + Index)->Subnet >> 24);
        VarBind->name.ids[OidIndex++] = (UINT)(((pScope + Index)->Subnet >> 16) & 0xFF);
        VarBind->name.ids[OidIndex++] = (UINT)(((pScope + Index)->Subnet >> 8) & 0xFF);
        VarBind->name.ids[OidIndex++] = (UINT)((pScope + Index)->Subnet & 0xFF);
        VarBind->name.idLength        = OidIndex;
    }

     //
     // Get the value
     //
     ErrStat = ScopeGet(VarBind);

     return(ErrStat);
}

  

UINT
ScopeMatch(
       IN RFC1157VarBind *VarBind,
       IN LPDWORD         pIndex,
       IN LPDWORD         pField,
       IN UINT                  PduAction,
       IN LPBOOL         pfFirst
        )
{
        DWORD                         OidIndex;
        DWORD                         Index;
        DWORD                         ScopeIndex;
        DWORD                          Add = 0;
        UINT                         ErrStat = SNMP_ERRORSTATUS_NOERROR;
        INT                          CmpVal;
        DWORD                         AddLen;
        LPSCOPE_MIB_INFO         pScope = spMibInfo->ScopeInfo;

        ASSERT(PduAction != MIB_SET);

        if (pfFirst != NULL)
        {
                *pfFirst = FALSE;
        } 
        //
        // If there are no keys, return error
        //
        if (spMibInfo->Scopes == 0)
        {
                if (PduAction == MIB_GETNEXT)
                {
                        *pfFirst = TRUE;
                }
                else
                {
                        ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
                }
                goto Exit;
        }

        //
        // fixed part of the PullPnr table entries
        //
        OidIndex = SCOPE_OIDLEN;

        //
        // if the field specified is more than the max. in the table entry
        // barf
        //
        if (
                (*pField = VarBind->name.ids[OidIndex++]) > 
                        (DWORD)NO_FLDS_IN_SCOPE_ROW
           )
        {
                if (PduAction == MIB_GETNEXT)
                {
                        *pIndex = spMibInfo->Scopes - 1;
                        goto Exit;
                }
                else
                {
                        ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
                        goto Exit;
                }
        }

        //
        // get the length of key specified
        //
        AddLen = VarBind->name.idLength - (SCOPE_OIDLEN + 1);
        
        ScopeIndex = OidIndex;
        for (Index = 0; Index < AddLen; Index++)
        {
           Add = Add | (((BYTE)(VarBind->name.ids[ScopeIndex++])) << (24 - (Index * 8)));
        } 

        //
        // Check if the address specified matches with one of the keys
        //
        for (Index = 0; Index < spMibInfo->Scopes; Index++, pScope++)
        {
                CmpVal = Add - pScope->Subnet;
                if (CmpVal == 0)
                {
                        *pIndex = Index;
                        return(SNMP_ERRORSTATUS_NOERROR);
                }
                else
                {
                        //
                        // if passed in value is greater, continue on to
                        // the next item.  The list is in ascending order
                        //
                        if (CmpVal > 0)
                        {
                                continue;
                        }
                        else
                        {
                                //
                                // the list element is > passed in value, 
                                // break out of the loop
                                //
                                break;
                        }
                }
        }

        //
        // if no match, but field is GetNext, return the (highest index - 1)
        // reached above.  This is because, ScopeFindNext will be called by
        // the caller 
        //
        if (PduAction == MIB_GETNEXT)
        {
                if (Index == 0)
                {
                        *pfFirst = TRUE;
                }
                else
                {
                        *pIndex = Index - 1;
                } 
                ErrStat =  SNMP_ERRORSTATUS_NOERROR;
        }
        else
        {
                ErrStat =  SNMP_ERRORSTATUS_NOSUCHNAME;
        }
Exit:
        return(ErrStat);
}

UINT
ScopeFindNext(
        INT           SubKeyIndex
        )
{
        DWORD i;
        LONG  nextif;
        LPSCOPE_MIB_INFO        pScope = spMibInfo->ScopeInfo;
        
        //
        // if SubKeyIndex is 0  or more, search for the key next to
        // the key passed.
        //
        for (nextif =  -1, i = 0 ; i < spMibInfo->Scopes; i++)
        {
                if (SubKeyIndex >= 0) 
                {
                        if (
                                (pScope + i)->Subnet <= 
                                        (pScope + SubKeyIndex)->Subnet
                           )
                        {
                           //
                           // This item is lexicographically less or equal, 
                           // continue 
                           //
                           continue;
                        }
                        else
                        {
                          //
                          // We found an item that is > than the item indicated
                          // to us.  Break out of the loop
                          //
                          nextif = i;
                          break;
                        }
                }
                else
                {

#if 0
                   //
                   // if we want the first entry, then continue until
                   // we get an entry that is lexicographically same or
                   // greater
                   //
                   if (
                        (nextif < 0) 
                           ||
                        (pScope + (i - 1))->Subnet < (pScope + nextif)->Subnet
                    )
                  {
                        nextif = i;
                  }
#endif
                    nextif = 0;
                    break;
                }
        }
        return(nextif);
}        

UINT
ScopeGetFirst(
       IN RFC1157VarBind *VarBind,
       IN MIB_ENTRY        *MibPtr
        )
{

        LPSCOPE_MIB_INFO pScope = spMibInfo->ScopeInfo;
        INT           Iface;
        UINT           TableEntryIds[5];
        AsnObjectIdentifier        TableEntryOid = { OID_SIZEOF(TableEntryIds),
                                                        TableEntryIds };
           UINT   ErrStat;

        
        //
        // If there is no entry in the table, go to the next MIB variable 
        //
        if (spMibInfo->Scopes == 0)
        {
                 return(GetNextVar(VarBind, MibPtr));
        }
        //
        // Get the first entry in the table
        //
        Iface = ScopeFindNext(-1);


        //
        // Write the object Id into the binding list and call get
        // func
        //
        SnmpUtilOidFree( &VarBind->name );
        SnmpUtilOidCpy( &VarBind->name, &MIB_OidPrefix );
        SnmpUtilOidAppend( &VarBind->name, &MibPtr->Oid );

        //
        // The fixed part of the objid is correct. Update the rest.
        //
        
        TableEntryIds[0] = 1;
        TableEntryIds[1] = (UINT)((pScope + Iface)->Subnet >> 24);
        TableEntryIds[2] = (UINT)(((pScope + Iface)->Subnet >> 16)  & 0xFF);
        TableEntryIds[3] = (UINT)(((pScope + Iface)->Subnet >> 8)  & 0xFF);
        TableEntryIds[4] = (UINT)((pScope + Iface)->Subnet & 0xFF);
        SnmpUtilOidAppend( &VarBind->name, &TableEntryOid );

        ErrStat = ScopeGet(VarBind);
        return(ErrStat);
}
                
