From e6b25374f30f124d47d211e8bfa52fae729bde71 Mon Sep 17 00:00:00 2001 From: Harald Wolff Date: Mon, 11 Mar 2019 08:57:19 +0100 Subject: [PATCH] USM WIP --- BasicEncodingRules.cs | 2 +- SNMPEngine.cs | 96 ++++++++--------- SNMPInterface.cs | 31 +++--- asn1/ASN1Value.cs | 185 +++++++++++++++++++++++++++++++++ channel/SnmpPDUChannel.cs | 20 ---- channel/SnmpV1Channel.cs | 49 --------- channel/SnmpV1Endpoint.cs | 42 ++++++++ channel/SnmpV2Channel.cs | 17 --- channel/SnmpV2Endpoint.cs | 42 ++++++++ channel/USMEndpoint.cs | 145 ++++++++++++++++++++++++++ ln.snmp.csproj | 21 +++- types/Boolean.cs | 3 + types/Integer.cs | 6 +- types/NullValue.cs | 3 +- types/ObjectIdentifier.cs | 3 + types/OctetString.cs | 35 +++++-- types/PDU.cs | 64 +++++++++--- types/ScopedPDU.cs | 44 ++++++++ types/Sequence.cs | 31 ++++-- types/SnmpMessage.cs | 48 +++++++++ types/SnmpV1Message.cs | 46 ++++++++ types/SnmpV2Message.cs | 50 +++++++++ types/USMMessage.cs | 110 ++++++++++++++++++++ types/UsmSecurityParameters.cs | 69 ++++++++++++ types/V3Packet.cs | 51 +++++++++ types/Variable.cs | 178 +++++++++++++++++-------------- 26 files changed, 1115 insertions(+), 276 deletions(-) create mode 100644 asn1/ASN1Value.cs delete mode 100644 channel/SnmpPDUChannel.cs delete mode 100644 channel/SnmpV1Channel.cs create mode 100644 channel/SnmpV1Endpoint.cs delete mode 100644 channel/SnmpV2Channel.cs create mode 100644 channel/SnmpV2Endpoint.cs create mode 100644 channel/USMEndpoint.cs create mode 100644 types/ScopedPDU.cs create mode 100644 types/SnmpMessage.cs create mode 100644 types/SnmpV1Message.cs create mode 100644 types/SnmpV2Message.cs create mode 100644 types/USMMessage.cs create mode 100644 types/UsmSecurityParameters.cs create mode 100644 types/V3Packet.cs diff --git a/BasicEncodingRules.cs b/BasicEncodingRules.cs index 0c64a16..5235d15 100644 --- a/BasicEncodingRules.cs +++ b/BasicEncodingRules.cs @@ -47,7 +47,7 @@ namespace ln.snmp public override string ToString() { - return String.Format("[ASN.1 Type Class={0} Constructed={1} Number={2} FirstByte=0x{3,02:X}]",IdentifierClass,Constructed,Number,Firstbyte); + return String.Format("[ASN.1 Type Class={0} Constructed={1} Number={2} FirstByte=0x{3,2:X}]",IdentifierClass,Constructed,Number,Firstbyte); } } diff --git a/SNMPEngine.cs b/SNMPEngine.cs index 46dd36b..0e135bf 100644 --- a/SNMPEngine.cs +++ b/SNMPEngine.cs @@ -13,6 +13,8 @@ using ln.snmp.types; using System.Net.Sockets; using System.Net; using System.Threading; +using ln.snmp.asn1; +using ln.logging; namespace ln.snmp { @@ -23,10 +25,14 @@ namespace ln.snmp public UdpClient LocalEndpoint { get; private set; } public int Timeout { get; set; } = 1000; - private Dictionary queuedRequests = new Dictionary(); + private Dictionary queuedRequests = new Dictionary(); private bool shutdown = false; private Thread ReceiverThread { get; set; } + + private int nextMessageID = (int)DateTimeOffset.Now.ToUnixTimeSeconds(); + public int NextMessageID => nextMessageID++; + public SNMPEngine() { LocalEndpoint = new UdpClient(); @@ -55,86 +61,81 @@ namespace ln.snmp IPEndPoint remoteEndpoint = null; byte[] datagram = LocalEndpoint.Receive(ref remoteEndpoint); + Logging.Log(LogLevel.DEBUG,"SNMPClient: Received: {0}", BitConverter.ToString(datagram)); + + ASN1Value asn = new ASN1Value(datagram); + + asn.Dump(); + + SnmpMessage snmpMessage = asn; + + snmpMessage.Dump(); + lock (queuedRequests) { - if (queuedRequests.ContainsKey(remoteEndpoint)) + if (queuedRequests.ContainsKey(snmpMessage.MessageID)) { - InternalRequest internalRequest = queuedRequests[remoteEndpoint]; - internalRequest.Response = datagram; - lock (internalRequest) + SnmpMessage snmpRequestMessage = queuedRequests[snmpMessage.MessageID]; + lock (snmpRequestMessage) { - Monitor.PulseAll(internalRequest); + queuedRequests[snmpMessage.MessageID] = snmpMessage; + Monitor.PulseAll(snmpRequestMessage); } } + else + { + Logging.Log(LogLevel.WARNING, "SnmpEngine.Receiver(): Can't find pending request with MessageID {0} ( 0x{0:x8} )",snmpMessage.MessageID); + } } } catch (SocketException se) { - + Logging.Log(se); } catch (Exception e) { - Console.WriteLine("Receiver(): {0}", e); + Logging.Log(e); } } } - private byte[] SendRequest(IPEndPoint remoteEndpoint,byte[] request,int timeout) + public SnmpMessage SNMPRequest(IPEndPoint remoteEndpoint,SnmpMessage snmpMessage,int timeout) { - InternalRequest internalRequest = new InternalRequest(); - internalRequest.RemoteEndpoint = remoteEndpoint; - - lock(queuedRequests) + lock (queuedRequests) { - if (queuedRequests.ContainsKey(remoteEndpoint)) - throw new ArgumentException("Already pending request exists for this remote endpoint", nameof(remoteEndpoint)); + ASN1Value snmpRequest = snmpMessage; + byte[] snmpRequestBytes = snmpRequest.AsByteArray; - queuedRequests.Add(remoteEndpoint,internalRequest); + queuedRequests.Add(snmpMessage.MessageID, snmpMessage); - if (DEBUG) - Console.WriteLine("SNMPClient: Send: {0}", BitConverter.ToString(request)); + Logging.Log(LogLevel.DEBUG,"SNMPClient: Send: {0}", BitConverter.ToString(snmpRequestBytes)); + Logging.Log(LogLevel.DEBUG,"sent snmpMessage with MessageID {0} ( 0x{0:x8} )",snmpMessage.MessageID); + snmpMessage.Dump(); - LocalEndpoint.Send(request, request.Length, remoteEndpoint); + LocalEndpoint.Send(snmpRequestBytes, snmpRequestBytes.Length, remoteEndpoint); - lock (internalRequest) + lock (snmpMessage) { Monitor.Exit(queuedRequests); - bool success = Monitor.Wait(internalRequest, timeout); + bool success = Monitor.Wait(snmpMessage, timeout); Monitor.Enter(queuedRequests); + if (!success) { + queuedRequests.Remove(snmpMessage.MessageID); throw new TimeoutException(); } } - queuedRequests.Remove(remoteEndpoint); + + SnmpMessage responseMessage = queuedRequests[snmpMessage.MessageID]; + queuedRequests.Remove(snmpMessage.MessageID); + + return responseMessage; } - - return internalRequest.Response; } - - public Variable SNMPRequest(IPEndPoint remoteEndpoint,Sequence request) - { - byte[] response = SendRequest(remoteEndpoint, request.ToBytes(), Timeout); - - if (DEBUG) - Console.WriteLine("SNMPClient: Received: {0}", BitConverter.ToString(response)); - - Variable vreply = Variable.Read(response); - return vreply; - } - - - //public abstract List Walk(ObjectIdentifier baseOID); - //public abstract List Get(List baseOID); - - //public virtual Variable Get(ObjectIdentifier oid) - //{ - // return Get(new List(new ObjectIdentifier[] { oid }))[0]; - //} - public void Dispose() { if (ReceiverThread != null) @@ -151,10 +152,5 @@ namespace ln.snmp } } - class InternalRequest - { - public IPEndPoint RemoteEndpoint; - public byte[] Response; - } } } diff --git a/SNMPInterface.cs b/SNMPInterface.cs index e2dd587..e661f7d 100644 --- a/SNMPInterface.cs +++ b/SNMPInterface.cs @@ -10,20 +10,20 @@ namespace ln.snmp { public enum SnmpVersion : int { V1 = 0, V2c = 1, V3 = 3 } - public class SNMPInterface + public abstract class SNMPInterface { - public SnmpPDUChannel PDUChannel { get; set; } - + public SNMPEngine SNMPEngine { get; } public IPEndPoint RemoteEndpoint { get; set; } - public SnmpVersion SnmpVersion => PDUChannel.SnmpVersion; + public abstract SnmpVersion SnmpVersion { get; } - public SNMPInterface(SnmpPDUChannel PDUChannel,IPEndPoint remoteEndpoint) + public SNMPInterface(SNMPEngine snmpEngine,IPEndPoint remoteEndpoint) { - this.PDUChannel = PDUChannel; + SNMPEngine = snmpEngine; RemoteEndpoint = remoteEndpoint; } + public abstract PDU snmpRequest(PDU pdu); private List snmpRequest(IEnumerable objectIdentifiers) where T: PDU, new() { @@ -34,6 +34,13 @@ namespace ln.snmp } PDU responsePDU = snmpRequest(pdu); + + if (responsePDU.Error.LongValue != 0) + { + string indicator = responsePDU.ErrorIndex > 0 && responsePDU.ErrorIndex <= pdu.VarBinds.Items.Length ? ((pdu.VarBinds.Items[(int)responsePDU.ErrorIndex - 1] as Sequence).Items[0] as ObjectIdentifier).AsString : ""; + throw new SnmpError(responsePDU.Error, responsePDU.ErrorIndex, indicator); + } + Sequence varBinds = responsePDU.VarBinds as Sequence; List results = new List(); @@ -44,12 +51,6 @@ namespace ln.snmp return results; } - private PDU snmpRequest(PDU pdu) - { - GetResponse responsePDU = PDUChannel.RequestResponse(pdu, RemoteEndpoint) as GetResponse; - return responsePDU; - } - public List snmpGet(IEnumerable objectIdentifiers) { return snmpRequest(objectIdentifiers); @@ -224,14 +225,10 @@ namespace ln.snmp } return results.ToArray(); } - else if (SnmpVersion == SnmpVersion.V2c) + else { return snmpGetBulk(objectIdentifiers); } - else - { - throw new NotImplementedException(); - } } public Sequence[][] snmpWalk(IEnumerable objectIdentifiers) { diff --git a/asn1/ASN1Value.cs b/asn1/ASN1Value.cs new file mode 100644 index 0000000..426d58e --- /dev/null +++ b/asn1/ASN1Value.cs @@ -0,0 +1,185 @@ +// /** +// * File: ASN1Value.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.Collections.Generic; +using System.IO; +using ln.snmp.types; +using ln.logging; +namespace ln.snmp.asn1 +{ + public class ASN1Value + { + public Identifier Identifier { get; set; } + + private byte[] octets; + private List items; + + public ASN1Value(byte[] source) + :this(new MemoryStream(source)) + { + } + public ASN1Value(Stream stream) + { + Identifier = BasicEncodingRules.ReadIdentifier(stream); + int length = BasicEncodingRules.ReadLength(stream); + + if (Identifier.Constructed) + { + items = new List(); + + long next = stream.Position + length; + while (stream.Position < next) + items.Add(new ASN1Value(stream)); + } + else + { + octets = new byte[length]; + stream.Read(octets, 0, length); + } + } + + public ASN1Value(Identifier identifier) + { + Identifier = identifier; + if (Identifier.Constructed) + items = new List(); + else + octets = new byte[0]; + } + + public byte[] AsByteArray + { + get + { + if (Identifier.Constructed) + { + byte[][] binaryItems = new byte[items.Count][]; + int length = 0; + for (int n = 0; n < binaryItems.Length; n++) + { + binaryItems[n] = items[n].AsByteArray; + length += binaryItems[n].Length; + } + + MemoryStream stream = new MemoryStream(); + BasicEncodingRules.WriteIdentifier(stream, Identifier); + BasicEncodingRules.WriteLength(stream, length); + for (int n = 0; n < binaryItems.Length; n++) + stream.Write(binaryItems[n], 0, binaryItems[n].Length); + + return stream.ToArray(); + } + else + { + MemoryStream stream = new MemoryStream(); + BasicEncodingRules.WriteIdentifier(stream, Identifier); + BasicEncodingRules.WriteLength(stream, octets.Length); + stream.Write(octets, 0, octets.Length); + return stream.ToArray(); + } + } + } + + + public byte[] Bytes + { + get + { + if (!Identifier.Constructed) + { + return octets; + } + else + { + byte[][] binaryItems = new byte[items.Count][]; + int length = 0; + for (int n = 0; n < binaryItems.Length; n++) + { + binaryItems[n] = items[n].AsByteArray; + length += binaryItems[n].Length; + } + + byte[] bytes = new byte[length]; + int offset = 0; + + for (int n = 0; n < binaryItems.Length; n++) + { + Array.Copy(binaryItems[n], 0, bytes, offset, binaryItems[n].Length); + offset += binaryItems[n].Length; + } + + return bytes; + } + } + set + { + if (!Identifier.Constructed) + { + octets = value; + } + else + { + throw new NotImplementedException(); + } + } + + } + + public ASN1Value[] Items + { + get + { + if (!Identifier.Constructed) + throw new ArgumentOutOfRangeException("ASN1Value is not constructed"); + + return items.ToArray(); + } + set + { + if (!Identifier.Constructed) + throw new ArgumentOutOfRangeException("ASN1Value is not constructed"); + + items = new List(value); + } + } + + public void Add(ASN1Value item) + { + if (!Identifier.Constructed) + throw new ArgumentOutOfRangeException("ASN1Value is not constructed"); + items.Add(item); + } + + public override string ToString() + { + return String.Format("[ASN1Value Identifier={0}]",Identifier); + } + + public void Dump() + { + Dump(""); + } + public void Dump(string prefix) + { + if (Identifier.Constructed) + { + Logging.Log(LogLevel.DEBUG, "{1}{0}", this.Identifier, prefix); + foreach (ASN1Value item in items) + item.Dump(prefix + " "); + } + else + { + Logging.Log(LogLevel.DEBUG, "{2}{0} {1}",this.Identifier,BitConverter.ToString(Bytes),prefix); + } + } + + + } +} diff --git a/channel/SnmpPDUChannel.cs b/channel/SnmpPDUChannel.cs deleted file mode 100644 index f4a1857..0000000 --- a/channel/SnmpPDUChannel.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using ln.snmp.types; -using System.Net; -namespace ln.snmp.channel -{ - public abstract class SnmpPDUChannel - { - public SNMPEngine SNMPClient { get; set; } - - - public abstract SnmpVersion SnmpVersion { get; } - - public SnmpPDUChannel(SNMPEngine client) - { - SNMPClient = client; - } - - public abstract PDU RequestResponse(PDU pdu, IPEndPoint remoteEndpoint); - } -} diff --git a/channel/SnmpV1Channel.cs b/channel/SnmpV1Channel.cs deleted file mode 100644 index 7ba570f..0000000 --- a/channel/SnmpV1Channel.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using ln.snmp.types; -using System.Net; -using System.Collections.Generic; - -namespace ln.snmp.channel -{ - public class SnmpV1Channel : SnmpPDUChannel - { - public override SnmpVersion SnmpVersion => SnmpVersion.V1; - - public OctetString CommunityString { get; set; } - - - public SnmpV1Channel(SNMPEngine client) - :base(client) - { - CommunityString = "public"; - } - public SnmpV1Channel(SNMPEngine client,string communityString) - :base(client) - { - CommunityString = communityString; - } - - public override PDU RequestResponse(PDU pdu,IPEndPoint remoteEndpoint) - { - Integer version = new Integer((int)SnmpVersion); - - Sequence snmpRequest = new Sequence(); - snmpRequest.Add(version); - snmpRequest.Add(CommunityString); - snmpRequest.Add(pdu); - - Variable reply = SNMPClient.SNMPRequest(remoteEndpoint, snmpRequest); - - Sequence sreply = reply as Sequence; - PDU responsePDU = sreply.Items[2] as PDU; - - if (responsePDU.Error.LongValue != 0) - { - string indicator = responsePDU.ErrorIndex > 0 && responsePDU.ErrorIndex <= pdu.VarBinds.Items.Length ? ((pdu.VarBinds.Items[(int)responsePDU.ErrorIndex - 1] as Sequence).Items[0] as ObjectIdentifier).AsString : ""; - throw new SnmpError(responsePDU.Error, responsePDU.ErrorIndex, indicator); - } - - return responsePDU; - } - } -} diff --git a/channel/SnmpV1Endpoint.cs b/channel/SnmpV1Endpoint.cs new file mode 100644 index 0000000..9930300 --- /dev/null +++ b/channel/SnmpV1Endpoint.cs @@ -0,0 +1,42 @@ +using System; +using ln.snmp.types; +using System.Net; +using System.Collections.Generic; + +namespace ln.snmp.channel +{ + public class SnmpV1Endpoint : SNMPInterface + { + public OctetString CommunityString { get; set; } + + public override SnmpVersion SnmpVersion => SnmpVersion.V1; + + public SnmpV1Endpoint(SNMPEngine snmpEngine, IPEndPoint remoteEndpoint) + : base(snmpEngine, remoteEndpoint) + { + CommunityString = "public"; + } + public SnmpV1Endpoint(SNMPEngine snmpEngine, IPEndPoint remoteEndpoint, OctetString communityString) + : base(snmpEngine, remoteEndpoint) + { + CommunityString = communityString; + } + public SnmpV1Endpoint(SNMPEngine snmpEngine, IPEndPoint remoteEndpoint, String communityString) + : base(snmpEngine, remoteEndpoint) + { + CommunityString = new OctetString(communityString); + } + + public override PDU snmpRequest(PDU pdu) + { + SnmpV1Message request = new SnmpV1Message(); + request.MessageID = SNMPEngine.NextMessageID; + request.snmpCommunity = CommunityString; + request.snmpPDU = pdu; + + SnmpV1Message response = SNMPEngine.SNMPRequest(RemoteEndpoint, request, SNMPEngine.Timeout) as SnmpV1Message; + return response.snmpPDU; + } + + } +} diff --git a/channel/SnmpV2Channel.cs b/channel/SnmpV2Channel.cs deleted file mode 100644 index 1eae9f1..0000000 --- a/channel/SnmpV2Channel.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -namespace ln.snmp.channel -{ - public class SnmpV2Channel : SnmpV1Channel - { - public override SnmpVersion SnmpVersion => SnmpVersion.V2c; - - public SnmpV2Channel(SNMPEngine client) - : base(client) - { - } - public SnmpV2Channel(SNMPEngine client, string communityString) - : base(client, communityString) - { } - - } -} diff --git a/channel/SnmpV2Endpoint.cs b/channel/SnmpV2Endpoint.cs new file mode 100644 index 0000000..4fea0c3 --- /dev/null +++ b/channel/SnmpV2Endpoint.cs @@ -0,0 +1,42 @@ +using System; +using ln.snmp.types; +using System.Net; +using System.Collections.Generic; + +namespace ln.snmp.channel +{ + public class SnmpV2Endpoint : SNMPInterface + { + public OctetString CommunityString { get; set; } + + public override SnmpVersion SnmpVersion => SnmpVersion.V2c; + + public SnmpV2Endpoint(SNMPEngine snmpEngine, IPEndPoint remoteEndpoint) + : base(snmpEngine, remoteEndpoint) + { + CommunityString = "public"; + } + public SnmpV2Endpoint(SNMPEngine snmpEngine, IPEndPoint remoteEndpoint, OctetString communityString) + : base(snmpEngine, remoteEndpoint) + { + CommunityString = communityString; + } + public SnmpV2Endpoint(SNMPEngine snmpEngine, IPEndPoint remoteEndpoint, String communityString) + : base(snmpEngine, remoteEndpoint) + { + CommunityString = new OctetString(communityString); + } + + public override PDU snmpRequest(PDU pdu) + { + SnmpV2Message request = new SnmpV2Message(); + request.MessageID = SNMPEngine.NextMessageID; + request.snmpCommunity = CommunityString; + request.snmpPDU = pdu; + + SnmpV2Message response = SNMPEngine.SNMPRequest(RemoteEndpoint, request, SNMPEngine.Timeout) as SnmpV2Message; + return response.snmpPDU; + } + + } +} diff --git a/channel/USMEndpoint.cs b/channel/USMEndpoint.cs new file mode 100644 index 0000000..7437b65 --- /dev/null +++ b/channel/USMEndpoint.cs @@ -0,0 +1,145 @@ +using System; +using System.Net; +using ln.snmp.types; +using System.Text; +using System.Security.Cryptography; +using ln.snmp.asn1; +using System.Linq; + +namespace ln.snmp.channel +{ + public enum SnmpV3AuthMethod { MD5, SHA } + public enum SnmpV3PrivMethod { DES } + public enum SnmpAuthLevel : int { noAuthNoPriv = 0, authNoPriv = 0x01, authPriv = 0x03 } + + public class USMEndpoint : SNMPInterface + { + public OctetString RemoteEngineID { get; set; } + + public string Username { get; set; } + public string AuthKey { get; set; } + public string PrivKey { get; set; } + + public SnmpV3AuthMethod AuthMethod { get; set; } + public SnmpV3PrivMethod PrivMethod { get; set; } + + public override SnmpVersion SnmpVersion => SnmpVersion.V3; + + public USMEndpoint(SNMPEngine snmpEngine,IPEndPoint remoteEndpoint) + :base(snmpEngine,remoteEndpoint) + { + } + + public SnmpAuthLevel AuthLevel + { + get + { + if (PrivKey != null) + return SnmpAuthLevel.authPriv; + if (AuthKey != null) + return SnmpAuthLevel.authNoPriv; + + return SnmpAuthLevel.noAuthNoPriv; + } + } + + public override PDU snmpRequest(PDU pdu) + { + if ((RemoteEngineID == null)||(RemoteEngineID.Bytes.Length == 0)) + { + QueryEngineID(); + } + + USMMessage request = new USMMessage(); + ScopedPDU scopedPDU = new ScopedPDU(); + + request.MessageID = SNMPEngine.NextMessageID; + request.msgData = scopedPDU; + + scopedPDU.contextEngineID = RemoteEngineID; + scopedPDU.PDU = pdu; + + AuthenticateMessage(request); + + + SnmpMessage reply = SNMPEngine.SNMPRequest(RemoteEndpoint, request, SNMPEngine.Timeout); + USMMessage replyUSM = reply as USMMessage; + + PDU responsePDU = (replyUSM.msgData as ScopedPDU).PDU; + + return responsePDU; + } + + public void QueryEngineID() + { + USMMessage queryMessage = new USMMessage(); + queryMessage.MessageID = SNMPEngine.NextMessageID; + + ScopedPDU scopedPDU = new ScopedPDU(); + + queryMessage.msgData = scopedPDU; + + queryMessage.Dump(); + + + SnmpMessage reply = SNMPEngine.SNMPRequest(RemoteEndpoint, queryMessage, SNMPEngine.Timeout); + USMMessage usmReply = reply as USMMessage; + + RemoteEngineID = usmReply.SecurityParameters.msgAuthoritativeEngineID; + } + + public bool AuthenticateMessage(USMMessage message) + { + message.msgGlobalData.msgFlags.Bytes = new byte[] { (byte)(int)AuthLevel }; + message.SecurityParameters.msgUserName.StringValue = Username; + message.SecurityParameters.msgAuthoritativeEngineID = RemoteEngineID; + message.SecurityParameters.msgAuthenticationParameters.Bytes = new byte[12]; + + byte[] wholeMsg = ((ASN1Value)message).AsByteArray; + + byte[] extendedAuthKey = new byte[64]; + byte[] authKey = Encoding.ASCII.GetBytes(AuthKey); + + //Array.Copy(authKey, extendedAuthKey, 16); + + //byte[] K1 = new byte[64]; + //for (int n = 0; n < 64; n++) + // K1[n] = (byte)(extendedAuthKey[n] ^ IPAD[n]); + + //byte[] K2 = new byte[64]; + //for (int n = 0; n < 64; n++) + //K2[n] = (byte)(extendedAuthKey[n] ^ OPAD[n]); + + HMAC hmac = null; + + switch (AuthMethod) + { + case SnmpV3AuthMethod.MD5: + hmac = HMACMD5.Create(); + break; + case SnmpV3AuthMethod.SHA: + hmac = HMACSHA1.Create(); + break; + } + + hmac.Key = authKey; + byte[] mac = hmac.ComputeHash(wholeMsg); + + message.SecurityParameters.msgAuthenticationParameters.Bytes = mac.Take(12).ToArray(); + + return true; + } + + static byte[] IPAD = __PAD(0x36); + static byte[] OPAD = __PAD(0x5C); + + public static byte[] __PAD(byte pad) + { + byte[] ipad = new byte[0x40]; + for (int n = 0; n < ipad.Length; n++) + ipad[n] = pad; + return ipad; + } + + } +} diff --git a/ln.snmp.csproj b/ln.snmp.csproj index 1236036..b165862 100644 --- a/ln.snmp.csproj +++ b/ln.snmp.csproj @@ -46,13 +46,28 @@ - - - + + + + + + + + + + + + + + + + {D471A566-9FB6-41B2-A777-3C32874ECD0E} + ln.logging + \ No newline at end of file diff --git a/types/Boolean.cs b/types/Boolean.cs index 71624b2..54c1df8 100644 --- a/types/Boolean.cs +++ b/types/Boolean.cs @@ -8,6 +8,8 @@ // * // **/ using System; +using ln.snmp.asn1; + namespace ln.snmp.types { public class Boolean : Variable @@ -19,6 +21,7 @@ namespace ln.snmp.types { } + public override byte[] Bytes { get => BooleanValue ? new byte[] { 0xFF } : new byte[] { 0x00 }; diff --git a/types/Integer.cs b/types/Integer.cs index 8247b8f..02e7174 100644 --- a/types/Integer.cs +++ b/types/Integer.cs @@ -8,6 +8,9 @@ // * // **/ using System; +using ln.snmp.asn1; +using ln.logging; +using System.Linq; namespace ln.snmp.types { public class Integer : Variable @@ -30,10 +33,11 @@ namespace ln.snmp.types { } + public override byte[] Bytes { get => BasicEncodingRules.EncodeInteger(LongValue); - set => LongValue = BasicEncodingRules.DecodeInteger(value); + set => LongValue = BasicEncodingRules.DecodeInteger(value.Reverse().ToArray()); } public override object Value { get => LongValue; set => LongValue = (long)value; } diff --git a/types/NullValue.cs b/types/NullValue.cs index 5d999e6..0834998 100644 --- a/types/NullValue.cs +++ b/types/NullValue.cs @@ -8,6 +8,7 @@ // * // **/ using System; +using ln.snmp.asn1; namespace ln.snmp.types { public class NullValue : Variable @@ -16,7 +17,7 @@ namespace ln.snmp.types byte[] value = new byte[0]; - private NullValue() + public NullValue() :base(new Identifier(IdentifierClass.UNIVERSAL,false,0x05)) { } diff --git a/types/ObjectIdentifier.cs b/types/ObjectIdentifier.cs index 2a07d30..334fe8b 100644 --- a/types/ObjectIdentifier.cs +++ b/types/ObjectIdentifier.cs @@ -9,6 +9,8 @@ // **/ using System; using System.Linq; +using ln.snmp.asn1; + namespace ln.snmp.types { public class ObjectIdentifier : Variable @@ -32,6 +34,7 @@ namespace ln.snmp.types OIDValue = ioid; } + /* Check is b is part of the my subtree or is equal*/ public bool Contains(ObjectIdentifier b) { diff --git a/types/OctetString.cs b/types/OctetString.cs index 400166d..042ebfa 100644 --- a/types/OctetString.cs +++ b/types/OctetString.cs @@ -9,35 +9,48 @@ // **/ using System; using System.Text; +using ln.snmp.asn1; namespace ln.snmp.types { public class OctetString : Variable { - public string StringValue { get; set; } + public override byte[] Bytes { get; set; } + + public string StringValue { + get => Encoding.UTF8.GetString(Bytes); + set => Bytes = Encoding.UTF8.GetBytes(value); + } + + public char[] Characters + { + get => StringValue.ToCharArray(); + set => StringValue = new string(value); + } + public override object Value { get => Bytes; set => Bytes = (byte[])value; } public OctetString() :base(new Identifier(IdentifierClass.UNIVERSAL, false, 4)) { + StringValue = ""; } public OctetString(String text) - :this() + : this() { StringValue = text; } - public override byte[] Bytes { - get => Encoding.UTF8.GetBytes(StringValue); - set => StringValue = Encoding.UTF8.GetString(value); - } - public override object Value { - get => StringValue; - set => StringValue = value as string; - } + public OctetString(byte[] bytes) + : this() + { + Bytes = bytes; + } + + public override string ToString() { - return String.Format("[OctetString StringValue={0}]", StringValue); + return String.Format("[OctetString StringValue={0}]", StringValue.Replace("\0","\\0")); } public static implicit operator String(OctetString octetString) diff --git a/types/PDU.cs b/types/PDU.cs index bca1edc..bdedf38 100644 --- a/types/PDU.cs +++ b/types/PDU.cs @@ -9,6 +9,7 @@ // **/ using System; using System.IO; +using ln.snmp.asn1; namespace ln.snmp.types { public class PDU : AbstractSequence @@ -22,9 +23,10 @@ namespace ln.snmp.types private Variable[] items; public PDU(Identifier identifier) - :base(identifier) + : base(identifier) { - RequestID = new Integer(Environment.TickCount); + //RequestID = new Integer(Environment.TickCount); + RequestID = new Integer(0xDEADBEEF); Error = new Integer(); ErrorIndex = new Integer(); @@ -33,7 +35,27 @@ namespace ln.snmp.types items = new Variable[] { RequestID, Error, ErrorIndex, VarBinds }; } - public override Variable[] Items => items; + public PDU(ASN1Value value) + : base(value.Identifier) + { + RequestID = (Integer)value.Items[0]; + + Error = (Integer)value.Items[1]; + Error = (Integer)value.Items[2]; + VarBinds = (Sequence)value.Items[3]; + } + + public override Variable[] Items + { + get => items; + set + { + RequestID = value[0] as Integer; + Error = value[1] as Integer; + ErrorIndex= value[2] as Integer; + VarBinds = value[3] as Sequence; + } + } public override void Add(Variable item) { @@ -45,18 +67,6 @@ namespace ln.snmp.types VarBinds.Remove(item); } - public override byte[] Bytes - { - set - { - MemoryStream bytes = new MemoryStream(value); - - RequestID = Variable.Read(bytes) as Integer; - Error = Variable.Read(bytes) as Integer; - ErrorIndex = Variable.Read(bytes) as Integer; - VarBinds = Variable.Read(bytes) as Sequence; - } - } @@ -67,20 +77,32 @@ namespace ln.snmp.types public GetRequest() : base(new Identifier(IdentifierClass.CONTEXT, true, 0x00)) { } + public GetRequest(ASN1Value value) + : base(value) + { } + } public class GetNextRequest : PDU { public GetNextRequest() : base(new Identifier(IdentifierClass.CONTEXT, true, 0x01)) { } + public GetNextRequest(ASN1Value value) + : base(value) + { } + } public class GetResponse : PDU { public GetResponse() : base(new Identifier(IdentifierClass.CONTEXT, true, 0x02)) { } + // public GetResponse(ASN1Value value) + //: base(value) + //{ } + } - public class GetBulkRequest: PDU + public class GetBulkRequest : PDU { public Integer NonRepeaters => Error; public Integer MaxRepetitions => ErrorIndex; @@ -89,14 +111,22 @@ namespace ln.snmp.types { NonRepeaters.LongValue = 0; MaxRepetitions.LongValue = 32; - } + } public class SetRequest : PDU { public SetRequest() : base(new Identifier(IdentifierClass.CONTEXT, true, 0x03)) { } + + } + + public class Report: PDU + { + public Report() : base(new Identifier(IdentifierClass.CONTEXT, true, 0x08)) + { + } } diff --git a/types/ScopedPDU.cs b/types/ScopedPDU.cs new file mode 100644 index 0000000..476bd2c --- /dev/null +++ b/types/ScopedPDU.cs @@ -0,0 +1,44 @@ +// /** +// * File: ScopedPDU.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.Runtime.Remoting.Messaging; +namespace ln.snmp.types +{ + public class ScopedPDU : AbstractSequence + { + public OctetString contextEngineID { get; set; } + public OctetString contextName { get; set; } + + public PDU PDU { get; set; } + + public ScopedPDU() + :base(new Identifier(IdentifierClass.UNIVERSAL,true,0x10)) + { + contextEngineID = new OctetString(); + contextName = new OctetString(); + PDU = new GetRequest(); + } + + public override Variable[] Items + { + get => new Variable[] { contextEngineID, contextName, PDU }; + set + { + contextEngineID = value[0] as OctetString; + contextName = value[1] as OctetString; + PDU = value[2] as PDU; + } + } + + public override void Add(Variable item) => throw new NotImplementedException(); + public override void Remove(Variable item) => throw new NotImplementedException(); + + } +} diff --git a/types/Sequence.cs b/types/Sequence.cs index b419fce..c397d7b 100644 --- a/types/Sequence.cs +++ b/types/Sequence.cs @@ -12,6 +12,8 @@ using System.Collections.Generic; using System.Linq; using System.IO; using System.Text; +using ln.snmp.asn1; + namespace ln.snmp.types { public class Sequence : AbstractSequence @@ -29,7 +31,13 @@ namespace ln.snmp.types Add(variable); } - public override Variable[] Items => items.ToArray(); + public override Variable[] Items { + get => items.ToArray(); + set + { + items = new List(value); + } + } public override void Add(Variable item) { this.items.Add(item); @@ -51,8 +59,9 @@ namespace ln.snmp.types { } + /* Items */ - public abstract Variable[] Items { get; } + public abstract Variable[] Items { get; set; } public abstract void Add(Variable item); public abstract void Remove(Variable item); public virtual void Remove(int n) @@ -74,29 +83,31 @@ namespace ln.snmp.types { get { - MemoryStream payload = new MemoryStream(); + MemoryStream stream = new MemoryStream(); - foreach (Variable item in Items) + foreach (byte[] binaryItem in Items.Select((x) => (ASN1Value)x).Select((asn1) => asn1.AsByteArray).ToArray()) { - item.Write(payload); + stream.Write(binaryItem, 0, binaryItem.Length); } - return payload.ToArray(); + return stream.ToArray(); } set { - MemoryStream bytes = new MemoryStream(value); + List items = new List(); + MemoryStream stream = new MemoryStream(value); - while (bytes.Position < bytes.Length) + while (stream.Position < stream.Length) { - Add(Variable.Read(bytes)); + items.Add(new ASN1Value(stream)); } + Items = items.Select((x) => (Variable)x).ToArray(); } } public override string ToString() { - return string.Format("[Sequence {0}]",String.Join(", ",Items.Select((x) => x.ToString()))); + return string.Format("[{1} {0}]",String.Join(", ",Items.Select((x) => x?.ToString())),GetType().Name); } } diff --git a/types/SnmpMessage.cs b/types/SnmpMessage.cs new file mode 100644 index 0000000..410a0f1 --- /dev/null +++ b/types/SnmpMessage.cs @@ -0,0 +1,48 @@ +// /** +// * File: SnmpMessage.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using ln.snmp.asn1; +namespace ln.snmp.types +{ + public abstract class SnmpMessage : AbstractSequence + { + public SnmpVersion SnmpVersion { get; } + + public abstract int MessageID { get; set; } + + + public SnmpMessage(SnmpVersion snmpVersion) + : base(new Identifier(IdentifierClass.UNIVERSAL, true, 0x10)) + { + SnmpVersion = snmpVersion; + } + + public override void Add(Variable item) => throw new NotImplementedException(); + public override void Remove(Variable item) => throw new NotImplementedException(); + + + public static implicit operator SnmpMessage(ASN1Value asn) + { + Integer snmpVersion = (Integer)(Variable)asn.Items[0]; + switch (snmpVersion.LongValue) + { + case 0: + return new SnmpV1Message(asn); + case 1: + return new SnmpV2Message(asn); + case 3: + return new USMMessage(asn); + } + throw new ArgumentException(); + } + + + } +} diff --git a/types/SnmpV1Message.cs b/types/SnmpV1Message.cs new file mode 100644 index 0000000..88c933c --- /dev/null +++ b/types/SnmpV1Message.cs @@ -0,0 +1,46 @@ +// /** +// * File: SnmpV1Message.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using ln.snmp.asn1; +namespace ln.snmp.types +{ + public class SnmpV1Message : SnmpMessage + { + public OctetString snmpCommunity { get; set; } + public PDU snmpPDU { get; set; } + + public SnmpV1Message() + :base(SnmpVersion.V1) + { + } + public SnmpV1Message(ASN1Value value) + :this() + { + snmpCommunity = (OctetString)value.Items[1]; + snmpPDU = (PDU)value.Items[2]; + } + + public override int MessageID + { + get => (int)snmpPDU.RequestID.LongValue; + set => snmpPDU.RequestID.LongValue = value; + } + + public override Variable[] Items + { + get => new Variable[] { new Integer((int)SnmpVersion), snmpCommunity, snmpPDU }; + set + { + snmpCommunity = value[1] as OctetString; + snmpPDU = value[2] as PDU; + } + } + } +} diff --git a/types/SnmpV2Message.cs b/types/SnmpV2Message.cs new file mode 100644 index 0000000..972a3e5 --- /dev/null +++ b/types/SnmpV2Message.cs @@ -0,0 +1,50 @@ +// /** +// * File: SnmpV1Message.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using ln.snmp.asn1; +using ln.logging; +namespace ln.snmp.types +{ + public class SnmpV2Message : SnmpMessage + { + public OctetString snmpCommunity { get; set; } + public PDU snmpPDU { get; set; } + + public SnmpV2Message() + :base(SnmpVersion.V2c) + { + } + public SnmpV2Message(ASN1Value value) + :this() + { + snmpCommunity = (OctetString)value.Items[1]; + + Logging.Log(LogLevel.DEBUG, "ASN1Value->PDU: {0}", value.Items[2]); + snmpPDU = (PDU)value.Items[2]; + Logging.Log(LogLevel.DEBUG, "PDU: {0}", snmpPDU); + } + + public override Variable[] Items + { + get => new Variable[] { new Integer((int)SnmpVersion), snmpCommunity, snmpPDU }; + set + { + snmpCommunity = value[1] as OctetString; + snmpPDU = value[2] as PDU; + } + } + + public override int MessageID + { + get => (int)snmpPDU.RequestID.LongValue; + set => snmpPDU.RequestID.LongValue = value; + } + } +} diff --git a/types/USMMessage.cs b/types/USMMessage.cs new file mode 100644 index 0000000..79a78ca --- /dev/null +++ b/types/USMMessage.cs @@ -0,0 +1,110 @@ +// /** +// * File: USMMessage.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using ln.snmp.asn1; +using System.Linq; +using ln.snmp.channel; +namespace ln.snmp.types +{ + public class USMMessage : SnmpMessage + { + public SnmpV3AuthMethod AuthMethod { get; set; } + public SnmpV3PrivMethod PrivMethod { get; set; } + + public Integer msgVersion { get; } = new Integer(3); + public MsgGlobalData msgGlobalData { get; set; } + public UsmSecurityParameters SecurityParameters { get; set; } + + public Variable msgData { get; set; } + + public USMMessage() + : base(SnmpVersion.V3) + { + msgGlobalData = new MsgGlobalData(); + SecurityParameters = new UsmSecurityParameters(); + msgData = new NullValue(); + } + + public USMMessage(ASN1Value asn) + :this() + { + Items = asn.Items.Select((x) => (Variable)x).ToArray(); + } + + public override Variable[] Items + { + get => new Variable[] { + msgVersion, + msgGlobalData, + new OctetString(((ASN1Value)SecurityParameters).AsByteArray), + msgData + }; + set + { + msgGlobalData = new MsgGlobalData(value[1]); + SecurityParameters = new UsmSecurityParameters(new ASN1Value(value[2].Bytes)); + msgData = value[3]; + } + } + + public override int MessageID + { + get => (int)msgGlobalData.msgID.LongValue; + set => msgGlobalData.msgID.LongValue = value; + } + + public override void Add(Variable item) + { + throw new NotImplementedException(); + } + public override void Remove(Variable item) + { + throw new NotImplementedException(); + } + + public class MsgGlobalData : AbstractSequence + { + public Integer msgID { get; set; } + public Integer msgMaxSize { get; set; } + public OctetString msgFlags { get; set; } + public Integer msgSecurityModel { get; set; } + + public MsgGlobalData() + :base(new Identifier(IdentifierClass.UNIVERSAL,true,0x10)) + { + msgID = new Integer((int)(DateTimeOffset.Now.ToUnixTimeSeconds())); + msgMaxSize = new Integer(65000); + msgFlags = new OctetString("\0"); + msgSecurityModel = new Integer(3); + } + public MsgGlobalData(ASN1Value asn) + :this() + { + + } + + public override void Add(Variable item) => throw new NotImplementedException(); + public override void Remove(Variable item) => throw new NotImplementedException(); + + public override Variable[] Items + { + get => new Variable[] { msgID,msgMaxSize,msgFlags,msgSecurityModel }; + set + { + msgID = value[0] as Integer; + msgMaxSize = value[1] as Integer; + msgFlags = value[2] as OctetString; + msgSecurityModel = value[3] as Integer; + } + } + } + + } +} diff --git a/types/UsmSecurityParameters.cs b/types/UsmSecurityParameters.cs new file mode 100644 index 0000000..7b87c5c --- /dev/null +++ b/types/UsmSecurityParameters.cs @@ -0,0 +1,69 @@ +// /** +// * File: UsmSecurityParameters.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using ln.snmp.asn1; +using System.Linq; +namespace ln.snmp.types +{ + public class UsmSecurityParameters : AbstractSequence + { + public OctetString msgAuthoritativeEngineID { get; set; } + public Integer msgAuthoritativeEngineBoots { get; set; } + public Integer msgAuthoritativeEngineTime { get; set; } + + public OctetString msgUserName { get; set; } + public OctetString msgAuthenticationParameters { get; set; } + public OctetString msgPrivacyParameters { get; set; } + + + public UsmSecurityParameters() + :base(new Identifier(IdentifierClass.UNIVERSAL,true,0x10)) + { + msgAuthoritativeEngineID = new OctetString(); + msgAuthoritativeEngineBoots = new Integer(); + msgAuthoritativeEngineTime = new Integer(); + + msgUserName = new OctetString(); + msgAuthenticationParameters = new OctetString(); + msgPrivacyParameters = new OctetString(); + } + + public UsmSecurityParameters(ASN1Value asn) + :base(asn.Identifier) + { + Items = asn.Items.Select((x) => (Variable)x).ToArray(); + } + + public override Variable[] Items + { + get => new Variable[] { + msgAuthoritativeEngineID, + msgAuthoritativeEngineBoots, + msgAuthoritativeEngineTime, + msgUserName, + msgAuthenticationParameters, + msgPrivacyParameters + }; + set + { + msgAuthoritativeEngineID = value[0] as OctetString; + msgAuthoritativeEngineBoots = value[1] as Integer; + msgAuthoritativeEngineTime = value[2] as Integer; + msgUserName = value[3] as OctetString; + msgAuthenticationParameters = value[4] as OctetString; + msgPrivacyParameters = value[5] as OctetString; + } + } + + public override void Add(Variable item) => throw new NotImplementedException(); + public override void Remove(Variable item) => throw new NotImplementedException(); + + } +} diff --git a/types/V3Packet.cs b/types/V3Packet.cs new file mode 100644 index 0000000..41e7ab6 --- /dev/null +++ b/types/V3Packet.cs @@ -0,0 +1,51 @@ +// /** +// * File: V3Packet.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.Runtime.Remoting.Messaging; +namespace ln.snmp.types +{ + public class V3Packet : AbstractSequence + { + public Integer msgVersion { get; } + public Sequence msgGlobalData { get; } + + public Integer msgID { get; } + public Integer msgMaxSize { get; } + public OctetString msgFlags { get; } + public Integer msgSecurityModel { get; } + + public V3Packet() + :base(new Identifier(IdentifierClass.UNIVERSAL,true,0x10)) + { + msgVersion = new Integer(3); + msgGlobalData = new Sequence(); + + msgID = new Integer(DateTimeOffset.Now.ToUnixTimeSeconds()); + msgMaxSize = new Integer(65000); + msgSecurityModel = new Integer(3); + } + + public override Variable[] Items + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + + public override void Add(Variable item) + { + throw new NotImplementedException(); + } + + public override void Remove(Variable item) + { + throw new NotImplementedException(); + } + } +} diff --git a/types/Variable.cs b/types/Variable.cs index 1b542b9..6cc799b 100644 --- a/types/Variable.cs +++ b/types/Variable.cs @@ -9,6 +9,12 @@ // **/ using System; using System.IO; +using ln.snmp.asn1; +using ln.logging; +using System.Collections.Generic; +using System.CodeDom; +using System.Reflection; +using System.Linq; namespace ln.snmp.types { public abstract class Variable @@ -23,96 +29,110 @@ namespace ln.snmp.types public abstract byte[] Bytes { get; set; } public abstract object Value { get; set; } - public virtual void Write(Stream stream) + public void Dump() { - byte[] payload = Bytes; - - BasicEncodingRules.WriteIdentifier(stream,Identifier); - BasicEncodingRules.WriteLength(stream, payload.Length); - stream.Write(payload, 0, payload.Length); + Dump(""); } - - public byte[] ToBytes() + public void Dump(string prefix) { - MemoryStream memoryStream = new MemoryStream(); - Write(memoryStream); - return memoryStream.ToArray(); - } - - - public static Variable Read(Stream stream) - { - Variable variable = null; - - Identifier identifier = BasicEncodingRules.ReadIdentifier(stream); - int length = BasicEncodingRules.ReadLength(stream); - byte[] payload = new byte[length]; - stream.Read(payload, 0, length); - - variable = FromIdentifier(identifier); - variable.Bytes = payload; - - return variable; - } - - public static Variable Read(byte[] bytes) - { - MemoryStream stream = new MemoryStream(bytes); - return Read(stream); - } - - public static void Write(Stream stream,Variable variable) - { - variable.Write(stream); - } - - - public static Variable FromIdentifier(Identifier identifier) - { - if (identifier.IdentifierClass == IdentifierClass.UNIVERSAL) + if (this is AbstractSequence) { - switch (identifier.Number) + AbstractSequence sequence = this as AbstractSequence; + Logging.Log(LogLevel.DEBUG, "{0}{1}", prefix, this.ToString()); + foreach (Variable item in sequence.Items) { - case 0x02: - return new Integer(); - case 0x04: - return new OctetString(); - case 0x05: - return NullValue.Instance; - case 0x06: - return new ObjectIdentifier(); - case 0x10: - return new Sequence(); + if (item != null) + item.Dump(prefix + " "); + else + Logging.Log(LogLevel.DEBUG, "{0}NULL", prefix); } } - else if (identifier.IdentifierClass == IdentifierClass.CONTEXT) + else { - switch (identifier.Number) - { - case 0x00: - return new GetRequest(); - case 0x02: - return new GetResponse(); - case 0x03: - return new SetRequest(); - } + Logging.Log(LogLevel.DEBUG, "{0}{1} {2}", prefix, this.ToString(), BitConverter.ToString(this.Bytes)); } - else if (identifier.IdentifierClass == IdentifierClass.APPLICATION) - { - switch (identifier.Number) - { - case 0x01: - return new Counter32(); - case 0x02: - return new Unsigned32(); - case 0x06: - return new Counter64(); - } - } - - throw new NotSupportedException(String.Format("Unsupported ASN Type: {0}",identifier)); } + + + public static implicit operator ASN1Value(Variable variable) + { + ASN1Value value = new ASN1Value(variable.Identifier); + if (variable is AbstractSequence) + { + AbstractSequence sequence = variable as AbstractSequence; + foreach (Variable item in sequence.Items) + value.Add(item); + } + else + { + value.Bytes = variable.Bytes; + } + return value; + } + + public static implicit operator Variable(ASN1Value value) + { + try + { + if (knownTypes.ContainsKey(value.Identifier)) + { + Type type = knownTypes[value.Identifier]; + + //Logging.Log(LogLevel.DEBUGDETAIL, "ASN1Value->Variable: {0} -> {1}", value, type.Name); + + Variable variable = Activator.CreateInstance(type,true) as Variable; + if (variable is AbstractSequence) + { + AbstractSequence sequence = variable as AbstractSequence; + sequence.Items = value.Items.Select((x) => (Variable)x).ToArray(); + } + else + { + variable.Bytes = value.Bytes; + } + + return variable; + } + throw new ArgumentException(String.Format("{0}",value.Identifier),nameof(value)); + } + catch (Exception e) + { + Logging.Log(e); + throw e; + } + } + + + static Dictionary knownTypes = new Dictionary(); + static bool __initialized__ = initialize(); + + static void registerKnownType() where T:Variable, new() + { + T i = new T(); + knownTypes.Add(i.Identifier, typeof(T)); + } + + static bool initialize() + { + registerKnownType(); + registerKnownType(); + registerKnownType(); + registerKnownType(); + registerKnownType(); + registerKnownType(); + registerKnownType(); + registerKnownType(); + registerKnownType(); + + registerKnownType(); + registerKnownType(); + registerKnownType(); + registerKnownType(); + registerKnownType(); + + return true; + } } }