@@ -508,6 +508,74 @@ impl std::fmt::Display for TransactionId {
508
508
}
509
509
}
510
510
511
+ /// The fixed length header of a STUN message. Allows reading the message header for a quick
512
+ /// check if this message is a valid STUN message. Can also be used to expose the length of the
513
+ /// complete message without needing to receive the entire message.
514
+ pub struct MessageHeader {
515
+ mtype : MessageType ,
516
+ transaction_id : TransactionId ,
517
+ length : u16 ,
518
+ }
519
+
520
+ impl MessageHeader {
521
+ /// The length of the STUN message header.
522
+ pub const LENGTH : usize = 20 ;
523
+
524
+ /// Deserialize a `MessageHeader`
525
+ ///
526
+ /// # Examples
527
+ ///
528
+ /// ```
529
+ /// # use stun_types::message::{MessageHeader, MessageType, MessageClass, BINDING};
530
+ /// let msg_data = [0, 1, 0, 8, 33, 18, 164, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 232];
531
+ /// let message = MessageHeader::from_bytes(&msg_data).unwrap();
532
+ /// assert_eq!(message.get_type(), MessageType::from_class_method(MessageClass::Request, BINDING));
533
+ /// assert_eq!(message.transaction_id(), 1000.into());
534
+ /// assert_eq!(message.data_length(), 8);
535
+ /// ```
536
+ pub fn from_bytes ( data : & [ u8 ] ) -> Result < Self , StunParseError > {
537
+ if data. len ( ) < 20 {
538
+ return Err ( StunParseError :: Truncated {
539
+ expected : 20 ,
540
+ actual : data. len ( ) ,
541
+ } ) ;
542
+ }
543
+ let mtype = MessageType :: from_bytes ( data) ?;
544
+ let mlength = BigEndian :: read_u16 ( & data[ 2 ..] ) ;
545
+ let tid = BigEndian :: read_u128 ( & data[ 4 ..] ) ;
546
+ let cookie = ( tid >> 96 ) as u32 ;
547
+ if cookie != MAGIC_COOKIE {
548
+ warn ! (
549
+ "malformed cookie constant {:?} != stored data {:?}" ,
550
+ MAGIC_COOKIE , cookie
551
+ ) ;
552
+ return Err ( StunParseError :: NotStun ) ;
553
+ }
554
+
555
+ Ok ( Self {
556
+ mtype,
557
+ transaction_id : tid. into ( ) ,
558
+ length : mlength,
559
+ } )
560
+ }
561
+
562
+ /// The number of bytes of content in this [`MessageHeader`]. Adding both `data_length()`
563
+ /// and [`MessageHeader::LENGTH`] will result in the size of the complete STUN message.
564
+ pub fn data_length ( & self ) -> u16 {
565
+ self . length
566
+ }
567
+
568
+ /// The [`TransactionId`] of this [`MessageHeader`]
569
+ pub fn transaction_id ( & self ) -> TransactionId {
570
+ self . transaction_id
571
+ }
572
+
573
+ /// The [`MessageType`] of this [`MessageHeader`]
574
+ pub fn get_type ( & self ) -> MessageType {
575
+ self . mtype
576
+ }
577
+ }
578
+
511
579
/// The structure that encapsulates the entirety of a STUN message
512
580
///
513
581
/// Contains the [`MessageType`], a transaction ID, and a list of STUN
@@ -788,40 +856,23 @@ impl<'a> Message<'a> {
788
856
pub fn from_bytes ( data : & ' a [ u8 ] ) -> Result < Self , StunParseError > {
789
857
let orig_data = data;
790
858
791
- if data. len ( ) < 20 {
792
- // always at least 20 bytes long
793
- debug ! ( "messsage data is too short {} < 20" , data. len( ) ) ;
794
- return Err ( StunParseError :: Truncated {
795
- expected : 20 ,
796
- actual : data. len ( ) ,
797
- } ) ;
798
- }
799
- let _mtype = MessageType :: try_from ( data) ?;
800
- let mlength = BigEndian :: read_u16 ( & data[ 2 ..] ) as usize ;
801
- if mlength + 20 > data. len ( ) {
859
+ let header = MessageHeader :: from_bytes ( data) ?;
860
+ let mlength = header. data_length ( ) as usize ;
861
+ if mlength + MessageHeader :: LENGTH > data. len ( ) {
802
862
// mlength + header
803
863
warn ! (
804
864
"malformed advertised size {:?} and data size {:?} don't match" ,
805
865
mlength + 20 ,
806
866
data. len( )
807
867
) ;
808
868
return Err ( StunParseError :: Truncated {
809
- expected : mlength + 20 ,
869
+ expected : mlength + MessageHeader :: LENGTH ,
810
870
actual : data. len ( ) ,
811
871
} ) ;
812
872
}
813
- let tid = BigEndian :: read_u128 ( & data[ 4 ..] ) ;
814
- let cookie = ( tid >> 96 ) as u32 ;
815
- if cookie != MAGIC_COOKIE {
816
- warn ! (
817
- "malformed cookie constant {:?} != stored data {:?}" ,
818
- MAGIC_COOKIE , cookie
819
- ) ;
820
- return Err ( StunParseError :: NotStun ) ;
821
- }
822
873
823
- let mut data_offset = 20 ;
824
- let mut data = & data[ 20 ..] ;
874
+ let mut data_offset = MessageHeader :: LENGTH ;
875
+ let mut data = & data[ MessageHeader :: LENGTH ..] ;
825
876
let mut seen_message_integrity = false ;
826
877
let mut seen_fingerprint = false ;
827
878
while !data. is_empty ( ) {
@@ -880,7 +931,7 @@ impl<'a> Message<'a> {
880
931
let mut fingerprint_data = orig_data[ ..data_offset] . to_vec ( ) ;
881
932
BigEndian :: write_u16 (
882
933
& mut fingerprint_data[ 2 ..4 ] ,
883
- ( data_offset + padded_len - 20 ) as u16 ,
934
+ ( data_offset + padded_len - MessageHeader :: LENGTH ) as u16 ,
884
935
) ;
885
936
let calculated_fingerprint = Fingerprint :: compute ( & fingerprint_data) ;
886
937
if & calculated_fingerprint != msg_fingerprint {
@@ -949,9 +1000,9 @@ impl<'a> Message<'a> {
949
1000
// find the location of the original MessageIntegrity attribute: XXX: maybe encode this into
950
1001
// the attribute instead?
951
1002
let data = self . data ;
952
- debug_assert ! ( data. len( ) >= 20 ) ;
953
- let mut data = & data[ 20 ..] ;
954
- let mut data_offset = 20 ;
1003
+ debug_assert ! ( data. len( ) >= MessageHeader :: LENGTH ) ;
1004
+ let mut data = & data[ MessageHeader :: LENGTH ..] ;
1005
+ let mut data_offset = MessageHeader :: LENGTH ;
955
1006
while !data. is_empty ( ) {
956
1007
let attr = RawAttribute :: from_bytes ( data) ?;
957
1008
if algo == IntegrityAlgorithm :: Sha1 && attr. get_type ( ) == MessageIntegrity :: TYPE {
@@ -962,7 +1013,10 @@ impl<'a> Message<'a> {
962
1013
// but with a length field including the MESSAGE_INTEGRITY attribute...
963
1014
let key = credentials. make_hmac_key ( ) ;
964
1015
let mut hmac_data = self . data [ ..data_offset] . to_vec ( ) ;
965
- BigEndian :: write_u16 ( & mut hmac_data[ 2 ..4 ] , data_offset as u16 + 24 - 20 ) ;
1016
+ BigEndian :: write_u16 (
1017
+ & mut hmac_data[ 2 ..4 ] ,
1018
+ data_offset as u16 + 24 - MessageHeader :: LENGTH as u16 ,
1019
+ ) ;
966
1020
return MessageIntegrity :: verify (
967
1021
& hmac_data,
968
1022
& key,
@@ -980,7 +1034,7 @@ impl<'a> Message<'a> {
980
1034
let mut hmac_data = self . data [ ..data_offset] . to_vec ( ) ;
981
1035
BigEndian :: write_u16 (
982
1036
& mut hmac_data[ 2 ..4 ] ,
983
- data_offset as u16 + attr. length ( ) + 4 - 20 ,
1037
+ data_offset as u16 + attr. length ( ) + 4 - MessageHeader :: LENGTH as u16 ,
984
1038
) ;
985
1039
return MessageIntegritySha256 :: verify ( & hmac_data, & key, & msg_hmac) ;
986
1040
}
@@ -1067,7 +1121,7 @@ impl<'a> Message<'a> {
1067
1121
pub fn iter_attributes ( & self ) -> impl Iterator < Item = RawAttribute > {
1068
1122
MessageAttributesIter {
1069
1123
data : self . data ,
1070
- data_i : 20 ,
1124
+ data_i : MessageHeader :: LENGTH ,
1071
1125
seen_message_integrity : false ,
1072
1126
}
1073
1127
}
@@ -1344,9 +1398,9 @@ impl<'a> MessageBuilder<'a> {
1344
1398
for attr in & self . attributes {
1345
1399
attr_size += padded_attr_size ( attr) ;
1346
1400
}
1347
- let mut ret = Vec :: with_capacity ( 20 + attr_size) ;
1401
+ let mut ret = Vec :: with_capacity ( MessageHeader :: LENGTH + attr_size) ;
1348
1402
ret. extend ( self . msg_type . to_bytes ( ) ) ;
1349
- ret. resize ( 20 , 0 ) ;
1403
+ ret. resize ( MessageHeader :: LENGTH , 0 ) ;
1350
1404
let transaction: u128 = self . transaction_id . into ( ) ;
1351
1405
let tid = ( MAGIC_COOKIE as u128 ) << 96 | transaction & 0xffff_ffff_ffff_ffff_ffff_ffff ;
1352
1406
BigEndian :: write_u128 ( & mut ret[ 4 ..20 ] , tid) ;
0 commit comments