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