// // 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.Linq; using System.Collections.Generic; using System.IO; using System.Net.NetworkInformation; using System.Threading.Tasks; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Dlna; using MediaBrowser.Model.Logging; namespace Mono.Nat { public static class NatUtility { private static ManualResetEvent searching; 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 }; searching = new ManualResetEvent(false); 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); }; }); Task.Factory.StartNew(SearchAndListen, TaskCreationOptions.LongRunning); } internal static void Log(string format, params object[] args) { var logger = Logger; if (logger != null) logger.Debug(format, args); } private static async Task SearchAndListen() { while (true) { searching.WaitOne(); try { var enabledProtocols = EnabledProtocols.ToList(); if (enabledProtocols.Contains(PmpSearcher.Instance.Protocol)) { Receive(PmpSearcher.Instance, PmpSearcher.sockets); } 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 void Receive (ISearcher searcher, List clients) { IPEndPoint received = new IPEndPoint(IPAddress.Parse("192.168.0.1"), 5351); foreach (UdpClient client in clients) { if (client.Available > 0) { IPAddress localAddress = ((IPEndPoint)client.Client.LocalEndPoint).Address; byte[] data = client.Receive(ref received); searcher.Handle(localAddress, data, received); } } } public static void StartDiscovery () { searching.Set(); } public static void StopDiscovery () { searching.Reset(); } //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); } } } }