/// <summary> /// Worker thread lives here and processes protocol messages infinitely, triggering events or other actions as necessary. /// </summary> private void GetRfbUpdates() { Thread.Sleep(1000); int rectangles; int enc; // Get the initial destkop from the host RequestScreenUpdate(true); while (true) { if (CheckIfThreadDone()) { break; } try { switch (rfb.ReadServerMessageType()) { case RfbProtocol.FRAMEBUFFER_UPDATE: rectangles = rfb.ReadFramebufferUpdate(); if (CheckIfThreadDone()) { break; } // TODO: consider gathering all update rectangles in a batch and *then* posting the event back to the main thread. for (int i = 0; i < rectangles; ++i) { // Get the update rectangle's info Rectangle rectangle; rfb.ReadFramebufferUpdateRectHeader(out rectangle, out enc); // Build a derived EncodedRectangle type and pull-down all the pixel info EncodedRectangle er = factory.Build(rectangle, BufferInfos.BitsPerPixel, enc); er.Decode(); // Let the UI know that an updated rectangle is available, but check // to see if the user closed things down first. if (!CheckIfThreadDone()) { updates.Add(er); } } break; case RfbProtocol.BELL: Beep(500, 300); // TODO: are there better values than these? break; case RfbProtocol.SERVER_CUT_TEXT: if (CheckIfThreadDone()) { break; } // TODO: This is invasive, should there be a bool property allowing this message to be ignored? // Clipboard.SetDataObject(rfb.ReadServerCutText().Replace("\n", Environment.NewLine), true); OnServerCutText(); break; case RfbProtocol.SET_COLOUR_MAP_ENTRIES: rfb.ReadColourMapEntry(); break; } } catch (Exception e) { OnConnectionLost(e); } } }
// vnc client handling - main thread private void RunNetThread() { while (!terminate) { if (autoconnect && host != "") { try { sleeptime = 0; Log.Info("Connecting to \"" + host + ":" + port + "\"..."); rfb = new RfbProtocol(); rfb.Connect(host, (int)port); Log.Debug("Connected."); rfb.ReadProtocolVersion(); // Handle possible repeater connection if (rfb.ServerVersion == 0.0) { rfb.WriteProxyAddress(); // Now we are connected to the real server; read the protocol version of the // server rfb.ReadProtocolVersion(); // Resume normal handshake and protocol } rfb.WriteProtocolVersion(); Log.Debug("Protocol Version:" + rfb.ServerVersion); // Figure out which type of authentication the server uses byte[] types = rfb.ReadSecurityTypes(); // Based on what the server sends back in the way of supported Security Types, one of // two things will need to be done: either the server will reject the connection (i.e., type = 0), // or a list of supported types will be sent, of which we need to choose and use one. if (types.Length > 0) { if (types[0] == 0) { // The server is not able (or willing) to accept the connection. // A message follows indicating why the connection was dropped. throw new Exception("Connection Failed. The server rejected the connection for the following reason: " + rfb.ReadSecurityFailureReason()); } else { byte securityType = GetSupportedSecurityType(types); if (securityType == 0) { throw new Exception("Unknown Security Type(s), The server sent one or more unknown Security Types."); } rfb.WriteSecurityType(securityType); // Protocol 3.8 states that a SecurityResult is still sent when using NONE (see 6.2.1) if (rfb.ServerVersion >= 3.799f && rfb.ServerVersion <= 3.801 && securityType == 1) { if (rfb.ReadSecurityResult() > 0) { // For some reason, the server is not accepting the connection. Get the // reason and throw an exception throw new Exception("Unable to Connect to the Server. The Server rejected the connection for the following reason: " + rfb.ReadSecurityFailureReason()); } } if (securityType > 1) { byte[] challenge = rfb.ReadSecurityChallenge(); rfb.WriteSecurityResponse(EncryptChallenge(pass, challenge)); if (rfb.ReadSecurityResult() != 0) { // Authentication failed, and if the server is using Protocol version 3.8, a // plain text message follows indicating why the error happend. I'm not // currently using this message, but it is read here to clean out the stream. // In earlier versions of the protocol, the server will just drop the connection. autoconnect = false; if (rfb.ServerVersion == 3.8) { throw new Exception("The server denyes the authentication. Reason: \"" + rfb.ReadSecurityFailureReason() + "\""); } throw new Exception("The server denyes the authentication."); } } } } else { // Something is wrong, since we should have gotten at least 1 Security Type throw new Exception("Protocol Error Connecting to Server. The Server didn't send any Security Types during the initial handshake."); } // Finish initializing protocol with host rfb.WriteClientInitialisation(shared); buffer = rfb.ReadServerInit(); rfb.WriteSetPixelFormat(buffer); // just use the server's framebuffer format if (hideRemoteCursor) { rfb.WriteSetEncodings(new int[] { RfbProtocol.ZRLE_ENCODING, RfbProtocol.HEXTILE_ENCODING, RfbProtocol.RRE_ENCODING, RfbProtocol.COPYRECT_ENCODING, RfbProtocol.RAW_ENCODING, RfbProtocol.CURSOR_PSEUDO_ENCODING }); } else { rfb.WriteSetEncodings(new int[] { RfbProtocol.ZRLE_ENCODING, RfbProtocol.HEXTILE_ENCODING, RfbProtocol.RRE_ENCODING, RfbProtocol.COPYRECT_ENCODING, RfbProtocol.RAW_ENCODING }); } // Create an EncodedRectangleFactory so that EncodedRectangles can be built according to set pixel layout factory = new EncodedRectangleFactory(rfb, buffer); // Get the initial destkop from the host rfb.WriteFramebufferUpdateRequest(0, 0, (ushort)buffer.Width, (ushort)buffer.Height, false); while (!terminate && autoconnect) { switch (rfb.ReadServerMessageType()) { case RfbProtocol.FRAMEBUFFER_UPDATE: int rectangles = rfb.ReadFramebufferUpdate(); for (int i = 0; i < rectangles; ++i) { // Get the update rectangle's info Rectangle rectangle; int enc; rfb.ReadFramebufferUpdateRectHeader(out rectangle, out enc); // Build a derived EncodedRectangle type and pull-down all the pixel info EncodedRectangle er = factory.Build(rectangle, enc); er.Decode(); er.Draw(); } break; case RfbProtocol.BELL: Log.Debug("Bell"); break; case RfbProtocol.SERVER_CUT_TEXT: rfb.ReadServerCutText(); Log.Debug("server-cut-text"); break; case RfbProtocol.SET_COLOUR_MAP_ENTRIES: Log.Debug("SET_COLOUR_MAP_ENTRIES"); rfb.ReadColourMapEntry(); break; default: throw new Exception("Unknown messagetype."); } invalid = true; rfb.WriteFramebufferUpdateRequest(0, 0, (ushort)buffer.Width, (ushort)buffer.Height, true); } } catch (SocketException) { Log.Info("Unable to connect to the server. Retry in 60s."); sleeptime = 60000; } catch (IOException e) { Log.Debug("Exception in NetThread(" + e.GetType().ToString() + "): " + e.Message); } catch (Exception e) { Log.Error("Exception in NetThread(" + e.GetType().ToString() + "): " + e.Message); sleeptime = 10000; } try { rfb.Close(); } catch (Exception) {} rfb = null; buffer = null; factory = null; invalid = true; Thread.Sleep(sleeptime); } } }