Skip to main content

rustlavel_db/sqlserver/
protocol.rs

1//! TDS — the Tabular Data Stream protocol SQL Server speaks.
2//!
3//! Everything travels in packets: an eight-byte header and a payload, with a
4//! *message* being the run of packets ending in one whose status carries the
5//! end-of-message bit. Requests are built as one payload and split by
6//! [`split_message`]; responses are reassembled and then read as a stream of
7//! tokens by [`TokenStream`].
8//!
9//! Unlike PostgreSQL, TDS is little-endian everywhere *except* the packet
10//! header, whose length and SPID are network order. That single inconsistency
11//! is the source of most first-attempt bugs, so the header has its own type
12//! rather than being read inline.
13
14use super::types::{self, Column};
15use crate::value::Value;
16use rustlavel_core::{Error, Result};
17use std::sync::Arc;
18
19/// The fixed size of a packet header.
20pub const HEADER_LEN: usize = 8;
21
22/// The packet size assumed before the server says otherwise.
23///
24/// The server may raise or lower it with an ENVCHANGE during login; until then
25/// 4096 is the value MS-TDS specifies every implementation must accept.
26pub const DEFAULT_PACKET_SIZE: usize = 4096;
27
28/// TDS 7.4, which is what SQL Server 2012 and later speak.
29pub const TDS_VERSION_7_4: u32 = 0x7400_0004;
30
31/// Packet types, from the `Type` byte of the header.
32pub mod packet {
33    pub const SQL_BATCH: u8 = 0x01;
34    pub const RPC: u8 = 0x03;
35    pub const TABULAR_RESULT: u8 = 0x04;
36    pub const ATTENTION: u8 = 0x06;
37    pub const LOGIN7: u8 = 0x10;
38    pub const SSPI: u8 = 0x11;
39    pub const PRE_LOGIN: u8 = 0x12;
40}
41
42/// Status bits from the `Status` byte of the header.
43pub mod status {
44    pub const NORMAL: u8 = 0x00;
45    pub const END_OF_MESSAGE: u8 = 0x01;
46    pub const IGNORE: u8 = 0x02;
47    pub const RESET_CONNECTION: u8 = 0x08;
48}
49
50/// The eight bytes in front of every packet.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub struct PacketHeader {
53    pub kind: u8,
54    pub status: u8,
55    /// Total packet length, header included.
56    pub length: u16,
57    pub spid: u16,
58    /// Increments per packet within a message, wrapping at 255.
59    pub id: u8,
60    pub window: u8,
61}
62
63impl PacketHeader {
64    pub fn parse(bytes: &[u8]) -> Result<PacketHeader> {
65        if bytes.len() < HEADER_LEN {
66            return Err(Error::Protocol("truncated packet header from the server".into()));
67        }
68        Ok(PacketHeader {
69            kind: bytes[0],
70            status: bytes[1],
71            // Length and SPID are the only big-endian fields in all of TDS.
72            length: u16::from_be_bytes([bytes[2], bytes[3]]),
73            spid: u16::from_be_bytes([bytes[4], bytes[5]]),
74            id: bytes[6],
75            window: bytes[7],
76        })
77    }
78
79    pub fn write_into(&self, out: &mut Vec<u8>) {
80        out.push(self.kind);
81        out.push(self.status);
82        out.extend_from_slice(&self.length.to_be_bytes());
83        out.extend_from_slice(&self.spid.to_be_bytes());
84        out.push(self.id);
85        out.push(self.window);
86    }
87
88    pub fn is_end_of_message(&self) -> bool {
89        self.status & status::END_OF_MESSAGE != 0
90    }
91}
92
93/// Frame a payload as one or more packets of at most `packet_size` bytes.
94///
95/// Only the final packet carries the end-of-message bit, which is how the peer
96/// knows a login or a statement longer than one packet is complete. The packet
97/// id restarts at one for every message, exactly as MS-TDS requires.
98///
99/// Each packet comes back as its own buffer rather than one concatenated block,
100/// and that is not a stylistic choice. **Over an encrypted connection SQL Server
101/// expects one TDS packet per TLS record**: give it a record holding two
102/// packets and it drops the connection without a word — a live server confirmed
103/// it, at exactly the payload size where a second packet appears, and only when
104/// encryption was on. Writing each packet separately puts each in its own
105/// record, which is what every working TDS client does.
106pub fn split_message(kind: u8, payload: &[u8], packet_size: usize) -> Vec<Vec<u8>> {
107    let capacity = packet_size.max(HEADER_LEN + 1) - HEADER_LEN;
108    let mut packets = Vec::with_capacity(payload.len() / capacity + 1);
109    let mut id: u8 = 1;
110
111    // An empty payload is still a message, so the loop runs at least once.
112    let mut offset = 0;
113    loop {
114        let end = (offset + capacity).min(payload.len());
115        let chunk = &payload[offset..end];
116        let last = end == payload.len();
117
118        let mut packet = Vec::with_capacity(HEADER_LEN + chunk.len());
119        PacketHeader {
120            kind,
121            status: if last { status::END_OF_MESSAGE } else { status::NORMAL },
122            length: (HEADER_LEN + chunk.len()) as u16,
123            spid: 0,
124            id,
125            window: 0,
126        }
127        .write_into(&mut packet);
128        packet.extend_from_slice(chunk);
129        packets.push(packet);
130
131        if last {
132            return packets;
133        }
134        offset = end;
135        id = id.wrapping_add(1);
136    }
137}
138
139// --- PRELOGIN ---
140
141/// PRELOGIN option tokens.
142pub mod prelogin_option {
143    pub const VERSION: u8 = 0x00;
144    pub const ENCRYPTION: u8 = 0x01;
145    pub const INSTOPT: u8 = 0x02;
146    pub const THREADID: u8 = 0x03;
147    pub const MARS: u8 = 0x04;
148    pub const TERMINATOR: u8 = 0xFF;
149}
150
151/// The values the ENCRYPTION option can carry, in both directions.
152pub mod encryption {
153    /// Available but off: only the login packet is encrypted.
154    pub const OFF: u8 = 0x00;
155    /// On for the whole session.
156    pub const ON: u8 = 0x01;
157    /// This side cannot do encryption at all.
158    pub const NOT_SUPPORTED: u8 = 0x02;
159    /// The server insists on it.
160    pub const REQUIRED: u8 = 0x03;
161}
162
163/// Build a PRELOGIN payload asking for a particular encryption level.
164///
165/// Every option is a five-byte entry — token, offset, length — and the offsets
166/// are counted from the start of this payload, which is why the option data
167/// cannot be written until the whole option header is sized.
168pub fn prelogin(encryption: u8) -> Vec<u8> {
169    let options: [(u8, Vec<u8>); 5] = [
170        // A version the server will accept; it does not gate anything.
171        (prelogin_option::VERSION, vec![9, 0, 0, 0, 0, 0]),
172        (prelogin_option::ENCRYPTION, vec![encryption]),
173        // No named instance: an empty, null-terminated instance name.
174        (prelogin_option::INSTOPT, vec![0]),
175        (prelogin_option::THREADID, 0u32.to_le_bytes().to_vec()),
176        // Multiple Active Result Sets off: one statement at a time per
177        // connection is exactly what the pool already guarantees.
178        (prelogin_option::MARS, vec![0]),
179    ];
180
181    let header_len = options.len() * 5 + 1;
182    let mut head = Vec::with_capacity(header_len);
183    let mut data = Vec::new();
184
185    for (token, value) in &options {
186        head.push(*token);
187        head.extend_from_slice(&((header_len + data.len()) as u16).to_be_bytes());
188        head.extend_from_slice(&(value.len() as u16).to_be_bytes());
189        data.extend_from_slice(value);
190    }
191    head.push(prelogin_option::TERMINATOR);
192
193    head.extend_from_slice(&data);
194    head
195}
196
197/// What the server answered in its PRELOGIN.
198#[derive(Debug, Clone, PartialEq, Eq)]
199pub struct PreloginResponse {
200    pub encryption: u8,
201}
202
203pub fn parse_prelogin(payload: &[u8]) -> Result<PreloginResponse> {
204    let mut encryption = encryption::NOT_SUPPORTED;
205    let mut at = 0;
206
207    while at < payload.len() {
208        let token = payload[at];
209        if token == prelogin_option::TERMINATOR {
210            break;
211        }
212        if at + 5 > payload.len() {
213            return Err(Error::Protocol("truncated PRELOGIN option header".into()));
214        }
215        let offset = u16::from_be_bytes([payload[at + 1], payload[at + 2]]) as usize;
216        let length = u16::from_be_bytes([payload[at + 3], payload[at + 4]]) as usize;
217        if offset + length > payload.len() {
218            return Err(Error::Protocol("PRELOGIN option points past the packet".into()));
219        }
220        if token == prelogin_option::ENCRYPTION && length >= 1 {
221            encryption = payload[offset];
222        }
223        at += 5;
224    }
225
226    Ok(PreloginResponse { encryption })
227}
228
229// --- LOGIN7 ---
230
231/// Everything LOGIN7 carries that is not a constant.
232#[derive(Debug, Clone)]
233pub struct Login7<'a> {
234    pub hostname: &'a str,
235    pub username: &'a str,
236    /// Already obfuscated by [`super::auth::obfuscate_password`].
237    pub password: &'a [u8],
238    pub application: &'a str,
239    pub server: &'a str,
240    pub library: &'a str,
241    pub language: &'a str,
242    pub database: &'a str,
243    pub packet_size: usize,
244}
245
246/// The size of the fixed part of LOGIN7 in TDS 7.4, which is also where the
247/// variable-length data starts.
248const LOGIN7_FIXED_LEN: usize = 94;
249
250/// fUseDB + fDatabase + fSetLang: the server reports a database or language
251/// change, and refuses the connection outright if the requested database
252/// cannot be opened — a silent fallback to `master` is far worse than an error.
253const OPTION_FLAGS_1: u8 = 0xE0;
254
255/// fODBC, which asks the server for ANSI defaults (quoted identifiers, ANSI
256/// nulls, ANSI warnings) rather than the legacy ones.
257const OPTION_FLAGS_2: u8 = 0x02;
258
259pub fn login7(login: &Login7<'_>) -> Vec<u8> {
260    let mut out = Vec::with_capacity(256);
261
262    // Reserved for the total length, patched once everything is written.
263    out.extend_from_slice(&0u32.to_le_bytes());
264    out.extend_from_slice(&TDS_VERSION_7_4.to_le_bytes());
265    out.extend_from_slice(&(login.packet_size as u32).to_le_bytes());
266    out.extend_from_slice(&0x0100_0000u32.to_le_bytes()); // client program version
267    out.extend_from_slice(&std::process::id().to_le_bytes());
268    out.extend_from_slice(&0u32.to_le_bytes()); // connection id
269    out.push(OPTION_FLAGS_1);
270    out.push(OPTION_FLAGS_2);
271    out.push(0); // type flags: a plain SQL client, read-write
272    out.push(0); // option flags 3: no feature extension
273    out.extend_from_slice(&0i32.to_le_bytes()); // client time zone
274    out.extend_from_slice(&0u32.to_le_bytes()); // client LCID: server default
275
276    // Strings live after the fixed part; each is referenced by an offset from
277    // the start of the payload and a length counted in *characters*, not bytes.
278    let mut data: Vec<u8> = Vec::new();
279    let place = |data: &mut Vec<u8>, text: &str| -> [u8; 4] {
280        let offset = (LOGIN7_FIXED_LEN + data.len()) as u16;
281        let mut characters = 0u16;
282        for unit in text.encode_utf16() {
283            data.extend_from_slice(&unit.to_le_bytes());
284            characters += 1;
285        }
286        let mut entry = [0u8; 4];
287        entry[..2].copy_from_slice(&offset.to_le_bytes());
288        entry[2..].copy_from_slice(&characters.to_le_bytes());
289        entry
290    };
291
292    let hostname = place(&mut data, login.hostname);
293    let username = place(&mut data, login.username);
294
295    // The password is placed by hand: it is already bytes, and its length is
296    // still counted in UTF-16 characters, so it is half the byte count.
297    let password_offset = (LOGIN7_FIXED_LEN + data.len()) as u16;
298    data.extend_from_slice(login.password);
299    let password_characters = (login.password.len() / 2) as u16;
300
301    let application = place(&mut data, login.application);
302    let server = place(&mut data, login.server);
303    let library = place(&mut data, login.library);
304    let language = place(&mut data, login.language);
305    let database = place(&mut data, login.database);
306    let tail = (LOGIN7_FIXED_LEN + data.len()) as u16;
307
308    out.extend_from_slice(&hostname);
309    out.extend_from_slice(&username);
310    out.extend_from_slice(&password_offset.to_le_bytes());
311    out.extend_from_slice(&password_characters.to_le_bytes());
312    out.extend_from_slice(&application);
313    out.extend_from_slice(&server);
314    out.extend_from_slice(&[0u8; 4]); // no extension block
315    out.extend_from_slice(&library);
316    out.extend_from_slice(&language);
317    out.extend_from_slice(&database);
318    out.extend_from_slice(&[0u8; 6]); // client MAC address, which nothing reads
319    out.extend_from_slice(&tail.to_le_bytes()); // ibSSPI
320    out.extend_from_slice(&0u16.to_le_bytes()); // cbSSPI: SQL authentication only
321    out.extend_from_slice(&tail.to_le_bytes()); // ibAtchDBFile
322    out.extend_from_slice(&0u16.to_le_bytes());
323    out.extend_from_slice(&tail.to_le_bytes()); // ibChangePassword
324    out.extend_from_slice(&0u16.to_le_bytes());
325    out.extend_from_slice(&0u32.to_le_bytes()); // cbSSPILong
326
327    debug_assert_eq!(out.len(), LOGIN7_FIXED_LEN);
328    out.extend_from_slice(&data);
329
330    let length = out.len() as u32;
331    out[..4].copy_from_slice(&length.to_le_bytes());
332    out
333}
334
335// --- Requests ---
336
337/// The ALL_HEADERS block every batch and RPC carries in TDS 7.2 and later.
338///
339/// It exists to name the transaction the request belongs to; getting it wrong
340/// makes the server reject the request rather than run it outside the
341/// transaction, which is the safer of the two failure modes.
342pub fn all_headers(transaction: u64) -> Vec<u8> {
343    let mut out = Vec::with_capacity(22);
344    out.extend_from_slice(&22u32.to_le_bytes()); // total length, itself included
345    out.extend_from_slice(&18u32.to_le_bytes()); // this header's length
346    out.extend_from_slice(&2u16.to_le_bytes()); // transaction descriptor header
347    out.extend_from_slice(&transaction.to_le_bytes());
348    out.extend_from_slice(&1u32.to_le_bytes()); // outstanding request count
349    out
350}
351
352/// A statement with no parameters, sent as text.
353pub fn sql_batch(sql: &str, transaction: u64) -> Vec<u8> {
354    let mut out = all_headers(transaction);
355    for unit in sql.encode_utf16() {
356        out.extend_from_slice(&unit.to_le_bytes());
357    }
358    out
359}
360
361/// The well-known procedure id of `sp_executesql`.
362///
363/// Sending the id rather than the name saves the server a name lookup, and is
364/// what every production TDS client does.
365pub const SP_EXECUTESQL: u16 = 10;
366
367/// One parameter of an RPC call: a name and its already-encoded type and value.
368#[derive(Debug, Clone)]
369pub struct RpcParameter {
370    pub name: String,
371    /// TYPE_INFO followed by TYPE_VARBYTE, as [`types::encode`] produces.
372    pub bytes: Vec<u8>,
373}
374
375pub fn rpc(proc_id: u16, parameters: &[RpcParameter], transaction: u64) -> Vec<u8> {
376    let mut out = all_headers(transaction);
377    // 0xFFFF says "a procedure id follows" rather than a name.
378    out.extend_from_slice(&0xFFFFu16.to_le_bytes());
379    out.extend_from_slice(&proc_id.to_le_bytes());
380    out.extend_from_slice(&0u16.to_le_bytes()); // option flags
381
382    for parameter in parameters {
383        let name: Vec<u16> = parameter.name.encode_utf16().collect();
384        out.push(name.len() as u8);
385        for unit in &name {
386            out.extend_from_slice(&unit.to_le_bytes());
387        }
388        out.push(0); // status flags: an input parameter
389        out.extend_from_slice(&parameter.bytes);
390    }
391
392    out
393}
394
395/// Build the `sp_executesql` call for a parameterised statement.
396///
397/// The statement text and the parameter declarations are themselves
398/// parameters, so nothing a caller binds is ever concatenated into SQL. This is
399/// the whole reason the driver takes the RPC route instead of the far simpler
400/// batch one.
401pub fn execute_sql(sql: &str, params: &[Value], transaction: u64) -> Vec<u8> {
402    let declaration = types::declare(params);
403
404    let mut parameters = Vec::with_capacity(params.len() + 2);
405    parameters.push(RpcParameter {
406        name: String::new(),
407        bytes: types::encode(&Value::Text(sql.to_string())),
408    });
409    parameters.push(RpcParameter {
410        name: String::new(),
411        bytes: types::encode(&Value::Text(declaration)),
412    });
413    for (index, value) in params.iter().enumerate() {
414        parameters.push(RpcParameter {
415            name: format!("@P{}", index + 1),
416            bytes: types::encode(value),
417        });
418    }
419
420    rpc(SP_EXECUTESQL, &parameters, transaction)
421}
422
423// --- The token stream ---
424
425/// Token identifiers from the response stream.
426pub mod token {
427    pub const RETURN_STATUS: u8 = 0x79;
428    pub const COLMETADATA: u8 = 0x81;
429    pub const ALTMETADATA: u8 = 0x88;
430    pub const TABNAME: u8 = 0xA4;
431    pub const COLINFO: u8 = 0xA5;
432    pub const ORDER: u8 = 0xA9;
433    pub const ERROR: u8 = 0xAA;
434    pub const INFO: u8 = 0xAB;
435    pub const RETURN_VALUE: u8 = 0xAC;
436    pub const LOGINACK: u8 = 0xAD;
437    pub const FEATUREEXTACK: u8 = 0xAE;
438    pub const ROW: u8 = 0xD1;
439    pub const NBCROW: u8 = 0xD2;
440    pub const ENVCHANGE: u8 = 0xE3;
441    pub const SSPI: u8 = 0xED;
442    pub const DONE: u8 = 0xFD;
443    pub const DONEPROC: u8 = 0xFE;
444    pub const DONEINPROC: u8 = 0xFF;
445}
446
447/// An error or an informational message from the server.
448///
449/// The two share a wire format; only the token byte and the severity separate
450/// a failure from a `print` statement.
451#[derive(Debug, Clone, Default, PartialEq, Eq)]
452pub struct ServerError {
453    pub number: i32,
454    pub state: u8,
455    /// SQL Server calls this the class; `raiserror` calls it the severity.
456    /// Anything above 10 is a real error.
457    pub severity: u8,
458    pub message: String,
459    pub server: String,
460    pub procedure: String,
461    pub line: u32,
462}
463
464impl ServerError {
465    pub fn into_error(self, sql: Option<&str>) -> Error {
466        let mut text = format!(
467            "SQL Server error {} (severity {}, state {}): {}",
468            self.number, self.severity, self.state, self.message
469        );
470        if !self.procedure.is_empty() {
471            text.push_str(&format!(" — in {}, line {}", self.procedure, self.line));
472        }
473        // Pointing at the offending statement is the difference between a
474        // usable error and a puzzle.
475        if let Some(sql) = sql {
476            text.push_str(&format!("\n  SQL: {sql}"));
477        }
478        Error::msg(text)
479    }
480}
481
482/// Which of the three DONE tokens arrived.
483///
484/// They are the same shape but mean different things: DONEINPROC ends one
485/// statement inside a procedure, DONEPROC ends the procedure, DONE ends a
486/// batch. Only the first carries a row count for a statement run through
487/// `sp_executesql`.
488#[derive(Debug, Clone, Copy, PartialEq, Eq)]
489pub enum DoneKind {
490    Batch,
491    Procedure,
492    InProcedure,
493}
494
495#[derive(Debug, Clone, Copy, PartialEq, Eq)]
496pub struct Done {
497    pub kind: DoneKind,
498    pub status: u16,
499    pub current_command: u16,
500    pub rows: u64,
501}
502
503impl Done {
504    /// DONE_COUNT: whether `rows` means anything at all.
505    pub fn has_count(&self) -> bool {
506        self.status & 0x0010 != 0
507    }
508
509    /// DONE_ERROR: the statement failed. An ERROR token said why.
510    pub fn has_error(&self) -> bool {
511        self.status & 0x0002 != 0
512    }
513
514    /// DONE_MORE: another result set follows in this same message.
515    pub fn has_more(&self) -> bool {
516        self.status & 0x0001 != 0
517    }
518}
519
520/// The LOGINACK that says the credentials were accepted.
521#[derive(Debug, Clone, PartialEq, Eq)]
522pub struct LoginAck {
523    pub interface: u8,
524    pub tds_version: u32,
525    pub program: String,
526    pub version: (u8, u8, u16),
527}
528
529/// A change of session state the server announces rather than being asked.
530#[derive(Debug, Clone, PartialEq, Eq)]
531pub enum EnvChange {
532    Database(String),
533    PacketSize(usize),
534    /// The descriptor every later request must quote in its ALL_HEADERS.
535    BeginTransaction(u64),
536    CommitTransaction,
537    RollbackTransaction,
538    /// A change the driver does not act on, named by its type byte.
539    Other(u8),
540}
541
542#[derive(Debug, Clone)]
543pub enum Token {
544    /// A new result set is starting; the columns describe every row after it.
545    ColumnMetadata(Arc<Vec<Column>>),
546    Row(Vec<Value>),
547    Done(Done),
548    Error(ServerError),
549    Info(ServerError),
550    LoginAck(LoginAck),
551    EnvChange(EnvChange),
552    ReturnStatus(i32),
553    /// A token that was parsed far enough to skip it safely.
554    Ignored(u8),
555}
556
557/// Reads a reassembled response message one token at a time.
558///
559/// The stream is stateful because rows carry no types of their own: they are
560/// decoded against the COLMETADATA that preceded them.
561pub struct TokenStream<'a> {
562    reader: Reader<'a>,
563    columns: Arc<Vec<Column>>,
564}
565
566impl<'a> TokenStream<'a> {
567    pub fn new(bytes: &'a [u8]) -> Self {
568        TokenStream { reader: Reader::new(bytes), columns: Arc::new(Vec::new()) }
569    }
570
571    /// The columns of the result set currently being read.
572    pub fn columns(&self) -> &Arc<Vec<Column>> {
573        &self.columns
574    }
575
576    /// The next token, or `None` at the end of the message.
577    ///
578    /// Not an `Iterator`: a token can fail to parse, and a stream that hides
579    /// that behind `None` would silently truncate a result set.
580    pub fn next_token(&mut self) -> Result<Option<Token>> {
581        if self.reader.is_empty() {
582            return Ok(None);
583        }
584
585        let tag = self.reader.u8()?;
586        let parsed = match tag {
587            token::COLMETADATA => {
588                self.columns = Arc::new(types::parse_column_metadata(&mut self.reader)?);
589                Token::ColumnMetadata(Arc::clone(&self.columns))
590            }
591            token::ROW => Token::Row(types::read_row(&mut self.reader, &self.columns)?),
592            token::NBCROW => Token::Row(types::read_nbc_row(&mut self.reader, &self.columns)?),
593            token::DONE => Token::Done(parse_done(DoneKind::Batch, &mut self.reader)?),
594            token::DONEPROC => Token::Done(parse_done(DoneKind::Procedure, &mut self.reader)?),
595            token::DONEINPROC => Token::Done(parse_done(DoneKind::InProcedure, &mut self.reader)?),
596            token::ERROR => Token::Error(parse_server_error(&mut self.reader)?),
597            token::INFO => Token::Info(parse_server_error(&mut self.reader)?),
598            token::LOGINACK => Token::LoginAck(parse_login_ack(&mut self.reader)?),
599            token::ENVCHANGE => Token::EnvChange(parse_env_change(&mut self.reader)?),
600            token::RETURN_STATUS => Token::ReturnStatus(self.reader.i32()?),
601            token::RETURN_VALUE => {
602                skip_return_value(&mut self.reader)?;
603                Token::Ignored(tag)
604            }
605            token::FEATUREEXTACK => {
606                skip_feature_ext_ack(&mut self.reader)?;
607                Token::Ignored(tag)
608            }
609            // Length-prefixed tokens the driver has no use for.
610            token::ORDER | token::TABNAME | token::COLINFO | token::SSPI | token::ALTMETADATA => {
611                let length = self.reader.u16()? as usize;
612                self.reader.skip(length)?;
613                Token::Ignored(tag)
614            }
615            other => {
616                // Guessing at an unknown token's length would silently
617                // desynchronise the whole stream; saying so is safer.
618                return Err(Error::Protocol(format!(
619                    "unknown TDS token 0x{other:02X} in the response stream"
620                )));
621            }
622        };
623
624        Ok(Some(parsed))
625    }
626}
627
628fn parse_done(kind: DoneKind, reader: &mut Reader<'_>) -> Result<Done> {
629    Ok(Done {
630        kind,
631        status: reader.u16()?,
632        current_command: reader.u16()?,
633        rows: reader.u64()?,
634    })
635}
636
637fn parse_server_error(reader: &mut Reader<'_>) -> Result<ServerError> {
638    // The length covers everything after itself; the fields are read rather
639    // than sliced, so a short token becomes a protocol error either way.
640    let _length = reader.u16()?;
641    Ok(ServerError {
642        number: reader.i32()?,
643        state: reader.u8()?,
644        severity: reader.u8()?,
645        message: reader.us_varchar()?,
646        server: reader.b_varchar()?,
647        procedure: reader.b_varchar()?,
648        line: reader.u32()?,
649    })
650}
651
652fn parse_login_ack(reader: &mut Reader<'_>) -> Result<LoginAck> {
653    let _length = reader.u16()?;
654    let interface = reader.u8()?;
655    let tds_version = reader.u32()?;
656    let program = reader.b_varchar()?;
657    let major = reader.u8()?;
658    let minor = reader.u8()?;
659    let build_high = reader.u8()?;
660    let build_low = reader.u8()?;
661
662    Ok(LoginAck {
663        interface,
664        tds_version,
665        program,
666        version: (major, minor, u16::from_be_bytes([build_high, build_low])),
667    })
668}
669
670fn parse_env_change(reader: &mut Reader<'_>) -> Result<EnvChange> {
671    let length = reader.u16()? as usize;
672    let body = reader.take(length)?;
673    let mut inner = Reader::new(body);
674
675    Ok(match inner.u8()? {
676        1 => EnvChange::Database(inner.b_varchar()?),
677        // The negotiated packet size arrives as a decimal string, not a number.
678        4 => EnvChange::PacketSize(
679            inner.b_varchar()?.parse().unwrap_or(DEFAULT_PACKET_SIZE),
680        ),
681        8 => {
682            let descriptor = inner.b_varbyte()?;
683            let mut bytes = [0u8; 8];
684            let taken = descriptor.len().min(8);
685            bytes[..taken].copy_from_slice(&descriptor[..taken]);
686            EnvChange::BeginTransaction(u64::from_le_bytes(bytes))
687        }
688        9 => EnvChange::CommitTransaction,
689        10 => EnvChange::RollbackTransaction,
690        other => EnvChange::Other(other),
691    })
692}
693
694/// Consume a RETURNVALUE token. `sp_executesql` is called without output
695/// parameters, so its value is read only to keep the stream aligned.
696fn skip_return_value(reader: &mut Reader<'_>) -> Result<()> {
697    reader.u16()?; // parameter ordinal
698    reader.b_varchar()?; // parameter name
699    reader.u8()?; // status
700    reader.u32()?; // user type
701    reader.u16()?; // flags
702    let type_info = types::parse_type_info(reader)?;
703    types::read_value(reader, &type_info)?;
704    Ok(())
705}
706
707/// Consume a FEATUREEXTACK token, which is a list terminated by 0xFF.
708fn skip_feature_ext_ack(reader: &mut Reader<'_>) -> Result<()> {
709    loop {
710        if reader.u8()? == 0xFF {
711            return Ok(());
712        }
713        let length = reader.u32()? as usize;
714        reader.skip(length)?;
715    }
716}
717
718/// A cursor over a reassembled message.
719///
720/// Every multi-byte field in TDS below the packet header is little-endian, so
721/// unlike the PostgreSQL reader this one never sees a big-endian integer.
722pub struct Reader<'a> {
723    bytes: &'a [u8],
724    position: usize,
725}
726
727impl<'a> Reader<'a> {
728    pub fn new(bytes: &'a [u8]) -> Self {
729        Reader { bytes, position: 0 }
730    }
731
732    pub fn is_empty(&self) -> bool {
733        self.position >= self.bytes.len()
734    }
735
736    pub fn remaining(&self) -> usize {
737        self.bytes.len().saturating_sub(self.position)
738    }
739
740    pub fn take(&mut self, count: usize) -> Result<&'a [u8]> {
741        let end = self.position.checked_add(count).ok_or_else(too_short)?;
742        if end > self.bytes.len() {
743            return Err(too_short());
744        }
745        let slice = &self.bytes[self.position..end];
746        self.position = end;
747        Ok(slice)
748    }
749
750    pub fn skip(&mut self, count: usize) -> Result<()> {
751        self.take(count).map(|_| ())
752    }
753
754    pub fn u8(&mut self) -> Result<u8> {
755        Ok(self.take(1)?[0])
756    }
757
758    pub fn u16(&mut self) -> Result<u16> {
759        Ok(u16::from_le_bytes(self.take(2)?.try_into().expect("2 bytes")))
760    }
761
762    pub fn i32(&mut self) -> Result<i32> {
763        Ok(i32::from_le_bytes(self.take(4)?.try_into().expect("4 bytes")))
764    }
765
766    pub fn u32(&mut self) -> Result<u32> {
767        Ok(u32::from_le_bytes(self.take(4)?.try_into().expect("4 bytes")))
768    }
769
770    pub fn u64(&mut self) -> Result<u64> {
771        Ok(u64::from_le_bytes(self.take(8)?.try_into().expect("8 bytes")))
772    }
773
774    /// A string with a one-byte character count.
775    pub fn b_varchar(&mut self) -> Result<String> {
776        let characters = self.u8()? as usize;
777        self.ucs2(characters)
778    }
779
780    /// A string with a two-byte character count.
781    pub fn us_varchar(&mut self) -> Result<String> {
782        let characters = self.u16()? as usize;
783        self.ucs2(characters)
784    }
785
786    /// A byte string with a one-byte length.
787    pub fn b_varbyte(&mut self) -> Result<&'a [u8]> {
788        let length = self.u8()? as usize;
789        self.take(length)
790    }
791
792    fn ucs2(&mut self, characters: usize) -> Result<String> {
793        let bytes = self.take(characters * 2)?;
794        Ok(decode_ucs2(bytes))
795    }
796}
797
798/// Decode UTF-16LE, which TDS calls UCS-2 and uses for every string it sends.
799pub fn decode_ucs2(bytes: &[u8]) -> String {
800    // `as_chunks` rather than `chunks_exact(2)`: the pair arrives as a fixed
801    // `[u8; 2]`, so `from_le_bytes` needs no bounds check and no copy. A
802    // trailing odd byte is dropped either way, which is right — half a UTF-16
803    // code unit is not a character.
804    let (pairs, _odd_trailing_byte) = bytes.as_chunks::<2>();
805    let units: Vec<u16> = pairs.iter().copied().map(u16::from_le_bytes).collect();
806    String::from_utf16_lossy(&units)
807}
808
809fn too_short() -> Error {
810    Error::Protocol("truncated message from the server".into())
811}
812
813#[cfg(test)]
814mod tests {
815    use super::*;
816
817    #[test]
818    fn a_packet_header_survives_a_round_trip() {
819        let header = PacketHeader {
820            kind: packet::SQL_BATCH,
821            status: status::END_OF_MESSAGE,
822            length: 4096,
823            spid: 53,
824            id: 7,
825            window: 0,
826        };
827
828        let mut bytes = Vec::new();
829        header.write_into(&mut bytes);
830
831        assert_eq!(bytes.len(), HEADER_LEN);
832        // Length and SPID are big-endian even though the rest of TDS is not.
833        assert_eq!(&bytes[2..4], &4096u16.to_be_bytes());
834        assert_eq!(PacketHeader::parse(&bytes).unwrap(), header);
835    }
836
837    #[test]
838    fn a_short_header_is_a_protocol_error() {
839        assert!(PacketHeader::parse(&[0x04, 0x01, 0x00]).is_err());
840    }
841
842    #[test]
843    fn a_payload_that_fits_becomes_one_packet_marked_final() {
844        let packets = split_message(packet::SQL_BATCH, b"hello", DEFAULT_PACKET_SIZE);
845        assert_eq!(packets.len(), 1);
846
847        let header = PacketHeader::parse(&packets[0]).unwrap();
848        assert_eq!(header.kind, packet::SQL_BATCH);
849        assert_eq!(header.length as usize, packets[0].len());
850        assert_eq!(header.id, 1);
851        assert!(header.is_end_of_message());
852        assert_eq!(&packets[0][HEADER_LEN..], b"hello");
853    }
854
855    #[test]
856    fn a_payload_larger_than_the_packet_size_is_split_and_only_the_last_ends_it() {
857        // Three packets: 8 bytes of header leaves 24 bytes of room in each.
858        let payload: Vec<u8> = (0..60u8).collect();
859        let packets = split_message(packet::SQL_BATCH, &payload, 32);
860
861        assert_eq!(packets.len(), 3);
862
863        let headers: Vec<PacketHeader> =
864            packets.iter().map(|p| PacketHeader::parse(p).unwrap()).collect();
865        assert_eq!(headers.iter().map(|h| h.id).collect::<Vec<_>>(), vec![1, 2, 3]);
866        assert!(!headers[0].is_end_of_message());
867        assert!(!headers[1].is_end_of_message());
868        assert!(headers[2].is_end_of_message());
869        // None exceeds the negotiated size, header included.
870        assert!(packets.iter().all(|p| p.len() <= 32));
871
872        // Split and reassembled, the payload is unchanged.
873        let rebuilt: Vec<u8> =
874            packets.iter().flat_map(|p| p[HEADER_LEN..].iter().copied()).collect();
875        assert_eq!(rebuilt, payload);
876    }
877
878    #[test]
879    fn an_empty_payload_is_still_one_end_of_message_packet() {
880        let packets = split_message(packet::PRE_LOGIN, &[], DEFAULT_PACKET_SIZE);
881
882        assert_eq!(packets.len(), 1);
883        assert_eq!(packets[0].len(), HEADER_LEN);
884        assert!(PacketHeader::parse(&packets[0]).unwrap().is_end_of_message());
885    }
886
887    #[test]
888    fn a_prelogin_requests_encryption_and_its_offsets_point_at_its_data() {
889        let payload = prelogin(encryption::ON);
890
891        // Five options of five bytes each, then the terminator.
892        assert_eq!(payload[25], prelogin_option::TERMINATOR);
893        assert_eq!(payload[5], prelogin_option::ENCRYPTION);
894
895        let offset = u16::from_be_bytes([payload[6], payload[7]]) as usize;
896        let length = u16::from_be_bytes([payload[8], payload[9]]) as usize;
897        assert_eq!(length, 1);
898        assert_eq!(payload[offset], encryption::ON);
899    }
900
901    #[test]
902    fn reads_the_encryption_level_the_server_chose() {
903        let mut answer = prelogin(encryption::REQUIRED);
904        assert_eq!(
905            parse_prelogin(&answer).unwrap(),
906            PreloginResponse { encryption: encryption::REQUIRED }
907        );
908
909        // A response with no ENCRYPTION option at all means no encryption.
910        answer.truncate(1);
911        answer[0] = prelogin_option::TERMINATOR;
912        assert_eq!(
913            parse_prelogin(&answer).unwrap().encryption,
914            encryption::NOT_SUPPORTED
915        );
916    }
917
918    #[test]
919    fn a_prelogin_option_pointing_past_the_packet_is_rejected() {
920        let payload = vec![prelogin_option::ENCRYPTION, 0xFF, 0xFF, 0x00, 0x01, 0xFF];
921        assert!(parse_prelogin(&payload).is_err());
922    }
923
924    #[test]
925    fn login7_declares_its_own_length_and_counts_strings_in_characters() {
926        let payload = login7(&Login7 {
927            hostname: "laptop",
928            username: "sa",
929            password: &[0xB3, 0xA5, 0x83, 0xA5],
930            application: "rustlavel",
931            server: "db",
932            library: "rustlavel-db",
933            language: "",
934            database: "blog",
935            packet_size: DEFAULT_PACKET_SIZE,
936        });
937
938        assert_eq!(
939            u32::from_le_bytes(payload[..4].try_into().unwrap()) as usize,
940            payload.len()
941        );
942        assert_eq!(u32::from_le_bytes(payload[4..8].try_into().unwrap()), TDS_VERSION_7_4);
943
944        // The username entry: offset then a count of characters, not bytes.
945        let username_offset = u16::from_le_bytes(payload[40..42].try_into().unwrap()) as usize;
946        let username_length = u16::from_le_bytes(payload[42..44].try_into().unwrap()) as usize;
947        assert_eq!(username_length, 2);
948        assert_eq!(
949            decode_ucs2(&payload[username_offset..username_offset + username_length * 2]),
950            "sa"
951        );
952
953        // The password is two UTF-16 characters, so four bytes.
954        let password_length = u16::from_le_bytes(payload[46..48].try_into().unwrap());
955        assert_eq!(password_length, 2);
956    }
957
958    #[test]
959    fn a_batch_carries_the_transaction_it_belongs_to() {
960        let payload = sql_batch("select 1", 0xDEAD_BEEF);
961
962        assert_eq!(u32::from_le_bytes(payload[..4].try_into().unwrap()), 22);
963        assert_eq!(u64::from_le_bytes(payload[10..18].try_into().unwrap()), 0xDEAD_BEEF);
964        assert_eq!(decode_ucs2(&payload[22..]), "select 1");
965    }
966
967    #[test]
968    fn an_rpc_names_its_procedure_by_id() {
969        let payload = rpc(SP_EXECUTESQL, &[], 0);
970
971        assert_eq!(u16::from_le_bytes(payload[22..24].try_into().unwrap()), 0xFFFF);
972        assert_eq!(u16::from_le_bytes(payload[24..26].try_into().unwrap()), SP_EXECUTESQL);
973    }
974
975    #[test]
976    fn a_parameterised_call_sends_the_statement_as_data_not_as_sql() {
977        let hostile = "'; drop table users; --";
978        let payload = execute_sql("select @P1", &[Value::Text(hostile.into())], 0);
979
980        // The statement text appears once, and the hostile value appears as a
981        // separate UTF-16 parameter — never spliced into the statement.
982        let statement: Vec<u8> = "select @P1".encode_utf16().flat_map(u16::to_le_bytes).collect();
983        let value: Vec<u8> = hostile.encode_utf16().flat_map(u16::to_le_bytes).collect();
984
985        assert!(payload.windows(statement.len()).any(|w| w == statement));
986        assert!(payload.windows(value.len()).any(|w| w == value));
987
988        // The declaration names the parameter and its type, so the server binds
989        // it rather than parsing it.
990        let declaration: Vec<u8> =
991            "@P1 nvarchar(max)".encode_utf16().flat_map(u16::to_le_bytes).collect();
992        assert!(payload.windows(declaration.len()).any(|w| w == declaration));
993    }
994
995    #[test]
996    fn an_error_token_names_its_number_and_severity() {
997        let mut body = vec![token::ERROR];
998        let mut fields = Vec::new();
999        fields.extend_from_slice(&18456i32.to_le_bytes());
1000        fields.push(1); // state
1001        fields.push(14); // severity
1002        let message = "Login failed for user 'sa'.";
1003        fields.extend_from_slice(&(message.encode_utf16().count() as u16).to_le_bytes());
1004        fields.extend(message.encode_utf16().flat_map(u16::to_le_bytes));
1005        fields.push(2); // server name length
1006        fields.extend("db".encode_utf16().flat_map(u16::to_le_bytes));
1007        fields.push(0); // no procedure
1008        fields.extend_from_slice(&1u32.to_le_bytes());
1009        body.extend_from_slice(&(fields.len() as u16).to_le_bytes());
1010        body.extend_from_slice(&fields);
1011
1012        let mut stream = TokenStream::new(&body);
1013        let error = match stream.next_token().unwrap().unwrap() {
1014            Token::Error(error) => error,
1015            other => panic!("expected an error token, got {other:?}"),
1016        };
1017
1018        assert_eq!(error.number, 18456);
1019        assert_eq!(error.severity, 14);
1020        assert_eq!(error.message, message);
1021        assert_eq!(error.server, "db");
1022
1023        let rendered = error.into_error(Some("select 1")).to_string();
1024        assert!(rendered.contains("18456"), "{rendered}");
1025        assert!(rendered.contains("severity 14"), "{rendered}");
1026        assert!(rendered.contains("SQL: select 1"), "{rendered}");
1027    }
1028
1029    #[test]
1030    fn a_done_token_reports_the_rows_a_statement_touched() {
1031        let mut body = vec![token::DONEINPROC];
1032        body.extend_from_slice(&0x0011u16.to_le_bytes()); // DONE_MORE | DONE_COUNT
1033        body.extend_from_slice(&0xC1u16.to_le_bytes()); // current command
1034        body.extend_from_slice(&3u64.to_le_bytes());
1035
1036        let mut stream = TokenStream::new(&body);
1037        let done = match stream.next_token().unwrap().unwrap() {
1038            Token::Done(done) => done,
1039            other => panic!("expected a done token, got {other:?}"),
1040        };
1041
1042        assert_eq!(done.kind, DoneKind::InProcedure);
1043        assert_eq!(done.rows, 3);
1044        assert!(done.has_count());
1045        assert!(done.has_more());
1046        assert!(!done.has_error());
1047    }
1048
1049    #[test]
1050    fn a_done_token_without_a_count_bit_reports_no_rows() {
1051        let mut body = vec![token::DONE];
1052        body.extend_from_slice(&0u16.to_le_bytes());
1053        body.extend_from_slice(&0u16.to_le_bytes());
1054        body.extend_from_slice(&99u64.to_le_bytes());
1055
1056        let mut stream = TokenStream::new(&body);
1057        match stream.next_token().unwrap().unwrap() {
1058            // The count is still on the wire; `has_count` is what says to trust it.
1059            Token::Done(done) => assert!(!done.has_count()),
1060            other => panic!("expected a done token, got {other:?}"),
1061        }
1062    }
1063
1064    #[test]
1065    fn an_env_change_announces_a_transaction_and_then_ends_it() {
1066        let mut body = vec![token::ENVCHANGE];
1067        let mut change = vec![8u8]; // begin transaction
1068        change.push(8); // descriptor length
1069        change.extend_from_slice(&0x0102_0304_0506_0708u64.to_le_bytes());
1070        change.push(0); // no old value
1071        body.extend_from_slice(&(change.len() as u16).to_le_bytes());
1072        body.extend_from_slice(&change);
1073
1074        body.push(token::ENVCHANGE);
1075        let ended = vec![9u8, 0, 0];
1076        body.extend_from_slice(&(ended.len() as u16).to_le_bytes());
1077        body.extend_from_slice(&ended);
1078
1079        let mut stream = TokenStream::new(&body);
1080        match stream.next_token().unwrap().unwrap() {
1081            Token::EnvChange(EnvChange::BeginTransaction(descriptor)) => {
1082                assert_eq!(descriptor, 0x0102_0304_0506_0708)
1083            }
1084            other => panic!("expected a transaction to begin, got {other:?}"),
1085        }
1086        match stream.next_token().unwrap().unwrap() {
1087            Token::EnvChange(EnvChange::CommitTransaction) => {}
1088            other => panic!("expected a commit, got {other:?}"),
1089        }
1090    }
1091
1092    #[test]
1093    fn the_negotiated_packet_size_arrives_as_a_decimal_string() {
1094        let mut body = vec![token::ENVCHANGE];
1095        let mut change = vec![4u8];
1096        change.push(4); // "8192" is four characters
1097        change.extend("8192".encode_utf16().flat_map(u16::to_le_bytes));
1098        change.push(0);
1099        body.extend_from_slice(&(change.len() as u16).to_le_bytes());
1100        body.extend_from_slice(&change);
1101
1102        let mut stream = TokenStream::new(&body);
1103        match stream.next_token().unwrap().unwrap() {
1104            Token::EnvChange(EnvChange::PacketSize(size)) => assert_eq!(size, 8192),
1105            other => panic!("expected a packet size change, got {other:?}"),
1106        }
1107    }
1108
1109    #[test]
1110    fn a_login_ack_reports_the_version_that_was_negotiated() {
1111        let mut fields = vec![1u8]; // interface
1112        fields.extend_from_slice(&TDS_VERSION_7_4.to_le_bytes());
1113        fields.push(4);
1114        fields.extend("mssq".encode_utf16().flat_map(u16::to_le_bytes));
1115        fields.extend_from_slice(&[16, 0, 0x0F, 0xA0]);
1116
1117        let mut body = vec![token::LOGINACK];
1118        body.extend_from_slice(&(fields.len() as u16).to_le_bytes());
1119        body.extend_from_slice(&fields);
1120
1121        let mut stream = TokenStream::new(&body);
1122        match stream.next_token().unwrap().unwrap() {
1123            Token::LoginAck(ack) => {
1124                assert_eq!(ack.program, "mssq");
1125                assert_eq!(ack.version, (16, 0, 0x0FA0));
1126                assert_eq!(ack.tds_version, TDS_VERSION_7_4);
1127            }
1128            other => panic!("expected a login ack, got {other:?}"),
1129        }
1130    }
1131
1132    #[test]
1133    fn an_unknown_token_stops_the_stream_rather_than_guessing() {
1134        let error = TokenStream::new(&[0x42]).next_token().unwrap_err().to_string();
1135        assert!(error.contains("0x42"), "{error}");
1136    }
1137
1138    #[test]
1139    fn an_empty_message_yields_no_tokens() {
1140        assert!(TokenStream::new(&[]).next_token().unwrap().is_none());
1141    }
1142
1143    #[test]
1144    fn a_truncated_token_is_a_protocol_error_not_a_panic() {
1145        // A DONE token that stops halfway through its row count.
1146        let mut body = vec![token::DONE];
1147        body.extend_from_slice(&[0, 0, 0, 0, 1, 2]);
1148
1149        assert!(TokenStream::new(&body).next_token().is_err());
1150    }
1151
1152    #[test]
1153    fn reads_little_endian_scalars_and_counted_strings() {
1154        let mut bytes = Vec::new();
1155        bytes.extend_from_slice(&0x1234u16.to_le_bytes());
1156        bytes.extend_from_slice(&(-5i32).to_le_bytes());
1157        bytes.push(3);
1158        bytes.extend("ada".encode_utf16().flat_map(u16::to_le_bytes));
1159
1160        let mut reader = Reader::new(&bytes);
1161        assert_eq!(reader.u16().unwrap(), 0x1234);
1162        assert_eq!(reader.i32().unwrap(), -5);
1163        assert_eq!(reader.b_varchar().unwrap(), "ada");
1164        assert!(reader.is_empty());
1165    }
1166}