trim nat project

This commit is contained in:
Luke Pulverenti 2016-10-27 23:11:21 -04:00
parent 5dca85fe15
commit bdcaf5dd02
22 changed files with 151 additions and 1464 deletions

View file

@ -6,8 +6,6 @@ using SharpCompress.Common;
using SharpCompress.Reader; using SharpCompress.Reader;
using SharpCompress.Reader.Zip; using SharpCompress.Reader.Zip;
using System.IO; using System.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.IO;
namespace MediaBrowser.Common.Implementations.Archiving namespace MediaBrowser.Common.Implementations.Archiving
{ {
@ -16,7 +14,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
/// </summary> /// </summary>
public class ZipClient : IZipClient public class ZipClient : IZipClient
{ {
private IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
public ZipClient(IFileSystem fileSystem) public ZipClient(IFileSystem fileSystem)
{ {

View file

@ -91,9 +91,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
NatUtility.DeviceLost += NatUtility_DeviceLost; NatUtility.DeviceLost += NatUtility_DeviceLost;
// it is hard to say what one should do when an unhandled exception is raised
// because there isn't anything one can do about it. Probably save a log or ignored it.
NatUtility.UnhandledException += NatUtility_UnhandledException;
NatUtility.StartDiscovery(); NatUtility.StartDiscovery();
_timer = new PeriodicTimer(ClearCreatedRules, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5)); _timer = new PeriodicTimer(ClearCreatedRules, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
@ -184,21 +181,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
} }
} }
void NatUtility_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = e.ExceptionObject as Exception;
if (ex == null)
{
//_logger.Error("Unidentified error reported by Mono.Nat");
}
else
{
// Seeing some blank exceptions coming through here
//_logger.ErrorException("Error reported by Mono.Nat: ", ex);
}
}
void NatUtility_DeviceFound(object sender, DeviceEventArgs e) void NatUtility_DeviceFound(object sender, DeviceEventArgs e)
{ {
try try
@ -287,7 +269,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
NatUtility.StopDiscovery(); NatUtility.StopDiscovery();
NatUtility.DeviceFound -= NatUtility_DeviceFound; NatUtility.DeviceFound -= NatUtility_DeviceFound;
NatUtility.DeviceLost -= NatUtility_DeviceLost; NatUtility.DeviceLost -= NatUtility_DeviceLost;
NatUtility.UnhandledException -= NatUtility_UnhandledException;
} }
// Statements in try-block will no fail because StopDiscovery is a one-line // Statements in try-block will no fail because StopDiscovery is a one-line
// method that was no chances to fail. // method that was no chances to fail.

View file

@ -52,43 +52,5 @@ namespace Mono.Nat
} }
public abstract Task CreatePortMap(Mapping mapping); public abstract Task CreatePortMap(Mapping mapping);
public virtual void DeletePortMap (Mapping mapping)
{
IAsyncResult result = BeginDeletePortMap (mapping, null, mapping);
EndDeletePortMap(result);
}
public virtual Mapping[] GetAllMappings ()
{
IAsyncResult result = BeginGetAllMappings (null, null);
return EndGetAllMappings (result);
}
public virtual IPAddress GetExternalIP ()
{
IAsyncResult result = BeginGetExternalIP(null, null);
return EndGetExternalIP(result);
}
public virtual Mapping GetSpecificMapping (Protocol protocol, int port)
{
IAsyncResult result = this.BeginGetSpecificMapping (protocol, port, null, null);
return this.EndGetSpecificMapping(result);
}
public abstract IAsyncResult BeginCreatePortMap(Mapping mapping, AsyncCallback callback, object asyncState);
public abstract IAsyncResult BeginDeletePortMap (Mapping mapping, AsyncCallback callback, object asyncState);
public abstract IAsyncResult BeginGetAllMappings (AsyncCallback callback, object asyncState);
public abstract IAsyncResult BeginGetExternalIP (AsyncCallback callback, object asyncState);
public abstract IAsyncResult BeginGetSpecificMapping(Protocol protocol, int externalPort, AsyncCallback callback, object asyncState);
public abstract void EndCreatePortMap (IAsyncResult result);
public abstract void EndDeletePortMap (IAsyncResult result);
public abstract Mapping[] EndGetAllMappings (IAsyncResult result);
public abstract IPAddress EndGetExternalIP (IAsyncResult result);
public abstract Mapping EndGetSpecificMapping (IAsyncResult result);
} }
} }

View file

@ -37,26 +37,8 @@ namespace Mono.Nat
public interface INatDevice public interface INatDevice
{ {
Task CreatePortMap (Mapping mapping); Task CreatePortMap (Mapping mapping);
void DeletePortMap (Mapping mapping);
IPAddress LocalAddress { get; } IPAddress LocalAddress { get; }
Mapping[] GetAllMappings ();
IPAddress GetExternalIP ();
Mapping GetSpecificMapping (Protocol protocol, int port);
IAsyncResult BeginCreatePortMap (Mapping mapping, AsyncCallback callback, object asyncState);
IAsyncResult BeginDeletePortMap (Mapping mapping, AsyncCallback callback, object asyncState);
IAsyncResult BeginGetAllMappings (AsyncCallback callback, object asyncState);
IAsyncResult BeginGetExternalIP (AsyncCallback callback, object asyncState);
IAsyncResult BeginGetSpecificMapping (Protocol protocol, int externalPort, AsyncCallback callback, object asyncState);
void EndCreatePortMap (IAsyncResult result);
void EndDeletePortMap (IAsyncResult result);
Mapping[] EndGetAllMappings (IAsyncResult result);
IPAddress EndGetExternalIP (IAsyncResult result);
Mapping EndGetSpecificMapping (IAsyncResult result);
DateTime LastSeen { get; set; } DateTime LastSeen { get; set; }
} }

View file

@ -56,20 +56,11 @@
<Compile Include="Pmp\PmpNatDevice.cs" /> <Compile Include="Pmp\PmpNatDevice.cs" />
<Compile Include="Pmp\Searchers\PmpSearcher.cs" /> <Compile Include="Pmp\Searchers\PmpSearcher.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Upnp\AsyncResults\GetAllMappingsAsyncResult.cs" />
<Compile Include="Upnp\AsyncResults\PortMapAsyncResult.cs" />
<Compile Include="Upnp\Messages\DiscoverDeviceMessage.cs" /> <Compile Include="Upnp\Messages\DiscoverDeviceMessage.cs" />
<Compile Include="Upnp\Messages\ErrorMessage.cs" /> <Compile Include="Upnp\Messages\ErrorMessage.cs" />
<Compile Include="Upnp\Messages\GetServicesMessage.cs" /> <Compile Include="Upnp\Messages\GetServicesMessage.cs" />
<Compile Include="Upnp\Messages\Requests\CreatePortMappingMessage.cs" /> <Compile Include="Upnp\Messages\Requests\CreatePortMappingMessage.cs" />
<Compile Include="Upnp\Messages\Requests\DeletePortMappingMessage.cs" />
<Compile Include="Upnp\Messages\Requests\GetExternalIPAddressMessage.cs" />
<Compile Include="Upnp\Messages\Requests\GetGenericPortMappingEntry.cs" />
<Compile Include="Upnp\Messages\Requests\GetSpecificPortMappingEntryMessage.cs" />
<Compile Include="Upnp\Messages\Responses\CreatePortMappingResponseMessage.cs" /> <Compile Include="Upnp\Messages\Responses\CreatePortMappingResponseMessage.cs" />
<Compile Include="Upnp\Messages\Responses\DeletePortMappingResponseMessage.cs" />
<Compile Include="Upnp\Messages\Responses\GetExternalIPAddressResponseMessage.cs" />
<Compile Include="Upnp\Messages\Responses\GetGenericPortMappingEntryResponseMessage.cs" />
<Compile Include="Upnp\Messages\UpnpMessage.cs" /> <Compile Include="Upnp\Messages\UpnpMessage.cs" />
<Compile Include="Upnp\Searchers\UpnpSearcher.cs" /> <Compile Include="Upnp\Searchers\UpnpSearcher.cs" />
<Compile Include="Upnp\Upnp.cs" /> <Compile Include="Upnp\Upnp.cs" />
@ -89,6 +80,9 @@
<Name>MediaBrowser.Model</Name> <Name>MediaBrowser.Model</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Upnp\AsyncResults\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View file

@ -34,6 +34,7 @@ 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 System.Threading.Tasks;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -46,8 +47,6 @@ namespace Mono.Nat
public static event EventHandler<DeviceEventArgs> DeviceFound; public static event EventHandler<DeviceEventArgs> DeviceFound;
public static event EventHandler<DeviceEventArgs> DeviceLost; public static event EventHandler<DeviceEventArgs> DeviceLost;
public static event EventHandler<UnhandledExceptionEventArgs> UnhandledException;
private static List<ISearcher> controllers; private static List<ISearcher> controllers;
private static bool verbose; private static bool verbose;
@ -87,9 +86,8 @@ namespace Mono.Nat
DeviceLost(sender, args); DeviceLost(sender, args);
}; };
}); });
Thread t = new Thread(SearchAndListen);
t.IsBackground = true; Task.Factory.StartNew(SearchAndListen, TaskCreationOptions.LongRunning);
t.Start();
} }
internal static void Log(string format, params object[] args) internal static void Log(string format, params object[] args)
@ -99,7 +97,7 @@ namespace Mono.Nat
logger.Debug(format, args); logger.Debug(format, args);
} }
private static void SearchAndListen() private static async Task SearchAndListen()
{ {
while (true) while (true)
{ {
@ -115,18 +113,19 @@ namespace Mono.Nat
} }
foreach (ISearcher s in controllers) foreach (ISearcher s in controllers)
{
if (s.NextSearch < DateTime.Now && enabledProtocols.Contains(s.Protocol)) if (s.NextSearch < DateTime.Now && enabledProtocols.Contains(s.Protocol))
{ {
Log("Searching for: {0}", s.GetType().Name); Log("Searching for: {0}", s.GetType().Name);
s.Search(); s.Search();
} }
}
} }
catch (Exception e) catch (Exception e)
{ {
if (UnhandledException != null)
UnhandledException(typeof(NatUtility), new UnhandledExceptionEventArgs(e, false));
} }
Thread.Sleep(10); await Task.Delay(100).ConfigureAwait(false);
} }
} }

View file

@ -34,234 +34,166 @@ using System.Threading.Tasks;
namespace Mono.Nat.Pmp namespace Mono.Nat.Pmp
{ {
internal sealed class PmpNatDevice : AbstractNatDevice, IEquatable<PmpNatDevice> internal sealed class PmpNatDevice : AbstractNatDevice, IEquatable<PmpNatDevice>
{ {
private AsyncResult externalIpResult; private IPAddress localAddress;
private bool pendingOp; private IPAddress publicAddress;
private IPAddress localAddress;
private IPAddress publicAddress;
internal PmpNatDevice (IPAddress localAddress, IPAddress publicAddress)
{
this.localAddress = localAddress;
this.publicAddress = publicAddress;
}
public override IPAddress LocalAddress
{
get { return localAddress; }
}
public override Task CreatePortMap(Mapping mapping) internal PmpNatDevice(IPAddress localAddress, IPAddress publicAddress)
{
CreatePortMap(mapping, true);
return Task.FromResult(true);
}
private void StartOp(ref AsyncResult result, AsyncCallback callback, object asyncState)
{ {
if (pendingOp == true) this.localAddress = localAddress;
throw new InvalidOperationException("Can only have one simultaenous async operation"); this.publicAddress = publicAddress;
pendingOp = true;
result = new AsyncResult(callback, asyncState);
} }
private void EndOp(IAsyncResult supplied, ref AsyncResult actual) public override IPAddress LocalAddress
{ {
if (supplied == null) get { return localAddress; }
throw new ArgumentNullException("result");
if (supplied != actual)
throw new ArgumentException("Supplied IAsyncResult does not match the stored result");
if (!supplied.IsCompleted)
supplied.AsyncWaitHandle.WaitOne();
if (actual.StoredException != null)
throw actual.StoredException;
pendingOp = false;
actual = null;
} }
public override bool Equals(object obj)
{
PmpNatDevice device = obj as PmpNatDevice;
return (device == null) ? false : this.Equals(device);
}
public override int GetHashCode ()
{
return this.publicAddress.GetHashCode();
}
public bool Equals (PmpNatDevice other) public override Task CreatePortMap(Mapping mapping)
{ {
return (other == null) ? false : this.publicAddress.Equals(other.publicAddress); return InternalCreatePortMapAsync(mapping, true);
} }
private Mapping CreatePortMap (Mapping mapping, bool create) public override bool Equals(object obj)
{ {
List<byte> package = new List<byte> (); PmpNatDevice device = obj as PmpNatDevice;
return (device == null) ? false : this.Equals(device);
package.Add (PmpConstants.Version); }
package.Add (mapping.Protocol == Protocol.Tcp ? PmpConstants.OperationCodeTcp : PmpConstants.OperationCodeUdp);
package.Add ((byte)0); //reserved
package.Add ((byte)0); //reserved
package.AddRange (BitConverter.GetBytes (IPAddress.HostToNetworkOrder((short)mapping.PrivatePort)));
package.AddRange (BitConverter.GetBytes (create ? IPAddress.HostToNetworkOrder((short)mapping.PublicPort) : (short)0));
package.AddRange (BitConverter.GetBytes (IPAddress.HostToNetworkOrder(mapping.Lifetime)));
CreatePortMapAsyncState state = new CreatePortMapAsyncState (); public override int GetHashCode()
state.Buffer = package.ToArray (); {
state.Mapping = mapping; return this.publicAddress.GetHashCode();
}
ThreadPool.QueueUserWorkItem (new WaitCallback (CreatePortMapAsync), state); public bool Equals(PmpNatDevice other)
WaitHandle.WaitAll (new WaitHandle[] {state.ResetEvent}); {
return (other == null) ? false : this.publicAddress.Equals(other.publicAddress);
if (!state.Success) { }
string type = create ? "create" : "delete";
throw new MappingException (String.Format ("Failed to {0} portmap (protocol={1}, private port={2}", type, mapping.Protocol, mapping.PrivatePort));
}
return state.Mapping;
}
private void CreatePortMapAsync (object obj)
{
CreatePortMapAsyncState state = obj as CreatePortMapAsyncState;
UdpClient udpClient = new UdpClient ();
CreatePortMapListenState listenState = new CreatePortMapListenState (state, udpClient);
int attempt = 0; private async Task<Mapping> InternalCreatePortMapAsync(Mapping mapping, bool create)
int delay = PmpConstants.RetryDelay; {
var package = new List<byte>();
ThreadPool.QueueUserWorkItem (new WaitCallback (CreatePortMapListen), listenState);
while (attempt < PmpConstants.RetryAttempts && !listenState.Success) { package.Add(PmpConstants.Version);
udpClient.Send (state.Buffer, state.Buffer.Length, new IPEndPoint (localAddress, PmpConstants.ServerPort)); package.Add(mapping.Protocol == Protocol.Tcp ? PmpConstants.OperationCodeTcp : PmpConstants.OperationCodeUdp);
listenState.UdpClientReady.Set(); package.Add(0); //reserved
package.Add(0); //reserved
package.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)mapping.PrivatePort)));
package.AddRange(
BitConverter.GetBytes(create ? IPAddress.HostToNetworkOrder((short)mapping.PublicPort) : (short)0));
package.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(mapping.Lifetime)));
attempt++; try
delay *= 2;
Thread.Sleep (delay);
}
state.Success = listenState.Success;
udpClient.Close ();
state.ResetEvent.Set ();
}
private void CreatePortMapListen (object obj)
{
CreatePortMapListenState state = obj as CreatePortMapListenState;
UdpClient udpClient = state.UdpClient;
state.UdpClientReady.WaitOne(); // Evidently UdpClient has some lazy-init Send/Receive race?
IPEndPoint endPoint = new IPEndPoint (localAddress, PmpConstants.ServerPort);
while (!state.Success)
{ {
byte[] data; byte[] buffer = package.ToArray();
try int attempt = 0;
int delay = PmpConstants.RetryDelay;
using (var udpClient = new UdpClient())
{ {
data = udpClient.Receive(ref endPoint); var cancellationTokenSource = new CancellationTokenSource();
while (attempt < PmpConstants.RetryAttempts)
{
await udpClient.SendAsync(buffer, buffer.Length,
new IPEndPoint(LocalAddress, PmpConstants.ServerPort));
if (attempt == 0)
{
Task.Run(() => CreatePortMapListen(udpClient, mapping, cancellationTokenSource.Token));
}
attempt++;
delay *= 2;
await Task.Delay(delay).ConfigureAwait(false);
}
cancellationTokenSource.Cancel();
} }
catch (SocketException) }
catch (OperationCanceledException)
{
}
catch (Exception e)
{
string type = create ? "create" : "delete";
string message = String.Format("Failed to {0} portmap (protocol={1}, private port={2}) {3}",
type,
mapping.Protocol,
mapping.PrivatePort,
e.Message);
NatUtility.Log(message);
var pmpException = e as MappingException;
throw new MappingException(message, pmpException);
}
return mapping;
}
private async void CreatePortMapListen(UdpClient udpClient, Mapping mapping, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var result = await udpClient.ReceiveAsync().ConfigureAwait(false);
var endPoint = result.RemoteEndPoint;
byte[] data = data = result.Buffer;
if (data.Length < 16)
continue;
if (data[0] != PmpConstants.Version)
continue;
var opCode = (byte)(data[1] & 127);
var protocol = Protocol.Tcp;
if (opCode == PmpConstants.OperationCodeUdp)
protocol = Protocol.Udp;
short resultCode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 2));
int epoch = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 4));
short privatePort = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 8));
short publicPort = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 10));
var lifetime = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 12));
if (privatePort < 0 || publicPort < 0 || resultCode != PmpConstants.ResultCodeSuccess)
{ {
state.Success = false; var errors = new[]
return; {
"Success",
"Unsupported Version",
"Not Authorized/Refused (e.g. box supports mapping, but user has turned feature off)"
,
"Network Failure (e.g. NAT box itself has not obtained a DHCP lease)",
"Out of resources (NAT box cannot create any more mappings at this time)",
"Unsupported opcode"
};
throw new MappingException(resultCode, errors[resultCode]);
} }
catch (ObjectDisposedException) if (lifetime == 0) return; //mapping was deleted
{
state.Success = false;
return;
}
if (data.Length < 16)
continue;
if (data[0] != PmpConstants.Version)
continue;
byte opCode = (byte)(data[1] & (byte)127);
Protocol protocol = Protocol.Tcp;
if (opCode == PmpConstants.OperationCodeUdp)
protocol = Protocol.Udp;
short resultCode = IPAddress.NetworkToHostOrder (BitConverter.ToInt16 (data, 2));
uint epoch = (uint)IPAddress.NetworkToHostOrder (BitConverter.ToInt32 (data, 4));
int privatePort = IPAddress.NetworkToHostOrder (BitConverter.ToInt16 (data, 8));
int publicPort = IPAddress.NetworkToHostOrder (BitConverter.ToInt16 (data, 10));
uint lifetime = (uint)IPAddress.NetworkToHostOrder (BitConverter.ToInt32 (data, 12));
if (publicPort < 0 || privatePort < 0 || resultCode != PmpConstants.ResultCodeSuccess)
{
state.Success = false;
return;
}
if (lifetime == 0)
{
//mapping was deleted
state.Success = true;
state.Mapping = null;
return;
}
else
{
//mapping was created
//TODO: verify that the private port+protocol are a match
Mapping mapping = state.Mapping;
mapping.PublicPort = publicPort;
mapping.Protocol = protocol;
mapping.Expiration = DateTime.Now.AddSeconds (lifetime);
state.Success = true;
}
}
}
//mapping was created
//TODO: verify that the private port+protocol are a match
mapping.PublicPort = publicPort;
mapping.Protocol = protocol;
mapping.Expiration = DateTime.Now.AddSeconds(lifetime);
return;
}
}
/// <summary> /// <summary>
/// Overridden. /// Overridden.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public override string ToString( ) public override string ToString()
{ {
return String.Format( "PmpNatDevice - Local Address: {0}, Public IP: {1}, Last Seen: {2}", return String.Format("PmpNatDevice - Local Address: {0}, Public IP: {1}, Last Seen: {2}",
this.localAddress, this.publicAddress, this.LastSeen ); this.localAddress, this.publicAddress, this.LastSeen);
} }
}
private class CreatePortMapAsyncState
{
internal byte[] Buffer;
internal ManualResetEvent ResetEvent = new ManualResetEvent (false);
internal Mapping Mapping;
internal bool Success;
}
private class CreatePortMapListenState
{
internal volatile bool Success;
internal Mapping Mapping;
internal UdpClient UdpClient;
internal ManualResetEvent UdpClientReady;
internal CreatePortMapListenState (CreatePortMapAsyncState state, UdpClient client)
{
Mapping = state.Mapping;
UdpClient = client; UdpClientReady = new ManualResetEvent(false);
}
}
}
} }

View file

@ -1,56 +0,0 @@
//
// Authors:
// Alan McGovern alan.mcgovern@gmail.com
//
// Copyright (C) 2006 Alan McGovern
//
// 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.Text;
using System.Net;
namespace Mono.Nat.Upnp
{
internal class GetAllMappingsAsyncResult : PortMapAsyncResult
{
private List<Mapping> mappings;
private Mapping specificMapping;
public GetAllMappingsAsyncResult(WebRequest request, AsyncCallback callback, object asyncState)
: base(request, callback, asyncState)
{
mappings = new List<Mapping>();
}
public List<Mapping> Mappings
{
get { return this.mappings; }
}
public Mapping SpecificMapping
{
get { return this.specificMapping; }
set { this.specificMapping = value; }
}
}
}

View file

@ -1,75 +0,0 @@
//
// Authors:
// Alan McGovern alan.mcgovern@gmail.com
//
// Copyright (C) 2006 Alan McGovern
//
// 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.Threading;
namespace Mono.Nat.Upnp
{
internal class PortMapAsyncResult : AsyncResult
{
private WebRequest request;
private MessageBase savedMessage;
protected PortMapAsyncResult(WebRequest request, AsyncCallback callback, object asyncState)
: base (callback, asyncState)
{
this.request = request;
}
internal WebRequest Request
{
get { return this.request; }
set { this.request = value; }
}
internal MessageBase SavedMessage
{
get { return this.savedMessage; }
set { this.savedMessage = value; }
}
internal static PortMapAsyncResult Create (MessageBase message, WebRequest request, AsyncCallback storedCallback, object asyncState)
{
if (message is GetGenericPortMappingEntry)
return new GetAllMappingsAsyncResult(request, storedCallback, asyncState);
if (message is GetSpecificPortMappingEntryMessage)
{
GetSpecificPortMappingEntryMessage mapMessage = (GetSpecificPortMappingEntryMessage)message;
GetAllMappingsAsyncResult result = new GetAllMappingsAsyncResult(request, storedCallback, asyncState);
result.SpecificMapping = new Mapping(mapMessage.protocol, 0, mapMessage.externalPort, 0);
return result;
}
return new PortMapAsyncResult(request, storedCallback, asyncState);
}
}
}

View file

@ -59,10 +59,5 @@ namespace Mono.Nat.Upnp
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override System.Net.WebRequest Encode(out byte[] body)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -60,17 +60,6 @@ namespace Mono.Nat.Upnp
} }
} }
public override WebRequest Encode(out byte[] body)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://" + this.hostAddress.ToString() + this.servicesDescriptionUrl);
req.Headers.Add("ACCEPT-LANGUAGE", "en");
req.Method = "GET";
body = new byte[0];
return req;
}
public override HttpRequestOptions Encode() public override HttpRequestOptions Encode()
{ {
var req = new HttpRequestOptions(); var req = new HttpRequestOptions();

View file

@ -71,25 +71,5 @@ namespace Mono.Nat.Upnp
writer.Flush(); writer.Flush();
return CreateRequest("AddPortMapping", builder.ToString()); return CreateRequest("AddPortMapping", builder.ToString());
} }
public override WebRequest Encode(out byte[] body)
{
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(), out body);
}
} }
} }

View file

@ -1,71 +0,0 @@
//
// Authors:
// Alan McGovern alan.mcgovern@gmail.com
//
// Copyright (C) 2006 Alan McGovern
//
// 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.Net;
using System.IO;
using System.Text;
using System.Xml;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp
{
internal class DeletePortMappingMessage : MessageBase
{
private Mapping mapping;
public DeletePortMappingMessage(Mapping mapping, UpnpNatDevice device)
: base(device)
{
this.mapping = mapping;
}
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);
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(), out body);
}
}
}

View file

@ -1,56 +0,0 @@
//
// Authors:
// Alan McGovern alan.mcgovern@gmail.com
//
// Copyright (C) 2006 Alan McGovern
//
// 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.Text;
using System.Net;
using System.IO;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp
{
internal class GetExternalIPAddressMessage : MessageBase
{
#region Constructors
public GetExternalIPAddressMessage(UpnpNatDevice device)
:base(device)
{
}
#endregion
public override HttpRequestOptions Encode()
{
return CreateRequest("GetExternalIPAddress", string.Empty);
}
public override WebRequest Encode(out byte[] body)
{
return CreateRequest("GetExternalIPAddress", string.Empty, out body);
}
}
}

View file

@ -1,67 +0,0 @@
//
// Authors:
// Alan McGovern alan.mcgovern@gmail.com
//
// Copyright (C) 2006 Alan McGovern
//
// 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.Text;
using System.Xml;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp
{
internal class GetGenericPortMappingEntry : MessageBase
{
private int index;
public GetGenericPortMappingEntry(int index, UpnpNatDevice device)
:base(device)
{
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)
{
StringBuilder sb = new StringBuilder(128);
XmlWriter writer = CreateWriter(sb);
WriteFullElement(writer, "NewPortMappingIndex", index.ToString());
writer.Flush();
return CreateRequest("GetGenericPortMappingEntry", sb.ToString(), out body);
}
}
}

View file

@ -1,74 +0,0 @@
//
// Authors:
// Alan McGovern alan.mcgovern@gmail.com
//
// Copyright (C) 2006 Alan McGovern
//
// 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.Text;
using System.Xml;
using System.Net;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp
{
internal class GetSpecificPortMappingEntryMessage : MessageBase
{
internal Protocol protocol;
internal int externalPort;
public GetSpecificPortMappingEntryMessage(Protocol protocol, int externalPort, UpnpNatDevice device)
: base(device)
{
this.protocol = protocol;
this.externalPort = externalPort;
}
public override WebRequest Encode(out byte[] body)
{
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(), 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

@ -44,10 +44,5 @@ namespace Mono.Nat.Upnp
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override System.Net.WebRequest Encode(out byte[] body)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -1,51 +0,0 @@
//
// Authors:
// Alan McGovern alan.mcgovern@gmail.com
//
// Copyright (C) 2006 Alan McGovern
//
// 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 MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp
{
internal class DeletePortMapResponseMessage : MessageBase
{
public DeletePortMapResponseMessage()
:base(null)
{
}
public override HttpRequestOptions Encode()
{
throw new NotSupportedException();
}
public override System.Net.WebRequest Encode(out byte[] body)
{
throw new NotSupportedException();
}
}
}

View file

@ -1,59 +0,0 @@
//
// Authors:
// Alan McGovern alan.mcgovern@gmail.com
//
// Copyright (C) 2006 Alan McGovern
//
// 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.Text;
using System.Net;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp
{
internal class GetExternalIPAddressResponseMessage : MessageBase
{
public IPAddress ExternalIPAddress
{
get { return this.externalIPAddress; }
}
private IPAddress externalIPAddress;
public GetExternalIPAddressResponseMessage(string ip)
:base(null)
{
this.externalIPAddress = IPAddress.Parse(ip);
}
public override HttpRequestOptions Encode()
{
throw new NotImplementedException();
}
public override WebRequest Encode(out byte[] body)
{
throw new NotImplementedException();
}
}
}

View file

@ -1,114 +0,0 @@
//
// Authors:
// Alan McGovern alan.mcgovern@gmail.com
//
// Copyright (C) 2006 Alan McGovern
//
// 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.Text;
using System.Xml;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp
{
internal class GetGenericPortMappingEntryResponseMessage : MessageBase
{
private string remoteHost;
private int externalPort;
private Protocol protocol;
private int internalPort;
private string internalClient;
private bool enabled;
private string portMappingDescription;
private int leaseDuration;
public string RemoteHost
{
get { return this.remoteHost; }
}
public int ExternalPort
{
get { return this.externalPort; }
}
public Protocol Protocol
{
get { return this.protocol; }
}
public int InternalPort
{
get { return this.internalPort; }
}
public string InternalClient
{
get { return this.internalClient; }
}
public bool Enabled
{
get { return this.enabled; }
}
public string PortMappingDescription
{
get { return this.portMappingDescription; }
}
public int LeaseDuration
{
get { return this.leaseDuration; }
}
public GetGenericPortMappingEntryResponseMessage(XmlNode data, bool genericMapping)
: base(null)
{
remoteHost = (genericMapping) ? data["NewRemoteHost"].InnerText : string.Empty;
externalPort = (genericMapping) ? Convert.ToInt32(data["NewExternalPort"].InnerText) : -1;
if (genericMapping)
protocol = data["NewProtocol"].InnerText.Equals("TCP", StringComparison.InvariantCultureIgnoreCase) ? Protocol.Tcp : Protocol.Udp;
else
protocol = Protocol.Udp;
internalPort = Convert.ToInt32(data["NewInternalPort"].InnerText);
internalClient = data["NewInternalClient"].InnerText;
enabled = data["NewEnabled"].InnerText == "1" ? true : false;
portMappingDescription = data["NewPortMappingDescription"].InnerText;
leaseDuration = Convert.ToInt32(data["NewLeaseDuration"].InnerText);
}
public override HttpRequestOptions Encode()
{
throw new NotImplementedException();
}
public override System.Net.WebRequest Encode(out byte[] body)
{
throw new NotImplementedException();
}
}
}

View file

@ -45,33 +45,6 @@ namespace Mono.Nat.Upnp
this.device = device; this.device = device;
} }
protected WebRequest CreateRequest(string upnpMethod, string methodParameters, out byte[] body)
{
string ss = "http://" + this.device.HostEndPoint.ToString() + this.device.ControlUrl;
NatUtility.Log("Initiating request to: {0}", ss);
Uri location = new Uri(ss);
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(location);
req.KeepAlive = false;
req.Method = "POST";
req.ContentType = "text/xml; charset=\"utf-8\"";
req.Headers.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";
body = System.Text.Encoding.UTF8.GetBytes(bodyString);
return req;
}
protected HttpRequestOptions CreateRequest(string upnpMethod, string methodParameters) protected HttpRequestOptions CreateRequest(string upnpMethod, string methodParameters)
{ {
string ss = "http://" + this.device.HostEndPoint.ToString() + this.device.ControlUrl; string ss = "http://" + this.device.HostEndPoint.ToString() + this.device.ControlUrl;
@ -98,50 +71,7 @@ namespace Mono.Nat.Upnp
return req; return req;
} }
public static MessageBase Decode(UpnpNatDevice device, string message)
{
XmlNode node;
XmlDocument doc = new XmlDocument();
doc.LoadXml(message);
XmlNamespaceManager nsm = new XmlNamespaceManager(doc.NameTable);
// Error messages should be found under this namespace
nsm.AddNamespace("errorNs", "urn:schemas-upnp-org:control-1-0");
nsm.AddNamespace("responseNs", device.ServiceType);
// Check to see if we have a fault code message.
if ((node = doc.SelectSingleNode("//errorNs:UPnPError", nsm)) != null) {
string errorCode = node["errorCode"] != null ? node["errorCode"].InnerText : "";
string errorDescription = node["errorDescription"] != null ? node["errorDescription"].InnerText : "";
return new ErrorMessage(Convert.ToInt32(errorCode, CultureInfo.InvariantCulture), errorDescription);
}
if ((doc.SelectSingleNode("//responseNs:AddPortMappingResponse", nsm)) != null)
return new CreatePortMappingResponseMessage();
if ((doc.SelectSingleNode("//responseNs:DeletePortMappingResponse", nsm)) != null)
return new DeletePortMapResponseMessage();
if ((node = doc.SelectSingleNode("//responseNs:GetExternalIPAddressResponse", nsm)) != null) {
string newExternalIPAddress = node["NewExternalIPAddress"] != null ? node["NewExternalIPAddress"].InnerText : "";
return new GetExternalIPAddressResponseMessage(newExternalIPAddress);
}
if ((node = doc.SelectSingleNode("//responseNs:GetGenericPortMappingEntryResponse", nsm)) != null)
return new GetGenericPortMappingEntryResponseMessage(node, true);
if ((node = doc.SelectSingleNode("//responseNs:GetSpecificPortMappingEntryResponse", nsm)) != null)
return new GetGenericPortMappingEntryResponseMessage(node, false);
NatUtility.Log("Unknown message returned. Please send me back the following XML:");
NatUtility.Log(message);
return null;
}
public abstract HttpRequestOptions Encode(); public abstract HttpRequestOptions Encode();
public abstract WebRequest Encode(out byte[] body);
public virtual string Method public virtual string Method
{ {

View file

@ -70,7 +70,7 @@ namespace Mono.Nat.Upnp
// 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
// Are we going to get addresses with the "http://" attached? // Are we going to get addresses with the "http://" attached?
if (locationDetails.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase)) if (locationDetails.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
{ {
NatUtility.Log("Found device at: {0}", locationDetails); NatUtility.Log("Found device at: {0}", locationDetails);
// This bit strings out the "http://" from the string // This bit strings out the "http://" from the string
@ -98,7 +98,7 @@ namespace Mono.Nat.Upnp
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.OrdinalIgnoreCase) + 9).Split('\r')[0];
this.serviceType = serviceType; this.serviceType = serviceType;
// Make sure we have no excess whitespace // Make sure we have no excess whitespace
@ -106,7 +106,7 @@ namespace Mono.Nat.Upnp
// 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
// Are we going to get addresses with the "http://" attached? // Are we going to get addresses with the "http://" attached?
if (locationDetails.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase)) if (locationDetails.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
{ {
NatUtility.Log("Found device at: {0}", locationDetails); NatUtility.Log("Found device at: {0}", locationDetails);
// This bit strings out the "http://" from the string // This bit strings out the "http://" from the string
@ -171,189 +171,12 @@ namespace Mono.Nat.Upnp
get { return serviceType; } get { return serviceType; }
} }
/// <summary>
/// Begins an async call to get the external ip address of the router
/// </summary>
public override IAsyncResult BeginGetExternalIP(AsyncCallback callback, object asyncState)
{
// Create the port map message
GetExternalIPAddressMessage message = new GetExternalIPAddressMessage(this);
return BeginMessageInternal(message, callback, asyncState, EndGetExternalIPInternal);
}
/// <summary>
/// Maps the specified port to this computer
/// </summary>
public override IAsyncResult BeginCreatePortMap(Mapping mapping, AsyncCallback callback, object asyncState)
{
CreatePortMappingMessage message = new CreatePortMappingMessage(mapping, localAddress, this);
return BeginMessageInternal(message, callback, asyncState, EndCreatePortMapInternal);
}
public override Task CreatePortMap(Mapping mapping) public override Task CreatePortMap(Mapping mapping)
{ {
CreatePortMappingMessage message = new CreatePortMappingMessage(mapping, localAddress, this); CreatePortMappingMessage message = new CreatePortMappingMessage(mapping, localAddress, this);
return _httpClient.SendAsync(message.Encode(), message.Method); return _httpClient.SendAsync(message.Encode(), message.Method);
} }
/// <summary>
/// Removes a port mapping from this computer
/// </summary>
public override IAsyncResult BeginDeletePortMap(Mapping mapping, AsyncCallback callback, object asyncState)
{
DeletePortMappingMessage message = new DeletePortMappingMessage(mapping, this);
return BeginMessageInternal(message, callback, asyncState, EndDeletePortMapInternal);
}
public override IAsyncResult BeginGetAllMappings(AsyncCallback callback, object asyncState)
{
GetGenericPortMappingEntry message = new GetGenericPortMappingEntry(0, this);
return BeginMessageInternal(message, callback, asyncState, EndGetAllMappingsInternal);
}
public override IAsyncResult BeginGetSpecificMapping(Protocol protocol, int port, AsyncCallback callback, object asyncState)
{
GetSpecificPortMappingEntryMessage message = new GetSpecificPortMappingEntryMessage(protocol, port, this);
return this.BeginMessageInternal(message, callback, asyncState, new AsyncCallback(this.EndGetSpecificMappingInternal));
}
/// <summary>
///
/// </summary>
/// <param name="result"></param>
public override void EndCreatePortMap(IAsyncResult result)
{
if (result == null) throw new ArgumentNullException("result");
PortMapAsyncResult mappingResult = result as PortMapAsyncResult;
if (mappingResult == null)
throw new ArgumentException("Invalid AsyncResult", "result");
// Check if we need to wait for the operation to finish
if (!result.IsCompleted)
result.AsyncWaitHandle.WaitOne();
// If we have a saved exception, it means something went wrong during the mapping
// so we just rethrow the exception and let the user figure out what they should do.
if (mappingResult.SavedMessage is ErrorMessage)
{
ErrorMessage msg = mappingResult.SavedMessage as ErrorMessage;
throw new MappingException(msg.ErrorCode, msg.Description);
}
//return result.AsyncState as Mapping;
}
/// <summary>
///
/// </summary>
/// <param name="result"></param>
public override void EndDeletePortMap(IAsyncResult result)
{
if (result == null)
throw new ArgumentNullException("result");
PortMapAsyncResult mappingResult = result as PortMapAsyncResult;
if (mappingResult == null)
throw new ArgumentException("Invalid AsyncResult", "result");
// Check if we need to wait for the operation to finish
if (!mappingResult.IsCompleted)
mappingResult.AsyncWaitHandle.WaitOne();
// If we have a saved exception, it means something went wrong during the mapping
// so we just rethrow the exception and let the user figure out what they should do.
if (mappingResult.SavedMessage is ErrorMessage)
{
ErrorMessage msg = mappingResult.SavedMessage as ErrorMessage;
throw new MappingException(msg.ErrorCode, msg.Description);
}
// If all goes well, we just return
//return true;
}
public override Mapping[] EndGetAllMappings(IAsyncResult result)
{
if (result == null)
throw new ArgumentNullException("result");
GetAllMappingsAsyncResult mappingResult = result as GetAllMappingsAsyncResult;
if (mappingResult == null)
throw new ArgumentException("Invalid AsyncResult", "result");
if (!mappingResult.IsCompleted)
mappingResult.AsyncWaitHandle.WaitOne();
if (mappingResult.SavedMessage is ErrorMessage)
{
ErrorMessage msg = mappingResult.SavedMessage as ErrorMessage;
if (msg.ErrorCode != 713)
throw new MappingException(msg.ErrorCode, msg.Description);
}
return mappingResult.Mappings.ToArray();
}
/// <summary>
/// Ends an async request to get the external ip address of the router
/// </summary>
public override IPAddress EndGetExternalIP(IAsyncResult result)
{
if (result == null) throw new ArgumentNullException("result");
PortMapAsyncResult mappingResult = result as PortMapAsyncResult;
if (mappingResult == null)
throw new ArgumentException("Invalid AsyncResult", "result");
if (!result.IsCompleted)
result.AsyncWaitHandle.WaitOne();
if (mappingResult.SavedMessage is ErrorMessage)
{
ErrorMessage msg = mappingResult.SavedMessage as ErrorMessage;
throw new MappingException(msg.ErrorCode, msg.Description);
}
if (mappingResult.SavedMessage == null)
return null;
else
return ((GetExternalIPAddressResponseMessage)mappingResult.SavedMessage).ExternalIPAddress;
}
public override Mapping EndGetSpecificMapping(IAsyncResult result)
{
if (result == null)
throw new ArgumentNullException("result");
GetAllMappingsAsyncResult mappingResult = result as GetAllMappingsAsyncResult;
if (mappingResult == null)
throw new ArgumentException("Invalid AsyncResult", "result");
if (!mappingResult.IsCompleted)
mappingResult.AsyncWaitHandle.WaitOne();
if (mappingResult.SavedMessage is ErrorMessage)
{
ErrorMessage message = mappingResult.SavedMessage as ErrorMessage;
if (message.ErrorCode != 0x2ca)
{
throw new MappingException(message.ErrorCode, message.Description);
}
}
if (mappingResult.Mappings.Count == 0)
return new Mapping(Protocol.Tcp, -1, -1);
return mappingResult.Mappings[0];
}
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
UpnpNatDevice device = obj as UpnpNatDevice; UpnpNatDevice device = obj as UpnpNatDevice;
@ -373,256 +196,6 @@ namespace Mono.Nat.Upnp
return (this.hostEndPoint.GetHashCode() ^ this.controlUrl.GetHashCode() ^ this.serviceDescriptionUrl.GetHashCode()); return (this.hostEndPoint.GetHashCode() ^ this.controlUrl.GetHashCode() ^ this.serviceDescriptionUrl.GetHashCode());
} }
private IAsyncResult BeginMessageInternal(MessageBase message, AsyncCallback storedCallback, object asyncState, AsyncCallback callback)
{
byte[] body;
WebRequest request = message.Encode(out body);
PortMapAsyncResult mappingResult = PortMapAsyncResult.Create(message, request, storedCallback, asyncState);
if (body.Length > 0)
{
request.ContentLength = body.Length;
request.BeginGetRequestStream(delegate (IAsyncResult result)
{
try
{
Stream s = request.EndGetRequestStream(result);
s.Write(body, 0, body.Length);
request.BeginGetResponse(callback, mappingResult);
}
catch (Exception ex)
{
mappingResult.Complete(ex);
}
}, null);
}
else
{
request.BeginGetResponse(callback, mappingResult);
}
return mappingResult;
}
private void CompleteMessage(IAsyncResult result)
{
PortMapAsyncResult mappingResult = result.AsyncState as PortMapAsyncResult;
mappingResult.CompletedSynchronously = result.CompletedSynchronously;
mappingResult.Complete();
}
private MessageBase DecodeMessageFromResponse(Stream s, long length)
{
StringBuilder data = new StringBuilder();
int bytesRead = 0;
int totalBytesRead = 0;
byte[] buffer = new byte[10240];
// Read out the content of the message, hopefully picking everything up in the case where we have no contentlength
if (length != -1)
{
while (totalBytesRead < length)
{
bytesRead = s.Read(buffer, 0, buffer.Length);
data.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
totalBytesRead += bytesRead;
}
}
else
{
while ((bytesRead = s.Read(buffer, 0, buffer.Length)) != 0)
data.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
}
// Once we have our content, we need to see what kind of message it is. It'll either a an error
// or a response based on the action we performed.
return MessageBase.Decode(this, data.ToString());
}
private void EndCreatePortMapInternal(IAsyncResult result)
{
EndMessageInternal(result);
CompleteMessage(result);
}
private void EndMessageInternal(IAsyncResult result)
{
HttpWebResponse response = null;
PortMapAsyncResult mappingResult = result.AsyncState as PortMapAsyncResult;
try
{
try
{
response = (HttpWebResponse)mappingResult.Request.EndGetResponse(result);
}
catch (WebException ex)
{
// Even if the request "failed" i want to continue on to read out the response from the router
response = ex.Response as HttpWebResponse;
if (response == null)
mappingResult.SavedMessage = new ErrorMessage((int)ex.Status, ex.Message);
}
if (response != null)
mappingResult.SavedMessage = DecodeMessageFromResponse(response.GetResponseStream(), response.ContentLength);
}
finally
{
if (response != null)
response.Close();
}
}
private void EndDeletePortMapInternal(IAsyncResult result)
{
EndMessageInternal(result);
CompleteMessage(result);
}
private void EndGetAllMappingsInternal(IAsyncResult result)
{
EndMessageInternal(result);
GetAllMappingsAsyncResult mappingResult = result.AsyncState as GetAllMappingsAsyncResult;
GetGenericPortMappingEntryResponseMessage message = mappingResult.SavedMessage as GetGenericPortMappingEntryResponseMessage;
if (message != null)
{
Mapping mapping = new Mapping(message.Protocol, message.InternalPort, message.ExternalPort, message.LeaseDuration);
mapping.Description = message.PortMappingDescription;
mappingResult.Mappings.Add(mapping);
GetGenericPortMappingEntry next = new GetGenericPortMappingEntry(mappingResult.Mappings.Count, this);
// It's ok to do this synchronously because we should already be on anther thread
// and this won't block the user.
byte[] body;
WebRequest request = next.Encode(out body);
if (body.Length > 0)
{
request.ContentLength = body.Length;
request.GetRequestStream().Write(body, 0, body.Length);
}
mappingResult.Request = request;
request.BeginGetResponse(EndGetAllMappingsInternal, mappingResult);
return;
}
CompleteMessage(result);
}
private void EndGetExternalIPInternal(IAsyncResult result)
{
EndMessageInternal(result);
CompleteMessage(result);
}
private void EndGetSpecificMappingInternal(IAsyncResult result)
{
EndMessageInternal(result);
GetAllMappingsAsyncResult mappingResult = result.AsyncState as GetAllMappingsAsyncResult;
GetGenericPortMappingEntryResponseMessage message = mappingResult.SavedMessage as GetGenericPortMappingEntryResponseMessage;
if (message != null)
{
Mapping mapping = new Mapping(mappingResult.SpecificMapping.Protocol, message.InternalPort, mappingResult.SpecificMapping.PublicPort, message.LeaseDuration);
mapping.Description = mappingResult.SpecificMapping.Description;
mappingResult.Mappings.Add(mapping);
}
CompleteMessage(result);
}
internal async Task<bool> GetServicesList()
{
// Create a HTTPWebRequest to download the list of services the device offers
var requestOptions = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint, _logger).Encode();
requestOptions.BufferContent = false;
using (var response = await _httpClient.Get(requestOptions).ConfigureAwait(false))
{
return ServicesReceived(response);
}
}
private bool ServicesReceived(Stream s)
{
int abortCount = 0;
int bytesRead = 0;
byte[] buffer = new byte[10240];
StringBuilder servicesXml = new StringBuilder();
XmlDocument xmldoc = new XmlDocument();
while (true)
{
bytesRead = s.Read(buffer, 0, buffer.Length);
servicesXml.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
try
{
xmldoc.LoadXml(servicesXml.ToString());
break;
}
catch (XmlException)
{
// If we can't receive the entire XML within 500ms, then drop the connection
// Unfortunately not all routers supply a valid ContentLength (mine doesn't)
// so this hack is needed to keep testing our recieved data until it gets successfully
// parsed by the xmldoc. Without this, the code will never pick up my router.
if (abortCount++ > 50)
{
return false;
}
NatUtility.Log("{0}: Couldn't parse services list", HostEndPoint);
System.Threading.Thread.Sleep(10);
}
}
NatUtility.Log("{0}: Parsed services list", HostEndPoint);
XmlNamespaceManager ns = new XmlNamespaceManager(xmldoc.NameTable);
ns.AddNamespace("ns", "urn:schemas-upnp-org:device-1-0");
XmlNodeList nodes = xmldoc.SelectNodes("//*/ns:serviceList", ns);
foreach (XmlNode node in nodes)
{
//Go through each service there
foreach (XmlNode service in node.ChildNodes)
{
//If the service is a WANIPConnection, then we have what we want
string type = service["serviceType"].InnerText;
NatUtility.Log("{0}: Found service: {1}", HostEndPoint, type);
StringComparison c = StringComparison.OrdinalIgnoreCase;
// TODO: Add support for version 2 of UPnP.
if (type.Equals("urn:schemas-upnp-org:service:WANPPPConnection:1", c) ||
type.Equals("urn:schemas-upnp-org:service:WANIPConnection:1", c))
{
this.controlUrl = service["controlURL"].InnerText;
NatUtility.Log("{0}: Found upnp service at: {1}", HostEndPoint, controlUrl);
try
{
Uri u = new Uri(controlUrl);
if (u.IsAbsoluteUri)
{
EndPoint old = hostEndPoint;
this.hostEndPoint = new IPEndPoint(IPAddress.Parse(u.Host), u.Port);
NatUtility.Log("{0}: Absolute URI detected. Host address is now: {1}", old, HostEndPoint);
this.controlUrl = controlUrl.Substring(u.GetLeftPart(UriPartial.Authority).Length);
NatUtility.Log("{0}: New control url: {1}", HostEndPoint, controlUrl);
}
}
catch
{
NatUtility.Log("{0}: Assuming control Uri is relative: {1}", HostEndPoint, controlUrl);
}
NatUtility.Log("{0}: Handshake Complete", HostEndPoint);
return true;
}
}
}
//If we get here, it means that we didn't get WANIPConnection service, which means no uPnP forwarding
//So we don't invoke the callback, so this device is never added to our lists
return false;
}
/// <summary> /// <summary>
/// Overridden. /// Overridden.
/// </summary> /// </summary>