- Removed direct connections (will be a different project in the future)
- removed chat for now - removed onionrcommunicatortimers
This commit is contained in:
		
							parent
							
								
									59330149e1
								
							
						
					
					
						commit
						30a4285b92
					
				
					 25 changed files with 21 additions and 823 deletions
				
			
		|  | @ -7,7 +7,6 @@ from gevent import sleep | |||
| 
 | ||||
| from httpapi import security, friendsapi, configapi, insertblock | ||||
| from httpapi import miscclientapi, onionrsitesapi, apiutils | ||||
| from httpapi import directconnections | ||||
| from httpapi import themeapi | ||||
| from httpapi import fileoffsetreader | ||||
| from httpapi.sse.private import private_sse_blueprint | ||||
|  | @ -42,8 +41,6 @@ def register_private_blueprints(private_api, app): | |||
|     app.register_blueprint(onionrsitesapi.site_api) | ||||
|     app.register_blueprint(apiutils.shutdown.shutdown_bp) | ||||
|     app.register_blueprint(miscclientapi.staticfiles.static_files_bp) | ||||
|     app.register_blueprint(directconnections.DirectConnectionManagement( | ||||
|         private_api).direct_conn_management_bp) | ||||
|     app.register_blueprint(themeapi.theme_blueprint) | ||||
|     app.register_blueprint(private_sse_blueprint) | ||||
|     app.register_blueprint(fileoffsetreader.offset_reader_api) | ||||
|  |  | |||
|  | @ -12,8 +12,6 @@ import onionrpeers | |||
| import onionrplugins as plugins | ||||
| from . import onlinepeers | ||||
| from . import uploadqueue | ||||
| from communicatorutils import servicecreator | ||||
| from communicatorutils import onionrcommunicatortimers | ||||
| from communicatorutils import downloadblocks | ||||
| from communicatorutils import lookupblocks | ||||
| from communicatorutils import lookupadders | ||||
|  | @ -25,7 +23,6 @@ from communicatorutils import housekeeping | |||
| from communicatorutils import netcheck | ||||
| from onionrthreads import add_onionr_thread | ||||
| from onionrcommands.openwebinterface import get_url | ||||
| import onionrservices | ||||
| from netcontroller import NetController | ||||
| from . import bootstrappeers | ||||
| from . import daemoneventhooks | ||||
|  | @ -44,8 +41,6 @@ from . import daemoneventhooks | |||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
| """ | ||||
| 
 | ||||
| OnionrCommunicatorTimers = onionrcommunicatortimers.OnionrCommunicatorTimers | ||||
| 
 | ||||
| config.reload() | ||||
| 
 | ||||
| 
 | ||||
|  | @ -66,9 +61,6 @@ class OnionrCommunicatorDaemon: | |||
|         if config.get('general.offline_mode', False): | ||||
|             self.kv.put('isOnline', False) | ||||
| 
 | ||||
|         # list of timer instances | ||||
|         self.timers = [] | ||||
| 
 | ||||
|         # initialize core with Tor socks port being 3rd argument | ||||
|         self.proxyPort = shared_state.get(NetController).socksPort | ||||
| 
 | ||||
|  | @ -118,24 +110,11 @@ class OnionrCommunicatorDaemon: | |||
|             uploadblocks.upload_blocks_from_communicator, | ||||
|             [self.shared_state], 5, 1) | ||||
| 
 | ||||
|         # Setup direct connections | ||||
|         if config.get('general.ephemeral_tunnels', False): | ||||
|             self.services = onionrservices.OnionrServices() | ||||
|             self.active_services = [] | ||||
|             self.service_greenlets = [] | ||||
|             OnionrCommunicatorTimers( | ||||
|                 self, servicecreator.service_creator, 5, | ||||
|                 max_threads=50, my_args=[self]) | ||||
|         else: | ||||
|             self.services = None | ||||
| 
 | ||||
|         # {peer_pubkey: ephemeral_address}, the address to reach them | ||||
|         self.direct_connection_clients = {} | ||||
| 
 | ||||
|         # This timer creates deniable blocks, | ||||
|         # in an attempt to further obfuscate block insertion metadata | ||||
|         if config.get('general.insert_deniable_blocks', True): | ||||
|             add_onionr_thread(deniableinserts.insert_deniable_block, [], 180, 10) | ||||
|             add_onionr_thread( | ||||
|                 deniableinserts.insert_deniable_block, [], 180, 10) | ||||
| 
 | ||||
|         if config.get('transports.tor', True): | ||||
|             # Timer to check for connectivity, | ||||
|  | @ -147,24 +126,14 @@ class OnionrCommunicatorDaemon: | |||
|         if config.get('general.security_level', 1) == 0 \ | ||||
|                 and config.get('general.announce_node', True): | ||||
|             # Default to high security level incase config breaks | ||||
|             announceTimer = OnionrCommunicatorTimers( | ||||
|                 self, | ||||
|                 announcenode.announce_node, | ||||
|                 3600, my_args=[self], requires_peer=True, max_threads=1) | ||||
|             announceTimer.count = (announceTimer.frequency - 60) | ||||
|             add_onionr_thread( | ||||
|                 announcenode.announce_node, [self.shared_state], 600, 60) | ||||
|         else: | ||||
|             logger.debug('Will not announce node.') | ||||
| 
 | ||||
|         # Timer to delete malfunctioning or long-dead peers | ||||
|         cleanupTimer = OnionrCommunicatorTimers( | ||||
|             self, self.peerCleanup, 300, requires_peer=True) | ||||
|         add_onionr_thread(onionrpeers.peer_cleanup, [], 300, 300) | ||||
| 
 | ||||
|         # Timer to cleanup dead ephemeral forward secrecy keys | ||||
|         OnionrCommunicatorTimers( | ||||
|             self, housekeeping.clean_keys, 15, my_args=[self], max_threads=1) | ||||
| 
 | ||||
|         # Adjust initial timer triggers | ||||
|         cleanupTimer.count = (cleanupTimer.frequency - 60) | ||||
|         add_onionr_thread(housekeeping.clean_keys, [], 15, 1) | ||||
| 
 | ||||
|         if config.get('general.use_bootstrap_list', True): | ||||
|             bootstrappeers.add_bootstrap_list_to_peer_list( | ||||
|  | @ -192,11 +161,6 @@ class OnionrCommunicatorDaemon: | |||
|         try: | ||||
|             while not self.shared_state.get_by_string( | ||||
|                     'DeadSimpleKV').get('shutdown'): | ||||
|                 for i in self.timers: | ||||
|                     if self.shared_state.get_by_string( | ||||
|                             'DeadSimpleKV').get('shutdown'): | ||||
|                         break | ||||
|                     i.processTimer() | ||||
|                 time.sleep(self.delay) | ||||
|         except KeyboardInterrupt: | ||||
|             self.shared_state.get_by_string( | ||||
|  | @ -204,14 +168,7 @@ class OnionrCommunicatorDaemon: | |||
| 
 | ||||
|         logger.info( | ||||
|             'Goodbye. (Onionr is cleaning up, and will exit)', terminal=True) | ||||
|         try: | ||||
|             self.service_greenlets | ||||
|         except AttributeError: | ||||
|             pass | ||||
|         else: | ||||
|             # Stop onionr direct connection services | ||||
|             for server in self.service_greenlets: | ||||
|                 server.stop() | ||||
| 
 | ||||
|         try: | ||||
|             time.sleep(0.5) | ||||
|         except KeyboardInterrupt: | ||||
|  |  | |||
|  | @ -32,16 +32,16 @@ if TYPE_CHECKING: | |||
| """ | ||||
| 
 | ||||
| 
 | ||||
| def announce_node(daemon): | ||||
| def announce_node(shared_state): | ||||
|     """Announce our node to our peers.""" | ||||
|     ret_data = False | ||||
|     kv: "DeadSimpleKV" = daemon.shared_state.get_by_string("DeadSimpleKV") | ||||
| 
 | ||||
|     kv: "DeadSimpleKV" = shared_state.get_by_string("DeadSimpleKV") | ||||
|     config = shared_state.get_by_string("OnionrCommunicatorDaemon").config | ||||
|     # Do not let announceCache get too large | ||||
|     if len(kv.get('announceCache')) >= 10000: | ||||
|         kv.get('announceCache').popitem() | ||||
| 
 | ||||
|     if daemon.config.get('general.security_level', 0) == 0: | ||||
|     if config.get('general.security_level', 0) == 0: | ||||
|         # Announce to random online peers | ||||
|         for i in kv.get('onlinePeers'): | ||||
|             if i not in kv.get('announceCache'): | ||||
|  | @ -67,12 +67,11 @@ def announce_node(daemon): | |||
|             if basicrequests.do_post_request( | ||||
|                     url, | ||||
|                     data, | ||||
|                     port=daemon.shared_state.get(NetController).socksPort)\ | ||||
|                     port=shared_state.get(NetController).socksPort)\ | ||||
|                     == 'Success': | ||||
|                 logger.info('Successfully introduced node to ' + peer, | ||||
|                             terminal=True) | ||||
|                 ret_data = True | ||||
|                 keydb.transportinfo.set_address_info(peer, 'introduced', 1) | ||||
| 
 | ||||
|     daemon.decrementThreadCount('announce_node') | ||||
|     return ret_data | ||||
|  |  | |||
|  | @ -68,7 +68,7 @@ def clean_old_blocks(shared_state): | |||
|             logger.info('Deleted block: %s' % (oldest,)) | ||||
| 
 | ||||
| 
 | ||||
| def clean_keys(comm_inst): | ||||
| def clean_keys(): | ||||
|     """Delete expired forward secrecy keys""" | ||||
|     conn = sqlite3.connect(dbfiles.user_id_info_db, | ||||
|                            timeout=DATABASE_LOCK_TIMEOUT) | ||||
|  | @ -88,5 +88,3 @@ def clean_keys(comm_inst): | |||
|     conn.close() | ||||
| 
 | ||||
|     onionrusers.deleteExpiredKeys() | ||||
| 
 | ||||
|     comm_inst.decrementThreadCount('clean_keys') | ||||
|  |  | |||
|  | @ -1,99 +0,0 @@ | |||
| """ | ||||
| Onionr - Private P2P Communication. | ||||
| 
 | ||||
| This file contains timer control for the communicator | ||||
| """ | ||||
| from __future__ import annotations  # thank you python, very cool | ||||
| import uuid | ||||
| import threading | ||||
| 
 | ||||
| import onionrexceptions | ||||
| import logger | ||||
| 
 | ||||
| from typing import TYPE_CHECKING | ||||
| from typing import Callable, NewType, Iterable | ||||
| if TYPE_CHECKING: | ||||
|     from deadsimplekv import DeadSimpleKV | ||||
|     from communicator import OnionrCommunicatorDaemon | ||||
| """ | ||||
|     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/>. | ||||
| """ | ||||
| 
 | ||||
| 
 | ||||
| CallFreqSeconds = NewType('CallFreqSeconds', int) | ||||
| 
 | ||||
| 
 | ||||
| class OnionrCommunicatorTimers: | ||||
|     def __init__(self, daemon_inst: OnionrCommunicatorDaemon, | ||||
|                  timer_function: Callable, frequency: CallFreqSeconds, | ||||
|                  make_thread: bool = True, | ||||
|                  thread_amount: int = 1, max_threads: int = 5, | ||||
|                  requires_peer: bool = False, my_args: Iterable = []): | ||||
|         self.timer_function = timer_function | ||||
|         self.frequency = frequency | ||||
|         self.thread_amount = thread_amount | ||||
|         self.make_thread = make_thread | ||||
|         self.requires_peer = requires_peer | ||||
|         self.daemon_inst = daemon_inst | ||||
|         self.max_threads = max_threads | ||||
|         self.args = my_args | ||||
|         self.kv: "DeadSimpleKV" = daemon_inst.shared_state.get_by_string( | ||||
|             "DeadSimpleKV") | ||||
| 
 | ||||
|         self.daemon_inst.timers.append(self) | ||||
|         self.count = 0 | ||||
| 
 | ||||
|     def processTimer(self): | ||||
| 
 | ||||
|         # mark # of instances of a thread we have (decremented at thread end) | ||||
|         try: | ||||
|             self.daemon_inst.threadCounts[self.timer_function.__name__] | ||||
|         except KeyError: | ||||
|             self.daemon_inst.threadCounts[self.timer_function.__name__] = 0 | ||||
| 
 | ||||
|         # execute timer's func, if we are not missing *required* online peer | ||||
|         if self.count == self.frequency and not self.kv.get('shutdown'): | ||||
|             try: | ||||
|                 if self.requires_peer and \ | ||||
|                         len(self.kv.get('onlinePeers')) == 0: | ||||
|                     raise onionrexceptions.OnlinePeerNeeded | ||||
|             except onionrexceptions.OnlinePeerNeeded: | ||||
|                 return | ||||
|             else: | ||||
|                 if self.make_thread: | ||||
|                     for i in range(self.thread_amount): | ||||
|                         """ | ||||
|                         Log if a timer has max num of active threads | ||||
|                         If this logs frequently it is indicative of a bug | ||||
|                         or need for optimization | ||||
|                         """ | ||||
|                         if self.daemon_inst.threadCounts[ | ||||
|                                 self.timer_function.__name__] >= \ | ||||
|                                 self.max_threads: | ||||
|                             logger.debug( | ||||
|                                 f'{self.timer_function.__name__} is currently using the maximum number of threads, not starting another.')  # noqa | ||||
|                         # if active number of threads for timer not reached yet | ||||
|                         else: | ||||
|                             self.daemon_inst.threadCounts[ | ||||
|                                 self.timer_function.__name__] += 1 | ||||
|                             newThread = threading.Thread( | ||||
|                                 target=self.timer_function, args=self.args, | ||||
|                                 daemon=True, | ||||
|                                 name=self.timer_function.__name__ + ' - ' + | ||||
|                                 str(uuid.uuid4())) | ||||
|                             newThread.start() | ||||
|                 else: | ||||
|                     self.timer_function() | ||||
|             self.count = -1  # negative 1 because its incremented at bottom | ||||
|         self.count += 1 | ||||
|  | @ -1,47 +0,0 @@ | |||
| """ | ||||
| Onionr - Private P2P Communication. | ||||
| 
 | ||||
| Creates an onionr direct connection service by scanning all connection blocks | ||||
| """ | ||||
| import communicator | ||||
| from onionrblocks import onionrblockapi | ||||
| from onionrutils import stringvalidators, bytesconverter | ||||
| from coredb import blockmetadb | ||||
| from onionrservices import server_exists | ||||
| """ | ||||
|     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/>. | ||||
| """ | ||||
| 
 | ||||
| 
 | ||||
| def service_creator(daemon): | ||||
|     assert isinstance(daemon, communicator.OnionrCommunicatorDaemon) | ||||
| 
 | ||||
|     # Find socket connection blocks | ||||
|     # TODO cache blocks and only look at recently received ones | ||||
|     con_blocks = blockmetadb.get_blocks_by_type('con') | ||||
|     for b in con_blocks: | ||||
|         if b not in daemon.active_services: | ||||
|             bl = onionrblockapi.Block(b, decrypt=True) | ||||
|             bs = bytesconverter.bytes_to_str(bl.bcontent) + '.onion' | ||||
|             if server_exists(bl.signer): | ||||
|                 continue | ||||
|             if stringvalidators.validate_pub_key(bl.signer) and \ | ||||
|                     stringvalidators.validate_transport(bs): | ||||
|                 signer = bytesconverter.bytes_to_str(bl.signer) | ||||
|                 daemon.active_services.append(b) | ||||
|                 daemon.active_services.append(signer) | ||||
|                 if not daemon.services.create_server(signer, bs, daemon): | ||||
|                     daemon.active_services.remove(b) | ||||
|                     daemon.active_services.remove(signer) | ||||
|     daemon.decrementThreadCount('service_creator') | ||||
|  | @ -1,78 +0,0 @@ | |||
| """Onionr - Private P2P Communication. | ||||
| 
 | ||||
| Misc client API endpoints too small to need their own file and that need access to the client api inst | ||||
| """ | ||||
| # For the client creation thread | ||||
| import threading | ||||
| 
 | ||||
| # For direct connection management HTTP endpoints | ||||
| from flask import Response | ||||
| # To make the direct connection management blueprint in the webUI | ||||
| from flask import Blueprint | ||||
| # Mainly to access the shared toomanyobjs object | ||||
| from flask import g | ||||
| import deadsimplekv | ||||
| 
 | ||||
| import filepaths | ||||
| import onionrservices | ||||
| from onionrservices import pool | ||||
| """ | ||||
|     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/>. | ||||
| """ | ||||
| 
 | ||||
| 
 | ||||
| def _get_communicator(g): | ||||
|     while True: | ||||
|         try: | ||||
|             return g.too_many.get_by_string("OnionrCommunicatorDaemon") | ||||
|         except KeyError: | ||||
|             pass | ||||
| 
 | ||||
| 
 | ||||
| class DirectConnectionManagement: | ||||
|     def __init__(self, client_api): | ||||
|         direct_conn_management_bp = Blueprint( | ||||
|             'direct_conn_management', __name__) | ||||
|         self.direct_conn_management_bp = direct_conn_management_bp | ||||
| 
 | ||||
|         cache = deadsimplekv.DeadSimpleKV(filepaths.cached_storage) | ||||
| 
 | ||||
|         @direct_conn_management_bp.route('/dc-client/isconnected/<pubkey>') | ||||
|         def is_connected(pubkey): | ||||
|             communicator = _get_communicator(g) | ||||
|             resp = "" | ||||
|             if pubkey in communicator.direct_connection_clients: | ||||
|                 resp = communicator.direct_connection_clients[pubkey] | ||||
|             return Response(resp) | ||||
| 
 | ||||
|         @direct_conn_management_bp.route('/dc-client/connect/<pubkey>') | ||||
|         def make_new_connection(pubkey): | ||||
|             communicator = _get_communicator(g) | ||||
|             resp = "pending" | ||||
|             if pubkey in communicator.shared_state.get( | ||||
|                     pool.ServicePool).bootstrap_pending: | ||||
|                 return Response(resp) | ||||
| 
 | ||||
|             if pubkey in communicator.direct_connection_clients: | ||||
|                 resp = communicator.direct_connection_clients[pubkey] | ||||
|             else: | ||||
|                 """Spawn a thread that will create the client and eventually add it to the | ||||
|                 communicator.active_services | ||||
|                 """ | ||||
|                 threading.Thread( | ||||
|                     target=onionrservices.OnionrServices().create_client, | ||||
|                     args=[pubkey, communicator], daemon=True).start() | ||||
| 
 | ||||
|             return Response(resp) | ||||
|              | ||||
|  | @ -60,9 +60,11 @@ def get_block_data(public_API, b_hash): | |||
|                     # Encode in case data is binary | ||||
|                     block = block.encode('utf-8') | ||||
|                 except AttributeError: | ||||
|                     if len(block) == 0: | ||||
|                     # 404 if no block data | ||||
|                     if not block: | ||||
|                         abort(404) | ||||
|                     if not len(block): | ||||
|                         abort(404) | ||||
|                 #block = bytesconverter.str_to_bytes(block) | ||||
|                 resp = block | ||||
|     if len(resp) == 0: | ||||
|         abort(404) | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ import hmac | |||
| 
 | ||||
| from flask import Blueprint, request, abort, g | ||||
| 
 | ||||
| from onionrservices import httpheaders | ||||
| from httpapi import httpheaders | ||||
| from . import pluginwhitelist | ||||
| import config | ||||
| import logger | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| Process incoming requests to the public api server for certain attacks | ||||
| """ | ||||
| from flask import Blueprint, request, abort, g | ||||
| from onionrservices import httpheaders | ||||
| from httpapi import httpheaders | ||||
| from onionrutils import epoch | ||||
| from lan import getip | ||||
| """ | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| Process incoming requests to the public api server for certain attacks | ||||
| """ | ||||
| from flask import Blueprint, request, abort, g | ||||
| from onionrservices import httpheaders | ||||
| from httpapi import httpheaders | ||||
| from onionrutils import epoch | ||||
| from utils import gettransports | ||||
| """ | ||||
|  |  | |||
|  | @ -1,13 +0,0 @@ | |||
| # onionrservices | ||||
| 
 | ||||
| onionservices is a submodule to handle direct connections to Onionr peers, using the Onionr network to broker them. | ||||
| 
 | ||||
| ## Files | ||||
| 
 | ||||
| __init__.py: Contains the OnionrServices class which can create direct connection servers or clients. | ||||
| 
 | ||||
| bootstrapservice.py: Creates a bootstrap server for a peer and announces the connection by creating a block encrypted to the peer we want to connect to. | ||||
| 
 | ||||
| connectionserver.py: Creates a direct connection server for a peer | ||||
| 
 | ||||
| httpheaders.py: Modifies a Flask response object http response headers for security purposes. | ||||
|  | @ -1,69 +0,0 @@ | |||
| """Onionr - Private P2P Communication. | ||||
| 
 | ||||
| Onionr services provide the server component to direct connections | ||||
| """ | ||||
| import time | ||||
| from . import connectionserver, bootstrapservice, serverexists | ||||
| from onionrutils import stringvalidators, basicrequests | ||||
| import config | ||||
| """ | ||||
|     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/>. | ||||
| """ | ||||
| 
 | ||||
| server_exists = serverexists.server_exists | ||||
| 
 | ||||
| 
 | ||||
| class OnionrServices: | ||||
|     """Create a client or server for connecting to peer interfaces.""" | ||||
|     def __init__(self): | ||||
|         self.servers = {} | ||||
|         self.clients = {} | ||||
|         self.shutdown = False | ||||
| 
 | ||||
|     def create_server(self, peer, address, comm_inst): | ||||
|         """ | ||||
|         Create a server for direct connections | ||||
| 
 | ||||
|         When a client wants to connect, contact their bootstrap address and tell them our ephemeral address for our service by creating a new ConnectionServer instance | ||||
|         """ | ||||
|         if not stringvalidators.validate_transport(address): | ||||
|             raise ValueError('address must be valid') | ||||
|         # How many times to attempt contacting the bootstrap server | ||||
|         BOOTSTRAP_TRIES = 10 | ||||
|         # Seconds to wait before trying bootstrap again | ||||
|         TRY_WAIT = 3 | ||||
|         # HTTP is fine because .onion/i2p is encrypted/authenticated | ||||
|         base_url = 'http://%s/' % (address,) | ||||
|         socks = config.get('tor.socksport') | ||||
|         for x in range(BOOTSTRAP_TRIES): | ||||
|             if basicrequests.do_get_request( | ||||
|                     base_url + 'ping', port=socks, ignoreAPI=True) == 'pong!': | ||||
|                 # if bootstrap sever is online, tell them our service address | ||||
|                 connectionserver.ConnectionServer( | ||||
|                     peer, address, comm_inst=comm_inst) | ||||
|             else: | ||||
|                 time.sleep(TRY_WAIT) | ||||
|         else: | ||||
|             return False | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def create_client(peer, comm_inst=None): | ||||
|         # Create ephemeral onion service to bootstrap connection to server | ||||
|         if comm_inst is not None: | ||||
|             try: | ||||
|                 return comm_inst.direct_connection_clients[peer] | ||||
|             except KeyError: | ||||
|                 pass | ||||
|         address = bootstrapservice.bootstrap_client_service(peer, comm_inst) | ||||
|         return address | ||||
|  | @ -1,116 +0,0 @@ | |||
| ''' | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     Bootstrap onion direct connections for the clients | ||||
| ''' | ||||
| ''' | ||||
|     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/>. | ||||
| ''' | ||||
| import time, threading, uuid, os | ||||
| from gevent.pywsgi import WSGIServer, WSGIHandler | ||||
| from stem.control import Controller | ||||
| from flask import Flask, Response | ||||
| from netcontroller import get_open_port | ||||
| from . import httpheaders | ||||
| from onionrutils import stringvalidators, epoch | ||||
| import logger | ||||
| import config, onionrblocks, filepaths | ||||
| import onionrexceptions | ||||
| import deadsimplekv as simplekv | ||||
| from . import pool | ||||
| 
 | ||||
| def __bootstrap_timeout(server: WSGIServer, timeout: int, signal_object): | ||||
|     time.sleep(timeout) | ||||
|     signal_object.timed_out = True | ||||
|     server.stop() | ||||
| 
 | ||||
| def bootstrap_client_service(peer, comm_inst=None, bootstrap_timeout=300): | ||||
|     ''' | ||||
|         Bootstrap client services | ||||
|     ''' | ||||
|     if not stringvalidators.validate_pub_key(peer): | ||||
|         raise ValueError('Peer must be valid base32 ed25519 public key') | ||||
| 
 | ||||
|     connection_pool = None | ||||
| 
 | ||||
|     # here we use a lambda for the timeout thread to set to true | ||||
|     timed_out = lambda: None | ||||
|     timed_out.timed_out = False | ||||
| 
 | ||||
|     bootstrap_port = get_open_port() | ||||
|     bootstrap_app = Flask(__name__) | ||||
|     bootstrap_app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 | ||||
| 
 | ||||
|     http_server = WSGIServer(('127.0.0.1', bootstrap_port), bootstrap_app, log=None) | ||||
|     try: | ||||
|         if comm_inst is None: raise ValueError | ||||
|     except (AttributeError, ValueError) as e: | ||||
|         pass | ||||
|     else: | ||||
|         comm_inst.service_greenlets.append(http_server) | ||||
|         connection_pool = comm_inst.shared_state.get(pool.ServicePool) | ||||
| 
 | ||||
|     bootstrap_address = '' | ||||
|     shutdown = False | ||||
|     bs_id = str(uuid.uuid4()) | ||||
|     key_store = simplekv.DeadSimpleKV(filepaths.cached_storage) | ||||
| 
 | ||||
|     @bootstrap_app.route('/ping') | ||||
|     def get_ping(): | ||||
|         return "pong!" | ||||
| 
 | ||||
|     @bootstrap_app.after_request | ||||
|     def afterReq(resp): | ||||
|         # Security headers | ||||
|         resp = httpheaders.set_default_onionr_http_headers(resp) | ||||
|         return resp | ||||
| 
 | ||||
|     @bootstrap_app.route('/bs/<address>', methods=['POST']) | ||||
|     def get_bootstrap(address): | ||||
|         if stringvalidators.validate_transport(address + '.onion'): | ||||
|             # Set the bootstrap address then close the server | ||||
|             bootstrap_address = address + '.onion' | ||||
|             key_store.put(bs_id, bootstrap_address) | ||||
|             http_server.stop() | ||||
|             return Response("success") | ||||
|         else: | ||||
|             return Response("") | ||||
| 
 | ||||
|     with Controller.from_port(port=config.get('tor.controlPort')) as controller: | ||||
|         if not connection_pool is None: connection_pool.bootstrap_pending.append(peer) | ||||
|         # Connect to the Tor process for Onionr | ||||
|         controller.authenticate(config.get('tor.controlpassword')) | ||||
|         # Create the v3 onion service | ||||
|         response = controller.create_ephemeral_hidden_service({80: bootstrap_port}, key_type = 'NEW', key_content = 'ED25519-V3', await_publication = True) | ||||
|         onionrblocks.insert(response.service_id, header='con', sign=True, encryptType='asym', | ||||
|         asymPeer=peer, disableForward=True, expire=(epoch.get_epoch() + bootstrap_timeout)) | ||||
| 
 | ||||
|         threading.Thread(target=__bootstrap_timeout, args=[http_server, bootstrap_timeout, timed_out], daemon=True).start() | ||||
| 
 | ||||
|         # Run the bootstrap server | ||||
|         try: | ||||
|             http_server.serve_forever() | ||||
|         except TypeError: | ||||
|             pass | ||||
|         # This line reached when server is shutdown by being bootstrapped | ||||
|     # Add the address to the client pool | ||||
|     if not comm_inst is None: | ||||
|         connection_pool.bootstrap_pending.remove(peer) | ||||
|         if timed_out.timed_out: | ||||
|             logger.warn('Could not connect to %s due to timeout' % (peer,)) | ||||
|             return None | ||||
|         comm_inst.direct_connection_clients[peer] = response.service_id | ||||
| 
 | ||||
|     # Now that the bootstrap server has received a server, return the address | ||||
|     return key_store.get(bs_id) | ||||
|  | @ -1,89 +0,0 @@ | |||
| ''' | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     This module does the second part of the bootstrap block handshake and creates the API server | ||||
| ''' | ||||
| ''' | ||||
|     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/>. | ||||
| ''' | ||||
| from gevent.pywsgi import WSGIServer | ||||
| from stem.control import Controller | ||||
| from flask import Flask | ||||
| import logger, httpapi | ||||
| import onionrexceptions, config, filepaths | ||||
| from netcontroller import get_open_port | ||||
| from httpapi import apiutils | ||||
| from onionrutils import stringvalidators, basicrequests, bytesconverter | ||||
| from . import httpheaders | ||||
| import deadsimplekv as simplekv | ||||
| 
 | ||||
| class ConnectionServer: | ||||
|     def __init__(self, peer, address, comm_inst=None): | ||||
| 
 | ||||
|         if not stringvalidators.validate_pub_key(peer): | ||||
|             raise ValueError('Peer must be valid base32 ed25519 public key') | ||||
|          | ||||
|         socks = config.get('tor.socksport') # Load config for Tor socks port for proxy | ||||
|         service_app = Flask(__name__) # Setup Flask app for server. | ||||
|         service_port = get_open_port() | ||||
|         service_ip = apiutils.setbindip.set_bind_IP() | ||||
|         http_server = WSGIServer(('127.0.0.1', service_port), service_app, log=None) | ||||
|         comm_inst.service_greenlets.append(http_server) | ||||
|         key_store = simplekv.DeadSimpleKV(filepaths.cached_storage) | ||||
| 
 | ||||
|         # TODO define basic endpoints useful for direct connections like stats | ||||
|          | ||||
|         httpapi.load_plugin_blueprints(service_app, blueprint='direct_blueprint') | ||||
| 
 | ||||
|         @service_app.route('/ping') | ||||
|         def get_ping(): | ||||
|             return "pong!" | ||||
|          | ||||
|         @service_app.route('/close') | ||||
|         def shutdown_server(): | ||||
|             comm_inst.service_greenlets.remove(http_server) | ||||
|             http_server.stop() | ||||
|             return Response('goodbye') | ||||
| 
 | ||||
|         @service_app.after_request | ||||
|         def afterReq(resp): | ||||
|             # Security headers | ||||
|             resp = httpheaders.set_default_onionr_http_headers(resp) | ||||
|             return resp | ||||
| 
 | ||||
|         with Controller.from_port(port=config.get('tor.controlPort')) as controller: | ||||
|             # Connect to the Tor process for Onionr | ||||
|             controller.authenticate(config.get('tor.controlpassword')) | ||||
|             # Create the v3 onion service for the peer to connect to | ||||
|             response = controller.create_ephemeral_hidden_service({80: service_port}, await_publication = True, key_type='NEW', key_content = 'ED25519-V3') | ||||
| 
 | ||||
|             try: | ||||
|                 for x in range(3): | ||||
|                     attempt = basicrequests.do_post_request('http://' + address + '/bs/' + response.service_id, port=socks) | ||||
|                     if attempt == 'success': | ||||
|                         break | ||||
|                 else: | ||||
|                     raise ConnectionError | ||||
|             except ConnectionError: | ||||
|                 # Re-raise | ||||
|                 raise ConnectionError('Could not reach %s bootstrap address %s' % (peer, address)) | ||||
|             else: | ||||
|                 # If no connection error, create the service and save it to local global key store | ||||
|                 peer = bytesconverter.bytes_to_str(peer) | ||||
|                 key_store.put('dc-' + peer, response.service_id) | ||||
|                 key_store.flush() | ||||
|                 logger.info('hosting on %s with %s' % (response.service_id,  peer)) | ||||
|                 http_server.serve_forever() | ||||
|                 http_server.stop() | ||||
|                 key_store.delete('dc-' + peer) | ||||
|  | @ -1,29 +0,0 @@ | |||
| ''' | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     Holds active onionrservices clients and servers | ||||
| ''' | ||||
| ''' | ||||
|     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/>. | ||||
| ''' | ||||
| from onionrutils import epoch | ||||
| class ServicePool: | ||||
|     def __init__(self): | ||||
|         self.servers = [] | ||||
|         self.clients = [] | ||||
|         self.bootstrap_pending = [] | ||||
|      | ||||
|     def add_server(self, service): | ||||
|         self.servers.append((service, epoch.get_epoch())) | ||||
|          | ||||
|  | @ -1,30 +0,0 @@ | |||
| ''' | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     Function to check if an onion server is created for a peer or not | ||||
| ''' | ||||
| ''' | ||||
|     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/>. | ||||
| ''' | ||||
| import deadsimplekv | ||||
| 
 | ||||
| import filepaths | ||||
| from onionrutils import bytesconverter | ||||
| 
 | ||||
| def server_exists(peer: str) -> bool: | ||||
|     '''checks if an onion server is created for a peer or not''' | ||||
|     peer = bytesconverter.bytes_to_str(peer) | ||||
|     kv = deadsimplekv.DeadSimpleKV(filepaths.cached_storage) | ||||
|     kv.refresh() | ||||
|     return not kv.get('dc-' + peer) is None | ||||
|  | @ -26,7 +26,7 @@ if TYPE_CHECKING: | |||
| 
 | ||||
| 
 | ||||
| def setup_kv(shared_vars: 'DeadSimpleKV'): | ||||
|     """Init initial pseudo-variables.""" | ||||
|     """Init initial pseudo-globals.""" | ||||
|     shared_vars.put('plaintextDisabledPeers', {}) | ||||
|     shared_vars.put('blockQueue', {}) | ||||
|     shared_vars.put('shutdown', False) | ||||
|  |  | |||
|  | @ -1,75 +0,0 @@ | |||
| """Onionr - Private P2P Communication. | ||||
| 
 | ||||
| HTTP endpoints for controlling IMs | ||||
| """ | ||||
| import ujson as json | ||||
| 
 | ||||
| from flask import Response, request, redirect, Blueprint, send_from_directory | ||||
| import deadsimplekv as simplekv | ||||
| 
 | ||||
| import filepaths | ||||
| """ | ||||
|     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/>. | ||||
| """ | ||||
| flask_blueprint = Blueprint('chat_control', __name__) | ||||
| key_store = simplekv.DeadSimpleKV(filepaths.cached_storage, refresh_seconds=5) | ||||
| @flask_blueprint.route('/chatapi/ping') | ||||
| def ping(): | ||||
|     return 'pong!' | ||||
| 
 | ||||
| @flask_blueprint.route('/chatapi/send/<peer>', methods=['POST']) | ||||
| def send_message(peer): | ||||
|     """Send a message to the peer""" | ||||
|     data = request.get_json(force=True) | ||||
|     key_store.refresh() | ||||
|     existing = key_store.get('s' + peer) | ||||
|     if existing is None: | ||||
|         existing = [] | ||||
|     existing.append(data) | ||||
|     key_store.put('s' + peer, existing) | ||||
|     key_store.flush() | ||||
|     return Response('success') | ||||
| 
 | ||||
| @flask_blueprint.route('/chatapi/gets/<peer>') | ||||
| def get_sent(peer): | ||||
|     """Get messages sent to peer""" | ||||
|     sent = key_store.get('s' + peer) | ||||
|     if sent is None: | ||||
|         sent = [] | ||||
|     return Response(json.dumps(sent)) | ||||
| 
 | ||||
| @flask_blueprint.route('/chatapi/addrec/<peer>', methods=['POST']) | ||||
| def add_rec(peer): | ||||
|     """Add a received message from the peer""" | ||||
|     data = request.get_json(force=True) | ||||
|     key_store.refresh() | ||||
|     existing = key_store.get('r' + peer) | ||||
|     if existing is None: | ||||
|         existing = [] | ||||
|     existing.append(data) | ||||
|     key_store.put('r' + peer, existing) | ||||
|     key_store.flush() | ||||
|     return Response('success') | ||||
| 
 | ||||
| @flask_blueprint.route('/chatapi/getrec/<peer>') | ||||
| def get_messages(peer): | ||||
|     """Get received messages for the peer""" | ||||
|     key_store.refresh() | ||||
|     existing = key_store.get('r' + peer) | ||||
|     if existing is None: | ||||
|         existing = [] | ||||
|     else: | ||||
|         existing = list(existing) | ||||
|         key_store.delete('r' + peer) | ||||
|     return Response(json.dumps(existing)) | ||||
|  | @ -1,5 +0,0 @@ | |||
| { | ||||
|     "name" : "chat", | ||||
|     "version" : "1.0", | ||||
|     "author" : "onionr" | ||||
| } | ||||
|  | @ -1,39 +0,0 @@ | |||
| ''' | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     Instant message conversations with Onionr peers | ||||
| ''' | ||||
| ''' | ||||
|     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/>. | ||||
| ''' | ||||
| 
 | ||||
| # Imports some useful libraries | ||||
| import locale, sys, os, threading, ujson as json | ||||
| locale.setlocale(locale.LC_ALL, '') | ||||
| import onionrservices, logger, config | ||||
| from onionrservices import bootstrapservice | ||||
| from onionrutils import stringvalidators, epoch, basicrequests | ||||
| 
 | ||||
| plugin_name = 'chat' | ||||
| PLUGIN_VERSION = '0.0.0' | ||||
| sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) | ||||
| import controlapi, peerserver | ||||
| flask_blueprint = controlapi.flask_blueprint | ||||
| direct_blueprint = peerserver.direct_blueprint | ||||
| security_whitelist = ['staticfiles.chat', 'staticfiles.chatIndex'] | ||||
| 
 | ||||
| def exit_with_error(text=''): | ||||
|     if text != '': | ||||
|         logger.error(text) | ||||
|     sys.exit(1) | ||||
|  | @ -1,65 +0,0 @@ | |||
| """Onionr - Private P2P Communication. | ||||
| 
 | ||||
| HTTP endpoints for communicating with peers | ||||
| """ | ||||
| import sys | ||||
| import os | ||||
| from json import JSONDecodeError | ||||
| 
 | ||||
| import deadsimplekv as simplekv | ||||
| import ujson as json | ||||
| from flask import Response, request, redirect, Blueprint, abort, g | ||||
| 
 | ||||
| from utils import identifyhome | ||||
| from onionrutils import localcommand | ||||
| import filepaths | ||||
| """ | ||||
|     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/>. | ||||
| """ | ||||
| sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) | ||||
| direct_blueprint = Blueprint('chat', __name__) | ||||
| 
 | ||||
| key_store = simplekv.DeadSimpleKV(filepaths.cached_storage, refresh_seconds=5) | ||||
| storage_dir = identifyhome.identify_home() | ||||
| 
 | ||||
| @direct_blueprint.before_request | ||||
| def request_setup(): | ||||
|     key_store.refresh() | ||||
|     host = request.host | ||||
|     host = host.strip('.b32.i2p') | ||||
|     host = host.strip('.onion') | ||||
|     g.host = host | ||||
|     g.peer = key_store.get('dc-' + g.host) | ||||
| 
 | ||||
| @direct_blueprint.route('/chat/ping') | ||||
| def pingdirect(): | ||||
|     return 'pong!' | ||||
| 
 | ||||
| @direct_blueprint.route('/chat/sendto', methods=['POST', 'GET']) | ||||
| def sendto(): | ||||
|     """Endpoint peers send chat messages to""" | ||||
|     try: | ||||
|         msg = request.get_json(force=True) | ||||
|     except JSONDecodeError: | ||||
|         msg = '' | ||||
|     else: | ||||
|         msg = json.dumps(msg) | ||||
|         localcommand.local_command('/chat/addrec/%s' % (g.peer,), post=True, post_data=msg) | ||||
|     return Response('success') | ||||
| 
 | ||||
| @direct_blueprint.route('/chat/poll') | ||||
| def poll_chat(): | ||||
|     """Endpoints peers get new messages from""" | ||||
|     return Response(localcommand.local_command('/chat/gets/%s' % (g.peer,))) | ||||
|      | ||||
|  | @ -46,7 +46,6 @@ | |||
|     }, | ||||
|     "plugins": { | ||||
|         "disabled": [ | ||||
|             "chat" | ||||
|         ], | ||||
|         "enabled": [] | ||||
|     }, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue