diff options
Diffstat (limited to 'RSSDP/SsdpCommunicationsServer.cs')
| -rw-r--r-- | RSSDP/SsdpCommunicationsServer.cs | 168 |
1 files changed, 89 insertions, 79 deletions
diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index fb5a66aa1..6ae260d55 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -25,18 +25,18 @@ namespace Rssdp.Infrastructure * Since stopping the service would be a bad idea (might not be allowed security wise and might * break other apps running on the system) the only other work around is to use two sockets. * - * We use one socket to listen for/receive notifications and search requests (_BroadcastListenSocket). - * We use a second socket, bound to a different local port, to send search requests and listen for - * responses (_SendSocket). The responses are sent to the local port this socket is bound to, - * which isn't port 1900 so the MS service doesn't steal them. While the caller can specify a local + * We use one group of sockets to listen for/receive notifications and search requests (_MulticastListenSockets). + * We use a second group, bound to a different local port, to send search requests and listen for + * responses (_SendSockets). The responses are sent to the local ports these sockets are bound to, + * which aren't port 1900 so the MS service doesn't steal them. While the caller can specify a local * port to use, we will default to 0 which allows the underlying system to auto-assign a free port. */ private object _BroadcastListenSocketSynchroniser = new object(); - private List<ISocket> _BroadcastListenSockets; + private List<Socket> _MulticastListenSockets; private object _SendSocketSynchroniser = new object(); - private List<ISocket> _sendSockets; + private List<Socket> _sendSockets; private HttpRequestParser _RequestParser; private HttpResponseParser _ResponseParser; @@ -78,7 +78,7 @@ namespace Rssdp.Infrastructure /// <exception cref="ArgumentOutOfRangeException">The <paramref name="multicastTimeToLive"/> argument is less than or equal to zero.</exception> public SsdpCommunicationsServer(ISocketFactory socketFactory, int localPort, int multicastTimeToLive, INetworkManager networkManager, ILogger logger, bool enableMultiSocketBinding) { - if (socketFactory == null) + if (socketFactory is null) { throw new ArgumentNullException(nameof(socketFactory)); } @@ -107,25 +107,25 @@ namespace Rssdp.Infrastructure /// Causes the server to begin listening for multicast messages, being SSDP search requests and notifications. /// </summary> /// <exception cref="ObjectDisposedException">Thrown if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true (because <seealso cref="DisposableManagedObjectBase.Dispose()" /> has been called previously).</exception> - public void BeginListeningForBroadcasts() + public void BeginListeningForMulticast() { ThrowIfDisposed(); lock (_BroadcastListenSocketSynchroniser) { - if (_BroadcastListenSockets == null) + if (_MulticastListenSockets is null) { try { - _BroadcastListenSockets = ListenForBroadcasts(); + _MulticastListenSockets = CreateMulticastSocketsAndListen(); } catch (SocketException ex) { - _logger.LogError("Failed to bind to port 1900: {Message}. DLNA will be unavailable", ex.Message); + _logger.LogError("Failed to bind to multicast address: {Message}. DLNA will be unavailable", ex.Message); } catch (Exception ex) { - _logger.LogError(ex, "Error in BeginListeningForBroadcasts"); + _logger.LogError(ex, "Error in BeginListeningForMulticast"); } } } @@ -135,15 +135,19 @@ namespace Rssdp.Infrastructure /// Causes the server to stop listening for multicast messages, being SSDP search requests and notifications. /// </summary> /// <exception cref="ObjectDisposedException">Thrown if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true (because <seealso cref="DisposableManagedObjectBase.Dispose()" /> has been called previously).</exception> - public void StopListeningForBroadcasts() + public void StopListeningForMulticast() { lock (_BroadcastListenSocketSynchroniser) { - if (_BroadcastListenSockets != null) + if (_MulticastListenSockets is not null) { _logger.LogInformation("{0} disposing _BroadcastListenSocket", GetType().Name); - _BroadcastListenSockets.ForEach(s => s.Dispose()); - _BroadcastListenSockets = null; + foreach (var socket in _MulticastListenSockets) + { + socket.Dispose(); + } + + _MulticastListenSockets = null; } } } @@ -153,7 +157,7 @@ namespace Rssdp.Infrastructure /// </summary> public async Task SendMessage(byte[] messageData, IPEndPoint destination, IPAddress fromLocalIpAddress, CancellationToken cancellationToken) { - if (messageData == null) + if (messageData is null) { throw new ArgumentNullException(nameof(messageData)); } @@ -177,11 +181,11 @@ namespace Rssdp.Infrastructure } } - private async Task SendFromSocket(ISocket socket, byte[] messageData, IPEndPoint destination, CancellationToken cancellationToken) + private async Task SendFromSocket(Socket socket, byte[] messageData, IPEndPoint destination, CancellationToken cancellationToken) { try { - await socket.SendToAsync(messageData, 0, messageData.Length, destination, cancellationToken).ConfigureAwait(false); + await socket.SendToAsync(messageData, destination, cancellationToken).ConfigureAwait(false); } catch (ObjectDisposedException) { @@ -191,37 +195,42 @@ namespace Rssdp.Infrastructure } catch (Exception ex) { - _logger.LogError(ex, "Error sending socket message from {0} to {1}", socket.LocalIPAddress.ToString(), destination.ToString()); + var localIP = ((IPEndPoint)socket.LocalEndPoint).Address; + _logger.LogError(ex, "Error sending socket message from {0} to {1}", localIP.ToString(), destination.ToString()); } } - private List<ISocket> GetSendSockets(IPAddress fromLocalIpAddress, IPEndPoint destination) + private List<Socket> GetSendSockets(IPAddress fromLocalIpAddress, IPEndPoint destination) { EnsureSendSocketCreated(); lock (_SendSocketSynchroniser) { - var sockets = _sendSockets.Where(i => i.LocalIPAddress.AddressFamily == fromLocalIpAddress.AddressFamily); + var sockets = _sendSockets.Where(s => s.AddressFamily == fromLocalIpAddress.AddressFamily); // Send from the Any socket and the socket with the matching address if (fromLocalIpAddress.AddressFamily == AddressFamily.InterNetwork) { - sockets = sockets.Where(i => i.LocalIPAddress.Equals(IPAddress.Any) || fromLocalIpAddress.Equals(i.LocalIPAddress)); + sockets = sockets.Where(s => ((IPEndPoint)s.LocalEndPoint).Address.Equals(IPAddress.Any) + || ((IPEndPoint)s.LocalEndPoint).Address.Equals(fromLocalIpAddress)); // If sending to the loopback address, filter the socket list as well if (destination.Address.Equals(IPAddress.Loopback)) { - sockets = sockets.Where(i => i.LocalIPAddress.Equals(IPAddress.Any) || i.LocalIPAddress.Equals(IPAddress.Loopback)); + sockets = sockets.Where(s => ((IPEndPoint)s.LocalEndPoint).Address.Equals(IPAddress.Any) + || ((IPEndPoint)s.LocalEndPoint).Address.Equals(IPAddress.Loopback)); } } else if (fromLocalIpAddress.AddressFamily == AddressFamily.InterNetworkV6) { - sockets = sockets.Where(i => i.LocalIPAddress.Equals(IPAddress.IPv6Any) || fromLocalIpAddress.Equals(i.LocalIPAddress)); + sockets = sockets.Where(s => ((IPEndPoint)s.LocalEndPoint).Address.Equals(IPAddress.IPv6Any) + || ((IPEndPoint)s.LocalEndPoint).Address.Equals(fromLocalIpAddress)); // If sending to the loopback address, filter the socket list as well if (destination.Address.Equals(IPAddress.IPv6Loopback)) { - sockets = sockets.Where(i => i.LocalIPAddress.Equals(IPAddress.IPv6Any) || i.LocalIPAddress.Equals(IPAddress.IPv6Loopback)); + sockets = sockets.Where(s => ((IPEndPoint)s.LocalEndPoint).Address.Equals(IPAddress.IPv6Any) + || ((IPEndPoint)s.LocalEndPoint).Address.Equals(IPAddress.IPv6Loopback)); } } @@ -239,7 +248,7 @@ namespace Rssdp.Infrastructure /// </summary> public async Task SendMulticastMessage(string message, int sendCount, IPAddress fromLocalIpAddress, CancellationToken cancellationToken) { - if (message == null) + if (message is null) { throw new ArgumentNullException(nameof(message)); } @@ -275,7 +284,7 @@ namespace Rssdp.Infrastructure { lock (_SendSocketSynchroniser) { - if (_sendSockets != null) + if (_sendSockets is not null) { var sockets = _sendSockets.ToList(); _sendSockets = null; @@ -284,7 +293,8 @@ namespace Rssdp.Infrastructure foreach (var socket in sockets) { - _logger.LogInformation("{0} disposing sendSocket from {1}", GetType().Name, socket.LocalIPAddress); + var socketAddress = ((IPEndPoint)socket.LocalEndPoint).Address; + _logger.LogInformation("{0} disposing sendSocket from {1}", GetType().Name, socketAddress); socket.Dispose(); } } @@ -312,7 +322,7 @@ namespace Rssdp.Infrastructure { if (disposing) { - StopListeningForBroadcasts(); + StopListeningForMulticast(); StopListeningForResponses(); } @@ -321,11 +331,11 @@ namespace Rssdp.Infrastructure private Task SendMessageIfSocketNotDisposed(byte[] messageData, IPEndPoint destination, IPAddress fromLocalIpAddress, CancellationToken cancellationToken) { var sockets = _sendSockets; - if (sockets != null) + if (sockets is not null) { sockets = sockets.ToList(); - var tasks = sockets.Where(s => (fromLocalIpAddress == null || fromLocalIpAddress.Equals(s.LocalIPAddress))) + var tasks = sockets.Where(s => (fromLocalIpAddress is null || fromLocalIpAddress.Equals(((IPEndPoint)s.LocalEndPoint).Address))) .Select(s => SendFromSocket(s, messageData, destination, cancellationToken)); return Task.WhenAll(tasks); } @@ -333,82 +343,78 @@ namespace Rssdp.Infrastructure return Task.CompletedTask; } - private List<ISocket> ListenForBroadcasts() + private List<Socket> CreateMulticastSocketsAndListen() { - var sockets = new List<ISocket>(); - var nonNullBindAddresses = _networkManager.GetInternalBindAddresses().Where(x => x.Address != null); - + var sockets = new List<Socket>(); + var multicastGroupAddress = IPAddress.Parse(SsdpConstants.MulticastLocalAdminAddress); if (_enableMultiSocketBinding) { - foreach (var address in nonNullBindAddresses) - { - if (address.AddressFamily == AddressFamily.InterNetworkV6) - { - // Not supporting IPv6 right now - continue; - } + // IPv6 is currently unsupported + var validInterfaces = _networkManager.GetInternalBindAddresses() + .Where(x => x.Address is not null) + .Where(x => x.AddressFamily == AddressFamily.InterNetwork) + .DistinctBy(x => x.Index); + foreach (var intf in validInterfaces) + { try { - sockets.Add(_SocketFactory.CreateUdpMulticastSocket(IPAddress.Parse(SsdpConstants.MulticastLocalAdminAddress), address.Address, _MulticastTtl, SsdpConstants.MulticastPort)); + var socket = _SocketFactory.CreateUdpMulticastSocket(multicastGroupAddress, intf, _MulticastTtl, SsdpConstants.MulticastPort); + _ = ListenToSocketInternal(socket); + sockets.Add(socket); } catch (Exception ex) { - _logger.LogError(ex, "Error in ListenForBroadcasts. IPAddress: {0}", address); + _logger.LogError(ex, "Error in CreateMulticastSocketsAndListen. IP address: {0}", intf.Address); } } } else { - sockets.Add(_SocketFactory.CreateUdpMulticastSocket(IPAddress.Parse(SsdpConstants.MulticastLocalAdminAddress), IPAddress.Any, _MulticastTtl, SsdpConstants.MulticastPort)); - } - - foreach (var socket in sockets) - { + var socket = _SocketFactory.CreateUdpMulticastSocket(multicastGroupAddress, new IPData(IPAddress.Any, null), _MulticastTtl, SsdpConstants.MulticastPort); _ = ListenToSocketInternal(socket); + sockets.Add(socket); } return sockets; } - private List<ISocket> CreateSocketAndListenForResponsesAsync() + private List<Socket> CreateSendSockets() { - var sockets = new List<ISocket>(); - + var sockets = new List<Socket>(); if (_enableMultiSocketBinding) { - foreach (var address in _networkManager.GetInternalBindAddresses()) - { - if (address.AddressFamily == AddressFamily.InterNetworkV6) - { - // Not supporting IPv6 right now - continue; - } + // IPv6 is currently unsupported + var validInterfaces = _networkManager.GetInternalBindAddresses() + .Where(x => x.Address is not null) + .Where(x => x.AddressFamily == AddressFamily.InterNetwork); + foreach (var intf in validInterfaces) + { try { - sockets.Add(_SocketFactory.CreateSsdpUdpSocket(address.Address, _LocalPort)); + var socket = _SocketFactory.CreateSsdpUdpSocket(intf, _LocalPort); + _ = ListenToSocketInternal(socket); + sockets.Add(socket); } catch (Exception ex) { - _logger.LogError(ex, "Error in CreateSsdpUdpSocket. IPAddress: {0}", address); + _logger.LogError(ex, "Error in CreateSsdpUdpSocket. IPAddress: {0}", intf.Address); } } } else { - sockets.Add(_SocketFactory.CreateSsdpUdpSocket(IPAddress.Any, _LocalPort)); - } - - foreach (var socket in sockets) - { + var socket = _SocketFactory.CreateSsdpUdpSocket(new IPData(IPAddress.Any, null), _LocalPort); _ = ListenToSocketInternal(socket); + sockets.Add(socket); } + return sockets; } - private async Task ListenToSocketInternal(ISocket socket) + private async Task ListenToSocketInternal(Socket socket) { var cancelled = false; var receiveBuffer = new byte[8192]; @@ -417,14 +423,17 @@ namespace Rssdp.Infrastructure { try { - var result = await socket.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false); + var result = await socket.ReceiveMessageFromAsync(receiveBuffer, SocketFlags.None, new IPEndPoint(IPAddress.Any, 0), CancellationToken.None).ConfigureAwait(false);; if (result.ReceivedBytes > 0) { - // Strange cannot convert compiler error here if I don't explicitly - // assign or cast to Action first. Assignment is easier to read, - // so went with that. - ProcessMessage(UTF8Encoding.UTF8.GetString(result.Buffer, 0, result.ReceivedBytes), result.RemoteEndPoint, result.LocalIPAddress); + var remoteEndpoint = (IPEndPoint)result.RemoteEndPoint; + var localEndpointAddress = result.PacketInformation.Address; + + ProcessMessage( + UTF8Encoding.UTF8.GetString(receiveBuffer, 0, result.ReceivedBytes), + remoteEndpoint, + localEndpointAddress); } } catch (ObjectDisposedException) @@ -440,11 +449,11 @@ namespace Rssdp.Infrastructure private void EnsureSendSocketCreated() { - if (_sendSockets == null) + if (_sendSockets is null) { lock (_SendSocketSynchroniser) { - _sendSockets ??= CreateSocketAndListenForResponsesAsync(); + _sendSockets ??= CreateSendSockets(); } } } @@ -455,6 +464,7 @@ namespace Rssdp.Infrastructure // requests start with a method which can vary and might be one we haven't // seen/don't know. We'll check if this message is a request or a response // by checking for the HTTP/ prefix on the start of the message. + _logger.LogDebug("Received data from {From} on {Port} at {Address}:\n{Data}", endPoint.Address, endPoint.Port, receivedOnLocalIpAddress, data); if (data.StartsWith("HTTP/", StringComparison.OrdinalIgnoreCase)) { HttpResponseMessage responseMessage = null; @@ -467,7 +477,7 @@ namespace Rssdp.Infrastructure // Ignore invalid packets. } - if (responseMessage != null) + if (responseMessage is not null) { OnResponseReceived(responseMessage, endPoint, receivedOnLocalIpAddress); } @@ -484,7 +494,7 @@ namespace Rssdp.Infrastructure // Ignore invalid packets. } - if (requestMessage != null) + if (requestMessage is not null) { OnRequestReceived(requestMessage, endPoint, receivedOnLocalIpAddress); } @@ -502,7 +512,7 @@ namespace Rssdp.Infrastructure } var handlers = this.RequestReceived; - if (handlers != null) + if (handlers is not null) { handlers(this, new RequestReceivedEventArgs(data, remoteEndPoint, receivedOnLocalIpAddress)); } @@ -511,7 +521,7 @@ namespace Rssdp.Infrastructure private void OnResponseReceived(HttpResponseMessage data, IPEndPoint endPoint, IPAddress localIpAddress) { var handlers = this.ResponseReceived; - if (handlers != null) + if (handlers is not null) { handlers(this, new ResponseReceivedEventArgs(data, endPoint) { |
