diff --git a/OpenRA.Game/GameRules/Settings.cs b/OpenRA.Game/GameRules/Settings.cs index 2c748f7a22..cad1eaad5f 100644 --- a/OpenRA.Game/GameRules/Settings.cs +++ b/OpenRA.Game/GameRules/Settings.cs @@ -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; diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index 3879fd76ae..fcc89aab46 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -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(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: diff --git a/OpenRA.Game/Server/UPnP.cs b/OpenRA.Game/Server/UPnP.cs index 06f68b07de..a4c7a0951e 100644 --- a/OpenRA.Game/Server/UPnP.cs +++ b/OpenRA.Game/Server/UPnP.cs @@ -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 "{3}"+ "0", 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("" + "{0}"+ "{1}", 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 = "" + - "" + - "" + - soap + - "" + - ""; - WebRequest r = HttpWebRequest.Create(url); + string body = "" + + "" + + "" + + soap + + "" + + ""; + 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; + } + } } } }