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 let mut out = Vec::from_hex(FAST_AUTH_PREFIX_HEX)
187 .map_err(|_| ProtocolError::TtcDecode("invalid static fast-auth prefix"))?;
188 append_auth_phase_two_token(
189 &mut out,
190 user,
191 token,
192 driver_name,
193 version_num,
194 connect_string,
195 edition,
196 )?;
197 Ok(out)
198}
199
200pub fn build_function_payload(function_code: u8, ttc_field_version: u8) -> Vec<u8> {
201 build_function_payload_with_seq(function_code, 1, ttc_field_version)
202}
203
204pub fn build_function_payload_with_seq(
205 function_code: u8,
206 seq_num: u8,
207 ttc_field_version: u8,
208) -> Vec<u8> {
209 build_function_payload_with_seq_and_token(function_code, seq_num, 0, ttc_field_version)
210}
211
212pub fn build_function_payload_with_seq_and_token(
218 function_code: u8,
219 seq_num: u8,
220 token_num: u64,
221 ttc_field_version: u8,
222) -> Vec<u8> {
223 let mut writer = TtcWriter::new();
224 writer.write_function_code_with_seq(function_code, seq_num);
225 if version_gates::writes_pipeline_token(ttc_field_version) {
226 writer.write_ub8(token_num);
227 } else {
228 debug_assert_eq!(
229 token_num, 0,
230 "pipeline tokens require a 23ai-negotiated connection"
231 );
232 }
233 writer.into_bytes()
234}
235
236pub(crate) fn skip_protocol_message(
237 reader: &mut TtcReader<'_>,
238) -> Result<Option<ClientCapabilities>> {
239 let _server_version = reader.read_u8()?;
240 reader.skip(1)?;
241 loop {
242 if reader.read_u8()? == 0 {
243 break;
244 }
245 }
246 let charset_id = reader.read_u16le()?;
247 let _server_flags = reader.read_u8()?;
248 let num_elem = reader.read_u16le()?;
249 reader.skip(usize::from(num_elem) * 5)?;
250 let fdo_len = reader.read_u16be()?;
251 reader.skip(usize::from(fdo_len))?;
252 let compile_caps = reader.read_bytes()?;
253 let runtime_caps = reader.read_bytes()?;
254 let Some(compile_caps) = compile_caps else {
255 return Ok(None);
256 };
257 let server_ttc_field_version = compile_caps
258 .get(TNS_CCAP_FIELD_VERSION)
259 .copied()
260 .unwrap_or_else(|| ClientCapabilities::default().ttc_field_version);
261 let ttc_field_version =
267 server_ttc_field_version.min(ClientCapabilities::default().ttc_field_version);
268 let max_string_size = if runtime_caps
269 .as_deref()
270 .and_then(|caps| caps.get(TNS_RCAP_TTC))
271 .is_some_and(|flags| flags & TNS_RCAP_TTC_32K != 0)
272 {
273 32_767
274 } else {
275 4_000
276 };
277 Ok(Some(ClientCapabilities {
278 ttc_field_version,
279 max_string_size,
280 charset_id,
281 }))
282}
283
284pub(crate) fn skip_data_types_response(reader: &mut TtcReader<'_>) -> Result<()> {
285 loop {
286 let data_type = reader.read_u16be()?;
287 if data_type == 0 {
288 break;
289 }
290 let conv_data_type = reader.read_u16be()?;
291 if conv_data_type != 0 {
292 reader.skip(4)?;
293 }
294 }
295 Ok(())
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301
302 #[test]
303 fn classic_handshake_messages_slice_the_fast_auth_prefix() {
304 let protocol = build_protocol_negotiation_payload().expect("protocol payload");
305 assert_eq!(protocol[0], TNS_MSG_TYPE_PROTOCOL);
306 assert_eq!(protocol[1], 6, "protocol version byte (8.1 and higher)");
307 assert_eq!(protocol[2], 0, "array terminator");
308 assert!(
309 protocol.ends_with(b"python-oracledb\0"),
310 "driver name string with NUL terminator"
311 );
312
313 let data_types = build_data_types_payload().expect("data types payload");
314 assert_eq!(data_types[0], TNS_MSG_TYPE_DATA_TYPES);
315 assert_eq!(&data_types[1..5], &[0x69, 0x03, 0x69, 0x03]);
317
318 let full = Vec::from_hex(FAST_AUTH_PREFIX_HEX).expect("prefix decodes");
321 assert_eq!(full[0], TNS_MSG_TYPE_FAST_AUTH);
322 assert_eq!(
323 &full[FAST_AUTH_PROTOCOL_MSG_START..FAST_AUTH_PROTOCOL_MSG_END],
324 &protocol[..]
325 );
326 assert_eq!(&full[FAST_AUTH_DATA_TYPES_MSG_START..], &data_types[..]);
327 }
328
329 fn accept_bytes(version: u16, options: u16, flags2: u32) -> Vec<u8> {
341 let mut w = TtcWriter::new();
342 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()
351 }
352
353 #[test]
354 fn accept_parsing_gates_capabilities_on_protocol_version() {
355 assert!(
358 !parse_accept_payload(&accept_bytes(319, 0, 0))
359 .unwrap()
360 .supports_oob,
361 "no CAN_RECV_ATTENTION bit => supports_oob false"
362 );
363 assert!(
364 parse_accept_payload(&accept_bytes(319, TNS_GSO_CAN_RECV_ATTENTION, 0))
365 .unwrap()
366 .supports_oob,
367 "CAN_RECV_ATTENTION bit => supports_oob true"
368 );
369
370 let flags2 = TNS_ACCEPT_FLAG_FAST_AUTH | TNS_ACCEPT_FLAG_CHECK_OOB;
374 assert!(
375 !parse_accept_payload(&accept_bytes(317, 0, flags2))
376 .unwrap()
377 .supports_fast_auth,
378 "below 318 flags2 is not read"
379 );
380 let at_318 = parse_accept_payload(&accept_bytes(318, 0, flags2)).unwrap();
381 assert!(at_318.supports_fast_auth, "at 318 flags2 is read");
382 assert!(
383 at_318.supports_oob_check,
384 "at 318 the CHECK_OOB flag is read"
385 );
386
387 assert!(
391 !parse_accept_payload(&accept_bytes(318, 0, TNS_ACCEPT_FLAG_HAS_END_OF_RESPONSE))
392 .unwrap()
393 .supports_end_of_response,
394 "318 < 319: no end-of-response even with the flag"
395 );
396 assert!(
397 parse_accept_payload(&accept_bytes(319, 0, TNS_ACCEPT_FLAG_HAS_END_OF_RESPONSE))
398 .unwrap()
399 .supports_end_of_response,
400 "319 + flag: end-of-response negotiated"
401 );
402 assert!(
403 !parse_accept_payload(&accept_bytes(319, 0, 0))
404 .unwrap()
405 .supports_end_of_response,
406 "319 without the flag: no end-of-response"
407 );
408 }
409}