using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Unosquare.Swan.Networking {
///
/// Represents a little SNMP client based on http://www.java2s.com/Code/CSharp/Network/SimpleSNMP.htm.
///
public static class SnmpClient {
private static readonly Byte[] DiscoverMessage =
{
48, 41, 2, 1, 1, 4, 6, 112, 117, 98, 108, 105, 99, 160, 28, 2, 4, 111, 81, 45, 144, 2, 1, 0, 2, 1, 0, 48,
14, 48, 12, 6, 8, 43, 6, 1, 2, 1, 1, 1, 0, 5, 0,
};
///
/// Discovers the specified SNMP time out.
///
/// The SNMP time out.
/// An array of network endpoint as an IP address and a port number.
public static IPEndPoint[] Discover(Int32 snmpTimeOut = 6000) {
List endpoints = new List();
Task[] tasks =
{
Task.Run(async () =>
{
using (UdpClient udp = new UdpClient(IPAddress.Broadcast.AddressFamily))
{
udp.EnableBroadcast = true;
await udp.SendAsync(
DiscoverMessage,
DiscoverMessage.Length,
new IPEndPoint(IPAddress.Broadcast, 161));
while (true)
{
try
{
Byte[] buffer = new Byte[udp.Client.ReceiveBufferSize];
EndPoint remote = new IPEndPoint(IPAddress.Any, 0);
udp.Client.ReceiveFrom(buffer, ref remote);
endpoints.Add(remote as IPEndPoint);
}
catch
{
break;
}
}
#if NET452
udp.Close();
#endif
}
}),
Task.Delay(snmpTimeOut),
};
Task.WaitAny(tasks);
return endpoints.ToArray();
}
///
/// Gets the name of the public.
///
/// The host.
///
/// A string that contains the results of decoding the specified sequence
/// of bytes ref=GetString".
///
public static String GetPublicName(IPEndPoint host) => GetString(host, "1.3.6.1.2.1.1.5.0");
///
/// Gets the up-time.
///
/// The host.
/// The mibString.
///
/// A time interval that represents a specified number of seconds,
/// where the specification is accurate to the nearest millisecond.
///
public static TimeSpan GetUptime(IPEndPoint host, String mibString = "1.3.6.1.2.1.1.3.0") {
Byte[] response = Get(host, mibString);
if(response[0] == 0xff) {
return TimeSpan.Zero;
}
// If response, get the community name and MIB lengths
Int16 commlength = Convert.ToInt16(response[6]);
Int16 miblength = Convert.ToInt16(response[23 + commlength]);
// Extract the MIB data from the SNMP response
Int16 datalength = Convert.ToInt16(response[25 + commlength + miblength]);
Int32 datastart = 26 + commlength + miblength;
Int32 uptime = 0;
while(datalength > 0) {
uptime = (uptime << 8) + response[datastart++];
datalength--;
}
return TimeSpan.FromSeconds(uptime);
}
///
/// Gets the string.
///
/// The host.
/// The mibString.
/// A that contains the results of decoding the specified sequence of bytes.
public static String GetString(IPEndPoint host, String mibString) {
Byte[] response = Get(host, mibString);
if(response[0] == 0xff) {
return String.Empty;
}
// If response, get the community name and MIB lengths
Int16 commlength = Convert.ToInt16(response[6]);
Int16 miblength = Convert.ToInt16(response[23 + commlength]);
// Extract the MIB data from the SNMP response
Int16 datalength = Convert.ToInt16(response[25 + commlength + miblength]);
Int32 datastart = 26 + commlength + miblength;
return Encoding.ASCII.GetString(response, datastart, datalength);
}
///
/// Gets the specified host.
///
/// The host.
/// The mibString.
/// A byte array containing the results of encoding the specified set of characters.
public static Byte[] Get(IPEndPoint host, String mibString) => Get("get", host, "public", mibString);
///
/// Gets the specified request.
///
/// The request.
/// The host.
/// The community.
/// The mibString.
/// A byte array containing the results of encoding the specified set of characters.
public static Byte[] Get(String request, IPEndPoint host, String community, String mibString) {
Byte[] packet = new Byte[1024];
Byte[] mib = new Byte[1024];
Int32 comlen = community.Length;
String[] mibvals = mibString.Split('.');
Int32 miblen = mibvals.Length;
Int32 cnt = 0;
Int32 orgmiblen = miblen;
Int32 pos = 0;
// Convert the string MIB into a byte array of integer values
// Unfortunately, values over 128 require multiple bytes
// which also increases the MIB length
for(Int32 i = 0; i < orgmiblen; i++) {
Int32 temp = Convert.ToInt16(mibvals[i]);
if(temp > 127) {
mib[cnt] = Convert.ToByte(128 + temp / 128);
mib[cnt + 1] = Convert.ToByte(temp - temp / 128 * 128);
cnt += 2;
miblen++;
} else {
mib[cnt] = Convert.ToByte(temp);
cnt++;
}
}
Int32 snmplen = 29 + comlen + miblen - 1;
// The SNMP sequence start
packet[pos++] = 0x30; // Sequence start
packet[pos++] = Convert.ToByte(snmplen - 2); // sequence size
// SNMP version
packet[pos++] = 0x02; // Integer type
packet[pos++] = 0x01; // length
packet[pos++] = 0x00; // SNMP version 1
// Community name
packet[pos++] = 0x04; // String type
packet[pos++] = Convert.ToByte(comlen); // length
// Convert community name to byte array
Byte[] data = Encoding.ASCII.GetBytes(community);
foreach(Byte t in data) {
packet[pos++] = t;
}
// Add GetRequest or GetNextRequest value
packet[pos++] = request == "get" ? (Byte)0xA0 : (Byte)0xA1;
packet[pos++] = Convert.ToByte(20 + miblen - 1); // Size of total MIB
// Request ID
packet[pos++] = 0x02; // Integer type
packet[pos++] = 0x04; // length
packet[pos++] = 0x00; // SNMP request ID
packet[pos++] = 0x00;
packet[pos++] = 0x00;
packet[pos++] = 0x01;
// Error status
packet[pos++] = 0x02; // Integer type
packet[pos++] = 0x01; // length
packet[pos++] = 0x00; // SNMP error status
// Error index
packet[pos++] = 0x02; // Integer type
packet[pos++] = 0x01; // length
packet[pos++] = 0x00; // SNMP error index
// Start of variable bindings
packet[pos++] = 0x30; // Start of variable bindings sequence
packet[pos++] = Convert.ToByte(6 + miblen - 1); // Size of variable binding
packet[pos++] = 0x30; // Start of first variable bindings sequence
packet[pos++] = Convert.ToByte(6 + miblen - 1 - 2); // size
packet[pos++] = 0x06; // Object type
packet[pos++] = Convert.ToByte(miblen - 1); // length
// Start of MIB
packet[pos++] = 0x2b;
// Place MIB array in packet
for(Int32 i = 2; i < miblen; i++) {
packet[pos++] = Convert.ToByte(mib[i]);
}
packet[pos++] = 0x05; // Null object value
packet[pos] = 0x00; // Null
// Send packet to destination
SendPacket(host, packet, snmplen);
return packet;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "")]
private static void SendPacket(IPEndPoint host, Byte[] packet, Int32 length) {
Socket sock = new Socket(
AddressFamily.InterNetwork,
SocketType.Dgram,
ProtocolType.Udp);
sock.SetSocketOption(
SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout,
5000);
EndPoint ep = (EndPoint)host;
_ = sock.SendTo(packet, length, SocketFlags.None, host);
// Receive response from packet
try {
_ = sock.ReceiveFrom(packet, ref ep);
} catch(SocketException) {
packet[0] = 0xff;
}
}
}
}