mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-07-08 23:00:51 +02:00
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;
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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();
|
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()
|
public override HttpRequestOptions Encode()
|
||||||
{
|
{
|
||||||
var req = new HttpRequestOptions();
|
var req = new HttpRequestOptions();
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
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;
|
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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue