fixed UPnP more crashes

proper timeout: does not crash/lag if no UPnP devices are found
close all ports, sockets, responses after they have been used
so it does not crash when port forwarding for the 2nd time
put all exceptions logs into OpenRA sourcecode
This commit is contained in:
Matthias Mailänder
2012-07-15 22:31:02 +02:00
parent 5e6b8deec1
commit 3728685c67
3 changed files with 132 additions and 68 deletions

View File

@@ -29,7 +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 AllowUPnP = false;
public bool AllowCheats = false;
public string Map = null;
public string[] Ban = null;

View File

@@ -52,7 +52,10 @@ namespace OpenRA.Server
XTimer gameTimeout;
volatile bool shutdown = false;
public void Shutdown() { shutdown = true; }
public void Shutdown()
{
shutdown = true;
}
public Server(IPEndPoint endpoint, string[] mods, ServerSettings settings, ModData modData)
{
@@ -69,8 +72,43 @@ namespace OpenRA.Server
randomSeed = (int)DateTime.Now.ToBinary();
if (settings.AllowUPnP)
PortForward();
if (Settings.AllowUPnP)
{
try
{
if (UPnP.NAT.Discover())
{
Log.Write("server", "UPnP-enabled router discovered.");
Log.Write("server", "Your IP is: {0}", UPnP.NAT.GetExternalIP() );
}
else
{
Log.Write("server", "No UPnP-enabled router detected.");
Settings.AllowUPnP = false;
}
}
catch (Exception e)
{
OpenRA.Log.Write("server", "Can't discover UPnP-enabled routers: {0}", e);
Settings.AllowUPnP = false;
}
}
if (Settings.AllowUPnP)
{
try
{
if (UPnP.NAT.ForwardPort(Port, ProtocolType.Tcp, "OpenRA"))
Log.Write("server", "Port {0} (TCP) has been forwarded.", Port);
else
Settings.AllowUPnP = false;
}
catch (Exception e)
{
OpenRA.Log.Write("server", "Can not forward ports via UPnP: {0}", e);
Settings.AllowUPnP = false;
}
}
foreach (var trait in modData.Manifest.ServerTraits)
ServerTraits.Add( modData.ObjectCreator.CreateObject<ServerTrait>(trait) );
@@ -102,7 +140,11 @@ namespace OpenRA.Server
Socket.Select( checkRead, null, null, timeout );
if (shutdown)
{
if (Settings.AllowUPnP)
RemovePortforward();
break;
}
foreach( Socket s in checkRead )
if( s == listener.Server ) AcceptConnection();
@@ -117,7 +159,11 @@ namespace OpenRA.Server
t.Tick(this);
if (shutdown)
{
if (Settings.AllowUPnP)
RemovePortforward();
break;
}
}
GameStarted = false;
@@ -129,20 +175,20 @@ namespace OpenRA.Server
try { listener.Stop(); }
catch { }
} ) { IsBackground = true }.Start();
}
void PortForward()
void RemovePortforward()
{
if (UPnP.NAT.Discover())
try
{
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() );
if (UPnP.NAT.DeleteForwardingRule(Port, ProtocolType.Tcp))
Log.Write("server", "Port {0} (TCP) forwarding rules has been removed.", Port);
}
catch (Exception e)
{
OpenRA.Log.Write("server", "Can not remove UPnP portforwarding rules: {0}", e);
}
else
Log.Write("server", "No UPnP-enabled router detected.");
return;
}
/* lobby rework todo:

View File

@@ -19,13 +19,13 @@ 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);
s.ReceiveTimeout = 3000; //3 seconds
string req = "M-SEARCH * HTTP/1.1\r\n" +
"HOST: 239.255.255.250:1900\r\n" +
"ST:upnp:rootdevice\r\n" +
@@ -35,56 +35,62 @@ namespace UPnP
IPEndPoint ipe = new IPEndPoint(IPAddress.Broadcast, 1900);
byte[] buffer = new byte[0x1000];
DateTime start = DateTime.Now;
try
{
s.SendTo(data, ipe);
int length = 0;
do
{
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);
}
catch (Exception e)
{
OpenRA.Log.Write("server", "UPNP discover failed: {0}", e);
}
length = s.Receive(buffer);
return false;
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)))
{
s.Close();
return true;
}
}
} while (length > 0);
s.Close();
return false;
}
catch
{
s.Close();
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;
HttpWebRequest r = (HttpWebRequest)WebRequest.Create(resp);
r.KeepAlive = false;
using (WebResponse wres = r.GetResponse())
{
using (Stream ress = wres.GetResponseStream())
{
desc.Load(ress);
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)
public static bool ForwardPort(int port, ProtocolType protocol, string description)
{
if (string.IsNullOrEmpty(_serviceUrl))
throw new Exception("No UPnP service available or Discover() has not been called");
@@ -95,17 +101,23 @@ namespace UPnP
"<NewPortMappingDescription>{3}</NewPortMappingDescription>"+
"<NewLeaseDuration>0</NewLeaseDuration></u:AddPortMapping>",
port, protocol.ToString().ToUpper(),Dns.GetHostAddresses(Dns.GetHostName())[0], description);
SOAPRequest(_serviceUrl, body, "AddPortMapping");
if (SOAPRequest(_serviceUrl, body, "AddPortMapping") != null)
return true;
else
return false;
}
public static void DeleteForwardingRule(int port, ProtocolType protocol)
public static bool 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");
if (SOAPRequest(_serviceUrl, body, "DeletePortMapping") != null)
return true;
else
return false;
}
public static IPAddress GetExternalIP()
@@ -122,24 +134,30 @@ namespace UPnP
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);
string body = "<?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>";
HttpWebRequest r = (HttpWebRequest)WebRequest.Create(url);
r.KeepAlive = false;
r.Method = "POST";
byte[] b = Encoding.UTF8.GetBytes(req);
byte[] b = Encoding.UTF8.GetBytes(body);
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);
Stream newStream = r.GetRequestStream();
newStream.Write(b, 0, b.Length);
XmlDocument resp = new XmlDocument();
WebResponse wres = r.GetResponse();
Stream ress = wres.GetResponseStream();
resp.Load(ress);
return resp;
using (WebResponse wres = r.GetResponse())
{
using (Stream ress = wres.GetResponseStream())
{
resp.Load(ress);
return resp;
}
}
}
}
}