// This file is part of SNMP#NET. // // SNMP#NET is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // SNMP#NET is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with SNMP#NET. If not, see . // using System; using System.Net; using System.Net.Sockets; using System.Diagnostics; namespace SnmpSharpNet { /// /// Callback used to pass result of an async SNMP operation back to the caller. /// /// Result code of the operation. /// SNMP packet received public delegate void SnmpAsyncResponse(AsyncRequestResult result, SnmpPacket packet); /// Transport class for IPv4 using UDP /// /// InternetProtocol version 4 User Datagram Protocol (IP/UDP) transport protocol /// implementation for use with SNMP versions 1, 2 and 3. /// public class UdpTarget: UdpTransport, IDisposable { /// /// SNMP request target host IP address /// protected IPAddress _address; /// /// Maximum number of retries. Value of 0 (zero) will result in a single request without /// retries. /// protected int _retry; /// /// SNMP target UDP port number /// protected int _port; /// /// SNMP request timeout value in milliseconds /// protected int _timeout; /// /// Get/Set Udp agent IP address /// public System.Net.IPAddress Address { get { return _address; } set { _address = value; if (_address.AddressFamily == AddressFamily.InterNetworkV6 && !base.IsIPv6) base.initSocket(true); else if (_address.AddressFamily == AddressFamily.InterNetwork && base.IsIPv6) base.initSocket(false); } } /// /// Get/Set Udp agent port number /// public int Port { get { return _port; } set { _port = value; } } /// /// Get/Set Udp agent timeout value in milliseconds /// public int Timeout { get { return _timeout; } set { _timeout = value; } } /// /// Get/Set Udp agent maximum retry value. Value of 0 (zero) will result in a single request /// being sent without further retry attempts. /// public int Retry { get { return _retry; } set { _retry = value; } } /// /// Constructor. /// /// SNMP peer IP address /// SNMP peer UDP port number /// SNMP peer timeout in milliseconds /// SNMP peer maximum retires setting. Value of 0 will result in a single request with no retries. public UdpTarget(IPAddress peer, int port, int timeout, int retry) : base(peer.AddressFamily == AddressFamily.InterNetworkV6) { _address = peer; _port = port; _timeout = timeout; _retry = retry; } /// /// Constructor /// /// /// Initializes the class with defaults for timeout (2000ms = 2 seconds), retry (two) and agent UDP port /// number (161). /// /// Agent IP address public UdpTarget(IPAddress peer) : base(peer.AddressFamily == AddressFamily.InterNetworkV6) { _address = peer; _port = 161; _timeout = 2000; _retry = 2; } /// Make SNMP Request /// /// Make SNMP request. With this method you can make blocked SNMP version 1, 2 and 3 requests of type GET, /// GET-NEXT, GET-BULK, SET and REPORT (request types have to compatible with the SNMP protocol version you /// are using). /// /// This method will pass through any exceptions thrown by parsing classes/methods so see individual packet /// classes, ASN.1 type classes, authentication, privacy, etc. classes for exceptions thrown. /// /// Pdu class (do not pass ScopedPdu) /// Security information for the request. Use /// for SNMP versions 1 and 2 requests. Use for SNMP version 3 /// requests. /// Appropriate SNMP packet class for the reply received (, /// , or . Null value if there was an error /// with the request. /// Thrown on SNMPv3 requests when authentication password /// is not specified on authNoPriv or authPriv requests in SecureAgentParameters or if incoming packet /// authentication check failed. /// /// With SNMP ver1 and ver2c, authentication check fails when invalid community name is parsed in the reply. /// Thrown on SNMPv3 requests when privacy password is not /// specified in SecureAgentParameters on authPriv requests. /// Thrown in following cases: /// /// * IAgentParameters.Valid() returned false. SnmpException.ErrorCode is set to SnmpException.InvalidIAgentParameters /// * No data received on request. SnmpException.ErrorCode is set to SnmpException.NoDataReceived /// * Invalid RequestId in reply. SnmpException.ErrorCode is set to SnmpException.InvalidRequestId /// public SnmpPacket Request(Pdu pdu, IAgentParameters agentParameters) { byte[] outPacket; if (agentParameters.Version == SnmpVersion.Ver3) { SecureAgentParameters secparams = (SecureAgentParameters)agentParameters; if (secparams.Authentication != AuthenticationDigests.None && secparams.AuthenticationSecret.Length <= 0) throw new SnmpAuthenticationException("Authentication password not specified."); if (secparams.Privacy != PrivacyProtocols.None && secparams.PrivacySecret.Length <= 0) throw new SnmpPrivacyException("Privacy password not specified."); _noSourceCheck = false; // this option is not valid for SNMP v3 requests ScopedPdu outPdu = new ScopedPdu(pdu); SnmpV3Packet packet = new SnmpV3Packet(outPdu); secparams.InitializePacket(packet); if (secparams.HasCachedKeys) outPacket = packet.encode(secparams.AuthenticationKey, secparams.PrivacyKey); else outPacket = packet.encode(); } else if (agentParameters.Version == SnmpVersion.Ver1) { AgentParameters param = (AgentParameters)agentParameters; if (!param.Valid()) throw new SnmpException(SnmpException.InvalidIAgentParameters,"Invalid AgentParameters. Unable to process request."); SnmpV1Packet packet = new SnmpV1Packet(); packet.Pdu.Set(pdu); packet.Community.Set(param.Community); outPacket = packet.encode(); _noSourceCheck = param.DisableReplySourceCheck; } else if (agentParameters.Version == SnmpVersion.Ver2) { AgentParameters param = (AgentParameters)agentParameters; if (!param.Valid()) throw new SnmpException(SnmpException.InvalidIAgentParameters, "Invalid AgentParameters. Unable to process request."); SnmpV2Packet packet = new SnmpV2Packet(); packet.Pdu.Set(pdu); packet.Community.Set(param.Community); _noSourceCheck = param.DisableReplySourceCheck; outPacket = packet.encode(); } else { throw new SnmpInvalidVersionException("Unsupported SNMP version."); } byte[] inBuffer = base.Request(_address, _port, outPacket, outPacket.Length, _timeout, _retry); if (inBuffer == null || inBuffer.Length <= 0) { throw new SnmpException(SnmpException.NoDataReceived, "No data received on request."); } // verify packet if (agentParameters.Version == SnmpVersion.Ver1) { SnmpV1Packet packet = new SnmpV1Packet(); AgentParameters param = (AgentParameters)agentParameters; packet.decode(inBuffer, inBuffer.Length); if (packet.Community != param.Community) { // invalid community name received. Ignore the rest of the packet throw new SnmpAuthenticationException("Invalid community name in reply."); } if (packet.Pdu.RequestId != pdu.RequestId) { // invalid request id. unmatched response ignored throw new SnmpException(SnmpException.InvalidRequestId, "Invalid request id in reply."); } return packet; } else if (agentParameters.Version == SnmpVersion.Ver2) { SnmpV2Packet packet = new SnmpV2Packet(); AgentParameters param = (AgentParameters)agentParameters; packet.decode(inBuffer, inBuffer.Length); if (packet.Community != param.Community) { // invalid community name received. Ignore the rest of the packet throw new SnmpAuthenticationException("Invalid community name in reply."); } if (packet.Pdu.RequestId != pdu.RequestId) { // invalid request id. unmatched response ignored throw new SnmpException(SnmpException.InvalidRequestId, "Invalid request id in reply."); } return packet; } else if (agentParameters.Version == SnmpVersion.Ver3) { SnmpV3Packet packet = new SnmpV3Packet(); SecureAgentParameters secparams = (SecureAgentParameters)agentParameters; secparams.InitializePacket(packet); if (secparams.HasCachedKeys) packet.decode(inBuffer, inBuffer.Length, secparams.AuthenticationKey, secparams.PrivacyKey); else packet.decode(inBuffer, inBuffer.Length); // first check if packet is a discovery response and process it if (packet.Pdu.Type == PduType.Report && packet.Pdu.VbCount > 0 && packet.Pdu.VbList[0].Oid.Equals(SnmpConstants.usmStatsUnknownEngineIDs)) { secparams.UpdateDiscoveryValues(packet); return packet; } else { if (!secparams.ValidateIncomingPacket(packet)) { return null; } else { secparams.UpdateDiscoveryValues(packet); // update time, etc. values return packet; } } } return null; } /// /// Internal event to send result of the async request to. /// protected event SnmpAsyncResponse _response; /// /// Internal storage of the agent parameters information passed to the async request member function. /// protected IAgentParameters _agentParameters; /// /// Make SNMP request. With this method you can make blocked SNMP version 1, 2 and 3 requests of type GET, /// GET-NEXT, GET-BULK, SET and REPORT (request types have to compatible with the SNMP protocol version you /// are using). /// /// This method will pass through any exceptions thrown by parsing classes/methods so see individual packet /// classes, ASN.1 type classes, authentication, privacy, etc. classes for exceptions thrown. /// /// Pdu class (do not pass ScopedPdu) /// Security information for the request. Use /// for SNMP versions 1 and 2 requests. Use for SNMP version 3 /// requests. /// Callback that receives the result of the async operation. /// True if async request was successfully initiated, otherwise false. public bool RequestAsync(Pdu pdu, IAgentParameters agentParameters, SnmpAsyncResponse responseCallback) { if (IsBusy) { return false; // class is busy } _response = null; _response += responseCallback; _agentParameters = agentParameters; byte[] outPacket; if (agentParameters.Version == SnmpVersion.Ver3) { SecureAgentParameters secparams = (SecureAgentParameters)agentParameters; if (secparams.Authentication != AuthenticationDigests.None && secparams.AuthenticationSecret.Length <= 0) { // _response(AsyncRequestResult.AuthenticationError, null); return false; } if (secparams.Privacy != PrivacyProtocols.None && secparams.PrivacySecret.Length <= 0) { // _response(AsyncRequestResult.PrivacyError, null); return false; } _noSourceCheck = false; // this option is not valid for SNMP v3 requests ScopedPdu outPdu = new ScopedPdu(pdu); outPdu.ContextEngineId.Set(secparams.EngineId); outPdu.ContextName.Set(secparams.ContextName); SnmpV3Packet packet = new SnmpV3Packet(outPdu); secparams.InitializePacket(packet); try { if (secparams.HasCachedKeys) outPacket = packet.encode(secparams.AuthenticationKey, secparams.PrivacyKey); else outPacket = packet.encode(); } catch( Exception ex ) { ex.GetType(); _response(AsyncRequestResult.EncodeError, packet); return false; } } else if (agentParameters.Version == (int)SnmpVersion.Ver1) { AgentParameters param = (AgentParameters)agentParameters; _noSourceCheck = param.DisableReplySourceCheck; SnmpV1Packet packet = new SnmpV1Packet(); packet.Pdu.Set(pdu); packet.Community.Set(param.Community); try { outPacket = packet.encode(); } catch( Exception ex) { ex.GetType(); _response(AsyncRequestResult.EncodeError, packet); return false; } } else if (agentParameters.Version == SnmpVersion.Ver2) { AgentParameters param = (AgentParameters)agentParameters; _noSourceCheck = param.DisableReplySourceCheck; SnmpV2Packet packet = new SnmpV2Packet(); packet.Pdu.Set(pdu); packet.Community.Set(param.Community); try { outPacket = packet.encode(); } catch( Exception ex ) { ex.GetType(); _response(AsyncRequestResult.EncodeError, packet); return false; } } else { throw new SnmpInvalidVersionException("Unsupported SNMP version."); } if( ! base.RequestAsync(_address, _port, outPacket, outPacket.Length, _timeout, _retry, new SnmpAsyncCallback(AsyncResponse) ) ) { return false; } return true; } /// /// Perform SNMP version 3 discovery operation. This is the first operation that needs to be /// performed on a newly accessed agent to retrieve agentId, agentBoots and agentTime values, critical /// for further authentication and privacy operations. /// /// class instance that will be updated /// with discovered agent values. This class with be reset to its defaults prior to agent /// discovered values so do not store any critical information in it prior to calling the /// discovery method /// True if discovery operation was a success, otherwise false public bool Discovery(SecureAgentParameters param) { param.Reset(); param.SecurityName.Set(""); param.Reportable = true; Pdu pdu = new Pdu(); // just leave everything at default. SnmpV3Packet inpkt = (SnmpV3Packet)Request(pdu, param); if (inpkt != null) { if (inpkt.USM.EngineBoots == 0 && inpkt.USM.EngineTime == 0) { inpkt = (SnmpV3Packet)Request(pdu, param); if (inpkt != null) return true; } else return true; } return false; } /// /// Make an async discovery request for protocol version 3. /// /// Agent parameters /// Callback method /// True if operation was correctly initiated, otherwise false. public bool DiscoveryAsync(SecureAgentParameters param, SnmpAsyncResponse callback) { Pdu p = new Pdu(); return RequestAsync(p, param, callback); } internal void AsyncResponse(AsyncRequestResult result, IPEndPoint peer, byte[] buffer, int buflen) { if (result != AsyncRequestResult.NoError) { _response(result, null); } else { if (buffer == null || buffer.Length <= 0 || buflen <= 0) { _response(AsyncRequestResult.NoDataReceived, null); return; } // verify packet if (_agentParameters.Version == (int)SnmpVersion.Ver1) { SnmpV1Packet packet = new SnmpV1Packet(); try { packet.decode(buffer, buflen); } catch(Exception ex) { ex.GetType(); // Console.WriteLine("Exception while decoding SNMP packet: " + ex.ToString()); _response(AsyncRequestResult.DecodeError, packet); return; } _response(AsyncRequestResult.NoError, packet); return; } else if (_agentParameters.Version == SnmpVersion.Ver2) { SnmpV2Packet packet = new SnmpV2Packet(); try { packet.decode(buffer, buflen); } catch (Exception ex) { ex.GetType(); // Console.WriteLine("Exception while decoding SNMP packet: " + ex.ToString()); // MutableByte b = new MutableByte(buffer, buflen); // Console.WriteLine("Buffer length {0}", buflen); // SnmpConstants.DumpHex(b); _response(AsyncRequestResult.DecodeError, packet); return; } _response(AsyncRequestResult.NoError, packet); } else if (_agentParameters.Version == SnmpVersion.Ver3) { SnmpV3Packet packet = new SnmpV3Packet(); SecureAgentParameters secparams = (SecureAgentParameters)_agentParameters; secparams.InitializePacket(packet); try { if (secparams.HasCachedKeys) packet.decode(buffer, buflen, secparams.AuthenticationKey, secparams.PrivacyKey); else packet.decode(buffer, buflen); } catch { _response(AsyncRequestResult.DecodeError, packet); return; } if (!secparams.ValidateIncomingPacket(packet)) { _response(AsyncRequestResult.AuthenticationError, packet); } else { secparams.UpdateDiscoveryValues(packet); // update time, etc. values if (packet.USM.EngineId.Length > 0 && packet.USM.EngineBoots == 0 && packet.USM.EngineTime == 0) { Pdu p = new Pdu(); RequestAsync(p, _agentParameters, _response); } else _response(AsyncRequestResult.NoError, packet); } } } } } }