cypheraddr/
host.rs

1// Set of libraries for privacy-preserving networking apps
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2019-2023 by
6//     Dr. Maxim Orlovsky <orlovsky@cyphernet.org>
7//
8// Copyright 2022-2023 Cyphernet DAO, Switzerland
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14//     http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21
22use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
23use std::str::FromStr;
24
25use crate::{AddrParseError, Host, Localhost};
26
27/// An Internet host name which can be resolved by standard OS means (and thus
28/// accepted by `std::net` methods via use of [`std::net::ToSocketAddrs`] trait,
29/// when combined with a port address).
30#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, From)]
31#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32#[display(inner)]
33#[cfg(feature = "dns")]
34pub enum InetHost {
35    /// IP name, including both IPv4 and IPv6 variants.
36    #[from]
37    #[from(Ipv4Addr)]
38    #[from(Ipv6Addr)]
39    Ip(IpAddr),
40
41    /// DNS-based name.
42    Dns(String),
43}
44
45#[cfg(feature = "dns")]
46impl Host for InetHost {
47    fn requires_proxy(&self) -> bool { false }
48}
49
50#[cfg(feature = "dns")]
51impl Localhost for InetHost {
52    fn localhost() -> Self { Self::Ip(Localhost::localhost()) }
53}
54
55#[cfg(feature = "dns")]
56impl FromStr for InetHost {
57    type Err = AddrParseError;
58
59    fn from_str(s: &str) -> Result<Self, Self::Err> {
60        match IpAddr::from_str(s) {
61            Ok(addr) => Ok(Self::Ip(addr)),
62            // TODO: Check charset and format of a DNS name
63            Err(_) => Ok(Self::Dns(s.to_owned())),
64        }
65    }
66}
67
68/// A host name covers multiple types which are not necessarily resolved by an
69/// OS and may require additional name resolvers (like via SOCKS5 etc). The type
70/// doesn't provide an information about the resolver; for that use
71/// [`super::ProxiedHost`].
72#[derive(Clone, PartialEq, Eq, Hash, Debug, Display, From)]
73#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
74#[display(inner)]
75#[non_exhaustive]
76pub enum HostName {
77    #[from]
78    #[from(Ipv4Addr)]
79    #[from(Ipv6Addr)]
80    Ip(IpAddr),
81
82    #[cfg(feature = "dns")]
83    Dns(String),
84
85    #[cfg(feature = "tor")]
86    #[from]
87    Tor(super::tor::OnionAddrV3),
88
89    #[cfg(feature = "i2p")]
90    #[from]
91    I2p(super::i2p::I2pAddr),
92
93    #[cfg(feature = "nym")]
94    #[from]
95    Nym(super::nym::NymAddr),
96}
97
98impl Host for HostName {
99    fn requires_proxy(&self) -> bool {
100        match self {
101            HostName::Ip(_) => false,
102            #[cfg(feature = "dns")]
103            HostName::Dns(_) => false,
104            #[allow(unreachable_patterns)]
105            _ => true,
106        }
107    }
108}
109
110impl Localhost for HostName {
111    fn localhost() -> Self { Self::Ip(Localhost::localhost()) }
112}
113
114#[cfg(feature = "dns")]
115impl From<InetHost> for HostName {
116    fn from(host: InetHost) -> Self {
117        match host {
118            InetHost::Ip(ip) => HostName::Ip(ip),
119            InetHost::Dns(dns) => HostName::Dns(dns),
120        }
121    }
122}
123
124impl FromStr for HostName {
125    type Err = AddrParseError;
126
127    fn from_str(s: &str) -> Result<Self, Self::Err> {
128        if let Ok(addr) = IpAddr::from_str(s) {
129            return Ok(Self::Ip(addr));
130        }
131        #[cfg(feature = "tor")]
132        if s.ends_with(".onion") {
133            return super::tor::OnionAddrV3::from_str(s)
134                .map(Self::Tor)
135                .map_err(AddrParseError::from);
136        }
137        // TODO: Support Num and I2P
138        #[cfg(feature = "dns")]
139        {
140            Ok(Self::Dns(s.to_owned()))
141        }
142        #[cfg(not(feature = "dns"))]
143        {
144            Err(AddrParseError::UnknownAddressFormat)
145        }
146    }
147}