Implement I2P peers

This commit is contained in:
str4d 2016-07-31 14:56:42 +12:00
parent a0cb3a430c
commit 5e57411c58
5 changed files with 125 additions and 6 deletions

View file

@ -257,6 +257,13 @@ class FileRequest(object):
if site.addPeer(*address):
added += 1
# Add sent peers to site
for packed_address in params.get("peers_i2p", []):
address = helper.unpackI2PAddress(packed_address)
got_peer_keys.append("%s:%s" % address)
if site.addPeer(*address):
added += 1
# Send back peers that is not in the sent list and connectable (not port 0)
packed_peers = helper.packPeers(site.getConnectablePeers(params["need"], got_peer_keys))
@ -265,7 +272,7 @@ class FileRequest(object):
if config.verbose:
self.log.debug(
"Added %s peers to %s using pex, sending back %s" %
(added, site, len(packed_peers["ip4"]) + len(packed_peers["onion"]))
(added, site, len(packed_peers["ip4"]) + len(packed_peers["onion"]) + len(packed_peers["i2p"]))
)
back = {}
@ -273,6 +280,8 @@ class FileRequest(object):
back["peers"] = packed_peers["ip4"]
if packed_peers["onion"]:
back["peers_onion"] = packed_peers["onion"]
if packed_peers["i2p"]:
back["peers_i2p"] = packed_peers["i2p"]
self.response(back)
@ -317,14 +326,20 @@ class FileRequest(object):
back_ip4 = {}
back_onion = {}
back_i2p = {}
for hash_id, peers in found.iteritems():
back_onion[hash_id] = [helper.packOnionAddress(peer.ip, peer.port) for peer in peers if peer.ip.endswith("onion")]
back_ip4[hash_id] = [helper.packAddress(peer.ip, peer.port) for peer in peers if not peer.ip.endswith("onion")]
back_i2p[hash_id] = [helper.packI2PAddress(peer.ip, peer.port) for peer in peers if peer.ip.endswith("i2p")]
back_ip4[hash_id] = [helper.packAddress(peer.ip, peer.port) for peer in peers if not (peer.ip.endswith("onion") or peer.ip.endswith("i2p"))]
# Check my hashfield
# TODO Is it implied that a site address can only be on Tor, I2P or clearnet at once?
if self.server.tor_manager and self.server.tor_manager.site_onions.get(site.address): # Running onion
my_ip = helper.packOnionAddress(self.server.tor_manager.site_onions[site.address], self.server.port)
my_back = back_onion
elif self.server.i2p_manager and self.server.i2p_manager.site_dests.get(site.address): # Running I2P dest
my_ip = helper.packI2PAddress(self.server.i2p_manager.site_dests[site.address], self.server.port)
my_back = back_i2p
elif config.ip_external: # External ip defined
my_ip = helper.packAddress(config.ip_external, self.server.port)
my_back = back_ip4
@ -340,10 +355,10 @@ class FileRequest(object):
if config.verbose:
self.log.debug(
"Found: IP4: %s, Onion: %s for %s hashids" %
(len(back_ip4), len(back_onion), len(params["hash_ids"]))
"Found: IP4: %s, Onion: %s, I2P: %s for %s hashids" %
(len(back_ip4), len(back_onion), len(back_i2p), len(params["hash_ids"]))
)
self.response({"peers": back_ip4, "peers_onion": back_onion})
self.response({"peers": back_ip4, "peers_onion": back_onion, "peers_i2p": back_i2p})
def actionSetHashfield(self, params):
site = self.sites.get(params["site"])

View file

@ -105,6 +105,8 @@ class Peer(object):
def packMyAddress(self):
if self.ip.endswith(".onion"):
return helper.packOnionAddress(self.ip, self.port)
elif self.ip.endswith(".i2p"):
return helper.packI2PAddress(self.ip, self.port)
else:
return helper.packAddress(self.ip, self.port)
@ -241,6 +243,8 @@ class Peer(object):
request = {"site": site.address, "peers": packed_peers["ip4"], "need": need_num}
if packed_peers["onion"]:
request["peers_onion"] = packed_peers["onion"]
if packed_peers["i2p"]:
request["peers_i2p"] = packed_peers["i2p"]
res = self.request("pex", request)
if not res or "error" in res:
return False
@ -255,6 +259,11 @@ class Peer(object):
address = helper.unpackOnionAddress(peer)
if site.addPeer(*address):
added += 1
# I2P
for peer in res.get("peers_i2p", []):
address = helper.unpackI2PAddress(peer)
if site.addPeer(*address):
added += 1
if added:
self.log("Added peers using pex: %s" % added)
@ -292,6 +301,11 @@ class Peer(object):
if not hash in back:
back[hash] = []
back[hash] += map(helper.unpackOnionAddress, onion_peers)
# Unpack I2P dest
for hash, i2p_peers in res.get("peers_i2p", {}).items()[0:30]:
if not hash in back:
back[hash] = []
back[hash] += map(helper.unpackI2PAddress, i2p_peers)
return back

View file

@ -377,11 +377,17 @@ class Site(object):
# Find out my ip and port
tor_manager = self.connection_server.tor_manager
i2p_manager = self.connection_server.i2p_manager
if tor_manager and tor_manager.enabled and tor_manager.start_onions:
my_ip = tor_manager.getOnion(self.address)
if my_ip:
my_ip += ".onion"
my_port = config.fileserver_port
elif i2p_manager and i2p_manager.enabled and i2p_manager.start_dests:
my_ip = i2p_manager.getDest(self.address)
if my_ip:
my_ip += ".i2p"
my_port = 0
else:
my_ip = config.ip_external
if self.connection_server.port_opened:

View file

@ -1,6 +1,8 @@
import pytest
import time
from File import FileServer
# stats.i2p
TEST_B64 = 'Okd5sN9hFWx-sr0HH8EFaxkeIMi6PC5eGTcjM1KB7uQ0ffCUJ2nVKzcsKZFHQc7pLONjOs2LmG5H-2SheVH504EfLZnoB7vxoamhOMENnDABkIRGGoRisc5AcJXQ759LraLRdiGSR0WTHQ0O1TU0hAz7vAv3SOaDp9OwNDr9u902qFzzTKjUTG5vMTayjTkLo2kOwi6NVchDeEj9M7mjj5ySgySbD48QpzBgcqw1R27oIoHQmjgbtbmV2sBL-2Tpyh3lRe1Vip0-K0Sf4D-Zv78MzSh8ibdxNcZACmZiVODpgMj2ejWJHxAEz41RsfBpazPV0d38Mfg4wzaS95R5hBBo6SdAM4h5vcZ5ESRiheLxJbW0vBpLRd4mNvtKOrcEtyCvtvsP3FpA-6IKVswyZpHgr3wn6ndDHiVCiLAQZws4MsIUE1nkfxKpKtAnFZtPrrB8eh7QO9CkH2JBhj7bG0ED6mV5~X5iqi52UpsZ8gnjZTgyG5pOF8RcFrk86kHxAAAA'
@ -62,6 +64,72 @@ class TestI2P:
assert "body" in connection_locked.request("getFile", {"site": site.address, "inner_path": "content.json", "location": 0})
assert connection_locked.request("getFile", {"site": "1OTHERSITE", "inner_path": "content.json", "location": 0})["error"] == "Invalid site"
def testPex(self, file_server, site, site_temp):
# Register site to currently running fileserver
site.connection_server = file_server
file_server.sites[site.address] = site
# Create a new file server to emulate new peer connecting to our peer
file_server_temp = FileServer("127.0.0.1", 1545)
site_temp.connection_server = file_server_temp
file_server_temp.sites[site_temp.address] = site_temp
# We will request peers from this
peer_source = site_temp.addPeer("127.0.0.1", 1544)
# Get ip4 peers from source site
assert peer_source.pex(need_num=10) == 1 # Need >5 to return also return non-connected peers
assert len(site_temp.peers) == 2 # Me, and the other peer
site.addPeer("1.2.3.4", 1555) # Add peer to source site
assert peer_source.pex(need_num=10) == 1
assert len(site_temp.peers) == 3
assert "1.2.3.4:1555" in site_temp.peers
# Get I2P peers from source site
site.addPeer(TEST_B64+".i2p", 1555)
assert TEST_B64+".i2p:1555" not in site_temp.peers
assert peer_source.pex(need_num=10) == 1 # Need >5 to return also return non-connected peers
assert TEST_B64+".i2p:1555" in site_temp.peers
def testFindHash(self, i2p_manager, file_server, site, site_temp):
file_server.ip_incoming = {} # Reset flood protection
file_server.sites[site.address] = site
assert file_server.i2p_manager == None
file_server.i2p_manager = i2p_manager
client = FileServer("127.0.0.1", 1545)
client.sites[site_temp.address] = site_temp
site_temp.connection_server = client
# Add file_server as peer to client
peer_file_server = site_temp.addPeer("127.0.0.1", 1544)
assert peer_file_server.findHashIds([1234]) == {}
# Add fake peer with requred hash
fake_peer_1 = site.addPeer(TEST_B64+".i2p", 1544)
fake_peer_1.hashfield.append(1234)
fake_peer_2 = site.addPeer("1.2.3.5", 1545)
fake_peer_2.hashfield.append(1234)
fake_peer_2.hashfield.append(1235)
fake_peer_3 = site.addPeer("1.2.3.6", 1546)
fake_peer_3.hashfield.append(1235)
fake_peer_3.hashfield.append(1236)
assert peer_file_server.findHashIds([1234, 1235]) == {
1234: [('1.2.3.5', 1545), (TEST_B64+".i2p", 1544)],
1235: [('1.2.3.6', 1546), ('1.2.3.5', 1545)]
}
# Test my address adding
site.content_manager.hashfield.append(1234)
my_i2p_address = i2p_manager.getDest(site_temp.address).base64()+".i2p"
res = peer_file_server.findHashIds([1234, 1235])
assert res[1234] == [('1.2.3.5', 1545), (TEST_B64+".i2p", 1544), (my_i2p_address, 1544)]
assert res[1235] == [('1.2.3.6', 1546), ('1.2.3.5', 1545)]
# Reset
file_server.i2p_manager = None
def testSiteDest(self, i2p_manager):
assert i2p_manager.getDest("address1") != i2p_manager.getDest("address2")
assert i2p_manager.getDest("address1") == i2p_manager.getDest("address1")

View file

@ -7,6 +7,7 @@ import collections
import time
import logging
import base64
from i2p.datatypes import Destination
def atomicWrite(dest, content, mode="w"):
@ -52,11 +53,13 @@ def shellquote(*args):
def packPeers(peers):
packed_peers = {"ip4": [], "onion": []}
packed_peers = {"ip4": [], "onion": [], "i2p": []}
for peer in peers:
try:
if peer.ip.endswith(".onion"):
packed_peers["onion"].append(peer.packMyAddress())
elif peer.ip.endswith(".i2p"):
packed_peers["i2p"].append(peer.packMyAddress())
else:
packed_peers["ip4"].append(peer.packMyAddress())
except Exception, err:
@ -86,6 +89,19 @@ def unpackOnionAddress(packed):
return base64.b32encode(packed[0:-2]).lower() + ".onion", struct.unpack("H", packed[-2:])[0]
# Destination, port to packed (389+)-byte format
def packI2PAddress(dest, port):
if not isinstance(dest, Destination):
dest = dest.replace(".i2p", "")
dest = Destination(raw=dest, b64=True)
return dest.serialize() + struct.pack("H", port)
# From (389+)-byte format to Destination, port
def unpackI2PAddress(packed):
return Destination(raw=packed[0:-2]).base64() + ".i2p", struct.unpack("H", packed[-2:])[0]
# Get dir from file
# Return: data/site/content.json -> data/site
def getDirname(path):