Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge https://github.com/HelloZeroNet/ZeroNet/pull/602 #224

Closed
wants to merge 14 commits into from
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ Decentralized websites using Bitcoin crypto and the BitTorrent network - https:/
* Password-less [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
based authorization: Your account is protected by the same cryptography as your Bitcoin wallet
* Built-in SQL server with P2P data synchronization: Allows easier site development and faster page load times
* Anonymity: Full Tor network support with .onion hidden services instead of IPv4 addresses
* Anonymity:
* Full Tor network support with .onion hidden services instead of IPv4 addresses
* Full I2P network support with I2P Destinations instead of IPv4 addresses
* TLS encrypted connections
* Automatic uPnP port opening
* Plugin for multiuser (openproxy) support
Expand Down Expand Up @@ -132,6 +134,7 @@ https://zeronet.ipfsscan.io/

* File transactions are not compressed
* No private sites
* ~~No more anonymous than Bittorrent~~ (built-in full Tor and I2P support added)


## How can I create a ZeroNet site?
Expand Down
2 changes: 1 addition & 1 deletion Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provision "shell",
inline: "sudo apt-get install msgpack-python python-gevent python-pip python-dev -y"
config.vm.provision "shell",
inline: "sudo pip install msgpack --upgrade"
inline: "sudo pip install -r requirements.txt --upgrade"

end
1 change: 0 additions & 1 deletion plugins
Submodule plugins deleted from 689d93
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ websocket_client
gevent-ws
coincurve
maxminddb
i2p.socket>=0.3.1
5 changes: 4 additions & 1 deletion src/Config.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def createArguments(self):
"http://tracker.files.fm:6969/announce",
"http://t.publictracker.xyz:6969/announce",
"https://tracker.lilithraws.cf:443/announce",
"https://tracker.babico.name.tr:443/announce",
"https://tracker.babico.name.tr:443/announce"
]
# Platform specific
if sys.platform.startswith("win"):
Expand Down Expand Up @@ -312,6 +312,9 @@ def createArguments(self):
self.parser.add_argument('--tor_hs_limit', help='Maximum number of hidden services in Tor always mode', metavar='limit', type=int, default=10)
self.parser.add_argument('--tor_hs_port', help='Hidden service port in Tor always mode', metavar='limit', type=int, default=15441)

self.parser.add_argument('--i2p', help='enable: Use only for I2P peers, always: Use I2P for every connection', choices=["disable", "enable", "always"], default='enable')
self.parser.add_argument('--i2p_sam', help='I2P SAM API address', metavar='ip:port', default='127.0.0.1:7656')

self.parser.add_argument('--version', action='version', version='ZeroNet %s r%s' % (self.version, self.rev))
self.parser.add_argument('--end', help='Stop multi value argument parsing', action='store_true')

Expand Down
31 changes: 27 additions & 4 deletions src/Connection/Connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ def connect(self):
self.sock = socks.socksocket()
proxy_ip, proxy_port = config.trackers_proxy.split(":")
self.sock.set_proxy(socks.PROXY_TYPE_SOCKS5, proxy_ip, int(proxy_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 = self.createSocket()

Expand Down Expand Up @@ -344,14 +348,14 @@ def handleStream(self, message, buff):
# My handshake info
def getHandshakeInfo(self):
# No TLS for onion connections
if self.ip_type == "onion":
if self.ip_type == "onion" or self.ip_type == "i2p":
crypt_supported = []
elif self.ip in self.server.broken_ssl_ips:
crypt_supported = []
else:
crypt_supported = CryptConnection.manager.crypt_supported
# No peer id for onion connections
if self.ip_type == "onion" or self.ip in config.ip_local:
if self.ip_type == "onion" or self.ip_type == "i2p" or self.ip in config.ip_local:
peer_id = ""
else:
peer_id = self.server.peer_id
Expand All @@ -360,6 +364,17 @@ def getHandshakeInfo(self):
self.target_onion = self.handshake.get("target_ip").replace(".onion", "") # My onion address
if not self.server.tor_manager.site_onions.values():
self.server.log.warning("Unknown target onion address: %s" % self.target_onion)
# Setup peer lock from requested onion address or I2P Destination
if self.handshake:
if self.handshake.get("target_ip", "").endswith(".onion") and self.server.tor_manager.start_onions:
self.target_onion = self.handshake.get("target_ip").replace(".onion", "") # My onion address
if not self.server.tor_manager.site_onions.values():
self.server.log.warning("Unknown target onion address: %s" % self.target_onion)
elif self.handshake.get("target_ip", "").endswith(".i2p"):
self.target_dest = self.handshake.get("target_ip").replace(".i2p", "") # My I2P Destination
if not dest_sites.get(target_dest):
self.server.log.error("Unknown target I2P Destination: %s" % target_dest)
self.site_lock = "unknown"

handshake = {
"version": config.version,
Expand All @@ -374,10 +389,13 @@ def getHandshakeInfo(self):
"crypt": self.crypt,
"time": int(time.time())
}

if self.target_onion:
handshake["onion"] = self.target_onion
elif self.ip_type == "onion":
handshake["onion"] = self.server.tor_manager.getOnion("global")
elif self.ip.endswith(".i2p"):
handshake["i2p"] = self.server.i2p_manager.getDest("global").base64()

if self.is_tracker_connection:
handshake["tracker_connection"] = True
Expand All @@ -397,7 +415,8 @@ def setHandshake(self, handshake):
return False

self.handshake = handshake
if handshake.get("port_opened", None) is False and "onion" not in handshake and not self.is_private_ip: # 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 = int(handshake["fileserver_port"]) # Set peer fileserver port
Expand All @@ -411,12 +430,16 @@ def setHandshake(self, handshake):
if unprocessed_bytes:
self.unpacker.feed(unprocessed_bytes)

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 self.ip not in self.server.broken_ssl_ips:
if type(handshake["crypt_supported"][0]) is bytes:
handshake["crypt_supported"] = [item.decode() for item in handshake["crypt_supported"]] # Backward compatibility

if self.ip_type == "onion" or self.ip in config.ip_local:
if self.ip_type == "onion" or self.ip_type == "i2p" or self.ip in config.ip_local:
crypt = None
elif handshake.get("crypt"): # Recommended crypt by server
crypt = handshake["crypt"]
Expand Down
16 changes: 13 additions & 3 deletions src/Connection/ConnectionServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from Config import config
from Crypt import CryptConnection
from Crypt import CryptHash
from I2P import I2PManager
from Tor import TorManager
from Site import SiteManager

Expand All @@ -38,6 +39,11 @@ def __init__(self, ip=None, port=None, request_handler=None):
self.peer_blacklist = SiteManager.peer_blacklist

self.tor_manager = TorManager(self.ip, self.port)
if config.i2p != "disabled":
self.i2p_manager = I2PManager(self.handleIncomingConnection)
else:
self.i2p_manager = None

self.connections = [] # Connections
self.whitelist = config.ip_local # No flood protection on this ips
self.ip_incoming = {} # Incoming connections from ip in the last minute to avoid connection flood
Expand Down Expand Up @@ -171,10 +177,13 @@ def handleMessage(self, *args, **kwargs):

def getConnection(self, ip=None, port=None, peer_id=None, create=True, site=None, is_tracker_connection=False):
ip_type = helper.getIpType(ip)
has_per_site_onion = (ip.endswith(".onion") or self.port_opened.get(ip_type, None) == False) and self.tor_manager.start_onions and site
if has_per_site_onion: # Site-unique connection for Tor
has_per_site_onion = (((ip.endswith(".onion") or self.port_opened.get("onion", None) == False) and self.tor_manager.start_onions) or \
((ip.endswith(".i2p") or self.port_opened.get("i2p", None) == False) and self.i2p_manager.start_dests)) and site
if has_per_site_onion: # Site-unique connection for Tor or I2P
if ip.endswith(".onion"):
site_onion = self.tor_manager.getOnion(site.address)
if ip.endswith(".i2p"):
site_onion = self.i2p_manager.getDest(site.address)
else:
site_onion = self.tor_manager.getOnion("global")
key = ip + site_onion
Expand All @@ -196,7 +205,8 @@ def getConnection(self, ip=None, port=None, peer_id=None, create=True, site=None
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 ip.replace(".onion", "") != connection.target_onion:
if (ip.endswith(".onion") and self.tor_manager.start_onions and ip.replace(".onion", "") != connection.target_onion) or \
(ip.endswith(".i2p") and self.i2p_manager.start_dests and ip.replace(".onion", "") != connection.target_dest):
# For different site
continue
if not connection.connected and create:
Expand Down
12 changes: 10 additions & 2 deletions src/File/FileRequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,13 @@ def actionPex(self, params):
if site.addPeer(*address, source="pex"):
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"], ignore=got_peer_keys, allow_private=False))

Expand All @@ -335,7 +342,8 @@ def actionPex(self, params):
back = {
"peers": packed_peers["ipv4"],
"peers_ipv6": packed_peers["ipv6"],
"peers_onion": packed_peers["onion"]
"peers_onion": packed_peers["onion"],
"peers_i2p": packed_peers["i2p"]
}

self.response(back)
Expand Down Expand Up @@ -410,7 +418,7 @@ def actionFindHashIds(self, params):
"Found: %s for %s hashids in %.3fs" %
({key: len(val) for key, val in back.items()}, len(params["hash_ids"]), time.time() - s)
)
self.response({"peers": back["ipv4"], "peers_onion": back["onion"], "peers_ipv6": back["ipv6"], "my": my_hashes})
self.response({"peers": back["ipv4"], "peers_onion": back["onion"], "peers_i2p": back["i2p"], "peers_ipv6": back["ipv6"], "my": my_hashes})

def actionSetHashfield(self, params):
site = self.sites.get(params["site"])
Expand Down
1 change: 1 addition & 0 deletions src/File/FileServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ def checkSites(self, check_files=False, force_port_check=False):

if not self.port_opened["ipv4"]:
self.tor_manager.startOnions()
self.i2p_manager.startDests()

if not sites_checking:
check_pool = gevent.pool.Pool(5)
Expand Down
Loading
Loading