Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge to shared - TdsParserStaticMethods #1603

Closed
Closed
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 @@ -475,6 +475,9 @@
<Compile Include="..\..\src\Microsoft\Data\SqlClient\TdsParameterSetter.cs">
<Link>Microsoft\Data\SqlClient\TdsParameterSetter.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\TdsParserStaticMethods.cs">
<Link>Microsoft\Data\SqlClient\TdsParserStaticMethods.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\TdsRecordBufferSetter.cs">
<Link>Microsoft\Data\SqlClient\TdsRecordBufferSetter.cs</Link>
</Compile>
Expand Down Expand Up @@ -649,7 +652,6 @@
<Compile Include="Microsoft\Data\SqlClient\TdsParserSessionPool.cs" />
<Compile Include="Microsoft\Data\SqlClient\TdsParserStateObject.cs" />
<Compile Include="Microsoft\Data\SqlClient\TdsParserStateObjectManaged.cs" />
<Compile Include="Microsoft\Data\SqlClient\TdsParserStaticMethods.cs" />
</ItemGroup>
<!-- Windows only -->
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,9 @@
<Compile Include="..\..\src\Microsoft\Data\SqlClient\TdsParameterSetter.cs">
<Link>Microsoft\Data\SqlClient\TdsParameterSetter.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\TdsParserStaticMethods.cs">
<Link>Microsoft\Data\SqlClient\TdsParserStaticMethods.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\TdsRecordBufferSetter.cs">
<Link>Microsoft\Data\SqlClient\TdsRecordBufferSetter.cs</Link>
</Compile>
Expand Down Expand Up @@ -634,7 +637,6 @@
<Compile Include="Microsoft\Data\SqlClient\TdsParserSafeHandles.cs" />
<Compile Include="Microsoft\Data\SqlClient\TdsParserSessionPool.cs" />
<Compile Include="Microsoft\Data\SqlClient\TdsParserStateObject.cs" />
<Compile Include="Microsoft\Data\SqlClient\TdsParserStaticMethods.cs" />
<Compile Include="Microsoft\Data\SqlTypes\SqlFileStream.cs" />
<Compile Include="Microsoft\Data\SqlTypes\SqlStreamChars.cs" />
<Compile Include="Microsoft\Data\SqlTypes\UnsafeNativeMethods.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,92 @@

using System;
using Microsoft.Data.Common;
#if NETFRAMEWORK
using System.Globalization;
using System.Runtime.Versioning;
using System.Security.Permissions;
#endif

namespace Microsoft.Data.SqlClient
{
internal sealed class TdsParserStaticMethods
{
#if NETFRAMEWORK
private TdsParserStaticMethods() { /* prevent utility class from being insantiated*/ }
//
// Static methods
//

// SxS: this method accesses registry to resolve the alias.
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
static internal void AliasRegistryLookup(ref string host, ref string protocol)
{
if (!ADP.IsEmpty(host))
{
const string folder = "SOFTWARE\\Microsoft\\MSSQLServer\\Client\\ConnectTo";
// Put a try...catch... around this so we don't abort ANY connection if we can't read the registry.
string aliasLookup = (string)ADP.LocalMachineRegistryValue(folder, host);
if (!ADP.IsEmpty(aliasLookup))
{
/* Result will be in the form of: "DBNMPNTW,\\server\pipe\sql\query". or
Result will be in the form of: "DBNETLIB, via:\\server\pipe\sql\query".

supported formats:
tcp - DBMSSOCN,[server|server\instance][,port]
np - DBNMPNTW,[\\server\pipe\sql\query | \\server\pipe\MSSQL$instance\sql\query]
where \sql\query is the pipename and can be replaced with any other pipe name
via - [DBMSGNET,server,port | DBNETLIB, via:server, port]
sm - DBMSLPCN,server

unsupported formats:
rpc - DBMSRPCN,server,[parameters] where parameters could be "username,password"
bv - DBMSVINN,service@group@organization
appletalk - DBMSADSN,objectname@zone
spx - DBMSSPXN,[service | address,port,network]
*/
// We must parse into the two component pieces, then map the first protocol piece to the
// appropriate value.
int index = aliasLookup.IndexOf(',');

// If we found the key, but there was no "," in the string, it is a bad Alias so return.
if (-1 != index)
{
string parsedProtocol = aliasLookup.Substring(0, index).ToLower(CultureInfo.InvariantCulture);

// If index+1 >= length, Alias consisted of "FOO," which is a bad alias so return.
if (index + 1 < aliasLookup.Length)
{
string parsedAliasName = aliasLookup.Substring(index + 1);

// Fix bug 298286
if ("dbnetlib" == parsedProtocol)
{
index = parsedAliasName.IndexOf(':');
if (-1 != index && index + 1 < parsedAliasName.Length)
{
parsedProtocol = parsedAliasName.Substring(0, index);
if (SqlConnectionString.ValidProtocol(parsedProtocol))
{
protocol = parsedProtocol;
host = parsedAliasName.Substring(index + 1);
}
}
}
else
{
protocol = (string)SqlConnectionString.NetlibMapping()[parsedProtocol];
if (null != protocol)
{
host = parsedAliasName;
}
}
}
}
}
}
}
#endif
// Obfuscate password to be sent to SQL Server
// Blurb from the TDS spec at https://msdn.microsoft.com/en-us/library/dd304523.aspx
// "Before submitting a password from the client to the server, for every byte in the password buffer
Expand Down Expand Up @@ -51,10 +132,18 @@ internal static byte[] ObfuscatePassword(byte[] password)
return password;
}

#if NETFRAMEWORK
[ResourceExposure(ResourceScope.None)] // SxS: we use this method for TDS login only
[ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
#else
private const int NoProcessId = -1;
private static int s_currentProcessId = NoProcessId;
#endif // NETFRAMEWORK
internal static int GetCurrentProcessIdForTdsLoginOnly()
{
#if NETFRAMEWORK
return Common.SafeNativeMethods.GetCurrentProcessId();
#else
if (s_currentProcessId == NoProcessId)
{
// Pick up the process Id from the current process instead of randomly generating it.
Expand All @@ -67,29 +156,85 @@ internal static int GetCurrentProcessIdForTdsLoginOnly()
System.Threading.Volatile.Write(ref s_currentProcessId, processId);
}
return s_currentProcessId;
#endif // NETFRAMEWORK
}


#if NETFRAMEWORK
[SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)]
[ResourceExposure(ResourceScope.None)] // SxS: we use this method for TDS login only
[ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
#endif
internal static int GetCurrentThreadIdForTdsLoginOnly()
{
#if NETFRAMEWORK
#pragma warning disable 618
return AppDomain.GetCurrentThreadId(); // don't need this to be support fibres;
#pragma warning restore 618
#else
return Environment.CurrentManagedThreadId;
#endif // NETFRAMEWORK
}


#if NETFRAMEWORK
[ResourceExposure(ResourceScope.None)] // SxS: we use MAC address for TDS login only
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
#else
private static byte[] s_nicAddress = null;
#endif // NETFRAMEWORK
internal static byte[] GetNetworkPhysicalAddressForTdsLoginOnly()
{
#if NETFRAMEWORK
// NIC address is stored in NetworkAddress key. However, if NetworkAddressLocal key
// has a value that is not zero, then we cannot use the NetworkAddress key and must
// instead generate a random one. I do not fully understand why, this is simply what
// the native providers do. As for generation, I use a random number generator, which
// means that different processes on the same machine will have different NIC address
// values on the server. It is not ideal, but native does not have the same value for
// different processes either.

const string key = "NetworkAddress";
const string localKey = "NetworkAddressLocal";
const string folder = "SOFTWARE\\Description\\Microsoft\\Rpc\\UuidTemporaryData";

int result = 0;
byte[] nicAddress = null;

object temp = ADP.LocalMachineRegistryValue(folder, localKey);
if (temp is int localKeyRegistryValue)
{
result = localKeyRegistryValue;
}

if (result <= 0)
{
temp = ADP.LocalMachineRegistryValue(folder, key);
if (temp is byte[] keyRegistryValue)
{
nicAddress = keyRegistryValue;
}
}

if (null == nicAddress)
{
nicAddress = new byte[TdsEnums.MAX_NIC_SIZE];
Random random = new();
random.NextBytes(nicAddress);
}

return nicAddress;
#else
// For ProjectK\CoreCLR we don't want to take a dependency on the registry to try to read a value
// that isn't usually set, so we'll just use a random value each time instead
if (null == s_nicAddress)
{
byte[] newNicAddress = new byte[TdsEnums.MAX_NIC_SIZE];
Random random = new Random();
Random random = new();
Copy link
Contributor

Choose a reason for hiding this comment

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

We should dispose the Random instance, there was a PR recently to make sure that was done elsewhere in this library.
The Type name = new() is less clear than var name = new Type() imo and I thought we had added an editorconfig entry to prevent auto suggestion of the first version in the main library projects.

random.NextBytes(newNicAddress);
System.Threading.Interlocked.CompareExchange(ref s_nicAddress, newNicAddress, null);
}

return s_nicAddress;
#endif // NETFRAMEWORK
}

// translates remaining time in stateObj (from user specified timeout) to timeout value for SNI
Expand Down