UPnP source code fixes

as suggested by Chris Forbes
This commit is contained in:
Matthias Mailänder
2012-05-21 17:06:03 +02:00
committed by Chris Forbes
parent a831e72fa2
commit aa36a56b27
6 changed files with 177 additions and 2 deletions

View File

@@ -29,6 +29,7 @@ namespace OpenRA.GameRules
public int ExternalPort = 1234; public int ExternalPort = 1234;
public bool AdvertiseOnline = true; public bool AdvertiseOnline = true;
public string MasterServer = "http://master.open-ra.org/"; public string MasterServer = "http://master.open-ra.org/";
public bool AllowUPnP = true;
public bool AllowCheats = false; public bool AllowCheats = false;
public string Map = null; public string Map = null;
public string[] Ban = null; public string[] Ban = null;
@@ -42,6 +43,7 @@ namespace OpenRA.GameRules
ExternalPort = other.ExternalPort; ExternalPort = other.ExternalPort;
AdvertiseOnline = other.AdvertiseOnline; AdvertiseOnline = other.AdvertiseOnline;
MasterServer = other.MasterServer; MasterServer = other.MasterServer;
AllowUPnP = other.AllowUPnP;
AllowCheats = other.AllowCheats; AllowCheats = other.AllowCheats;
Map = other.Map; Map = other.Map;
Ban = other.Ban; Ban = other.Ban;

View File

@@ -142,6 +142,7 @@
<Compile Include="Server\Server.cs" /> <Compile Include="Server\Server.cs" />
<Compile Include="Server\ServerOrder.cs" /> <Compile Include="Server\ServerOrder.cs" />
<Compile Include="Server\TraitInterfaces.cs" /> <Compile Include="Server\TraitInterfaces.cs" />
<Compile Include="Server\UPnP.cs" />
<Compile Include="Sound.cs" /> <Compile Include="Sound.cs" />
<Compile Include="Support\Arguments.cs" /> <Compile Include="Support\Arguments.cs" />
<Compile Include="Support\PerfHistory.cs" /> <Compile Include="Support\PerfHistory.cs" />

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2012 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -13,9 +13,13 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Net.NetworkInformation;
using UPnP;
using System.Threading; using System.Threading;
using System.Xml;
using OpenRA.FileFormats; using OpenRA.FileFormats;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Network; using OpenRA.Network;
@@ -64,6 +68,9 @@ namespace OpenRA.Server
randomSeed = (int)DateTime.Now.ToBinary(); randomSeed = (int)DateTime.Now.ToBinary();
if (settings.AllowUPnP)
PortForward();
foreach (var trait in modData.Manifest.ServerTraits) foreach (var trait in modData.Manifest.ServerTraits)
ServerTraits.Add( modData.ObjectCreator.CreateObject<ServerTrait>(trait) ); ServerTraits.Add( modData.ObjectCreator.CreateObject<ServerTrait>(trait) );
@@ -123,6 +130,20 @@ namespace OpenRA.Server
} ) { IsBackground = true }.Start(); } ) { IsBackground = true }.Start();
} }
void PortForward()
{
if (UPnP.NAT.Discover())
{
Log.Write("server", "UPnP-enabled router discovered.");
UPnP.NAT.ForwardPort(Port, ProtocolType.Tcp, "OpenRA"); //might timeout after second try
Log.Write("server", "Port {0} (TCP) has been forwarded.", Port);
Log.Write("server", "Your IP is: {0}", UPnP.NAT.GetExternalIP() );
}
else
Log.Write("server", "No UPnP-enabled router detected.");
return;
}
/* lobby rework todo: /* lobby rework todo:
* - "teams together" option for team games -- will eliminate most need * - "teams together" option for team games -- will eliminate most need
* for manual spawnpoint choosing. * for manual spawnpoint choosing.

139
OpenRA.Game/Server/UPnP.cs Normal file
View File

@@ -0,0 +1,139 @@
#region Copyright & License Information
/*
* Copyright 2008-2009 http://www.codeproject.com/Members/Harold-Aptroot
* Source: http://www.codeproject.com/Articles/27992/NAT-Traversal-with-UPnP-in-C
* This file is licensed under A Public Domain dedication.
* For more information, see http://creativecommons.org/licenses/publicdomain/
*/
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Xml;
using System.IO;
namespace UPnP
{
public class NAT
{
public static TimeSpan _timeout = new TimeSpan(0, 0, 0, 3);
static string _serviceUrl;
public static bool Discover()
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
string req = "M-SEARCH * HTTP/1.1\r\n" +
"HOST: 239.255.255.250:1900\r\n" +
"ST:upnp:rootdevice\r\n" +
"MAN:\"ssdp:discover\"\r\n" +
"MX:3\r\n\r\n";
byte[] data = Encoding.ASCII.GetBytes(req);
IPEndPoint ipe = new IPEndPoint(IPAddress.Broadcast, 1900);
byte[] buffer = new byte[0x1000];
DateTime start = DateTime.Now;
do
{
s.SendTo(data, ipe);
s.SendTo(data, ipe);
s.SendTo(data, ipe);
int length = 0;
do
{
length = s.Receive(buffer);
string resp = Encoding.ASCII.GetString(buffer, 0, length).ToLower();
if (resp.Contains("upnp:rootdevice"))
{
resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9);
resp = resp.Substring(0, resp.IndexOf("\r")).Trim();
if (!string.IsNullOrEmpty(_serviceUrl = GetServiceUrl(resp)))
return true;
}
} while (length > 0);
} while ((start - DateTime.Now) < _timeout);
return false;
}
private static String GetServiceUrl(string resp)
{
XmlDocument desc = new XmlDocument();
desc.Load(WebRequest.Create(resp).GetResponse().GetResponseStream());
XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable);
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr);
if (!typen.Value.Contains("InternetGatewayDevice"))
return null;
XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:WANIPConnection:1\"]/tns:controlURL/text()", nsMgr);
if (node == null)
return null;
Uri respUri = new Uri(resp);
Uri combinedUri = new Uri(respUri, node.Value);
return combinedUri.AbsoluteUri;
}
public static void ForwardPort(int port, ProtocolType protocol, string description)
{
if (string.IsNullOrEmpty(_serviceUrl))
throw new Exception("No UPnP service available or Discover() has not been called");
string body = String.Format("<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">"+
"<NewRemoteHost></NewRemoteHost><NewExternalPort>{0}</NewExternalPort>"+
"<NewProtocol>{1}</NewProtocol><NewInternalPort>{0}</NewInternalPort>" +
"<NewInternalClient>{2}</NewInternalClient><NewEnabled>1</NewEnabled>" +
"<NewPortMappingDescription>{3}</NewPortMappingDescription>"+
"<NewLeaseDuration>0</NewLeaseDuration></u:AddPortMapping>",
port, protocol.ToString().ToUpper(),Dns.GetHostAddresses(Dns.GetHostName())[0], description);
SOAPRequest(_serviceUrl, body, "AddPortMapping");
}
public static void DeleteForwardingRule(int port, ProtocolType protocol)
{
if (string.IsNullOrEmpty(_serviceUrl))
throw new Exception("No UPnP service available or Discover() has not been called");
string body = String.Format("<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
"<NewRemoteHost></NewRemoteHost><NewExternalPort>{0}</NewExternalPort>"+
"<NewProtocol>{1}</NewProtocol></u:DeletePortMapping>", port, protocol.ToString().ToUpper() );
SOAPRequest(_serviceUrl, body, "DeletePortMapping");
}
public static IPAddress GetExternalIP()
{
if (string.IsNullOrEmpty(_serviceUrl))
throw new Exception("No UPnP service available or Discover() has not been called");
XmlDocument xdoc = SOAPRequest(_serviceUrl, "<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
"</u:GetExternalIPAddress>", "GetExternalIPAddress");
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable);
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
string IP = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value;
return IPAddress.Parse(IP);
}
private static XmlDocument SOAPRequest(string url, string soap, string function)
{
string req = "<?xml version=\"1.0\"?>" +
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
"<s:Body>" +
soap +
"</s:Body>" +
"</s:Envelope>";
WebRequest r = HttpWebRequest.Create(url);
r.Method = "POST";
byte[] b = Encoding.UTF8.GetBytes(req);
r.Headers.Add("SOAPACTION", "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + function + "\"");
r.ContentType = "text/xml; charset=\"utf-8\"";
r.ContentLength = b.Length;
r.GetRequestStream().Write(b, 0, b.Length);
XmlDocument resp = new XmlDocument();
WebResponse wres = r.GetResponse();
Stream ress = wres.GetResponseStream();
resp.Load(ress);
return resp;
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2012 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -22,6 +22,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
Action onExit; Action onExit;
Map map; Map map;
bool advertiseOnline; bool advertiseOnline;
bool allowUPnP;
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public ServerCreationLogic(Widget widget, Action onExit, Action openLobby) public ServerCreationLogic(Widget widget, Action onExit, Action openLobby)
@@ -64,6 +65,10 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var advertiseCheckbox = panel.Get<CheckboxWidget>("ADVERTISE_CHECKBOX"); var advertiseCheckbox = panel.Get<CheckboxWidget>("ADVERTISE_CHECKBOX");
advertiseCheckbox.IsChecked = () => advertiseOnline; advertiseCheckbox.IsChecked = () => advertiseOnline;
advertiseCheckbox.OnClick = () => advertiseOnline ^= true; advertiseCheckbox.OnClick = () => advertiseOnline ^= true;
var UPnPCheckbox = panel.Get<CheckboxWidget>("UPNP_CHECKBOX");
UPnPCheckbox.IsChecked = () => allowUPnP;
UPnPCheckbox.OnClick = () => allowUPnP ^= true;
} }
void CreateAndJoin() void CreateAndJoin()
@@ -81,6 +86,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
Game.Settings.Server.ListenPort = listenPort; Game.Settings.Server.ListenPort = listenPort;
Game.Settings.Server.ExternalPort = externalPort; Game.Settings.Server.ExternalPort = externalPort;
Game.Settings.Server.AdvertiseOnline = advertiseOnline; Game.Settings.Server.AdvertiseOnline = advertiseOnline;
Game.Settings.Server.AllowUPnP = allowUPnP;
Game.Settings.Server.Map = map.Uid; Game.Settings.Server.Map = map.Uid;
Game.Settings.Save(); Game.Settings.Save();

View File

@@ -60,6 +60,12 @@ Background@CREATESERVER_BG:
Width:300 Width:300
Height:20 Height:20
Text:Advertise game Online Text:Advertise game Online
Checkbox@UPNP_CHECKBOX:
X:165
Y:165
Width:300
Height:20
Text:Allow UPnP port forwarding
Button@CREATE_BUTTON: Button@CREATE_BUTTON:
X:130 X:130
Y:PARENT_BOTTOM - 45 Y:PARENT_BOTTOM - 45