update port mapper

This commit is contained in:
Luke Pulverenti 2016-10-27 21:07:40 -04:00
parent 8e57296f69
commit ce043225c4
25 changed files with 773 additions and 665 deletions

View file

@ -8,6 +8,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Net; using System.Net;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Server.Implementations.Threading; using MediaBrowser.Server.Implementations.Threading;
@ -17,18 +18,20 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
{ {
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IDeviceDiscovery _deviceDiscovery; private readonly IDeviceDiscovery _deviceDiscovery;
private PeriodicTimer _timer; private PeriodicTimer _timer;
private bool _isStarted; private bool _isStarted;
public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery) public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient)
{ {
_logger = logmanager.GetLogger("PortMapper"); _logger = logmanager.GetLogger("PortMapper");
_appHost = appHost; _appHost = appHost;
_config = config; _config = config;
_deviceDiscovery = deviceDiscovery; _deviceDiscovery = deviceDiscovery;
_httpClient = httpClient;
} }
private string _lastConfigIdentifier; private string _lastConfigIdentifier;
@ -63,6 +66,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
public void Run() public void Run()
{ {
NatUtility.Logger = _logger; NatUtility.Logger = _logger;
NatUtility.HttpClient = _httpClient;
if (_config.Configuration.EnableUPnP) if (_config.Configuration.EnableUPnP)
{ {
@ -136,7 +140,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
_usnsHandled.Add(identifier); _usnsHandled.Add(identifier);
} }
_logger.Debug("Calling Nat.Handle on " + identifier); _logger.Debug("Found NAT device: " + identifier);
IPAddress address; IPAddress address;
if (IPAddress.TryParse(info.Location.Host, out address)) if (IPAddress.TryParse(info.Location.Host, out address))
@ -150,16 +154,23 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
{ {
var localAddressString = await _appHost.GetLocalApiUrl().ConfigureAwait(false); var localAddressString = await _appHost.GetLocalApiUrl().ConfigureAwait(false);
if (!IPAddress.TryParse(localAddressString, out localAddress)) Uri uri;
if (Uri.TryCreate(localAddressString, UriKind.Absolute, out uri))
{ {
return; localAddressString = uri.Host;
if (!IPAddress.TryParse(localAddressString, out localAddress))
{
return;
}
} }
} }
catch catch (Exception ex)
{ {
return; return;
} }
_logger.Debug("Calling Nat.Handle on " + identifier);
NatUtility.Handle(localAddress, info, endpoint, NatProtocol.Upnp); NatUtility.Handle(localAddress, info, endpoint, NatProtocol.Upnp);
} }
} }
@ -229,13 +240,21 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
} }
} }
private void CreatePortMap(INatDevice device, int privatePort, int publicPort) private async void CreatePortMap(INatDevice device, int privatePort, int publicPort)
{ {
_logger.Debug("Creating port map on port {0}", privatePort); _logger.Debug("Creating port map on port {0}", privatePort);
device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
try
{ {
Description = _appHost.Name await device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
}); {
Description = _appHost.Name
}).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error creating port map", ex);
}
} }
// As I said before, this method will be never invoked. You can remove it. // As I said before, this method will be never invoked. You can remove it.

View file

@ -30,6 +30,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Net; using System.Net;
using System.Threading.Tasks;
namespace Mono.Nat namespace Mono.Nat
{ {
@ -50,11 +51,7 @@ namespace Mono.Nat
set { lastSeen = value; } set { lastSeen = value; }
} }
public virtual void CreatePortMap (Mapping mapping) public abstract Task CreatePortMap(Mapping mapping);
{
IAsyncResult result = BeginCreatePortMap (mapping, null, null);
EndCreatePortMap(result);
}
public virtual void DeletePortMap (Mapping mapping) public virtual void DeletePortMap (Mapping mapping)
{ {

View file

@ -30,12 +30,13 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Net; using System.Net;
using System.Threading.Tasks;
namespace Mono.Nat namespace Mono.Nat
{ {
public interface INatDevice public interface INatDevice
{ {
void CreatePortMap (Mapping mapping); Task CreatePortMap (Mapping mapping);
void DeletePortMap (Mapping mapping); void DeletePortMap (Mapping mapping);
IPAddress LocalAddress { get; } IPAddress LocalAddress { get; }

View file

@ -54,7 +54,6 @@
<Compile Include="NatUtility.cs" /> <Compile Include="NatUtility.cs" />
<Compile Include="Pmp\AsyncResults\PortMapAsyncResult.cs" /> <Compile Include="Pmp\AsyncResults\PortMapAsyncResult.cs" />
<Compile Include="Pmp\Mappers\PmpMapper.cs" /> <Compile Include="Pmp\Mappers\PmpMapper.cs" />
<Compile Include="Pmp\Pmp.cs" />
<Compile Include="Pmp\PmpConstants.cs" /> <Compile Include="Pmp\PmpConstants.cs" />
<Compile Include="Pmp\PmpNatDevice.cs" /> <Compile Include="Pmp\PmpNatDevice.cs" />
<Compile Include="Pmp\Searchers\PmpSearcher.cs" /> <Compile Include="Pmp\Searchers\PmpSearcher.cs" />
@ -80,6 +79,10 @@
<Compile Include="Upnp\UpnpNatDevice.cs" /> <Compile Include="Upnp\UpnpNatDevice.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
<Name>MediaBrowser.Common</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj"> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project> <Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
<Name>MediaBrowser.Controller</Name> <Name>MediaBrowser.Controller</Name>

View file

@ -34,9 +34,11 @@ using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using Mono.Nat.Pmp.Mappers; using Mono.Nat.Pmp.Mappers;
using Mono.Nat.Upnp;
using Mono.Nat.Upnp.Mappers; using Mono.Nat.Upnp.Mappers;
namespace Mono.Nat namespace Mono.Nat
@ -55,8 +57,9 @@ namespace Mono.Nat
public static List<NatProtocol> EnabledProtocols { get; set; } public static List<NatProtocol> EnabledProtocols { get; set; }
public static ILogger Logger { get; set; } public static ILogger Logger { get; set; }
public static IHttpClient HttpClient { get; set; }
public static bool Verbose public static bool Verbose
{ {
get { return verbose; } get { return verbose; }
set { verbose = value; } set { verbose = value; }
@ -153,32 +156,6 @@ namespace Mono.Nat
{ {
searching.Reset(); 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(Logger);
mapper.DeviceFound += (sender, args) =>
{
if (DeviceFound != null)
DeviceFound(sender, args);
};
mapper.Map(gatewayAddress);
break;
default:
throw new InvalidOperationException("Unsuported type given");
}
searching.Reset();
}
//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)
@ -217,11 +194,21 @@ namespace Mono.Nat
switch (protocol) switch (protocol)
{ {
case NatProtocol.Upnp: case NatProtocol.Upnp:
new UpnpSearcher(Logger).Handle(localAddress, deviceInfo, endpoint); var searcher = new UpnpSearcher(Logger, HttpClient);
searcher.DeviceFound += Searcher_DeviceFound;
searcher.Handle(localAddress, deviceInfo, endpoint);
break; break;
default: default:
throw new ArgumentException("Unexpected protocol: " + protocol); throw new ArgumentException("Unexpected protocol: " + protocol);
} }
} }
private static void Searcher_DeviceFound(object sender, DeviceEventArgs e)
{
if (DeviceFound != null)
{
DeviceFound(sender, e);
}
}
} }
} }

View file

@ -34,26 +34,12 @@ using Mono.Nat.Pmp;
namespace Mono.Nat.Pmp.Mappers namespace Mono.Nat.Pmp.Mappers
{ {
internal class PmpMapper : Pmp, IMapper internal class PmpMapper : IMapper
{ {
public event EventHandler<DeviceEventArgs> DeviceFound; public event EventHandler<DeviceEventArgs> DeviceFound;
static PmpMapper()
{
CreateSocketsAndAddGateways();
}
public void Map(IPAddress gatewayAddress) public void Map(IPAddress gatewayAddress)
{ {
sockets.ForEach(x => Map(x, gatewayAddress));
}
void Map(UdpClient client, IPAddress gatewayAddress)
{
// The nat-pmp search message. Must be sent to GatewayIP:53531
byte[] buffer = new byte[] { PmpConstants.Version, PmpConstants.OperationCode };
client.Send(buffer, buffer.Length, new IPEndPoint(gatewayAddress, PmpConstants.ServerPort));
} }
public void Handle(IPAddress localAddres, byte[] response) public void Handle(IPAddress localAddres, byte[] response)

View file

@ -1,118 +0,0 @@
//
// Authors:
// Ben Motmans <ben.motmans@gmail.com>
// Nicholas Terry <nick.i.terry@gmail.com>
//
// 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.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
namespace Mono.Nat.Pmp
{
internal abstract class Pmp
{
public static List<UdpClient> sockets;
protected static Dictionary<UdpClient, List<IPEndPoint>> gatewayLists;
internal static void CreateSocketsAndAddGateways()
{
sockets = new List<UdpClient>();
gatewayLists = new Dictionary<UdpClient, List<IPEndPoint>>();
try
{
foreach (NetworkInterface n in NetworkInterface.GetAllNetworkInterfaces())
{
if (n.OperationalStatus != OperationalStatus.Up && n.OperationalStatus != OperationalStatus.Unknown)
continue;
IPInterfaceProperties properties = n.GetIPProperties();
List<IPEndPoint> gatewayList = new List<IPEndPoint>();
foreach (GatewayIPAddressInformation gateway in properties.GatewayAddresses)
{
if (gateway.Address.AddressFamily == AddressFamily.InterNetwork)
{
gatewayList.Add(new IPEndPoint(gateway.Address, PmpConstants.ServerPort));
}
}
if (gatewayList.Count == 0)
{
/* Mono on OSX doesn't give any gateway addresses, so check DNS entries */
foreach (var gw2 in properties.DnsAddresses)
{
if (gw2.AddressFamily == AddressFamily.InterNetwork)
{
gatewayList.Add(new IPEndPoint(gw2, PmpConstants.ServerPort));
}
}
foreach (var unicast in properties.UnicastAddresses)
{
if (/*unicast.DuplicateAddressDetectionState == DuplicateAddressDetectionState.Preferred
&& unicast.AddressPreferredLifetime != UInt32.MaxValue
&& */unicast.Address.AddressFamily == AddressFamily.InterNetwork)
{
var bytes = unicast.Address.GetAddressBytes();
bytes[3] = 1;
gatewayList.Add(new IPEndPoint(new IPAddress(bytes), PmpConstants.ServerPort));
}
}
}
if (gatewayList.Count > 0)
{
foreach (UnicastIPAddressInformation address in properties.UnicastAddresses)
{
if (address.Address.AddressFamily == AddressFamily.InterNetwork)
{
UdpClient client;
try
{
client = new UdpClient(new IPEndPoint(address.Address, 0));
}
catch (SocketException)
{
continue; // Move on to the next address.
}
gatewayLists.Add(client, gatewayList);
sockets.Add(client);
}
}
}
}
}
catch (Exception)
{
// NAT-PMP does not use multicast, so there isn't really a good fallback.
}
}
}
}

View file

@ -30,6 +30,7 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
namespace Mono.Nat.Pmp namespace Mono.Nat.Pmp
{ {
@ -56,6 +57,12 @@ namespace Mono.Nat.Pmp
return publicAddress; return publicAddress;
} }
public override Task CreatePortMap(Mapping mapping)
{
CreatePortMap(mapping, true);
return Task.FromResult(true);
}
public override IAsyncResult BeginCreatePortMap(Mapping mapping, AsyncCallback callback, object asyncState) public override IAsyncResult BeginCreatePortMap(Mapping mapping, AsyncCallback callback, object asyncState)
{ {
PortMapAsyncResult pmar = new PortMapAsyncResult (mapping.Protocol, mapping.PublicPort, PmpConstants.DefaultLeaseTime, callback, asyncState); PortMapAsyncResult pmar = new PortMapAsyncResult (mapping.Protocol, mapping.PublicPort, PmpConstants.DefaultLeaseTime, callback, asyncState);

View file

@ -40,7 +40,7 @@ using System.Linq;
namespace Mono.Nat namespace Mono.Nat
{ {
internal class PmpSearcher : Pmp.Pmp, ISearcher internal class PmpSearcher : ISearcher
{ {
static PmpSearcher instance = new PmpSearcher(); static PmpSearcher instance = new PmpSearcher();
@ -60,6 +60,83 @@ namespace Mono.Nat
CreateSocketsAndAddGateways(); CreateSocketsAndAddGateways();
} }
public static List<UdpClient> sockets;
protected static Dictionary<UdpClient, List<IPEndPoint>> gatewayLists;
internal static void CreateSocketsAndAddGateways()
{
sockets = new List<UdpClient>();
gatewayLists = new Dictionary<UdpClient, List<IPEndPoint>>();
try
{
foreach (NetworkInterface n in NetworkInterface.GetAllNetworkInterfaces())
{
if (n.OperationalStatus != OperationalStatus.Up && n.OperationalStatus != OperationalStatus.Unknown)
continue;
IPInterfaceProperties properties = n.GetIPProperties();
List<IPEndPoint> gatewayList = new List<IPEndPoint>();
foreach (GatewayIPAddressInformation gateway in properties.GatewayAddresses)
{
if (gateway.Address.AddressFamily == AddressFamily.InterNetwork)
{
gatewayList.Add(new IPEndPoint(gateway.Address, PmpConstants.ServerPort));
}
}
if (gatewayList.Count == 0)
{
/* Mono on OSX doesn't give any gateway addresses, so check DNS entries */
foreach (var gw2 in properties.DnsAddresses)
{
if (gw2.AddressFamily == AddressFamily.InterNetwork)
{
gatewayList.Add(new IPEndPoint(gw2, PmpConstants.ServerPort));
}
}
foreach (var unicast in properties.UnicastAddresses)
{
if (/*unicast.DuplicateAddressDetectionState == DuplicateAddressDetectionState.Preferred
&& unicast.AddressPreferredLifetime != UInt32.MaxValue
&& */unicast.Address.AddressFamily == AddressFamily.InterNetwork)
{
var bytes = unicast.Address.GetAddressBytes();
bytes[3] = 1;
gatewayList.Add(new IPEndPoint(new IPAddress(bytes), PmpConstants.ServerPort));
}
}
}
if (gatewayList.Count > 0)
{
foreach (UnicastIPAddressInformation address in properties.UnicastAddresses)
{
if (address.Address.AddressFamily == AddressFamily.InterNetwork)
{
UdpClient client;
try
{
client = new UdpClient(new IPEndPoint(address.Address, 0));
}
catch (SocketException)
{
continue; // Move on to the next address.
}
gatewayLists.Add(client, gatewayList);
sockets.Add(client);
}
}
}
}
}
catch (Exception)
{
// NAT-PMP does not use multicast, so there isn't really a good fallback.
}
}
PmpSearcher() PmpSearcher()
{ {
timeout = 250; timeout = 250;

View file

@ -32,19 +32,20 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
namespace Mono.Nat.Upnp.Mappers namespace Mono.Nat.Upnp.Mappers
{ {
internal class UpnpMapper : Upnp, IMapper internal class UpnpMapper : Upnp, IMapper
{ {
public event EventHandler<DeviceEventArgs> DeviceFound; public event EventHandler<DeviceEventArgs> DeviceFound;
public UdpClient Client { get; set; } public UdpClient Client { get; set; }
public UpnpMapper(ILogger logger) public UpnpMapper(ILogger logger, IHttpClient httpClient)
: base(logger) : base(logger, httpClient)
{ {
//Bind to local port 1900 for ssdp responses //Bind to local port 1900 for ssdp responses
Client = new UdpClient(1900); Client = new UdpClient(1900);
@ -60,7 +61,7 @@ namespace Mono.Nat.Upnp.Mappers
new Thread(Receive).Start(); new Thread(Receive).Start();
} }
public void Receive() public async void Receive()
{ {
while (true) while (true)
{ {
@ -69,28 +70,36 @@ namespace Mono.Nat.Upnp.Mappers
{ {
IPAddress localAddress = ((IPEndPoint)Client.Client.LocalEndPoint).Address; IPAddress localAddress = ((IPEndPoint)Client.Client.LocalEndPoint).Address;
byte[] data = Client.Receive(ref received); byte[] data = Client.Receive(ref received);
Handle(localAddress, data, received);
await Handle(localAddress, data, received);
} }
} }
} }
public void Handle(IPAddress localAddres, byte[] response) public void Handle(IPAddress localAddres, byte[] response)
{ {
Handle(localAddres, response, null);
} }
public void Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint) public override async Task<UpnpNatDevice> Handle(IPAddress localAddress, byte[] response, 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
// we should still be in a position to handle the next reply correctly. // we should still be in a position to handle the next reply correctly.
try try
{ {
UpnpNatDevice d = base.Handle(localAddress, response, endpoint); var d = await base.Handle(localAddress, response, endpoint).ConfigureAwait(false);
d.GetServicesList(DeviceSetupComplete); var result = await d.GetServicesList().ConfigureAwait(false);
if (result)
{
DeviceSetupComplete(d);
}
return d;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.ErrorException("Error mapping port. Data string: {0}", ex, Encoding.UTF8.GetString(response)); Logger.ErrorException("Error mapping port. Data string: {0}", ex, Encoding.UTF8.GetString(response));
return null;
} }
} }

View file

@ -25,6 +25,7 @@
// //
using System; using System;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
@ -54,6 +55,10 @@ namespace Mono.Nat.Upnp
} }
#endregion #endregion
public override HttpRequestOptions Encode()
{
throw new NotImplementedException();
}
public override System.Net.WebRequest Encode(out byte[] body) public override System.Net.WebRequest Encode(out byte[] body)
{ {

View file

@ -27,6 +27,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Net; using System.Net;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
@ -38,7 +39,7 @@ namespace Mono.Nat.Upnp
private readonly ILogger _logger; private readonly ILogger _logger;
public GetServicesMessage(string description, EndPoint hostAddress, ILogger logger) public GetServicesMessage(string description, EndPoint hostAddress, ILogger logger)
:base(null) : base(null)
{ {
if (string.IsNullOrEmpty(description)) if (string.IsNullOrEmpty(description))
_logger.Warn("Description is null"); _logger.Warn("Description is null");
@ -51,6 +52,13 @@ namespace Mono.Nat.Upnp
_logger = logger; _logger = logger;
} }
public override string Method
{
get
{
return "GET";
}
}
public override WebRequest Encode(out byte[] body) public override WebRequest Encode(out byte[] body)
{ {
@ -61,5 +69,16 @@ namespace Mono.Nat.Upnp
body = new byte[0]; body = new byte[0];
return req; return req;
} }
public override HttpRequestOptions Encode()
{
var req = new HttpRequestOptions();
req.Url = "http://" + this.hostAddress.ToString() + this.servicesDescriptionUrl;
req.RequestHeaders.Add("ACCEPT-LANGUAGE", "en");
return req;
}
} }
} }

View file

@ -29,6 +29,7 @@ using System.IO;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
@ -51,6 +52,25 @@ namespace Mono.Nat.Upnp
} }
#endregion #endregion
public override HttpRequestOptions Encode()
{
CultureInfo culture = CultureInfo.InvariantCulture;
StringBuilder builder = new StringBuilder(256);
XmlWriter writer = CreateWriter(builder);
WriteFullElement(writer, "NewRemoteHost", string.Empty);
WriteFullElement(writer, "NewExternalPort", this.mapping.PublicPort.ToString(culture));
WriteFullElement(writer, "NewProtocol", this.mapping.Protocol == Protocol.Tcp ? "TCP" : "UDP");
WriteFullElement(writer, "NewInternalPort", this.mapping.PrivatePort.ToString(culture));
WriteFullElement(writer, "NewInternalClient", this.localIpAddress.ToString());
WriteFullElement(writer, "NewEnabled", "1");
WriteFullElement(writer, "NewPortMappingDescription", string.IsNullOrEmpty(mapping.Description) ? "Mono.Nat" : mapping.Description);
WriteFullElement(writer, "NewLeaseDuration", mapping.Lifetime.ToString());
writer.Flush();
return CreateRequest("AddPortMapping", builder.ToString());
}
public override WebRequest Encode(out byte[] body) public override WebRequest Encode(out byte[] body)
{ {

View file

@ -28,6 +28,7 @@ using System.Net;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
@ -41,7 +42,20 @@ namespace Mono.Nat.Upnp
this.mapping = mapping; this.mapping = mapping;
} }
public override WebRequest Encode(out byte[] body) public override HttpRequestOptions Encode()
{
StringBuilder builder = new StringBuilder(256);
XmlWriter writer = CreateWriter(builder);
WriteFullElement(writer, "NewRemoteHost", string.Empty);
WriteFullElement(writer, "NewExternalPort", mapping.PublicPort.ToString(MessageBase.Culture));
WriteFullElement(writer, "NewProtocol", mapping.Protocol == Protocol.Tcp ? "TCP" : "UDP");
writer.Flush();
return CreateRequest("DeletePortMapping", builder.ToString());
}
public override WebRequest Encode(out byte[] body)
{ {
StringBuilder builder = new StringBuilder(256); StringBuilder builder = new StringBuilder(256);
XmlWriter writer = CreateWriter(builder); XmlWriter writer = CreateWriter(builder);

View file

@ -29,6 +29,7 @@ using System.Collections.Generic;
using System.Text; using System.Text;
using System.Net; using System.Net;
using System.IO; using System.IO;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
@ -42,6 +43,10 @@ namespace Mono.Nat.Upnp
} }
#endregion #endregion
public override HttpRequestOptions Encode()
{
return CreateRequest("GetExternalIPAddress", string.Empty);
}
public override WebRequest Encode(out byte[] body) public override WebRequest Encode(out byte[] body)
{ {

View file

@ -28,6 +28,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
@ -41,6 +42,17 @@ namespace Mono.Nat.Upnp
this.index = index; this.index = index;
} }
public override HttpRequestOptions Encode()
{
StringBuilder sb = new StringBuilder(128);
XmlWriter writer = CreateWriter(sb);
WriteFullElement(writer, "NewPortMappingIndex", index.ToString());
writer.Flush();
return CreateRequest("GetGenericPortMappingEntry", sb.ToString());
}
public override System.Net.WebRequest Encode(out byte[] body) public override System.Net.WebRequest Encode(out byte[] body)
{ {
StringBuilder sb = new StringBuilder(128); StringBuilder sb = new StringBuilder(128);

View file

@ -29,6 +29,7 @@ using System.Collections.Generic;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using System.Net; using System.Net;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
@ -55,6 +56,19 @@ namespace Mono.Nat.Upnp
writer.Flush(); writer.Flush();
return CreateRequest("GetSpecificPortMappingEntry", sb.ToString(), out body); return CreateRequest("GetSpecificPortMappingEntry", sb.ToString(), out body);
} }
}
public override HttpRequestOptions Encode()
{
StringBuilder sb = new StringBuilder(64);
XmlWriter writer = CreateWriter(sb);
WriteFullElement(writer, "NewRemoteHost", string.Empty);
WriteFullElement(writer, "NewExternalPort", externalPort.ToString());
WriteFullElement(writer, "NewProtocol", protocol == Protocol.Tcp ? "TCP" : "UDP");
writer.Flush();
return CreateRequest("GetSpecificPortMappingEntry", sb.ToString());
}
}
} }

View file

@ -27,6 +27,8 @@
using System; using System;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
internal class CreatePortMappingResponseMessage : MessageBase internal class CreatePortMappingResponseMessage : MessageBase
@ -38,6 +40,11 @@ namespace Mono.Nat.Upnp
} }
#endregion #endregion
public override HttpRequestOptions Encode()
{
throw new NotImplementedException();
}
public override System.Net.WebRequest Encode(out byte[] body) public override System.Net.WebRequest Encode(out byte[] body)
{ {
throw new NotImplementedException(); throw new NotImplementedException();

View file

@ -27,6 +27,8 @@
using System; using System;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
internal class DeletePortMapResponseMessage : MessageBase internal class DeletePortMapResponseMessage : MessageBase
@ -36,6 +38,11 @@ namespace Mono.Nat.Upnp
{ {
} }
public override HttpRequestOptions Encode()
{
throw new NotSupportedException();
}
public override System.Net.WebRequest Encode(out byte[] body) public override System.Net.WebRequest Encode(out byte[] body)
{ {
throw new NotSupportedException(); throw new NotSupportedException();

View file

@ -28,6 +28,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Net; using System.Net;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
@ -45,6 +46,11 @@ namespace Mono.Nat.Upnp
this.externalIPAddress = IPAddress.Parse(ip); this.externalIPAddress = IPAddress.Parse(ip);
} }
public override HttpRequestOptions Encode()
{
throw new NotImplementedException();
}
public override WebRequest Encode(out byte[] body) public override WebRequest Encode(out byte[] body)
{ {
throw new NotImplementedException(); throw new NotImplementedException();

View file

@ -28,6 +28,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
@ -100,6 +101,11 @@ namespace Mono.Nat.Upnp
leaseDuration = Convert.ToInt32(data["NewLeaseDuration"].InnerText); leaseDuration = Convert.ToInt32(data["NewLeaseDuration"].InnerText);
} }
public override HttpRequestOptions Encode()
{
throw new NotImplementedException();
}
public override System.Net.WebRequest Encode(out byte[] body) public override System.Net.WebRequest Encode(out byte[] body)
{ {
throw new NotImplementedException(); throw new NotImplementedException();

View file

@ -31,6 +31,7 @@ using System.Net;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Globalization; using System.Globalization;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
{ {
@ -71,6 +72,32 @@ namespace Mono.Nat.Upnp
return req; return req;
} }
protected HttpRequestOptions CreateRequest(string upnpMethod, string methodParameters)
{
string ss = "http://" + this.device.HostEndPoint.ToString() + this.device.ControlUrl;
NatUtility.Log("Initiating request to: {0}", ss);
var req = new HttpRequestOptions();
req.Url = ss;
req.EnableKeepAlive = false;
req.RequestContentType = "text/xml; charset=\"utf-8\"";
req.RequestHeaders.Add("SOAPACTION", "\"" + device.ServiceType + "#" + upnpMethod + "\"");
string bodyString = "<s:Envelope "
+ "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<s:Body>"
+ "<u:" + upnpMethod + " "
+ "xmlns:u=\"" + device.ServiceType + "\">"
+ methodParameters
+ "</u:" + upnpMethod + ">"
+ "</s:Body>"
+ "</s:Envelope>\r\n\r\n";
req.RequestContentBytes = System.Text.Encoding.UTF8.GetBytes(bodyString);
return req;
}
public static MessageBase Decode(UpnpNatDevice device, string message) public static MessageBase Decode(UpnpNatDevice device, string message)
{ {
XmlNode node; XmlNode node;
@ -113,8 +140,14 @@ namespace Mono.Nat.Upnp
return null; return null;
} }
public abstract HttpRequestOptions Encode();
public abstract WebRequest Encode(out byte[] body); public abstract WebRequest Encode(out byte[] body);
public virtual string Method
{
get { return "POST"; }
}
internal static void WriteFullElement(XmlWriter writer, string element, string value) internal static void WriteFullElement(XmlWriter writer, string element, string value)
{ {
writer.WriteStartElement(element); writer.WriteStartElement(element);

View file

@ -36,6 +36,7 @@ using Mono.Nat.Upnp;
using System.Diagnostics; using System.Diagnostics;
using System.Net.Sockets; using System.Net.Sockets;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -48,10 +49,12 @@ namespace Mono.Nat
private DateTime nextSearch; private DateTime nextSearch;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
public UpnpSearcher(ILogger logger) public UpnpSearcher(ILogger logger, IHttpClient httpClient)
{ {
_logger = logger; _logger = logger;
_httpClient = httpClient;
} }
public void Search() public void Search()
@ -76,7 +79,7 @@ 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, _logger); UpnpNatDevice d = new UpnpNatDevice(localAddress, deviceInfo, endpoint, string.Empty, _logger, _httpClient);
NatUtility.Log("Fetching service list: {0}", d.HostEndPoint); NatUtility.Log("Fetching service list: {0}", d.HostEndPoint);
OnDeviceFound(new DeviceEventArgs(d)); OnDeviceFound(new DeviceEventArgs(d));

View file

@ -33,6 +33,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
namespace Mono.Nat.Upnp namespace Mono.Nat.Upnp
@ -40,13 +42,15 @@ namespace Mono.Nat.Upnp
internal class Upnp internal class Upnp
{ {
protected readonly ILogger Logger; protected readonly ILogger Logger;
protected readonly IHttpClient HttpClient;
public Upnp(ILogger logger) public Upnp(ILogger logger, IHttpClient httpClient)
{ {
Logger = logger; Logger = logger;
HttpClient = httpClient;
} }
public UpnpNatDevice Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint) public virtual Task<UpnpNatDevice> Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint)
{ {
// Convert it to a string for easy parsing // Convert it to a string for easy parsing
string dataString = null; string dataString = null;
@ -85,7 +89,8 @@ 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, Logger); var device = new UpnpNatDevice(localAddress, dataString, urn, Logger, HttpClient);
return Task.FromResult(device);
} }
} }
} }

File diff suppressed because it is too large Load diff