Sanity check server addresses against common mistakes
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
|
||||
# Enables detailed tracebacks and an interactive Python console on errors.
|
||||
# Never use in production!
|
||||
DEBUG = False
|
||||
@@ -26,3 +25,6 @@ BANNED_SERVERS = []
|
||||
# This WILL cause problems such as mapgen, mods and privilege information missing from the list
|
||||
ALLOW_UPDATE_WITHOUT_OLD = False
|
||||
|
||||
# Reject servers with private addresses and domain names.
|
||||
# Enable this if you are running a list on the public internet.
|
||||
REJECT_PRIVATE_ADDRESSES = False
|
||||
|
||||
73
server.py
73
server.py
@@ -27,6 +27,28 @@ else:
|
||||
|
||||
# Helpers
|
||||
|
||||
# checkRequestAddress() error codes
|
||||
ADDR_IS_PRIVATE = 1
|
||||
ADDR_IS_INVALID = 2
|
||||
ADDR_IS_INVALID_PORT = 3
|
||||
ADDR_IS_UNICODE = 4
|
||||
ADDR_IS_EXAMPLE = 5
|
||||
|
||||
ADDR_ERROR_HELP_TEXTS = {
|
||||
ADDR_IS_PRIVATE: "The server_address you provided is private or local. "
|
||||
"It is only reachable in your local network.\n"
|
||||
"If you meant to host a public server, adjust the setting and make sure your "
|
||||
"firewall is permitting connections (e.g. port forwarding).",
|
||||
ADDR_IS_INVALID: "The server_address you provided is invalid.\n"
|
||||
"If you do not have a domain name or need to configure the external IP, "
|
||||
"try removing the setting from your configuration.",
|
||||
ADDR_IS_INVALID_PORT: "The server_address you provided is invalid.\n"
|
||||
"Note that the value must not include a port number.",
|
||||
ADDR_IS_UNICODE: "The server_address you provided includes Unicode characters.\n"
|
||||
"If you have a domain name please enter the punycode notation.",
|
||||
ADDR_IS_EXAMPLE: "The server_address you provided is an example value.",
|
||||
}
|
||||
|
||||
def geoip_lookup_continent(ip):
|
||||
if ip.startswith("::ffff:"):
|
||||
ip = ip[7:]
|
||||
@@ -133,6 +155,12 @@ def announce():
|
||||
else:
|
||||
return "Server to update not found."
|
||||
|
||||
# Since 'address' isn't the primary key it can change
|
||||
if action == "start" or old.get("address") != server.get("address"):
|
||||
err = checkRequestAddress(server)
|
||||
if err:
|
||||
return ADDR_ERROR_HELP_TEXTS[err], 400
|
||||
|
||||
server["update_time"] = int(time.time())
|
||||
|
||||
if action == "start":
|
||||
@@ -226,6 +254,51 @@ def serverUp(info):
|
||||
sock.close()
|
||||
|
||||
|
||||
def checkRequestAddress(server):
|
||||
# will fall back to IP of requester, can't possibly be wrong
|
||||
if "address" not in server or not server["address"]:
|
||||
return
|
||||
|
||||
name = server["address"].lower()
|
||||
|
||||
# example value from minetest.conf
|
||||
if name == "game.minetest.net":
|
||||
return ADDR_IS_EXAMPLE
|
||||
|
||||
# length limit for good measure
|
||||
if len(name) > 255:
|
||||
return ADDR_IS_INVALID
|
||||
# characters invalid in DNS names and IPs
|
||||
if any(c in name for c in " @#/*\"'\t\v\r\n\x00") or name.startswith("-"):
|
||||
return ADDR_IS_INVALID
|
||||
# if not ipv6, there must be at least one dot (two components)
|
||||
# Note: This is not actually true ('com' is valid domain), but we'll assume
|
||||
# nobody who owns a TLD will ever host a Minetest server on the root domain.
|
||||
# getaddrinfo also allows specifying IPs as integers, we don't want people
|
||||
# to do that either.
|
||||
if ":" not in name and "." not in name:
|
||||
return ADDR_IS_INVALID
|
||||
|
||||
if app.config["REJECT_PRIVATE_ADDRESSES"]:
|
||||
# private IPs (there are more but in practice these are 99% of cases)
|
||||
PRIVATE_NETS = ("10.", "192.168.", "127.", "0.")
|
||||
if any(name.startswith(s) for s in PRIVATE_NETS):
|
||||
return ADDR_IS_PRIVATE
|
||||
# reserved TLDs
|
||||
RESERVED_TLDS = (".localhost", ".local", ".internal")
|
||||
if name == "localhost" or any(name.endswith(s) for s in RESERVED_TLDS):
|
||||
return ADDR_IS_PRIVATE
|
||||
|
||||
# ipv4/domain with port -or- ipv6 bracket notation
|
||||
if ("." in name and ":" in name) or (":" in name and "[" in name):
|
||||
return ADDR_IS_INVALID_PORT
|
||||
|
||||
# unicode in hostname
|
||||
# Not sure about Python but the Minetest client definitely doesn't support it.
|
||||
if any(ord(c) > 127 for c in name):
|
||||
return ADDR_IS_UNICODE
|
||||
|
||||
|
||||
# fieldName: (Required, Type, SubType)
|
||||
fields = {
|
||||
"action": (True, "str"),
|
||||
|
||||
Reference in New Issue
Block a user