From f152c844f640a8f793fc6cfea2e38304ca643c8b Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Wed, 3 Apr 2024 18:24:20 +0200 Subject: [PATCH] Make dname_from_addr public. --- src/base/name/builder.rs | 64 +++++++++++++++++++++++++++++++ src/base/name/dname.rs | 62 +++++++++++++++++++++++++++++- src/resolv/lookup/addr.rs | 81 ++------------------------------------- 3 files changed, 128 insertions(+), 79 deletions(-) diff --git a/src/base/name/builder.rs b/src/base/name/builder.rs index c6896a497..8ab4ead6d 100644 --- a/src/base/name/builder.rs +++ b/src/base/name/builder.rs @@ -266,6 +266,70 @@ where Ok(()) } + /// Appends a label with the decimal representation of `u8`. + /// + /// If there currently is a label under construction, it will be ended + /// before appending `label`. + /// + /// Returns an error if appending would result in a name longer than 254 + /// bytes. + pub fn append_dec_u8_label( + &mut self, + value: u8, + ) -> Result<(), PushError> { + self.end_label(); + let hecto = value / 100; + if hecto > 0 { + self.push(hecto + b'0')?; + } + let deka = (value / 10) % 10; + if hecto > 0 || deka > 0 { + self.push(deka + b'0')?; + } + self.push(value % 10 + b'0')?; + self.end_label(); + Ok(()) + } + + /// Appends a label with the hex digit. + /// + /// If there currently is a label under construction, it will be ended + /// before appending `label`. + /// + /// Returns an error if appending would result in a name longer than 254 + /// bytes. + pub fn append_hex_digit_label( + &mut self, + nibble: u8, + ) -> Result<(), PushError> { + fn hex_digit(nibble: u8) -> u8 { + match nibble & 0x0F { + 0 => b'0', + 1 => b'1', + 2 => b'2', + 3 => b'3', + 4 => b'4', + 5 => b'5', + 6 => b'6', + 7 => b'7', + 8 => b'8', + 9 => b'9', + 10 => b'A', + 11 => b'B', + 12 => b'C', + 13 => b'D', + 14 => b'E', + 15 => b'F', + _ => unreachable!(), + } + } + + self.end_label(); + self.push(hex_digit(nibble))?; + self.end_label(); + Ok(()) + } + /// Appends a relative domain name. /// /// If there currently is a label under construction, it will be ended diff --git a/src/base/name/dname.rs b/src/base/name/dname.rs index 145ac261c..5edd09b21 100644 --- a/src/base/name/dname.rs +++ b/src/base/name/dname.rs @@ -5,10 +5,11 @@ use super::super::cmp::CanonicalOrd; use super::super::scan::{Scanner, Symbol, SymbolCharsError, Symbols}; use super::super::wire::{FormError, ParseError}; -use super::builder::{DnameBuilder, FromStrError}; +use super::builder::{DnameBuilder, FromStrError, PushError}; use super::label::{Label, LabelTypeError, SplitLabelError}; use super::relative::{DnameIter, RelativeDname}; use super::traits::{FlattenInto, ToDname, ToLabelIter}; +use crate::base::net::IpAddr; #[cfg(feature = "bytes")] use bytes::Bytes; use core::ops::{Bound, RangeBounds}; @@ -181,6 +182,42 @@ impl Dname { { unsafe { Self::from_octets_unchecked(b"\0".as_ref().into()) } } + + /// Creates a domain name for reverse IP address lookup. + /// + /// The returned name will use the standard suffixes of `in-addr.arpa.` + /// for IPv4 addresses and `ip6.arpa.` for IPv6. + pub fn from_addr(addr: IpAddr) -> Result + where + Octs: FromBuilder, + ::Builder: EmptyBuilder + + FreezeBuilder + + AsRef<[u8]> + + AsMut<[u8]>, + { + let mut builder = + DnameBuilder::<::Builder>::new(); + match addr { + IpAddr::V4(addr) => { + let [a, b, c, d] = addr.octets(); + builder.append_dec_u8_label(d)?; + builder.append_dec_u8_label(c)?; + builder.append_dec_u8_label(b)?; + builder.append_dec_u8_label(a)?; + builder.append_label(b"in-addr")?; + builder.append_label(b"arpa")?; + } + IpAddr::V6(addr) => { + for &item in addr.octets().iter().rev() { + builder.append_hex_digit_label(item)?; + builder.append_hex_digit_label(item >> 4)?; + } + builder.append_label(b"ip6")?; + builder.append_label(b"arpa")?; + } + } + builder.into_dname() + } } impl Dname<[u8]> { @@ -1324,6 +1361,29 @@ pub(crate) mod test { ); } + #[test] + fn test_dname_from_addr() { + type TestDname = Dname>; + + assert_eq!( + TestDname::from_addr([192, 0, 2, 12].into()).unwrap(), + TestDname::from_str("12.2.0.192.in-addr.arpa").unwrap() + ); + assert_eq!( + TestDname::from_addr( + [0x2001, 0xdb8, 0x1234, 0x0, 0x5678, 0x1, 0x9abc, 0xdef] + .into() + ) + .unwrap(), + TestDname::from_str( + "f.e.d.0.c.b.a.9.1.0.0.0.8.7.6.5.\ + 0.0.0.0.4.3.2.1.8.b.d.0.1.0.0.2.\ + ip6.arpa" + ) + .unwrap() + ); + } + // `Dname::from_chars` is covered in the `FromStr` test. // // No tests for the simple conversion methods because, well, simple. diff --git a/src/resolv/lookup/addr.rs b/src/resolv/lookup/addr.rs index 6516b175a..89a8aed04 100644 --- a/src/resolv/lookup/addr.rs +++ b/src/resolv/lookup/addr.rs @@ -2,13 +2,12 @@ use crate::base::iana::Rtype; use crate::base::message::RecordIter; -use crate::base::name::{Dname, DnameBuilder, ParsedDname}; +use crate::base::name::{Dname, ParsedDname}; use crate::rdata::Ptr; use crate::resolv::resolver::Resolver; use octseq::octets::Octets; use std::io; use std::net::IpAddr; -use std::str::FromStr; //------------ Octets128 ----------------------------------------------------- @@ -28,7 +27,8 @@ pub async fn lookup_addr( resolv: &R, addr: IpAddr, ) -> Result, io::Error> { - let name = dname_from_addr(addr); + let name = Dname::::from_addr(addr) + .expect("address domain name too long"); resolv.query((name, Rtype::Ptr)).await.map(FoundAddrs) } @@ -94,78 +94,3 @@ impl<'a, Octs: Octets> Iterator for FoundAddrsIter<'a, Octs> { None } } - -//------------ Helper Functions --------------------------------------------- - -/// Translates an IP address into a domain name. -fn dname_from_addr(addr: IpAddr) -> Dname { - match addr { - IpAddr::V4(addr) => { - let octets = addr.octets(); - Dname::from_str(&format!( - "{}.{}.{}.{}.in-addr.arpa.", - octets[3], octets[2], octets[1], octets[0] - )) - .unwrap() - } - IpAddr::V6(addr) => { - let mut res = DnameBuilder::::new(); - for &item in addr.octets().iter().rev() { - res.append_label(&[hexdigit(item)]).unwrap(); - res.append_label(&[hexdigit(item >> 4)]).unwrap(); - } - res.append_label(b"ip6").unwrap(); - res.append_label(b"arpa").unwrap(); - res.into_dname().unwrap() - } - } -} - -fn hexdigit(nibble: u8) -> u8 { - match nibble & 0x0F { - 0 => b'0', - 1 => b'1', - 2 => b'2', - 3 => b'3', - 4 => b'4', - 5 => b'5', - 6 => b'6', - 7 => b'7', - 8 => b'8', - 9 => b'9', - 10 => b'A', - 11 => b'B', - 12 => b'C', - 13 => b'D', - 14 => b'E', - 15 => b'F', - _ => unreachable!(), - } -} - -//============ Tests ========================================================= - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_dname_from_addr() { - assert_eq!( - dname_from_addr([192, 0, 2, 12].into()), - Dname::::from_str("12.2.0.192.in-addr.arpa").unwrap() - ); - assert_eq!( - dname_from_addr( - [0x2001, 0xdb8, 0x1234, 0x0, 0x5678, 0x1, 0x9abc, 0xdef] - .into() - ), - Dname::::from_str( - "f.e.d.0.c.b.a.9.1.0.0.0.8.7.6.5.\ - 0.0.0.0.4.3.2.1.8.b.d.0.1.0.0.2.\ - ip6.arpa" - ) - .unwrap() - ); - } -}