This is an automated email from the ASF dual-hosted git repository.

curth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git


The following commit(s) were added to refs/heads/main by this push:
     new eb6c088a1 feat(csharp/src/Drivers/Apache): Add support for Sasl 
transport in Hive and Impala ADBC Driver (#2822)
eb6c088a1 is described below

commit eb6c088a11bc946408d44796c5492f897337bbf0
Author: amangoyal <[email protected]>
AuthorDate: Wed May 14 20:00:25 2025 +0530

    feat(csharp/src/Drivers/Apache): Add support for Sasl transport in Hive and 
Impala ADBC Driver (#2822)
    
    1. Added support of SASL transport in thrift folder.
    2. Implemented PLAIN mechanism in SASL transport
    3. Added HiveServer2StandardConnection to support binary transport mode.
    4. Used SASL transport in Hiveserver2 standard connection with basic
    auth.
    
    ---------
    
    Co-authored-by: Aman Goyal <[email protected]>
    Co-authored-by: Sudhir Emmadi <[email protected]>
---
 .../src/Apache.Arrow.Adbc/Apache.Arrow.Adbc.csproj |   2 +-
 .../Apache/Hive2/HiveServer2ConnectionFactory.cs   |   7 +-
 .../Apache/Hive2/HiveServer2ExtendedConnection.cs  | 177 ++++++++++++++++++++
 .../Apache/Hive2/HiveServer2HttpConnection.cs      | 147 +----------------
 .../Drivers/Apache/Hive2/HiveServer2Parameters.cs  |   1 +
 .../Apache/Hive2/HiveServer2StandardConnection.cs  | 176 ++++++++++++++++++++
 .../Apache/Hive2/HiveServer2TransportType.cs       |   6 +-
 .../Apache/Impala/ImpalaStandardConnection.cs      |  26 ++-
 .../Drivers/Apache/Thrift/Sasl/ISaslMechanism.cs   |  43 +++++
 .../Apache/Thrift/Sasl/NegotiationStatus.cs        |  65 ++++++++
 .../Apache/Thrift/Sasl/PlainSaslMechanism.cs       |  66 ++++++++
 .../Drivers/Apache/Thrift/Sasl/TSaslTransport.cs   | 181 +++++++++++++++++++++
 12 files changed, 752 insertions(+), 145 deletions(-)

diff --git a/csharp/src/Apache.Arrow.Adbc/Apache.Arrow.Adbc.csproj 
b/csharp/src/Apache.Arrow.Adbc/Apache.Arrow.Adbc.csproj
index 598655d59..12110bafa 100644
--- a/csharp/src/Apache.Arrow.Adbc/Apache.Arrow.Adbc.csproj
+++ b/csharp/src/Apache.Arrow.Adbc/Apache.Arrow.Adbc.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
diff --git a/csharp/src/Drivers/Apache/Hive2/HiveServer2ConnectionFactory.cs 
b/csharp/src/Drivers/Apache/Hive2/HiveServer2ConnectionFactory.cs
index 1666ff264..47d3e32c4 100644
--- a/csharp/src/Drivers/Apache/Hive2/HiveServer2ConnectionFactory.cs
+++ b/csharp/src/Drivers/Apache/Hive2/HiveServer2ConnectionFactory.cs
@@ -33,7 +33,12 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
             {
                 throw new ArgumentOutOfRangeException(nameof(properties), 
$"Unsupported or unknown value '{type}' given for property 
'{HiveServer2Parameters.TransportType}'. Supported types: 
{HiveServer2TransportTypeParser.SupportedList}");
             }
-            return new HiveServer2HttpConnection(properties);
+            return typeValue switch
+            {
+                HiveServer2TransportType.Http => new 
HiveServer2HttpConnection(properties),
+                HiveServer2TransportType.Standard => new 
HiveServer2StandardConnection(properties),
+                _ => throw new ArgumentOutOfRangeException(nameof(properties), 
$"Unsupported or unknown value '{type}' given for property 
'{HiveServer2Parameters.TransportType}'. Supported types: 
{HiveServer2TransportTypeParser.SupportedList}"),
+            };
         }
     }
 }
diff --git a/csharp/src/Drivers/Apache/Hive2/HiveServer2ExtendedConnection.cs 
b/csharp/src/Drivers/Apache/Hive2/HiveServer2ExtendedConnection.cs
new file mode 100644
index 000000000..49fba7084
--- /dev/null
+++ b/csharp/src/Drivers/Apache/Hive2/HiveServer2ExtendedConnection.cs
@@ -0,0 +1,177 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*    http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using Apache.Arrow.Ipc;
+using Apache.Hive.Service.Rpc.Thrift;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
+{
+    internal abstract class HiveServer2ExtendedConnection : 
HiveServer2Connection
+    {
+        private const string ProductVersionDefault = "1.0.0";
+        private const string DriverName = "ADBC Hive Driver";
+        private const string ArrowVersion = "1.0.0";
+        private const string BasicAuthenticationScheme = "Basic";
+        private readonly Lazy<string> _productVersion;
+        internal static readonly string s_userAgent = $"{DriverName.Replace(" 
", "")}/{ProductVersionDefault}";
+
+        protected override string GetProductVersionDefault() => 
ProductVersionDefault;
+
+        protected override string ProductVersion => _productVersion.Value;
+
+        public HiveServer2ExtendedConnection(IReadOnlyDictionary<string, 
string> properties) : base(properties)
+        {
+            ValidateProperties();
+            _productVersion = new Lazy<string>(() => GetProductVersion(), 
LazyThreadSafetyMode.PublicationOnly);
+        }
+
+        private void ValidateProperties()
+        {
+            ValidateAuthentication();
+            ValidateConnection();
+            ValidateOptions();
+        }
+
+        protected abstract void ValidateAuthentication();
+
+        protected abstract void ValidateConnection();
+
+        protected abstract void ValidateOptions();
+
+        public override AdbcStatement CreateStatement()
+        {
+            return new HiveServer2Statement(this);
+        }
+
+        internal override IArrowArrayStream NewReader<T>(T statement, Schema 
schema, TGetResultSetMetadataResp? metadataResp = null) => new 
HiveServer2Reader(
+            statement,
+            schema,
+            dataTypeConversion: statement.Connection.DataTypeConversion,
+            enableBatchSizeStopCondition: false);
+
+        internal override void SetPrecisionScaleAndTypeName(
+            short colType,
+            string typeName,
+            TableInfo? tableInfo,
+            int columnSize,
+            int decimalDigits)
+        {
+            // Keep the original type name
+            tableInfo?.TypeName.Add(typeName);
+            switch (colType)
+            {
+                case (short)ColumnTypeId.DECIMAL:
+                case (short)ColumnTypeId.NUMERIC:
+                    {
+                        // Precision/scale is provide in the API call.
+                        SqlDecimalParserResult result = 
SqlTypeNameParser<SqlDecimalParserResult>.Parse(typeName, colType);
+                        tableInfo?.Precision.Add(columnSize);
+                        tableInfo?.Scale.Add((short)decimalDigits);
+                        tableInfo?.BaseTypeName.Add(result.BaseTypeName);
+                        break;
+                    }
+
+                case (short)ColumnTypeId.CHAR:
+                case (short)ColumnTypeId.NCHAR:
+                case (short)ColumnTypeId.VARCHAR:
+                case (short)ColumnTypeId.LONGVARCHAR:
+                case (short)ColumnTypeId.LONGNVARCHAR:
+                case (short)ColumnTypeId.NVARCHAR:
+                    {
+                        // Precision is provide in the API call.
+                        SqlCharVarcharParserResult result = 
SqlTypeNameParser<SqlCharVarcharParserResult>.Parse(typeName, colType);
+                        tableInfo?.Precision.Add(columnSize);
+                        tableInfo?.Scale.Add(null);
+                        tableInfo?.BaseTypeName.Add(result.BaseTypeName);
+                        break;
+                    }
+
+                default:
+                    {
+                        SqlTypeNameParserResult result = 
SqlTypeNameParser<SqlTypeNameParserResult>.Parse(typeName, colType);
+                        tableInfo?.Precision.Add(null);
+                        tableInfo?.Scale.Add(null);
+                        tableInfo?.BaseTypeName.Add(result.BaseTypeName);
+                        break;
+                    }
+            }
+        }
+
+        protected override ColumnsMetadataColumnNames 
GetColumnsMetadataColumnNames()
+        {
+            return new ColumnsMetadataColumnNames()
+            {
+                TableCatalog = TableCat,
+                TableSchema = TableSchem,
+                TableName = TableName,
+                ColumnName = ColumnName,
+                DataType = DataType,
+                TypeName = TypeName,
+                Nullable = Nullable,
+                ColumnDef = ColumnDef,
+                OrdinalPosition = OrdinalPosition,
+                IsNullable = IsNullable,
+                IsAutoIncrement = IsAutoIncrement,
+                ColumnSize = ColumnSize,
+                DecimalDigits = DecimalDigits,
+            };
+        }
+
+        protected override Task<TGetResultSetMetadataResp> 
GetResultSetMetadataAsync(TGetSchemasResp response, CancellationToken 
cancellationToken = default) =>
+            GetResultSetMetadataAsync(response.OperationHandle, Client, 
cancellationToken);
+        protected override Task<TGetResultSetMetadataResp> 
GetResultSetMetadataAsync(TGetCatalogsResp response, CancellationToken 
cancellationToken = default) =>
+            GetResultSetMetadataAsync(response.OperationHandle, Client, 
cancellationToken);
+        protected override Task<TGetResultSetMetadataResp> 
GetResultSetMetadataAsync(TGetColumnsResp response, CancellationToken 
cancellationToken = default) =>
+            GetResultSetMetadataAsync(response.OperationHandle, Client, 
cancellationToken);
+        protected override Task<TGetResultSetMetadataResp> 
GetResultSetMetadataAsync(TGetTablesResp response, CancellationToken 
cancellationToken = default) =>
+            GetResultSetMetadataAsync(response.OperationHandle, Client, 
cancellationToken);
+        protected internal override Task<TGetResultSetMetadataResp> 
GetResultSetMetadataAsync(TGetPrimaryKeysResp response, CancellationToken 
cancellationToken = default) =>
+            GetResultSetMetadataAsync(response.OperationHandle, Client, 
cancellationToken);
+        protected override Task<TRowSet> GetRowSetAsync(TGetTableTypesResp 
response, CancellationToken cancellationToken = default) =>
+            FetchResultsAsync(response.OperationHandle, cancellationToken: 
cancellationToken);
+        protected override Task<TRowSet> GetRowSetAsync(TGetColumnsResp 
response, CancellationToken cancellationToken = default) =>
+            FetchResultsAsync(response.OperationHandle, cancellationToken: 
cancellationToken);
+        protected override Task<TRowSet> GetRowSetAsync(TGetTablesResp 
response, CancellationToken cancellationToken = default) =>
+            FetchResultsAsync(response.OperationHandle, cancellationToken: 
cancellationToken);
+        protected override Task<TRowSet> GetRowSetAsync(TGetCatalogsResp 
response, CancellationToken cancellationToken = default) =>
+            FetchResultsAsync(response.OperationHandle, cancellationToken: 
cancellationToken);
+        protected override Task<TRowSet> GetRowSetAsync(TGetSchemasResp 
response, CancellationToken cancellationToken = default) =>
+            FetchResultsAsync(response.OperationHandle, cancellationToken: 
cancellationToken);
+        protected internal override Task<TRowSet> 
GetRowSetAsync(TGetPrimaryKeysResp response, CancellationToken 
cancellationToken = default) =>
+            FetchResultsAsync(response.OperationHandle, cancellationToken: 
cancellationToken);
+
+        protected internal override int PositionRequiredOffset => 0;
+
+        protected override string InfoDriverName => DriverName;
+
+        protected override string InfoDriverArrowVersion => ArrowVersion;
+
+        protected override bool IsColumnSizeValidForDecimal => false;
+
+        protected override bool GetObjectsPatternsRequireLowerCase => false;
+
+        internal override SchemaParser SchemaParser => new 
HiveServer2SchemaParser();
+
+        protected abstract HiveServer2TransportType Type { get; }
+
+        protected override int ColumnMapIndexOffset => 1;
+    }
+}
diff --git a/csharp/src/Drivers/Apache/Hive2/HiveServer2HttpConnection.cs 
b/csharp/src/Drivers/Apache/Hive2/HiveServer2HttpConnection.cs
index 0bffe8714..0bba4735c 100644
--- a/csharp/src/Drivers/Apache/Hive2/HiveServer2HttpConnection.cs
+++ b/csharp/src/Drivers/Apache/Hive2/HiveServer2HttpConnection.cs
@@ -15,6 +15,7 @@
 * limitations under the License.
 */
 
+using Apache.Hive.Service.Rpc.Thrift;
 using System;
 using System.Collections.Generic;
 using System.Globalization;
@@ -24,8 +25,6 @@ using System.Net.Http.Headers;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
-using Apache.Arrow.Ipc;
-using Apache.Hive.Service.Rpc.Thrift;
 using Thrift;
 using Thrift.Protocol;
 using Thrift.Transport;
@@ -33,33 +32,15 @@ using Thrift.Transport.Client;
 
 namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
 {
-    internal class HiveServer2HttpConnection : HiveServer2Connection
+    internal class HiveServer2HttpConnection : HiveServer2ExtendedConnection
     {
-        private const string ProductVersionDefault = "1.0.0";
-        private const string DriverName = "ADBC Hive Driver";
-        private const string ArrowVersion = "1.0.0";
         private const string BasicAuthenticationScheme = "Basic";
-        private readonly Lazy<string> _productVersion;
-        private static readonly string s_userAgent = $"{DriverName.Replace(" 
", "")}/{ProductVersionDefault}";
-
-        protected override string GetProductVersionDefault() => 
ProductVersionDefault;
-
-        protected override string ProductVersion => _productVersion.Value;
 
         public HiveServer2HttpConnection(IReadOnlyDictionary<string, string> 
properties) : base(properties)
         {
-            ValidateProperties();
-            _productVersion = new Lazy<string>(() => GetProductVersion(), 
LazyThreadSafetyMode.PublicationOnly);
         }
 
-        private void ValidateProperties()
-        {
-            ValidateAuthentication();
-            ValidateConnection();
-            ValidateOptions();
-        }
-
-        private void ValidateAuthentication()
+        protected override void ValidateAuthentication()
         {
             // Validate authentication parameters
             Properties.TryGetValue(AdbcOptions.Username, out string? username);
@@ -96,7 +77,7 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
             }
         }
 
-        private void ValidateConnection()
+        protected override void ValidateConnection()
         {
             // HostName or Uri is required parameter
             Properties.TryGetValue(AdbcOptions.Uri, out string? uri);
@@ -125,7 +106,7 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
             };
         }
 
-        private void ValidateOptions()
+        protected override void ValidateOptions()
         {
             Properties.TryGetValue(HiveServer2Parameters.DataTypeConv, out 
string? dataTypeConv);
             DataTypeConversion = DataTypeConversionParser.Parse(dataTypeConv);
@@ -139,17 +120,6 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
             TlsOptions = HiveServer2TlsImpl.GetHttpTlsOptions(Properties);
         }
 
-        public override AdbcStatement CreateStatement()
-        {
-            return new HiveServer2Statement(this);
-        }
-
-        internal override IArrowArrayStream NewReader<T>(T statement, Schema 
schema, TGetResultSetMetadataResp? metadataResp = null) => new 
HiveServer2Reader(
-            statement,
-            schema,
-            dataTypeConversion: statement.Connection.DataTypeConversion,
-            enableBatchSizeStopCondition: false);
-
         protected override TTransport CreateTransport()
         {
             // Assumption: parameters have already been validated.
@@ -225,111 +195,6 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
             return req;
         }
 
-        internal override void SetPrecisionScaleAndTypeName(
-            short colType,
-            string typeName,
-            TableInfo? tableInfo,
-            int columnSize,
-            int decimalDigits)
-        {
-            // Keep the original type name
-            tableInfo?.TypeName.Add(typeName);
-            switch (colType)
-            {
-                case (short)ColumnTypeId.DECIMAL:
-                case (short)ColumnTypeId.NUMERIC:
-                    {
-                        // Precision/scale is provide in the API call.
-                        SqlDecimalParserResult result = 
SqlTypeNameParser<SqlDecimalParserResult>.Parse(typeName, colType);
-                        tableInfo?.Precision.Add(columnSize);
-                        tableInfo?.Scale.Add((short)decimalDigits);
-                        tableInfo?.BaseTypeName.Add(result.BaseTypeName);
-                        break;
-                    }
-
-                case (short)ColumnTypeId.CHAR:
-                case (short)ColumnTypeId.NCHAR:
-                case (short)ColumnTypeId.VARCHAR:
-                case (short)ColumnTypeId.LONGVARCHAR:
-                case (short)ColumnTypeId.LONGNVARCHAR:
-                case (short)ColumnTypeId.NVARCHAR:
-                    {
-                        // Precision is provide in the API call.
-                        SqlCharVarcharParserResult result = 
SqlTypeNameParser<SqlCharVarcharParserResult>.Parse(typeName, colType);
-                        tableInfo?.Precision.Add(columnSize);
-                        tableInfo?.Scale.Add(null);
-                        tableInfo?.BaseTypeName.Add(result.BaseTypeName);
-                        break;
-                    }
-
-                default:
-                    {
-                        SqlTypeNameParserResult result = 
SqlTypeNameParser<SqlTypeNameParserResult>.Parse(typeName, colType);
-                        tableInfo?.Precision.Add(null);
-                        tableInfo?.Scale.Add(null);
-                        tableInfo?.BaseTypeName.Add(result.BaseTypeName);
-                        break;
-                    }
-            }
-        }
-
-        protected override ColumnsMetadataColumnNames 
GetColumnsMetadataColumnNames()
-        {
-            return new ColumnsMetadataColumnNames()
-            {
-                TableCatalog = TableCat,
-                TableSchema = TableSchem,
-                TableName = TableName,
-                ColumnName = ColumnName,
-                DataType = DataType,
-                TypeName = TypeName,
-                Nullable = Nullable,
-                ColumnDef = ColumnDef,
-                OrdinalPosition = OrdinalPosition,
-                IsNullable = IsNullable,
-                IsAutoIncrement = IsAutoIncrement,
-                ColumnSize = ColumnSize,
-                DecimalDigits = DecimalDigits,
-            };
-        }
-
-        protected override Task<TGetResultSetMetadataResp> 
GetResultSetMetadataAsync(TGetSchemasResp response, CancellationToken 
cancellationToken = default) =>
-            GetResultSetMetadataAsync(response.OperationHandle, Client, 
cancellationToken);
-        protected override Task<TGetResultSetMetadataResp> 
GetResultSetMetadataAsync(TGetCatalogsResp response, CancellationToken 
cancellationToken = default) =>
-            GetResultSetMetadataAsync(response.OperationHandle, Client, 
cancellationToken);
-        protected override Task<TGetResultSetMetadataResp> 
GetResultSetMetadataAsync(TGetColumnsResp response, CancellationToken 
cancellationToken = default) =>
-            GetResultSetMetadataAsync(response.OperationHandle, Client, 
cancellationToken);
-        protected override Task<TGetResultSetMetadataResp> 
GetResultSetMetadataAsync(TGetTablesResp response, CancellationToken 
cancellationToken = default) =>
-            GetResultSetMetadataAsync(response.OperationHandle, Client, 
cancellationToken);
-        protected internal override Task<TGetResultSetMetadataResp> 
GetResultSetMetadataAsync(TGetPrimaryKeysResp response, CancellationToken 
cancellationToken = default) =>
-            GetResultSetMetadataAsync(response.OperationHandle, Client, 
cancellationToken);
-        protected override Task<TRowSet> GetRowSetAsync(TGetTableTypesResp 
response, CancellationToken cancellationToken = default) =>
-            FetchResultsAsync(response.OperationHandle, cancellationToken: 
cancellationToken);
-        protected override Task<TRowSet> GetRowSetAsync(TGetColumnsResp 
response, CancellationToken cancellationToken = default) =>
-            FetchResultsAsync(response.OperationHandle, cancellationToken: 
cancellationToken);
-        protected override Task<TRowSet> GetRowSetAsync(TGetTablesResp 
response, CancellationToken cancellationToken = default) =>
-            FetchResultsAsync(response.OperationHandle, cancellationToken: 
cancellationToken);
-        protected override Task<TRowSet> GetRowSetAsync(TGetCatalogsResp 
response, CancellationToken cancellationToken = default) =>
-            FetchResultsAsync(response.OperationHandle, cancellationToken: 
cancellationToken);
-        protected override Task<TRowSet> GetRowSetAsync(TGetSchemasResp 
response, CancellationToken cancellationToken = default) =>
-            FetchResultsAsync(response.OperationHandle, cancellationToken: 
cancellationToken);
-        protected internal override Task<TRowSet> 
GetRowSetAsync(TGetPrimaryKeysResp response, CancellationToken 
cancellationToken = default) =>
-            FetchResultsAsync(response.OperationHandle, cancellationToken: 
cancellationToken);
-
-        protected internal override int PositionRequiredOffset => 0;
-
-        protected override string InfoDriverName => DriverName;
-
-        protected override string InfoDriverArrowVersion => ArrowVersion;
-
-        protected override bool IsColumnSizeValidForDecimal => false;
-
-        protected override bool GetObjectsPatternsRequireLowerCase => false;
-
-        internal override SchemaParser SchemaParser => new 
HiveServer2SchemaParser();
-
-        internal HiveServer2TransportType Type => 
HiveServer2TransportType.Http;
-
-        protected override int ColumnMapIndexOffset => 1;
+        protected override HiveServer2TransportType Type => 
HiveServer2TransportType.Http;
     }
 }
diff --git a/csharp/src/Drivers/Apache/Hive2/HiveServer2Parameters.cs 
b/csharp/src/Drivers/Apache/Hive2/HiveServer2Parameters.cs
index 5d44948bf..d40ee2cc9 100644
--- a/csharp/src/Drivers/Apache/Hive2/HiveServer2Parameters.cs
+++ b/csharp/src/Drivers/Apache/Hive2/HiveServer2Parameters.cs
@@ -38,6 +38,7 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
     public static class HiveServer2TransportTypeConstants
     {
         public const string Http = "http";
+        public const string Standard = "standard";
     }
 
     public static class DataTypeConversionOptions
diff --git a/csharp/src/Drivers/Apache/Hive2/HiveServer2StandardConnection.cs 
b/csharp/src/Drivers/Apache/Hive2/HiveServer2StandardConnection.cs
new file mode 100644
index 000000000..29c9ea795
--- /dev/null
+++ b/csharp/src/Drivers/Apache/Hive2/HiveServer2StandardConnection.cs
@@ -0,0 +1,176 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*    http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using Apache.Hive.Service.Rpc.Thrift;
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocol;
+using Thrift.Transport;
+using Thrift.Transport.Client;
+
+namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
+{
+    internal class HiveServer2StandardConnection : 
HiveServer2ExtendedConnection
+    {
+        public HiveServer2StandardConnection(IReadOnlyDictionary<string, 
string> properties) : base(properties)
+        {
+        }
+
+        protected override void ValidateAuthentication()
+        {
+            Properties.TryGetValue(AdbcOptions.Username, out string? username);
+            Properties.TryGetValue(AdbcOptions.Password, out string? password);
+            Properties.TryGetValue(HiveServer2Parameters.AuthType, out string? 
authType);
+            if (!HiveServer2AuthTypeParser.TryParse(authType, out 
HiveServer2AuthType authTypeValue))
+            {
+                throw new 
ArgumentOutOfRangeException(HiveServer2Parameters.AuthType, authType, 
$"Unsupported {HiveServer2Parameters.AuthType} value.");
+            }
+            switch (authTypeValue)
+            {
+                case HiveServer2AuthType.None:
+                    break;
+                case HiveServer2AuthType.Basic:
+                    if (string.IsNullOrWhiteSpace(username) || 
string.IsNullOrWhiteSpace(password))
+                        throw new ArgumentException(
+                            $"Parameter '{HiveServer2Parameters.AuthType}' is 
set to '{HiveServer2AuthTypeConstants.Basic}' but parameters 
'{AdbcOptions.Username}' or '{AdbcOptions.Password}' are not set. Please 
provide a values for these parameters.",
+                            nameof(Properties));
+                    break;
+                case HiveServer2AuthType.Empty:
+                    if (string.IsNullOrWhiteSpace(username) || 
string.IsNullOrWhiteSpace(password))
+                        throw new ArgumentException(
+                            $"Parameters must include valid authentiation 
settings. Please provide '{AdbcOptions.Username}' and 
'{AdbcOptions.Password}'.",
+                            nameof(Properties));
+                    break;
+                default:
+                    throw new 
ArgumentOutOfRangeException(HiveServer2Parameters.AuthType, authType, 
$"Unsupported {HiveServer2Parameters.AuthType} value.");
+            }
+        }
+
+        protected override void ValidateConnection()
+        {
+            // HostName is required parameter
+            Properties.TryGetValue(HiveServer2Parameters.HostName, out string? 
hostName);
+            if (Uri.CheckHostName(hostName) == UriHostNameType.Unknown)
+            {
+                throw new ArgumentException(
+                    $"Required parameter '{HiveServer2Parameters.HostName}' is 
missing or invalid. Please provide a valid hostname for the data source.",
+                    nameof(Properties));
+            }
+
+            // Validate port range
+            Properties.TryGetValue(HiveServer2Parameters.Port, out string? 
port);
+            if (int.TryParse(port, out int portNumber) && (portNumber <= 
IPEndPoint.MinPort || portNumber > IPEndPoint.MaxPort))
+                throw new ArgumentOutOfRangeException(
+                    nameof(Properties),
+                    port,
+                    $"Parameter '{HiveServer2Parameters.Port}' value is not in 
the valid range of {IPEndPoint.MinPort + 1} .. {IPEndPoint.MaxPort}.");
+        }
+
+        protected override void ValidateOptions()
+        {
+            Properties.TryGetValue(HiveServer2Parameters.DataTypeConv, out 
string? dataTypeConv);
+            DataTypeConversion = DataTypeConversionParser.Parse(dataTypeConv);
+            TlsOptions = HiveServer2TlsImpl.GetStandardTlsOptions(Properties);
+        }
+
+        protected override TTransport CreateTransport()
+        {
+            // Required properties (validated previously)
+            Properties.TryGetValue(HiveServer2Parameters.HostName, out string? 
hostName);
+            Properties.TryGetValue(HiveServer2Parameters.Port, out string? 
port);
+            Properties.TryGetValue(HiveServer2Parameters.AuthType, out string? 
authType);
+
+            if (!HiveServer2AuthTypeParser.TryParse(authType, out 
HiveServer2AuthType authTypeValue))
+            {
+                throw new 
ArgumentOutOfRangeException(HiveServer2Parameters.AuthType, authType, 
$"Unsupported {HiveServer2Parameters.AuthType} value.");
+            }
+
+            // Delay the open connection until later.
+            bool connectClient = false;
+            int portValue = int.Parse(port!);
+
+            // TLS setup
+            TTransport baseTransport;
+            if (TlsOptions.IsTlsEnabled)
+            {
+                X509Certificate2? trustedCert = 
!string.IsNullOrEmpty(TlsOptions.TrustedCertificatePath)
+                    ? new X509Certificate2(TlsOptions.TrustedCertificatePath!)
+                    : null;
+
+                var certValidator = 
HiveServer2TlsImpl.GetCertificateValidator(TlsOptions);
+
+                if (IPAddress.TryParse(hostName!, out var ipAddress))
+                {
+                    baseTransport = new TTlsSocketTransport(ipAddress, 
portValue, config: new(), 0, trustedCert, certValidator);
+                }
+                else
+                {
+                    baseTransport = new TTlsSocketTransport(hostName!, 
portValue, config: new(), 0, trustedCert, certValidator);
+                }
+            }
+            else
+            {
+                baseTransport = new TSocketTransport(hostName!, portValue, 
connectClient, config: new());
+            }
+
+            TBufferedTransport bufferedTransport = new 
TBufferedTransport(baseTransport);
+            switch (authTypeValue)
+            {
+                case HiveServer2AuthType.None:
+                    return bufferedTransport;
+
+                case HiveServer2AuthType.Basic:
+                    Properties.TryGetValue(AdbcOptions.Username, out string? 
username);
+                    Properties.TryGetValue(AdbcOptions.Password, out string? 
password);
+
+                    if (string.IsNullOrWhiteSpace(username) || 
string.IsNullOrWhiteSpace(password))
+                    {
+                        throw new InvalidOperationException("Username and 
password must be provided for this authentication type.");
+                    }
+
+                    PlainSaslMechanism saslMechanism = new(username, password);
+                    TSaslTransport saslTransport = new(bufferedTransport, 
saslMechanism, config: new());
+                    return new TFramedTransport(saslTransport);
+
+                default:
+                    throw new NotSupportedException($"Authentication type 
'{authTypeValue}' is not supported.");
+            }
+        }
+
+        protected override async Task<TProtocol> 
CreateProtocolAsync(TTransport transport, CancellationToken cancellationToken = 
default)
+        {
+            if (!transport.IsOpen) await 
transport.OpenAsync(cancellationToken);
+            return new TBinaryProtocol(transport, true, true);
+        }
+
+        protected override TOpenSessionReq CreateSessionRequest()
+        {
+            TOpenSessionReq request = new TOpenSessionReq
+            {
+                Client_protocol = 
TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V11,
+                CanUseMultipleCatalogs = true,
+            };
+            return request;
+        }
+
+        protected override HiveServer2TransportType Type => 
HiveServer2TransportType.Standard;
+    }
+}
diff --git a/csharp/src/Drivers/Apache/Hive2/HiveServer2TransportType.cs 
b/csharp/src/Drivers/Apache/Hive2/HiveServer2TransportType.cs
index b0c0ee83a..38deab210 100644
--- a/csharp/src/Drivers/Apache/Hive2/HiveServer2TransportType.cs
+++ b/csharp/src/Drivers/Apache/Hive2/HiveServer2TransportType.cs
@@ -20,12 +20,13 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
     internal enum HiveServer2TransportType
     {
         Http,
+        Standard,
         Empty = int.MaxValue,
     }
 
     internal static class HiveServer2TransportTypeParser
     {
-        internal const string SupportedList = 
HiveServer2TransportTypeConstants.Http;
+        internal const string SupportedList = 
HiveServer2TransportTypeConstants.Http + ", " + 
HiveServer2TransportTypeConstants.Standard;
 
         internal static bool TryParse(string? serverType, out 
HiveServer2TransportType serverTypeValue)
         {
@@ -38,6 +39,9 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
                 case HiveServer2TransportTypeConstants.Http:
                     serverTypeValue = HiveServer2TransportType.Http;
                     return true;
+                case HiveServer2TransportTypeConstants.Standard:
+                    serverTypeValue = HiveServer2TransportType.Standard;
+                    return true;
                 default:
                     serverTypeValue = default;
                     return false;
diff --git a/csharp/src/Drivers/Apache/Impala/ImpalaStandardConnection.cs 
b/csharp/src/Drivers/Apache/Impala/ImpalaStandardConnection.cs
index c1cc1320e..a016a4dc0 100644
--- a/csharp/src/Drivers/Apache/Impala/ImpalaStandardConnection.cs
+++ b/csharp/src/Drivers/Apache/Impala/ImpalaStandardConnection.cs
@@ -104,6 +104,11 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Impala
             // Assumption: hostName and port have already been validated.
             Properties.TryGetValue(ImpalaParameters.HostName, out string? 
hostName);
             Properties.TryGetValue(ImpalaParameters.Port, out string? port);
+            Properties.TryGetValue(ImpalaParameters.AuthType, out string? 
authType);
+            if (!ImpalaAuthTypeParser.TryParse(authType, out ImpalaAuthType 
authTypeValue))
+            {
+                throw new 
ArgumentOutOfRangeException(ImpalaParameters.AuthType, authType, $"Unsupported 
{ImpalaParameters.AuthType} value.");
+            }
 
             // Delay the open connection until later.
             bool connectClient = false;
@@ -125,7 +130,26 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Impala
             }
 
             TBufferedTransport bufferedTransport = new(transport);
-            return bufferedTransport;
+            switch (authTypeValue)
+            {
+                case ImpalaAuthType.None:
+                    return bufferedTransport;
+
+                case ImpalaAuthType.Basic:
+                    Properties.TryGetValue(AdbcOptions.Username, out string? 
username);
+                    Properties.TryGetValue(AdbcOptions.Password, out string? 
password);
+                    if (string.IsNullOrWhiteSpace(username) || 
string.IsNullOrWhiteSpace(password))
+                    {
+                        throw new InvalidOperationException("Username and 
password must be provided for this authentication type.");
+                    }
+
+                    PlainSaslMechanism saslMechanism = new(username, password);
+                    TSaslTransport saslTransport = new(bufferedTransport, 
saslMechanism, config: new());
+                    return new TFramedTransport(saslTransport);
+
+                default:
+                    throw new NotSupportedException($"Authentication type 
'{authTypeValue}' is not supported.");
+            }
         }
 
         protected override async Task<TProtocol> 
CreateProtocolAsync(TTransport transport, CancellationToken cancellationToken = 
default)
diff --git a/csharp/src/Drivers/Apache/Thrift/Sasl/ISaslMechanism.cs 
b/csharp/src/Drivers/Apache/Thrift/Sasl/ISaslMechanism.cs
new file mode 100644
index 000000000..6c072d56c
--- /dev/null
+++ b/csharp/src/Drivers/Apache/Thrift/Sasl/ISaslMechanism.cs
@@ -0,0 +1,43 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*    http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+namespace Apache.Arrow.Adbc.Drivers.Apache
+{
+    /// <summary>
+    /// Defines the contract for implementing SASL authentication mechanisms 
(e.g., PLAIN, GSSAPI).
+    /// This interface allows the client to authenticate over a Thrift 
transport using the selected SASL mechanism.
+    /// </summary>
+    internal interface ISaslMechanism
+    {
+        /// <summary>
+        /// Gets the name of the SASL mechanism (e.g., "PLAIN", "GSSAPI").
+        /// </summary>
+        string Name { get; }
+
+        /// <summary>
+        /// Evaluates the challenge from the server and returns the 
appropriate response payload.
+        /// </summary>
+        /// <param name="challenge">The server challenge; may be null or empty 
for mechanisms like PLAIN.</param>
+        /// <returns>The response payload to send to the server.</returns>
+        byte[] EvaluateChallenge(byte[]? challenge);
+
+        /// <summary>
+        /// Gets a value indicating whether the SASL negotiation process has 
completed.
+        /// </summary>
+        bool IsNegotiationCompleted { get; set; }
+    }
+}
diff --git a/csharp/src/Drivers/Apache/Thrift/Sasl/NegotiationStatus.cs 
b/csharp/src/Drivers/Apache/Thrift/Sasl/NegotiationStatus.cs
new file mode 100644
index 000000000..0c5bdd52b
--- /dev/null
+++ b/csharp/src/Drivers/Apache/Thrift/Sasl/NegotiationStatus.cs
@@ -0,0 +1,65 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*    http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+namespace Apache.Arrow.Adbc.Drivers.Apache
+{
+    /// <summary>
+    /// Represents the status of a SASL negotiation between client and server.
+    /// </summary>
+    internal sealed class NegotiationStatus
+    {
+        public static readonly NegotiationStatus Start = new 
NegotiationStatus(0x01, nameof(Start));
+        public static readonly NegotiationStatus Ok = new 
NegotiationStatus(0x02, nameof(Ok));
+        public static readonly NegotiationStatus Bad = new 
NegotiationStatus(0x03, nameof(Bad));
+        public static readonly NegotiationStatus Error = new 
NegotiationStatus(0x04, nameof(Error));
+        public static readonly NegotiationStatus Complete = new 
NegotiationStatus(0x05, nameof(Complete));
+
+        /// <summary>
+        /// Gets the byte value representing the negotiation status.
+        /// </summary>
+        public byte Value { get; }
+
+        /// <summary>
+        /// Gets the name of the status (e.g., "Start", "Ok").
+        /// </summary>
+        public string Name { get; }
+
+        private NegotiationStatus(byte value, string name)
+        {
+            Value = value;
+            Name = name;
+        }
+
+        /// <summary>
+        /// Gets a <see cref="NegotiationStatus"/> instance by its byte value.
+        /// </summary>
+        /// <param name="value">The byte value of the status.</param>
+        /// <returns>The corresponding <see cref="NegotiationStatus"/>, or 
null if unknown.</returns>
+        public static NegotiationStatus? FromValue(byte value)
+        {
+            return value switch
+            {
+                0x01 => Start,
+                0x02 => Ok,
+                0x03 => Bad,
+                0x04 => Error,
+                0x05 => Complete,
+                _ => null
+            };
+        }
+    }
+}
diff --git a/csharp/src/Drivers/Apache/Thrift/Sasl/PlainSaslMechanism.cs 
b/csharp/src/Drivers/Apache/Thrift/Sasl/PlainSaslMechanism.cs
new file mode 100644
index 000000000..da096a0ff
--- /dev/null
+++ b/csharp/src/Drivers/Apache/Thrift/Sasl/PlainSaslMechanism.cs
@@ -0,0 +1,66 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*    http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System;
+using System.Text;
+
+namespace Apache.Arrow.Adbc.Drivers.Apache
+{
+    /// <summary>
+    /// Implements the SASL PLAIN mechanism for simple username/password 
authentication.
+    /// </summary>
+    internal class PlainSaslMechanism : ISaslMechanism
+    {
+        private readonly string _username;
+        private readonly string _password;
+        private readonly string _authorizationId;
+        private bool _isNegotiationCompleted;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="PlainSaslMechanism"/> 
class.
+        /// </summary>
+        /// <param name="username">The username for authentication.</param>
+        /// <param name="password">The password for authentication.</param>
+        public PlainSaslMechanism(string username, string password, string 
authorizationId = "")
+        {
+            _username = username ?? throw new 
ArgumentNullException(nameof(username));
+            _password = password ?? throw new 
ArgumentNullException(nameof(password));
+            _authorizationId = authorizationId;
+        }
+
+        public string Name => "PLAIN";
+
+        public byte[] EvaluateChallenge(byte[]? challenge)
+        {
+            if (_isNegotiationCompleted)
+            {
+                // PLAIN is single-step, so return empty array if already done
+                return [];
+            }
+
+            string message = $"{_authorizationId}\0{_username}\0{_password}";
+            _isNegotiationCompleted = true;
+            return Encoding.UTF8.GetBytes(message);
+        }
+
+        public bool IsNegotiationCompleted
+        {
+            get => _isNegotiationCompleted;
+            set => _isNegotiationCompleted = value;
+        }
+    }
+}
diff --git a/csharp/src/Drivers/Apache/Thrift/Sasl/TSaslTransport.cs 
b/csharp/src/Drivers/Apache/Thrift/Sasl/TSaslTransport.cs
new file mode 100644
index 000000000..87f0e0d4f
--- /dev/null
+++ b/csharp/src/Drivers/Apache/Thrift/Sasl/TSaslTransport.cs
@@ -0,0 +1,181 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*    http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System;
+using System.Security.Authentication;
+using System.Text;
+using System.Threading.Tasks;
+using System.Threading;
+using Thrift;
+using Thrift.Transport;
+
+namespace Apache.Arrow.Adbc.Drivers.Apache
+{
+    internal class TSaslTransport : TEndpointTransport
+    {
+        private readonly TTransport _innerTransport;
+        private readonly ISaslMechanism _saslMechanism;
+        private readonly TConfiguration _configuration;
+        private byte[] messageHeader = new byte[STATUS_BYTES + 
PAYLOAD_LENGTH_BYTES];
+
+        protected const int MECHANISM_NAME_BYTES = 1;
+        protected const int STATUS_BYTES = 1;
+        protected const int PAYLOAD_LENGTH_BYTES = 4;
+
+        public TSaslTransport(TTransport innerTransport, ISaslMechanism 
saslMechanism, TConfiguration config)
+            : base(config)
+        {
+            _innerTransport = innerTransport ?? throw new 
ArgumentNullException(nameof(innerTransport));
+            _saslMechanism = saslMechanism ?? throw new 
ArgumentNullException(nameof(saslMechanism));
+            _configuration = config ?? new TConfiguration();
+        }
+
+        public override bool IsOpen => _innerTransport.IsOpen;
+
+        public override async Task OpenAsync(CancellationToken 
cancellationToken = default)
+        {
+            // Open the transport and negotiate SASL authentication
+            await 
_innerTransport.OpenAsync(cancellationToken).ConfigureAwait(false);
+            await NegotiateAsync(cancellationToken).ConfigureAwait(false);
+        }
+
+        public override void Close()
+        {
+            _innerTransport.Close();
+        }
+
+        public override ValueTask<int> ReadAsync(byte[] buffer, int offset, 
int length, CancellationToken cancellationToken = default)
+        {
+            return _innerTransport.ReadAsync(buffer, offset, length, 
cancellationToken);
+        }
+
+        public override Task WriteAsync(byte[] buffer, int offset, int length, 
CancellationToken cancellationToken = default)
+        {
+            return _innerTransport.WriteAsync(buffer, offset, length, 
cancellationToken);
+        }
+
+        public override Task FlushAsync(CancellationToken cancellationToken = 
default)
+        {
+            return _innerTransport.FlushAsync(cancellationToken);
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                _innerTransport.Dispose();
+            }
+        }
+
+        private async Task NegotiateAsync(CancellationToken cancellationToken)
+        {
+            // Send the SASL mechanism name
+            await SendMechanismAsync(_saslMechanism.Name, 
cancellationToken).ConfigureAwait(false);
+
+            // Send the authentication message
+            var authMessage = _saslMechanism.EvaluateChallenge(null);
+            await SendSaslMessageAsync(NegotiationStatus.Ok, authMessage, 
cancellationToken).ConfigureAwait(false);
+
+            // Receive server's response (authentication status)
+            var serverResponse = await 
ReceiveSaslMessageAsync(cancellationToken).ConfigureAwait(false);
+
+            if (serverResponse.status == null || serverResponse.status != 
NegotiationStatus.Complete)
+            {
+                throw new AuthenticationException($"SASL {_saslMechanism.Name} 
authentication failed.");
+            }
+
+            _saslMechanism.IsNegotiationCompleted = true;
+        }
+
+        private async Task SendMechanismAsync(string mechanismName, 
CancellationToken cancellationToken)
+        {
+            // Send the mechanism name to the server
+            byte[] mechanismNameBytes = Encoding.UTF8.GetBytes(mechanismName);
+            await SendSaslMessageAsync(NegotiationStatus.Start, 
mechanismNameBytes, cancellationToken);
+        }
+
+        protected async Task SendSaslMessageAsync(NegotiationStatus status, 
byte[] payload, CancellationToken cancellationToken)
+        {
+            payload ??= [];
+            // Set status byte
+            messageHeader[0] = status.Value;
+            // Encode payload length
+            EncodeBigEndian(payload.Length, messageHeader, STATUS_BYTES);
+            await _innerTransport.WriteAsync(messageHeader, 0, 
messageHeader.Length);
+            await _innerTransport.WriteAsync(payload, 0, payload.Length);
+            await _innerTransport.FlushAsync(cancellationToken);
+        }
+
+        protected async Task<SaslResponse> 
ReceiveSaslMessageAsync(CancellationToken cancellationToken = default)
+        {
+            await _innerTransport.ReadAllAsync(messageHeader, 0, 
messageHeader.Length, cancellationToken);
+
+            byte statusByte = messageHeader[0];
+
+            NegotiationStatus? status = 
NegotiationStatus.FromValue(statusByte);
+            if (status == null)
+            {
+                throw new TTransportException("Received invalid SASL 
negotiation status. The status byte was null.");
+            }
+
+            int payloadBytes = DecodeBigEndian(messageHeader, STATUS_BYTES);
+            if (payloadBytes < 0 || payloadBytes > 
_configuration.MaxMessageSize)
+            {
+                throw new TTransportException($"Received payload size out of 
range: {payloadBytes}. Expected between 0 and {new 
TConfiguration().MaxMessageSize}.");
+            }
+
+            byte[] payload = new byte[payloadBytes];
+            await _innerTransport.ReadAllAsync(payload, 0, payload.Length, 
cancellationToken);
+
+            if (status == NegotiationStatus.Bad || status == 
NegotiationStatus.Error)
+            {
+                string remoteMessage = Encoding.UTF8.GetString(payload);
+                throw new TTransportException($"Peer indicated failure: 
{remoteMessage}");
+            }
+
+            return new SaslResponse(status, payload);
+        }
+
+        private int DecodeBigEndian(byte[] buf, int offset)
+        {
+            return ((buf[offset] & 0xff) << 24)
+                 | ((buf[offset + 1] & 0xff) << 16)
+                 | ((buf[offset + 2] & 0xff) << 8)
+                 | (buf[offset + 3] & 0xff);
+        }
+
+        private void EncodeBigEndian(int value, byte[] buf, int offset)
+        {
+            buf[offset] = (byte)((value >> 24) & 0xff);
+            buf[offset + 1] = (byte)((value >> 16) & 0xff);
+            buf[offset + 2] = (byte)((value >> 8) & 0xff);
+            buf[offset + 3] = (byte)(value & 0xff);
+        }
+    }
+
+    internal class SaslResponse
+    {
+        public NegotiationStatus status;
+        public byte[] payload;
+
+        public SaslResponse(NegotiationStatus status, byte[] payload)
+        {
+            this.status = status;
+            this.payload = payload;
+        }
+    }
+}

Reply via email to