1#![forbid(unsafe_code)]
2
3use super::*;
4
5pub fn connect_data_fits_inline(connect_data: &str) -> bool {
10 connect_data.len() <= TNS_MAX_CONNECT_DATA
11}
12
13pub fn build_connect_packet_payload(connect_data: &str, sdu: u16) -> Result<Vec<u8>> {
14 let connect_bytes = connect_data.as_bytes();
15 let connect_len =
16 u16::try_from(connect_bytes.len()).map_err(|_| ProtocolError::PacketTooLarge {
17 length: connect_bytes.len(),
18 })?;
19
20 let mut writer = TtcWriter::new();
21 writer.write_u16be(TNS_VERSION_DESIRED);
22 writer.write_u16be(TNS_VERSION_MIN);
23 writer.write_u16be(TNS_GSO_DONT_CARE);
24 writer.write_u16be(sdu);
25 writer.write_u16be(sdu);
26 writer.write_u16be(TNS_PROTOCOL_CHARACTERISTICS);
27 writer.write_u16be(0);
28 writer.write_u16be(1);
29 writer.write_u16be(connect_len);
30 writer.write_u16be(74);
31 writer.write_u32be(0);
32 let nsi_flags = TNS_NSI_SUPPORT_SECURITY_RENEG | TNS_NSI_DISABLE_NA;
33 writer.write_u8(nsi_flags);
34 writer.write_u8(nsi_flags);
35 writer.write_u64be(0);
36 writer.write_u64be(0);
37 writer.write_u64be(0);
38 writer.write_u32be(u32::from(sdu));
39 writer.write_u32be(u32::from(sdu));
40 writer.write_u32be(0);
41 writer.write_u32be(0);
42 if connect_data_fits_inline(connect_data) {
45 writer.write_raw(connect_bytes);
46 }
47 Ok(writer.into_bytes())
48}
49
50pub fn parse_accept_payload(payload: &[u8]) -> Result<AcceptInfo> {
51 let mut reader = TtcReader::new(payload);
52 let protocol_version = reader.read_u16be()?;
53 let protocol_options = reader.read_u16be()?;
54 reader.skip(10)?;
55 let flags1 = reader.read_u8()?;
56 if has_u8_flag(flags1, TNS_NSI_NA_REQUIRED) {
57 return Err(ProtocolError::UnsupportedFeature(
58 "Native Network Encryption and Data Integrity",
59 ));
60 }
61 reader.skip(9)?;
62 let sdu = reader.read_u32be()?;
63 let mut flags2 = 0;
64 if protocol_version >= 318 {
65 reader.skip(5)?;
66 flags2 = reader.read_u32be()?;
67 }
68
69 Ok(AcceptInfo {
70 protocol_version,
71 protocol_options,
72 sdu,
73 supports_fast_auth: has_u32_flag(flags2, TNS_ACCEPT_FLAG_FAST_AUTH),
74 supports_oob_check: has_u32_flag(flags2, TNS_ACCEPT_FLAG_CHECK_OOB),
75 supports_oob: protocol_options & TNS_GSO_CAN_RECV_ATTENTION != 0,
78 supports_end_of_response: protocol_version >= 319
79 && has_u32_flag(flags2, TNS_ACCEPT_FLAG_HAS_END_OF_RESPONSE),
80 })
81}
82
83pub fn build_fast_auth_phase_one_payload(
84 user: &str,
85 program: &str,
86 machine: &str,
87 osuser: &str,
88 terminal: &str,
89 pid: u32,
90) -> Result<Vec<u8>> {
91 let mut out = Vec::from_hex(FAST_AUTH_PREFIX_HEX)
92 .map_err(|_| ProtocolError::TtcDecode("invalid static fast-auth prefix"))?;
93 append_auth_phase_one(&mut out, user, program, machine, osuser, terminal, pid)?;
94 Ok(out)
95}
96
97const FAST_AUTH_PROTOCOL_MSG_START: usize = 4;
110const FAST_AUTH_PROTOCOL_MSG_END: usize = 23;
111const FAST_AUTH_DATA_TYPES_MSG_START: usize = 29;
112
113fn fast_auth_prefix_slice(start: usize, end: Option<usize>) -> Result<Vec<u8>> {
114 let prefix = Vec::from_hex(FAST_AUTH_PREFIX_HEX)
115 .map_err(|_| ProtocolError::TtcDecode("invalid static fast-auth prefix"))?;
116 let slice = match end {
117 Some(end) => prefix.get(start..end),
118 None => prefix.get(start..),
119 };
120 slice
121 .map(<[u8]>::to_vec)
122 .ok_or(ProtocolError::TtcDecode("fast-auth prefix too short"))
123}
124
125pub fn build_protocol_negotiation_payload() -> Result<Vec<u8>> {
129 let payload = fast_auth_prefix_slice(
130 FAST_AUTH_PROTOCOL_MSG_START,
131 Some(FAST_AUTH_PROTOCOL_MSG_END),
132 )?;
133 debug_assert_eq!(payload.first(), Some(&TNS_MSG_TYPE_PROTOCOL));
134 Ok(payload)
135}
136
137pub fn build_data_types_payload() -> Result<Vec<u8>> {
141 let payload = fast_auth_prefix_slice(FAST_AUTH_DATA_TYPES_MSG_START, None)?;
142 debug_assert_eq!(payload.first(), Some(&TNS_MSG_TYPE_DATA_TYPES));
143 Ok(payload)
144}
145
146pub fn build_auth_phase_one_payload(
150 user: &str,
151 program: &str,
152 machine: &str,
153 osuser: &str,
154 terminal: &str,
155 pid: u32,
156) -> Result<Vec<u8>> {
157 let mut out = Vec::new();
158 append_auth_phase_one(&mut out, user, program, machine, osuser, terminal, pid)?;
159 Ok(out)
160}
161
162pub fn build_fast_auth_token_payload(
167 user: &str,
168 token: &str,
169 driver_name: &str,
170 version_num: u32,
171 connect_string: &str,
172 edition: Option<&str>,
173) -> Result<Vec<u8>> {
174 let mut out = Vec::from_hex(FAST_AUTH_PREFIX_HEX)
175 .map_err(|_| ProtocolError::TtcDecode("invalid static fast-auth prefix"))?;
176 append_auth_phase_two_token(
177 &mut out,
178 user,
179 token,
180 driver_name,
181 version_num,
182 connect_string,
183 edition,
184 )?;
185 Ok(out)
186}
187
188pub fn build_function_payload(function_code: u8, ttc_field_version: u8) -> Vec<u8> {
189 build_function_payload_with_seq(function_code, 1, ttc_field_version)
190}
191
192pub fn build_function_payload_with_seq(
193 function_code: u8,
194 seq_num: u8,
195 ttc_field_version: u8,
196) -> Vec<u8> {
197 build_function_payload_with_seq_and_token(function_code, seq_num, 0, ttc_field_version)
198}
199
200pub fn build_function_payload_with_seq_and_token(
206 function_code: u8,
207 seq_num: u8,
208 token_num: u64,
209 ttc_field_version: u8,
210) -> Vec<u8> {
211 let mut writer = TtcWriter::new();
212 writer.write_function_code_with_seq(function_code, seq_num);
213 if ttc_field_version >= TNS_CCAP_FIELD_VERSION_23_1_EXT_1 {
214 writer.write_ub8(token_num);
215 } else {
216 debug_assert_eq!(
217 token_num, 0,
218 "pipeline tokens require a 23ai-negotiated connection"
219 );
220 }
221 writer.into_bytes()
222}
223
224pub(crate) fn skip_protocol_message(
225 reader: &mut TtcReader<'_>,
226) -> Result<Option<ClientCapabilities>> {
227 let _server_version = reader.read_u8()?;
228 reader.skip(1)?;
229 loop {
230 if reader.read_u8()? == 0 {
231 break;
232 }
233 }
234 let charset_id = reader.read_u16le()?;
235 let _server_flags = reader.read_u8()?;
236 let num_elem = reader.read_u16le()?;
237 reader.skip(usize::from(num_elem) * 5)?;
238 let fdo_len = reader.read_u16be()?;
239 reader.skip(usize::from(fdo_len))?;
240 let compile_caps = reader.read_bytes()?;
241 let runtime_caps = reader.read_bytes()?;
242 let Some(compile_caps) = compile_caps else {
243 return Ok(None);
244 };
245 let server_ttc_field_version = compile_caps
246 .get(TNS_CCAP_FIELD_VERSION)
247 .copied()
248 .unwrap_or_else(|| ClientCapabilities::default().ttc_field_version);
249 let ttc_field_version =
255 server_ttc_field_version.min(ClientCapabilities::default().ttc_field_version);
256 let max_string_size = if runtime_caps
257 .as_deref()
258 .and_then(|caps| caps.get(TNS_RCAP_TTC))
259 .is_some_and(|flags| flags & TNS_RCAP_TTC_32K != 0)
260 {
261 32_767
262 } else {
263 4_000
264 };
265 Ok(Some(ClientCapabilities {
266 ttc_field_version,
267 max_string_size,
268 charset_id,
269 }))
270}
271
272pub(crate) fn skip_data_types_response(reader: &mut TtcReader<'_>) -> Result<()> {
273 loop {
274 let data_type = reader.read_u16be()?;
275 if data_type == 0 {
276 break;
277 }
278 let conv_data_type = reader.read_u16be()?;
279 if conv_data_type != 0 {
280 reader.skip(4)?;
281 }
282 }
283 Ok(())
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 #[test]
291 fn classic_handshake_messages_slice_the_fast_auth_prefix() {
292 let protocol = build_protocol_negotiation_payload().expect("protocol payload");
293 assert_eq!(protocol[0], TNS_MSG_TYPE_PROTOCOL);
294 assert_eq!(protocol[1], 6, "protocol version byte (8.1 and higher)");
295 assert_eq!(protocol[2], 0, "array terminator");
296 assert!(
297 protocol.ends_with(b"python-oracledb\0"),
298 "driver name string with NUL terminator"
299 );
300
301 let data_types = build_data_types_payload().expect("data types payload");
302 assert_eq!(data_types[0], TNS_MSG_TYPE_DATA_TYPES);
303 assert_eq!(&data_types[1..5], &[0x69, 0x03, 0x69, 0x03]);
305
306 let full = Vec::from_hex(FAST_AUTH_PREFIX_HEX).expect("prefix decodes");
309 assert_eq!(full[0], TNS_MSG_TYPE_FAST_AUTH);
310 assert_eq!(
311 &full[FAST_AUTH_PROTOCOL_MSG_START..FAST_AUTH_PROTOCOL_MSG_END],
312 &protocol[..]
313 );
314 assert_eq!(&full[FAST_AUTH_DATA_TYPES_MSG_START..], &data_types[..]);
315 }
316}