UPnP source code fixes
as suggested by Chris Forbes
This commit is contained in:
committed by
Chris Forbes
parent
a831e72fa2
commit
aa36a56b27
@@ -29,6 +29,7 @@ namespace OpenRA.GameRules
|
||||
public int ExternalPort = 1234;
|
||||
public bool AdvertiseOnline = true;
|
||||
public string MasterServer = "http://master.open-ra.org/";
|
||||
public bool AllowUPnP = true;
|
||||
public bool AllowCheats = false;
|
||||
public string Map = null;
|
||||
public string[] Ban = null;
|
||||
@@ -42,6 +43,7 @@ namespace OpenRA.GameRules
|
||||
ExternalPort = other.ExternalPort;
|
||||
AdvertiseOnline = other.AdvertiseOnline;
|
||||
MasterServer = other.MasterServer;
|
||||
AllowUPnP = other.AllowUPnP;
|
||||
AllowCheats = other.AllowCheats;
|
||||
Map = other.Map;
|
||||
Ban = other.Ban;
|
||||
|
||||
@@ -142,6 +142,7 @@
|
||||
<Compile Include="Server\Server.cs" />
|
||||
<Compile Include="Server\ServerOrder.cs" />
|
||||
<Compile Include="Server\TraitInterfaces.cs" />
|
||||
<Compile Include="Server\UPnP.cs" />
|
||||
<Compile Include="Sound.cs" />
|
||||
<Compile Include="Support\Arguments.cs" />
|
||||
<Compile Include="Support\PerfHistory.cs" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#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
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
@@ -13,9 +13,13 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Net.NetworkInformation;
|
||||
using UPnP;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Network;
|
||||
@@ -64,6 +68,9 @@ namespace OpenRA.Server
|
||||
|
||||
randomSeed = (int)DateTime.Now.ToBinary();
|
||||
|
||||
if (settings.AllowUPnP)
|
||||
PortForward();
|
||||
|
||||
foreach (var trait in modData.Manifest.ServerTraits)
|
||||
ServerTraits.Add( modData.ObjectCreator.CreateObject<ServerTrait>(trait) );
|
||||
|
||||
@@ -123,6 +130,20 @@ namespace OpenRA.Server
|
||||
} ) { 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:
|
||||
* - "teams together" option for team games -- will eliminate most need
|
||||
* for manual spawnpoint choosing.
|
||||
|
||||
139
OpenRA.Game/Server/UPnP.cs
Normal file
139
OpenRA.Game/Server/UPnP.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#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
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
@@ -22,6 +22,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
Action onExit;
|
||||
Map map;
|
||||
bool advertiseOnline;
|
||||
bool allowUPnP;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
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");
|
||||
advertiseCheckbox.IsChecked = () => advertiseOnline;
|
||||
advertiseCheckbox.OnClick = () => advertiseOnline ^= true;
|
||||
|
||||
var UPnPCheckbox = panel.Get<CheckboxWidget>("UPNP_CHECKBOX");
|
||||
UPnPCheckbox.IsChecked = () => allowUPnP;
|
||||
UPnPCheckbox.OnClick = () => allowUPnP ^= true;
|
||||
}
|
||||
|
||||
void CreateAndJoin()
|
||||
@@ -81,6 +86,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
Game.Settings.Server.ListenPort = listenPort;
|
||||
Game.Settings.Server.ExternalPort = externalPort;
|
||||
Game.Settings.Server.AdvertiseOnline = advertiseOnline;
|
||||
Game.Settings.Server.AllowUPnP = allowUPnP;
|
||||
Game.Settings.Server.Map = map.Uid;
|
||||
Game.Settings.Save();
|
||||
|
||||
|
||||
@@ -60,6 +60,12 @@ Background@CREATESERVER_BG:
|
||||
Width:300
|
||||
Height:20
|
||||
Text:Advertise game Online
|
||||
Checkbox@UPNP_CHECKBOX:
|
||||
X:165
|
||||
Y:165
|
||||
Width:300
|
||||
Height:20
|
||||
Text:Allow UPnP port forwarding
|
||||
Button@CREATE_BUTTON:
|
||||
X:130
|
||||
Y:PARENT_BOTTOM - 45
|
||||
|
||||
Reference in New Issue
Block a user