smb_msg/
negotiate.rs

1use binrw::io::{SeekFrom, TakeSeekExt};
2use binrw::prelude::*;
3use modular_bitfield::prelude::*;
4
5use smb_dtyp::{binrw_util::prelude::*, guid::Guid};
6use smb_msg_derive::*;
7
8/// SMB2 NEGOTIATE Request.
9///
10/// Used by the client to notify the server what dialects of the SMB 2 Protocol
11/// the client understands.
12///
13/// Reference: MS-SMB2 2.2.3
14#[smb_request(size = 36)]
15pub struct NegotiateRequest {
16    #[bw(try_calc(u16::try_from(dialects.len())))]
17    #[br(temp)]
18    dialect_count: u16,
19    /// Security mode flags indicating signing requirements.
20    pub security_mode: NegotiateSecurityMode,
21    reserved: u16,
22    /// Client capabilities.
23    pub capabilities: GlobalCapabilities,
24    /// Client GUID, used to identify the client.
25    pub client_guid: Guid,
26
27    #[bw(calc = PosMarker::default())]
28    #[br(temp)]
29    negotiate_context_offset: PosMarker<u32>,
30    #[bw(try_calc(u16::try_from(negotiate_context_list.as_ref().map(|v| v.len()).unwrap_or(0))))]
31    #[br(temp)]
32    negotiate_context_count: u16,
33    reserved: u16,
34    /// List of SMB dialects supported by the client.
35    #[br(count = dialect_count)]
36    pub dialects: Vec<Dialect>,
37    /// Negotiate contexts (SMB 3.1.1+ only).
38    #[brw(if(dialects.contains(&Dialect::Smb0311)), align_before = 8)]
39    #[br(count = negotiate_context_count, seek_before = SeekFrom::Start(negotiate_context_offset.value as u64))]
40    #[bw(write_with = PosMarker::write_aoff, args(&negotiate_context_offset))]
41    pub negotiate_context_list: Option<Vec<NegotiateContext>>,
42}
43
44/// Flags for SMB2 negotiation security mode.
45///
46/// See [NegotiateSecurityMode].
47///
48/// Reference: MS-SMB2 2.2.3
49#[smb_dtyp::mbitfield]
50pub struct NegotiateSecurityMode {
51    /// Signing is enabled.
52    pub signing_enabled: bool,
53    /// Signing is required.
54    pub signing_required: bool,
55    #[skip]
56    __: B14,
57}
58
59/// Global capabilities flags for SMB2/SMB3.
60///
61/// Indicates various protocol capabilities supported by the client or server.
62///
63/// Reference: MS-SMB2 2.2.3
64#[smb_dtyp::mbitfield]
65pub struct GlobalCapabilities {
66    /// DFS support.
67    pub dfs: bool,
68    /// File leasing support.
69    pub leasing: bool,
70    /// Large MTU support (multiple credit operations).
71    pub large_mtu: bool,
72    /// Multi-channel support.
73    pub multi_channel: bool,
74
75    /// Persistent handles support.
76    pub persistent_handles: bool,
77    /// Directory leasing support.
78    pub directory_leasing: bool,
79    /// Encryption support.
80    pub encryption: bool,
81    /// Change notifications support.
82    pub notifications: bool,
83
84    #[skip]
85    __: B24,
86}
87
88/// SMB2 NEGOTIATE Response.
89///
90/// Sent by the server to notify the client of the preferred common dialect.
91///
92/// Reference: MS-SMB2 2.2.4
93#[smb_response(size = 65)]
94pub struct NegotiateResponse {
95    /// Server security mode.
96    pub security_mode: NegotiateSecurityMode,
97    /// Selected dialect revision.
98    pub dialect_revision: NegotiateDialect,
99    #[bw(try_calc(u16::try_from(negotiate_context_list.as_ref().map(|v| v.len()).unwrap_or(0))))]
100    #[br(assert(if dialect_revision == NegotiateDialect::Smb0311 { negotiate_context_count > 0 } else { negotiate_context_count == 0 }))]
101    #[br(temp)]
102    negotiate_context_count: u16,
103    /// Server GUID.
104    pub server_guid: Guid,
105    /// Server capabilities.
106    pub capabilities: GlobalCapabilities,
107    /// Maximum transaction size supported by the server.
108    pub max_transact_size: u32,
109    /// Maximum read size supported by the server.
110    pub max_read_size: u32,
111    /// Maximum write size supported by the server.
112    pub max_write_size: u32,
113    /// Current system time on the server.
114    pub system_time: FileTime,
115    /// Server start time.
116    pub server_start_time: FileTime,
117    #[bw(calc = PosMarker::default())]
118    #[br(temp)]
119    _security_buffer_offset: PosMarker<u16>,
120    #[bw(try_calc(u16::try_from(buffer.len())))]
121    #[br(temp)]
122    security_buffer_length: u16,
123    #[bw(calc = PosMarker::default())]
124    #[br(temp)]
125    negotiate_context_offset: PosMarker<u32>,
126    /// Security buffer containing GSSAPI token.
127    #[br(count = security_buffer_length)]
128    #[bw(write_with = PosMarker::write_aoff, args(&_security_buffer_offset))]
129    pub buffer: Vec<u8>,
130
131    /// Negotiate contexts (SMB 3.1.1+ only).
132    #[brw(if(matches!(dialect_revision, NegotiateDialect::Smb0311)), align_before = 8)]
133    #[br(count = negotiate_context_count, seek_before = SeekFrom::Start(negotiate_context_offset.value as u64))]
134    #[bw(write_with = PosMarker::write_aoff, args(&negotiate_context_offset))]
135    pub negotiate_context_list: Option<Vec<NegotiateContext>>,
136}
137
138/// SMB2/SMB3 protocol dialect revisions.
139///
140/// Reference: MS-SMB2 2.2.3
141#[derive(BinRead, BinWrite, Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
142#[brw(repr(u16))]
143pub enum Dialect {
144    Smb0202 = 0x0202,
145    Smb021 = 0x0210,
146    Smb030 = 0x0300,
147    Smb0302 = 0x0302,
148    Smb0311 = 0x0311,
149}
150
151impl Dialect {
152    pub const MAX: Dialect = Dialect::Smb0311;
153    pub const MIN: Dialect = Dialect::Smb0202;
154    pub const ALL: [Dialect; 5] = [
155        Dialect::Smb0202,
156        Dialect::Smb021,
157        Dialect::Smb030,
158        Dialect::Smb0302,
159        Dialect::Smb0311,
160    ];
161
162    /// Whether this is an SMB3 dialect.
163    #[inline]
164    pub fn is_smb3(&self) -> bool {
165        self >= &Dialect::Smb030
166    }
167}
168
169/// Dialects that may be used in the SMB Negotiate Response.
170///
171/// The same as [Dialect] but includes a wildcard revision for SMB 2.0.
172///
173/// Reference: MS-SMB2 2.2.4
174#[derive(BinRead, BinWrite, Debug, PartialEq, Eq, Clone, Copy)]
175#[brw(repr(u16))]
176pub enum NegotiateDialect {
177    Smb0202 = Dialect::Smb0202 as isize,
178    Smb021 = Dialect::Smb021 as isize,
179    Smb030 = Dialect::Smb030 as isize,
180    Smb0302 = Dialect::Smb0302 as isize,
181    Smb0311 = Dialect::Smb0311 as isize,
182    Smb02Wildcard = 0x02FF,
183}
184
185impl TryFrom<NegotiateDialect> for Dialect {
186    type Error = crate::SmbMsgError;
187
188    fn try_from(value: NegotiateDialect) -> Result<Self, Self::Error> {
189        match value {
190            NegotiateDialect::Smb0202 => Ok(Dialect::Smb0202),
191            NegotiateDialect::Smb021 => Ok(Dialect::Smb021),
192            NegotiateDialect::Smb030 => Ok(Dialect::Smb030),
193            NegotiateDialect::Smb0302 => Ok(Dialect::Smb0302),
194            NegotiateDialect::Smb0311 => Ok(Dialect::Smb0311),
195            _ => Err(Self::Error::InvalidDialect(value)),
196        }
197    }
198}
199
200/// A single negotiate context item.
201///
202/// Used in SMB 3.1.1 to negotiate additional capabilities beyond the base protocol.
203///
204/// Note: This struct should usually be NOT used directly.
205/// To construct it, use `impl From<ContextValueStruct> for NegotiateContext`:
206/// ```
207/// # use smb_msg::*;
208/// let signing_ctx: NegotiateContext = SigningCapabilities {
209///     signing_algorithms: vec![SigningAlgorithmId::AesGmac]
210/// }.into();
211/// ```
212///
213/// Reference: MS-SMB2 2.2.3.1
214#[smb_message_binrw]
215pub struct NegotiateContext {
216    /// Type of the negotiate context.
217    #[brw(align_before = 8)]
218    pub context_type: NegotiateContextType,
219    #[bw(calc = PosMarker::default())]
220    #[br(temp)]
221    data_length: PosMarker<u16>,
222    reserved: u32,
223    /// Context-specific data.
224    #[br(args(&context_type))]
225    #[br(map_stream = |s| s.take_seek(data_length.value as u64))]
226    #[bw(write_with = PosMarker::write_size, args(&data_length))]
227    pub data: NegotiateContextValue,
228}
229
230macro_rules! negotiate_context_type {
231    ($($name:ident = $id:literal,)+) => {
232/// Negotiate context type identifiers.
233///
234/// Reference: MS-SMB2 2.2.3.1
235#[derive(BinRead, BinWrite, Debug, PartialEq, Eq)]
236#[brw(repr(u16))]
237pub enum NegotiateContextType {
238    $(
239        $name = $id,
240    )+
241}
242
243/// Negotiate context values.
244///
245/// Each variant corresponds to a specific negotiate context type.
246#[derive(BinRead, BinWrite, Debug, PartialEq, Eq)]
247#[br(import(context_type: &NegotiateContextType))]
248pub enum NegotiateContextValue {
249    $(
250        #[br(pre_assert(context_type == &NegotiateContextType::$name))]
251        $name($name),
252    )+
253}
254
255impl NegotiateContextValue {
256    /// Gets the matching negotiate context type for this value.
257    pub fn get_matching_type(&self) -> NegotiateContextType {
258        match self {
259            $(
260                NegotiateContextValue::$name(_) => {
261                    NegotiateContextType::$name
262                }
263            )+
264        }
265    }
266}
267
268$(
269    impl From<$name> for NegotiateContext {
270        fn from(val: $name) -> Self {
271            NegotiateContext {
272                context_type: NegotiateContextType::$name,
273                data: NegotiateContextValue::$name(val),
274            }
275        }
276    }
277)+
278
279/// (Internal) Macro to generate impls for getting negotiate contexts from messages.
280macro_rules! gen_impl_for_neg_msg_type {
281    ($msg_type:ident) => {
282
283impl $msg_type {
284    $(
285        pastey::paste! {
286            #[doc = concat!("Gets the negotiate context of type [`", stringify!($name), "`] if present.")]
287            ///
288            /// _This method is auto-generated by the `negotiate_context_type!` macro._
289            pub fn [<get_ctx_ $name:snake>] (&self) -> Option<& $name> {
290                self.negotiate_context_list.as_ref().and_then(|contexts| {
291                    contexts.iter().find_map(|context| match &context.context_type {
292                        NegotiateContextType::$name => match &context.data {
293                            NegotiateContextValue::$name(caps) => Some(caps),
294                            _ => None,
295                        },
296                        _ => None,
297                    })
298                })
299            }
300
301        }
302    )+
303}
304
305    }
306}
307
308gen_impl_for_neg_msg_type!(NegotiateRequest);
309gen_impl_for_neg_msg_type!(NegotiateResponse);
310    };
311}
312
313negotiate_context_type!(
314    PreauthIntegrityCapabilities = 0x0001,
315    EncryptionCapabilities = 0x0002,
316    CompressionCapabilities = 0x0003,
317    NetnameNegotiateContextId = 0x0005,
318    TransportCapabilities = 0x0006,
319    RdmaTransformCapabilities = 0x0007,
320    SigningCapabilities = 0x0008,
321);
322
323/// Hash algorithms for pre-authentication integrity.
324///
325/// Reference: MS-SMB2 2.2.3.1.1
326#[derive(BinRead, BinWrite, Debug, PartialEq, Eq, Clone, Copy)]
327#[brw(repr(u16))]
328pub enum HashAlgorithm {
329    Sha512 = 0x01,
330}
331
332/// (Context) Pre-authentication integrity capabilities.
333///
334/// Specifies the hash algorithm and salt used for pre-authentication integrity.
335///
336/// Reference: MS-SMB2 2.2.3.1.1
337#[smb_message_binrw]
338pub struct PreauthIntegrityCapabilities {
339    #[bw(try_calc(u16::try_from(hash_algorithms.len())))]
340    hash_algorithm_count: u16,
341    #[bw(try_calc(u16::try_from(salt.len())))]
342    salt_length: u16,
343    /// Supported hash algorithms for pre-authentication integrity.
344    #[br(count = hash_algorithm_count)]
345    pub hash_algorithms: Vec<HashAlgorithm>,
346    /// Salt value for pre-authentication integrity.
347    #[br(count = salt_length)]
348    pub salt: Vec<u8>,
349}
350
351/// (Context) Encryption capabilities.
352///
353/// Specifies the encryption ciphers supported by the client or server.
354///
355/// Reference: MS-SMB2 2.2.3.1.2
356#[smb_message_binrw]
357pub struct EncryptionCapabilities {
358    #[bw(try_calc(u16::try_from(ciphers.len())))]
359    cipher_count: u16,
360    /// Supported encryption ciphers in preference order.
361    #[br(count = cipher_count)]
362    pub ciphers: Vec<EncryptionCipher>,
363}
364
365/// Encryption cipher identifiers.
366///
367/// Reference: MS-SMB2 2.2.3.1.2
368#[derive(BinRead, BinWrite, Debug, PartialEq, Eq, Clone, Copy)]
369#[brw(repr(u16))]
370pub enum EncryptionCipher {
371    Aes128Ccm = 0x0001,
372    Aes128Gcm = 0x0002,
373    Aes256Ccm = 0x0003,
374    Aes256Gcm = 0x0004,
375}
376
377/// (Context) Compression capabilities.
378///
379/// Specifies the compression algorithms supported by the client or server.
380///
381/// Reference: MS-SMB2 2.2.3.1.3
382#[smb_message_binrw]
383#[derive(Clone)]
384pub struct CompressionCapabilities {
385    #[bw(try_calc(u16::try_from(compression_algorithms.len())))]
386    compression_algorithm_count: u16,
387    #[bw(calc = 0)]
388    _padding: u16,
389    /// Compression capability flags.
390    pub flags: CompressionCapsFlags,
391    /// Supported compression algorithms in preference order.
392    #[br(count = compression_algorithm_count)]
393    pub compression_algorithms: Vec<CompressionAlgorithm>,
394}
395
396/// Compression algorithm identifiers.
397///
398/// Reference: MS-SMB2 2.2.3.1.3
399#[derive(BinRead, BinWrite, Debug, PartialEq, Eq, Clone, Copy)]
400#[brw(repr(u16))]
401#[repr(u16)]
402pub enum CompressionAlgorithm {
403    None = 0x0000,
404    LZNT1 = 0x0001,
405    LZ77 = 0x0002,
406    LZ77Huffman = 0x0003,
407    PatternV1 = 0x0004,
408    LZ4 = 0x0005,
409}
410
411impl CompressionAlgorithm {
412    /// Relevant for processing compressed messages.
413    pub fn original_size_required(&self) -> bool {
414        matches!(
415            self,
416            CompressionAlgorithm::LZNT1
417                | CompressionAlgorithm::LZ77
418                | CompressionAlgorithm::LZ77Huffman
419                | CompressionAlgorithm::LZ4
420        )
421    }
422}
423
424impl std::fmt::Display for CompressionAlgorithm {
425    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
426        let message_as_string = match self {
427            CompressionAlgorithm::None => "None",
428            CompressionAlgorithm::LZNT1 => "LZNT1",
429            CompressionAlgorithm::LZ77 => "LZ77",
430            CompressionAlgorithm::LZ77Huffman => "LZ77+Huffman",
431            CompressionAlgorithm::PatternV1 => "PatternV1",
432            CompressionAlgorithm::LZ4 => "LZ4",
433        };
434        write!(f, "{} ({:#x})", message_as_string, *self as u16)
435    }
436}
437
438/// Flags to indicate compression capabilities.
439///
440/// See [CompressionCapabilities].
441///
442/// Reference: MS-SMB2 2.2.3.1.3
443#[smb_dtyp::mbitfield]
444pub struct CompressionCapsFlags {
445    /// Chained compression support.
446    pub chained: bool,
447    #[skip]
448    __: B31,
449}
450
451/// Netname negotiate context.
452///
453/// Specifies the server name the client wants to connect to.
454///
455/// Reference: MS-SMB2 2.2.3.1.4
456#[derive(BinRead, BinWrite, Debug, PartialEq, Eq)]
457pub struct NetnameNegotiateContextId {
458    /// Server name the client intends to connect to.
459    #[br(parse_with = binrw::helpers::until_eof)]
460    pub netname: SizedWideString,
461}
462
463/// (Context) Transport capabilities.
464///
465/// Specifies whether QUIC transport is supported.
466///
467/// Reference: MS-SMB2 2.2.3.1.5
468#[smb_dtyp::mbitfield]
469pub struct TransportCapabilities {
470    /// QUIC transport support.
471    pub accept_transport_layer_security: bool,
472    #[skip]
473    __: B31,
474}
475
476/// (Context) RDMA transform capabilities.
477///
478/// Specifies RDMA transform IDs supported for SMB Direct connections.
479///
480/// Reference: MS-SMB2 2.2.3.1.6
481#[smb_message_binrw]
482pub struct RdmaTransformCapabilities {
483    #[bw(try_calc(u16::try_from(transforms.len())))]
484    transform_count: u16,
485
486    reserved: u16,
487    reserved: u32,
488
489    /// Supported RDMA transform IDs.
490    #[br(count = transform_count)]
491    pub transforms: Vec<RdmaTransformId>,
492}
493
494/// RDMA transform identifiers.
495///
496/// Reference: MS-SMB2 2.2.3.1.6
497#[smb_message_binrw]
498#[brw(repr(u16))]
499pub enum RdmaTransformId {
500    None = 0x0000,
501    Encryption = 0x0001,
502    Signing = 0x0002,
503}
504
505/// (Context) Signing capabilities.
506///
507/// Specifies the signing algorithms supported by the client or server.
508///
509/// Reference: MS-SMB2 2.2.3.1.7
510#[smb_message_binrw]
511pub struct SigningCapabilities {
512    #[bw(try_calc(u16::try_from(signing_algorithms.len())))]
513    signing_algorithm_count: u16,
514    /// Supported signing algorithms in preference order.
515    #[br(count = signing_algorithm_count)]
516    pub signing_algorithms: Vec<SigningAlgorithmId>,
517}
518
519/// Signing algorithm identifiers.
520///
521/// Reference: MS-SMB2 2.2.3.1.7
522#[derive(BinRead, BinWrite, Debug, PartialEq, Eq, Clone, Copy)]
523#[brw(repr(u16))]
524pub enum SigningAlgorithmId {
525    HmacSha256 = 0x0000,
526    AesCmac = 0x0001,
527    AesGmac = 0x0002,
528}
529
530#[cfg(test)]
531mod tests {
532    use smb_dtyp::make_guid;
533    use smb_tests::hex_to_u8_array;
534    use time::macros::datetime;
535
536    use super::*;
537    use crate::*;
538
539    test_request! {
540        Negotiate {
541            security_mode: NegotiateSecurityMode::new().with_signing_enabled(true),
542            capabilities: GlobalCapabilities::new()
543                .with_dfs(true)
544                .with_leasing(true)
545                .with_large_mtu(true)
546                .with_multi_channel(true)
547                .with_persistent_handles(true)
548                .with_directory_leasing(true)
549                .with_encryption(true)
550                .with_notifications(true),
551            client_guid: make_guid!("{c12e0ddf-43dd-11f0-8b87-000c29801682}"),
552            dialects: vec![
553                Dialect::Smb0202,
554                Dialect::Smb021,
555                Dialect::Smb030,
556                Dialect::Smb0302,
557                Dialect::Smb0311,
558            ],
559            negotiate_context_list: Some(vec![
560                PreauthIntegrityCapabilities {
561                    hash_algorithms: vec![HashAlgorithm::Sha512],
562                    salt: hex_to_u8_array! {"ed006c304e332890b2bd98617b5ad9ef075994154673696280ffcc0f1291a15d"}
563                }.into(),
564                EncryptionCapabilities { ciphers: vec![
565                    EncryptionCipher::Aes128Gcm,
566                    EncryptionCipher::Aes128Ccm,
567                    EncryptionCipher::Aes256Gcm,
568                    EncryptionCipher::Aes256Ccm,
569                ] }.into(),
570                CompressionCapabilities {
571                    flags: CompressionCapsFlags::new().with_chained(true),
572                    compression_algorithms: vec![
573                        CompressionAlgorithm::PatternV1,
574                        CompressionAlgorithm::LZ77,
575                        CompressionAlgorithm::LZ77Huffman,
576                        CompressionAlgorithm::LZNT1,
577                        CompressionAlgorithm::LZ4,
578                    ]
579                }.into(),
580                SigningCapabilities { signing_algorithms: vec![
581                    SigningAlgorithmId::AesGmac,
582                    SigningAlgorithmId::AesCmac,
583                    SigningAlgorithmId::HmacSha256,
584                ] }.into(),
585                NetnameNegotiateContextId { netname: "localhost".into() }.into(),
586                RdmaTransformCapabilities { transforms: vec![RdmaTransformId::Encryption, RdmaTransformId::Signing] }.into()
587            ])
588        } => "2400050001000000ff000000df0d2ec1dd43f0118b87000c298
589        016827000000006000000020210020003020311030000010026000000
590        0000010020000100ed006c304e332890b2bd98617b5ad9ef075994154
591        673696280ffcc0f1291a15d000002000a000000000004000200010004
592        000300000000000000030012000000000005000000010000000400020
593        003000100050000000000000008000800000000000300020001000000
594        05001200000000006c006f00630061006c0068006f007300740000000
595        000000007000c0000000000020000000000000001000200"
596    }
597
598    test_response! {
599        Negotiate {
600            security_mode: NegotiateSecurityMode::new().with_signing_enabled(true),
601            dialect_revision: NegotiateDialect::Smb0311,
602            server_guid: Guid::from([
603                0xb9, 0x21, 0xf8, 0xe0, 0x15, 0x7, 0xaa, 0x41, 0xbe, 0x38, 0x67, 0xfe, 0xbf,
604                0x5e, 0x2e, 0x11
605            ]),
606            capabilities: GlobalCapabilities::new()
607                .with_dfs(true)
608                .with_leasing(true)
609                .with_large_mtu(true)
610                .with_multi_channel(true)
611                .with_directory_leasing(true),
612            max_transact_size: 8388608,
613            max_read_size: 8388608,
614            max_write_size: 8388608,
615            system_time: datetime!(2025-01-18 16:24:39.448746400).into(),
616            server_start_time: FileTime::default(),
617            buffer: [
618                0x60, 0x28, 0x6, 0x6, 0x2b, 0x6, 0x1, 0x5, 0x5, 0x2, 0xa0, 0x1e, 0x30, 0x1c,
619                0xa0, 0x1a, 0x30, 0x18, 0x6, 0xa, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x82, 0x37, 0x2,
620                0x2, 0x1e, 0x6, 0xa, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x82, 0x37, 0x2, 0x2, 0xa
621            ]
622            .to_vec(),
623            negotiate_context_list: Some(vec![
624                PreauthIntegrityCapabilities {
625                        hash_algorithms: vec![HashAlgorithm::Sha512],
626                        salt: [
627                            0xd5, 0x67, 0x1b, 0x24, 0xa1, 0xe9, 0xcc, 0xc8, 0x93, 0xf5, 0x55,
628                            0x5a, 0x31, 0x3, 0x43, 0x5a, 0x85, 0x2b, 0xc3, 0xcb, 0x1a, 0xd3,
629                            0x2d, 0xc5, 0x1f, 0x92, 0x80, 0x6e, 0xf3, 0xfb, 0x4d, 0xd4
630                        ]
631                        .to_vec()
632                    }
633                .into(),
634                EncryptionCapabilities {
635                    ciphers: vec![EncryptionCipher::Aes128Gcm]
636                }
637                .into(),
638                SigningCapabilities {
639                    signing_algorithms: vec![SigningAlgorithmId::AesGmac]
640                }
641                .into(),
642                RdmaTransformCapabilities {
643                    transforms: vec![RdmaTransformId::Encryption, RdmaTransformId::Signing]
644                }
645                .into(),
646                CompressionCapabilities {
647                    flags: CompressionCapsFlags::new().with_chained(true),
648                    compression_algorithms: vec![
649                        CompressionAlgorithm::LZ77,
650                        CompressionAlgorithm::PatternV1
651                    ]
652                }
653                .into(),
654            ])
655        } => "4100010011030500b921f8e01507aa41be3867febf5e2e112f000000000080000000800000008000a876d878c569db01000000000000000080002a00b0000000602806062b0601050502a01e301ca01a3018060a2b06010401823702021e060a2b06010401823702020a0000000000000100260000000000010020000100d5671b24a1e9ccc893f5555a3103435a852bc3cb1ad32dc51f92806ef3fb4dd40000020004000000000001000200000000000800040000000000010002000000000007000c00000000000200000000000000010002000000000003000c0000000000020000000100000002000400"
656    }
657}