From 8ba286567dcb64b92840193f48e0c5d574451c9a Mon Sep 17 00:00:00 2001 From: powerjungle Date: Mon, 3 Jul 2023 20:24:34 +0200 Subject: [PATCH] Changes to server to allow better control over binding to interfaces (#582) * refactor: remove unused imports * server: new options to choose certain IP versions On some setups, IPv6 or IPv4 might be disabled in the OS. In my case IPv6 is disabled and this causes errors when starting the server. * server: add options to choose the address to bind to Sometimes a user might want to bind to localhost only for testing or have multiple interfaces per IP version and only one must be used. --- syncplay/ep_server.py | 31 +++++++++++++++++++++++++------ syncplay/messages_de.py | 4 ++++ syncplay/messages_en.py | 4 ++++ syncplay/messages_eo.py | 4 ++++ syncplay/messages_es.py | 4 ++++ syncplay/messages_fr.py | 4 ++++ syncplay/messages_it.py | 4 ++++ syncplay/messages_pt_BR.py | 4 ++++ syncplay/messages_pt_PT.py | 4 ++++ syncplay/messages_ru.py | 4 ++++ syncplay/messages_tr.py | 4 ++++ syncplay/messages_zh_CN.py | 4 ++++ syncplay/server.py | 16 +++++++++------- syncplay/utils.py | 1 - 14 files changed, 78 insertions(+), 14 deletions(-) diff --git a/syncplay/ep_server.py b/syncplay/ep_server.py index 548ddd0..10a74b5 100644 --- a/syncplay/ep_server.py +++ b/syncplay/ep_server.py @@ -6,19 +6,26 @@ from twisted.internet.error import CannotListenError from syncplay.server import SyncFactory, ConfigurationGetter -class ServerStatus: pass + +class ServerStatus: + listening6 = False + listening4 = False + def isListening6(f): ServerStatus.listening6 = True + def isListening4(f): ServerStatus.listening4 = True + def failed6(f): ServerStatus.listening6 = False print(f.value) print("IPv6 listening failed.") + def failed4(f): ServerStatus.listening4 = False if f.type is CannotListenError and ServerStatus.listening6: @@ -27,9 +34,11 @@ def failed4(f): print(f.value) print("IPv4 listening failed.") + def main(): argsGetter = ConfigurationGetter() args = argsGetter.getConfiguration() + factory = SyncFactory( args.port, args.password, @@ -45,15 +54,25 @@ def main(): args.stats_db_file, args.tls ) - endpoint6 = TCP6ServerEndpoint(reactor, int(args.port)) - endpoint6.listen(factory).addCallbacks(isListening6, failed6) - endpoint4 = TCP4ServerEndpoint(reactor, int(args.port)) - endpoint4.listen(factory).addCallbacks(isListening4, failed4) + + if args.ipv6_only is True: + endpoint6 = TCP6ServerEndpoint(reactor, int(args.port), interface=args.interface_ipv6) + endpoint6.listen(factory).addCallbacks(isListening6, failed6) + elif args.ipv4_only is True: + endpoint4 = TCP4ServerEndpoint(reactor, int(args.port), interface=args.interface_ipv4) + endpoint4.listen(factory).addCallbacks(isListening4, failed4) + else: + endpoint6 = TCP6ServerEndpoint(reactor, int(args.port), interface=args.interface_ipv6) + endpoint6.listen(factory).addCallbacks(isListening6, failed6) + endpoint4 = TCP4ServerEndpoint(reactor, int(args.port), interface=args.interface_ipv4) + endpoint4.listen(factory).addCallbacks(isListening4, failed4) + if ServerStatus.listening6 or ServerStatus.listening4: reactor.run() else: print("Unable to listen using either IPv4 and IPv6 protocols. Quitting the server now.") sys.exit() + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index 9718162..affa51f 100755 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -499,6 +499,10 @@ de = { "server-startTLS-argument": "Erlaube TLS-Verbindungen mit den Zertifikatdateien im Angegebenen Pfad", "server-messed-up-motd-unescaped-placeholders": "Die Nachricht des Tages hat unmaskierte Platzhalter. Alle $-Zeichen sollten verdoppelt werden ($$).", "server-messed-up-motd-too-long": "Die Nachricht des Tages ist zu lang - Maximal {} Zeichen, aktuell {}.", + "server-listen-only-on-ipv4": "Listen only on IPv4 when starting the server.", + "server-listen-only-on-ipv6": "Listen only on IPv6 when starting the server.", + "server-interface-ipv4": "The IP address to bind to for IPv4. Leaving it empty defaults to using all.", + "server-interface-ipv6": "The IP address to bind to for IPv6. Leaving it empty defaults to using all.", # Server errors "unknown-command-server-error": "Unbekannter Befehl {}", # message diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index 4798962..97a759d 100644 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -500,6 +500,10 @@ en = { "server-startTLS-argument": "Enable TLS connections using the certificate files in the path provided", "server-messed-up-motd-unescaped-placeholders": "Message of the Day has unescaped placeholders. All $ signs should be doubled ($$).", "server-messed-up-motd-too-long": "Message of the Day is too long - maximum of {} chars, {} given.", + "server-listen-only-on-ipv4": "Listen only on IPv4 when starting the server.", + "server-listen-only-on-ipv6": "Listen only on IPv6 when starting the server.", + "server-interface-ipv4": "The IP address to bind to for IPv4. Leaving it empty defaults to using all.", + "server-interface-ipv6": "The IP address to bind to for IPv6. Leaving it empty defaults to using all.", # Server errors "unknown-command-server-error": "Unknown command {}", # message diff --git a/syncplay/messages_eo.py b/syncplay/messages_eo.py index f2fe771..4225f7c 100644 --- a/syncplay/messages_eo.py +++ b/syncplay/messages_eo.py @@ -503,6 +503,10 @@ eo = { "server-startTLS-argument": "Ŝalti TLS-konektojn per la atestilaj dosieroj en la donita vojo", "server-messed-up-motd-unescaped-placeholders": "Mesaĝo de tago havas neŝirmitajn anstataŭiĝojn. Ĉiuj dolarsignoj devus aperi duoble ($$).", "server-messed-up-motd-too-long": "Mesaĝo de tago estas tro longa – maksimuma kvanto estas {} signoj, sed {} estas donitaj.", + "server-listen-only-on-ipv4": "Listen only on IPv4 when starting the server.", + "server-listen-only-on-ipv6": "Listen only on IPv6 when starting the server.", + "server-interface-ipv4": "The IP address to bind to for IPv4. Leaving it empty defaults to using all.", + "server-interface-ipv6": "The IP address to bind to for IPv6. Leaving it empty defaults to using all.", # Server errors "unknown-command-server-error": "Nekonata ordono {}", # message diff --git a/syncplay/messages_es.py b/syncplay/messages_es.py index f46761a..890b249 100644 --- a/syncplay/messages_es.py +++ b/syncplay/messages_es.py @@ -499,6 +499,10 @@ es = { "server-startTLS-argument": "Habilitar conexiones TLS usando los archivos de certificado en la ruta provista", "server-messed-up-motd-unescaped-placeholders": "El mensaje del dia contiene marcadores de posición sin escapar. Todos los signos $ deberían ser dobles ($$).", "server-messed-up-motd-too-long": "El mensaje del día es muy largo - máximo de {} caracteres, se recibieron {}.", + "server-listen-only-on-ipv4": "Listen only on IPv4 when starting the server.", + "server-listen-only-on-ipv6": "Listen only on IPv6 when starting the server.", + "server-interface-ipv4": "The IP address to bind to for IPv4. Leaving it empty defaults to using all.", + "server-interface-ipv6": "The IP address to bind to for IPv6. Leaving it empty defaults to using all.", # Server errors "unknown-command-server-error": "Comando desconocido {}", # message diff --git a/syncplay/messages_fr.py b/syncplay/messages_fr.py index b35adb2..6d95add 100644 --- a/syncplay/messages_fr.py +++ b/syncplay/messages_fr.py @@ -500,6 +500,10 @@ fr = { "server-startTLS-argument": "Activer les connexions TLS à l'aide des fichiers de certificat dans le chemin fourni", "server-messed-up-motd-unescaped-placeholders": "Le message du jour a des espaces réservés non échappés. Tous les signes $ doivent être doublés ($$).", "server-messed-up-motd-too-long": "Le message du jour est trop long: {}caractères maximum, {} donnés.", + "server-listen-only-on-ipv4": "Listen only on IPv4 when starting the server.", + "server-listen-only-on-ipv6": "Listen only on IPv6 when starting the server.", + "server-interface-ipv4": "The IP address to bind to for IPv4. Leaving it empty defaults to using all.", + "server-interface-ipv6": "The IP address to bind to for IPv6. Leaving it empty defaults to using all.", # Server errors "unknown-command-server-error": "Commande inconnue {}", # message diff --git a/syncplay/messages_it.py b/syncplay/messages_it.py index 2fdad31..cdf2b76 100755 --- a/syncplay/messages_it.py +++ b/syncplay/messages_it.py @@ -499,6 +499,10 @@ it = { "server-startTLS-argument": "Abilita il protocollo TLS usando i certificati contenuti nel percorso indicato", "server-messed-up-motd-unescaped-placeholders": "Il messaggio del giorno ha dei caratteri non 'escaped'. Tutti i simboli $ devono essere doppi ($$).", "server-messed-up-motd-too-long": "Il messaggio del giorno è troppo lungo - numero massimo di caratteri è {}, {} trovati.", + "server-listen-only-on-ipv4": "Listen only on IPv4 when starting the server.", + "server-listen-only-on-ipv6": "Listen only on IPv6 when starting the server.", + "server-interface-ipv4": "The IP address to bind to for IPv4. Leaving it empty defaults to using all.", + "server-interface-ipv6": "The IP address to bind to for IPv6. Leaving it empty defaults to using all.", # Server errors "unknown-command-server-error": "Comando non riconosciuto {}", # message diff --git a/syncplay/messages_pt_BR.py b/syncplay/messages_pt_BR.py index e328180..c7a2535 100644 --- a/syncplay/messages_pt_BR.py +++ b/syncplay/messages_pt_BR.py @@ -500,6 +500,10 @@ pt_BR = { "server-startTLS-argument": "Habilita conexões TLS usando os arquivos de certificado no caminho fornecido", "server-messed-up-motd-unescaped-placeholders": "A Mensagem do Dia possui placeholders não escapados. Todos os sinais de $ devem ser dobrados (como em $$).", "server-messed-up-motd-too-long": "A Mensagem do Dia é muito longa - máximo de {} caracteres, {} foram dados.", + "server-listen-only-on-ipv4": "Listen only on IPv4 when starting the server.", + "server-listen-only-on-ipv6": "Listen only on IPv6 when starting the server.", + "server-interface-ipv4": "The IP address to bind to for IPv4. Leaving it empty defaults to using all.", + "server-interface-ipv6": "The IP address to bind to for IPv6. Leaving it empty defaults to using all.", # Server errors "unknown-command-server-error": "Comando desconhecido: {}", # message diff --git a/syncplay/messages_pt_PT.py b/syncplay/messages_pt_PT.py index 8b48efe..a32d24c 100644 --- a/syncplay/messages_pt_PT.py +++ b/syncplay/messages_pt_PT.py @@ -499,6 +499,10 @@ pt_PT = { "server-startTLS-argument": "Habilita conexões TLS usando os arquivos de certificado no caminho fornecido", "server-messed-up-motd-unescaped-placeholders": "A Mensagem do Dia possui placeholders não escapados. Todos os sinais de $ devem ser dobrados (como em $$).", "server-messed-up-motd-too-long": "A Mensagem do Dia é muito longa - máximo de {} caracteres, {} foram dados.", + "server-listen-only-on-ipv4": "Listen only on IPv4 when starting the server.", + "server-listen-only-on-ipv6": "Listen only on IPv6 when starting the server.", + "server-interface-ipv4": "The IP address to bind to for IPv4. Leaving it empty defaults to using all.", + "server-interface-ipv6": "The IP address to bind to for IPv6. Leaving it empty defaults to using all.", # Server errors "unknown-command-server-error": "Comando desconhecido: {}", # message diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index 6ecef4e..ce30c21 100755 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -496,6 +496,10 @@ ru = { "server-startTLS-argument": "Включить TLS-соединения используя файлы сертификатов в указанном пути", "server-messed-up-motd-unescaped-placeholders": "MOTD-сообщение содержит неэкранированные спецсимволы. Все знаки $ должны быть продублированы ($$).", "server-messed-up-motd-too-long": "MOTD-сообщение слишком длинное: максимальная длина - {} символ(ов), текущая длина - {} символ(ов).", + "server-listen-only-on-ipv4": "Listen only on IPv4 when starting the server.", + "server-listen-only-on-ipv6": "Listen only on IPv6 when starting the server.", + "server-interface-ipv4": "The IP address to bind to for IPv4. Leaving it empty defaults to using all.", + "server-interface-ipv6": "The IP address to bind to for IPv6. Leaving it empty defaults to using all.", # Server errors "unknown-command-server-error": "Неизвестная команда: {}", # message diff --git a/syncplay/messages_tr.py b/syncplay/messages_tr.py index 09cc5aa..cb4e759 100644 --- a/syncplay/messages_tr.py +++ b/syncplay/messages_tr.py @@ -500,6 +500,10 @@ tr = { "server-startTLS-argument": "Dosya yolundaki sertifika dosyalarını kullanarak TLS bağlantılarını etkinleştirin", "server-messed-up-motd-unescaped-placeholders": "Günün Mesajında çıkış karaktersiz yer tutucular var. Tüm $ işaretleri iki katına çıkarılmalıdır ($$).", "server-messed-up-motd-too-long": "Günün Mesajı çok uzun - maksimum {} karakter olmalı, {} verildi.", + "server-listen-only-on-ipv4": "Listen only on IPv4 when starting the server.", + "server-listen-only-on-ipv6": "Listen only on IPv6 when starting the server.", + "server-interface-ipv4": "The IP address to bind to for IPv4. Leaving it empty defaults to using all.", + "server-interface-ipv6": "The IP address to bind to for IPv6. Leaving it empty defaults to using all.", # Server errors "unknown-command-server-error": "Bilinmeyen komut {}", # message diff --git a/syncplay/messages_zh_CN.py b/syncplay/messages_zh_CN.py index 0df6abf..9fbdff7 100644 --- a/syncplay/messages_zh_CN.py +++ b/syncplay/messages_zh_CN.py @@ -500,6 +500,10 @@ zh_CN = { "server-startTLS-argument": "使用提供的路径中的证书文件启用TLS连接", "server-messed-up-motd-unescaped-placeholders": "每日信息中有未转义的占位符。所有 $ 字符应当重复两遍 ($$).", "server-messed-up-motd-too-long": "每日信息过长 - 最大{}个chars, 给出的长度{}", + "server-listen-only-on-ipv4": "Listen only on IPv4 when starting the server.", + "server-listen-only-on-ipv6": "Listen only on IPv6 when starting the server.", + "server-interface-ipv4": "The IP address to bind to for IPv4. Leaving it empty defaults to using all.", + "server-interface-ipv6": "The IP address to bind to for IPv6. Leaving it empty defaults to using all.", # Server errors "unknown-command-server-error": "未知命令 {}", # message diff --git a/syncplay/server.py b/syncplay/server.py index 66e9888..c5a04ac 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -2,9 +2,7 @@ import argparse import codecs import hashlib import os -import random import time -import json from string import Template from twisted.enterprise import adbapi @@ -292,18 +290,18 @@ class StatsRecorder(object): def __init__(self, dbHandle, roomManager): self._dbHandle = dbHandle self._roomManagerHandle = roomManager - + def startRecorder(self, delay): try: self._dbHandle.connect() reactor.callLater(delay, self._scheduleClientSnapshot) except: print("--- Error in initializing the stats database. Server Stats not enabled. ---") - + def _scheduleClientSnapshot(self): self._clientSnapshotTimer = task.LoopingCall(self._runClientSnapshot) - self._clientSnapshotTimer.start(constants.SERVER_STATS_SNAPSHOT_INTERVAL) - + self._clientSnapshotTimer.start(constants.SERVER_STATS_SNAPSHOT_INTERVAL) + def _runClientSnapshot(self): try: snapshotTime = int(time.time()) @@ -499,7 +497,7 @@ class RoomManager(object): while username.lower() in allnames: username += '_' return username - + def exportRooms(self): return self._rooms @@ -891,3 +889,7 @@ class ConfigurationGetter(object): self._argparser.add_argument('--max-username-length', metavar='maxUsernameLength', type=int, nargs='?', help=getMessage("server-maxusernamelength-argument").format(constants.MAX_USERNAME_LENGTH)) self._argparser.add_argument('--stats-db-file', metavar='file', type=str, nargs='?', help=getMessage("server-stats-db-file-argument")) self._argparser.add_argument('--tls', metavar='path', type=str, nargs='?', help=getMessage("server-startTLS-argument")) + self._argparser.add_argument('--ipv4-only', action='store_true', help=getMessage("server-listen-only-on-ipv4")) + self._argparser.add_argument('--ipv6-only', action='store_true', help=getMessage("server-listen-only-on-ipv6")) + self._argparser.add_argument('--interface-ipv4', metavar='interfaceIPv4', type=str, nargs='?', help=getMessage("server-interface-ipv4"), default='') + self._argparser.add_argument('--interface-ipv6', metavar='interfaceIPv6', type=str, nargs='?', help=getMessage("server-interface-ipv6"), default='') diff --git a/syncplay/utils.py b/syncplay/utils.py index 5f42a39..71572f1 100755 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -12,7 +12,6 @@ import subprocess import sys import time import traceback -import unicodedata import urllib.error import urllib.parse import urllib.request