Skip to main content

hickory_proto/serialize/txt/
mod.rs

1/*
2 * Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com>
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! Text serialization types
18
19use alloc::string::ToString;
20use core::str::FromStr;
21
22mod errors;
23pub use errors::{LexerError, ParseError, ParseResult};
24
25#[cfg(feature = "__dnssec")]
26pub mod trust_anchor;
27
28#[cfg(feature = "std")]
29mod zone;
30#[cfg(feature = "std")]
31pub use zone::Parser;
32
33mod zone_lex;
34pub(crate) use zone_lex::Lexer;
35pub use zone_lex::Token;
36
37/// parses the string following the rules from:
38///  <https://tools.ietf.org/html/rfc2308> (NXCaching RFC) and
39///  <https://www.zytrax.com/books/dns/apa/time.html>
40///
41/// default is seconds
42/// #s = seconds = # x 1 seconds (really!)
43/// #m = minutes = # x 60 seconds
44/// #h = hours   = # x 3600 seconds
45/// #d = day     = # x 86400 seconds
46/// #w = week    = # x 604800 seconds
47///
48/// returns the result of the parsing or and error
49///
50/// # Example
51/// ```
52/// use hickory_proto::serialize::txt::parse_ttl;
53///
54/// assert_eq!(parse_ttl("0").unwrap(),  0);
55/// assert!(parse_ttl("s").is_err());
56/// assert!(parse_ttl("").is_err());
57/// assert_eq!(parse_ttl("0s").unwrap(), 0);
58/// assert_eq!(parse_ttl("1").unwrap(),  1);
59/// assert_eq!(parse_ttl("1S").unwrap(), 1);
60/// assert_eq!(parse_ttl("1s").unwrap(), 1);
61/// assert_eq!(parse_ttl("1M").unwrap(), 60);
62/// assert_eq!(parse_ttl("1m").unwrap(), 60);
63/// assert_eq!(parse_ttl("1H").unwrap(), 3600);
64/// assert_eq!(parse_ttl("1h").unwrap(), 3600);
65/// assert_eq!(parse_ttl("1D").unwrap(), 86400);
66/// assert_eq!(parse_ttl("1d").unwrap(), 86400);
67/// assert_eq!(parse_ttl("1W").unwrap(), 604800);
68/// assert_eq!(parse_ttl("1w").unwrap(), 604800);
69/// assert_eq!(parse_ttl("1s2d3w4h2m").unwrap(), 1+2*86400+3*604800+4*3600+2*60);
70/// assert_eq!(parse_ttl("3w3w").unwrap(), 3*604800+3*604800);
71/// assert!(parse_ttl("7102w").is_err());
72/// ```
73pub fn parse_ttl(ttl_str: &str) -> ParseResult<u32> {
74    if ttl_str.is_empty() {
75        return Err(ParseError::ParseTime(ttl_str.to_string()));
76    }
77
78    let (mut state, mut value) = (None, 0_u32);
79    for (i, c) in ttl_str.char_indices() {
80        let start = match (state, c) {
81            (None, '0'..='9') => {
82                state = Some(i);
83                continue;
84            }
85            (Some(_), '0'..='9') => continue,
86            (Some(start), 'S' | 's' | 'M' | 'm' | 'H' | 'h' | 'D' | 'd' | 'W' | 'w') => start,
87            _ => return Err(ParseError::ParseTime(ttl_str.to_string())),
88        };
89
90        // All allowed chars are ASCII, so using char indexes to slice &[u8] is OK
91        let number = u32::from_str(&ttl_str[start..i])
92            .map_err(|_| ParseError::ParseTime(ttl_str.to_string()))?;
93
94        let multiplier = match c {
95            'S' | 's' => 1,
96            'M' | 'm' => 60,
97            'H' | 'h' => 3_600,
98            'D' | 'd' => 86_400,
99            'W' | 'w' => 604_800,
100            _ => unreachable!(),
101        };
102
103        value = number
104            .checked_mul(multiplier)
105            .and_then(|add| value.checked_add(add))
106            .ok_or_else(|| ParseError::ParseTime(ttl_str.to_string()))?;
107
108        state = None;
109    }
110
111    if let Some(start) = state {
112        // All allowed chars are ASCII, so using char indexes to slice &[u8] is OK
113        let number = u32::from_str(&ttl_str[start..])
114            .map_err(|_| ParseError::ParseTime(ttl_str.to_string()))?;
115        value = value
116            .checked_add(number)
117            .ok_or_else(|| ParseError::ParseTime(ttl_str.to_string()))?;
118    }
119
120    Ok(value)
121}