hickory_proto/rr/rdata/
a.rs

1// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! IPv4 address record data
9//!
10//! [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
11//!
12//! ```text
13//! 3.4. Internet specific RRs
14//!
15//! 3.4.1. A RDATA format
16//!
17//!     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
18//!     |                    ADDRESS                    |
19//!     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
20//!
21//! where:
22//!
23//! ADDRESS         A 32 bit Internet address.
24//!
25//! Hosts that have multiple Internet addresses will have multiple A
26//! records.
27//!
28//! A records cause no additional section processing.  The RDATA section of
29//! an A line in a Zone File is an Internet address expressed as four
30//! decimal numbers separated by dots without any embedded spaces (e.g.,
31//! "10.2.0.52" or "192.0.5.6").
32//! ```
33
34#[cfg(not(feature = "std"))]
35use core::net::AddrParseError;
36use core::{fmt, ops::Deref, str};
37#[cfg(feature = "std")]
38use std::net::AddrParseError;
39
40#[cfg(not(feature = "std"))]
41pub use core::net::Ipv4Addr;
42#[cfg(feature = "std")]
43pub use std::net::Ipv4Addr;
44
45#[cfg(feature = "serde")]
46use serde::{Deserialize, Serialize};
47
48use crate::{
49    error::*,
50    rr::{RData, RecordData, RecordType},
51    serialize::binary::*,
52};
53
54/// The DNS A record type, an IPv4 address
55#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
56#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
57pub struct A(pub Ipv4Addr);
58
59impl A {
60    /// Construct a new AAAA record with the 32 bits of IPv4 address
61    pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Self {
62        Self(Ipv4Addr::new(a, b, c, d))
63    }
64}
65
66impl RecordData for A {
67    fn try_from_rdata(data: RData) -> Result<Self, crate::rr::RData> {
68        match data {
69            RData::A(ipv4) => Ok(ipv4),
70            _ => Err(data),
71        }
72    }
73
74    fn try_borrow(data: &RData) -> Option<&Self> {
75        match data {
76            RData::A(ipv4) => Some(ipv4),
77            _ => None,
78        }
79    }
80
81    fn record_type(&self) -> RecordType {
82        RecordType::A
83    }
84
85    fn into_rdata(self) -> RData {
86        RData::A(self)
87    }
88}
89
90impl BinEncodable for A {
91    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
92        let segments = self.octets();
93
94        encoder.emit(segments[0])?;
95        encoder.emit(segments[1])?;
96        encoder.emit(segments[2])?;
97        encoder.emit(segments[3])?;
98        Ok(())
99    }
100}
101
102impl<'r> BinDecodable<'r> for A {
103    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
104        // TODO: would this be more efficient as a single u32 read?
105        Ok(Ipv4Addr::new(
106            decoder.pop()?.unverified(/*valid as any u8*/),
107            decoder.pop()?.unverified(/*valid as any u8*/),
108            decoder.pop()?.unverified(/*valid as any u8*/),
109            decoder.pop()?.unverified(/*valid as any u8*/),
110        )
111        .into())
112    }
113}
114
115/// Read the RData from the given Decoder
116#[deprecated(note = "use the BinDecodable::read method instead")]
117pub fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<A> {
118    <A as BinDecodable>::read(decoder)
119}
120
121/// Write the RData from the given Decoder
122#[deprecated(note = "use the BinEncodable::emit method instead")]
123pub fn emit(encoder: &mut BinEncoder<'_>, address: Ipv4Addr) -> ProtoResult<()> {
124    BinEncodable::emit(&A::from(address), encoder)
125}
126
127impl From<Ipv4Addr> for A {
128    fn from(a: Ipv4Addr) -> Self {
129        Self(a)
130    }
131}
132
133impl From<A> for Ipv4Addr {
134    fn from(a: A) -> Self {
135        a.0
136    }
137}
138
139impl Deref for A {
140    type Target = Ipv4Addr;
141
142    fn deref(&self) -> &Self::Target {
143        &self.0
144    }
145}
146
147impl fmt::Display for A {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
149        write!(f, "{}", self.0)
150    }
151}
152
153impl str::FromStr for A {
154    type Err = AddrParseError;
155    fn from_str(s: &str) -> Result<Self, AddrParseError> {
156        Ipv4Addr::from_str(s).map(From::from)
157    }
158}
159
160#[cfg(test)]
161mod mytests {
162    use alloc::vec::Vec;
163
164    use super::*;
165    use crate::serialize::binary::bin_tests::{test_emit_data_set, test_read_data_set};
166
167    fn get_data() -> Vec<(A, Vec<u8>)> {
168        vec![
169            (A::from(Ipv4Addr::UNSPECIFIED), vec![0, 0, 0, 0]), // base case
170            (A::from(Ipv4Addr::new(1, 0, 0, 0)), vec![1, 0, 0, 0]),
171            (A::from(Ipv4Addr::new(0, 1, 0, 0)), vec![0, 1, 0, 0]),
172            (A::from(Ipv4Addr::new(0, 0, 1, 0)), vec![0, 0, 1, 0]),
173            (A::from(Ipv4Addr::new(0, 0, 0, 1)), vec![0, 0, 0, 1]),
174            (A::from(Ipv4Addr::LOCALHOST), vec![127, 0, 0, 1]),
175            (
176                A::from(Ipv4Addr::new(192, 168, 64, 32)),
177                vec![192, 168, 64, 32],
178            ),
179        ]
180    }
181
182    #[test]
183    fn test_parse() {
184        test_read_data_set(get_data(), |mut d| A::read(&mut d));
185    }
186
187    #[test]
188    fn test_write_to() {
189        test_emit_data_set(get_data(), |e, d| d.emit(e));
190    }
191}