
Introduction
This is a simple tool to walk through MIB tree using Simple Network Management Protocol (SNMP). I developed this tool solely for learning. Here, I'm presenting this tool to explain the techniques behind SNMP manager (client) development. This tool is not intended for serious managing stuff. But if you want to use it for serious system management, go ahead and use it. I won't stop you. But after that, don't complain to me about the bugs and lacking features ;). You are most welcome to modify or fix the bugs (if you have time ;)).
This tool will do two things. One is capturing the traps and the other is sending request and getting the response. See the above figure, first list control displays response and the bottom one displays the traps. Traps are captured independently by a thread, which is started in OnInitDialog
function. So whenever you run this tool, it will start displaying the traps coming to your system. On this figure, displayed traps are from MyAgent.DLL agent, which I presented on the previous articles. Even in the rest of this article, I'm using MyAgent.DLL agent to explain this tool functionality.
Requesting To Agent
Under this heading, I am explaining the first part of this tool in detail. Like all communication, here also there is a need to call Open before making any request to server. Making the request is simple. All you have to do is, first open it using SnmpMgrOpen
API, then make a request using SnmpMgrRequest
and close it using SnmpMgrClose
API. To open it, you should specify the agent's IP address and the community name. This seems simple, isn't it? Yeh!... it is simple. OK, now, take a look in to request types.
Example: Type IP address of your agent, here that is "127.0.0.1". Type the community name as "public" and click "Open". Make sure "public" community has READ WRITE access.
Set request (SNMP_PDU_SET
): Set request can be performed only on to the variable with write access. Also, you should have opened the session with READ & WRITE enabled community in SnmpMgrOpen
API. First, this tool will get the string from the value edit box and translate to the type specified by the type combo box (combo next to the value edit box), and then it will pass it to the following function below as AsnObjectIdentifier
variable. This function will make the request based on the selected OID after putting this variable in to a SnmpVarBindList
list. (Even though it is a list, only one variable name & value will be placed.)
Example: Type ".1.3.6.1.4.1.15.0.0.2" on the OID edit box, type "Some thing" on value edit box, and select "octet" in the type combo next to the value edit box. Then, click "Set". Now you can see traps coming with "Some thing". Which shows that the variable is set successfully.
int CMySNMPDlg::SetRequest(AsnObjectIdentifier &asnOid)
{
char *asciiStr = (char*)malloc(sizeof(char)*255);
char *pBuff, *pBuff2;
int i;
SnmpVarBindList snmpVarList;
AsnInteger errorStatus=0;
AsnInteger errorIndex=0;
snmpVarList.list = NULL;
snmpVarList.len = 0;
snmpVarList.list = (SnmpVarBind *)SNMP_realloc(
snmpVarList.list,
sizeof(SnmpVarBind) *snmpVarList.len);
snmpVarList.len++;
SnmpUtilOidCpy(&snmpVarList.list[0].name,&asnOid);
snmpVarList.list[0].value.asnType = ASN_NULL;
int nSel = ((CComboBox*)GetDlgItem(IDC_CMBOIDTYPE))->GetCurSel();
snmpVarList.list[0].value.asnType =
(INT)((CComboBox*)GetDlgItem(IDC_CMBOIDTYPE))->GetItemData(nSel);
GetDlgItemText(IDC_EDTVALUE,asciiStr,255);
switch (snmpVarList.list[0].value.asnType)
{
case ASN_OCTETSTRING:
snmpVarList.list[0].value.asnValue.string.dynamic = TRUE;
snmpVarList.list[0].value.asnValue.string.length = strlen(asciiStr);
snmpVarList.list[0].value.asnValue.string.stream =
(unsigned char*)malloc(
snmpVarList.list[0].value.asnValue.string.length*
sizeof(char));
strcpy((char*)snmpVarList.list[0].value.asnValue.string.stream,asciiStr);
break;
case ASN_INTEGER32:
snmpVarList.list[0].value.asnValue.number = atoi(asciiStr);
break;
case ASN_TIMETICKS:
snmpVarList.list[0].value.asnValue.ticks = atoi(asciiStr);
break;
case ASN_GAUGE32:
snmpVarList.list[0].value.asnValue.gauge = atoi(asciiStr);
break;
case ASN_COUNTER32:
snmpVarList.list[0].value.asnValue.counter = atoi(asciiStr);
break;
case ASN_IPADDRESS:
pBuff = asciiStr;
pBuff2 = pBuff;
snmpVarList.list[0].value.asnValue.address.dynamic = TRUE;
snmpVarList.list[0].value.asnValue.address.length = 4;
snmpVarList.list[0].value.asnValue.address.stream =
(UCHAR*)SnmpUtilMemAlloc(4);
for(i=0;i<4;i++)
{
pBuff2 = strchr(pBuff,'.');
if(pBuff2)
{
pBuff2[0]='\0';
}
else
{
pBuff = pBuff;
pBuff[strlen(pBuff)] = '\0';
}
snmpVarList.list[0].value.asnValue.address.stream[i] =
atoi(pBuff);
pBuff = ++pBuff2;
}
break;
case ASN_OBJECTIDENTIFIER:
if(!SnmpMgrStrToOid(asciiStr,&snmpVarList.list[0].value.asnValue.object))
{
MessageBox("Invalid value","Snmp Value Error",MB_OK|MB_ICONERROR);
SnmpUtilVarBindListFree(&snmpVarList);
SnmpUtilOidFree(&asnOid);
return 1;
}
break;
case ASN_SEQUENCE:
snmpVarList.list[0].value.asnValue.sequence.dynamic = TRUE;
snmpVarList.list[0].value.asnValue.sequence.length = strlen(asciiStr)+1;
snmpVarList.list[0].value.asnValue.sequence.stream =
(UCHAR*) SnmpUtilMemAlloc(strlen(asciiStr) + 1);
strncpy((char*)snmpVarList.list[0].value.asnValue.sequence.stream,
asciiStr,
strlen(asciiStr)+1);
break;
case ASN_OPAQUE:
snmpVarList.list[0].value.asnValue.arbitrary.dynamic = TRUE;
snmpVarList.list[0].value.asnValue.arbitrary.length = strlen(asciiStr)+1;
snmpVarList.list[0].value.asnValue.arbitrary.stream =
(UCHAR*) SnmpUtilMemAlloc(strlen(asciiStr) + 1);
strncpy((char*)snmpVarList.list[0].value.asnValue.arbitrary.stream,
asciiStr,
strlen(asciiStr)+1);
case ASN_NULL:
default:
strcpy(asciiStr,"");
SnmpUtilVarBindListFree(&snmpVarList);
SnmpUtilOidFree(&asnOid);
MessageBox("Error Unsupported Type","Error",MB_OK|MB_ICONERROR);
return 1;
}
if(!SnmpMgrRequest(m_lpMgrSession,SNMP_PDU_SET,
&snmpVarList,&errorStatus,&errorIndex))
{
MessageBox("Snmp Request Failed","Snmp Error",MB_OK|MB_ICONERROR);
SnmpUtilVarBindListFree(&snmpVarList);
SnmpUtilOidFree(&asnOid);
return 1;
}
if(errorStatus > 0)
{
PrintStatusError(errorStatus,pBuff);
sprintf(asciiStr,"Snmp Request Failed\nErrorStatus: %s ErrorIndex: %d",
pBuff,errorIndex);
MessageBox(asciiStr,"Snmp Error",MB_OK|MB_ICONERROR);
free(asciiStr);
free(pBuff);
SnmpUtilVarBindListFree(&snmpVarList);
SnmpUtilOidFree(&asnOid);
return 1;
}
SnmpUtilVarBindListFree(&snmpVarList);
return 0;
}
Get Request (SNMP_PDU_GET
): This is reverse of the SET request. This tool will take the OID and make the request. The returning value will be translated to string and the edit box will be populated with that.
Example: Type ".1.3.6.1.4.1.15.0.0.1" on the OID and click "Get". You can see the "Author : Ramanan.T" coming on the value edit box and "octet" on the type combo.
int CMySNMPDlg::GetRequest(AsnObjectIdentifier &asnOid)
{
char *asciiStr, *tmpStr;
CComboBox *pCmdType = (CComboBox *)GetDlgItem(IDC_CMBOIDTYPE);
AsnInteger errorStatus=0;
AsnInteger errorIndex=0;
SnmpVarBindList snmpVarList;
snmpVarList.list = NULL;
snmpVarList.len = 0;
snmpVarList.list =
(SnmpVarBind *)SNMP_realloc(
snmpVarList.list,
sizeof(SnmpVarBind) *snmpVarList.len);
snmpVarList.len++;
SnmpUtilOidCpy(&snmpVarList.list[0].name,&asnOid);
snmpVarList.list[0].value.asnType = ASN_NULL;
if(!SnmpMgrRequest(m_lpMgrSession,SNMP_PDU_GET,
&snmpVarList,&errorStatus,&errorIndex))
{
SnmpUtilVarBindListFree(&snmpVarList);
SnmpUtilOidFree(&asnOid);
asciiStr = (char*)malloc(sizeof(char)*128);
PrintStatusError(errorStatus,tmpStr);
sprintf(asciiStr,"Snmp Request Failed\nErrorStatus: %s ErrorIndex: %d",
tmpStr,errorIndex);
MessageBox(asciiStr,"Snmp Error",MB_OK|MB_ICONERROR);
free(asciiStr);
free(tmpStr);
return 1;
}
if(errorStatus > 0)
{
SnmpUtilVarBindListFree(&snmpVarList);
SnmpUtilOidFree(&asnOid);
asciiStr = (char*)malloc(sizeof(char)*128);
PrintStatusError(errorStatus,tmpStr);
sprintf(asciiStr,"ErrorStatus: %s ErrorIndex: %d",tmpStr,errorIndex);
MessageBox(asciiStr,"Snmp Error",MB_OK|MB_ICONERROR);
free(asciiStr);
free(tmpStr);
return 1;
}
asciiStr = SNMP_AnyToStr(&snmpVarList.list[0].value);
int nTypes = pCmdType->GetCount();
for(int i=0;i<nTypes;i++)
{
if(pCmdType->GetItemData(i) == snmpVarList.list[0].value.asnType)
{
pCmdType->SetCurSel(i);
break;
}
}
SetDlgItemText(IDC_EDTVALUE,asciiStr);
SnmpUtilVarBindListFree(&snmpVarList);
if(asciiStr)
SnmpUtilMemFree(asciiStr);
return 0;
}
Get Next Request (SNMP_PDU_GETNEXT
): This is same like the GET request, but instead of returning the requested OID value, it will return the next available OID and its value. It's very useful to traverse the MIB tree.
Example: To experiment this, type ".1.3.6.1.4.1.15" on the OID and click "Get All". You can see all the three nodes in MyAgent.DLL displayed on the list box. Another interesting thing, type ".1.3.6" on the OID, click "Get All", and wait for a while. You can see a long list showing most of your computer details.
void CMySNMPDlg::OnBtnGetAll()
{
char *szOID = (char*)malloc(sizeof(char)*255);
char *asciiStr, *tmpStr;
char *szListEntry;
SnmpVarBindList snmpVarList;
AsnObjectIdentifier asnOid, asnOidTemp;
AsnInteger errorStatus=0;
AsnInteger errorIndex=0;
snmpVarList.list = NULL;
snmpVarList.len = 0;
memset(szOID,0,255);
GetDlgItemText(IDC_CMBOID,szOID,255);
if(!SnmpMgrStrToOid(szOID, &asnOid))
{
MessageBox("Invalid Oid","Error",MB_OK|MB_ICONERROR);
return;
}
snmpVarList.len++;
snmpVarList.list =
(SnmpVarBind *)SNMP_realloc(snmpVarList.list,
sizeof(SnmpVarBind) *snmpVarList.len);
SnmpUtilOidCpy(&snmpVarList.list[0].name,&asnOid);
snmpVarList.list[0].value.asnType = ASN_NULL;
((CListBox*)GetDlgItem(IDC_LSTOID))->ResetContent();
for(;;)
{
if(!SnmpMgrRequest(m_lpMgrSession,SNMP_PDU_GETNEXT,
&snmpVarList, &errorStatus, &errorIndex))
{
asciiStr = (char*)malloc(sizeof(char)*255);
PrintStatusError(errorStatus,tmpStr);
sprintf(asciiStr,"Snmp Request Failed\nErrorStatus: %s ErrorIndex: %d",
tmpStr,errorIndex);
MessageBox(asciiStr,"Snmp Error",MB_OK|MB_ICONERROR);
free(asciiStr);
free(tmpStr);
free(szOID);
SnmpUtilVarBindListFree(&snmpVarList);
SnmpUtilOidFree(&asnOid);
return;
}
if(errorStatus == SNMP_ERRORSTATUS_NOSUCHNAME||
SnmpUtilOidNCmp(&snmpVarList.list[0].name,&asnOid, asnOid.idLength))
break;
if(errorStatus > 0)
{
asciiStr = (char*)malloc(sizeof(char)*255);
PrintStatusError(errorStatus,tmpStr);
sprintf(asciiStr,"ErrorStatus: %s ErrorIndex: %d",tmpStr,errorIndex);
MessageBox(asciiStr,"Snmp Error",MB_OK|MB_ICONERROR);
free(asciiStr);
free(tmpStr);
free(szOID);
break;
}
else
{
char *szOidString = NULL;
if(snmpVarList.list[0].name.idLength)
{
szOidString =
(char *)SnmpUtilMemAlloc(
snmpVarList.list[0].name.idLength * 5);
if(szOidString)
{
UINT i;
char szBuf[17];
strcpy(szOidString,".");
for(i = 0; i < snmpVarList.list[0].name.idLength; i++)
{
lstrcat(szOidString,
itoa(snmpVarList.list[0].name.ids[i],
szBuf, 10 ) );
if(i < snmpVarList.list[0].name.idLength-1)
lstrcat(szOidString, ".");
}
}
}
asciiStr = SNMP_AnyToStr(&snmpVarList.list[0].value);
if(!asciiStr)
{
asciiStr = (char*)SnmpUtilMemAlloc(5);
strcpy(asciiStr,"");
}
szListEntry =
(char*)malloc(sizeof(char)*(strlen(asciiStr)+
strlen(szOidString)+5));
strcpy(szListEntry,szOidString);
strcat(szListEntry," : ");
strcat(szListEntry,asciiStr);
((CListBox*)GetDlgItem(IDC_LSTOID))->AddString(szListEntry);
SnmpUtilMemFree(asciiStr);
SNMP_free(szOidString);
free(szListEntry);
}
SnmpUtilOidCpy(&asnOidTemp, &snmpVarList.list[0].name);
SnmpUtilVarBindFree(&snmpVarList.list[0]);
SnmpUtilOidCpy(&snmpVarList.list[0].name, &asnOidTemp);
snmpVarList.list[0].value.asnType = ASN_NULL;
SnmpUtilOidFree(&asnOidTemp);
}
SnmpUtilVarBindListFree(&snmpVarList);
SnmpUtilOidFree(&asnOid);
free(szOID);
}
Capturing Traps
Only the following thread is dealing with traps. It is independent from the requests. To capture traps, you don't have to open it (click "Open" button). To capture traps, event is necessary. Event should be created (using CreateEvent
) and passed to SnmpMgrTrapListen
API. Then, using WaitForSingleObject
API, wait for any traps, and when it appears, get it using SnmpMgrGetTrap
API. Here in this tool, these captured traps are translated to string type and list control is populated with that.
unsigned long __stdcall CMySNMPDlg::TrapCaptureThread(void *lpVoid)
{
CMySNMPDlg *pDlg = (CMySNMPDlg*)lpVoid;
DWORD dwResult;
if(!(dwResult = SnmpMgrTrapListen(&pDlg->m_hNewTrapsEvent)))
{
LPVOID lpMsgBuf;
dwResult = GetLastError();
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM|
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwResult,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL);
::MessageBox(pDlg->m_hWnd,(char*)lpMsgBuf,
"Listen Error",MB_OK|MB_ICONERROR);
free(lpMsgBuf);
return 1;
}
for(;!pDlg->m_bStopTrapCaptureThread;)
{
dwResult = WaitForSingleObject(pDlg->m_hNewTrapsEvent, 0xffffffff);
if(dwResult != WAIT_OBJECT_0)
continue;
if(pDlg->m_bStopTrapCaptureThread)
break;
ResetEvent(pDlg->m_hNewTrapsEvent);
AsnObjectIdentifier enterprise;
AsnNetworkAddress IPAddress;
AsnInteger genericTrap;
AsnInteger specificTrap;
AsnTimeticks timeStamp;
RFC1157VarBindList variableBindings;
UINT i;
char *oidStr = NULL;
char *asciiStr =NULL;
char *tempStr = NULL;
char *ipStr = NULL;
while(SnmpMgrGetTrap(&enterprise, &IPAddress,
&genericTrap,&specificTrap, &timeStamp, &variableBindings))
{
if (IPAddress.length == 4)
{
ipStr = (char*)malloc(sizeof(char)*(64));
sprintf(ipStr,"%d.%d.%d.%d",
(int)IPAddress.stream[0],
(int)IPAddress.stream[1],
(int)IPAddress.stream[2],
(int)IPAddress.stream[3]);
}
;
if(IPAddress.dynamic)
{
SnmpUtilMemFree(IPAddress.stream);
}
for(i=0; i < variableBindings.len; i++)
{
SnmpMgrOidToStr(&variableBindings.list[i].name, &oidStr);
int nErr = GetLastError();
asciiStr = pDlg->SNMP_AnyToStr(&variableBindings.list[i].value);
tempStr =
(char*)malloc(sizeof(char)*
(strlen(ipStr)+strlen(oidStr)+
strlen(asciiStr)+128));
strcpy(tempStr,"Trap: IP:");
strcat(tempStr, ipStr);
strcat(tempStr," OID:");
strcat(tempStr, oidStr);
strcat(tempStr," Value:");
strcat(tempStr, asciiStr);
((CListBox*)pDlg->GetDlgItem(IDC_LSTTRAP))->AddString(tempStr);
if(oidStr)
{
SnmpUtilMemFree(oidStr);
oidStr = NULL;
}
if(tempStr)
{
free(tempStr);
tempStr = NULL;
}
if(asciiStr)
{
SnmpUtilMemFree(asciiStr);
asciiStr = NULL;
}
}
if(ipStr)
{
free(ipStr);
ipStr = NULL;
}
SnmpUtilOidFree(&enterprise);
SnmpUtilVarBindListFree(&variableBindings);
}
}
if(pDlg->m_hNewTrapsEvent)
{
CloseHandle(pDlg->m_hNewTrapsEvent);
pDlg->m_hNewTrapsEvent = NULL;
}
pDlg->m_bStopTrapCaptureThread = FALSE;
return 0;
}
Some Issues
- This may crash when you try to exit this dialog while receiving traps.
- It can't display UNICODE string from agent.
- There can be some memory leaks (I didn't check this thoroughly).
Conclusion
That's all folks. Hope you got a clear picture of SNMP and its functionality. Using this, you might mess up network printers, hubs, etc. Please refrain from annoying others. Also, make sure you set appropriate security settings when you configure your system for SNMP. Others can peek in to your system (may be they can uninstall some thing ;)).
If you want, just take a look at the free and powerful tool GetIf to walk MIB tree.