;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2010-2015. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;; VNC client for KolibriOS ;; ;; ;; ;; Written by hidnplayr@kolibrios.org ;; ;; ;; ;; GNU GENERAL PUBLIC LICENSE ;; ;; Version 2, June 1991 ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; thread_start: mcall 40, 0 ; disable all events for this thread ; Extract port number from server address mov esi, serveraddr @@: lodsb test al, al jz .port_done cmp al, ':' jne @r mov byte[esi-1], 0 ; replace colon with 0 byte, we dont want to upset getaddrinfo xor eax, eax xor ebx, ebx ; port number @@: lodsb test al, al jz @f sub al, '0' jb err_dns cmp al, 9 ja err_dns lea ebx, [ebx*4+ebx] lea ebx, [ebx*2+eax] jmp @b @@: xchg bl, bh mov [sockaddr1.port], bx .port_done: ; Resolve hostname push esp ; reserve stack place invoke getaddrinfo, serveraddr, 0, 0, esp pop esi test eax, eax jnz err_dns mov eax, [esi+addrinfo.ai_addr] mov eax, [eax+sockaddr_in.sin_addr] mov [sockaddr1.ip], eax invoke freeaddrinfo, esi DEBUGF 1, "Connecting to %u.%u.%u.%u:%u\n", \ [sockaddr1.ip]:1, [sockaddr1.ip+1]:1, [sockaddr1.ip+2]:1, [sockaddr1.ip+3]:1, \ [sockaddr1.port]:2 ; Open socket mcall socket, AF_INET4, SOCK_STREAM, 0 cmp eax, -1 je err_sock mov [socketnum], eax ; Connect to the server mcall connect, [socketnum], sockaddr1, 18 cmp eax, -1 je err_connect ; Verify handshake from server call read_data cmp eax, 12 jb err_proto cmp dword[esi], "RFB " jne err_proto add esi, 12 ; Did we get an error message already? cmp eax, 16 jb @f lodsd test eax, eax je err_handshake @@: ; Reply to handshake DEBUGF 1, "Sending handshake\n" mcall send, [socketnum], HandShake, 12, 0 ; VNC 3.3 protocol: server decides security type call read_data cmp eax, 4 jb err_proto lodsd cmp eax, 0x00000000 je err_handshake cmp eax, 0x01000000 ; no security je initialize cmp eax, 0x02000000 ; VNC security je vnc_security jmp err_proto vnc_security: lea eax, [esi+8] cmp [datapointer], eax jb err_proto push esi ; pointer to message mov dword[password], 0 mov dword[password+4], 0 and [USERbox.flags], not ed_focus or [USERbox.flags], ed_disabled or [PASSbox.flags], ed_focus mov [status], STATUS_REQ_LOGIN or [work], WORK_GUI @@: mcall 5, 10 cmp [status], STATUS_LOGIN je @f cmp [status], STATUS_REQ_LOGIN je @r mcall -1 @@: DEBUGF 1, "VNC authentication\n" ; Bit reverse the password and create DES keys mov ebx, dword[password] mov edx, ebx and ebx, 0xf0f0f0f0 shr ebx, 4 and edx, 0x0f0f0f0f shl edx, 4 or ebx, edx mov edx, ebx and ebx, 0xCCCCCCCC shr ebx, 2 and edx, 0x33333333 shl edx, 2 or ebx, edx mov edx, ebx and ebx, 0xAAAAAAAA shr ebx, 1 and edx, 0x55555555 shl edx, 1 or ebx, edx bswap ebx mov eax, dword[password+4] mov edx, eax and eax, 0xf0f0f0f0 shr eax, 4 and edx, 0x0f0f0f0f shl edx, 4 or eax, edx mov edx, eax and eax, 0xCCCCCCCC shr eax, 2 and edx, 0x33333333 shl edx, 2 or eax, edx mov edx, eax and eax, 0xAAAAAAAA shr eax, 1 and edx, 0x55555555 shl edx, 1 or edx, eax bswap edx mov edi, keys call DES_create_keys ; Encrypt message with DES mov esi, [esp] mov ebx, dword[esi+0] mov edx, dword[esi+4] call encrypt_DES mov esi, [esp] mov dword[esi+0], ebx mov dword[esi+4], edx mov ebx, dword[esi+8] mov edx, dword[esi+12] call encrypt_DES mov esi, [esp] mov dword[esi+8], ebx mov dword[esi+12], edx ; Blank out the password and key fields in RAM mov edi, password mov ecx, 384/4 xor eax, eax rep stosd ; Send the authentication response to server pop edx mcall send, [socketnum], , 16, 0 securityresult: ; Wait for SecurityResult from server call read_data cmp eax, 4 jb err_proto cmp dword[esi], 0 ; OK jne err_login initialize: DEBUGF 1, "Sending ClientInit\n" mcall send, [socketnum], ClientInit, 1, 0 call read_data ; now the server should send init message cmp eax, ServerInit.name jb err_proto DEBUGF 2, "Serverinit: bpp: %u depth: %u bigendian: %u truecolor: %u\n", \ [esi+ServerInit.pixelformat.bpp]:1, \ [esi+ServerInit.pixelformat.depth]:1, \ [esi+ServerInit.pixelformat.big_endian]:1, \ [esi+ServerInit.pixelformat.true_color]:1 mov eax, dword[esi+ServerInit.width] mov dword[FramebufferUpdateRequest.width], eax bswap eax mov dword[screen], eax DEBUGF 1, "Screen width=%u, height=%u\n", [screen.width]:2, [screen.height]:2 ; Set main window caption to servername mov ecx, dword[esi+ServerInit.name_length] bswap ecx add esi, ServerInit.name lea eax, [esi+ecx] cmp [datapointer], eax jb err_proto cmp ecx, 64 ; Limit name length to 64 chars jbe @f mov ecx, 64 @@: mov edi, servername rep movsb mov byte[edi], 0 mov [name.dash], "-" DEBUGF 1, "Sending pixel format\n" mcall send, [socketnum], SetPixelFormat, 20, 0 DEBUGF 1, "Sending encoding info\n" mcall send, [socketnum], SetEncodings, SetEncodings.length, 0 ; Tell the main thread we are ready for business! mov [status], STATUS_CONNECTED ; Request initial framebuffer update from server mov [FramebufferUpdateRequest.inc], 0 request_fbu: DEBUGF 1, "Requesting framebuffer update\n" mcall send, [socketnum], FramebufferUpdateRequest, 10, 0 mov [FramebufferUpdateRequest.inc], 1 thread_loop: call read_data ; Read the data into the buffer lodsb cmp al, 0 je framebufferupdate cmp al, 1 je setcolourmapentries cmp al, 2 je bell cmp al, 3 je servercuttext DEBUGF 2, "Unknown server command: %u\n", al jmp thread_loop framebufferupdate: @@: lea eax, [esi+6] cmp [datapointer], eax jae @f call read_data.more jmp @b @@: inc esi ; padding lodsw xchg al, ah mov [rectangles], ax DEBUGF 1, "Framebufferupdate: %u rectangles\n", ax rectangle_loop: @@: lea eax, [esi+12] cmp [datapointer], eax jae @f call read_data.more jmp @b @@: xor eax, eax lodsw xchg al, ah mov [rectangle.x], eax lodsw xchg al, ah mov [rectangle.y], eax lodsw xchg al, ah mov [rectangle.width], eax lodsw xchg al, ah mov [rectangle.height], eax lodsd ; encoding bswap eax DEBUGF 1, "Rectangle: x=%u y=%u width=%u height=%u encoding: ",\ [rectangle.x]:2, [rectangle.y]:2, [rectangle.width]:2, [rectangle.height]:2 cmp eax, 0 je encoding_raw cmp eax, 1 je encoding_CopyRect cmp eax, 2 je encoding_RRE cmp eax, 15 je encoding_TRLE cmp eax, 16 je encoding_ZRLE cmp eax, 0xffffff11 je encoding_cursor DEBUGF 2, "unknown encoding: %u\n", eax jmp thread_loop next_rectangle: or [work], WORK_FRAMEBUFFER dec [rectangles] jnz rectangle_loop jmp request_fbu setcolourmapentries: DEBUGF 1, "Server sent SetColourMapEntries message\n" @@: lea eax, [esi+5] cmp [datapointer], eax jae @f call read_data.more jmp @b @@: inc esi ; padding xor eax, eax lodsw ; first color (just ignore for now) lodsw ; number of colors (use to find end of message) xchg al, ah lea eax, [eax*2+eax] shl eax, 1 @@: push eax add eax, esi cmp [datapointer], eax jae @f call read_data.more pop eax jmp @b @@: pop eax add esi, eax ; Just skip it for now. jmp thread_loop bell: mcall 55, 55, , , beep jmp thread_loop servercuttext: DEBUGF 1, "Server cut text\n" @@: lea eax, [esi+7] cmp [datapointer], eax jae @f call read_data.more jmp @b @@: add esi, 3 lodsd bswap eax mov ecx, eax @@: lea eax, [esi+ecx] cmp [datapointer], eax jae @f call read_data.more jmp @b @@: ; TODO: paste text to clipboard DEBUGF 1, "%u bytes of text\n", ecx add esi, ecx jmp thread_loop read_data: mov [datapointer], receive_buffer mov esi, receive_buffer .more: push ebx ecx edx esi edi neg esi add esi, receive_buffer + RECEIVE_BUFFER_SIZE jz .buffer_end_reached xor edi, edi mcall recv, [socketnum], [datapointer] pop edi esi edx ecx ebx cmp eax, -1 je err_sock test eax, eax jz err_disconnected add [datapointer], eax ret .buffer_end_reached: DEBUGF 1, "end of buffer reached, re-organizing\n" pop edi esi edx ecx ebx ; Buffer is full, first needed data by program is pointed to by esi. ; Move all usefull data to begin of buffer cmp esi, receive_buffer je err_proto mov ecx, [datapointer] sub ecx, esi mov edi, receive_buffer rep movsb mov [datapointer], edi ; new end of data mov esi, receive_buffer ; new start of data jmp .more err_disconnected: mov [status], STATUS_DISCONNECTED or [work], WORK_GUI mcall -1 err_dns: mov [status], STATUS_DNS_ERR or [work], WORK_GUI mcall -1 err_sock: ; TODO: distinguish between different socket errors! DEBUGF 2, "Socket error: %u\n", ebx mov [status], STATUS_SOCK_ERR or [work], WORK_GUI mcall -1 err_connect: mov [status], STATUS_CONNECT_ERR or [work], WORK_GUI mcall -1 ret err_proto: mov [status], STATUS_PROTO_ERR or [work], WORK_GUI mcall -1 ret err_handshake: mov [status], STATUS_SECURITY_ERR lodsd ; Custom message from server? test eax, eax jz .no_msg bswap eax mov ecx, eax cmp ecx, 512 jb @f mov ecx, 512 @@: mov edi, sz_err_security_c rep movsb mov byte[edi], 0 mov [status], STATUS_SECURITY_ERR_C .no_msg: or [work], WORK_GUI mcall -1 ret err_login: mov [status], STATUS_LOGIN_FAILED or [work], WORK_GUI mcall -1 ret