BOOL vncClientThread::InitVersion() { // Generate the server's protocol version rfbProtocolVersionMsg protocolMsg; sprintf((char *)protocolMsg, rfbProtocolVersionFormat, rfbProtocolMajorVersion, rfbProtocolMinorVersion); // Send the protocol message if (!m_socket->SendExact((char *)&protocolMsg, sz_rfbProtocolVersionMsg)) return FALSE; // Now, get the client's protocol version rfbProtocolVersionMsg protocol_ver; protocol_ver[12] = 0; if (!m_socket->ReadExact((char *)&protocol_ver, sz_rfbProtocolVersionMsg)) return FALSE; // Check the protocol version int major, minor; sscanf((char *)&protocol_ver, rfbProtocolVersionFormat, &major, &minor); if (major != rfbProtocolMajorVersion) return FALSE; return TRUE; }
qint64 vsocketDispatcher( char * _buf, const qint64 _len, const SocketOpCodes _op_code, void * _user ) { VSocket * sock = static_cast<VSocket *>( _user ); switch( _op_code ) { case SocketRead: return( sock->ReadExact( _buf, _len ) ? _len : 0 ); case SocketWrite: return( sock->SendExact( _buf, _len ) ? _len : 0 ); case SocketGetPeerAddress: strncpy( _buf, sock->GetPeerName(), _len ); return( 0 ); } return( 0 ); }
void vncClientThread::run(void *arg) { // All this thread does is go into a socket-recieve loop, // waiting for stuff on the given socket // IMPORTANT : ALWAYS call RemoveClient on the server before quitting // this thread. vnclog.Print(LL_CLIENTS, VNCLOG("client connected : %s (%hd)\n"), m_client->GetClientName(), m_client->GetClientId()); // Save the handle to the thread's original desktop HDESK home_desktop = GetThreadDesktop(GetCurrentThreadId()); // To avoid people connecting and then halting the connection, set a timeout if (!m_socket->SetTimeout(30000)) vnclog.Print(LL_INTERR, VNCLOG("failed to set socket timeout(%d)\n"), GetLastError()); // Initially blacklist the client so that excess connections from it get dropped m_server->AddAuthHostsBlacklist(m_client->GetClientName()); // LOCK INITIAL SETUP // All clients have the m_protocol_ready flag set to FALSE initially, to prevent // updates and suchlike interfering with the initial protocol negotiations. // GET PROTOCOL VERSION if (!InitVersion()) { m_server->RemoveClient(m_client->GetClientId()); return; } vnclog.Print(LL_INTINFO, VNCLOG("negotiated version\n")); // AUTHENTICATE LINK if (!InitAuthenticate()) { m_server->RemoveClient(m_client->GetClientId()); return; } // Authenticated OK - remove from blacklist and remove timeout m_server->RemAuthHostsBlacklist(m_client->GetClientName()); m_socket->SetTimeout(m_server->AutoIdleDisconnectTimeout()*1000); vnclog.Print(LL_INTINFO, VNCLOG("authenticated connection\n")); // INIT PIXEL FORMAT // Get the screen format m_client->m_fullscreen = m_client->m_encodemgr.GetSize(); // Get the name of this desktop char desktopname[MAX_COMPUTERNAME_LENGTH+1]; DWORD desktopnamelen = MAX_COMPUTERNAME_LENGTH + 1; if (GetComputerName(desktopname, &desktopnamelen)) { // Make the name lowercase for (int x=0; x<strlen(desktopname); x++) { desktopname[x] = tolower(desktopname[x]); } } else { strcpy(desktopname, "WinVNC"); } // Send the server format message to the client rfbServerInitMsg server_ini; server_ini.format = m_client->m_encodemgr.m_buffer->GetLocalFormat(); // Endian swaps server_ini.framebufferWidth = Swap16IfLE(m_client->m_fullscreen.br.x); server_ini.framebufferHeight = Swap16IfLE(m_client->m_fullscreen.br.y); server_ini.format.redMax = Swap16IfLE(server_ini.format.redMax); server_ini.format.greenMax = Swap16IfLE(server_ini.format.greenMax); server_ini.format.blueMax = Swap16IfLE(server_ini.format.blueMax); server_ini.nameLength = Swap32IfLE(strlen(desktopname)); if (!m_socket->SendExact((char *)&server_ini, sizeof(server_ini))) { m_server->RemoveClient(m_client->GetClientId()); return; } if (!m_socket->SendExact(desktopname, strlen(desktopname))) { m_server->RemoveClient(m_client->GetClientId()); return; } vnclog.Print(LL_INTINFO, VNCLOG("sent pixel format to client\n")); // UNLOCK INITIAL SETUP // Initial negotiation is complete, so set the protocol ready flag m_client->EnableProtocol(); // Add a fullscreen update to the client's update list { omni_mutex_lock l(m_client->GetUpdateLock()); m_client->m_update_tracker.add_changed(m_client->m_fullscreen); } // Clear the CapsLock and NumLock keys if (m_client->m_keyboardenabled) { ClearKeyState(VK_CAPITAL); // *** JNW - removed because people complain it's wrong //ClearKeyState(VK_NUMLOCK); ClearKeyState(VK_SCROLL); } // MAIN LOOP // Set the input thread to a high priority set_priority(omni_thread::PRIORITY_HIGH); BOOL connected = TRUE; while (connected) { rfbClientToServerMsg msg; // Ensure that we're running in the correct desktop if (!vncService::InputDesktopSelected()) if (!vncService::SelectDesktop(NULL)) break; // Try to read a message ID if (!m_socket->ReadExact((char *)&msg.type, sizeof(msg.type))) { connected = FALSE; break; } // What to do is determined by the message id switch(msg.type) { case rfbSetPixelFormat: // Read the rest of the message: if (!m_socket->ReadExact(((char *) &msg)+1, sz_rfbSetPixelFormatMsg-1)) { connected = FALSE; break; } // Swap the relevant bits. msg.spf.format.redMax = Swap16IfLE(msg.spf.format.redMax); msg.spf.format.greenMax = Swap16IfLE(msg.spf.format.greenMax); msg.spf.format.blueMax = Swap16IfLE(msg.spf.format.blueMax); // Prevent updates while the pixel format is changed m_client->DisableProtocol(); // Tell the buffer object of the change if (!m_client->m_encodemgr.SetClientFormat(msg.spf.format)) { vnclog.Print(LL_CONNERR, VNCLOG("remote pixel format invalid\n")); connected = FALSE; } // Set the palette-changed flag, just in case... m_client->m_palettechanged = TRUE; // Re-enable updates m_client->EnableProtocol(); break; case rfbSetEncodings: // Read the rest of the message: if (!m_socket->ReadExact(((char *) &msg)+1, sz_rfbSetEncodingsMsg-1)) { connected = FALSE; break; } // Prevent updates while the encoder is changed m_client->DisableProtocol(); // Read in the preferred encodings msg.se.nEncodings = Swap16IfLE(msg.se.nEncodings); { int x; BOOL encoding_set = FALSE; // By default, don't use copyrect! m_client->m_update_tracker.enable_copyrect(false); for (x=0; x<msg.se.nEncodings; x++) { CARD32 encoding; // Read an encoding in if (!m_socket->ReadExact((char *)&encoding, sizeof(encoding))) { connected = FALSE; break; } // Is this the CopyRect encoding (a special case)? if (Swap32IfLE(encoding) == rfbEncodingCopyRect) { m_client->m_update_tracker.enable_copyrect(true); continue; } // Have we already found a suitable encoding? if (!encoding_set) { // No, so try the buffer to see if this encoding will work... if (m_client->m_encodemgr.SetEncoding(Swap32IfLE(encoding))) encoding_set = TRUE; } } // If no encoding worked then default to RAW! if (!encoding_set) { vnclog.Print(LL_INTINFO, VNCLOG("defaulting to raw encoder\n")); if (!m_client->m_encodemgr.SetEncoding(Swap32IfLE(rfbEncodingRaw))) { vnclog.Print(LL_INTERR, VNCLOG("failed to select raw encoder!\n")); connected = FALSE; } } } // Re-enable updates m_client->EnableProtocol(); break; case rfbFramebufferUpdateRequest: // Read the rest of the message: if (!m_socket->ReadExact(((char *) &msg)+1, sz_rfbFramebufferUpdateRequestMsg-1)) { connected = FALSE; break; } { rfb::Rect update; // Get the specified rectangle as the region to send updates for. update.tl.x = Swap16IfLE(msg.fur.x); update.tl.y = Swap16IfLE(msg.fur.y); update.br.x = update.tl.x + Swap16IfLE(msg.fur.w); update.br.y = update.tl.y + Swap16IfLE(msg.fur.h); rfb::Region2D update_rgn = update; if (update_rgn.is_empty()) { vnclog.Print(LL_INTERR, VNCLOG("FATAL! client update region is empty!\n")); connected = FALSE; break; } { omni_mutex_lock l(m_client->GetUpdateLock()); // Add the requested area to the incremental update cliprect m_client->m_incr_rgn = m_client->m_incr_rgn.union_(update_rgn); // Is this request for a full update? if (!msg.fur.incremental) { // Yes, so add the region to the update tracker m_client->m_update_tracker.add_changed(update_rgn); // Tell the desktop grabber to fetch the region's latest state m_client->m_encodemgr.m_buffer->m_desktop->QueueRect(update); } // Kick the update thread (and create it if not there already) m_client->TriggerUpdateThread(); } } break; case rfbKeyEvent: // Read the rest of the message: if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbKeyEventMsg-1)) { if (m_client->m_keyboardenabled) { msg.ke.key = Swap32IfLE(msg.ke.key); // Get the keymapper to do the work vncKeymap::keyEvent(msg.ke.key, msg.ke.down); m_client->m_remoteevent = TRUE; } } break; case rfbPointerEvent: // Read the rest of the message: if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbPointerEventMsg-1)) { if (m_client->m_pointerenabled) { // Convert the coords to Big Endian msg.pe.x = Swap16IfLE(msg.pe.x); msg.pe.y = Swap16IfLE(msg.pe.y); // Work out the flags for this event DWORD flags = MOUSEEVENTF_ABSOLUTE; long data = 0; if (msg.pe.x != m_client->m_ptrevent.x || msg.pe.y != m_client->m_ptrevent.y) flags |= MOUSEEVENTF_MOVE; if ( (msg.pe.buttonMask & rfbButton1Mask) != (m_client->m_ptrevent.buttonMask & rfbButton1Mask) ) { if (GetSystemMetrics(SM_SWAPBUTTON)) flags |= (msg.pe.buttonMask & rfbButton1Mask) ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; else flags |= (msg.pe.buttonMask & rfbButton1Mask) ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; } if ( (msg.pe.buttonMask & rfbButton2Mask) != (m_client->m_ptrevent.buttonMask & rfbButton2Mask) ) { flags |= (msg.pe.buttonMask & rfbButton2Mask) ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; } if ( (msg.pe.buttonMask & rfbButton3Mask) != (m_client->m_ptrevent.buttonMask & rfbButton3Mask) ) { if (GetSystemMetrics(SM_SWAPBUTTON)) flags |= (msg.pe.buttonMask & rfbButton3Mask) ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; else flags |= (msg.pe.buttonMask & rfbButton3Mask) ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; } // Mouse wheel support if (msg.pe.buttonMask & rfbWheelUpMask) { flags |= MOUSEEVENTF_WHEEL; data = WHEEL_DELTA; } if (msg.pe.buttonMask & rfbWheelDownMask) { flags |= MOUSEEVENTF_WHEEL; data = -WHEEL_DELTA; } // Generate coordinate values unsigned long x = (msg.pe.x * 65535) / (m_client->m_fullscreen.br.x); unsigned long y = (msg.pe.y * 65535) / (m_client->m_fullscreen.br.y); // Do the pointer event ::mouse_event(flags, (DWORD) x, (DWORD) y, data, 0); // Save the old position m_client->m_ptrevent = msg.pe; // Flag that a remote event occurred m_client->m_remoteevent = TRUE; // Tell the desktop hook system to grab the screen... m_client->m_encodemgr.m_buffer->m_desktop->TriggerUpdate(); } } break; case rfbClientCutText: // Read the rest of the message: if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbClientCutTextMsg-1)) { // Allocate storage for the text const UINT length = Swap32IfLE(msg.cct.length); char *text = new char [length+1]; if (text == NULL) break; text[length] = 0; // Read in the text if (!m_socket->ReadExact(text, length)) { delete [] text; break; } // Get the server to update the local clipboard m_server->UpdateLocalClipText(text); // Free the clip text we read delete [] text; } break; default: // Unknown message, so fail! connected = FALSE; } } // Move into the thread's original desktop vncService::SelectHDESK(home_desktop); // Quit this thread. This will automatically delete the thread and the // associated client. vnclog.Print(LL_CLIENTS, VNCLOG("client disconnected : %s (%hd)\n"), m_client->GetClientName(), m_client->GetClientId()); // Disable the protocol to ensure that the update thread // is not accessing the desktop and buffer objects m_client->DisableProtocol(); // Finally, it's safe to kill the update thread here if (m_client->m_updatethread) { m_client->m_updatethread->Kill(); m_client->m_updatethread->join(NULL); } // Remove the client from the server // This may result in the desktop and buffer being destroyed // It also guarantees that the client will not be passed further // updates m_server->RemoveClient(m_client->GetClientId()); }
BOOL vncClientThread::InitAuthenticate() { // Retrieve the local password char password[MAXPWLEN]; m_server->GetPassword(password); vncPasswd::ToText plain(password); // Verify the peer host name against the AuthHosts string vncServer::AcceptQueryReject verified; if (m_auth) { verified = vncServer::aqrAccept; } else { verified = m_server->VerifyHost(m_socket->GetPeerName()); } // If necessary, query the connection with a timed dialog if (verified == vncServer::aqrQuery) { vncAcceptDialog *acceptDlg = new vncAcceptDialog(m_server->QueryTimeout(), m_socket->GetPeerName()); if ((acceptDlg == 0) || (!(acceptDlg->DoDialog()))) verified = vncServer::aqrReject; } if (verified == vncServer::aqrReject) { CARD32 auth_val = Swap32IfLE(rfbConnFailed); char *errmsg = "Your connection has been rejected."; CARD32 errlen = Swap32IfLE(strlen(errmsg)); if (!m_socket->SendExact((char *)&auth_val, sizeof(auth_val))) return FALSE; if (!m_socket->SendExact((char *)&errlen, sizeof(errlen))) return FALSE; m_socket->SendExact(errmsg, strlen(errmsg)); return FALSE; } // By default we disallow passwordless workstations! if ((strlen(plain) == 0) && m_server->AuthRequired()) { vnclog.Print(LL_CONNERR, VNCLOG("no password specified for server - client rejected\n")); // Send an error message to the client CARD32 auth_val = Swap32IfLE(rfbConnFailed); char *errmsg = "This server does not have a valid password enabled. " "Until a password is set, incoming connections cannot be accepted."; CARD32 errlen = Swap32IfLE(strlen(errmsg)); if (!m_socket->SendExact((char *)&auth_val, sizeof(auth_val))) return FALSE; if (!m_socket->SendExact((char *)&errlen, sizeof(errlen))) return FALSE; m_socket->SendExact(errmsg, strlen(errmsg)); return FALSE; } // By default we filter out local loop connections, because they're pointless if (!m_server->LoopbackOk()) { char *localname = strdup(m_socket->GetSockName()); char *remotename = strdup(m_socket->GetPeerName()); // Check that the local & remote names are different! if ((localname != NULL) && (remotename != NULL)) { BOOL ok = strcmp(localname, remotename) != 0; if (localname != NULL) free(localname); if (remotename != NULL) free(remotename); if (!ok) { vnclog.Print(LL_CONNERR, VNCLOG("loopback connection attempted - client rejected\n")); // Send an error message to the client CARD32 auth_val = Swap32IfLE(rfbConnFailed); char *errmsg = "Local loop-back connections are disabled."; CARD32 errlen = Swap32IfLE(strlen(errmsg)); if (!m_socket->SendExact((char *)&auth_val, sizeof(auth_val))) return FALSE; if (!m_socket->SendExact((char *)&errlen, sizeof(errlen))) return FALSE; m_socket->SendExact(errmsg, strlen(errmsg)); return FALSE; } } } // Authenticate the connection, if required if (m_auth || (strlen(plain) == 0)) { // Send no-auth-required message CARD32 auth_val = Swap32IfLE(rfbNoAuth); if (!m_socket->SendExact((char *)&auth_val, sizeof(auth_val))) return FALSE; } else { // Send auth-required message CARD32 auth_val = Swap32IfLE(rfbVncAuth); if (!m_socket->SendExact((char *)&auth_val, sizeof(auth_val))) return FALSE; BOOL auth_ok = TRUE; { // Now create a 16-byte challenge char challenge[16]; vncRandomBytes((BYTE *)&challenge); // Send the challenge to the client if (!m_socket->SendExact(challenge, sizeof(challenge))) return FALSE; // Read the response char response[16]; if (!m_socket->ReadExact(response, sizeof(response)))\ return FALSE; // Encrypt the challenge bytes vncEncryptBytes((BYTE *)&challenge, plain); // Compare them to the response for (int i=0; i<sizeof(challenge); i++) { if (challenge[i] != response[i]) { auth_ok = FALSE; break; } } } // Did the authentication work? CARD32 authmsg; if (!auth_ok) { vnclog.Print(LL_CONNERR, VNCLOG("authentication failed\n")); authmsg = Swap32IfLE(rfbVncAuthFailed); m_socket->SendExact((char *)&authmsg, sizeof(authmsg)); return FALSE; } else { // Tell the client we're ok authmsg = Swap32IfLE(rfbVncAuthOK); if (!m_socket->SendExact((char *)&authmsg, sizeof(authmsg))) return FALSE; } } // Read the client's initialisation message rfbClientInitMsg client_ini; if (!m_socket->ReadExact((char *)&client_ini, sz_rfbClientInitMsg)) return FALSE; // If the client wishes to have exclusive access then remove other clients if (!client_ini.shared && !m_shared) { // Which client takes priority, existing or incoming? if (m_server->ConnectPriority() < 1) { // Incoming vnclog.Print(LL_INTINFO, VNCLOG("non-shared connection - disconnecting old clients\n")); m_server->KillAuthClients(); } else if (m_server->ConnectPriority() > 1) { // Existing if (m_server->AuthClientCount() > 0) { vnclog.Print(LL_CLIENTS, VNCLOG("connections already exist - client rejected\n")); return FALSE; } } } // Tell the server that this client is ok return m_server->Authenticated(m_client->GetClientId()); }