jellyfin/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs

273 lines
8.4 KiB
C#
Raw Normal View History

using MediaBrowser.Common.Events;
2014-09-17 03:04:10 +00:00
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
2015-04-29 18:48:34 +00:00
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
2015-12-26 03:36:38 +00:00
using MoreLinq;
namespace MediaBrowser.Dlna.Ssdp
{
2015-07-23 16:32:34 +00:00
public class DeviceDiscovery : IDeviceDiscovery, IDisposable
{
private bool _disposed;
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
2015-07-23 16:32:34 +00:00
private SsdpHandler _ssdpHandler;
private readonly CancellationTokenSource _tokenSource;
2014-09-17 03:04:10 +00:00
private readonly IServerApplicationHost _appHost;
2014-07-22 01:29:06 +00:00
public event EventHandler<SsdpMessageEventArgs> DeviceDiscovered;
public event EventHandler<SsdpMessageEventArgs> DeviceLeft;
2015-07-23 16:32:34 +00:00
public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, IServerApplicationHost appHost)
{
_tokenSource = new CancellationTokenSource();
_logger = logger;
_config = config;
2014-09-17 03:04:10 +00:00
_appHost = appHost;
}
2015-12-26 03:23:02 +00:00
private List<IPAddress> GetLocalIpAddresses()
{
NetworkInterface[] interfaces;
try
{
interfaces = NetworkInterface.GetAllNetworkInterfaces();
}
catch (Exception ex)
{
_logger.ErrorException("Error in GetAllNetworkInterfaces", ex);
return new List<IPAddress>();
}
return interfaces.SelectMany(network => {
try
{
2015-12-26 03:36:38 +00:00
_logger.Debug("Querying interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
2015-12-26 03:23:02 +00:00
var properties = network.GetIPProperties();
return properties.UnicastAddresses
2015-12-26 03:36:38 +00:00
.Select(i => i.Address)
.Where(i => i.AddressFamily == AddressFamily.InterNetwork)
2015-12-26 03:23:02 +00:00
.ToList();
}
catch (Exception ex)
{
_logger.ErrorException("Error querying network interface", ex);
return new List<IPAddress>();
}
})
2015-12-26 03:36:38 +00:00
.DistinctBy(i => i.ToString())
2015-12-26 03:23:02 +00:00
.ToList();
}
2015-07-23 16:32:34 +00:00
public void Start(SsdpHandler ssdpHandler)
{
2015-07-23 16:32:34 +00:00
_ssdpHandler = ssdpHandler;
2014-07-22 01:29:06 +00:00
_ssdpHandler.MessageReceived += _ssdpHandler_MessageReceived;
2015-12-26 03:23:02 +00:00
foreach (var localIp in GetLocalIpAddresses())
{
try
{
CreateListener(localIp);
}
catch (Exception e)
{
_logger.ErrorException("Failed to Initilize Socket", e);
}
}
}
2015-07-23 16:32:34 +00:00
2014-08-26 02:30:52 +00:00
void _ssdpHandler_MessageReceived(object sender, SsdpMessageEventArgs e)
{
2014-07-22 01:29:06 +00:00
string nts;
e.Headers.TryGetValue("NTS", out nts);
if (String.Equals(e.Method, "NOTIFY", StringComparison.OrdinalIgnoreCase) &&
String.Equals(nts, "ssdp:byebye", StringComparison.OrdinalIgnoreCase) &&
!_disposed)
{
EventHelper.FireEventIfNotNull(DeviceLeft, this, e, _logger);
return;
}
2014-08-26 02:30:52 +00:00
try
2014-07-22 01:29:06 +00:00
{
2015-05-12 02:40:26 +00:00
if (e.LocalEndPoint == null)
{
var ip = _appHost.LocalIpAddress;
e.LocalEndPoint = new IPEndPoint(IPAddress.Parse(ip), 0);
}
2014-08-26 02:30:52 +00:00
2015-05-12 02:40:26 +00:00
if (e.LocalEndPoint != null)
{
TryCreateDevice(e);
}
2014-07-22 01:29:06 +00:00
}
catch (OperationCanceledException)
{
}
catch (Exception ex)
{
_logger.ErrorException("Error creating play to controller", ex);
}
}
2014-09-17 03:04:10 +00:00
private void CreateListener(IPAddress localIp)
{
Task.Factory.StartNew(async (o) =>
{
try
{
2015-12-26 03:23:02 +00:00
_logger.Info("Creating SSDP listener on {0}", localIp);
2015-12-26 03:23:02 +00:00
var endPoint = new IPEndPoint(localIp, 1900);
2015-12-26 03:23:02 +00:00
var socket = GetMulticastSocket(localIp, endPoint);
var receiveBuffer = new byte[64000];
CreateNotifier(localIp);
while (!_tokenSource.IsCancellationRequested)
{
var receivedBytes = await socket.ReceiveAsync(receiveBuffer, 0, 64000);
if (receivedBytes > 0)
{
var args = SsdpHelper.ParseSsdpResponse(receiveBuffer);
args.EndPoint = endPoint;
2015-05-12 02:40:26 +00:00
args.LocalEndPoint = new IPEndPoint(localIp, 0);
2015-05-05 23:19:47 +00:00
if (!_ssdpHandler.IsSelfNotification(args))
{
TryCreateDevice(args);
}
}
}
_logger.Info("SSDP listener - Task completed");
}
catch (OperationCanceledException)
{
}
catch (Exception e)
{
_logger.ErrorException("Error in listener", e);
}
}, _tokenSource.Token, TaskCreationOptions.LongRunning);
}
private void CreateNotifier(IPAddress localIp)
{
Task.Factory.StartNew(async (o) =>
{
try
{
while (true)
{
_ssdpHandler.SendSearchMessage(new IPEndPoint(localIp, 1900));
var delay = _config.GetDlnaConfiguration().ClientDiscoveryIntervalSeconds * 1000;
await Task.Delay(delay, _tokenSource.Token).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
}
catch (Exception ex)
{
_logger.ErrorException("Error in notifier", ex);
}
}, _tokenSource.Token, TaskCreationOptions.LongRunning);
}
2014-09-17 03:04:10 +00:00
private Socket GetMulticastSocket(IPAddress localIpAddress, EndPoint localEndpoint)
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
2014-09-17 03:04:10 +00:00
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), localIpAddress));
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
2014-09-17 03:04:10 +00:00
socket.Bind(localEndpoint);
2015-07-23 16:32:34 +00:00
return socket;
}
2014-08-26 02:30:52 +00:00
private void TryCreateDevice(SsdpMessageEventArgs args)
{
string nts;
args.Headers.TryGetValue("NTS", out nts);
2015-05-04 18:01:01 +00:00
if (String.Equals(nts, "ssdp:byebye", StringComparison.OrdinalIgnoreCase))
{
if (String.Equals(args.Method, "NOTIFY", StringComparison.OrdinalIgnoreCase))
{
if (!_disposed)
{
EventHelper.FireEventIfNotNull(DeviceLeft, this, args, _logger);
}
}
return;
}
string usn;
if (!args.Headers.TryGetValue("USN", out usn)) usn = string.Empty;
string nt;
if (!args.Headers.TryGetValue("NT", out nt)) nt = string.Empty;
// Need to be able to download device description
string location;
if (!args.Headers.TryGetValue("Location", out location) ||
string.IsNullOrEmpty(location))
{
return;
}
if (_config.GetDlnaConfiguration().EnableDebugLogging)
{
var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value));
var headerText = string.Join(",", headerTexts.ToArray());
_logger.Debug("{0} Device message received from {1}. Headers: {2}", args.Method, args.EndPoint, headerText);
}
2014-07-22 01:29:06 +00:00
EventHelper.FireEventIfNotNull(DeviceDiscovered, this, args, _logger);
}
public void Dispose()
{
2015-07-23 16:32:34 +00:00
if (_ssdpHandler != null)
{
_ssdpHandler.MessageReceived -= _ssdpHandler_MessageReceived;
}
2014-08-26 02:30:52 +00:00
if (!_disposed)
{
_disposed = true;
_tokenSource.Cancel();
}
}
}
}