// Messenger class.
// Copyright (C) 2009-2010 Lex Li
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
/*
* Created by SharpDevelop.
* User: lextm
* Date: 2009/3/29
* Time: 17:52
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Net;
using System.Threading.Tasks;
using GSF.Net.Snmp.Security;
namespace GSF.Net.Snmp.Messaging
{
///
/// Messenger class contains all static helper methods you need to send out SNMP messages.
/// Static methods in Manager or Agent class will be removed in the future.
///
///
/// SNMP v3 is not supported in many methods of this class. Please use derived classes directly
/// if you want to do v3 operations.
///
public static class Messenger
{
///
/// RFC 3416 (3.)
///
private static readonly Lazy RequestCounterFullRange = new Lazy(() => new NumberGenerator(int.MinValue, int.MaxValue));
private static readonly Lazy RequestCounterPositive = new Lazy(() => new NumberGenerator(0, int.MaxValue));
private static NumberGenerator RequestCounter
{
get { return UseFullRange ? RequestCounterFullRange.Value : RequestCounterPositive.Value; }
}
///
/// A flag to control request ID range.
///
/// Default is true.
/// Should be set to false when SNMP devices might not support negative request ID values.
///
public static bool UseFullRange { get; set; } = true;
///
/// RFC 3412 (6.)
///
private static readonly NumberGenerator MessageCounter = new NumberGenerator(0, int.MaxValue);
private static readonly ObjectIdentifier IdUnsupportedSecurityLevel = new ObjectIdentifier(new uint[] { 1, 3, 6, 1, 6, 3, 15, 1, 1, 1, 0 });
private static readonly ObjectIdentifier IdNotInTimeWindow = new ObjectIdentifier(new uint[] { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0 });
private static readonly ObjectIdentifier IdUnknownSecurityName = new ObjectIdentifier(new uint[] { 1, 3, 6, 1, 6, 3, 15, 1, 1, 3, 0 });
private static readonly ObjectIdentifier IdUnknownEngineID = new ObjectIdentifier(new uint[] { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0 });
private static readonly ObjectIdentifier IdAuthenticationFailure = new ObjectIdentifier(new uint[] { 1, 3, 6, 1, 6, 3, 15, 1, 1, 5, 0 });
private static readonly ObjectIdentifier IdDecryptionError = new ObjectIdentifier(new uint[] { 1, 3, 6, 1, 6, 3, 15, 1, 1, 6, 0 });
#region async methods
///
/// Gets a list of variable binds.
///
/// Protocol version.
/// Endpoint.
/// Community name.
/// Variable binds.
///
public static async Task> GetAsync(VersionCode version, IPEndPoint endpoint, OctetString community, IList variables)
{
if (endpoint == null)
{
throw new ArgumentNullException(nameof(endpoint));
}
if (community == null)
{
throw new ArgumentNullException(nameof(community));
}
if (variables == null)
{
throw new ArgumentNullException(nameof(variables));
}
if (version == VersionCode.V3)
{
throw new NotSupportedException("SNMP v3 is not supported");
}
GetRequestMessage message = new GetRequestMessage(RequestCounter.NextId, version, community, variables);
ISnmpMessage response = await message.GetResponseAsync(endpoint).ConfigureAwait(false);
ISnmpPdu pdu = response.Pdu();
if (pdu.ErrorStatus.ToInt32() != 0)
{
throw ErrorException.Create(
"error in response",
endpoint.Address,
response);
}
return pdu.Variables;
}
///
/// Sets a list of variable binds.
///
/// Protocol version.
/// Endpoint.
/// Community name.
/// Variable binds.
///
public static async Task> SetAsync(VersionCode version, IPEndPoint endpoint, OctetString community, IList variables)
{
if (endpoint == null)
{
throw new ArgumentNullException(nameof(endpoint));
}
if (community == null)
{
throw new ArgumentNullException(nameof(community));
}
if (variables == null)
{
throw new ArgumentNullException(nameof(variables));
}
if (version == VersionCode.V3)
{
throw new NotSupportedException("SNMP v3 is not supported");
}
SetRequestMessage message = new SetRequestMessage(RequestCounter.NextId, version, community, variables);
ISnmpMessage response = await message.GetResponseAsync(endpoint).ConfigureAwait(false);
ISnmpPdu pdu = response.Pdu();
if (pdu.ErrorStatus.ToInt32() != 0)
{
throw ErrorException.Create(
"error in response",
endpoint.Address,
response);
}
return pdu.Variables;
}
///
/// Walks (based on GET NEXT).
///
/// Protocol version.
/// Endpoint.
/// Community name.
/// OID.
/// A list to hold the results.
/// Walk mode.
///
/// Returns row count if the OID is a table. Otherwise this value is meaningless.
///
/// This method only supports SNMP v1 and v2c.
public static async Task WalkAsync(VersionCode version, IPEndPoint endpoint, OctetString community, ObjectIdentifier table, IList list, WalkMode mode)
{
if (list == null)
{
throw new ArgumentNullException(nameof(list));
}
int result = 0;
Variable tableV = new Variable(table);
Variable seed;
Variable next = tableV;
string rowMask = string.Format(CultureInfo.InvariantCulture, "{0}.1.1.", table);
string subTreeMask = string.Format(CultureInfo.InvariantCulture, "{0}.", table);
Tuple data = new Tuple(false, next);
do
{
seed = data.Item2;
if (seed == tableV)
{
data = await HasNextAsync(version, endpoint, community, seed).ConfigureAwait(false);
continue;
}
if (mode == WalkMode.WithinSubtree && !seed.Id.ToString().StartsWith(subTreeMask, StringComparison.Ordinal))
{
// not in sub tree
break;
}
list.Add(seed);
if (seed.Id.ToString().StartsWith(rowMask, StringComparison.Ordinal))
{
result++;
}
data = await HasNextAsync(version, endpoint, community, seed).ConfigureAwait(false);
}
while (data.Item1);
return result;
}
///
/// Determines whether the specified seed has next item.
///
/// The version.
/// The endpoint.
/// The community.
/// The seed.
///
/// true if the specified seed has next item; otherwise, false.
///
/// This method only supports SNMP v1 and v2c.
private static async Task> HasNextAsync(VersionCode version, IPEndPoint endpoint, OctetString community, Variable seed)
{
if (seed == null)
{
throw new ArgumentNullException(nameof(seed));
}
List variables = new List { new Variable(seed.Id) };
GetNextRequestMessage message = new GetNextRequestMessage(
RequestCounter.NextId,
version,
community,
variables);
ISnmpMessage response = await message.GetResponseAsync(endpoint).ConfigureAwait(false);
ISnmpPdu pdu = response.Pdu();
bool errorFound = pdu.ErrorStatus.ToErrorCode() == ErrorCode.NoSuchName;
return new Tuple(!errorFound, errorFound ? null : pdu.Variables[0]);
}
///
/// Walks (based on GET BULK).
///
/// Protocol version.
/// Endpoint.
/// Community name (v2c) or user name (v3).
/// OID.
/// A list to hold the results.
/// The max repetitions.
/// Walk mode.
/// The privacy provider.
/// The report.
/// Returns row count if the OID is a table. Otherwise this value is meaningless.
/// This method only supports SNMP v2c and v3.
[Obsolete("Please use other overloading ones.")]
public static async Task BulkWalkAsync(VersionCode version, IPEndPoint endpoint, OctetString community, ObjectIdentifier table, IList list, int maxRepetitions, WalkMode mode, IPrivacyProvider privacy, ISnmpMessage report)
=> await BulkWalkAsync(version, endpoint, community, OctetString.Empty, table, list, maxRepetitions, mode, privacy, report);
///
/// Walks (based on GET BULK).
///
/// Protocol version.
/// Endpoint.
/// Community name (v2c) or user name (v3).
/// Context name.
/// OID.
/// A list to hold the results.
/// The max repetitions.
/// Walk mode.
/// The privacy provider.
/// The report.
/// Returns row count if the OID is a table. Otherwise this value is meaningless.
/// This method only supports SNMP v2c and v3.
public static async Task BulkWalkAsync(VersionCode version, IPEndPoint endpoint, OctetString community, OctetString contextName, ObjectIdentifier table, IList list, int maxRepetitions, WalkMode mode, IPrivacyProvider privacy, ISnmpMessage report)
{
if (list == null)
{
throw new ArgumentNullException(nameof(list));
}
Variable tableV = new Variable(table);
Variable seed = tableV;
int result = 0;
ISnmpMessage message = report;
Tuple, ISnmpMessage> data = await BulkHasNextAsync(version, endpoint, community, contextName, seed, maxRepetitions, privacy, message).ConfigureAwait(false);
IList next = data.Item2;
message = data.Item3;
while (data.Item1)
{
string subTreeMask = string.Format(CultureInfo.InvariantCulture, "{0}.", table);
string rowMask = string.Format(CultureInfo.InvariantCulture, "{0}.1.1.", table);
foreach (Variable v in next)
{
string id = v.Id.ToString();
if (v.Data.TypeCode == SnmpType.EndOfMibView)
{
goto end;
}
if (mode == WalkMode.WithinSubtree && !id.StartsWith(subTreeMask, StringComparison.Ordinal))
{
// not in sub tree
goto end;
}
list.Add(v);
if (id.StartsWith(rowMask, StringComparison.Ordinal))
{
result++;
}
}
seed = next[next.Count - 1];
data = await BulkHasNextAsync(version, endpoint, community, contextName, seed, maxRepetitions, privacy, message).ConfigureAwait(false);
next = data.Item2;
message = data.Item3;
}
end:
return result;
}
///
/// Sends a TRAP v1 message.
///
/// Receiver.
/// Agent.
/// Community name.
/// Enterprise OID.
/// Generic code.
/// Specific code.
/// Timestamp.
/// Variable bindings.
/// This method only supports SNMP v1.
[CLSCompliant(false)]
public static async Task SendTrapV1Async(EndPoint receiver, IPAddress agent, OctetString community, ObjectIdentifier enterprise, GenericCode generic, int specific, uint timestamp, IList variables)
{
TrapV1Message message = new TrapV1Message(VersionCode.V1, agent, community, enterprise, generic, specific, timestamp, variables);
await message.SendAsync(receiver).ConfigureAwait(false);
}
///
/// Sends TRAP v2 message.
///
/// Protocol version.
/// Receiver.
/// Community name.
/// Enterprise OID.
/// Timestamp.
/// Variable bindings.
/// Request ID.
/// This method only supports SNMP v2c.
[CLSCompliant(false)]
public static async Task SendTrapV2Async(int requestId, VersionCode version, EndPoint receiver, OctetString community, ObjectIdentifier enterprise, uint timestamp, IList variables)
{
if (version != VersionCode.V2)
{
throw new NotSupportedException("Only SNMP v2c is supported");
}
TrapV2Message message = new TrapV2Message(requestId, version, community, enterprise, timestamp, variables);
await message.SendAsync(receiver).ConfigureAwait(false);
}
///
/// Sends INFORM message.
///
/// The request id.
/// Protocol version.
/// Receiver.
/// Community name.
/// Enterprise OID.
/// Timestamp.
/// Variable bindings.
/// The privacy provider.
/// The report.
/// This method supports SNMP v2c and v3.
[CLSCompliant(false)]
[Obsolete("Please use other overloading ones.")]
public static async Task SendInformAsync(int requestId, VersionCode version, IPEndPoint receiver, OctetString community, ObjectIdentifier enterprise, uint timestamp, IList variables, IPrivacyProvider privacy, ISnmpMessage report)
=> await SendInformAsync(requestId, version, receiver, community, OctetString.Empty, enterprise, timestamp, variables, privacy, report);
///
/// Sends INFORM message.
///
/// The request ID.
/// Protocol version.
/// Receiver.
/// Community name.
/// Context name.
/// Enterprise OID.
/// Timestamp.
/// Variable bindings.
/// The privacy provider.
/// The report.
/// This method supports SNMP v2c and v3.
[CLSCompliant(false)]
public static async Task SendInformAsync(int requestId, VersionCode version, IPEndPoint receiver, OctetString community, OctetString contextName, ObjectIdentifier enterprise, uint timestamp, IList variables, IPrivacyProvider privacy, ISnmpMessage report)
{
if (receiver == null)
{
throw new ArgumentNullException(nameof(receiver));
}
if (community == null)
{
throw new ArgumentNullException(nameof(community));
}
if (contextName == null)
{
throw new ArgumentNullException(nameof(contextName));
}
if (enterprise == null)
{
throw new ArgumentNullException(nameof(enterprise));
}
if (variables == null)
{
throw new ArgumentNullException(nameof(variables));
}
if (version == VersionCode.V1)
{
throw new NotSupportedException("SNMP v1 is not supported");
}
if (version == VersionCode.V3 && privacy == null)
{
throw new ArgumentNullException(nameof(privacy));
}
if (version == VersionCode.V3 && report == null)
{
throw new ArgumentNullException(nameof(report));
}
InformRequestMessage message = version == VersionCode.V3
? new InformRequestMessage(
version,
MessageCounter.NextId,
requestId,
community,
contextName,
enterprise,
timestamp,
variables,
privacy,
MaxMessageSize,
report)
: new InformRequestMessage(
requestId,
version,
community,
enterprise,
timestamp,
variables);
ISnmpMessage response = await message.GetResponseAsync(receiver).ConfigureAwait(false);
if (response.Pdu().ErrorStatus.ToInt32() != 0)
{
throw ErrorException.Create(
"error in response",
receiver.Address,
response);
}
}
///
/// Determines whether the specified seed has next item.
///
/// The version.
/// The receiver.
/// Community name (v2c) or user name (v3).
/// The seed.
/// The max repetitions.
/// The privacy provider.
/// The report.
///
/// true if the specified seed has next item; otherwise, false.
///
/// This method supports SNMP v2c and v3.
[Obsolete("Please use other overloading ones.")]
private static async Task, ISnmpMessage>> BulkHasNextAsync(VersionCode version, IPEndPoint receiver, OctetString community, Variable seed, int maxRepetitions, IPrivacyProvider privacy, ISnmpMessage report)
=> await BulkHasNextAsync(version, receiver, community, OctetString.Empty, seed, maxRepetitions, privacy, report);
///
/// Determines whether the specified seed has next item.
///
/// The version.
/// The receiver.
/// The community name (v2c) or user name (v3).
/// The context name.
/// The seed.
/// The max repetitions.
/// The privacy provider.
/// The report.
///
/// true if the specified seed has next item; otherwise, false.
///
/// This method supports SNMP v2c and v3.
private static async Task, ISnmpMessage>> BulkHasNextAsync(VersionCode version, IPEndPoint receiver, OctetString community, OctetString contextName, Variable seed, int maxRepetitions, IPrivacyProvider privacy, ISnmpMessage report)
{
// TODO: report should be updated with latest message from agent.
if (version == VersionCode.V1)
{
throw new NotSupportedException("SNMP v1 is not supported");
}
List variables = new List { new Variable(seed.Id) };
GetBulkRequestMessage request = version == VersionCode.V3
? new GetBulkRequestMessage(
version,
MessageCounter.NextId,
RequestCounter.NextId,
community,
contextName,
0,
maxRepetitions,
variables,
privacy,
MaxMessageSize,
report)
: new GetBulkRequestMessage(
RequestCounter.NextId,
version,
community,
0,
maxRepetitions,
variables);
ISnmpMessage reply = await request.GetResponseAsync(receiver).ConfigureAwait(false);
if (reply is ReportMessage)
{
if (reply.Pdu().Variables.Count == 0)
{
// TODO: whether it is good to return?
return new Tuple, ISnmpMessage>(false, new List(0), report);
}
ObjectIdentifier id = reply.Pdu().Variables[0].Id;
if (id != IdNotInTimeWindow)
{
// var error = id.GetErrorMessage();
// TODO: whether it is good to return?
return new Tuple, ISnmpMessage>(false, new List(0), report);
}
// according to RFC 3414, send a second request to sync time.
request = new GetBulkRequestMessage(
version,
MessageCounter.NextId,
RequestCounter.NextId,
community,
contextName,
0,
maxRepetitions,
variables,
privacy,
MaxMessageSize,
reply);
reply = await request.GetResponseAsync(receiver).ConfigureAwait(false);
}
else if (reply.Pdu().ErrorStatus.ToInt32() != 0)
{
throw ErrorException.Create(
"error in response",
receiver.Address,
reply);
}
IList next = reply.Pdu().Variables;
return new Tuple, ISnmpMessage>(next.Count != 0, next, request);
}
#endregion
#region sync methods
///
/// Gets a list of variable binds.
///
/// Protocol version.
/// Endpoint.
/// Community name.
/// Variable binds.
/// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
///
/// This method supports SNMP v1 and v2c.
public static IList Get(VersionCode version, IPEndPoint endpoint, OctetString community, IList variables, int timeout)
{
if (endpoint == null)
{
throw new ArgumentNullException(nameof(endpoint));
}
if (community == null)
{
throw new ArgumentNullException(nameof(community));
}
if (variables == null)
{
throw new ArgumentNullException(nameof(variables));
}
if (version == VersionCode.V3)
{
throw new NotSupportedException("SNMP v3 is not supported");
}
GetRequestMessage message = new GetRequestMessage(RequestCounter.NextId, version, community, variables);
ISnmpMessage response = message.GetResponse(timeout, endpoint);
ISnmpPdu pdu = response.Pdu();
if (pdu.ErrorStatus.ToInt32() != 0)
{
throw ErrorException.Create(
"error in response",
endpoint.Address,
response);
}
return pdu.Variables;
}
///
/// Sets a list of variable binds.
///
/// Protocol version.
/// Endpoint.
/// Community name.
/// Variable binds.
/// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
///
/// This method supports SNMP v1 and v2c.
public static IList Set(VersionCode version, IPEndPoint endpoint, OctetString community, IList variables, int timeout)
{
if (endpoint == null)
{
throw new ArgumentNullException(nameof(endpoint));
}
if (community == null)
{
throw new ArgumentNullException(nameof(community));
}
if (variables == null)
{
throw new ArgumentNullException(nameof(variables));
}
if (version == VersionCode.V3)
{
throw new NotSupportedException("SNMP v3 is not supported");
}
SetRequestMessage message = new SetRequestMessage(RequestCounter.NextId, version, community, variables);
ISnmpMessage response = message.GetResponse(timeout, endpoint);
ISnmpPdu pdu = response.Pdu();
if (pdu.ErrorStatus.ToInt32() != 0)
{
throw ErrorException.Create(
"error in response",
endpoint.Address,
response);
}
return pdu.Variables;
}
///
/// Walks (based on GET NEXT).
///
/// Protocol version.
/// Endpoint.
/// Community name.
/// OID.
/// A list to hold the results.
/// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
/// Walk mode.
///
/// Returns row count if the OID is a table. Otherwise this value is meaningless.
///
/// This method supports SNMP v1 and v2c.
public static int Walk(VersionCode version, IPEndPoint endpoint, OctetString community, ObjectIdentifier table, IList list, int timeout, WalkMode mode)
{
if (list == null)
{
throw new ArgumentNullException(nameof(list));
}
int result = 0;
Variable tableV = new Variable(table);
Variable seed;
Variable next = tableV;
string rowMask = string.Format(CultureInfo.InvariantCulture, "{0}.1.1.", table);
string subTreeMask = string.Format(CultureInfo.InvariantCulture, "{0}.", table);
do
{
seed = next;
if (seed == tableV)
{
continue;
}
if (mode == WalkMode.WithinSubtree && !seed.Id.ToString().StartsWith(subTreeMask, StringComparison.Ordinal))
{
// not in sub tree
break;
}
list.Add(seed);
if (seed.Id.ToString().StartsWith(rowMask, StringComparison.Ordinal))
{
result++;
}
}
while (HasNext(version, endpoint, community, seed, timeout, out next));
return result;
}
///
/// Determines whether the specified seed has next item.
///
/// The version.
/// The endpoint.
/// The community.
/// The seed.
/// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
/// The next.
///
/// true if the specified seed has next item; otherwise, false.
///
/// This method supports SNMP v1 and v2c.
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "5#")]
private static bool HasNext(VersionCode version, IPEndPoint endpoint, OctetString community, Variable seed, int timeout, out Variable next)
{
if (seed == null)
{
throw new ArgumentNullException(nameof(seed));
}
List variables = new List { new Variable(seed.Id) };
GetNextRequestMessage message = new GetNextRequestMessage(
RequestCounter.NextId,
version,
community,
variables);
ISnmpMessage response = message.GetResponse(timeout, endpoint);
ISnmpPdu pdu = response.Pdu();
bool errorFound = pdu.ErrorStatus.ToErrorCode() == ErrorCode.NoSuchName;
next = errorFound ? null : pdu.Variables[0];
return !errorFound;
}
///
/// Walks (based on GET BULK).
///
/// Protocol version.
/// Endpoint.
/// Community name (v2c) or user name (v3).
/// OID.
/// A list to hold the results.
/// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
/// The max repetitions.
/// Walk mode.
/// The privacy provider.
/// The report.
/// Returns row count if the OID is a table. Otherwise this value is meaningless.
/// This method supports SNMP v2c and v3.
[Obsolete("Please use other overloading ones.")]
public static int BulkWalk(VersionCode version, IPEndPoint endpoint, OctetString community, ObjectIdentifier table, IList list, int timeout, int maxRepetitions, WalkMode mode, IPrivacyProvider privacy, ISnmpMessage report)
=> BulkWalk(version, endpoint, community, OctetString.Empty, table, list, timeout, maxRepetitions, mode, privacy, report);
///
/// Walks (based on GET BULK).
///
/// Protocol version.
/// Endpoint.
/// Community name (v2c) or user name (v3).
/// Context name.
/// OID.
/// A list to hold the results.
/// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
/// The max repetitions.
/// Walk mode.
/// The privacy provider.
/// The report.
/// Returns row count if the OID is a table. Otherwise this value is meaningless.
/// This method supports SNMP v2c and v3.
public static int BulkWalk(VersionCode version, IPEndPoint endpoint, OctetString community, OctetString contextName, ObjectIdentifier table, IList list, int timeout, int maxRepetitions, WalkMode mode, IPrivacyProvider privacy, ISnmpMessage report)
{
if (list == null)
{
throw new ArgumentNullException(nameof(list));
}
if (version == VersionCode.V1)
{
throw new NotSupportedException("SNMP v1 is not supported");
}
Variable tableV = new Variable(table);
Variable seed = tableV;
IList next;
int result = 0;
ISnmpMessage message = report;
while (BulkHasNext(version, endpoint, community, contextName, seed, timeout, maxRepetitions, out next, privacy, ref message))
{
string subTreeMask = string.Format(CultureInfo.InvariantCulture, "{0}.", table);
string rowMask = string.Format(CultureInfo.InvariantCulture, "{0}.1.1.", table);
foreach (Variable v in next)
{
string id = v.Id.ToString();
if (v.Data.TypeCode == SnmpType.EndOfMibView)
{
goto end;
}
if (mode == WalkMode.WithinSubtree && !id.StartsWith(subTreeMask, StringComparison.Ordinal))
{
// not in sub tree
goto end;
}
list.Add(v);
if (id.StartsWith(rowMask, StringComparison.Ordinal))
{
result++;
}
}
seed = next[next.Count - 1];
}
end:
return result;
}
///
/// Sends a TRAP v1 message.
///
/// Receiver.
/// Agent.
/// Community name.
/// Enterprise OID.
/// Generic code.
/// Specific code.
/// Timestamp.
/// Variable bindings.
/// This method supports SNMP v1.
[CLSCompliant(false)]
public static void SendTrapV1(EndPoint receiver, IPAddress agent, OctetString community, ObjectIdentifier enterprise, GenericCode generic, int specific, uint timestamp, IList variables)
{
TrapV1Message message = new TrapV1Message(VersionCode.V1, agent, community, enterprise, generic, specific, timestamp, variables);
message.Send(receiver);
}
///
/// Sends TRAP v2 message.
///
/// Protocol version.
/// Receiver.
/// Community name.
/// Enterprise OID.
/// Timestamp.
/// Variable bindings.
/// Request ID.
/// This method supports SNMP v2c.
[CLSCompliant(false)]
public static void SendTrapV2(int requestId, VersionCode version, EndPoint receiver, OctetString community, ObjectIdentifier enterprise, uint timestamp, IList variables)
{
if (version != VersionCode.V2)
{
throw new NotSupportedException("Only SNMP v2c is supported");
}
TrapV2Message message = new TrapV2Message(requestId, version, community, enterprise, timestamp, variables);
message.Send(receiver);
}
///
/// Sends INFORM message.
///
/// The request ID.
/// Protocol version.
/// Receiver.
/// Community name (v2c) or user name (v3).
/// Enterprise OID.
/// Timestamp.
/// Variable bindings.
/// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
/// The privacy provider.
/// The report.
/// This method supports SNMP v2c and v3.
[CLSCompliant(false)]
[Obsolete("Please use other overloading ones.")]
public static void SendInform(int requestId, VersionCode version, IPEndPoint receiver, OctetString community, ObjectIdentifier enterprise, uint timestamp, IList variables, int timeout, IPrivacyProvider privacy, ISnmpMessage report)
=> SendInform(requestId, version, receiver, community, OctetString.Empty, enterprise, timestamp, variables, timeout, privacy, report);
///
/// Sends INFORM message.
///
/// The request ID.
/// Protocol version.
/// Receiver.
/// Community name (v2c) or user name (v3).
/// Context name.
/// Enterprise OID.
/// Timestamp.
/// Variable bindings.
/// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
/// The privacy provider.
/// The report.
/// This method supports SNMP v2c and v3.
[CLSCompliant(false)]
public static void SendInform(int requestId, VersionCode version, IPEndPoint receiver, OctetString community, OctetString contextName, ObjectIdentifier enterprise, uint timestamp, IList variables, int timeout, IPrivacyProvider privacy, ISnmpMessage report)
{
if (receiver == null)
{
throw new ArgumentNullException(nameof(receiver));
}
if (community == null)
{
throw new ArgumentNullException(nameof(community));
}
if (contextName == null)
{
throw new ArgumentException(nameof(contextName));
}
if (enterprise == null)
{
throw new ArgumentNullException(nameof(enterprise));
}
if (variables == null)
{
throw new ArgumentNullException(nameof(variables));
}
if (version == VersionCode.V1)
{
throw new NotSupportedException("SNMP v1 is not supported");
}
if (version == VersionCode.V3 && privacy == null)
{
throw new ArgumentNullException(nameof(privacy));
}
if (version == VersionCode.V3 && report == null)
{
throw new ArgumentNullException(nameof(report));
}
InformRequestMessage message = version == VersionCode.V3
? new InformRequestMessage(
version,
MessageCounter.NextId,
requestId,
community,
contextName,
enterprise,
timestamp,
variables,
privacy,
MaxMessageSize,
report)
: new InformRequestMessage(
requestId,
version,
community,
enterprise,
timestamp,
variables);
ISnmpMessage response = message.GetResponse(timeout, receiver);
if (response.Pdu().ErrorStatus.ToInt32() != 0)
{
throw ErrorException.Create(
"error in response",
receiver.Address,
response);
}
}
///
/// Determines whether the specified seed has next item.
///
/// The version.
/// The receiver.
/// The community name (v2c) or user name (v3).
/// The seed.
/// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
/// The max repetitions.
/// The next.
/// The privacy provider.
/// The report.
///
/// true if the specified seed has next item; otherwise, false.
///
/// This method supports SNMP v2c and v3.
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "5#")]
[Obsolete("Please use other overloading ones.")]
private static bool BulkHasNext(VersionCode version, IPEndPoint receiver, OctetString community, Variable seed, int timeout, int maxRepetitions, out IList next, IPrivacyProvider privacy, ref ISnmpMessage report)
=> BulkHasNext(version, receiver, community, OctetString.Empty, seed, timeout, maxRepetitions, out next, privacy, ref report);
///
/// Determines whether the specified seed has next item.
///
/// The version.
/// The receiver.
/// The community name (v2c) or user name (v3).
/// The context name.
/// The seed.
/// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
/// The max repetitions.
/// The next.
/// The privacy provider.
/// The report.
///
/// true if the specified seed has next item; otherwise, false.
///
/// This method supports SNMP v2c and v3.
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "5#")]
private static bool BulkHasNext(VersionCode version, IPEndPoint receiver, OctetString community, OctetString contextName, Variable seed, int timeout, int maxRepetitions, out IList next, IPrivacyProvider privacy, ref ISnmpMessage report)
{
if (version == VersionCode.V1)
{
throw new NotSupportedException("SNMP v1 is not supported");
}
List variables = new List { new Variable(seed.Id) };
GetBulkRequestMessage request = version == VersionCode.V3
? new GetBulkRequestMessage(
version,
MessageCounter.NextId,
RequestCounter.NextId,
community,
contextName,
0,
maxRepetitions,
variables,
privacy,
MaxMessageSize,
report)
: new GetBulkRequestMessage(
RequestCounter.NextId,
version,
community,
0,
maxRepetitions,
variables);
ISnmpMessage reply = request.GetResponse(timeout, receiver);
if (reply is ReportMessage)
{
if (reply.Pdu().Variables.Count == 0)
{
// TODO: whether it is good to return?
next = new List(0);
return false;
}
ObjectIdentifier id = reply.Pdu().Variables[0].Id;
if (id != IdNotInTimeWindow)
{
// var error = id.GetErrorMessage();
// TODO: whether it is good to return?
next = new List(0);
return false;
}
// according to RFC 3414, send a second request to sync time.
request = new GetBulkRequestMessage(
version,
MessageCounter.NextId,
RequestCounter.NextId,
community,
contextName,
0,
maxRepetitions,
variables,
privacy,
MaxMessageSize,
reply);
reply = request.GetResponse(timeout, receiver);
}
else if (reply.Pdu().ErrorStatus.ToInt32() != 0)
{
throw ErrorException.Create(
"error in response",
receiver.Address,
reply);
}
next = reply.Pdu().Variables;
report = request;
return next.Count != 0;
}
///
/// Gets a table of variables.
///
/// Protocol version.
/// Endpoint.
/// Community name.
/// Table OID.
/// The time-out value, in milliseconds. The default value is 0, which indicates an infinite time-out period. Specifying -1 also indicates an infinite time-out period.
/// The max repetitions.
///
/// This method supports SNMP v2c and v3.
[SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", MessageId = "Return", Justification = "ByDesign")]
[SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", MessageId = "Body", Justification = "ByDesign")]
[CLSCompliant(false)]
[Obsolete("This method only works for a few scenarios. Might be replaced by new methods in the future. If it does not work for you, parse WALK result on your own.")]
public static Variable[,] GetTable(VersionCode version, IPEndPoint endpoint, OctetString community, ObjectIdentifier table, int timeout, int maxRepetitions)
{
if (version == VersionCode.V3)
{
throw new NotSupportedException("SNMP v3 is not supported");
}
IList list = new List();
int rows = version == VersionCode.V1 ? Walk(version, endpoint, community, table, list, timeout, WalkMode.WithinSubtree) : BulkWalk(version, endpoint, community, OctetString.Empty, table, list, timeout, maxRepetitions, WalkMode.WithinSubtree, null, null);
if (rows == 0)
{
return new Variable[0, 0];
}
int cols = list.Count / rows;
int k = 0;
Variable[,] result = new Variable[rows, cols];
for (int j = 0; j < cols; j++)
{
for (int i = 0; i < rows; i++)
{
result[i, j] = list[k];
k++;
}
}
return result;
}
#endregion
///
/// Gets the request counter.
///
/// The request counter.
public static int NextRequestId
{
get { return RequestCounter.NextId; }
}
///
/// Gets the message counter.
///
/// The message counter.
public static int NextMessageId
{
get { return MessageCounter.NextId; }
}
///
/// Max message size used in #SNMP.
///
///
/// You can use any value for your own application.
/// Also this value may be changed in #SNMP in future releases.
///
public static int MaxMessageSize { get; set; } = Header.MaxMessageSize;
///
/// Returns a new discovery request.
///
///
[Obsolete("Please use GetNextDiscovery")]
public static Discovery NextDiscovery
{
get { return new Discovery(NextMessageId, NextRequestId, MaxMessageSize); }
}
///
/// If the privacy module returns failure, then the message can
/// not be processed, so the usmStatsDecryptionErrors counter is
/// incremented and an error indication (decryptionError) together
/// with the OID and value of the incremented counter is returned
/// to the calling module.
///
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
public static ObjectIdentifier DecryptionError
{
get { return IdDecryptionError; }
}
///
/// If the authentication module returns failure, then the message
/// cannot be trusted, so the usmStatsWrongDigests counter is
/// incremented and an error indication (authenticationFailure)
/// together with the OID and value of the incremented counter is
/// returned to the calling module.
///
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
public static ObjectIdentifier AuthenticationFailure
{
get { return IdAuthenticationFailure; }
}
///
/// If the value of the msgAuthoritativeEngineID field in the
/// securityParameters is unknown then
/// the usmStatsUnknownEngineIDs counter is incremented, and an
/// error indication (unknownEngineID) together with the OID and
/// value of the incremented counter is returned to the calling
/// module.
///
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
public static ObjectIdentifier UnknownEngineId
{
get { return IdUnknownEngineID; }
}
///
/// Information about the value of the msgUserName and
/// msgAuthoritativeEngineID fields is extracted from the Local
/// Configuration Datastore (LCD, usmUserTable). If no information
/// is available for the user, then the usmStatsUnknownUserNames
/// counter is incremented and an error indication
/// (unknownSecurityName) together with the OID and value of the
/// incremented counter is returned to the calling module.
///
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
public static ObjectIdentifier UnknownSecurityName
{
get { return IdUnknownSecurityName; }
}
///
/// If the message is considered to be outside of the Time Window
/// then the usmStatsNotInTimeWindows counter is incremented and
/// an error indication (notInTimeWindow) together with the OID,
/// the value of the incremented counter, and an indication that
/// the error must be reported with a securityLevel of authNoPriv,
/// is returned to the calling module
///
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
public static ObjectIdentifier NotInTimeWindow
{
get { return IdNotInTimeWindow; }
}
///
/// If the information about the user indicates that it does not
/// support the securityLevel requested by the caller, then the
/// usmStatsUnsupportedSecLevels counter is incremented and an error
/// indication (unsupportedSecurityLevel) together with the OID and
/// value of the incremented counter is returned to the calling
/// module.
///
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
public static ObjectIdentifier UnsupportedSecurityLevel
{
get { return IdUnsupportedSecurityLevel; }
}
///
/// Returns a new discovery request.
///
/// Message type.
///
public static Discovery GetNextDiscovery(SnmpType type)
{
return new Discovery(NextMessageId, NextRequestId, MaxMessageSize, type);
}
///
/// Returns error message for the specific .
///
/// The OID.
/// Error message.
public static string GetErrorMessage(this ObjectIdentifier id)
{
if (id == IdUnsupportedSecurityLevel)
{
return "unsupported security level";
}
if (id == IdNotInTimeWindow)
{
return "not in time window";
}
if (id == IdUnknownSecurityName)
{
return "unknown security name";
}
if (id == IdUnknownEngineID)
{
return "unknown engine ID";
}
if (id == IdAuthenticationFailure)
{
return "authentication failure";
}
if (id == IdDecryptionError)
{
return "decryption error";
}
return "unknown error";
}
}
}