// // Authors: // Ben Motmans // Nicholas Terry // // Copyright (C) 2007 Ben Motmans // Copyright (C) 2014 Nicholas Terry // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Net; using System.Net.Sockets; using System.Threading; using System.Collections.Generic; using System.IO; using System.Net.NetworkInformation; using System.Threading.Tasks; using MediaBrowser.Common.Net; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Logging; namespace Mono.Nat { public static class NatUtility { public static event EventHandler DeviceFound; public static event EventHandler DeviceLost; private static List controllers; private static bool verbose; public static List EnabledProtocols { get; set; } public static ILogger Logger { get; set; } public static IHttpClient HttpClient { get; set; } public static bool Verbose { get { return verbose; } set { verbose = value; } } static NatUtility() { EnabledProtocols = new List { NatProtocol.Pmp }; controllers = new List(); controllers.Add(PmpSearcher.Instance); controllers.ForEach(searcher => { searcher.DeviceFound += (sender, args) => { if (DeviceFound != null) DeviceFound(sender, args); }; searcher.DeviceLost += (sender, args) => { if (DeviceLost != null) DeviceLost(sender, args); }; }); } internal static void Log(string format, params object[] args) { var logger = Logger; if (logger != null) logger.Debug(format, args); } private static async Task SearchAndListen(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { if (EnabledProtocols.Contains(PmpSearcher.Instance.Protocol)) { await Receive(PmpSearcher.Instance, PmpSearcher.sockets).ConfigureAwait(false); } foreach (ISearcher s in controllers) { if (s.NextSearch < DateTime.Now && EnabledProtocols.Contains(s.Protocol)) { Log("Searching for: {0}", s.GetType().Name); s.Search(); } } } catch (Exception e) { } await Task.Delay(100).ConfigureAwait(false); } } static async Task Receive(ISearcher searcher, List clients) { foreach (UdpClient client in clients) { if (client.Available > 0) { IPAddress localAddress = ((IPEndPoint)client.Client.LocalEndPoint).Address; var result = await client.ReceiveAsync().ConfigureAwait(false); var data = result.Buffer; var received = result.RemoteEndPoint; searcher.Handle(localAddress, data, received); } } } private static CancellationTokenSource _currentCancellationTokenSource; private static object _runSyncLock = new object(); public static void StartDiscovery() { lock (_runSyncLock) { if (_currentCancellationTokenSource == null) { return; } var tokenSource = new CancellationTokenSource(); _currentCancellationTokenSource = tokenSource; //Task.Factory.StartNew(() => SearchAndListen(tokenSource.Token), tokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); } } public static void StopDiscovery() { lock (_runSyncLock) { var tokenSource = _currentCancellationTokenSource; if (tokenSource != null) { try { tokenSource.Cancel(); tokenSource.Dispose(); } catch { } _currentCancellationTokenSource = null; } } } //checks if an IP address is a private address space as defined by RFC 1918 public static bool IsPrivateAddressSpace(IPAddress address) { byte[] ba = address.GetAddressBytes(); switch ((int)ba[0]) { case 10: return true; //10.x.x.x case 172: return ((int)ba[1] & 16) != 0; //172.16-31.x.x case 192: return (int)ba[1] == 168; //192.168.x.x default: return false; } } public static void Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint, NatProtocol protocol) { switch (protocol) { case NatProtocol.Upnp: //UpnpSearcher.Instance.Handle(localAddress, response, endpoint); break; case NatProtocol.Pmp: PmpSearcher.Instance.Handle(localAddress, response, endpoint); break; default: throw new ArgumentException("Unexpected protocol: " + protocol); } } public static void Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint, NatProtocol protocol) { switch (protocol) { case NatProtocol.Upnp: var searcher = new UpnpSearcher(Logger, HttpClient); searcher.DeviceFound += Searcher_DeviceFound; searcher.Handle(localAddress, deviceInfo, endpoint); break; default: throw new ArgumentException("Unexpected protocol: " + protocol); } } private static void Searcher_DeviceFound(object sender, DeviceEventArgs e) { if (DeviceFound != null) { DeviceFound(sender, e); } } } }