/// <summary> /// Opens the connection /// </summary> private async Task OpenAsyncInternal(Logger logger) { //switch state to connecting if not done so int state = Interlocked.CompareExchange(ref _connectionState, 1, 0); if (state == 1) return; if (state == 2) throw new ObjectDisposedException("Connection disposed before opening!"); try { //create TCP connection _client = new TcpClient(); await _client.ConnectAsync(_address, _cluster.Config.Port).ConfigureAwait(false); _writeStream = _client.GetStream(); _readStream = _client.GetStream(); logger.LogVerbose("TCP connection to {0} is opened", Address); //start readloop StartReadingAsync(); //get compression option _allowCompression = false; //assume false unless if (_cluster.Config.AllowCompression) { //check wether compression is supported by getting compression options from server var options = new OptionsFrame(); var supported = await SendRequestAsync(options, logger, 1, true).ConfigureAwait(false) as SupportedFrame; if (supported == null) throw new ProtocolException(0, "Expected Supported frame not received"); IList<string> compressionOptions; //check if options contain compression if (supported.SupportedOptions.TryGetValue("COMPRESSION", out compressionOptions)) { //check wether snappy is supported _allowCompression = compressionOptions.Contains("snappy"); } //dispose supported frame supported.Dispose(); } //submit startup frame var startup = new StartupFrame(_cluster.Config.CqlVersion); if (_allowCompression) { logger.LogVerbose("Enabling Snappy Compression."); startup.Options["COMPRESSION"] = "snappy"; } Frame response = await SendRequestAsync(startup, logger, 1, true).ConfigureAwait(false); //authenticate if required var auth = response as AuthenticateFrame; if (auth != null) { logger.LogVerbose("Authentication requested, attempting to provide credentials", Address); //check if _username is actually set if (_cluster.Config.Username == null || _cluster.Config.Password == null) throw new UnauthorizedException("No credentials provided"); //dispose AuthenticateFrame response.Dispose(); var cred = new CredentialsFrame(_cluster.Config.Username, _cluster.Config.Password); response = await SendRequestAsync(cred, logger, 1, true).ConfigureAwait(false); } //check if ready if (!(response is ReadyFrame)) throw new ProtocolException(0, "Expected Ready frame not received"); //dispose ready frame response.Dispose(); using (logger.ThreadBinding()) { if (OnConnectionChange != null) OnConnectionChange(this, new ConnectionChangeEvent { Connected = true }); } logger.LogInfo("{0} is opened and ready for use", this); } catch (Exception ex) { using (logger.ThreadBinding()) { Dispose(true, ex); throw; } } }
/// <summary> /// Authenticates the connection. /// </summary> /// <param name="auth">The authentication request from the server.</param> /// <param name="logger">The logger.</param> /// <returns></returns> /// <exception cref="AuthenticationException"> /// Unsupported Authenticator: + auth.Authenticator;null /// or /// Authentication failed, SASL Challenge was rejected by client /// or /// Authentication failed, Authenticator rejected SASL result /// or /// Expected a Authentication Challenge from Server! /// or /// No credentials provided in configuration /// or /// Authentication failed: Ready frame not received /// </exception> private async Task AuthenticateAsync(AuthenticateFrame auth, Logger logger) { logger.LogVerbose("Authentication requested, attempting to provide credentials"); //dispose AuthenticateFrame auth.Dispose(); if (auth.ProtocolVersion >= 2) { //protocol version2: use SASL AuthResponse to authenticate //get an AuthenticatorFactory IAuthenticatorFactory factory = Loader.Extensions.AuthenticationFactories.FirstOrDefault( f => f.Name.Equals(auth.Authenticator, StringComparison.OrdinalIgnoreCase)); if (factory == null) throw new AuthenticationException(auth.ProtocolVersion, "Unsupported Authenticator: " + auth.Authenticator); logger.LogVerbose("Attempting authentication for scheme {0}", factory.Name); //grab an authenticator instance IAuthenticator authenticator = factory.CreateAuthenticator(_config); //start authentication loop byte[] saslChallenge = null; while (true) { //check for challenge byte[] saslResponse; if (!authenticator.Authenticate(auth.ProtocolVersion, saslChallenge, out saslResponse)) { throw new AuthenticationException(auth.ProtocolVersion, "Authentication failed, SASL Challenge was rejected by client"); } //send response var cred = new AuthResponseFrame(saslResponse); var authResponse = await SendRequestAsyncInternal(cred, logger, 1, CancellationToken.None).AutoConfigureAwait(); //dispose authResponse (makes sure all is read) authResponse.Dispose(); //check for success var success = authResponse as AuthSuccessFrame; if (success != null) { if (!authenticator.Authenticate(auth.ProtocolVersion, success.SaslResult)) { throw new AuthenticationException(authResponse.ProtocolVersion, "Authentication failed, Authenticator rejected SASL result", authResponse.TracingId); } //yeah, authenticated, break from the authentication loop break; } //no success yet, lets try next round var challenge = authResponse as AuthChallengeFrame; if (challenge == null) { throw new AuthenticationException(authResponse.ProtocolVersion, "Expected a Authentication Challenge from Server!", authResponse.TracingId); } saslChallenge = challenge.SaslChallenge; } } else { //protocol version1: use Credentials to authenticate //check if _username is actually set if (_config.Username == null || _config.Password == null) throw new AuthenticationException(auth.ProtocolVersion, "No credentials provided in configuration"); var cred = new CredentialsFrame(_config.Username, _config.Password); var authResponse = await SendRequestAsyncInternal(cred, logger, 1, CancellationToken.None).AutoConfigureAwait(); //dispose authResponse (makes sure all is read) authResponse.Dispose(); if (!(authResponse is ReadyFrame)) { throw new AuthenticationException(authResponse.ProtocolVersion, "Authentication failed: Ready frame not received", authResponse.TracingId); } } }