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
34pub use std::net::Ipv4Addr;
35use std::{fmt, net::AddrParseError, ops::Deref, str};
36
37#[cfg(feature = "serde-config")]
38use serde::{Deserialize, Serialize};
39
40use crate::{
41    error::*,
42    rr::{RData, RecordData, RecordType},
43    serialize::binary::*,
44};
45
46/// The DNS A record type, an IPv4 address
47#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
48#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
49pub struct A(pub Ipv4Addr);
50
51impl A {
52    /// Construct a new AAAA record with the 32 bits of IPv4 address
53    pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Self {
54        Self(Ipv4Addr::new(a, b, c, d))
55    }
56}
57
58impl RecordData for A {
59    fn try_from_rdata(data: RData) -> Result<Self, crate::rr::RData> {
60        match data {
61            RData::A(ipv4) => Ok(ipv4),
62            _ => Err(data),
63        }
64    }
65
66    fn try_borrow(data: &RData) -> Option<&Self> {
67        match data {
68            RData::A(ipv4) => Some(ipv4),
69            _ => None,
70        }
71    }
72
73    fn record_type(&self) -> RecordType {
74        RecordType::A
75    }
76
77    fn into_rdata(self) -> RData {
78        RData::A(self)
79    }
80}
81
82impl BinEncodable for A {
83    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
84        let segments = self.octets();
85
86        encoder.emit(segments[0])?;
87        encoder.emit(segments[1])?;
88        encoder.emit(segments[2])?;
89        encoder.emit(segments[3])?;
90        Ok(())
91    }
92}
93
94impl<'r> BinDecodable<'r> for A {
95    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
96        // TODO: would this be more efficient as a single u32 read?
97        Ok(Ipv4Addr::new(
98            decoder.pop()?.unverified(/*valid as any u8*/),
99            decoder.pop()?.unverified(/*valid as any u8*/),
100            decoder.pop()?.unverified(/*valid as any u8*/),
101            decoder.pop()?.unverified(/*valid as any u8*/),
102        )
103        .into())
104    }
105}
106
107/// Read the RData from the given Decoder
108#[deprecated(note = "use the BinDecodable::read method instead")]
109pub fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<A> {
110    <A as BinDecodable>::read(decoder)
111}
112
113/// Write the RData from the given Decoder
114#[deprecated(note = "use the BinEncodable::emit method instead")]
115pub fn emit(encoder: &mut BinEncoder<'_>, address: Ipv4Addr) -> ProtoResult<()> {
116    BinEncodable::emit(&A::from(address), encoder)
117}
118
119impl From<Ipv4Addr> for A {
120    fn from(a: Ipv4Addr) -> Self {
121        Self(a)
122    }
123}
124
125impl From<A> for Ipv4Addr {
126    fn from(a: A) -> Self {
127        a.0
128    }
129}
130
131impl Deref for A {
132    type Target = Ipv4Addr;
133
134    fn deref(&self) -> &Self::Target {
135        &self.0
136    }
137}
138
139impl fmt::Display for A {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
141        write!(f, "{}", self.0)
142    }
143}
144
145impl str::FromStr for A {
146    type Err = AddrParseError;
147    fn from_str(s: &str) -> Result<Self, AddrParseError> {
148        Ipv4Addr::from_str(s).map(From::from)
149    }
150}
151
152#[cfg(test)]
153mod mytests {
154    use std::str::FromStr;
155
156    use super::*;
157    use crate::serialize::binary::bin_tests::{test_emit_data_set, test_read_data_set};
158
159    fn get_data() -> Vec<(A, Vec<u8>)> {
160        vec![
161            (A::from_str("0.0.0.0").unwrap(), vec![0, 0, 0, 0]), // base case
162            (A::from_str("1.0.0.0").unwrap(), vec![1, 0, 0, 0]),
163            (A::from_str("0.1.0.0").unwrap(), vec![0, 1, 0, 0]),
164            (A::from_str("0.0.1.0").unwrap(), vec![0, 0, 1, 0]),
165            (A::from_str("0.0.0.1").unwrap(), vec![0, 0, 0, 1]),
166            (A::from_str("127.0.0.1").unwrap(), vec![127, 0, 0, 1]),
167            (
168                A::from_str("192.168.64.32").unwrap(),
169                vec![192, 168, 64, 32],
170            ),
171        ]
172    }
173
174    #[test]
175    fn test_parse() {
176        test_read_data_set(get_data(), |ref mut d| A::read(d));
177    }
178
179    #[test]
180    fn test_write_to() {
181        test_emit_data_set(get_data(), |ref mut e, d| d.emit(e));
182    }
183}