Enable python 3.11 and resolve TLS issues #1

Merged
PeterSurda merged 8 commits from lee.miller/MiNode:ssl into v0.3 2024-06-25 00:49:12 +02:00
8 changed files with 73 additions and 74 deletions

View File

@ -1,16 +1,21 @@
FROM ubuntu:focal FROM ubuntu:jammy
RUN apt-get update RUN apt-get update
RUN apt-get install -yq software-properties-common RUN apt-get install -yq software-properties-common
RUN apt-add-repository ppa:purplei2p/i2pd RUN apt-add-repository ppa:purplei2p/i2pd && apt-get update -qq
RUN apt-get update
RUN apt-get install -yq --no-install-suggests --no-install-recommends \ RUN apt-get install -yq --no-install-suggests --no-install-recommends \
python3-dev python3-pip python3.9 python3.9-dev python3.9-venv sudo i2pd python3-dev python3-pip python-is-python3 python3.11-dev python3.11-venv
RUN apt-get install -yq --no-install-suggests --no-install-recommends sudo i2pd
RUN echo 'builder ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers RUN echo 'builder ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
RUN python3.9 -m pip install setuptools wheel RUN pip install setuptools wheel
RUN python3.9 -m pip install --upgrade pip tox virtualenv RUN pip install --upgrade pip tox virtualenv
ADD . .
CMD .buildbot/ubuntu/test.sh

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
.git
.tox
dist

View File

@ -1,44 +1,20 @@
name: Blind Test name: Testing
on: [push]
on: push
jobs: jobs:
default: default:
runs-on: ubuntu-20.04
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
python-version: [3.8]
include:
- os: ubuntu-latest
python-version: 3.9
steps: steps:
- uses: actions/checkout@v2 - name: Install dependencies
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install python dependencies
run: | run: |
python -m pip install --upgrade pip apt-get update
pip install wheel apt-get install -yq --no-install-suggests --no-install-recommends \
pip install bandit flake8 pylint python3-dev python3-pip python3-venv python-is-python3
pip install -r requirements.txt pip install setuptools wheel
python setup.py install pip install --upgrade pip tox virtualenv
- name: Lint - name: Check out repository code
if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.8' }} uses: actions/checkout@v3
run: | - name: Quick lint
# stop the build if there are Python syntax errors or undefined names run: tox -e lint-basic
flake8 minode --count --select=E9,F63,F7,F82 --show-source --statistics - name: Run tests
flake8 minode --count --statistics run: tox
pylint minode --exit-zero --rcfile=tox.ini
bandit -r --exit-zero -x tests minode
- name: Test
run: |
export PYTHONWARNINGS=all
coverage run -a -m tests
- name: Summary
run: coverage report

View File

@ -1,6 +1,6 @@
# MiNode # MiNode
[![Quick Test](https://github.com/g1itch/MiNode/actions/workflows/test.yml/badge.svg)](https://github.com/g1itch/MiNode/actions/workflows/test.yml) [![Testing](https://git.bitmessage.org/Bitmessage/MiNode/actions/workflows/test.yml/badge.svg)](https://git.bitmessage.org/Bitmessage/MiNode/actions?workflow=test.yml)
Python 3 implementation of the Bitmessage protocol. Designed only to route Python 3 implementation of the Bitmessage protocol. Designed only to route
objects inside the network. objects inside the network.

View File

@ -207,6 +207,13 @@ class Connection(threading.Thread):
context.options = ( context.options = (
ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
| ssl.OP_SINGLE_ECDH_USE | ssl.OP_CIPHER_SERVER_PREFERENCE) | ssl.OP_SINGLE_ECDH_USE | ssl.OP_CIPHER_SERVER_PREFERENCE)
# OP_NO_SSL* is deprecated since 3.6
try:
# TODO: ssl.TLSVersion.TLSv1 is deprecated
context.minimum_version = ssl.TLSVersion.TLSv1
context.maximum_version = ssl.TLSVersion.TLSv1_2
except AttributeError:
pass
self.s = context.wrap_socket( self.s = context.wrap_socket(
self.s, server_side=self.server, do_handshake_on_connect=False) self.s, server_side=self.server, do_handshake_on_connect=False)
@ -224,11 +231,14 @@ class Connection(threading.Thread):
'Disconnecting from %s:%s. Reason: %s', 'Disconnecting from %s:%s. Reason: %s',
self.host_print, self.port, e) self.host_print, self.port, e)
self.status = 'disconnecting' self.status = 'disconnecting'
break if isinstance(e, ssl.SSLError): # pylint: disable=no-member
logging.debug('ssl.SSLError reason: %s', e.reason)
shared.node_pool.discard((self.host, self.port))
return
self.tls = True self.tls = True
logging.debug( logging.debug(
'Established TLS connection with %s:%s', 'Established TLS connection with %s:%s (%s)',
self.host_print, self.port) self.host_print, self.port, self.s.version())
def _send_message(self, m): def _send_message(self, m):
if isinstance(m, message.Message) and m.command == b'object': if isinstance(m, message.Message) and m.command == b'object':
@ -256,11 +266,11 @@ class Connection(threading.Thread):
if len(shared.node_pool) > 10: if len(shared.node_pool) > 10:
addr.update({ addr.update({
structure.NetAddr(1, a[0], a[1]) structure.NetAddr(1, a[0], a[1])
for a in random.sample(shared.node_pool, 10)}) for a in random.sample(tuple(shared.node_pool), 10)})
if len(shared.unchecked_node_pool) > 10: if len(shared.unchecked_node_pool) > 10:
addr.update({ addr.update({
structure.NetAddr(1, a[0], a[1]) structure.NetAddr(1, a[0], a[1])
for a in random.sample(shared.unchecked_node_pool, 10)}) for a in random.sample(tuple(shared.unchecked_node_pool), 10)})
if len(addr) != 0: if len(addr) != 0:
self.send_queue.put(message.Addr(addr)) self.send_queue.put(message.Addr(addr))
@ -274,7 +284,7 @@ class Connection(threading.Thread):
# We limit size of inv messaged to 10000 entries # We limit size of inv messaged to 10000 entries
# because they might time out # because they might time out
# in very slow networks (I2P) # in very slow networks (I2P)
pack = random.sample(to_send, 10000) pack = random.sample(tuple(to_send), 10000)
self.send_queue.put(message.Inv(pack)) self.send_queue.put(message.Inv(pack))
to_send.difference_update(pack) to_send.difference_update(pack)
else: else:
@ -446,7 +456,7 @@ class Connection(threading.Thread):
logging.info( logging.info(
'Queued %s vectors to get', len(self.vectors_to_get)) 'Queued %s vectors to get', len(self.vectors_to_get))
if len(self.vectors_to_get) > 64: if len(self.vectors_to_get) > 64:
pack = random.sample(self.vectors_to_get, 64) pack = random.sample(tuple(self.vectors_to_get), 64)
self.send_queue.put(message.GetData(pack)) self.send_queue.put(message.GetData(pack))
self.vectors_requested.update({ self.vectors_requested.update({
vector: time.time() for vector in pack vector: time.time() for vector in pack
@ -476,7 +486,7 @@ class Connection(threading.Thread):
logging.info( logging.info(
'Preparing to send %s objects', len(self.vectors_to_send)) 'Preparing to send %s objects', len(self.vectors_to_send))
if len(self.vectors_to_send) > 16: if len(self.vectors_to_send) > 16:
to_send = random.sample(self.vectors_to_send, 16) to_send = random.sample(tuple(self.vectors_to_send), 16)
self.vectors_to_send.difference_update(to_send) self.vectors_to_send.difference_update(to_send)
else: else:
to_send = self.vectors_to_send.copy() to_send = self.vectors_to_send.copy()

View File

@ -98,24 +98,26 @@ class Manager(threading.Thread):
if shared.ip_enabled: if shared.ip_enabled:
if len(shared.unchecked_node_pool) > 16: if len(shared.unchecked_node_pool) > 16:
to_connect.update(random.sample( to_connect.update(random.sample(
shared.unchecked_node_pool, 16)) tuple(shared.unchecked_node_pool), 16))
else: else:
to_connect.update(shared.unchecked_node_pool) to_connect.update(shared.unchecked_node_pool)
shared.unchecked_node_pool.difference_update(to_connect) shared.unchecked_node_pool.difference_update(to_connect)
if len(shared.node_pool) > 8: if len(shared.node_pool) > 8:
to_connect.update(random.sample(shared.node_pool, 8)) to_connect.update(random.sample(
tuple(shared.node_pool), 8))
else: else:
to_connect.update(shared.node_pool) to_connect.update(shared.node_pool)
if shared.i2p_enabled: if shared.i2p_enabled:
if len(shared.i2p_unchecked_node_pool) > 16: if len(shared.i2p_unchecked_node_pool) > 16:
to_connect.update( to_connect.update(random.sample(
random.sample(shared.i2p_unchecked_node_pool, 16)) tuple(shared.i2p_unchecked_node_pool), 16))
else: else:
to_connect.update(shared.i2p_unchecked_node_pool) to_connect.update(shared.i2p_unchecked_node_pool)
shared.i2p_unchecked_node_pool.difference_update(to_connect) shared.i2p_unchecked_node_pool.difference_update(to_connect)
if len(shared.i2p_node_pool) > 8: if len(shared.i2p_node_pool) > 8:
to_connect.update(random.sample(shared.i2p_node_pool, 8)) to_connect.update(random.sample(
tuple(shared.i2p_node_pool), 8))
else: else:
to_connect.update(shared.i2p_node_pool) to_connect.update(shared.i2p_node_pool)
@ -214,17 +216,18 @@ class Manager(threading.Thread):
@staticmethod @staticmethod
def pickle_nodes(): def pickle_nodes():
if len(shared.node_pool) > 10000: if len(shared.node_pool) > 10000:
shared.node_pool = set(random.sample(shared.node_pool, 10000)) shared.node_pool = set(random.sample(
tuple(shared.node_pool), 10000))
if len(shared.unchecked_node_pool) > 1000: if len(shared.unchecked_node_pool) > 1000:
shared.unchecked_node_pool = set( shared.unchecked_node_pool = set(random.sample(
random.sample(shared.unchecked_node_pool, 1000)) tuple(shared.unchecked_node_pool), 1000))
if len(shared.i2p_node_pool) > 1000: if len(shared.i2p_node_pool) > 1000:
shared.i2p_node_pool = set( shared.i2p_node_pool = set(random.sample(
random.sample(shared.i2p_node_pool, 1000)) tuple(shared.i2p_node_pool), 1000))
if len(shared.i2p_unchecked_node_pool) > 100: if len(shared.i2p_unchecked_node_pool) > 100:
shared.i2p_unchecked_node_pool = set( shared.i2p_unchecked_node_pool = set(random.sample(
random.sample(shared.i2p_unchecked_node_pool, 100)) tuple(shared.i2p_unchecked_node_pool), 100))
try: try:
with open( with open(

View File

@ -69,12 +69,15 @@ class TestProcessProto(unittest.TestCase):
class TestProcessShutdown(TestProcessProto): class TestProcessShutdown(TestProcessProto):
"""Separate test case for SIGTERM""" """Separate test case for SIGTERM"""
_wait_time = 30
# longer wait time because it's not a benchmark
def test_shutdown(self): def test_shutdown(self):
"""Send to minode SIGTERM and ensure it stopped""" """Send to minode SIGTERM and ensure it stopped"""
# longer wait time because it's not a benchmark
self.assertTrue( self.assertTrue(
self._stop_process(20), self._stop_process(self._wait_time),
'%s has not stopped in 20 sec' % ' '.join(self._process_cmd)) '%s has not stopped in %i sec' % (
' '.join(self._process_cmd), self._wait_time))
class TestProcess(TestProcessProto): class TestProcess(TestProcessProto):
@ -105,8 +108,8 @@ class TestProcess(TestProcessProto):
time.sleep(0.5) time.sleep(0.5)
else: else:
self.fail( self.fail(
'Failed establish at least %s connections in %s sec' 'Failed establish at least %i connections in %s sec'
% (self._connection_limit / 2, self._wait_time)) % (int(self._connection_limit / 2), self._wait_time))
if self._check_limit: if self._check_limit:
continue_check_limit(_time_to_connect) continue_check_limit(_time_to_connect)
@ -128,7 +131,6 @@ class TestProcessI2P(TestProcess):
"""Test minode process with --i2p and no IP""" """Test minode process with --i2p and no IP"""
_process_cmd = ['minode', '--i2p', '--no-ip'] _process_cmd = ['minode', '--i2p', '--no-ip']
_connection_limit = 4 _connection_limit = 4
_wait_time = 120
_listen = True _listen = True
_listening_port = 8448 _listening_port = 8448

View File

@ -1,5 +1,5 @@
[tox] [tox]
envlist = reset,py{36,37,38,39,310},stats envlist = reset,py3{6,7,8,9,10,11},stats
skip_missing_interpreters = true skip_missing_interpreters = true
[testenv] [testenv]