From 7b7c4e01cb5cad376c666763280603d098598139 Mon Sep 17 00:00:00 2001 From: Arinerron Date: Tue, 1 May 2018 23:01:20 -0700 Subject: [PATCH 1/5] Add debug info on exceptions --- onionr/communicator.py | 2 +- onionr/core.py | 4 ++-- onionr/logger.py | 14 ++++++++++++-- onionr/onionr.py | 38 +++++++++++++++++++++----------------- 4 files changed, 36 insertions(+), 22 deletions(-) diff --git a/onionr/communicator.py b/onionr/communicator.py index 13f95341..e4150b29 100755 --- a/onionr/communicator.py +++ b/onionr/communicator.py @@ -352,7 +352,7 @@ class OnionrCommunicate: if not peer.endswith('.onion') and not peer.endswith('.onion/'): raise PeerError('Currently only Tor .onion peers are supported. You must manually specify .onion') - + if len(self._core.hsAdder.strip()) == 0: raise Exception("Could not perform self address check in performGet due to not knowing our address") if selfCheck: diff --git a/onionr/core.py b/onionr/core.py index 7b94924a..b978cfb4 100644 --- a/onionr/core.py +++ b/onionr/core.py @@ -155,7 +155,7 @@ class Core: return True else: return False - + def removeBlock(self, block): ''' remove a block from this node @@ -590,7 +590,7 @@ class Core: conn.commit() conn.close() return - + def updateBlockInfo(self, hash, key, data): ''' sets info associated with a block diff --git a/onionr/logger.py b/onionr/logger.py index 23ede687..1aade107 100644 --- a/onionr/logger.py +++ b/onionr/logger.py @@ -18,7 +18,7 @@ along with this program. If not, see . ''' -import re, sys, time +import re, sys, time, traceback class colors: ''' @@ -220,9 +220,19 @@ def error(data, error=None, timestamp=True): if get_level() <= LEVEL_ERROR: log('-', data, colors.fg.red, timestamp=timestamp) if not error is None: - debug('Error details: ' + str(error)) + debug('Error details: ' + str(error) + parse_error()) # fatal: when the something so bad has happened that the prorgam must stop def fatal(data, timestamp=True): if get_level() <= LEVEL_FATAL: log('#', data, colors.bg.red + colors.fg.green + colors.bold, timestamp=timestamp) + +# returns a formatted error message +def parse_error(): + details = traceback.extract_tb(sys.exc_info()[2]) + output = '' + + for line in details: + output += '\n ... module %s in %s:%i' % (line[2], line[0], line[1]) + + return output diff --git a/onionr/onionr.py b/onionr/onionr.py index b89b9d58..096ac9f7 100755 --- a/onionr/onionr.py +++ b/onionr/onionr.py @@ -293,11 +293,11 @@ class Onionr: Displays the Onionr version ''' - logger.info('Onionr ' + ONIONR_VERSION + ' (' + platform.machine() + ') - API v' + API_VERSION) + logger.info('Onionr %s (%s) - API v%s' % (ONIONR_VERSION, platform.machine(), API_VERSION)) if verbosity >= 1: logger.info(ONIONR_TAGLINE) if verbosity >= 2: - logger.info('Running on ' + platform.platform() + ' ' + platform.release()) + logger.info('Running on %s %s' % (platform.platform(), platform.release())) return @@ -323,7 +323,7 @@ class Onionr: except KeyboardInterrupt: pass else: - logger.info("Sending message to " + peer) + logger.info("Sending message to: " + logger.colors.underline + peer) self.onionrUtils.sendPM(peer, message) @@ -355,6 +355,7 @@ class Onionr: ''' Adds a Onionr node address ''' + try: newAddress = sys.argv[2] except: @@ -374,22 +375,25 @@ class Onionr: ''' while True: - - messageToAdd = logger.readline('Broadcast message to network: ') - if len(messageToAdd) >= 1: - break + try: + messageToAdd = logger.readline('Broadcast message to network: ') + if len(messageToAdd) >= 1: + break + except KeyboardInterrupt: + return #addedHash = self.onionrCore.setData(messageToAdd) addedHash = self.onionrCore.insertBlock(messageToAdd, header='txt') #self.onionrCore.addToBlockDB(addedHash, selfInsert=True) #self.onionrCore.setBlockType(addedHash, 'txt') - logger.info("inserted your message as block: " + addedHash) + logger.info("Message inserted as as block %s" % addedHash) return def getPMs(self): ''' display PMs sent to us ''' + self.onionrUtils.loadPMs() def enablePlugin(self): @@ -399,10 +403,10 @@ class Onionr: if len(sys.argv) >= 3: plugin_name = sys.argv[2] - logger.info('Enabling plugin \"' + plugin_name + '\"...') + logger.info('Enabling plugin "%s"...' % plugin_name) plugins.enable(plugin_name, self) else: - logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' ') + logger.info('%s %s ' % (sys.argv[0], sys.argv[1])) return @@ -413,10 +417,10 @@ class Onionr: if len(sys.argv) >= 3: plugin_name = sys.argv[2] - logger.info('Disabling plugin \"' + plugin_name + '\"...') + logger.info('Disabling plugin "%s"...' % plugin_name) plugins.disable(plugin_name, self) else: - logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' ') + logger.info('%s %s ' % (sys.argv[0], sys.argv[1])) return @@ -427,7 +431,7 @@ class Onionr: if len(sys.argv) >= 3: plugin_name = sys.argv[2] - logger.info('Reloading plugin \"' + plugin_name + '\"...') + logger.info('Reloading plugin "%s"...' % plugin_name) plugins.stop(plugin_name, self) plugins.start(plugin_name, self) else: @@ -446,21 +450,21 @@ class Onionr: plugin_name = re.sub('[^0-9a-zA-Z]+', '', str(sys.argv[2]).lower()) if not plugins.exists(plugin_name): - logger.info('Creating plugin \"' + plugin_name + '\"...') + logger.info('Creating plugin "%s"...' % plugin_name) os.makedirs(plugins.get_plugins_folder(plugin_name)) with open(plugins.get_plugins_folder(plugin_name) + '/main.py', 'a') as main: main.write(open('static-data/default_plugin.txt').read().replace('$user', os.getlogin()).replace('$date', datetime.datetime.now().strftime('%Y-%m-%d'))) - logger.info('Enabling plugin \"' + plugin_name + '\"...') + logger.info('Enabling plugin "%s"...' % plugin_name) plugins.enable(plugin_name, self) else: - logger.warn('Cannot create plugin directory structure; plugin "' + plugin_name + '" exists.') + logger.warn('Cannot create plugin directory structure; plugin "%s" exists.' % plugin_name) except Exception as e: logger.error('Failed to create plugin directory structure.', e) else: - logger.info(sys.argv[0] + ' ' + sys.argv[1] + ' ') + logger.info('%s %s ' % (sys.argv[0], sys.argv[1])) return From c144bc40b3c145572ba356fa8b554421abd1bee6 Mon Sep 17 00:00:00 2001 From: Arinerron Date: Tue, 1 May 2018 23:22:40 -0700 Subject: [PATCH 2/5] Add status check function --- onionr/communicator.py | 4 +++ onionr/default-plugins/pluginmanager/main.py | 6 ++--- onionr/onionrutils.py | 27 +++++++++++++++++--- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/onionr/communicator.py b/onionr/communicator.py index e4150b29..2bd49826 100755 --- a/onionr/communicator.py +++ b/onionr/communicator.py @@ -111,6 +111,10 @@ class OnionrCommunicate: if announceAttemptCount >= announceAttempts: logger.warn('Unable to announce to ' + command[1]) break + elif command[0] == 'runCheck': + logger.info('Status check; looks good.') + open('data/.runcheck', 'w+').close() + break apiRunningCheckCount += 1 # check if local API is up if apiRunningCheckCount > apiRunningCheckRate: diff --git a/onionr/default-plugins/pluginmanager/main.py b/onionr/default-plugins/pluginmanager/main.py index 9bf7229a..cfcf1911 100644 --- a/onionr/default-plugins/pluginmanager/main.py +++ b/onionr/default-plugins/pluginmanager/main.py @@ -99,17 +99,17 @@ def commandInstallPlugin(): return True elif valid_hash and real_block: blockhash = str(pkobh) - logger.debug('Using block ' + blockhash + '...') + logger.debug('Using block %s...' % blockhash) elif valid_key and not real_key: logger.error('Public key not found. Try adding the node by address manually, if possible.') logger.debug('Is valid key, but the key is not a known one.') elif valid_key and real_key: publickey = str(pkobh) - logger.debug('Using public key ' + publickey + '...') + logger.debug('Using public key %s...' % publickey) saveKey(pluginname, pkobh) else: - logger.error('Unknown data \"' + str(pkobh) + '\"; must be public key or block hash.') + logger.error('Unknown data "%s"; must be public key or block hash.' % str(pkobh)) return else: help() diff --git a/onionr/onionrutils.py b/onionr/onionrutils.py index 0c770fcf..3c4d0497 100644 --- a/onionr/onionrutils.py +++ b/onionr/onionrutils.py @@ -59,14 +59,14 @@ class OnionrUtils: sign = self._core._crypto.edSign(message, self._core._crypto.privKey, encodeResult=True) #encrypted = self._core._crypto.pubKeyEncrypt(message, pubkey, anonymous=True, encodedData=True).decode() - + payload['sig'] = sign payload['msg'] = message payload = json.dumps(payload) message = payload encrypted = self._core._crypto.pubKeyEncrypt(message, pubkey, anonymous=True, encodedData=True).decode() - + block = self._core.insertBlock(encrypted, header='pm', sign=False) if block == '': logger.error('Could not send PM') @@ -383,7 +383,7 @@ class OnionrUtils: except Exception as error: logger.error('Failed to open block ' + str(i) + '.', error=error) return - + def getPeerByHashId(self, hash): ''' Return the pubkey of the user if known from the hash @@ -398,4 +398,23 @@ class OnionrUtils: for row in c.execute('SELECT ID FROM peers where hashID=?', command): if row[0] != '': retData = row[0] - return retData \ No newline at end of file + return retData + + def isCommunicatorRunning(timeout = 5, interval = 0.1): + runcheck_file = 'data/.runcheck' + + if os.path.isfile(runcheck_file): + os.remove(runcheck_file) + logger.debug('%s file appears to have existed before the run check.' % runcheck_file, timestamp = False) + + self._core.daemonQueueAdd('runCheck') + starttime = time.time() + + while True: + time.sleep(interval) + if os.path.isfile(runcheck_file): + os.remove(runcheck_file) + + return True + elif starttime - time.time() >= timeout: + return False From cfb1986ad05808e1467a0badb549916be9c8df4c Mon Sep 17 00:00:00 2001 From: Arinerron Date: Tue, 1 May 2018 23:25:44 -0700 Subject: [PATCH 3/5] Add run check to node introduction function --- onionr/core.py | 41 +++++++++++++++++++++++++++++------------ onionr/onionr.py | 4 ++-- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/onionr/core.py b/onionr/core.py index b978cfb4..912e7efd 100644 --- a/onionr/core.py +++ b/onionr/core.py @@ -611,12 +611,15 @@ class Core: ''' Inserts a block into the network ''' + try: data.decode() except AttributeError: data = data.encode() + retData = '' metadata = {'type': header} + if sign: signature = self._crypto.edSign(data, self._crypto.privKey, encodeResult=True) ourID = self._crypto.pubKeyHashID() @@ -627,8 +630,10 @@ class Core: pass metadata['id'] = ourID metadata['sig'] = signature + metadata = json.dumps(metadata) metadata = metadata.encode() + if len(data) == 0: logger.error('Will not insert empty block') else: @@ -642,16 +647,28 @@ class Core: ''' Introduces our node into the network by telling X many nodes our HS address ''' - announceAmount = 2 - nodeList = self.listAdders() - if len(nodeList) == 0: - for i in self.bootstrapList: - if self._utils.validateID(i): - self.addAddress(i) - nodeList.append(i) - if announceAmount > len(nodeList): - announceAmount = len(nodeList) - for i in range(announceAmount): - self.daemonQueueAdd('announceNode', nodeList[i]) - events.event('introduction', onionr = None) + + if(self._utils.isCommunicatorRunning()): + announceAmount = 2 + nodeList = self.listAdders() + + if len(nodeList) == 0: + for i in self.bootstrapList: + if self._utils.validateID(i): + self.addAddress(i) + nodeList.append(i) + + if announceAmount > len(nodeList): + announceAmount = len(nodeList) + + for i in range(announceAmount): + self.daemonQueueAdd('announceNode', nodeList[i]) + + events.event('introduction', onionr = None) + + return True + else: + logger.error('Onionr daemon is not running.') + return False + return diff --git a/onionr/onionr.py b/onionr/onionr.py index 096ac9f7..5e7c6cd5 100755 --- a/onionr/onionr.py +++ b/onionr/onionr.py @@ -203,13 +203,13 @@ class Onionr: 'disable-plugin': 'Disables and stops a plugin', 'reload-plugin': 'Reloads a plugin', 'create-plugin': 'Creates directory structure for a plugin', - 'add-peer': 'Adds a peer (?)', + 'add-peer': 'Adds a peer to database', 'list-peers': 'Displays a list of peers', 'add-msg': 'Broadcasts a message to the Onionr network', 'pm': 'Adds a private message to block', 'get-pms': 'Shows private messages sent to you', 'addfile': 'Create an Onionr block from a file', - 'introduce': 'Introduce your node to the public Onionr network (DAEMON MUST BE RUNNING)', + 'introduce': 'Introduce your node to the public Onionr network', } # initialize plugins From 0a7c3aeaab061574f30798c7de797811f7a92267 Mon Sep 17 00:00:00 2001 From: Arinerron Date: Tue, 1 May 2018 23:35:08 -0700 Subject: [PATCH 4/5] Improve daemon stopping error handling --- onionr/logger.py | 2 +- onionr/onionr.py | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/onionr/logger.py b/onionr/logger.py index 1aade107..990a8b95 100644 --- a/onionr/logger.py +++ b/onionr/logger.py @@ -220,7 +220,7 @@ def error(data, error=None, timestamp=True): if get_level() <= LEVEL_ERROR: log('-', data, colors.fg.red, timestamp=timestamp) if not error is None: - debug('Error details: ' + str(error) + parse_error()) + debug('Error: ' + str(error) + parse_error()) # fatal: when the something so bad has happened that the prorgam must stop def fatal(data, timestamp=True): diff --git a/onionr/onionr.py b/onionr/onionr.py index 5e7c6cd5..a8fe223a 100755 --- a/onionr/onionr.py +++ b/onionr/onionr.py @@ -527,15 +527,18 @@ class Onionr: Shutdown the Onionr daemon ''' - logger.warn('Killing the running daemon') - events.event('daemon_stop', onionr = self) - net = NetController(config.get('client')['port']) + logger.warn('Killing the running daemon...', timestamp = False) try: - self.onionrUtils.localCommand('shutdown') - except requests.exceptions.ConnectionError: - pass - self.onionrCore.daemonQueueAdd('shutdown') - net.killTor() + events.event('daemon_stop', onionr = self) + net = NetController(config.get('client')['port']) + try: + self.onionrUtils.localCommand('shutdown') + except requests.exceptions.ConnectionError: + pass + self.onionrCore.daemonQueueAdd('shutdown') + net.killTor() + except Exception as e: + logger.error('Failed to shutdown daemon.', error = e, timestamp = False) return @@ -543,6 +546,7 @@ class Onionr: ''' Displays statistics and exits ''' + logger.info('Our pubkey: ' + self.onionrCore._crypto.pubKey) logger.info('Our address: ' + self.get_hostname()) return From 7973d7c8a607e74e432ce2d126fd338e1772a533 Mon Sep 17 00:00:00 2001 From: Arinerron Date: Tue, 1 May 2018 23:50:29 -0700 Subject: [PATCH 5/5] Fix bug involving runcheck --- onionr/api.py | 4 ++-- onionr/communicator.py | 4 +++- onionr/onionrutils.py | 31 +++++++++++++++++-------------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/onionr/api.py b/onionr/api.py index 903a35c1..a1f260ca 100755 --- a/onionr/api.py +++ b/onionr/api.py @@ -136,7 +136,7 @@ class API: if action == 'hello': resp = Response('Hello, World! ' + request.host) elif action == 'shutdown': - request.environ.get('werkzeug.server.shutdown')() + # request.environ.get('werkzeug.server.shutdown')() resp = Response('Goodbye') elif action == 'ping': resp = Response('pong') @@ -163,7 +163,7 @@ class API: time.sleep(self._privateDelayTime - elapsed) return resp - + @app.route('/') def banner(): self.mimeType = 'text/html' diff --git a/onionr/communicator.py b/onionr/communicator.py index 2bd49826..a5476507 100755 --- a/onionr/communicator.py +++ b/onionr/communicator.py @@ -114,8 +114,9 @@ class OnionrCommunicate: elif command[0] == 'runCheck': logger.info('Status check; looks good.') open('data/.runcheck', 'w+').close() - break + apiRunningCheckCount += 1 + # check if local API is up if apiRunningCheckCount > apiRunningCheckRate: if self._core._utils.localCommand('ping') != 'pong': @@ -131,6 +132,7 @@ class OnionrCommunicate: apiRunningCheckCount = 0 time.sleep(1) + self._netController.killTor() return diff --git a/onionr/onionrutils.py b/onionr/onionrutils.py index 3c4d0497..4fbb30e8 100644 --- a/onionr/onionrutils.py +++ b/onionr/onionrutils.py @@ -400,21 +400,24 @@ class OnionrUtils: retData = row[0] return retData - def isCommunicatorRunning(timeout = 5, interval = 0.1): - runcheck_file = 'data/.runcheck' + def isCommunicatorRunning(self, timeout = 5, interval = 0.1): + try: + runcheck_file = 'data/.runcheck' - if os.path.isfile(runcheck_file): - os.remove(runcheck_file) - logger.debug('%s file appears to have existed before the run check.' % runcheck_file, timestamp = False) - - self._core.daemonQueueAdd('runCheck') - starttime = time.time() - - while True: - time.sleep(interval) if os.path.isfile(runcheck_file): os.remove(runcheck_file) + logger.debug('%s file appears to have existed before the run check.' % runcheck_file, timestamp = False) - return True - elif starttime - time.time() >= timeout: - return False + self._core.daemonQueueAdd('runCheck') + starttime = time.time() + + while True: + time.sleep(interval) + if os.path.isfile(runcheck_file): + os.remove(runcheck_file) + + return True + elif time.time() - starttime >= timeout: + return False + except: + return False