Onionr/static-data/default-plugins/torgossip/client.py

175 lines
5.7 KiB
Python
Raw Normal View History

2021-02-02 06:58:06 +00:00
"""Onionr - Private P2P Communication.
Torgossip client
Create streams to random peers
"""
2021-02-08 01:52:12 +00:00
import sys
2021-02-06 06:35:18 +00:00
from base64 import b32encode
from os import path
2021-02-11 21:03:41 +00:00
from time import sleep
from typing import TYPE_CHECKING
from random import SystemRandom
2021-02-20 00:41:46 +00:00
from threading import Thread
2021-02-06 06:35:18 +00:00
import socks as socket
2021-02-13 20:22:07 +00:00
from stem import SocketClosed
2021-02-06 06:35:18 +00:00
from netcontroller.torcontrol.onionserviceonline import service_online_recently
from netcontroller.torcontrol import torcontroller
2021-02-11 21:03:41 +00:00
import logger
if TYPE_CHECKING:
from .peerdb import TorGossipPeers
2021-02-06 06:35:18 +00:00
from stem.control import Controller
2021-02-09 23:02:19 +00:00
sys.path.insert(0, path.dirname(path.realpath(__file__)))
2021-02-08 01:52:12 +00:00
from commands import GossipCommands
2021-02-11 21:03:41 +00:00
from clientfuncs import download_blocks
2021-02-13 20:22:07 +00:00
from constants import HOSTNAME_FILE
2021-02-02 06:58:06 +00:00
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
2021-02-11 21:03:41 +00:00
def client_funcs(shared_state, socket_pool):
controller = torcontroller.get_controller()
2021-02-11 21:03:41 +00:00
def _client_pool(shared_state, socket_pool: dict):
2021-02-21 02:29:00 +00:00
peer_db: 'TorGossipPeers' = shared_state.get_by_string(
'TorGossipPeers')
2021-02-11 21:03:41 +00:00
socks_port = shared_state.get_by_string('NetController').socksPort
2021-02-11 21:03:41 +00:00
peers = peer_db.get_highest_score_peers(20)
SystemRandom().shuffle(peers)
2021-02-11 21:03:41 +00:00
for peer in peers:
2021-02-13 20:22:07 +00:00
peer = peer[0]
2021-02-11 21:03:41 +00:00
if peer in socket_pool:
continue
2021-02-13 20:22:07 +00:00
p_encoded = b32encode(peer).decode().lower() + ".onion"
if not service_online_recently(controller, p_encoded):
2021-02-11 21:03:41 +00:00
continue
s = socket.socksocket()
s.set_proxy(
socket.SOCKS5, '127.0.0.1', socks_port, rdns=True)
try:
2021-02-15 09:11:16 +00:00
s.connect(
2021-02-13 20:22:07 +00:00
(p_encoded, 2021))
2021-02-15 09:11:16 +00:00
socket_pool[peer] = s
logger.info(f"[TorGossip] Connected to {p_encoded}", terminal=True)
2021-02-11 21:03:41 +00:00
except socket.GeneralProxyError:
s.close()
2021-02-08 01:52:12 +00:00
2021-02-11 21:03:41 +00:00
def client_loop(shared_state, socket_pool):
2021-02-20 00:41:46 +00:00
2021-02-11 21:03:41 +00:00
sleep_t = 60
block_db = shared_state.get_by_string('SafeDB')
2021-02-06 06:35:18 +00:00
2021-02-20 00:41:46 +00:00
peer_info = {}
block_types = ['txt', 'bin', 'png']
peers = list(socket_pool)
SystemRandom().shuffle(peers)
def download_all_missed():
while not socket_pool:
sleep(4)
peer = list(socket_pool)[0]
logger.info("[TorGossip] Downloading missed blocks", terminal=True)
for bl_type in block_types:
while True:
try:
peer_info[peer]
except KeyError:
peer_info[peer] = {'offsets': {}}
else:
try:
peer_info[peer]['offsets']
except KeyError:
peer_info[peer]['offsets'] = {}
try:
offset = peer_info[peer]['offsets'][bl_type]
except KeyError:
offset = 0
try:
peer_info[peer]['offsets'][bl_type] = \
download_blocks(
block_db, socket_pool[peer], offset, bl_type)
except BrokenPipeError:
del peer_info[peer]['offsets']
del socket_pool[peer]
else:
# Go back to for loop
break
Thread(target=download_all_missed, daemon=True).start()
2021-02-11 21:03:41 +00:00
while True:
if not socket_pool:
2021-02-13 20:22:07 +00:00
try:
_client_pool(shared_state, socket_pool)
except SocketClosed: # Probably shutting down, or tor crashed
sleep(1)
continue
2021-02-11 21:03:41 +00:00
try:
2021-02-21 02:29:00 +00:00
peers[0]
2021-02-11 21:03:41 +00:00
except IndexError:
logger.error(
"There are no known TorGossip peers." +
2021-02-13 20:22:07 +00:00
f" Sleeping for {sleep_t}s",
2021-02-11 21:03:41 +00:00
terminal=True)
sleep(sleep_t)
continue
2021-02-20 00:41:46 +00:00
peers = list(socket_pool)
SystemRandom().shuffle(peers)
2021-02-11 21:03:41 +00:00
_client_pool(shared_state, socket_pool)
client_loop(shared_state, socket_pool)
2021-02-08 07:32:31 +00:00
2021-02-11 21:03:41 +00:00
def _add_bootstrap_peers(peer_db: 'TorGossipPeers'):
2021-02-13 20:22:07 +00:00
# If we have peers, ignore encrypt mark in db
if len(peer_db.db.db_conn.keys()) > 1:
2021-02-11 21:03:41 +00:00
return
2021-02-13 20:22:07 +00:00
with open(HOSTNAME_FILE, "rb") as hf:
our_host = b32encode(hf.read()).decode().lower()
2021-02-11 21:03:41 +00:00
bootstap_peers = path.dirname(path.realpath(__file__)) + "/bootstrap.txt"
with open(bootstap_peers, 'r') as bs_peers:
peers = bs_peers.read().split(',')
2021-02-15 09:11:16 +00:00
try:
peers.remove(our_host)
except ValueError:
pass
2021-02-11 21:03:41 +00:00
for peer in peers:
try:
peer_db.db.get(peer)
except KeyError:
pass
else:
continue
if peer:
peer_db.add_peer(peer)
2021-02-02 23:24:35 +00:00
2021-02-02 06:58:06 +00:00
2021-02-08 01:52:12 +00:00
def start_client(shared_state):
# add boot strap peers to db if we need peers
_add_bootstrap_peers(shared_state.get_by_string('TorGossipPeers'))
# create and fill pool of sockets to peers (over tor socks)
socket_pool = {}
2021-02-11 21:03:41 +00:00
client_funcs(shared_state, socket_pool)