smtp_codec/parse/
address.rs

1//! 4.1.3.  Address Literals (RFC 5321)
2
3use abnf_core::streaming::is_DIGIT;
4use nom::{
5    branch::alt,
6    bytes::streaming::{tag, tag_no_case, take_while1, take_while_m_n},
7    character::is_hex_digit,
8    combinator::{map_res, opt, recognize},
9    multi::{count, many_m_n},
10    sequence::{delimited, tuple},
11    IResult,
12};
13
14use crate::parse::Ldh_str;
15
16/// address-literal = "[" (
17///                       IPv4-address-literal /
18///                       IPv6-address-literal /
19///                       General-address-literal
20///                   ) "]"
21///                     ; See Section 4.1.3
22pub fn address_literal(input: &[u8]) -> IResult<&[u8], &str> {
23    delimited(
24        tag(b"["),
25        map_res(
26            alt((
27                IPv4_address_literal,
28                IPv6_address_literal,
29                General_address_literal,
30            )),
31            std::str::from_utf8,
32        ),
33        tag(b"]"),
34    )(input)
35}
36
37/// IPv4-address-literal = Snum 3("."  Snum)
38pub fn IPv4_address_literal(input: &[u8]) -> IResult<&[u8], &[u8]> {
39    let parser = tuple((Snum, count(tuple((tag(b"."), Snum)), 3)));
40
41    let (remaining, parsed) = recognize(parser)(input)?;
42
43    Ok((remaining, parsed))
44}
45
46/// Representing a decimal integer value in the range 0 through 255
47///
48/// Snum = 1*3DIGIT
49pub fn Snum(input: &[u8]) -> IResult<&[u8], &[u8]> {
50    take_while_m_n(1, 3, is_DIGIT)(input)
51}
52
53/// IPv6-address-literal = "IPv6:" IPv6-addr
54pub fn IPv6_address_literal(input: &[u8]) -> IResult<&[u8], &[u8]> {
55    let parser = tuple((tag_no_case(b"IPv6:"), IPv6_addr));
56
57    let (remaining, parsed) = recognize(parser)(input)?;
58
59    Ok((remaining, parsed))
60}
61
62/// IPv6-addr = IPv6-full / IPv6-comp / IPv6v4-full / IPv6v4-comp
63pub fn IPv6_addr(input: &[u8]) -> IResult<&[u8], &[u8]> {
64    let parser = alt((IPv6_full, IPv6_comp, IPv6v4_full, IPv6v4_comp));
65
66    let (remaining, parsed) = recognize(parser)(input)?;
67
68    Ok((remaining, parsed))
69}
70
71/// IPv6-full = IPv6-hex 7(":" IPv6-hex)
72pub fn IPv6_full(input: &[u8]) -> IResult<&[u8], &[u8]> {
73    let parser = tuple((IPv6_hex, count(tuple((tag(b":"), IPv6_hex)), 7)));
74
75    let (remaining, parsed) = recognize(parser)(input)?;
76
77    Ok((remaining, parsed))
78}
79
80/// IPv6-hex = 1*4HEXDIG
81pub fn IPv6_hex(input: &[u8]) -> IResult<&[u8], &[u8]> {
82    take_while_m_n(1, 4, is_hex_digit)(input)
83}
84
85/// The "::" represents at least 2 16-bit groups of zeros.
86/// No more than 6 groups in addition to the "::" may be present.
87///
88/// IPv6-comp = [IPv6-hex *5(":" IPv6-hex)] "::" [IPv6-hex *5(":" IPv6-hex)]
89pub fn IPv6_comp(input: &[u8]) -> IResult<&[u8], &[u8]> {
90    let parser = tuple((
91        opt(tuple((
92            IPv6_hex,
93            many_m_n(0, 5, tuple((tag(b":"), IPv6_hex))),
94        ))),
95        tag(b"::"),
96        opt(tuple((
97            IPv6_hex,
98            many_m_n(0, 5, tuple((tag(b":"), IPv6_hex))),
99        ))),
100    ));
101
102    let (remaining, parsed) = recognize(parser)(input)?;
103
104    Ok((remaining, parsed))
105}
106
107/// IPv6v4-full = IPv6-hex 5(":" IPv6-hex) ":" IPv4-address-literal
108pub fn IPv6v4_full(input: &[u8]) -> IResult<&[u8], &[u8]> {
109    let parser = tuple((
110        IPv6_hex,
111        count(tuple((tag(b":"), IPv6_hex)), 5),
112        tag(b":"),
113        IPv4_address_literal,
114    ));
115
116    let (remaining, parsed) = recognize(parser)(input)?;
117
118    Ok((remaining, parsed))
119}
120
121/// The "::" represents at least 2 16-bit groups of zeros.
122/// No more than 4 groups in addition to the "::" and IPv4-address-literal may be present.
123///
124/// IPv6v4-comp = [IPv6-hex *3(":" IPv6-hex)] "::"
125///               [IPv6-hex *3(":" IPv6-hex) ":"]
126///               IPv4-address-literal
127pub fn IPv6v4_comp(input: &[u8]) -> IResult<&[u8], &[u8]> {
128    let parser = tuple((
129        opt(tuple((
130            IPv6_hex,
131            many_m_n(0, 3, tuple((tag(b":"), IPv6_hex))),
132        ))),
133        tag(b"::"),
134        opt(tuple((
135            IPv6_hex,
136            many_m_n(0, 3, tuple((tag(b":"), IPv6_hex))),
137            tag(b":"),
138        ))),
139        IPv4_address_literal,
140    ));
141
142    let (remaining, parsed) = recognize(parser)(input)?;
143
144    Ok((remaining, parsed))
145}
146
147/// General-address-literal = Standardized-tag ":" 1*dcontent
148pub fn General_address_literal(input: &[u8]) -> IResult<&[u8], &[u8]> {
149    let parser = tuple((Standardized_tag, tag(b":"), take_while1(is_dcontent)));
150
151    let (remaining, parsed) = recognize(parser)(input)?;
152
153    Ok((remaining, parsed))
154}
155
156/// Standardized-tag MUST be specified in a Standards-Track RFC and registered with IANA
157///
158/// Standardized-tag = Ldh-str
159pub fn Standardized_tag(input: &[u8]) -> IResult<&[u8], &[u8]> {
160    Ldh_str(input)
161}
162
163/// Printable US-ASCII excl. "[", "\", "]"
164///
165/// dcontent = %d33-90 / %d94-126
166pub fn is_dcontent(byte: u8) -> bool {
167    matches!(byte, 33..=90 | 94..=126)
168}