From b582377c8c8dcc66e41e449fb7899057de3be85c Mon Sep 17 00:00:00 2001 From: Kevin Froman Date: Wed, 15 May 2019 18:25:36 -0500 Subject: [PATCH] fixed config and other bugs, improved connection server --- README.md | 8 +++---- onionr/api.py | 2 +- onionr/communicator.py | 1 + onionr/communicatorutils/downloadblocks.py | 4 +++- onionr/config.py | 3 --- onionr/netcontroller.py | 2 +- onionr/onionr.py | 3 +-- onionr/onionrcommands/daemonlaunch.py | 4 ++-- onionr/onionrservices/__init__.py | 13 +++++++++-- onionr/onionrservices/connectionserver.py | 27 ++++++++++++++++------ onionr/setupconfig.py | 23 +++++++++--------- 11 files changed, 56 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index f3be8f12..5dc71daf 100755 --- a/README.md +++ b/README.md @@ -27,10 +27,12 @@ Users are identified by ed25519/curve25519 public keys, which can be used to sig Onionr can be used for mail, as a social network, instant messenger, file sharing software, or for encrypted group discussion. -The whitepaper (subject to change prior to first alpha release) is available [here](docs/whitepaper.md). +The whitepaper (subject to change prior to alpha release) is available [here](docs/whitepaper.md). ![node web illustration](docs/onionr-web.png) +![Tor stinks slide image](docs/tor-stinks-02.png) + ## Main Features * [X] 🌐 Fully p2p/decentralized, no trackers or other single points of failure @@ -117,6 +119,4 @@ The 'open source badge' is by Maik Ellerbrock and is licensed under a Creative C The Onionr logo was created by [Anhar Ismail](https://github.com/anharismail) under the [Creative Commons Attribution 4.0 International License](https://creativecommons.org/licenses/by/4.0/). -If you modify and redistribute our code ("forking"), please use a different logo and project name to avoid confusion. Please do not use our logo in a way that makes it seem like we endorse you without our permission. - -![Tor stinks slide image](docs/tor-stinks-02.png) +If you modify and redistribute our code ("forking"), please use a different logo and project name to avoid confusion. Please do not use our logo in a way that makes it seem like we endorse you without our permission. \ No newline at end of file diff --git a/onionr/api.py b/onionr/api.py index 11197a0a..f85501dd 100755 --- a/onionr/api.py +++ b/onionr/api.py @@ -81,7 +81,7 @@ class PublicAPI: def validateRequest(): '''Validate request has the correct hostname''' # If high security level, deny requests to public (HS should be disabled anyway for Tor, but might not be for I2P) - if config.get('general.security_level', default=0) > 0: + if config.get('general.security_level', default=1) > 0: abort(403) if type(self.torAdder) is None and type(self.i2pAdder) is None: # abort if our hs addresses are not known diff --git a/onionr/communicator.py b/onionr/communicator.py index b8b9e3f1..d018a53a 100755 --- a/onionr/communicator.py +++ b/onionr/communicator.py @@ -33,6 +33,7 @@ OnionrCommunicatorTimers = onionrcommunicatortimers.OnionrCommunicatorTimers config.reload() class OnionrCommunicatorDaemon: def __init__(self, onionrInst, proxyPort, developmentMode=config.get('general.dev_mode', False)): + config.reload() onionrInst.communicatorInst = self # configure logger and stuff onionr.Onionr.setupConfig('data/', self = self) diff --git a/onionr/communicatorutils/downloadblocks.py b/onionr/communicatorutils/downloadblocks.py index d6df074c..a2d4908c 100644 --- a/onionr/communicatorutils/downloadblocks.py +++ b/onionr/communicatorutils/downloadblocks.py @@ -18,11 +18,13 @@ along with this program. If not, see . ''' import communicator, onionrexceptions -import logger +import logger, onionrpeers def download_blocks_from_communicator(comm_inst): assert isinstance(comm_inst, communicator.OnionrCommunicatorDaemon) for blockHash in list(comm_inst.blockQueue): + if len(comm_inst.onlinePeers) == 0: + break triedQueuePeers = [] # List of peers we've tried for a block try: blockPeers = list(comm_inst.blockQueue[blockHash]) diff --git a/onionr/config.py b/onionr/config.py index a546de17..5576a1d3 100755 --- a/onionr/config.py +++ b/onionr/config.py @@ -98,9 +98,6 @@ def check(): if not os.path.exists(os.path.dirname(get_config_file())): os.makedirs(os.path.dirname(get_config_file())) - if not os.path.isfile(get_config_file()): - open(get_config_file(), 'a', encoding="utf8").close() - save() def save(): ''' diff --git a/onionr/netcontroller.py b/onionr/netcontroller.py index 15986cf1..fa64c938 100755 --- a/onionr/netcontroller.py +++ b/onionr/netcontroller.py @@ -103,7 +103,7 @@ CookieAuthentication 1 ControlPort ''' + str(controlPort) + ''' HashedControlPassword ''' + str(password) + ''' ''' - if config.get('general.security_level') == 0: + if config.get('general.security_level', 1) == 0: torrcData += '''\nHiddenServiceDir ''' + self.dataDir + '''hs/ \n''' + hsVer + '''\n HiddenServicePort 80 ''' + self.apiServerIP + ''':''' + str(self.hsPort) diff --git a/onionr/onionr.py b/onionr/onionr.py index b8374905..30ec5184 100755 --- a/onionr/onionr.py +++ b/onionr/onionr.py @@ -71,7 +71,7 @@ class Onionr: logger.set_file(os.environ.get('LOG_DIR', 'data') + '/onionr.log') # Load global configuration data - data_exists = Onionr.setupConfig(self.dataDir, self = self) + data_exists = Onionr.setupConfig(self.dataDir, self) if netcontroller.torBinary() is None: logger.error('Tor is not installed') @@ -122,7 +122,6 @@ class Onionr: config.set('client.client.port', randomPort, savefile=True) if type(config.get('client.public.port')) is type(None): randomPort = netcontroller.getOpenPort() - print(randomPort) config.set('client.public.port', randomPort, savefile=True) if type(config.get('client.participate')) is type(None): config.set('client.participate', True, savefile=True) diff --git a/onionr/onionrcommands/daemonlaunch.py b/onionr/onionrcommands/daemonlaunch.py index dac35115..9dab9f57 100644 --- a/onionr/onionrcommands/daemonlaunch.py +++ b/onionr/onionrcommands/daemonlaunch.py @@ -50,7 +50,7 @@ def daemon(o_inst): except FileNotFoundError: pass time.sleep(0.5) - onionr.Onionr.setupConfig('data/', self = o_inst) + #onionr.Onionr.setupConfig('data/', self = o_inst) if o_inst._developmentMode: logger.warn('DEVELOPMENT MODE ENABLED (NOT RECOMMENDED)', timestamp = False) @@ -59,7 +59,7 @@ def daemon(o_inst): if not net.startTor(): o_inst.onionrUtils.localCommand('shutdown') sys.exit(1) - if len(net.myID) > 0 and o_inst.onionrCore.config.get('general.security_level') == 0: + if len(net.myID) > 0 and o_inst.onionrCore.config.get('general.security_level', 1) == 0: logger.debug('Started .onion service: %s' % (logger.colors.underline + net.myID)) else: logger.debug('.onion service disabled') diff --git a/onionr/onionrservices/__init__.py b/onionr/onionrservices/__init__.py index cae33ab2..2faa4cdd 100644 --- a/onionr/onionrservices/__init__.py +++ b/onionr/onionrservices/__init__.py @@ -23,6 +23,9 @@ import core from . import connectionserver, bootstrapservice class OnionrServices: + ''' + Create a client or server for connecting to peer interfaces + ''' def __init__(self, onionr_core): assert isinstance(onionr_core, core.Core) self._core = onionr_core @@ -32,13 +35,19 @@ class OnionrServices: return def create_server(self, peer, address): + ''' + 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 + ''' assert self._core._utils.validateID(address) - BOOTSTRAP_TRIES = 10 - TRY_WAIT = 3 + BOOTSTRAP_TRIES = 10 # How many times to attempt contacting the bootstrap server + TRY_WAIT = 3 # Seconds to wait before trying bootstrap again + # HTTP is fine because .onion/i2p is encrypted/authenticated base_url = 'http://%s/' % (address,) socks = self._core.config.get('tor.socksport') for x in range(BOOTSTRAP_TRIES): if self._core._utils.doGetRequest(base_url + 'ping', port=socks, ignoreAPI=True) == 'pong!': + # if bootstrap sever is online, tell them our service address connectionserver.ConnectionServer(peer, address, core_inst=self._core) else: time.sleep(TRY_WAIT) diff --git a/onionr/onionrservices/connectionserver.py b/onionr/onionrservices/connectionserver.py index 31e429f8..0878b100 100644 --- a/onionr/onionrservices/connectionserver.py +++ b/onionr/onionrservices/connectionserver.py @@ -22,6 +22,7 @@ from gevent.pywsgi import WSGIServer, WSGIHandler from stem.control import Controller from flask import Flask import core, logger, httpapi +import onionrexceptions from netcontroller import getOpenPort import api from . import httpheaders @@ -66,11 +67,23 @@ class ConnectionServer: with Controller.from_port(port=core_inst.config.get('tor.controlPort')) as controller: # Connect to the Tor process for Onionr controller.authenticate(core_inst.config.get('tor.controlpassword')) - # Create the v3 onion service + # 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') - self.core_inst.keyStore.put('dc-' + response.service_id, self.core_inst._utils.bytesToStr(peer)) - self.core_inst._utils.doPostRequest('http://' + address + '/bs/' + response.service_id, port=socks) - logger.info('hosting on %s with %s' % (response.service_id, peer)) - http_server.serve_forever() - self.core_inst.keyStore.delete('dc-' + response.service_id) - http_server.stop() \ No newline at end of file + + try: + for x in range(3): + attempt = self.core_inst._utils.doPostRequest('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 + self.core_inst.keyStore.put('dc-' + response.service_id, self.core_inst._utils.bytesToStr(peer)) + logger.info('hosting on %s with %s' % (response.service_id, peer)) + http_server.serve_forever() + http_server.stop() + self.core_inst.keyStore.delete('dc-' + response.service_id) \ No newline at end of file diff --git a/onionr/setupconfig.py b/onionr/setupconfig.py index 1229faeb..e439845a 100644 --- a/onionr/setupconfig.py +++ b/onionr/setupconfig.py @@ -3,21 +3,22 @@ import config, logger def setup_config(dataDir, o_inst = None): data_exists = os.path.exists(dataDir) - if not data_exists: os.mkdir(dataDir) + config.reload() + + + if not os.path.exists(config._configfile): + if os.path.exists('static-data/default_config.json'): + # this is the default config, it will be overwritten if a config file already exists. Else, it saves it + with open('static-data/default_config.json', 'r') as configReadIn: + config.set_config(json.loads(configReadIn.read())) + else: + # the default config file doesn't exist, try hardcoded config + logger.warn('Default configuration file does not exist, switching to hardcoded fallback configuration!') + config.set_config({'dev_mode': True, 'log': {'file': {'output': True, 'path': dataDir + 'output.log'}, 'console': {'output': True, 'color': True}}}) - if os.path.exists('static-data/default_config.json'): - # this is the default config, it will be overwritten if a config file already exists. Else, it saves it - with open('static-data/default_config.json', 'r') as configReadIn: - config.set_config(json.loads(configReadIn.read())) - else: - # the default config file doesn't exist, try hardcoded config - logger.warn('Default configuration file does not exist, switching to hardcoded fallback configuration!') - config.set_config({'dev_mode': True, 'log': {'file': {'output': True, 'path': dataDir + 'output.log'}, 'console': {'output': True, 'color': True}}}) - if not data_exists: config.save() - config.reload() # this will read the configuration file into memory settings = 0b000 if config.get('log.console.color', True):