Skip to content

Commit dd9db7e

Browse files
committed
Add unit test verifying behaviour of TDS version validation
1 parent 0c41cf4 commit dd9db7e

3 files changed

Lines changed: 168 additions & 7 deletions

File tree

src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,17 @@ internal static void ValidateCommandBehavior(CommandBehavior value)
492492
}
493493
}
494494

495+
internal static void ValidateTdsVersion(uint tdsVersion)
496+
{
497+
if (tdsVersion is not TdsEnums.SQL2005_VERSION
498+
and not TdsEnums.SQL2008_VERSION
499+
and not TdsEnums.TDS7X_VERSION
500+
and not TdsEnums.TDS80_VERSION)
501+
{
502+
throw SQL.InvalidTDSVersion();
503+
}
504+
}
505+
495506
internal static ArgumentOutOfRangeException InvalidUserDefinedTypeSerializationFormat(Format value)
496507
{
497508
#if DEBUG

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4308,13 +4308,7 @@ private TdsOperationStatus TryProcessLoginAck(TdsParserStateObject stateObj)
43084308
// Reference: MS-TDS, 2.2.7.14, footnote on TDSVersion field.
43094309
uint tdsVersion = BinaryPrimitives.ReadUInt32BigEndian(b);
43104310

4311-
if (tdsVersion is not TdsEnums.SQL2005_VERSION
4312-
and not TdsEnums.SQL2008_VERSION
4313-
and not TdsEnums.TDS7X_VERSION
4314-
and not TdsEnums.TDS80_VERSION)
4315-
{
4316-
throw SQL.InvalidTDSVersion();
4317-
}
4311+
ADP.ValidateTdsVersion(tdsVersion);
43184312

43194313
stateObj._outBytesUsed = stateObj._outputHeaderLen;
43204314
byte len;
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using Microsoft.Data.SqlClient;
7+
using Xunit;
8+
9+
namespace Microsoft.Data.Common.UnitTests;
10+
11+
/// <summary>
12+
/// Class containing tests of the various utility methods in <see cref="ADP"/>.
13+
/// </summary>
14+
public class AdapterUtilTest
15+
{
16+
/// <summary>
17+
/// Gets a collection of test data representing the list of server TDS versions
18+
/// which are expected by the client.
19+
/// </summary>
20+
/// <see cref="ValidTdsVersion_ReturnsSuccessfully"/>
21+
public static TheoryData<uint> ValidTdsVersions => [
22+
// SQL Server 2005
23+
TdsEnums.SQL2005_VERSION,
24+
// SQL Server 2008 R2
25+
TdsEnums.SQL2008_VERSION,
26+
// SQL Server 2012+ (TDS 7.x)
27+
TdsEnums.TDS7X_VERSION,
28+
// SQL Server 2022+ (TDS 8.0)
29+
TdsEnums.TDS80_VERSION
30+
];
31+
32+
/// <summary>
33+
/// Gets a collection of test data representing the list of known server TDS versions
34+
/// which the client does not expect to receive and should reject.
35+
/// </summary>
36+
/// <see cref="InvalidTdsVersion_ThrowsInvalidOperationException"/>
37+
public static TheoryData<uint> InvalidTdsVersions => [
38+
// Empty TDS version
39+
0x00000000,
40+
// SQL Server 7.0
41+
0x07000000,
42+
// SQL Server 2000
43+
0x07010000,
44+
// SQL Server 2000 SP1
45+
0x71000001,
46+
// SQL Server 2008
47+
0x730A0003
48+
];
49+
50+
/// <summary>
51+
/// Combines the test data sets in <see cref="ValidTdsVersions"/> and <see cref="InvalidTdsVersions"/>.
52+
/// </summary>
53+
/// <see cref="TdsVersionValidation_AlignsWithLegacyValidation"/>
54+
public static TheoryData<uint> AllTdsVersions => [.. ValidTdsVersions, .. InvalidTdsVersions];
55+
56+
/// <summary>
57+
/// Verifies that the client throws an InvalidOperationException in response to receiving an
58+
/// unsupported TDS protocol version.
59+
/// </summary>
60+
/// <param name="tdsVersion">
61+
/// The TDS protocol version to validate. Represents an unsupported or invalid version value.
62+
/// </param>
63+
[Theory]
64+
[MemberData(nameof(InvalidTdsVersions))]
65+
public void InvalidTdsVersion_ThrowsInvalidOperationException(uint tdsVersion)
66+
{
67+
Assert.Throws<InvalidOperationException>(() => ADP.ValidateTdsVersion(tdsVersion));
68+
}
69+
70+
/// <summary>
71+
/// Verifies that the client accepts one of the supported TDS protocol versions without throwing
72+
/// an exception.
73+
/// </summary>
74+
/// <param name="tdsVersion">
75+
/// The TDS protocol version to validate. Represents a supported version value.
76+
/// </param>
77+
[Theory]
78+
[MemberData(nameof(ValidTdsVersions))]
79+
public void ValidTdsVersion_ReturnsSuccessfully(uint tdsVersion) =>
80+
ADP.ValidateTdsVersion(tdsVersion);
81+
82+
/// <summary>
83+
/// Verifies that the SqlClient v7.1+ TDS version validation logic implemented in ADP.ValidateTdsVersion
84+
/// is consistent with the legacy validation logic.
85+
/// </summary>
86+
/// <param name="tdsVersion">
87+
/// The TDS protocol version to validate. Represents a version value to be checked against the legacy
88+
/// validation logic.
89+
/// </param>
90+
[Theory]
91+
[MemberData(nameof(AllTdsVersions))]
92+
public void TdsVersionValidation_AlignsWithLegacyValidation(uint tdsVersion)
93+
{
94+
Action validation = () => ADP.ValidateTdsVersion(tdsVersion);
95+
96+
if (LegacyVersionValidation(tdsVersion))
97+
{
98+
validation();
99+
}
100+
else
101+
{
102+
Assert.Throws<InvalidOperationException>(validation);
103+
}
104+
}
105+
106+
private static bool LegacyVersionValidation(uint tdsVersion)
107+
{
108+
const int SQL2005_MAJOR = 0x72;
109+
const int SQL2008_MAJOR = 0x73;
110+
const int SQL2012_MAJOR = 0x74;
111+
const int TDS8_MAJOR = 0x08;
112+
113+
const int SQL2005_INCREMENT = 0x09;
114+
const int SQL2008_INCREMENT = 0x0b;
115+
const int SQL2012_INCREMENT = 0x00;
116+
const int TDS8_INCREMENT = 0x00;
117+
118+
const int SQL2005_RTM_MINOR = 0x0002;
119+
const int SQL2008_MINOR = 0x0003;
120+
const int SQL2012_MINOR = 0x0004;
121+
const int TDS8_MINOR = 0x00;
122+
123+
uint majorMinor = tdsVersion & 0xff00ffff;
124+
uint increment = (tdsVersion >> 16) & 0xff;
125+
126+
switch (majorMinor)
127+
{
128+
case SQL2005_MAJOR << 24 | SQL2005_RTM_MINOR:
129+
if (increment != SQL2005_INCREMENT)
130+
{
131+
return false;
132+
}
133+
return true;
134+
case SQL2008_MAJOR << 24 | SQL2008_MINOR:
135+
if (increment != SQL2008_INCREMENT)
136+
{
137+
return false;
138+
}
139+
return true;
140+
case SQL2012_MAJOR << 24 | SQL2012_MINOR:
141+
if (increment != SQL2012_INCREMENT)
142+
{
143+
return false;
144+
}
145+
return true;
146+
case TDS8_MAJOR << 24 | TDS8_MINOR:
147+
if (increment != TDS8_INCREMENT)
148+
{
149+
return false;
150+
}
151+
return true;
152+
default:
153+
return false;
154+
}
155+
}
156+
}

0 commit comments

Comments
 (0)