1#![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
17pub const BUFFER_LENGTH: u32 = 1024;
19
20pub fn connect(db_name: &str, user: &str, username: &str, hostname: &str, srp_key: &[u8]) -> Bytes {
22 let protocols = [
23 [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.put_u32(1); connect.put_wire_bytes(db_name.as_bytes());
39
40 connect.put_u32(protocols.len() as u32);
42
43 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 uid.put_u8(Cnct::Login as u8);
53 uid.put_u8(user.len() as u8);
54 uid.put(user.as_bytes());
55
56 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 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 for i in protocols.iter().flatten() {
100 connect.put_u32(*i);
101 }
102
103 connect.freeze()
104}
105
106pub 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
121pub 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 req.put_wire_bytes(algo.as_bytes());
128 req.put_wire_bytes(kind.as_bytes());
130
131 req.freeze()
132}
133
134pub 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); attach.put_wire_bytes(db_name.as_bytes());
162
163 attach.put_wire_bytes(&dpb);
164
165 attach.freeze()
166}
167
168pub 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); create.put_wire_bytes(db_name.as_bytes());
189
190 create.put_wire_bytes(&dpb);
191
192 create.freeze()
193}
194
195fn 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); 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 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 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 ProtocolVersion::V13 => {}
255 }
256
257 dpb.freeze()
258}
259
260pub 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
270pub 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
280pub 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
291pub 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
308pub 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); req.put_u32(dialect);
322 req.put_wire_bytes(&bytes);
323 req.put_u32(0); req.put_u32(BUFFER_LENGTH);
325
326 Ok(req.freeze())
327}
328
329pub 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
339pub 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); req.put_u32(BUFFER_LENGTH);
359
360 Ok(req.freeze())
361}
362
363pub 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); req.put_wire_bytes(requested_items);
371 req.put_u32(BUFFER_LENGTH);
372
373 req.freeze()
374}
375
376pub 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
387pub 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
404pub 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); req.put_u32(if input_blr.is_empty() { 0 } else { 1 }); req.put_slice(input_data);
424
425 req.put_wire_bytes(output_blr);
426 req.put_u32(0); req.freeze()
429}
430
431pub 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); req.put_u32(1); req.freeze()
442}
443
444pub 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); req.freeze()
453}
454
455pub 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
466pub 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); req.freeze()
476}
477
478pub 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
490pub 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)]
501pub struct Response {
503 pub handle: u32,
504 pub object_id: u64,
505 pub data: Bytes,
506}
507
508pub 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
524pub 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
542pub 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 let mut len = xsqlda.len() / 8;
558 len += if xsqlda.len() % 8 == 0 { 0 } else { 1 };
559 if len % 4 != 0 {
560 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 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 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 let sqltype = var.sqltype as u32 & (!1);
591
592 if version >= ProtocolVersion::V13 && read_null(resp, col_index)? {
593 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)?; 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
735pub enum ParsedColumn {
737 Complete(Column),
739 Blob {
741 binary: bool,
743 id: BlobId,
745 col_name: String,
747 },
748}
749
750impl ParsedColumn {
751 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
794pub fn parse_status_vector(resp: &mut Bytes) -> Result<(), FbError> {
796 let mut sql_code = -1;
798 let mut message = String::new();
800
801 let mut gds_code = 0;
803 let mut num_arg = 0;
805
806 loop {
807 match resp.get_u32()? {
808 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 ibase::isc_arg_number => {
820 let num = resp.get_i32()?;
821 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 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 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 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)]
875pub 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
888pub 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 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()?; resp.get_u32()?; 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
938pub 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 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
969pub 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 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 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
1003pub 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); }
1012 debug_assert_eq!(item, ibase::isc_info_sql_records as u8);
1013
1014 data.advance(2)?; loop {
1017 match data.get_u8()? as u32 {
1018 ibase::isc_info_req_select_count => {
1019 data.advance(6)?; }
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)?; 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}