Ejemplo n.º 1
0
    def handle_hello(self, payload: str) -> None:
        """ Executed when a HELLO message is received. It basically
        checks the application compatibility.
        """
        protocol = self.protocol
        try:
            data = json.loads(payload)
        except ValueError:
            protocol.send_error_and_close_connection('Invalid payload.')
            return

        required_fields = {
            'app', 'network', 'remote_address', 'genesis_short_hash',
            'timestamp', 'capabilities'
        }
        # settings_dict is optional
        if not set(data).issuperset(required_fields):
            # If data does not contain all required fields
            protocol.send_error_and_close_connection('Invalid payload.')
            return

        if settings.ENABLE_PEER_WHITELIST and settings.CAPABILITY_WHITELIST not in data[
                'capabilities']:
            # If peer is not sending whitelist capability we must close the connection
            protocol.send_error_and_close_connection(
                'Must have whitelist capability.')
            return

        if data['app'] != self._app():
            self.log.warn('different versions',
                          theirs=data['app'],
                          ours=self._app())

        if data['network'] != protocol.network:
            protocol.send_error_and_close_connection('Wrong network.')
            return

        if data['genesis_short_hash'] != get_genesis_short_hash():
            protocol.send_error_and_close_connection('Different genesis.')
            return

        if abs(data['timestamp'] - protocol.node.reactor.seconds()
               ) > settings.MAX_FUTURE_TIMESTAMP_ALLOWED / 2:
            protocol.send_error_and_close_connection(
                'Nodes timestamps too far apart.')
            return

        settings_dict = get_settings_hello_dict()
        if 'settings_dict' in data and data['settings_dict'] != settings_dict:
            # If settings_dict is sent we must validate it
            protocol.send_error_and_close_connection(
                'Settings values are different. {}'.format(
                    json.dumps(settings_dict)))
            return

        protocol.app_version = data['app']
        protocol.change_state(protocol.PeerState.PEER_ID)
Ejemplo n.º 2
0
 def _get_hello_data(self) -> Dict[str, Any]:
     """ Returns a dict with information about this node that will
     be sent to a peer.
     """
     protocol = self.protocol
     remote = protocol.transport.getPeer()
     return {
         'app': self._app(),
         'network': protocol.network,
         'remote_address': '{}:{}'.format(remote.host, remote.port),
         'genesis_short_hash': get_genesis_short_hash(),
         'timestamp': protocol.node.reactor.seconds(),
         'settings_dict': get_settings_hello_dict(),
         'capabilities': [],
     }
Ejemplo n.º 3
0
    def handle_hello(self, payload: str) -> None:
        """ Executed when a HELLO message is received. It basically
        checks the application compatibility.
        """
        protocol = self.protocol
        try:
            data = json.loads(payload)
        except ValueError:
            protocol.send_error_and_close_connection('Invalid payload.')
            return

        required_fields = {
            'app', 'network', 'remote_address', 'genesis_short_hash',
            'timestamp', 'capabilities'
        }
        # settings_dict is optional
        if not set(data).issuperset(required_fields):
            # If data does not contain all required fields
            protocol.send_error_and_close_connection('Invalid payload.')
            return

        if settings.ENABLE_PEER_WHITELIST and settings.CAPABILITY_WHITELIST not in data[
                'capabilities']:
            # If peer is not sending whitelist capability we must close the connection
            protocol.send_error_and_close_connection(
                'Must have whitelist capability.')
            return

        my_sync_versions = self._get_sync_versions()
        try:
            remote_sync_versions = _parse_sync_versions(data)
        except HathorError as e:
            # this will only happen if the remote implementation is wrong
            self.log.warn('invalid protocol', error=e)
            protocol.send_error_and_close_connection('invalid protocol')
            return

        common_sync_versions = my_sync_versions & remote_sync_versions
        if not common_sync_versions:
            # no compatible sync version to use, this is fine though we just can't connect to this peer
            self.log.info('no compatible sync version to use')
            protocol.send_error_and_close_connection(
                'no compatible sync version to use')
            return

        # choose the best version, sorting is implemented in hathor.p2p.sync_versions.__lt__
        protocol.sync_version = max(common_sync_versions)

        if data['app'] != self._app():
            self.log.warn('different versions',
                          theirs=data['app'],
                          ours=self._app())

        if data['network'] != protocol.network:
            protocol.send_error_and_close_connection('Wrong network.')
            return

        if data['genesis_short_hash'] != get_genesis_short_hash():
            protocol.send_error_and_close_connection('Different genesis.')
            return

        dt = data['timestamp'] - protocol.node.reactor.seconds()
        if abs(dt) > settings.MAX_FUTURE_TIMESTAMP_ALLOWED / 2:
            protocol.send_error_and_close_connection(
                'Nodes timestamps too far apart.')
            return

        if 'settings_dict' in data:
            # If settings_dict is sent we must validate it
            settings_dict = get_settings_hello_dict()
            if data['settings_dict'] != settings_dict:
                protocol.send_error_and_close_connection(
                    'Settings values are different. {}'.format(
                        json.dumps(settings_dict)))
                return

        protocol.app_version = data['app']
        protocol.diff_timestamp = dt

        from hathor.p2p.netfilter import get_table
        from hathor.p2p.netfilter.context import NetfilterContext
        context = NetfilterContext(
            protocol=self.protocol,
            connections=self.protocol.connections,
            addr=self.protocol.transport.getPeer(),
        )
        verdict = get_table('filter').get_chain('post_hello').process(context)
        if not bool(verdict):
            self.protocol.disconnect(
                'rejected by netfilter: filter post_hello', force=True)
            return

        protocol.change_state(protocol.PeerState.PEER_ID)