trim nat project
This commit is contained in:
parent
5dca85fe15
commit
bdcaf5dd02
|
@ -6,8 +6,6 @@ using SharpCompress.Common;
|
|||
using SharpCompress.Reader;
|
||||
using SharpCompress.Reader.Zip;
|
||||
using System.IO;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Archiving
|
||||
{
|
||||
|
@ -16,7 +14,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
|||
/// </summary>
|
||||
public class ZipClient : IZipClient
|
||||
{
|
||||
private IFileSystem _fileSystem;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public ZipClient(IFileSystem fileSystem)
|
||||
{
|
||||
|
|
|
@ -91,9 +91,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
|
|||
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();
|
||||
|
||||
_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)
|
||||
{
|
||||
try
|
||||
|
@ -287,7 +269,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
|
|||
NatUtility.StopDiscovery();
|
||||
NatUtility.DeviceFound -= NatUtility_DeviceFound;
|
||||
NatUtility.DeviceLost -= NatUtility_DeviceLost;
|
||||
NatUtility.UnhandledException -= NatUtility_UnhandledException;
|
||||
}
|
||||
// Statements in try-block will no fail because StopDiscovery is a one-line
|
||||
// method that was no chances to fail.
|
||||
|
|
|
@ -52,43 +52,5 @@ namespace Mono.Nat
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,26 +37,8 @@ namespace Mono.Nat
|
|||
public interface INatDevice
|
||||
{
|
||||
Task CreatePortMap (Mapping mapping);
|
||||
void DeletePortMap (Mapping mapping);
|
||||
|
||||
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; }
|
||||
}
|
||||
|
|
|
@ -56,20 +56,11 @@
|
|||
<Compile Include="Pmp\PmpNatDevice.cs" />
|
||||
<Compile Include="Pmp\Searchers\PmpSearcher.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\ErrorMessage.cs" />
|
||||
<Compile Include="Upnp\Messages\GetServicesMessage.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\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\Searchers\UpnpSearcher.cs" />
|
||||
<Compile Include="Upnp\Upnp.cs" />
|
||||
|
@ -89,6 +80,9 @@
|
|||
<Name>MediaBrowser.Model</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Upnp\AsyncResults\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- 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.
|
||||
|
|
|
@ -34,6 +34,7 @@ using System.Linq;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
@ -46,8 +47,6 @@ namespace Mono.Nat
|
|||
public static event EventHandler<DeviceEventArgs> DeviceFound;
|
||||
public static event EventHandler<DeviceEventArgs> DeviceLost;
|
||||
|
||||
public static event EventHandler<UnhandledExceptionEventArgs> UnhandledException;
|
||||
|
||||
private static List<ISearcher> controllers;
|
||||
private static bool verbose;
|
||||
|
||||
|
@ -87,9 +86,8 @@ namespace Mono.Nat
|
|||
DeviceLost(sender, args);
|
||||
};
|
||||
});
|
||||
Thread t = new Thread(SearchAndListen);
|
||||
t.IsBackground = true;
|
||||
t.Start();
|
||||
|
||||
Task.Factory.StartNew(SearchAndListen, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
internal static void Log(string format, params object[] args)
|
||||
|
@ -99,7 +97,7 @@ namespace Mono.Nat
|
|||
logger.Debug(format, args);
|
||||
}
|
||||
|
||||
private static void SearchAndListen()
|
||||
private static async Task SearchAndListen()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
|
@ -115,18 +113,19 @@ namespace Mono.Nat
|
|||
}
|
||||
|
||||
foreach (ISearcher s in controllers)
|
||||
{
|
||||
if (s.NextSearch < DateTime.Now && enabledProtocols.Contains(s.Protocol))
|
||||
{
|
||||
Log("Searching for: {0}", s.GetType().Name);
|
||||
s.Search();
|
||||
s.Search();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (UnhandledException != null)
|
||||
UnhandledException(typeof(NatUtility), new UnhandledExceptionEventArgs(e, false));
|
||||
|
||||
}
|
||||
Thread.Sleep(10);
|
||||
await Task.Delay(100).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,234 +34,166 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Mono.Nat.Pmp
|
||||
{
|
||||
internal sealed class PmpNatDevice : AbstractNatDevice, IEquatable<PmpNatDevice>
|
||||
{
|
||||
private AsyncResult externalIpResult;
|
||||
private bool pendingOp;
|
||||
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; }
|
||||
}
|
||||
internal sealed class PmpNatDevice : AbstractNatDevice, IEquatable<PmpNatDevice>
|
||||
{
|
||||
private IPAddress localAddress;
|
||||
private IPAddress publicAddress;
|
||||
|
||||
public override Task CreatePortMap(Mapping mapping)
|
||||
{
|
||||
CreatePortMap(mapping, true);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
private void StartOp(ref AsyncResult result, AsyncCallback callback, object asyncState)
|
||||
internal PmpNatDevice(IPAddress localAddress, IPAddress publicAddress)
|
||||
{
|
||||
if (pendingOp == true)
|
||||
throw new InvalidOperationException("Can only have one simultaenous async operation");
|
||||
|
||||
pendingOp = true;
|
||||
result = new AsyncResult(callback, asyncState);
|
||||
this.localAddress = localAddress;
|
||||
this.publicAddress = publicAddress;
|
||||
}
|
||||
|
||||
private void EndOp(IAsyncResult supplied, ref AsyncResult actual)
|
||||
public override IPAddress LocalAddress
|
||||
{
|
||||
if (supplied == null)
|
||||
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;
|
||||
get { return localAddress; }
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return (other == null) ? false : this.publicAddress.Equals(other.publicAddress);
|
||||
}
|
||||
public override Task CreatePortMap(Mapping mapping)
|
||||
{
|
||||
return InternalCreatePortMapAsync(mapping, true);
|
||||
}
|
||||
|
||||
private Mapping CreatePortMap (Mapping mapping, bool create)
|
||||
{
|
||||
List<byte> package = new List<byte> ();
|
||||
|
||||
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)));
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
PmpNatDevice device = obj as PmpNatDevice;
|
||||
return (device == null) ? false : this.Equals(device);
|
||||
}
|
||||
|
||||
CreatePortMapAsyncState state = new CreatePortMapAsyncState ();
|
||||
state.Buffer = package.ToArray ();
|
||||
state.Mapping = mapping;
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.publicAddress.GetHashCode();
|
||||
}
|
||||
|
||||
ThreadPool.QueueUserWorkItem (new WaitCallback (CreatePortMapAsync), state);
|
||||
WaitHandle.WaitAll (new WaitHandle[] {state.ResetEvent});
|
||||
|
||||
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);
|
||||
public bool Equals(PmpNatDevice other)
|
||||
{
|
||||
return (other == null) ? false : this.publicAddress.Equals(other.publicAddress);
|
||||
}
|
||||
|
||||
int attempt = 0;
|
||||
int delay = PmpConstants.RetryDelay;
|
||||
|
||||
ThreadPool.QueueUserWorkItem (new WaitCallback (CreatePortMapListen), listenState);
|
||||
private async Task<Mapping> InternalCreatePortMapAsync(Mapping mapping, bool create)
|
||||
{
|
||||
var package = new List<byte>();
|
||||
|
||||
while (attempt < PmpConstants.RetryAttempts && !listenState.Success) {
|
||||
udpClient.Send (state.Buffer, state.Buffer.Length, new IPEndPoint (localAddress, PmpConstants.ServerPort));
|
||||
listenState.UdpClientReady.Set();
|
||||
package.Add(PmpConstants.Version);
|
||||
package.Add(mapping.Protocol == Protocol.Tcp ? PmpConstants.OperationCodeTcp : PmpConstants.OperationCodeUdp);
|
||||
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++;
|
||||
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)
|
||||
try
|
||||
{
|
||||
byte[] data;
|
||||
try
|
||||
byte[] buffer = package.ToArray();
|
||||
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;
|
||||
return;
|
||||
var errors = new[]
|
||||
{
|
||||
"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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lifetime == 0) return; //mapping was deleted
|
||||
|
||||
//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>
|
||||
/// Overridden.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString( )
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format( "PmpNatDevice - Local Address: {0}, Public IP: {1}, Last Seen: {2}",
|
||||
this.localAddress, this.publicAddress, this.LastSeen );
|
||||
return String.Format("PmpNatDevice - Local Address: {0}, Public IP: {1}, Last Seen: {2}",
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -59,10 +59,5 @@ namespace Mono.Nat.Upnp
|
|||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override System.Net.WebRequest Encode(out byte[] body)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
var req = new HttpRequestOptions();
|
||||
|
|
|
@ -71,25 +71,5 @@ namespace Mono.Nat.Upnp
|
|||
writer.Flush();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -44,10 +44,5 @@ namespace Mono.Nat.Upnp
|
|||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override System.Net.WebRequest Encode(out byte[] body)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -45,33 +45,6 @@ namespace Mono.Nat.Upnp
|
|||
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)
|
||||
{
|
||||
string ss = "http://" + this.device.HostEndPoint.ToString() + this.device.ControlUrl;
|
||||
|
@ -98,50 +71,7 @@ namespace Mono.Nat.Upnp
|
|||
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 WebRequest Encode(out byte[] body);
|
||||
|
||||
public virtual string Method
|
||||
{
|
||||
|
|
|
@ -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
|
||||
// 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);
|
||||
// This bit strings out the "http://" from the string
|
||||
|
@ -98,7 +98,7 @@ namespace Mono.Nat.Upnp
|
|||
this.localAddress = localAddress;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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
|
||||
// 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);
|
||||
// This bit strings out the "http://" from the string
|
||||
|
@ -171,189 +171,12 @@ namespace Mono.Nat.Upnp
|
|||
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)
|
||||
{
|
||||
CreatePortMappingMessage message = new CreatePortMappingMessage(mapping, localAddress, this);
|
||||
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)
|
||||
{
|
||||
UpnpNatDevice device = obj as UpnpNatDevice;
|
||||
|
@ -373,256 +196,6 @@ namespace Mono.Nat.Upnp
|
|||
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>
|
||||
/// Overridden.
|
||||
/// </summary>
|
||||
|
|
Loading…
Reference in New Issue
Block a user