|
@@ -37,96 +37,27 @@ using System.Diagnostics;
|
|
|
using System.Net.Sockets;
|
|
|
using System.Net.NetworkInformation;
|
|
|
using MediaBrowser.Controller.Dlna;
|
|
|
+using MediaBrowser.Model.Logging;
|
|
|
|
|
|
namespace Mono.Nat
|
|
|
{
|
|
|
internal class UpnpSearcher : ISearcher
|
|
|
{
|
|
|
- private const int SearchPeriod = 5 * 60; // The time in seconds between each search
|
|
|
- static UpnpSearcher instance = new UpnpSearcher();
|
|
|
- public static List<UdpClient> sockets = CreateSockets();
|
|
|
-
|
|
|
- public static UpnpSearcher Instance
|
|
|
- {
|
|
|
- get { return instance; }
|
|
|
- }
|
|
|
-
|
|
|
public event EventHandler<DeviceEventArgs> DeviceFound;
|
|
|
public event EventHandler<DeviceEventArgs> DeviceLost;
|
|
|
|
|
|
- private List<INatDevice> devices;
|
|
|
- private Dictionary<IPAddress, DateTime> lastFetched;
|
|
|
private DateTime nextSearch;
|
|
|
- private IPEndPoint searchEndpoint;
|
|
|
+ private readonly ILogger _logger;
|
|
|
|
|
|
- UpnpSearcher()
|
|
|
+ public UpnpSearcher(ILogger logger)
|
|
|
{
|
|
|
- devices = new List<INatDevice>();
|
|
|
- lastFetched = new Dictionary<IPAddress, DateTime>();
|
|
|
- //searchEndpoint = new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900);
|
|
|
- searchEndpoint = new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900);
|
|
|
+ _logger = logger;
|
|
|
}
|
|
|
|
|
|
- static List<UdpClient> CreateSockets()
|
|
|
- {
|
|
|
- List<UdpClient> clients = new List<UdpClient>();
|
|
|
- try
|
|
|
- {
|
|
|
- foreach (NetworkInterface n in NetworkInterface.GetAllNetworkInterfaces())
|
|
|
- {
|
|
|
- foreach (UnicastIPAddressInformation address in n.GetIPProperties().UnicastAddresses)
|
|
|
- {
|
|
|
- if (address.Address.AddressFamily == AddressFamily.InterNetwork)
|
|
|
- {
|
|
|
- try
|
|
|
- {
|
|
|
- clients.Add(new UdpClient(new IPEndPoint(address.Address, 0)));
|
|
|
- }
|
|
|
- catch
|
|
|
- {
|
|
|
- continue; // Move on to the next address.
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- catch (Exception)
|
|
|
- {
|
|
|
- clients.Add(new UdpClient(0));
|
|
|
- }
|
|
|
- return clients;
|
|
|
- }
|
|
|
-
|
|
|
public void Search()
|
|
|
{
|
|
|
- foreach (UdpClient s in sockets)
|
|
|
- {
|
|
|
- try
|
|
|
- {
|
|
|
- Search(s);
|
|
|
- }
|
|
|
- catch
|
|
|
- {
|
|
|
- // Ignore any search errors
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
- void Search(UdpClient client)
|
|
|
- {
|
|
|
- nextSearch = DateTime.Now.AddSeconds(SearchPeriod);
|
|
|
- byte[] data = DiscoverDeviceMessage.EncodeSSDP();
|
|
|
-
|
|
|
- // UDP is unreliable, so send 3 requests at a time (per Upnp spec, sec 1.1.2)
|
|
|
- for (int i = 0; i < 3; i++)
|
|
|
- client.Send(data, data.Length, searchEndpoint);
|
|
|
- }
|
|
|
-
|
|
|
- public IPEndPoint SearchEndpoint
|
|
|
- {
|
|
|
- get { return searchEndpoint; }
|
|
|
- }
|
|
|
-
|
|
|
public void Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint)
|
|
|
{
|
|
|
// No matter what, this method should never throw an exception. If something goes wrong
|
|
@@ -145,113 +76,19 @@ namespace Mono.Nat
|
|
|
prefix. */
|
|
|
|
|
|
// We have an internet gateway device now
|
|
|
- UpnpNatDevice d = new UpnpNatDevice(localAddress, deviceInfo, endpoint, string.Empty);
|
|
|
+ UpnpNatDevice d = new UpnpNatDevice(localAddress, deviceInfo, endpoint, string.Empty, _logger);
|
|
|
|
|
|
- if (devices.Contains(d))
|
|
|
- {
|
|
|
- // We already have found this device, so we just refresh it to let people know it's
|
|
|
- // Still alive. If a device doesn't respond to a search, we dump it.
|
|
|
- devices[devices.IndexOf(d)].LastSeen = DateTime.Now;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
-
|
|
|
- // If we send 3 requests at a time, ensure we only fetch the services list once
|
|
|
- // even if three responses are received
|
|
|
- if (lastFetched.ContainsKey(endpoint.Address))
|
|
|
- {
|
|
|
- DateTime last = lastFetched[endpoint.Address];
|
|
|
- if ((DateTime.Now - last) < TimeSpan.FromSeconds(20))
|
|
|
- return;
|
|
|
- }
|
|
|
- lastFetched[endpoint.Address] = DateTime.Now;
|
|
|
-
|
|
|
- // Once we've parsed the information we need, we tell the device to retrieve it's service list
|
|
|
- // Once we successfully receive the service list, the callback provided will be invoked.
|
|
|
- NatUtility.Log("Fetching service list: {0}", d.HostEndPoint);
|
|
|
- d.GetServicesList(DeviceSetupComplete);
|
|
|
- }
|
|
|
+ NatUtility.Log("Fetching service list: {0}", d.HostEndPoint);
|
|
|
+ OnDeviceFound(new DeviceEventArgs(d));
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
- NatUtility.Log("Unhandled exception when trying to decode a device's response Send me the following data: ");
|
|
|
- NatUtility.Log("ErrorMessage:");
|
|
|
- NatUtility.Log(ex.Message);
|
|
|
+ _logger.ErrorException("Error decoding device response", ex);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public void Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint)
|
|
|
{
|
|
|
- // Convert it to a string for easy parsing
|
|
|
- string dataString = null;
|
|
|
-
|
|
|
- // No matter what, this method should never throw an exception. If something goes wrong
|
|
|
- // we should still be in a position to handle the next reply correctly.
|
|
|
- try {
|
|
|
- string urn;
|
|
|
- dataString = Encoding.UTF8.GetString(response);
|
|
|
-
|
|
|
- if (NatUtility.Verbose)
|
|
|
- NatUtility.Log("UPnP Response: {0}", dataString);
|
|
|
-
|
|
|
- /* For UPnP Port Mapping we need ot find either WANPPPConnection or WANIPConnection.
|
|
|
- Any other device type is no good to us for this purpose. See the IGP overview paper
|
|
|
- page 5 for an overview of device types and their hierarchy.
|
|
|
- http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf */
|
|
|
-
|
|
|
- /* TODO: Currently we are assuming version 1 of the protocol. We should figure out which
|
|
|
- version it is and apply the correct URN. */
|
|
|
-
|
|
|
- /* Some routers don't correctly implement the version ID on the URN, so we only search for the type
|
|
|
- prefix. */
|
|
|
-
|
|
|
- string log = "UPnP Response: Router advertised a '{0}' service";
|
|
|
- StringComparison c = StringComparison.OrdinalIgnoreCase;
|
|
|
- if (dataString.IndexOf("urn:schemas-upnp-org:service:WANIPConnection:", c) != -1) {
|
|
|
- urn = "urn:schemas-upnp-org:service:WANIPConnection:1";
|
|
|
- NatUtility.Log(log, "urn:schemas-upnp-org:service:WANIPConnection:1");
|
|
|
- } else if (dataString.IndexOf("urn:schemas-upnp-org:service:WANPPPConnection:", c) != -1) {
|
|
|
- urn = "urn:schemas-upnp-org:service:WANPPPConnection:1";
|
|
|
- NatUtility.Log(log, "urn:schemas-upnp-org:service:WANPPPConnection:");
|
|
|
- } else
|
|
|
- return;
|
|
|
-
|
|
|
- // We have an internet gateway device now
|
|
|
- UpnpNatDevice d = new UpnpNatDevice(localAddress, dataString, urn);
|
|
|
-
|
|
|
- if (devices.Contains(d))
|
|
|
- {
|
|
|
- // We already have found this device, so we just refresh it to let people know it's
|
|
|
- // Still alive. If a device doesn't respond to a search, we dump it.
|
|
|
- devices[devices.IndexOf(d)].LastSeen = DateTime.Now;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
-
|
|
|
- // If we send 3 requests at a time, ensure we only fetch the services list once
|
|
|
- // even if three responses are received
|
|
|
- if (lastFetched.ContainsKey(endpoint.Address))
|
|
|
- {
|
|
|
- DateTime last = lastFetched[endpoint.Address];
|
|
|
- if ((DateTime.Now - last) < TimeSpan.FromSeconds(20))
|
|
|
- return;
|
|
|
- }
|
|
|
- lastFetched[endpoint.Address] = DateTime.Now;
|
|
|
-
|
|
|
- // Once we've parsed the information we need, we tell the device to retrieve it's service list
|
|
|
- // Once we successfully receive the service list, the callback provided will be invoked.
|
|
|
- NatUtility.Log("Fetching service list: {0}", d.HostEndPoint);
|
|
|
- d.GetServicesList(DeviceSetupComplete);
|
|
|
- }
|
|
|
- }
|
|
|
- catch (Exception ex)
|
|
|
- {
|
|
|
- Trace.WriteLine("Unhandled exception when trying to decode a device's response Send me the following data: ");
|
|
|
- Trace.WriteLine("ErrorMessage:");
|
|
|
- Trace.WriteLine(ex.Message);
|
|
|
- Trace.WriteLine("Data string:");
|
|
|
- Trace.WriteLine(dataString);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
public DateTime NextSearch
|
|
@@ -259,20 +96,6 @@ namespace Mono.Nat
|
|
|
get { return nextSearch; }
|
|
|
}
|
|
|
|
|
|
- private void DeviceSetupComplete(INatDevice device)
|
|
|
- {
|
|
|
- lock (this.devices)
|
|
|
- {
|
|
|
- // We don't want the same device in there twice
|
|
|
- if (devices.Contains(device))
|
|
|
- return;
|
|
|
-
|
|
|
- devices.Add(device);
|
|
|
- }
|
|
|
-
|
|
|
- OnDeviceFound(new DeviceEventArgs(device));
|
|
|
- }
|
|
|
-
|
|
|
private void OnDeviceFound(DeviceEventArgs args)
|
|
|
{
|
|
|
if (DeviceFound != null)
|