def _secure(s, host, ssl_context, **config):
    # Secure the connection if an SSL context has been provided
    if ssl_context and SSL_AVAILABLE:
        log_debug("~~ [SECURE] %s", host)
        try:
            s = ssl_context.wrap_socket(s, server_hostname=host if HAS_SNI and host else None)
        except SSLError as cause:
            _force_close(s)
            error = SecurityError("Failed to establish secure connection to {!r}".format(cause.args[1]))
            error.__cause__ = cause
            raise error
        else:
            # Check that the server provides a certificate
            der_encoded_server_certificate = s.getpeercert(binary_form=True)
            if der_encoded_server_certificate is None:
                _force_close(s)
                raise ProtocolError("When using a secure socket, the server should always "
                                    "provide a certificate")
            trust = config.get("trust", default_config["trust"])
            if trust == TRUST_ON_FIRST_USE:
                from neo4j.bolt.cert import PersonalCertificateStore
                store = PersonalCertificateStore()
                if not store.match_or_trust(host, der_encoded_server_certificate):
                    _force_close(s)
                    raise ProtocolError("Server certificate does not match known certificate "
                                        "for %r; check details in file %r" % (host, KNOWN_HOSTS))
    else:
        der_encoded_server_certificate = None
    return s, der_encoded_server_certificate
Exemple #2
0
def _handshake(s, resolved_address):
    """

    :param s:
    :return:
    """
    local_port = s.getsockname()[1]

    # Send details of the protocol versions supported
    supported_versions = [3, 0, 0, 0]
    handshake = [Bolt.MAGIC_PREAMBLE] + supported_versions
    log.debug("[#%04X]  C: <MAGIC> 0x%08X", local_port, Bolt.MAGIC_PREAMBLE)
    log.debug("[#%04X]  C: <HANDSHAKE> 0x%08X 0x%08X 0x%08X 0x%08X",
              local_port, *supported_versions)
    data = b"".join(struct_pack(">I", num) for num in handshake)
    s.sendall(data)

    # Handle the handshake response
    ready_to_read = False
    while not ready_to_read:
        ready_to_read, _, _ = select((s, ), (), (), 1)
    try:
        data = s.recv(4)
    except OSError:
        raise ServiceUnavailable("Failed to read any data from server {!r} "
                                 "after connected".format(resolved_address))
    data_size = len(data)
    if data_size == 0:
        # If no data is returned after a successful select
        # response, the server has closed the connection
        log.debug("[#%04X]  S: <CLOSE>", local_port)
        s.close()
        raise ServiceUnavailable("Connection to %r closed without handshake "
                                 "response" % (resolved_address, ))
    if data_size != 4:
        # Some garbled data has been received
        log.debug("[#%04X]  S: @*#!", local_port)
        s.close()
        raise ProtocolError("Expected four byte Bolt handshake response "
                            "from %r, received %r instead; check for "
                            "incorrect port number" % (resolved_address, data))
    elif data == b"HTTP":
        log.debug("[#%04X]  S: <CLOSE>", local_port)
        s.close()
        raise ServiceUnavailable("Cannot to connect to Bolt service on {!r} "
                                 "(looks like HTTP)".format(resolved_address))
    agreed_version = data[-1], data[-2]
    log.debug("[#%04X]  S: <HANDSHAKE> 0x%06X%02X", local_port,
              agreed_version[1], agreed_version[0])
    if agreed_version == (0, 0):
        log.debug("[#%04X]  C: <CLOSE>", local_port)
        s.shutdown(SHUT_RDWR)
        s.close()
    elif agreed_version in ((3, 0), ):
        return s, agreed_version
    else:
        log.debug("[#%04X]  S: <CLOSE>", local_port)
        s.close()
        raise ProtocolError("Unknown Bolt protocol version: "
                            "{}".format(agreed_version))
def _handshake(s, resolved_address, der_encoded_server_certificate, error_handler, **config):
    """

    :param s:
    :return:
    """

    # Send details of the protocol versions supported
    supported_versions = [2, 1, 0, 0]
    handshake = [MAGIC_PREAMBLE] + supported_versions
    log_debug("C: [HANDSHAKE] 0x%X %r", MAGIC_PREAMBLE, supported_versions)
    data = b"".join(struct_pack(">I", num) for num in handshake)
    s.sendall(data)

    # Handle the handshake response
    ready_to_read, _, _ = select((s,), (), (), 0)
    while not ready_to_read:
        ready_to_read, _, _ = select((s,), (), (), 0)
    try:
        data = s.recv(4)
    except ConnectionResetError:
        raise ServiceUnavailable("Failed to read any data from server {!r} after connected".format(resolved_address))
    data_size = len(data)
    if data_size == 0:
        # If no data is returned after a successful select
        # response, the server has closed the connection
        log_error("S: [CLOSE]")
        s.close()
        raise ProtocolError("Connection to %r closed without handshake response" % (resolved_address,))
    if data_size != 4:
        # Some garbled data has been received
        log_error("S: @*#!")
        s.close()
        raise ProtocolError("Expected four byte handshake response, received %r instead" % data)
    agreed_version, = struct_unpack(">I", data)
    log_debug("S: [HANDSHAKE] %d", agreed_version)
    if agreed_version == 0:
        log_debug("~~ [CLOSE]")
        s.shutdown(SHUT_RDWR)
        s.close()
    elif agreed_version in (1, 2):
        connection = Connection(resolved_address, s, agreed_version,
                                der_encoded_server_certificate=der_encoded_server_certificate,
                                error_handler=error_handler, **config)
        connection.init()
        return connection
    elif agreed_version == 0x48545450:
        log_error("S: [CLOSE]")
        s.close()
        raise ServiceUnavailable("Cannot to connect to Bolt service on {!r} "
                                 "(looks like HTTP)".format(resolved_address))
    else:
        log_error("S: [CLOSE]")
        s.close()
        raise ProtocolError("Unknown Bolt protocol version: {}".format(agreed_version))
Exemple #4
0
    def open(cls, address, *, auth=None, timeout=None, **config):
        """ Open a new Bolt connection to a given server address.

        :param address:
        :param auth:
        :param timeout:
        :param config:
        :return:
        """
        config = PoolConfig.consume(config)
        s, config.protocol_version = connect(address,
                                             timeout=timeout,
                                             config=config)

        if config.protocol_version == (3, 0):
            from neo4j.io._bolt3 import Bolt3
            connection = Bolt3(address, s, auth=auth, **config)
        else:
            log.debug("[#%04X]  S: <CLOSE>", s.getpeername()[1])
            s.shutdown(SHUT_RDWR)
            s.close()
            raise ProtocolError(
                "Driver does not support Bolt protocol version: 0x%06X%02X",
                config.protocol_version[0], config.protocol_version[1])

        connection.hello()
        return connection
Exemple #5
0
 def _yield_messages(self, sock):
     try:
         buffer = UnpackableBuffer()
         chunk_loader = self._load_chunks(sock, buffer)
         unpacker = Unpacker(buffer)
         details = []
         while True:
             unpacker.reset()
             details[:] = ()
             chunk_size = -1
             while chunk_size != 0:
                 chunk_size = next(chunk_loader)
             summary_signature = None
             summary_metadata = None
             size, signature = unpacker.unpack_structure_header()
             if size > 1:
                 raise ProtocolError("Expected one field")
             if signature == b"\x71":
                 data = unpacker.unpack()
                 details.append(data)
             else:
                 summary_signature = signature
                 summary_metadata = unpacker.unpack_map()
             yield details, summary_signature, summary_metadata
     except OSError as error:
         self.on_error(error)
Exemple #6
0
    def _fetch(self):
        """ Receive at least one message from the server, if available.

        :return: 2-tuple of number of detail messages and number of summary messages fetched
        """
        if self.closed():
            raise self.Error(
                "Failed to read from closed connection {!r}".format(
                    self.server.address))
        if self.defunct():
            raise self.Error(
                "Failed to read from defunct connection {!r}".format(
                    self.server.address))
        if not self.responses:
            return 0, 0

        self._receive()

        details, summary_signature, summary_metadata = self._unpack()

        if details:
            log_debug("S: RECORD * %d", len(details))  # TODO
            self.responses[0].on_records(details)

        if summary_signature is None:
            return len(details), 0

        response = self.responses.popleft()
        response.complete = True
        if summary_signature == SUCCESS:
            log_debug("S: SUCCESS (%r)", summary_metadata)
            response.on_success(summary_metadata or {})
        elif summary_signature == IGNORED:
            self._last_run_statement = None
            log_debug("S: IGNORED (%r)", summary_metadata)
            response.on_ignored(summary_metadata or {})
        elif summary_signature == FAILURE:
            self._last_run_statement = None
            log_debug("S: FAILURE (%r)", summary_metadata)
            response.on_failure(summary_metadata or {})
        else:
            self._last_run_statement = None
            raise ProtocolError(
                "Unexpected response message with signature %02X" %
                summary_signature)

        return len(details), 1
Exemple #7
0
    def driver(cls, uri, **config):
        """ Acquire a :class:`.Driver` instance for the given URI and
        configuration. The URI scheme determines the Driver implementation
        that will be returned. Options are:

            ``bolt``
              Returns a :class:`.DirectDriver`.

            ``bolt+routing``
              Returns a :class:`.RoutingDriver`.

        :param uri: URI for a graph database service
        :param config: configuration and authentication details (valid keys are listed below)

            `auth`
              An authentication token for the server, for example
              ``("neo4j", "password")``.

            `der_encoded_server_certificate`
              The server certificate in DER format, if required.

            `encrypted`
              A boolean flag to determine whether encryption should be used.
              Defaults to :const:`True`.

            `trust`
              Trust level: one of :attr:`.TRUST_ALL_CERTIFICATES` (default) or
              :attr:`.TRUST_SYSTEM_CA_SIGNED_CERTIFICATES`.

            `user_agent`
              A custom user agent string, if required.

              for more config options see neo4j.config.default_config

        """
        parsed = urlparse(uri)
        try:
            driver_class = cls.uri_schemes[parsed.scheme]
        except KeyError:
            raise ProtocolError("URI scheme %r not supported" % parsed.scheme)
        else:
            return driver_class(uri, **config)
Exemple #8
0
    def _unpack(self):
        unpacker = self.unpacker
        input_buffer = self.input_buffer

        details = []
        summary_signature = None
        summary_metadata = None
        more = True
        while more:
            unpacker.attach(input_buffer.frame())
            size, signature = unpacker.unpack_structure_header()
            if size > 1:
                raise ProtocolError("Expected one field")
            if signature == RECORD:
                data = unpacker.unpack_list()
                details.append(data)
                more = input_buffer.frame_message()
            else:
                summary_signature = signature
                summary_metadata = unpacker.unpack_map()
                more = False
        return details, summary_signature, summary_metadata
Exemple #9
0
def _secure(s, host, ssl_context):
    local_port = s.getsockname()[1]
    # Secure the connection if an SSL context has been provided
    if ssl_context:
        log.debug("[#%04X]  C: <SECURE> %s", local_port, host)
        try:
            sni_host = host if HAS_SNI and host else None
            s = ssl_context.wrap_socket(s, server_hostname=sni_host)
        except SSLError as cause:
            s.close()
            error = SecurityError("Failed to establish secure connection "
                                  "to {!r}".format(cause.args[1]))
            error.__cause__ = cause
            raise error
        else:
            # Check that the server provides a certificate
            der_encoded_server_certificate = s.getpeercert(binary_form=True)
            if der_encoded_server_certificate is None:
                s.close()
                raise ProtocolError("When using a secure socket, the server "
                                    "should always provide a certificate")
    return s
Exemple #10
0
    def fetch_message(self):
        """ Receive at least one message from the server, if available.

        :return: 2-tuple of number of detail messages and number of summary
                 messages fetched
        """
        if self._closed:
            raise ServiceUnavailable(
                "Failed to read from closed connection {!r} ({!r})".format(
                    self.unresolved_address, self.server.address))

        if self._defunct:
            raise ServiceUnavailable(
                "Failed to read from defunct connection {!r} ({!r})".format(
                    self.unresolved_address, self.server.address))

        if not self.responses:
            return 0, 0

        # Receive exactly one message
        try:
            details, summary_signature, summary_metadata = next(self.inbox)
        except (IOError, OSError) as error:
            log.error("Failed to read data from connection "
                      "{!r} ({!r}); ({!r})".format(
                          self.unresolved_address, self.server.address,
                          "; ".join(map(repr, error.args))))
            if self.pool:
                self.pool.deactivate(self.unresolved_address)
            raise

        if details:
            log.debug("[#%04X]  S: RECORD * %d", self.local_port,
                      len(details))  # TODO
            self.responses[0].on_records(details)

        if summary_signature is None:
            return len(details), 0

        response = self.responses.popleft()
        response.complete = True
        if summary_signature == b"\x70":
            log.debug("[#%04X]  S: SUCCESS %r", self.local_port,
                      summary_metadata)
            response.on_success(summary_metadata or {})
        elif summary_signature == b"\x7E":
            log.debug("[#%04X]  S: IGNORED", self.local_port)
            response.on_ignored(summary_metadata or {})
        elif summary_signature == b"\x7F":
            log.debug("[#%04X]  S: FAILURE %r", self.local_port,
                      summary_metadata)
            try:
                response.on_failure(summary_metadata or {})
            except (ServiceUnavailable, DatabaseUnavailableError):
                if self.pool:
                    self.pool.deactivate(self.unresolved_address),
                raise
            except (NotALeaderError, ForbiddenOnReadOnlyDatabaseError):
                if self.pool:
                    self.pool.on_write_failure(self.unresolved_address),
                raise
        else:
            raise ProtocolError("Unexpected response message with "
                                "signature %02X" % summary_signature)

        return len(details), 1
Exemple #11
0
 def fail(metadata):
     raise ProtocolError("RESET failed %r" % metadata)
Exemple #12
0
def connect(address, ssl_context=None, error_handler=None, **config):
    """ Connect and perform a handshake and return a valid Connection object, assuming
    a protocol version can be agreed.
    """

    # Establish a connection to the host and port specified
    # Catches refused connections see:
    # https://docs.python.org/2/library/errno.html
    log_info("~~ [CONNECT] %s", address)
    s = None
    try:
        if len(address) == 2:
            s = socket(AF_INET)
        elif len(address) == 4:
            s = socket(AF_INET6)
        else:
            raise ValueError("Unsupported address {!r}".format(address))
        t = s.gettimeout()
        s.settimeout(
            config.get("connection_timeout",
                       default_config["connection_timeout"]))
        s.connect(address)
        s.settimeout(t)
        s.setsockopt(
            SOL_SOCKET, SO_KEEPALIVE,
            1 if config.get("keep_alive", default_config["keep_alive"]) else 0)
    except SocketTimeout:
        if s:
            try:
                s.close()
            except:
                pass
        raise ServiceUnavailable(
            "Timed out trying to establish connection to {!r}".format(address))
    except SocketError as error:
        if s:
            try:
                s.close()
            except:
                pass
        if error.errno in (61, 111, 10061):
            raise ServiceUnavailable(
                "Failed to establish connection to {!r}".format(address))
        else:
            raise
    except ConnectionResetError:
        raise ServiceUnavailable(
            "Failed to establish connection to {!r}".format(address))

    # Secure the connection if an SSL context has been provided
    if ssl_context and SSL_AVAILABLE:
        host = address[0]
        log_info("~~ [SECURE] %s", host)
        try:
            s = ssl_context.wrap_socket(
                s, server_hostname=host if HAS_SNI else None)
        except SSLError as cause:
            s.close()
            error = SecurityError(
                "Failed to establish secure connection to {!r}".format(
                    cause.args[1]))
            error.__cause__ = cause
            raise error
        else:
            # Check that the server provides a certificate
            der_encoded_server_certificate = s.getpeercert(binary_form=True)
            if der_encoded_server_certificate is None:
                s.close()
                raise ProtocolError(
                    "When using a secure socket, the server should always "
                    "provide a certificate")
            trust = config.get("trust", default_config["trust"])
            if trust == TRUST_ON_FIRST_USE:
                from neo4j.bolt.cert import PersonalCertificateStore
                store = PersonalCertificateStore()
                if not store.match_or_trust(host,
                                            der_encoded_server_certificate):
                    s.close()
                    raise ProtocolError(
                        "Server certificate does not match known certificate "
                        "for %r; check details in file %r" %
                        (host, KNOWN_HOSTS))
    else:
        der_encoded_server_certificate = None

    # Send details of the protocol versions supported
    supported_versions = [1, 0, 0, 0]
    handshake = [MAGIC_PREAMBLE] + supported_versions
    log_info("C: [HANDSHAKE] 0x%X %r", MAGIC_PREAMBLE, supported_versions)
    data = b"".join(struct_pack(">I", num) for num in handshake)
    s.sendall(data)

    # Handle the handshake response
    ready_to_read, _, _ = select((s, ), (), (), 0)
    while not ready_to_read:
        ready_to_read, _, _ = select((s, ), (), (), 0)
    try:
        data = s.recv(4)
    except ConnectionResetError:
        raise ServiceUnavailable(
            "Failed to read any data from server {!r} after connected".format(
                address))
    data_size = len(data)
    if data_size == 0:
        # If no data is returned after a successful select
        # response, the server has closed the connection
        log_error("S: [CLOSE]")
        s.close()
        raise ProtocolError(
            "Connection to %r closed without handshake response" % (address, ))
    if data_size != 4:
        # Some garbled data has been received
        log_error("S: @*#!")
        s.close()
        raise ProtocolError(
            "Expected four byte handshake response, received %r instead" %
            data)
    agreed_version, = struct_unpack(">I", data)
    log_info("S: [HANDSHAKE] %d", agreed_version)
    if agreed_version == 0:
        log_info("~~ [CLOSE]")
        s.shutdown(SHUT_RDWR)
        s.close()
    elif agreed_version == 1:
        connection = Connection(
            address,
            s,
            der_encoded_server_certificate=der_encoded_server_certificate,
            error_handler=error_handler,
            **config)
        connection.init()
        return connection
    elif agreed_version == 0x48545450:
        log_error("S: [CLOSE]")
        s.close()
        raise ServiceUnavailable("Cannot to connect to Bolt service on {!r} "
                                 "(looks like HTTP)".format(address))
    else:
        log_error("S: [CLOSE]")
        s.close()
        raise ProtocolError(
            "Unknown Bolt protocol version: {}".format(agreed_version))
 def on_failure(self, metadata):
     raise ProtocolError("RESET failed")
 def on_failure(self, metadata):
     raise ProtocolError("ACK_FAILURE failed")