1
//! Support for identifying a particular transport.
2
//!
3
//! A "transport" is a mechanism to connect to a relay on the Tor network and
4
//! make a `Channel`. Currently, two types of transports exist: the "built-in"
5
//! transport, which uses TLS over TCP, and various anti-censorship "pluggable
6
//! transports", which use TLS over other protocols to avoid detection by
7
//! censors.
8

            
9
use std::fmt::{self, Debug, Display};
10
use std::net::SocketAddr;
11
use std::slice;
12
use std::str::FromStr;
13

            
14
use itertools::Either;
15
use safelog::Redactable;
16
use serde::{Deserialize, Serialize};
17
use thiserror::Error;
18

            
19
use crate::HasAddrs;
20

            
21
/// Identify a type of Transport.
22
///
23
/// If this crate is compiled with the `pt-client` feature, this type can
24
/// support pluggable transports; otherwise, only the built-in transport type is
25
/// supported.
26
///
27
/// This can be displayed as, or parsed from, a string.
28
/// `"-"` is used to indicate the builtin transport,
29
/// and `""` and `"bridge"` and `"<none>"` are also recognised for that.
30
//
31
// We recognise "bridge" as pluggable; "BRIDGE" is rejected as invalid.
32
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
33
pub struct TransportId(Inner);
34

            
35
/// Helper type to implement [`TransportId`].
36
///
37
/// This is a separate type so that TransportId can be opaque.
38
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
39
enum Inner {
40
    /// The built-in transport type.
41
    #[default]
42
    BuiltIn,
43

            
44
    /// A pluggable transport type, specified by its name.
45
    #[cfg(feature = "pt-client")]
46
    Pluggable(PtTransportName),
47
}
48

            
49
/// The name of a Pluggable Transport protocol.
50
///
51
/// The name has been syntax-checked.
52
///
53
/// These names are used to identify the particular transport protocol, such as
54
/// "obfs4" or "snowflake".  They match a name of a protocol that the transport
55
/// binary knows how to provide to the name of a protocol that a bridge is
56
/// configured to use.
57
#[derive(
58
    Debug,
59
    Clone,
60
    Default,
61
    Eq,
62
    PartialEq,
63
    Hash,
64
    serde_with::DeserializeFromStr,
65
    serde_with::SerializeDisplay,
66
)]
67
pub struct PtTransportName(String);
68

            
69
impl FromStr for PtTransportName {
70
    type Err = TransportIdError;
71

            
72
4749
    fn from_str(s: &str) -> Result<Self, Self::Err> {
73
4749
        s.to_string().try_into()
74
4749
    }
75
}
76

            
77
impl TryFrom<String> for PtTransportName {
78
    type Error = TransportIdError;
79

            
80
4749
    fn try_from(s: String) -> Result<PtTransportName, Self::Error> {
81
4749
        if is_well_formed_id(&s) {
82
4617
            Ok(PtTransportName(s))
83
        } else {
84
132
            Err(TransportIdError::BadId(s))
85
        }
86
4749
    }
87
}
88

            
89
impl Display for PtTransportName {
90
1201
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91
1201
        Display::fmt(&self.0, f)
92
1201
    }
93
}
94

            
95
impl AsRef<str> for PtTransportName {
96
    fn as_ref(&self) -> &str {
97
        &self.0
98
    }
99
}
100

            
101
/// These identifiers are used to indicate the built-in transport.
102
///
103
/// When outputting string representations, the first (`"-"`) is used.
104
//
105
// Actual pluggable transport names are restricted to the syntax of C identifiers.
106
// These strings are deliberately not in that syntax so as to avoid clashes.
107
// `"bridge"` is likewise prohibited by the spec.
108
const BUILT_IN_IDS: &[&str] = &["-", "", "bridge", "<none>"];
109

            
110
impl FromStr for TransportId {
111
    type Err = TransportIdError;
112

            
113
3682
    fn from_str(s: &str) -> Result<Self, Self::Err> {
114
3682
        if BUILT_IN_IDS.contains(&s) {
115
2217
            return Ok(TransportId(Inner::BuiltIn));
116
1465
        };
117

            
118
        #[cfg(feature = "pt-client")]
119
        {
120
1465
            let name: PtTransportName = s.parse()?;
121
1459
            Ok(TransportId(Inner::Pluggable(name)))
122
        }
123

            
124
        #[cfg(not(feature = "pt-client"))]
125
        Err(TransportIdError::NoSupport)
126
3682
    }
127
}
128

            
129
impl Display for TransportId {
130
6
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131
6
        match &self.0 {
132
4
            Inner::BuiltIn => write!(f, "{}", BUILT_IN_IDS[0]),
133
            #[cfg(feature = "pt-client")]
134
2
            Inner::Pluggable(name) => write!(f, "{}", name),
135
        }
136
6
    }
137
}
138

            
139
#[cfg(feature = "pt-client")]
140
impl From<PtTransportName> for TransportId {
141
6
    fn from(name: PtTransportName) -> Self {
142
6
        TransportId(Inner::Pluggable(name))
143
6
    }
144
}
145

            
146
/// Return true if `s` is a well-formed transport ID.
147
///
148
/// According to the specification, a well-formed transport ID follows the same
149
/// rules as a C99 identifier: It must follow the regular expression
150
/// `[a-zA-Z_][a-zA-Z0-9_]*`.
151
4767
fn is_well_formed_id(s: &str) -> bool {
152
    // It's okay to use a bytes iterator, since non-ascii strings are not
153
    // allowed.
154
4767
    let mut bytes = s.bytes();
155

            
156
4767
    if let Some(first) = bytes.next() {
157
4765
        (first.is_ascii_alphabetic() || first == b'_')
158
21673
            && bytes.all(|b| b.is_ascii_alphanumeric() || b == b'_')
159
4692
            && !s.eq_ignore_ascii_case("bridge")
160
    } else {
161
2
        false
162
    }
163
4767
}
164

            
165
/// An error related to parsing a TransportId.
166
#[derive(Clone, Debug, thiserror::Error)]
167
#[non_exhaustive]
168
pub enum TransportIdError {
169
    /// Arti was compiled without client-side pluggable transport support, and
170
    /// we tried to use a pluggable transport.
171
    #[error("Not compiled with pluggable transport support")]
172
    NoSupport,
173

            
174
    /// Tried to parse a pluggable transport whose name was not well-formed.
175
    #[error("{0:?} is not a valid pluggable transport ID")]
176
    BadId(String),
177
}
178

            
179
impl TransportId {
180
    /// Return a new `TransportId` referencing the builtin transport
181
    ///
182
    /// This is equivalent to the `Default` impl.
183
    pub fn new_builtin() -> Self {
184
        TransportId(Inner::BuiltIn)
185
    }
186

            
187
    /// Return a new `TransportId` referencing a pluggable transport
188
    ///
189
    /// This is equivalent to the `From<PtTransportName>` impl.
190
    #[cfg(feature = "pt-client")]
191
    pub fn new_pluggable(pt: PtTransportName) -> Self {
192
        pt.into()
193
    }
194

            
195
    /// Return true if this is the built-in transport.
196
2778
    pub fn is_builtin(&self) -> bool {
197
2778
        self.0 == Inner::BuiltIn
198
2778
    }
199

            
200
    /// Returns the pluggable transport name
201
    ///
202
    /// Or `None` if `self` doesn't specify a pluggable transport
203
    /// (e.g. if it specifies the builtin transport).
204
    #[cfg(feature = "pt-client")]
205
1071
    pub fn as_pluggable(&self) -> Option<&PtTransportName> {
206
1071
        match &self.0 {
207
            Inner::BuiltIn => None,
208
            #[cfg(feature = "pt-client")]
209
1071
            Inner::Pluggable(pt) => Some(pt),
210
        }
211
1071
    }
212

            
213
    /// Consumes this `TransportId` and returns the pluggable transport name
214
    ///
215
    /// Or `None` if `self` doesn't specify a pluggable transport
216
    /// (e.g. if it specifies the builtin transport).
217
    #[cfg(feature = "pt-client")]
218
1953
    pub fn into_pluggable(self) -> Option<PtTransportName> {
219
1953
        match self.0 {
220
504
            Inner::BuiltIn => None,
221
            #[cfg(feature = "pt-client")]
222
1449
            Inner::Pluggable(pt) => Some(pt),
223
        }
224
1953
    }
225
}
226

            
227
/// This identifier is used to indicate no transport address.
228
const NONE_ADDR: &str = "-";
229

            
230
/// An address that an be passed to a pluggable transport to tell it where to
231
/// connect (typically, to a bridge).
232
///
233
/// Not every transport accepts all kinds of addresses.
234
///
235
/// This is semantically very similar to `Option<BridgeAddr>`,
236
/// but it has some of its own conversion methods and bespoke `FromStr` and `Display`.
237
//
238
// Implementations for `PtTargetAddr` are in terms of those for `BridgeAddr`
239
// wheresoever possible, to ensure that they do not diverge in semantics.
240
#[derive(
241
    Clone, Debug, PartialEq, Eq, Hash, serde_with::DeserializeFromStr, serde_with::SerializeDisplay,
242
)]
243
#[non_exhaustive]
244
pub enum PtTargetAddr {
245
    /// An IP address and port for a Tor relay.
246
    ///
247
    /// This is the only address type supported by the BuiltIn transport.
248
    IpPort(SocketAddr),
249
    /// A hostname-and-port target address.  Some transports may support this.
250
    HostPort(String, u16),
251
    /// A completely absent target address.  Some transports support this.
252
    None,
253
}
254

            
255
/// An address of a bridge, for use in configuration.
256
///
257
/// Contains precisely, either:
258
///  * A hostname (as a string), plus a `u16` port; or
259
///  * An (IPv4 or IPv6) socket address including port - i.e., a `SocketAddr`,
260
///    or to put it another way, an IP address (v4 or v6) plus a `u16` port.
261
///
262
/// Hostnames which are not syntactically invalid Internet hostnames,
263
/// and a port value of zero,
264
/// *can* be represented within a `BridgeAddr`.
265
#[derive(
266
    Clone,
267
    Debug,
268
    PartialEq,
269
    Eq,
270
    Hash,
271
    serde_with::DeserializeFromStr,
272
    serde_with::SerializeDisplay,
273
    derive_more::Display,
274
)]
275
pub struct BridgeAddr(BridgeAddrInner<SocketAddr, String>);
276

            
277
/// `BridgeAddr` contents; type parameters allow use with references to avoid some copying
278
///
279
/// `SA` is always `SocketAddr` or `&SocketAddr`.
280
///
281
/// `HN` is always `String` or `&str`.
282
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
283
enum BridgeAddrInner<SA, HN> {
284
    /// An IP address and port for a bridge
285
    IpPort(SA),
286
    /// A hostname-and-port target address
287
    HostPort(HN, u16),
288
}
289

            
290
// These methods have long slightly awkward names because we think
291
// we may want to change their names and types later, and/or deprecate them.
292
// So let's not use up the nice names now.
293
//
294
// TODO: decide on, and implement, a nicer API, or functions with nicer names.
295
// TODO: add From/Into conversions for SocketAddr and maybe (String, u16).
296
// TODO: consider providing constructor/accessor/deconstructor to/from Either.
297
impl BridgeAddr {
298
    /// Create a new `BridgeAddr` referring to a numeric address and port
299
882
    pub fn new_addr_from_sockaddr(sa: SocketAddr) -> Self {
300
882
        BridgeAddr(BridgeAddrInner::IpPort(sa))
301
882
    }
302

            
303
    /// If this is a numeric address, return it as a `SocketAddr`
304
1449
    pub fn as_socketaddr(&self) -> Option<&SocketAddr> {
305
1449
        match &self.0 {
306
1323
            BridgeAddrInner::IpPort(sa) => Some(sa),
307
126
            BridgeAddrInner::HostPort(..) => None,
308
        }
309
1449
    }
310

            
311
    /// Create a new `BridgeAddr` referring to a numeric address and port
312
    pub fn new_named_host_port(hostname: impl Into<String>, port: u16) -> Self {
313
        BridgeAddr(BridgeAddrInner::HostPort(hostname.into(), port))
314
    }
315

            
316
    /// If this is a named host and port, return it as hostname string and port
317
126
    pub fn as_host_port(&self) -> Option<(&str, u16)> {
318
126
        match &self.0 {
319
            BridgeAddrInner::IpPort(..) => None,
320
126
            BridgeAddrInner::HostPort(hn, port) => Some((hn, *port)),
321
        }
322
126
    }
323
}
324

            
325
impl From<PtTargetAddr> for Option<BridgeAddr> {
326
669
    fn from(pt: PtTargetAddr) -> Option<BridgeAddr> {
327
669
        match pt {
328
4
            PtTargetAddr::IpPort(sa) => Some(BridgeAddrInner::IpPort(sa)),
329
665
            PtTargetAddr::HostPort(hn, p) => Some(BridgeAddrInner::HostPort(hn, p)),
330
            PtTargetAddr::None => None,
331
        }
332
669
        .map(BridgeAddr)
333
669
    }
334
}
335
impl From<Option<BridgeAddr>> for PtTargetAddr {
336
2282
    fn from(pt: Option<BridgeAddr>) -> PtTargetAddr {
337
2282
        match pt.map(|ba| ba.0) {
338
199
            Some(BridgeAddrInner::IpPort(sa)) => PtTargetAddr::IpPort(sa),
339
2083
            Some(BridgeAddrInner::HostPort(hn, p)) => PtTargetAddr::HostPort(hn, p),
340
            None => PtTargetAddr::None,
341
        }
342
2282
    }
343
}
344

            
345
/// An error from parsing a [`BridgeAddr`] or [`PtTargetAddr`].
346
#[derive(Clone, Debug, thiserror::Error)]
347
#[non_exhaustive]
348
pub enum BridgeAddrError {
349
    /// We were compiled without support for addresses of this type.
350
    #[error("Not compiled with pluggable transport support.")]
351
    NoSupport,
352
    /// We cannot parse this address.
353
    #[error("Cannot parse {0:?} as an address.")]
354
    BadAddress(String),
355
}
356

            
357
impl FromStr for BridgeAddr {
358
    type Err = BridgeAddrError;
359

            
360
2292
    fn from_str(s: &str) -> Result<Self, Self::Err> {
361
2292
        Ok(BridgeAddr(if let Ok(addr) = s.parse() {
362
703
            BridgeAddrInner::IpPort(addr)
363
1589
        } else if let Some((name, port)) = s.rsplit_once(':') {
364
1516
            let port = port
365
1516
                .parse()
366
1516
                .map_err(|_| BridgeAddrError::BadAddress(s.to_string()))?;
367

            
368
1516
            BridgeAddrInner::HostPort(name.to_string(), port)
369
        } else {
370
73
            return Err(BridgeAddrError::BadAddress(s.to_string()));
371
        }))
372
2292
    }
373
}
374

            
375
impl FromStr for PtTargetAddr {
376
    type Err = BridgeAddrError;
377

            
378
1400
    fn from_str(s: &str) -> Result<Self, Self::Err> {
379
1400
        Ok(if s == NONE_ADDR {
380
2
            PtTargetAddr::None
381
        } else {
382
1398
            Some(BridgeAddr::from_str(s)?).into()
383
        })
384
1400
    }
385
}
386

            
387
impl PtTargetAddr {
388
    /// Obtain an `Option<BridgeAddrInner>` containing references
389
    ///
390
    /// This is a useful helper for display-like implementations,
391
    /// which can then implement for `PtTargetAddr` in terms of the impls for `BridgeAddrInner`.
392
    ///
393
    /// (See the code comment for `PtTargetAddr`.)
394
388
    fn as_bridge_ref(&self) -> Option<BridgeAddrInner<&SocketAddr, &str>> {
395
388
        match self {
396
195
            PtTargetAddr::IpPort(addr) => Some(BridgeAddrInner::IpPort(addr)),
397
191
            PtTargetAddr::HostPort(host, port) => Some(BridgeAddrInner::HostPort(host, *port)),
398
2
            PtTargetAddr::None => None,
399
        }
400
388
    }
401
}
402

            
403
impl<SA: Display, HN: Display> Display for BridgeAddrInner<SA, HN> {
404
959
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405
959
        match self {
406
514
            BridgeAddrInner::IpPort(addr) => write!(f, "{}", addr),
407
445
            BridgeAddrInner::HostPort(host, port) => write!(f, "{}:{}", host, port),
408
        }
409
959
    }
410
}
411

            
412
// impl Display for BridgeAddr is done with derive_more, on the struct definition.
413

            
414
impl Display for PtTargetAddr {
415
388
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416
388
        match self.as_bridge_ref() {
417
386
            Some(b) => write!(f, "{}", b),
418
2
            None => write!(f, "{}", NONE_ADDR),
419
        }
420
388
    }
421
}
422

            
423
impl<SA: Debug + Redactable, HN: Debug + Display + AsRef<str>> Redactable
424
    for BridgeAddrInner<SA, HN>
425
{
426
    #[allow(clippy::string_slice)] // TODO
427
    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
428
        match self {
429
            BridgeAddrInner::IpPort(a) => a.display_redacted(f),
430
            BridgeAddrInner::HostPort(host, port) => write!(f, "{}…:{}", &host.as_ref()[..2], port),
431
        }
432
    }
433
}
434

            
435
impl Redactable for BridgeAddr {
436
    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
437
        self.0.display_redacted(f)
438
    }
439
}
440

            
441
impl Redactable for PtTargetAddr {
442
    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
443
        match self.as_bridge_ref() {
444
            Some(b) => b.display_redacted(f),
445
            None => write!(f, "{}", NONE_ADDR),
446
        }
447
    }
448
}
449

            
450
/// A set of options to be passed along to a pluggable transport along with a
451
/// single target bridge relay.
452
///
453
/// These options typically describe aspects of the targeted bridge relay that
454
/// are not included in its address and Tor keys, such as additional
455
/// transport-specific keys or parameters.
456
///
457
/// This type is _not_ for settings that apply to _all_ of the connections over
458
/// a transport.
459
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
460
#[serde(into = "Vec<(String, String)>", try_from = "Vec<(String, String)>")]
461
pub struct PtTargetSettings {
462
    /// A list of (key,value) pairs
463
    settings: Vec<(String, String)>,
464
}
465

            
466
impl PtTargetSettings {
467
    /// Return an error if `k,v` is not a valid setting.
468
2591
    fn check_setting(k: &str, v: &str) -> Result<(), PtTargetInvalidSetting> {
469
        // Unfortunately the spec is not very clear about the valid syntax.
470
        // https://gitlab.torproject.org/tpo/core/torspec/-/issues/173
471
        //
472
        // For now we reject things that cannot be represented in a bridge line
473
19408
        if k.find(|c: char| c == '=' || c.is_whitespace()).is_some() {
474
2
            return Err(PtTargetInvalidSetting::Key(k.to_string()));
475
2589
        }
476
16251
        if v.find(|c: char| c.is_whitespace()).is_some() {
477
2
            return Err(PtTargetInvalidSetting::Value(v.to_string()));
478
2587
        }
479
2587
        Ok(())
480
2591
    }
481

            
482
    /// Add `k,v` to this list of settings, if it is valid.
483
1071
    fn push_setting(
484
1071
        &mut self,
485
1071
        k: impl Into<String>,
486
1071
        v: impl Into<String>,
487
1071
    ) -> Result<(), PtTargetInvalidSetting> {
488
1071
        let k = k.into();
489
1071
        let v = v.into();
490
1071
        Self::check_setting(&k, &v)?;
491
1071
        self.settings.push((k, v));
492
1071
        Ok(())
493
1071
    }
494

            
495
    /// Return the inner list of (key, value) pairs
496
819
    pub fn into_inner(self) -> Vec<(String, String)> {
497
819
        self.settings
498
819
    }
499
}
500

            
501
impl TryFrom<Vec<(String, String)>> for PtTargetSettings {
502
    type Error = PtTargetInvalidSetting;
503

            
504
8
    fn try_from(settings: Vec<(String, String)>) -> Result<Self, Self::Error> {
505
8
        for (k, v) in settings.iter() {
506
8
            Self::check_setting(k, v)?;
507
        }
508
4
        Ok(Self { settings })
509
8
    }
510
}
511

            
512
impl From<PtTargetSettings> for Vec<(String, String)> {
513
4
    fn from(settings: PtTargetSettings) -> Self {
514
4
        settings.settings
515
4
    }
516
}
517

            
518
/// The set of information passed to the  pluggable transport subsystem in order
519
/// to establish a connection to a bridge relay.
520
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
521
pub struct PtTarget {
522
    /// The transport to be used.
523
    transport: PtTransportName,
524
    /// The address of the bridge relay, if any.
525
    addr: PtTargetAddr,
526
    /// Any additional settings used by the transport.
527
    #[serde(default)]
528
    settings: PtTargetSettings,
529
}
530

            
531
/// Invalid PT parameter setting
532
#[derive(Error, Clone, Debug, Eq, PartialEq)]
533
#[non_exhaustive]
534
pub enum PtTargetInvalidSetting {
535
    /// Currently: the key contains whitespace or `=`
536
    ///
537
    /// Will probably be generated for a greater variety of values
538
    /// when the spec is more nailed down.
539
    #[error("key {0:?} has invalid or unsupported syntax")]
540
    Key(String),
541

            
542
    /// Currently: the value contains whitespace
543
    ///
544
    /// Will probably be generated for a greater variety of values
545
    /// when the spec is more nailed down.
546
    #[error("value {0:?} has invalid or unsupported syntax")]
547
    Value(String),
548
}
549

            
550
impl PtTarget {
551
    /// Create a new `PtTarget` (with no settings)
552
2402
    pub fn new(transport: PtTransportName, addr: PtTargetAddr) -> Self {
553
2402
        PtTarget {
554
2402
            transport,
555
2402
            addr,
556
2402
            settings: Default::default(),
557
2402
        }
558
2402
    }
559

            
560
    /// Add a setting (to be passed during the SOCKS handshake)
561
1071
    pub fn push_setting(
562
1071
        &mut self,
563
1071
        k: impl Into<String>,
564
1071
        v: impl Into<String>,
565
1071
    ) -> Result<(), PtTargetInvalidSetting> {
566
1071
        self.settings.push_setting(k, v)
567
1071
    }
568

            
569
    /// Get the transport name
570
386
    pub fn transport(&self) -> &PtTransportName {
571
386
        &self.transport
572
386
    }
573

            
574
    /// Get the transport target address (or host and port)
575
380
    pub fn addr(&self) -> &PtTargetAddr {
576
380
        &self.addr
577
380
    }
578

            
579
    /// Iterate over the PT setting strings
580
378
    pub fn settings(&self) -> impl Iterator<Item = (&str, &str)> {
581
390
        self.settings.settings.iter().map(|(k, v)| (&**k, &**v))
582
378
    }
583

            
584
    /// Return all the advertized socket addresses to which this target may
585
    /// connect.
586
    ///
587
    /// Returns `Some(&[])` if there is no way to connect to this target, and
588
    /// `None` if this target does not use `SocketAddr` to connect
589
    ///
590
    /// NOTE that these are not necessarily an address to which you can open a
591
    /// TCP connection! The address will be interpreted by the implementation of
592
    /// this pluggable transport.
593
6
    pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
594
6
        match self {
595
            PtTarget {
596
2
                addr: PtTargetAddr::IpPort(addr),
597
                ..
598
2
            } => Some(std::slice::from_ref(addr)),
599

            
600
4
            _ => None,
601
        }
602
6
    }
603

            
604
    /// Consume the `PtTarget` and return the component parts
605
819
    pub fn into_parts(self) -> (PtTransportName, PtTargetAddr, PtTargetSettings) {
606
819
        (self.transport, self.addr, self.settings)
607
819
    }
608
}
609

            
610
impl Display for PtTarget {
611
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
612
        write!(
613
            f,
614
            "{} @ {} with {:?}",
615
            self.transport, self.addr, self.settings
616
        )
617
    }
618
}
619

            
620
/// The way to approach a single relay in order to open a channel.
621
///
622
/// For direct connections, this is simply an address.  For connections via a
623
/// pluggable transport, this includes information about the transport, and any
624
/// address and settings information that transport requires.
625
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
626
#[non_exhaustive]
627
pub enum ChannelMethod {
628
    /// Connect to the relay directly at one of several addresses.
629
    Direct(Vec<std::net::SocketAddr>),
630

            
631
    /// Connect to a bridge relay via a pluggable transport.
632
    #[cfg(feature = "pt-client")]
633
    Pluggable(PtTarget),
634
}
635

            
636
impl ChannelMethod {
637
    /// Return all the advertized socket addresses to which this method may connect.
638
    ///
639
    /// Returns `Some(&[])` if there is no way to connect to this target, and
640
    /// `None` if this target does not use `SocketAddr` to connect
641
    ///
642
    /// NOTE that these are not necessarily an address to which you can open a
643
    /// TCP connection! If this `ChannelMethod` is using a non-`Direct`
644
    /// transport, then this address will be interpreted by that transport's
645
    /// implementation.
646
7946
    pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
647
7946
        match self {
648
7940
            ChannelMethod::Direct(addr) => Some(addr.as_ref()),
649

            
650
            #[cfg(feature = "pt-client")]
651
6
            ChannelMethod::Pluggable(t) => t.socket_addrs(),
652
        }
653
7946
    }
654

            
655
    /// Return a BridgeAddr that this ChannelMethod uses.
656
    //
657
    // TODO this is kind of weird, what does Some(PtTargetAddr::None) mean?
658
8
    pub fn target_addr(&self) -> Option<PtTargetAddr> {
659
2
        match self {
660
2
            ChannelMethod::Direct(addr) if !addr.is_empty() => Some(PtTargetAddr::IpPort(addr[0])),
661

            
662
            #[cfg(feature = "pt-client")]
663
6
            ChannelMethod::Pluggable(PtTarget { addr, .. }) => Some(addr.clone()),
664

            
665
            _ => None,
666
        }
667
8
    }
668

            
669
    /// Return the [`SocketAddr`] if this method is [`ChannelMethod::Direct`] and there is one and
670
    /// only one available address.
671
    ///
672
    /// This is single address requirement is very important as this call is used during the
673
    /// channel handshake on which we need the actual peer address we are connected to and not all
674
    /// the possibilities.
675
    ///
676
    /// When connecting or accepting, that address is put in the OwnedChanTarget and so this
677
    /// extracts it.
678
    pub fn unique_direct_addr(&self) -> Option<SocketAddr> {
679
        match self {
680
            Self::Direct(addrs) => match addrs.as_slice() {
681
                [addr] => Some(*addr),
682
                // Remember, more than one, we don't have a single address.
683
                _ => None,
684
            },
685
            #[cfg(feature = "pt-client")]
686
            Self::Pluggable(_) => None,
687
        }
688
    }
689

            
690
    /// Return true if this is a method for a direct connection.
691
71
    pub fn is_direct(&self) -> bool {
692
71
        matches!(self, ChannelMethod::Direct(_))
693
71
    }
694

            
695
    /// Return an identifier for the Transport to be used by this `ChannelMethod`.
696
8
    pub fn transport_id(&self) -> TransportId {
697
8
        match self {
698
2
            ChannelMethod::Direct(_) => TransportId::default(),
699
            #[cfg(feature = "pt-client")]
700
6
            ChannelMethod::Pluggable(target) => target.transport().clone().into(),
701
        }
702
8
    }
703

            
704
    ///
705
    /// Change this `ChannelMethod` by removing every socket address that
706
    /// does not satisfy `pred`.
707
    ///
708
    /// `Hostname` and `None` addresses are never removed.
709
    ///
710
    /// Return an error if we have removed every address.
711
75
    pub fn retain_addrs<P>(&mut self, pred: P) -> Result<(), RetainAddrsError>
712
75
    where
713
75
        P: Fn(&std::net::SocketAddr) -> bool,
714
    {
715
        #[cfg(feature = "pt-client")]
716
        use PtTargetAddr as Pt;
717

            
718
67
        match self {
719
67
            ChannelMethod::Direct(d) if d.is_empty() => {}
720
67
            ChannelMethod::Direct(d) => {
721
67
                d.retain(pred);
722
67
                if d.is_empty() {
723
2
                    return Err(RetainAddrsError::NoAddrsLeft);
724
65
                }
725
            }
726
            #[cfg(feature = "pt-client")]
727
8
            ChannelMethod::Pluggable(PtTarget { addr, .. }) => match addr {
728
4
                Pt::IpPort(a) => {
729
4
                    if !pred(a) {
730
2
                        *addr = Pt::None;
731
2
                        return Err(RetainAddrsError::NoAddrsLeft);
732
2
                    }
733
                }
734
2
                Pt::HostPort(_, _) => {}
735
2
                Pt::None => {}
736
            },
737
        }
738
71
        Ok(())
739
75
    }
740

            
741
    /// Return true if every method to contact `self` is also a method to
742
    /// contact `other`.
743
12
    pub fn contained_by(&self, other: &ChannelMethod) -> bool {
744
        use ChannelMethod as CM;
745
12
        match (self, other) {
746
6
            (CM::Direct(our_addrs), CM::Direct(their_addrs)) => {
747
17
                our_addrs.iter().all(|a| their_addrs.contains(a))
748
            }
749
            #[cfg(feature = "pt-client")]
750
6
            (CM::Pluggable(our_target), CM::Pluggable(their_target)) => our_target == their_target,
751
            #[cfg(feature = "pt-client")]
752
            (_, _) => false,
753
        }
754
12
    }
755
}
756

            
757
/// An error that occurred while filtering addresses from a ChanMethod.
758
#[derive(Clone, Debug, thiserror::Error)]
759
pub enum RetainAddrsError {
760
    /// We removed all of the addresses from this method.
761
    #[error("All addresses were removed.")]
762
    NoAddrsLeft,
763
}
764

            
765
impl HasAddrs for PtTargetAddr {
766
8
    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
767
8
        match self {
768
4
            PtTargetAddr::IpPort(sockaddr) => slice::from_ref(sockaddr),
769
4
            PtTargetAddr::HostPort(..) | PtTargetAddr::None => &[],
770
        }
771
8
        .iter()
772
8
        .copied()
773
8
    }
774
}
775

            
776
impl HasAddrs for ChannelMethod {
777
2772
    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
778
2772
        let r = match self {
779
2772
            ChannelMethod::Direct(addrs) => Either::Left(addrs.iter().copied()),
780
            #[cfg(feature = "pt-client")]
781
            ChannelMethod::Pluggable(pt) => Either::Right(pt.addr.addrs()),
782
        };
783

            
784
        // Unfortunately, when pt-client is configured out, the compiler can't infer
785
        // the type for Either::Right.  Ideally we'd use Void rather than iter::Empty
786
        // but Void doesn't implement Iterator.
787
        #[cfg(not(feature = "pt-client"))]
788
        let _: &Either<_, std::iter::Empty<_>> = &r;
789

            
790
2772
        r
791
2772
    }
792
}
793

            
794
#[cfg(test)]
795
mod test {
796
    // @@ begin test lint list maintained by maint/add_warning @@
797
    #![allow(clippy::bool_assert_comparison)]
798
    #![allow(clippy::clone_on_copy)]
799
    #![allow(clippy::dbg_macro)]
800
    #![allow(clippy::mixed_attributes_style)]
801
    #![allow(clippy::print_stderr)]
802
    #![allow(clippy::print_stdout)]
803
    #![allow(clippy::single_char_pattern)]
804
    #![allow(clippy::unwrap_used)]
805
    #![allow(clippy::unchecked_time_subtraction)]
806
    #![allow(clippy::useless_vec)]
807
    #![allow(clippy::needless_pass_by_value)]
808
    #![allow(clippy::string_slice)] // See arti#2571
809
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
810
    use super::*;
811
    use itertools::Itertools;
812

            
813
    #[test]
814
    fn builtin() {
815
        assert!(TransportId::default().is_builtin());
816
        assert_eq!(
817
            TransportId::default(),
818
            "<none>".parse().expect("Couldn't parse default ID")
819
        );
820
    }
821

            
822
    #[test]
823
    #[cfg(not(feature = "pt-client"))]
824
    fn nosupport() {
825
        // We should get this error whenever we parse a non-default PT and we have no PT support.
826
        assert!(matches!(
827
            TransportId::from_str("obfs4"),
828
            Err(TransportIdError::NoSupport)
829
        ));
830
    }
831

            
832
    #[test]
833
    #[cfg(feature = "pt-client")]
834
    fn wellformed() {
835
        for id in &["snowflake", "obfs4", "_ohai", "Z", "future_WORK2"] {
836
            assert!(is_well_formed_id(id));
837
        }
838

            
839
        for id in &[" ", "Mölm", "12345", ""] {
840
            assert!(!is_well_formed_id(id));
841
        }
842
    }
843

            
844
    #[test]
845
    #[cfg(feature = "pt-client")]
846
    fn parsing() {
847
        let obfs = TransportId::from_str("obfs4").unwrap();
848
        let dflt = TransportId::default();
849
        let dflt2 = TransportId::from_str("<none>").unwrap();
850
        let dflt3 = TransportId::from_str("-").unwrap();
851
        let dflt4 = TransportId::from_str("").unwrap();
852
        let dflt5 = TransportId::from_str("bridge").unwrap();
853
        let snow = TransportId::from_str("snowflake").unwrap();
854
        let obfs_again = TransportId::from_str("obfs4").unwrap();
855

            
856
        assert_eq!(obfs, obfs_again);
857
        assert_eq!(dflt, dflt2);
858
        assert_eq!(dflt, dflt3);
859
        assert_eq!(dflt, dflt4);
860
        assert_eq!(dflt, dflt5);
861
        assert_ne!(snow, obfs);
862
        assert_ne!(snow, dflt);
863

            
864
        assert_eq!(dflt.to_string(), "-");
865

            
866
        assert!(matches!(
867
            TransportId::from_str("12345"),
868
            Err(TransportIdError::BadId(_))
869
        ));
870
        assert!(matches!(
871
            TransportId::from_str("Bridge"),
872
            Err(TransportIdError::BadId(_))
873
        ));
874
    }
875

            
876
    #[test]
877
    fn addr() {
878
        let chk_bridge_addr = |a: &PtTargetAddr, addr: &str| {
879
            let ba: BridgeAddr = addr.parse().unwrap();
880
            assert_eq!(&ba.to_string(), addr);
881

            
882
            assert_eq!(&PtTargetAddr::from(Some(ba.clone())), a);
883
            let reba: Option<BridgeAddr> = a.clone().into();
884
            assert_eq!(reba.as_ref(), Some(&ba));
885
        };
886

            
887
        for addr in &["1.2.3.4:555", "[::1]:9999"] {
888
            let a: PtTargetAddr = addr.parse().unwrap();
889
            assert_eq!(&a.to_string(), addr);
890

            
891
            let sa: SocketAddr = addr.parse().unwrap();
892
            assert_eq!(a.addrs().collect_vec(), &[sa]);
893

            
894
            chk_bridge_addr(&a, addr);
895
        }
896

            
897
        for addr in &["www.example.com:9100", "-"] {
898
            let a: PtTargetAddr = addr.parse().unwrap();
899
            assert_eq!(&a.to_string(), addr);
900
            assert_eq!(a.addrs().collect_vec(), &[]);
901

            
902
            if a == PtTargetAddr::None {
903
                let e = BridgeAddr::from_str(addr).unwrap_err();
904
                assert!(matches!(e, BridgeAddrError::BadAddress(_)));
905
            } else {
906
                chk_bridge_addr(&a, addr);
907
            }
908
        }
909

            
910
        for addr in &["foobar", "<<<>>>"] {
911
            let e = PtTargetAddr::from_str(addr).unwrap_err();
912
            assert!(matches!(e, BridgeAddrError::BadAddress(_)));
913

            
914
            let e = BridgeAddr::from_str(addr).unwrap_err();
915
            assert!(matches!(e, BridgeAddrError::BadAddress(_)));
916
        }
917
    }
918

            
919
    #[test]
920
    fn transport_id() {
921
        let id1: TransportId = "<none>".parse().unwrap();
922
        assert!(id1.is_builtin());
923
        assert_eq!(id1.to_string(), "-".to_string());
924

            
925
        #[cfg(feature = "pt-client")]
926
        {
927
            let id2: TransportId = "obfs4".parse().unwrap();
928
            assert_ne!(id2, id1);
929
            assert!(!id2.is_builtin());
930
            assert_eq!(id2.to_string(), "obfs4");
931

            
932
            assert!(matches!(
933
                TransportId::from_str("==="),
934
                Err(TransportIdError::BadId(_))
935
            ));
936
        }
937

            
938
        #[cfg(not(feature = "pt-client"))]
939
        {
940
            assert!(matches!(
941
                TransportId::from_str("obfs4"),
942
                Err(TransportIdError::NoSupport)
943
            ));
944
        }
945
    }
946

            
947
    #[test]
948
    fn settings() {
949
        let s = PtTargetSettings::try_from(vec![]).unwrap();
950
        assert_eq!(Vec::<_>::from(s), vec![]);
951

            
952
        let v = vec![("abc".into(), "def".into()), ("ghi".into(), "jkl".into())];
953
        let s = PtTargetSettings::try_from(v.clone()).unwrap();
954
        assert_eq!(Vec::<_>::from(s), v);
955

            
956
        let v = vec![("a=b".into(), "def".into())];
957
        let s = PtTargetSettings::try_from(v);
958
        assert!(matches!(s, Err(PtTargetInvalidSetting::Key(_))));
959

            
960
        let v = vec![("abc".into(), "d ef".into())];
961
        let s = PtTargetSettings::try_from(v);
962
        assert!(matches!(s, Err(PtTargetInvalidSetting::Value(_))));
963
    }
964

            
965
    #[test]
966
    fn chanmethod_direct() {
967
        let a1 = "127.0.0.1:8080".parse().unwrap();
968
        let a2 = "127.0.0.2:8181".parse().unwrap();
969
        let a3 = "127.0.0.3:8282".parse().unwrap();
970

            
971
        let m = ChannelMethod::Direct(vec![a1, a2]);
972
        assert_eq!(m.socket_addrs(), Some(&[a1, a2][..]));
973
        assert_eq!((m.target_addr()), Some(PtTargetAddr::IpPort(a1)));
974
        assert!(m.is_direct());
975
        assert_eq!(m.transport_id(), TransportId::default());
976

            
977
        let m2 = ChannelMethod::Direct(vec![a1, a2, a3]);
978
        assert!(m.contained_by(&m));
979
        assert!(m.contained_by(&m2));
980
        assert!(!m2.contained_by(&m));
981

            
982
        let mut m3 = m2.clone();
983
        m3.retain_addrs(|a| a.port() != 8282).unwrap();
984
        assert_eq!(m3, m);
985
        assert_ne!(m3, m2);
986
    }
987

            
988
    #[test]
989
    #[cfg(feature = "pt-client")]
990
    fn chanmethod_pt() {
991
        use itertools::Itertools;
992

            
993
        let transport = "giraffe".parse().unwrap();
994
        let addr1 = PtTargetAddr::HostPort("pt.example.com".into(), 1234);
995
        let target1 = PtTarget::new("giraffe".parse().unwrap(), addr1.clone());
996
        let m1 = ChannelMethod::Pluggable(target1);
997

            
998
        let addr2 = PtTargetAddr::IpPort("127.0.0.1:567".parse().unwrap());
999
        let target2 = PtTarget::new("giraffe".parse().unwrap(), addr2.clone());
        let m2 = ChannelMethod::Pluggable(target2);
        let addr3 = PtTargetAddr::None;
        let target3 = PtTarget::new("giraffe".parse().unwrap(), addr3.clone());
        let m3 = ChannelMethod::Pluggable(target3);
        assert_eq!(m1.socket_addrs(), None);
        assert_eq!(
            m2.socket_addrs(),
            Some(&["127.0.0.1:567".parse().unwrap()][..])
        );
        assert_eq!(m3.socket_addrs(), None);
        assert_eq!(m1.target_addr(), Some(addr1));
        assert_eq!(m2.target_addr(), Some(addr2));
        assert_eq!(m3.target_addr(), Some(addr3));
        assert!(!m1.is_direct());
        assert!(!m2.is_direct());
        assert!(!m3.is_direct());
        assert_eq!(m1.transport_id(), transport);
        assert_eq!(m2.transport_id(), transport);
        assert_eq!(m3.transport_id(), transport);
        for v in [&m1, &m2, &m3].iter().combinations(2) {
            let first = v[0];
            let second = v[1];
            assert_eq!(first.contained_by(second), first == second);
        }
        let mut m1new = m1.clone();
        let mut m2new = m2.clone();
        let mut m3new = m3.clone();
        // this will retain the IpPort target, and ignore the other targets.
        m1new.retain_addrs(|a| a.port() == 567).unwrap();
        m2new.retain_addrs(|a| a.port() == 567).unwrap();
        m3new.retain_addrs(|a| a.port() == 567).unwrap();
        assert_eq!(m1new, m1);
        assert_eq!(m2new, m2);
        assert_eq!(m3new, m3);
        // But if we try to remove the ipport target, we get an error.
        assert!(matches!(
            m2new.retain_addrs(|a| a.port() == 999),
            Err(RetainAddrsError::NoAddrsLeft)
        ));
    }
}