diff --git a/README.md b/README.md new file mode 100644 index 0000000..e70d45a --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# PyVNCs + +Simple command line multiplatform python VNC Server. + +This is a simple command line VNC server, aimed to quick remote support situations. + +This VNC Server is proven to work on: +- Linux (Xorg, Wayland not supported so far) +- Mac OS +- Windows (7 and onwards) + +Supported encodings: +- raw +- zlib + +TODO: +- Add support for more (efficient) encodings +- VEnCrypt support (WIP) +- Add support for uPnP (for incomming connections on supported nat gateways) + +FAQ: +Q: Why a VNC server on python? +A: Because python is fun! diff --git a/lib/auth/vencrypt.py b/lib/auth/vencrypt.py new file mode 100644 index 0000000..49a8a54 --- /dev/null +++ b/lib/auth/vencrypt.py @@ -0,0 +1,88 @@ +from lib import log +from time import sleep +import ssl +import select +from struct import * + +class VeNCrypt(): + + subtypes = [ + 256, # Plain + #258, # TLSVnc # FIXME: not yet implemented + #259, # TLSPlain # FIXME: not yet implemented + ] + + def __init__(self, sock): + self.getbuff = lambda _: None + self.sock = sock + self.client_subtype = None + self.pem_file = None + log.debug(__name__, "initialized") + + # send version + version = b'\x00\x02' # 0.2 + sock.send(version) + data = sock.recv(2) + if data != version: + sock.send(b'\x01') + sock.close() + raise Exception("unknown vencrypt version") + + sock.send(b'\x00') + + def send_subtypes(self): + # send subtypes + data = pack('!B', len(self.subtypes)) + for i in self.subtypes: + data += pack('!I', i) + log.debug(__name__, "subtype", i) + + self.sock.send(data) + + # get client choosen subtype + data = self.sock.recv(4) + (data,) = unpack('!I', data) + log.debug("client subtype", data) + self.client_subtype = data + + def auth_plain(self, userlist={}): + data = self.sock.recv(8) + user_length, pass_length = unpack('!II', data) + username = self.sock.recv(user_length).decode() + password = self.sock.recv(pass_length).decode() + #log.debug("user", username, password) + + if userlist.get(username) == password: + self.sock.send(pack("!I", 0)) + log.debug(__name__, "Auth OK") + return True + else: + log.debug(__name__, "Invalid auth") + sleep(3) + self.sock.send(pack("!I", 1)) + return False + + def auth_tls_plain(self, userlist={}): + #TODO: implement TLS plain + log.debug(__name__, 'Using TLSPlain') + + self.sock.sendall(pack("!I", 1)) # send ACK + + #data = self.getbuff(30) + #print("data", data) + + + #sslctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + sslctx = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_SERVER) + sslctx.protocol = ssl.PROTOCOL_TLS + #sslctx.load_cert_chain(certfile=self.pem_file, keyfile=self.pem_file) + # this is quite insecure... + sslctx.set_ciphers(":aNULL:kDHE:kEDH:ADH:DH:kECDHE:kEECDH:AECDH:ECDH") + + self.sock.settimeout(30) + self.sock = sslctx.wrap_socket(self.sock, server_side=True) + self.sock.settimeout(None) + + ret = self.auth_plain(userlist=userlist) + return ret + diff --git a/lib/auth/vnc_auth.py b/lib/auth/vnc_auth.py new file mode 100644 index 0000000..543af23 --- /dev/null +++ b/lib/auth/vnc_auth.py @@ -0,0 +1,45 @@ +from time import sleep +from struct import * +from pyDes import * +import os +from lib import log + +class VNCAuth(): + + def __init__(self): + self.getbuff = lambda _: None + + def _mirrorBits(self, key): + newkey = [] + for ki in range(len(key)): + bsrc = key[ki] + btgt = 0 + for i in range(8): + if ord(bsrc) & (1 << i): + btgt = btgt | (1 << 7-i) + newkey.append(btgt) + + return newkey + + def auth(self, sock, password): + # el cliente encripta el challenge con la contraseƱa ingresada como key + pw = (password + '\0' * 8)[:8] + challenge = os.urandom(16) # challenge + sock.send(challenge) # send challenge + # obtener desde el cliente el dato encritado + data = self.getbuff(30) + # la encriptacion de challenge, con pw como key debe dar data + + k = des(self._mirrorBits(pw)) + crypted = k.encrypt(challenge) + + if data == crypted: + # Handshake successful + sock.send(pack("!I", 0)) + log.debug(__name__, "Auth OK") + return True + else: + log.debug(__name__, "Invalid auth") + sleep(3) + sock.send(pack("!I", 1)) + return False diff --git a/lib/clipboardctrl.py b/lib/clipboardctrl.py new file mode 100644 index 0000000..20058d2 --- /dev/null +++ b/lib/clipboardctrl.py @@ -0,0 +1,30 @@ +from struct import * + +class ClipboardController(): + + def __init__(self): + pass + + def client_cut_text(self, sock): + """ + The client has new ISO 8859-1 (Latin-1) text in its cut buffer. + Ends of lines are represented by the linefeed / newline character (value 10) alone. No carriage-return (value 13) is needed. + + No. of bytes Type [Value] Description + 1 U8 6 message-type + 3 padding + 4 U32 length + length U8 array text + """ + + # read padding + _ = sock.recv(3) + + # read length + length = sock.recv(4) + (length, ) = unpack('!I', length) + + # read text + text = sock.recv(length) + + return text \ No newline at end of file diff --git a/lib/common.py b/lib/common.py index 6af18f8..38d1dde 100644 --- a/lib/common.py +++ b/lib/common.py @@ -66,3 +66,9 @@ class proc: def waitproc(self): while psutil.pid_exists(self.pid): time.sleep(.25) + +def reshape(a, cols): + + for i in range(0, int(len(a)/cols), cols): + print(i) + print(a[i:i+cols]) diff --git a/lib/log.py b/lib/log.py index 5c43b42..0358326 100644 --- a/lib/log.py +++ b/lib/log.py @@ -15,14 +15,41 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . +import inspect import logging -logging.basicConfig(level=logging.DEBUG, format='[%(threadName)s] %(message)s') + + +logging.basicConfig(level=logging.DEBUG, format='%(asctime)s: %(message)s') logger = logging.getLogger('pyvncs') +def _log(*args, logtype='debug'): -def debug(*args): - str = "" + func = inspect.stack()[2][3] + if func[0] != '<': + func = "%s():" % func + + _str = func + for s in args: - str = "%s %s" % (str, s) - str = str.strip() - logger.debug(str) + _str = "%s %s" % (_str, s) + _str = _str.strip() + f = getattr(logger, logtype) + #logger.debug(str) + f(_str) + +def __getattr__(name): + + def method(*args): + _str = '' + if args: + for s in args: + _str = "%s %s" % (_str, s) + _str = _str.strip() + + _log(_str, logtype=name) + + return method + + + + diff --git a/lib/rfb_bitmap.py b/lib/rfb_bitmap.py new file mode 100644 index 0000000..3bdd827 --- /dev/null +++ b/lib/rfb_bitmap.py @@ -0,0 +1,109 @@ +import numpy as np +from PIL import Image, ImageChops, ImageDraw, ImagePalette +from lib import bgr233_palette + +__all__ = ['RfbBitmap'] + +class RfbBitmap(): + + def __init__(self): + self.bpp = None + self.depth = None + self.truecolor = None + self.primaryOrder = 'rgb' + self.dither = False + self.red_shift = None + self.green_shift = None + self.blue_shift = None + + def __quantizetopalette(self, silf, palette, dither=False): + """Converts an RGB or L mode image to use a given P image's palette.""" + silf.load() + + # use palette from reference image + palette.load() + if palette.mode != "P": + raise ValueError("bad mode for palette image") + if silf.mode != "RGB" and silf.mode != "L": + raise ValueError( + "only RGB or L mode images can be quantized to a palette" + ) + im = silf.im.convert("P", 1 if dither else 0, palette.im) + # the 0 above means turn OFF dithering + + # Later versions of Pillow (4.x) rename _makeself to _new + try: + return silf._new(im) + except AttributeError: + return silf._makeself(im) + + + def get_bitmap(self, rectangle): + if self.bpp == 32: + redBits = 8 + greenBits = 8 + blueBits = 8 + + # image array + a = np.asarray(rectangle).copy() + + redMask = ((1 << redBits) - 1) << self.red_shift + greenMask = ((1 << greenBits) - 1) << self.green_shift + blueMask = ((1 << blueBits) - 1) << self.blue_shift + a[..., 0] = ( a[..., 0] ) & redMask >> self.red_shift + a[..., 1] = ( a[..., 1] ) & greenMask >> self.green_shift + a[..., 2] = ( a[..., 2] ) & blueMask >> self.blue_shift + + image = Image.fromarray(a) + if self.primaryOrder == "rgb": + (b, g, r) = image.split() + image = Image.merge("RGB", (r, g, b)) + del b,g,r + image = image.convert("RGBX") + return image + + elif self.bpp == 16: #BGR565 + greenBits = 5 + blueBits = 6 + redBits = 5 + image = rectangle + + if self.primaryOrder == "bgr": # FIXME: does not work + (b, g, r) = image.split() + image = Image.merge("RGB", (r, g, b)) + + if self.depth == 16: + image = image.convert('BGR;16') + if self.depth == 15: + image = image.convert('BGR;15') + + return image + + elif self.bpp == 8: #bgr233 + redBits = 3 + greenBits = 3 + blueBits = 2 + image = rectangle + + palette = bgr233_palette.palette + if self.primaryOrder == "rgb": + #(b, g, r) = image.split() + #image = Image.merge("RGB", (r, g, b)) + + palette = np.reshape(palette, (-3,3)) + palette[:,[0, 2]] = palette[:,[2, 0]] + palette = palette.flatten() + palette = list(palette) + + p = Image.new('P',(16,16)) + p.putpalette(palette) + + image = self.__quantizetopalette(image, p, dither=self.dither) + + #image = image.convert('RGB', colors=4).quantize(palette=p) + #log.debug(image) + return image + + else: + # unsupported BPP + return None diff --git a/pyvncs/server.py b/pyvncs/server.py index 7de6298..61ae83a 100644 --- a/pyvncs/server.py +++ b/pyvncs/server.py @@ -30,94 +30,52 @@ import numpy as np from lib import mousectrl from lib import kbdctrl +from lib import clipboardctrl from lib.imagegrab import ImageGrab +from lib.rfb_bitmap import RfbBitmap from lib import log -from lib import bgr233_palette # encodings support import lib.encodings as encs from lib.encodings.common import ENCODINGS -def hexdump(data): - str = "" - for d in data: - str += hex(d) - str += "(%s) " % d - return str - -def quantizetopalette(silf, palette, dither=False): - """Converts an RGB or L mode image to use a given P image's palette.""" - - silf.load() - - # use palette from reference image - palette.load() - if palette.mode != "P": - raise ValueError("bad mode for palette image") - if silf.mode != "RGB" and silf.mode != "L": - raise ValueError( - "only RGB or L mode images can be quantized to a palette" - ) - im = silf.im.convert("P", 1 if dither else 0, palette.im) - # the 0 above means turn OFF dithering - - # Later versions of Pillow (4.x) rename _makeself to _new - try: - return silf._new(im) - except AttributeError: - return silf._makeself(im) +# auth support +from lib.auth.vnc_auth import VNCAuth +from lib.auth.vencrypt import VeNCrypt class VncServer(): - class CONFIG: - _8bitdither = False + class RFB_SECTYPES: + vncauth = 2 # plain VNC auth + vencrypt = 19 # VeNCrypt + unix = 129 # Unix Login Authentication encoding_object = None - def __init__(self, socket, password): + def __init__(self, socket, password=None, auth_type=None, pem_file='', vnc_config = None): self.RFB_VERSION = '003.008' - self.RFB_SECTYPES = [ - 2, # VNC auth - 19 # VeNCrypt - ] self.initmsg = ("RFB %s\n" % self.RFB_VERSION) self.socket = socket self.framebuffer = None self.password = password - self.sectypes = self.RFB_SECTYPES self.cursor_support = False - + self.auth_type = auth_type + self.pem_file = pem_file + self.vnc_config = vnc_config + + log.debug("Configured auth type:", self.auth_type) + + def __del__(self): log.debug("VncServer died") - def encrypt(self, key, data): - k = des(key, ECB, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) - d = k.encrypt(data) - return d - - def decrypt(self, challenge, data): - k = des(challenge, ECB, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) - return k.decrypt(data) - - def mirrorBits(self, key): - newkey = [] - for ki in range(len(key)): - bsrc = key[ki] - btgt = 0 - for i in range(8): - if ord(bsrc) & (1 << i): - btgt = btgt | (1 << 7-i) - newkey.append(btgt) - - return newkey - def sendmessage(self, message): ''' sends a RFB message, usually an error message ''' sock = self.socket message = bytes(message, 'iso8859-1') # 4 bytes lenght and string buff = pack("I%ds" % (len(message),), len(message), message) - sock.send(message) + sock.send(buff) def getbuff(self, timeout): sock = self.socket @@ -149,17 +107,27 @@ class VncServer(): log.debug("client, server:", client_version, server_version) # security types handshake - sendbuff = pack("B", len(self.sectypes)) # number of security types - sendbuff += pack('%sB' % len(self.sectypes), *self.sectypes) # send available sec types + # sectypes = [ + # self.RFB_SECTYPES.vncauth, + # self.RFB_SECTYPES.vencrypt + # ] + + sectypes = [ + self.auth_type + ] + log.debug('sectypes', sectypes) + sendbuff = pack("B", len(sectypes)) # number of security types + sendbuff += pack('%sB' % len(sectypes), *sectypes) # send available sec types sock.send(sendbuff) + # get client choosen security type data = self.getbuff(30) try: sectype = unpack("B", data)[0] except: sectype = None - if sectype not in self.sectypes: + if sectype not in sectypes: log.debug("Incompatible security type: %s" % data) sock.send(pack("B", 1)) # failed handshake self.sendmessage("Incompatible security type") @@ -169,28 +137,57 @@ class VncServer(): log.debug("sec type data: %s" % data) # VNC Auth - if sectype == 2: - # el cliente encripta el challenge con la contraseƱa ingresada como key - pw = (self.password + '\0' * 8)[:8] - challenge = os.urandom(16) # challenge - sock.send(challenge) # send challenge - # obtener desde el cliente el dato encritado - data = self.getbuff(30) - # la encriptacion de challenge, con pw como key debe dar data - - k = des(self.mirrorBits(pw)) - crypted = k.encrypt(challenge) - - if data == crypted: - # Handshake successful - sock.send(pack("I", 0)) - log.debug("Auth OK") - else: - log.debug("Invalid auth") + if sectype == self.RFB_SECTYPES.vncauth: + auth = VNCAuth() + auth.getbuff = self.getbuff + if not auth.auth(sock, self.password): + msg = "Auth failed." + sendbuff = pack("I", len(msg)) + sendbuff += msg.encode() + sock.send(sendbuff) + sock.close() return False + # VeNCrypt + elif sectype == self.RFB_SECTYPES.vencrypt: + userlist = {} + try: + userlist[self.password.split(':')[0]] = self.password.split(':')[1] + except Exception as ex: + log.debug("Unable to parse username:password combination.\n%s" % ex) + sock.close() + return False + + auth = VeNCrypt(sock) + auth.getbuff = self.getbuff + auth.send_subtypes() + client_subtype = auth.client_subtype + + if client_subtype == 256: # Vencrypt Plain auth + if not auth.auth_plain(userlist): + sock.close() + return False + + if client_subtype == 259: # Vencrypt TLSPlain auth + auth.pem_file = self.pem_file + auth.socket = self.socket + if not auth.auth_tls_plain(userlist): + sock.close() + return False + + else: + # unsupported subtype + log.debug("Unsuported client_subtype", client_subtype) + sock.close() + return False + + #elif sectype == self.RFB_SECTYPES.unix: + # log.debug("UNIX!!") + #unsupported VNC auth type else: + log.debug("Unsupported auth type") + sock.close() return False # get ClientInit @@ -216,7 +213,7 @@ class VncServer(): height = size[1] self.height = height bpp = 32 # FIXME: get real bpp - depth = 32 # FIXME: get real depth + depth = 24 # FIXME: get real depth self.depth = depth self.bpp = bpp bigendian = 0 @@ -233,13 +230,14 @@ class VncServer(): self.green_shift = green_shift blue_shift = 0 self.blue_shift = blue_shift + self.rfb_bitmap = RfbBitmap() sendbuff = pack("!HH", width, height) sendbuff += pack("!BBBB", bpp, depth, bigendian, truecolor) sendbuff += pack("!HHHBBB", red_maximum, green_maximum, blue_maximum, red_shift, green_shift, blue_shift) sendbuff += pack("!xxx") # padding - desktop_name = "Test VNC" + desktop_name = self.vnc_config.win_title desktop_name_len = len(desktop_name) sendbuff += pack("!I", desktop_name_len) @@ -261,6 +259,7 @@ class VncServer(): mousecontroller = mousectrl.MouseController() kbdcontroller = kbdctrl.KeyboardController() + clipboardcontroller = clipboardctrl.ClipboardController() self.primaryOrder = "rgb" self.encoding = ENCODINGS.raw @@ -357,6 +356,10 @@ class VncServer(): if data[0] == 5: # PointerEvent mousecontroller.process_event(sock.recv(5, socket.MSG_WAITALL)) continue + + if data[0] == 6: # ClientCutText + text = clipboardcontroller.client_cut_text(sock) + log.debug("ClientCutText:", text) else: data2 = sock.recv(4096) @@ -421,66 +424,17 @@ class VncServer(): sock.settimeout(None) if self.bpp == 32 or self.bpp == 16 or self.bpp == 8: + bitmap = self.rfb_bitmap + bitmap.bpp = self.bpp + bitmap.depth = self.depth + bitmap.dither = self.vnc_config.eightbitdither + bitmap.primaryOrder = self.primaryOrder + bitmap.truecolor = self.truecolor + bitmap.red_shift = self.red_shift + bitmap.green_shift = self.green_shift + bitmap.blue_shift = self.blue_shift - if self.bpp == 32: - redBits = 8 - greenBits = 8 - blueBits = 8 - - # image array - a = np.asarray(rectangle).copy() - - if self.primaryOrder == "bgr": # bit shifting - blueMask = (1 << blueBits) - 1 - greenMask = ((1 << greenBits) - 1) << self.green_shift - redMask = ((1 << redBits) - 1) << self.red_shift - - a[..., 0] = ( a[..., 0] ) & blueMask >> self.blue_shift - a[..., 1] = ( a[..., 1] ) & greenMask >> self.green_shift - a[..., 2] = ( a[..., 2] ) & redMask >> self.red_shift - - else: # RGB - redMask = ((1 << redBits) - 1) << self.red_shift - greenMask = ((1 << greenBits) - 1) << self.green_shift - blueMask = ((1 << blueBits) - 1) << self.blue_shift - a[..., 0] = ( a[..., 0] ) & redMask >> self.red_shift - a[..., 1] = ( a[..., 1] ) & greenMask >> self.green_shift - a[..., 2] = ( a[..., 2] ) & blueMask >> self.blue_shift - - image = Image.fromarray(a) - if self.primaryOrder == "rgb": - (b, g, r) = image.split() - image = Image.merge("RGB", (r, g, b)) - del b,g,r - image = image.convert("RGBX") - - if self.bpp == 16: #BGR565 - greenBits = 5 - blueBits = 6 - redBits = 5 - - image = rectangle - if self.depth == 16: - image = image.convert('BGR;16') - if self.depth == 15: - image = image.convert('BGR;15') - - elif self.bpp == 8: #bgr233 - redBits = 3 - greenBits = 3 - blueBits = 2 - - image = rectangle - - p = Image.new('P',(16,16)) - p.putpalette(bgr233_palette.palette) - - image = quantizetopalette(image, p, dither=self.CONFIG._8bitdither) - - #image = image.convert('RGB', colors=4).quantize(palette=p) - #log.debug(image) - - + image = bitmap.get_bitmap(rectangle) # send image with client defined encoding sendbuff.extend(self.encoding_object.send_image(x, y, w, h, image)) diff --git a/test.py b/test.py deleted file mode 100644 index 52c7033..0000000 --- a/test.py +++ /dev/null @@ -1,16 +0,0 @@ -from lib import common -import time - -if __name__ == '__main__': - #c = ["/bin/ls", "-alh"] - #c = ["/bin/sleep", "5"] - c = ["./test.sh"] - r = common.proc() - r.run(c) - print("PID", r.getpid()) - #print("Waiting...") - #r.waitproc() - time.sleep(1) - r.terminate() - del r - print("DONE") diff --git a/test.sh b/test.sh deleted file mode 100755 index 9c56fd9..0000000 --- a/test.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -for a in $(seq 1 5); do -echo "$a $$" -sleep 1 -done - diff --git a/server.py b/vncserver.py similarity index 53% rename from server.py rename to vncserver.py index 6101922..b4357f8 100755 --- a/server.py +++ b/vncserver.py @@ -7,11 +7,11 @@ from threading import Thread from time import sleep import sys import socket +import ssl import signal from lib import log _debug = log.debug -#_debug = print def signal_handler(signal, frame): _debug("Exiting on %s signal..." % signal) @@ -19,80 +19,85 @@ def signal_handler(signal, frame): signal.signal(signal.SIGINT, signal_handler) - -class ControlThread(Thread): - def __init__(self, threads): - Thread.__init__(self) - self.threads = threads - self.setDaemon(True) - - def run(self): - # elimina los threads muertos - while True: - sleep(1) - for t in threads: - if not t.isAlive(): - _debug("ControlThread removing dead", t) - threads.remove(t) - class ClientThread(Thread): - def __init__(self, sock, ip, port): + def __init__(self, sock, ip, port, vnc_config): Thread.__init__(self) self.ip = ip self.port = port self.sock = sock self.setDaemon(True) + self.vnc_config = vnc_config + def __del__(self): _debug("ClientThread died") def run(self): _debug("[+] New server socket thread started for " + self.ip + ":" + str(self.port)) - #_debug("Thread", self) - server = pyvncs.server.VncServer(self.sock, VNC_PASSWORD) - server.CONFIG._8bitdither = CONFIG._8bitdither + server = pyvncs.server.VncServer(self.sock, + auth_type=self.vnc_config.auth_type, + password=self.vnc_config.vnc_password, + pem_file=self.vnc_config.pem_file, + vnc_config=self.vnc_config + ) + #server.vnc_config.eightbitdither = self.vnc_config.eightbitdither status = server.init() if not status: _debug("Error negotiating client init") return False + server.protocol() def main(argv): - global CONFIG, TCP_IP, TCP_PORT, VNC_PASSWORD, threads, controlthread - class CONFIG: - _8bitdither = False + class vnc_config: + pass parser = ArgumentParser() - parser.add_argument("-l", "--listen-address", dest="TCP_IP", + parser.add_argument("-l", "--listen-address", dest="listen_addr", help="Listen in this address, default: %s" % ("0.0.0.0"), required=False, default='0.0.0.0') - parser.add_argument("-p", "--port", dest="TCP_PORT", + parser.add_argument("-p", "--port", dest="listen_port", help="Listen in this port, default: %s" % ("5901"), type=int, required=False, default='5901') - parser.add_argument("-P", "--password", help="Sets password", required=True, dest="VNC_PASSWORD") + parser.add_argument("-A", "--auth-type", + help="Sets VNC authentication type (supported: 2(vnc), 19(vencrypt))", + required=False, + type=int, + default=2, + dest="auth_type" + ) + parser.add_argument("-C", "--cert-file", + help="SSL PEM file", + required=False, + type=str, + default='', + dest='pem_file' + ) + parser.add_argument("-P", "--vncpassword", help="Sets authentication password", required=True, dest="vnc_password") parser.add_argument("-8", "--8bitdither", help="Enable 8 bit dithering", required=False, action='store_true', dest="dither") - parser.add_argument("-O", "--output-file", help="Redirects all debug output to file", required=False, dest="OUTFILE") + parser.add_argument("-O", "--output-file", help="Redirects all debug output to file", required=False, dest="outfile") + parser.add_argument("-t", "--title", help="VNC Window title", required=False, dest="win_title", default="pyvncs") args = parser.parse_args() - if args.OUTFILE is not None: - fsock = open(args.OUTFILE, 'w') + if args.outfile is not None: + try: + fsock = open(args.outfile, 'w') + except Exception as ex: + print("Error:", ex, file=sys.stderr) + sys.exit(1) sys.stdout = sys.stderr = fsock - # Multithreaded Python server - TCP_IP = '0.0.0.0' if not hasattr(args,"TCP_IP") else args.TCP_IP - TCP_PORT = '5901' if not hasattr(args,"TCP_PORT") else args.TCP_PORT - VNC_PASSWORD = args.VNC_PASSWORD - CONFIG._8bitdither = args.dither + vnc_config.vnc_password = args.vnc_password + vnc_config.eightbitdither = args.dither + vnc_config.auth_type = args.auth_type + vnc_config.pem_file = args.pem_file + vnc_config.win_title = args.win_title sockServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sockServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sockServer.bind((TCP_IP, TCP_PORT)) + sockServer.bind((args.listen_addr, args.listen_port)) - controlthread = ControlThread(threads) - controlthread.start() - threads.append(controlthread) - _debug("Multithreaded Python server : Waiting for connections from TCP clients...") _debug("Runing on:", sys.platform) if sys.platform in ['win32', 'win64']: @@ -111,21 +116,16 @@ def main(argv): while True: sockServer.listen(4) (conn, (ip,port)) = sockServer.accept() - newthread = ClientThread(conn, ip, port) + newthread = ClientThread(sock=conn, ip=ip, port=port, vnc_config=vnc_config) newthread.setDaemon(True) newthread.start() - threads.append(newthread) - #print(threads) if __name__ == "__main__": try: - threads = [] main(sys.argv) except KeyboardInterrupt: # quit _debug("Exiting on ctrl+c...") - #for t in threads: - # _debug("Killing", t) sys.exit() \ No newline at end of file