Skip to main content

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