1use super::types::{self, Column};
15use crate::value::Value;
16use rustlavel_core::{Error, Result};
17use std::sync::Arc;
18
19pub const HEADER_LEN: usize = 8;
21
22pub const DEFAULT_PACKET_SIZE: usize = 4096;
27
28pub const TDS_VERSION_7_4: u32 = 0x7400_0004;
30
31pub 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
42pub 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub struct PacketHeader {
53 pub kind: u8,
54 pub status: u8,
55 pub length: u16,
57 pub spid: u16,
58 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: 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
93pub 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 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
139pub 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
151pub mod encryption {
153 pub const OFF: u8 = 0x00;
155 pub const ON: u8 = 0x01;
157 pub const NOT_SUPPORTED: u8 = 0x02;
159 pub const REQUIRED: u8 = 0x03;
161}
162
163pub fn prelogin(encryption: u8) -> Vec<u8> {
169 let options: [(u8, Vec<u8>); 5] = [
170 (prelogin_option::VERSION, vec![9, 0, 0, 0, 0, 0]),
172 (prelogin_option::ENCRYPTION, vec![encryption]),
173 (prelogin_option::INSTOPT, vec![0]),
175 (prelogin_option::THREADID, 0u32.to_le_bytes().to_vec()),
176 (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#[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#[derive(Debug, Clone)]
233pub struct Login7<'a> {
234 pub hostname: &'a str,
235 pub username: &'a str,
236 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
246const LOGIN7_FIXED_LEN: usize = 94;
249
250const OPTION_FLAGS_1: u8 = 0xE0;
254
255const OPTION_FLAGS_2: u8 = 0x02;
258
259pub fn login7(login: &Login7<'_>) -> Vec<u8> {
260 let mut out = Vec::with_capacity(256);
261
262 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()); out.extend_from_slice(&std::process::id().to_le_bytes());
268 out.extend_from_slice(&0u32.to_le_bytes()); out.push(OPTION_FLAGS_1);
270 out.push(OPTION_FLAGS_2);
271 out.push(0); out.push(0); out.extend_from_slice(&0i32.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); 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 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]); out.extend_from_slice(&library);
316 out.extend_from_slice(&language);
317 out.extend_from_slice(&database);
318 out.extend_from_slice(&[0u8; 6]); out.extend_from_slice(&tail.to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&tail.to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes());
323 out.extend_from_slice(&tail.to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes());
325 out.extend_from_slice(&0u32.to_le_bytes()); 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
335pub fn all_headers(transaction: u64) -> Vec<u8> {
343 let mut out = Vec::with_capacity(22);
344 out.extend_from_slice(&22u32.to_le_bytes()); out.extend_from_slice(&18u32.to_le_bytes()); out.extend_from_slice(&2u16.to_le_bytes()); out.extend_from_slice(&transaction.to_le_bytes());
348 out.extend_from_slice(&1u32.to_le_bytes()); out
350}
351
352pub 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
361pub const SP_EXECUTESQL: u16 = 10;
366
367#[derive(Debug, Clone)]
369pub struct RpcParameter {
370 pub name: String,
371 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 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()); 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); out.extend_from_slice(¶meter.bytes);
390 }
391
392 out
393}
394
395pub 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, ¶meters, transaction)
421}
422
423pub 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#[derive(Debug, Clone, Default, PartialEq, Eq)]
452pub struct ServerError {
453 pub number: i32,
454 pub state: u8,
455 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 if let Some(sql) = sql {
476 text.push_str(&format!("\n SQL: {sql}"));
477 }
478 Error::msg(text)
479 }
480}
481
482#[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 pub fn has_count(&self) -> bool {
506 self.status & 0x0010 != 0
507 }
508
509 pub fn has_error(&self) -> bool {
511 self.status & 0x0002 != 0
512 }
513
514 pub fn has_more(&self) -> bool {
516 self.status & 0x0001 != 0
517 }
518}
519
520#[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#[derive(Debug, Clone, PartialEq, Eq)]
531pub enum EnvChange {
532 Database(String),
533 PacketSize(usize),
534 BeginTransaction(u64),
536 CommitTransaction,
537 RollbackTransaction,
538 Other(u8),
540}
541
542#[derive(Debug, Clone)]
543pub enum Token {
544 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 Ignored(u8),
555}
556
557pub 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 pub fn columns(&self) -> &Arc<Vec<Column>> {
573 &self.columns
574 }
575
576 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 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 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 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 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
694fn skip_return_value(reader: &mut Reader<'_>) -> Result<()> {
697 reader.u16()?; reader.b_varchar()?; reader.u8()?; reader.u32()?; reader.u16()?; let type_info = types::parse_type_info(reader)?;
703 types::read_value(reader, &type_info)?;
704 Ok(())
705}
706
707fn 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
718pub 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 pub fn b_varchar(&mut self) -> Result<String> {
776 let characters = self.u8()? as usize;
777 self.ucs2(characters)
778 }
779
780 pub fn us_varchar(&mut self) -> Result<String> {
782 let characters = self.u16()? as usize;
783 self.ucs2(characters)
784 }
785
786 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
798pub fn decode_ucs2(bytes: &[u8]) -> String {
800 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 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 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 assert!(packets.iter().all(|p| p.len() <= 32));
871
872 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 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 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 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 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 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 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); fields.push(14); 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); fields.extend("db".encode_utf16().flat_map(u16::to_le_bytes));
1007 fields.push(0); 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()); body.extend_from_slice(&0xC1u16.to_le_bytes()); 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 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]; change.push(8); change.extend_from_slice(&0x0102_0304_0506_0708u64.to_le_bytes());
1070 change.push(0); 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); 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]; 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 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}