bug fix on vnc stack, add password challenge for vnc, add rdpy-vncscreenshot binary

This commit is contained in:
speyrefitte
2014-11-03 17:20:23 +01:00
parent 1e139dd3d2
commit cafed06b82
10 changed files with 408 additions and 125 deletions

View File

@@ -39,6 +39,30 @@ rdpy-rdpclient is a simple RDP Qt4 client .
$ rdpy/bin/rdpy-rdpclient [-u username] [-p password] [-d domain] [...] XXX.XXX.XXX.XXX[:3389] $ rdpy/bin/rdpy-rdpclient [-u username] [-p password] [-d domain] [...] XXX.XXX.XXX.XXX[:3389]
``` ```
### rdpy-vncclient
rdpy-vncclient is a simple VNC Qt4 client .
```
$ rdpy/bin/rdpy-vncclient [-p password] XXX.XXX.XXX.XXX[:5900]
```
### rdpy-rdpscreenshot
rdpy-rdpscreenshot save login screen in file.
```
$ rdpy/bin/rdpy-rdpscreenshot [-w width] [-l height] [-o output_file_path] XXX.XXX.XXX.XXX[:3389]
```
### rdpy-vncscreenshot
rdpy-vncscreenshot save first screen update in file.
```
$ rdpy/bin/rdpy-vncscreenshot [-p password] [-o output_file_path] XXX.XXX.XXX.XXX[:5900]
```
### rdpy-rdpproxy ### rdpy-rdpproxy
rdpy-rdpproxy is a RDP proxy. It is used to manage and control access to the RDP servers as well as watch live sessions through any RDP client. It can be compared to a HTTP reverse proxy with added spy features. rdpy-rdpproxy is a RDP proxy. It is used to manage and control access to the RDP servers as well as watch live sessions through any RDP client. It can be compared to a HTTP reverse proxy with added spy features.
@@ -115,3 +139,35 @@ from twisted.internet import reactor
reactor.connectTCP("XXX.XXX.XXX.XXX", 3389), MyRDPFactory()) reactor.connectTCP("XXX.XXX.XXX.XXX", 3389), MyRDPFactory())
reactor.run() reactor.run()
``` ```
The VNC client code looks like this:
```
from rdpy.protocol.rfb import rdp
class MyRDPFactory(rfb.ClientFactory):
def clientConnectionLost(self, connector, reason):
reactor.stop()
def clientConnectionFailed(self, connector, reason):
reactor.stop()
def buildObserver(self, controller, addr):
class MyObserver(rfb.RFBClientObserver)
def onReady(self):
pass
def onUpdate(self, width, height, x, y, pixelFormat, encoding, data):
#here code handle bitmap
pass
def onClose(self):
pass
return MyObserver(controller)
from twisted.internet import reactor
reactor.connectTCP("XXX.XXX.XXX.XXX", 3389), MyRDPFactory())
reactor.run()
```

View File

@@ -31,6 +31,9 @@ from PyQt4 import QtGui
from rdpy.ui.qt4 import RDPClientQt from rdpy.ui.qt4 import RDPClientQt
from rdpy.protocol.rdp import rdp from rdpy.protocol.rdp import rdp
import rdpy.base.log as log
log._LOG_LEVEL = log.Level.INFO
class RDPClientQtFactory(rdp.ClientFactory): class RDPClientQtFactory(rdp.ClientFactory):
""" """
@summary: Factory create a RDP GUI client @summary: Factory create a RDP GUI client

View File

@@ -39,6 +39,8 @@ from rdpy.ui import view
from twisted.internet import reactor from twisted.internet import reactor
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
log._LOG_LEVEL = log.Level.INFO
class ProxyServer(rdp.RDPServerObserver): class ProxyServer(rdp.RDPServerObserver):
""" """
@summary: Server side of proxy @summary: Server side of proxy

View File

@@ -22,8 +22,7 @@
example of use rdpy as VNC client example of use rdpy as VNC client
""" """
import sys import sys, os, getopt
import os
# Change path so we find rdpy # Change path so we find rdpy
sys.path.insert(1, os.path.join(sys.path[0], '..')) sys.path.insert(1, os.path.join(sys.path[0], '..'))
@@ -31,18 +30,30 @@ sys.path.insert(1, os.path.join(sys.path[0], '..'))
from PyQt4 import QtGui from PyQt4 import QtGui
from rdpy.ui.qt4 import RFBClientQt from rdpy.ui.qt4 import RFBClientQt
from rdpy.protocol.rfb import rfb from rdpy.protocol.rfb import rfb
import rdpy.base.log as log
log._LOG_LEVEL = log.Level.INFO
class RFBClientQtFactory(rfb.ClientFactory): class RFBClientQtFactory(rfb.ClientFactory):
""" """
Factory create a VNC GUI client @summary: Factory create a VNC GUI client
""" """
def buildObserver(self, controller): def __init__(self, password):
""" """
Build RFB Client observer @param password: password for VNC authentication
"""
self._password = password
def buildObserver(self, controller, addr):
"""
@summary: Build RFB Client observer
@param controller: build by factory @param controller: build by factory
@param addr: destination
""" """
#set password
controller.setPassword(self._password)
#create client observer #create client observer
client = RFBClientQt(controller, 1024, 800) client = RFBClientQt(controller)
#create qt widget #create qt widget
self._w = client.getWidget() self._w = client.getWidget()
self._w.setWindowTitle('rdpy-vncclient') self._w.setWindowTitle('rdpy-vncclient')
@@ -51,7 +62,7 @@ class RFBClientQtFactory(rfb.ClientFactory):
def clientConnectionLost(self, connector, reason): def clientConnectionLost(self, connector, reason):
""" """
Connection lost event @summary: Connection lost event
@param connector: twisted connector use for vnc connection (use reconnect to restart connection) @param connector: twisted connector use for vnc connection (use reconnect to restart connection)
@param reason: str use to advertise reason of lost connection @param reason: str use to advertise reason of lost connection
""" """
@@ -61,7 +72,7 @@ class RFBClientQtFactory(rfb.ClientFactory):
def clientConnectionFailed(self, connector, reason): def clientConnectionFailed(self, connector, reason):
""" """
Connection failed event @summary: Connection failed event
@param connector: twisted connector use for vnc connection (use reconnect to restart connection) @param connector: twisted connector use for vnc connection (use reconnect to restart connection)
@param reason: str use to advertise reason of lost connection @param reason: str use to advertise reason of lost connection
""" """
@@ -70,6 +81,26 @@ class RFBClientQtFactory(rfb.ClientFactory):
app.exit() app.exit()
if __name__ == '__main__': if __name__ == '__main__':
#default script argument
password = ""
try:
opts, args = getopt.getopt(sys.argv[1:], "hp:")
except getopt.GetoptError:
help()
for opt, arg in opts:
if opt == "-h":
help()
sys.exit()
elif opt == "-p":
password = arg
if ':' in args[0]:
ip, port = args[0].split(':')
else:
ip, port = args[0], "5900"
#create application #create application
app = QtGui.QApplication(sys.argv) app = QtGui.QApplication(sys.argv)
@@ -78,7 +109,6 @@ if __name__ == '__main__':
qt4reactor.install() qt4reactor.install()
from twisted.internet import reactor from twisted.internet import reactor
reactor.connectTCP(sys.argv[1], int(sys.argv[2]), RFBClientQtFactory()) reactor.connectTCP(ip, int(port), RFBClientQtFactory(password))
reactor.runReturn() reactor.runReturn()
app.exec_() app.exec_()
reactor.stop()

View File

@@ -31,6 +31,7 @@ sys.path.insert(1, os.path.join(sys.path[0], '..'))
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from rdpy.protocol.rfb import rfb from rdpy.protocol.rfb import rfb
import rdpy.base.log as log import rdpy.base.log as log
from rdpy.ui.qt4 import qtImageFormatFromRFBPixelFormat
from twisted.internet import task from twisted.internet import task
#set log level #set log level
@@ -40,16 +41,18 @@ class RFBScreenShotFactory(rfb.ClientFactory):
""" """
@summary: Factory for screenshot exemple @summary: Factory for screenshot exemple
""" """
def __init__(self, path): def __init__(self, password, path):
""" """
@param password: password for VNC authentication
@param path: path of output screenshot @param path: path of output screenshot
""" """
self._path = path self._path = path
self._password = password
def clientConnectionLost(self, connector, reason): def clientConnectionLost(self, connector, reason):
""" """
@summary: Connection lost event @summary: Connection lost event
@param connector: twisted connector use for rdp connection (use reconnect to restart connection) @param connector: twisted connector use for rfb connection (use reconnect to restart connection)
@param reason: str use to advertise reason of lost connection @param reason: str use to advertise reason of lost connection
""" """
log.info("connection lost : %s"%reason) log.info("connection lost : %s"%reason)
@@ -59,7 +62,7 @@ class RFBScreenShotFactory(rfb.ClientFactory):
def clientConnectionFailed(self, connector, reason): def clientConnectionFailed(self, connector, reason):
""" """
@summary: Connection failed event @summary: Connection failed event
@param connector: twisted connector use for rdp connection (use reconnect to restart connection) @param connector: twisted connector use for rfb connection (use reconnect to restart connection)
@param reason: str use to advertise reason of lost connection @param reason: str use to advertise reason of lost connection
""" """
log.info("connection failed : %s"%reason) log.info("connection failed : %s"%reason)
@@ -82,25 +85,39 @@ class RFBScreenShotFactory(rfb.ClientFactory):
@param controller: RFBClientController @param controller: RFBClientController
@param path: path of output screenshot @param path: path of output screenshot
""" """
rdp.RDPClientObserver.__init__(self, controller) rfb.RFBClientObserver.__init__(self, controller)
self._buffer = QtGui.QImage(width, height, QtGui.QImage.Format_RGB32)
self._path = path self._path = path
self._buffer = None
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): def onUpdate(self, width, height, x, y, pixelFormat, encoding, data):
""" """
@summary: callback use when bitmap is received Implement RFBClientObserver interface
@param width: width of new image
@param height: height of new image
@param x: x position of new image
@param y: y position of new image
@param pixelFormat: pixefFormat structure in rfb.message.PixelFormat
@param encoding: encoding type rfb.message.Encoding
@param data: image data in accordance with pixel format and encoding
""" """
self._hasUpdated = True imageFormat = qtImageFormatFromRFBPixelFormat(pixelFormat)
image = RDPBitmapToQtImage(destLeft, width, height, bitsPerPixel, isCompress, data); if imageFormat is None:
log.error("Receive image in bad format")
return
image = QtGui.QImage(data, width, height, imageFormat)
with QtGui.QPainter(self._buffer) as qp: with QtGui.QPainter(self._buffer) as qp:
#draw image #draw image
qp.drawImage(destLeft, destTop, image, 0, 0, destRight - destLeft + 1, destBottom - destTop + 1) qp.drawImage(x, y, image, 0, 0, width, height)
self._controller.close()
def onReady(self): def onReady(self):
""" """
@summary: callback use when RDP stack is connected (just before received bitmap) @summary: callback use when RDP stack is connected (just before received bitmap)
""" """
log.info("connected %s"%addr) log.info("connected %s"%addr)
width, height = self._controller.getScreen()
self._buffer = QtGui.QImage(width, height, QtGui.QImage.Format_RGB32)
def onClose(self): def onClose(self):
""" """
@@ -108,51 +125,38 @@ class RFBScreenShotFactory(rfb.ClientFactory):
""" """
log.info("save screenshot into %s"%self._path) log.info("save screenshot into %s"%self._path)
self._buffer.save(self._path) self._buffer.save(self._path)
def checkUpdate(self):
if not self._hasUpdated:
log.info("close connection on timeout without updating orders")
self._controller.close();
return
self._hasUpdated = False
return ScreenShotObserver(controller, self._width, self._height, self._path, self._timeout) controller.setPassword(self._password)
return ScreenShotObserver(controller, self._path)
def help(): def help():
print "Usage: rdpy-rdpscreenshot [options] ip[:port]" print "Usage: rdpy-vncscreenshot [options] ip[:port]"
print "\t-w: width of screen default value is 1024" print "\t-o: file path of screenshot default(/tmp/rdpy-vncscreenshot.jpg)"
print "\t-l: height of screen default value is 800" print "\t-p: password for VNC Session"
print "\t-o: file path of screenshot default(/tmp/rdpy-rdpscreenshot.jpg)"
print "\t-t: timeout of connection without any updating order (default is 2s)"
if __name__ == '__main__': if __name__ == '__main__':
#default script argument #default script argument
width = 1024 path = "/tmp/rdpy-vncscreenshot.jpg"
height = 800 password = ""
path = "/tmp/rdpy-rdpscreenshot.jpg"
timeout = 2.0
try: try:
opts, args = getopt.getopt(sys.argv[1:], "hw:l:o:t:") opts, args = getopt.getopt(sys.argv[1:], "hp:o:")
except getopt.GetoptError: except getopt.GetoptError:
help() help()
for opt, arg in opts: for opt, arg in opts:
if opt == "-h": if opt == "-h":
help() help()
sys.exit() sys.exit()
elif opt == "-w":
width = int(arg)
elif opt == "-l":
height = int(arg)
elif opt == "-o": elif opt == "-o":
path = arg path = arg
elif opt == "-t": elif opt == "-p":
timeout = float(arg) password = arg
if ':' in args[0]: if ':' in args[0]:
ip, port = args[0].split(':') ip, port = args[0].split(':')
else: else:
ip, port = args[0], "3389" ip, port = args[0], "5900"
#create application #create application
app = QtGui.QApplication(sys.argv) app = QtGui.QApplication(sys.argv)
@@ -162,6 +166,6 @@ if __name__ == '__main__':
qt4reactor.install() qt4reactor.install()
from twisted.internet import reactor from twisted.internet import reactor
reactor.connectTCP(ip, int(port), RDPScreenShotFactory(width, height, path, timeout)) reactor.connectTCP(ip, int(port), RFBScreenShotFactory(password, path))
reactor.runReturn() reactor.runReturn()
app.exec_() app.exec_()

View File

@@ -127,14 +127,14 @@ class RawLayerClientFactory(protocol.ClientFactory):
@summary: Override this function to build raw layer @summary: Override this function to build raw layer
@param addr: destination address @param addr: destination address
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "buildRawLayer", "RawLayerClientFactory"))
def connectionLost(self, rawlayer): def connectionLost(self, rawlayer):
""" """
@summary: Override this method to handle connection lost @summary: Override this method to handle connection lost
@param rawlayer: rawLayer that cause connectionLost event @param rawlayer: rawLayer that cause connectionLost event
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "connectionLost", "RawLayerClientFactory"))
class RawLayerServerFactory(protocol.ClientFactory): class RawLayerServerFactory(protocol.ClientFactory):
""" """

View File

@@ -474,6 +474,7 @@ class CompositeType(Type):
break break
s.pos -= sizeof(self.__dict__[tmpName]) s.pos -= sizeof(self.__dict__[tmpName])
raise e raise e
if not self._readLen is None and readLen < self._readLen.value: if not self._readLen is None and readLen < self._readLen.value:
log.debug("Still have correct data in packet %s, read it as padding"%self.__class__) log.debug("Still have correct data in packet %s, read it as padding"%self.__class__)
s.read(self._readLen.value - readLen) s.read(self._readLen.value - readLen)

View File

@@ -425,7 +425,7 @@ class RDPServerController(pdu.layer.PDUServerListener):
class ClientFactory(layer.RawLayerClientFactory): class ClientFactory(layer.RawLayerClientFactory):
""" """
Factory of Client RDP protocol @summary: Factory of Client RDP protocol
""" """
def connectionLost(self, tpktLayer): def connectionLost(self, tpktLayer):
#retrieve controller #retrieve controller
@@ -437,7 +437,7 @@ class ClientFactory(layer.RawLayerClientFactory):
def buildRawLayer(self, addr): def buildRawLayer(self, addr):
""" """
Function call from twisted and build rdp protocol stack @summary: Function call from twisted and build rdp protocol stack
@param addr: destination address @param addr: destination address
""" """
controller = RDPClientController() controller = RDPClientController()
@@ -446,7 +446,7 @@ class ClientFactory(layer.RawLayerClientFactory):
def buildObserver(self, controller, addr): def buildObserver(self, controller, addr):
""" """
Build observer use for connection @summary: Build observer use for connection
@param controller: RDPClientController @param controller: RDPClientController
@param addr: destination address @param addr: destination address
""" """

View File

@@ -22,18 +22,18 @@ Implement Remote FrameBuffer protocol use in VNC client and server
@see: http://www.realvnc.com/docs/rfbproto.pdf @see: http://www.realvnc.com/docs/rfbproto.pdf
@todo: server side of protocol @todo: server side of protocol
@todo: vnc security type
@todo: more encoding rectangle @todo: more encoding rectangle
""" """
from rdpy.network.layer import RawLayer, RawLayerClientFactory from rdpy.network.layer import RawLayer, RawLayerClientFactory
from rdpy.network.type import UInt8, UInt16Be, UInt32Be, SInt32Be, String, CompositeType from rdpy.network.type import UInt8, UInt16Be, UInt32Be, SInt32Be, String, CompositeType
from rdpy.base.error import InvalidValue, CallPureVirtualFuntion from rdpy.base.error import InvalidValue, CallPureVirtualFuntion
from rdpy.protocol.rfb.pyDes import des
import rdpy.base.log as log import rdpy.base.log as log
class ProtocolVersion(object): class ProtocolVersion(object):
""" """
Different protocol version @summary: Different protocol version
""" """
UNKNOWN = "" UNKNOWN = ""
RFB003003 = "RFB 003.003\n" RFB003003 = "RFB 003.003\n"
@@ -42,7 +42,7 @@ class ProtocolVersion(object):
class SecurityType(object): class SecurityType(object):
""" """
Security type supported @summary: Security type supported
""" """
INVALID = 0 INVALID = 0
NONE = 1 NONE = 1
@@ -50,9 +50,9 @@ class SecurityType(object):
class Pointer(object): class Pointer(object):
""" """
Mouse event code (which button) @summary: Mouse event code (which button)
actually in RFB specification only actually in RFB specification only
three buttons are supported three buttons are supported
""" """
BUTTON1 = 0x1 BUTTON1 = 0x1
BUTTON2 = 0x2 BUTTON2 = 0x2
@@ -60,13 +60,13 @@ class Pointer(object):
class Encoding(object): class Encoding(object):
""" """
Encoding types of FrameBuffer update @summary: Encoding types of FrameBuffer update
""" """
RAW = 0 RAW = 0
class ClientToServerMessages(object): class ClientToServerMessages(object):
""" """
Client to server messages types @summary: Client to server messages types
""" """
PIXEL_FORMAT = 0 PIXEL_FORMAT = 0
ENCODING = 2 ENCODING = 2
@@ -77,7 +77,7 @@ class ClientToServerMessages(object):
class PixelFormat(CompositeType): class PixelFormat(CompositeType):
""" """
Pixel format structure @summary: Pixel format structure
""" """
def __init__(self): def __init__(self):
CompositeType.__init__(self) CompositeType.__init__(self)
@@ -95,8 +95,8 @@ class PixelFormat(CompositeType):
class ServerInit(CompositeType): class ServerInit(CompositeType):
""" """
Server init structure @summary: Server init structure
FrameBuffer configuration FrameBuffer configuration
""" """
def __init__(self): def __init__(self):
CompositeType.__init__(self) CompositeType.__init__(self)
@@ -106,9 +106,9 @@ class ServerInit(CompositeType):
class FrameBufferUpdateRequest(CompositeType): class FrameBufferUpdateRequest(CompositeType):
""" """
FrameBuffer update request send from client to server @summary: FrameBuffer update request send from client to server
Incremental means that server send update with a specific Incremental means that server send update with a specific
order, and client must draw orders in same order order, and client must draw orders in same order
""" """
def __init__(self, incremental = False, x = 0, y = 0, width = 0, height = 0): def __init__(self, incremental = False, x = 0, y = 0, width = 0, height = 0):
CompositeType.__init__(self) CompositeType.__init__(self)
@@ -121,7 +121,7 @@ class FrameBufferUpdateRequest(CompositeType):
class Rectangle(CompositeType): class Rectangle(CompositeType):
""" """
Header message of update rectangle @summary: Header message of update rectangle
""" """
def __init__(self): def __init__(self):
CompositeType.__init__(self) CompositeType.__init__(self)
@@ -133,8 +133,8 @@ class Rectangle(CompositeType):
class KeyEvent(CompositeType): class KeyEvent(CompositeType):
""" """
Key event structure message @summary: Key event structure message
Use to send a keyboard event Use to send a keyboard event
""" """
def __init__(self): def __init__(self):
CompositeType.__init__(self) CompositeType.__init__(self)
@@ -144,8 +144,8 @@ class KeyEvent(CompositeType):
class PointerEvent(CompositeType): class PointerEvent(CompositeType):
""" """
Pointer event structure message @summary: Pointer event structure message
Use to send mouse event Use to send mouse event
""" """
def __init__(self): def __init__(self):
CompositeType.__init__(self) CompositeType.__init__(self)
@@ -155,18 +155,27 @@ class PointerEvent(CompositeType):
class ClientCutText(CompositeType): class ClientCutText(CompositeType):
""" """
Client cut text message message @summary: Client cut text message message
Use to simulate copy paste (ctrl-c ctrl-v) only for text Use to simulate copy paste (ctrl-c ctrl-v) only for text
""" """
def __init__(self, text = ""): def __init__(self, text = ""):
CompositeType.__init__(self) CompositeType.__init__(self)
self.padding = (UInt16Be(), UInt8()) self.padding = (UInt16Be(), UInt8())
self.size = UInt32Be(len(text)) self.size = UInt32Be(len(text))
self.message = String(text) self.message = String(text)
class ServerCutTextHeader(CompositeType):
"""
@summary: Cut text header send from server to client
"""
def __init__(self):
CompositeType.__init__(self)
self.padding = (UInt16Be(), UInt8())
self.size = UInt32Be()
class RFB(RawLayer): class RFB(RawLayer):
""" """
Implement RFB protocol @summary: Implement RFB protocol
""" """
def __init__(self, listener): def __init__(self, listener):
""" """
@@ -194,8 +203,8 @@ class RFB(RawLayer):
self._nbRect = 0 self._nbRect = 0
#current rectangle header #current rectangle header
self._currentRect = Rectangle() self._currentRect = Rectangle()
#ready to send events #for vnc security type
self._ready = False self._password = '\0' * 8
def expectWithHeader(self, expectedHeaderLen, callbackBody): def expectWithHeader(self, expectedHeaderLen, callbackBody):
""" """
@@ -291,8 +300,30 @@ class RFB(RawLayer):
break break
#send back security level choosen #send back security level choosen
self.send(self._securityLevel) self.send(self._securityLevel)
if self._securityLevel.value == SecurityType.VNC:
self.expect(16, self.recvVNCChallenge)
else:
self.expect(4, self.recvSecurityResult)
def recvVNCChallenge(self, data):
"""
@summary: receive challenge in VNC authentication case
@param data: Stream that contain well formed packet
"""
key = (self._password + '\0' * 8)[:8]
newkey = []
for ki in range(len(key)):
bsrc = ord(key[ki])
btgt = 0
for i in range(8):
if bsrc & (1 << i):
btgt = btgt | (1 << 7-i)
newkey.append(chr(btgt))
algo = des(newkey)
self.send(String(algo.encrypt(data.getvalue())))
self.expect(4, self.recvSecurityResult) self.expect(4, self.recvSecurityResult)
def recvSecurityResult(self, data): def recvSecurityResult(self, data):
""" """
Read security result packet Read security result packet
@@ -326,7 +357,7 @@ class RFB(RawLayer):
def recvServerName(self, data): def recvServerName(self, data):
""" """
Read server name @summary: Read server name
@param data: Stream that contains well formed packet @param data: Stream that contains well formed packet
""" """
data.readType(self._serverName) data.readType(self._serverName)
@@ -339,24 +370,29 @@ class RFB(RawLayer):
#request entire zone #request entire zone
self.sendFramebufferUpdateRequest(False, 0, 0, self._serverInit.width.value, self._serverInit.height.value) self.sendFramebufferUpdateRequest(False, 0, 0, self._serverInit.width.value, self._serverInit.height.value)
#now i'm ready to send event #now i'm ready to send event
self._ready = True; self._clientListener.onReady()
self.expect(1, self.recvServerOrder) self.expect(1, self.recvServerOrder)
def recvServerOrder(self, data): def recvServerOrder(self, data):
""" """
Read order receive from server @summary: Read order receive from server
Main function for bitmap update from server to client Main function for bitmap update from server to client
@param data: Stream that contains well formed packet @param data: Stream that contains well formed packet
""" """
packet_type = UInt8() packetType = UInt8()
data.readType(packet_type) data.readType(packetType)
if packet_type == UInt8(0): if packetType.value == 0:
self.expect(3, self.recvFrameBufferUpdateHeader) self.expect(3, self.recvFrameBufferUpdateHeader)
elif packetType.value == 2:
self._clientListener.onBell()
elif packetType.value == 3:
self.expect(7, self.recvServerCutTextHeader)
else:
log.error("Unknown message type %s"%packetType.value)
def recvFrameBufferUpdateHeader(self, data): def recvFrameBufferUpdateHeader(self, data):
""" """
Read frame buffer update packet header @summary: Read frame buffer update packet header
@param data: Stream that contains well formed packet @param data: Stream that contains well formed packet
""" """
#padding #padding
@@ -367,7 +403,7 @@ class RFB(RawLayer):
def recvRectHeader(self, data): def recvRectHeader(self, data):
""" """
Read rectangle header @summary: Read rectangle header
@param data: Stream that contains well formed packet @param data: Stream that contains well formed packet
""" """
data.readType(self._currentRect) data.readType(self._currentRect)
@@ -376,12 +412,12 @@ class RFB(RawLayer):
def recvRectBody(self, data): def recvRectBody(self, data):
""" """
Read body of rectangle update @summary: Read body of rectangle update
@param data: Stream that contains well formed packet @param data: Stream that contains well formed packet
""" """
self._clientListener.recvRectangle(self._currentRect, self._pixelFormat, data.getvalue()) self._clientListener.recvRectangle(self._currentRect, self._pixelFormat, data.getvalue())
self._nbRect = self._nbRect - 1 self._nbRect -= 1
#if there is another rect to read #if there is another rect to read
if self._nbRect == 0: if self._nbRect == 0:
#job is finish send a request #job is finish send a request
@@ -389,69 +425,99 @@ class RFB(RawLayer):
self.expect(1, self.recvServerOrder) self.expect(1, self.recvServerOrder)
else: else:
self.expect(12, self.recvRectHeader) self.expect(12, self.recvRectHeader)
def recvServerCutTextHeader(self, data):
"""
@summary: callback when expect server cut text message
@param data: Stream that contains well formed packet
"""
header = ServerCutTextHeader()
data.readType(header)
self.expect(header.size.value, self.recvServerCutTextBody)
def recvServerCutTextBody(self, data):
"""
@summary: Receive server cut text body
@param data: Stream that contains well formed packet
"""
self._clientListener.onCutText(data.getvalue())
self.expect(1, self.recvServerOrder)
def sendClientInit(self): def sendClientInit(self):
""" """
Send client init packet @summary: Send client init packet
""" """
self.send(self._sharedFlag) self.send(self._sharedFlag)
self.expect(20, self.recvServerInit) self.expect(20, self.recvServerInit)
def sendPixelFormat(self, pixelFormat): def sendPixelFormat(self, pixelFormat):
""" """
Send pixel format structure @summary: Send pixel format structure
Very important packet that inform the image struct supported by the client Very important packet that inform the image struct supported by the client
@param pixelFormat: PixelFormat struct @param pixelFormat: PixelFormat struct
""" """
self.send((UInt8(ClientToServerMessages.PIXEL_FORMAT), UInt16Be(), UInt8(), pixelFormat)) self.send((UInt8(ClientToServerMessages.PIXEL_FORMAT), UInt16Be(), UInt8(), pixelFormat))
def sendSetEncoding(self): def sendSetEncoding(self):
""" """
Send set encoding packet @summary: Send set encoding packet
Actually only RAW bitmap encoding are used Actually only RAW bitmap encoding are used
""" """
self.send((UInt8(ClientToServerMessages.ENCODING), UInt8(), UInt16Be(1), SInt32Be(Encoding.RAW))) self.send((UInt8(ClientToServerMessages.ENCODING), UInt8(), UInt16Be(1), SInt32Be(Encoding.RAW)))
def sendFramebufferUpdateRequest(self, incremental, x, y, width, height): def sendFramebufferUpdateRequest(self, incremental, x, y, width, height):
""" """
Request server the specified zone @summary: Request server the specified zone
incremental means request only change before last update incremental means request only change before last update
""" """
self.send((UInt8(ClientToServerMessages.FRAME_BUFFER_UPDATE_REQUEST), FrameBufferUpdateRequest(incremental, x, y, width, height))) self.send((UInt8(ClientToServerMessages.FRAME_BUFFER_UPDATE_REQUEST), FrameBufferUpdateRequest(incremental, x, y, width, height)))
def sendKeyEvent(self, keyEvent): def sendKeyEvent(self, keyEvent):
""" """
Write key event packet @summary: Write key event packet
@param keyEvent: KeyEvent struct to send @param keyEvent: KeyEvent struct to send
""" """
self.send((UInt8(ClientToServerMessages.KEY_EVENT), keyEvent)) self.send((UInt8(ClientToServerMessages.KEY_EVENT), keyEvent))
def sendPointerEvent(self, pointerEvent): def sendPointerEvent(self, pointerEvent):
""" """
Write pointer event packet @summary: Write pointer event packet
@param pointerEvent: PointerEvent struct use @param pointerEvent: PointerEvent struct use
""" """
self.send((UInt8(ClientToServerMessages.POINTER_EVENT), pointerEvent)) self.send((UInt8(ClientToServerMessages.POINTER_EVENT), pointerEvent))
def sendClientCutText(self, text): def sendClientCutText(self, text):
""" """
write client cut text event packet @summary: write client cut text event packet
""" """
self.send((UInt8(ClientToServerMessages.CUT_TEXT), ClientCutText(text))) self.send((UInt8(ClientToServerMessages.CUT_TEXT), ClientCutText(text)))
class RFBClientListener(object): class RFBClientListener(object):
""" """
Interface use to expose event receive from RFB layer @summary: Interface use to expose event receive from RFB layer
""" """
def recvRectangle(self, rectangle, pixelFormat, data): def recvRectangle(self, rectangle, pixelFormat, data):
""" """
Receive rectangle order @summary: Receive rectangle order
Main update order type Main update order type
@param rectangle: Rectangle type header of packet @param rectangle: Rectangle type header of packet
@param pixelFormat: pixelFormat struct of current session @param pixelFormat: pixelFormat struct of current session
@param data: image data @param data: image data
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recvRectangle", "RFBListener")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recvRectangle", "RFBClientListener"))
def onBell(self):
"""
@summary: receive bip from server
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onBell", "RFBClientListener"))
def onCutText(self, text):
"""
@summary: Receive cut text from server
@param text: text inner cut text event
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onCutText", "RFBClientListener"))
class RFBClientController(RFBClientListener): class RFBClientController(RFBClientListener):
@@ -462,6 +528,7 @@ class RFBClientController(RFBClientListener):
self._clientObservers = [] self._clientObservers = []
#rfb layer to send client orders #rfb layer to send client orders
self._rfbLayer = RFB(self) self._rfbLayer = RFB(self)
self._isReady = False
def getProtocol(self): def getProtocol(self):
""" """
@@ -471,16 +538,49 @@ class RFBClientController(RFBClientListener):
def addClientObserver(self, observer): def addClientObserver(self, observer):
""" """
Add new observer for this protocol @summary: Add new observer for this protocol
@param observer: new observer @param observer: new observer
""" """
self._clientObservers.append(observer) self._clientObservers.append(observer)
observer._clientListener = self observer._clientListener = self
def getWidth(self):
"""
@return: width of framebuffer
"""
return self._rfbLayer._serverInit.width.value
def getHeight(self):
"""
@return: height of framebuffer
"""
return self._rfbLayer._serverInit.height.value
def getScreen(self):
"""
@return: (width, height) of screen
"""
return (self.getWidth(), self.getHeight())
def setPassword(self, password):
"""
@summary: set password for vnc authentication type
@param password: password for session
"""
self._rfbLayer._password = password
def onReady(self):
"""
@summary: rfb stack is reday to send or receive event
"""
self._isReady = True
for observer in self._clientObservers:
observer.onReady()
def recvRectangle(self, rectangle, pixelFormat, data): def recvRectangle(self, rectangle, pixelFormat, data):
""" """
Receive rectangle order @summary: Receive rectangle order
Main update order type Main update order type
@param rectangle: Rectangle type header of packet @param rectangle: Rectangle type header of packet
@param pixelFormat: pixelFormat struct of current session @param pixelFormat: pixelFormat struct of current session
@param data: image data @param data: image data
@@ -488,13 +588,38 @@ class RFBClientController(RFBClientListener):
for observer in self._clientObservers: for observer in self._clientObservers:
observer.onUpdate(rectangle.width.value, rectangle.height.value, rectangle.x.value, rectangle.y.value, pixelFormat, rectangle.encoding, data) observer.onUpdate(rectangle.width.value, rectangle.height.value, rectangle.x.value, rectangle.y.value, pixelFormat, rectangle.encoding, data)
def onBell(self):
"""
@summary: biiiip event
"""
for observer in self._clientObservers:
observer.onBell()
def onCutText(self, text):
"""
@summary: receive cut text event
@param text: text in cut text event
"""
for observer in self._clientObservers:
observer.onCutText(text)
def onClose(self):
"""
@summary: handle on close events
"""
if not self._isReady:
log.debug("Close on non ready layer means authentication error")
return
for observer in self._clientObservers:
observer.onClose()
def sendKeyEvent(self, isDown, key): def sendKeyEvent(self, isDown, key):
""" """
Send a key event throw RFB protocol @summary: Send a key event throw RFB protocol
@param isDown: boolean notify if key is pressed or not (True if key is pressed) @param isDown: boolean notify if key is pressed or not (True if key is pressed)
@param key: ASCII code of key @param key: ASCII code of key
""" """
if not self._rfbLayer._ready: if not self._isReady:
log.info("Try to send key event on non ready layer") log.info("Try to send key event on non ready layer")
return return
try: try:
@@ -508,12 +633,12 @@ class RFBClientController(RFBClientListener):
def sendPointerEvent(self, mask, x, y): def sendPointerEvent(self, mask, x, y):
""" """
Send a pointer event throw RFB protocol @summary: Send a pointer event throw RFB protocol
@param mask: mask of button if button 1 and 3 are pressed then mask is 00000101 @param mask: mask of button if button 1 and 3 are pressed then mask is 00000101
@param x: x coordinate of mouse pointer @param x: x coordinate of mouse pointer
@param y: y pointer of mouse pointer @param y: y pointer of mouse pointer
""" """
if not self._rfbLayer._ready: if not self._isReady:
log.info("Try to send pointer event on non ready layer") log.info("Try to send pointer event on non ready layer")
return return
try: try:
@@ -535,27 +660,37 @@ class RFBClientController(RFBClientListener):
class ClientFactory(RawLayerClientFactory): class ClientFactory(RawLayerClientFactory):
""" """
Twisted Factory of RFB protocol @summary: Twisted Factory of RFB protocol
""" """
def buildProtocol(self, addr): def buildRawLayer(self, addr):
""" """
Function call by twisted on connection @summary: Function call by twisted on connection
@param addr: address where client try to connect @param addr: address where client try to connect
""" """
controller = RFBClientController() controller = RFBClientController()
self.buildObserver(controller) self.buildObserver(controller, addr)
return controller.getProtocol() return controller.getProtocol()
def buildObserver(self, controller): def connectionLost(self, rfblayer):
""" """
Build an RFB observer object @summary: Override this method to handle connection lost
@param rfblayer: rfblayer that cause connectionLost event
"""
#call controller
rfblayer._clientListener.onClose()
def buildObserver(self, controller, addr):
"""
@summary: Build an RFB observer object
@param controller: controller use for rfb session
@param addr: destination
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "buildObserver", "ClientFactory")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "buildObserver", "ClientFactory"))
class RFBClientObserver(object): class RFBClientObserver(object):
""" """
RFB client protocol observer @summary: RFB client protocol observer
""" """
def __init__(self, controller): def __init__(self, controller):
self._controller = controller self._controller = controller
@@ -569,7 +704,7 @@ class RFBClientObserver(object):
def keyEvent(self, isPressed, key): def keyEvent(self, isPressed, key):
""" """
Send a key event @summary: Send a key event
@param isPressed: state of key @param isPressed: state of key
@param key: ASCII code of key @param key: ASCII code of key
""" """
@@ -577,7 +712,7 @@ class RFBClientObserver(object):
def mouseEvent(self, button, x, y): def mouseEvent(self, button, x, y):
""" """
Send a mouse event to RFB Layer @summary: Send a mouse event to RFB Layer
@param button: button number which is pressed (0,1,2,3,4,5,6,7) @param button: button number which is pressed (0,1,2,3,4,5,6,7)
@param x: x coordinate of mouse pointer @param x: x coordinate of mouse pointer
@param y: y coordinate of mouse pointer @param y: y coordinate of mouse pointer
@@ -590,9 +725,21 @@ class RFBClientObserver(object):
self._controller.sendPointerEvent(mask, x, y) self._controller.sendPointerEvent(mask, x, y)
def onReady(self):
"""
@summary: Event when network stack is ready to receive or send event
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RFBClientObserver"))
def onClose(self):
"""
@summary: On close event
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onClose", "RFBClientObserver"))
def onUpdate(self, width, height, x, y, pixelFormat, encoding, data): def onUpdate(self, width, height, x, y, pixelFormat, encoding, data):
""" """
Receive FrameBuffer update @summary: Receive FrameBuffer update
@param width : width of image @param width : width of image
@param height : height of image @param height : height of image
@param x : x position @param x : x position
@@ -602,3 +749,16 @@ class RFBClientObserver(object):
@param data : in respect of dataFormat and pixelFormat @param data : in respect of dataFormat and pixelFormat
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "RFBClientObserver")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "RFBClientObserver"))
def onCutText(self, text):
"""
@summary: event when server send cut text event
@param text: text received
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onCutText", "RFBClientObserver"))
def onBell(self):
"""
@summary: event when server send biiip
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onBell", "RFBClientObserver"))

View File

@@ -84,14 +84,14 @@ class RFBClientQt(RFBClientObserver, QAdaptor):
QAdaptor for specific RFB protocol stack QAdaptor for specific RFB protocol stack
is to an RFB observer is to an RFB observer
""" """
def __init__(self, controller, width, height): def __init__(self, controller):
""" """
@param controller: controller for observer @param controller: controller for observer
@param width: width of widget @param width: width of widget
@param height: height of widget @param height: height of widget
""" """
RFBClientObserver.__init__(self, controller) RFBClientObserver.__init__(self, controller)
self._widget = QRemoteDesktop(self, width, height) self._widget = QRemoteDesktop(self, 1024, 800)
def getWidget(self): def getWidget(self):
""" """
@@ -118,6 +118,26 @@ class RFBClientQt(RFBClientObserver, QAdaptor):
image = QtGui.QImage(data, width, height, imageFormat) image = QtGui.QImage(data, width, height, imageFormat)
self._widget.notifyImage(x, y, image, width, height) self._widget.notifyImage(x, y, image, width, height)
def onCutText(self, text):
"""
@summary: event when server send cut text event
@param text: text received
"""
pass
def onBell(self):
"""
@summary: event when server send biiip
"""
pass
def onReady(self):
"""
@summary: Event when network stack is ready to receive or send event
"""
(width, height) = self._controller.getScreen()
self._widget.resize(width, height)
def sendMouseEvent(self, e, isPressed): def sendMouseEvent(self, e, isPressed):
""" """
Convert Qt mouse event to RFB mouse event Convert Qt mouse event to RFB mouse event
@@ -148,6 +168,13 @@ class RFBClientQt(RFBClientObserver, QAdaptor):
@param: QCloseEvent @param: QCloseEvent
""" """
self._controller.close() self._controller.close()
def onClose(self):
"""
Call when stack is close
"""
#do something maybe a message
pass
def RDPBitmapToQtImage(destLeft, width, height, bitsPerPixel, isCompress, data): def RDPBitmapToQtImage(destLeft, width, height, bitsPerPixel, isCompress, data):
""" """
@@ -273,9 +300,9 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
def onClose(self): def onClose(self):
""" """
Call when stack is ready Call when stack is close
""" """
#do something maybe a loader #do something maybe a message
pass pass