1
+ use std:: convert:: TryInto ;
2
+
3
+ #[ derive( Debug ) ]
4
+ pub enum Opt < ' a > {
5
+ Malformed ( u8 , & ' a [ u8 ] ) ,
6
+ Unknown ( u8 , & ' a [ u8 ] ) ,
7
+ //Pad, // #0
8
+
9
+ /// Subnet mask, encoded as an address
10
+ SubnetMask ( [ u8 ; 4 ] ) ,
11
+ /// Timezone seconds east of UTC
12
+ TimeOffset ( i32 ) ,
13
+ /// A list of routers
14
+ Routers ( & ' a [ [ u8 ; 4 ] ] ) ,
15
+ /// A list of ?NTP time servers
16
+ TimeServers ( & ' a [ [ u8 ; 4 ] ] ) ,
17
+ /// A list of IEN-116 name servers
18
+ NameServersIen116 ( & ' a [ [ u8 ; 4 ] ] ) ,
19
+ /// #6 A list of DNS name servers
20
+ NameServersDns ( & ' a [ [ u8 ; 4 ] ] ) ,
21
+
22
+ /// #12 Client hostname
23
+ HostName ( & ' a [ u8 ] ) ,
24
+ /// #15 Domain Name
25
+ DomainName ( & ' a [ u8 ] ) ,
26
+
27
+ /// #42 Vendor-specific
28
+ VendorSpecific ( & ' a [ u8 ] ) ,
29
+
30
+ /// #50 Allows client to request a specific IP address
31
+ RequestedIpAddress ( [ u8 ; 4 ] ) ,
32
+
33
+ /// #52 Specifies that `file` or `sname` (or both) also contain options
34
+ ///
35
+ /// 1 = only `file`
36
+ /// 2 = only `sname`
37
+ /// 3 = both
38
+ OptionOverload ( u8 ) ,
39
+
40
+ /// #53
41
+ DhcpMessageType ( u8 ) ,
42
+
43
+ /// #54 IPv4 address of the server that sent this offer/ack
44
+ ServerIdentifier ( [ u8 ; 4 ] ) ,
45
+
46
+ /// #56 - Human-readable (ASCII) text error message for DHCPNAK
47
+ Message ( & ' a [ u8 ] ) ,
48
+
49
+ /// #61 - An opaque blob client identifier
50
+ ClientIdentifier ( & ' a [ u8 ] ) ,
51
+ }
52
+ macro_rules! enc_dec_option {
53
+ ( $in_data: ident ;
54
+ $(
55
+ $idx: literal $name: ident( $valname: ident ) : $dec: expr => $enc: expr ;
56
+ ) *
57
+ ) => {
58
+ impl <' a> Opt <' a> {
59
+ pub fn decode( code: u8 , $in_data: & ' a [ u8 ] ) -> Self {
60
+ match code {
61
+ 0 => unreachable!( ) ,
62
+ $(
63
+ $idx => if let Some ( v) = $dec { Opt :: $name( v) } else { Opt :: Malformed ( code, $in_data) } ,
64
+ ) *
65
+ _ => Opt :: Unknown ( code, $in_data)
66
+ }
67
+ }
68
+ pub fn encode( & self , mut push: impl FnMut ( u8 , & [ u8 ] ) ) {
69
+ fn flatten( data: & [ [ u8 ; 4 ] ] ) -> & [ u8 ] {
70
+ // SAFE: Same alignment, correct length
71
+ unsafe { :: core:: slice:: from_raw_parts( data. as_ptr( ) as * const u8 , data. len( ) * 4 ) }
72
+ }
73
+ match self {
74
+ Opt :: Malformed ( _op, _data) => {
75
+ // Ignore malformed data
76
+ }
77
+ Opt :: Unknown ( op, data) => push( * op, data) ,
78
+ $(
79
+ Opt :: $name( $valname) => push( $idx, $enc) ,
80
+ ) *
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+ fn get_u8_4 ( data : & [ u8 ] ) -> Option < & [ u8 ; 4 ] > {
87
+ data. try_into ( ) . ok ( )
88
+ }
89
+ fn get_u8_4_seq ( data : & [ u8 ] ) -> Option < & [ [ u8 ; 4 ] ] > {
90
+ if data. len ( ) % 4 == 0 {
91
+ // SAFE: Same alignment, and length is aligned to 4
92
+ Some ( unsafe { :: core:: slice:: from_raw_parts ( data. as_ptr ( ) as * const [ u8 ; 4 ] , data. len ( ) / 4 ) } )
93
+ }
94
+ else {
95
+ None
96
+ }
97
+ }
98
+ enc_dec_option ! { d;
99
+ // 0 Pad (not encoded)
100
+ 1 SubnetMask ( data) : get_u8_4( d) . copied( ) => data;
101
+ 2 TimeOffset ( ofs) : get_u8_4( d) . map( |v| i32 :: from_be_bytes( * v) ) => & ofs. to_le_bytes( ) ;
102
+ 3 Routers ( addrs) : get_u8_4_seq( d) => flatten( addrs) ;
103
+ 4 TimeServers ( addrs) : get_u8_4_seq( d) => flatten( addrs) ;
104
+ 5 NameServersIen116 ( addrs) : get_u8_4_seq( d) => flatten( addrs) ;
105
+ 6 NameServersDns ( addrs) : get_u8_4_seq( d) => flatten( addrs) ;
106
+ 12 HostName ( name) : Some ( d) => name;
107
+ 15 DomainName ( name) : Some ( d) => name;
108
+ 42 VendorSpecific ( data) : Some ( d) => data;
109
+ 50 RequestedIpAddress ( addr) : get_u8_4( d) . copied( ) => addr;
110
+ 52 OptionOverload ( v) : match d { & [ v] => Some ( v) , _ => None } => & [ * v] ;
111
+ 53 DhcpMessageType ( v) : match d { & [ v] => Some ( v) , _ => None } => & [ * v] ;
112
+ 54 ServerIdentifier ( data) : get_u8_4( d) . copied( ) => data;
113
+ 56 Message ( msg) : Some ( d) => msg;
114
+ 61 ClientIdentifier ( blob) : Some ( d) => blob;
115
+ // 255 End (not encoded)
116
+ }
117
+
118
+ #[ derive( Debug ) ] // TODO: Implement using clone+run
119
+ pub struct OptionsIter < ' a > ( pub & ' a [ u8 ] ) ;
120
+ impl < ' a > Iterator for OptionsIter < ' a > {
121
+ type Item = Opt < ' a > ;
122
+ fn next ( & mut self ) -> Option < Self :: Item > {
123
+ match self . 0 {
124
+ [ ] => None ,
125
+ // Padding: Handled specially
126
+ [ 0 , tail @ ..] => {
127
+ self . 0 = tail;
128
+ //Some(Opt::Pad)
129
+ self . next ( )
130
+ } ,
131
+ // End
132
+ [ 255 , ..] => None ,
133
+ [ _] => {
134
+ self . 0 = & [ ] ;
135
+ None
136
+ } ,
137
+ & [ code, len, ref tail @ ..] => {
138
+ let Some ( ( data, tail) ) = tail. split_at_checked ( len as usize ) else {
139
+ self . 0 = & [ ] ;
140
+ return None ;
141
+ } ;
142
+ self . 0 = tail;
143
+ Some ( Opt :: decode ( code, data) )
144
+ }
145
+ }
146
+ }
147
+ }
0 commit comments