lighten up nat project

This commit is contained in:
Luke Pulverenti 2016-10-27 19:53:57 -04:00
parent 0c663a99dc
commit 8e57296f69
7 changed files with 41 additions and 262 deletions

View file

@ -25,11 +25,9 @@
// //
using System; using System;
using System.Security.Permissions;
namespace Mono.Nat namespace Mono.Nat
{ {
[Serializable]
public class MappingException : Exception public class MappingException : Exception
{ {
private int errorCode; private int errorCode;
@ -45,7 +43,6 @@ namespace Mono.Nat
get { return this.errorText; } get { return this.errorText; }
} }
#region Constructors
public MappingException() public MappingException()
: base() : base()
{ {
@ -67,21 +64,5 @@ namespace Mono.Nat
: base(message, innerException) : base(message, innerException)
{ {
} }
protected MappingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
: base(info, context)
{
}
#endregion
[SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)]
public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
{
if(info==null) throw new ArgumentNullException("info");
this.errorCode = info.GetInt32("errorCode");
this.errorText = info.GetString("errorText");
base.GetObjectData(info, context);
}
} }
} }

View file

@ -66,14 +66,12 @@ namespace Mono.Nat
{ {
EnabledProtocols = new List<NatProtocol> EnabledProtocols = new List<NatProtocol>
{ {
NatProtocol.Upnp,
NatProtocol.Pmp NatProtocol.Pmp
}; };
searching = new ManualResetEvent(false); searching = new ManualResetEvent(false);
controllers = new List<ISearcher>(); controllers = new List<ISearcher>();
controllers.Add(UpnpSearcher.Instance);
controllers.Add(PmpSearcher.Instance); controllers.Add(PmpSearcher.Instance);
controllers.ForEach(searcher => controllers.ForEach(searcher =>
@ -111,10 +109,6 @@ namespace Mono.Nat
{ {
var enabledProtocols = EnabledProtocols.ToList(); var enabledProtocols = EnabledProtocols.ToList();
if (enabledProtocols.Contains(UpnpSearcher.Instance.Protocol))
{
Receive(UpnpSearcher.Instance, UpnpSearcher.sockets);
}
if (enabledProtocols.Contains(PmpSearcher.Instance.Protocol)) if (enabledProtocols.Contains(PmpSearcher.Instance.Protocol))
{ {
Receive(PmpSearcher.Instance, PmpSearcher.sockets); Receive(PmpSearcher.Instance, PmpSearcher.sockets);
@ -150,20 +144,6 @@ namespace Mono.Nat
} }
} }
static void Receive(IMapper mapper, List<UdpClient> 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 () public static void StartDiscovery ()
{ {
searching.Set(); searching.Set();
@ -184,7 +164,7 @@ namespace Mono.Nat
mapper = new PmpMapper(); mapper = new PmpMapper();
break; break;
case MapperType.Upnp: case MapperType.Upnp:
mapper = new UpnpMapper(); mapper = new UpnpMapper(Logger);
mapper.DeviceFound += (sender, args) => mapper.DeviceFound += (sender, args) =>
{ {
if (DeviceFound != null) if (DeviceFound != null)
@ -200,23 +180,6 @@ namespace Mono.Nat
} }
//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<IPAddress> addresses = new List<IPAddress> ();
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 //checks if an IP address is a private address space as defined by RFC 1918
public static bool IsPrivateAddressSpace (IPAddress address) public static bool IsPrivateAddressSpace (IPAddress address)
{ {
@ -239,7 +202,7 @@ namespace Mono.Nat
switch (protocol) switch (protocol)
{ {
case NatProtocol.Upnp: case NatProtocol.Upnp:
UpnpSearcher.Instance.Handle(localAddress, response, endpoint); //UpnpSearcher.Instance.Handle(localAddress, response, endpoint);
break; break;
case NatProtocol.Pmp: case NatProtocol.Pmp:
PmpSearcher.Instance.Handle(localAddress, response, endpoint); PmpSearcher.Instance.Handle(localAddress, response, endpoint);
@ -254,7 +217,7 @@ namespace Mono.Nat
switch (protocol) switch (protocol)
{ {
case NatProtocol.Upnp: case NatProtocol.Upnp:
UpnpSearcher.Instance.Handle(localAddress, deviceInfo, endpoint); new UpnpSearcher(Logger).Handle(localAddress, deviceInfo, endpoint);
break; break;
default: default:
throw new ArgumentException("Unexpected protocol: " + protocol); throw new ArgumentException("Unexpected protocol: " + protocol);

View file

@ -32,6 +32,7 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using MediaBrowser.Model.Logging;
namespace Mono.Nat.Upnp.Mappers namespace Mono.Nat.Upnp.Mappers
{ {
@ -42,7 +43,8 @@ namespace Mono.Nat.Upnp.Mappers
public UdpClient Client { get; set; } public UdpClient Client { get; set; }
public UpnpMapper() public UpnpMapper(ILogger logger)
: base(logger)
{ {
//Bind to local port 1900 for ssdp responses //Bind to local port 1900 for ssdp responses
Client = new UdpClient(1900); Client = new UdpClient(1900);
@ -88,11 +90,7 @@ namespace Mono.Nat.Upnp.Mappers
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine("Unhandled exception when trying to decode a device's response Send me the following data: "); Logger.ErrorException("Error mapping port. Data string: {0}", ex, Encoding.UTF8.GetString(response));
Trace.WriteLine("ErrorMessage:");
Trace.WriteLine(ex.Message);
Trace.WriteLine("Data string:");
Trace.WriteLine(Encoding.UTF8.GetString(response));
} }
} }

View file

@ -27,6 +27,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Net; using System.Net;
using MediaBrowser.Model.Logging;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
@ -34,18 +35,20 @@ namespace Mono.Nat.Upnp
{ {
private string servicesDescriptionUrl; private string servicesDescriptionUrl;
private EndPoint hostAddress; private EndPoint hostAddress;
private readonly ILogger _logger;
public GetServicesMessage(string description, EndPoint hostAddress) public GetServicesMessage(string description, EndPoint hostAddress, ILogger logger)
:base(null) :base(null)
{ {
if (string.IsNullOrEmpty(description)) if (string.IsNullOrEmpty(description))
Trace.WriteLine("Description is null"); _logger.Warn("Description is null");
if (hostAddress == null) if (hostAddress == null)
Trace.WriteLine("hostaddress is null"); _logger.Warn("hostaddress is null");
this.servicesDescriptionUrl = description; this.servicesDescriptionUrl = description;
this.hostAddress = hostAddress; this.hostAddress = hostAddress;
_logger = logger;
} }

View file

@ -37,96 +37,27 @@ using System.Diagnostics;
using System.Net.Sockets; using System.Net.Sockets;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Logging;
namespace Mono.Nat namespace Mono.Nat
{ {
internal class UpnpSearcher : ISearcher 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> DeviceFound;
public event EventHandler<DeviceEventArgs> DeviceLost; public event EventHandler<DeviceEventArgs> DeviceLost;
private List<INatDevice> devices;
private Dictionary<IPAddress, DateTime> lastFetched;
private DateTime nextSearch; private DateTime nextSearch;
private IPEndPoint searchEndpoint; private readonly ILogger _logger;
UpnpSearcher() public UpnpSearcher(ILogger logger)
{ {
devices = new List<INatDevice>(); _logger = logger;
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);
} }
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() 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) public void Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint)
{ {
// No matter what, this method should never throw an exception. If something goes wrong // No matter what, this method should never throw an exception. If something goes wrong
@ -145,113 +76,19 @@ namespace Mono.Nat
prefix. */ prefix. */
// We have an internet gateway device now // 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)) NatUtility.Log("Fetching service list: {0}", d.HostEndPoint);
{ OnDeviceFound(new DeviceEventArgs(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) catch (Exception ex)
{ {
NatUtility.Log("Unhandled exception when trying to decode a device's response Send me the following data: "); _logger.ErrorException("Error decoding device response", ex);
NatUtility.Log("ErrorMessage:");
NatUtility.Log(ex.Message);
} }
} }
public void Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint) 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 public DateTime NextSearch
@ -259,20 +96,6 @@ namespace Mono.Nat
get { return nextSearch; } 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) private void OnDeviceFound(DeviceEventArgs args)
{ {
if (DeviceFound != null) if (DeviceFound != null)

View file

@ -33,11 +33,19 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text; using System.Text;
using MediaBrowser.Model.Logging;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
internal class Upnp internal class Upnp
{ {
protected readonly ILogger Logger;
public Upnp(ILogger logger)
{
Logger = logger;
}
public UpnpNatDevice Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint) public UpnpNatDevice Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint)
{ {
// Convert it to a string for easy parsing // Convert it to a string for easy parsing
@ -77,7 +85,7 @@ namespace Mono.Nat.Upnp
throw new NotSupportedException("Received non-supported device type"); throw new NotSupportedException("Received non-supported device type");
// We have an internet gateway device now // We have an internet gateway device now
return new UpnpNatDevice(localAddress, dataString, urn); return new UpnpNatDevice(localAddress, dataString, urn, Logger);
} }
} }
} }

View file

@ -33,6 +33,7 @@ using System.Xml;
using System.Text; using System.Text;
using System.Diagnostics; using System.Diagnostics;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Logging;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
@ -43,8 +44,9 @@ namespace Mono.Nat.Upnp
private string serviceDescriptionUrl; private string serviceDescriptionUrl;
private string controlUrl; private string controlUrl;
private string serviceType; private string serviceType;
private readonly ILogger _logger;
public override IPAddress LocalAddress public override IPAddress LocalAddress
{ {
get { return localAddress; } get { return localAddress; }
} }
@ -54,7 +56,7 @@ namespace Mono.Nat.Upnp
/// </summary> /// </summary>
private NatDeviceCallback callback; private NatDeviceCallback callback;
internal UpnpNatDevice(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint hostEndPoint, string serviceType) internal UpnpNatDevice(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint hostEndPoint, string serviceType, ILogger logger)
{ {
this.LastSeen = DateTime.Now; this.LastSeen = DateTime.Now;
this.localAddress = localAddress; this.localAddress = localAddress;
@ -62,6 +64,7 @@ namespace Mono.Nat.Upnp
// Split the string at the "location" section so i can extract the ipaddress and service description url // Split the string at the "location" section so i can extract the ipaddress and service description url
string locationDetails = deviceInfo.Location.ToString(); string locationDetails = deviceInfo.Location.ToString();
this.serviceType = serviceType; this.serviceType = serviceType;
_logger = logger;
// Make sure we have no excess whitespace // Make sure we have no excess whitespace
locationDetails = locationDetails.Trim(); locationDetails = locationDetails.Trim();
@ -88,16 +91,17 @@ namespace Mono.Nat.Upnp
} }
} }
internal UpnpNatDevice (IPAddress localAddress, string deviceDetails, string serviceType) internal UpnpNatDevice (IPAddress localAddress, string deviceDetails, string serviceType, ILogger logger)
{ {
this.LastSeen = DateTime.Now; _logger = logger;
this.LastSeen = DateTime.Now;
this.localAddress = localAddress; this.localAddress = localAddress;
// Split the string at the "location" section so i can extract the ipaddress and service description url // Split the string at the "location" section so i can extract the ipaddress and service description url
string locationDetails = deviceDetails.Substring(deviceDetails.IndexOf("Location", StringComparison.InvariantCultureIgnoreCase) + 9).Split('\r')[0]; string locationDetails = deviceDetails.Substring(deviceDetails.IndexOf("Location", StringComparison.InvariantCultureIgnoreCase) + 9).Split('\r')[0];
this.serviceType = serviceType; this.serviceType = serviceType;
// Make sure we have no excess whitespace // Make sure we have no excess whitespace
locationDetails = locationDetails.Trim(); locationDetails = locationDetails.Trim();
// FIXME: Is this reliable enough. What if we get a hostname as opposed to a proper http address // FIXME: Is this reliable enough. What if we get a hostname as opposed to a proper http address
@ -131,8 +135,7 @@ namespace Mono.Nat.Upnp
} }
else else
{ {
Trace.WriteLine("Couldn't decode address. Please send following string to the developer: "); logger.Warn("Couldn't decode address: " + deviceDetails);
Trace.WriteLine(deviceDetails);
} }
} }
@ -527,7 +530,7 @@ namespace Mono.Nat.Upnp
// Create a HTTPWebRequest to download the list of services the device offers // Create a HTTPWebRequest to download the list of services the device offers
byte[] body; byte[] body;
WebRequest request = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint).Encode(out body); WebRequest request = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint, _logger).Encode(out body);
if (body.Length > 0) if (body.Length > 0)
NatUtility.Log("Error: Services Message contained a body"); NatUtility.Log("Error: Services Message contained a body");
request.BeginGetResponse(this.ServicesReceived, request); request.BeginGetResponse(this.ServicesReceived, request);