support multi target in rdp proxy

This commit is contained in:
speyrefitte
2014-07-31 17:58:36 +02:00
parent a4f799d175
commit 9af647ab09
3 changed files with 105 additions and 42 deletions

View File

@@ -45,7 +45,22 @@ class ProxyServer(rdp.RDPServerObserver):
rdp.RDPServerObserver.__init__(self, controller)
self._credentialProvider = credentialProvider
self._client = None
self._window = None
def showSelectView(self, machines):
self._machines = dict([("%s:%s"%(ip, port), (ip, port)) for ip, port in machines])
width, height = self._controller.getScreen()
self._window = view.Window(width, height, QtGui.QColor(24, 93, 123))
self._window.addView(view.Anchor(width / 2 - 250, 100, view.Label("Please select following server", 500, 50, QtGui.QFont('arial', 18, QtGui.QFont.Bold), backgroundColor = QtGui.QColor(24, 93, 123))))
self._window.addView(view.Anchor(width / 2 - 250, 150, view.List(self._machines.keys(), 500, 500, self.onSelectMachine, QtGui.QColor(24, 93, 123))), True)
self._window.update(view.RDPRenderer(self._controller, self._controller.getColorDepth()), True)
def onSelectMachine(self, machine):
ip, port = self._machines[machine]
width, height = self._controller.getScreen()
domain, username, password = self._controller.getCredentials()
reactor.connectTCP(ip, port, ProxyClientFactory(self, width, height, domain, username, password, "%s\\%s on %s:%s"%(domain, username, ip, port)))
def clientConnected(self, client):
"""
Event throw by client when it's ready
@@ -54,23 +69,35 @@ class ProxyServer(rdp.RDPServerObserver):
self._client = client
self._controller.setColorDepth(self._client._controller.getColorDepth())
def showErrorMessage(self, message):
"""
Print a message to the client
@param message: string
"""
width, height = self._controller.getScreen()
popup = view.Window(width, height, QtGui.QColor(24, 93, 123))
popup.addView(view.Anchor(width / 2 - 250, height / 2 - 25, view.Label(message, 500, 50, QtGui.QFont('arial', 18, QtGui.QFont.Bold), backgroundColor = QtGui.QColor(24, 93, 123))))
popup.update(view.RDPRenderer(self._controller, self._controller.getColorDepth()), True)
def onReady(self):
"""
Event use to inform state of server stack
Use to connect client
On ready is not launch only on connection but after a reactivation process
On ready is not launch only after connection sequence but after a reactivation sequence too
"""
if self._client is None:
#try a connection
domain, username, password = self._controller.getCredentials()
try:
ip, port = self._credentialProvider.getProxyPass(domain, username)
except error.InvalidExpectedDataException as e:
log.info(e.message)
return
machines = self._credentialProvider.getProxyPass(domain, username)
width, height = self._controller.getScreen()
reactor.connectTCP(ip, port, ProxyClientFactory(self, width, height, domain, username, password, "%s\\%s on %s:%s"%(domain, username, ip, port)))
if len(machines) == 0:
self.showErrorMessage("No servers attach to account %s\\%s"%(domain, username))
elif len(machines) == 1:
ip, port = machines[0]
width, height = self._controller.getScreen()
reactor.connectTCP(ip, port, ProxyClientFactory(self, width, height, domain, username, password, "%s\\%s on %s:%s"%(domain, username, ip, port)))
else:
self.showSelectView(machines)
else:
#refresh client
width, height = self._controller.getScreen()
@@ -91,9 +118,12 @@ class ProxyServer(rdp.RDPServerObserver):
@param isPressed: True if key is down
"""
#no client connected
if self._client is None:
return
self._client._controller.sendKeyEventScancode(code, isPressed)
if not self._client is None:
self._client._controller.sendKeyEventScancode(code, isPressed)
elif not self._window is None and isPressed:
self._window.keyEvent(code)
self._window.update(view.RDPRenderer(self._controller, self._controller.getColorDepth()))
def onKeyEventUnicode(self, code, isPressed):
"""
@@ -262,10 +292,9 @@ class ProxyAdmin(rdp.RDPServerObserver):
Init GUI view
"""
width, height = self._controller.getScreen()
self._window = view.WindowView(width, height, QtGui.QColor(24, 93, 123))
self._list = view.ListView(ProxyClient._CONNECTED_.keys(), 500, 500, self.onSelect, QtGui.QColor(24, 93, 123))
self._window.addView(view.AnchorView(width / 2 - 250, height / 2 - 250, self._list))
self._render = view.RDPRenderer(self._controller, self._controller.getColorDepth())
self._window = view.Window(width, height, QtGui.QColor(24, 93, 123))
self._window.addView(view.Anchor(width / 2 - 250, 100, view.Label("Please select following session", 500, 50, QtGui.QFont('arial', 18, QtGui.QFont.Bold), backgroundColor = QtGui.QColor(24, 93, 123))))
self._window.addView(view.Anchor(width / 2 - 250, 150, view.List(ProxyClient._CONNECTED_.keys(), 500, 500, self.onSelect, QtGui.QColor(24, 93, 123))), True)
def onReady(self):
"""
@@ -274,7 +303,7 @@ class ProxyAdmin(rdp.RDPServerObserver):
"""
if self._state == ProxyAdmin.State.GUI:
self.initView()
self._window.update(self._render, True)
self._window.update(view.RDPRenderer(self._controller, self._controller.getColorDepth()), True)
elif self._state == ProxyAdmin.State.SPY:
#refresh client
width, height = self._controller.getScreen()
@@ -296,7 +325,7 @@ class ProxyAdmin(rdp.RDPServerObserver):
if not isPressed:
return
self._window.keyEvent(code)
self._window.update(self._render)
self._window.update(view.RDPRenderer(self._controller, self._controller.getColorDepth()))
elif code == 1:
#escape button refresh GUI
self._state = ProxyAdmin.State.GUI
@@ -364,20 +393,19 @@ class CredentialProvider(object):
def getAccount(self, domain, username):
if not self._config.has_key(domain) or not self._config[domain].has_key(username):
raise error.InvalidExpectedDataException("Invalid credentials %s\\%s"%(domain, username))
return None
return self._config[domain][username]
def getProxyPass(self, domain, username):
"""
@param domain: domain to check
@param username: username in domain
@return: (ip, port) or None if error
@return: [(ip, port)]
"""
account = self.getAccount(domain, username)
if not account.has_key("ip") or not account.has_key("port"):
raise error.InvalidExpectedDataException("Invalid credentials declaration %s\\%s"%(domain, username))
return str(account['ip']), account['port']
if account is None:
return []
return [(str(machine["ip"]), machine["port"]) for machine in account]
def help():
"""

View File

@@ -646,7 +646,7 @@ class DataPDU(CompositeType):
"""
Create object in accordance self.shareDataHeader.pduType2 value
"""
for c in [UpdateDataPDU, SynchronizeDataPDU, ControlDataPDU, ErrorInfoDataPDU, FontListDataPDU, FontMapDataPDU, PersistentListPDU, ClientInputEventPDU, ShutdownDeniedPDU, ShutdownRequestPDU, SuppressOutputDataPDU]:
for c in [UpdateDataPDU, SynchronizeDataPDU, ControlDataPDU, ErrorInfoDataPDU, FontListDataPDU, FontMapDataPDU, PersistentListPDU, ClientInputEventPDU, ShutdownDeniedPDU, ShutdownRequestPDU, SupressOutputDataPDU]:
if self.shareDataHeader.pduType2.value == c._PDUTYPE2_:
return c()
log.debug("unknown PDU data type : %s"%hex(self.shareDataHeader.pduType2.value))
@@ -812,13 +812,13 @@ class InclusiveRectangle(CompositeType):
@see: http://msdn.microsoft.com/en-us/library/cc240643.aspx
"""
def __init__(self, conditional = lambda:True):
CompositeType.__init__(self)
CompositeType.__init__(self, conditional = conditional)
self.left = UInt16Le()
self.top = UInt16Le()
self.right = UInt16Le()
self.bottom = UInt16Le()
class SuppressOutputDataPDU(CompositeType):
class SupressOutputDataPDU(CompositeType):
"""
@see: http://msdn.microsoft.com/en-us/library/cc240648.aspx
"""

View File

@@ -86,7 +86,7 @@ class IView(object):
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "update", "IView"))
class AnchorView(IView):
class Anchor(IView):
def __init__(self, x, y, view):
self._x = x
self._y = y
@@ -103,7 +103,7 @@ class AnchorView(IView):
self._view.update(render, force)
render.translate(-self._x, -self._y)
class ListView(IView):
class List(IView):
"""
List widget simulate by QT painter
"""
@@ -158,15 +158,17 @@ class ListView(IView):
i += 1
render.drawImage(drawArea)
class WindowView(IView):
class Window(IView):
def __init__(self, width, height, backgroundColor = QtCore.Qt.black):
self._views = []
self._focusIndex = 0
self._width = width
self._height = height
self._backgroundColor = backgroundColor
def addView(self, view):
def addView(self, view, focus = False):
self._views.append(view)
if focus:
self._focusIndex = len(self._views) - 1
def keyEvent(self, code):
if self._focusIndex < len(self._views):
self._views[self._focusIndex].keyEvent(code)
@@ -181,6 +183,43 @@ class WindowView(IView):
render.drawImage(drawArea)
for view in self._views:
view.update(render, force)
class Label(IView):
def __init__(self, label, width, height, font = QtGui.QFont(), fontColor = QtCore.Qt.white, backgroundColor = QtCore.Qt.black):
self._label = label
self._width = width
self._height = height
self._font = font
self._fontColor = fontColor
self._backgroundColor = backgroundColor
def keyEvent(self, code):
"""
Nothing to do
"""
pass
def pointerEvent(self, x, y, button):
"""
Nothing to do
"""
pass
def update(self, render, force = False):
"""
Update view
@param render: IRender
@param force: force update
"""
if not force:
return;
drawArea = QtGui.QImage(self._width, self._height, render.getImageFormat())
drawArea.fill(self._backgroundColor)
with QtGui.QPainter(drawArea) as qp:
qp.setFont(self._font)
qp.setPen(self._fontColor)
qp.drawText(drawArea.rect(), QtCore.Qt.AlignCenter, self._label)
render.drawImage(drawArea)
class RDPRenderer(object):
def __init__(self, controller, colorDepth):
@@ -192,7 +231,6 @@ class RDPRenderer(object):
self._colorDepth = colorDepth
self._dx = 0
self._dy = 0
self._renderSize = 64
def getImageFormat(self):
if self._colorDepth == 15:
@@ -212,14 +250,11 @@ class RDPRenderer(object):
"""
Render of widget
"""
nbWidth = image.width() / self._renderSize + 1
nbHeight = image.height() / self._renderSize + 1
for i in range(0, nbWidth):
for j in range(0, nbHeight):
tmp = image.copy(i * self._renderSize, j * self._renderSize, self._renderSize, self._renderSize)
#in RDP image or bottom top encoded
tmp = tmp.transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
ptr = tmp.bits()
ptr.setsize(tmp.byteCount())
self._controller.sendUpdate(i * self._renderSize + self._dx, j * self._renderSize + self._dy, min((i + 1) * self._renderSize, image.width()) + self._dx - 1, min((j + 1) * self._renderSize, image.height()) + self._dy - 1, tmp.width(), tmp.height(), self._colorDepth, False, ptr.asstring())
padding = image.width() % 4
for i in range(0, image.height()):
tmp = image.copy(0, i, image.width() + padding, 1)
#in RDP image or bottom top encoded
ptr = tmp.bits()
ptr.setsize(tmp.byteCount())
self._controller.sendUpdate(self._dx, i + self._dy, image.width() + self._dx - 1, i + self._dy, tmp.width(), tmp.height(), self._colorDepth, False, ptr.asstring())