1#[cfg(feature = "client")]
3use binrw::io::TakeSeekExt;
4use binrw::{NullWideString, prelude::*};
5use modular_bitfield::prelude::*;
6use smb_dtyp::binrw_util::prelude::*;
7use smb_msg_derive::{smb_message_binrw, smb_request_binrw, smb_response_binrw};
8
9use crate::{Dialect, NegotiateSecurityMode};
10
11use crate::dfsc::{ReqGetDfsReferral, ReqGetDfsReferralEx, RespGetDfsReferral};
12use smb_dtyp::*;
13use smb_fscc::*;
14
15use super::common::IoctlRequestContent;
16use crate::IoctlBuffer;
17use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
18use std::ops::{Deref, DerefMut};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[repr(u32)]
22pub enum FsctlCodes {
23 DfsGetReferrals = 0x00060194,
24 OffloadRead = 0x00094264,
25 PipePeek = 0x0011400C,
26 PipeWait = 0x00110018,
27 PipeTransceive = 0x0011C017,
28 SrvCopychunk = 0x001440F2,
29 SrvEnumerateSnapshots = 0x00144064,
30 SrvRequestResumeKey = 0x00140078,
31 SrvReadHash = 0x001441bb,
32 SrvCopychunkWrite = 0x001480F2,
33 LmrRequestResiliency = 0x001401D4,
34 QueryNetworkInterfaceInfo = 0x001401FC,
35 SetReparsePoint = 0x000900A4,
36 DfsGetReferralsEx = 0x000601B0,
37 FileLevelTrim = 0x00098208,
38 ValidateNegotiateInfo = 0x00140204,
39 QueryAllocatedRanges = 0x000940CF,
40}
41
42#[smb_message_binrw]
47pub struct SrvCopychunkCopy {
48 pub source_key: [u8; SrvCopychunkCopy::SRV_KEY_LENGTH],
51 #[bw(try_calc = chunks.len().try_into())]
53 chunk_count: u32,
54 reserved: u32,
55 #[br(count = chunk_count)]
58 pub chunks: Vec<SrvCopychunkItem>,
59}
60
61impl SrvCopychunkCopy {
62 pub const SRV_KEY_LENGTH: usize = 24;
63 pub const SIZE: usize = Self::SRV_KEY_LENGTH + 4 + 4;
64}
65
66#[smb_message_binrw]
71pub struct SrvCopychunkItem {
72 pub source_offset: u64,
75 pub target_offset: u64,
78 pub length: u32,
80 reserved: u32,
81}
82
83impl SrvCopychunkItem {
84 pub const SIZE: usize = size_of::<u64>() * 2 + size_of::<u32>() * 2;
85}
86
87impl IoctlRequestContent for SrvCopychunkCopy {
88 fn get_bin_size(&self) -> u32 {
89 (Self::SIZE + self.chunks.len() * SrvCopychunkItem::SIZE) as u32
90 }
91}
92
93#[smb_request_binrw]
99pub struct SrvReadHashReq {
100 #[bw(calc = 1)]
103 #[br(assert(hash_type == 1))]
104 pub hash_type: u32,
105 #[br(assert((1..=2).contains(&hash_version)))]
109 #[bw(assert((1..=2).contains(hash_version)))]
110 pub hash_version: u32,
111 pub hash_retrieval_type: SrvHashRetrievalType,
113}
114
115impl IoctlRequestContent for SrvReadHashReq {
116 fn get_bin_size(&self) -> u32 {
117 size_of::<u32>() as u32 * 3
118 }
119}
120
121#[smb_request_binrw]
126#[brw(repr(u32))]
127pub enum SrvHashRetrievalType {
128 HashBased = 1,
131 FileBased = 2,
135}
136
137#[smb_request_binrw]
143pub struct NetworkResiliencyRequest {
144 pub timeout: u32,
147 reserved: u32,
148}
149
150impl IoctlRequestContent for NetworkResiliencyRequest {
151 fn get_bin_size(&self) -> u32 {
152 size_of::<u32>() as u32 * 2
153 }
154}
155
156#[smb_request_binrw]
162pub struct ValidateNegotiateInfoRequest {
163 pub capabilities: u32,
165 pub guid: Guid,
167 pub security_mode: NegotiateSecurityMode,
169 #[bw(try_calc = dialects.len().try_into())]
171 dialect_count: u16,
172 #[br(count = dialect_count)]
175 pub dialects: Vec<Dialect>,
176}
177
178impl IoctlRequestContent for ValidateNegotiateInfoRequest {
179 fn get_bin_size(&self) -> u32 {
180 (size_of::<u32>()
181 + Guid::GUID_SIZE
182 + 2
183 + size_of::<u16>()
184 + self.dialects.len() * size_of::<u16>()) as u32
185 }
186}
187
188#[smb_response_binrw]
194pub struct SrvSnapshotArray {
195 pub number_of_snap_shots: u32,
197 pub number_of_snap_shots_returned: u32,
200 #[bw(calc = PosMarker::default())]
203 #[br(temp)]
204 pub snap_shot_array_size: PosMarker<u32>,
205 #[br(parse_with = binrw::helpers::until_eof, map_stream = |s| s.take_seek(snap_shot_array_size.value as u64))]
209 #[bw(write_with = PosMarker::write_size, args(&snap_shot_array_size))]
210 pub snap_shots: Vec<NullWideString>,
211}
212
213#[cfg(all(feature = "client", not(feature = "server")))]
214pub trait FsctlResponseContent: for<'a> BinRead<Args<'a> = ()> + std::fmt::Debug {
216 const FSCTL_CODES: &'static [FsctlCodes];
217}
218
219#[cfg(all(feature = "server", not(feature = "client")))]
220pub trait FsctlResponseContent: for<'a> BinWrite<Args<'a> = ()> + std::fmt::Debug {
222 const FSCTL_CODES: &'static [FsctlCodes];
223}
224
225#[cfg(all(feature = "client", feature = "server"))]
226pub trait FsctlResponseContent:
228 for<'a> BinRead<Args<'a> = ()> + for<'b> BinWrite<Args<'b> = ()> + std::fmt::Debug
229{
230 const FSCTL_CODES: &'static [FsctlCodes];
231}
232
233macro_rules! impl_fsctl_response {
234 ($code:ident, $type:ty) => {
235 impl FsctlResponseContent for $type {
236 const FSCTL_CODES: &'static [FsctlCodes] = &[FsctlCodes::$code];
237 }
238 };
239}
240
241#[smb_response_binrw]
247pub struct SrvRequestResumeKey {
248 pub resume_key: [u8; SrvCopychunkCopy::SRV_KEY_LENGTH],
252 #[bw(calc = 0)]
256 #[br(temp)]
257 context_length: u32,
258 #[br(count = context_length)]
260 #[bw(assert(context.len() == context_length as usize))]
261 pub context: Vec<u8>,
262}
263
264impl_fsctl_response!(SrvRequestResumeKey, SrvRequestResumeKey);
265
266#[smb_response_binrw]
272pub struct SrvCopychunkResponse {
273 pub chunks_written: u32,
276 pub chunk_bytes_written: u32,
281 pub total_bytes_written: u32,
285}
286
287impl_fsctl_response!(SrvCopychunk, SrvCopychunkResponse);
288
289#[smb_response_binrw]
295pub struct SrvReadHashRes {
296 #[bw(calc = 1)]
298 #[br(assert(hash_type == 1))]
299 hash_type: u32,
300 #[br(assert((1..=2).contains(&hash_version)))]
303 #[bw(assert((1..=2).contains(hash_version)))]
304 hash_version: u32,
305 source_file_change_time: FileTime,
307 source_file_size: u64,
309 hash_blob_length: PosMarker<u32>,
311 hash_blob_offset: PosMarker<u32>,
313 dirty: u16,
315 #[bw(try_calc = source_file_name.len().try_into())]
317 source_file_name_length: u16,
318 #[br(count = source_file_name_length)]
320 source_file_name: Vec<u8>,
321}
322
323impl_fsctl_response!(SrvReadHash, SrvReadHashRes);
324
325#[smb_response_binrw]
330pub struct SrvHashRetrieveHashBased {
331 pub offset: u64,
334 #[bw(try_calc = blob.len().try_into())]
336 buffer_length: u32,
337 reserved: u32,
338 #[br(count = buffer_length)]
341 blob: Vec<u8>,
342}
343
344impl_fsctl_response!(SrvReadHash, SrvHashRetrieveHashBased);
345
346#[smb_response_binrw]
352pub struct SrvHashRetrieveFileBased {
353 pub file_data_offset: u64,
355 pub file_data_length: u64,
358 #[bw(try_calc = buffer.len().try_into())]
360 buffer_length: u32,
361 reserved: u32,
362 #[br(count = buffer_length)]
365 pub buffer: Vec<u8>,
366}
367
368pub type NetworkInterfacesInfo = ChainedItemList<NetworkInterfaceInfo>;
369
370impl_fsctl_response!(QueryNetworkInterfaceInfo, NetworkInterfacesInfo);
371
372#[smb_response_binrw]
377pub struct NetworkInterfaceInfo {
378 pub if_index: u32,
380 pub capability: NetworkInterfaceCapability,
382 reserved: u32,
383 pub link_speed: u64,
385 pub sockaddr: SocketAddrStorage,
388}
389
390#[smb_dtyp::mbitfield]
395pub struct NetworkInterfaceCapability {
396 pub rss: bool,
398 pub rdma: bool,
400 #[skip]
401 __: B30,
402}
403
404#[smb_response_binrw]
405pub enum SocketAddrStorage {
406 V4(SocketAddrStorageV4),
407 V6(SocketAddrStorageV6),
408}
409
410impl SocketAddrStorage {
411 pub fn socket_addr(&self) -> SocketAddr {
412 match self {
413 SocketAddrStorage::V4(v4) => SocketAddr::V4(v4.to_addr()),
414 SocketAddrStorage::V6(v6) => SocketAddr::V6(v6.to_addr()),
415 }
416 }
417}
418
419#[smb_response_binrw]
420#[brw(magic(b"\x02\x00"))] pub struct SocketAddrStorageV4 {
422 pub port: u16,
423 pub address: u32,
424 reserved: [u8; 128 - (2 + 2 + 4)],
425}
426
427impl SocketAddrStorageV4 {
428 fn to_addr(&self) -> SocketAddrV4 {
429 SocketAddrV4::new(Ipv4Addr::from(self.address.to_be()), self.port)
430 }
431}
432
433#[smb_response_binrw]
434#[brw(magic(b"\x17\x00"))] pub struct SocketAddrStorageV6 {
436 pub port: u16,
437 pub flow_info: u32,
438 pub address: u128,
439 pub scope_id: u32,
440 reserved: [u8; 128 - (2 + 2 + 4 + 16 + 4)],
441}
442
443impl SocketAddrStorageV6 {
444 fn to_addr(&self) -> SocketAddrV6 {
445 SocketAddrV6::new(
446 Ipv6Addr::from(self.address.to_be()),
447 self.port,
448 self.flow_info,
449 self.scope_id,
450 )
451 }
452}
453
454#[smb_response_binrw]
460pub struct ValidateNegotiateInfoResponse {
461 pub capabilities: u32,
463 pub guid: Guid,
465 pub security_mode: NegotiateSecurityMode,
467 pub dialect: Dialect,
469}
470
471impl_fsctl_response!(ValidateNegotiateInfo, ValidateNegotiateInfoResponse);
472
473impl FsctlResponseContent for RespGetDfsReferral {
475 const FSCTL_CODES: &'static [FsctlCodes] =
476 &[FsctlCodes::DfsGetReferrals, FsctlCodes::DfsGetReferralsEx];
477}
478
479impl IoctlRequestContent for ReqGetDfsReferral {
480 fn get_bin_size(&self) -> u32 {
481 (size_of::<u16>() + (self.request_file_name.len() + 1) * size_of::<u16>()) as u32
482 }
483}
484
485impl IoctlRequestContent for ReqGetDfsReferralEx {
486 fn get_bin_size(&self) -> u32 {
487 (size_of::<u16>() * 2 + size_of::<u32>() + self.request_data.get_bin_size()) as u32
488 }
489}
490
491#[smb_message_binrw] #[derive(Default)]
493pub struct QueryAllocRangesItem {
494 pub offset: u64,
495 pub len: u64,
496}
497
498impl IoctlRequestContent for QueryAllocRangesItem {
499 fn get_bin_size(&self) -> u32 {
500 (size_of::<u64>() * 2) as u32
501 }
502}
503
504#[smb_response_binrw]
505#[derive(Default)]
506pub struct QueryAllocRangesResult {
507 #[br(parse_with = binrw::helpers::until_eof)]
508 values: Vec<QueryAllocRangesItem>,
509}
510
511impl Deref for QueryAllocRangesResult {
512 type Target = Vec<QueryAllocRangesItem>;
513 fn deref(&self) -> &Self::Target {
514 &self.values
515 }
516}
517
518impl From<Vec<QueryAllocRangesItem>> for QueryAllocRangesResult {
519 fn from(value: Vec<QueryAllocRangesItem>) -> Self {
520 Self { values: value }
521 }
522}
523
524impl_fsctl_response!(QueryAllocatedRanges, QueryAllocRangesResult);
525
526#[smb_request_binrw]
531pub struct PipeWaitRequest {
532 pub timeout: u64,
535 #[bw(calc = name.size() as u32)]
536 #[br(temp)]
537 name_length: u32,
538 pub timeout_specified: Boolean,
541 reserved: u8,
543 #[br(args {size: SizedStringSize::bytes(name_length)})]
546 pub name: SizedWideString,
547}
548
549impl IoctlRequestContent for PipeWaitRequest {
550 fn get_bin_size(&self) -> u32 {
551 (size_of::<u64>()
552 + size_of::<u32>()
553 + size_of::<Boolean>()
554 + size_of::<u8>()
555 + self.name.size() as usize) as u32
556 }
557}
558
559#[smb_request_binrw]
563pub struct SetReparsePointRequest {
564 #[bw(assert((reparse_tag & 0x80000000 == 0) == reparse_guid.is_some()))]
566 pub reparse_tag: u32,
567 #[bw(calc = reparse_data.len() as u32)]
568 reparse_data_length: u32,
569 #[br(if(reparse_tag & 0x80000000 == 0))]
573 pub reparse_guid: Option<Guid>,
574 #[br(count = reparse_data_length)]
576 pub reparse_data: Vec<u8>,
577}
578
579impl IoctlRequestContent for SetReparsePointRequest {
580 fn get_bin_size(&self) -> u32 {
581 (size_of::<u32>()
582 + size_of::<u32>()
583 + self.reparse_guid.as_ref().map_or(0, |_| size_of::<Guid>())
584 + self.reparse_data.len()) as u32
585 }
586}
587
588#[smb_request_binrw]
589pub struct FileLevelTrimRequest {
590 reserved: u32,
592 #[bw(calc = ranges.len() as u32)]
593 num_ranges: u32,
594 #[br(count = num_ranges)]
596 pub ranges: Vec<FileLevelTrimRange>,
597}
598
599#[smb_request_binrw]
603pub struct FileLevelTrimRange {
604 pub offset: u64,
606 pub length: u64,
608}
609
610impl IoctlRequestContent for FileLevelTrimRequest {
611 fn get_bin_size(&self) -> u32 {
612 (size_of::<u32>() + size_of::<u32>() + self.ranges.len() * size_of::<FileLevelTrimRange>())
613 as u32
614 }
615}
616
617#[smb_response_binrw]
619pub struct PipePeekResponse {
620 pub named_pipe_state: NamedPipeState,
622 #[bw(calc = data.len() as u32)]
623 read_data_available: u32,
625 pub number_of_messages: u32,
627 pub message_length: u32,
629 #[br(count = read_data_available as u64)]
631 pub data: Vec<u8>,
632}
633
634impl_fsctl_response!(PipePeek, PipePeekResponse);
635
636#[smb_response_binrw]
638pub struct SrvEnumerateSnapshotsResponse {
639 pub number_of_snap_shots: u32,
641 pub number_of_snap_shots_returned: u32,
645 #[bw(calc = PosMarker::default())]
647 #[br(temp)]
648 snap_shot_array_size: PosMarker<u32>,
649 #[br(map_stream = |s| s.take_seek(snap_shot_array_size.value as u64))]
651 #[bw(write_with = PosMarker::write_size, args(&snap_shot_array_size))]
652 pub snap_shots: MultiWSz,
653}
654
655impl_fsctl_response!(SrvEnumerateSnapshots, SrvEnumerateSnapshotsResponse);
656
657#[smb_response_binrw]
659pub struct FileLevelTrimResponse {
660 pub num_ranges_processed: u32,
662}
663
664impl_fsctl_response!(FileLevelTrim, FileLevelTrimResponse);
665
666#[smb_request_binrw]
668pub struct OffloadReadRequest {
669 #[bw(calc = 0x20)]
670 #[br(assert(_size == 0x20))]
671 #[br(temp)]
672 _size: u32,
673 pub flags: u32,
675 pub token_time_to_live: u32,
677 reserved: u32,
678 pub file_offset: u64,
681 pub copy_length: u64,
684}
685
686impl IoctlRequestContent for OffloadReadRequest {
687 fn get_bin_size(&self) -> u32 {
688 (size_of::<u32>() * 4 + size_of::<u64>() * 2) as u32
689 }
690}
691
692#[smb_response_binrw]
694pub struct OffloadReadResponse {
695 #[bw(calc = 528)]
696 #[br(assert(_size == 528))]
697 _size: u32,
698
699 pub all_zero_beyond_current_range: Boolean,
702 _padding: u8,
703 _padding2: u16,
704
705 pub transfer_length: u64,
710
711 pub token: [u8; 512], }
715
716impl_fsctl_response!(OffloadRead, OffloadReadResponse);
717
718macro_rules! make_newtype {
724 ($attr_type:ident $vis:vis $name:ident($inner:ty)) => {
725 #[$attr_type]
726 pub struct $name(pub $inner);
727
728 impl $name {
729 pub fn new(inner: $inner) -> Self {
730 Self(inner)
731 }
732 }
733
734 impl From<$inner> for $name {
735 fn from(inner: $inner) -> Self {
736 Self(inner)
737 }
738 }
739
740 impl Deref for $name {
741 type Target = $inner;
742
743 fn deref(&self) -> &Self::Target {
744 &self.0
745 }
746 }
747
748 impl DerefMut for $name {
749 fn deref_mut(&mut self) -> &mut Self::Target {
750 &mut self.0
751 }
752 }
753 };
754}
755
756macro_rules! make_req_newtype {
757 ($vis:vis $name:ident($inner:ty)) => {
758 make_newtype!(smb_request_binrw $vis $name($inner));
759 impl IoctlRequestContent for $name {
760 fn get_bin_size(&self) -> u32 {
761 self.0.get_bin_size()
762 }
763 }
764 }
765}
766
767macro_rules! make_res_newtype {
768 ($fsctl:ident: $vis:vis $name:ident($inner:ty)) => {
769 make_newtype!(smb_response_binrw $vis $name($inner));
770 impl FsctlResponseContent for $name {
771 const FSCTL_CODES: &'static [FsctlCodes] = &[FsctlCodes::$fsctl];
772 }
773 }
774}
775
776make_req_newtype!(pub PipePeekRequest(()));
777make_req_newtype!(pub SrvEnumerateSnapshotsRequest(()));
778make_req_newtype!(pub SrvRequestResumeKeyRequest(()));
779make_req_newtype!(pub QueryNetworkInterfaceInfoRequest(()));
780make_req_newtype!(pub PipeTransceiveRequest(IoctlBuffer));
781make_req_newtype!(pub SrvCopyChunkCopyWrite(SrvCopychunkCopy));
782
783make_res_newtype!(
784 PipeWait: pub PipeWaitResponse(())
785);
786make_res_newtype!(
787 PipeTransceive: pub PipeTransceiveResponse(IoctlBuffer)
788);
789make_res_newtype!(
790 SetReparsePoint: pub SetReparsePointResponse(())
791);
792
793make_res_newtype!(
794 LmrRequestResiliency: pub LmrRequestResiliencyResponse(())
795);
796
797#[cfg(test)]
798mod tests {
799 use super::*;
800 use crate::*;
801
802 test_binrw_request! {
803 struct OffloadReadRequest {
804 flags: 0,
805 token_time_to_live: 0,
806 file_offset: 0,
807 copy_length: 10485760,
808 } => "2000000000000000000000000000000000000000000000000000a00000000000"
809 }
810
811 test_binrw_response! {
812 struct SrvRequestResumeKey {
813 resume_key: [
814 0x2d, 0x3, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x27, 0x11, 0x6a, 0x26, 0x30, 0xd2,
815 0xdb, 0x1, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
816 ],
817 context: vec![],
818 } => "2d0300001c00000027116a2630d2db01fffe00000000000000000000"
819 }
820
821 const CHUNK_SIZE: u32 = 1 << 20; const TOTAL_SIZE: u32 = 10417096;
823 const BLOCK_NUM: u32 = (TOTAL_SIZE + CHUNK_SIZE - 1) / CHUNK_SIZE;
824
825 test_binrw_request! {
826 struct SrvCopychunkCopy {
827 source_key: [
828 0x2d, 0x3, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x27, 0x11, 0x6a, 0x26, 0x30, 0xd2, 0xdb,
829 0x1, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
830 ],
831 chunks: (0..BLOCK_NUM).map(|i| SrvCopychunkItem {
832 source_offset: (i * CHUNK_SIZE) as u64,
833 target_offset: (i * CHUNK_SIZE) as u64,
834 length: if i == BLOCK_NUM - 1 {
835 TOTAL_SIZE % CHUNK_SIZE
836 } else {
837 CHUNK_SIZE
838 },
839 }).collect(),
840 } => "2d0300001c00000027116a2630d2db01fffe0000000000000a000000000000000
841 00000000000000000000000000000000000100000000000000010000000000000001000
842 00000000000010000000000000002000000000000000200000000000000010000000000
843 00000300000000000000030000000000000001000000000000000400000000000000040
844 00000000000000100000000000000050000000000000005000000000000000100000000
845 00000006000000000000000600000000000000010000000000000007000000000000000
846 70000000000000001000000000000000800000000000000080000000000000001000000
847 0000000009000000000000000900000000000c8f30e0000000000"
848 }
849
850 test_binrw_response! {
851 struct SrvCopychunkResponse {
852 chunks_written: 10,
853 chunk_bytes_written: 0,
854 total_bytes_written: 10417096,
855 } => "0a00000000000000c8f39e00"
856 }
857
858 test_binrw_response! {
859 struct QueryAllocRangesResult {
860 values: vec![
861 QueryAllocRangesItem {
862 offset: 0,
863 len: 4096,
864 },
865 QueryAllocRangesItem {
866 offset: 8192,
867 len: 46801,
868 },
869 ],
870 } => "000000000000000000100000000000000020000000000000d1b6000000000000"
871 }
872
873 test_binrw_response! {
874 NetworkInterfacesInfo: NetworkInterfacesInfo::from(vec![
875 NetworkInterfaceInfo {
876 if_index: 2,
877 capability: NetworkInterfaceCapability::new().with_rdma(true),
878 link_speed: 1000000000,
879 sockaddr: SocketAddrStorage::V4(SocketAddrStorageV4 {
880 port: 0,
881 address: 0xac10cc84u32.to_be(),
882 })
883 },
884 NetworkInterfaceInfo {
885 if_index: 2,
886 capability: NetworkInterfaceCapability::new().with_rdma(true),
887 link_speed: 1000000000,
888 sockaddr: SocketAddrStorage::V6(SocketAddrStorageV6 {
889 port: 0,
890 flow_info: 0,
891 address: 0xfe80000000000000020c29fffe9f8bf3u128.to_be(),
892 scope_id: 0,
893 })
894 },
895 ]) => "9800000002000000020000000000000000ca9a3b0000000002000000ac10cc8400000000000000
896 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000
897 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000
898 0000000000000000000000000000000000000000000000000000000000000000020000000200000000000
899 00000ca9a3b000000001700000000000000fe80000000000000020c29fffe9f8bf3000000000000000000
900 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000
901 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000
902 00000000000000000000"
903 }
904
905 }