Skip to content

Align SqlException Numbers across platforms #3461

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

Merged
merged 4 commits into from
Jul 14, 2025
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -1507,7 +1507,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
string providerRid = string.Format("SNI_PN{0}", details.Provider);
string providerName = StringsHelper.GetResourceString(providerRid);
Debug.Assert(!string.IsNullOrEmpty(providerName), $"invalid providerResourceId '{providerRid}'");
uint win32ErrorCode = details.NativeError;
int win32ErrorCode = details.NativeError;

SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > SNI Native Error Code = {0}", win32ErrorCode);
if (details.SniErrorNumber == 0)
Expand Down Expand Up @@ -1557,7 +1557,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
// If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code
if (details.SniErrorNumber == SniErrors.LocalDBErrorCode)
{
errorMessage += LocalDbApi.GetLocalDbMessage((int)details.NativeError);
errorMessage += LocalDbApi.GetLocalDbMessage(details.NativeError);
win32ErrorCode = 0;
}
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Extracting the latest exception from native SNI. errorMessage: {0}", errorMessage);
Expand All @@ -1567,9 +1567,9 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
sqlContextInfo, providerName, (int)details.SniErrorNumber, errorMessage);

SqlClientEventSource.Log.TryAdvancedTraceErrorEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > SNI Error Message. Native Error = {0}, Line Number ={1}, Function ={2}, Exception ={3}, Server = {4}",
(int)details.NativeError, (int)details.LineNumber, details.Function, details.Exception, _server);
details.NativeError, (int)details.LineNumber, details.Function, details.Exception, _server);

return new SqlError(infoNumber: (int)details.NativeError, errorState: 0x00, TdsEnums.FATAL_ERROR_CLASS, _server,
return new SqlError(infoNumber: details.NativeError, errorState: 0x00, TdsEnums.FATAL_ERROR_CLASS, _server,
errorMessage, details.Function, (int)details.LineNumber, win32ErrorCode: details.NativeError, details.Exception);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,6 @@ namespace Microsoft.Data.SqlClient
{
internal sealed partial class TdsParser
{
internal struct SNIErrorDetails
{
public string errorMessage;
public uint nativeError;
public uint sniErrorNumber;
public int provider;
public uint lineNumber;
public string function;
public Exception exception;
}

internal static void FillGuidBytes(Guid guid, Span<byte> buffer) => guid.TryWriteBytes(buffer);

internal static void FillDoubleBytes(double value, Span<byte> buffer) => BinaryPrimitives.TryWriteInt64LittleEndian(buffer, BitConverter.DoubleToInt64Bits(value));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1588,7 +1588,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
string providerRid = string.Format("SNI_PN{0}", (int)details.Provider);
string providerName = StringsHelper.GetString(providerRid);
Debug.Assert(!string.IsNullOrEmpty(providerName), $"invalid providerResourceId '{providerRid}'");
uint win32ErrorCode = details.NativeError;
int win32ErrorCode = details.NativeError;

if (details.SniErrorNumber == 0)
{
Expand Down Expand Up @@ -1626,14 +1626,14 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
// If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code
if (details.SniErrorNumber == SniErrors.LocalDBErrorCode)
{
errorMessage += LocalDbApi.GetLocalDbMessage((int)details.NativeError);
errorMessage += LocalDbApi.GetLocalDbMessage(details.NativeError);
win32ErrorCode = 0;
}
}
errorMessage = string.Format("{0} (provider: {1}, error: {2} - {3})",
sqlContextInfo, providerName, (int)details.SniErrorNumber, errorMessage);

return new SqlError((int)details.NativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS,
return new SqlError(details.NativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS,
_server, errorMessage, details.Function, (int)details.LineNumber, win32ErrorCode);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal struct SniError
internal Provider provider;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 261)]
internal string errorMessage;
internal uint nativeError;
internal int nativeError;
internal uint sniError;
[MarshalAs(UnmanagedType.LPWStr)]
internal string fileName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ internal static IPAddress[] GetDnsIpAddresses(string serverName)
/// <param name="sniError">SNI error code</param>
/// <param name="errorMessage">Error message</param>
/// <returns></returns>
internal static uint ReportSNIError(SniProviders provider, uint nativeError, uint sniError, string errorMessage)
internal static uint ReportSNIError(SniProviders provider, int nativeError, uint sniError, string errorMessage)
{
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SniCommon), EventType.ERR, "Provider = {0}, native Error = {1}, SNI Error = {2}, Error Message = {3}", args0: provider, args1: nativeError, args2: sniError, args3: errorMessage);
return ReportSNIError(new SniError(provider, nativeError, sniError, errorMessage));
Expand All @@ -203,7 +203,7 @@ internal static uint ReportSNIError(SniProviders provider, uint nativeError, uin
/// <param name="sniException">SNI Exception</param>
/// <param name="nativeErrorCode">Native SNI error code</param>
/// <returns></returns>
internal static uint ReportSNIError(SniProviders provider, uint sniError, Exception sniException, uint nativeErrorCode = 0)
internal static uint ReportSNIError(SniProviders provider, uint sniError, Exception sniException, int nativeErrorCode = 0)
{
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SniCommon), EventType.ERR, "Provider = {0}, SNI Error = {1}, Exception = {2}", args0: provider, args1: sniError, args2: sniException?.Message);
return ReportSNIError(new SniError(provider, sniError, sniException, nativeErrorCode));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#if NET

using System;
using System.ComponentModel;
using System.Net.Sockets;

namespace Microsoft.Data.SqlClient.ManagedSni
{
Expand All @@ -14,17 +16,18 @@ namespace Microsoft.Data.SqlClient.ManagedSni
internal class SniError
{
// Error numbers from native SNI implementation
internal const uint CertificateValidationErrorCode = 2148074277;
// This is signed int representation of the error code 0x80090325
internal const int CertificateValidationErrorCode = -2146893019;

public readonly SniProviders provider;
public readonly string errorMessage;
public readonly uint nativeError;
public readonly int nativeError;
public readonly uint sniError;
public readonly string function;
public readonly uint lineNumber;
public readonly Exception exception;

public SniError(SniProviders provider, uint nativeError, uint sniErrorCode, string errorMessage)
public SniError(SniProviders provider, int nativeError, uint sniErrorCode, string errorMessage)
{
lineNumber = 0;
function = string.Empty;
Expand All @@ -35,12 +38,25 @@ public SniError(SniProviders provider, uint nativeError, uint sniErrorCode, stri
exception = null;
}

public SniError(SniProviders provider, uint sniErrorCode, Exception sniException, uint nativeErrorCode = 0)
public SniError(SniProviders provider, uint sniErrorCode, Exception sniException, int nativeErrorCode = 0)
{
lineNumber = 0;
function = string.Empty;
this.provider = provider;
nativeError = nativeErrorCode;
if (nativeErrorCode == 0)
{
if (sniException is SocketException socketException)
{
// SocketErrorCode values are cross-plat consistent in .NET (matching native Windows error codes)
// underlying type of SocketErrorCode is int
nativeError = (int)socketException.SocketErrorCode;
}
else if (sniException is Win32Exception win32Exception)
{
nativeError = win32Exception.NativeErrorCode; // Replicates native SNI behavior
}
}
sniError = sniErrorCode;
errorMessage = string.Empty;
exception = sniException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ public override uint Receive(out SniPacket packet, int timeout)
packet = null;
var e = new Win32Exception();
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SniNpHandle), EventType.ERR, "Connection Id {0}, Packet length found 0, Win32 exception raised: {1}", args0: _connectionId, args1: e?.Message);
return ReportErrorAndReleasePacket(errorPacket, (uint)e.NativeErrorCode, 0, e.Message);
return ReportErrorAndReleasePacket(errorPacket, e.NativeErrorCode, 0, e.Message);
}
}
catch (ObjectDisposedException ode)
Expand Down Expand Up @@ -413,7 +413,7 @@ private uint ReportErrorAndReleasePacket(SniPacket packet, Exception sniExceptio
return SniCommon.ReportSNIError(SniProviders.NP_PROV, SniCommon.InternalExceptionError, sniException);
}

private uint ReportErrorAndReleasePacket(SniPacket packet, uint nativeError, uint sniError, string errorMessage)
private uint ReportErrorAndReleasePacket(SniPacket packet, int nativeError, uint sniError, string errorMessage)
{
if (packet != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,8 @@ private static Socket Connect(string serverName, int port, TimeoutTimer timeout,

IEnumerable<IPAddress> ipAddresses = GetHostAddressesSortedByPreference(serverName, ipPreference);

SocketException lastSocketException = null;

foreach (IPAddress ipAddress in ipAddresses)
{
bool isSocketSelected = false;
Expand Down Expand Up @@ -426,7 +428,7 @@ private static Socket Connect(string serverName, int port, TimeoutTimer timeout,
{
if (timeout.IsExpired)
{
return null;
throw new Win32Exception(258, "The operation has timed out.");
}

int socketSelectTimeout =
Expand All @@ -442,10 +444,24 @@ private static Socket Connect(string serverName, int port, TimeoutTimer timeout,

Socket.Select(checkReadLst, checkWriteLst, checkErrorLst, socketSelectTimeout);
// nothing selected means timeout
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SniTcpHandle), EventType.INFO,
"Socket.Select results: checkReadLst.Count: {0}, checkWriteLst.Count: {1}, checkErrorLst.Count: {2}",
checkReadLst.Count, checkWriteLst.Count, checkErrorLst.Count);
} while (checkReadLst.Count == 0 && checkWriteLst.Count == 0 && checkErrorLst.Count == 0);

// workaround: false positive socket.Connected on linux: https://github.com/dotnet/runtime/issues/55538
isConnected = socket.Connected && checkErrorLst.Count == 0;
if (!isConnected)
{
// Retrieve the socket error code
int socketErrorCode = (int)socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Error);
SocketError socketError = (SocketError)socketErrorCode;

SqlClientEventSource.Log.TrySNITraceEvent(nameof(SniTcpHandle), EventType.ERR,
"Socket connection failed. SocketError: {0} ({1})", socketError, socketErrorCode);

lastSocketException = new SocketException(socketErrorCode);
}
}

if (isConnected)
Expand All @@ -463,6 +479,8 @@ private static Socket Connect(string serverName, int port, TimeoutTimer timeout,
}
pendingDNSInfo = new SQLDNSInfo(cachedFQDN, iPv4String, iPv6String, port.ToString());
isSocketSelected = true;
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SniTcpHandle), EventType.INFO,
"Connected to socket: {0}", socket.RemoteEndPoint);
return socket;
}
}
Expand All @@ -471,6 +489,7 @@ private static Socket Connect(string serverName, int port, TimeoutTimer timeout,
SqlClientEventSource.Log.TryAdvancedTraceEvent(
"{0}.{1}{2}THIS EXCEPTION IS BEING SWALLOWED: {3}",
nameof(SniTcpHandle), nameof(Connect), EventType.ERR, e);
lastSocketException = e;
}
finally
{
Expand All @@ -479,6 +498,14 @@ private static Socket Connect(string serverName, int port, TimeoutTimer timeout,
}
}

if (lastSocketException != null)
{
SqlClientEventSource.Log.TryAdvancedTraceEvent(
"{0}.{1}{2}Last Socket Exception: {3}",
nameof(SniTcpHandle), nameof(Connect), EventType.ERR, lastSocketException);
throw lastSocketException;
}

return null;
}
}
Expand Down Expand Up @@ -574,6 +601,20 @@ private static Socket ParallelConnect(IPAddress[] serverAddresses, int port, Tim
Socket.Select(checkReadLst, checkWriteLst, checkErrorLst, socketSelectTimeout);
// nothing selected means select timed out
} while (checkReadLst.Count == 0 && checkWriteLst.Count == 0 && checkErrorLst.Count == 0 && !timeout.IsExpired);
foreach (Socket socket in checkErrorLst)
{
// Retrieve the socket error code
int socketErrorCode = (int)socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Error);
SocketError socketError = (SocketError)socketErrorCode;

// Log any failed sockets
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SniTcpHandle), EventType.INFO,
"Socket connection failed for {0}. SocketError: {1} ({2})",
sockets[socket], socketError, socketErrorCode);

lastError = new SocketException(socketErrorCode);
}

}
catch (SocketException e)
{
Expand All @@ -588,6 +629,7 @@ private static Socket ParallelConnect(IPAddress[] serverAddresses, int port, Tim
{
SqlClientEventSource.Log.TryAdvancedTraceEvent(
"{0}.{1}{2}ParallelConnect timeout expired.", nameof(SniTcpHandle), nameof(ParallelConnect), EventType.INFO);
// We will throw below after cleanup
break;
}

Expand Down Expand Up @@ -654,9 +696,19 @@ private static Socket ParallelConnect(IPAddress[] serverAddresses, int port, Tim

if (connectedSocket == null)
{
if (timeout.IsExpired)
{
throw new Win32Exception(258, "The operation has timed out.");
}

SqlClientEventSource.Log.TryAdvancedTraceEvent(
"{0}.{1}{2}No socket connections succeeded. Last error: {3}",
"{0}.{1}{2} No socket connections succeeded. Last error: {3}",
nameof(SniTcpHandle), nameof(ParallelConnect), EventType.ERR, lastError);

if (lastError != null)
{
throw lastError;
}
}

return connectedSocket;
Expand Down Expand Up @@ -861,7 +913,7 @@ public override uint Receive(out SniPacket packet, int timeoutInMilliseconds)
packet = null;
var e = new Win32Exception();
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SniTcpHandle), EventType.ERR, "Connection Id {0}, Win32 exception occurred: {1}", args0: _connectionId, args1: e?.Message);
return ReportErrorAndReleasePacket(errorPacket, (uint)e.NativeErrorCode, 0, e.Message);
return ReportErrorAndReleasePacket(errorPacket, e.NativeErrorCode, 0, e.Message);
}

SqlClientEventSource.Log.TrySNITraceEvent(nameof(SniTcpHandle), EventType.INFO, "Connection Id {0}, Data read from stream synchronously", args0: _connectionId);
Expand Down Expand Up @@ -992,13 +1044,13 @@ public override uint CheckConnection()
return TdsEnums.SNI_SUCCESS;
}

private uint ReportTcpSNIError(Exception sniException, uint nativeErrorCode = 0)
private uint ReportTcpSNIError(Exception sniException, int nativeErrorCode = 0)
{
_status = TdsEnums.SNI_ERROR;
return SniCommon.ReportSNIError(SniProviders.TCP_PROV, SniCommon.InternalExceptionError, sniException, nativeErrorCode);
}

private uint ReportTcpSNIError(uint nativeError, uint sniError, string errorMessage)
private uint ReportTcpSNIError(int nativeError, uint sniError, string errorMessage)
{
_status = TdsEnums.SNI_ERROR;
return SniCommon.ReportSNIError(SniProviders.TCP_PROV, nativeError, sniError, errorMessage);
Expand All @@ -1013,7 +1065,7 @@ private uint ReportErrorAndReleasePacket(SniPacket packet, Exception sniExceptio
return ReportTcpSNIError(sniException);
}

private uint ReportErrorAndReleasePacket(SniPacket packet, uint nativeError, uint sniError, string errorMessage)
private uint ReportErrorAndReleasePacket(SniPacket packet, int nativeError, uint sniError, string errorMessage)
{
if (packet != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ internal long TryScopeEnterEvent(string className, [System.Runtime.CompilerServi
{
StringBuilder sb = new StringBuilder(className);
sb.Append(".").Append(memberName).Append(" | INFO | SCOPE | Entering Scope {0}");
return SNIScopeEnter(sb.ToString());
return ScopeEnter(sb.ToString());
}
return 0;
}
Expand Down
Loading
Loading