From 3e670b37896b3134ad4d9f168fa3d1e74013a49e Mon Sep 17 00:00:00 2001 From: Lex Li Date: Sat, 19 Oct 2024 00:52:14 -0700 Subject: [PATCH] Fixed multicast based discoverer. --- SharpSnmpLib/Messaging/Discoverer.cs | 61 ++++++++++-------------- SharpSnmpLib/Messaging/Net6Discoverer.cs | 42 +++++++--------- 2 files changed, 42 insertions(+), 61 deletions(-) diff --git a/SharpSnmpLib/Messaging/Discoverer.cs b/SharpSnmpLib/Messaging/Discoverer.cs index fbeea5b8..666293b1 100644 --- a/SharpSnmpLib/Messaging/Discoverer.cs +++ b/SharpSnmpLib/Messaging/Discoverer.cs @@ -33,7 +33,6 @@ namespace Lextm.SharpSnmpLib.Messaging public sealed partial class Discoverer { private int _active; - private int _bufferSize; private int _requestId; private static readonly UserRegistry Empty = new(); private readonly IList _defaultVariables = new List { new(new ObjectIdentifier(new uint[] { 1, 3, 6, 1, 2, 1, 1, 1, 0 })) }; @@ -88,7 +87,7 @@ public void Discover(VersionCode version, IPEndPoint broadcastAddress, OctetStri using var udp = new UdpClient(addressFamily); if (addressFamily == AddressFamily.InterNetworkV6) { - udp.JoinMulticastGroup(broadcastAddress.Address); + udp.MulticastLoopback = false; } else if (addressFamily == AddressFamily.InterNetwork) { @@ -108,12 +107,12 @@ public void Discover(VersionCode version, IPEndPoint broadcastAddress, OctetStri return; } - _bufferSize = udp.Client.ReceiveBufferSize = Messenger.MaxMessageSize; + udp.Client.ReceiveBufferSize = Messenger.MaxMessageSize; #if ASYNC - Task.Factory.StartNew(() => AsyncBeginReceive(udp.Client)); + Task.Factory.StartNew(() => AsyncBeginReceive(udp)); #else - Task.Factory.StartNew(() => AsyncReceive(udp.Client)); + Task.Factory.StartNew(() => AsyncReceive(udp)); #endif Thread.Sleep(interval); @@ -198,7 +197,7 @@ private void AsyncEndReceive(IAsyncResult iar) } #else - private void AsyncReceive(Socket socket) + private void AsyncReceive(UdpClient client) { while (true) { @@ -210,10 +209,9 @@ private void AsyncReceive(Socket socket) try { - var buffer = new byte[_bufferSize]; - EndPoint remote = new IPEndPoint(IPAddress.Any, 0); - var count = socket.ReceiveFrom(buffer, ref remote); - Task.Factory.StartNew(() => HandleMessage(buffer, count, (IPEndPoint)remote)); + var remote = new IPEndPoint(IPAddress.Any, 0); + var buffer = client.Receive(ref remote); + Task.Factory.StartNew(() => HandleMessage(buffer, buffer.Length, remote)); } catch (SocketException ex) { @@ -298,11 +296,6 @@ public async Task DiscoverAsync(VersionCode version, IPEndPoint broadcastAddress } var addressFamily = broadcastAddress.AddressFamily; - if (addressFamily == AddressFamily.InterNetworkV6) - { - throw new ArgumentException("IP v6 is not yet supported.", nameof(broadcastAddress)); - } - byte[] bytes; _requestId = Messenger.NextRequestId; if (version == VersionCode.V3) @@ -316,11 +309,19 @@ public async Task DiscoverAsync(VersionCode version, IPEndPoint broadcastAddress bytes = message.ToBytes(); } - using var udp = new Socket(addressFamily, SocketType.Dgram, ProtocolType.Udp); - udp.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); - var buffer = new ArraySegment(bytes); - await udp.SendToAsync(buffer, SocketFlags.None, broadcastAddress); + using var udp = new UdpClient(addressFamily); + if (addressFamily == AddressFamily.InterNetworkV6) + { + udp.MulticastLoopback = false; + } + else if (addressFamily == AddressFamily.InterNetwork) + { +#if (!CF) + udp.EnableBroadcast = true; +#endif + } + await udp.SendAsync(bytes, bytes.Length, broadcastAddress); var activeBefore = Interlocked.CompareExchange(ref _active, Active, Inactive); if (activeBefore == Active) { @@ -328,13 +329,12 @@ public async Task DiscoverAsync(VersionCode version, IPEndPoint broadcastAddress return; } - _bufferSize = udp.ReceiveBufferSize; #if NET6_0_OR_GREATER var source = new CancellationTokenSource(); source.CancelAfter(interval); try { - await ReceiveAsync(udp, source.Token); + await ReceiveAsync(udp.Client, source.Token); } catch (OperationCanceledException) { @@ -346,18 +346,10 @@ await Task.WhenAny( Task.Delay(interval)); #endif Interlocked.CompareExchange(ref _active, Inactive, Active); - try - { - udp.Shutdown(SocketShutdown.Both); - } - catch (SocketException) - { - // This exception is thrown in .NET Core <=2.1.4 on non-Windows systems. - // However, the shutdown call is necessary to release the socket binding. - } + udp.Close(); } #if !NET6_0_OR_GREATER - private async Task ReceiveAsync(Socket socket) + private async Task ReceiveAsync(UdpClient client) { while (true) { @@ -369,11 +361,8 @@ private async Task ReceiveAsync(Socket socket) try { - EndPoint remote = new IPEndPoint(IPAddress.Any, 0); - - var buffer = new byte[_bufferSize]; - var result = await socket.ReceiveMessageFromAsync(new ArraySegment(buffer), SocketFlags.None, remote); - await Task.Factory.StartNew(() => HandleMessage(buffer, result.ReceivedBytes, (IPEndPoint) result.RemoteEndPoint)) + var result = await client.ReceiveAsync(); + await Task.Factory.StartNew(() => HandleMessage(result.Buffer, result.Buffer.Length, result.RemoteEndPoint)) .ConfigureAwait(false); } catch (SocketException ex) diff --git a/SharpSnmpLib/Messaging/Net6Discoverer.cs b/SharpSnmpLib/Messaging/Net6Discoverer.cs index 913aeebb..cf6fb442 100644 --- a/SharpSnmpLib/Messaging/Net6Discoverer.cs +++ b/SharpSnmpLib/Messaging/Net6Discoverer.cs @@ -81,12 +81,12 @@ public void Discover(VersionCode version, IPEndPoint broadcastAddress, OctetStri return; } - _bufferSize = udp.Client.ReceiveBufferSize = Messenger.MaxMessageSize; + udp.Client.ReceiveBufferSize = Messenger.MaxMessageSize; #if ASYNC Task.Factory.StartNew(() => AsyncBeginReceive(udp.Client, token)); #else - Task.Factory.StartNew(() => AsyncReceive(udp.Client), token); + Task.Factory.StartNew(() => AsyncReceive(udp), token); #endif token.WaitHandle.WaitOne(); Interlocked.CompareExchange(ref _active, Inactive, Active); @@ -154,11 +154,6 @@ public async Task DiscoverAsync(VersionCode version, IPEndPoint broadcastAddress } var addressFamily = broadcastAddress.AddressFamily; - if (addressFamily == AddressFamily.InterNetworkV6) - { - throw new ArgumentException("IP v6 is not yet supported.", nameof(broadcastAddress)); - } - byte[] bytes; _requestId = Messenger.NextRequestId; if (version == VersionCode.V3) @@ -172,11 +167,17 @@ public async Task DiscoverAsync(VersionCode version, IPEndPoint broadcastAddress bytes = message.ToBytes(); } - using var udp = new Socket(addressFamily, SocketType.Dgram, ProtocolType.Udp); - udp.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); - var buffer = new ArraySegment(bytes); - await udp.SendToAsync(buffer, SocketFlags.None, broadcastAddress, token); + using var udp = new UdpClient(addressFamily); + if (addressFamily == AddressFamily.InterNetworkV6) + { + udp.MulticastLoopback = false; + } + else if (addressFamily == AddressFamily.InterNetwork) + { + udp.EnableBroadcast = true; + } + await udp.Client.SendToAsync(bytes, SocketFlags.None, broadcastAddress, token); var activeBefore = Interlocked.CompareExchange(ref _active, Active, Inactive); if (activeBefore == Active) { @@ -184,19 +185,10 @@ public async Task DiscoverAsync(VersionCode version, IPEndPoint broadcastAddress return; } - _bufferSize = udp.ReceiveBufferSize; - await ReceiveAsync(udp, token); + await ReceiveAsync(udp.Client, token); Interlocked.CompareExchange(ref _active, Inactive, Active); - try - { - udp.Shutdown(SocketShutdown.Both); - } - catch (SocketException) - { - // This exception is thrown in .NET Core <=2.1.4 on non-Windows systems. - // However, the shutdown call is necessary to release the socket binding. - } + udp.Close(); } private async Task ReceiveAsync(Socket socket, CancellationToken token) @@ -216,10 +208,10 @@ private async Task ReceiveAsync(Socket socket, CancellationToken token) try { - EndPoint remote = new IPEndPoint(IPAddress.Any, 0); + EndPoint remote = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0); - var buffer = new byte[_bufferSize]; - var result = await socket.ReceiveMessageFromAsync(new ArraySegment(buffer), SocketFlags.None, remote, token); + var buffer = new byte[socket.ReceiveBufferSize]; + var result = await socket.ReceiveFromAsync(new ArraySegment(buffer), SocketFlags.None, remote, token); await Task.Factory.StartNew(() => HandleMessage(buffer, result.ReceivedBytes, (IPEndPoint)result.RemoteEndPoint), token) .ConfigureAwait(false); }