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::{convert::TryFrom, str};
7
8use crate::{
9    client::{BlobId, FirebirdWireConnection},
10    consts::{gds_to_msg, AuthPluginType, Cnct, ProtocolVersion, WireOp},
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) -> Bytes {
145    let dpb = build_dpb(
146        user,
147        pass,
148        protocol,
149        charset,
150        None,
151        role_name,
152        dialect,
153        no_db_triggers,
154    );
155
156    let mut attach = BytesMut::with_capacity(16 + db_name.len() + dpb.len());
157
158    attach.put_u32(WireOp::Attach as u32);
159    attach.put_u32(0); // Database Object ID
160
161    attach.put_wire_bytes(db_name.as_bytes());
162
163    attach.put_wire_bytes(&dpb);
164
165    attach.freeze()
166}
167
168/// Create db request
169pub fn create(
170    db_name: &str,
171    user: &str,
172    pass: &str,
173    protocol: ProtocolVersion,
174    charset: Charset,
175    page_size: Option<u32>,
176    role_name: Option<&str>,
177    dialect: Dialect,
178) -> Bytes {
179    let dpb = build_dpb(
180        user, pass, protocol, charset, page_size, role_name, dialect, false,
181    );
182
183    let mut create = BytesMut::with_capacity(16 + db_name.len() + dpb.len());
184
185    create.put_u32(WireOp::Create as u32);
186    create.put_u32(0); // Database Object ID
187
188    create.put_wire_bytes(db_name.as_bytes());
189
190    create.put_wire_bytes(&dpb);
191
192    create.freeze()
193}
194
195/// Dpb builder
196fn build_dpb(
197    user: &str,
198    pass: &str,
199    protocol: ProtocolVersion,
200    charset: Charset,
201    page_size: Option<u32>,
202    role_name: Option<&str>,
203    dialect: Dialect,
204    no_db_triggers: bool,
205) -> Bytes {
206    let mut dpb = BytesMut::with_capacity(64);
207
208    dpb.put_u8(1); //Version
209
210    if let Some(ps) = page_size {
211        dpb.put_slice(&[ibase::isc_dpb_page_size as u8, 4]);
212        dpb.put_u32(ps);
213    }
214
215    let charset = charset.on_firebird.as_bytes();
216
217    dpb.put_slice(&[ibase::isc_dpb_lc_ctype as u8, charset.len() as u8]);
218    dpb.put_slice(charset);
219
220    dpb.put_slice(&[ibase::isc_dpb_user_name as u8, user.len() as u8]);
221    dpb.put_slice(user.as_bytes());
222
223    if let Some(role) = role_name {
224        dpb.extend(&[ibase::isc_dpb_sql_role_name as u8, role.len() as u8]);
225        dpb.extend(role.bytes());
226    }
227
228    dpb.extend(&[ibase::isc_dpb_sql_dialect as u8, 1 as u8]);
229    dpb.extend(&[dialect as u8]);
230
231    if no_db_triggers {
232        dpb.extend(&[ibase::isc_dpb_no_db_triggers as u8, 1 as u8]);
233        dpb.extend(&[1 as u8]);
234    }
235
236    match protocol {
237        // Plaintext password
238        ProtocolVersion::V10 => {
239            dpb.put_slice(&[ibase::isc_dpb_password as u8, pass.len() as u8]);
240            dpb.put_slice(pass.as_bytes());
241        }
242
243        // Hashed password
244        ProtocolVersion::V11 | ProtocolVersion::V12 => {
245            #[allow(deprecated)]
246            let enc_pass = pwhash::unix_crypt::hash_with("9z", pass).unwrap();
247            let enc_pass = &enc_pass[2..];
248
249            dpb.put_slice(&[ibase::isc_dpb_password_enc as u8, enc_pass.len() as u8]);
250            dpb.put_slice(enc_pass.as_bytes());
251        }
252
253        // Password already verified
254        ProtocolVersion::V13 => {}
255    }
256
257    dpb.freeze()
258}
259
260/// Detach from the database request
261pub fn detach(db_handle: u32) -> Bytes {
262    let mut tr = BytesMut::with_capacity(8);
263
264    tr.put_u32(WireOp::Detach as u32);
265    tr.put_u32(db_handle);
266
267    tr.freeze()
268}
269
270/// Drop database request
271pub fn drop_database(db_handle: u32) -> Bytes {
272    let mut tr = BytesMut::with_capacity(8);
273
274    tr.put_u32(WireOp::DropDatabase as u32);
275    tr.put_u32(db_handle);
276
277    tr.freeze()
278}
279
280/// Begin transaction request
281pub fn transaction(db_handle: u32, tpb: &[u8]) -> Bytes {
282    let mut tr = BytesMut::with_capacity(12 + tpb.len());
283
284    tr.put_u32(WireOp::Transaction as u32);
285    tr.put_u32(db_handle);
286    tr.put_wire_bytes(tpb);
287
288    tr.freeze()
289}
290
291/// Commit / Rollback transaction request
292pub fn transaction_operation(tr_handle: u32, op: TrOp) -> Bytes {
293    let mut tr = BytesMut::with_capacity(8);
294
295    let op = match op {
296        TrOp::Commit => WireOp::Commit,
297        TrOp::CommitRetaining => WireOp::CommitRetaining,
298        TrOp::Rollback => WireOp::Rollback,
299        TrOp::RollbackRetaining => WireOp::RollbackRetaining,
300    };
301
302    tr.put_u32(op as u32);
303    tr.put_u32(tr_handle);
304
305    tr.freeze()
306}
307
308/// Execute immediate request
309pub fn exec_immediate(
310    tr_handle: u32,
311    dialect: u32,
312    sql: &str,
313    charset: &Charset,
314) -> Result<Bytes, FbError> {
315    let bytes = charset.encode(sql)?;
316    let mut req = BytesMut::with_capacity(28 + bytes.len());
317
318    req.put_u32(WireOp::ExecImmediate as u32);
319    req.put_u32(tr_handle);
320    req.put_u32(0); // Statement handle, apparently unused
321    req.put_u32(dialect);
322    req.put_wire_bytes(&bytes);
323    req.put_u32(0); // TODO: parameters
324    req.put_u32(BUFFER_LENGTH);
325
326    Ok(req.freeze())
327}
328
329/// Statement allocation request (lazy response)
330pub fn allocate_statement(db_handle: u32) -> Bytes {
331    let mut req = BytesMut::with_capacity(8);
332
333    req.put_u32(WireOp::AllocateStatement as u32);
334    req.put_u32(db_handle);
335
336    req.freeze()
337}
338
339/// Prepare statement request. Use u32::MAX as `stmt_handle` if the statement was allocated
340/// in the previous request
341pub fn prepare_statement(
342    tr_handle: u32,
343    stmt_handle: u32,
344    dialect: u32,
345    query: &str,
346    charset: &Charset,
347) -> Result<Bytes, FbError> {
348    let bytes = charset.encode(query)?;
349    let mut req = BytesMut::with_capacity(28 + bytes.len() + XSQLDA_DESCRIBE_VARS.len());
350
351    req.put_u32(WireOp::PrepareStatement as u32);
352    req.put_u32(tr_handle);
353    req.put_u32(stmt_handle);
354    req.put_u32(dialect);
355    req.put_wire_bytes(&bytes);
356    req.put_wire_bytes(&XSQLDA_DESCRIBE_VARS); // Data to be returned
357
358    req.put_u32(BUFFER_LENGTH);
359
360    Ok(req.freeze())
361}
362
363/// Statement information request
364pub fn info_sql(stmt_handle: u32, requested_items: &[u8]) -> Bytes {
365    let mut req = BytesMut::with_capacity(24 + requested_items.len());
366
367    req.put_u32(WireOp::InfoSql as u32);
368    req.put_u32(stmt_handle);
369    req.put_u32(0); // Incarnation of object
370    req.put_wire_bytes(requested_items);
371    req.put_u32(BUFFER_LENGTH);
372
373    req.freeze()
374}
375
376/// Close or drop statement request
377pub fn free_statement(stmt_handle: u32, op: FreeStmtOp) -> Bytes {
378    let mut req = BytesMut::with_capacity(12);
379
380    req.put_u32(WireOp::FreeStatement as u32);
381    req.put_u32(stmt_handle);
382    req.put_u32(op as u32);
383
384    req.freeze()
385}
386
387/// Execute prepared statement request.
388pub fn execute(tr_handle: u32, stmt_handle: u32, input_blr: &[u8], input_data: &[u8]) -> Bytes {
389    let mut req = BytesMut::with_capacity(36 + input_blr.len() + input_data.len());
390
391    req.put_u32(WireOp::Execute as u32);
392    req.put_u32(stmt_handle);
393    req.put_u32(tr_handle);
394
395    req.put_wire_bytes(input_blr);
396    req.put_u32(0);
397    req.put_u32(if input_blr.is_empty() { 0 } else { 1 });
398
399    req.put_slice(input_data);
400
401    req.freeze()
402}
403
404/// Execute prepared statement request.
405pub fn execute2(
406    tr_handle: u32,
407    stmt_handle: u32,
408    input_blr: &[u8],
409    input_data: &[u8],
410    output_blr: &[u8],
411) -> Bytes {
412    let mut req =
413        BytesMut::with_capacity(40 + input_blr.len() + input_data.len() + output_blr.len());
414
415    req.put_u32(WireOp::Execute2 as u32);
416    req.put_u32(stmt_handle);
417    req.put_u32(tr_handle);
418
419    req.put_wire_bytes(input_blr);
420    req.put_u32(0); // Input message number
421    req.put_u32(if input_blr.is_empty() { 0 } else { 1 }); // Messages
422
423    req.put_slice(input_data);
424
425    req.put_wire_bytes(output_blr);
426    req.put_u32(0); // Output message number
427
428    req.freeze()
429}
430
431/// Fetch row request
432pub fn fetch(stmt_handle: u32, blr: &[u8]) -> Bytes {
433    let mut req = BytesMut::with_capacity(20 + blr.len());
434
435    req.put_u32(WireOp::Fetch as u32);
436    req.put_u32(stmt_handle);
437    req.put_wire_bytes(blr);
438    req.put_u32(0); // Message number
439    req.put_u32(1); // Message count TODO: increase to return more rows in one fetch request
440
441    req.freeze()
442}
443
444/// Create blob request
445pub fn create_blob(tr_handle: u32) -> Bytes {
446    let mut req = BytesMut::with_capacity(16);
447
448    req.put_u32(WireOp::CreateBlob as u32);
449    req.put_u32(tr_handle);
450    req.put_u64(0); // Blob id, but we are creating one!?
451
452    req.freeze()
453}
454
455/// Open blob request
456pub fn open_blob(tr_handle: u32, blob_id: u64) -> Bytes {
457    let mut req = BytesMut::with_capacity(16);
458
459    req.put_u32(WireOp::OpenBlob as u32);
460    req.put_u32(tr_handle);
461    req.put_u64(blob_id);
462
463    req.freeze()
464}
465
466/// Get blob segment request
467pub fn get_segment(blob_handle: u32) -> Bytes {
468    let mut req = BytesMut::with_capacity(16);
469
470    req.put_u32(WireOp::GetSegment as u32);
471    req.put_u32(blob_handle);
472    req.put_u32(BUFFER_LENGTH);
473    req.put_u32(0); // Data segment, apparently unused
474
475    req.freeze()
476}
477
478/// Put blob segment request
479pub fn put_segment(blob_handle: u32, segment: &[u8]) -> Bytes {
480    let mut req = BytesMut::with_capacity(8 + segment.len());
481
482    req.put_u32(WireOp::PutSegment as u32);
483    req.put_u32(blob_handle);
484    req.put_u32(segment.len() as u32);
485    req.put_wire_bytes(segment);
486
487    req.freeze()
488}
489
490/// Close blob segment request
491pub fn close_blob(blob_handle: u32) -> Bytes {
492    let mut req = BytesMut::with_capacity(8);
493
494    req.put_u32(WireOp::CloseBlob as u32);
495    req.put_u32(blob_handle);
496
497    req.freeze()
498}
499
500#[derive(Debug)]
501/// `WireOp::Response` response
502pub struct Response {
503    pub handle: u32,
504    pub object_id: u64,
505    pub data: Bytes,
506}
507
508/// Parse a server response (`WireOp::Response`)
509pub fn parse_response(resp: &mut Bytes) -> Result<Response, FbError> {
510    let handle = resp.get_u32()?;
511    let object_id = resp.get_u64()?;
512
513    let data = resp.get_wire_bytes()?;
514
515    parse_status_vector(resp)?;
516
517    Ok(Response {
518        handle,
519        object_id,
520        data,
521    })
522}
523
524/// Parse a server sql response (`WireOp::FetchResponse`)
525pub fn parse_fetch_response(
526    resp: &mut Bytes,
527    xsqlda: &[XSqlVar],
528    version: ProtocolVersion,
529    charset: &Charset,
530) -> Result<Option<Vec<ParsedColumn>>, FbError> {
531    const END_OF_STREAM: u32 = 100;
532
533    let status = resp.get_u32()?;
534
535    if status == END_OF_STREAM {
536        return Ok(None);
537    }
538
539    Ok(Some(parse_sql_response(resp, xsqlda, version, charset)?))
540}
541
542/// Parse a server sql response (`WireOp::SqlResponse`)
543/// Identical to the FetchResponse, but has no status
544pub fn parse_sql_response(
545    resp: &mut Bytes,
546    xsqlda: &[XSqlVar],
547    version: ProtocolVersion,
548    charset: &Charset,
549) -> Result<Vec<ParsedColumn>, FbError> {
550    let has_row = resp.get_u32()? != 0;
551    if !has_row {
552        return Err("Fetch returned no columns".into());
553    }
554
555    let null_map = if version >= ProtocolVersion::V13 {
556        // Read the null bitmap, 8 columns per byte
557        let mut len = xsqlda.len() / 8;
558        len += if xsqlda.len() % 8 == 0 { 0 } else { 1 };
559        if len % 4 != 0 {
560            // Align to 4 bytes
561            len += 4 - (len % 4);
562        }
563
564        if resp.remaining() < len {
565            return err_invalid_response();
566        }
567        let null_map = resp.slice(..len);
568        resp.advance(len)?;
569
570        Some(null_map)
571    } else {
572        None
573    };
574
575    let read_null = |resp: &mut Bytes, i: usize| {
576        if version >= ProtocolVersion::V13 {
577            // read from the null bitmap
578            let null_map = null_map.as_ref().expect("Null map was not initialized");
579            Ok::<_, FbError>((null_map[i / 8] >> (i % 8)) & 1 != 0)
580        } else {
581            // read from the response
582            Ok(resp.get_u32()? != 0)
583        }
584    };
585
586    let mut data = Vec::with_capacity(xsqlda.len());
587
588    for (col_index, var) in xsqlda.iter().enumerate() {
589        // Remove nullable type indicator
590        let sqltype = var.sqltype as u32 & (!1);
591
592        if version >= ProtocolVersion::V13 && read_null(resp, col_index)? {
593            // There is no data in protocol 13 if null, so just continue
594            data.push(ParsedColumn::Complete(Column::new(
595                var.alias_name.clone(),
596                sqltype,
597                SqlType::Null,
598            )));
599            continue;
600        }
601
602        match sqltype {
603            ibase::SQL_VARYING => {
604                let d = resp.get_wire_bytes()?;
605
606                let null = read_null(resp, col_index)?;
607                if null {
608                    data.push(ParsedColumn::Complete(Column::new(
609                        var.alias_name.clone(),
610                        sqltype,
611                        SqlType::Null,
612                    )))
613                } else {
614                    data.push(ParsedColumn::Complete(Column::new(
615                        var.alias_name.clone(),
616                        sqltype,
617                        SqlType::Text(charset.decode(&d[..])?),
618                    )))
619                }
620            }
621
622            ibase::SQL_INT64 => {
623                let i = resp.get_i64()?;
624
625                let null = read_null(resp, col_index)?;
626                if null {
627                    data.push(ParsedColumn::Complete(Column::new(
628                        var.alias_name.clone(),
629                        sqltype,
630                        SqlType::Null,
631                    )))
632                } else {
633                    data.push(ParsedColumn::Complete(Column::new(
634                        var.alias_name.clone(),
635                        sqltype,
636                        SqlType::Integer(i),
637                    )))
638                }
639            }
640
641            ibase::SQL_DOUBLE => {
642                let f = resp.get_f64()?;
643
644                let null = read_null(resp, col_index)?;
645                if null {
646                    data.push(ParsedColumn::Complete(Column::new(
647                        var.alias_name.clone(),
648                        sqltype,
649                        SqlType::Null,
650                    )))
651                } else {
652                    data.push(ParsedColumn::Complete(Column::new(
653                        var.alias_name.clone(),
654                        sqltype,
655                        SqlType::Floating(f),
656                    )))
657                }
658            }
659
660            ibase::SQL_TIMESTAMP => {
661                let ts = ibase::ISC_TIMESTAMP {
662                    timestamp_date: resp.get_i32()?,
663                    timestamp_time: resp.get_u32()?,
664                };
665
666                let null = read_null(resp, col_index)?;
667                if null {
668                    data.push(ParsedColumn::Complete(Column::new(
669                        var.alias_name.clone(),
670                        sqltype,
671                        SqlType::Null,
672                    )))
673                } else {
674                    data.push(ParsedColumn::Complete(Column::new(
675                        var.alias_name.clone(),
676                        sqltype,
677                        SqlType::Timestamp(rsfbclient_core::date_time::decode_timestamp(ts)),
678                    )))
679                }
680            }
681
682            ibase::SQL_BLOB if var.sqlsubtype <= 1 => {
683                let id = resp.get_u64()?;
684
685                let null = read_null(resp, col_index)?;
686                if null {
687                    data.push(ParsedColumn::Complete(Column::new(
688                        var.alias_name.clone(),
689                        sqltype,
690                        SqlType::Null,
691                    )))
692                } else {
693                    data.push(ParsedColumn::Blob {
694                        binary: var.sqlsubtype == 0,
695                        id: BlobId(id),
696                        col_name: var.alias_name.clone(),
697                    })
698                }
699            }
700
701            ibase::SQL_BOOLEAN => {
702                let b = resp.get_u8()? == 1;
703                resp.advance(3)?; // Pad to 4 bytes
704
705                let null = read_null(resp, col_index)?;
706
707                if null {
708                    data.push(ParsedColumn::Complete(Column::new(
709                        var.alias_name.clone(),
710                        sqltype,
711                        SqlType::Null,
712                    )))
713                } else {
714                    data.push(ParsedColumn::Complete(Column::new(
715                        var.alias_name.clone(),
716                        sqltype,
717                        SqlType::Boolean(b),
718                    )))
719                }
720            }
721
722            sqltype => {
723                return Err(format!(
724                    "Conversion from sql type {} (subtype {}) not implemented",
725                    sqltype, var.sqlsubtype
726                )
727                .into());
728            }
729        }
730    }
731
732    Ok(data)
733}
734
735/// Column data parsed from a fetch response
736pub enum ParsedColumn {
737    /// All data received
738    Complete(Column),
739    /// Blobs need more requests to get the actual data
740    Blob {
741        /// True if blob type 0
742        binary: bool,
743        /// Blob id
744        id: BlobId,
745        /// Column name
746        col_name: String,
747    },
748}
749
750impl ParsedColumn {
751    /// Get the rest of the data needed for the columns if necessary
752    pub fn into_column(
753        self,
754        conn: &mut FirebirdWireConnection,
755        tr_handle: &mut crate::TrHandle,
756    ) -> Result<Column, FbError> {
757        Ok(match self {
758            ParsedColumn::Complete(c) => c,
759            ParsedColumn::Blob {
760                binary,
761                id,
762                col_name,
763            } => {
764                let mut data = Vec::with_capacity(256);
765
766                let blob_handle = conn.open_blob(tr_handle, id)?;
767
768                loop {
769                    let (mut segment, end) = conn.get_segment(blob_handle)?;
770
771                    data.put(&mut segment);
772
773                    if end {
774                        break;
775                    }
776                }
777
778                conn.close_blob(blob_handle)?;
779
780                Column::new(
781                    col_name,
782                    ibase::SQL_BLOB,
783                    if binary {
784                        SqlType::Binary(data)
785                    } else {
786                        SqlType::Text(conn.charset.decode(data)?)
787                    },
788                )
789            }
790        })
791    }
792}
793
794/// Parses the error messages from the response
795pub fn parse_status_vector(resp: &mut Bytes) -> Result<(), FbError> {
796    // Sql error code (default to -1)
797    let mut sql_code = -1;
798    // Error messages
799    let mut message = String::new();
800
801    // Code of the last error message
802    let mut gds_code = 0;
803    // Error message argument index
804    let mut num_arg = 0;
805
806    loop {
807        match resp.get_u32()? {
808            // New error message
809            ibase::isc_arg_gds => {
810                gds_code = resp.get_u32()?;
811
812                if gds_code != 0 {
813                    message += gds_to_msg(gds_code);
814                    num_arg = 0;
815                }
816            }
817
818            // Error message arg number
819            ibase::isc_arg_number => {
820                let num = resp.get_i32()?;
821                // Sql error code
822                if gds_code == 335544436 {
823                    sql_code = num
824                }
825
826                num_arg += 1;
827                message = message.replace(&format!("@{}", num_arg), &format!("{}", num));
828            }
829
830            // Error message arg string
831            ibase::isc_arg_string => {
832                let msg = resp.get_wire_bytes()?;
833                let msg = std::str::from_utf8(&msg[..]).unwrap_or("**Invalid message**");
834
835                num_arg += 1;
836                message = message.replace(&format!("@{}", num_arg), msg);
837            }
838
839            // Aditional error message string
840            ibase::isc_arg_interpreted => {
841                let msg = resp.get_wire_bytes()?;
842                let msg = std::str::from_utf8(&msg[..]).unwrap_or("**Invalid message**");
843
844                message += msg;
845            }
846
847            ibase::isc_arg_sql_state => {
848                resp.get_wire_bytes()?;
849            }
850
851            // End of error messages
852            ibase::isc_arg_end => break,
853
854            cod => {
855                return Err(format!("Invalid / Unknown status vector item: {}", cod).into());
856            }
857        }
858    }
859
860    if message.ends_with('\n') {
861        message.pop();
862    }
863
864    if !message.is_empty() {
865        Err(FbError::Sql {
866            code: sql_code,
867            msg: message,
868        })
869    } else {
870        Ok(())
871    }
872}
873
874#[derive(Debug)]
875/// Data from the response of a connection request
876pub struct ConnectionResponse {
877    pub version: ProtocolVersion,
878    pub auth_plugin: Option<AuthPlugin>,
879}
880
881#[derive(Debug)]
882pub struct AuthPlugin {
883    pub kind: AuthPluginType,
884    pub data: Option<SrpAuthData>,
885    pub keys: Bytes,
886}
887
888/// Parse the connect response response (`WireOp::Accept`, `WireOp::AcceptData`, `WireOp::CondAccept` )
889pub fn parse_accept(resp: &mut Bytes) -> Result<ConnectionResponse, FbError> {
890    let op_code = resp.get_u32()?;
891
892    if op_code == WireOp::Response as u32 {
893        // Returned an error
894        parse_response(resp)?;
895    }
896
897    if op_code != WireOp::Accept as u32
898        && op_code != WireOp::AcceptData as u32
899        && op_code != WireOp::CondAccept as u32
900    {
901        return err_conn_rejected(op_code);
902    }
903
904    let version =
905        ProtocolVersion::try_from(resp.get_u32()?).map_err(|e| FbError::Other(e.to_string()))?;
906    resp.get_u32()?; // Arch
907    resp.get_u32()?; // Type
908
909    let auth_plugin =
910        if op_code == WireOp::AcceptData as u32 || op_code == WireOp::CondAccept as u32 {
911            let auth_data = parse_srp_auth_data(&mut resp.get_wire_bytes()?)?;
912
913            let plugin = AuthPluginType::parse(&resp.get_wire_bytes()?)?;
914
915            let authenticated = resp.get_u32()? != 0;
916
917            let keys = resp.get_wire_bytes()?;
918
919            if authenticated {
920                None
921            } else {
922                Some(AuthPlugin {
923                    kind: plugin,
924                    data: auth_data,
925                    keys,
926                })
927            }
928        } else {
929            None
930        };
931
932    Ok(ConnectionResponse {
933        version,
934        auth_plugin,
935    })
936}
937
938/// Parse an authentication continuation response (`WireOp::ContAuth`)
939pub fn parse_cont_auth(resp: &mut Bytes) -> Result<AuthPlugin, FbError> {
940    let op_code = resp.get_u32()?;
941
942    if op_code == WireOp::Response as u32 {
943        // Returned an error
944        parse_response(resp)?;
945    }
946
947    if op_code != WireOp::ContAuth as u32 {
948        return err_conn_rejected(op_code);
949    }
950
951    let auth_data = parse_srp_auth_data(&mut resp.get_wire_bytes()?)?;
952    let plugin = AuthPluginType::parse(&resp.get_wire_bytes()?)?;
953    let _plugin_list = resp.get_wire_bytes()?;
954    let keys = resp.get_wire_bytes()?;
955
956    Ok(AuthPlugin {
957        kind: plugin,
958        data: auth_data,
959        keys,
960    })
961}
962
963#[derive(Debug)]
964pub struct SrpAuthData {
965    pub salt: Box<[u8]>,
966    pub pub_key: Box<[u8]>,
967}
968
969/// Parse the auth data from the Srp / Srp256 plugin
970pub fn parse_srp_auth_data(resp: &mut Bytes) -> Result<Option<SrpAuthData>, FbError> {
971    if resp.is_empty() {
972        return Ok(None);
973    }
974
975    let len = resp.get_u16_le()? as usize;
976    if resp.remaining() < len {
977        return err_invalid_response();
978    }
979    let salt = resp.slice(..len);
980    // * DO NOT PARSE AS HEXADECIMAL *
981    let salt = salt.to_vec();
982    resp.advance(len)?;
983
984    let len = resp.get_u16_le()? as usize;
985    if resp.remaining() < len {
986        return err_invalid_response();
987    }
988    let mut pub_key = resp.slice(..len).to_vec();
989    if len % 2 != 0 {
990        // We need to add a 0 to the start
991        pub_key = [b"0", &pub_key[..]].concat();
992    }
993    let pub_key =
994        hex::decode(&pub_key).map_err(|_| FbError::from("Invalid hex pub_key in srp data"))?;
995    resp.advance(len)?;
996
997    Ok(Some(SrpAuthData {
998        salt: salt.into_boxed_slice(),
999        pub_key: pub_key.into_boxed_slice(),
1000    }))
1001}
1002
1003/// Parse the result of an `InfoSql` requesting affected rows data
1004pub fn parse_info_sql_affected_rows(data: &mut Bytes) -> Result<usize, FbError> {
1005    let mut affected_rows = 0;
1006
1007    let item = data.get_u8()?;
1008
1009    if item == ibase::isc_info_end as u8 {
1010        return Ok(0); // No affected rows data
1011    }
1012    debug_assert_eq!(item, ibase::isc_info_sql_records as u8);
1013
1014    data.advance(2)?; // Skip data length
1015
1016    loop {
1017        match data.get_u8()? as u32 {
1018            ibase::isc_info_req_select_count => {
1019                // Not interested in the selected count
1020                data.advance(6)?; //  Skip data length (assume 0x04 0x00) and data (4 bytes)
1021            }
1022
1023            ibase::isc_info_req_insert_count
1024            | ibase::isc_info_req_update_count
1025            | ibase::isc_info_req_delete_count => {
1026                data.advance(2)?; //  Skip data length (assume 0x04 0x00)
1027
1028                affected_rows += data.get_u32_le()? as usize;
1029            }
1030
1031            ibase::isc_info_end => {
1032                break;
1033            }
1034
1035            _ => return Err(FbError::from("Invalid affected rows response")),
1036        }
1037    }
1038
1039    Ok(affected_rows)
1040}