Esempio n. 1
0
    def addPeer(self, peerID, name=''):
        '''
            Adds a public key to the key database (misleading function name)
        '''
        assert peerID not in self.listPeers()

        # This function simply adds a peer to the DB
        if not self._utils.validatePubKey(peerID):
            return False

        events.event('pubkey_add',
                     data={'key': peerID},
                     onionr=self.onionrInst)

        conn = sqlite3.connect(self.peerDB, timeout=30)
        hashID = self._crypto.pubKeyHashID(peerID)
        c = conn.cursor()
        t = (peerID, name, 'unknown', hashID, 0)

        for i in c.execute("SELECT * FROM peers WHERE id = ?;", (peerID, )):
            try:
                if i[0] == peerID:
                    conn.close()
                    return False
            except ValueError:
                pass
            except IndexError:
                pass
        c.execute(
            'INSERT INTO peers (id, name, dateSeen, hashID, trust) VALUES(?, ?, ?, ?, ?);',
            t)
        conn.commit()
        conn.close()

        return True
Esempio n. 2
0
    def daemonQueue(self):
        '''
            Gives commands to the communication proccess/daemon by reading an sqlite3 database

            This function intended to be used by the client. Queue to exchange data between "client" and server.
        '''

        retData = False
        if not os.path.exists(self.queueDB):
            self.dbCreate.createDaemonDB()
        else:
            conn = sqlite3.connect(self.queueDB, timeout=30)
            c = conn.cursor()
            try:
                for row in c.execute(
                        'SELECT command, data, date, min(ID), responseID FROM commands group by id'
                ):
                    retData = row
                    break
            except sqlite3.OperationalError:
                self.dbCreate.createDaemonDB()
            else:
                if retData != False:
                    c.execute('DELETE FROM commands WHERE id=?;',
                              (retData[3], ))
            conn.commit()
            conn.close()

        events.event('queue_pop',
                     data={'data': retData},
                     onionr=self.onionrInst)

        return retData
Esempio n. 3
0
def handle_daemon_commands(comm_inst):
    cmd = comm_inst._core.daemonQueue()
    response = ''
    if cmd is not False:
        events.event('daemon_command', onionr = comm_inst._core.onionrInst, data = {'cmd' : cmd})
        if cmd[0] == 'shutdown':
            comm_inst.shutdown = True
        elif cmd[0] == 'announceNode':
            if len(comm_inst.onlinePeers) > 0:
                comm_inst.announce(cmd[1])
            else:
                logger.debug("No nodes connected. Will not introduce node.")
        elif cmd[0] == 'runCheck': # deprecated
            logger.debug('Status check; looks good.')
            open(comm_inst._core.dataDir + '.runcheck', 'w+').close()
        elif cmd[0] == 'connectedPeers':
            response = '\n'.join(list(comm_inst.onlinePeers)).strip()
            if response == '':
                response = 'none'
        elif cmd[0] == 'localCommand':
            response = comm_inst._core._utils.localCommand(cmd[1])
        elif cmd[0] == 'pex':
            for i in comm_inst.timers:
                if i.timerFunction.__name__ == 'lookupAdders':
                    i.count = (i.frequency - 1)
        elif cmd[0] == 'uploadBlock':
            comm_inst.blocksToUpload.append(cmd[1])

        if cmd[0] not in ('', None):
            if response != '':
                comm_inst._core._utils.localCommand('queueResponseAdd/' + cmd[4], post=True, postData={'data': response})
        response = ''

    comm_inst.decrementThreadCount('daemonCommands')
Esempio n. 4
0
    def testPluginEvent(self):
        logger.debug('-'*26 + '\n')
        logger.info('Running plugin event test...')

        import onionrplugins as plugins, onionrevents as events

        plugins.start('test')
        if not events.call(plugins.get_plugin('test'), 'test'):
            self.assertTrue(False)

        events.event('test', data = {'tests': self})

        self.assertTrue(True)
Esempio n. 5
0
    def processBlockMetadata(self, blockHash):
        '''
            Read metadata from a block and cache it to the block database
        '''
        curTime = self.getRoundedEpoch(roundS=60)
        myBlock = Block(blockHash, self._core)
        if myBlock.isEncrypted:
            myBlock.decrypt()
        if (myBlock.isEncrypted
                and myBlock.decrypted) or (not myBlock.isEncrypted):
            blockType = myBlock.getMetadata(
                'type'
            )  # we would use myBlock.getType() here, but it is bugged with encrypted blocks
            signer = self.bytesToStr(myBlock.signer)
            valid = myBlock.verifySig()
            if myBlock.getMetadata('newFSKey') is not None:
                onionrusers.OnionrUser(self._core, signer).addForwardKey(
                    myBlock.getMetadata('newFSKey'))

            try:
                if len(blockType) <= 10:
                    self._core.updateBlockInfo(blockHash, 'dataType',
                                               blockType)
            except TypeError:
                logger.warn("Missing block information")
                pass
            # Set block expire time if specified
            try:
                expireTime = myBlock.getHeader('expire')
                assert len(
                    str(int(expireTime))
                ) < 20  # test that expire time is an integer of sane length (for epoch)
            except (AssertionError, ValueError, TypeError) as e:
                expireTime = onionrvalues.OnionrValues(
                ).default_expire + curTime
            finally:
                self._core.updateBlockInfo(blockHash, 'expire', expireTime)
            if not blockType is None:
                self._core.updateBlockInfo(blockHash, 'dataType', blockType)
            onionrevents.event('processblocks',
                               data={
                                   'block': myBlock,
                                   'type': blockType,
                                   'signer': signer,
                                   'validSig': valid
                               },
                               onionr=self._core.onionrInst)
        else:
            pass
Esempio n. 6
0
    def testPluginEvent(self):
        logger.debug('-' * 26 + '\n')
        logger.info('Running plugin event test...')

        import onionrplugins as plugins, onionrevents as events, os

        if not plugins.exists('test'):
            os.makedirs(plugins.get_plugins_folder('test'))
            with open(plugins.get_plugins_folder('test') + '/main.py',
                      'a') as main:
                main.write(
                    "print('Running')\n\ndef on_test(pluginapi, data = None):\n    print('received test event!')\n    print('thread test started...')\n    import time\n    time.sleep(1)\n    \n    return True\n\ndef on_start(pluginapi, data = None):\n    print('start event called')\n\ndef on_stop(pluginapi, data = None):\n    print('stop event called')\n\ndef on_enable(pluginapi, data = None):\n    print('enable event called')\n\ndef on_disable(pluginapi, data = None):\n    print('disable event called')\n"
                )
            plugins.enable('test')

        plugins.start('test')
        if not events.call(plugins.get_plugin('test'), 'enable'):
            self.assertTrue(False)

        logger.debug('preparing to start thread', timestamp=False)
        thread = events.event('test', data={'tests': self})
        logger.debug('thread running...', timestamp=False)
        thread.join()
        logger.debug('thread finished.', timestamp=False)

        self.assertTrue(True)
Esempio n. 7
0
def kill_daemon(o_inst):
    '''
        Shutdown the Onionr daemon
    '''

    logger.warn('Stopping the running daemon...', timestamp = False)
    try:
        events.event('daemon_stop', onionr = o_inst)
        net = NetController(o_inst.onionrCore.config.get('client.port', 59496))
        try:
            o_inst.onionrCore.daemonQueueAdd('shutdown')
        except sqlite3.OperationalError:
            pass

        net.killTor()
    except Exception as e:
        logger.error('Failed to shutdown daemon.', error = e, timestamp = False)
    return
Esempio n. 8
0
    def removeAddress(self, address):
        '''
            Remove an address from the address database
        '''

        if self._utils.validateID(address):
            conn = sqlite3.connect(self.addressDB, timeout=30)
            c = conn.cursor()
            t = (address, )
            c.execute('Delete from adders where address=?;', t)
            conn.commit()
            conn.close()

            events.event('address_remove',
                         data={'address': address},
                         onionr=self.onionrInst)
            return True
        else:
            return False
Esempio n. 9
0
 def detectAPICrash(self):
     '''exit if the api server crashes/stops'''
     if self._core._utils.localCommand('ping',
                                       silent=False) not in ('pong',
                                                             'pong!'):
         for i in range(300):
             if self._core._utils.localCommand('ping') in (
                     'pong', 'pong!') or self.shutdown:
                 break  # break for loop
             time.sleep(1)
         else:
             # This executes if the api is NOT detected to be running
             events.event('daemon_crash',
                          onionr=self._core.onionrInst,
                          data={})
             logger.error(
                 'Daemon detected API crash (or otherwise unable to reach API after long time), stopping...'
             )
             self.shutdown = True
     self.decrementThreadCount('detectAPICrash')
Esempio n. 10
0
    def addAddress(self, address):
        '''
            Add an address to the address database (only tor currently)
        '''

        if type(address) is None or len(address) == 0:
            return False
        if self._utils.validateID(address):
            if address == config.get('i2p.ownAddr',
                                     None) or address == self.hsAddress:
                return False
            conn = sqlite3.connect(self.addressDB, timeout=30)
            c = conn.cursor()
            # check if address is in database
            # this is safe to do because the address is validated above, but we strip some chars here too just in case
            address = address.replace('\'', '').replace(';', '').replace(
                '"', '').replace('\\', '')
            for i in c.execute("SELECT * FROM adders WHERE address = ?;",
                               (address, )):
                try:
                    if i[0] == address:
                        conn.close()
                        return False
                except ValueError:
                    pass
                except IndexError:
                    pass

            t = (address, 1)
            c.execute('INSERT INTO adders (address, type) VALUES(?, ?);', t)
            conn.commit()
            conn.close()

            events.event('address_add',
                         data={'address': address},
                         onionr=self.onionrInst)

            return True
        else:
            #logger.debug('Invalid ID: %s' % address)
            return False
Esempio n. 11
0
    def killDaemon(self):
        '''
            Shutdown the Onionr daemon
        '''

        logger.warn('Killing the running daemon...', timestamp=False)
        try:
            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
Esempio n. 12
0
    def daemon(self):
        '''
            Starts the Onionr communication daemon
        '''

        if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
            if self._developmentMode:
                logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
            net = NetController(config.get('client')['port'])
            logger.info('Tor is starting...')
            if not net.startTor():
                sys.exit(1)
            logger.info('Started Tor .onion service: ' +
                        logger.colors.underline + net.myID)
            logger.info('Our Public key: ' + self.onionrCore._crypto.pubKey)
            time.sleep(1)
            subprocess.Popen(["./communicator.py", "run", str(net.socksPort)])
            logger.debug('Started communicator')
            events.event('daemon_start', onionr=self)
        api.API(self.debug)

        return
Esempio n. 13
0
def daemon(o_inst):
    '''
        Starts the Onionr communication daemon
    '''

    # remove runcheck if it exists
    if os.path.isfile('%s/.runcheck' % (o_inst.onionrCore.dataDir,)):
        logger.debug('Runcheck file found on daemon start, deleting in advance.')
        os.remove('%s/.runcheck' % (o_inst.onionrCore.dataDir,))

    Thread(target=api.API, args=(o_inst, o_inst.debug, onionr.API_VERSION)).start()
    Thread(target=api.PublicAPI, args=[o_inst.getClientApi()]).start()
    try:
        time.sleep(0)
    except KeyboardInterrupt:
        logger.debug('Got keyboard interrupt, shutting down...')
        _proper_shutdown(o_inst)

    apiHost = ''
    while apiHost == '':
        try:
            with open(o_inst.onionrCore.publicApiHostFile, 'r') as hostFile:
                apiHost = hostFile.read()
        except FileNotFoundError:
            pass
        time.sleep(0.5)
    #onionr.Onionr.setupConfig('data/', self = o_inst)

    if o_inst._developmentMode:
        logger.warn('DEVELOPMENT MODE ENABLED (NOT RECOMMENDED)', timestamp = False)
    net = NetController(o_inst.onionrCore.config.get('client.public.port', 59497), apiServerIP=apiHost)
    logger.debug('Tor is starting...')
    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', 1) == 0:
        logger.debug('Started .onion service: %s' % (logger.colors.underline + net.myID))
    else:
        logger.debug('.onion service disabled')
    logger.debug('Using public key: %s' % (logger.colors.underline + o_inst.onionrCore._crypto.pubKey))

    try:
        time.sleep(1)
    except KeyboardInterrupt:
        _proper_shutdown(o_inst)

    o_inst.onionrCore.torPort = net.socksPort
    communicatorThread = Thread(target=communicator.startCommunicator, args=(o_inst, str(net.socksPort)))
    communicatorThread.start()
    
    while o_inst.communicatorInst is None:
        time.sleep(0.1)

    # print nice header thing :)
    if o_inst.onionrCore.config.get('general.display_header', True):
        o_inst.header()

    # print out debug info
    o_inst.version(verbosity = 5, function = logger.debug)
    logger.debug('Python version %s' % platform.python_version())

    logger.debug('Started communicator.')

    events.event('daemon_start', onionr = o_inst)
    while True:
        try:
            time.sleep(3)
        except KeyboardInterrupt:
            o_inst.communicatorInst.shutdown = True
        finally:
            # Debug to print out used FDs (regular and net)
            #proc = psutil.Process()
            #print('api-files:',proc.open_files(), len(psutil.net_connections()))
            # Break if communicator process ends, so we don't have left over processes
            if o_inst.communicatorInst.shutdown:
                break
            if o_inst.killed:
                break # Break out if sigterm for clean exit

    signal.signal(signal.SIGINT, _ignore_sigint)
    o_inst.onionrCore.daemonQueueAdd('shutdown')
    o_inst.onionrUtils.localCommand('shutdown')

    net.killTor()
    time.sleep(3)
    o_inst.deleteRunFiles()
    return
Esempio n. 14
0
    def __init__(self):
        '''
            Main Onionr class. This is for the CLI program, and does not handle much of the logic.
            In general, external programs and plugins should not use this class.
        '''

        try:
            os.chdir(sys.path[0])
        except FileNotFoundError:
            pass

        # Load global configuration data

        data_exists = os.path.exists('data/')

        if not data_exists:
            os.mkdir('data/')

        if os.path.exists('static-data/default_config.json'):
            config.set_config(
                json.loads(open('static-data/default_config.json').read())
            )  # this is the default config, it will be overwritten if a config file already exists. Else, it saves it
        else:
            # the default config file doesn't exist, try hardcoded config
            config.set_config({
                'devmode': True,
                'log': {
                    'file': {
                        'output': True,
                        'path': 'data/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}})['console']['color']:
            settings = settings | logger.USE_ANSI
        if config.get('log', {'console': {
                'output': True
        }})['console']['output']:
            settings = settings | logger.OUTPUT_TO_CONSOLE
        if config.get('log', {'file': {'output': True}})['file']['output']:
            settings = settings | logger.OUTPUT_TO_FILE
            logger.set_file(
                config.get('log', {'file': {
                    'path': 'data/output.log'
                }})['file']['path'])
        logger.set_settings(settings)

        if str(config.get('devmode', True)).lower() == 'true':
            self._developmentMode = True
            logger.set_level(logger.LEVEL_DEBUG)
        else:
            self._developmentMode = False
            logger.set_level(logger.LEVEL_INFO)

        self.onionrCore = core.Core()
        self.onionrUtils = OnionrUtils(self.onionrCore)

        # Handle commands

        self.debug = False  # Whole application debugging

        if os.path.exists('data-encrypted.dat'):
            while True:
                print('Enter password to decrypt:')
                password = getpass.getpass()
                result = self.onionrCore.dataDirDecrypt(password)
                if os.path.exists('data/'):
                    break
                else:
                    logger.error('Failed to decrypt: ' + result[1],
                                 timestamp=False)
        else:
            # If data folder does not exist
            if not data_exists:
                if not os.path.exists('data/blocks/'):
                    os.mkdir('data/blocks/')

            # Copy default plugins into plugins folder
            if not os.path.exists(plugins.get_plugins_folder()):
                if os.path.exists('static-data/default-plugins/'):
                    names = [
                        f for f in os.listdir("static-data/default-plugins/")
                        if not os.path.isfile(f)
                    ]
                    shutil.copytree('static-data/default-plugins/',
                                    plugins.get_plugins_folder())

                    # Enable plugins
                    for name in names:
                        if not name in plugins.get_enabled_plugins():
                            plugins.enable(name, self)

        for name in plugins.get_enabled_plugins():
            if not os.path.exists(plugins.get_plugin_data_folder(name)):
                try:
                    os.mkdir(plugins.get_plugin_data_folder(name))
                except:
                    plugins.disable(name, onionr=self, stop_event=False)

        if not os.path.exists(self.onionrCore.peerDB):
            self.onionrCore.createPeerDB()
            pass
        if not os.path.exists(self.onionrCore.addressDB):
            self.onionrCore.createAddressDB()

        # Get configuration

        if not data_exists:
            # Generate default config
            # Hostname should only be set if different from 127.x.x.x. Important for DNS rebinding attack prevention.
            if self.debug:
                randomPort = 8080
            else:
                while True:
                    randomPort = random.randint(1024, 65535)
                    if self.onionrUtils.checkPort(randomPort):
                        break
            config.set(
                'client', {
                    'participate': 'true',
                    'client_hmac': base64.b16encode(
                        os.urandom(32)).decode('utf-8'),
                    'port': randomPort,
                    'api_version': API_VERSION
                }, True)

        self.cmds = {
            '': self.showHelpSuggestion,
            'help': self.showHelp,
            'version': self.version,
            'config': self.configure,
            'start': self.start,
            'stop': self.killDaemon,
            'status': self.showStats,
            'statistics': self.showStats,
            'stats': self.showStats,
            'enable-plugin': self.enablePlugin,
            'enplugin': self.enablePlugin,
            'enableplugin': self.enablePlugin,
            'enmod': self.enablePlugin,
            'disable-plugin': self.disablePlugin,
            'displugin': self.disablePlugin,
            'disableplugin': self.disablePlugin,
            'dismod': self.disablePlugin,
            'reload-plugin': self.reloadPlugin,
            'reloadplugin': self.reloadPlugin,
            'reload-plugins': self.reloadPlugin,
            'reloadplugins': self.reloadPlugin,
            'create-plugin': self.createPlugin,
            'createplugin': self.createPlugin,
            'plugin-create': self.createPlugin,
            'listkeys': self.listKeys,
            'list-keys': self.listKeys,
            'addmsg': self.addMessage,
            'addmessage': self.addMessage,
            'add-msg': self.addMessage,
            'add-message': self.addMessage,
            'pm': self.sendEncrypt,
            'getpms': self.getPMs,
            'get-pms': self.getPMs,
            'addpeer': self.addPeer,
            'add-peer': self.addPeer,
            'add-address': self.addAddress,
            'add-addr': self.addAddress,
            'addaddr': self.addAddress,
            'addaddress': self.addAddress,
            'addfile': self.addFile,
            'importblocks': self.onionrUtils.importNewBlocks,
            'introduce': self.onionrCore.introduceNode,
            'connect': self.addAddress
        }

        self.cmdhelp = {
            'help': 'Displays this Onionr help menu',
            'version': 'Displays the Onionr version',
            'config': 'Configures something and adds it to the file',
            'start': 'Starts the Onionr daemon',
            'stop': 'Stops the Onionr daemon',
            'stats': 'Displays node statistics',
            'enable-plugin': 'Enables and starts a plugin',
            '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 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',
            'importblocks':
            'import blocks from the disk (Onionr is transport-agnostic!)',
            'introduce': 'Introduce your node to the public Onionr network',
        }

        # initialize plugins
        events.event('init', onionr=self, threaded=False)

        command = ''
        try:
            command = sys.argv[1].lower()
        except IndexError:
            command = ''
        finally:
            self.execute(command)

        if not self._developmentMode:
            encryptionPassword = self.onionrUtils.getPassword(
                'Enter password to encrypt directory: ')
            self.onionrCore.dataDirEncrypt(encryptionPassword)
            shutil.rmtree('data/')

        return
Esempio n. 15
0
    def __init__(self):
        '''
            Main Onionr class. This is for the CLI program, and does not handle much of the logic.
            In general, external programs and plugins should not use this class.
        '''
        self.userRunDir = os.getcwd() # Directory user runs the program from
        self.killed = False

        if sys.argv[0] == os.path.basename(__file__):
            try:
                os.chdir(sys.path[0])
            except FileNotFoundError:
                pass

        # set data dir
        self.dataDir = os.environ.get('ONIONR_HOME', os.environ.get('DATA_DIR', 'data/'))
        if not self.dataDir.endswith('/'):
            self.dataDir += '/'

        # set log file
        logger.set_file(os.environ.get('LOG_DIR', 'data') + '/onionr.log')

        # Load global configuration data
        data_exists = Onionr.setupConfig(self.dataDir, self)

        if netcontroller.torBinary() is None:
            logger.error('Tor is not installed')
            sys.exit(1)

        # If block data folder does not exist
        if not os.path.exists(self.dataDir + 'blocks/'):
            os.mkdir(self.dataDir + 'blocks/')

        # Copy default plugins into plugins folder
        if not os.path.exists(plugins.get_plugins_folder()):
            if os.path.exists('static-data/default-plugins/'):
                names = [f for f in os.listdir("static-data/default-plugins/")]
                shutil.copytree('static-data/default-plugins/', plugins.get_plugins_folder())

                # Enable plugins
                for name in names:
                    if not name in plugins.get_enabled_plugins():
                        plugins.enable(name, self)

        for name in plugins.get_enabled_plugins():
            if not os.path.exists(plugins.get_plugin_data_folder(name)):
                try:
                    os.mkdir(plugins.get_plugin_data_folder(name))
                except:
                    plugins.disable(name, onionr = self, stop_event = False)

        self.communicatorInst = None
        self.onionrCore = core.Core()
        self.onionrCore.onionrInst = self
        #self.deleteRunFiles()
        self.onionrUtils = onionrutils.OnionrUtils(self.onionrCore)

        self.clientAPIInst = '' # Client http api instance
        self.publicAPIInst = '' # Public http api instance

        signal.signal(signal.SIGTERM, self.exitSigterm)

        # Handle commands

        self.debug = False # Whole application debugging

        # Get configuration
        if type(config.get('client.webpassword')) is type(None):
            config.set('client.webpassword', base64.b16encode(os.urandom(32)).decode('utf-8'), savefile=True)
        if type(config.get('client.client.port')) is type(None):
            randomPort = netcontroller.getOpenPort()
            config.set('client.client.port', randomPort, savefile=True)
        if type(config.get('client.public.port')) is type(None):
            randomPort = netcontroller.getOpenPort()
            config.set('client.public.port', randomPort, savefile=True)
        if type(config.get('client.api_version')) is type(None):
            config.set('client.api_version', API_VERSION, savefile=True)

        self.cmds = commands.get_commands(self)
        self.cmdhelp = commands.cmd_help

        # initialize plugins
        events.event('init', onionr = self, threaded = False)

        command = ''
        try:
            command = sys.argv[1].lower()
        except IndexError:
            command = ''
        finally:
            self.execute(command)

        return