#region Copyright & License Information /* * Copyright 2007-2022 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, either version 3 of * the License, or (at your option) any later version. For more * information, see COPYING. */ #endregion using System; using System.Threading; using Mono.Nat; namespace OpenRA.Network { public enum NatStatus { Enabled, Disabled, NotSupported } public class Nat { public static NatStatus Status => NatUtility.IsSearching ? natDevice != null ? NatStatus.Enabled : NatStatus.NotSupported : NatStatus.Disabled; static Mapping mapping; static INatDevice natDevice; static bool initialized; public static void Initialize() { if (initialized) return; if (Game.Settings.Server.DiscoverNatDevices) { NatUtility.DeviceFound += DeviceFound; NatUtility.StartDiscovery(); } initialized = true; } static readonly SemaphoreSlim Locker = new SemaphoreSlim(1, 1); static async void DeviceFound(object sender, DeviceEventArgs args) { await Locker.WaitAsync(); try { // Only interact with one at a time. Some support both UPnP and NAT-PMP. natDevice = args.Device; Log.Write("nat", "Device found: {0}", natDevice.DeviceEndpoint); Log.Write("nat", "Type: {0}", natDevice.NatProtocol); } finally { Locker.Release(); } } public static bool TryForwardPort(int listen, int external) { if (natDevice == null) return false; var lifetime = Game.Settings.Server.NatPortMappingLifetime; mapping = new Mapping(Protocol.Tcp, listen, external, lifetime, "OpenRA"); try { natDevice.CreatePortMap(mapping); } catch (Exception e) { Console.WriteLine("Port forwarding failed: {0}", e.Message); Log.Write("nat", e.StackTrace); return false; } return true; } public static bool TryRemovePortForward() { if (natDevice == null) return false; try { natDevice.DeletePortMap(mapping); } catch (Exception e) { Console.WriteLine("Port removal failed: {0}", e.Message); Log.Write("nat", e.StackTrace); return false; } return true; } } }