/*********************************************************************
*
* SNMP Module for Microchip TCP/IP Stack
*
*********************************************************************
* FileName: SNMP.c
* Dependencies: StackTsk.h
* UDP.h
* Processor: PIC18
* Complier: MCC18 v1.00.50 or higher
* HITECH PICC-18 V8.10PL1 or higher
* Company: Microchip Technology, Inc.
*
* Software License Agreement
*
* The software supplied herewith by Microchip Technology Incorporated
* (the Company) for its PICmicro® Microcontroller is intended and
* supplied to you, the Companys customer, for use solely and
* exclusively on Microchip PICmicro Microcontroller products. The
* software is owned by the Company and/or its supplier, and is
* protected under applicable copyright laws. All rights are reserved.
* Any use in violation of the foregoing restrictions may subject the
* user to criminal sanctions under applicable laws, as well as to
* civil liability for the breach of the terms and conditions of this
* license.
*
* THIS SOFTWARE IS PROVIDED IN AN AS IS CONDITION. NO WARRANTIES,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
* TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
* IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
* CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
*
* HiTech PICC18 Compiler Options excluding device selection:
* -FAKELOCAL -G -Zg -E -C
*
* Author Date Comment
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Nilesh Rajbharti 1/9/03 Original (Rev 1.0)
* Dan Cohen 12/11/03 Removed trap support by #define if not
* required to lower code space requirements
*
********************************************************************/
#define THIS_IS_SNMP_SERVER
#include <string.h>
#include "..\Include\snmp.h"
#include "..\Include\stacktsk.h"
#ifndef _WIN32
#include "..\Include\udp.h"
#include "..\Include\tick.h"
#include "..\Include\arptsk.h"
#endif
#include "..\Include\mpfs.h"
#if _WIN32
#include <windows.h>
#endif
#if !defined(STACK_USE_SNMP_SERVER)
#error SNMP module is not enabled.
#error If you do not want SNMP module, remove this file from your
#error project to reduce your code size.
#error If you do want SNMP module, make sure that STACK_USE_SNMP_SERVER
#error is defined in StackTsk.h file.
#endif
#define SNMP_V1 (0)
#define STRUCTURE (0x30)
#define ASN_INT (0x02)
#define OCTET_STRING (0x04)
#define ASN_NULL (0x05)
#define ASN_OID (0x06)
// SNMP specific variables
#define SNMP_IP_ADDR (0x40)
#define SNMP_COUNTER32 (0x41)
#define SNMP_GAUGE32 (0x42)
#define SNMP_TIME_TICKS (0x43)
#define SNMP_OPAQUE (0x44)
#define SNMP_NSAP_ADDR (0x45)
#define GET_REQUEST (0xa0)
#define GET_NEXT_REQUEST (0xa1)
#define GET_RESPONSE (0xa2)
#define SET_REQUEST (0xa3)
#define TRAP (0xa4)
#define IS_STRUCTURE(a) (a==STRUCTURE)
#define IS_ASN_INT(a) (a==ASN_INT)
#define IS_OCTET_STRING(a) (a==OCTET_STRING)
#define IS_OID(a) (a==ASN_OID)
#define IS_ASN_NULL(a) (a==ASN_NULL)
#define IS_GET_REQUEST(a) (a==GET_REQUEST)
#define IS_GET_NEXT_REQUEST(a) (a==GET_NEXT_REQUEST)
#define IS_GET_RESPONSE(a) (a==GET_RESPONSE)
#define IS_SET_REQUEST(a) (a==SET_REQUEST)
#define IS_TRAP(a) (a==TRAP)
#define IS_AGENT_PDU(a) (a==GET_REQUEST || \
a==GET_NEXT_REQUEST || \
a==SET_REQUEST)
typedef enum _SNMP_ERR_STATUS
{
SNMP_NO_ERR = 0,
SNMP_TOO_BIG,
SNMP_NO_SUCH_NAME,
SNMP_BAD_VALUE,
SNMP_READ_ONLY,
SNMP_GEN_ERR
} SNMP_ERR_STATUS;
typedef union _SNMP_STATUS
{
struct
{
unsigned int bIsFileOpen : 1;
} Flags;
BYTE Val;
} SNMP_STATUS;
#define SNMP_AGENT_PORT (161)
#define SNMP_NMS_PORT (162)
#define AGENT_NOTIFY_PORT (0xfffe)
#ifdef _WIN32
#define ROM
extern int _RxIndex;
extern int _TxIndex;
extern int _TxLength;
extern BYTE _TxPacket[1000];
extern BYTE _RxPacket[1000];
#define INVALID_UDP_SOCKET (0XFFFF)
#define UDPGet(a) _UDPGet(a)
#define UDPFlush()
#define UDPClose(a)
#define ARPResolve(a)
#define ARPIsResolved(a, b) (TRUE)
static void _UDPGet(BYTE *v)
{
*v = _RxPacket[_RxIndex++];
}
#define UDP_SOCKET int
#define BUFFER int
#define UDPOpen(a, b, c) (0)
#define UDPDiscard() (_TxLength = 0)
#define UDPPut(a) \
{ \
if ( _TxIndex >= _TxLength ) \
_TxLength = _TxIndex+1; \
_TxPacket[_TxIndex++] = a; \
}
#define UDPIsGetReady(a) TRUE
#define UDPIsPutReady(a) TRUE
#define MACGetRxBuffer() 0
#define MACGetTxBuffer() 0
#define UDPSetRxBuffer(a) (_RxIndex = a)
#define UDPSetTxBuffer(a, b) (_TxIndex = a+b)
#define SNMPValidate(a, b) TRUE
#endif
UDP_SOCKET SNMPAgentSocket;
typedef struct _SNMP_NOTIFY_INFO
{
char community[NOTIFY_COMMUNITY_LEN];
BYTE communityLen;
SNMP_ID agentIDVar;
BYTE notificationCode;
UDP_SOCKET socket;
DWORD_VAL timestamp;
} SNMP_NOTIFY_INFO;
// SNMPNotifyInfo is not required if TRAP is disabled
#if !defined(SNMP_TRAP_DISABED)
static SNMP_NOTIFY_INFO SNMPNotifyInfo;
#endif
typedef enum _DATA_TYPE
{
INT8_VAL = 0x00,
INT16_VAL = 0x01,
INT32_VAL = 0x02,
BYTE_ARRAY = 0x03,
ASCII_STRING = 0x04,
IP_ADDRESS = 0x05,
COUNTER32 = 0x06,
TIME_TICKS_VAL = 0x07,
GAUGE32 = 0x08,
OID_VAL = 0x09,
DATA_TYPE_UNKNOWN
} DATA_TYPE;
typedef union _INDEX_INFO
{
struct
{
unsigned int bIsOID:1;
} Flags;
BYTE Val;
} INDEX_INFO;
typedef struct _DATA_TYPE_INFO
{
BYTE asnType;
BYTE asnLen;
} DATA_TYPE_INFO;
ROM DATA_TYPE_INFO dataTypeTable[] =
{
/* INT8_VAL */ { ASN_INT, 1 },
/* INT16_VAL */ { ASN_INT, 2 },
/* INT32_VAL */ { ASN_INT, 4 },
/* BYTE_ARRAY */ { OCTET_STRING, 0xff },
/* ASCII_ARRAY */ { OCTET_STRING, 0xff },
/* IP_ADDRESS */ { SNMP_IP_ADDR, 4 },
/* COUNTER32 */ { SNMP_COUNTER32, 4 },
/* TIME_TICKS_VAL */ { SNMP_TIME_TICKS, 4 },
/* GAUTE32 */ { SNMP_GAUGE32, 4 },
/* OID_VAL */ { ASN_OID, 0xff }
};
#define DATA_TYPE_TABLE_SIZE (sizeof(dataTypeTable)/sizeof(dataTypeTable[0]))
typedef union _MIB_INFO
{
struct
{
unsigned int bIsDistantSibling : 1;
unsigned int bIsConstant : 1;
unsigned int bIsSequence : 1;
unsigned int bIsSibling : 1;
unsigned int bIsParent : 1;
unsigned int bIsEditable : 1;
unsigned int bIsAgentID : 1;
unsigned int bIsIDPresent : 1;
} Flags;
BYTE Val;
} MIB_INFO;
typedef struct _OID_INFO
{
MPFS hNode;
BYTE oid;
MIB_INFO nodeInfo;
DATA_TYPE dataType;
SNMP_ID id;
WORD_VAL dataLen;
MPFS hData;
MPFS hSibling;
MPFS hChild;
BYTE index;
BYTE indexLen;
} OID_INFO;
static WORD SNMPTxOffset;
static WORD SNMPRxOffset;
static BUFFER SNMPRxBuffer;
static BUFFER SNMPTxBuffer;
static SNMP_STATUS SNMPStatus;
#define _SNMPSetTxOffset(o) (SNMPTxOffset = o)
#define _SNMPGetTxOffset() SNMPTxOffset
static SNMP_ACTION ProcessHeader(char *community, BYTE *len);
static BOOL ProcessGetSetHeader(DWORD *requestID);
static BOOL ProcessVariables(char *community,
BYTE len,
DWORD_VAL *request,
BYTE pduType);
static BOOL OIDLookup(BYTE *oid, BYTE oidLen, OID_INFO *rec);
static BOOL IsValidCommunity(char* community, BYTE *len);
static BOOL IsValidInt(DWORD *val);
static BOOL IsValidPDU(SNMP_ACTION *pdu);
static BYTE IsValidLength(WORD *len);
static BOOL IsASNNull(void);
static BOOL IsValidOID(BYTE *oid, BYTE *len);
static BYTE IsValidStructure(WORD *dataLen);
static void _SNMPDuplexInit(UDP_SOCKET socket);
static void _SNMPPut(BYTE v);
static BYTE _SNMPGet(void);
static BOOL GetNextLeaf(OID_INFO *n);
static void ReadMIBRecord(MPFS h, OID_INFO *rec);
static BOOL GetDataTypeInfo(DATA_TYPE dataType, DATA_TYPE_INFO *info);
static BYTE ProcessGetVar(OID_INFO *rec, BOOL bAsOID);
static BYTE ProcessGetNextVar(OID_INFO *rec);
static BOOL GetOIDStringByAddr(OID_INFO *rec, BYTE *oidString, BYTE *len);
static BYTE ProcessSetVar(OID_INFO *rec, SNMP_ERR_STATUS *errorStatus);
static void SetErrorStatus(WORD errorStatusOffset,
WORD errorIndexOffset,
SNMP_ERR_STATUS errorStatus,
BYTE errorIndex);
// This function is used only when TRAP is enabled.
#if !defined(SNMP_TRAP_DISABED)
static BOOL GetOIDStringByID(SNMP_ID id, OID_INFO *info, BYTE *oidString, BYTE *len);
#endif
/*********************************************************************
* Function: void SNMPInit(void)
*
* PreCondition: At least one UDP socket must be available.
* UDPInit() is already called.
*
* Input: None
*
* Output: SNMP agent module is initialized.
*
* Side Effects: One UDP socket will be used.
*
* Overview: Initialize SNMP module internals
*
* Note: This function is called only once during lifetime
* of the application.
********************************************************************/
void SNMPInit(void)
{
// Start with no error or flag set.
SNMPStatus.Val = 0;
SNMPAgentSocket = UDPOpen(SNMP_AGENT_PORT, 0, INVALID_UDP_SOCKET);
// SNMPAgentSocket must not be INVALID_UDP_SOCKET.
// If it is, compile time value of UDP Socket numbers must be increased.
return;
}
/*********************************************************************
* Function: BOOL SNMPTask(void)
*
* PreCondition: SNMPInit is already called.
*
* Input: None
*
* Output: TRUE if SNMP module has finished with a state
* FALSE if a state has not been finished.
*
*
* Side Effects: None
*
* Overview: Handle incoming SNMP requests as well as any
* outgoing SNMP responses and timeout conditions
*
* Note: None.
********************************************************************/
BOOL SNMPTask(void)
{
char community[SNMP_COMMUNITY_MAX_LEN];
BYTE communityLen;
DWORD_VAL requestID;
BYTE pdu;
MPFS hMIBFile;
BOOL lbReturn;
char snmpBIBFile[] = SNMP_BIB_FILE_NAME;
// Check to see if there is any packet on SNMP Agent socket.
if ( !UDPIsGetReady(SNMPAgentSocket) )
return TRUE;
// As we process SNMP variables, we will prepare response on-the-fly
// creating full duplex transfer.
// Current MAC layer does not support full duplex transfer, so
// SNMP needs to manage its own full duplex connection.
// Prepare for full duplex transfer.
_SNMPDuplexInit(SNMPAgentSocket);
pdu = ProcessHeader(community, &communityLen);
if ( pdu == SNMP_ACTION_UNKNOWN )
goto _SNMPDiscard;
if ( !ProcessGetSetHeader(&requestID.Val) )
goto _SNMPDiscard;
// Open MIB file.
SNMPStatus.Flags.bIsFileOpen = FALSE;
hMIBFile = MPFSOpen((BYTE*)snmpBIBFile);
if ( hMIBFile != MPFS_INVALID )
{
SNMPStatus.Flags.bIsFileOpen = TRUE;
}
lbReturn = ProcessVariables(community, communityLen, &requestID, pdu);
if ( SNMPStatus.Flags.bIsFileOpen )
{
MPFSClose();
}
if ( lbReturn == FALSE )
goto _SNMPDiscard;
UDPFlush();
return TRUE;
_SNMPDiscard:
UDPDiscard();
return TRUE;
}
#if !defined(SNMP_TRAP_DISABED)
/*********************************************************************
* Function: void SNMPNotifyPrepare(IP_ADDR *remoteHost,
* char *community,
* BYTE communityLen,
* SNMP_ID agentIDVar,
* BYTE notificationCode,
* DWORD timestamp)
*
* PreCondition: SNMPInit is already called.
*
* Input: remoteHost - pointer to remote Host IP address
* community - Community string to use to notify
* communityLen- Community string length
* agentIDVar - System ID to use identify this agent
* notificaitonCode - Notification Code to use
* timestamp - Notification timestamp in 100th
* of second.
*
* Output: None
*
* Side Effects: None
*
* Overview: This function prepares SNMP module to send SNMP
* trap (notification) to remote host.
*
* Note: This is first of series of functions to complete
* SNMP notification.
********************************************************************/
void SNMPNotifyPrepare(IP_ADDR *remoteHost,
char *community,
BYTE communityLen,
SNMP_ID agentIDVar,
BYTE notificationCode,
DWORD timestamp )
{
strcpy(SNMPNotifyInfo.
community, community
);
SNMPNotifyInfo.communityLen = communityLen;
SNMPNotifyInfo.agentIDVar = agentIDVar;
SNMPNotifyInfo.notificationCode = notificationCode;
SNMPNotifyInfo.timestamp.Val = timestamp;
ARPResolve(remoteHost);
}
/*********************************************************************
* Function: BOOL SNMPIsNotifyReady(IP_ADDR *remoteHost)
*
* PreCondition: SNMPNotifyPrepare is already called and returned
* TRUE.
*
* Input: remoteHost - pointer to remote Host IP address
*
* Output: TRUE if remoteHost IP address is resolved and
* SNMPNotify may be called.
* FALSE otherwise.
* This would fail if there were not UDP socket
* to open.
*
* Side Effects: None
*
* Overview: This function resolves given remoteHost IP address
* into MAC address using ARP module.
* If remoteHost is not aviailable, this function
* would never return TRUE.
* Application must implement timeout logic to
* handle "remoteHost not avialable" situation.
*
* Note: None
********************************************************************/
BOOL SNMPIsNotifyReady(IP_ADDR *remoteHost)
{
NODE_INFO remoteNode;
if ( ARPIsResolved(remoteHost, &remoteNode.MACAddr) )
{
remoteNode.IPAddr.Val = remoteHost->Val;
SNMPNotifyInfo.socket = UDPOpen(AGENT_NOTIFY_PORT, &remoteNode, SNMP_NMS_PORT);
return (SNMPNotifyInfo.socket != INVALID_UDP_SOCKET);
}
return FALSE;
}
/*********************************************************************
* Function: BOOL SNMPNotify(SNMP_ID var,
* SNMP_VAL val,
* SNMP_INDEX index)
*
* PreCondition: SNMPIsNotified is already called and returned
* TRUE.
*
* Input: var - SNMP var ID that is to be used in
* notification
* val - Value of var. Only value of
* BYTE, WORD or DWORD can be sent.
* index - Index of var. If this var is a single,
* index would be 0, or else if this var
* is a sequence, index could be any
* value from 0 to 127.
*
* Output: TRUE if SNMP notification was successful sent.
* This does not guarantee that remoteHost recieved
* it.
* FALSE otherwise.
* This would fail under following contions:
* 1) Given SNMP_BIB_FILE does not exist in MPFS
* 2) Given var does not exist.
* 3) Previously given agentID does not exist
* 4) Data type of given var is unknown - only
* possible if MPFS itself was corrupted.
*
* Side Effects: None
*
* Overview: This function creates SNMP trap PDU and sends it
* to previously specified remoteHost.
*
* Note: None
********************************************************************/
BOOL SNMPNotify(SNMP_ID var,
SNMP_VAL val,
SNMP_INDEX index)
{
BYTE len;
BYTE OIDValue[OID_MAX_LEN];
BYTE OIDLen;
BYTE agentIDLen;
BYTE *pOIDValue;
OID_INFO rec;
DATA_TYPE_INFO dataTypeInfo;
WORD packetStructLenOffset;
WORD pduStructLenOffset;
WORD varBindStructLenOffset;
WORD varPairStructLenOffset;
WORD prevOffset;
char *pCommunity;
MPFS hMIBFile;
char snmpBIBFile[] = SNMP_BIB_FILE_NAME;
hMIBFile = MPFSOpen((BYTE*)snmpBIBFile);
if ( hMIBFile == MPFS_INVALID )
{
UDPClose(SNMPNotifyInfo.socket);
return FALSE;
}
_SNMPDuplexInit(SNMPNotifyInfo.socket);
len = SNMPNotifyInfo.communityLen;
pCommunity = SNMPNotifyInfo.community;
_SNMPPut(STRUCTURE); // First item is packet structure
packetStructLenOffset = SNMPTxOffset;
_SNMPPut(0);
// Put SNMP version info - only v1.0 is supported.
_SNMPPut(ASN_INT); // Int type.
_SNMPPut(1); // One byte long value.
_SNMPPut(SNMP_V1); // v1.0.
//len = strlen(community); // Save community length for later use.
_SNMPPut(OCTET_STRING); // Octet string type.
_SNMPPut(len); // community string length
while( len-- ) // Copy entire string.
_SNMPPut(*(pCommunity++));
// Put PDU type. SNMP agent's response is always GET RESPONSE
_SNMPPut(TRAP);
pduStructLenOffset = SNMPTxOffset;
_SNMPPut(0);
// Get complete OID string from MPFS.
if ( !GetOIDStringByID(SNMPNotifyInfo.agentIDVar,
&rec, OIDValue, &agentIDLen) )
{
MPFSClose();
UDPClose(SNMPNotifyInfo.socket);
return FALSE;
}
if ( !rec.nodeInfo.Flags.bIsAgentID )
{
MPFSClose();
UDPClose(SNMPNotifyInfo.socket);
return FALSE;
}
MPFSGetBegin(rec.hData);
_SNMPPut(ASN_OID);
len = MPFSGet();
agentIDLen = len;
_SNMPPut(len);
while( len-- )
_SNMPPut(MPFSGet());
MPFSGetEnd();
// This agent's IP address.
_SNMPPut(SNMP_IP_ADDR);
_SNMPPut(4);
_SNMPPut(AppConfig.MyIPAddr.v[0]);
_SNMPPut(AppConfig.MyIPAddr.v[1]);
_SNMPPut(AppConfig.MyIPAddr.v[2]);
_SNMPPut(AppConfig.MyIPAddr.v[3]);
// Trap code
_SNMPPut(ASN_INT);
_SNMPPut(1);
_SNMPPut(6); // Enterprisespecific trap code
_SNMPPut(ASN_INT);
_SNMPPut(1);
_SNMPPut(SNMPNotifyInfo.notificationCode);
// Time stamp
_SNMPPut(SNMP_TIME_TICKS);
_SNMPPut(4);
_SNMPPut(SNMPNotifyInfo.timestamp.v[3]);
_SNMPPut(SNMPNotifyInfo.timestamp.v[2]);
_SNMPPut(SNMPNotifyInfo.timestamp.v[1]);
_SNMPPut(SNMPNotifyInfo.timestamp.v[0]);
// Variable binding structure header
_SNMPPut(0x30);
varBindStructLenOffset = SNMPTxOffset;
_SNMPPut(0);
// Create variable name-pair structure
_SNMPPut(0x30);
varPairStructLenOffset = SNMPTxOffset;
_SNMPPut(0);
// Get complete notification variable OID string.
if ( !GetOIDStringByID(var, &rec, OIDValue, &OIDLen) )
{
MPFSClose();
UDPClose(SNMPNotifyInfo.socket);
return FALSE;
}
// Copy OID string into packet.
_SNMPPut(ASN_OID);
_SNMPPut((BYTE)(OIDLen+1));
len = OIDLen;
pOIDValue = OIDValue;
while( len-- )
_SNMPPut(*pOIDValue++);
_SNMPPut(index);
// Encode and Copy actual data bytes
if ( !GetDataTypeInfo(rec.dataType, &dataTypeInfo) )
{
MPFSClose();
UDPClose(SNMPNotifyInfo.socket);
return FALSE;
}
_SNMPPut(dataTypeInfo.asnType);
// In this version, only data type of 4 bytes or less long can be
// notification variable.
if ( dataTypeInfo.asnLen == 0xff )
{
MPFSClose();
UDPClose(SNMPNotifyInfo.socket);
return FALSE;
}
len = dataTypeInfo.asnLen;
_SNMPPut(len);
while( len-- )
_SNMPPut(val.v[len]);
len = dataTypeInfo.asnLen // data bytes count
+ 1 // Length byte
+ 1 // Data type byte
+ OIDLen // OID bytes
+ 2 // OID header bytes
+ 1; // index byte
prevOffset = _SNMPGetTxOffset();
_SNMPSetTxOffset(varPairStructLenOffset);
_SNMPPut(len);
len += 2; // Variable Binding structure header
_SNMPSetTxOffset(varBindStructLenOffset);
_SNMPPut(len);
len = len
+ 2 // Var bind struct header
+ 6 // 6 bytes of timestamp
+ 3 // 3 bytes of trap code
+ 3 // 3 bytes of notification code
+ 6 // 6 bytes of agnent IP address
+ agentIDLen // Agent ID bytes
+ 2; // Agent ID header bytes
_SNMPSetTxOffset(pduStructLenOffset);
_SNMPPut(len);
len = len // PDU struct length
+ 2 // PDU header
+ SNMPNotifyInfo.communityLen // Community string bytes
+ 2 // Community header bytes
+ 3; // SNMP version bytes
_SNMPSetTxOffset(packetStructLenOffset);
_SNMPPut(len);
_SNMPSetTxOffset(prevOffset);
MPFSClose();
UDPFlush();
UDPClose(SNMPNotifyInfo.socket);
return TRUE;
}
#endif // Code removed when SNMP_TRAP_DISABLED
static SNMP_ACTION ProcessHeader(char *community, BYTE *len)
{
DWORD_VAL tempLen;
SNMP_ACTION pdu;
// Very first item must be a structure
if ( !IsValidStructure((WORD*)&tempLen) )
return SNMP_ACTION_UNKNOWN;
// Only SNMP v1.0 is supported.
if ( !IsValidInt(&tempLen.Val) )
return SNMP_ACTION_UNKNOWN;
if ( tempLen.v[0] != SNMP_V1 )
return SNMP_ACTION_UNKNOWN;
// This function populates response as it processes community string.
if ( !IsValidCommunity(community, len) )
return SNMP_ACTION_UNKNOWN;
// Fetch and validate pdu type. Only "Get" and "Get Next" are expected.
if ( !IsValidPDU(&pdu) )
return SNMP_ACTION_UNKNOWN;
// Ask main application to verify community name against requested
// pdu type.
if ( !SNMPValidate(pdu, community) )
return SNMP_ACTION_UNKNOWN;
return pdu;
}
static BOOL ProcessGetSetHeader(DWORD *requestID)
{
DWORD_VAL tempData;
// Fetch and save request ID.
if ( IsValidInt(&tempData.Val) )
*requestID = tempData.Val;
else
return FALSE;
// Fetch and discard error status
if ( !IsValidInt(&tempData.Val) )
return FALSE;
// Fetch and disacard error index
return IsValidInt(&tempData.Val);
}
static BOOL ProcessVariables(char *community, BYTE len, DWORD_VAL *request, BYTE pduType)
{
BYTE temp;
WORD_VAL varBindingLen;
WORD_VAL tempLen;
BYTE errorIndex;
SNMP_ERR_STATUS errorStatus;
BYTE varIndex;
WORD packetStructLenOffset;
WORD pduLenOffset;
WORD errorStatusOffset;
WORD errorIndexOffset;
WORD varBindStructOffset;
WORD varStructLenOffset;
BYTE OIDValue[OID_MAX_LEN];
BYTE OIDLen;
BYTE *ptemp;
OID_INFO OIDInfo;
WORD_VAL varPairLen;
WORD_VAL varBindLen;
BYTE communityLen;
WORD oidOffset;
WORD prevOffset;
// Before each variables are processed, prepare necessary header.
_SNMPPut(STRUCTURE); // First item is packet structure
// Since we do not know length of structure at this point, use
// placeholder bytes that will be replaced with actual value.
_SNMPPut(0x82);
packetStructLenOffset = SNMPTxOffset;
_SNMPPut(0);
_SNMPPut(0);
// Put SNMP version info - only v1.0 is supported.
_SNMPPut(ASN_INT); // Int type.
_SNMPPut(1); // One byte long value.
_SNMPPut(SNMP_V1); // v1.0.
// Put community string
communityLen = len; // Save community length for later use.
_SNMPPut(OCTET_STRING); // Octet string type.
_SNMPPut(len); // community string length
while( len-- ) // Copy entire string.
_SNMPPut(*community++);
// Put PDU type. SNMP agent's response is always GET RESPONSE
_SNMPPut(GET_RESPONSE);
// Since we don't know length of this response, use placeholders until
// we know for sure...
_SNMPPut(0x82); // Be prepared for 2 byte-long length
pduLenOffset = SNMPTxOffset;
_SNMPPut(0);
_SNMPPut(0);
// Put original request back.
_SNMPPut(ASN_INT); // Int type.
_SNMPPut(4); // To simplify logic, always use 4 byte long requestID
_SNMPPut(request->v[3]); // Start MSB
_SNMPPut(request->v[2]);
_SNMPPut(request->v[1]);
_SNMPPut(request->v[0]);
// Put error status.
// Since we do not know error status, put place holder until we know it...
_SNMPPut(ASN_INT); // Int type
_SNMPPut(1); // One byte long.
errorStatusOffset = SNMPTxOffset;
_SNMPPut(0); // Placeholder.
// Similarly put error index.
_SNMPPut(ASN_INT); // Int type
_SNMPPut(1); // One byte long
errorIndexOffset = SNMPTxOffset;
_SNMPPut(0); // Placeholder.
varIndex = 0;
errorIndex = 0;
errorStatus = SNMP_NO_ERR;
// Decode variable binding structure
if ( !IsValidStructure(&varBindingLen.Val) )
return FALSE;
// Put variable binding response structure
_SNMPPut(STRUCTURE);
_SNMPPut(0x82);
varBindStructOffset = SNMPTxOffset;
_SNMPPut(0);
_SNMPPut(0);
varBindLen.Val = 0;
while( varBindingLen.Val )
{
// Need to know what variable we are processing, so that in case
// if there is problem for that varaible, we can put it in
// errorIndex location of SNMP packet.
varIndex++;
// Decode variable length structure
temp = IsValidStructure(&tempLen.Val);
if ( !temp )
return FALSE;
varBindingLen.Val -= tempLen.Val;
varBindingLen.Val -= temp;
// Prepare variable response structure.
_SNMPPut(STRUCTURE);
_SNMPPut(0x82);
varStructLenOffset = SNMPTxOffset;
_SNMPPut(0);
_SNMPPut(0);
// Decode next object
if ( !IsValidOID(OIDValue, &OIDLen) )
return FALSE;
// For Get & Get-Next, value must be NULL.
if ( pduType != (BYTE)SET_REQUEST )
{
if ( !IsASNNull() )
return FALSE;
}
// Prepare response - original variable
_SNMPPut(ASN_OID);
oidOffset = SNMPTxOffset;
_SNMPPut(OIDLen);
ptemp = OIDValue;
temp = OIDLen;
while( temp-- )
_SNMPPut(*ptemp++);
// Lookup current OID into our compiled database.
if ( !OIDLookup(OIDValue, OIDLen, &OIDInfo) )
{
errorStatus = SNMP_NO_SUCH_NAME;
SetErrorStatus(errorStatusOffset,
errorIndexOffset,
SNMP_NO_SUCH_NAME,
varIndex);
if ( pduType != SNMP_SET )
{
_SNMPPut(ASN_NULL);
_SNMPPut(0);
varPairLen.Val = OIDLen + 4;
}
else
{
// Copy original value as it is and goto next variable.
// Copy data type
_SNMPPut(_SNMPGet());
// Get data length.
temp = _SNMPGet();
_SNMPPut(temp);
// Start counting total number of bytes in this structure.
varPairLen.Val = OIDLen // OID name bytes
+ 2 // OID header bytes
+ 2; // Value header bytes
// Copy entire data bytes as it is.
while( temp-- )
{
varPairLen.Val++;
_SNMPPut(_SNMPGet());
}
}
}
else
{
// Now handle specific pduType request...
if ( pduType == SNMP_GET )
{
// Start counting total number of bytes in this structure.
varPairLen.Val = OIDLen + 2;
prevOffset = _SNMPGetTxOffset();
temp = ProcessGetVar(&OIDInfo, FALSE);
if ( temp == 0 )
{
_SNMPSetTxOffset(prevOffset);
errorStatus = SNMP_NO_SUCH_NAME;
SetErrorStatus(errorStatusOffset,
errorIndexOffset,
SNMP_NO_SUCH_NAME,
varIndex);
_SNMPPut(ASN_NULL);
_SNMPPut(0);
temp = 2;
}
varPairLen.Val += temp;
}
else if ( pduType == SNMP_GET_NEXT )
{
prevOffset = _SNMPGetTxOffset();
_SNMPSetTxOffset(oidOffset);
temp = ProcessGetNextVar(&OIDInfo);
if ( temp == 0 )
{
_SNMPSetTxOffset(prevOffset);
SetErrorStatus(errorStatusOffset,
errorIndexOffset,
SNMP_NO_SUCH_NAME,
varIndex);
_SNMPPut(ASN_NULL);
_SNMPPut(0);
// Start counting total number of bytes in this structure.
varPairLen.Val = OIDLen // as put by GetNextVar()
+ 2 // OID header
+ 2; // ASN_NULL bytes
}
else
varPairLen.Val = (temp + 2); // + OID headerbytes
}
else if ( pduType == SNMP_SET )
{
temp = ProcessSetVar(&OIDInfo, &errorStatus);
if ( errorStatus != SNMP_NO_ERR )
{
SetErrorStatus(errorStatusOffset,
errorIndexOffset,
errorStatus,
varIndex);
}
varPairLen.Val = OIDLen +2 // OID name + header bytes
+ temp; // value bytes as put by SetVar
}
}
prevOffset = _SNMPGetTxOffset();
_SNMPSetTxOffset(varStructLenOffset);
_SNMPPut(varPairLen.byte.MSB);
_SNMPPut(varPairLen.byte.LSB);
varBindLen.Val += 4 // Variable Pair STRUCTURE byte + 1 length byte.
+ varPairLen.Val;
_SNMPSetTxOffset(prevOffset);
}
//MACSetTxBuffer(SNMPTxBuffer, varBindStructOffset);
_SNMPSetTxOffset(varBindStructOffset);
_SNMPPut(varBindLen.byte.MSB);
_SNMPPut(varBindLen.byte.LSB);
// varBindLen is reused as "pduLen"
varBindLen.Val = varBindLen.Val+4 // Variable Binding Strucure length
+ 6 // Request ID bytes
+ 3 // Error status
+ 3; // Error index
//MACSetTxBuffer(SNMPTxBuffer, pduLenOffset);
_SNMPSetTxOffset(pduLenOffset);
_SNMPPut(varBindLen.byte.MSB);
_SNMPPut(varBindLen.byte.LSB);
// varBindLen is reused as "packetLen".
varBindLen.Val = 3 // SNMP Version bytes
+ 2 + communityLen // community string bytes
+ 4 // PDU structure header bytes.
+ varBindLen.Val;
//MACSetTxBuffer(SNMPTxBuffer, packetStructLenOffset);
_SNMPSetTxOffset(packetStructLenOffset);
_SNMPPut(varBindLen.byte.MSB);
_SNMPPut(varBindLen.byte.LSB);
return TRUE;
}
static BYTE ProcessGetNextVar(OID_INFO *rec)
{
WORD_VAL temp;
BYTE putBytes;
OID_INFO indexRec;
BYTE *pOIDValue;
BYTE OIDValue[51];
BYTE OIDLen;
INDEX_INFO indexInfo;
MIB_INFO varNodeInfo;
SNMP_ID varID;
WORD OIDValOffset;
WORD prevOffset;
BOOL lbNextLeaf;
BYTE ref;
SNMP_VAL v;
BYTE varDataType;
BYTE indexBytes;
lbNextLeaf = FALSE;
temp.byte.LSB = 0;
// Get next leaf only if this OID is a parent or a simple leaf
// node.
if ( rec->nodeInfo.Flags.bIsParent ||
(!rec->nodeInfo.Flags.bIsParent && !rec->nodeInfo.Flags.bIsSequence) )
{
_GetNextLeaf:
lbNextLeaf = TRUE;
if ( !GetNextLeaf(rec) )
return 0;
}
// Get complete OID string from oid record.
if ( !GetOIDStringByAddr(rec, OIDValue, &OIDLen) )
return 0;
// Copy complete OID string to create response packet.
pOIDValue = OIDValue;
OIDValOffset = _SNMPGetTxOffset();
temp.byte.LSB = OIDLen;
_SNMPSetTxOffset(OIDValOffset+1);
while( temp.byte.LSB-- )
_SNMPPut(*pOIDValue++);
// Start counting number of bytes put - OIDLen is already counted.
temp.byte.LSB = OIDLen;
varDataType = rec->dataType;
varID = rec->id;
// If this is a simple OID, handle it as a GetVar command.
if ( !rec->nodeInfo.Flags.bIsSequence )
{
// This is an addition to previously copied OID string.
// This is index value of '0'.
_SNMPPut(0);
temp.byte.LSB++;
// Since we added one more byte to previously copied OID
// string, we need to update OIDLen value.
prevOffset = _SNMPGetTxOffset();
_SNMPSetTxOffset(OIDValOffset);
_SNMPPut(++OIDLen);
_SNMPSetTxOffset(prevOffset);
// Now do Get on this simple variable.
prevOffset = _SNMPGetTxOffset();
putBytes = ProcessGetVar(rec, FALSE);
if ( putBytes == 0 )
{
_SNMPSetTxOffset(prevOffset);
_SNMPPut(ASN_NULL);
_SNMPPut(0);
putBytes = 2;
}
temp.byte.LSB += putBytes; // ProcessGetVar(rec, FALSE);
// Return with total number of bytes copied to response packet.
return temp.byte.LSB;
}
// This is a sequence variable.
// First of all make sure that there is a next index after this
// index. We also need to make sure that we do not do this foerever.
// So make sure that this is not a repeat test.
ref = 0;
if ( lbNextLeaf == TRUE )
{
// Let application tell us whether this is a valid index or not.
if ( !SNMPGetVar(rec->id, rec->index, &ref, &v) )
{
// If not, then we need to get next leaf in line.
// Remember that we have already did this once, so that we do not
// do this forever.
//lbNextSequence = TRUE;
// Reset the response packet pointer to begining of OID.
_SNMPSetTxOffset(OIDValOffset);
// Jump to this label within this function - Not a good SW engineering
// practice, but this will reuse code at much lower expense.
goto _GetNextLeaf;
}
}
// Need to fetch index information from MIB and prepare complete OID+
// index response.
varNodeInfo.Val = rec->nodeInfo.Val;
MPFSGetBegin(MPFSTell());
// In this version, only 7-bit index is supported.
MPFSGet();
indexBytes = 0;
indexInfo.Val = MPFSGet();
// Fetch index ID.
indexRec.id = MPFSGet();
// Fetch index data type.
indexRec.dataType = MPFSGet();
indexRec.index = rec->index;
MPFSGetEnd();
// Check with application to see if there exists next index
// for this index id.
if ( !lbNextLeaf && !SNMPGetNextIndex(indexRec.id, &indexRec.index) )
{
//lbNextSeqeuence = TRUE;
// Reset the response packet pointer to begining of OID.
_SNMPSetTxOffset(OIDValOffset);
// Jump to this label. Not a good practice, but once-in-a-while
// it should be acceptable !
goto _GetNextLeaf;
}
// Index is assumed to be dynamic, and leaf node.
// mib2bib has already ensured that this was the case.
indexRec.nodeInfo.Flags.bIsConstant = 0;
indexRec.nodeInfo.Flags.bIsParent = 0;
indexRec.nodeInfo.Flags.bIsSequence = 1;
// Now handle this as simple GetVar.
// Keep track of number of bytes added to OID.
indexBytes += ProcessGetVar(&indexRec, TRUE);
rec->index = indexRec.index;
// These are the total number of bytes put so far as a result of this function.
temp.byte.LSB += indexBytes;
// These are the total number of bytes in OID string including index bytes.
OIDLen += indexBytes;
// Since we added index bytes to previously copied OID
// string, we need to update OIDLen value.
prevOffset = _SNMPGetTxOffset();
_SNMPSetTxOffset(OIDValOffset);
_SNMPPut(OIDLen);
_SNMPSetTxOffset(prevOffset);
// Fetch actual value itself.
// Need to restore original OID value.
rec->nodeInfo.Val = varNodeInfo.Val;
rec->id = varID;
rec->dataType = varDataType;
temp.byte.LSB += ProcessGetVar(rec, FALSE);
return temp.byte.LSB;
}
// This is the binary mib format:
// <oid, nodeInfo, [id], [SiblingOffset], [DistantSibling], [dataType], [dataLen], [data], [{IndexCount, <IndexType>, <Index>, ...>]}, ChildNode
static BOOL OIDLookup(BYTE *oid, BYTE oidLen, OID_INFO *rec)
{
WORD_VAL tempData;
BYTE tempOID;
MPFS hNode;
BYTE matchedCount;
if ( !SNMPStatus.Flags.bIsFileOpen )
return FALSE;
hNode = MPFSSeek(0);
matchedCount = oidLen;
// Begin reading the data...
//MPFSGetBegin(hNode);
while( 1 )
{
MPFSGetBegin(hNode);
// Remember offset of this node so that we can find its sibling
// and child data.
rec->hNode = MPFSTell(); // hNode;
// Read OID byte.
tempOID = MPFSGet();
// Read Node Info
rec->nodeInfo.Val = MPFSGet();
// Next byte will be node id, if this is a leaf node with variable data.
if ( rec->nodeInfo.Flags.bIsIDPresent )
rec->id = MPFSGet();
// Read sibling offset, if there is any.
if ( rec->nodeInfo.Flags.bIsSibling )
{
tempData.v[0] = MPFSGet();
tempData.v[1] = MPFSGet();
rec->hSibling = (MPFS)tempData.Val;
}
if ( tempOID != *oid )
{
// If very first OID byte does not match, it may be because it is
// 0, 1 or 2. In that case declare that there is a match.
// The command processor would detect OID type and continue or reject
// this OID as a valid argument.
if ( matchedCount == oidLen )
goto FoundIt;
if ( rec->nodeInfo.Flags.bIsSibling )
{
MPFSGetEnd();
hNode = MPFSSeek((MPFS)tempData.Val);
}
else
goto DidNotFindIt;
}
else
{
// One more oid byte matched.
matchedCount--;
oid++;
// A node is said to be matched if last matched node is a leaf node
// or all but last OID string is matched and last byte of OID is '0'.
// i.e. single index.
if ( !rec->nodeInfo.Flags.bIsParent )
{
// Read and discard Distant Sibling info if there is any.
if ( rec->nodeInfo.Flags.bIsDistantSibling )
{
tempData.v[0] = MPFSGet();
tempData.v[1] = MPFSGet();
rec->hSibling = (MPFS)tempData.Val;
}
rec->dataType = MPFSGet();
rec->hData = MPFSTell();
goto FoundIt;
}
else if ( matchedCount == 1 && *oid == 0x00 )
{
goto FoundIt;
}
else if ( matchedCount == 0 )
{
goto FoundIt;
}
else
{
//hNode = rec->hChild;
hNode = MPFSTell();
MPFSGetEnd();
// Try to match following child node.
continue;
}
}
}
FoundIt:
MPFSGetEnd();
// Convert index info from OID to regular value format.
tempOID = *oid;
rec->index = tempOID;
// In this version, we only support 7-bit index.
if ( matchedCount == 0 )
{
rec->index = SNMP_INDEX_INVALID;
rec->indexLen = 0;
}
else if ( matchedCount > 1 || tempOID & 0x80 )
{
// Current instnace spans across more than 7-bit.
rec->indexLen = 0xff;
return FALSE;
}
else
rec->indexLen = 1;
return TRUE;
DidNotFindIt:
MPFSGetEnd();
return FALSE;
}
static BOOL GetNextLeaf(OID_INFO *n)
{
WORD_VAL temp;
// If current node is leaf, its next sibling (near or distant) is the next leaf.
if ( !n->nodeInfo.Flags.bIsParent )
{
// Since this is a leaf node, it must have at least one distant or near
// sibling to get next sibling.
if ( n->nodeInfo.Flags.bIsSibling ||
n->nodeInfo.Flags.bIsDistantSibling )
{
// Reposition at sibling.
MPFSSeek(n->hSibling);
// Fetch node related information
}
// There is no sibling to this leaf. This must be the very last node on the tree.
else
{
//--MPFSClose();
return FALSE;
}
}
while( 1 )
{
// Remember current MPFS position for this node.
n->hNode = MPFSTell();
MPFSGetBegin(n->hNode);
// Read OID byte.
n->oid = MPFSGet();
// Read Node Info
n->nodeInfo.Val = MPFSGet();
// Next byte will be node id, if this is a leaf node with variable data.
if ( n->nodeInfo.Flags.bIsIDPresent )
n->id = MPFSGet();
// Fetch sibling offset, if there is any.
if ( n->nodeInfo.Flags.bIsSibling ||
n->nodeInfo.Flags.bIsDistantSibling )
{
temp.byte.LSB = MPFSGet();
temp.byte.MSB = MPFSGet();
n->hSibling = temp.Val;
}
// If we have not reached a leaf yet, continue fetching next child in line.
if ( n->nodeInfo.Flags.bIsParent )
{
MPFSGetEnd();
continue;
}
// Fetch data type.
n->dataType = MPFSGet();
n->hData = MPFSTell();
// Since we just found next leaf in line, it will always have zero index
// to it.
n->indexLen = 1;
n->index = 0;
MPFSGetEnd();
return TRUE;
}
return FALSE;
}
static BOOL IsValidCommunity(char* community, BYTE *len)
{
BYTE tempData;
BYTE tempLen;
tempData = _SNMPGet();
if ( !IS_OCTET_STRING(tempData) )
return FALSE;
tempLen = _SNMPGet();
*len = tempLen;
if ( tempLen > SNMP_COMMUNITY_MAX_LEN )
return FALSE;
while( tempLen-- )
{
tempData = _SNMPGet();
*community++ = tempData;
}
*community = '\0';
return TRUE;
}
static BOOL IsValidInt(DWORD *val)
{
DWORD_VAL tempData;
DWORD_VAL tempLen;
tempLen.Val = 0;
// Get variable type
if ( !IS_ASN_INT(_SNMPGet()) )
return FALSE;
if ( !IsValidLength(&tempLen.word.LSW) )
return FALSE;
// Integer length of more than 32-bit is not supported.
if ( tempLen.Val > 4 )
return FALSE;
tempData.Val = 0;
while( tempLen.v[0]-- )
tempData.v[tempLen.v[0]] = _SNMPGet();
*val = tempData.Val;
return TRUE;
}
static BOOL IsValidPDU(SNMP_ACTION *pdu)
{
BYTE tempData;
WORD tempLen;
// Fetch pdu data type
tempData = _SNMPGet();
if ( !IS_AGENT_PDU(tempData) )
return FALSE;
*pdu = tempData;
// Now fetch pdu length. We don't need to remember pdu length.
return IsValidLength(&tempLen);
}
// Checks current packet and returns total length value
// as well as actual length bytes.
static BYTE IsValidLength(WORD *len)
{
BYTE tempData;
WORD_VAL tempLen;
BYTE lengthBytes;
// Initialize length value.
tempLen.Val = 0;
lengthBytes = 0;
tempData = _SNMPGet();
tempLen.v[0] = tempData;
if ( tempData & 0x80 )
{
tempData &= 0x7F;
// We do not support any length byte count of more than 2
// i.e. total length value must not be more than 16-bit.
if ( tempData > 2 )
return FALSE;
// Total length bytes are 0x80 itself plus tempData.
lengthBytes = tempData + 1;
// Get upto 2 bytes of length value.
while( tempData-- )
tempLen.v[tempData] = _SNMPGet();
}
else
lengthBytes = 1;
*len = tempLen.Val;
return lengthBytes;
}
static BOOL IsASNNull(void)
{
// Fetch and verify that this is NULL data type.
if ( !IS_ASN_NULL(_SNMPGet()) )
return FALSE;
// Fetch and verify that length value is zero.
return (_SNMPGet() == 0 );
}
static BOOL IsValidOID(BYTE *oid, BYTE *len)
{
DWORD_VAL tempLen;
// Fetch and verify that this is OID.
if ( !IS_OID(_SNMPGet()) )
return FALSE;
// Retrieve OID length
if ( !IsValidLength(&tempLen.word.LSW) )
return FALSE;
// Make sure that OID length is within our capability.
if ( tempLen.word.LSW > OID_MAX_LEN )
return FALSE;
*len = tempLen.v[0];
while( tempLen.v[0]-- )
*oid++ = _SNMPGet();
return TRUE;
}
static BYTE IsValidStructure(WORD *dataLen)
{
DWORD_VAL tempLen;
BYTE headerBytes;
if ( !IS_STRUCTURE(_SNMPGet()) )
return FALSE;
// Retrieve structure length
headerBytes = IsValidLength(&tempLen.word.LSW);
if ( !headerBytes )
return FALSE;
headerBytes++;
// Since we are using UDP as our transport and UDP are not fragmented,
// this structure length cannot be more than 1500 bytes.
// As a result, we will only use lower WORD of length value.
*dataLen = tempLen.word.LSW;
return headerBytes;
}
static void _SNMPDuplexInit(UDP_SOCKET socket)
{
// In full duplex transfer, transport protocol must be ready to
// accept new transmit packet.
while( !UDPIsPutReady(socket) ) ;
// Fetch and remember current tx and rx buffer id.
SNMPRxBuffer = MACGetRxBuffer();
SNMPTxBuffer = MACGetTxBuffer();
// Initialize buffer offsets.
SNMPRxOffset = 0;
SNMPTxOffset = 0;
}
static void _SNMPPut(BYTE v)
{
UDPSetTxBuffer(SNMPTxBuffer, SNMPTxOffset);
UDPPut(v);
SNMPTxOffset++;
}
static BYTE _SNMPGet(void)
{
BYTE v;
UDPSetRxBuffer(SNMPRxOffset++);
UDPGet(&v);
return v;
}
#if !defined(SNMP_TRAP_DISABED)
static BOOL GetOIDStringByID(SNMP_ID id, OID_INFO *info, BYTE *oidString, BYTE *len)
{
MPFS hCurrent;
hCurrent = MPFSSeek(0);
while (1)
{
ReadMIBRecord(hCurrent, info);
if ( !info->nodeInfo.Flags.bIsParent )
{
if ( info->nodeInfo.Flags.bIsIDPresent )
{
if ( info->id == id )
return GetOIDStringByAddr(info, oidString, len);
}
if ( info->nodeInfo.Flags.bIsSibling ||
info->nodeInfo.Flags.bIsDistantSibling )
MPFSSeek(info->hSibling);
else
break;
}
hCurrent = MPFSTell();
}
return FALSE;
}
#endif
static BOOL GetOIDStringByAddr(OID_INFO *rec, BYTE *oidString, BYTE *len)
{
MPFS hTarget;
MPFS hCurrent;
MPFS hNext;
OID_INFO currentMIB;
BYTE index;
enum { SM_PROBE_SIBLING, SM_PROBE_CHILD } state;
hCurrent = MPFSSeek(0);
hTarget = rec->hNode;
state = SM_PROBE_SIBLING;
index = 0;
while( 1 )
{
ReadMIBRecord(hCurrent, ¤tMIB);
oidString[index] = currentMIB.oid;
if ( hTarget == hCurrent )
{
*len = ++index;
return TRUE;
}
switch(state)
{
case SM_PROBE_SIBLING:
if ( !currentMIB.nodeInfo.Flags.bIsSibling )
state = SM_PROBE_CHILD;
else
{
hNext = currentMIB.hSibling;
MPFSSeek(hNext);
hNext = MPFSTell();
if ( hTarget >= hNext )
{
hCurrent = hNext;
break;
}
else
state = SM_PROBE_CHILD;
}
case SM_PROBE_CHILD:
if ( !currentMIB.nodeInfo.Flags.bIsParent )
return FALSE;
index++;
hCurrent = currentMIB.hChild;
state = SM_PROBE_SIBLING;
break;
}
}
return FALSE;
}
static void ReadMIBRecord(MPFS h, OID_INFO *rec)
{
MIB_INFO nodeInfo;
WORD_VAL tempVal;
MPFSGetBegin(h);
// Remember location of this record.
rec->hNode = h;
// Read OID
rec->oid = MPFSGet();
// Read nodeInfo
rec->nodeInfo.Val = MPFSGet();
nodeInfo = rec->nodeInfo;
// Read id, if there is any: Only leaf node with dynamic data will have id.
if ( nodeInfo.Flags.bIsIDPresent )
rec->id = MPFSGet();
// Read Sibling offset if there is any - any node may have sibling
if ( nodeInfo.Flags.bIsSibling )
{
tempVal.byte.LSB = MPFSGet();
tempVal.byte.MSB = MPFSGet();
rec->hSibling = (MPFS)tempVal.Val;
}
// All rest of the parameters are applicable to leaf node only.
if ( nodeInfo.Flags.bIsParent )
rec->hChild = MPFSTell();
else
{
if ( nodeInfo.Flags.bIsDistantSibling )
{
// Read Distant Sibling if there is any - only leaf node will have distant sibling
tempVal.byte.LSB = MPFSGet();
tempVal.byte.MSB = MPFSGet();
rec->hSibling = (MPFS)tempVal.Val;
}
// Save data type for this node.
rec->dataType = MPFSGet();
rec->hData = MPFSTell();
}
MPFSGetEnd();
}
static BOOL GetDataTypeInfo(DATA_TYPE dataType, DATA_TYPE_INFO *info )
{
if ( dataType >= DATA_TYPE_UNKNOWN )
return FALSE;
info->asnType = dataTypeTable[dataType].asnType;
info->asnLen = dataTypeTable[dataType].asnLen;
return TRUE;
}
static BYTE ProcessSetVar(OID_INFO *rec, SNMP_ERR_STATUS *errorStatus)
{
SNMP_ERR_STATUS errorCode;
DATA_TYPE_INFO actualDataTypeInfo;
BYTE dataType;
BYTE dataLen;
SNMP_VAL dataValue;
BYTE ref;
BYTE temp;
BYTE copiedBytes;
// Start with no error.
errorCode = SNMP_NO_ERR;
copiedBytes = 0;
// Non-leaf, Constant and ReadOnly node cannot be modified
if ( rec->nodeInfo.Flags.bIsParent ||
rec->nodeInfo.Flags.bIsConstant ||
!rec->nodeInfo.Flags.bIsEditable )
errorCode = SNMP_NO_SUCH_NAME;
dataType = _SNMPGet();
_SNMPPut(dataType);
copiedBytes++;
// Get data type for this node.
//actualDataType = MPFSGet();
if ( !GetDataTypeInfo(rec->dataType, &actualDataTypeInfo) )
errorCode = SNMP_BAD_VALUE;
// Make sure that received data type is same as what is declared
// for this node.
if ( dataType != actualDataTypeInfo.asnType )
errorCode = SNMP_BAD_VALUE;
// Make sure that received data length is within our capability.
dataLen = _SNMPGet();
_SNMPPut(dataLen);
copiedBytes++;
// Only max data length of 127 is supported.
if ( dataLen > 0x7f )
errorCode = SNMP_BAD_VALUE;
// If this is a Simple variable and given index is other than '0',
// it is considered bad value
if ( !rec->nodeInfo.Flags.bIsSequence && rec->index != 0x00 )
errorCode = SNMP_NO_SUCH_NAME;
dataValue.dword = 0;
ref = 0;
// If data length is within 4 bytes, fetch all at once and pass it
// to application.
if ( actualDataTypeInfo.asnLen != 0xff )
{
// According to mib def., this data length for this data type/
// must be less or equal to 4, if not, we don't know what this
// is.
if ( dataLen <= 4 )
{
// Now that we have verified data length, fetch them all
// at once and save it in correct place.
//dataLen--;
while( dataLen-- )
{
temp = _SNMPGet();
dataValue.v[dataLen] = temp;
// Copy same byte back to create response...
_SNMPPut(temp);
copiedBytes++;
}
// Pass it to application.
if ( errorCode == SNMP_NO_ERR )
{
if ( !SNMPSetVar(rec->id, rec->index, ref, dataValue) )
errorCode = SNMP_BAD_VALUE;
}
}
else
errorCode = SNMP_BAD_VALUE;
}
else
{
// This is a multi-byte Set operation.
// Check with application to see if this many bytes can be
// written to current variable.
if ( !SNMPIsValidSetLen(rec->id, dataLen) )
errorCode = SNMP_BAD_VALUE;
// Even though there may have been error processing this
// variable, we still need to reply with original data
// so at least copy those bytes.
while( dataLen-- )
{
dataValue.byte = _SNMPGet();
_SNMPPut(dataValue.byte);
copiedBytes++;
// Ask applicaton to set this variable only if there was
// no previous error.
if ( errorCode == SNMP_NO_ERR )
{
if ( !SNMPSetVar(rec->id, rec->index, ref++, dataValue) )
errorCode = SNMP_BAD_VALUE;
}
}
// Let application know about end of data transfer
if ( errorCode == SNMP_NO_ERR )
SNMPSetVar(rec->id, rec->index, (WORD)SNMP_END_OF_VAR, dataValue);
}
*errorStatus = errorCode;
return copiedBytes;
}
static BYTE ProcessGetVar(OID_INFO *rec, BOOL bAsOID)
{
BYTE ref;
BYTE temp;
SNMP_VAL v;
BYTE varLen;
BYTE dataType;
DATA_TYPE_INFO dataTypeInfo;
WORD offset;
WORD prevOffset;
v.dword = 0;
// Non-leaf node does not contain any data.
if ( rec->nodeInfo.Flags.bIsParent )
return 0;
// If current OID is Simple variable and index is other than .0
// we don't Get this variable.
if ( !rec->nodeInfo.Flags.bIsSequence )
{
// index of other than '0' is not invalid.
if ( rec->index > 0 )
return 0;
}
dataType = rec->dataType;
if ( !GetDataTypeInfo(dataType, &dataTypeInfo) )
return 0;
if ( !bAsOID )
{
_SNMPPut(dataTypeInfo.asnType);
offset = SNMPTxOffset;
_SNMPPut(dataTypeInfo.asnLen);
}
if ( rec->nodeInfo.Flags.bIsConstant )
{
MPFSGetBegin(rec->hData);
varLen = MPFSGet();
temp = varLen;
while( temp-- )
_SNMPPut(MPFSGet());
MPFSGetEnd();
}
else
{
ref = SNMP_START_OF_VAR;
v.dword = 0;
varLen = 0;
do
{
if ( SNMPGetVar(rec->id, rec->index, &ref, &v) )
{
if ( dataTypeInfo.asnLen != 0xff )
{
varLen = dataTypeInfo.asnLen;
while( dataTypeInfo.asnLen )
_SNMPPut(v.v[--dataTypeInfo.asnLen]);
break;
}
else
{
varLen++;
_SNMPPut(v.v[0]);
}
}
else
return 0;
} while( ref != SNMP_END_OF_VAR );
}
if ( !bAsOID )
{
prevOffset = _SNMPGetTxOffset();
_SNMPSetTxOffset(offset);
_SNMPPut(varLen);
_SNMPSetTxOffset(prevOffset);
varLen++;
varLen++;
}
return varLen;
}
static void SetErrorStatus(WORD errorStatusOffset,
WORD errorIndexOffset,
SNMP_ERR_STATUS errorStatus,
BYTE errorIndex)
{
WORD prevOffset;
prevOffset = _SNMPGetTxOffset();
_SNMPSetTxOffset(errorStatusOffset);
_SNMPPut((BYTE)errorStatus);
_SNMPSetTxOffset(errorIndexOffset);
_SNMPPut(errorIndex);
_SNMPSetTxOffset(prevOffset);
}