1
//! Types and traits for converting objects to addresses which
2
//! Tor can connect to.
3

            
4
use crate::err::ErrorDetail;
5
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
6
use std::str::FromStr;
7
use thiserror::Error;
8

            
9
// ----------------------------------------------------------------------
10

            
11
/// An object that can be converted to a [`TorAddr`] with a minimum of risk.
12
///
13
/// Typically, this trait will be implemented for a hostname or service name.
14
///
15
/// Don't implement this trait for IP addresses and similar types; instead,
16
/// implement [`DangerouslyIntoTorAddr`] for those.  (The trouble with accepting
17
/// IP addresses is that, in order to get an IP address, most programs will do a
18
/// local hostname lookup, which will leak the target address to the DNS
19
/// resolver. The `DangerouslyIntoTorAddr` trait provides a contract for careful
20
/// programs to say, "I have gotten this IP address from somewhere safe."  This
21
/// trait is for name-based addressing and similar, which _usually_ gets its
22
/// addresses from a safer source.)
23
///
24
/// [*See also: the `TorAddr` documentation.*](TorAddr)
25
///
26
/// # Design note
27
///
28
/// We use a separate trait here, instead of using `Into<TorAddr>` or
29
/// `TryInto<TorAddr>`, because `IntoTorAddr` implies additional guarantees
30
/// relating to privacy risk.  The separate trait alerts users that something
31
/// tricky is going on here, and encourages them to think twice before
32
/// implementing `IntoTorAddr` for their own types.
33
pub trait IntoTorAddr {
34
    /// Try to make a [`TorAddr`] to represent connecting to this
35
    /// address.
36
    fn into_tor_addr(self) -> Result<TorAddr, TorAddrError>;
37
}
38

            
39
/// An object that can be converted to a [`TorAddr`], but which it
40
/// might be risky to get in the first place if you're hoping for
41
/// anonymity.
42
///
43
/// For example, you can use this trait to convert a [`SocketAddr`]
44
/// into a [`TorAddr`], and it's safe to do that conversion.  But
45
/// where did you get the [`SocketAddr`] in the first place?  If it
46
/// comes from a local DNS lookup, then you have leaked the address
47
/// you were resolving to your DNS resolver, and probably your ISP.
48
///
49
/// [*See also: the `TorAddr` documentation.*](TorAddr)
50
pub trait DangerouslyIntoTorAddr {
51
    /// Try to make a [`TorAddr`] to represent connecting to `self`.
52
    ///
53
    /// By calling this function, the caller asserts that `self` was
54
    /// obtained from some secure, private mechanism, and **not** from a local
55
    /// DNS lookup or something similar.
56
    fn into_tor_addr_dangerously(self) -> Result<TorAddr, TorAddrError>;
57
}
58

            
59
/// An address object that you can connect to over the Tor network.
60
///
61
/// When you're making a connection with Tor, you shouldn't do your DNS
62
/// lookups locally: that would leak your target address to your DNS server.
63
/// Instead, it's better to use a combination of a hostname and a port
64
/// directly.
65
///
66
/// The preferred way to create a `TorAddr` is via the [`IntoTorAddr`] trait,
67
/// using a hostname and a port (or a string containing a hostname and a
68
/// port).  It's also okay to use an IP and Port there, but only if they come
69
/// from some source _other than_ a local DNS lookup.
70
///
71
/// In order to discourage local hostname lookups, the functions that
72
/// construct a [`TorAddr`] from [`IpAddr`], [`SocketAddr`], and so
73
/// forth are labeled as "dangerous".
74
///
75
/// # Examples
76
///
77
/// Making a `TorAddr` from various "safe" sources:
78
///
79
/// ```rust
80
/// # use anyhow::Result;
81
/// # fn main() -> Result<()> {
82
/// use arti_client::IntoTorAddr;
83
///
84
/// let example_from_tuple = ("example.com", 80).into_tor_addr()?;
85
/// let example_from_string = "example.com:80".into_tor_addr()?;
86
///
87
/// assert_eq!(example_from_tuple, example_from_string);
88
/// # Ok(())
89
/// # }
90
/// ```
91
///
92
/// Making a `TorAddr` from an IP address and port:
93
///
94
/// > **Warning:** This example is only safe because we're not doing a DNS lookup; rather, the
95
/// > intent is to connect to a hardcoded IP address.
96
/// > If you're using [`DangerouslyIntoTorAddr`], pay careful attention to where your IP addresses
97
/// > are coming from, and whether there's a risk of information leakage.
98
///
99
/// ```rust
100
/// # use anyhow::Result;
101
/// # fn main() -> Result<()> {
102
/// use arti_client::DangerouslyIntoTorAddr;
103
/// use std::net::{IpAddr, SocketAddr};
104
///
105
/// let quad_one_dns: SocketAddr = "1.1.1.1:53".parse()?;
106
/// let addr_from_socketaddr = quad_one_dns.into_tor_addr_dangerously()?;
107
///
108
/// let quad_one_ip: IpAddr = "1.1.1.1".parse()?;
109
/// let addr_from_tuple = (quad_one_ip, 53).into_tor_addr_dangerously()?;
110
///
111
/// assert_eq!(addr_from_socketaddr, addr_from_tuple);
112
/// # Ok(())
113
/// # }
114
/// ```
115
12
#[derive(Debug, Clone, Eq, PartialEq)]
116
pub struct TorAddr {
117
    /// The target host.
118
    host: Host,
119
    /// The target port number.
120
    port: u16,
121
}
122

            
123
impl TorAddr {
124
    /// Construct a TorAddr from its constituent parts, rejecting it if the
125
    /// port is zero.
126
42
    fn new(host: Host, port: u16) -> Result<Self, TorAddrError> {
127
42
        if port == 0 {
128
1
            Err(TorAddrError::BadPort)
129
        } else {
130
41
            Ok(TorAddr { host, port })
131
        }
132
42
    }
133

            
134
    /// Construct a `TorAddr` from any object that implements
135
    /// [`IntoTorAddr`].
136
23
    pub fn from<A: IntoTorAddr>(addr: A) -> Result<Self, TorAddrError> {
137
23
        addr.into_tor_addr()
138
23
    }
139
    /// Construct a `TorAddr` from any object that implements
140
    /// [`DangerouslyIntoTorAddr`].
141
    ///
142
    /// See [`DangerouslyIntoTorAddr`] for an explanation of why the
143
    /// style of programming supported by this function is dangerous
144
    /// to use.
145
7
    pub fn dangerously_from<A: DangerouslyIntoTorAddr>(addr: A) -> Result<Self, TorAddrError> {
146
7
        addr.into_tor_addr_dangerously()
147
7
    }
148

            
149
    /// Return true if this is an IP address (rather than a hostname).
150
5
    pub fn is_ip_address(&self) -> bool {
151
5
        matches!(&self.host, Host::Ip(_))
152
5
    }
153

            
154
    /// Extract a `String`-based hostname and a `u16` port from this
155
    /// address.
156
3
    pub(crate) fn into_string_and_port(self) -> (String, u16) {
157
3
        let host = self.host.to_string();
158
3
        let port = self.port;
159
3
        (host, port)
160
3
    }
161

            
162
    /// Return true if the `host` in this address is local.
163
10
    fn is_local(&self) -> bool {
164
10
        self.host.is_local()
165
10
    }
166

            
167
    /// Give an error if this address doesn't conform to the rules set in
168
    /// `cfg`.
169
10
    pub(crate) fn enforce_config(
170
10
        &self,
171
10
        cfg: &crate::config::ClientAddrConfig,
172
10
    ) -> Result<(), ErrorDetail> {
173
10
        if !cfg.allow_local_addrs && self.is_local() {
174
1
            return Err(ErrorDetail::LocalAddress);
175
9
        }
176

            
177
9
        if let Host::Hostname(addr) = &self.host {
178
5
            if !is_valid_hostname(addr) {
179
1
                return Err(ErrorDetail::InvalidHostname);
180
4
            }
181
4
            if addr.to_lowercase().ends_with(".onion") {
182
1
                return Err(ErrorDetail::OnionAddressNotSupported);
183
3
            }
184
4
        }
185

            
186
7
        Ok(())
187
10
    }
188
}
189

            
190
impl std::fmt::Display for TorAddr {
191
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192
9
        match self.host {
193
4
            Host::Ip(IpAddr::V6(addr)) => write!(f, "[{}]:{}", addr, self.port),
194
8
            _ => write!(f, "{}:{}", self.host, self.port),
195
        }
196
12
    }
197
}
198

            
199
/// An error created while making or using a [`TorAddr`].
200
2
#[derive(Debug, Error, Clone, Eq, PartialEq)]
201
#[non_exhaustive]
202
pub enum TorAddrError {
203
    /// Tried to parse a string that can never be interpreted as a valid host.
204
    #[error("String can never be a valid hostname")]
205
    InvalidHostname,
206
    /// Tried to parse a string as an `address:port`, but it had no port.
207
    #[error("No port found in string")]
208
    NoPort,
209
    /// Tried to parse a port that wasn't a valid nonzero `u16`.
210
    #[error("Could not parse port")]
211
    BadPort,
212
}
213

            
214
/// A host that Tor can connect to: either a hostname or an IP address.
215
12
#[derive(Clone, Debug, Eq, PartialEq)]
216
enum Host {
217
    /// A hostname.  This variant should never be used if the `Ip`
218
    /// variant could be used instead.
219
    Hostname(String),
220
    /// An IP address.
221
    Ip(IpAddr),
222
}
223

            
224
impl FromStr for Host {
225
    type Err = TorAddrError;
226
    fn from_str(s: &str) -> Result<Host, TorAddrError> {
227
24
        if let Ok(ip_addr) = s.parse() {
228
5
            Ok(Host::Ip(ip_addr))
229
        } else {
230
            // TODO(nickm): we might someday want to reject some kinds of bad
231
            // hostnames here, rather than when we're about to connect to them.
232
            // But that would be an API break, and maybe not what people want.
233
            // Maybe instead we should have a method to check whether a hostname
234
            // is "bad"? Not sure; we'll need to decide the right behavior here.
235
19
            Ok(Host::Hostname(s.to_owned()))
236
        }
237
24
    }
238
}
239

            
240
impl Host {
241
    /// Return true if this address is one that is "internal": that is,
242
    /// relative to the particular host that is resolving it.
243
    fn is_local(&self) -> bool {
244
8
        match self {
245
8
            Host::Hostname(name) => name.eq_ignore_ascii_case("localhost"),
246
            // TODO: use is_global once it's stable.
247
5
            Host::Ip(IpAddr::V4(ip)) => ip.is_loopback() || ip.is_private(),
248
3
            Host::Ip(IpAddr::V6(ip)) => ip.is_loopback(),
249
        }
250
16
    }
251
}
252

            
253
impl std::fmt::Display for Host {
254
11
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
255
11
        match self {
256
5
            Host::Hostname(s) => write!(f, "{}", s),
257
6
            Host::Ip(ip) => write!(f, "{}", ip),
258
        }
259
11
    }
260
}
261

            
262
impl IntoTorAddr for TorAddr {
263
2
    fn into_tor_addr(self) -> Result<TorAddr, TorAddrError> {
264
2
        Ok(self)
265
2
    }
266
}
267

            
268
impl<A: IntoTorAddr + Clone> IntoTorAddr for &A {
269
1
    fn into_tor_addr(self) -> Result<TorAddr, TorAddrError> {
270
1
        self.clone().into_tor_addr()
271
1
    }
272
}
273

            
274
impl IntoTorAddr for &str {
275
    fn into_tor_addr(self) -> Result<TorAddr, TorAddrError> {
276
30
        if let Ok(sa) = SocketAddr::from_str(self) {
277
17
            TorAddr::new(Host::Ip(sa.ip()), sa.port())
278
        } else {
279
13
            let (host, port) = self.rsplit_once(':').ok_or(TorAddrError::NoPort)?;
280
12
            let host = host.parse()?;
281
12
            let port = port.parse().map_err(|_| TorAddrError::BadPort)?;
282
11
            TorAddr::new(host, port)
283
        }
284
30
    }
285
}
286

            
287
impl IntoTorAddr for String {
288
1
    fn into_tor_addr(self) -> Result<TorAddr, TorAddrError> {
289
1
        self[..].into_tor_addr()
290
1
    }
291
}
292

            
293
impl FromStr for TorAddr {
294
    type Err = TorAddrError;
295
5
    fn from_str(s: &str) -> Result<Self, TorAddrError> {
296
5
        s.into_tor_addr()
297
5
    }
298
}
299

            
300
impl IntoTorAddr for (&str, u16) {
301
7
    fn into_tor_addr(self) -> Result<TorAddr, TorAddrError> {
302
7
        let (host, port) = self;
303
7
        let host = host.parse()?;
304
7
        TorAddr::new(host, port)
305
7
    }
306
}
307

            
308
impl IntoTorAddr for (String, u16) {
309
1
    fn into_tor_addr(self) -> Result<TorAddr, TorAddrError> {
310
1
        let (host, port) = self;
311
1
        (&host[..], port).into_tor_addr()
312
1
    }
313
}
314

            
315
impl<T: DangerouslyIntoTorAddr + Clone> DangerouslyIntoTorAddr for &T {
316
1
    fn into_tor_addr_dangerously(self) -> Result<TorAddr, TorAddrError> {
317
1
        self.clone().into_tor_addr_dangerously()
318
1
    }
319
}
320

            
321
impl DangerouslyIntoTorAddr for (IpAddr, u16) {
322
3
    fn into_tor_addr_dangerously(self) -> Result<TorAddr, TorAddrError> {
323
3
        let (addr, port) = self;
324
3
        TorAddr::new(Host::Ip(addr), port)
325
3
    }
326
}
327

            
328
impl DangerouslyIntoTorAddr for (Ipv4Addr, u16) {
329
2
    fn into_tor_addr_dangerously(self) -> Result<TorAddr, TorAddrError> {
330
2
        let (addr, port) = self;
331
2
        TorAddr::new(Host::Ip(addr.into()), port)
332
2
    }
333
}
334

            
335
impl DangerouslyIntoTorAddr for (Ipv6Addr, u16) {
336
2
    fn into_tor_addr_dangerously(self) -> Result<TorAddr, TorAddrError> {
337
2
        let (addr, port) = self;
338
2
        TorAddr::new(Host::Ip(addr.into()), port)
339
2
    }
340
}
341

            
342
impl DangerouslyIntoTorAddr for SocketAddr {
343
1
    fn into_tor_addr_dangerously(self) -> Result<TorAddr, TorAddrError> {
344
1
        let (addr, port) = (self.ip(), self.port());
345
1
        (addr, port).into_tor_addr_dangerously()
346
1
    }
347
}
348

            
349
impl DangerouslyIntoTorAddr for SocketAddrV4 {
350
1
    fn into_tor_addr_dangerously(self) -> Result<TorAddr, TorAddrError> {
351
1
        let (addr, port) = (self.ip(), self.port());
352
1
        (*addr, port).into_tor_addr_dangerously()
353
1
    }
354
}
355

            
356
impl DangerouslyIntoTorAddr for SocketAddrV6 {
357
1
    fn into_tor_addr_dangerously(self) -> Result<TorAddr, TorAddrError> {
358
1
        let (addr, port) = (self.ip(), self.port());
359
1
        (*addr, port).into_tor_addr_dangerously()
360
1
    }
361
}
362

            
363
/// Check whether `hostname` is a valid hostname or not.
364
///
365
/// (Note that IPv6 addresses don't follow these rules.)
366
///
367
/// TODO: Check whether the rules given here are in fact the same rules
368
/// as Tor follows, and whether they conform to anything.
369
12
fn is_valid_hostname(hostname: &str) -> bool {
370
12
    /// Check if we have the valid characters for a hostname
371
174
    fn is_valid_char(byte: u8) -> bool {
372
174
        ((b'a'..=b'z').contains(&byte))
373
27
            || ((b'A'..=b'Z').contains(&byte))
374
27
            || ((b'0'..=b'9').contains(&byte))
375
16
            || byte == b'-'
376
13
            || byte == b'.'
377
176
    }
378
12

            
379
174
    !(hostname.bytes().any(|byte| !is_valid_char(byte))
380
9
        || hostname.ends_with('-')
381
9
        || hostname.starts_with('-')
382
7
        || hostname.ends_with('.')
383
7
        || hostname.starts_with('.')
384
7
        || hostname.is_empty())
385
12
}
386

            
387
#[cfg(test)]
388
mod test {
389
    #![allow(clippy::unwrap_used)]
390
    use super::*;
391

            
392
    #[test]
393
    fn validate_hostname() {
394
        // Valid hostname tests
395
        assert!(is_valid_hostname("torproject.org"));
396
        assert!(is_valid_hostname("Tor-Project.org"));
397

            
398
        // Invalid hostname tests
399
        assert!(!is_valid_hostname("-torproject.org"));
400
        assert!(!is_valid_hostname("_torproject.org"));
401
        assert!(!is_valid_hostname("tor_project1.org"));
402
        assert!(!is_valid_hostname("iwanna$money.org"));
403
        assert!(!is_valid_hostname(""));
404
    }
405

            
406
    #[test]
407
    fn validate_addr() {
408
        use crate::err::ErrorDetail;
409
        fn val<A: IntoTorAddr>(addr: A) -> Result<TorAddr, ErrorDetail> {
410
            let toraddr = addr.into_tor_addr()?;
411
            toraddr.enforce_config(&Default::default())?;
412
            Ok(toraddr)
413
        }
414

            
415
        assert!(val("[2001:db8::42]:20").is_ok());
416
        assert!(val(("2001:db8::42", 20)).is_ok());
417
        assert!(val(("198.151.100.42", 443)).is_ok());
418
        assert!(val("198.151.100.42:443").is_ok());
419
        assert!(val("www.torproject.org:443").is_ok());
420
        assert!(val(("www.torproject.org", 443)).is_ok());
421

            
422
        assert!(matches!(
423
            val("-foobar.net:443"),
424
            Err(ErrorDetail::InvalidHostname)
425
        ));
426
        assert!(matches!(
427
            val("www.torproject.org"),
428
            Err(ErrorDetail::Address(TorAddrError::NoPort))
429
        ));
430

            
431
        assert!(matches!(
432
            val("192.168.0.1:80"),
433
            Err(ErrorDetail::LocalAddress)
434
        ));
435
        assert!(matches!(
436
            val("eweiibe6tdjsdprb4px6rqrzzcsi22m4koia44kc5pcjr7nec2rlxyad.onion:443"),
437
            Err(ErrorDetail::OnionAddressNotSupported)
438
        ));
439
    }
440

            
441
    #[test]
442
    fn local_addrs() {
443
        fn is_local_hostname(s: &str) -> bool {
444
            let h: Host = s.parse().unwrap();
445
            h.is_local()
446
        }
447

            
448
        assert!(is_local_hostname("localhost"));
449
        assert!(is_local_hostname("loCALHOST"));
450
        assert!(is_local_hostname("127.0.0.1"));
451
        assert!(is_local_hostname("::1"));
452
        assert!(is_local_hostname("192.168.0.1"));
453

            
454
        assert!(!is_local_hostname("www.example.com"));
455
    }
456

            
457
    #[test]
458
    fn is_ip_address() {
459
        fn ip(s: &str) -> bool {
460
            TorAddr::from(s).unwrap().is_ip_address()
461
        }
462

            
463
        assert!(ip("192.168.0.1:80"));
464
        assert!(ip("[::1]:80"));
465
        assert!(ip("[2001:db8::42]:65535"));
466
        assert!(!ip("example.com:80"));
467
        assert!(!ip("example.onion:80"));
468
    }
469

            
470
    #[test]
471
    fn string_and_port() {
472
        fn sap(s: &str) -> (String, u16) {
473
            TorAddr::from(s).unwrap().into_string_and_port()
474
        }
475

            
476
        assert_eq!(
477
            sap("[2001:db8::42]:9001"),
478
            ("2001:db8::42".to_owned(), 9001)
479
        );
480
        assert_eq!(sap("example.com:80"), ("example.com".to_owned(), 80));
481
    }
482

            
483
    #[test]
484
    fn bad_ports() {
485
        assert_eq!(
486
            TorAddr::from("www.example.com:squirrel"),
487
            Err(TorAddrError::BadPort)
488
        );
489
        assert_eq!(
490
            TorAddr::from("www.example.com:0"),
491
            Err(TorAddrError::BadPort)
492
        );
493
    }
494

            
495
    #[test]
496
    fn convert_safe() {
497
        fn check<A: IntoTorAddr>(a: A, s: &str) {
498
            let a1 = TorAddr::from(a).unwrap();
499
            let a2 = s.parse().unwrap();
500
            assert_eq!(a1, a2);
501
            assert_eq!(&a1.to_string(), s);
502
        }
503

            
504
        check(("www.example.com", 8000), "www.example.com:8000");
505
        check(
506
            TorAddr::from(("www.example.com", 8000)).unwrap(),
507
            "www.example.com:8000",
508
        );
509
        check(
510
            &TorAddr::from(("www.example.com", 8000)).unwrap(),
511
            "www.example.com:8000",
512
        );
513
        check("[2001:db8::0042]:9001".to_owned(), "[2001:db8::42]:9001");
514
        check(("2001:db8::0042".to_owned(), 9001), "[2001:db8::42]:9001");
515
    }
516

            
517
    #[test]
518
    fn convert_dangerous() {
519
        fn check<A: DangerouslyIntoTorAddr>(a: A, s: &str) {
520
            let a1 = TorAddr::dangerously_from(a).unwrap();
521
            let a2 = TorAddr::from(s).unwrap();
522
            assert_eq!(a1, a2);
523
            assert_eq!(&a1.to_string(), s);
524
        }
525

            
526
        let ip: IpAddr = "203.0.133.6".parse().unwrap();
527
        let ip4: Ipv4Addr = "203.0.133.7".parse().unwrap();
528
        let ip6: Ipv6Addr = "2001:db8::42".parse().unwrap();
529
        let sa: SocketAddr = "203.0.133.8:80".parse().unwrap();
530
        let sa4: SocketAddrV4 = "203.0.133.8:81".parse().unwrap();
531
        let sa6: SocketAddrV6 = "[2001:db8::43]:82".parse().unwrap();
532

            
533
        check(&(ip, 443), "203.0.133.6:443");
534
        check((ip, 443), "203.0.133.6:443");
535
        check((ip4, 444), "203.0.133.7:444");
536
        check((ip6, 445), "[2001:db8::42]:445");
537
        check(sa, "203.0.133.8:80");
538
        check(sa4, "203.0.133.8:81");
539
        check(sa6, "[2001:db8::43]:82");
540
    }
541
}