def handshake(self, sock): data = sock.recv(4) if data == BOLT: log.info("C: <BOLT>") else: if data: log.error("C: <#?@!>") self.stop() return raw_data = sock.recv(16) suggested_version_1, = raw_unpack(INT_32, raw_data, 0) suggested_version_2, = raw_unpack(INT_32, raw_data, 4) suggested_version_3, = raw_unpack(INT_32, raw_data, 8) suggested_version_4, = raw_unpack(INT_32, raw_data, 12) client_requested_versions = [suggested_version_1, suggested_version_2, suggested_version_3, suggested_version_4] log.info("C: <VERSION> [0x%08x, 0x%08x, 0x%08x, 0x%08x]" % tuple(client_requested_versions)) v = self.script.protocol_version if v not in client_requested_versions: raise RuntimeError("Script protocol version %r not offered by client" % v) # only single protocol version is currently supported response = raw_pack(INT_32, v) log.info("S: <VERSION> 0x%08x" % v) self.peers[sock].version = v sock.send(response)
def handshake(self, sock): data = sock.recv(4) if data == BOLT: log.info("C: <BOLT>") else: if data: log.error("C: <#?@!>") self.stop() return raw_data = sock.recv(16) suggested_version_1, = raw_unpack(INT_32, raw_data, 0) suggested_version_2, = raw_unpack(INT_32, raw_data, 4) suggested_version_3, = raw_unpack(INT_32, raw_data, 8) suggested_version_4, = raw_unpack(INT_32, raw_data, 12) client_requested_versions = [suggested_version_1, suggested_version_2, suggested_version_3, suggested_version_4] log.info("C: <VERSION> [0x%08x, 0x%08x, 0x%08x, 0x%08x]" % tuple(client_requested_versions)) v = self.script.bolt_version if v not in client_requested_versions: raise RuntimeError("Script protocol version %r not offered by client" % v) # only single protocol version is currently supported response = raw_pack(INT_32, v) log.info("S: <VERSION> 0x%08x" % v) self.peers[sock].bolt_version = v sock.send(response)
def connect(address): """ Connect and perform a handshake in order to return a valid Connection object, assuming a protocol version can be agreed. """ # Establish a connection to the host and port specified log("~~ [CONNECT] %r", address) socket = create_connection(address) log("C: [MAGIC] %s", h(MAGIC)) socket.sendall(MAGIC) # Send details of the protocol versions supported supported_versions = [1, 0, 0, 0] data = b"".join(raw_pack(UINT_32, version) for version in supported_versions) log("C: [HANDSHAKE] %s", h(data)) socket.sendall(data) # Handle the handshake response data = socket.recv(4) log("S: [HANDSHAKE] %s", h(data)) agreed_version, = raw_unpack(UINT_32, data) if agreed_version == 1: return Connection(socket) else: log("~~ [DISCONNECT] Could not negotiate protocol version") socket.close() raise RuntimeError("Could not negotiate protocol version")
def fetch(self): """ Receive exactly one message from an open socket """ # Receive chunks of data until chunk_size == 0 data = [] chunk_size = -1 while chunk_size != 0: chunk_size, = raw_unpack(UINT_16, self.socket.recv(2)) if chunk_size > 0: data.append(self.socket.recv(chunk_size)) message = unpack(b"".join(data)) log("S: %s" % message_repr(*message)) # Handle message request = self.incoming[0] try: request.on_message(*message) except Failure as failure: if isinstance(failure.request, QueryRequest): self.outgoing.append(Request(BOLT["ACK_FAILURE"])) else: self.close() raise finally: if request.complete: self.incoming.popleft() return not request.complete
def fetch_one(self): """ Receive exactly one response message from the server. This method blocks until either a message arrives or the connection is terminated. """ # Receive chunks of data until chunk_size == 0 data = [] chunk_size = -1 while chunk_size != 0: chunk_size, = raw_unpack(UINT_16, self.socket.recv(2)) if chunk_size > 0: data.append(self.socket.recv(chunk_size)) message = unpack(b"".join(data)) # Handle message response = self.responses[0] try: response.on_message(message.tag, *message.fields) except Failure as failure: if isinstance(failure.response, QueryResponse): self.reset() else: self.close() raise finally: if response.complete(): self.responses.pop(0)
def _open_to(cls, address, auth, user_agent, bolt_versions): """ Attempt to open a connection to a Bolt server, given a single socket address. """ cx = None handshake_data = BOLT + b"".join(raw_pack(UINT_32, version) for version in bolt_versions) s = socket(family={2: AF_INET, 4: AF_INET6}[len(address)]) try: s.connect(address) s.sendall(handshake_data) raw_bolt_version = s.recv(4) if raw_bolt_version: bolt_version, = raw_unpack(UINT_32, raw_bolt_version) if bolt_version > 0 and bolt_version in bolt_versions: cx = cls(s, bolt_version, auth, user_agent) else: log.error("Could not negotiate protocol version " "(outcome=%d)", bolt_version) else: pass # recv returned empty, peer closed connection finally: if not cx: s.close() return cx
def raw_unpack(self, type_code): value, = raw_unpack(type_code, self.data, self.offset) self.offset += { INT_8: 1, INT_16: 2, INT_32: 4, INT_64: 8, UINT_8: 1, UINT_16: 2, UINT_32: 4, FLOAT_64: 8, }[type_code] return value
def fetch(self): """ Receive exactly one response message from the server. This method blocks until either a message arrives or the connection is terminated. """ # Receive chunks of data until chunk_size == 0 data = [] chunk_size = -1 while chunk_size != 0: chunk_size, = raw_unpack(UINT_16, self.socket.recv(2)) if chunk_size > 0: data.append(self.socket.recv(chunk_size)) message = unpacked(b"".join(data)) # Handle message response = self.responses[0] try: response.on_message(*message) except Failure as failure: if isinstance(failure.response, QueryResponse): self.requests.append(ACK_FAILURE_REQUEST) self.responses.append(Response()) else: self.close() raise finally: if response.complete(): self.responses.popleft()
def handle_request(self, sock): v = self.peers[sock].version chunked_data = b"" message_data = b"" chunk_size = -1 debug = [] while chunk_size != 0: chunk_header = sock.recv(2) if len(chunk_header) == 0: self.stop() return chunked_data += chunk_header chunk_size, = raw_unpack(UINT_16, chunk_header) if chunk_size > 0: chunk = sock.recv(chunk_size) chunked_data += chunk message_data += chunk else: chunk = b"" debug.append(" [%s] %s" % (h(chunk_header), h(chunk))) request = unpacked(message_data) if self.script.match_request(request): # explicitly matched log.info("C: %s", message_repr(v, *request)) elif self.script.match_auto_request(request): # auto matched log.info("C! %s", message_repr(v, *request)) else: # not matched log.error("C: %s", message_repr(v, *request)) responses = self.script.match_responses() if not responses and self.script.match_auto_request(request): # These are hard-coded and therefore not very future-proof. if request[0] in (CLIENT[v].get("HELLO"), CLIENT[v].get("INIT")): responses = [(SERVER[v]["SUCCESS"], { u"server": u"Neo4j/9.99.999" })] elif request[0] == CLIENT[v].get("GOODBYE"): log.info("S: <EXIT>") exit(0) elif request[0] == CLIENT[v]["RUN"]: responses = [(SERVER[v]["SUCCESS"], {u"fields": []})] else: responses = [(SERVER[v]["SUCCESS"], {})] for response in responses: if isinstance(response, tuple): data = packed(response) self.send_chunk(sock, data) self.send_chunk(sock) log.info("S: %s", message_repr(v, *response)) elif isinstance(response, ExitCommand): exit(0) else: raise RuntimeError("Unknown response type %r" % response)
def handle_request(self, sock): v = self.peers[sock].bolt_version chunked_data = b"" message_data = b"" chunk_size = -1 debug = [] while chunk_size != 0: chunk_header = sock.recv(2) if len(chunk_header) == 0: self.stop() return chunked_data += chunk_header chunk_size, = raw_unpack(UINT_16, chunk_header) if chunk_size > 0: chunk = sock.recv(chunk_size) chunked_data += chunk message_data += chunk else: chunk = b"" debug.append(" [%s] %s" % (h(chunk_header), h(chunk))) request = unpacked(message_data) if self.script.match_request(request): # explicitly matched log.info("C: %s", message_repr(v, request)) elif self.script.match_auto_request(request): # auto matched log.info("C! %s", message_repr(v, request)) else: # not matched log.error("C: %s", message_repr(v, request)) responses = self.script.match_responses() if not responses and self.script.match_auto_request(request): # These are hard-coded and therefore not very future-proof. if request.tag in (CLIENT[v].get("HELLO"), CLIENT[v].get("INIT")): responses = [Structure(SERVER[v]["SUCCESS"], {"server": server_agents.get(v, "Neo4j/9.99.999")})] elif request.tag == CLIENT[v].get("GOODBYE"): log.info("S: <EXIT>") exit(0) elif request.tag == CLIENT[v]["RUN"]: responses = [Structure(SERVER[v]["SUCCESS"], {"fields": []})] else: responses = [Structure(SERVER[v]["SUCCESS"], {})] for response in responses: if isinstance(response, Structure): data = packed(response) self.send_chunk(sock, data) self.send_chunk(sock) log.info("S: %s", message_repr(v, Structure(response.tag, *response.fields))) elif isinstance(response, ExitCommand): exit(0) else: raise RuntimeError("Unknown response type %r" % (response,))
def __init__(self, client, server): super(ProxyPair, self).__init__() self.client = client self.server = server print("C: <CONNECT> {} -> {}".format(self.client.address, self.server.address)) print("C: <BOLT> {}".format(h(self.forward_bytes(client, server, 4)))) print("C: <VERSION> {}".format(h(self.forward_bytes(client, server, 16)))) raw_bolt_version = self.forward_bytes(server, client, 4) bolt_version, = raw_unpack(UINT_32, raw_bolt_version) self.client.bolt_version = self.server.bolt_version = bolt_version print("S: <VERSION> {}".format(h(raw_bolt_version))) self.client_messages = {v: k for k, v in CLIENT[self.client.bolt_version].items()} self.server_messages = {v: k for k, v in SERVER[self.server.bolt_version].items()}
def __init__(self, client, server): super(ProxyPair, self).__init__() self.client = client self.server = server log.debug("C: <CONNECT> {} -> {}".format(self.client.address, self.server.address)) log.debug("C: <BOLT> {}".format(h(self.forward_bytes(client, server, 4)))) log.debug("C: <VERSION> {}".format(h(self.forward_bytes(client, server, 16)))) raw_bolt_version = self.forward_bytes(server, client, 4) bolt_version, = raw_unpack(UINT_32, raw_bolt_version) self.client.bolt_version = self.server.bolt_version = bolt_version log.debug("S: <VERSION> {}".format(h(raw_bolt_version))) self.client_messages = {v: k for k, v in CLIENT[self.client.bolt_version].items()} self.server_messages = {v: k for k, v in SERVER[self.server.bolt_version].items()}
def handle_request(self, sock): chunked_data = b"" message_data = b"" chunk_size = -1 debug = [] while chunk_size != 0: chunk_header = sock.recv(2) if len(chunk_header) == 0: self.stop() return chunked_data += chunk_header chunk_size, = raw_unpack(UINT_16, chunk_header) if chunk_size > 0: chunk = sock.recv(chunk_size) chunked_data += chunk message_data += chunk else: chunk = b"" debug.append(" [%s] %s" % (h(chunk_header), h(chunk))) request = unpacked(message_data) if self.script.match_request(request): # explicitly matched log.info("C: %s", message_repr(*request)) elif self.script.match_auto_request(request): # auto matched log.info("C! %s", message_repr(*request)) else: # not matched log.error("C: %s", message_repr(*request)) responses = self.script.match_responses() if not responses and self.script.match_auto_request(request): responses = [(SERVER["SUCCESS"], { u"fields": [] } if request[0] == CLIENT["RUN"] else {})] for response in responses: if isinstance(response, tuple): data = packed(response) self.send_chunk(sock, data) self.send_chunk(sock) log.info("S: %s", message_repr(*response)) elif isinstance(response, ExitCommand): exit(0) else: raise RuntimeError("Unknown response type %r" % response)
def fetch_one(self): """ Receive exactly one response message from the server. This method blocks until either a message arrives or the connection is terminated. """ # Receive chunks of data until chunk_size == 0 data = [] chunk_size = -1 while chunk_size != 0: chunk_size, = raw_unpack(UINT_16, self.socket.recv(2)) if chunk_size > 0: data.append(self.socket.recv(chunk_size)) message = unpack(b"".join(data)) # Handle message response = self.responses[0] response.on_message(message.tag, *message.fields) if response.complete: self.responses.pop(0)
def handle_request(self, sock): chunked_data = b"" message_data = b"" chunk_size = -1 debug = [] while chunk_size != 0: chunk_header = sock.recv(2) if len(chunk_header) == 0: self.stop() return chunked_data += chunk_header chunk_size, = raw_unpack(UINT_16, chunk_header) if chunk_size > 0: chunk = sock.recv(chunk_size) chunked_data += chunk message_data += chunk else: chunk = b"" debug.append(" [%s] %s" % (h(chunk_header), h(chunk))) request = unpacked(message_data) if self.script.match_request(request): # explicitly matched log.info("C: %s", message_repr(*request)) elif self.script.match_auto_request(request): # auto matched log.info("C! %s", message_repr(*request)) else: # not matched log.error("C: %s", message_repr(*request)) responses = self.script.match_responses() if not responses and self.script.match_auto_request(request): responses = [(SERVER["SUCCESS"], {u"fields": []} if request[0] == CLIENT["RUN"] else {})] for response in responses: data = packed(response) self.send_chunk(sock, data) self.send_chunk(sock) log.info("S: %s", message_repr(*response))
def connect(address, connection_settings): """ Establish a connection to a Bolt server. It is here that we create a low-level socket connection and carry out version negotiation. Following this (and assuming success) a Connection instance will be returned. This Connection takes ownership of the underlying socket and is subsequently responsible for managing its lifecycle. Args: address: A tuple of host and port, such as ("127.0.0.1", 7687). connection_settings: Settings for initialising the connection once established. Returns: A connection to the Bolt server. Raises: ProtocolError: if the protocol version could not be negotiated. """ # Establish a connection to the host and port specified log.info("~~ <CONNECT> \"%s\" %d", *address) socket = create_connection(address) socket.settimeout(SOCKET_TIMEOUT) log.info("C: <BOLT>") socket.sendall(BOLT) # Send details of the protocol versions supported log.info("C: <VERSION> %d", BOLT_VERSION) socket.sendall(RAW_BOLT_VERSIONS) # Handle the handshake response raw_version = socket.recv(4) version, = raw_unpack(UINT_32, raw_version) log.info("S: <VERSION> %d", version) if version == BOLT_VERSION: return Connection(socket, connection_settings) else: log.error("~~ <CLOSE> Could not negotiate protocol version") socket.close() raise ProtocolError("Could not negotiate protocol version")
def connect(address, connection_settings): """ Establish a connection to a Bolt server. It is here that we create a low-level socket connection and carry out version negotiation. Following this (and assuming success) a Connection instance will be returned. This Connection takes ownership of the underlying socket and is subsequently responsible for managing its lifecycle. Args: address: A tuple of host and port, such as ("127.0.0.1", 7687). connection_settings: Settings for initialising the connection once established. Returns: A connection to the Bolt server. Raises: ProtocolError: if the protocol version could not be negotiated. """ # Establish a connection to the host and port specified log.info("~~ <CONNECT> \"%s\" %d", *address) socket = create_connection(address) log.info("C: <BOLT>") socket.sendall(BOLT) # Send details of the protocol versions supported log.info("C: <VERSION> %d", BOLT_VERSION) socket.sendall(RAW_BOLT_VERSIONS) # Handle the handshake response raw_version = socket.recv(4) version, = raw_unpack(UINT_32, raw_version) log.info("S: <VERSION> %d", version) if version == BOLT_VERSION: return Connection(socket, connection_settings) else: log.error("~~ <CLOSE> Could not negotiate protocol version") socket.close() raise ProtocolError("Could not negotiate protocol version")
def handle_request(self, sock): v = self.peers[sock].bolt_version chunked_data = b"" message_data = b"" chunk_size = -1 debug = [] while chunk_size != 0: chunk_header = sock.recv(2) if len(chunk_header) == 0: self.stop() return chunked_data += chunk_header chunk_size, = raw_unpack(UINT_16, chunk_header) if chunk_size > 0: chunk = sock.recv(chunk_size) chunked_data += chunk message_data += chunk else: chunk = b"" debug.append(" [%s] %s" % (h(chunk_header), h(chunk))) request = unpack(message_data) if self.script.match_request(request): # explicitly matched log.debug("C: %s", message_repr(v, request)) elif self.script.match_auto_request(request): # auto matched log.debug("C! %s", message_repr(v, request)) else: # not matched if self.script.lines: expected = message_repr(v, self.script.lines[0].message) else: expected = "END OF SCRIPT" log.debug("C: %s", message_repr(v, request)) log.error("Message mismatch (expected <%s>, " "received <%s>)", expected, message_repr(v, request)) self.stop() raise SystemExit(EXIT_OFF_SCRIPT) responses = self.script.match_responses() if not responses and self.script.match_auto_request(request): # These are hard-coded and therefore not very future-proof. if request.tag in (CLIENT[v].get("HELLO"), CLIENT[v].get("INIT")): responses = [ Structure( SERVER[v]["SUCCESS"], {"server": server_agents.get(v, "Neo4j/9.99.999")}) ] elif request.tag == CLIENT[v].get("GOODBYE"): log.debug("S: <EXIT>") self.stop() raise SystemExit(EXIT_OK) elif request.tag == CLIENT[v]["RUN"]: responses = [Structure(SERVER[v]["SUCCESS"], {"fields": []})] else: responses = [Structure(SERVER[v]["SUCCESS"], {})] for response in responses: if isinstance(response, Structure): data = pack(response) self.send_chunk(sock, data) self.send_chunk(sock) log.debug( "S: %s", message_repr(v, Structure(response.tag, *response.fields))) elif isinstance(response, ExitCommand): self.stop() raise SystemExit(EXIT_OK) else: raise RuntimeError("Unknown response type %r" % (response, ))
def raw_unpack(self, type_code): value, = raw_unpack(type_code, self.data, self.offset) self.offset += type_sizes[type_code] return value