MiNode/minode/i2p/controller.py

101 lines
3.2 KiB
Python
Raw Normal View History

2017-06-09 18:41:33 +00:00
# -*- coding: utf-8 -*-
import base64
import logging
import os
import socket
import threading
2017-06-11 05:55:53 +00:00
import time
2017-06-09 18:41:33 +00:00
2021-03-09 14:40:59 +00:00
from .util import receive_line, pub_from_priv
2017-06-09 18:41:33 +00:00
class I2PController(threading.Thread):
2021-03-09 14:40:59 +00:00
def __init__(self, state, host='127.0.0.1', port=7656, dest_priv=b''):
2017-06-09 18:41:33 +00:00
super().__init__(name='I2P Controller')
2021-03-09 14:40:59 +00:00
self.state = state
2017-06-09 18:41:33 +00:00
self.host = host
self.port = port
self.nick = b'MiNode_' + base64.b16encode(os.urandom(4)).lower()
while True:
try:
self.s = socket.create_connection((self.host, self.port))
break
except ConnectionRefusedError:
2021-03-08 15:06:07 +00:00
logging.error(
'Error while connecting to I2P SAM bridge. Retrying.')
time.sleep(10)
2017-06-09 18:41:33 +00:00
self.version_reply = []
self.init_connection()
if dest_priv:
self.dest_priv = dest_priv
self.dest_pub = pub_from_priv(dest_priv)
else:
self.dest_priv = b''
self.dest_pub = b''
self.generate_destination()
self.create_session()
def _receive_line(self):
line = receive_line(self.s)
2021-03-08 15:06:07 +00:00
# logging.debug('I2PController <- %s', line)
2017-06-09 18:41:33 +00:00
return line
def _send(self, command):
2021-03-08 15:06:07 +00:00
# logging.debug('I2PController -> %s', command)
2017-06-09 18:41:33 +00:00
self.s.sendall(command)
def init_connection(self):
self._send(b'HELLO VERSION MIN=3.0 MAX=3.3\n')
self.version_reply = self._receive_line().split()
assert b'RESULT=OK' in self.version_reply
def generate_destination(self):
if b'VERSION=3.0' in self.version_reply:
# We will now receive old DSA_SHA1 destination :(
self._send(b'DEST GENERATE\n')
else:
self._send(b'DEST GENERATE SIGNATURE_TYPE=EdDSA_SHA512_Ed25519\n')
reply = self._receive_line().split()
for par in reply:
if par.startswith(b'PUB='):
self.dest_pub = par.replace(b'PUB=', b'')
if par.startswith(b'PRIV='):
self.dest_priv = par.replace(b'PRIV=', b'')
assert self.dest_priv
def create_session(self):
2021-03-09 14:40:59 +00:00
self._send(
b'SESSION CREATE STYLE=STREAM ID=' + self.nick
+ b' inbound.length=' + str(self.state.i2p_tunnel_length).encode()
+ b' outbound.length=' + str(self.state.i2p_tunnel_length).encode()
+ b' DESTINATION=' + self.dest_priv + b'\n')
2017-06-09 18:41:33 +00:00
reply = self._receive_line().split()
2017-06-11 05:55:53 +00:00
if b'RESULT=OK' not in reply:
logging.warning(reply)
2021-03-08 15:06:07 +00:00
logging.warning(
'We could not create I2P session, retrying in 5 seconds.')
2017-06-11 05:55:53 +00:00
time.sleep(5)
self.create_session()
2017-06-09 18:41:33 +00:00
def run(self):
self.s.settimeout(1)
while True:
2021-03-09 14:40:59 +00:00
if not self.state.shutting_down:
2017-06-09 18:41:33 +00:00
try:
msg = self._receive_line().split(b' ')
if msg[0] == b'PING':
self._send(b'PONG ' + msg[1] + b'\n')
except socket.timeout:
pass
else:
logging.debug('Shutting down I2P Controller')
self.s.close()
break