Example #1
0
    def test_match_peer_id(self):
        network = 'testnet'
        peer_id1 = PeerId()
        peer_id2 = PeerId()
        manager1 = self.create_peer(network, peer_id=peer_id1)
        manager2 = self.create_peer(network, peer_id=peer_id2)

        conn = FakeConnection(manager1, manager2)
        self.assertTrue(conn.proto2.is_state(conn.proto2.PeerState.HELLO))

        matcher = NetfilterMatchPeerId(str(peer_id1.id))
        context = NetfilterContext(protocol=conn.proto2)
        self.assertFalse(matcher.match(context))

        conn.run_one_step()
        self.assertTrue(conn.proto2.is_state(conn.proto2.PeerState.PEER_ID))
        self.assertFalse(matcher.match(context))

        # Success because the connection is ready and proto2 is connected to proto1.
        conn.run_one_step()
        conn.run_one_step()
        self.assertTrue(conn.proto2.is_state(conn.proto2.PeerState.READY))
        self.assertTrue(matcher.match(context))

        # Fail because proto1 is connected to proto2, and the peer id cannot match.
        context = NetfilterContext(protocol=conn.proto1)
        self.assertFalse(matcher.match(context))
Example #2
0
 def test_match_ip_address_ipv4_ip(self):
     matcher = NetfilterMatchIPAddress('192.168.0.1/32')
     context = NetfilterContext(addr=IPv4Address('TCP', '192.168.0.1', 1234))
     self.assertTrue(matcher.match(context))
     context = NetfilterContext(addr=IPv4Address('TCP', '192.168.0.10', 1234))
     self.assertFalse(matcher.match(context))
     context = NetfilterContext(addr=IPv4Address('TCP', '', 1234))
     self.assertFalse(matcher.match(context))
Example #3
0
 def test_match_ip_address_ipv6_ip(self):
     matcher = NetfilterMatchIPAddress('2001:0db8:0:f101::1/128')
     context = NetfilterContext(addr=IPv6Address('TCP', '2001:db8:0:f101::1', 1234))
     self.assertTrue(matcher.match(context))
     context = NetfilterContext(addr=IPv6Address('TCP', '2001:db8::8a2e:370:7334', 1234))
     self.assertFalse(matcher.match(context))
     context = NetfilterContext(addr=IPv6Address('TCP', '2001:db8:0:f101:2::7334', 1234))
     self.assertFalse(matcher.match(context))
Example #4
0
 def buildProtocol(self, addr: IAddress) -> Protocol:
     context = NetfilterContext(
         connections=self.connections,
         addr=addr,
     )
     verdict = get_table('filter').get_chain('pre_conn').process(context)
     if not bool(verdict):
         return None
     return super().buildProtocol(addr)
Example #5
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)
Example #6
0
 def test_match_ip_address_empty_context(self):
     matcher = NetfilterMatchIPAddress('192.168.0.0/24')
     context = NetfilterContext()
     self.assertFalse(matcher.match(context))
Example #7
0
    def handle_peer_id(self, payload: str) -> Generator[Any, Any, None]:
        """ Executed when a PEER-ID is received. It basically checks
        the identity of the peer. Only after this step, the peer connection
        is considered established and ready to communicate.
        """
        protocol = self.protocol
        data = json.loads(payload)

        peer = PeerId.create_from_json(data)
        peer.validate()
        assert peer.id is not None

        # If the connection URL had a peer-id parameter we need to check it's the same
        if protocol.expected_peer_id and peer.id != protocol.expected_peer_id:
            protocol.send_error_and_close_connection(
                'Peer id different from the requested one.')
            return

        # is it on the whitelist?
        if peer.id and self._should_block_peer(peer.id):
            if settings.WHITELIST_WARN_BLOCKED_PEERS:
                protocol.send_error_and_close_connection(
                    f'Blocked (by {peer.id}). Get in touch with Hathor team.')
            else:
                protocol.send_error_and_close_connection(
                    'Connection rejected.')
            return

        if peer.id == protocol.my_peer.id:
            protocol.send_error_and_close_connection('Are you my clone?!')
            return

        if protocol.connections is not None:
            if protocol.connections.is_peer_connected(peer.id):
                protocol.send_error_and_close_connection(
                    'We are already connected.')
                return

        entrypoint_valid = yield peer.validate_entrypoint(protocol)
        if not entrypoint_valid:
            protocol.send_error_and_close_connection(
                'Connection string is not in the entrypoints.')
            return

        if protocol.use_ssl:
            certificate_valid = peer.validate_certificate(protocol)
            if not certificate_valid:
                protocol.send_error_and_close_connection(
                    'Public keys from peer and certificate are not the same.')
                return

        # If it gets here, the peer is validated, and we are ready to start communicating.
        protocol.peer = peer

        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_peerid').process(context)
        if not bool(verdict):
            self.protocol.disconnect(
                'rejected by netfilter: filter post_peerid', force=True)
            return

        self.send_ready()
Example #8
0
 def test_match_or_success_01(self):
     m1 = NetfilterNeverMatch()
     m2 = NetfilterMatchAll()
     matcher = NetfilterMatchOr(m1, m2)
     context = NetfilterContext()
     self.assertTrue(matcher.match(context))
Example #9
0
 def test_match_or_fail_00(self):
     m1 = NetfilterNeverMatch()
     m2 = NetfilterNeverMatch()
     matcher = NetfilterMatchOr(m1, m2)
     context = NetfilterContext()
     self.assertFalse(matcher.match(context))
Example #10
0
 def test_match_and_success(self):
     m1 = NetfilterMatchAll()
     m2 = NetfilterMatchAll()
     matcher = NetfilterMatchAnd(m1, m2)
     context = NetfilterContext()
     self.assertTrue(matcher.match(context))
Example #11
0
 def test_match_and_fail_10(self):
     m1 = NetfilterMatchAll()
     m2 = NetfilterNeverMatch()
     matcher = NetfilterMatchAnd(m1, m2)
     context = NetfilterContext()
     self.assertFalse(matcher.match(context))
Example #12
0
 def test_match_all(self):
     matcher = NetfilterMatchAll()
     context = NetfilterContext()
     self.assertTrue(matcher.match(context))
Example #13
0
 def test_never_match(self):
     matcher = NetfilterNeverMatch()
     context = NetfilterContext()
     self.assertFalse(matcher.match(context))
Example #14
0
 def test_match_peer_id_empty_context(self):
     matcher = NetfilterMatchPeerId('123')
     context = NetfilterContext()
     self.assertFalse(matcher.match(context))
Example #15
0
 def test_match_ip_address_ipv4_hostname(self):
     matcher = NetfilterMatchIPAddress('192.168.0.1/32')
     context = NetfilterContext(addr=HostnameAddress('hathor.network', 80))
     self.assertFalse(matcher.match(context))
Example #16
0
 def test_match_ip_address_ipv6_ipv4(self):
     matcher = NetfilterMatchIPAddress('2001:0db8:0:f101::1/128')
     context = NetfilterContext(addr=IPv4Address('TCP', '192.168.0.1', 1234))
     self.assertFalse(matcher.match(context))
Example #17
0
 def test_match_ip_address_ipv6_unix(self):
     matcher = NetfilterMatchIPAddress('2001:0db8:0:f101::1/128')
     context = NetfilterContext(addr=UNIXAddress('/unix.sock'))
     self.assertFalse(matcher.match(context))
Example #18
0
 def test_match_ip_address_ipv6_hostname(self):
     matcher = NetfilterMatchIPAddress('2001:0db8:0:f101::1/128')
     context = NetfilterContext(addr=HostnameAddress('hathor.network', 80))
     self.assertFalse(matcher.match(context))
Example #19
0
 def test_match_ip_address_ipv4_unix(self):
     matcher = NetfilterMatchIPAddress('192.168.0.1/32')
     context = NetfilterContext(addr=UNIXAddress('/unix.sock'))
     self.assertFalse(matcher.match(context))
Example #20
0
 def test_match_ip_address_ipv4_ipv6(self):
     matcher = NetfilterMatchIPAddress('192.168.0.1/32')
     context = NetfilterContext(addr=IPv6Address('TCP', '2001:db8::', 80))
     self.assertFalse(matcher.match(context))
     context = NetfilterContext(addr=IPv6Address('TCP', '', 80))
     self.assertFalse(matcher.match(context))