diff --git a/syncplay/client.py b/syncplay/client.py index 6f5aa9a..32dfca2 100755 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -710,7 +710,6 @@ class SyncplayClient(object): trust_root = Certificate.loadPEM(cert_file.read()) self._endpoint = HostnameEndpoint(reactor, host, port) self.protocolFactory.options = optionsForClientTLS(hostname=host, trustRoot = trust_root) - def retry(retries): self._lastGlobalUpdate = None diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index 8c869ee..82c55e7 100755 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -439,6 +439,7 @@ en = { "server-chat-maxchars-argument": "Maximum number of characters in a chat message (default is {})", # Default number of characters "server-maxusernamelength-argument": "Maximum number of characters in a username (default is {})", "server-stats-db-file-argument": "Enable server stats using the SQLite db file provided", + "server-tls-argument": "Enable TLS connections using the certificate file 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.", diff --git a/syncplay/protocols.py b/syncplay/protocols.py index 938a174..df960ba 100755 --- a/syncplay/protocols.py +++ b/syncplay/protocols.py @@ -316,10 +316,12 @@ class SyncClientProtocol(JSONCommandProtocol): def handleTLS(self, message): answer = message["startTLS"] if "startTLS" in message else None - if "true" in answer and not self.logged: + if "true" in answer and not self.logged and self._client.protocolFactory.options is not None: self.transport.startTLS(self._client.protocolFactory.options) self._client.ui.showMessage("Secure connection established") - self.sendHello() + elif "false" in answer: + self._client.ui.showErrorMessage("This server does not support TLS") + self.sendHello() class SyncServerProtocol(JSONCommandProtocol): def __init__(self, factory): @@ -626,9 +628,12 @@ class SyncServerProtocol(JSONCommandProtocol): def handleTLS(self, message): inquiry = message["startTLS"] if "startTLS" in message else None - if "send" in inquiry and not self.isLogged(): - self.sendTLS({"startTLS": "true"}) - self.transport.startTLS(self._factory.options) + if "send" in inquiry: + if not self.isLogged() and self._factory.options is not None: + self.sendTLS({"startTLS": "true"}) + self.transport.startTLS(self._factory.options) + else: + self.sendTLS({"startTLS": "false"}) class PingService(object): diff --git a/syncplay/server.py b/syncplay/server.py index d62d8d5..e9d7136 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -7,7 +7,7 @@ import time from string import Template from twisted.enterprise import adbapi -from twisted.internet import task, reactor +from twisted.internet import task, reactor, ssl from twisted.internet.protocol import Factory import syncplay @@ -20,7 +20,7 @@ from syncplay.utils import RoomPasswordProvider, NotControlledRoom, RandomString class SyncFactory(Factory): def __init__(self, port='', password='', motdFilePath=None, isolateRooms=False, salt=None, disableReady=False, disableChat=False, maxChatMessageLength=constants.MAX_CHAT_MESSAGE_LENGTH, - maxUsernameLength=constants.MAX_USERNAME_LENGTH, statsDbFile=None): + maxUsernameLength=constants.MAX_USERNAME_LENGTH, statsDbFile=None, tlsCert=None): self.isolateRooms = isolateRooms print(getMessage("welcome-server-notification").format(syncplay.version)) self.port = port @@ -48,6 +48,16 @@ class SyncFactory(Factory): self._statsRecorder.startRecorder(statsDelay) else: self._statsDbHandle = None + self.options = None + if tlsCert is not None: + try: + with open(tlsCert) as f: + certData = f.read() + cert = ssl.PrivateCertificate.loadPEM(certData).options() + self.options = cert + except Exception as e: + print(e) + print("Cannot import certificate. TLS support not enabled.") def buildProtocol(self, addr): return SyncServerProtocol(self) @@ -624,3 +634,4 @@ class ConfigurationGetter(object): self._argparser.add_argument('--max-chat-message-length', metavar='maxChatMessageLength', type=int, nargs='?', help=getMessage("server-chat-maxchars-argument").format(constants.MAX_CHAT_MESSAGE_LENGTH)) 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='file', type=str, nargs='?', help=getMessage("server-tls-argument")) diff --git a/syncplayServer.py b/syncplayServer.py index 0be1618..679e6c4 100755 --- a/syncplayServer.py +++ b/syncplayServer.py @@ -14,16 +14,11 @@ except AttributeError: warnings.warn("You must run Syncplay with Python 3.4 or newer!") from OpenSSL import crypto -from twisted.internet import reactor, ssl +from twisted.internet import reactor from twisted.internet.endpoints import TCP4ServerEndpoint, TCP6ServerEndpoint from syncplay.server import SyncFactory, ConfigurationGetter -with open('cert/server.pem') as f: - certData = f.read() - -cert = ssl.PrivateCertificate.loadPEM(certData).options() - if __name__ == '__main__': argsGetter = ConfigurationGetter() args = argsGetter.getConfiguration() @@ -37,9 +32,9 @@ if __name__ == '__main__': args.disable_chat, args.max_chat_message_length, args.max_username_length, - args.stats_db_file + args.stats_db_file, + args.tls ) - factory.options = cert endpoint4 = TCP4ServerEndpoint(reactor, int(args.port)) endpoint4.listen(factory) endpoint6 = TCP6ServerEndpoint(reactor, int(args.port))