Implement I2P connections
This commit is contained in:
parent
6840b80c7b
commit
a0cb3a430c
4 changed files with 81 additions and 18 deletions
|
@ -89,6 +89,10 @@ class Connection(object):
|
|||
if not self.server.tor_manager or not self.server.tor_manager.enabled:
|
||||
raise Exception("Can't connect to onion addresses, no Tor controller present")
|
||||
self.sock = self.server.tor_manager.createSocket(self.ip, self.port)
|
||||
elif self.ip.endswith(".i2p"):
|
||||
if not self.server.i2p_manager or not self.server.i2p_manager.enabled:
|
||||
raise Exception("Can't connect to I2P addresses, no SAM API present")
|
||||
self.sock = self.server.i2p_manager.createSocket(self.ip, self.port)
|
||||
else:
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sock.connect((self.ip, int(self.port)))
|
||||
|
@ -164,24 +168,32 @@ class Connection(object):
|
|||
|
||||
# My handshake info
|
||||
def getHandshakeInfo(self):
|
||||
# No TLS for onion connections
|
||||
if self.ip.endswith(".onion"):
|
||||
# No TLS for onion or I2P connections
|
||||
if self.ip.endswith(".onion") or self.ip.endswith(".i2p"):
|
||||
crypt_supported = []
|
||||
else:
|
||||
crypt_supported = CryptConnection.manager.crypt_supported
|
||||
# No peer id for onion connections
|
||||
if self.ip.endswith(".onion") or self.ip == "127.0.0.1":
|
||||
# No peer id for onion or I2P connections
|
||||
if self.ip.endswith(".onion") or self.ip.endswith(".i2p") or self.ip == "127.0.0.1":
|
||||
peer_id = ""
|
||||
else:
|
||||
peer_id = self.server.peer_id
|
||||
# Setup peer lock from requested onion address
|
||||
if self.handshake and self.handshake.get("target_ip", "").endswith(".onion"):
|
||||
target_onion = self.handshake.get("target_ip").replace(".onion", "") # My onion address
|
||||
onion_sites = {v: k for k, v in self.server.tor_manager.site_onions.items()} # Inverse, Onion: Site address
|
||||
self.site_lock = onion_sites.get(target_onion)
|
||||
if not self.site_lock:
|
||||
self.server.log.error("Unknown target onion address: %s" % target_onion)
|
||||
self.site_lock = "unknown"
|
||||
# Setup peer lock from requested onion address or I2P Destination
|
||||
if self.handshake:
|
||||
if self.handshake.get("target_ip", "").endswith(".onion"):
|
||||
target_onion = self.handshake.get("target_ip").replace(".onion", "") # My onion address
|
||||
onion_sites = {v: k for k, v in self.server.tor_manager.site_onions.items()} # Inverse, Onion: Site address
|
||||
self.site_lock = onion_sites.get(target_onion)
|
||||
if not self.site_lock:
|
||||
self.server.log.error("Unknown target onion address: %s" % target_onion)
|
||||
self.site_lock = "unknown"
|
||||
elif self.handshake.get("target_ip", "").endswith(".i2p"):
|
||||
target_dest = self.handshake.get("target_ip").replace(".i2p", "") # My I2P Destination
|
||||
dest_sites = {v.base64(): k for k, v in self.server.i2p_manager.site_dests.items()} # Inverse, I2P Destination: Site address
|
||||
self.site_lock = dest_sites.get(target_dest)
|
||||
if not self.site_lock:
|
||||
self.server.log.error("Unknown target I2P Destination: %s" % target_dest)
|
||||
self.site_lock = "unknown"
|
||||
|
||||
handshake = {
|
||||
"version": config.version,
|
||||
|
@ -195,15 +207,21 @@ class Connection(object):
|
|||
"crypt": self.crypt
|
||||
}
|
||||
if self.site_lock:
|
||||
handshake["onion"] = self.server.tor_manager.getOnion(self.site_lock)
|
||||
if self.ip.endswith(".onion"):
|
||||
handshake["onion"] = self.server.tor_manager.getOnion(self.site_lock)
|
||||
elif self.ip.endswith(".i2p"):
|
||||
handshake["i2p"] = self.server.i2p_manager.getDest(self.site_lock).base64()
|
||||
elif self.ip.endswith(".onion"):
|
||||
handshake["onion"] = self.server.tor_manager.getOnion("global")
|
||||
elif self.ip.endswith(".i2p"):
|
||||
handshake["i2p"] = self.server.i2p_manager.getDest("global").base64()
|
||||
|
||||
return handshake
|
||||
|
||||
def setHandshake(self, handshake):
|
||||
self.handshake = handshake
|
||||
if handshake.get("port_opened", None) is False and "onion" not in handshake: # Not connectable
|
||||
if handshake.get("port_opened", None) is False and "onion" not in handshake and \
|
||||
"i2p" not in handshake: # Not connectable
|
||||
self.port = 0
|
||||
else:
|
||||
self.port = handshake["fileserver_port"] # Set peer fileserver port
|
||||
|
@ -212,9 +230,13 @@ class Connection(object):
|
|||
self.ip = handshake["onion"] + ".onion"
|
||||
self.updateName()
|
||||
|
||||
if handshake.get("i2p") and not self.ip.endswith(".i2p"): # Set incoming connection's I2P Destination
|
||||
self.ip = handshake["i2p"] + ".i2p"
|
||||
self.updateName()
|
||||
|
||||
# Check if we can encrypt the connection
|
||||
if handshake.get("crypt_supported") and handshake["peer_id"] not in self.server.broken_ssl_peer_ids:
|
||||
if self.ip.endswith(".onion"):
|
||||
if self.ip.endswith(".onion") or self.ip.endswith(".i2p"):
|
||||
crypt = None
|
||||
elif handshake.get("crypt"): # Recommended crypt by server
|
||||
crypt = handshake["crypt"]
|
||||
|
|
|
@ -12,6 +12,7 @@ from Connection import Connection
|
|||
from Config import config
|
||||
from Crypt import CryptConnection
|
||||
from Crypt import CryptHash
|
||||
from I2P import I2PManager
|
||||
from Tor import TorManager
|
||||
|
||||
|
||||
|
@ -28,6 +29,11 @@ class ConnectionServer:
|
|||
else:
|
||||
self.tor_manager = None
|
||||
|
||||
if config.i2p != "disabled":
|
||||
self.i2p_manager = I2PManager(self.handleIncomingConnection)
|
||||
else:
|
||||
self.i2p_manager = None
|
||||
|
||||
self.connections = [] # Connections
|
||||
self.whitelist = ("127.0.0.1",) # No flood protection on this ips
|
||||
self.ip_incoming = {} # Incoming connections from ip in the last minute to avoid connection flood
|
||||
|
@ -96,7 +102,8 @@ class ConnectionServer:
|
|||
connection.handleIncomingConnection(sock)
|
||||
|
||||
def getConnection(self, ip=None, port=None, peer_id=None, create=True, site=None):
|
||||
if ip.endswith(".onion") and self.tor_manager.start_onions and site: # Site-unique connection for Tor
|
||||
if ((ip.endswith(".onion") and self.tor_manager.start_onions) or \
|
||||
(ip.endswith(".i2p") and self.i2p_manager.start_dests)) and site: # Site-unique connection for Tor or I2P
|
||||
key = ip + site.address
|
||||
else:
|
||||
key = ip
|
||||
|
@ -116,7 +123,8 @@ class ConnectionServer:
|
|||
if connection.ip == ip:
|
||||
if peer_id and connection.handshake.get("peer_id") != peer_id: # Does not match
|
||||
continue
|
||||
if ip.endswith(".onion") and self.tor_manager.start_onions and connection.site_lock != site.address:
|
||||
if ((ip.endswith(".onion") and self.tor_manager.start_onions) or \
|
||||
(ip.endswith(".i2p") and self.i2p_manager.start_dests)) and connection.site_lock != site.address:
|
||||
# For different site
|
||||
continue
|
||||
if not connection.connected and create:
|
||||
|
@ -130,7 +138,8 @@ class ConnectionServer:
|
|||
if port == 0:
|
||||
raise Exception("This peer is not connectable")
|
||||
try:
|
||||
if ip.endswith(".onion") and self.tor_manager.start_onions and site: # Lock connection to site
|
||||
if ((ip.endswith(".onion") and self.tor_manager.start_onions) or \
|
||||
(ip.endswith(".i2p") and self.i2p_manager.start_dests)) and site: # Lock connection to site
|
||||
connection = Connection(self, ip, port, site_lock=site.address)
|
||||
else:
|
||||
connection = Connection(self, ip, port)
|
||||
|
|
|
@ -191,6 +191,7 @@ class FileServer(ConnectionServer):
|
|||
self.openport()
|
||||
if self.port_opened is False:
|
||||
self.tor_manager.startOnions()
|
||||
self.i2p_manager.startDests()
|
||||
|
||||
if not sites_checking:
|
||||
for address, site in self.sites.items(): # Check sites integrity
|
||||
|
|
|
@ -31,6 +31,37 @@ class TestI2P:
|
|||
# Delete
|
||||
i2p_manager.delDest(dest)
|
||||
|
||||
@pytest.mark.skipif(not pytest.config.getvalue("slow"), reason="--slow not requested (takes around ~ 1min)")
|
||||
def testConnection(self, i2p_manager, file_server, site, site_temp):
|
||||
file_server.i2p_manager.start_dests = True
|
||||
dest = file_server.i2p_manager.getDest(site.address)
|
||||
assert dest
|
||||
print "Connecting to", dest.base32()
|
||||
for retry in range(5): # Wait for Destination creation
|
||||
time.sleep(10)
|
||||
try:
|
||||
connection = file_server.getConnection(dest.base64()+".i2p", 1544)
|
||||
if connection:
|
||||
break
|
||||
except Exception, err:
|
||||
continue
|
||||
assert connection.handshake
|
||||
assert not connection.handshake["peer_id"] # No peer_id for I2P connections
|
||||
|
||||
# Return the same connection without site specified
|
||||
assert file_server.getConnection(dest.base64()+".i2p", 1544) == connection
|
||||
# No reuse for different site
|
||||
assert file_server.getConnection(dest.base64()+".i2p", 1544, site=site) != connection
|
||||
assert file_server.getConnection(dest.base64()+".i2p", 1544, site=site) == file_server.getConnection(dest.base64()+".i2p", 1544, site=site)
|
||||
site_temp.address = "1OTHERSITE"
|
||||
assert file_server.getConnection(dest.base64()+".i2p", 1544, site=site) != file_server.getConnection(dest.base64()+".i2p", 1544, site=site_temp)
|
||||
|
||||
# Only allow to query from the locked site
|
||||
file_server.sites[site.address] = site
|
||||
connection_locked = file_server.getConnection(dest.base64()+".i2p", 1544, site=site)
|
||||
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 testSiteDest(self, i2p_manager):
|
||||
assert i2p_manager.getDest("address1") != i2p_manager.getDest("address2")
|
||||
assert i2p_manager.getDest("address1") == i2p_manager.getDest("address1")
|
||||
|
|
Loading…
Reference in a new issue