<%@ WebService Language="C#" Class="Snmp" %> // ASP-based SNMP access via Web Services // // Copyright 2003 Ken Shirriff Ken.Shirriff@sun.com // http://www.righto.com/snmp // // This software comes with no warranty. Chances are very good that it has memory leaks or // other memory issues that may make it crash. // // To run: install Snmp.asmx in C:\Inetpub\Scripts // Then access http://localhost/Scripts/Snmp.asmx. // The exposed services are getSnmp and setSnmp, which will do the specified operation // on the specified host and community. Note that only a subset of SNMP is supported. // This should be considered more of just a proof of concept. using System; using System.Web.Services; using System.Web.Services.Protocols; using System.Runtime.InteropServices; [WebService(Namespace="http://www.righto.com")] public class Snmp : WebService { // Reference: Microsoft SDK\Samples\netds\Snmp\SnmpUtil.C private IntPtr session; // The SNMP session information private SnmpVarBindList variableBindings; // The SNMP variable information // Helper to open SNMP connection private void SnmpOpen(String agent, String community) { int timeout = 2000; // Hardwire to 2 seconds, 3 retries. int retries = 3; if ((session = SnmpMgrOpen(agent, community, timeout, retries)) == IntPtr.Zero) { SnmpUtilVarBindListFree(ref variableBindings); throw new SoapException("SnmpMgrOpen fault occurred "+agent+" "+community, SoapException.ServerFaultCode); } } // Helper to close SNMP connection private void SnmpClose() { if (!SnmpMgrClose(session)) { SnmpUtilVarBindListFree(ref variableBindings); throw new SoapException("SnmpMgrClose fault occurred", SoapException.ServerFaultCode); } } // Helper method to create SnmpVarBindList from oidStr private void SnmpVarBindListCreate(String oidStr) { oidStr = "." + oidStr; // Make SnmpMgrStrToOid use absolute OID, not internet subtree variableBindings = new SnmpVarBindList(); SnmpVarBind varBind = new SnmpVarBind(); // Create variable binding // SnmpMgrStrToOid will allocate memory for the oid, which must be freed later bool status = SnmpMgrStrToOid(oidStr, ref varBind.name); if (!status) { throw new SoapException("Oid fault occurred", SoapException.ClientFaultCode, Context.Request.Url.AbsoluteUri); } // Since sucessfull, add to the variable bindings list. // Code assumes one oid in the variableBindings list. IntPtr tmpVb; if ((tmpVb = SnmpUtilMemAlloc((uint)Marshal.SizeOf(typeof(SnmpVarBind)))) == IntPtr.Zero) { SnmpUtilOidFree(ref varBind.name); throw new SoapException("Oid allocation fault occurred", SoapException.ServerFaultCode, Context.Request.Url.AbsoluteUri); } // Note: tmpVb memory will be freed when variableBinding // is freed, since we're putting it in variableBindings.list. varBind.value.asnType = ASN_NULL; Marshal.StructureToPtr(varBind, tmpVb, false); variableBindings.list = tmpVb; variableBindings.len = 1; } // Helper method to do the request private void SnmpRequest(byte requestType) { int errorStatus = 0; int errorIndex = 0; if (SnmpMgrRequest(session, requestType, ref variableBindings, ref errorStatus, ref errorIndex) == 0) { SnmpUtilVarBindListFree(ref variableBindings); throw new SoapException("SnmpMgrRequest fault occurred", SoapException.ServerFaultCode); } if (errorStatus == 2) { // Variable does not exist IntPtr sPtr = IntPtr.Zero; SnmpMgrOidToStr(variableBindings.list, ref sPtr); String s = Marshal.PtrToStringAnsi(sPtr); SnmpUtilMemFree(sPtr); throw new SoapException("SnmpMgrRequest: variable does not exist "+s, SoapException.ServerFaultCode); } else if (errorStatus > 0) { SnmpUtilVarBindListFree(ref variableBindings); throw new SoapException("SnmpMgrRequest fault occurred "+errorStatus+" "+ errorIndex, SoapException.ServerFaultCode); } // General case is currently not handled if (requestType == SNMP_PDU_GET && variableBindings.len != 1) { uint len = variableBindings.len; SnmpUtilVarBindListFree(ref variableBindings); throw new SoapException("SNMP: unexpected return length "+len, SoapException.ServerFaultCode); } } // Helper method to convert SNMP variable to string String SnmpVarToString(SnmpVarBind varBind) { String result = ""; switch (varBind.value.asnType) { case ASN_INTEGER: result = ""+varBind.value.asnValue.number; break; case ASN_OCTETSTRING: result = Marshal.PtrToStringAnsi(varBind.value.asnValue.str.stream, (int)varBind.value.asnValue.str.length); break; default: // Exercise for the reader throw new SoapException("Unimplemented ASN type: "+varBind.value.asnType, SoapException.ClientFaultCode, Context.Request.Url.AbsoluteUri); break; } return result; } // This performs a SNMP get of the specified agent, community, and oid, and returns // the results as a string. [WebMethod] public String SNMPget(String agent, String community, String oidStr) { // The try/catch makes it easier to see what's happening in a browser. // If you want to receive SOAP faults, remove the try/catch, but then // a browser will just give error 500, which isn't too informative. try { SnmpOpen(agent, community); // Open the connection SnmpVarBindListCreate(oidStr); // Convert the oid into internal form SnmpRequest(SNMP_PDU_GET); // Perform the request SnmpClose(); // Close the connection // Process the output, using the first entry in the variableBindings list. SnmpVarBind varBind = (SnmpVarBind)Marshal.PtrToStructure(variableBindings.list, typeof(SnmpVarBind)); String result = SnmpVarToString(varBind); SnmpUtilVarBindListFree(ref variableBindings); // Clean up return result; } catch (Exception e) { return "Exception "+e; } } // This performs a SNMP set of an int to the specified agent, community, and oid. [WebMethod] public String SNMPset(String agent, String community, String oidStr, int value) { try{ SnmpOpen(agent, community); // Open the connection SnmpVarBindListCreate(oidStr); // Convert the oid into internal form // Set the value in variableBindings AsnAny asnValue = new AsnAny(); asnValue.asnType = ASN_INTEGER; asnValue.asnValue.number = value; IntPtr valuePtr = (IntPtr) ((int)variableBindings.list + (int)Marshal.OffsetOf(typeof(SnmpVarBind), "value")); Marshal.StructureToPtr(asnValue, valuePtr, false); SnmpRequest(SNMP_PDU_SET); // Perform the request SnmpClose(); // Close the connection SnmpUtilVarBindListFree(ref variableBindings); // Clean up return "Set was successful"; } catch (Exception e) { return "Exception "+e; } } // The following prototypes map onto functions in the SNMP Management API. // The tricky part is that this API uses its own memory allocation // and free functions, so the automatic allocation/free of the .Net // marshaller cannot be used. Instead, we need to use IntPtr, manually // marshal things, and use the API functions to allocate memory. This // requires careful attention to which things are in managed (i.e. C#) // memory, and which things are in unmanaged memory (i.e. dll). Hopefully // most of the details are correct. Read the .NET SDK documentation on Interop // and Platform Invoke to see what I'm trying to do here. /* BOOL SNMP_FUNC_TYPE SnmpMgrStrToOid( IN LPSTR string, // OID string to be converted OUT AsnObjectIdentifier *oid // OID internal representation ); */ // oid will need to be freed with SnmpUtilOidFree, or will need // to be freed indirectly by SnmpUtilVarBindListFree [DllImport("mgmtapi.dll")] public static extern bool SnmpMgrStrToOid( string str, ref AsnObjectIdentifier oid ); /* BOOL SNMP_FUNC_TYPE SnmpMgrOidToStr( IN AsnObjectIdentifier *oid // OID internal representation OUT LPSTR *string, // OID string to be converted ); */ [DllImport("mgmtapi.dll")] public static extern bool SnmpMgrOidToStr( ref AsnObjectIdentifier oid, IntPtr str ); /* LPSNMP_MGR_SESSION SNMP_FUNC_TYPE SnmpMgrOpen( IN LPSTR lpAgentAddress, // Name/address of target agent IN LPSTR lpAgentCommunity, // Community for target agent IN INT nTimeOut, // Comm time-out in milliseconds IN INT nRetries // Comm time-out/retry count ); */ [DllImport("mgmtapi.dll")] public static extern IntPtr SnmpMgrOpen( string lpAgentAddress, string lpAgentCommunity, int nTimeOut, int nRetries ); /* BOOL SNMP_FUNC_TYPE SnmpMgrClose( IN LPSNMP_MGR_SESSION session // SNMP session pointer ); */ [DllImport("mgmtapi.dll")] public static extern bool SnmpMgrClose( IntPtr session ); /* SNMPAPI SNMP_FUNC_TYPE SnmpMgrRequest( IN LPSNMP_MGR_SESSION session, // SNMP session pointer IN BYTE requestType, // Get, GetNext, or Set IN OUT RFC1157VarBindList *variableBindings, // Varible bindings OUT AsnInteger *errorStatus, // Result error status OUT AsnInteger *errorIndex // Result error index ); */ [DllImport("mgmtapi.dll")] public static extern int SnmpMgrRequest( IntPtr session, byte requestType, ref SnmpVarBindList variableBindings, ref int errorStatus, ref int errorIndex ); /* VOID SNMP_FUNC_TYPE SnmpUtilVarBindFree( SnmpVarBind * pVb ); */ [DllImport("snmpapi.dll")] public static extern void SnmpUtilVarBindFree( IntPtr pVb ); /* VOID SNMP_FUNC_TYPE SnmpUtilMemFree( LPVOID pMem ); */ [DllImport("snmpapi.dll")] public static extern void SnmpUtilMemFree( IntPtr pMem ); /* LPVOID SNMP_FUNC_TYPE SnmpUtilMemAlloc( UINT nBytes ); */ [DllImport("snmpapi.dll")] public static extern IntPtr SnmpUtilMemAlloc( uint nBytes ); /* VOID SNMP_FUNC_TYPE SnmpUtilVarBindListFree( SnmpVarBindList * pVbl ); */ [DllImport("snmpapi.dll")] public static extern void SnmpUtilVarBindListFree( ref SnmpVarBindList pVbl ); /* VOID SNMP_FUNC_TYPE SnmpUtilOidFree( AsnObjectIdentifier * pOid ); */ // This frees the oids in the AsnObjectIdentifier [DllImport("snmpapi.dll")] public static extern void SnmpUtilOidFree( ref AsnObjectIdentifier pOid ); const byte ASN_INTEGER = (0x0 | 0x0 | 0x2); const byte ASN_OCTETSTRING = (0x0 | 0x0 | 0x4); const byte ASN_NULL = (0x0 | 0x0 | 0x5); const byte SNMP_PDU_GET = (0x80 | 0x20 | 0x0); const byte SNMP_PDU_SET = (0x80 | 0x20 | 0x3); } [ StructLayout( LayoutKind.Sequential)] public struct SnmpVarBindList { public IntPtr list; // SnmpVarBind * public uint len; } [ StructLayout( LayoutKind.Sequential)] public struct AsnObjectIdentifier { public uint idLength; public IntPtr ids; // UINT * } [ StructLayout( LayoutKind.Explicit)] public struct AsnUnion { [FieldOffset(0)] public int number; [FieldOffset(0)] public AsnOctetString str; } [ StructLayout( LayoutKind.Sequential)] public struct AsnOctetString { public IntPtr stream; public uint length; public bool dynamic; } [ StructLayout( LayoutKind.Sequential)] public struct AsnAny { public byte asnType; public AsnUnion asnValue; } [ StructLayout( LayoutKind.Sequential)] public struct SnmpVarBind { public AsnObjectIdentifier name; public AsnAny value; }