Improved UPnP support #280

This commit is contained in:
MaxXor 2015-07-25 16:58:36 +02:00
parent 1b24fd8d65
commit 0251e42cd3
8 changed files with 205 additions and 117 deletions

View File

@ -74,7 +74,7 @@ protected override void OnPaint(PaintEventArgs e)
Alignment = StringAlignment.Center
});
}
catch (Exception ex)
catch (Exception)
{
g.DrawString(TabPages[i].Text, new Font(Font.FontFamily, Font.Size, FontStyle.Bold),
Brushes.Black, x2, new StringFormat
@ -118,7 +118,7 @@ protected override void OnPaint(PaintEventArgs e)
Alignment = StringAlignment.Center
});
}
catch (Exception ex)
catch (Exception)
{
g.DrawString(TabPages[i].Text, Font, Brushes.DimGray, x2, new StringFormat
{

View File

@ -1,94 +0,0 @@
using System.Collections.Generic;
using System.Threading;
using Mono.Nat;
namespace xServer.Core.Helper
{
internal static class UPnP
{
public static bool IsPortForwarded { get; private set; }
public static ushort Port { get; private set; }
private static readonly HashSet<INatDevice> Devices = new HashSet<INatDevice>();
private static bool _eventSub;
private static bool _isDiscovering;
private static readonly object _isDiscoveringLock = new object();
public static void ForwardPort(ushort port)
{
lock (_isDiscoveringLock)
{
if (_isDiscovering) return;
_isDiscovering = true;
}
Port = port;
if (!_eventSub)
{
NatUtility.DeviceFound += DeviceFound;
NatUtility.DeviceLost += DeviceLost;
_eventSub = true;
}
new Thread(() =>
{
NatUtility.StartDiscovery();
int trys = 0;
while (Devices.Count == 0 && trys < 8) // wait until first device found
{
trys++;
Thread.Sleep(1000);
}
if (Devices.Count > 0)
{
try
{
foreach (var device in Devices)
{
if (device.GetSpecificMapping(Protocol.Tcp, Port).PublicPort < 0) // if port is not mapped
{
device.CreatePortMap(new Mapping(Protocol.Tcp, Port, Port));
IsPortForwarded = true;
}
}
}
catch (MappingException)
{
IsPortForwarded = false;
}
}
NatUtility.StopDiscovery();
lock (_isDiscoveringLock)
{
_isDiscovering = false;
}
}).Start();
}
private static void DeviceFound(object sender, DeviceEventArgs args)
{
Devices.Add(args.Device);
}
private static void DeviceLost(object sender, DeviceEventArgs args)
{
Devices.Remove(args.Device);
}
public static void RemovePort()
{
foreach (var device in Devices)
{
if (device.GetSpecificMapping(Protocol.Tcp, Port).PublicPort > 0) // if port map exists
{
device.DeletePortMap(new Mapping(Protocol.Tcp, Port, Port));
IsPortForwarded = false;
}
}
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using xServer.Core.Networking.Utilities;
using xServer.Core.Packets;
namespace xServer.Core.Networking

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
namespace xServer.Core.Networking
namespace xServer.Core.Networking.Utilities
{
/// <summary>
/// Implements a pool of byte arrays to improve allocation performance when parsing data.

View File

@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using Mono.Nat;
namespace xServer.Core.Networking.Utilities
{
internal static class UPnP
{
private static Dictionary<int, Mapping> _mappings;
private static bool _discoveryComplete;
private static INatDevice _device;
private static int _port = -1;
/// <summary>
/// Initializes the discovery of new UPnP devices.
/// </summary>
public static void Initialize()
{
_mappings = new Dictionary<int, Mapping>();
try
{
NatUtility.DeviceFound += DeviceFound;
NatUtility.DeviceLost += DeviceLost;
_discoveryComplete = false;
NatUtility.StartDiscovery();
}
catch (Exception)
{
}
}
/// <summary>
/// Initializes the discovery of new UPnP devices
/// and creates a port map with the given port.
/// </summary>
/// <param name="port">The port to map.</param>
public static void Initialize(int port)
{
_port = port;
Initialize();
}
/// <summary>
/// Tells if the class found an UPnP device.
/// </summary>
public static bool IsDeviceFound
{
get { return _device != null; }
}
/// <summary>
/// Creates a new port map.
/// </summary>
/// <param name="port">The port to map.</param>
/// <param name="externalPort">The port which has been mapped, -1 if it failed.</param>
/// <returns>True if successfull, else False.</returns>
public static bool CreatePortMap(int port, out int externalPort)
{
if (!_discoveryComplete)
{
externalPort = -1;
return false;
}
try
{
Mapping mapping = new Mapping(Protocol.Tcp, port, port);
_device.CreatePortMap(mapping);
if (_mappings.ContainsKey(mapping.PrivatePort))
_mappings[mapping.PrivatePort] = mapping;
else
_mappings.Add(mapping.PrivatePort, mapping);
externalPort = mapping.PublicPort;
return true;
}
catch (Exception)
{
externalPort = -1;
return false;
}
}
/// <summary>
/// Deletes an existing port map.
/// </summary>
/// <param name="port">The port to delete.</param>
public static void DeletePortMap(int port)
{
if (!_discoveryComplete)
return;
Mapping mapping;
if (_mappings.TryGetValue(port, out mapping))
{
try
{
_device.DeletePortMap(mapping);
}
catch (Exception)
{
}
}
}
private static void DeviceFound(object sender, DeviceEventArgs args)
{
_device = args.Device;
NatUtility.StopDiscovery();
_discoveryComplete = true;
if (_port > 0)
{
int outPort;
CreatePortMap(_port, out outPort);
}
}
private static void DeviceLost(object sender, DeviceEventArgs args)
{
_device = null;
_discoveryComplete = false;
}
}
}

View File

@ -9,6 +9,7 @@
using xServer.Core.Helper;
using xServer.Core.Misc;
using xServer.Core.Networking;
using xServer.Core.Networking.Utilities;
using xServer.Settings;
using UserStatus = xServer.Core.Commands.CommandHandler.UserStatus;
using ShutdownAction = xServer.Core.Commands.CommandHandler.ShutdownAction;
@ -109,16 +110,22 @@ private void InitializeServer()
ConServer.ClientDisconnected += ClientDisconnected;
}
private void FrmMain_Load(object sender, EventArgs e)
private void AutostartListeningP()
{
InitializeServer();
if (XMLSettings.AutoListen)
if (XMLSettings.AutoListen && XMLSettings.UseUPnP)
{
if (XMLSettings.UseUPnP)
UPnP.ForwardPort(ushort.Parse(XMLSettings.ListenPort.ToString()));
UPnP.Initialize(XMLSettings.ListenPort);
ConServer.Listen(XMLSettings.ListenPort);
}
else if (XMLSettings.AutoListen)
{
UPnP.Initialize();
ConServer.Listen(XMLSettings.ListenPort);
}
else
{
UPnP.Initialize();
}
if (XMLSettings.IntegrateNoIP)
{
@ -126,13 +133,16 @@ private void FrmMain_Load(object sender, EventArgs e)
}
}
private void FrmMain_Load(object sender, EventArgs e)
{
InitializeServer();
AutostartListeningP();
}
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
ConServer.Disconnect();
if (UPnP.IsPortForwarded)
UPnP.RemovePort();
UPnP.DeletePortMap(XMLSettings.ListenPort);
nIcon.Visible = false;
nIcon.Dispose();
Instance = null;

View File

@ -1,9 +1,9 @@
using System;
using System.Globalization;
using System.Windows.Forms;
using xServer.Core.Helper;
using xServer.Core.Misc;
using xServer.Core.Networking;
using xServer.Core.Networking.Utilities;
using xServer.Settings;
namespace xServer.Forms
@ -42,17 +42,49 @@ private void FrmSettings_Load(object sender, EventArgs e)
txtNoIPPass.Text = XMLSettings.NoIPPassword;
}
private ushort GetPortSafe()
{
var portValue = ncPort.Value.ToString(CultureInfo.InvariantCulture);
ushort port;
return (!ushort.TryParse(portValue, out port)) ? (ushort)0 : port;
}
private void btnListen_Click(object sender, EventArgs e)
{
ushort port = GetPortSafe();
if (port == 0)
{
MessageBox.Show("Please enter a valid port > 0.", "Please enter a valid port", MessageBoxButtons.OK,
MessageBoxIcon.Warning);
return;
}
if (btnListen.Text == "Start listening" && !_listenServer.Listening)
{
try
{
if (chkUseUpnp.Checked && !UPnP.IsPortForwarded)
UPnP.ForwardPort(ushort.Parse(ncPort.Value.ToString(CultureInfo.InvariantCulture)));
if (chkUseUpnp.Checked)
{
if (!UPnP.IsDeviceFound)
{
MessageBox.Show("No available UPnP device found!", "No UPnP device", MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
else
{
int outPort;
UPnP.CreatePortMap(port, out outPort);
if (port != outPort)
{
MessageBox.Show("Creating a port map with the UPnP device failed!\nPlease check if your device allows to create new port maps.", "Creating port map failed", MessageBoxButtons.OK,
MessageBoxIcon.Warning);
}
}
}
if(chkNoIPIntegration.Checked)
NoIpUpdater.Start();
_listenServer.Listen(ushort.Parse(ncPort.Value.ToString(CultureInfo.InvariantCulture)));
_listenServer.Listen(port);
}
finally
{
@ -66,8 +98,7 @@ private void btnListen_Click(object sender, EventArgs e)
try
{
_listenServer.Disconnect();
if (UPnP.IsPortForwarded)
UPnP.RemovePort();
UPnP.DeletePortMap(port);
}
finally
{
@ -80,8 +111,17 @@ private void btnListen_Click(object sender, EventArgs e)
private void btnSave_Click(object sender, EventArgs e)
{
XMLSettings.WriteValue("ListenPort", ncPort.Value.ToString(CultureInfo.InvariantCulture));
XMLSettings.ListenPort = ushort.Parse(ncPort.Value.ToString(CultureInfo.InvariantCulture));
ushort port = GetPortSafe();
if (port == 0)
{
MessageBox.Show("Please enter a valid port > 0.", "Please enter a valid port", MessageBoxButtons.OK,
MessageBoxIcon.Warning);
return;
}
XMLSettings.WriteValue("ListenPort", port.ToString());
XMLSettings.ListenPort = port;
XMLSettings.WriteValue("AutoListen", chkAutoListen.Checked.ToString());
XMLSettings.AutoListen = chkAutoListen.Checked;

View File

@ -85,7 +85,7 @@
<Compile Include="Core\Encryption\AES.cs" />
<Compile Include="Core\Helper\FileSplit.cs" />
<Compile Include="Core\Helper\UnsafeStreamCodec.cs" />
<Compile Include="Core\Helper\UPnP.cs" />
<Compile Include="Core\Networking\Utilities\UPnP.cs" />
<Compile Include="Core\Misc\InputBox.cs" />
<Compile Include="Core\Compression\JpgCompression.cs" />
<Compile Include="Core\Misc\ListViewColumnSorter.cs" />
@ -93,7 +93,7 @@
<Compile Include="Core\Misc\NoIpUpdater.cs" />
<Compile Include="Core\Misc\SavedVariables.cs" />
<Compile Include="Core\Networking\ConnectionHandler.cs" />
<Compile Include="Core\Networking\PooledBufferManager.cs" />
<Compile Include="Core\Networking\Utilities\PooledBufferManager.cs" />
<Compile Include="Core\Packets\ClientPackets\GetDesktopResponse.cs" />
<Compile Include="Core\Packets\ClientPackets\GetDirectoryResponse.cs" />
<Compile Include="Core\Packets\ClientPackets\DoDownloadFileResponse.cs" />