Improved I2P Interface

This commit is contained in:
Mark Qvist 2022-02-24 01:30:10 +01:00
parent 987ff0658b
commit 48184134e4
3 changed files with 89 additions and 36 deletions

View File

@ -48,6 +48,8 @@ class I2PController:
import RNS.vendor.i2plib as i2plib
import RNS.vendor.i2plib.utils
self.client_tunnels = {}
self.server_tunnels = {}
self.loop = None
self.i2plib = i2plib
self.utils = i2plib.utils
@ -68,28 +70,46 @@ class I2PController:
finally:
self.loop.close()
def stop(self):
for task in asyncio.Task.all_tasks(loop=self.loop):
task.cancel()
self.loop.stop()
def get_free_port(self):
return self.i2plib.utils.get_free_port()
def client_tunnel(self, owner, i2p_destination):
self.client_tunnels[i2p_destination] = False
while True:
if not self.client_tunnels[i2p_destination]:
try:
async def tunnel_up():
RNS.log("Bringing up I2P tunnel to "+str(owner)+" in background, this may take a while...", RNS.LOG_INFO)
tunnel = self.i2plib.ClientTunnel(i2p_destination, owner.local_addr, sam_address=self.sam_address)
RNS.log("Bringing up I2P tunnel to "+str(owner)+", this may take a while...", RNS.LOG_INFO)
tunnel = self.i2plib.ClientTunnel(i2p_destination, owner.local_addr, sam_address=self.sam_address, loop=self.loop)
await tunnel.run()
owner.awaiting_i2p_tunnel = False
RNS.log(str(owner)+ " tunnel setup complete", RNS.LOG_VERBOSE)
asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop)
try:
self.loop.ext_owner = self
future = asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
self.client_tunnels[i2p_destination] = True
except Exception as e:
RNS.log("Error while setting up I2P tunnel: "+str(e))
raise e
except Exception as e:
raise IOError("Could not connect to I2P SAM API while configuring to "+str(owner)+". Check that I2P is running and SAM is enabled.")
time.sleep(5)
def server_tunnel(self, owner):
i2p_dest_hash = RNS.Identity.full_hash(RNS.Identity.full_hash(owner.name.encode("utf-8")))
@ -110,19 +130,24 @@ class I2PController:
i2p_b32 = i2p_dest.base32
self.server_tunnels[i2p_b32] = False
while self.server_tunnels[i2p_b32] == False:
try:
async def tunnel_up():
RNS.log(str(owner)+" Bringing up I2P tunnel in background, this may take a while...", RNS.LOG_INFO)
RNS.log(str(owner)+" Bringing up I2P endpoint, this may take a while...", RNS.LOG_INFO)
tunnel = self.i2plib.ServerTunnel((owner.bind_ip, owner.bind_port), loop=self.loop, destination=i2p_dest, sam_address=self.sam_address)
await tunnel.run()
owner.awaiting_i2p_tunnel = False
RNS.log(str(owner)+ " tunnel setup complete, instance reachable at: "+str(i2p_dest.base32)+".b32.i2p", RNS.LOG_VERBOSE)
RNS.log(str(owner)+ " endpoint setup complete. Now reachable at: "+str(i2p_dest.base32)+".b32.i2p", RNS.LOG_VERBOSE)
asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop)
asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
self.server_tunnels[i2p_b32] = True
except Exception as e:
raise IOError("Could not connect to I2P SAM API while configuring "+str(self)+". Check that I2P is running and SAM is enabled.")
time.sleep(5)
def get_loop(self):
return asyncio.get_event_loop()
@ -148,6 +173,7 @@ class I2PInterfacePeer(Interface):
self.OUT = False
self.socket = None
self.parent_interface = parent_interface
self.parent_count = True
self.name = name
self.initiator = False
self.reconnecting = False
@ -188,8 +214,21 @@ class I2PInterfacePeer(Interface):
self.target_port = self.bind_port
self.awaiting_i2p_tunnel = True
def tunnel_job():
self.parent_interface.i2p.client_tunnel(self, target_i2p_dest)
thread = threading.Thread(target=tunnel_job)
thread.setDaemon(True)
thread.start()
def wait_job():
while self.awaiting_i2p_tunnel:
time.sleep(0.25)
if not self.kiss_framing:
self.wants_tunnel = True
if not self.connect(initial=True):
thread = threading.Thread(target=self.reconnect)
thread.setDaemon(True)
@ -198,8 +237,10 @@ class I2PInterfacePeer(Interface):
thread = threading.Thread(target=self.read_loop)
thread.setDaemon(True)
thread.start()
if not self.kiss_framing:
self.wants_tunnel = True
thread = threading.Thread(target=wait_job)
thread.setDaemon(True)
thread.start()
def set_timeouts_linux(self):
@ -274,6 +315,9 @@ class I2PInterfacePeer(Interface):
self.writing = False
self.never_connected = False
if not self.kiss_framing and self.wants_tunnel:
RNS.Transport.synthesize_tunnel(self)
return True
@ -316,7 +360,7 @@ class I2PInterfacePeer(Interface):
def processIncoming(self, data):
self.rxb += len(data)
if hasattr(self, "parent_interface") and self.parent_interface != None:
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
self.parent_interface.rxb += len(data)
self.owner.inbound(data, self)
@ -337,7 +381,7 @@ class I2PInterfacePeer(Interface):
self.socket.sendall(data)
self.writing = False
self.txb += len(data)
if hasattr(self, "parent_interface") and self.parent_interface != None:
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
self.parent_interface.txb += len(data)
except Exception as e:
@ -456,12 +500,13 @@ class I2PInterfacePeer(Interface):
class I2PInterface(Interface):
def __init__(self, owner, name, rns_storagepath, peers):
def __init__(self, owner, name, rns_storagepath, peers, connectable = True):
self.rxb = 0
self.txb = 0
self.online = False
self.clients = 0
self.owner = owner
self.connectable = connectable
self.i2p_tunneled = True
self.i2p = I2PController(rns_storagepath)
@ -492,8 +537,14 @@ class I2PInterface(Interface):
thread.setDaemon(True)
thread.start()
if self.connectable:
def tunnel_job():
self.i2p.server_tunnel(self)
thread = threading.Thread(target=tunnel_job)
thread.setDaemon(True)
thread.start()
if peers != None:
for peer_addr in peers:
interface_name = peer_addr
@ -501,6 +552,7 @@ class I2PInterface(Interface):
peer_interface.OUT = True
peer_interface.IN = True
peer_interface.parent_interface = self
peer_interface.parent_count = False
RNS.Transport.interfaces.append(peer_interface)
self.online = True

View File

@ -399,12 +399,14 @@ class Reticulum:
if c["type"] == "I2PInterface":
i2p_peers = c.as_list("peers") if "peers" in c else None
connectable = c.as_bool("connectable") if "connectable" in c else False
interface = I2PInterface.I2PInterface(
RNS.Transport,
name,
Reticulum.storagepath,
i2p_peers
i2p_peers,
connectable = connectable,
)
if "outgoing" in c and c.as_bool("outgoing") == True:

View File

@ -95,8 +95,7 @@ class ClientTunnel(I2PTunnel):
asyncio.ensure_future(proxy_data(client_reader, remote_writer),
loop=self.loop)
self.server = await asyncio.start_server(handle_client, *self.local_address,
loop=self.loop)
self.server = await asyncio.start_server(handle_client, *self.local_address, loop=self.loop)
def stop(self):
super().stop()