digigrupp.com       Home  Downloads  Support  About  Contact  Search 

Simple SNMP Query Tool - Open Source - C# (C-sharp)

Downloadmain.cssnmp.cssnmpsocket.csmib.csrfc2mib.cscompile.cmd  

SNMP packet structure

// Author: Toomas Kaljus
// http://www.digigrupp.com

namespace DG
{
    public enum SnmpType
    {
        Boolean = 0x01,
        Integer = 0x02,
        BitString = 0x03,
        OctetString = 0x04,
        Null = 0x05,
        ObjectIdentifier = 0x06,
        Sequence = 0x30,
        IPAddress = 0x40,
        Counter32 = 0x41,
        Gauge = 0x42,
        TimeTicks = 0x43,
        Opaque = 0x44,
        NetAddress = 0x45,
        Counter64 = 0x46,
        UInt32 = 0x47,
        GetRequestPDU = 0xa0,
        GetNextRequestPDU = 0xa1,
        GetResponsePDU = 0xa2,
        SetRequestPDU = 0xa3,
        TrapPDUv1 = 0xa4,
        GetBulkRequest = 0xa5,
        InformRequest = 0xa6,
        TrapPDUv2 = 0xa7
    }

    public class SNMPPacket
    {
        public long Version;
        public string Community;
        public SnmpType PacketType;
        public long RequestID;
        public long Error;
        public long Index;
        public string ObjectID;
        public SnmpType ValueType;
        public object Value;

        public SNMPPacket() : base() { }

        // commonly used snmp type constants
        public SNMPPacket(ref byte[] Packet) : base()
        {
            try {
                int Pos = 0;
                GetSNMPSequence(ref Packet, ref Pos, (byte)SnmpType.Sequence);
                Version = GetSNMPInteger(ref Packet, ref Pos);
                Community = GetSNMPString(ref Packet, ref Pos);
                GetSNMPSequence(ref Packet, ref Pos, (byte)SnmpType.GetResponsePDU);
                RequestID = GetSNMPInteger(ref Packet, ref Pos);
                Error = GetSNMPInteger(ref Packet, ref Pos);
                Index = GetSNMPInteger(ref Packet, ref Pos);
                GetSNMPSequence(ref Packet, ref Pos, (byte)SnmpType.Sequence);
                GetSNMPSequence(ref Packet, ref Pos, (byte)SnmpType.Sequence);
                ObjectID = GetSNMPObjectID(ref Packet, ref Pos);
                ValueType = 0;
                Value = null;
                if (Error == 0) {
                    // extract first data component (usually there only is one)
                    ValueType = (SnmpType)Packet[Pos];
                    switch (ValueType) {
                        case SnmpType.Boolean:
                            Value = GetSNMPInteger(ref Packet, ref Pos, (byte)ValueType); break;
                        case SnmpType.Integer:
                            Value = GetSNMPInteger(ref Packet, ref Pos); break;
                        case SnmpType.OctetString:
                            Value = GetSNMPString(ref Packet, ref Pos); break;
                        case SnmpType.Null:
                            Value = GetSNMPNull(ref Packet, ref Pos); break;
                        case SnmpType.ObjectIdentifier:
                            Value = GetSNMPObjectID(ref Packet, ref Pos); break;
                        case SnmpType.IPAddress:
                            Value = GetSNMPIPAddress(ref Packet, ref Pos); break;
                        case SnmpType.Counter32:
                            Value = GetSNMPInteger(ref Packet, ref Pos, (byte)ValueType); break;
                        case SnmpType.Gauge:
                            Value = GetSNMPInteger(ref Packet, ref Pos, (byte)ValueType); break;
                        case SnmpType.TimeTicks:
                            Value = new System.TimeSpan((long)GetSNMPInteger(ref Packet, ref Pos, (byte)ValueType) * 100000); break;
                        case SnmpType.Opaque:
                            Value = GetSNMPInteger(ref Packet, ref Pos, (byte)ValueType); break;
                        case SnmpType.NetAddress:
                            Value = GetSNMPInteger(ref Packet, ref Pos, (byte)ValueType); break;
                        case SnmpType.Counter64:
                            Value = GetSNMPInteger(ref Packet, ref Pos, (byte)ValueType); break;
                        case SnmpType.UInt32:
                            Value = GetSNMPInteger(ref Packet, ref Pos, (byte)ValueType); break;
                        default:
                            Value = GetSNMPBinary(ref Packet, ref Pos); break;
                    }
                    //GetSNMPNull(ref Packet, ref Pos);
                }
            } catch { }
        }

        public byte[] GetBytes()
        {
            if (RequestID == 0) RequestID = 1;

            // create components for request Packet
            byte[] _Version = SNMPInteger(Version);
            byte[] _Community = SNMPString(Community);
            byte[] _RequestID = SNMPInteger(RequestID);
            byte[] _Error = SNMPInteger(Error);
            byte[] _Index = SNMPInteger(Index);
            byte[] _ObjectID = SNMPObjectID(ObjectID);
            byte[] _EndMarker = SNMPNull();

            byte[] Packet = new byte[8 + _Version.Length + _Community.Length + _RequestID.Length + _Error.Length + _Index.Length + _ObjectID.Length + _EndMarker.Length];

            byte[] _ASN1Header = SNMPSequence(0x30, Packet.Length - 2);
            int RequestLength = 6 + _RequestID.Length + _Error.Length + _Index.Length + _ObjectID.Length;
            byte[] _RequestHeader = SNMPSequence((int)PacketType, RequestLength);
            int VarBindListLength = 4 + _ObjectID.Length;
            byte[] _VarBindList = SNMPSequence(0x30, VarBindListLength);
            int VarBindLength = 2 + _ObjectID.Length;
            byte[] _VarBind = SNMPSequence(0x30, VarBindLength);

            // combine components to request Packet
            int Pos = 0;
            System.Buffer.BlockCopy(_ASN1Header, 0, Packet, Pos, _ASN1Header.Length);
            Pos += _ASN1Header.Length;
            System.Buffer.BlockCopy(_Version, 0, Packet, Pos, _Version.Length);
            Pos += _Version.Length;
            System.Buffer.BlockCopy(_Community, 0, Packet, Pos, _Community.Length);
            Pos += _Community.Length;
            System.Buffer.BlockCopy(_RequestHeader, 0, Packet, Pos, _RequestHeader.Length);
            Pos += _RequestHeader.Length;
            System.Buffer.BlockCopy(_RequestID, 0, Packet, Pos, _RequestID.Length);
            Pos += _RequestID.Length;
            System.Buffer.BlockCopy(_Error, 0, Packet, Pos, _Error.Length);
            Pos += _Error.Length;
            System.Buffer.BlockCopy(_Index, 0, Packet, Pos, _Index.Length);
            Pos += _Index.Length;
            System.Buffer.BlockCopy(_VarBindList, 0, Packet, Pos, _VarBindList.Length);
            Pos += _VarBindList.Length;
            System.Buffer.BlockCopy(_VarBind, 0, Packet, Pos, _VarBind.Length);
            Pos += _VarBind.Length;
            System.Buffer.BlockCopy(_ObjectID, 0, Packet, Pos, _ObjectID.Length);
            Pos += _ObjectID.Length;
            System.Buffer.BlockCopy(_EndMarker, 0, Packet, Pos, _EndMarker.Length);
            Pos += _EndMarker.Length;

            return Packet;
        }
        
        // add variable length in front of byte list
        private void AddSNMPLength(ref System.Collections.ArrayList B, int Len)
        {
            int Pos = 0;
            B.Insert(Pos++, (byte)(Len & 0xFF));
            Len >>= 8;
            while (Len != 0) {
                B.Insert(Pos++, (byte)(Len & 0xFF));
                Len >>= 8;
            }
            if (Pos > 1) B.Insert(0, (byte)(B.Count | 0x80));
        }

        // create snmp integer
        private byte[] SNMPInteger(long Val)
        {
            System.Collections.ArrayList B = new System.Collections.ArrayList();
            do {
                B.Insert(0, (byte)(Val & 0xFF));
                Val >>= 8;
            } while (Val != 0);
            AddSNMPLength(ref B, B.Count);
            byte[] Result = new byte[1 + B.Count];
            int Pos = 0;
            Result[Pos++] = (byte)SnmpType.Integer;
            for (int i=0; i<B.Count; i++) Result[Pos++] = (byte)B[i];
            return Result;
        }

        // create snmp octet string
        private byte[] SNMPString(string Val)
        {
            System.Collections.ArrayList B = new System.Collections.ArrayList();
            AddSNMPLength(ref B, Val.Length);
            byte[] Result = new byte[1 + B.Count + Val.Length];
            int Pos = 0;
            Result[Pos++] = (byte)SnmpType.OctetString;
            for (int i=0; i<B.Count; i++) Result[Pos++] = (byte)B[i];
            System.Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Val), 0, Result, Pos, Val.Length);
            return Result;
        }

        // create snmp null
        private byte[] SNMPNull()
        {
            byte[] Result = new byte[2];
            Result[0] = (byte)SnmpType.Null;
            Result[1] = 0x00; // length is zero
            return Result;
        }

        // create snmp OID
        private byte[] SNMPObjectID(string Val)
        {
            if (Val.StartsWith("1.3")) Val = "43" + Val.Substring(3);
            System.Collections.ArrayList B = new System.Collections.ArrayList();
            string[] A = Val.Split('.');
            int Len = 0;
            for (int i=0; i<A.Length; i++) {
                int V = System.Convert.ToInt32(A[i]);
                B.Insert(Len, (byte)(V & 0x7F));
                V >>= 7;
                while(V != 0) {
                    B.Insert(Len, (byte)(V & 0x7F | 0x80));
                    V >>= 7;
                }
                Len = B.Count;
            }
            AddSNMPLength(ref B, B.Count);
            byte[] Result = new byte[1 + B.Count];
            int Pos = 0;
            Result[Pos++] = (byte)SnmpType.ObjectIdentifier;
            for (int i=0; i<B.Count; i++) Result[Pos++] = (byte)B[i];
            return Result;
        }

        // create snmp sequence header
        private byte[] SNMPSequence(long Tag, int Len)
        {
            System.Collections.ArrayList B = new System.Collections.ArrayList();
            AddSNMPLength(ref B, Len);
            byte[] Result = new byte[1 + B.Count];
            int Pos = 0;
            Result[Pos++] = (byte)Tag;
            for (int i=0; i<B.Count; i++) Result[Pos++] = (byte)B[i];
            return Result;
        }

        // get snmp variable length
        private int GetSNMPLength(ref byte[] Packet, ref int Pos)
        {
            int Len = Packet[Pos++];
            if (Len > 128) {
                int L = Len & 0x7F;
                Len = 0;
                for (int i=0; i<L; i++) Len = (Len << 8) + Packet[Pos++];
            }
            return Len;
        }

        // get snmp integer
        private long GetSNMPInteger(ref byte[] Packet, ref int Pos)
        {
            if (Packet[Pos++] != (byte)SnmpType.Integer) throw new System.Exception("ASN.1 Integer expected.");
            int Len = GetSNMPLength(ref Packet, ref Pos);
            long Val = 0;
            for (int i=0; i<Len; i++) Val = (Val << 8) + Packet[Pos++];
            return Val;
        }

        // get snmp variable as integer (used for booleans, integers of all sizes, counters, etc.)
        private long GetSNMPInteger(ref byte[] Packet, ref int Pos, int Tag)
        {
            if (Packet[Pos++] != Tag) throw new System.Exception("ASN.1 Integer " + System.String.Format("0x{0:x}", Tag) + " expected.");
            int Len = GetSNMPLength(ref Packet, ref Pos);
            long Val = 0;
            for (int i=0; i<Len; i++) Val = (Val << 8) + Packet[Pos++];
            return Val;
        }

        // get snmp octet string (detect dates and numeric mac addresses)
        private string GetSNMPString(ref byte[] Packet, ref int Pos)
        {
            if (Packet[Pos++] != (byte)SnmpType.OctetString) throw new System.Exception("ASN.1 String expected.");
            int Len = GetSNMPLength(ref Packet, ref Pos);

            // detect date/time
            try {
                int y = (Packet[Pos] * 256) + Packet[Pos + 1];
                int m = Packet[Pos + 2];
                int d = Packet[Pos + 3];
                if ((y < 2020) && (y > 1990) && (m < 13) && (d < 32)) {
                    int h = Packet[Pos + 4];
                    int n = Packet[Pos + 5];
                    int s = Packet[Pos + 6];
                    int t = Packet[Pos + 7];
                    string DateResult = y + "-" + ((m < 10) ? "0" : "") + m + "-" + ((d < 10) ? "0" : "") + d + " "
                        + ((h < 10) ? "0" : "") + h + ":" + ((n < 10) ? "0" : "") + n + ":" + ((s < 10) ? "0" : "") + s
                        + "." + t;
                    Pos += Len;
                    return DateResult;
                }
            } catch { }

            // detect numbers
            string Result = "";
            if (Len <= 8) for (int l=Pos; l<Pos+Len-1; l++) if (Packet[l] < 32) {
                Result = System.BitConverter.ToString(Packet, Pos, Len);
                break;
            }

            if (Result == "") Result = System.Text.Encoding.ASCII.GetString(Packet, Pos, Len);
            Pos += Len;
            return Result;
        }

        // get snmp null
        private string GetSNMPNull(ref byte[] Packet, ref int Pos)
        {
            if (Packet[Pos++] != (byte)SnmpType.Null) throw new System.Exception("ASN.1 Null expected.");
            Pos++;
            return "";
        }

        // get snmp OID
        private string GetSNMPObjectID(ref byte[] Packet, ref int Pos)
        {
            if (Packet[Pos++] != (byte)SnmpType.ObjectIdentifier) throw new System.Exception("ASN.1 ObjectID expected.");
            int Len = GetSNMPLength(ref Packet, ref Pos);
            string Result = "";
            for (int i = Pos; i < Pos + Len; i++) {
                int Val = 0;
                int V;
                do {
                    V = Packet[i];
                    Val = (Val << 7) + (V & 0x7F);
                    if (V > 127) i++; else break;
                } while(true);
                if (Result != "") Result += ".";
                Result += Val;
            }
            if (Result.StartsWith("43")) Result = "1.3" + Result.Substring(2);
            Pos += Len;
            return Result;
        }

        // get snmp sequence length (used for Sequence, GetResponsePDU, etc)
        private int GetSNMPSequence(ref byte[] Packet, ref int Pos, int Tag)
        {
            if (Packet[Pos++] != (byte)Tag) throw new System.Exception("ASN.1 " + System.String.Format("0x{0:x}", Tag) + " expected.");
            return GetSNMPLength(ref Packet, ref Pos);
        }

        // get snmp ipaddress
        private string GetSNMPIPAddress(ref byte[] Packet, ref int Pos)
        {
            if (Packet[Pos++] != (byte)SnmpType.IPAddress) throw new System.Exception("ASN.1 IPAddress expected.");
            int Len = GetSNMPLength(ref Packet, ref Pos);
            string Result = "";
            for (int i=0; i<Len; i++) {
                if (Result != "") Result += ".";
                Result += Packet[Pos++];
            }
            return Result;
        }

        // get binary snmp data (used for unknown data types)
        private string GetSNMPBinary(ref byte[] Packet, ref int Pos)
        {
            Pos++;
            int Len = GetSNMPLength(ref Packet, ref Pos);
            string Result = System.BitConverter.ToString(Packet, Pos, Len);
            Pos += Len;
            return Result;
        }
    }
}