Skip to main content

rsfbclient_rust/
wire.rs

1//! Structs and functions to write and parse the firebird wire protocol messages
2
3#![allow(non_upper_case_globals)]
4
5use bytes::{BufMut, Bytes, BytesMut};
6use std::{borrow::Cow, convert::TryFrom, str};
7
8use crate::{
9    client::{BlobId, FirebirdWireConnection},
10    consts::{gds_to_msg, AuthPluginType, Cnct, ProtocolVersion, WireOp, P_REQ_ASYNC},
11    srp::*,
12    util::*,
13    xsqlda::{XSqlVar, XSQLDA_DESCRIBE_VARS},
14};
15use rsfbclient_core::{ibase, Charset, Column, Dialect, FbError, FreeStmtOp, SqlType, TrOp};
16
17/// Buffer length to use in the connection
18pub const BUFFER_LENGTH: u32 = 1024;
19
20/// Connection request
21pub fn connect(db_name: &str, user: &str, username: &str, hostname: &str, srp_key: &[u8]) -> Bytes {
22    let protocols = [
23        // PROTOCOL_VERSION, Arch type (Generic=1), min, max, weight
24        [ProtocolVersion::V10 as u32, 1, 0, 5, 2],
25        [ProtocolVersion::V11 as u32, 1, 0, 5, 4],
26        [ProtocolVersion::V12 as u32, 1, 0, 5, 6],
27        [ProtocolVersion::V13 as u32, 1, 0, 5, 8],
28    ];
29
30    let mut connect = BytesMut::with_capacity(256);
31
32    connect.put_u32(WireOp::Connect as u32);
33    connect.put_u32(WireOp::Attach as u32);
34    connect.put_u32(3); // CONNECT_VERSION
35    connect.put_u32(1); // arch_generic
36
37    // Db file path / name
38    connect.put_wire_bytes(db_name.as_bytes());
39
40    // Protocol versions understood
41    connect.put_u32(protocols.len() as u32);
42
43    // Request SRP by default, so use Sha1
44    let srp = SrpClient::<sha1::Sha1>::new(srp_key, &SRP_GROUP);
45
46    let uid = {
47        let mut uid = BytesMut::new();
48
49        let pubkey = hex::encode(srp.get_a_pub());
50
51        // Database username
52        uid.put_u8(Cnct::Login as u8);
53        uid.put_u8(user.len() as u8);
54        uid.put(user.as_bytes());
55
56        // Request SRP by default
57        let plugin = AuthPluginType::Srp.name();
58
59        uid.put_u8(Cnct::PluginName as u8);
60        uid.put_u8(plugin.len() as u8);
61        uid.put(plugin.as_bytes());
62
63        let plugin_list = AuthPluginType::plugin_list();
64
65        uid.put_u8(Cnct::PluginList as u8);
66        uid.put_u8(plugin_list.len() as u8);
67        uid.put(plugin_list.as_bytes());
68
69        for (i, pk_chunk) in pubkey.as_bytes().chunks(254).enumerate() {
70            uid.put_u8(Cnct::SpecificData as u8);
71            uid.put_u8(pk_chunk.len() as u8 + 1);
72            uid.put_u8(i as u8);
73            uid.put(pk_chunk);
74        }
75
76        let wire_crypt = "\x01\x00\x00\x00";
77
78        uid.put_u8(Cnct::ClientCrypt as u8);
79        uid.put_u8(wire_crypt.len() as u8);
80        uid.put(wire_crypt.as_bytes());
81
82        // System username
83        uid.put_u8(Cnct::User as u8);
84        uid.put_u8(username.len() as u8);
85        uid.put(username.as_bytes());
86
87        uid.put_u8(Cnct::Host as u8);
88        uid.put_u8(hostname.len() as u8);
89        uid.put(hostname.as_bytes());
90
91        uid.put_u8(Cnct::UserVerification as u8);
92        uid.put_u8(0);
93
94        uid.freeze()
95    };
96    connect.put_wire_bytes(&uid);
97
98    // Protocols
99    for i in protocols.iter().flatten() {
100        connect.put_u32(*i);
101    }
102
103    connect.freeze()
104}
105
106/// Continue authentication request
107pub fn cont_auth(data: &[u8], plugin: AuthPluginType, plugin_list: String, keys: &[u8]) -> Bytes {
108    let mut req = BytesMut::with_capacity(
109        20 + data.len() + plugin.name().len() + plugin_list.len() + keys.len(),
110    );
111
112    req.put_u32(WireOp::ContAuth as u32);
113    req.put_wire_bytes(data);
114    req.put_wire_bytes(plugin.name().as_bytes());
115    req.put_wire_bytes(plugin_list.as_bytes());
116    req.put_wire_bytes(keys);
117
118    req.freeze()
119}
120
121/// Wire encryption request
122pub fn crypt(algo: &str, kind: &str) -> Bytes {
123    let mut req = BytesMut::with_capacity(12 + algo.len() + kind.len());
124
125    req.put_u32(WireOp::Crypt as u32);
126    // Encryption algorithm
127    req.put_wire_bytes(algo.as_bytes());
128    // Encryption type
129    req.put_wire_bytes(kind.as_bytes());
130
131    req.freeze()
132}
133
134/// Attach request
135pub fn attach(
136    db_name: &str,
137    user: &str,
138    pass: &str,
139    protocol: ProtocolVersion,
140    charset: Charset,
141    role_name: Option<&str>,
142    dialect: Dialect,
143    no_db_triggers: bool,
144    auth_plugin: Option<&AuthPlugin>,
145    srp_key: &[u8; 32],
146) -> Result<Bytes, FbError> {
147    let dpb = build_dpb(
148        user,
149        pass,
150        protocol,
151        charset,
152        None,
153        role_name,
154        dialect,
155        no_db_triggers,
156        auth_plugin,
157        srp_key,
158    )?;
159
160    let mut attach = BytesMut::with_capacity(16 + db_name.len() + dpb.len());
161
162    attach.put_u32(WireOp::Attach as u32);
163    attach.put_u32(0); // Database Object ID
164
165    attach.put_wire_bytes(db_name.as_bytes());
166
167    attach.put_wire_bytes(&dpb);
168
169    Ok(attach.freeze())
170}
171
172/// Create db request
173pub fn create(
174    db_name: &str,
175    user: &str,
176    pass: &str,
177    protocol: ProtocolVersion,
178    charset: Charset,
179    page_size: Option<u32>,
180    role_name: Option<&str>,
181    dialect: Dialect,
182    auth_plugin: Option<&AuthPlugin>,
183    srp_key: &[u8; 32],
184) -> Result<Bytes, FbError> {
185    let dpb = build_dpb(
186        user,
187        pass,
188        protocol,
189        charset,
190        page_size,
191        role_name,
192        dialect,
193        false,
194        auth_plugin,
195        srp_key,
196    )?;
197
198    let mut create = BytesMut::with_capacity(16 + db_name.len() + dpb.len());
199
200    create.put_u32(WireOp::Create as u32);
201    create.put_u32(0); // Database Object ID
202
203    create.put_wire_bytes(db_name.as_bytes());
204
205    create.put_wire_bytes(&dpb);
206
207    Ok(create.freeze())
208}
209
210/// Dpb builder
211fn build_dpb(
212    user: &str,
213    pass: &str,
214    protocol: ProtocolVersion,
215    charset: Charset,
216    page_size: Option<u32>,
217    role_name: Option<&str>,
218    dialect: Dialect,
219    no_db_triggers: bool,
220    auth_plugin: Option<&AuthPlugin>,
221    srp_key: &[u8; 32],
222) -> Result<Bytes, FbError> {
223    let mut params = Vec::new();
224
225    if let Some(ps) = page_size {
226        params.push(DpbData {
227            tag: ibase::isc_dpb_page_size as u8,
228            data: ps.to_be_bytes().to_vec().into(),
229        });
230    }
231
232    let charset = charset.on_firebird.as_bytes();
233
234    params.push(DpbData {
235        tag: ibase::isc_dpb_lc_ctype as u8,
236        data: charset.into(),
237    });
238
239    params.push(DpbData {
240        tag: ibase::isc_dpb_user_name as u8,
241        data: user.as_bytes().into(),
242    });
243
244    if let Some(role) = role_name {
245        params.push(DpbData {
246            tag: ibase::isc_dpb_sql_role_name as u8,
247            data: role.as_bytes().into(),
248        });
249    }
250
251    params.push(DpbData {
252        tag: ibase::isc_dpb_sql_dialect as u8,
253        data: vec![dialect as u8].into(),
254    });
255
256    if no_db_triggers {
257        params.push(DpbData {
258            tag: ibase::isc_dpb_no_db_triggers as u8,
259            data: vec![1].into(),
260        });
261    }
262
263    match protocol {
264        // Plaintext password
265        ProtocolVersion::V10 => {
266            params.push(DpbData {
267                tag: ibase::isc_dpb_password as u8,
268                data: pass.as_bytes().to_vec().into(),
269            });
270        }
271
272        // Hashed password
273        ProtocolVersion::V11 | ProtocolVersion::V12 => {
274            #[allow(deprecated)]
275            let enc_pass = pwhash::unix_crypt::hash_with("9z", pass).unwrap();
276            let enc_pass = &enc_pass[2..];
277
278            params.push(DpbData {
279                tag: ibase::isc_dpb_password_enc as u8,
280                data: enc_pass.as_bytes().to_vec().into(),
281            });
282        }
283
284        ProtocolVersion::V13
285            if let Some(auth) = auth_plugin
286                && let Some(auth_data) = &auth.data =>
287        {
288            // Password not verified on cont_auth (WireCrypt = Disabled)
289            params.push(DpbData {
290                tag: ibase::isc_dpb_auth_plugin_name as u8,
291                data: auth.kind.name().as_bytes().into(),
292            });
293
294            params.push(DpbData {
295                tag: ibase::isc_dpb_auth_plugin_list as u8,
296                data: AuthPluginType::plugin_list().into_bytes().into(),
297            });
298
299            let proof = {
300                match auth.kind {
301                    AuthPluginType::Srp => {
302                        let srp = SrpClient::<sha1::Sha1>::new(srp_key, &SRP_GROUP);
303                        let verifier = crate::client::srp_verifier(srp, user, pass, auth_data)?;
304
305                        hex::encode(verifier.get_proof())
306                    }
307                    AuthPluginType::Srp256 => {
308                        let srp = SrpClient::<sha2::Sha256>::new(srp_key, &SRP_GROUP);
309                        let verifier = crate::client::srp_verifier(srp, user, pass, auth_data)?;
310
311                        hex::encode(verifier.get_proof())
312                    }
313                }
314            };
315
316            params.push(DpbData {
317                tag: ibase::isc_dpb_specific_auth_data as u8,
318                data: proof.into_bytes().into(),
319            });
320        }
321
322        // Password already verified
323        ProtocolVersion::V13 => {}
324    }
325
326    Ok(dpb(&params))
327}
328
329struct DpbData<'a> {
330    tag: u8,
331    data: Cow<'a, [u8]>,
332}
333
334fn dpb(data: &[DpbData<'_>]) -> Bytes {
335    let max_len = data.iter().map(|d| d.data.len()).max().unwrap_or_default();
336
337    let mut dpb = BytesMut::with_capacity(64);
338
339    let v2 = max_len > 255;
340
341    dpb.put_u8(if v2 {
342        ibase::isc_dpb_version2
343    } else {
344        ibase::isc_dpb_version1
345    } as u8); //Version
346
347    for d in data {
348        dpb.put_u8(d.tag);
349        if v2 {
350            dpb.put_u32_le(d.data.len() as u32);
351        } else {
352            dpb.put_u8(d.data.len() as u8);
353        }
354        dpb.put_slice(&d.data);
355    }
356
357    dpb.freeze()
358}
359
360/// Detach from the database request
361pub fn detach(db_handle: u32) -> Bytes {
362    let mut tr = BytesMut::with_capacity(8);
363
364    tr.put_u32(WireOp::Detach as u32);
365    tr.put_u32(db_handle);
366
367    tr.freeze()
368}
369
370/// Drop database request
371pub fn drop_database(db_handle: u32) -> Bytes {
372    let mut tr = BytesMut::with_capacity(8);
373
374    tr.put_u32(WireOp::DropDatabase as u32);
375    tr.put_u32(db_handle);
376
377    tr.freeze()
378}
379
380/// Begin transaction request
381pub fn transaction(db_handle: u32, tpb: &[u8]) -> Bytes {
382    let mut tr = BytesMut::with_capacity(12 + tpb.len());
383
384    tr.put_u32(WireOp::Transaction as u32);
385    tr.put_u32(db_handle);
386    tr.put_wire_bytes(tpb);
387
388    tr.freeze()
389}
390
391/// Commit / Rollback transaction request
392pub fn transaction_operation(tr_handle: u32, op: TrOp) -> Bytes {
393    let mut tr = BytesMut::with_capacity(8);
394
395    let op = match op {
396        TrOp::Commit => WireOp::Commit,
397        TrOp::CommitRetaining => WireOp::CommitRetaining,
398        TrOp::Rollback => WireOp::Rollback,
399        TrOp::RollbackRetaining => WireOp::RollbackRetaining,
400    };
401
402    tr.put_u32(op as u32);
403    tr.put_u32(tr_handle);
404
405    tr.freeze()
406}
407
408/// Execute immediate request
409pub fn exec_immediate(
410    tr_handle: u32,
411    dialect: u32,
412    sql: &str,
413    charset: &Charset,
414) -> Result<Bytes, FbError> {
415    let bytes = charset.encode(sql)?;
416    let mut req = BytesMut::with_capacity(28 + bytes.len());
417
418    req.put_u32(WireOp::ExecImmediate as u32);
419    req.put_u32(tr_handle);
420    req.put_u32(0); // Statement handle, apparently unused
421    req.put_u32(dialect);
422    req.put_wire_bytes(&bytes);
423    req.put_u32(0); // TODO: parameters
424    req.put_u32(BUFFER_LENGTH);
425
426    Ok(req.freeze())
427}
428
429/// Statement allocation request (lazy response)
430pub fn allocate_statement(db_handle: u32) -> Bytes {
431    let mut req = BytesMut::with_capacity(8);
432
433    req.put_u32(WireOp::AllocateStatement as u32);
434    req.put_u32(db_handle);
435
436    req.freeze()
437}
438
439/// Prepare statement request. Use u32::MAX as `stmt_handle` if the statement was allocated
440/// in the previous request
441pub fn prepare_statement(
442    tr_handle: u32,
443    stmt_handle: u32,
444    dialect: u32,
445    query: &str,
446    charset: &Charset,
447) -> Result<Bytes, FbError> {
448    let bytes = charset.encode(query)?;
449    let mut req = BytesMut::with_capacity(28 + bytes.len() + XSQLDA_DESCRIBE_VARS.len());
450
451    req.put_u32(WireOp::PrepareStatement as u32);
452    req.put_u32(tr_handle);
453    req.put_u32(stmt_handle);
454    req.put_u32(dialect);
455    req.put_wire_bytes(&bytes);
456    req.put_wire_bytes(&XSQLDA_DESCRIBE_VARS); // Data to be returned
457
458    req.put_u32(BUFFER_LENGTH);
459
460    Ok(req.freeze())
461}
462
463/// Statement information request
464pub fn info_sql(stmt_handle: u32, requested_items: &[u8]) -> Bytes {
465    let mut req = BytesMut::with_capacity(24 + requested_items.len());
466
467    req.put_u32(WireOp::InfoSql as u32);
468    req.put_u32(stmt_handle);
469    req.put_u32(0); // Incarnation of object
470    req.put_wire_bytes(requested_items);
471    req.put_u32(BUFFER_LENGTH);
472
473    req.freeze()
474}
475
476/// Close or drop statement request
477pub fn free_statement(stmt_handle: u32, op: FreeStmtOp) -> Bytes {
478    let mut req = BytesMut::with_capacity(12);
479
480    req.put_u32(WireOp::FreeStatement as u32);
481    req.put_u32(stmt_handle);
482    req.put_u32(op as u32);
483
484    req.freeze()
485}
486
487/// Execute prepared statement request.
488pub fn execute(tr_handle: u32, stmt_handle: u32, input_blr: &[u8], input_data: &[u8]) -> Bytes {
489    let mut req = BytesMut::with_capacity(36 + input_blr.len() + input_data.len());
490
491    req.put_u32(WireOp::Execute as u32);
492    req.put_u32(stmt_handle);
493    req.put_u32(tr_handle);
494
495    req.put_wire_bytes(input_blr);
496    req.put_u32(0);
497    req.put_u32(if input_blr.is_empty() { 0 } else { 1 });
498
499    req.put_slice(input_data);
500
501    req.freeze()
502}
503
504/// Execute prepared statement request.
505pub fn execute2(
506    tr_handle: u32,
507    stmt_handle: u32,
508    input_blr: &[u8],
509    input_data: &[u8],
510    output_blr: &[u8],
511) -> Bytes {
512    let mut req =
513        BytesMut::with_capacity(40 + input_blr.len() + input_data.len() + output_blr.len());
514
515    req.put_u32(WireOp::Execute2 as u32);
516    req.put_u32(stmt_handle);
517    req.put_u32(tr_handle);
518
519    req.put_wire_bytes(input_blr);
520    req.put_u32(0); // Input message number
521    req.put_u32(if input_blr.is_empty() { 0 } else { 1 }); // Messages
522
523    req.put_slice(input_data);
524
525    req.put_wire_bytes(output_blr);
526    req.put_u32(0); // Output message number
527
528    req.freeze()
529}
530
531/// Fetch row request
532pub fn fetch(stmt_handle: u32, blr: &[u8], count: u32) -> Bytes {
533    let mut req = BytesMut::with_capacity(20 + blr.len());
534
535    req.put_u32(WireOp::Fetch as u32);
536    req.put_u32(stmt_handle);
537    req.put_wire_bytes(blr);
538    req.put_u32(0); // Message number
539    req.put_u32(count); // Message count: request a batch of rows in a single round-trip
540
541    req.freeze()
542}
543
544/// Auxiliary channel request.
545///
546/// Firebird does not push the event notifications on the connection that
547/// registered them: it asks the client to open a second, "async" connection
548/// and answers this request with the address it listens on for it.
549pub fn connect_request(db_handle: u32) -> Bytes {
550    let mut req = BytesMut::with_capacity(16);
551
552    req.put_u32(WireOp::ConnectRequest as u32);
553    req.put_u32(P_REQ_ASYNC); // Connection type
554    req.put_u32(db_handle); // Related object
555    req.put_u32(0); // Partner identification
556
557    req.freeze()
558}
559
560/// Event notification request
561pub fn que_events(db_handle: u32, epb: &[u8], event_id: u32) -> Bytes {
562    let mut req = BytesMut::with_capacity(24 + epb.len());
563
564    req.put_u32(WireOp::QueEvents as u32);
565    req.put_u32(db_handle);
566    req.put_wire_bytes(epb); // Event parameter block
567    req.put_u32(0); // Ast routine address, unused by the remote protocol
568    req.put_u32(0); // Ast routine argument, unused by the remote protocol
569    req.put_u32(event_id);
570
571    req.freeze()
572}
573
574/// Cancel an event notification request
575pub fn cancel_events(db_handle: u32, event_id: u32) -> Bytes {
576    let mut req = BytesMut::with_capacity(12);
577
578    req.put_u32(WireOp::CancelEvents as u32);
579    req.put_u32(db_handle);
580    req.put_u32(event_id);
581
582    req.freeze()
583}
584
585#[derive(Debug)]
586/// `WireOp::Event` notification, pushed by the server on the auxiliary channel
587pub struct EventNotification {
588    /// Id of the `WireOp::QueEvents` registration this notification answers
589    pub event_id: u32,
590    /// Event parameter block holding the updated occurrence counters
591    pub epb: Bytes,
592}
593
594/// Parse an event notification (`WireOp::Event`), op code included
595pub fn parse_event_notification(resp: &mut Bytes) -> Result<EventNotification, FbError> {
596    let op_code = resp.get_u32()?;
597
598    if op_code != WireOp::Event as u32 {
599        return err_conn_rejected(op_code);
600    }
601
602    resp.get_u32()?; // Database handle
603    let epb = resp.get_wire_bytes()?;
604    resp.get_u32()?; // Ast routine address, always zero
605    resp.get_u32()?; // Ast routine argument, always zero
606    let event_id = resp.get_u32()?;
607
608    Ok(EventNotification { event_id, epb })
609}
610
611/// Extract the port of the auxiliary channel from the `WireOp::ConnectRequest`
612/// response data, which holds a raw `sockaddr` of the server.
613///
614/// Only the port is used: the address the server reports is the one it sees
615/// locally, which is wrong as soon as it sits behind a NAT, so the reference
616/// client reuses the address of the main connection instead. This is what
617/// `aux_connect` does in the firebird sources. The port lives at offset 2 in
618/// network byte order for both `sockaddr_in` and `sockaddr_in6`, whatever the
619/// sockaddr layout of the server.
620pub fn parse_aux_port(data: &[u8]) -> Result<u16, FbError> {
621    if data.len() < 4 {
622        return Err(FbError::from(
623            "Invalid auxiliary channel address returned by the server",
624        ));
625    }
626
627    let port = u16::from_be_bytes([data[2], data[3]]);
628    if port == 0 {
629        return Err(FbError::from(
630            "The server did not return a port for the auxiliary event channel",
631        ));
632    }
633
634    Ok(port)
635}
636
637/// Create blob request
638pub fn create_blob(tr_handle: u32) -> Bytes {
639    let mut req = BytesMut::with_capacity(16);
640
641    req.put_u32(WireOp::CreateBlob as u32);
642    req.put_u32(tr_handle);
643    req.put_u64(0); // Blob id, but we are creating one!?
644
645    req.freeze()
646}
647
648/// Open blob request
649pub fn open_blob(tr_handle: u32, blob_id: u64) -> Bytes {
650    let mut req = BytesMut::with_capacity(16);
651
652    req.put_u32(WireOp::OpenBlob as u32);
653    req.put_u32(tr_handle);
654    req.put_u64(blob_id);
655
656    req.freeze()
657}
658
659/// Get blob segment request
660pub fn get_segment(blob_handle: u32) -> Bytes {
661    let mut req = BytesMut::with_capacity(16);
662
663    req.put_u32(WireOp::GetSegment as u32);
664    req.put_u32(blob_handle);
665    req.put_u32(BUFFER_LENGTH);
666    req.put_u32(0); // Data segment, apparently unused
667
668    req.freeze()
669}
670
671/// Put blob segment request
672pub fn put_segment(blob_handle: u32, segment: &[u8]) -> Bytes {
673    let mut req = BytesMut::with_capacity(8 + segment.len());
674
675    req.put_u32(WireOp::PutSegment as u32);
676    req.put_u32(blob_handle);
677    req.put_u32(segment.len() as u32);
678    req.put_wire_bytes(segment);
679
680    req.freeze()
681}
682
683/// Close blob segment request
684pub fn close_blob(blob_handle: u32) -> Bytes {
685    let mut req = BytesMut::with_capacity(8);
686
687    req.put_u32(WireOp::CloseBlob as u32);
688    req.put_u32(blob_handle);
689
690    req.freeze()
691}
692
693#[derive(Debug)]
694/// `WireOp::Response` response
695pub struct Response {
696    pub handle: u32,
697    pub object_id: u64,
698    pub data: Bytes,
699}
700
701/// Parse a server response (`WireOp::Response`)
702pub fn parse_response(resp: &mut Bytes) -> Result<Response, FbError> {
703    let handle = resp.get_u32()?;
704    let object_id = resp.get_u64()?;
705
706    let data = resp.get_wire_bytes()?;
707
708    parse_status_vector(resp)?;
709
710    Ok(Response {
711        handle,
712        object_id,
713        data,
714    })
715}
716
717/// Parse a server sql response (`WireOp::FetchResponse`)
718pub fn parse_fetch_response(
719    resp: &mut Bytes,
720    xsqlda: &[XSqlVar],
721    version: ProtocolVersion,
722    charset: &Charset,
723) -> Result<Option<Vec<ParsedColumn>>, FbError> {
724    const END_OF_STREAM: u32 = 100;
725
726    let status = resp.get_u32()?;
727
728    if status == END_OF_STREAM {
729        return Ok(None);
730    }
731
732    Ok(Some(parse_sql_response(resp, xsqlda, version, charset)?))
733}
734
735/// Parse a server sql response (`WireOp::SqlResponse`)
736/// Identical to the FetchResponse, but has no status
737pub fn parse_sql_response(
738    resp: &mut Bytes,
739    xsqlda: &[XSqlVar],
740    version: ProtocolVersion,
741    charset: &Charset,
742) -> Result<Vec<ParsedColumn>, FbError> {
743    let has_row = resp.get_u32()? != 0;
744    if !has_row {
745        return Err("Fetch returned no columns".into());
746    }
747
748    let null_map = if version >= ProtocolVersion::V13 {
749        // Read the null bitmap, 8 columns per byte
750        let mut len = xsqlda.len() / 8;
751        len += if xsqlda.len() % 8 == 0 { 0 } else { 1 };
752        if len % 4 != 0 {
753            // Align to 4 bytes
754            len += 4 - (len % 4);
755        }
756
757        if resp.remaining() < len {
758            return err_invalid_response();
759        }
760        let null_map = resp.slice(..len);
761        resp.advance(len)?;
762
763        Some(null_map)
764    } else {
765        None
766    };
767
768    let read_null = |resp: &mut Bytes, i: usize| {
769        if version >= ProtocolVersion::V13 {
770            // read from the null bitmap
771            let null_map = null_map.as_ref().expect("Null map was not initialized");
772            Ok::<_, FbError>((null_map[i / 8] >> (i % 8)) & 1 != 0)
773        } else {
774            // read from the response
775            Ok(resp.get_u32()? != 0)
776        }
777    };
778
779    let mut data = Vec::with_capacity(xsqlda.len());
780
781    for (col_index, var) in xsqlda.iter().enumerate() {
782        // Remove nullable type indicator
783        let sqltype = var.sqltype as u32 & (!1);
784
785        if version >= ProtocolVersion::V13 && read_null(resp, col_index)? {
786            // There is no data in protocol 13 if null, so just continue
787            data.push(ParsedColumn::Complete(Column::new(
788                var.alias_name.clone(),
789                sqltype,
790                SqlType::Null,
791            )));
792            continue;
793        }
794
795        match sqltype {
796            ibase::SQL_VARYING => {
797                let d = resp.get_wire_bytes()?;
798
799                let null = read_null(resp, col_index)?;
800                if null {
801                    data.push(ParsedColumn::Complete(Column::new(
802                        var.alias_name.clone(),
803                        sqltype,
804                        SqlType::Null,
805                    )))
806                } else {
807                    data.push(ParsedColumn::Complete(Column::new(
808                        var.alias_name.clone(),
809                        sqltype,
810                        SqlType::Text(charset.decode(&d[..])?),
811                    )))
812                }
813            }
814
815            ibase::SQL_INT64 => {
816                let i = resp.get_i64()?;
817
818                let null = read_null(resp, col_index)?;
819                if null {
820                    data.push(ParsedColumn::Complete(Column::new(
821                        var.alias_name.clone(),
822                        sqltype,
823                        SqlType::Null,
824                    )))
825                } else {
826                    data.push(ParsedColumn::Complete(Column::new(
827                        var.alias_name.clone(),
828                        sqltype,
829                        SqlType::Integer(i),
830                    )))
831                }
832            }
833
834            ibase::SQL_DOUBLE => {
835                let f = resp.get_f64()?;
836
837                let null = read_null(resp, col_index)?;
838                if null {
839                    data.push(ParsedColumn::Complete(Column::new(
840                        var.alias_name.clone(),
841                        sqltype,
842                        SqlType::Null,
843                    )))
844                } else {
845                    data.push(ParsedColumn::Complete(Column::new(
846                        var.alias_name.clone(),
847                        sqltype,
848                        SqlType::Floating(f),
849                    )))
850                }
851            }
852
853            ibase::SQL_TIMESTAMP => {
854                let ts = ibase::ISC_TIMESTAMP {
855                    timestamp_date: resp.get_i32()?,
856                    timestamp_time: resp.get_u32()?,
857                };
858
859                let null = read_null(resp, col_index)?;
860                if null {
861                    data.push(ParsedColumn::Complete(Column::new(
862                        var.alias_name.clone(),
863                        sqltype,
864                        SqlType::Null,
865                    )))
866                } else {
867                    data.push(ParsedColumn::Complete(Column::new(
868                        var.alias_name.clone(),
869                        sqltype,
870                        SqlType::Timestamp(rsfbclient_core::date_time::decode_timestamp(ts)),
871                    )))
872                }
873            }
874
875            ibase::SQL_BLOB if var.sqlsubtype <= 1 => {
876                let id = resp.get_u64()?;
877
878                let null = read_null(resp, col_index)?;
879                if null {
880                    data.push(ParsedColumn::Complete(Column::new(
881                        var.alias_name.clone(),
882                        sqltype,
883                        SqlType::Null,
884                    )))
885                } else {
886                    data.push(ParsedColumn::Blob {
887                        binary: var.sqlsubtype == 0,
888                        id: BlobId(id),
889                        col_name: var.alias_name.clone(),
890                    })
891                }
892            }
893
894            ibase::SQL_BOOLEAN => {
895                let b = resp.get_u8()? == 1;
896                resp.advance(3)?; // Pad to 4 bytes
897
898                let null = read_null(resp, col_index)?;
899
900                if null {
901                    data.push(ParsedColumn::Complete(Column::new(
902                        var.alias_name.clone(),
903                        sqltype,
904                        SqlType::Null,
905                    )))
906                } else {
907                    data.push(ParsedColumn::Complete(Column::new(
908                        var.alias_name.clone(),
909                        sqltype,
910                        SqlType::Boolean(b),
911                    )))
912                }
913            }
914
915            sqltype => {
916                return Err(format!(
917                    "Conversion from sql type {} (subtype {}) not implemented",
918                    sqltype, var.sqlsubtype
919                )
920                .into());
921            }
922        }
923    }
924
925    Ok(data)
926}
927
928/// Column data parsed from a fetch response
929pub enum ParsedColumn {
930    /// All data received
931    Complete(Column),
932    /// Blobs need more requests to get the actual data
933    Blob {
934        /// True if blob type 0
935        binary: bool,
936        /// Blob id
937        id: BlobId,
938        /// Column name
939        col_name: String,
940    },
941}
942
943impl ParsedColumn {
944    /// Get the rest of the data needed for the columns if necessary
945    pub fn into_column(
946        self,
947        conn: &mut FirebirdWireConnection,
948        tr_handle: &mut crate::TrHandle,
949    ) -> Result<Column, FbError> {
950        Ok(match self {
951            ParsedColumn::Complete(c) => c,
952            ParsedColumn::Blob {
953                binary,
954                id,
955                col_name,
956            } => {
957                let mut data = Vec::with_capacity(256);
958
959                let blob_handle = conn.open_blob(tr_handle, id)?;
960
961                loop {
962                    let (mut segment, end) = conn.get_segment(blob_handle)?;
963
964                    data.put(&mut segment);
965
966                    if end {
967                        break;
968                    }
969                }
970
971                conn.close_blob(blob_handle)?;
972
973                Column::new(
974                    col_name,
975                    ibase::SQL_BLOB,
976                    if binary {
977                        SqlType::Binary(data)
978                    } else {
979                        SqlType::Text(conn.charset.decode(data)?)
980                    },
981                )
982            }
983        })
984    }
985}
986
987/// Parses the error messages from the response
988pub fn parse_status_vector(resp: &mut Bytes) -> Result<(), FbError> {
989    // Sql error code (default to -1)
990    let mut sql_code = -1;
991    // Error messages
992    let mut message = String::new();
993
994    // Code of the last error message
995    let mut gds_code = 0;
996    // Error message argument index
997    let mut num_arg = 0;
998
999    loop {
1000        match resp.get_u32()? {
1001            // New error message
1002            ibase::isc_arg_gds => {
1003                gds_code = resp.get_u32()?;
1004
1005                if gds_code != 0 {
1006                    message += gds_to_msg(gds_code);
1007                    num_arg = 0;
1008                }
1009            }
1010
1011            // Error message arg number
1012            ibase::isc_arg_number => {
1013                let num = resp.get_i32()?;
1014                // Sql error code
1015                if gds_code == 335544436 {
1016                    sql_code = num
1017                }
1018
1019                num_arg += 1;
1020                message = message.replace(&format!("@{}", num_arg), &format!("{}", num));
1021            }
1022
1023            // Error message arg string
1024            ibase::isc_arg_string => {
1025                let msg = resp.get_wire_bytes()?;
1026                let msg = std::str::from_utf8(&msg[..]).unwrap_or("**Invalid message**");
1027
1028                num_arg += 1;
1029                message = message.replace(&format!("@{}", num_arg), msg);
1030            }
1031
1032            // Aditional error message string
1033            ibase::isc_arg_interpreted => {
1034                let msg = resp.get_wire_bytes()?;
1035                let msg = std::str::from_utf8(&msg[..]).unwrap_or("**Invalid message**");
1036
1037                message += msg;
1038            }
1039
1040            ibase::isc_arg_sql_state => {
1041                resp.get_wire_bytes()?;
1042            }
1043
1044            // End of error messages
1045            ibase::isc_arg_end => break,
1046
1047            cod => {
1048                return Err(format!("Invalid / Unknown status vector item: {}", cod).into());
1049            }
1050        }
1051    }
1052
1053    if message.ends_with('\n') {
1054        message.pop();
1055    }
1056
1057    if !message.is_empty() {
1058        Err(FbError::Sql {
1059            code: sql_code,
1060            msg: message,
1061        })
1062    } else {
1063        Ok(())
1064    }
1065}
1066
1067#[derive(Debug)]
1068/// Data from the response of a connection request
1069pub struct ConnectionResponse {
1070    pub version: ProtocolVersion,
1071    pub auth_plugin: Option<AuthPlugin>,
1072    pub continue_auth: bool,
1073}
1074
1075#[derive(Debug)]
1076pub struct AuthPlugin {
1077    pub kind: AuthPluginType,
1078    pub data: Option<SrpAuthData>,
1079    pub keys: Bytes,
1080}
1081
1082/// Parse the connect response response (`WireOp::Accept`, `WireOp::AcceptData`, `WireOp::CondAccept` )
1083pub fn parse_accept(resp: &mut Bytes) -> Result<ConnectionResponse, FbError> {
1084    let op_code = resp.get_u32()?;
1085
1086    if op_code == WireOp::Response as u32 {
1087        // Returned an error
1088        parse_response(resp)?;
1089    }
1090
1091    if op_code != WireOp::Accept as u32
1092        && op_code != WireOp::AcceptData as u32
1093        && op_code != WireOp::CondAccept as u32
1094    {
1095        return err_conn_rejected(op_code);
1096    }
1097
1098    let continue_auth = op_code == WireOp::CondAccept as u32;
1099
1100    let version =
1101        ProtocolVersion::try_from(resp.get_u32()?).map_err(|e| FbError::Other(e.to_string()))?;
1102    resp.get_u32()?; // Arch
1103    resp.get_u32()?; // Type
1104
1105    let auth_plugin =
1106        if op_code == WireOp::AcceptData as u32 || op_code == WireOp::CondAccept as u32 {
1107            let auth_data = parse_srp_auth_data(&mut resp.get_wire_bytes()?)?;
1108
1109            let plugin = AuthPluginType::parse(&resp.get_wire_bytes()?)?;
1110
1111            let authenticated = resp.get_u32()? != 0;
1112
1113            let keys = resp.get_wire_bytes()?;
1114
1115            if authenticated {
1116                None
1117            } else {
1118                Some(AuthPlugin {
1119                    kind: plugin,
1120                    data: auth_data,
1121                    keys,
1122                })
1123            }
1124        } else {
1125            None
1126        };
1127
1128    Ok(ConnectionResponse {
1129        version,
1130        auth_plugin,
1131        continue_auth,
1132    })
1133}
1134
1135/// Parse an authentication continuation response (`WireOp::ContAuth`)
1136pub fn parse_cont_auth(resp: &mut Bytes) -> Result<AuthPlugin, FbError> {
1137    let op_code = resp.get_u32()?;
1138
1139    if op_code == WireOp::Response as u32 {
1140        // Returned an error
1141        parse_response(resp)?;
1142    }
1143
1144    if op_code != WireOp::ContAuth as u32 {
1145        return err_conn_rejected(op_code);
1146    }
1147
1148    let auth_data = parse_srp_auth_data(&mut resp.get_wire_bytes()?)?;
1149    let plugin = AuthPluginType::parse(&resp.get_wire_bytes()?)?;
1150    let _plugin_list = resp.get_wire_bytes()?;
1151    let keys = resp.get_wire_bytes()?;
1152
1153    Ok(AuthPlugin {
1154        kind: plugin,
1155        data: auth_data,
1156        keys,
1157    })
1158}
1159
1160#[derive(Debug, Clone)]
1161pub struct SrpAuthData {
1162    pub salt: Box<[u8]>,
1163    pub pub_key: Box<[u8]>,
1164}
1165
1166/// Parse the auth data from the Srp / Srp256 plugin
1167pub fn parse_srp_auth_data(resp: &mut Bytes) -> Result<Option<SrpAuthData>, FbError> {
1168    if resp.is_empty() {
1169        return Ok(None);
1170    }
1171
1172    let len = resp.get_u16_le()? as usize;
1173    if resp.remaining() < len {
1174        return err_invalid_response();
1175    }
1176    let salt = resp.slice(..len);
1177    // * DO NOT PARSE AS HEXADECIMAL *
1178    let salt = salt.to_vec();
1179    resp.advance(len)?;
1180
1181    let len = resp.get_u16_le()? as usize;
1182    if resp.remaining() < len {
1183        return err_invalid_response();
1184    }
1185    let mut pub_key = resp.slice(..len).to_vec();
1186    if len % 2 != 0 {
1187        // We need to add a 0 to the start
1188        pub_key = [b"0", &pub_key[..]].concat();
1189    }
1190    let pub_key =
1191        hex::decode(&pub_key).map_err(|_| FbError::from("Invalid hex pub_key in srp data"))?;
1192    resp.advance(len)?;
1193
1194    Ok(Some(SrpAuthData {
1195        salt: salt.into_boxed_slice(),
1196        pub_key: pub_key.into_boxed_slice(),
1197    }))
1198}
1199
1200/// Parse the result of an `InfoSql` requesting affected rows data
1201pub fn parse_info_sql_affected_rows(data: &mut Bytes) -> Result<usize, FbError> {
1202    let mut affected_rows = 0;
1203
1204    let item = data.get_u8()?;
1205
1206    if item == ibase::isc_info_end as u8 {
1207        return Ok(0); // No affected rows data
1208    }
1209    debug_assert_eq!(item, ibase::isc_info_sql_records as u8);
1210
1211    data.advance(2)?; // Skip data length
1212
1213    loop {
1214        match data.get_u8()? as u32 {
1215            ibase::isc_info_req_select_count => {
1216                // Not interested in the selected count
1217                data.advance(6)?; //  Skip data length (assume 0x04 0x00) and data (4 bytes)
1218            }
1219
1220            ibase::isc_info_req_insert_count
1221            | ibase::isc_info_req_update_count
1222            | ibase::isc_info_req_delete_count => {
1223                data.advance(2)?; //  Skip data length (assume 0x04 0x00)
1224
1225                affected_rows += data.get_u32_le()? as usize;
1226            }
1227
1228            ibase::isc_info_end => {
1229                break;
1230            }
1231
1232            _ => return Err(FbError::from("Invalid affected rows response")),
1233        }
1234    }
1235
1236    Ok(affected_rows)
1237}