2 Wrapper for sipb-xen VNC proxying
6 from twisted.internet import reactor, protocol, defer
7 from twisted.python import log
23 TOKEN_KEY = "0M6W0U1IXexThi5idy8mnkqPKEq1LtEnlK/pZSn0cDrN"
25 def getPort(name, auth_data):
26 if (auth_data["machine"] == name):
27 port = get_port.findPort(name)
30 return int(port.split(':')[1])
34 class VNCAuthOutgoing(protocol.Protocol):
36 def __init__(self,socks):
39 def connectionMade(self):
40 peer = self.transport.getPeer()
41 self.socks.makeReply(200)
42 self.socks.otherConn=self
44 def connectionLost(self, reason):
45 self.socks.transport.loseConnection()
47 def dataReceived(self,data):
48 #self.socks.log(self,"R"+data)
49 self.socks.write(data)
52 #self.socks.log(self,'W'+data)
53 self.transport.write(data)
56 class VNCAuth(protocol.Protocol):
58 def __init__(self,logging=None,server="localhost"):
63 def connectionMade(self):
67 def validateToken(self, token):
69 self.auth_error = "Invalid token"
71 token = base64.urlsafe_b64decode(token)
72 token = cPickle.loads(token)
73 m = hmac.new(TOKEN_KEY, digestmod=sha)
74 m.update(token['data'])
75 if (m.digest() == token['digest']):
76 data = cPickle.loads(token['data'])
77 expires = data["expires"]
78 if (time.time() < expires):
79 self.auth = data["user"]
80 self.auth_error = None
81 self.auth_machine = data["machine"]
84 self.auth_error = "Token has expired; please try logging in again"
85 except (TypeError, cPickle.UnpicklingError):
89 def dataReceived(self,data):
91 self.otherConn.write(data)
93 self.buf=self.buf+data
94 if ('\r\n\r\n' in self.buf) or ('\n\n' in self.buf) or ('\r\r' in self.buf):
95 lines = self.buf.splitlines()
96 args = lines.pop(0).split()
101 (header, data) = line.split(": ", 1)
102 headers[header] = data
106 if command == "AUTHTOKEN":
108 token = headers["Auth-token"]
109 if token == "1": #FIXME
111 self.makeReply(200, "Authentication successful")
114 elif command == "CONNECTVNC":
116 if ("Auth-token" in headers):
117 token = headers["Auth-token"]
118 self.validateToken(token)
119 if self.auth is not None:
120 port = getPort(vmname, self.auth_data)
121 if port is not None: # FIXME
123 d = self.connectClass(self.server, port, VNCAuthOutgoing, self)
124 d.addErrback(lambda result, self=self: self.makeReply(404, result.getErrorMessage()))
126 self.makeReply(404, "Unable to find VNC for VM "+vmname)
128 self.makeReply(401, "Unauthorized to connect to VM "+vmname)
131 self.makeReply(401, self.auth_error)
133 self.makeReply(401, "Invalid token")
135 self.makeReply(401, "Login first")
137 self.makeReply(501, "unknown method "+command)
139 if False and '\000' in self.buf[8:]:
140 head,self.buf=self.buf[:8],self.buf[8:]
142 version,code,port=struct.unpack("!BBH",head[:4])
144 raise RuntimeError, "struct error with head='%s' and buf='%s'"%(repr(head),repr(self.buf))
145 user,self.buf=string.split(self.buf,"\000",1)
146 if head[4:7]=="\000\000\000": # domain is after
147 server,self.buf=string.split(self.buf,'\000',1)
148 #server=gethostbyname(server)
150 server=socket.inet_ntoa(head[4:8])
151 assert version==4, "Bad version code: %s"%version
152 if not self.authorize(code,server,port,user):
155 if code==1: # CONNECT
156 d = self.connectClass(server, port, SOCKSv4Outgoing, self)
157 d.addErrback(lambda result, self=self: self.makeReply(91))
159 raise RuntimeError, "Bad Connect Code: %s" % code
160 assert self.buf=="","hmm, still stuff in buffer... %s" % repr(self.buf)
162 def connectionLost(self, reason):
164 self.otherConn.transport.loseConnection()
166 def authorize(self,code,server,port,user):
167 log.msg("code %s connection to %s:%s (user %s) authorized" % (code,server,port,user))
170 def connectClass(self, host, port, klass, *args):
171 return protocol.ClientCreator(reactor, klass, *args).connectTCP(host,port)
173 def makeReply(self,reply,message=""):
174 self.transport.write("VNCProxy/1.0 %d %s\r\n\r\n" % (reply, message))
175 if int(reply / 100)!=2: self.transport.loseConnection()
177 def write(self,data):
179 self.transport.write(data)
181 def log(self,proto,data):
182 if not self.logging: return
183 peer = self.transport.getPeer()
184 their_peer = self.otherConn.transport.getPeer()
185 f=open(self.logging,"a")
186 f.write("%s\t%s:%d %s %s:%d\n"%(time.ctime(),
188 ((proto==self and '<') or '>'),
189 their_peer.host,their_peer.port))
191 p,data=data[:16],data[16:]
192 f.write(string.join(map(lambda x:'%02X'%ord(x),p),' ')+' ')
193 f.write((16-len(p))*3*' ')
195 if len(repr(c))>3: f.write('.')
202 class VNCAuthFactory(protocol.Factory):
203 """A factory for a VNC auth proxy.
205 Constructor accepts one argument, a log file name.
208 def __init__(self, log, server):
212 def buildProtocol(self, addr):
213 return VNCAuth(self.logging, self.server)