Skip to main content

oracledb_protocol/thin/
connect.rs

1#![forbid(unsafe_code)]
2
3use super::*;
4
5pub fn build_connect_packet_payload(connect_data: &str, sdu: u16) -> Result<Vec<u8>> {
6    let connect_bytes = connect_data.as_bytes();
7    let connect_len =
8        u16::try_from(connect_bytes.len()).map_err(|_| ProtocolError::PacketTooLarge {
9            length: connect_bytes.len(),
10        })?;
11
12    let mut writer = TtcWriter::new();
13    writer.write_u16be(TNS_VERSION_DESIRED);
14    writer.write_u16be(TNS_VERSION_MIN);
15    writer.write_u16be(TNS_GSO_DONT_CARE);
16    writer.write_u16be(sdu);
17    writer.write_u16be(sdu);
18    writer.write_u16be(TNS_PROTOCOL_CHARACTERISTICS);
19    writer.write_u16be(0);
20    writer.write_u16be(1);
21    writer.write_u16be(connect_len);
22    writer.write_u16be(74);
23    writer.write_u32be(0);
24    let nsi_flags = TNS_NSI_SUPPORT_SECURITY_RENEG | TNS_NSI_DISABLE_NA;
25    writer.write_u8(nsi_flags);
26    writer.write_u8(nsi_flags);
27    writer.write_u64be(0);
28    writer.write_u64be(0);
29    writer.write_u64be(0);
30    writer.write_u32be(u32::from(sdu));
31    writer.write_u32be(u32::from(sdu));
32    writer.write_u32be(0);
33    writer.write_u32be(0);
34    writer.write_raw(connect_bytes);
35    Ok(writer.into_bytes())
36}
37
38pub fn parse_accept_payload(payload: &[u8]) -> Result<AcceptInfo> {
39    let mut reader = TtcReader::new(payload);
40    let protocol_version = reader.read_u16be()?;
41    let protocol_options = reader.read_u16be()?;
42    reader.skip(10)?;
43    let flags1 = reader.read_u8()?;
44    if has_u8_flag(flags1, TNS_NSI_NA_REQUIRED) {
45        return Err(ProtocolError::UnsupportedFeature(
46            "Native Network Encryption and Data Integrity",
47        ));
48    }
49    reader.skip(9)?;
50    let sdu = reader.read_u32be()?;
51    let mut flags2 = 0;
52    if protocol_version >= 318 {
53        reader.skip(5)?;
54        flags2 = reader.read_u32be()?;
55    }
56
57    Ok(AcceptInfo {
58        protocol_version,
59        protocol_options,
60        sdu,
61        supports_fast_auth: has_u32_flag(flags2, TNS_ACCEPT_FLAG_FAST_AUTH),
62        supports_oob_check: has_u32_flag(flags2, TNS_ACCEPT_FLAG_CHECK_OOB),
63        // Reference: Capabilities.supports_oob = protocol_options &
64        // TNS_GSO_CAN_RECV_ATTENTION (capabilities.pyx:121).
65        supports_oob: protocol_options & TNS_GSO_CAN_RECV_ATTENTION != 0,
66        supports_end_of_response: protocol_version >= 319
67            && has_u32_flag(flags2, TNS_ACCEPT_FLAG_HAS_END_OF_RESPONSE),
68    })
69}
70
71pub fn build_fast_auth_phase_one_payload(
72    user: &str,
73    program: &str,
74    machine: &str,
75    osuser: &str,
76    terminal: &str,
77    pid: u32,
78) -> Result<Vec<u8>> {
79    let mut out = Vec::from_hex(FAST_AUTH_PREFIX_HEX)
80        .map_err(|_| ProtocolError::TtcDecode("invalid static fast-auth prefix"))?;
81    append_auth_phase_one(&mut out, user, program, machine, osuser, terminal, pid)?;
82    Ok(out)
83}
84
85/// Fast-auth bundle for **token authentication**: the same static
86/// protocol/data-types prefix as [`build_fast_auth_phase_one_payload`], but with
87/// a phase-two `AUTH_TOKEN` message appended (no verifier round-trip). The caller
88/// sends this once and reads a single auth response.
89pub fn build_fast_auth_token_payload(
90    user: &str,
91    token: &str,
92    driver_name: &str,
93    version_num: u32,
94    connect_string: &str,
95    edition: Option<&str>,
96) -> Result<Vec<u8>> {
97    let mut out = Vec::from_hex(FAST_AUTH_PREFIX_HEX)
98        .map_err(|_| ProtocolError::TtcDecode("invalid static fast-auth prefix"))?;
99    append_auth_phase_two_token(
100        &mut out,
101        user,
102        token,
103        driver_name,
104        version_num,
105        connect_string,
106        edition,
107    )?;
108    Ok(out)
109}
110
111pub fn build_function_payload(function_code: u8) -> Vec<u8> {
112    build_function_payload_with_seq(function_code, 1)
113}
114
115pub fn build_function_payload_with_seq(function_code: u8, seq_num: u8) -> Vec<u8> {
116    build_function_payload_with_seq_and_token(function_code, seq_num, 0)
117}
118
119/// Bare function message with an explicit pipeline token (messages/base.pyx
120/// `_write_function_code` writes `ub8 token_num` for field version >= 23.1
121/// ext 1; non-pipelined messages carry 0).
122pub fn build_function_payload_with_seq_and_token(
123    function_code: u8,
124    seq_num: u8,
125    token_num: u64,
126) -> Vec<u8> {
127    let mut writer = TtcWriter::new();
128    writer.write_function_code_with_seq(function_code, seq_num);
129    writer.write_ub8(token_num);
130    writer.into_bytes()
131}
132
133pub(crate) fn skip_protocol_message(
134    reader: &mut TtcReader<'_>,
135) -> Result<Option<ClientCapabilities>> {
136    let _server_version = reader.read_u8()?;
137    reader.skip(1)?;
138    loop {
139        if reader.read_u8()? == 0 {
140            break;
141        }
142    }
143    let charset_id = reader.read_u16le()?;
144    let _server_flags = reader.read_u8()?;
145    let num_elem = reader.read_u16le()?;
146    reader.skip(usize::from(num_elem) * 5)?;
147    let fdo_len = reader.read_u16be()?;
148    reader.skip(usize::from(fdo_len))?;
149    let compile_caps = reader.read_bytes()?;
150    let runtime_caps = reader.read_bytes()?;
151    let Some(compile_caps) = compile_caps else {
152        return Ok(None);
153    };
154    let server_ttc_field_version = compile_caps
155        .get(TNS_CCAP_FIELD_VERSION)
156        .copied()
157        .unwrap_or_else(|| ClientCapabilities::default().ttc_field_version);
158    let ttc_field_version =
159        server_ttc_field_version.max(ClientCapabilities::default().ttc_field_version);
160    let max_string_size = if runtime_caps
161        .as_deref()
162        .and_then(|caps| caps.get(TNS_RCAP_TTC))
163        .is_some_and(|flags| flags & TNS_RCAP_TTC_32K != 0)
164    {
165        32_767
166    } else {
167        4_000
168    };
169    Ok(Some(ClientCapabilities {
170        ttc_field_version,
171        max_string_size,
172        charset_id,
173    }))
174}
175
176pub(crate) fn skip_data_types_response(reader: &mut TtcReader<'_>) -> Result<()> {
177    loop {
178        let data_type = reader.read_u16be()?;
179        if data_type == 0 {
180            break;
181        }
182        let conv_data_type = reader.read_u16be()?;
183        if conv_data_type != 0 {
184            reader.skip(4)?;
185        }
186    }
187    Ok(())
188}