Status host uses net.port by default, add UPnP port forwarding option (#2237)

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
This commit is contained in:
20kdc
2021-11-21 14:19:39 +00:00
committed by GitHub
parent 3e718575ff
commit e7a0409645
6 changed files with 106 additions and 7 deletions

View File

@@ -156,6 +156,9 @@ namespace Robust.Server.ServerStatus
private void RegisterCVars()
{
// Set status host binding to match network manager by default
SetCVarIfUnmodified(CVars.StatusBind, $"*:{_netManager.Port}");
// Check build.json
var path = PathHelpers.ExecutableRelativeFile("build.json");
if (File.Exists(path))

View File

@@ -1,4 +1,7 @@
# Welcome to the example configuration file!
# Remember that if this is in bin/Content.Server or such, it may be overwritten on build.
# Consider copying it and using the --config-file and --data-dir options.
[log]
path = "logs"
format = "log_%(date)s-%(time)s.txt"
@@ -9,19 +12,27 @@ enabled = false
tickrate = 60
port = 1212
bindto = "::,0.0.0.0"
# Automatic port forwarding!
# Disabled by default because you may not want to do this.
# upnp = true
# The status server is the TCP side, used by the launcher to determine engine version, etc.
[status]
# The status server is the TCP side, used by the launcher to determine engine version, etc.
# To be clear: Disabling it makes the launcher unable to connect!
enabled = true
bind = "*:1212"
# This is the address and port the status server binds to.
# The port is by default set based on net.port so it will follow what you set there.
# bind = "*:1212"
# This is the address of the SS14 server as the launcher uses it.
# This is only needed if you're proxying the status HTTP server.
# This is only needed if you're proxying the status HTTP server -
# by default the launcher will assume the address and port match that of the status server.
# connectaddress = "udp://localhost:1212"
[game]
hostname = "MyServer"
# map = "maps/saltern.yml"
# map = "Maps/saltern.yml"
maxplayers = 64
type = 1
welcomemsg = "Welcome to the server!"
@@ -62,6 +73,7 @@ loginlocal = true
# Build hash - this is a *capitalized* SHA256 hash of the client ZIP.
# Optional in any case and automatically set if hosting a client ZIP.
# This hash is an example only.
# build = "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855"
[auth]

View File

@@ -113,6 +113,12 @@ namespace Robust.Shared
public static readonly CVarDef<bool> NetEncrypt =
CVarDef.Create("net.encrypt", true, CVar.CLIENTONLY);
/// <summary>
/// If true, use UPnP to automatically forward ports on startup if possible.
/// </summary>
public static readonly CVarDef<bool> NetUPnP =
CVarDef.Create("net.upnp", false, CVar.SERVERONLY);
/**
* SUS
*/
@@ -163,8 +169,10 @@ namespace Robust.Shared
public static readonly CVarDef<bool> StatusEnabled =
CVarDef.Create("status.enabled", true, CVar.ARCHIVE | CVar.SERVERONLY);
// Example: *:1212
// But this is now autogenerated by default to match NetPort
public static readonly CVarDef<string> StatusBind =
CVarDef.Create("status.bind", "*:1212", CVar.ARCHIVE | CVar.SERVERONLY);
CVarDef.Create("status.bind", "", CVar.ARCHIVE | CVar.SERVERONLY);
public static readonly CVarDef<int> StatusMaxConnections =
CVarDef.Create("status.max_connections", 5, CVar.SERVERONLY);

View File

@@ -0,0 +1,70 @@
using System;
using System.Linq;
using System.Net.Sockets;
using System.Threading;
using Lidgren.Network;
using Robust.Shared.Log;
namespace Robust.Shared.Network;
public partial class NetManager
{
private void InitUpnp()
{
var sawmill = Logger.GetSawmill("net.upnp");
var port = Port;
var peers = _netPeers.Select(p => p.Peer).Where(p => p.Configuration.EnableUPnP).ToArray();
if (peers.Length == 0)
{
sawmill.Warning("Can't UPnP forward: No IPv4-compatible NetPeers available.");
return;
}
// We DON'T want to hold up the main server on this!
new Thread(() =>
{
try
{
foreach (var peer in peers)
{
// The way the NetUPnP code is written, we're doing guesswork anyway
// It seems to be that the assumption in regards to IPv6 is "what?" w/ UPnP????
// ATTENTION FUTURE 20KDC (or anyone else who comes by):
// IF YOU GET IPv6 FOR REALSIES, WORK OUT HOW TO DEAL W/ THIS!
var upnp = peer.UPnP;
while (upnp.Status == UPnPStatus.Discovering)
{
// Sleep while the network thread does the work
NetUtility.Sleep(250);
}
// Clear these forwarding rules because we don't want any OTHER SS14 servers on our network (or different local IP addresses of ourself) conflicting
upnp.DeleteForwardingRule(port, "UDP");
upnp.DeleteForwardingRule(port, "TCP");
var udpRes = upnp.ForwardPort(port, "RobustToolbox UDP", 0, "UDP");
var tcpRes = upnp.ForwardPort(port, "RobustToolbox TCP", 0, "TCP");
// Message needs to show in warning if something went wrong
var message = $"UPnP setup for port {port} on peer {peer.Configuration.LocalAddress} results: TCP {tcpRes}, UDP {udpRes}";
if (tcpRes && udpRes)
{
sawmill.Info(message);
}
else
{
sawmill.Warning(message);
}
}
}
catch (Exception e)
{
sawmill.Warning($"UPnP threw an exception: {e}");
}
}).Start();
}
private static bool UpnpCompatible(NetPeerConfiguration cfg)
{
return cfg.LocalAddress.AddressFamily == AddressFamily.InterNetwork || cfg.DualStack;
}
}

View File

@@ -349,6 +349,9 @@ namespace Robust.Shared.Network
config.DualStack = true;
}
if (UpnpCompatible(config))
config.EnableUPnP = true;
var peer = IsServer ? (NetPeer) new NetServer(config) : new NetClient(config);
peer.Start();
_netPeers.Add(new NetPeerData(peer));
@@ -365,6 +368,9 @@ namespace Robust.Shared.Network
Logger.WarningS("net",
"IPv6 Dual Stack is enabled but no IPv6 addresses have been bound to. This will not work.");
}
if (_config.GetCVar(CVars.NetUPnP))
InitUpnp();
}
/// <inheritdoc />