def _client_handshake(self, client_tx, client_rx): """ Perform handshake/authentication with a connecting client Outline: 1. Client connects 2. We fake RFB 3.8 protocol and require VNC authentication [processing also supports RFB 3.3] 3. Client accepts authentication method 4. We send an authentication challenge 5. Client sends the authentication response 6. We check the authentication Upon return, self.client socket is connected to the client. """ client_tx.send(rfb.RFB_VERSION_3_8 + "\n") client_version_str = client_rx.recv(1024) client_version = rfb.check_version(client_version_str) if not client_version: self.error("Invalid version: %s", client_version_str) raise gevent.GreenletExit # Both for RFB 3.3 and 3.8 self.debug("Requesting authentication") auth_request = rfb.make_auth_request(rfb.RFB_AUTHTYPE_VNC, version=client_version) client_tx.send(auth_request) # The client gets to propose an authtype only for RFB 3.8 if client_version == rfb.RFB_VERSION_3_8: res = client_rx.recv(1024) type = rfb.parse_client_authtype(res) if type == rfb.RFB_AUTHTYPE_ERROR: self.warn("Client refused authentication: %s", res[1:]) else: self.debug("Client requested authtype %x", type) if type != rfb.RFB_AUTHTYPE_VNC: self.error("Wrong auth type: %d", type) client_tx.send(rfb.to_u32(rfb.RFB_AUTH_ERROR)) raise gevent.GreenletExit # Generate the challenge challenge = os.urandom(16) client_tx.send(challenge) response = client_rx.recv(1024) if len(response) != 16: self.error("Wrong response length %d, should be 16", len(response)) raise gevent.GreenletExit if rfb.check_password(challenge, response, self.password): self.debug("Authentication successful") else: self.warn("Authentication failed") client_tx.send(rfb.to_u32(rfb.RFB_AUTH_ERROR)) raise gevent.GreenletExit # Accept the authentication client_tx.send(rfb.to_u32(rfb.RFB_AUTH_SUCCESS))
def _perform_server_handshake(self): """ Initiate a connection with the backend server and perform basic RFB 3.8 handshake with it. Return a socket connected to the backend server. """ server = None tries = VncAuthProxy.connect_retries while tries: tries -= 1 # Initiate server connection for res in socket.getaddrinfo(self.daddr, self.dport, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): af, socktype, proto, canonname, sa = res try: server = socket.socket(af, socktype, proto) except socket.error: server = None continue # Set socket timeout for the initial handshake server.settimeout(VncAuthProxy.server_timeout) try: self.debug("Connecting to %s:%s", *sa[:2]) server.connect(sa) self.debug("Connection to %s:%s successful", *sa[:2]) except socket.error: self.debug("Failed to perform sever hanshake, retrying...") server.close() server = None continue # We succesfully connected to the server tries = 0 break # Wait and retry gevent.sleep(VncAuthProxy.retry_wait) if server is None: raise InternalError("Failed to connect to server") version = server.recv(1024) if not rfb.check_version(version): raise InternalError("Unsupported RFB version: %s" % version.strip()) server.send(rfb.RFB_VERSION_3_8 + "\n") res = server.recv(1024) types = rfb.parse_auth_request(res) if not types: raise InternalError("Error handshaking with the server") else: self.debug("Supported authentication types: %s", " ".join([str(x) for x in types])) if rfb.RFB_AUTHTYPE_NONE not in types: raise InternalError("Error, server demands authentication") server.send(rfb.to_u8(rfb.RFB_AUTHTYPE_NONE)) # Check authentication response res = server.recv(4) res = rfb.from_u32(res) if res != 0: raise InternalError("Authentication error") # Reset the timeout for the rest of the session server.settimeout(None) self.server = server