Skip to main content

mssql_tds/
core.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use crate::error::Error;
5use crate::error::Error::OperationCancelledError;
6use futures::FutureExt;
7use futures::future::Either;
8use std::future::Future;
9use std::path::PathBuf;
10use tokio_util::sync::CancellationToken;
11
12/// Alias for `Result<T, crate::error::Error>` used throughout the crate.
13pub type TdsResult<T> = Result<T, Error>;
14
15/// ALPN protocol identifier for TDS 8.0 connections.
16pub const TDS_8_ALPN_PROTOCOL: &str = "tds/8.0";
17
18/// Cooperative cancellation handle backed by a [`CancellationToken`].
19///
20/// Pass to [`TdsConnectionProvider::create_client()`](crate::connection_provider::tds_connection_provider::TdsConnectionProvider::create_client)
21/// to cancel a pending connect, or hold for later query cancellation.
22#[derive(Debug)]
23pub struct CancelHandle {
24    pub(crate) cancel_token: CancellationToken,
25}
26
27impl CancelHandle {
28    /// Create a new, uncancelled handle.
29    pub fn new() -> Self {
30        CancelHandle {
31            cancel_token: CancellationToken::new(),
32        }
33    }
34
35    /// Trigger cancellation, notifying all child handles.
36    pub fn cancel(self) {
37        self.cancel_token.cancel();
38    }
39
40    /// Derive a child handle that is cancelled when this handle is.
41    pub fn child_handle(&self) -> Self {
42        Self::from(self.cancel_token.child_token())
43    }
44
45    pub(crate) fn run_until_cancelled<'a, F, ResultType>(
46        cancel_handle: Option<&'a CancelHandle>,
47        f: F,
48    ) -> impl Future<Output = F::Output> + Send + 'a
49    where
50        F: Future<Output = TdsResult<ResultType>> + Send + 'a,
51    {
52        match cancel_handle {
53            Some(handle) => Either::Left(handle.cancel_token.run_until_cancelled(f).map(
54                |result| match result {
55                    Some(result) => result,
56                    None => Err(OperationCancelledError("Request was cancelled".to_string())),
57                },
58            )),
59            None => Either::Right(f),
60        }
61    }
62}
63
64impl From<CancellationToken> for CancelHandle {
65    fn from(value: CancellationToken) -> Self {
66        CancelHandle {
67            cancel_token: value,
68        }
69    }
70}
71
72impl Default for CancelHandle {
73    fn default() -> Self {
74        Self::new()
75    }
76}
77
78/// SQL Server major-version discriminant derived from the server's reported version.
79#[derive(PartialEq, Debug)]
80pub enum SQLServerVersion {
81    /// Unsupported or unknown server version.
82    SqlServerNotsupported = 0,
83    /// SQL Server 2000.
84    SqlServer2000 = 8,
85    /// SQL Server 2005.
86    SqlServer2005 = 9,
87    /// SQL Server 2008 / 2008 R2.
88    SqlServer2008 = 10,
89    /// SQL Server 2012.
90    SqlServer2012 = 11,
91    /// SQL Server 2014.
92    SqlServer2014 = 12,
93    /// SQL Server 2016.
94    SqlServer2016 = 13,
95    /// SQL Server 2017.
96    SqlServer2017 = 14,
97    /// SQL Server 2019.
98    SqlServer2019 = 15,
99    /// SQL Server 2022.
100    SqlServer2022 = 16,
101    /// SQL Server 2022+ (version 17).
102    SqlServer2022lus = 17,
103}
104
105impl From<u8> for SQLServerVersion {
106    fn from(v: u8) -> Self {
107        match v {
108            0 => SQLServerVersion::SqlServerNotsupported,
109            8 => SQLServerVersion::SqlServer2000,
110            9 => SQLServerVersion::SqlServer2005,
111            10 => SQLServerVersion::SqlServer2008,
112            11 => SQLServerVersion::SqlServer2012,
113            12 => SQLServerVersion::SqlServer2014,
114            13 => SQLServerVersion::SqlServer2016,
115            14 => SQLServerVersion::SqlServer2017,
116            15 => SQLServerVersion::SqlServer2019,
117            16 => SQLServerVersion::SqlServer2022,
118            17 => SQLServerVersion::SqlServer2022lus,
119            _ => SQLServerVersion::SqlServerNotsupported,
120        }
121    }
122}
123
124/// Four-part server version reported during the TDS pre-login handshake.
125#[derive(Clone, Copy, PartialEq, Debug)]
126pub struct Version {
127    /// Major version number.
128    pub major: u8,
129    /// Minor version number.
130    pub minor: u8,
131    /// Build number.
132    pub build: u16,
133    /// Revision number.
134    pub revision: u16,
135}
136
137impl Version {
138    /// Creates a new `Version`.
139    pub fn new(major: u8, minor: u8, build: u16, revision: u16) -> Self {
140        Version {
141            major,
142            minor,
143            build,
144            revision,
145        }
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    #[test]
154    fn sql_server_version_from_known_values() {
155        assert_eq!(
156            SQLServerVersion::from(0),
157            SQLServerVersion::SqlServerNotsupported
158        );
159        assert_eq!(SQLServerVersion::from(8), SQLServerVersion::SqlServer2000);
160        assert_eq!(SQLServerVersion::from(9), SQLServerVersion::SqlServer2005);
161        assert_eq!(SQLServerVersion::from(10), SQLServerVersion::SqlServer2008);
162        assert_eq!(SQLServerVersion::from(11), SQLServerVersion::SqlServer2012);
163        assert_eq!(SQLServerVersion::from(12), SQLServerVersion::SqlServer2014);
164        assert_eq!(SQLServerVersion::from(13), SQLServerVersion::SqlServer2016);
165        assert_eq!(SQLServerVersion::from(14), SQLServerVersion::SqlServer2017);
166        assert_eq!(SQLServerVersion::from(15), SQLServerVersion::SqlServer2019);
167        assert_eq!(SQLServerVersion::from(16), SQLServerVersion::SqlServer2022);
168        assert_eq!(
169            SQLServerVersion::from(17),
170            SQLServerVersion::SqlServer2022lus
171        );
172    }
173
174    #[test]
175    fn sql_server_version_from_unknown_defaults_to_not_supported() {
176        assert_eq!(
177            SQLServerVersion::from(1),
178            SQLServerVersion::SqlServerNotsupported
179        );
180        assert_eq!(
181            SQLServerVersion::from(7),
182            SQLServerVersion::SqlServerNotsupported
183        );
184        assert_eq!(
185            SQLServerVersion::from(18),
186            SQLServerVersion::SqlServerNotsupported
187        );
188        assert_eq!(
189            SQLServerVersion::from(255),
190            SQLServerVersion::SqlServerNotsupported
191        );
192    }
193
194    #[test]
195    fn cancel_handle_default() {
196        let handle = CancelHandle::default();
197        assert!(!handle.cancel_token.is_cancelled());
198    }
199
200    #[tokio::test]
201    async fn run_until_cancelled_none_handle() {
202        let result: TdsResult<i32> =
203            CancelHandle::run_until_cancelled(None, async { Ok(42) }).await;
204        assert_eq!(result.unwrap(), 42);
205    }
206
207    #[tokio::test]
208    async fn run_until_cancelled_with_handle_completes() {
209        let handle = CancelHandle::new();
210        let result: TdsResult<i32> =
211            CancelHandle::run_until_cancelled(Some(&handle), async { Ok(99) }).await;
212        assert_eq!(result.unwrap(), 99);
213    }
214
215    #[tokio::test]
216    async fn run_until_cancelled_with_cancelled_handle_stops_pending_future() {
217        let handle = CancelHandle::new();
218        handle.cancel_token.cancel();
219
220        let result = CancelHandle::run_until_cancelled(
221            Some(&handle),
222            std::future::pending::<TdsResult<i32>>(),
223        )
224        .await;
225
226        assert!(matches!(result, Err(OperationCancelledError(_))));
227    }
228}
229
230/// TLS and encryption settings for a TDS connection.
231#[derive(Clone, PartialEq, Debug)]
232pub struct EncryptionOptions {
233    /// Encryption mode negotiated with the server.
234    pub mode: EncryptionSetting,
235    /// Skip server certificate chain validation.
236    pub trust_server_certificate: bool,
237    /// Expected CN or SAN in the server certificate.
238    pub host_name_in_cert: Option<String>,
239    /// Path to a DER or PEM encoded X.509 certificate file for certificate pinning.
240    /// When specified, the driver performs an exact binary match between the provided
241    /// certificate and the server's certificate, bypassing standard CA chain validation.
242    pub server_certificate: Option<PathBuf>,
243}
244
245impl EncryptionOptions {
246    /// Creates encryption options defaulting to `Strict` mode.
247    pub fn new() -> Self {
248        EncryptionOptions {
249            mode: EncryptionSetting::Strict,
250            trust_server_certificate: false,
251            host_name_in_cert: None,
252            server_certificate: None,
253        }
254    }
255}
256
257impl Default for EncryptionOptions {
258    fn default() -> Self {
259        Self::new()
260    }
261}
262
263/// Encryption level requested by the client during the TDS pre-login.
264#[derive(Clone, Copy, PartialEq, Debug)]
265pub enum EncryptionSetting {
266    /// Don't encrypt if the server allows it.
267    PreferOff,
268    /// Encrypt the connection after pre-login.
269    On,
270    /// Require encryption after pre-login (semantically identical to `On`).
271    Required,
272    /// Encrypt the entire stream including pre-login (TDS 8.0).
273    Strict,
274}
275
276#[derive(Clone, Copy, PartialEq, Debug)]
277pub(crate) enum NegotiatedEncryptionSetting {
278    Strict,
279    LoginOnly,
280    Mandatory,
281    NoEncryption,
282}