2019-12-03 18:44:25 +01:00
using System ;
using System.Collections.Generic ;
using System.Net ;
using System.Net.Sockets ;
using System.Text ;
using System.Threading.Tasks ;
namespace Unosquare.Swan.Networking {
/// <summary>
/// Represents a little SNMP client based on http://www.java2s.com/Code/CSharp/Network/SimpleSNMP.htm.
/// </summary>
public static class SnmpClient {
private static readonly Byte [ ] DiscoverMessage =
2019-02-17 14:08:57 +01:00
{
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 ,
2019-12-03 18:44:25 +01:00
} ;
/// <summary>
/// Discovers the specified SNMP time out.
/// </summary>
/// <param name="snmpTimeOut">The SNMP time out.</param>
/// <returns>An array of network endpoint as an IP address and a port number.</returns>
public static IPEndPoint [ ] Discover ( Int32 snmpTimeOut = 6000 ) {
List < IPEndPoint > endpoints = new List < IPEndPoint > ( ) ;
Task [ ] tasks =
{
2019-02-17 14:08:57 +01:00
Task . Run ( async ( ) = >
{
2019-12-03 18:44:25 +01:00
using ( UdpClient udp = new UdpClient ( IPAddress . Broadcast . AddressFamily ) )
2019-02-17 14:08:57 +01:00
{
udp . EnableBroadcast = true ;
await udp . SendAsync (
DiscoverMessage ,
DiscoverMessage . Length ,
new IPEndPoint ( IPAddress . Broadcast , 161 ) ) ;
while ( true )
{
try
{
2019-12-03 18:44:25 +01:00
Byte [ ] buffer = new Byte [ udp . Client . ReceiveBufferSize ] ;
2019-02-17 14:08:57 +01:00
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 ) ,
2019-12-03 18:44:25 +01:00
} ;
Task . WaitAny ( tasks ) ;
return endpoints . ToArray ( ) ;
}
/// <summary>
/// Gets the name of the public.
/// </summary>
/// <param name="host">The host.</param>
/// <returns>
/// A string that contains the results of decoding the specified sequence
/// of bytes ref=GetString".
/// </returns>
public static String GetPublicName ( IPEndPoint host ) = > GetString ( host , "1.3.6.1.2.1.1.5.0" ) ;
/// <summary>
/// Gets the up-time.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="mibString">The mibString.</param>
/// <returns>
/// A time interval that represents a specified number of seconds,
/// where the specification is accurate to the nearest millisecond.
/// </returns>
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 ) ;
}
/// <summary>
/// Gets the string.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="mibString">The mibString.</param>
/// <returns>A <see cref="System.String" /> that contains the results of decoding the specified sequence of bytes.</returns>
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 ) ;
}
/// <summary>
/// Gets the specified host.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="mibString">The mibString.</param>
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
public static Byte [ ] Get ( IPEndPoint host , String mibString ) = > Get ( "get" , host , "public" , mibString ) ;
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="host">The host.</param>
/// <param name="community">The community.</param>
/// <param name="mibString">The mibString.</param>
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
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 = "<Ausstehend>")]
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 ;
}
}
}
2019-02-17 14:08:57 +01:00
}