sqlx_core/mssql/protocol/
packet.rs

1use bitflags::bitflags;
2use bytes::{Buf, Bytes};
3
4use crate::error::Error;
5use crate::io::{Decode, Encode};
6
7#[derive(Debug)]
8pub(crate) struct PacketHeader {
9    // Type defines the type of message. Type is a 1-byte unsigned char.
10    pub(crate) r#type: PacketType,
11
12    // Status is a bit field used to indicate the message state. Status is a 1-byte unsigned char.
13    pub(crate) status: Status,
14
15    // Length is the size of the packet including the 8 bytes in the packet header.
16    pub(crate) length: u16,
17
18    // The process ID on the server, corresponding to the current connection.
19    pub(crate) server_process_id: u16,
20
21    // Packet ID is used for numbering message packets that contain data in addition to the packet
22    // header. Packet ID is a 1-byte, unsigned char. Each time packet data is sent, the value of
23    // PacketID is incremented by 1, modulo 256. This allows the receiver to track the sequence
24    // of TDS packets for a given message. This value is currently ignored.
25    pub(crate) packet_id: u8,
26}
27
28impl<'s> Encode<'s, &'s mut usize> for PacketHeader {
29    fn encode_with(&self, buf: &mut Vec<u8>, offset: &'s mut usize) {
30        buf.push(self.r#type as u8);
31        buf.push(self.status.bits());
32
33        *offset = buf.len();
34        buf.extend(&self.length.to_be_bytes());
35
36        buf.extend(&self.server_process_id.to_be_bytes());
37        buf.push(self.packet_id);
38
39        // window, unused
40        buf.push(0);
41    }
42}
43
44impl Decode<'_> for PacketHeader {
45    fn decode_with(mut buf: Bytes, _: ()) -> Result<Self, Error> {
46        Ok(Self {
47            r#type: PacketType::get(buf.get_u8())?,
48            status: Status::from_bits_truncate(buf.get_u8()),
49            length: buf.get_u16(),
50            server_process_id: buf.get_u16(),
51            packet_id: buf.get_u8(),
52        })
53    }
54}
55
56#[derive(Debug, Copy, PartialEq, Clone)]
57pub(crate) enum PacketType {
58    // Pre-login. Should always be #18 unless we decide to try and support pre 7.0 TDS
59    PreTds7Login = 2,
60    PreLogin = 18,
61
62    SqlBatch = 1,
63    Rpc = 3,
64    AttentionSignal = 6,
65    BulkLoadData = 7,
66    FederatedAuthToken = 8,
67    TransactionManagerRequest = 14,
68    Tds7Login = 16,
69    Sspi = 17,
70
71    TabularResult = 4,
72}
73
74impl PacketType {
75    pub fn get(value: u8) -> Result<Self, Error> {
76        Ok(match value {
77            1 => PacketType::SqlBatch,
78            2 => PacketType::PreTds7Login,
79            3 => PacketType::Rpc,
80            4 => PacketType::TabularResult,
81            6 => PacketType::AttentionSignal,
82            7 => PacketType::BulkLoadData,
83            8 => PacketType::FederatedAuthToken,
84            14 => PacketType::TransactionManagerRequest,
85            16 => PacketType::Tds7Login,
86            17 => PacketType::Sspi,
87            18 => PacketType::PreLogin,
88
89            ty => {
90                return Err(err_protocol!("unknown packet type: {}", ty));
91            }
92        })
93    }
94}
95
96// Status is a bit field used to indicate the message state. Status is a 1-byte unsigned char.
97// The following Status bit flags are defined.
98bitflags! {
99    pub(crate) struct Status: u8 {
100        // "Normal" message.
101        const NORMAL = 0x00;
102
103        // End of message (EOM). The packet is the last packet in the whole request.
104        const END_OF_MESSAGE = 0x01;
105
106        // (From client to server) Ignore this event (0x01 MUST also be set).
107        const IGNORE_EVENT = 0x02;
108
109        // RESETCONNECTION
110        //
111        // (Introduced in TDS 7.1)
112        //
113        // (From client to server) Reset this connection
114        // before processing event. Only set for event types Batch, RPC, or Transaction Manager
115        // request. If clients want to set this bit, it MUST be part of the first packet of the
116        // message. This signals the server to clean up the environment state of the connection
117        // back to the default environment setting, effectively simulating a logout and a
118        // subsequent login, and provides server support for connection pooling. This bit SHOULD
119        // be ignored if it is set in a packet that is not the first packet of the message.
120        //
121        // This status bit MUST NOT be set in conjunction with the RESETCONNECTIONSKIPTRAN bit.
122        // Distributed transactions and isolation levels will not be reset.
123        const RESET_CONN = 0x08;
124
125        // RESETCONNECTIONSKIPTRAN
126        //
127        // (Introduced in TDS 7.3)
128        //
129        // (From client to server) Reset the
130        // connection before processing event but do not modify the transaction state (the
131        // state will remain the same before and after the reset). The transaction in the
132        // session can be a local transaction that is started from the session or it can
133        // be a distributed transaction in which the session is enlisted. This status bit
134        // MUST NOT be set in conjunction with the RESETCONNECTION bit.
135        // Otherwise identical to RESETCONNECTION.
136        const RESET_CONN_SKIP_TRAN = 0x10;
137    }
138}