def _secure(s, host, ssl_context, **config): local_port = s.getsockname()[1] # Secure the connection if an SSL context has been provided if ssl_context and SSL_AVAILABLE: log_debug("[#%04X] C: <SECURE> %s", local_port, host) try: s = ssl_context.wrap_socket(s, server_hostname=host if HAS_SNI and host 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", TRUST_DEFAULT) if trust == TRUST_ON_FIRST_USE: 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 return s, der_encoded_server_certificate
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: s = ssl_context.wrap_socket( s, server_hostname=host if HAS_SNI and host 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") else: der_encoded_server_certificate = None return s, der_encoded_server_certificate
def run(self, statement, parameters=None, bookmarks=None, metadata=None, timeout=None, **handlers): if self.server.supports("statement_reuse"): if statement.upper() not in (u"BEGIN", u"COMMIT", u"ROLLBACK"): if statement == self._last_run_statement: statement = "" else: self._last_run_statement = statement if not parameters: parameters = {} if self.protocol_version >= 3: extra = {} if bookmarks: try: extra["bookmarks"] = list(bookmarks) except TypeError: raise TypeError("Bookmarks must be provided within an iterable") if metadata: try: extra["tx_metadata"] = dict(metadata) except TypeError: raise TypeError("Metadata must be coercible to a dict") if timeout: try: extra["tx_timeout"] = int(1000 * timeout) except TypeError: raise TypeError("Timeout must be specified as a number of seconds") fields = (statement, parameters, extra) else: if metadata: raise ProtocolError("Bolt v%d does not support RUN metadata" % self.protocol_version) fields = (statement, parameters) log_debug("[#%04X] C: RUN %s", self.local_port, " ".join(map(repr, fields))) self._append(b"\x10", fields, Response(self, **handlers))
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)
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("[#%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": self._last_run_statement = None log_debug("[#%04X] S: IGNORED", self.local_port) response.on_ignored(summary_metadata or {}) elif summary_signature == b"\x7F": self._last_run_statement = None log_debug("[#%04X] S: FAILURE %r", self.local_port, 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
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 == b"\x71": 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
def _handshake(s, resolved_address, der_encoded_server_certificate, **config): """ :param s: :return: """ local_port = s.getsockname()[1] # Send details of the protocol versions supported supported_versions = [3, 2, 1, 0] handshake = [MAGIC_PREAMBLE] + supported_versions log_debug("[#%04X] C: <MAGIC> 0x%08X", local_port, 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 (IOError, OSError): # TODO 2.0: remove IOError alias 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)) agreed_version, = struct_unpack(">I", data) log_debug("[#%04X] S: <HANDSHAKE> 0x%08X", local_port, agreed_version) if agreed_version == 0: log_debug("[#%04X] C: <CLOSE>", local_port) s.shutdown(SHUT_RDWR) s.close() elif agreed_version in (1, 2): connection = Connection( agreed_version, resolved_address, s, der_encoded_server_certificate=der_encoded_server_certificate, **config) connection.init() return connection elif agreed_version in (3, ): connection = Connection( agreed_version, resolved_address, s, der_encoded_server_certificate=der_encoded_server_certificate, **config) connection.hello() return connection elif agreed_version == 0x48545450: 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)) else: log_debug("[#%04X] S: <CLOSE>", local_port) s.close() raise ProtocolError( "Unknown Bolt protocol version: {}".format(agreed_version))
def fail(metadata): raise ProtocolError("RESET failed %r" % metadata)
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 self.Error("Failed to read from closed connection " "{!r} ({!r})".format(self.unresolved_address, self.server.address)) if self._defunct: raise self.Error("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 (ConnectionExpired, ServiceUnavailable, DatabaseUnavailableError): if self.pool: self.pool.deactivate(self.unresolved_address), raise except (NotALeaderError, ForbiddenOnReadOnlyDatabaseError): if self.pool: self.pool.remove_writer(self.unresolved_address), raise else: raise ProtocolError("Unexpected response message with " "signature %02X" % summary_signature) return len(details), 1