// // 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 MediaBrowser.Controller.Dlna; using MediaBrowser.Model.Logging; using Mono.Nat.Pmp.Mappers; using Mono.Nat.Upnp.Mappers; namespace Mono.Nat { public static class NatUtility { private static ManualResetEvent searching; public static event EventHandler DeviceFound; public static event EventHandler DeviceLost; public static event EventHandler UnhandledException; private static List controllers; private static bool verbose; public static List EnabledProtocols { get; set; } public static ILogger Logger { get; set; } public static bool Verbose { get { return verbose; } set { verbose = value; } } static NatUtility() { EnabledProtocols = new List { NatProtocol.Upnp, NatProtocol.Pmp }; searching = new ManualResetEvent(false); controllers = new List(); controllers.Add(UpnpSearcher.Instance); 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); }; }); Thread t = new Thread(SearchAndListen); t.IsBackground = true; t.Start(); } internal static void Log(string format, params object[] args) { var logger = Logger; if (logger != null) logger.Debug(format, args); } private static void SearchAndListen() { while (true) { searching.WaitOne(); try { var enabledProtocols = EnabledProtocols.ToList(); if (enabledProtocols.Contains(UpnpSearcher.Instance.Protocol)) { Receive(UpnpSearcher.Instance, UpnpSearcher.sockets); } 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) { if (UnhandledException != null) UnhandledException(typeof(NatUtility), new UnhandledExceptionEventArgs(e, false)); } Thread.Sleep(10); } } 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); } } } static void Receive(IMapper mapper, 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); mapper.Handle(localAddress, data); } } } public static void StartDiscovery () { searching.Set(); } public static void StopDiscovery () { searching.Reset(); } //This is for when you know the Gateway IP and want to skip the costly search... public static void DirectMap(IPAddress gatewayAddress, MapperType type) { IMapper mapper; switch (type) { case MapperType.Pmp: mapper = new PmpMapper(); break; case MapperType.Upnp: mapper = new UpnpMapper(); mapper.DeviceFound += (sender, args) => { if (DeviceFound != null) DeviceFound(sender, args); }; mapper.Map(gatewayAddress); break; default: throw new InvalidOperationException("Unsuported type given"); } searching.Reset(); } //So then why is it here? -Nick [Obsolete ("This method serves no purpose and shouldn't be used")] public static IPAddress[] GetLocalAddresses (bool includeIPv6) { List addresses = new List (); IPHostEntry hostInfo = Dns.GetHostEntry (Dns.GetHostName ()); foreach (IPAddress address in hostInfo.AddressList) { if (address.AddressFamily == AddressFamily.InterNetwork || (includeIPv6 && address.AddressFamily == AddressFamily.InterNetworkV6)) { addresses.Add (address); } } return addresses.ToArray (); } //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: UpnpSearcher.Instance.Handle(localAddress, deviceInfo, endpoint); break; default: throw new ArgumentException("Unexpected protocol: " + protocol); } } } }