Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ internal ServerInfo(
PreRoutingServerName = preRoutingServerName;
UserProtocol = TdsEnums.TCP;
SetDerivedNames(UserProtocol, UserServerName);
ResolvedDatabaseName = userOptions.InitialCatalog;
ResolvedDatabaseName = routing?.DatabaseName ?? userOptions.InitialCatalog;
ServerSPN = serverSpn;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ internal class SqlConnectionInternal : DbConnectionInternal, IDisposable

private string _currentLanguage;

private int _currentPacketSize;


/// <summary>
/// Pool this connection is associated with, if any.
Expand Down Expand Up @@ -262,7 +262,7 @@ internal class SqlConnectionInternal : DbConnectionInternal, IDisposable
/// </summary>
private DbConnectionPoolAuthenticationContext _newDbConnectionPoolAuthenticationContext;

private Guid _originalClientConnectionId = Guid.Empty;


private string _originalDatabase;

Expand All @@ -280,7 +280,7 @@ internal class SqlConnectionInternal : DbConnectionInternal, IDisposable
// @TODO: Rename to match naming conventions (remove f prefix)
private readonly bool _fResetConnection;

private string _routingDestination = null;


// @TODO: Rename to match naming conventions
private bool _SQLDNSRetryEnabled = false;
Expand Down Expand Up @@ -669,17 +669,9 @@ internal override bool IsTransactionRoot
get => DelegatedTransaction?.IsActive == true;
}

// @TODO: Make auto-property
internal Guid OriginalClientConnectionId
{
get => _originalClientConnectionId;
}
internal Guid OriginalClientConnectionId { get; private set; } = Guid.Empty;

// @TODO: Make auto-property
internal int PacketSize
{
get => _currentPacketSize;
}
internal int PacketSize { get; private set; }

// @TODO: Make auto-property
internal TdsParser Parser
Expand All @@ -706,11 +698,7 @@ internal SqlConnectionPoolGroupProviderInfo PoolGroupProviderInfo
/// </summary>
internal byte[] PromotedDtcToken { get; private set; }

// @TODO: Make auto-property
internal string RoutingDestination
{
get => _routingDestination;
}
internal string RoutingDestination { get; private set; }

internal RoutingInfo RoutingInfo { get; private set; } = null;

Expand Down Expand Up @@ -1187,7 +1175,7 @@ internal void OnEnvChange(SqlEnvChange rec)
break;

case TdsEnums.ENV_PACKETSIZE:
_currentPacketSize = int.Parse(rec._newValue, CultureInfo.InvariantCulture);
PacketSize = int.Parse(rec._newValue, CultureInfo.InvariantCulture);
break;

case TdsEnums.ENV_COLLATION:
Expand Down Expand Up @@ -1267,6 +1255,23 @@ internal void OnEnvChange(SqlEnvChange rec)
RoutingInfo = rec._newRoutingInfo;
break;

case TdsEnums.ENV_ENHANCEDROUTING:
SqlClientEventSource.Log.TryAdvancedTraceEvent(
$"SqlInternalConnectionTds.OnEnvChange | ADV | " +
$"Object ID {ObjectID}, " +
$"Received enhanced routing info");

if (string.IsNullOrEmpty(rec._newRoutingInfo.ServerName) ||
string.IsNullOrEmpty(rec._newRoutingInfo.DatabaseName) ||
rec._newRoutingInfo.Protocol != 0 ||
rec._newRoutingInfo.Port == 0)
{
throw SQL.ROR_InvalidEnhancedRoutingInfo(this);
}

RoutingInfo = rec._newRoutingInfo;
break;

default:
Debug.Fail("Missed token in EnvChange!");
break;
Expand Down Expand Up @@ -1733,11 +1738,6 @@ internal void OnFeatureExtAck(int featureId, byte[] data)
}
case TdsEnums.FEATUREEXT_ENHANCEDROUTINGSUPPORT:
{
SqlClientEventSource.Log.TryAdvancedTraceEvent(
$"SqlInternalConnectionTds.OnFeatureExtAck | ADV | " +
$"Object ID {ObjectID}, " +
$"Received feature extension acknowledgement for ENHANCEDROUTINGSUPPORT");

if (data.Length != 1)
{
SqlClientEventSource.Log.TryTraceEvent(
Expand All @@ -1750,6 +1750,12 @@ internal void OnFeatureExtAck(int featureId, byte[] data)

// A value of 1 indicates that the server supports the feature.
IsEnhancedRoutingSupportEnabled = data[0] == 1;

SqlClientEventSource.Log.TryAdvancedTraceEvent(
$"SqlInternalConnectionTds.OnFeatureExtAck | ADV | " +
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following the format of other trace messages in this file. As a separate piece of work, the messages should be edited up to better match the file name.

$"Object ID {ObjectID}, " +
$"Received feature extension acknowledgement for " +
$"ENHANCEDROUTINGSUPPORT = {IsEnhancedRoutingSupportEnabled}");
break;
}
case TdsEnums.FEATUREEXT_USERAGENT:
Expand Down Expand Up @@ -2967,7 +2973,7 @@ private void Login(
// Gather all the settings the user set in the connection string or properties and do
// the login
CurrentDatabase = server.ResolvedDatabaseName;
_currentPacketSize = ConnectionOptions.PacketSize;
PacketSize = ConnectionOptions.PacketSize;
_currentLanguage = ConnectionOptions.CurrentLanguage;

int timeoutInSeconds = 0;
Expand Down Expand Up @@ -3018,7 +3024,7 @@ private void Login(
// Treat AD Integrated like Windows integrated when against a non-FedAuth endpoint
login.useSSPI = ConnectionOptions.IntegratedSecurity || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && !_fedAuthRequired);

login.packetSize = _currentPacketSize;
login.packetSize = PacketSize;
login.newPassword = newPassword;
login.readOnlyIntent = ConnectionOptions.ApplicationIntent == ApplicationIntent.ReadOnly;
login.credential = _credential;
Expand Down Expand Up @@ -3302,11 +3308,11 @@ private void LoginNoFailover(
serverInfo.ResolvedServerName,
serverInfo.ServerSPN);
_timeoutErrorInternal.SetInternalSourceType(SqlConnectionInternalSourceType.RoutingDestination);
_originalClientConnectionId = _clientConnectionId;
_routingDestination = serverInfo.UserServerName;
OriginalClientConnectionId = _clientConnectionId;
RoutingDestination = serverInfo.UserServerName;

// Restore properties that could be changed by the environment tokens
_currentPacketSize = ConnectionOptions.PacketSize;
PacketSize = ConnectionOptions.PacketSize;
_currentLanguage = _originalLanguage = ConnectionOptions.CurrentLanguage;
CurrentDatabase = _originalDatabase = ConnectionOptions.InitialCatalog;
ServerProvidedFailoverPartner = null;
Expand Down Expand Up @@ -3605,11 +3611,11 @@ private void LoginWithFailover(
currentServerInfo.ResolvedServerName,
currentServerInfo.ServerSPN);
_timeoutErrorInternal.SetInternalSourceType(SqlConnectionInternalSourceType.RoutingDestination);
_originalClientConnectionId = _clientConnectionId;
_routingDestination = currentServerInfo.UserServerName;
OriginalClientConnectionId = _clientConnectionId;
RoutingDestination = currentServerInfo.UserServerName;

// Restore properties that could be changed by the environment tokens
_currentPacketSize = connectionOptions.PacketSize;
PacketSize = connectionOptions.PacketSize;
_currentLanguage = _originalLanguage = ConnectionOptions.CurrentLanguage;
CurrentDatabase = _originalDatabase = connectionOptions.InitialCatalog;
ServerProvidedFailoverPartner = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,15 @@ internal static Exception ROR_InvalidRoutingInfo(SqlConnectionInternal internalC
return exc;
}

internal static Exception ROR_InvalidEnhancedRoutingInfo(SqlConnectionInternal internalConnection)
{
SqlErrorCollection errors = new SqlErrorCollection();
errors.Add(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, null, (StringsHelper.GetString(Strings.SQLROR_InvalidEnhancedRoutingInfo)), "", 0));
SqlException exc = SqlException.CreateException(errors, null, internalConnection, innerException: null, batchCommand: null);
exc._doNotReconnect = true;
return exc;
}

internal static Exception ROR_TimeoutAfterRoutingInfo(SqlConnectionInternal internalConnection)
{
SqlErrorCollection errors = new SqlErrorCollection();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,29 +186,7 @@ internal static class TdsEnums
public const byte ENV_SPRESETCONNECTIONACK = 18; // SP_Reset_Connection ack
public const byte ENV_USERINSTANCE = 19; // User Instance
public const byte ENV_ROUTING = 20; // Routing (ROR) information

public enum EnvChangeType : byte
{
ENVCHANGE_DATABASE = ENV_DATABASE,
ENVCHANGE_LANG = ENV_LANG,
ENVCHANGE_CHARSET = ENV_CHARSET,
ENVCHANGE_PACKETSIZE = ENV_PACKETSIZE,
ENVCHANGE_LOCALEID = ENV_LOCALEID,
ENVCHANGE_COMPFLAGS = ENV_COMPFLAGS,
ENVCHANGE_COLLATION = ENV_COLLATION,
ENVCHANGE_BEGINTRAN = ENV_BEGINTRAN,
ENVCHANGE_COMMITTRAN = ENV_COMMITTRAN,
ENVCHANGE_ROLLBACKTRAN = ENV_ROLLBACKTRAN,
ENVCHANGE_ENLISTDTC = ENV_ENLISTDTC,
ENVCHANGE_DEFECTDTC = ENV_DEFECTDTC,
ENVCHANGE_LOGSHIPNODE = ENV_LOGSHIPNODE,
ENVCHANGE_PROMOTETRANSACTION = ENV_PROMOTETRANSACTION,
ENVCHANGE_TRANSACTIONMANAGERADDRESS = ENV_TRANSACTIONMANAGERADDRESS,
ENVCHANGE_TRANSACTIONENDED = ENV_TRANSACTIONENDED,
ENVCHANGE_SPRESETCONNECTIONACK = ENV_SPRESETCONNECTIONACK,
ENVCHANGE_USERINSTANCE = ENV_USERINSTANCE,
ENVCHANGE_ROUTING = ENV_ROUTING
}
public const byte ENV_ENHANCEDROUTING = 21; // Enhanced Routing (ROR) information

// done status stream bit masks
public const int DONE_MORE = 0x0001; // more command results coming
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3385,6 +3385,7 @@ private TdsOperationStatus TryProcessEnvChange(int tokenLength, TdsParserStateOb
break;

case TdsEnums.ENV_ROUTING:
{
ushort newLength;
result = stateObj.TryReadUInt16(out newLength);
if (result != TdsOperationStatus.Done)
Expand Down Expand Up @@ -3428,8 +3429,22 @@ private TdsOperationStatus TryProcessEnvChange(int tokenLength, TdsParserStateOb
{
return result;
}
env._length = env._newLength + oldLength + 5; // 5=2*sizeof(UInt16)+sizeof(byte) [token+newLength+oldLength]
// Set the total length of the token
// total = headers + size of new value + size of old value
// headers = token id+newLengthHeader+oldLengthHeader = sizeof(byte) + 2*sizeof(UInt16) = 5
env._length = env._newLength + oldLength + 5;
break;
}

case TdsEnums.ENV_ENHANCEDROUTING:
{
result = TryProcessEnhancedRoutingToken(env, stateObj);
if (result != TdsOperationStatus.Done)
{
return result;
}
break;
}

default:
Debug.Fail("Unknown environment change token: " + env._type);
Expand All @@ -3442,6 +3457,95 @@ private TdsOperationStatus TryProcessEnvChange(int tokenLength, TdsParserStateOb
return TdsOperationStatus.Done;
}

/// <summary>
/// Processes an enhanced routing ENVCHANGE token from the TDS stream.
/// This token contains the routing information for the connection, including the protocol,
/// port, server name, and database name. The enhanced routing token has the following structure:
/// <list type="bullet">
/// <item>NewValueLength (USHORT) - length of the new value</item>
/// <item>Protocol (BYTE) - routing protocol (must be 0 = TCP)</item>
/// <item>Port (USHORT) - routing port number</item>
/// <item>AlternateServerNameLength (USHORT) - length of the alternate server name in characters</item>
/// <item>AlternateServerName (UNICODE_STRING) - the server name to route to</item>
/// <item>AlternateDatabaseNameLength (USHORT) - length of the alternate database name in characters</item>
/// <item>AlternateDatabaseName (UNICODE_STRING) - the database name to route to</item>
/// <item>OldValueLength (USHORT) - length of the old value</item>
/// <item>OldValue (BYTE[]) - old value (skipped)</item>
/// </list>
/// </summary>
private TdsOperationStatus TryProcessEnhancedRoutingToken(SqlEnvChange env, TdsParserStateObject stateObj)
{
ushort newLength;
TdsOperationStatus result = stateObj.TryReadUInt16(out newLength);
if (result != TdsOperationStatus.Done)
{
return result;
}
env._newLength = newLength;

byte protocol;
result = stateObj.TryReadByte(out protocol);
if (result != TdsOperationStatus.Done)
{
return result;
}

ushort port;
result = stateObj.TryReadUInt16(out port);
if (result != TdsOperationStatus.Done)
{
return result;
}

ushort serverLen;
result = stateObj.TryReadUInt16(out serverLen);
if (result != TdsOperationStatus.Done)
{
return result;
}

string serverName;
result = stateObj.TryReadString(serverLen, out serverName);
if (result != TdsOperationStatus.Done)
{
return result;
}

ushort databaseLen;
result = stateObj.TryReadUInt16(out databaseLen);
if (result != TdsOperationStatus.Done)
{
return result;
}

string databaseName;
result = stateObj.TryReadString(databaseLen, out databaseName);
if (result != TdsOperationStatus.Done)
{
return result;
}

env._newRoutingInfo = new RoutingInfo(protocol, port, serverName, databaseName);

ushort oldLength;
result = stateObj.TryReadUInt16(out oldLength);
if (result != TdsOperationStatus.Done)
{
return result;
}

result = stateObj.TrySkipBytes(oldLength);
if (result != TdsOperationStatus.Done)
{
return result;
}

// 5 = 2*sizeof(UInt16)+sizeof(byte) [token+newLength+oldLength]
env._length = env._newLength + oldLength + 5;

return TdsOperationStatus.Done;
}

private TdsOperationStatus TryReadTwoBinaryFields(SqlEnvChange env, TdsParserStateObject stateObj)
{
// Used by ProcessEnvChangeToken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,18 @@ internal class RoutingInfo
internal ushort Port { get; private set; }
internal string ServerName { get; private set; }

internal RoutingInfo(byte protocol, ushort port, string servername)
/// <summary>
/// The DatabaseName property is only used when routing via an EnhancedRouting ENVCHANGE token.
/// It is not used when routing via the normal Routing ENVCHANGE token.
/// </summary>
internal string DatabaseName { get; private set; }

internal RoutingInfo(byte protocol, ushort port, string serverName, string databaseName = null)
{
Protocol = protocol;
Port = port;
ServerName = servername;
ServerName = serverName;
DatabaseName = databaseName;
}
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Microsoft.Data.SqlClient/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -4335,6 +4335,9 @@
<data name="SQLROR_InvalidRoutingInfo" xml:space="preserve">
<value>Invalid routing information received.</value>
</data>
<data name="SQLROR_InvalidEnhancedRoutingInfo" xml:space="preserve">
<value>Invalid enhanced routing information received.</value>
</data>
<data name="SQLROR_TimeoutAfterRoutingInfo" xml:space="preserve">
<value>Server provided routing information, but timeout already expired.</value>
</data>
Expand Down
Loading
Loading