1#![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
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 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); attach.put_wire_bytes(db_name.as_bytes());
166
167 attach.put_wire_bytes(&dpb);
168
169 Ok(attach.freeze())
170}
171
172pub 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); create.put_wire_bytes(db_name.as_bytes());
204
205 create.put_wire_bytes(&dpb);
206
207 Ok(create.freeze())
208}
209
210fn 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 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 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 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 ProtocolVersion::V13 => {}
324 }
325
326 Ok(dpb(¶ms))
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); 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
360pub 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
370pub 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
380pub 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
391pub 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
408pub 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); req.put_u32(dialect);
422 req.put_wire_bytes(&bytes);
423 req.put_u32(0); req.put_u32(BUFFER_LENGTH);
425
426 Ok(req.freeze())
427}
428
429pub 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
439pub 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); req.put_u32(BUFFER_LENGTH);
459
460 Ok(req.freeze())
461}
462
463pub 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); req.put_wire_bytes(requested_items);
471 req.put_u32(BUFFER_LENGTH);
472
473 req.freeze()
474}
475
476pub 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
487pub 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
504pub 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); req.put_u32(if input_blr.is_empty() { 0 } else { 1 }); req.put_slice(input_data);
524
525 req.put_wire_bytes(output_blr);
526 req.put_u32(0); req.freeze()
529}
530
531pub 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); req.put_u32(count); req.freeze()
542}
543
544pub 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); req.put_u32(db_handle); req.put_u32(0); req.freeze()
558}
559
560pub 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); req.put_u32(0); req.put_u32(0); req.put_u32(event_id);
570
571 req.freeze()
572}
573
574pub 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)]
586pub struct EventNotification {
588 pub event_id: u32,
590 pub epb: Bytes,
592}
593
594pub 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()?; let epb = resp.get_wire_bytes()?;
604 resp.get_u32()?; resp.get_u32()?; let event_id = resp.get_u32()?;
607
608 Ok(EventNotification { event_id, epb })
609}
610
611pub 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
637pub 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); req.freeze()
646}
647
648pub 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
659pub 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); req.freeze()
669}
670
671pub 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
683pub 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)]
694pub struct Response {
696 pub handle: u32,
697 pub object_id: u64,
698 pub data: Bytes,
699}
700
701pub 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
717pub 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
735pub 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 let mut len = xsqlda.len() / 8;
751 len += if xsqlda.len() % 8 == 0 { 0 } else { 1 };
752 if len % 4 != 0 {
753 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 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 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 let sqltype = var.sqltype as u32 & (!1);
784
785 if version >= ProtocolVersion::V13 && read_null(resp, col_index)? {
786 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)?; 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
928pub enum ParsedColumn {
930 Complete(Column),
932 Blob {
934 binary: bool,
936 id: BlobId,
938 col_name: String,
940 },
941}
942
943impl ParsedColumn {
944 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
987pub fn parse_status_vector(resp: &mut Bytes) -> Result<(), FbError> {
989 let mut sql_code = -1;
991 let mut message = String::new();
993
994 let mut gds_code = 0;
996 let mut num_arg = 0;
998
999 loop {
1000 match resp.get_u32()? {
1001 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 ibase::isc_arg_number => {
1013 let num = resp.get_i32()?;
1014 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 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 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 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)]
1068pub 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
1082pub 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 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()?; resp.get_u32()?; 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
1135pub 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 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
1166pub 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 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 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
1200pub 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); }
1209 debug_assert_eq!(item, ibase::isc_info_sql_records as u8);
1210
1211 data.advance(2)?; loop {
1214 match data.get_u8()? as u32 {
1215 ibase::isc_info_req_select_count => {
1216 data.advance(6)?; }
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)?; 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}