fix rdp stander security layer server side bug, fix lisense automata, ready for next release

This commit is contained in:
speyrefitte
2015-01-05 18:34:50 +01:00
parent f4808d0ae2
commit 7ff5c5caa3
16 changed files with 353 additions and 457 deletions

View File

@@ -35,16 +35,17 @@ class RDPClientQtFactory(rdp.ClientFactory):
"""
@summary: Factory create a RDP GUI client
"""
def __init__(self, width, height, username, password, domain, fullscreen, keyboardLayout, optimized):
def __init__(self, width, height, username, password, domain, fullscreen, keyboardLayout, optimized, security):
"""
@param width: width of client
@param heigth: heigth of client
@param username: username present to the server
@param password: password present to the server
@param domain: microsoft domain
@param fullscreen: show widget in fullscreen mode
@param keyboardLayout: keyboard layout
@param optimized: enable optimized session orders
@param width: {integer} width of client
@param heigth: {integer} heigth of client
@param username: {str} username present to the server
@param password: {str} password present to the server
@param domain: {str} microsoft domain
@param fullscreen: {bool} show widget in fullscreen mode
@param keyboardLayout: {str} (fr|en) keyboard layout
@param optimized: {bool} enable optimized session orders
@param security: {str} (ssl | rdp | nego)
"""
self._width = width
self._height = height
@@ -54,8 +55,12 @@ class RDPClientQtFactory(rdp.ClientFactory):
self._fullscreen = fullscreen
self._keyboardLayout = keyboardLayout
self._optimized = optimized
self._nego = security == "nego"
if self._nego:
self._security = "ssl"
else:
self._security = security
self._w = None
self._basicRDPSecurity = False
def buildObserver(self, controller, addr):
"""
@@ -66,9 +71,9 @@ class RDPClientQtFactory(rdp.ClientFactory):
@return: RDPClientQt
"""
#create client observer
client = RDPClientQt(controller, self._width, self._height)
self._client = RDPClientQt(controller, self._width, self._height)
#create qt widget
self._w = client.getWidget()
self._w = self._client.getWidget()
self._w.setWindowTitle('rdpy-rdpclient')
if self._fullscreen:
self._w.showFullScreen()
@@ -82,14 +87,9 @@ class RDPClientQtFactory(rdp.ClientFactory):
controller.setHostname(socket.gethostname())
if self._optimized:
controller.setPerformanceSession()
if self._basicRDPSecurity:
controller.setRDPBasicSecurity()
controller.setSecurityLevel(self._security)
return client
def startedConnecting(self, connector):
pass
return self._client
def clientConnectionLost(self, connector, reason):
"""
@@ -98,8 +98,12 @@ class RDPClientQtFactory(rdp.ClientFactory):
@param reason: str use to advertise reason of lost connection
"""
#try reconnect with basic RDP security
if reason.type == RDPSecurityNegoFail and not self._basicRDPSecurity:
self._basicRDPSecurity = True
if reason.type == RDPSecurityNegoFail and self._nego:
#stop nego
log.info("due to security nego error back to standard RDP security layer")
self._nego = False
self._security = "rdp"
self._client._widget.hide()
connector.connect()
return
@@ -205,12 +209,9 @@ if __name__ == '__main__':
width = QtGui.QDesktopWidget().screenGeometry().width()
height = QtGui.QDesktopWidget().screenGeometry().height()
log.info("keyboard layout set to %s"%keyboardLayout)
from twisted.internet import reactor
reactor.connectTCP(ip, int(port), RDPClientQtFactory(width, height, username, password, domain, fullscreen, keyboardLayout, optimized))
reactor.connectTCP(ip, int(port), RDPClientQtFactory(width, height, username, password, domain, fullscreen, keyboardLayout, optimized, "nego"))
reactor.runReturn()
app.exec_()

View File

@@ -23,10 +23,10 @@ RDP proxy with spy capabilities
---------------------------
Client RDP -> | ProxyServer | ProxyClient | -> Server RDP
---------------------------
| ProxyAdmin |
------------
^
Admin ----------------------|
| ProxyShadow |
--------------
^
Shadow -------------------|
"""
import sys, os, getopt, json
@@ -37,77 +37,32 @@ from rdpy.ui import view
from twisted.internet import reactor
from PyQt4 import QtCore, QtGui
#log._LOG_LEVEL = log.Level.INFO
log._LOG_LEVEL = log.Level.INFO
class ProxyServer(rdp.RDPServerObserver):
"""
@summary: Server side of proxy
"""
def __init__(self, controller, credentialProvider):
_SESSIONS_ = {}
def __init__(self, controller, target):
"""
@param controller: RDPServerController
@param credentialProvider: CredentialProvider
@param controller: {RDPServerController}
@param target: {tuple(ip, port)}
"""
rdp.RDPServerObserver.__init__(self, controller)
self._credentialProvider = credentialProvider
self._target = target
self._client = None
self._window = None
def showSelectView(self, machines):
"""
@summary: Show select sever view to the client
@param machines: [(ip, port)]
"""
self._machines = machines
width, height = self._controller.getScreen()
self._window = view.Window(width, height, QtGui.QColor(8, 24, 66))
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(8, 24, 66))))
self._window.addView(view.Anchor(width / 2 - 250, 150,
view.List(["%s:%s"%(ip, port) for ip, port in machines],
500, 500, self.onSelectMachine,
QtGui.QColor(8, 24, 66))), True)
self._window.update(view.RDPRenderer(self._controller), True)
def onSelectMachine(self, index):
"""
@summary: Callback of view.List in Select server view
@param: index in list
"""
ip, port = self._machines[index]
width, height = self._controller.getScreen()
domain, username, password = self._controller.getCredentials()
reactor.connectTCP(ip, port, ProxyClientFactory(self, width, height, domain, username, password))
def clientConnected(self, client):
"""
@summary: Event throw by client when it's ready
@param client: ProxyClient
@param client: {ProxyClient}
"""
self._client = client
#need to reevaluate color depth
self._controller.setColorDepth(self._client._controller.getColorDepth())
def showMessage(self, message):
"""
@summary: Print a message to the client
@param message: string
"""
width, height = self._controller.getScreen()
popup = view.Window(width, height, QtGui.QColor(8, 24, 66))
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(8, 24, 66))))
popup.update(view.RDPRenderer(self._controller), True)
ProxyServer._SESSIONS_[self._controller.getHostname()] = client
nowConnected()
def onReady(self):
"""
@@ -115,23 +70,15 @@ class ProxyServer(rdp.RDPServerObserver):
First time this event is called is when human client is connected
Second time is after color depth nego, because color depth nego
restart a connection sequence
Use to connect proxy client or show available server
@see: rdp.RDPServerObserver.onReady
"""
if self._client is None:
#try a connection
domain, username, password = self._controller.getCredentials()
machines = self._credentialProvider.getProxyPass(domain, username)
if len(machines) == 0:
self.showMessage("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))
else:
self.showSelectView(machines)
width, height = self._controller.getScreen()
reactor.connectTCP(self._target[0], int(self._target[1]), ProxyClientFactory(self, width, height,
domain, username, password))
else:
#refresh client
width, height = self._controller.getScreen()
@@ -144,6 +91,9 @@ class ProxyServer(rdp.RDPServerObserver):
"""
if self._client is None:
return
del ProxyServer._SESSIONS_[self._controller.getHostname()]
nowConnected()
#close proxy client
self._client._controller.close()
@@ -154,13 +104,9 @@ class ProxyServer(rdp.RDPServerObserver):
@param isPressed: True if key is down
@see: rdp.RDPServerObserver.onKeyEventScancode
"""
#no client connected
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))
if self._client is None:
return
self._client._controller.sendKeyEventScancode(code, isPressed)
def onKeyEventUnicode(self, code, isPressed):
"""
@@ -169,7 +115,6 @@ class ProxyServer(rdp.RDPServerObserver):
@param isPressed: True if key is down
@see: rdp.RDPServerObserver.onKeyEventUnicode
"""
#no client connected domain
if self._client is None:
return
self._client._controller.sendKeyEventUnicode(code, isPressed)
@@ -183,7 +128,6 @@ class ProxyServer(rdp.RDPServerObserver):
@param isPressed: True if mouse button is pressed
@see: rdp.RDPServerObserver.onPointerEvent
"""
#no client connected
if self._client is None:
return
self._client._controller.sendPointerEvent(x, y, button, isPressed)
@@ -192,14 +136,14 @@ class ProxyServerFactory(rdp.ServerFactory):
"""
@summary: Factory on listening events
"""
def __init__(self, credentialProvider, privateKeyFilePath, certificateFilePath):
def __init__(self, target, privateKeyFilePath = None, certificateFilePath = None):
"""
@param credentialProvider: CredentialProvider
@param privateKeyFilePath: file contain server private key
@param certificateFilePath: file contain server certificate
@param target: {tuple(ip, prt)}
@param privateKeyFilePath: {str} file contain server private key (if none -> back to standard RDP security)
@param certificateFilePath: {str} file contain server certificate (if none -> back to standard RDP security)
"""
rdp.ServerFactory.__init__(self, privateKeyFilePath, certificateFilePath, 16)
self._credentialProvider = credentialProvider
rdp.ServerFactory.__init__(self, 16, privateKeyFilePath, certificateFilePath)
self._target = target
def buildObserver(self, controller, addr):
"""
@@ -207,23 +151,19 @@ class ProxyServerFactory(rdp.ServerFactory):
@param addr: destination address
@see: rdp.ServerFactory.buildObserver
"""
return ProxyServer(controller, self._credentialProvider)
return ProxyServer(controller, self._target)
class ProxyClient(rdp.RDPClientObserver):
"""
@summary: Client side of proxy
"""
_CONNECTED_ = []
def __init__(self, controller, server, name = None):
def __init__(self, controller, server):
"""
@param controller: rdp.RDPClientController
@param server: ProxyServer
@param name: name of session None if you don't
want to spy this session
@param server: ProxyServer
"""
rdp.RDPClientObserver.__init__(self, controller)
self._server = server
self._name = name
self._connected = False
def onReady(self):
@@ -239,9 +179,7 @@ class ProxyClient(rdp.RDPClientObserver):
return
else:
self._connected = True
if not self._name is None:
ProxyClient._CONNECTED_.append(self)
self._server.clientConnected(self)
def onClose(self):
@@ -249,8 +187,6 @@ class ProxyClient(rdp.RDPClientObserver):
@summary: Event inform that stack is close
@see: rdp.RDPClientObserver.onClose
"""
if not self._name is None:
ProxyClient._CONNECTED_.remove(self)
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
"""
@@ -302,57 +238,18 @@ class ProxyClientFactory(rdp.ClientFactory):
controller.setDomain(self._domain)
controller.setUsername(self._username)
controller.setPassword(self._password)
return ProxyClient(controller, self._server, "%s\\%s on %s"%(self._domain, self._username, addr))
return ProxyClient(controller, self._server)
def startedConnecting(self, connector):
pass
def clientConnectionLost(self, connector, reason):
pass
def clientConnectionFailed(self, connector, reason):
pass
class ProxyAdmin(rdp.RDPServerObserver):
class Shadow(rdp.RDPServerObserver):
"""
@summary: Use to manage admin session
Add GUI to select which session to see
Just escape key is authorized during spy session
To switch from spy state to admin state
"""
class State(object):
GUI = 0 #->list of active session
SPY = 1 #->watch active session
def __init__(self, controller):
"""
@param server: rdp.RDPServerController
"""
rdp.RDPServerObserver.__init__(self, controller)
self._spy = None
self._state = ProxyAdmin.State.GUI
def initView(self):
"""
@summary: Initialize Admin GUI view
"""
self._sessions = list(ProxyClient._CONNECTED_) #copy at t time
width, height = self._controller.getScreen()
self._window = view.Window(width, height, QtGui.QColor(8, 24, 66))
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(8, 24, 66))))
self._window.addView(view.Anchor(width / 2 - 250, 150,
view.List([p._name for p in self._sessions],
500, 500, self.onSelect,
QtGui.QColor(8, 24, 66))), True)
def clientConnected(self, client):
pass
self._client = None
def onReady(self):
"""
@@ -360,73 +257,39 @@ class ProxyAdmin(rdp.RDPServerObserver):
May be called after an setColorDepth too
@see: rdp.RDPServerObserver.onReady
"""
if self._state == ProxyAdmin.State.GUI:
self.initView()
self._window.update(view.RDPRenderer(self._controller), True)
elif self._state == ProxyAdmin.State.SPY:
if self._client is None:
username = self._controller.getUsername()
if not ProxyServer._SESSIONS_.has_key(username):
log.info("invalid session name [%s]"%username)
self._controller.close()
return
self._client = ProxyClient(ProxyServer._SESSIONS_[username]._controller, self)
self._controller.setColorDepth(self._client._controller.getColorDepth())
else:
#refresh client
width, height = self._controller.getScreen()
self._spy._controller.sendRefreshOrder(0, 0, width, height)
self._client._controller.sendRefreshOrder(0, 0, width, height)
def onClose(self):
"""
@summary: Stack is close
@see: rdp.RDPServerObserver.onClose
"""
pass
def onKeyEventScancode(self, code, isPressed):
""" Shadow
"""
@summary: Event call when a keyboard event
is catch in scan code format
@param code: scan code of key
@param isPressed: True if key is down
@see: rdp.RDPServerObserver.onKeyEventScancode
"""
if self._state == ProxyAdmin.State.GUI:
if not isPressed:
return
self._window.keyEvent(code)
self._window.update(view.RDPRenderer(self._controller))
elif code == 1:
#escape button refresh GUI
self._state = ProxyAdmin.State.GUI
self._spy._controller.removeClientObserver(self._spy)
self.onReady()
def onKeyEventUnicode(self, code, isPressed):
""" Shadow
"""
@summary: Event call when a keyboard event is catch in unicode format
Admin GUI add filter for this event
@param code: unicode of key
@param isPressed: True if key is down
@see: rdp.RDPServerObserver.onKeyEventUnicode
"""
pass
def onPointerEvent(self, x, y, button, isPressed):
""" Shadow
"""
@summary: Event call on mouse event
Admin GUI add filter for this event
@param x: x position
@param y: y position
@param button: 1, 2 or 3 button
@param isPressed: True if mouse button is pressed
@see: rdp.RDPServerObserver.onPointerEvent
"""
pass
def onSelect(self, index):
"""
@summary: Callback of list view of active session
Connect to select session
@param index: index in sessions array
"""
self._state = ProxyAdmin.State.SPY
self._spy = ProxyClient(self._sessions[index]._controller, self)
self._controller.setColorDepth(self._spy._controller.getColorDepth())
class ProxyAdminFactory(rdp.ServerFactory):
class ShadowFactory(rdp.ServerFactory):
"""
@summary: Factory for admin session
"""
@@ -435,7 +298,7 @@ class ProxyAdminFactory(rdp.ServerFactory):
@param privateKeyFilePath: private key for admin session
@param certificateFilePath: certificate for admin session
"""
rdp.ServerFactory.__init__(self, privateKeyFilePath, certificateFilePath, 16)
rdp.ServerFactory.__init__(self, 16, privateKeyFilePath, certificateFilePath)
def buildObserver(self, controller, addr):
"""
@@ -445,116 +308,58 @@ class ProxyAdminFactory(rdp.ServerFactory):
@return: ProxyAdmin
@see: rdp.ServerFactory.buildObserver
"""
return ProxyAdmin(controller)
class CredentialProvider(object):
"""
@summary: Credential provider for proxy
"""
def __init__(self, config):
"""
@param config: rdp proxy config
"""
self._config = config
def getAccount(self, domain, username):
"""
@summary: Find account that match domain::username in config file
@param domain: Windows domain
@param username: username for session
@return: [(unicode(ip), port] or None if not found
"""
if not self._config.has_key(domain) or not self._config[domain].has_key(username):
return None
return self._config[domain][username]
def getProxyPass(self, domain, username):
"""
@summary: Find list of server available for thi account
@param domain: domain to check
@param username: username in domain
@return: [(ip, port)]
"""
account = self.getAccount(domain, username)
if account is None:
return []
return [(str(machine["ip"]), machine["port"]) for machine in account]
return Shadow(controller)
def help():
"""
@summary: Print help in console
"""
print "Usage: rdpy-rdpproxy -f credential_file_path -k private_key_file_path -c certificate_file_path [-i admin_ip[:admin_port]] listen_port"
print "Usage: rdpy-rdpproxy -t target_ip[:target_port] [-k private_key_file_path (mandatory for SSL)] [-c certificate_file_path (mandatory for SSL)] [-i admin_ip[:admin_port]] listen_port"
def parseIpPort(interface, defaultPort = "3389"):
if ':' in interface:
return interface.split(':')
else:
return interface, defaultPort
def loadConfig(configFilePath):
"""
@summary: Load and check config file
@param configFilePath: config file path
"""
if not os.path.isfile(configFilePath):
log.error("File %s doesn't exist"%configFilePath)
return None
f = open(configFilePath, 'r')
config = json.load(f)
f.close()
return config
def nowConnected():
log.info("*" * 50)
log.info("Now connected")
log.info(ProxyServer._SESSIONS_.keys())
log.info("*" * 50)
if __name__ == '__main__':
configFilePath = None
target = None
privateKeyFilePath = None
certificateFilePath = None
adminInterface = None
shadowInterface = None
try:
opts, args = getopt.getopt(sys.argv[1:], "hf:k:c:i:")
opts, args = getopt.getopt(sys.argv[1:], "ht:k:c:i:")
except getopt.GetoptError:
help()
for opt, arg in opts:
if opt == "-h":
help()
sys.exit()
elif opt == "-f":
configFilePath = arg
elif opt == "-t":
target = arg
elif opt == "-k":
privateKeyFilePath = arg
elif opt == "-c":
certificateFilePath = arg
elif opt == "-i":
adminInterface = arg
shadowInterface = arg
if configFilePath is None:
print "Config file is mandatory"
help()
sys.exit()
if certificateFilePath is None:
print "Certificate file is mandatory"
help()
sys.exit()
if privateKeyFilePath is None:
print "Private key file is mandatory"
if target is None:
log.error("Target is mandatory")
help()
sys.exit()
#load config file
config = loadConfig(configFilePath)
if config is None:
log.error("Bad configuration file")
sys.exit()
reactor.listenTCP(int(args[0]), ProxyServerFactory(parseIpPort(target), privateKeyFilePath, certificateFilePath))
#use to init font
app = QtGui.QApplication(sys.argv)
reactor.listenTCP(int(args[0]), ProxyServerFactory(CredentialProvider(config), privateKeyFilePath, certificateFilePath))
if not adminInterface is None:
if ':' in adminInterface:
adminInterface, adminPort = adminInterface.split(':')
else:
adminInterface, adminPort = adminInterface, "3390"
log.info("Admin listen on %s:%s"%(adminInterface, adminPort))
reactor.listenTCP(int(adminPort), ProxyAdminFactory(privateKeyFilePath, certificateFilePath), interface = adminInterface)
if not shadowInterface is None:
shadowInterface, shadowPort = parseIpPort(shadowInterface)
log.info("Shadow listener on %s:%s"%(shadowInterface, shadowPort))
reactor.listenTCP(int(shadowPort), ShadowFactory(privateKeyFilePath, certificateFilePath), interface = shadowInterface)
reactor.run()

View File

@@ -29,6 +29,7 @@ from PyQt4 import QtCore, QtGui
from rdpy.protocol.rdp import rdp
from rdpy.ui.qt4 import RDPBitmapToQtImage
import rdpy.core.log as log
from rdpy.core.error import RDPSecurityNegoFail
from twisted.internet import task
#set log level
@@ -39,18 +40,23 @@ class RDPScreenShotFactory(rdp.ClientFactory):
@summary: Factory for screenshot exemple
"""
__INSTANCE__ = 0
def __init__(self, width, height, path, timeout):
__STATE__ = []
def __init__(self, reactor, app, width, height, path, timeout):
"""
@param width: width of screen
@param height: height of screen
@param path: path of output screenshot
@param timeout: close connection after timeout s without any updating
@param reactor: twisted reactor
@param width: {integer} width of screen
@param height: {integer} height of screen
@param path: {str} path of output screenshot
@param timeout: {float} close connection after timeout s without any updating
"""
RDPScreenShotFactory.__INSTANCE__ += 1
self._reactor = reactor
self._app = app
self._width = width
self._height = height
self._path = path
self._timeout = timeout
self._security = "ssl"
def clientConnectionLost(self, connector, reason):
"""
@@ -58,11 +64,18 @@ class RDPScreenShotFactory(rdp.ClientFactory):
@param connector: twisted connector use for rdp connection (use reconnect to restart connection)
@param reason: str use to advertise reason of lost connection
"""
if reason.type == RDPSecurityNegoFail and self._security != "rdp":
log.info("due to RDPSecurityNegoFail try standard security layer")
self._security = "rdp"
connector.connect()
return
log.info("connection lost : %s"%reason)
RDPScreenShotFactory.__STATE__.append((connector.host, connector.port, reason))
RDPScreenShotFactory.__INSTANCE__ -= 1
if(RDPScreenShotFactory.__INSTANCE__ == 0):
reactor.stop()
app.exit()
self._reactor.stop()
self._app.exit()
def clientConnectionFailed(self, connector, reason):
"""
@@ -71,10 +84,11 @@ class RDPScreenShotFactory(rdp.ClientFactory):
@param reason: str use to advertise reason of lost connection
"""
log.info("connection failed : %s"%reason)
RDPScreenShotFactory.__STATE__.append((connector.host, connector.port, reason))
RDPScreenShotFactory.__INSTANCE__ -= 1
if(RDPScreenShotFactory.__INSTANCE__ == 0):
reactor.stop()
app.exit()
self._reactor.stop()
self._app.exit()
def buildObserver(self, controller, addr):
@@ -87,20 +101,24 @@ class RDPScreenShotFactory(rdp.ClientFactory):
"""
@summary: observer that connect, cache every image received and save at deconnection
"""
def __init__(self, controller, width, height, path, timeout):
def __init__(self, controller, width, height, security, path, timeout, reactor):
"""
@param controller: RDPClientController
@param width: width of screen
@param height: height of screen
@param path: path of output screenshot
@param timeout: close connection after timeout s without any updating
@param controller: {RDPClientController}
@param width: {integer} width of screen
@param height: {integer} height of screen
@param security: {str} (ssl | rdp) security level
@param path: {str} path of output screenshot
@param timeout: {float} close connection after timeout s without any updating
@param reactor: twisted reactor
"""
rdp.RDPClientObserver.__init__(self, controller)
controller.setScreen(width, height);
controller.setSecurityLevel(security)
self._buffer = QtGui.QImage(width, height, QtGui.QImage.Format_RGB32)
self._path = path
self._timeout = timeout
self._startTimeout = False
self._reactor = reactor
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
"""
@@ -112,7 +130,7 @@ class RDPScreenShotFactory(rdp.ClientFactory):
qp.drawImage(destLeft, destTop, image, 0, 0, destRight - destLeft + 1, destBottom - destTop + 1)
if not self._startTimeout:
self._startTimeout = False
reactor.callLater(self._timeout, self.checkUpdate)
self._reactor.callLater(self._timeout, self.checkUpdate)
def onReady(self):
"""
@@ -130,7 +148,37 @@ class RDPScreenShotFactory(rdp.ClientFactory):
def checkUpdate(self):
self._controller.close();
return ScreenShotObserver(controller, self._width, self._height, self._path, self._timeout)
return ScreenShotObserver(controller, self._width, self._height, self._security, self._path, self._timeout, self._reactor)
def main(width, height, path, timeout, hosts):
"""
@summary: main algorithm
@param height: {integer} height of screenshot
@param width: {integer} width of screenshot
@param timeout: {float} in sec
@param hosts: {list(str(ip[:port]))}
@return: {list(tuple(ip, port, Failure instance)} list of connection state
"""
#create application
app = QtGui.QApplication(sys.argv)
#add qt4 reactor
import qt4reactor
qt4reactor.install()
from twisted.internet import reactor
for host in hosts:
if ':' in host:
ip, port = host.split(':')
else:
ip, port = host, "3389"
reactor.connectTCP(ip, int(port), RDPScreenShotFactory(reactor, app, width, height, path + "%s.jpg"%ip, timeout))
reactor.runReturn()
app.exec_()
return RDPScreenShotFactory.__STATE__
def help():
print "Usage: rdpy-rdpscreenshot [options] ip[:port]"
@@ -163,22 +211,4 @@ if __name__ == '__main__':
elif opt == "-t":
timeout = float(arg)
#create application
app = QtGui.QApplication(sys.argv)
#add qt4 reactor
import qt4reactor
qt4reactor.install()
from twisted.internet import reactor
for arg in args:
if ':' in arg:
ip, port = arg.split(':')
else:
ip, port = arg, "3389"
reactor.connectTCP(ip, int(port), RDPScreenShotFactory(width, height, path + "%s.jpg"%ip, timeout))
reactor.runReturn()
app.exec_()
main(width, height, path, timeout, args)