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
4449
    fn from_str(s: &str) -> Result<Self, Self::Err> {
73
4449
        s.to_string().try_into()
74
4449
    }
75
}
76

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

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

            
89
impl Display for PtTransportName {
90
1125
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91
1125
        Display::fmt(&self.0, f)
92
1125
    }
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
3450
    fn from_str(s: &str) -> Result<Self, Self::Err> {
114
3450
        if BUILT_IN_IDS.contains(&s) {
115
2077
            return Ok(TransportId(Inner::BuiltIn));
116
1373
        };
117

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

            
124
        #[cfg(not(feature = "pt-client"))]
125
        Err(TransportIdError::NoSupport)
126
3450
    }
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
4467
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
4467
    let mut bytes = s.bytes();
155

            
156
4467
    if let Some(first) = bytes.next() {
157
4465
        (first.is_ascii_alphabetic() || first == b'_')
158
20313
            && bytes.all(|b| b.is_ascii_alphanumeric() || b == b'_')
159
4396
            && !s.eq_ignore_ascii_case("bridge")
160
    } else {
161
2
        false
162
    }
163
4467
}
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
2602
    pub fn is_builtin(&self) -> bool {
197
2602
        self.0 == Inner::BuiltIn
198
2602
    }
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
1003
    pub fn as_pluggable(&self) -> Option<&PtTransportName> {
206
1003
        match &self.0 {
207
            Inner::BuiltIn => None,
208
            #[cfg(feature = "pt-client")]
209
1003
            Inner::Pluggable(pt) => Some(pt),
210
        }
211
1003
    }
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
1829
    pub fn into_pluggable(self) -> Option<PtTransportName> {
219
1829
        match self.0 {
220
472
            Inner::BuiltIn => None,
221
            #[cfg(feature = "pt-client")]
222
1357
            Inner::Pluggable(pt) => Some(pt),
223
        }
224
1829
    }
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
826
    pub fn new_addr_from_sockaddr(sa: SocketAddr) -> Self {
300
826
        BridgeAddr(BridgeAddrInner::IpPort(sa))
301
826
    }
302

            
303
    /// If this is a numeric address, return it as a `SocketAddr`
304
1357
    pub fn as_socketaddr(&self) -> Option<&SocketAddr> {
305
1357
        match &self.0 {
306
1239
            BridgeAddrInner::IpPort(sa) => Some(sa),
307
118
            BridgeAddrInner::HostPort(..) => None,
308
        }
309
1357
    }
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
118
    pub fn as_host_port(&self) -> Option<(&str, u16)> {
318
118
        match &self.0 {
319
            BridgeAddrInner::IpPort(..) => None,
320
118
            BridgeAddrInner::HostPort(hn, port) => Some((hn, *port)),
321
        }
322
118
    }
323
}
324

            
325
impl From<PtTargetAddr> for Option<BridgeAddr> {
326
630
    fn from(pt: PtTargetAddr) -> Option<BridgeAddr> {
327
630
        match pt {
328
4
            PtTargetAddr::IpPort(sa) => Some(BridgeAddrInner::IpPort(sa)),
329
626
            PtTargetAddr::HostPort(hn, p) => Some(BridgeAddrInner::HostPort(hn, p)),
330
            PtTargetAddr::None => None,
331
        }
332
630
        .map(BridgeAddr)
333
630
    }
334
}
335
impl From<Option<BridgeAddr>> for PtTargetAddr {
336
2138
    fn from(pt: Option<BridgeAddr>) -> PtTargetAddr {
337
2138
        match pt.map(|ba| ba.0) {
338
187
            Some(BridgeAddrInner::IpPort(sa)) => PtTargetAddr::IpPort(sa),
339
1951
            Some(BridgeAddrInner::HostPort(hn, p)) => PtTargetAddr::HostPort(hn, p),
340
            None => PtTargetAddr::None,
341
        }
342
2138
    }
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
2148
    fn from_str(s: &str) -> Result<Self, Self::Err> {
361
2148
        Ok(BridgeAddr(if let Ok(addr) = s.parse() {
362
659
            BridgeAddrInner::IpPort(addr)
363
1489
        } else if let Some((name, port)) = s.rsplit_once(':') {
364
1420
            let port = port
365
1420
                .parse()
366
1420
                .map_err(|_| BridgeAddrError::BadAddress(s.to_string()))?;
367

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

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

            
378
1312
    fn from_str(s: &str) -> Result<Self, Self::Err> {
379
1312
        Ok(if s == NONE_ADDR {
380
2
            PtTargetAddr::None
381
        } else {
382
1310
            Some(BridgeAddr::from_str(s)?).into()
383
        })
384
1312
    }
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
364
    fn as_bridge_ref(&self) -> Option<BridgeAddrInner<&SocketAddr, &str>> {
395
364
        match self {
396
183
            PtTargetAddr::IpPort(addr) => Some(BridgeAddrInner::IpPort(addr)),
397
179
            PtTargetAddr::HostPort(host, port) => Some(BridgeAddrInner::HostPort(host, *port)),
398
2
            PtTargetAddr::None => None,
399
        }
400
364
    }
401
}
402

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
789
2596
        r
790
2596
    }
791
}
792

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

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

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

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

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

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

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

            
862
        assert_eq!(dflt.to_string(), "-");
863

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

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

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

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

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

            
892
            chk_bridge_addr(&a, addr);
893
        }
894

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
996
        let addr2 = PtTargetAddr::IpPort("127.0.0.1:567".parse().unwrap());
997
        let target2 = PtTarget::new("giraffe".parse().unwrap(), addr2.clone());
998
        let m2 = ChannelMethod::Pluggable(target2);
999

            
        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)
        ));
    }
}