├── .hgignore ├── README.md ├── inputbox.py ├── pyDes.py ├── rfb.py └── vncviewer.py /.hgignore: -------------------------------------------------------------------------------- 1 | .pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Simple VNC viewer that is built with 2 | [Twisted-Python](https://twistedmatrix.com/trac/) and 3 | [PyGame](http://www.pygame.org/). Originally written by 4 | [Chris Liechti](http://homepage.hispeed.ch/py430/python/). 5 | 6 | The viewer supports the following encodings: 7 | `Hextile, CoRRE, RRE, RAW, CopyRect` 8 | 9 | The display is done using pygame because of it's good graphics 10 | performance, but any GUI system can be used as the code is 11 | modular and can be easily adapted. Two good examples of code 12 | reuse are 13 | [VNC client in browser](http://arkaitzj.wordpress.com/2011/11/12/vnc-in-your-browser-through-websockets-handled-by-gevent/) 14 | by Arkaitz Jimenez, and most recent 15 | [vncdotool](https://github.com/sibson/vncdotool) by Marc Sibson. 16 | Pygame version supports clipboard transfer, but it's not used in 17 | the sample application. 18 | 19 | Usage 20 | ----- 21 | You can simply start `vncviewer.py` and it will ask for a hostname 22 | and password if required. Hostnames are in the "host:display" 23 | form, where "display" is the VNC dispaly number. 24 | 25 | These settings can be passed through command line, but note 26 | it's a bad idea to pass the password in this way as it can be 27 | snooped by other users with a simple process listing! 28 | Try `-h` or `--help` to see supported options. 29 | 30 | Please keep in mind that VNC transimts keypresses in cleartext, 31 | so don't type in passwords on non-encrypted connection over 32 | insecure networks. 33 | 34 | With "--depth" a display depth can be gived, use "8" for 35 | slower connections. 36 | 37 | The "--fast" option uses only `RAW` and `CopyRect` encodings 38 | and is thus only suitable for fast connections. But it delivers 39 | better performance than other encodings. 40 | 41 | What is it good for? 42 | -------------------- 43 | Nothing ;-) Use the original VNC viewer for better performance. 44 | 45 | However it works very well and I think with a good speed. 46 | It could be embedded in other Python applications or it can 47 | server as a base of various supervision or remote desktop 48 | applications, automated tests of GUIs or be embedded in a tool 49 | for remote support... 50 | 51 | Bugs, etc 52 | --------- 53 | - Properties dialog is missing. Like for specifying encodings etc. 54 | - Listen mode not implemented. 55 | - Key repetition does not work (pygame?) 56 | - It does not reduce update requests when minimized. 57 | - Screen cannot be scolled, impractical if remote desktop is larger 58 | than local 59 | - The password dialog blocks twisted and everthing else 60 | 61 | References: 62 | ----------- 63 | - http://homepage.hispeed.ch/py430/python/ 64 | - http://code.google.com/p/vnc2flv/ 65 | - http://arkaitzj.wordpress.com/2011/11/12/vnc-in-your-browser-through-websockets-handled-by-gevent/ 66 | - http://sibson.github.io/vncdotool/ 67 | - http://www.python.org 68 | - http://twistedmatrix.com/ 69 | - http://www.pygame.org 70 | - http://www.realvnc.org 71 | 72 | ------- 73 | - (c) 2003 chris 74 | - (c) 2009 techtonik 75 | 76 | Released under the MIT License. 77 | 78 | You're free to use it for commercial and noncommercial 79 | application, modify and redistribute it as long as the 80 | copyright notices are intact. There are no warranties, not 81 | even that it does what it says to do ;-) 82 | 83 | 84 | Changes: 85 | -------- 86 | 2015.08.29 - expored to Github 87 | 2009.12.14 - 4. another update 88 | * replaced crippled_des.py with pyDes 89 | * TAB and BACKSPACE keys now work 90 | 2009.12.3 - 3. update 91 | * changed license to MIT with Chris consent as Python license 92 | is not supported by Google Code 93 | * works with twisted 8.2.0 94 | * works with pygame 1.9.1 (blit failed on locked surfaces) 95 | * don't refuse to connect to 3.7 and 3.8 VNC servers 96 | 2003.3.4 - 2. release 97 | * improved performance with RRE, CoRRE 98 | * color depth can be choosen (32, 8) 99 | * added "fast" option 100 | 2003.3.3 - 1. public release 101 | -------------------------------------------------------------------------------- /inputbox.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple text input box. 3 | 4 | havily based on inputbox.py from Timothy Downs 5 | added 3d box style, escape -> abort function 6 | changed event handling so that it uses the unicode 7 | attribute -> handle localized keyboard 8 | 9 | 10 | """ 11 | 12 | import pygame, pygame.font, pygame.event, pygame.draw, string 13 | from pygame.locals import * 14 | 15 | def get_key(): 16 | while 1: 17 | event = pygame.event.poll() 18 | if event.type == KEYDOWN: 19 | return event.unicode and ord(event.unicode) or event.key 20 | elif event.type == QUIT: 21 | return K_ESCAPE 22 | else: 23 | pass 24 | 25 | def display_box(screen, message): 26 | "Print a message in a box in the middle of the screen" 27 | fontobject = pygame.font.Font(None,18) 28 | #ease bg 29 | pygame.draw.rect(screen, (0,0,0), 30 | ((screen.get_width() / 2) - 102, 31 | (screen.get_height() / 2) - 10, 32 | 202,20), 0) 33 | #draw border 34 | pygame.draw.rect(screen, (255,255,255), 35 | ((screen.get_width() / 2) - 104, 36 | (screen.get_height() / 2) - 12, 37 | 204,24), 2) 38 | #3d appearance 39 | x = (screen.get_width() / 2) - 104 40 | y = (screen.get_height() / 2) - 12 41 | pygame.draw.line(screen, (155,155,155), 42 | ((screen.get_width() / 2) - 104, 43 | y+24), 44 | (x+204,y+24), 2) 45 | pygame.draw.line(screen, (100,100,100), 46 | (x+204, 47 | (screen.get_height() / 2) - 12), 48 | (x+204,y+24), 2) 49 | if len(message) != 0: 50 | screen.blit(fontobject.render(message, 1, (255,255,255)), 51 | ((screen.get_width() / 2) - 100, (screen.get_height() / 2) - 8)) 52 | pygame.display.flip() 53 | 54 | def ask(screen, question, default='', password=0): 55 | "ask(screen, question) -> answer" 56 | pygame.font.init() 57 | current_string = list(default) 58 | display_box(screen, question + ": " + string.join(current_string,"")) 59 | while 1: 60 | inkey = get_key() 61 | if inkey == K_BACKSPACE: 62 | current_string = current_string[0:-1] 63 | elif inkey == K_RETURN: 64 | break 65 | elif inkey == K_ESCAPE: 66 | return default 67 | #~ elif inkey == K_MINUS: 68 | #~ current_string.append("_") 69 | elif inkey <= 127: 70 | current_string.append(chr(inkey)) 71 | if password: 72 | display_box(screen, question + ": " + "*"*len(current_string)) 73 | else: 74 | display_box(screen, question + ": " + string.join(current_string,"")) 75 | return string.join(current_string,"") 76 | 77 | def main(): 78 | screen = pygame.display.set_mode((220,40)) 79 | screen.fill((0,100,255)) 80 | print ask(screen, "Name") + " was entered" 81 | 82 | if __name__ == '__main__': main() 83 | -------------------------------------------------------------------------------- /pyDes.py: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # Documentation # 3 | ############################################################################# 4 | 5 | # Author: Todd Whiteman 6 | # Date: 16th March, 2009 7 | # Verion: 2.0.0 8 | # License: Public Domain - free to do as you wish 9 | # Homepage: http://twhiteman.netfirms.com/des.html 10 | # 11 | # This is a pure python implementation of the DES encryption algorithm. 12 | # It's pure python to avoid portability issues, since most DES 13 | # implementations are programmed in C (for performance reasons). 14 | # 15 | # Triple DES class is also implemented, utilising the DES base. Triple DES 16 | # is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key. 17 | # 18 | # See the README.txt that should come with this python module for the 19 | # implementation methods used. 20 | # 21 | # Thanks to: 22 | # * David Broadwell for ideas, comments and suggestions. 23 | # * Mario Wolff for pointing out and debugging some triple des CBC errors. 24 | # * Santiago Palladino for providing the PKCS5 padding technique. 25 | # * Shaya for correcting the PAD_PKCS5 triple des CBC errors. 26 | # 27 | """A pure python implementation of the DES and TRIPLE DES encryption algorithms. 28 | 29 | Class initialization 30 | -------------------- 31 | pyDes.des(key, [mode], [IV], [pad], [padmode]) 32 | pyDes.triple_des(key, [mode], [IV], [pad], [padmode]) 33 | 34 | key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes 35 | for Triple DES 36 | mode -> Optional argument for encryption type, can be either 37 | pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining) 38 | IV -> Optional Initial Value bytes, must be supplied if using CBC mode. 39 | Length must be 8 bytes. 40 | pad -> Optional argument, set the pad character (PAD_NORMAL) to use during 41 | all encrypt/decrpt operations done with this instance. 42 | padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) 43 | to use during all encrypt/decrpt operations done with this instance. 44 | 45 | I recommend to use PAD_PKCS5 padding, as then you never need to worry about any 46 | padding issues, as the padding can be removed unambiguously upon decrypting 47 | data that was encrypted using PAD_PKCS5 padmode. 48 | 49 | Common methods 50 | -------------- 51 | encrypt(data, [pad], [padmode]) 52 | decrypt(data, [pad], [padmode]) 53 | 54 | data -> Bytes to be encrypted/decrypted 55 | pad -> Optional argument. Only when using padmode of PAD_NORMAL. For 56 | encryption, adds this characters to the end of the data block when 57 | data is not a multiple of 8 bytes. For decryption, will remove the 58 | trailing characters that match this pad character from the last 8 59 | bytes of the unencrypted data block. 60 | padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL 61 | or PAD_PKCS5). Defaults to PAD_NORMAL. 62 | 63 | 64 | Example 65 | ------- 66 | from pyDes import * 67 | 68 | data = "Please encrypt my data" 69 | k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) 70 | # For Python3, you'll need to use bytes, i.e.: 71 | # data = b"Please encrypt my data" 72 | # k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) 73 | d = k.encrypt(data) 74 | print "Encrypted: %r" % d 75 | print "Decrypted: %r" % k.decrypt(d) 76 | assert k.decrypt(d, padmode=PAD_PKCS5) == data 77 | 78 | 79 | See the module source (pyDes.py) for more examples of use. 80 | You can also run the pyDes.py file without and arguments to see a simple test. 81 | 82 | Note: This code was not written for high-end systems needing a fast 83 | implementation, but rather a handy portable solution with small usage. 84 | 85 | """ 86 | 87 | import sys 88 | 89 | # _pythonMajorVersion is used to handle Python2 and Python3 differences. 90 | _pythonMajorVersion = sys.version_info[0] 91 | 92 | # Modes of crypting / cyphering 93 | ECB = 0 94 | CBC = 1 95 | 96 | # Modes of padding 97 | PAD_NORMAL = 1 98 | PAD_PKCS5 = 2 99 | 100 | # PAD_PKCS5: is a method that will unambiguously remove all padding 101 | # characters after decryption, when originally encrypted with 102 | # this padding mode. 103 | # For a good description of the PKCS5 padding technique, see: 104 | # http://www.faqs.org/rfcs/rfc1423.html 105 | 106 | # The base class shared by des and triple des. 107 | class _baseDes(object): 108 | def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): 109 | if IV: 110 | IV = self._guardAgainstUnicode(IV) 111 | if pad: 112 | pad = self._guardAgainstUnicode(pad) 113 | self.block_size = 8 114 | # Sanity checking of arguments. 115 | if pad and padmode == PAD_PKCS5: 116 | raise ValueError("Cannot use a pad character with PAD_PKCS5") 117 | if IV and len(IV) != self.block_size: 118 | raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") 119 | 120 | # Set the passed in variables 121 | self._mode = mode 122 | self._iv = IV 123 | self._padding = pad 124 | self._padmode = padmode 125 | 126 | def getKey(self): 127 | """getKey() -> bytes""" 128 | return self.__key 129 | 130 | def setKey(self, key): 131 | """Will set the crypting key for this object.""" 132 | key = self._guardAgainstUnicode(key) 133 | self.__key = key 134 | 135 | def getMode(self): 136 | """getMode() -> pyDes.ECB or pyDes.CBC""" 137 | return self._mode 138 | 139 | def setMode(self, mode): 140 | """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" 141 | self._mode = mode 142 | 143 | def getPadding(self): 144 | """getPadding() -> bytes of length 1. Padding character.""" 145 | return self._padding 146 | 147 | def setPadding(self, pad): 148 | """setPadding() -> bytes of length 1. Padding character.""" 149 | if pad is not None: 150 | pad = self._guardAgainstUnicode(pad) 151 | self._padding = pad 152 | 153 | def getPadMode(self): 154 | """getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" 155 | return self._padmode 156 | 157 | def setPadMode(self, mode): 158 | """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" 159 | self._padmode = mode 160 | 161 | def getIV(self): 162 | """getIV() -> bytes""" 163 | return self._iv 164 | 165 | def setIV(self, IV): 166 | """Will set the Initial Value, used in conjunction with CBC mode""" 167 | if not IV or len(IV) != self.block_size: 168 | raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") 169 | IV = self._guardAgainstUnicode(IV) 170 | self._iv = IV 171 | 172 | def _padData(self, data, pad, padmode): 173 | # Pad data depending on the mode 174 | if padmode is None: 175 | # Get the default padding mode. 176 | padmode = self.getPadMode() 177 | if pad and padmode == PAD_PKCS5: 178 | raise ValueError("Cannot use a pad character with PAD_PKCS5") 179 | 180 | if padmode == PAD_NORMAL: 181 | if len(data) % self.block_size == 0: 182 | # No padding required. 183 | return data 184 | 185 | if not pad: 186 | # Get the default padding. 187 | pad = self.getPadding() 188 | if not pad: 189 | raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.") 190 | data += (self.block_size - (len(data) % self.block_size)) * pad 191 | 192 | elif padmode == PAD_PKCS5: 193 | pad_len = 8 - (len(data) % self.block_size) 194 | if _pythonMajorVersion < 3: 195 | data += pad_len * chr(pad_len) 196 | else: 197 | data += bytes([pad_len] * pad_len) 198 | 199 | return data 200 | 201 | def _unpadData(self, data, pad, padmode): 202 | # Unpad data depending on the mode. 203 | if not data: 204 | return data 205 | if pad and padmode == PAD_PKCS5: 206 | raise ValueError("Cannot use a pad character with PAD_PKCS5") 207 | if padmode is None: 208 | # Get the default padding mode. 209 | padmode = self.getPadMode() 210 | 211 | if padmode == PAD_NORMAL: 212 | if not pad: 213 | # Get the default padding. 214 | pad = self.getPadding() 215 | if pad: 216 | data = data[:-self.block_size] + \ 217 | data[-self.block_size:].rstrip(pad) 218 | 219 | elif padmode == PAD_PKCS5: 220 | if _pythonMajorVersion < 3: 221 | pad_len = ord(data[-1]) 222 | else: 223 | pad_len = data[-1] 224 | data = data[:-pad_len] 225 | 226 | return data 227 | 228 | def _guardAgainstUnicode(self, data): 229 | # Only accept byte strings or ascii unicode values, otherwise 230 | # there is no way to correctly decode the data into bytes. 231 | if _pythonMajorVersion < 3: 232 | if isinstance(data, unicode): 233 | raise ValueError("pyDes can only work with bytes, not Unicode strings.") 234 | else: 235 | if isinstance(data, str): 236 | # Only accept ascii unicode values. 237 | try: 238 | return data.encode('ascii') 239 | except UnicodeEncodeError: 240 | pass 241 | raise ValueError("pyDes can only work with encoded strings, not Unicode.") 242 | return data 243 | 244 | ############################################################################# 245 | # DES # 246 | ############################################################################# 247 | class des(_baseDes): 248 | """DES encryption/decrytpion class 249 | 250 | Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. 251 | 252 | pyDes.des(key,[mode], [IV]) 253 | 254 | key -> Bytes containing the encryption key, must be exactly 8 bytes 255 | mode -> Optional argument for encryption type, can be either pyDes.ECB 256 | (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) 257 | IV -> Optional Initial Value bytes, must be supplied if using CBC mode. 258 | Must be 8 bytes in length. 259 | pad -> Optional argument, set the pad character (PAD_NORMAL) to use 260 | during all encrypt/decrpt operations done with this instance. 261 | padmode -> Optional argument, set the padding mode (PAD_NORMAL or 262 | PAD_PKCS5) to use during all encrypt/decrpt operations done 263 | with this instance. 264 | """ 265 | 266 | 267 | # Permutation and translation tables for DES 268 | __pc1 = [56, 48, 40, 32, 24, 16, 8, 269 | 0, 57, 49, 41, 33, 25, 17, 270 | 9, 1, 58, 50, 42, 34, 26, 271 | 18, 10, 2, 59, 51, 43, 35, 272 | 62, 54, 46, 38, 30, 22, 14, 273 | 6, 61, 53, 45, 37, 29, 21, 274 | 13, 5, 60, 52, 44, 36, 28, 275 | 20, 12, 4, 27, 19, 11, 3 276 | ] 277 | 278 | # number left rotations of pc1 279 | __left_rotations = [ 280 | 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 281 | ] 282 | 283 | # permuted choice key (table 2) 284 | __pc2 = [ 285 | 13, 16, 10, 23, 0, 4, 286 | 2, 27, 14, 5, 20, 9, 287 | 22, 18, 11, 3, 25, 7, 288 | 15, 6, 26, 19, 12, 1, 289 | 40, 51, 30, 36, 46, 54, 290 | 29, 39, 50, 44, 32, 47, 291 | 43, 48, 38, 55, 33, 52, 292 | 45, 41, 49, 35, 28, 31 293 | ] 294 | 295 | # initial permutation IP 296 | __ip = [57, 49, 41, 33, 25, 17, 9, 1, 297 | 59, 51, 43, 35, 27, 19, 11, 3, 298 | 61, 53, 45, 37, 29, 21, 13, 5, 299 | 63, 55, 47, 39, 31, 23, 15, 7, 300 | 56, 48, 40, 32, 24, 16, 8, 0, 301 | 58, 50, 42, 34, 26, 18, 10, 2, 302 | 60, 52, 44, 36, 28, 20, 12, 4, 303 | 62, 54, 46, 38, 30, 22, 14, 6 304 | ] 305 | 306 | # Expansion table for turning 32 bit blocks into 48 bits 307 | __expansion_table = [ 308 | 31, 0, 1, 2, 3, 4, 309 | 3, 4, 5, 6, 7, 8, 310 | 7, 8, 9, 10, 11, 12, 311 | 11, 12, 13, 14, 15, 16, 312 | 15, 16, 17, 18, 19, 20, 313 | 19, 20, 21, 22, 23, 24, 314 | 23, 24, 25, 26, 27, 28, 315 | 27, 28, 29, 30, 31, 0 316 | ] 317 | 318 | # The (in)famous S-boxes 319 | __sbox = [ 320 | # S1 321 | [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 322 | 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 323 | 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 324 | 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13], 325 | 326 | # S2 327 | [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 328 | 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 329 | 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 330 | 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9], 331 | 332 | # S3 333 | [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 334 | 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 335 | 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 336 | 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12], 337 | 338 | # S4 339 | [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 340 | 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 341 | 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, 342 | 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14], 343 | 344 | # S5 345 | [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 346 | 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 347 | 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, 348 | 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3], 349 | 350 | # S6 351 | [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 352 | 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 353 | 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 354 | 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13], 355 | 356 | # S7 357 | [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 358 | 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 359 | 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 360 | 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12], 361 | 362 | # S8 363 | [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 364 | 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 365 | 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 366 | 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], 367 | ] 368 | 369 | 370 | # 32-bit permutation function P used on the output of the S-boxes 371 | __p = [ 372 | 15, 6, 19, 20, 28, 11, 373 | 27, 16, 0, 14, 22, 25, 374 | 4, 17, 30, 9, 1, 7, 375 | 23,13, 31, 26, 2, 8, 376 | 18, 12, 29, 5, 21, 10, 377 | 3, 24 378 | ] 379 | 380 | # final permutation IP^-1 381 | __fp = [ 382 | 39, 7, 47, 15, 55, 23, 63, 31, 383 | 38, 6, 46, 14, 54, 22, 62, 30, 384 | 37, 5, 45, 13, 53, 21, 61, 29, 385 | 36, 4, 44, 12, 52, 20, 60, 28, 386 | 35, 3, 43, 11, 51, 19, 59, 27, 387 | 34, 2, 42, 10, 50, 18, 58, 26, 388 | 33, 1, 41, 9, 49, 17, 57, 25, 389 | 32, 0, 40, 8, 48, 16, 56, 24 390 | ] 391 | 392 | # Type of crypting being done 393 | ENCRYPT = 0x00 394 | DECRYPT = 0x01 395 | 396 | # Initialisation 397 | def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): 398 | # Sanity checking of arguments. 399 | if len(key) != 8: 400 | raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.") 401 | _baseDes.__init__(self, mode, IV, pad, padmode) 402 | self.key_size = 8 403 | 404 | self.L = [] 405 | self.R = [] 406 | self.Kn = [ [0] * 48 ] * 16 # 16 48-bit keys (K1 - K16) 407 | self.final = [] 408 | 409 | self.setKey(key) 410 | 411 | def setKey(self, key): 412 | """Will set the crypting key for this object. Must be 8 bytes.""" 413 | _baseDes.setKey(self, key) 414 | self.__create_sub_keys() 415 | 416 | def __String_to_BitList(self, data): 417 | """Turn the string data, into a list of bits (1, 0)'s""" 418 | if _pythonMajorVersion < 3: 419 | # Turn the strings into integers. Python 3 uses a bytes 420 | # class, which already has this behaviour. 421 | data = [ord(c) for c in data] 422 | l = len(data) * 8 423 | result = [0] * l 424 | pos = 0 425 | for ch in data: 426 | i = 7 427 | while i >= 0: 428 | if ch & (1 << i) != 0: 429 | result[pos] = 1 430 | else: 431 | result[pos] = 0 432 | pos += 1 433 | i -= 1 434 | 435 | return result 436 | 437 | def __BitList_to_String(self, data): 438 | """Turn the list of bits -> data, into a string""" 439 | result = [] 440 | pos = 0 441 | c = 0 442 | while pos < len(data): 443 | c += data[pos] << (7 - (pos % 8)) 444 | if (pos % 8) == 7: 445 | result.append(c) 446 | c = 0 447 | pos += 1 448 | 449 | if _pythonMajorVersion < 3: 450 | return ''.join([ chr(c) for c in result ]) 451 | else: 452 | return bytes(result) 453 | 454 | def __permutate(self, table, block): 455 | """Permutate this block with the specified table""" 456 | return list(map(lambda x: block[x], table)) 457 | 458 | # Transform the secret key, so that it is ready for data processing 459 | # Create the 16 subkeys, K[1] - K[16] 460 | def __create_sub_keys(self): 461 | """Create the 16 subkeys K[1] to K[16] from the given key""" 462 | key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey())) 463 | i = 0 464 | # Split into Left and Right sections 465 | self.L = key[:28] 466 | self.R = key[28:] 467 | while i < 16: 468 | j = 0 469 | # Perform circular left shifts 470 | while j < des.__left_rotations[i]: 471 | self.L.append(self.L[0]) 472 | del self.L[0] 473 | 474 | self.R.append(self.R[0]) 475 | del self.R[0] 476 | 477 | j += 1 478 | 479 | # Create one of the 16 subkeys through pc2 permutation 480 | self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R) 481 | 482 | i += 1 483 | 484 | # Main part of the encryption algorithm, the number cruncher :) 485 | def __des_crypt(self, block, crypt_type): 486 | """Crypt the block of data through DES bit-manipulation""" 487 | block = self.__permutate(des.__ip, block) 488 | self.L = block[:32] 489 | self.R = block[32:] 490 | 491 | # Encryption starts from Kn[1] through to Kn[16] 492 | if crypt_type == des.ENCRYPT: 493 | iteration = 0 494 | iteration_adjustment = 1 495 | # Decryption starts from Kn[16] down to Kn[1] 496 | else: 497 | iteration = 15 498 | iteration_adjustment = -1 499 | 500 | i = 0 501 | while i < 16: 502 | # Make a copy of R[i-1], this will later become L[i] 503 | tempR = self.R[:] 504 | 505 | # Permutate R[i - 1] to start creating R[i] 506 | self.R = self.__permutate(des.__expansion_table, self.R) 507 | 508 | # Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here 509 | self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration])) 510 | B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]] 511 | # Optimization: Replaced below commented code with above 512 | #j = 0 513 | #B = [] 514 | #while j < len(self.R): 515 | # self.R[j] = self.R[j] ^ self.Kn[iteration][j] 516 | # j += 1 517 | # if j % 6 == 0: 518 | # B.append(self.R[j-6:j]) 519 | 520 | # Permutate B[1] to B[8] using the S-Boxes 521 | j = 0 522 | Bn = [0] * 32 523 | pos = 0 524 | while j < 8: 525 | # Work out the offsets 526 | m = (B[j][0] << 1) + B[j][5] 527 | n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4] 528 | 529 | # Find the permutation value 530 | v = des.__sbox[j][(m << 4) + n] 531 | 532 | # Turn value into bits, add it to result: Bn 533 | Bn[pos] = (v & 8) >> 3 534 | Bn[pos + 1] = (v & 4) >> 2 535 | Bn[pos + 2] = (v & 2) >> 1 536 | Bn[pos + 3] = v & 1 537 | 538 | pos += 4 539 | j += 1 540 | 541 | # Permutate the concatination of B[1] to B[8] (Bn) 542 | self.R = self.__permutate(des.__p, Bn) 543 | 544 | # Xor with L[i - 1] 545 | self.R = list(map(lambda x, y: x ^ y, self.R, self.L)) 546 | # Optimization: This now replaces the below commented code 547 | #j = 0 548 | #while j < len(self.R): 549 | # self.R[j] = self.R[j] ^ self.L[j] 550 | # j += 1 551 | 552 | # L[i] becomes R[i - 1] 553 | self.L = tempR 554 | 555 | i += 1 556 | iteration += iteration_adjustment 557 | 558 | # Final permutation of R[16]L[16] 559 | self.final = self.__permutate(des.__fp, self.R + self.L) 560 | return self.final 561 | 562 | 563 | # Data to be encrypted/decrypted 564 | def crypt(self, data, crypt_type): 565 | """Crypt the data in blocks, running it through des_crypt()""" 566 | 567 | # Error check the data 568 | if not data: 569 | return '' 570 | if len(data) % self.block_size != 0: 571 | if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks 572 | raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.") 573 | if not self.getPadding(): 574 | raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character") 575 | else: 576 | data += (self.block_size - (len(data) % self.block_size)) * self.getPadding() 577 | # print "Len of data: %f" % (len(data) / self.block_size) 578 | 579 | if self.getMode() == CBC: 580 | if self.getIV(): 581 | iv = self.__String_to_BitList(self.getIV()) 582 | else: 583 | raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering") 584 | 585 | # Split the data into blocks, crypting each one seperately 586 | i = 0 587 | dict = {} 588 | result = [] 589 | #cached = 0 590 | #lines = 0 591 | while i < len(data): 592 | # Test code for caching encryption results 593 | #lines += 1 594 | #if dict.has_key(data[i:i+8]): 595 | #print "Cached result for: %s" % data[i:i+8] 596 | # cached += 1 597 | # result.append(dict[data[i:i+8]]) 598 | # i += 8 599 | # continue 600 | 601 | block = self.__String_to_BitList(data[i:i+8]) 602 | 603 | # Xor with IV if using CBC mode 604 | if self.getMode() == CBC: 605 | if crypt_type == des.ENCRYPT: 606 | block = list(map(lambda x, y: x ^ y, block, iv)) 607 | #j = 0 608 | #while j < len(block): 609 | # block[j] = block[j] ^ iv[j] 610 | # j += 1 611 | 612 | processed_block = self.__des_crypt(block, crypt_type) 613 | 614 | if crypt_type == des.DECRYPT: 615 | processed_block = list(map(lambda x, y: x ^ y, processed_block, iv)) 616 | #j = 0 617 | #while j < len(processed_block): 618 | # processed_block[j] = processed_block[j] ^ iv[j] 619 | # j += 1 620 | iv = block 621 | else: 622 | iv = processed_block 623 | else: 624 | processed_block = self.__des_crypt(block, crypt_type) 625 | 626 | 627 | # Add the resulting crypted block to our list 628 | #d = self.__BitList_to_String(processed_block) 629 | #result.append(d) 630 | result.append(self.__BitList_to_String(processed_block)) 631 | #dict[data[i:i+8]] = d 632 | i += 8 633 | 634 | # print "Lines: %d, cached: %d" % (lines, cached) 635 | 636 | # Return the full crypted string 637 | if _pythonMajorVersion < 3: 638 | return ''.join(result) 639 | else: 640 | return bytes.fromhex('').join(result) 641 | 642 | def encrypt(self, data, pad=None, padmode=None): 643 | """encrypt(data, [pad], [padmode]) -> bytes 644 | 645 | data : Bytes to be encrypted 646 | pad : Optional argument for encryption padding. Must only be one byte 647 | padmode : Optional argument for overriding the padding mode. 648 | 649 | The data must be a multiple of 8 bytes and will be encrypted 650 | with the already specified key. Data does not have to be a 651 | multiple of 8 bytes if the padding character is supplied, or 652 | the padmode is set to PAD_PKCS5, as bytes will then added to 653 | ensure the be padded data is a multiple of 8 bytes. 654 | """ 655 | data = self._guardAgainstUnicode(data) 656 | if pad is not None: 657 | pad = self._guardAgainstUnicode(pad) 658 | data = self._padData(data, pad, padmode) 659 | return self.crypt(data, des.ENCRYPT) 660 | 661 | def decrypt(self, data, pad=None, padmode=None): 662 | """decrypt(data, [pad], [padmode]) -> bytes 663 | 664 | data : Bytes to be encrypted 665 | pad : Optional argument for decryption padding. Must only be one byte 666 | padmode : Optional argument for overriding the padding mode. 667 | 668 | The data must be a multiple of 8 bytes and will be decrypted 669 | with the already specified key. In PAD_NORMAL mode, if the 670 | optional padding character is supplied, then the un-encrypted 671 | data will have the padding characters removed from the end of 672 | the bytes. This pad removal only occurs on the last 8 bytes of 673 | the data (last data block). In PAD_PKCS5 mode, the special 674 | padding end markers will be removed from the data after decrypting. 675 | """ 676 | data = self._guardAgainstUnicode(data) 677 | if pad is not None: 678 | pad = self._guardAgainstUnicode(pad) 679 | data = self.crypt(data, des.DECRYPT) 680 | return self._unpadData(data, pad, padmode) 681 | 682 | 683 | 684 | ############################################################################# 685 | # Triple DES # 686 | ############################################################################# 687 | class triple_des(_baseDes): 688 | """Triple DES encryption/decrytpion class 689 | 690 | This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or 691 | the DES-EDE2 (when a 16 byte key is supplied) encryption methods. 692 | Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. 693 | 694 | pyDes.des(key, [mode], [IV]) 695 | 696 | key -> Bytes containing the encryption key, must be either 16 or 697 | 24 bytes long 698 | mode -> Optional argument for encryption type, can be either pyDes.ECB 699 | (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) 700 | IV -> Optional Initial Value bytes, must be supplied if using CBC mode. 701 | Must be 8 bytes in length. 702 | pad -> Optional argument, set the pad character (PAD_NORMAL) to use 703 | during all encrypt/decrpt operations done with this instance. 704 | padmode -> Optional argument, set the padding mode (PAD_NORMAL or 705 | PAD_PKCS5) to use during all encrypt/decrpt operations done 706 | with this instance. 707 | """ 708 | def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): 709 | _baseDes.__init__(self, mode, IV, pad, padmode) 710 | self.setKey(key) 711 | 712 | def setKey(self, key): 713 | """Will set the crypting key for this object. Either 16 or 24 bytes long.""" 714 | self.key_size = 24 # Use DES-EDE3 mode 715 | if len(key) != self.key_size: 716 | if len(key) == 16: # Use DES-EDE2 mode 717 | self.key_size = 16 718 | else: 719 | raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long") 720 | if self.getMode() == CBC: 721 | if not self.getIV(): 722 | # Use the first 8 bytes of the key 723 | self._iv = key[:self.block_size] 724 | if len(self.getIV()) != self.block_size: 725 | raise ValueError("Invalid IV, must be 8 bytes in length") 726 | self.__key1 = des(key[:8], self._mode, self._iv, 727 | self._padding, self._padmode) 728 | self.__key2 = des(key[8:16], self._mode, self._iv, 729 | self._padding, self._padmode) 730 | if self.key_size == 16: 731 | self.__key3 = self.__key1 732 | else: 733 | self.__key3 = des(key[16:], self._mode, self._iv, 734 | self._padding, self._padmode) 735 | _baseDes.setKey(self, key) 736 | 737 | # Override setter methods to work on all 3 keys. 738 | 739 | def setMode(self, mode): 740 | """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" 741 | _baseDes.setMode(self, mode) 742 | for key in (self.__key1, self.__key2, self.__key3): 743 | key.setMode(mode) 744 | 745 | def setPadding(self, pad): 746 | """setPadding() -> bytes of length 1. Padding character.""" 747 | _baseDes.setPadding(self, pad) 748 | for key in (self.__key1, self.__key2, self.__key3): 749 | key.setPadding(pad) 750 | 751 | def setPadMode(self, mode): 752 | """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" 753 | _baseDes.setPadMode(self, mode) 754 | for key in (self.__key1, self.__key2, self.__key3): 755 | key.setPadMode(mode) 756 | 757 | def setIV(self, IV): 758 | """Will set the Initial Value, used in conjunction with CBC mode""" 759 | _baseDes.setIV(self, IV) 760 | for key in (self.__key1, self.__key2, self.__key3): 761 | key.setIV(IV) 762 | 763 | def encrypt(self, data, pad=None, padmode=None): 764 | """encrypt(data, [pad], [padmode]) -> bytes 765 | 766 | data : bytes to be encrypted 767 | pad : Optional argument for encryption padding. Must only be one byte 768 | padmode : Optional argument for overriding the padding mode. 769 | 770 | The data must be a multiple of 8 bytes and will be encrypted 771 | with the already specified key. Data does not have to be a 772 | multiple of 8 bytes if the padding character is supplied, or 773 | the padmode is set to PAD_PKCS5, as bytes will then added to 774 | ensure the be padded data is a multiple of 8 bytes. 775 | """ 776 | ENCRYPT = des.ENCRYPT 777 | DECRYPT = des.DECRYPT 778 | data = self._guardAgainstUnicode(data) 779 | if pad is not None: 780 | pad = self._guardAgainstUnicode(pad) 781 | # Pad the data accordingly. 782 | data = self._padData(data, pad, padmode) 783 | if self.getMode() == CBC: 784 | self.__key1.setIV(self.getIV()) 785 | self.__key2.setIV(self.getIV()) 786 | self.__key3.setIV(self.getIV()) 787 | i = 0 788 | result = [] 789 | while i < len(data): 790 | block = self.__key1.crypt(data[i:i+8], ENCRYPT) 791 | block = self.__key2.crypt(block, DECRYPT) 792 | block = self.__key3.crypt(block, ENCRYPT) 793 | self.__key1.setIV(block) 794 | self.__key2.setIV(block) 795 | self.__key3.setIV(block) 796 | result.append(block) 797 | i += 8 798 | if _pythonMajorVersion < 3: 799 | return ''.join(result) 800 | else: 801 | return bytes.fromhex('').join(result) 802 | else: 803 | data = self.__key1.crypt(data, ENCRYPT) 804 | data = self.__key2.crypt(data, DECRYPT) 805 | return self.__key3.crypt(data, ENCRYPT) 806 | 807 | def decrypt(self, data, pad=None, padmode=None): 808 | """decrypt(data, [pad], [padmode]) -> bytes 809 | 810 | data : bytes to be encrypted 811 | pad : Optional argument for decryption padding. Must only be one byte 812 | padmode : Optional argument for overriding the padding mode. 813 | 814 | The data must be a multiple of 8 bytes and will be decrypted 815 | with the already specified key. In PAD_NORMAL mode, if the 816 | optional padding character is supplied, then the un-encrypted 817 | data will have the padding characters removed from the end of 818 | the bytes. This pad removal only occurs on the last 8 bytes of 819 | the data (last data block). In PAD_PKCS5 mode, the special 820 | padding end markers will be removed from the data after 821 | decrypting, no pad character is required for PAD_PKCS5. 822 | """ 823 | ENCRYPT = des.ENCRYPT 824 | DECRYPT = des.DECRYPT 825 | data = self._guardAgainstUnicode(data) 826 | if pad is not None: 827 | pad = self._guardAgainstUnicode(pad) 828 | if self.getMode() == CBC: 829 | self.__key1.setIV(self.getIV()) 830 | self.__key2.setIV(self.getIV()) 831 | self.__key3.setIV(self.getIV()) 832 | i = 0 833 | result = [] 834 | while i < len(data): 835 | iv = data[i:i+8] 836 | block = self.__key3.crypt(iv, DECRYPT) 837 | block = self.__key2.crypt(block, ENCRYPT) 838 | block = self.__key1.crypt(block, DECRYPT) 839 | self.__key1.setIV(iv) 840 | self.__key2.setIV(iv) 841 | self.__key3.setIV(iv) 842 | result.append(block) 843 | i += 8 844 | if _pythonMajorVersion < 3: 845 | data = ''.join(result) 846 | else: 847 | data = bytes.fromhex('').join(result) 848 | else: 849 | data = self.__key3.crypt(data, DECRYPT) 850 | data = self.__key2.crypt(data, ENCRYPT) 851 | data = self.__key1.crypt(data, DECRYPT) 852 | return self._unpadData(data, pad, padmode) 853 | -------------------------------------------------------------------------------- /rfb.py: -------------------------------------------------------------------------------- 1 | """ 2 | RFB protocol implementattion, client side. 3 | 4 | Override RFBClient and RFBFactory in your application. 5 | See vncviewer.py for an example. 6 | 7 | Reference: 8 | http://www.realvnc.com/docs/rfbproto.pdf 9 | 10 | (C) 2003 cliechti@gmx.net 11 | 12 | MIT License 13 | """ 14 | 15 | import sys 16 | from struct import pack, unpack 17 | import pyDes 18 | from twisted.python import usage, log 19 | from twisted.internet.protocol import Factory, Protocol 20 | from twisted.internet import protocol 21 | from twisted.application import internet, service 22 | 23 | #~ from twisted.internet import reactor 24 | 25 | 26 | #encoding-type 27 | #for SetEncodings() 28 | RAW_ENCODING = 0 29 | COPY_RECTANGLE_ENCODING = 1 30 | RRE_ENCODING = 2 31 | CORRE_ENCODING = 4 32 | HEXTILE_ENCODING = 5 33 | ZLIB_ENCODING = 6 34 | TIGHT_ENCODING = 7 35 | ZLIBHEX_ENCODING = 8 36 | ZRLE_ENCODING = 16 37 | #0xffffff00 to 0xffffffff tight options 38 | 39 | #keycodes 40 | #for KeyEvent() 41 | KEY_BackSpace = 0xff08 42 | KEY_Tab = 0xff09 43 | KEY_Return = 0xff0d 44 | KEY_Escape = 0xff1b 45 | KEY_Insert = 0xff63 46 | KEY_Delete = 0xffff 47 | KEY_Home = 0xff50 48 | KEY_End = 0xff57 49 | KEY_PageUp = 0xff55 50 | KEY_PageDown = 0xff56 51 | KEY_Left = 0xff51 52 | KEY_Up = 0xff52 53 | KEY_Right = 0xff53 54 | KEY_Down = 0xff54 55 | KEY_F1 = 0xffbe 56 | KEY_F2 = 0xffbf 57 | KEY_F3 = 0xffc0 58 | KEY_F4 = 0xffc1 59 | KEY_F5 = 0xffc2 60 | KEY_F6 = 0xffc3 61 | KEY_F7 = 0xffc4 62 | KEY_F8 = 0xffc5 63 | KEY_F9 = 0xffc6 64 | KEY_F10 = 0xffc7 65 | KEY_F11 = 0xffc8 66 | KEY_F12 = 0xffc9 67 | KEY_F13 = 0xFFCA 68 | KEY_F14 = 0xFFCB 69 | KEY_F15 = 0xFFCC 70 | KEY_F16 = 0xFFCD 71 | KEY_F17 = 0xFFCE 72 | KEY_F18 = 0xFFCF 73 | KEY_F19 = 0xFFD0 74 | KEY_F20 = 0xFFD1 75 | KEY_ShiftLeft = 0xffe1 76 | KEY_ShiftRight = 0xffe2 77 | KEY_ControlLeft = 0xffe3 78 | KEY_ControlRight = 0xffe4 79 | KEY_MetaLeft = 0xffe7 80 | KEY_MetaRight = 0xffe8 81 | KEY_AltLeft = 0xffe9 82 | KEY_AltRight = 0xffea 83 | 84 | KEY_Scroll_Lock = 0xFF14 85 | KEY_Sys_Req = 0xFF15 86 | KEY_Num_Lock = 0xFF7F 87 | KEY_Caps_Lock = 0xFFE5 88 | KEY_Pause = 0xFF13 89 | KEY_Super_L = 0xFFEB 90 | KEY_Super_R = 0xFFEC 91 | KEY_Hyper_L = 0xFFED 92 | KEY_Hyper_R = 0xFFEE 93 | 94 | KEY_KP_0 = 0xFFB0 95 | KEY_KP_1 = 0xFFB1 96 | KEY_KP_2 = 0xFFB2 97 | KEY_KP_3 = 0xFFB3 98 | KEY_KP_4 = 0xFFB4 99 | KEY_KP_5 = 0xFFB5 100 | KEY_KP_6 = 0xFFB6 101 | KEY_KP_7 = 0xFFB7 102 | KEY_KP_8 = 0xFFB8 103 | KEY_KP_9 = 0xFFB9 104 | KEY_KP_Enter = 0xFF8D 105 | 106 | class RFBClient(Protocol): 107 | 108 | def __init__(self): 109 | self._packet = [] 110 | self._packet_len = 0 111 | self._handler = self._handleInitial 112 | self._already_expecting = 0 113 | 114 | #------------------------------------------------------ 115 | # states used on connection startup 116 | #------------------------------------------------------ 117 | 118 | def _handleInitial(self): 119 | buffer = ''.join(self._packet) 120 | if '\n' in buffer: 121 | if buffer[:3] == 'RFB': 122 | #~ print "rfb" 123 | maj, min = [int(x) for x in buffer[3:-1].split('.')] 124 | #~ print maj, min 125 | if (maj, min) not in [(3,3), (3,7), (3,8)]: 126 | log.msg("wrong protocol version\n") 127 | self.transport.loseConnection() 128 | buffer = buffer[12:] 129 | self.transport.write('RFB 003.003\n') 130 | log.msg("connected\n") 131 | self._packet[:] = [buffer] 132 | self._packet_len = len(buffer) 133 | self._handler = self._handleExpected 134 | self.expect(self._handleAuth, 4) 135 | else: 136 | self._packet[:] = [buffer] 137 | self._packet_len = len(buffer) 138 | 139 | def _handleAuth(self, block): 140 | (auth,) = unpack("!I", block) 141 | #~ print "auth:", auth 142 | if auth == 0: 143 | self.expect(self._handleConnFailed, 4) 144 | elif auth == 1: 145 | self._doClientInitialization() 146 | return 147 | elif auth == 2: 148 | self.expect(self._handleVNCAuth, 16) 149 | else: 150 | log.msg("unknown auth response (%d)\n" % auth) 151 | 152 | def _handleConnFailed(self): 153 | (waitfor,) = unpack("!I", block) 154 | self.expect(self._handleConnMessage, waitfor) 155 | 156 | def _handleConnMessage(self, block): 157 | log.msg("Connection refused: %r\n" % block) 158 | 159 | def _handleVNCAuth(self, block): 160 | self._challenge = block 161 | self.vncRequestPassword() 162 | self.expect(self._handleVNCAuthResult, 4) 163 | 164 | def sendPassword(self, password): 165 | """send password""" 166 | pw = (password + '\0' * 8)[:8] #make sure its 8 chars long, zero padded 167 | des = RFBDes(pw) 168 | response = des.encrypt(self._challenge) 169 | self.transport.write(response) 170 | 171 | def _handleVNCAuthResult(self, block): 172 | (result,) = unpack("!I", block) 173 | #~ print "auth:", auth 174 | if result == 0: #OK 175 | self._doClientInitialization() 176 | return 177 | elif result == 1: #failed 178 | self.vncAuthFailed("autenthication failed") 179 | self.transport.loseConnection() 180 | elif result == 2: #too many 181 | slef.vncAuthFailed("too many tries to log in") 182 | self.transport.loseConnection() 183 | else: 184 | log.msg("unknown auth response (%d)\n" % auth) 185 | 186 | def _doClientInitialization(self): 187 | self.transport.write(pack("!B", self.factory.shared)) 188 | self.expect(self._handleServerInit, 24) 189 | 190 | def _handleServerInit(self, block): 191 | (self.width, self.height, pixformat, namelen) = unpack("!HH16sI", block) 192 | (self.bpp, self.depth, self.bigendian, self.truecolor, 193 | self.redmax, self.greenmax, self.bluemax, 194 | self.redshift, self.greenshift, self.blueshift) = \ 195 | unpack("!BBBBHHHBBBxxx", pixformat) 196 | self.bypp = self.bpp / 8 #calc bytes per pixel 197 | self.expect(self._handleServerName, namelen) 198 | 199 | def _handleServerName(self, block): 200 | self.name = block 201 | #callback: 202 | self.vncConnectionMade() 203 | self.expect(self._handleConnection, 1) 204 | 205 | #------------------------------------------------------ 206 | # Server to client messages 207 | #------------------------------------------------------ 208 | def _handleConnection(self, block): 209 | (msgid,) = unpack("!B", block) 210 | if msgid == 0: 211 | self.expect(self._handleFramebufferUpdate, 3) 212 | elif msgid == 2: 213 | self.bell() 214 | self.expect(self._handleConnection, 1) 215 | elif msgid == 3: 216 | self.expect(self._handleServerCutText, 7) 217 | else: 218 | log.msg("unknown message received (id %d)\n" % msgid) 219 | self.expect(self._handleConnection, 1) 220 | 221 | def _handleFramebufferUpdate(self, block): 222 | (self.rectangles,) = unpack("!xH", block) 223 | self.rectanglePos = [] 224 | self.beginUpdate() 225 | self._doConnection() 226 | 227 | def _doConnection(self): 228 | if self.rectangles: 229 | self.expect(self._handleRectangle, 12) 230 | else: 231 | self.commitUpdate(self.rectanglePos) 232 | self.expect(self._handleConnection, 1) 233 | 234 | def _handleRectangle(self, block): 235 | (x, y, width, height, encoding) = unpack("!HHHHI", block) 236 | if self.rectangles: 237 | self.rectangles -= 1 238 | self.rectanglePos.append( (x, y, width, height) ) 239 | if encoding == COPY_RECTANGLE_ENCODING: 240 | self.expect(self._handleDecodeCopyrect, 4, x, y, width, height) 241 | elif encoding == RAW_ENCODING: 242 | self.expect(self._handleDecodeRAW, width*height*self.bypp, x, y, width, height) 243 | elif encoding == HEXTILE_ENCODING: 244 | self._doNextHextileSubrect(None, None, x, y, width, height, None, None) 245 | elif encoding == CORRE_ENCODING: 246 | self.expect(self._handleDecodeCORRE, 4 + self.bypp, x, y, width, height) 247 | elif encoding == RRE_ENCODING: 248 | self.expect(self._handleDecodeRRE, 4 + self.bypp, x, y, width, height) 249 | #~ elif encoding == ZRLE_ENCODING: 250 | #~ self.expect(self._handleDecodeZRLE, ) 251 | else: 252 | log.msg("unknown encoding received (encoding %d)\n" % encoding) 253 | self._doConnection() 254 | else: 255 | self._doConnection() 256 | 257 | # --- RAW Encoding 258 | 259 | def _handleDecodeRAW(self, block, x, y, width, height): 260 | #TODO convert pixel format? 261 | self.updateRectangle(x, y, width, height, block) 262 | self._doConnection() 263 | 264 | # --- CopyRect Encoding 265 | 266 | def _handleDecodeCopyrect(self, block, x, y, width, height): 267 | (srcx, srcy) = unpack("!HH", block) 268 | self.copyRectangle(srcx, srcy, x, y, width, height) 269 | self._doConnection() 270 | 271 | # --- RRE Encoding 272 | 273 | def _handleDecodeRRE(self, block, x, y, width, height): 274 | (subrects,) = unpack("!I", block[:4]) 275 | color = block[4:] 276 | self.fillRectangle(x, y, width, height, color) 277 | if subrects: 278 | self.expect(self._handleRRESubRectangles, (8 + self.bypp) * subrects, x, y) 279 | else: 280 | self._doConnection() 281 | 282 | def _handleRRESubRectangles(self, block, topx, topy): 283 | #~ print "_handleRRESubRectangle" 284 | pos = 0 285 | end = len(block) 286 | sz = self.bypp + 8 287 | format = "!%dsHHHH" % self.bypp 288 | while pos < end: 289 | (color, x, y, width, height) = unpack(format, block[pos:pos+sz]) 290 | self.fillRectangle(topx + x, topy + y, width, height, color) 291 | pos += sz 292 | self._doConnection() 293 | 294 | # --- CoRRE Encoding 295 | 296 | def _handleDecodeCORRE(self, block, x, y, width, height): 297 | (subrects,) = unpack("!I", block[:4]) 298 | color = block[4:] 299 | self.fillRectangle(x, y, width, height, color) 300 | if subrects: 301 | self.expect(self._handleDecodeCORRERectangles, (4 + self.bypp)*subrects, x, y) 302 | else: 303 | self._doConnection() 304 | 305 | def _handleDecodeCORRERectangles(self, block, topx, topy): 306 | #~ print "_handleDecodeCORRERectangle" 307 | pos = 0 308 | end = len(block) 309 | sz = self.bypp + 4 310 | format = "!%dsBBBB" % self.bypp 311 | while pos < sz: 312 | (color, x, y, width, height) = unpack(format, block[pos:pos+sz]) 313 | self.fillRectangle(topx + x, topy + y, width, height, color) 314 | pos += sz 315 | self._doConnection() 316 | 317 | # --- Hexile Encoding 318 | 319 | def _doNextHextileSubrect(self, bg, color, x, y, width, height, tx, ty): 320 | #~ print "_doNextHextileSubrect %r" % ((color, x, y, width, height, tx, ty), ) 321 | #coords of next tile 322 | #its line after line of tiles 323 | #finished when the last line is completly received 324 | 325 | #dont inc the first time 326 | if tx is not None: 327 | #calc next subrect pos 328 | tx += 16 329 | if tx >= x + width: 330 | tx = x 331 | ty += 16 332 | else: 333 | tx = x 334 | ty = y 335 | #more tiles? 336 | if ty >= y + height: 337 | self._doConnection() 338 | else: 339 | self.expect(self._handleDecodeHextile, 1, bg, color, x, y, width, height, tx, ty) 340 | 341 | def _handleDecodeHextile(self, block, bg, color, x, y, width, height, tx, ty): 342 | (subencoding,) = unpack("!B", block) 343 | #calc tile size 344 | tw = th = 16 345 | if x + width - tx < 16: tw = x + width - tx 346 | if y + height - ty < 16: th = y + height- ty 347 | #decode tile 348 | if subencoding & 1: #RAW 349 | self.expect(self._handleDecodeHextileRAW, tw*th*self.bypp, bg, color, x, y, width, height, tx, ty, tw, th) 350 | else: 351 | numbytes = 0 352 | if subencoding & 2: #BackgroundSpecified 353 | numbytes += self.bypp 354 | if subencoding & 4: #ForegroundSpecified 355 | numbytes += self.bypp 356 | if subencoding & 8: #AnySubrects 357 | numbytes += 1 358 | if numbytes: 359 | self.expect(self._handleDecodeHextileSubrect, numbytes, subencoding, bg, color, x, y, width, height, tx, ty, tw, th) 360 | else: 361 | self.fillRectangle(tx, ty, tw, th, bg) 362 | self._doNextHextileSubrect(bg, color, x, y, width, height, tx, ty) 363 | 364 | def _handleDecodeHextileSubrect(self, block, subencoding, bg, color, x, y, width, height, tx, ty, tw, th): 365 | subrects = 0 366 | pos = 0 367 | if subencoding & 2: #BackgroundSpecified 368 | bg = block[:self.bypp] 369 | pos += self.bypp 370 | self.fillRectangle(tx, ty, tw, th, bg) 371 | if subencoding & 4: #ForegroundSpecified 372 | color = block[pos:pos+self.bypp] 373 | pos += self.bypp 374 | if subencoding & 8: #AnySubrects 375 | #~ (subrects, ) = unpack("!B", block) 376 | subrects = ord(block[pos]) 377 | #~ print subrects 378 | if subrects: 379 | if subencoding & 16: #SubrectsColoured 380 | self.expect(self._handleDecodeHextileSubrectsColoured, (self.bypp + 2)*subrects, bg, color, subrects, x, y, width, height, tx, ty, tw, th) 381 | else: 382 | self.expect(self._handleDecodeHextileSubrectsFG, 2*subrects, bg, color, subrects, x, y, width, height, tx, ty, tw, th) 383 | else: 384 | self._doNextHextileSubrect(bg, color, x, y, width, height, tx, ty) 385 | 386 | 387 | def _handleDecodeHextileRAW(self, block, bg, color, x, y, width, height, tx, ty, tw, th): 388 | """the tile is in raw encoding""" 389 | self.updateRectangle(tx, ty, tw, th, block) 390 | self._doNextHextileSubrect(bg, color, x, y, width, height, tx, ty) 391 | 392 | def _handleDecodeHextileSubrectsColoured(self, block, bg, color, subrects, x, y, width, height, tx, ty, tw, th): 393 | """subrects with their own color""" 394 | sz = self.bypp + 2 395 | pos = 0 396 | end = len(block) 397 | while pos < end: 398 | pos2 = pos + self.bypp 399 | color = block[pos:pos2] 400 | xy = ord(block[pos2]) 401 | wh = ord(block[pos2+1]) 402 | sx = xy >> 4 403 | sy = xy & 0xf 404 | sw = (wh >> 4) + 1 405 | sh = (wh & 0xf) + 1 406 | self.fillRectangle(tx + sx, ty + sy, sw, sh, color) 407 | pos += sz 408 | self._doNextHextileSubrect(bg, color, x, y, width, height, tx, ty) 409 | 410 | def _handleDecodeHextileSubrectsFG(self, block, bg, color, subrects, x, y, width, height, tx, ty, tw, th): 411 | """all subrect with same color""" 412 | pos = 0 413 | end = len(block) 414 | while pos < end: 415 | xy = ord(block[pos]) 416 | wh = ord(block[pos+1]) 417 | sx = xy >> 4 418 | sy = xy & 0xf 419 | sw = (wh >> 4) + 1 420 | sh = (wh & 0xf) + 1 421 | self.fillRectangle(tx + sx, ty + sy, sw, sh, color) 422 | pos += 2 423 | self._doNextHextileSubrect(bg, color, x, y, width, height, tx, ty) 424 | 425 | 426 | # --- ZRLE Encoding 427 | 428 | def _handleDecodeZRLE(self, block): 429 | raise NotImplementedError 430 | 431 | # --- other server messages 432 | 433 | def _handleServerCutText(self, block): 434 | (length, ) = unpack("!xxxI", block) 435 | self.expect(self._handleServerCutTextValue, length) 436 | 437 | def _handleServerCutTextValue(self, block): 438 | self.copy_text(block) 439 | self.expect(self._handleConnection, 1) 440 | 441 | #------------------------------------------------------ 442 | # incomming data redirector 443 | #------------------------------------------------------ 444 | def dataReceived(self, data): 445 | #~ sys.stdout.write(repr(data) + '\n') 446 | #~ print len(data), ", ", len(self._packet) 447 | self._packet.append(data) 448 | self._packet_len += len(data) 449 | self._handler() 450 | 451 | def _handleExpected(self): 452 | if self._packet_len >= self._expected_len: 453 | buffer = ''.join(self._packet) 454 | while len(buffer) >= self._expected_len: 455 | self._already_expecting = 1 456 | block, buffer = buffer[:self._expected_len], buffer[self._expected_len:] 457 | #~ log.msg("handle %r with %r\n" % (block, self._expected_handler.__name__)) 458 | self._expected_handler(block, *self._expected_args, **self._expected_kwargs) 459 | self._packet[:] = [buffer] 460 | self._packet_len = len(buffer) 461 | self._already_expecting = 0 462 | 463 | def expect(self, handler, size, *args, **kwargs): 464 | #~ log.msg("expect(%r, %r, %r, %r)\n" % (handler.__name__, size, args, kwargs)) 465 | self._expected_handler = handler 466 | self._expected_len = size 467 | self._expected_args = args 468 | self._expected_kwargs = kwargs 469 | if not self._already_expecting: 470 | self._handleExpected() #just in case that there is already enough data 471 | 472 | #------------------------------------------------------ 473 | # client -> server messages 474 | #------------------------------------------------------ 475 | 476 | def setPixelFormat(self, bpp=32, depth=24, bigendian=0, truecolor=1, redmax=255, greenmax=255, bluemax=255, redshift=0, greenshift=8, blueshift=16): 477 | pixformat = pack("!BBBBHHHBBBxxx", bpp, depth, bigendian, truecolor, redmax, greenmax, bluemax, redshift, greenshift, blueshift) 478 | self.transport.write(pack("!Bxxx16s", 0, pixformat)) 479 | #rember these settings 480 | self.bpp, self.depth, self.bigendian, self.truecolor = bpp, depth, bigendian, truecolor 481 | self.redmax, self.greenmax, self.bluemax = redmax, greenmax, bluemax 482 | self.redshift, self.greenshift, self.blueshift = redshift, greenshift, blueshift 483 | self.bypp = self.bpp / 8 #calc bytes per pixel 484 | #~ print self.bypp 485 | 486 | def setEncodings(self, list_of_encodings): 487 | self.transport.write(pack("!BxH", 2, len(list_of_encodings))) 488 | for encoding in list_of_encodings: 489 | self.transport.write(pack("!I", encoding)) 490 | 491 | def framebufferUpdateRequest(self, x=0, y=0, width=None, height=None, incremental=0): 492 | if width is None: width = self.width - x 493 | if height is None: height = self.height - y 494 | self.transport.write(pack("!BBHHHH", 3, incremental, x, y, width, height)) 495 | 496 | def keyEvent(self, key, down=1): 497 | """For most ordinary keys, the "keysym" is the same as the corresponding ASCII value. 498 | Other common keys are shown in the KEY_ constants.""" 499 | self.transport.write(pack("!BBxxI", 4, down, key)) 500 | 501 | def pointerEvent(self, x, y, buttonmask=0): 502 | """Indicates either pointer movement or a pointer button press or release. The pointer is 503 | now at (x-position, y-position), and the current state of buttons 1 to 8 are represented 504 | by bits 0 to 7 of button-mask respectively, 0 meaning up, 1 meaning down (pressed). 505 | """ 506 | self.transport.write(pack("!BBHH", 5, buttonmask, x, y)) 507 | 508 | def clientCutText(self, message): 509 | """The client has new ASCII text in its cut buffer. 510 | (aka clipboard) 511 | """ 512 | self.transport.write(pack("!BxxxI", 6, len(message)) + message) 513 | 514 | #------------------------------------------------------ 515 | # callbacks 516 | # override these in your application 517 | #------------------------------------------------------ 518 | def vncConnectionMade(self): 519 | """connection is initialized and ready. 520 | typicaly, the pixel format is set here.""" 521 | 522 | def vncRequestPassword(self): 523 | """a password is needed to log on, use sendPassword() to 524 | send one.""" 525 | if self.factory.password is None: 526 | log.msg("need a password\n") 527 | self.transport.loseConnection() 528 | return 529 | self.sendPassword(self.factory.password) 530 | 531 | def vncAuthFailed(self, reason): 532 | """called when the authentication failed. 533 | the connection is closed.""" 534 | log.msg("Cannot connect: %s\n" % reason) 535 | 536 | def beginUpdate(self): 537 | """called before a series of updateRectangle(), 538 | copyRectangle() or fillRectangle().""" 539 | 540 | def commitUpdate(self, rectangles=None): 541 | """called after a series of updateRectangle(), copyRectangle() 542 | or fillRectangle() are finished. 543 | typicaly, here is the place to request the next screen 544 | update with FramebufferUpdateRequest(incremental=1). 545 | argument is a list of tuples (x,y,w,h) with the updated 546 | rectangles.""" 547 | 548 | def updateRectangle(self, x, y, width, height, data): 549 | """new bitmap data. data is a string in the pixel format set 550 | up earlier.""" 551 | 552 | def copyRectangle(self, srcx, srcy, x, y, width, height): 553 | """used for copyrect encoding. copy the given rectangle 554 | (src, srxy, width, height) to the target coords (x,y)""" 555 | 556 | def fillRectangle(self, x, y, width, height, color): 557 | """fill the area with the color. the color is a string in 558 | the pixel format set up earlier""" 559 | #fallback variant, use update recatngle 560 | #override with specialized function for better performance 561 | self.updateRectangle(x, y, width, height, color*width*height) 562 | 563 | def bell(self): 564 | """bell""" 565 | 566 | def copy_text(self, text): 567 | """The server has new ASCII text in its cut buffer. 568 | (aka clipboard)""" 569 | 570 | class RFBFactory(protocol.ClientFactory): 571 | """A factory for remote frame buffer connections.""" 572 | 573 | # the class of the protocol to build 574 | # should be overriden by application to use a derrived class 575 | protocol = RFBClient 576 | 577 | def __init__(self, password = None, shared = 0): 578 | self.password = password 579 | self.shared = shared 580 | 581 | class RFBDes(pyDes.des): 582 | def setKey(self, key): 583 | """RFB protocol for authentication requires client to encrypt 584 | challenge sent by server with password using DES method. However, 585 | bits in each byte of the password are put in reverse order before 586 | using it as encryption key.""" 587 | newkey = [] 588 | for ki in range(len(key)): 589 | bsrc = ord(key[ki]) 590 | btgt = 0 591 | for i in range(8): 592 | if bsrc & (1 << i): 593 | btgt = btgt | (1 << 7-i) 594 | newkey.append(chr(btgt)) 595 | super(RFBDes, self).setKey(newkey) 596 | 597 | 598 | # --- test code only, see vncviewer.py 599 | 600 | if __name__ == '__main__': 601 | class RFBTest(RFBClient): 602 | """dummy client""" 603 | def vncConnectionMade(self): 604 | print "Screen format: depth=%d bytes_per_pixel=%r" % (self.depth, self.bpp) 605 | print "Desktop name: %r" % self.name 606 | self.SetEncodings([RAW_ENCODING]) 607 | self.FramebufferUpdateRequest() 608 | 609 | def updateRectangle(self, x, y, width, height, data): 610 | print "%s " * 5 % (x, y, width, height, repr(data[:20])) 611 | 612 | class RFBTestFactory(protocol.ClientFactory): 613 | """test factory""" 614 | protocol = RFBTest 615 | def clientConnectionLost(self, connector, reason): 616 | print reason 617 | from twisted.internet import reactor 618 | reactor.stop() 619 | #~ connector.connect() 620 | 621 | def clientConnectionFailed(self, connector, reason): 622 | print "connection failed:", reason 623 | from twisted.internet import reactor 624 | reactor.stop() 625 | 626 | class Options(usage.Options): 627 | """command line options""" 628 | optParameters = [ 629 | ['display', 'd', '0', 'VNC display'], 630 | ['host', 'h', 'localhost', 'remote hostname'], 631 | ['outfile', 'o', None, 'Logfile [default: sys.stdout]'], 632 | ] 633 | 634 | o = Options() 635 | try: 636 | o.parseOptions() 637 | except usage.UsageError, errortext: 638 | print "%s: %s" % (sys.argv[0], errortext) 639 | print "%s: Try --help for usage details." % (sys.argv[0]) 640 | raise SystemExit, 1 641 | 642 | logFile = sys.stdout 643 | if o.opts['outfile']: 644 | logFile = o.opts['outfile'] 645 | log.startLogging(logFile) 646 | 647 | host = o.opts['host'] 648 | port = int(o.opts['display']) + 5900 649 | 650 | application = service.Application("rfb test") # create Application 651 | 652 | # connect to this host and port, and reconnect if we get disconnected 653 | vncClient = internet.TCPClient(host, port, RFBFactory()) # create the service 654 | vncClient.setServiceParent(application) 655 | 656 | # this file should be run as 'twistd -y rfb.py' but it didn't work - 657 | # could't import crippled_des.py, so using this hack. 658 | # now with crippled_des.py replaced with pyDes this can be no more actual 659 | from twisted.internet import reactor 660 | vncClient.startService() 661 | reactor.run() 662 | -------------------------------------------------------------------------------- /vncviewer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Python VNC Viewer 4 | PyGame version 5 | (C) 2003 6 | 7 | MIT License 8 | """ 9 | 10 | #twisted modules 11 | from twisted.python import usage, log 12 | from twisted.internet import reactor, protocol 13 | #~ from twisted.internet import defer 14 | from twisted.internet.protocol import Factory, Protocol 15 | 16 | #import pygame stuff 17 | import pygame 18 | from pygame.locals import * 19 | 20 | #std stuff 21 | import sys, struct 22 | 23 | #local 24 | import rfb 25 | import inputbox 26 | 27 | POINTER = tuple([(8,8), (4,4)] + list(pygame.cursors.compile(( 28 | #01234567 29 | " ", #0 30 | " ", #1 31 | " ", #2 32 | " .X. ", #3 33 | " X.X ", #4 34 | " .X. ", #5 35 | " ", #6 36 | " ", #7 37 | ), 'X', '.'))) 38 | 39 | #keyboard mappings pygame -> vnc 40 | KEYMAPPINGS = { 41 | K_BACKSPACE: rfb.KEY_BackSpace, 42 | K_TAB: rfb.KEY_Tab, 43 | K_RETURN: rfb.KEY_Return, 44 | K_ESCAPE: rfb.KEY_Escape, 45 | K_KP0: rfb.KEY_KP_0, 46 | K_KP1: rfb.KEY_KP_1, 47 | K_KP2: rfb.KEY_KP_2, 48 | K_KP3: rfb.KEY_KP_3, 49 | K_KP4: rfb.KEY_KP_4, 50 | K_KP5: rfb.KEY_KP_5, 51 | K_KP6: rfb.KEY_KP_6, 52 | K_KP7: rfb.KEY_KP_7, 53 | K_KP8: rfb.KEY_KP_8, 54 | K_KP9: rfb.KEY_KP_9, 55 | K_KP_ENTER: rfb.KEY_KP_Enter, 56 | K_UP: rfb.KEY_Up, 57 | K_DOWN: rfb.KEY_Down, 58 | K_RIGHT: rfb.KEY_Right, 59 | K_LEFT: rfb.KEY_Left, 60 | K_INSERT: rfb.KEY_Insert, 61 | K_DELETE: rfb.KEY_Delete, 62 | K_HOME: rfb.KEY_Home, 63 | K_END: rfb.KEY_End, 64 | K_PAGEUP: rfb.KEY_PageUp, 65 | K_PAGEDOWN: rfb.KEY_PageDown, 66 | K_F1: rfb.KEY_F1, 67 | K_F2: rfb.KEY_F2, 68 | K_F3: rfb.KEY_F3, 69 | K_F4: rfb.KEY_F4, 70 | K_F5: rfb.KEY_F5, 71 | K_F6: rfb.KEY_F6, 72 | K_F7: rfb.KEY_F7, 73 | K_F8: rfb.KEY_F8, 74 | K_F9: rfb.KEY_F9, 75 | K_F10: rfb.KEY_F10, 76 | K_F11: rfb.KEY_F11, 77 | K_F12: rfb.KEY_F12, 78 | K_F13: rfb.KEY_F13, 79 | K_F14: rfb.KEY_F14, 80 | K_F15: rfb.KEY_F15, 81 | } 82 | 83 | MODIFIERS = { 84 | K_NUMLOCK: rfb.KEY_Num_Lock, 85 | K_CAPSLOCK: rfb.KEY_Caps_Lock, 86 | K_SCROLLOCK: rfb.KEY_Scroll_Lock, 87 | K_RSHIFT: rfb.KEY_ShiftRight, 88 | K_LSHIFT: rfb.KEY_ShiftLeft, 89 | K_RCTRL: rfb.KEY_ControlRight, 90 | K_LCTRL: rfb.KEY_ControlLeft, 91 | K_RALT: rfb.KEY_AltRight, 92 | K_LALT: rfb.KEY_AltLeft, 93 | K_RMETA: rfb.KEY_MetaRight, 94 | K_LMETA: rfb.KEY_MetaLeft, 95 | K_LSUPER: rfb.KEY_Super_L, 96 | K_RSUPER: rfb.KEY_Super_R, 97 | K_MODE: rfb.KEY_Hyper_R, #??? 98 | #~ K_HELP: rfb. 99 | #~ K_PRINT: rfb. 100 | K_SYSREQ: rfb.KEY_Sys_Req, 101 | K_BREAK: rfb.KEY_Pause, #??? 102 | K_MENU: rfb.KEY_Hyper_L, #??? 103 | #~ K_POWER: rfb. 104 | #~ K_EURO: rfb. 105 | } 106 | 107 | 108 | class TextSprite(pygame.sprite.Sprite): 109 | """a text label""" 110 | SIZE = 20 111 | def __init__(self, pos, color = (255,0,0, 120)): 112 | self.pos = pos 113 | #self.containers = containers 114 | #pygame.sprite.Sprite.__init__(self, self.containers) 115 | pygame.sprite.Sprite.__init__(self) 116 | self.font = pygame.font.Font(None, self.SIZE) 117 | self.lastmsg = None 118 | self.update() 119 | self.rect = self.image.get_rect().move(pos) 120 | 121 | def update(self, msg=' '): 122 | if msg != self.lastmsg: 123 | self.lastscore = msg 124 | self.image = self.font.render(msg, 0, (255,255,255)) 125 | 126 | 127 | #~ class PyGameApp(pb.Referenceable, Game.Game): 128 | class PyGameApp: 129 | """Pygame main application""" 130 | 131 | def __init__(self): 132 | width, height = 640, 480 133 | self.setRFBSize(width, height) 134 | pygame.display.set_caption('Python VNC Viewer') 135 | pygame.mouse.set_cursor(*POINTER) 136 | pygame.key.set_repeat(500, 30) 137 | self.clock = pygame.time.Clock() 138 | self.alive = 1 139 | self.loopcounter = 0 140 | self.sprites = pygame.sprite.RenderUpdates() 141 | self.statustext = TextSprite((5, 0)) 142 | self.sprites.add(self.statustext) 143 | self.buttons = 0 144 | self.protocol = None 145 | 146 | def setRFBSize(self, width, height, depth=32): 147 | """change screen size""" 148 | self.width, self.height = width, height 149 | self.area = Rect(0, 0, width, height) 150 | winstyle = 0 # |FULLSCREEN 151 | if depth == 32: 152 | self.screen = pygame.display.set_mode(self.area.size, winstyle, 32) 153 | elif depth == 8: 154 | self.screen = pygame.display.set_mode(self.area.size, winstyle, 8) 155 | #default palette is perfect ;-) 156 | #~ pygame.display.set_palette([(x,x,x) for x in range(256)]) 157 | #~ elif depth is None: 158 | #~ bestdepth = pygame.display.mode_ok((width, height), winstyle, 32) 159 | #~ print "bestdepth %r" % bestdepth 160 | #~ self.screen = pygame.display.set_mode(self.area.size, winstyle, best) 161 | #then communicate that to the protocol... 162 | else: 163 | #~ self.screen = pygame.display.set_mode(self.area.size, winstyle, depth) 164 | raise ValueError, "color depth not supported" 165 | self.background = pygame.Surface((self.width, self.height), depth) 166 | self.background.fill(0) #black 167 | 168 | def setProtocol(self, protocol): 169 | """attach a protocol instance to post the events to""" 170 | self.protocol = protocol 171 | 172 | def checkEvents(self): 173 | """process events from the queue""" 174 | seen_events = 0 175 | for e in pygame.event.get(): 176 | seen_events = 1 177 | #~ print e 178 | if e.type == QUIT: 179 | self.alive = 0 180 | reactor.stop() 181 | #~ elif e.type == KEYUP and e.key == K_ESCAPE: 182 | #~ self.alive = 0 183 | #~ reactor.stop() 184 | if self.protocol is not None: 185 | if e.type == KEYDOWN: 186 | if e.key in MODIFIERS: 187 | self.protocol.keyEvent(MODIFIERS[e.key], down=1) 188 | elif e.key in KEYMAPPINGS: 189 | self.protocol.keyEvent(KEYMAPPINGS[e.key]) 190 | elif e.unicode: 191 | self.protocol.keyEvent(ord(e.unicode)) 192 | else: 193 | print "warning: unknown key %r" % (e) 194 | elif e.type == KEYUP: 195 | if e.key in MODIFIERS: 196 | self.protocol.keyEvent(MODIFIERS[e.key], down=0) 197 | #~ else: 198 | #~ print "unknown key %r" % (e) 199 | elif e.type == MOUSEMOTION: 200 | self.buttons = e.buttons[0] and 1 201 | self.buttons |= e.buttons[1] and 2 202 | self.buttons |= e.buttons[2] and 4 203 | self.protocol.pointerEvent(e.pos[0], e.pos[1], self.buttons) 204 | #~ print e.pos 205 | elif e.type == MOUSEBUTTONUP: 206 | if e.button == 1: self.buttons &= ~1 207 | if e.button == 2: self.buttons &= ~2 208 | if e.button == 3: self.buttons &= ~4 209 | if e.button == 4: self.buttons &= ~8 210 | if e.button == 5: self.buttons &= ~16 211 | self.protocol.pointerEvent(e.pos[0], e.pos[1], self.buttons) 212 | elif e.type == MOUSEBUTTONDOWN: 213 | if e.button == 1: self.buttons |= 1 214 | if e.button == 2: self.buttons |= 2 215 | if e.button == 3: self.buttons |= 4 216 | if e.button == 4: self.buttons |= 8 217 | if e.button == 5: self.buttons |= 16 218 | self.protocol.pointerEvent(e.pos[0], e.pos[1], self.buttons) 219 | return not seen_events 220 | return not seen_events 221 | 222 | def mainloop(self, dum=None): 223 | """gui 'mainloop', it is called repeated by twisteds mainloop 224 | by using callLater""" 225 | #~ self.clock.tick() 226 | no_work = self.checkEvents() 227 | 228 | #~ self.sprites.clear(self.screen, self.background) 229 | #~ dirty = self.sprites.draw(self.screen) 230 | #~ pygame.display.update(dirty) 231 | 232 | #~ self.statustext.update("iteration %d" % self.loopcounter) 233 | #~ self.loopcounter += 1 234 | 235 | #~ pygame.display.flip() 236 | 237 | if self.alive: 238 | #~ d = defer.Deferred() 239 | #~ d.addCallback(self.mainloop) 240 | #~ d.callback(None) 241 | reactor.callLater(no_work and 0.020, self.mainloop) 242 | 243 | #~ def error(self): 244 | #~ log.msg('error, stopping reactor') 245 | #~ reactor.stop() 246 | 247 | 248 | 249 | 250 | class RFBToGUI(rfb.RFBClient): 251 | """RFBClient protocol that talks to the GUI app""" 252 | 253 | def vncConnectionMade(self): 254 | """choose appropriate color depth, resize screen""" 255 | #~ print "Screen format: depth=%d bytes_per_pixel=%r" % (self.depth, self.bpp) 256 | #~ print "Desktop name: %r" % self.name 257 | 258 | #~ print "redmax=%r, greenmax=%r, bluemax=%r" % (self.redmax, self.greenmax, self.bluemax) 259 | #~ print "redshift=%r, greenshift=%r, blueshift=%r" % (self.redshift, self.greenshift, self.blueshift) 260 | 261 | self.remoteframebuffer = self.factory.remoteframebuffer 262 | self.screen = self.remoteframebuffer.screen 263 | self.remoteframebuffer.setProtocol(self) 264 | self.remoteframebuffer.setRFBSize(self.width, self.height, 32) 265 | self.setEncodings(self.factory.encodings) 266 | self.setPixelFormat() #set up pixel format to 32 bits 267 | self.framebufferUpdateRequest() #request initial screen update 268 | 269 | def vncRequestPassword(self): 270 | if self.factory.password is not None: 271 | self.sendPassword(self.factory.password) 272 | else: 273 | #XXX hack, this is blocking twisted!!!!!!! 274 | screen = pygame.display.set_mode((220,40)) 275 | screen.fill((255,100,0)) #redish bg 276 | self.sendPassword(inputbox.ask(screen, "Password", password=1)) 277 | 278 | #~ def beginUpdate(self): 279 | #~ """start with a new series of display updates""" 280 | 281 | def beginUpdate(self): 282 | """begin series of display updates""" 283 | #~ log.msg("screen lock") 284 | 285 | def commitUpdate(self, rectangles = None): 286 | """finish series of display updates""" 287 | #~ log.msg("screen unlock") 288 | pygame.display.update(rectangles) 289 | self.framebufferUpdateRequest(incremental=1) 290 | 291 | def updateRectangle(self, x, y, width, height, data): 292 | """new bitmap data""" 293 | #~ print "%s " * 5 % (x, y, width, height, len(data)) 294 | #~ log.msg("screen update") 295 | self.screen.blit( 296 | pygame.image.fromstring(data, (width, height), 'RGBX'), #TODO color format 297 | (x, y) 298 | ) 299 | 300 | def copyRectangle(self, srcx, srcy, x, y, width, height): 301 | """copy src rectangle -> destinantion""" 302 | #~ print "copyrect", (srcx, srcy, x, y, width, height) 303 | self.screen.blit(self.screen, 304 | (x, y), 305 | (srcx, srcy, width, height) 306 | ) 307 | 308 | def fillRectangle(self, x, y, width, height, color): 309 | """fill rectangle with one color""" 310 | #~ remoteframebuffer.CopyRect(srcx, srcy, x, y, width, height) 311 | self.screen.fill(struct.unpack("BBBB", color), (x, y, width, height)) 312 | 313 | def bell(self): 314 | print "katsching" 315 | 316 | def copy_text(self, text): 317 | print "Clipboard: %r" % text 318 | 319 | #use a derrived class for other depths. hopefully with better performance 320 | #that a single class with complicated/dynamic color conversion. 321 | class RFBToGUIeightbits(RFBToGUI): 322 | def vncConnectionMade(self): 323 | """choose appropriate color depth, resize screen""" 324 | self.remoteframebuffer = self.factory.remoteframebuffer 325 | self.screen = self.remoteframebuffer.screen 326 | self.remoteframebuffer.setProtocol(self) 327 | self.remoteframebuffer.setRFBSize(self.width, self.height, 8) 328 | self.setEncodings(self.factory.encodings) 329 | self.setPixelFormat(bpp=8, depth=8, bigendian=0, truecolor=1, 330 | redmax=7, greenmax=7, bluemax=3, 331 | redshift=5, greenshift=2, blueshift=0 332 | ) 333 | self.palette = self.screen.get_palette() 334 | self.framebufferUpdateRequest() 335 | 336 | def updateRectangle(self, x, y, width, height, data): 337 | """new bitmap data""" 338 | #~ print "%s " * 5 % (x, y, width, height, len(data)) 339 | #~ assert len(data) == width*height 340 | bmp = pygame.image.fromstring(data, (width, height), 'P') 341 | bmp.set_palette(self.palette) 342 | self.screen.blit(bmp, (x, y)) 343 | 344 | def fillRectangle(self, x, y, width, height, color): 345 | """fill rectangle with one color""" 346 | self.screen.fill(ord(color), (x, y, width, height)) 347 | 348 | class VNCFactory(rfb.RFBFactory): 349 | """A factory for remote frame buffer connections.""" 350 | 351 | def __init__(self, remoteframebuffer, depth, fast, *args, **kwargs): 352 | rfb.RFBFactory.__init__(self, *args, **kwargs) 353 | self.remoteframebuffer = remoteframebuffer 354 | if depth == 32: 355 | self.protocol = RFBToGUI 356 | elif depth == 8: 357 | self.protocol = RFBToGUIeightbits 358 | else: 359 | raise ValueError, "color depth not supported" 360 | 361 | if fast: 362 | self.encodings = [ 363 | rfb.COPY_RECTANGLE_ENCODING, 364 | rfb.RAW_ENCODING, 365 | ] 366 | else: 367 | self.encodings = [ 368 | rfb.COPY_RECTANGLE_ENCODING, 369 | rfb.HEXTILE_ENCODING, 370 | rfb.CORRE_ENCODING, 371 | rfb.RRE_ENCODING, 372 | rfb.RAW_ENCODING, 373 | ] 374 | 375 | 376 | def buildProtocol(self, addr): 377 | display = addr.port - 5900 378 | pygame.display.set_caption('Python VNC Viewer on %s:%s' % (addr.host, display)) 379 | return rfb.RFBFactory.buildProtocol(self, addr) 380 | 381 | def clientConnectionLost(self, connector, reason): 382 | log.msg("connection lost: %r" % reason.getErrorMessage()) 383 | reactor.stop() 384 | 385 | def clientConnectionFailed(self, connector, reason): 386 | log.msg("cannot connect to server: %r\n" % reason.getErrorMessage()) 387 | reactor.stop() 388 | 389 | class Options(usage.Options): 390 | optParameters = [ 391 | ['display', 'd', '0', 'VNC display'], 392 | ['host', 'h', None, 'remote hostname'], 393 | ['outfile', 'o', None, 'Logfile [default: sys.stdout]'], 394 | ['password', 'p', None, 'VNC password'], 395 | ['depth', 'D', '32', 'Color depth'], 396 | ] 397 | optFlags = [ 398 | ['shared', 's', 'Request shared session'], 399 | ['fast', 'f', 'Fast connection is used'], 400 | ] 401 | 402 | #~ def eventcollector(): 403 | #~ while remoteframebuffer.alive: 404 | #~ pygame.event.pump() 405 | #~ e = pygame.event.poll() 406 | #~ if e.type != NOEVENT: 407 | #~ print e 408 | #~ reactor.callFromThread(remoteframebuffer.processEvent, e) 409 | #~ print 'xxxxxxxxxxxxx' 410 | 411 | def main(): 412 | o = Options() 413 | try: 414 | o.parseOptions() 415 | except usage.UsageError, errortext: 416 | print "%s: %s" % (sys.argv[0], errortext) 417 | print "%s: Try --help for usage details." % (sys.argv[0]) 418 | raise SystemExit, 1 419 | 420 | depth = int(o.opts['depth']) 421 | 422 | logFile = sys.stdout 423 | if o.opts['outfile']: 424 | logFile = o.opts['outfile'] 425 | log.startLogging(logFile) 426 | 427 | pygame.init() 428 | remoteframebuffer = PyGameApp() 429 | 430 | #~ from twisted.python import threadable 431 | #~ threadable.init() 432 | #~ reactor.callInThread(eventcollector) 433 | 434 | host = o.opts['host'] 435 | display = int(o.opts['display']) 436 | if host is None: 437 | screen = pygame.display.set_mode((220,40)) 438 | screen.fill((0,100,255)) #blue bg 439 | host = inputbox.ask(screen, "Host") 440 | if host == '': 441 | raise SystemExit 442 | if ':' in host: 443 | host, display = host.split(':') 444 | if host == '': host = 'localhost' 445 | display = int(display) 446 | 447 | # connect to this host and port, and reconnect if we get disconnected 448 | reactor.connectTCP( 449 | host, #remote hostname 450 | display + 5900, #TCP port number 451 | VNCFactory( 452 | remoteframebuffer, #the application/display 453 | depth, #color depth 454 | o.opts['fast'], #if a fast connection is used 455 | o.opts['password'], #password or none 456 | int(o.opts['shared']), #shared session flag 457 | ) 458 | ) 459 | 460 | # run the application 461 | reactor.callLater(0.1, remoteframebuffer.mainloop) 462 | reactor.run() 463 | 464 | 465 | if __name__ == '__main__': 466 | main() 467 | --------------------------------------------------------------------------------