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()); }
void * vncDesktopThread::run_undetached(void *arg) { // Save the thread's "home" desktop, under NT (no effect under 9x) HDESK home_desktop = GetThreadDesktop(GetCurrentThreadId()); // Attempt to initialise and return success or failure if (!m_desktop->Startup()) { vncService::SelectHDESK(home_desktop); ReturnVal(FALSE); return NULL; } // Grab the initial display contents // *** m_desktop->m_buffer.GrabRegion(m_desktop->m_bmrect); // *** m_desktop->m_buffer.Clear(m_desktop->m_bmrect); // Succeeded to initialise ok ReturnVal(TRUE); // START PROCESSING DESKTOP MESSAGES // We set a flag inside the desktop handler here, to indicate it's now safe // to handle clipboard messages m_desktop->SetClipboardActive(TRUE); // All changes in the state of the display are stored in a local // UpdateTracker object, and are flushed to the vncServer whenever // client updates are about to be triggered rfb::SimpleUpdateTracker clipped_updates; rfb::ClippedUpdateTracker updates(clipped_updates, m_desktop->m_bmrect); clipped_updates.enable_copyrect(true); // Incoming update messages are collated into a single region cache // The region cache areas are checked for changes before an update // is triggered, and the changed areas are passed to the UpdateTracker rfb::Region2D rgncache = m_desktop->m_bmrect; // The previous cursor position is stored, to allow us to erase the // old instance whenever it moves. rfb::Point oldcursorpos; // Set the hook thread to a high priority // *** set_priority(omni_thread::PRIORITY_HIGH); BOOL idle_skip = TRUE; ULONG idle_skip_count = 0; MSG msg; while (TRUE) { if (!PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) { // // - MESSAGE QUEUE EMPTY // Whenever the message queue becomes empty, we check to see whether // there are updates to be passed to clients. if (idle_skip) { idle_skip = FALSE; if (idle_skip_count++ < 4) { Sleep(5); continue; } } idle_skip_count = 0; // Clear the triggered flag m_desktop->m_update_triggered = FALSE; // // CHECK SCREEN FORMAT // First, we must check that the screen hasnt changed too much. if (m_desktop->m_displaychanged || !vncService::InputDesktopSelected()) { rfbServerInitMsg oldscrinfo = m_desktop->m_scrinfo; m_desktop->m_displaychanged = FALSE; // Attempt to close the old hooks if (!m_desktop->Shutdown()) { m_server->KillAuthClients(); break; } // Now attempt to re-install them! if (!m_desktop->Startup()) { m_server->KillAuthClients(); break; } // Check that the screen info hasn't changed //vnclog.Print(LL_INTINFO, VNCLOG("SCR: old screen format %dx%dx%d\n"), // oldscrinfo.framebufferWidth, // oldscrinfo.framebufferHeight, // oldscrinfo.format.bitsPerPixel); //vnclog.Print(LL_INTINFO, VNCLOG("SCR: new screen format %dx%dx%d\n"), // m_desktop->m_scrinfo.framebufferWidth, // m_desktop->m_scrinfo.framebufferHeight, // m_desktop->m_scrinfo.format.bitsPerPixel); if ((m_desktop->m_scrinfo.framebufferWidth != oldscrinfo.framebufferWidth) || (m_desktop->m_scrinfo.framebufferHeight != oldscrinfo.framebufferHeight)) { m_server->KillAuthClients(); break; } else if (memcmp(&m_desktop->m_scrinfo.format, &oldscrinfo.format, sizeof(rfbPixelFormat)) != 0) { m_server->UpdateLocalFormat(); } // Adjust the UpdateTracker clip region updates.set_clip_region(m_desktop->m_bmrect); // Add a full screen update to all the clients rgncache = rgncache.union_(rfb::Region2D(m_desktop->m_bmrect)); m_server->UpdatePalette(); } // // CALCULATE CHANGES // if (m_desktop->m_server->UpdateWanted()) { // POLL PROBLEM AREAS // We add specific areas of the screen to the region cache, // causing them to be fetched for processing. if (m_desktop->m_server->PollFullScreen()) { rfb::Rect rect = m_desktop->m_qtrscreen; rect = rect.translate(rfb::Point(0, m_desktop->m_pollingcycle * m_desktop->m_qtrscreen.br.y)); rgncache = rgncache.union_(rfb::Region2D(rect)); m_desktop->m_pollingcycle = (m_desktop->m_pollingcycle + 1) % 4; } if (m_desktop->m_server->PollForeground()) { // Get the window rectangle for the currently selected window HWND hwnd = GetForegroundWindow(); if (hwnd != NULL) PollWindow(rgncache, hwnd); } if (m_desktop->m_server->PollUnderCursor()) { // Find the mouse position POINT mousepos; if (GetCursorPos(&mousepos)) { // Find the window under the mouse HWND hwnd = WindowFromPoint(mousepos); if (hwnd != NULL) PollWindow(rgncache, hwnd); } } // PROCESS THE MOUSE POINTER // Some of the hard work is done in clients, some here // This code fetches the desktop under the old pointer position // but the client is responsible for actually encoding and sending // it when required. // This code also renders the pointer and saves the rendered position // Clients include this when rendering updates. // The code is complicated in this way because we wish to avoid // rendering parts of the screen the mouse moved through between // client updates, since in practice they will probably not have changed. // Re-render the mouse's old location if it's moved BOOL cursormoved = FALSE; POINT cursorpos; if (GetCursorPos(&cursorpos) && ((cursorpos.x != oldcursorpos.x) || (cursorpos.y != oldcursorpos.y))) { cursormoved = TRUE; oldcursorpos = rfb::Point(cursorpos); } if (cursormoved) { if (!m_desktop->m_cursorpos.is_empty()) rgncache = rgncache.union_(rfb::Region2D(m_desktop->m_cursorpos)); } { // Prevent any clients from accessing the Buffer omni_mutex_lock l(m_desktop->m_update_lock); // CHECK FOR COPYRECTS // This actually just checks where the Foreground window is m_desktop->CalcCopyRects(updates); // GRAB THE DISPLAY // Fetch data from the display to our display cache. m_desktop->m_buffer.GrabRegion(rgncache); // Render the mouse m_desktop->m_buffer.GrabMouse(); if (cursormoved) { // Inform clients that it has moved m_desktop->m_server->UpdateMouse(); // Get the buffer to fetch the pointer bitmap if (!m_desktop->m_cursorpos.is_empty()) rgncache = rgncache.union_(rfb::Region2D(m_desktop->m_cursorpos)); } // SCAN THE CHANGED REGION FOR ACTUAL CHANGES // The hooks return hints as to areas that may have changed. // We check the suggested areas, and just send the ones that // have actually changed. // Note that we deliberately don't check the copyrect destination // here, to reduce the overhead & the likelihood of corrupting the // backbuffer contents. rfb::Region2D checkrgn = rgncache.subtract(clipped_updates.get_copied_region()); rgncache = clipped_updates.get_copied_region(); rfb::Region2D changedrgn; m_desktop->m_buffer.CheckRegion(changedrgn, checkrgn); // FLUSH UPDATES TO CLIENTS // Add the bits that have really changed to their update regions // Note that the cursor is NOT included - they must do that // themselves, for the reasons above. // This call implicitly kicks clients to update themselves updates.add_changed(changedrgn); clipped_updates.get_update(m_server->GetUpdateTracker()); } // Clear the update tracker and region cache clipped_updates.clear(); } // Now wait for more messages to be queued if (!WaitMessage()) { //vnclog.Print(LL_INTERR, VNCLOG("WaitMessage() failed\n")); break; } } else if (msg.message == RFB_SCREEN_UPDATE) { // Process an incoming update event // An area of the screen has changed rfb::Rect rect; rect.tl = rfb::Point((SHORT)LOWORD(msg.wParam), (SHORT)HIWORD(msg.wParam)); rect.br = rfb::Point((SHORT)LOWORD(msg.lParam), (SHORT)HIWORD(msg.lParam)); rect = rect.intersect(m_desktop->m_bmrect); if (!rect.is_empty()) { rgncache = rgncache.union_(rfb::Region2D(rect)); } idle_skip = TRUE; } else if (msg.message == RFB_MOUSE_UPDATE) { // Process an incoming mouse event // Save the cursor ID m_desktop->SetCursor((HCURSOR) msg.wParam); idle_skip = TRUE; } else if (msg.message == WM_QUIT) { break; } else { // Process any other messages normally DispatchMessage(&msg); idle_skip = TRUE; } } m_desktop->SetClipboardActive(FALSE); //vnclog.Print(LL_INTINFO, VNCLOG("quitting desktop server thread\n")); // Clear all the hooks and close windows, etc. m_desktop->Shutdown(); // Clear the shift modifier keys, now that there are no remote clients vncKeymap::ClearShiftKeys(); // Switch back into our home desktop, under NT (no effect under 9x) vncService::SelectHDESK(home_desktop); return NULL; }