@@ -68,11 +68,16 @@ public void KerberosTest_ProtocolNone_NamedInstanceWithSsrpResolution()
6868
6969 KerberosTicketManagemnt . Init ( DataTestUtility . KerberosDomainUser , DataTestUtility . KerberosDomainPassword ) ;
7070
71- // Build a connection string with Protocol.None (no prefix) pointing to the named instance
72- // SSRP resolution should occur and populate the port in the SPN
73- string protocolNoneConnStr = $ "Data Source={ hostname } \\ { instanceName } ;Integrated Security=true;";
71+ // Build from the base connection string to preserve environment settings (Encrypt,
72+ // TrustServerCertificate, timeouts, etc.), overriding only DataSource and IntegratedSecurity.
73+ // SSRP resolution should occur and populate the port in the SPN.
74+ SqlConnectionStringBuilder protocolNoneBuilder = new ( tcpConnStr )
75+ {
76+ DataSource = $ "{ hostname } \\ { instanceName } ",
77+ IntegratedSecurity = true
78+ } ;
7479
75- using SqlConnection conn = new ( protocolNoneConnStr ) ;
80+ using SqlConnection conn = new ( protocolNoneBuilder . ConnectionString ) ;
7681 conn . Open ( ) ; // Connection should succeed with Kerberos using the SSRP-resolved port in SPN
7782
7883 // Verify authentication occurred with KERBEROS auth_scheme
@@ -97,20 +102,30 @@ public void KerberosTest_ProtocolTcp_NamedInstanceWithExplicitPort()
97102 string tcpConnStr = DataTestUtility . TCPConnectionString ;
98103 if ( string . IsNullOrEmpty ( tcpConnStr ) ||
99104 ! DataTestUtility . ParseDataSource ( new SqlConnectionStringBuilder ( tcpConnStr ) . DataSource ,
100- out string hostname , out int port , out string instanceName ) )
105+ out string hostname , out int port , out string instanceName ) ||
106+ string . IsNullOrEmpty ( instanceName ) )
101107 {
102- return ; // Skip test
108+ return ; // Skip test; no named instance available
103109 }
104110
105111 KerberosTicketManagemnt . Init ( DataTestUtility . KerberosDomainUser , DataTestUtility . KerberosDomainPassword ) ;
106112
107- // If an explicit port is available in the test connection string, use it
108- // Otherwise, use a typical SQL instance port (1433) and rely on SSRP if needed
109- int testPort = port > 0 ? port : 1433 ;
113+ // Build the tcp: data source. Include an explicit port only when the test connection string
114+ // already has one; otherwise leave SSRP to resolve the port for the named instance.
115+ // Do NOT fall back to port 1433: that disables SSRP and is unlikely to be correct for
116+ // named instances, and produces an invalid "host\,1433" when instanceName is empty.
117+ string newDataSource = port > 0
118+ ? $ "tcp:{ hostname } \\ { instanceName } ,{ port } "
119+ : $ "tcp:{ hostname } \\ { instanceName } ";
110120
111- string protocolTcpConnStr = $ "Data Source=tcp:{ hostname } \\ { instanceName } ,{ testPort } ;Integrated Security=true;";
121+ // Preserve the base connection string settings (Encrypt, TrustServerCertificate, etc.)
122+ SqlConnectionStringBuilder builder = new ( tcpConnStr )
123+ {
124+ DataSource = newDataSource ,
125+ IntegratedSecurity = true
126+ } ;
112127
113- using SqlConnection conn = new ( protocolTcpConnStr ) ;
128+ using SqlConnection conn = new ( builder . ConnectionString ) ;
114129 conn . Open ( ) ;
115130
116131 using SqlCommand command = new ( "SELECT auth_scheme from sys.dm_exec_connections where session_id = @@spid" , conn ) ;
@@ -142,19 +157,20 @@ public void KerberosTest_CustomServerSPN_BypassesAutoGeneration()
142157 return ; // Skip test
143158 }
144159
160+ // For a reliable custom SPN test, we need to know the exact port so we can construct
161+ // the TCP-format SPN the server expects: MSSQLSvc/fqdn:port.
162+ // Using the instance name here is wrong for TCP environments that register only port-based SPNs.
163+ if ( port <= 0 )
164+ {
165+ return ; // Skip test; cannot construct a valid port-based custom SPN without an explicit port
166+ }
167+
145168 KerberosTicketManagemnt . Init ( DataTestUtility . KerberosDomainUser , DataTestUtility . KerberosDomainPassword ) ;
146169
147- // Build the expected SPN for the server
170+ // Build the TCP-format SPN that matches what the driver would auto-generate.
171+ // TCP Kerberos connections use MSSQLSvc/fqdn:port regardless of instance name.
148172 string fqdn = DataTestUtility . GetMachineFQDN ( hostname ) ;
149- string customSpn = $ "MSSQLSvc/{ fqdn } ";
150- if ( ! string . IsNullOrEmpty ( instanceName ) )
151- {
152- customSpn += ":" + instanceName ;
153- }
154- else if ( port > 0 )
155- {
156- customSpn += ":" + port ;
157- }
173+ string customSpn = $ "MSSQLSvc/{ fqdn } :{ port } ";
158174
159175 SqlConnectionStringBuilder builder = new ( tcpConnStr ) ;
160176 builder . IntegratedSecurity = true ;
@@ -185,35 +201,38 @@ public void KerberosTest_ProtocolAdmin_DedicatedAdminConnection()
185201 string tcpConnStr = DataTestUtility . TCPConnectionString ;
186202 if ( string . IsNullOrEmpty ( tcpConnStr ) ||
187203 ! DataTestUtility . ParseDataSource ( new SqlConnectionStringBuilder ( tcpConnStr ) . DataSource ,
188- out string hostname , out int port , out string instanceName ) )
204+ out string hostname , out int port , out string instanceName ) ||
205+ string . IsNullOrEmpty ( instanceName ) )
189206 {
190- return ; // Skip test
207+ return ; // Skip test; no named instance available
191208 }
192209
193210 KerberosTicketManagemnt . Init ( DataTestUtility . KerberosDomainUser , DataTestUtility . KerberosDomainPassword ) ;
194211
195- int testPort = port > 0 ? port : 1433 ;
212+ // Build the admin: data source from the base connection string to preserve environment
213+ // settings. Do NOT fall back to port 1433 — the DAC port is separate from the regular
214+ // SQL Server port and must be discovered via SSRP if not explicitly known.
215+ string newDataSource = port > 0
216+ ? $ "admin:{ hostname } \\ { instanceName } ,{ port } "
217+ : $ "admin:{ hostname } \\ { instanceName } ";
196218
197- // Build admin: connection string
198- string protocolAdminConnStr = $ "Data Source=admin:{ hostname } \\ { instanceName } ,{ testPort } ;Integrated Security=true;";
199-
200- try
201- {
202- using SqlConnection conn = new ( protocolAdminConnStr ) ;
203- conn . Open ( ) ;
204-
205- using SqlCommand command = new ( "SELECT auth_scheme from sys.dm_exec_connections where session_id = @@spid" , conn ) ;
206- using SqlDataReader reader = command . ExecuteReader ( ) ;
207- if ( reader . Read ( ) )
208- {
209- Assert . Equal ( "KERBEROS" , reader . GetString ( 0 ) ) ;
210- }
211- }
212- catch ( SqlException ex ) when ( ex . Message . Contains ( "DAC" ) || ex . Message . Contains ( "Dedicated" ) )
219+ SqlConnectionStringBuilder adminBuilder = new ( tcpConnStr )
213220 {
214- // DAC may not be enabled or accessible; skip this test without failing
215- return ;
216- }
221+ DataSource = newDataSource ,
222+ IntegratedSecurity = true
223+ } ;
224+
225+ // Note: this test requires DAC to be enabled on the target instance
226+ // (sp_configure 'remote admin connections', 1). If DAC is not enabled,
227+ // the connection will fail with a SqlException and the test will report as failed,
228+ // which is the desired behavior — the test environment should be fixed.
229+ using SqlConnection conn = new ( adminBuilder . ConnectionString ) ;
230+ conn . Open ( ) ;
231+
232+ using SqlCommand command = new ( "SELECT auth_scheme from sys.dm_exec_connections where session_id = @@spid" , conn ) ;
233+ using SqlDataReader reader = command . ExecuteReader ( ) ;
234+ Assert . True ( reader . Read ( ) , "Expected to receive one row data" ) ;
235+ Assert . Equal ( "KERBEROS" , reader . GetString ( 0 ) ) ;
217236 }
218237 }
219238
0 commit comments