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 if protocol_version < TNS_VERSION_MIN_ACCEPTED {
60 return Err(ProtocolError::UnsupportedVersion {
61 version: protocol_version,
62 minimum: TNS_VERSION_MIN_ACCEPTED,
63 });
64 }
65 let protocol_options = reader.read_u16be()?;
66 reader.skip(10)?;
67 let flags1 = reader.read_u8()?;
68 if has_u8_flag(flags1, TNS_NSI_NA_REQUIRED) {
69 return Err(ProtocolError::UnsupportedFeature(
70 "Native Network Encryption and Data Integrity",
71 ));
72 }
73 reader.skip(9)?;
74 let sdu = reader.read_u32be()?;
75 let mut flags2 = 0;
76 if protocol_version >= 318 {
77 reader.skip(5)?;
78 flags2 = reader.read_u32be()?;
79 }
80
81 Ok(AcceptInfo {
82 protocol_version,
83 protocol_options,
84 sdu,
85 supports_fast_auth: has_u32_flag(flags2, TNS_ACCEPT_FLAG_FAST_AUTH),
86 supports_oob_check: has_u32_flag(flags2, TNS_ACCEPT_FLAG_CHECK_OOB),
87 supports_oob: protocol_options & TNS_GSO_CAN_RECV_ATTENTION != 0,
90 supports_end_of_response: protocol_version >= 319
91 && has_u32_flag(flags2, TNS_ACCEPT_FLAG_HAS_END_OF_RESPONSE),
92 })
93}
94
95pub fn build_fast_auth_phase_one_payload(
96 user: &str,
97 program: &str,
98 machine: &str,
99 osuser: &str,
100 terminal: &str,
101 pid: u32,
102) -> Result<Vec<u8>> {
103 let mut out = Vec::from_hex(FAST_AUTH_PREFIX_HEX)
104 .map_err(|_| ProtocolError::TtcDecode("invalid static fast-auth prefix"))?;
105 append_auth_phase_one(&mut out, user, program, machine, osuser, terminal, pid)?;
106 Ok(out)
107}
108
109const FAST_AUTH_PROTOCOL_MSG_START: usize = 4;
122const FAST_AUTH_PROTOCOL_MSG_END: usize = 23;
123const FAST_AUTH_DATA_TYPES_MSG_START: usize = 29;
124
125fn fast_auth_prefix_slice(start: usize, end: Option<usize>) -> Result<Vec<u8>> {
126 let prefix = Vec::from_hex(FAST_AUTH_PREFIX_HEX)
127 .map_err(|_| ProtocolError::TtcDecode("invalid static fast-auth prefix"))?;
128 let slice = match end {
129 Some(end) => prefix.get(start..end),
130 None => prefix.get(start..),
131 };
132 slice
133 .map(<[u8]>::to_vec)
134 .ok_or(ProtocolError::TtcDecode("fast-auth prefix too short"))
135}
136
137pub fn build_protocol_negotiation_payload() -> Result<Vec<u8>> {
141 let payload = fast_auth_prefix_slice(
142 FAST_AUTH_PROTOCOL_MSG_START,
143 Some(FAST_AUTH_PROTOCOL_MSG_END),
144 )?;
145 debug_assert_eq!(payload.first(), Some(&TNS_MSG_TYPE_PROTOCOL));
146 Ok(payload)
147}
148
149pub fn build_data_types_payload() -> Result<Vec<u8>> {
153 let payload = fast_auth_prefix_slice(FAST_AUTH_DATA_TYPES_MSG_START, None)?;
154 debug_assert_eq!(payload.first(), Some(&TNS_MSG_TYPE_DATA_TYPES));
155 Ok(payload)
156}
157
158pub fn build_auth_phase_one_payload(
162 user: &str,
163 program: &str,
164 machine: &str,
165 osuser: &str,
166 terminal: &str,
167 pid: u32,
168) -> Result<Vec<u8>> {
169 let mut out = Vec::new();
170 append_auth_phase_one(&mut out, user, program, machine, osuser, terminal, pid)?;
171 Ok(out)
172}
173
174pub fn build_fast_auth_token_payload(
179 user: &str,
180 token: &str,
181 driver_name: &str,
182 version_num: u32,
183 connect_string: &str,
184 edition: Option<&str>,
185) -> Result<Vec<u8>> {
186 build_fast_auth_token_payload_with_pop(
187 user,
188 token,
189 driver_name,
190 version_num,
191 connect_string,
192 edition,
193 None,
194 )
195}
196
197pub fn build_fast_auth_token_payload_with_pop(
201 user: &str,
202 token: &str,
203 driver_name: &str,
204 version_num: u32,
205 connect_string: &str,
206 edition: Option<&str>,
207 pop: Option<TokenPop<'_>>,
208) -> Result<Vec<u8>> {
209 let mut out = Vec::from_hex(FAST_AUTH_PREFIX_HEX)
210 .map_err(|_| ProtocolError::TtcDecode("invalid static fast-auth prefix"))?;
211 append_auth_phase_two_token_with_pop(
212 &mut out,
213 user,
214 token,
215 driver_name,
216 version_num,
217 connect_string,
218 edition,
219 pop,
220 )?;
221 Ok(out)
222}
223
224pub fn build_auth_phase_two_token_payload(
231 user: &str,
232 token: &str,
233 driver_name: &str,
234 version_num: u32,
235 connect_string: &str,
236 edition: Option<&str>,
237) -> Result<Vec<u8>> {
238 build_auth_phase_two_token_payload_with_pop(
239 user,
240 token,
241 driver_name,
242 version_num,
243 connect_string,
244 edition,
245 None,
246 )
247}
248
249pub fn build_auth_phase_two_token_payload_with_pop(
253 user: &str,
254 token: &str,
255 driver_name: &str,
256 version_num: u32,
257 connect_string: &str,
258 edition: Option<&str>,
259 pop: Option<TokenPop<'_>>,
260) -> Result<Vec<u8>> {
261 let mut out = Vec::new();
262 append_auth_phase_two_token_with_pop(
263 &mut out,
264 user,
265 token,
266 driver_name,
267 version_num,
268 connect_string,
269 edition,
270 pop,
271 )?;
272 Ok(out)
273}
274
275pub fn build_function_payload(function_code: u8, ttc_field_version: u8) -> Vec<u8> {
276 build_function_payload_with_seq(function_code, 1, ttc_field_version)
277}
278
279pub fn build_function_payload_with_seq(
280 function_code: u8,
281 seq_num: u8,
282 ttc_field_version: u8,
283) -> Vec<u8> {
284 build_function_payload_with_seq_and_token(function_code, seq_num, 0, ttc_field_version)
285}
286
287pub fn build_function_payload_with_seq_and_token(
293 function_code: u8,
294 seq_num: u8,
295 token_num: u64,
296 ttc_field_version: u8,
297) -> Vec<u8> {
298 let mut writer = TtcWriter::new();
299 writer.write_function_code_with_seq(function_code, seq_num);
300 if version_gates::writes_pipeline_token(ttc_field_version) {
301 writer.write_ub8(token_num);
302 } else {
303 debug_assert_eq!(
304 token_num, 0,
305 "pipeline tokens require a 23ai-negotiated connection"
306 );
307 }
308 writer.into_bytes()
309}
310
311pub(crate) fn skip_protocol_message(
312 reader: &mut TtcReader<'_>,
313) -> Result<Option<ClientCapabilities>> {
314 let _server_version = reader.read_u8()?;
315 reader.skip(1)?;
316 loop {
317 if reader.read_u8()? == 0 {
318 break;
319 }
320 }
321 let charset_id = reader.read_u16le()?;
322 let _server_flags = reader.read_u8()?;
323 let num_elem = reader.read_u16le()?;
324 reader.skip(usize::from(num_elem) * 5)?;
325 let fdo_len = reader.read_u16be()?;
326 reader.skip(usize::from(fdo_len))?;
327 let compile_caps = reader.read_bytes()?;
328 let runtime_caps = reader.read_bytes()?;
329 let Some(compile_caps) = compile_caps else {
330 return Ok(None);
331 };
332 let server_ttc_field_version = compile_caps
333 .get(TNS_CCAP_FIELD_VERSION)
334 .copied()
335 .unwrap_or_else(|| ClientCapabilities::default().ttc_field_version);
336 let ttc_field_version =
342 server_ttc_field_version.min(ClientCapabilities::default().ttc_field_version);
343 let max_string_size = if runtime_caps
344 .as_deref()
345 .and_then(|caps| caps.get(TNS_RCAP_TTC))
346 .is_some_and(|flags| flags & TNS_RCAP_TTC_32K != 0)
347 {
348 32_767
349 } else {
350 4_000
351 };
352 Ok(Some(ClientCapabilities {
353 ttc_field_version,
354 max_string_size,
355 charset_id,
356 }))
357}
358
359pub(crate) fn skip_data_types_response(reader: &mut TtcReader<'_>) -> Result<()> {
360 loop {
361 let data_type = reader.read_u16be()?;
362 if data_type == 0 {
363 break;
364 }
365 let conv_data_type = reader.read_u16be()?;
366 if conv_data_type != 0 {
367 reader.skip(4)?;
368 }
369 }
370 Ok(())
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376
377 #[test]
378 fn classic_handshake_messages_slice_the_fast_auth_prefix() {
379 let protocol = build_protocol_negotiation_payload().expect("protocol payload");
380 assert_eq!(protocol[0], TNS_MSG_TYPE_PROTOCOL);
381 assert_eq!(protocol[1], 6, "protocol version byte (8.1 and higher)");
382 assert_eq!(protocol[2], 0, "array terminator");
383 assert!(
384 protocol.ends_with(b"python-oracledb\0"),
385 "driver name string with NUL terminator"
386 );
387
388 let data_types = build_data_types_payload().expect("data types payload");
389 assert_eq!(data_types[0], TNS_MSG_TYPE_DATA_TYPES);
390 assert_eq!(&data_types[1..5], &[0x69, 0x03, 0x69, 0x03]);
392
393 let full = Vec::from_hex(FAST_AUTH_PREFIX_HEX).expect("prefix decodes");
396 assert_eq!(full[0], TNS_MSG_TYPE_FAST_AUTH);
397 assert_eq!(
398 &full[FAST_AUTH_PROTOCOL_MSG_START..FAST_AUTH_PROTOCOL_MSG_END],
399 &protocol[..]
400 );
401 assert_eq!(&full[FAST_AUTH_DATA_TYPES_MSG_START..], &data_types[..]);
402 }
403
404 #[test]
405 fn classic_token_payload_is_the_fast_auth_phase_two_suffix() {
406 let classic = build_auth_phase_two_token_payload(
407 "scott",
408 "token-secret",
409 "rust-oracledb",
410 4_000_000_000,
411 "db.example.com/service",
412 Some("MY_EDITION"),
413 )
414 .expect("classic token payload");
415 let fast = build_fast_auth_token_payload(
416 "scott",
417 "token-secret",
418 "rust-oracledb",
419 4_000_000_000,
420 "db.example.com/service",
421 Some("MY_EDITION"),
422 )
423 .expect("fast token payload");
424 let prefix = Vec::from_hex(FAST_AUTH_PREFIX_HEX).expect("prefix decodes");
425
426 assert_eq!(&fast[..prefix.len()], prefix.as_slice());
427 assert_eq!(&fast[prefix.len()..], classic.as_slice());
428 assert_eq!(classic[0], TNS_MSG_TYPE_FUNCTION);
429 assert_eq!(classic[1], TNS_FUNC_AUTH_PHASE_TWO);
430 assert_eq!(
431 classic[2], 1,
432 "standalone phase two is the first TTC function"
433 );
434 }
435
436 fn accept_bytes(version: u16, options: u16, flags2: u32) -> Vec<u8> {
448 let mut w = TtcWriter::new();
449 w.write_u16be(version); w.write_u16be(options); w.write_raw(&[0u8; 10]); w.write_u8(0); w.write_raw(&[0u8; 9]); w.write_u32be(8192); w.write_raw(&[0u8; 5]); w.write_u32be(flags2); w.into_bytes()
458 }
459
460 #[test]
461 fn accept_parsing_gates_capabilities_on_protocol_version() {
462 assert!(
465 !parse_accept_payload(&accept_bytes(319, 0, 0))
466 .unwrap()
467 .supports_oob,
468 "no CAN_RECV_ATTENTION bit => supports_oob false"
469 );
470 assert!(
471 parse_accept_payload(&accept_bytes(319, TNS_GSO_CAN_RECV_ATTENTION, 0))
472 .unwrap()
473 .supports_oob,
474 "CAN_RECV_ATTENTION bit => supports_oob true"
475 );
476
477 let flags2 = TNS_ACCEPT_FLAG_FAST_AUTH | TNS_ACCEPT_FLAG_CHECK_OOB;
481 assert!(
482 !parse_accept_payload(&accept_bytes(317, 0, flags2))
483 .unwrap()
484 .supports_fast_auth,
485 "below 318 flags2 is not read"
486 );
487 let at_318 = parse_accept_payload(&accept_bytes(318, 0, flags2)).unwrap();
488 assert!(at_318.supports_fast_auth, "at 318 flags2 is read");
489 assert!(
490 at_318.supports_oob_check,
491 "at 318 the CHECK_OOB flag is read"
492 );
493
494 assert!(
498 !parse_accept_payload(&accept_bytes(318, 0, TNS_ACCEPT_FLAG_HAS_END_OF_RESPONSE))
499 .unwrap()
500 .supports_end_of_response,
501 "318 < 319: no end-of-response even with the flag"
502 );
503 assert!(
504 parse_accept_payload(&accept_bytes(319, 0, TNS_ACCEPT_FLAG_HAS_END_OF_RESPONSE))
505 .unwrap()
506 .supports_end_of_response,
507 "319 + flag: end-of-response negotiated"
508 );
509 assert!(
510 !parse_accept_payload(&accept_bytes(319, 0, 0))
511 .unwrap()
512 .supports_end_of_response,
513 "319 without the flag: no end-of-response"
514 );
515 }
516}