X-Git-Url: http://xvm.mit.edu/gitweb/invirt/scripts/vnc-client.git/blobdiff_plain/6cf191d7e0f35287144a47be4e654372109e1baf..e717bb0335826dc7b760ae97c35848eb7ea5dcd5:/invirt-vnc-client diff --git a/invirt-vnc-client b/invirt-vnc-client index 8a17f1d..f71366a 100755 --- a/invirt-vnc-client +++ b/invirt-vnc-client @@ -1,38 +1,40 @@ -#!/usr/bin/python +#!/usr/bin/env python from twisted.internet import reactor, ssl, protocol, error from OpenSSL import SSL import base64, pickle import getopt, sys, os, time +import io verbose = False def usage(): - print """%s [-v] [-l [HOST:]PORT] {-a AUTHTOKEN|VMNAME} + print("""%s [-v] [-l [HOST:]PORT] {-a AUTHTOKEN|VMNAME} -l, --listen [HOST:]PORT port (and optionally host) to listen on for connections (default is 127.0.0.1 and a randomly - chosen port) + chosen port). Use an empty HOST to listen on all + interfaces (INSECURE!) -a, --authtoken AUTHTOKEN Authentication token for connecting to the VNC server VMNAME VM name to connect to (automatically fetches an authentication token using remctl) - -v verbose status messages""" % (sys.argv[0]) + -v verbose status messages""" % (sys.argv[0])) class ClientContextFactory(ssl.ClientContextFactory): def _verify(self, connection, x509, errnum, errdepth, ok): if verbose: - print '_verify (ok=%d):' % ok - print ' subject:', x509.get_subject() - print ' issuer:', x509.get_issuer() - print ' errnum %s, errdepth %d' % (errnum, errdepth) + print('_verify (ok=%d):' % ok) + print(' subject:', x509.get_subject()) + print(' issuer:', x509.get_issuer()) + print(' errnum %s, errdepth %d' % (errnum, errdepth)) if errnum == 10: - print 'The VNC server certificate has expired. Please contact xvm@mit.edu.' + print('The VNC server certificate has expired. Please contact xvm@mit.edu.') return ok def getContext(self): ctx = ssl.ClientContextFactory.getContext(self) certFile = '/mit/xvm/vnc/servers.cert' - if verbose: print "Loading certificates from %s" % certFile + if verbose: print("Loading certificates from %s" % certFile) ctx.load_verify_locations(certFile) ctx.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT, self._verify) @@ -58,19 +60,19 @@ class ProxyClient(Proxy): def connectionMade(self): self.peer.setPeer(self) - data = "CONNECTVNC %s VNCProxy/1.0\r\nAuth-token: %s\r\n\r\n" % (self.factory.machine, self.factory.authtoken) + data = b"CONNECTVNC %s VNCProxy/1.0\r\nAuth-token: %s\r\n\r\n" % (self.factory.machine.encode(), self.factory.authtoken.encode()) self.transport.write(data) - if verbose: print "ProxyClient: connection made" + if verbose: print("ProxyClient: connection made") def dataReceived(self, data): if not self.ready: - if verbose: print 'ProxyClient: received data "%s"' % data - if data.startswith("VNCProxy/1.0 200 "): + if verbose: print('ProxyClient: received data %r' % data) + if data.startswith(b"VNCProxy/1.0 200 "): self.ready = True - if "\n" in data: - self.peer.transport.write(data[data.find("\n")+3:]) + if b"\n" in data: + self.peer.transport.write(data[data.find(b"\n")+3:]) self.peer.transport.resumeProducing() # Allow reading else: - print "Failed to connect: %s" % data + print("Failed to connect: %r" % data) self.transport.loseConnection() else: self.peer.transport.write(data) @@ -104,7 +106,7 @@ class ProxyServer(Proxy): # somewhere to send it to. self.transport.pauseProducing() - if verbose: print "ProxyServer: connection made" + if verbose: print("ProxyServer: connection made") client = self.clientProtocolFactory(self.factory.authtoken, self.factory.machine) client.setServer(self) @@ -121,13 +123,17 @@ class ProxyFactory(protocol.Factory): self.authtoken = authtoken self.machine = machine +class SafeUnpickler(pickle.Unpickler): + def find_class(self, module, name): + raise pickle.UnpicklingError("globals are forbidden") + def main(): global verbose try: opts, args = getopt.gnu_getopt(sys.argv[1:], "hl:a:v", ["help", "listen=", "authtoken="]) - except getopt.GetoptError, err: - print str(err) # will print something like "option -a not recognized" + except getopt.GetoptError as err: + print(str(err)) # will print something like "option -a not recognized" usage() sys.exit(2) listen = ["127.0.0.1", None] @@ -141,8 +147,9 @@ def main(): elif o in ("-l", "--listen"): if ":" in a: listen = a.split(":", 2) + listen[1] = int(listen[1]) else: - listen[1] = a + listen[1] = int(a) elif o in ("-a", "--authtoken"): authtoken = a else: @@ -152,56 +159,54 @@ def main(): if authtoken is None: # User didn't give us an authentication token, so we need to get one if len(args) != 1: - print "VMNAME not given or too many arguments" + print("VMNAME not given or too many arguments") usage() sys.exit(2) from subprocess import PIPE, Popen try: - p = Popen(["remctl", "remote", "control", args[0], "vnctoken"], - stdout=PIPE) + p = Popen(["remctl", "xvm-remote.mit.edu", "control", args[0], "vnctoken"], + stdout=PIPE, universal_newlines=True) except OSError: - if verbose: print "remctl not found in path. Trying remctl locker." + if verbose: print("remctl not found in path. Trying remctl locker.") p = Popen(["athrun", "remctl", "remctl", - "remote", "control", args[0], "vnctoken"], - stdout=PIPE) + "xvm-remote.mit.edu", "control", args[0], "vnctoken"], + stdout=PIPE, universal_newlines=True) authtoken = p.communicate()[0] if p.returncode != 0: - print "Unable to get authentication token" + print("Unable to get authentication token") sys.exit(1) - if verbose: print 'Got authentication token "%s" for VM %s' % \ - (authtoken, args[0]) + if verbose: print('Got authentication token "%s" for VM %s' % \ + (authtoken, args[0])) # Unpack authentication token try: - token_outer = base64.urlsafe_b64decode(authtoken) - token_outer = pickle.loads(token_outer) - token_inner = pickle.loads(token_outer["data"]) + token_inner = SafeUnpickler(io.BytesIO(base64.urlsafe_b64decode((authtoken.split("."))[0]))).load() machine = token_inner["machine"] connect_host = token_inner["connect_host"] connect_port = token_inner["connect_port"] token_expires = token_inner["expires"] - if verbose: print "Unpacked authentication token:\n%s" % \ - repr(token_inner) + if verbose: print("Unpacked authentication token:\n%s" % \ + repr(token_inner)) except: - print "Invalid authentication token" + print("Invalid authentication token") sys.exit(1) - if verbose: print "Will connect to %s:%s" % (connect_host, connect_port) + if verbose: print("Will connect to %s:%s" % (connect_host, connect_port)) if listen[1] is None: listen[1] = 5900 ready = False - while not ready: + while not ready and listen[1] < 6000: try: - reactor.listenTCP(listen[1], ProxyFactory(connect_host, connect_port, authtoken, machine)) + reactor.listenTCP(listen[1], ProxyFactory(connect_host, connect_port, authtoken, machine), interface=listen[0]) ready = True except error.CannotListenError: listen[1] += 1 else: reactor.listenTCP(listen[1], ProxyFactory(connect_host, connect_port, authtoken, machine)) - print "Ready to connect. Connect to %s:%s (display %d) now with your VNC client. The password is 'moocow'." % (listen[0], listen[1], listen[1]-5900) - print "You must connect before your authentication token expires at %s." % \ - (time.ctime(token_expires)) + print("Ready to connect. Connect to %s:%s (display %d) now with your VNC client. The password is 'moocow'." % (listen[0], listen[1], listen[1]-5900)) + print("You must connect before your authentication token expires at %s." % \ + (time.ctime(token_expires))) reactor.run()