Queer European MD passionate about IT
Selaa lähdekoodia

Handle SSL exceptions

Davte 5 vuotta sitten
vanhempi
sitoutus
d131688794
4 muutettua tiedostoa jossa 75 lisäystä ja 15 poistoa
  1. 46 0
      README.md
  2. 1 1
      filebridging/__init__.py
  3. 24 12
      filebridging/client.py
  4. 4 2
      filebridging/server.py

+ 46 - 0
README.md

@@ -4,6 +4,10 @@ Share files via a bridge server using TCP over SSL and end-to-end encryption.
 
 ## Requirements
 Python3.8+ is needed for this package.
+You may find it [here](https://www.python.org/downloads/).
+
+OpenSSL 1.1.1+ is required as well to handle SSL connection and end-to-end cryptography.
+On Windows, installing [git for Windows](https://gitforwindows.org/) will install OpenSSL as well.
 
 ## Usage
 If you need a virtual environment, create it.
@@ -29,6 +33,7 @@ python -m filebridging.client --help
     python -m filebridging.client r --host localhost --port 5000 --certificate ~/.ssh/server.crt --token 12345678 --password supersecretpasswordhere --path ~/Downloads 
     ```
 
+
 * Client-client example
     ```bash
     # 2 distinct tabs
@@ -37,6 +42,7 @@ python -m filebridging.client --help
     ```
     The receiver client may be standalone as well: just add the `--key` parameter (for SSL-secured sessions) and the `--standalone` flag. 
 
+
 * Configuration file example
     ```python
     #!/bin/python
@@ -51,3 +57,43 @@ python -m filebridging.client --help
     token = 'sessiontok'
     file_path = '.'
     ```
+
+## Generating SSL certificates
+
+
+Store configuration in file `mycert.csr.cnf` and run the following command to generate a self-signed SSL certificate.
+```bash
+openssl req -newkey rsa:2048 -nodes -keyout ./mycert.key \
+ -x509 -days 365 -out ./mycert.crt \
+ -config <( cat mycert.csr.cnf )
+```
+
+
+**mycert.csr.cnf**
+```text
+[req]
+default_bits = 2048
+prompt = no
+default_md = sha256
+distinguished_name = dn
+req_extensions = v3_req
+subjectAltName = @alt_names
+
+[ v3_req ]
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName = @alt_names
+
+[dn]
+C=US
+ST=YourState
+L=YourTown
+O=FileBridging
+OU=filebridging
+emailAddress=filebridging@yourdomain.com
+CN = yourdomain.com
+
+[ alt_names ]
+DNS.1 = yourdomain.com
+DNS.2 = 1.111.111.11
+```

+ 1 - 1
filebridging/__init__.py

@@ -13,6 +13,6 @@ __author__ = "Davide Testa"
 __email__ = "davide@davte.it"
 __credits__ = []
 __license__ = "GNU General Public License v3.0"
-__version__ = "0.0.5"
+__version__ = "0.0.6"
 __maintainer__ = "Davide Testa"
 __contact__ = "t.me/davte"

+ 24 - 12
filebridging/client.py

@@ -40,25 +40,27 @@ class Client:
                  buffer_length_limit=10 ** 4,
                  file_path=None,
                  password=None,
-                 token=None):
+                 token=None,
+                 ssl_handshake_timeout=None):
         self._host = host
         self._port = port
         self._ssl_context = ssl_context
         self._action = action
         self._standalone = standalone
-        self._stopping = False
-        self._reader = None
-        self._writer = None
-        # Shared queue of bytes
-        self.buffer = collections.deque()
+        self._file_path = file_path
+        self._password = password
+        self._token = token
+        self._ssl_handshake_timeout = ssl_handshake_timeout
         # How many bytes per chunk
         self._buffer_chunk_size = buffer_chunk_size
         # How many chunks in buffer
         self._buffer_length_limit = buffer_length_limit
-        self._file_path = file_path
+        # Shared queue of bytes
+        self.buffer = collections.deque()
         self._working = False
-        self._token = token
-        self._password = password
+        self._stopping = False
+        self._reader = None
+        self._writer = None
         self._encryption_complete = False
         self._file_name = None
         self._file_size = None
@@ -138,6 +140,16 @@ class Client:
     def set_ssl_context(self, ssl_context: ssl.SSLContext):
         self._ssl_context = ssl_context
 
+    @property
+    def ssl_handshake_timeout(self) -> Union[int, None]:
+        """Return SSL handshake timeout.
+
+        If SSL context is not set, return None.
+        Otherwise, return seconds to wait before considering handshake failed.
+        """
+        if self.ssl_context:
+            return self._ssl_handshake_timeout
+
     @property
     def token(self):
         """Session token.
@@ -199,13 +211,13 @@ class Client:
                     host=self.host,
                     port=self.port,
                     ssl=self.ssl_context,
-                    ssl_handshake_timeout=5
+                    ssl_handshake_timeout=self.ssl_handshake_timeout
                 )
             except (ConnectionRefusedError, ConnectionResetError,
                     ConnectionAbortedError) as exception:
                 logging.error(f"Connection error: {exception}")
                 return
-            except ssl.SSLCertVerificationError as exception:
+            except ssl.SSLError as exception:
                 logging.error(f"SSL error: {exception}")
                 return
             await self.connect(reader=reader, writer=writer)
@@ -276,7 +288,7 @@ class Client:
         while 1:
             server_hello = await self.reader.readline()
             if not server_hello:
-                logging.error("Server disconnected.")
+                logging.error("Server refused connection.")
                 return
             server_hello = server_hello.decode('utf-8').strip('\n').split('|')
             if self.action == 'receive' and server_hello[0] == 's':

+ 4 - 2
filebridging/server.py

@@ -237,8 +237,10 @@ class Server:
             return
 
     def disconnect(self, connection_token: str) -> None:
-        del self.buffers[connection_token]
-        del self.connections[connection_token]
+        if connection_token in self.buffers:
+            del self.buffers[connection_token]
+        if connection_token in self.connections:
+            del self.connections[connection_token]
 
     def run(self):
         loop = asyncio.get_event_loop()