domain/base/opt/
subnet.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
//! EDNS option for carrying client subnet information.
//!
//! The option in this module – [`ClientSubnet`] – can be used by a resolver
//! to include information about the network a query originated from in its
//! own query to an authoritative server so it can tailor its response for
//! that network.
//!
//! The option is defined in [RFC 7871](https://tools.ietf.org/html/rfc7871)
//! which also includes some guidance on its use.

use core::fmt;
use super::super::iana::OptionCode;
use super::super::message_builder::OptBuilder;
use super::super::net::IpAddr;
use super::super::wire::{Compose, Composer, FormError, ParseError};
use super::{Opt, OptData, ComposeOptData, ParseOptData};
use octseq::builder::OctetsBuilder;
use octseq::octets::Octets;
use octseq::parse::Parser;

//------------ ClientSubnet --------------------------------------------------

/// Option data for the client subnet option.
///
/// This option allows a resolver to include information about the network a
/// query originated from. This information can then be used by an
/// authoritative server to provide the best response for this network.
///
/// The option identifies the network through an address prefix, i.e., an
/// IP address of which only a certain number of left-side bits is
/// interpreted. The option uses two such numbers: The _source prefix length_
/// is the number of bits provided by the client when describing its network
/// and the _scope prefix length_ is the number of bits that the server
/// considered when providing the answer. The scope prefix length is zero
/// in a query. It can be used by a caching resolver to cache multiple
/// responses for different client subnets.
///
/// The option is defined in [RFC 7871](https://tools.ietf.org/html/rfc7871)
/// which also includes some guidance on its use.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ClientSubnet {
    /// The source prefix length.
    source_prefix_len: u8,

    /// The scope prefix length.
    scope_prefix_len: u8,

    /// The address.
    addr: IpAddr,
}

impl ClientSubnet {
    /// The option code for this option.
    pub(super) const CODE: OptionCode = OptionCode::CLIENT_SUBNET;
    
    /// Creates a new client subnet value.
    ///
    /// The function is very forgiving regarding the arguments and corrects
    /// illegal values. That is, it limit the prefix lengths given to a number
    /// meaningful for the address family. It will also set all bits not
    /// covered by the source prefix length in the address to zero.
    #[must_use]
    pub fn new(
        source_prefix_len: u8,
        scope_prefix_len: u8,
        addr: IpAddr,
    ) -> ClientSubnet {
        let source_prefix_len = normalize_prefix_len(addr, source_prefix_len);
        let scope_prefix_len = normalize_prefix_len(addr, scope_prefix_len);
        let (addr, _) = addr_apply_mask(addr, source_prefix_len);

        ClientSubnet {
            source_prefix_len,
            scope_prefix_len,
            addr,
        }
    }

    /// Returns the source prefix length.
    ///
    /// The source prefix length is the prefix length as specified by the
    /// client in a query.
    #[must_use]
    pub fn source_prefix_len(&self) -> u8 {
        self.source_prefix_len
    }

    /// Returns the scope prefix length.
    ///
    /// The scope prefix length is the prefix length used by the server for
    /// its answer.
    #[must_use]
    pub fn scope_prefix_len(&self) -> u8 {
        self.scope_prefix_len
    }

    /// Returns the address.
    #[must_use]
    pub fn addr(&self) -> IpAddr {
        self.addr
    }

    /// Parses a value from its wire format.
    pub fn parse<Octs: AsRef<[u8]>>(
        parser: &mut Parser<Octs>
    ) -> Result<Self, ParseError> {
        const ERR_ADDR_LEN: &str = "invalid address length in client \
                                    subnet option";

        let family = parser.parse_u16_be()?;
        let source_prefix_len = parser.parse_u8()?;
        let scope_prefix_len = parser.parse_u8()?;

        // https://tools.ietf.org/html/rfc7871#section-6
        //
        // | ADDRESS, variable number of octets, contains either an IPv4 or
        // | IPv6 address, depending on FAMILY, which MUST be truncated to
        // | the number of bits indicated by the SOURCE PREFIX-LENGTH field,
        // | padding with 0 bits to pad to the end of the last octet needed.
        let prefix_bytes = prefix_bytes(source_prefix_len);

        let addr = match family {
            1 => {
                let mut buf = [0; 4];
                if prefix_bytes > buf.len() {
                    return Err(ParseError::form_error(ERR_ADDR_LEN));
                }
                parser
                    .parse_buf(&mut buf[..prefix_bytes])
                    .map_err(|_| ParseError::form_error(ERR_ADDR_LEN))?;

                if parser.remaining() != 0 {
                    return Err(ParseError::form_error(ERR_ADDR_LEN));
                }

                IpAddr::from(buf)
            }
            2 => {
                let mut buf = [0; 16];
                if prefix_bytes > buf.len() {
                    return Err(ParseError::form_error(ERR_ADDR_LEN));
                }
                parser
                    .parse_buf(&mut buf[..prefix_bytes])
                    .map_err(|_| ParseError::form_error(ERR_ADDR_LEN))?;

                if parser.remaining() != 0 {
                    return Err(ParseError::form_error(ERR_ADDR_LEN));
                }

                IpAddr::from(buf)
            }
            _ => {
                return Err(FormError::new(
                    "invalid client subnet address family",
                )
                .into())
            }
        };

        // If the trailing bits beyond prefix length are not zero,
        // return form error.
        let (addr, modified) = addr_apply_mask(addr, source_prefix_len);
        if modified {
            return Err(ParseError::form_error(ERR_ADDR_LEN));
        }

        // no need to pass the normalizer in constructor again
        Ok(ClientSubnet {
            source_prefix_len,
            scope_prefix_len,
            addr,
        })
    }

    /// Placeholder for unnecessary octets conversion.
    ///
    /// This method only exists for the `AllOptData` macro.
    pub(super) fn try_octets_from<E>(src: Self) -> Result<Self, E> {
        Ok(src)
    }
}

//--- OptData

impl OptData for ClientSubnet {
    fn code(&self) -> OptionCode {
        OptionCode::CLIENT_SUBNET
    }
}

impl<'a, Octs: AsRef<[u8]>> ParseOptData<'a, Octs> for ClientSubnet {
    fn parse_option(
        code: OptionCode,
        parser: &mut Parser<'a, Octs>,
    ) -> Result<Option<Self>, ParseError> {
        if code == OptionCode::CLIENT_SUBNET {
            Self::parse(parser).map(Some)
        }
        else {
            Ok(None)
        }
    }
}

impl ComposeOptData for ClientSubnet {
    fn compose_len(&self) -> u16 {
        u16::try_from(prefix_bytes(self.source_prefix_len)).unwrap() + 4
    }

    fn compose_option<Target: OctetsBuilder + ?Sized>(
        &self, target: &mut Target
    ) -> Result<(), Target::AppendError> {
        let prefix_bytes = prefix_bytes(self.source_prefix_len);
        match self.addr {
            IpAddr::V4(addr) => {
                1u16.compose(target)?;
                self.source_prefix_len.compose(target)?;
                self.scope_prefix_len.compose(target)?;
                let array = addr.octets();
                assert!(prefix_bytes <= array.len());
                target.append_slice(&array[..prefix_bytes])
            }
            IpAddr::V6(addr) => {
                2u16.compose(target)?;
                self.source_prefix_len.compose(target)?;
                self.scope_prefix_len.compose(target)?;
                let array = addr.octets();
                assert!(prefix_bytes <= array.len());
                target.append_slice(&array[..prefix_bytes])
            }
        }
    }
}

//--- Display

impl fmt::Display for ClientSubnet {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.addr {
            IpAddr::V4(a) => {
                if self.scope_prefix_len != 0 {
                    write!(f, "{}/{}/{}", a, self.source_prefix_len,
                        self.scope_prefix_len)?;
                } else {
                    write!(f, "{}/{}", a, self.source_prefix_len)?;
                }
            }
            IpAddr::V6(a) => {
                if self.scope_prefix_len != 0 {
                    write!(f, "{}/{}/{}", a, self.source_prefix_len,
                        self.scope_prefix_len)?;
                } else {
                    write!(f, "{}/{}", a, self.source_prefix_len)?;
                }
            }
        }

        Ok(())
    }
}

//--- Extended Opt and OptBuilder

impl<Octs: Octets> Opt<Octs> {
    /// Returns the first client subnet option if present.
    ///
    /// This option allows a resolver to include information about the
    /// network a query originated from. This information can then be
    /// used by an authoritative server to provide the best response for
    /// this network.
    pub fn client_subnet(&self) -> Option<ClientSubnet> {
        self.first()
    }
}

impl<'a, Target: Composer> OptBuilder<'a, Target> {
    pub fn client_subnet(
        &mut self,
        source_prefix_len: u8,
        scope_prefix_len: u8,
        addr: IpAddr,
    ) -> Result<(), Target::AppendError> {
        self.push(
            &ClientSubnet::new(source_prefix_len, scope_prefix_len, addr)
        )
    }
}

//------------ Helper Functions ----------------------------------------------

/// Returns the number of bytes needed for a prefix of a given length
fn prefix_bytes(bits: u8) -> usize {
    (usize::from(bits) + 7) / 8
}

/// Only keeps the left-most `mask` bits and zeros out the rest.
///
/// Returns whether the buffer has been modified.
fn apply_bit_mask(buf: &mut [u8], mask: usize) -> bool {
    let mut modified = false;

    // skip full bytes covered by prefix length
    let mut p = mask / 8;
    if p >= buf.len() {
        return modified;
    }

    // clear extra bits in a byte
    let bits = mask % 8;
    if bits != 0 {
        if buf[p].trailing_zeros() < (8 - bits) as u32 {
            buf[p] &= 0xff << (8 - bits);
            modified = true;
        }
        p += 1;
    }

    // clear the rest bytes
    while p < buf.len() {
        if buf[p] != 0 {
            buf[p] = 0;
            modified = true;
        }
        p += 1;
    }

    modified
}

/// Zeros out unused bits in a address prefix of the given length
///
/// Returns the new address and whether it was changed.
fn addr_apply_mask(addr: IpAddr, len: u8) -> (IpAddr, bool) {
    match addr {
        IpAddr::V4(a) => {
            let mut array = a.octets();
            let m = apply_bit_mask(&mut array, len as usize);
            (array.into(), m)
        }
        IpAddr::V6(a) => {
            let mut array = a.octets();
            let m = apply_bit_mask(&mut array, len as usize);
            (array.into(), m)
        }
    }
}

/// Limits a prefix length for the given address.
fn normalize_prefix_len(addr: IpAddr, len: u8) -> u8 {
    let max = match addr {
        IpAddr::V4(_) => 32,
        IpAddr::V6(_) => 128,
    };

    core::cmp::min(len, max)
}

//============ Testing =======================================================

#[cfg(all(test, feature="std", feature = "bytes"))]
mod tests {
    use super::*;
    use super::super::test::test_option_compose_parse;
    use octseq::builder::infallible;
    use std::vec::Vec;
    use core::str::FromStr;

    macro_rules! check {
        ($name:ident, $addr:expr, $prefix:expr, $exp:expr, $ok:expr) => {
            #[test]
            fn $name() {
                let addr = $addr.parse().unwrap();
                let opt = ClientSubnet::new($prefix, 0, addr);
                assert_eq!(opt.addr(), $exp.parse::<IpAddr>().unwrap());

                // Check parse by mangling the addr in option to
                // generate maybe invalid buffer.
                let mut opt_ = opt.clone();
                opt_.addr = addr;
                let mut buf = Vec::new();

                infallible(opt_.compose_option(&mut buf));
                match ClientSubnet::parse(&mut Parser::from_ref(&buf)) {
                    Ok(v) => assert_eq!(opt, v),
                    Err(_) => assert!(!$ok),
                }
            }
        };
    }

    check!(prefix_at_boundary_v4, "192.0.2.0", 24, "192.0.2.0", true);
    check!(prefix_at_boundary_v6, "2001:db8::", 32, "2001:db8::", true);
    check!(prefix_no_truncation, "192.0.2.0", 23, "192.0.2.0", true);
    check!(prefix_need_truncation, "192.0.2.0", 22, "192.0.0.0", false);
    check!(prefix_min, "192.0.2.0", 0, "0.0.0.0", true);
    check!(prefix_max, "192.0.2.0", 32, "192.0.2.0", true);
    check!(prefix_too_long, "192.0.2.0", 100, "192.0.2.0", false);
    
    #[test]
    #[allow(clippy::redundant_closure)] // lifetimes ...
    fn client_subnet_compose_parse() {
        test_option_compose_parse(
            &ClientSubnet::new(4, 6, IpAddr::from_str("127.0.0.1").unwrap()),
            |parser| ClientSubnet::parse(parser)
        );
    }
}