2019-01-13 15:03:10 -05:00
using System ;
2016-10-29 18:22:20 -04:00
using System.Collections.Generic ;
using System.Linq ;
2019-07-07 15:03:26 -04:00
using System.Net ;
2016-10-29 18:22:20 -04:00
using System.Net.Http ;
2019-01-12 16:25:55 -05:00
using System.Net.Sockets ;
2016-10-29 18:22:20 -04:00
using System.Text ;
2017-02-05 15:44:08 -05:00
using System.Threading ;
2016-10-29 18:22:20 -04:00
using System.Threading.Tasks ;
2016-12-04 16:55:02 -05:00
using MediaBrowser.Common.Net ;
2020-03-24 11:12:06 -04:00
using MediaBrowser.Model.Net ;
using Microsoft.Extensions.Logging ;
2016-10-29 18:22:20 -04:00
namespace Rssdp.Infrastructure
{
/// <summary>
/// Provides the platform independent logic for publishing device existence and responding to search requests.
/// </summary>
public sealed class SsdpCommunicationsServer : DisposableManagedObjectBase , ISsdpCommunicationsServer
{
2019-01-07 18:24:34 -05:00
/ * We could technically use one socket listening on port 1900 for everything .
* This should get both multicast ( notifications ) and unicast ( search response ) messages , however
* this often doesn ' t work under Windows because the MS SSDP service is running . If that service
* is running then it will steal the unicast messages and we will never see search responses .
* 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 .
*
2023-02-16 12:15:12 -05:00
* 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
2019-01-07 18:24:34 -05:00
* port to use , we will default to 0 which allows the underlying system to auto - assign a free port .
* /
2016-10-29 18:22:20 -04:00
2023-10-10 18:02:37 -04:00
private object _BroadcastListenSocketSynchroniser = new ( ) ;
2023-02-16 12:15:12 -05:00
private List < Socket > _MulticastListenSockets ;
2016-10-29 18:22:20 -04:00
2023-10-10 18:02:37 -04:00
private object _SendSocketSynchroniser = new ( ) ;
2023-02-16 12:15:12 -05:00
private List < Socket > _sendSockets ;
2016-10-29 18:22:20 -04:00
private HttpRequestParser _RequestParser ;
private HttpResponseParser _ResponseParser ;
2016-12-04 16:55:02 -05:00
private readonly ILogger _logger ;
2016-10-29 18:22:20 -04:00
private ISocketFactory _SocketFactory ;
2021-05-23 18:30:41 -04:00
private readonly INetworkManager _networkManager ;
2016-10-29 18:22:20 -04:00
private int _LocalPort ;
private int _MulticastTtl ;
private bool _IsShared ;
/// <summary>
/// Raised when a HTTPU request message is received by a socket (unicast or multicast).
/// </summary>
public event EventHandler < RequestReceivedEventArgs > RequestReceived ;
/// <summary>
/// Raised when an HTTPU response message is received by a socket (unicast or multicast).
/// </summary>
public event EventHandler < ResponseReceivedEventArgs > ResponseReceived ;
/// <summary>
/// Minimum constructor.
/// </summary>
2019-01-13 15:37:13 -05:00
/// <exception cref="ArgumentNullException">The <paramref name="socketFactory"/> argument is null.</exception>
2023-10-10 18:02:37 -04:00
public SsdpCommunicationsServer (
ISocketFactory socketFactory ,
INetworkManager networkManager ,
ILogger logger )
: this ( socketFactory , 0 , SsdpConstants . SsdpDefaultMulticastTimeToLive , networkManager , logger )
2016-10-29 18:22:20 -04:00
{
2021-05-23 18:30:41 -04:00
2016-10-29 18:22:20 -04:00
}
/// <summary>
/// Full constructor.
/// </summary>
2019-01-13 15:37:13 -05:00
/// <exception cref="ArgumentNullException">The <paramref name="socketFactory"/> argument is null.</exception>
/// <exception cref="ArgumentOutOfRangeException">The <paramref name="multicastTimeToLive"/> argument is less than or equal to zero.</exception>
2023-10-10 18:02:37 -04:00
public SsdpCommunicationsServer (
ISocketFactory socketFactory ,
int localPort ,
int multicastTimeToLive ,
INetworkManager networkManager ,
ILogger logger )
2016-10-29 18:22:20 -04:00
{
2023-02-16 12:15:12 -05:00
if ( socketFactory is null )
2020-06-20 04:35:29 -04:00
{
throw new ArgumentNullException ( nameof ( socketFactory ) ) ;
}
if ( multicastTimeToLive < = 0 )
{
throw new ArgumentOutOfRangeException ( nameof ( multicastTimeToLive ) , "multicastTimeToLive must be greater than zero." ) ;
}
2016-10-29 18:22:20 -04:00
2023-10-10 18:02:37 -04:00
_BroadcastListenSocketSynchroniser = new ( ) ;
_SendSocketSynchroniser = new ( ) ;
2016-10-29 18:22:20 -04:00
_LocalPort = localPort ;
_SocketFactory = socketFactory ;
2023-10-10 18:02:37 -04:00
_RequestParser = new ( ) ;
_ResponseParser = new ( ) ;
2016-10-29 18:22:20 -04:00
_MulticastTtl = multicastTimeToLive ;
2016-12-04 16:55:02 -05:00
_networkManager = networkManager ;
_logger = logger ;
2016-10-29 18:22:20 -04:00
}
/// <summary>
/// Causes the server to begin listening for multicast messages, being SSDP search requests and notifications.
/// </summary>
2019-01-13 15:37:13 -05:00
/// <exception cref="ObjectDisposedException">Thrown if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true (because <seealso cref="DisposableManagedObjectBase.Dispose()" /> has been called previously).</exception>
2023-02-16 12:15:12 -05:00
public void BeginListeningForMulticast ( )
2016-10-29 18:22:20 -04:00
{
ThrowIfDisposed ( ) ;
2022-07-21 03:26:18 -04:00
lock ( _BroadcastListenSocketSynchroniser )
2016-10-29 18:22:20 -04:00
{
2023-02-16 12:15:12 -05:00
if ( _MulticastListenSockets is null )
2016-10-29 18:22:20 -04:00
{
2022-07-21 03:26:18 -04:00
try
{
2023-02-16 12:15:12 -05:00
_MulticastListenSockets = CreateMulticastSocketsAndListen ( ) ;
2022-07-21 03:26:18 -04:00
}
catch ( SocketException ex )
2018-09-12 13:26:21 -04:00
{
2023-02-16 12:15:12 -05:00
_logger . LogError ( "Failed to bind to multicast address: {Message}. DLNA will be unavailable" , ex . Message ) ;
2022-07-21 03:26:18 -04:00
}
catch ( Exception ex )
{
2023-02-16 12:15:12 -05:00
_logger . LogError ( ex , "Error in BeginListeningForMulticast" ) ;
2018-09-12 13:26:21 -04:00
}
2016-10-29 18:22:20 -04:00
}
}
}
/// <summary>
/// Causes the server to stop listening for multicast messages, being SSDP search requests and notifications.
/// </summary>
2019-01-13 15:37:13 -05:00
/// <exception cref="ObjectDisposedException">Thrown if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true (because <seealso cref="DisposableManagedObjectBase.Dispose()" /> has been called previously).</exception>
2023-02-16 12:15:12 -05:00
public void StopListeningForMulticast ( )
2016-10-29 18:22:20 -04:00
{
lock ( _BroadcastListenSocketSynchroniser )
{
2023-02-16 12:15:12 -05:00
if ( _MulticastListenSockets is not null )
2016-10-29 18:22:20 -04:00
{
2019-01-12 16:25:55 -05:00
_logger . LogInformation ( "{0} disposing _BroadcastListenSocket" , GetType ( ) . Name ) ;
2023-02-16 12:15:12 -05:00
foreach ( var socket in _MulticastListenSockets )
{
socket . Dispose ( ) ;
}
_MulticastListenSockets = null ;
2016-10-29 18:22:20 -04:00
}
}
}
/// <summary>
/// Sends a message to a particular address (uni or multicast) and port.
/// </summary>
2023-02-17 13:27:36 -05:00
public async Task SendMessage ( byte [ ] messageData , IPEndPoint destination , IPAddress fromlocalIPAddress , CancellationToken cancellationToken )
2016-10-29 18:22:20 -04:00
{
2023-02-16 12:15:12 -05:00
if ( messageData is null )
2020-06-20 04:35:29 -04:00
{
throw new ArgumentNullException ( nameof ( messageData ) ) ;
}
2016-10-29 18:22:20 -04:00
ThrowIfDisposed ( ) ;
2023-02-17 13:27:36 -05:00
var sockets = GetSendSockets ( fromlocalIPAddress , destination ) ;
2016-12-04 16:55:02 -05:00
if ( sockets . Count = = 0 )
{
return ;
}
2016-10-29 18:22:20 -04:00
// SSDP spec recommends sending messages multiple times (not more than 3) to account for possible packet loss over UDP.
2016-11-04 19:57:21 -04:00
for ( var i = 0 ; i < SsdpConstants . UdpResendCount ; i + + )
{
2017-02-05 15:44:08 -05:00
var tasks = sockets . Select ( s = > SendFromSocket ( s , messageData , destination , cancellationToken ) ) . ToArray ( ) ;
2016-12-04 16:55:02 -05:00
await Task . WhenAll ( tasks ) . ConfigureAwait ( false ) ;
2016-11-04 19:57:21 -04:00
2017-02-05 15:44:08 -05:00
await Task . Delay ( 100 , cancellationToken ) . ConfigureAwait ( false ) ;
2016-11-04 19:57:21 -04:00
}
2016-10-29 18:22:20 -04:00
}
2023-02-16 12:15:12 -05:00
private async Task SendFromSocket ( Socket socket , byte [ ] messageData , IPEndPoint destination , CancellationToken cancellationToken )
2016-12-04 16:55:02 -05:00
{
try
{
2023-02-16 12:15:12 -05:00
await socket . SendToAsync ( messageData , destination , cancellationToken ) . ConfigureAwait ( false ) ;
2016-12-20 00:21:52 -05:00
}
catch ( ObjectDisposedException )
{
2017-03-26 15:00:35 -04:00
}
catch ( OperationCanceledException )
{
2016-12-04 16:55:02 -05:00
}
catch ( Exception ex )
{
2023-02-16 12:15:12 -05:00
var localIP = ( ( IPEndPoint ) socket . LocalEndPoint ) . Address ;
_logger . LogError ( ex , "Error sending socket message from {0} to {1}" , localIP . ToString ( ) , destination . ToString ( ) ) ;
2016-12-04 16:55:02 -05:00
}
}
2023-02-17 13:27:36 -05:00
private List < Socket > GetSendSockets ( IPAddress fromlocalIPAddress , IPEndPoint destination )
2016-12-04 16:55:02 -05:00
{
EnsureSendSocketCreated ( ) ;
lock ( _SendSocketSynchroniser )
{
2023-02-17 13:27:36 -05:00
var sockets = _sendSockets . Where ( s = > s . AddressFamily = = fromlocalIPAddress . AddressFamily ) ;
2016-12-04 16:55:02 -05:00
// Send from the Any socket and the socket with the matching address
2023-02-17 13:27:36 -05:00
if ( fromlocalIPAddress . AddressFamily = = AddressFamily . InterNetwork )
2016-12-04 16:55:02 -05:00
{
2023-02-16 12:15:12 -05:00
sockets = sockets . Where ( s = > ( ( IPEndPoint ) s . LocalEndPoint ) . Address . Equals ( IPAddress . Any )
2023-02-17 13:27:36 -05:00
| | ( ( IPEndPoint ) s . LocalEndPoint ) . Address . Equals ( fromlocalIPAddress ) ) ;
2016-12-05 13:46:38 -05:00
// If sending to the loopback address, filter the socket list as well
2019-07-07 15:03:26 -04:00
if ( destination . Address . Equals ( IPAddress . Loopback ) )
2016-12-05 13:46:38 -05:00
{
2023-02-16 12:15:12 -05:00
sockets = sockets . Where ( s = > ( ( IPEndPoint ) s . LocalEndPoint ) . Address . Equals ( IPAddress . Any )
| | ( ( IPEndPoint ) s . LocalEndPoint ) . Address . Equals ( IPAddress . Loopback ) ) ;
2016-12-05 13:46:38 -05:00
}
2016-12-04 16:55:02 -05:00
}
2023-02-17 13:27:36 -05:00
else if ( fromlocalIPAddress . AddressFamily = = AddressFamily . InterNetworkV6 )
2016-12-04 16:55:02 -05:00
{
2023-02-16 12:15:12 -05:00
sockets = sockets . Where ( s = > ( ( IPEndPoint ) s . LocalEndPoint ) . Address . Equals ( IPAddress . IPv6Any )
2023-02-17 13:27:36 -05:00
| | ( ( IPEndPoint ) s . LocalEndPoint ) . Address . Equals ( fromlocalIPAddress ) ) ;
2016-12-04 16:55:02 -05:00
2016-12-05 13:46:38 -05:00
// If sending to the loopback address, filter the socket list as well
2019-07-07 15:03:26 -04:00
if ( destination . Address . Equals ( IPAddress . IPv6Loopback ) )
2016-12-05 13:46:38 -05:00
{
2023-02-16 12:15:12 -05:00
sockets = sockets . Where ( s = > ( ( IPEndPoint ) s . LocalEndPoint ) . Address . Equals ( IPAddress . IPv6Any )
| | ( ( IPEndPoint ) s . LocalEndPoint ) . Address . Equals ( IPAddress . IPv6Loopback ) ) ;
2016-12-05 13:46:38 -05:00
}
2016-12-04 16:55:02 -05:00
}
return sockets . ToList ( ) ;
}
}
2023-02-17 13:27:36 -05:00
public Task SendMulticastMessage ( string message , IPAddress fromlocalIPAddress , CancellationToken cancellationToken )
2018-09-12 13:26:21 -04:00
{
2023-02-17 13:27:36 -05:00
return SendMulticastMessage ( message , SsdpConstants . UdpResendCount , fromlocalIPAddress , cancellationToken ) ;
2018-09-12 13:26:21 -04:00
}
2016-10-29 18:22:20 -04:00
/// <summary>
/// Sends a message to the SSDP multicast address and port.
/// </summary>
2023-02-17 13:27:36 -05:00
public async Task SendMulticastMessage ( string message , int sendCount , IPAddress fromlocalIPAddress , CancellationToken cancellationToken )
2016-10-29 18:22:20 -04:00
{
2023-02-16 12:15:12 -05:00
if ( message is null )
2020-06-20 04:35:29 -04:00
{
throw new ArgumentNullException ( nameof ( message ) ) ;
}
2016-11-14 14:48:01 -05:00
byte [ ] messageData = Encoding . UTF8 . GetBytes ( message ) ;
2016-10-29 18:22:20 -04:00
ThrowIfDisposed ( ) ;
2017-02-05 15:44:08 -05:00
cancellationToken . ThrowIfCancellationRequested ( ) ;
2016-10-29 18:22:20 -04:00
EnsureSendSocketCreated ( ) ;
// SSDP spec recommends sending messages multiple times (not more than 3) to account for possible packet loss over UDP.
2018-09-12 13:26:21 -04:00
for ( var i = 0 ; i < sendCount ; i + + )
2016-11-04 19:57:21 -04:00
{
2019-07-07 15:03:26 -04:00
await SendMessageIfSocketNotDisposed (
messageData ,
new IPEndPoint (
IPAddress . Parse ( SsdpConstants . MulticastLocalAdminAddress ) ,
SsdpConstants . MulticastPort ) ,
2023-02-17 13:27:36 -05:00
fromlocalIPAddress ,
2019-07-07 15:03:26 -04:00
cancellationToken ) . ConfigureAwait ( false ) ;
2016-11-04 19:57:21 -04:00
2017-02-05 15:44:08 -05:00
await Task . Delay ( 100 , cancellationToken ) . ConfigureAwait ( false ) ;
2016-11-04 19:57:21 -04:00
}
2016-10-29 18:22:20 -04:00
}
/// <summary>
/// Stops listening for search responses on the local, unicast socket.
/// </summary>
2019-01-13 15:37:13 -05:00
/// <exception cref="ObjectDisposedException">Thrown if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true (because <seealso cref="DisposableManagedObjectBase.Dispose()" /> has been called previously).</exception>
2016-10-29 18:22:20 -04:00
public void StopListeningForResponses ( )
{
lock ( _SendSocketSynchroniser )
{
2023-02-16 12:15:12 -05:00
if ( _sendSockets is not null )
2016-12-04 16:55:02 -05:00
{
var sockets = _sendSockets . ToList ( ) ;
_sendSockets = null ;
2018-12-13 08:18:25 -05:00
_logger . LogInformation ( "{0} Disposing {1} sendSockets" , GetType ( ) . Name , sockets . Count ) ;
2018-09-12 13:26:21 -04:00
2016-12-04 16:55:02 -05:00
foreach ( var socket in sockets )
{
2023-02-16 12:15:12 -05:00
var socketAddress = ( ( IPEndPoint ) socket . LocalEndPoint ) . Address ;
_logger . LogInformation ( "{0} disposing sendSocket from {1}" , GetType ( ) . Name , socketAddress ) ;
2016-12-04 16:55:02 -05:00
socket . Dispose ( ) ;
}
}
2016-10-29 18:22:20 -04:00
}
}
/// <summary>
2022-02-06 16:21:17 -05:00
/// Gets or sets a boolean value indicating whether or not this instance is shared amongst multiple <see cref="SsdpDeviceLocator"/> and/or <see cref="ISsdpDevicePublisher"/> instances.
2016-10-29 18:22:20 -04:00
/// </summary>
/// <remarks>
2022-02-06 16:21:17 -05:00
/// <para>If true, disposing an instance of a <see cref="SsdpDeviceLocator"/>or a <see cref="ISsdpDevicePublisher"/> will not dispose this comms server instance. The calling code is responsible for managing the lifetime of the server.</para>
2016-10-29 18:22:20 -04:00
/// </remarks>
public bool IsShared
{
get { return _IsShared ; }
2020-06-15 17:43:52 -04:00
2016-10-29 18:22:20 -04:00
set { _IsShared = value ; }
}
/// <summary>
/// Stops listening for requests, disposes this instance and all internal resources.
/// </summary>
/// <param name="disposing"></param>
protected override void Dispose ( bool disposing )
{
if ( disposing )
{
2023-02-16 12:15:12 -05:00
StopListeningForMulticast ( ) ;
2016-10-29 18:22:20 -04:00
2018-09-12 13:26:21 -04:00
StopListeningForResponses ( ) ;
2016-10-29 18:22:20 -04:00
}
}
2023-02-17 13:27:36 -05:00
private Task SendMessageIfSocketNotDisposed ( byte [ ] messageData , IPEndPoint destination , IPAddress fromlocalIPAddress , CancellationToken cancellationToken )
2016-10-29 18:22:20 -04:00
{
2016-12-04 16:55:02 -05:00
var sockets = _sendSockets ;
2023-02-16 12:15:12 -05:00
if ( sockets is not null )
2016-10-29 18:22:20 -04:00
{
2016-12-04 16:55:02 -05:00
sockets = sockets . ToList ( ) ;
2023-10-10 18:02:37 -04:00
var tasks = sockets . Where ( s = > fromlocalIPAddress is null | | fromlocalIPAddress . Equals ( ( ( IPEndPoint ) s . LocalEndPoint ) . Address ) )
2019-02-21 23:06:49 -05:00
. Select ( s = > SendFromSocket ( s , messageData , destination , cancellationToken ) ) ;
2018-09-12 13:26:21 -04:00
return Task . WhenAll ( tasks ) ;
2016-10-29 18:22:20 -04:00
}
2018-09-12 13:26:21 -04:00
return Task . CompletedTask ;
2016-10-29 18:22:20 -04:00
}
2023-02-16 12:15:12 -05:00
private List < Socket > CreateMulticastSocketsAndListen ( )
2016-10-29 18:22:20 -04:00
{
2023-02-16 12:15:12 -05:00
var sockets = new List < Socket > ( ) ;
var multicastGroupAddress = IPAddress . Parse ( SsdpConstants . MulticastLocalAdminAddress ) ;
2022-07-21 03:26:18 -04:00
2023-10-10 18:02:37 -04:00
// IPv6 is currently unsupported
var validInterfaces = _networkManager . GetInternalBindAddresses ( )
. Where ( x = > x . Address is not null )
. Where ( x = > x . SupportsMulticast )
. Where ( x = > x . AddressFamily = = AddressFamily . InterNetwork )
. DistinctBy ( x = > x . Index ) ;
foreach ( var intf in validInterfaces )
{
try
2023-02-16 12:15:12 -05:00
{
2023-10-10 18:02:37 -04:00
var socket = _SocketFactory . CreateUdpMulticastSocket ( multicastGroupAddress , intf , _MulticastTtl , SsdpConstants . MulticastPort ) ;
_ = ListenToSocketInternal ( socket ) ;
sockets . Add ( socket ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Failed to create SSDP UDP multicast socket for {0} on interface {1} (index {2})" , intf . Address , intf . Name , intf . Index ) ;
2022-07-21 03:26:18 -04:00
}
}
2016-10-29 18:22:20 -04:00
2022-07-21 03:26:18 -04:00
return sockets ;
2016-10-29 18:22:20 -04:00
}
2023-02-16 12:15:12 -05:00
private List < Socket > CreateSendSockets ( )
2016-10-29 18:22:20 -04:00
{
2023-02-16 12:15:12 -05:00
var sockets = new List < Socket > ( ) ;
2023-10-10 18:02:37 -04:00
// IPv6 is currently unsupported
var validInterfaces = _networkManager . GetInternalBindAddresses ( )
. Where ( x = > x . Address is not null )
. Where ( x = > x . SupportsMulticast )
. Where ( x = > x . AddressFamily = = AddressFamily . InterNetwork ) ;
if ( OperatingSystem . IsMacOS ( ) )
2016-12-04 16:55:02 -05:00
{
2023-10-10 18:02:37 -04:00
// Manually remove loopback on macOS due to https://github.com/dotnet/runtime/issues/24340
validInterfaces = validInterfaces . Where ( x = > ! x . Address . Equals ( IPAddress . Loopback ) ) ;
}
2017-11-12 16:05:40 -05:00
2023-10-10 18:02:37 -04:00
foreach ( var intf in validInterfaces )
{
try
2023-02-16 12:15:12 -05:00
{
2023-10-10 18:02:37 -04:00
var socket = _SocketFactory . CreateSsdpUdpSocket ( intf , _LocalPort ) ;
_ = ListenToSocketInternal ( socket ) ;
sockets . Add ( socket ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Failed to create SSDP UDP sender socket for {0} on interface {1} (index {2})" , intf . Address , intf . Name , intf . Index ) ;
2016-12-04 16:55:02 -05:00
}
}
2023-02-16 12:15:12 -05:00
2016-12-04 16:55:02 -05:00
return sockets ;
2016-10-29 18:22:20 -04:00
}
2023-02-16 12:15:12 -05:00
private async Task ListenToSocketInternal ( Socket socket )
2018-09-12 13:26:21 -04:00
{
var cancelled = false ;
2020-06-24 12:23:16 -04:00
var receiveBuffer = new byte [ 8192 ] ;
2017-05-24 15:12:55 -04:00
2018-09-12 13:26:21 -04:00
while ( ! cancelled & & ! IsDisposed )
{
try
2016-10-29 18:22:20 -04:00
{
2023-10-10 18:02:37 -04:00
var result = await socket . ReceiveMessageFromAsync ( receiveBuffer , new IPEndPoint ( IPAddress . Any , _LocalPort ) , CancellationToken . None ) . ConfigureAwait ( false ) ; ;
2016-10-29 18:22:20 -04:00
2018-09-12 13:26:21 -04:00
if ( result . ReceivedBytes > 0 )
2016-10-29 18:22:20 -04:00
{
2023-02-16 12:15:12 -05:00
var remoteEndpoint = ( IPEndPoint ) result . RemoteEndPoint ;
2023-02-20 15:51:15 -05:00
var localEndpointAdapter = _networkManager . GetAllBindInterfaces ( ) . First ( a = > a . Index = = result . PacketInformation . Interface ) ;
2023-02-16 12:15:12 -05:00
ProcessMessage (
2023-10-10 18:02:37 -04:00
Encoding . UTF8 . GetString ( receiveBuffer , 0 , result . ReceivedBytes ) ,
2023-02-16 12:15:12 -05:00
remoteEndpoint ,
2023-02-17 12:24:13 -05:00
localEndpointAdapter . Address ) ;
2016-10-29 18:22:20 -04:00
}
}
2018-09-12 13:26:21 -04:00
catch ( ObjectDisposedException )
{
cancelled = true ;
}
catch ( TaskCanceledException )
{
cancelled = true ;
}
}
2016-10-29 18:22:20 -04:00
}
private void EnsureSendSocketCreated ( )
{
2023-02-16 12:15:12 -05:00
if ( _sendSockets is null )
2016-10-29 18:22:20 -04:00
{
lock ( _SendSocketSynchroniser )
{
2023-02-16 12:15:12 -05:00
_sendSockets ? ? = CreateSendSockets ( ) ;
2016-10-29 18:22:20 -04:00
}
}
}
2023-02-17 13:27:36 -05:00
private void ProcessMessage ( string data , IPEndPoint endPoint , IPAddress receivedOnlocalIPAddress )
2016-10-29 18:22:20 -04:00
{
2019-02-23 21:16:19 -05:00
// Responses start with the HTTP version, prefixed with HTTP/ while
// 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.
2023-02-17 13:27:36 -05:00
_logger . LogDebug ( "Received data from {From} on {Port} at {Address}:\n{Data}" , endPoint . Address , endPoint . Port , receivedOnlocalIPAddress , data ) ;
2016-10-29 18:22:20 -04:00
if ( data . StartsWith ( "HTTP/" , StringComparison . OrdinalIgnoreCase ) )
{
HttpResponseMessage responseMessage = null ;
try
{
responseMessage = _ResponseParser . Parse ( data ) ;
}
2018-09-12 13:26:21 -04:00
catch ( ArgumentException )
2016-12-04 16:55:02 -05:00
{
// Ignore invalid packets.
}
2016-10-29 18:22:20 -04:00
2023-02-16 12:15:12 -05:00
if ( responseMessage is not null )
2019-02-23 21:16:19 -05:00
{
2023-02-17 13:27:36 -05:00
OnResponseReceived ( responseMessage , endPoint , receivedOnlocalIPAddress ) ;
2019-02-23 21:16:19 -05:00
}
2016-10-29 18:22:20 -04:00
}
else
{
HttpRequestMessage requestMessage = null ;
try
{
requestMessage = _RequestParser . Parse ( data ) ;
}
2018-09-12 13:26:21 -04:00
catch ( ArgumentException )
2016-12-04 16:55:02 -05:00
{
// Ignore invalid packets.
}
2016-10-29 18:22:20 -04:00
2023-02-16 12:15:12 -05:00
if ( requestMessage is not null )
2016-12-04 16:55:02 -05:00
{
2023-02-17 13:27:36 -05:00
OnRequestReceived ( requestMessage , endPoint , receivedOnlocalIPAddress ) ;
2016-12-04 16:55:02 -05:00
}
2016-10-29 18:22:20 -04:00
}
}
2023-02-17 13:27:36 -05:00
private void OnRequestReceived ( HttpRequestMessage data , IPEndPoint remoteEndPoint , IPAddress receivedOnlocalIPAddress )
2016-10-29 18:22:20 -04:00
{
2020-06-14 05:11:11 -04:00
// SSDP specification says only * is currently used but other uri's might
// be implemented in the future and should be ignored unless understood.
// Section 4.2 - http://tools.ietf.org/html/draft-cai-ssdp-v1-03#page-11
2016-12-04 16:55:02 -05:00
if ( data . RequestUri . ToString ( ) ! = "*" )
{
return ;
}
2016-10-29 18:22:20 -04:00
2023-10-10 18:02:37 -04:00
var handlers = RequestReceived ;
2023-02-16 12:15:12 -05:00
if ( handlers is not null )
2020-06-20 05:12:36 -04:00
{
2023-02-17 13:27:36 -05:00
handlers ( this , new RequestReceivedEventArgs ( data , remoteEndPoint , receivedOnlocalIPAddress ) ) ;
2020-06-20 05:12:36 -04:00
}
2016-10-29 18:22:20 -04:00
}
2023-02-17 13:27:36 -05:00
private void OnResponseReceived ( HttpResponseMessage data , IPEndPoint endPoint , IPAddress localIPAddress )
2016-10-29 18:22:20 -04:00
{
2023-10-10 18:02:37 -04:00
var handlers = ResponseReceived ;
2023-02-16 12:15:12 -05:00
if ( handlers is not null )
2020-06-20 05:12:36 -04:00
{
2017-01-24 14:54:18 -05:00
handlers ( this , new ResponseReceivedEventArgs ( data , endPoint )
{
2023-02-17 13:27:36 -05:00
LocalIPAddress = localIPAddress
2017-01-24 14:54:18 -05:00
} ) ;
2020-06-20 05:12:36 -04:00
}
2016-10-29 18:22:20 -04:00
}
}
2018-12-13 08:18:25 -05:00
}