private async Task <RfbProtocolVersion> NegotiateProtocolVersionAsync(ITransport transport, CancellationToken cancellationToken = default) { // Read maximum supported server protocol version RfbProtocolVersion serverProtocolVersion = await ReadProtocolVersionAsync(transport, cancellationToken).ConfigureAwait(false); // Select used protocol version RfbProtocolVersion clientProtocolVersion; if (serverProtocolVersion == RfbProtocolVersion.Unknown) { clientProtocolVersion = RfbProtocolVersions.LatestSupported; _logger.LogDebug("Supported server protocol version is unknown, too new? Trying latest protocol version {clientProtocolVersion}.", clientProtocolVersion.ToReadableString()); } else if (serverProtocolVersion > RfbProtocolVersions.LatestSupported) { clientProtocolVersion = RfbProtocolVersions.LatestSupported; _logger.LogDebug("Supported server protocol version {serverProtocolVersion} is too new. Requesting latest version supported by the client.", serverProtocolVersion.ToReadableString()); } else { clientProtocolVersion = serverProtocolVersion; _logger.LogDebug("Server supports protocol version {serverProtocolVersion}. Choosing that as the highest one that's supported by both sides.", serverProtocolVersion.ToReadableString()); } // Send selected protocol version await SendProtocolVersionAsync(transport, clientProtocolVersion, cancellationToken).ConfigureAwait(false); return(clientProtocolVersion); }
/// <inheritdoc /> public async Task <ITransport?> DoHandshakeAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); _logger.LogDebug("Doing protocol handshake..."); ITransport currentTransport = _context.Transport ?? throw new InvalidOperationException("Cannot do handshake before a transport has been created."); // Negotiate the protocol version that both sides will use RfbProtocolVersion protocolVersion = await NegotiateProtocolVersionAsync(currentTransport, cancellationToken).ConfigureAwait(false); _state.ProtocolVersion = protocolVersion; // Negotiate which security type will be used ISecurityType usedSecurityType = await NegotiateSecurityTypeAsync(currentTransport, cancellationToken).ConfigureAwait(false); _state.UsedSecurityType = usedSecurityType; // Execute authentication _logger.LogDebug("Negotiated security type: {name}({id}). Authenticating...", usedSecurityType.Name, usedSecurityType.Id); AuthenticationResult authenticationResult = await usedSecurityType .AuthenticateAsync(_context.Connection.Parameters.AuthenticationHandler, cancellationToken).ConfigureAwait(false); // When a tunnel was built, use that transport for further communication if (authenticationResult.TunnelTransport != null) { currentTransport = authenticationResult.TunnelTransport; } if (authenticationResult.ExpectSecurityResult) { // Read the security result ReadOnlyMemory <byte> securityResultBytes = await currentTransport.Stream.ReadAllAsync(4, cancellationToken).ConfigureAwait(false); uint securityResult = BinaryPrimitives.ReadUInt32BigEndian(securityResultBytes.Span); // Authentication failed? if (securityResult > 0) { // From version 3.8 onwards the server sends a reason if (protocolVersion >= RfbProtocolVersion.RFB_3_8) { string reason = await ReadFailureReasonAsync(currentTransport, cancellationToken).ConfigureAwait(false); throw new HandshakeFailedException($"Authentication failed: {reason}"); } // There is no reason throw new HandshakeFailedException("Authentication failed" + (securityResult == 2 ? " because of too many attempts." : ".")); } } return(authenticationResult.TunnelTransport); }
private async Task SendProtocolVersionAsync(ITransport transport, RfbProtocolVersion protocolVersion, CancellationToken cancellationToken = default) { _logger.LogDebug("Sending protocol version {protocolVersion}...", protocolVersion.ToReadableString()); string protocolVersionString = protocolVersion.GetStringRepresentation() + '\n'; byte[] bytes = Encoding.ASCII.GetBytes(protocolVersionString); Debug.Assert(bytes.Length == 12, "bytes.Length == 12"); await transport.Stream.WriteAsync(bytes, cancellationToken).ConfigureAwait(false); }
private async Task <RfbProtocolVersion> ReadProtocolVersionAsync(ITransport transport, CancellationToken cancellationToken = default) { _logger.LogDebug("Reading protocol version..."); // The protocol version info always consists of 12 bytes ReadOnlyMemory <byte> bytes = await transport.Stream.ReadAllAsync(12, cancellationToken).ConfigureAwait(false); string protocolVersionString = Encoding.ASCII.GetString(bytes.Span).TrimEnd('\n'); RfbProtocolVersion protocolVersion = RfbProtocolVersions.GetFromStringRepresentation(protocolVersionString); if (protocolVersion == RfbProtocolVersion.Unknown) { _logger.LogWarning("Unknown protocol version {protocolVersionString}.", protocolVersionString); } return(protocolVersion); }
/// <summary> /// Sets the value of the <seealso cref="RfbConnection.ProtocolVersion"/> property on the <see cref="RfbConnection"/> object. /// </summary> /// <param name="protocolVersion">The new protocol version value.</param> public void SetProtocolVersion(RfbProtocolVersion protocolVersion) => _connection.ProtocolVersion = protocolVersion;
/// <summary> /// Returns the string representation of a given protocol version. /// </summary> /// <param name="protocolVersion">The protocol version.</param> /// <returns>The string representation defined by the protocol.</returns> public static string GetStringRepresentation(this RfbProtocolVersion protocolVersion) => protocolVersion switch {