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 safelog::Redactable;
15
use serde::{Deserialize, Serialize};
16
use thiserror::Error;
17

            
18
use crate::HasAddrs;
19

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

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

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

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

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

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

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

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

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

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

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

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

            
112
2870
    fn from_str(s: &str) -> Result<Self, Self::Err> {
113
2870
        if BUILT_IN_IDS.contains(&s) {
114
1727
            return Ok(TransportId(Inner::BuiltIn));
115
1143
        };
116

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

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

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

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

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

            
155
3717
    if let Some(first) = bytes.next() {
156
3715
        (first.is_ascii_alphabetic() || first == b'_')
157
16913
            && bytes.all(|b| b.is_ascii_alphanumeric() || b == b'_')
158
3656
            && !s.eq_ignore_ascii_case("bridge")
159
    } else {
160
2
        false
161
    }
162
3717
}
163

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
324
impl From<PtTargetAddr> for Option<BridgeAddr> {
325
561
    fn from(pt: PtTargetAddr) -> Option<BridgeAddr> {
326
561
        match pt {
327
78
            PtTargetAddr::IpPort(sa) => Some(BridgeAddrInner::IpPort(sa)),
328
483
            PtTargetAddr::HostPort(hn, p) => Some(BridgeAddrInner::HostPort(hn, p)),
329
            PtTargetAddr::None => None,
330
        }
331
561
        .map(BridgeAddr)
332
561
    }
333
}
334
impl From<Option<BridgeAddr>> for PtTargetAddr {
335
1778
    fn from(pt: Option<BridgeAddr>) -> PtTargetAddr {
336
1821
        match pt.map(|ba| ba.0) {
337
157
            Some(BridgeAddrInner::IpPort(sa)) => PtTargetAddr::IpPort(sa),
338
1621
            Some(BridgeAddrInner::HostPort(hn, p)) => PtTargetAddr::HostPort(hn, p),
339
            None => PtTargetAddr::None,
340
        }
341
1778
    }
342
}
343

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

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

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

            
367
1180
            BridgeAddrInner::HostPort(name.to_string(), port)
368
        } else {
369
59
            return Err(BridgeAddrError::BadAddress(s.to_string()));
370
        }))
371
1788
    }
372
}
373

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
608
/// The way to approach a single relay in order to open a channel.
609
///
610
/// For direct connections, this is simply an address.  For connections via a
611
/// pluggable transport, this includes information about the transport, and any
612
/// address and settings information that transport requires.
613
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
614
#[non_exhaustive]
615
pub enum ChannelMethod {
616
    /// Connect to the relay directly at one of several addresses.
617
    Direct(Vec<std::net::SocketAddr>),
618

            
619
    /// Connect to a bridge relay via a pluggable transport.
620
    #[cfg(feature = "pt-client")]
621
    Pluggable(PtTarget),
622
}
623

            
624
impl ChannelMethod {
625
    /// Return all the advertized socket addresses to which this method may connect.
626
    ///
627
    /// Returns `Some(&[])` if there is no way to connect to this target, and
628
    /// `None` if this target does not use `SocketAddr` to connect
629
    ///
630
    /// NOTE that these are not necessarily an address to which you can open a
631
    /// TCP connection! If this `ChannelMethod` is using a non-`Direct`
632
    /// transport, then this address will be interpreted by that transport's
633
    /// implementation.
634
4614
    pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
635
4614
        match self {
636
4608
            ChannelMethod::Direct(addr) => Some(addr.as_ref()),
637

            
638
            #[cfg(feature = "pt-client")]
639
6
            ChannelMethod::Pluggable(t) => t.socket_addrs(),
640
        }
641
4614
    }
642

            
643
    /// Return a BridgeAddr that this ChannelMethod uses.
644
    //
645
    // TODO this is kind of weird, what does Some(PtTargetAddr::None) mean?
646
57
    pub fn target_addr(&self) -> Option<PtTargetAddr> {
647
51
        match self {
648
51
            ChannelMethod::Direct(addr) if !addr.is_empty() => Some(PtTargetAddr::IpPort(addr[0])),
649

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

            
653
            _ => None,
654
        }
655
57
    }
656

            
657
    /// Return true if this is a method for a direct connection.
658
57
    pub fn is_direct(&self) -> bool {
659
57
        matches!(self, ChannelMethod::Direct(_))
660
57
    }
661

            
662
    /// Return an identifier for the Transport to be used by this `ChannelMethod`.
663
8
    pub fn transport_id(&self) -> TransportId {
664
8
        match self {
665
2
            ChannelMethod::Direct(_) => TransportId::default(),
666
            #[cfg(feature = "pt-client")]
667
6
            ChannelMethod::Pluggable(target) => target.transport().clone().into(),
668
        }
669
8
    }
670

            
671
    ///
672
    /// Change this `ChannelMethod` by removing every socket address that
673
    /// does not satisfy `pred`.
674
    ///
675
    /// `Hostname` and `None` addresses are never removed.
676
    ///
677
    /// Return an error if we have removed every address.
678
20
    pub fn retain_addrs<P>(&mut self, pred: P) -> Result<(), RetainAddrsError>
679
20
    where
680
20
        P: Fn(&std::net::SocketAddr) -> bool,
681
20
    {
682
        #[cfg(feature = "pt-client")]
683
        use PtTargetAddr as Pt;
684

            
685
12
        match self {
686
12
            ChannelMethod::Direct(d) if d.is_empty() => {}
687
12
            ChannelMethod::Direct(d) => {
688
12
                d.retain(pred);
689
12
                if d.is_empty() {
690
                    return Err(RetainAddrsError::NoAddrsLeft);
691
12
                }
692
            }
693
            #[cfg(feature = "pt-client")]
694
8
            ChannelMethod::Pluggable(PtTarget { addr, .. }) => match addr {
695
4
                Pt::IpPort(a) => {
696
4
                    if !pred(a) {
697
2
                        *addr = Pt::None;
698
2
                        return Err(RetainAddrsError::NoAddrsLeft);
699
2
                    }
700
                }
701
2
                Pt::HostPort(_, _) => {}
702
2
                Pt::None => {}
703
            },
704
        }
705
18
        Ok(())
706
20
    }
707

            
708
    /// Return true if every method to contact `self` is also a method to
709
    /// contact `other`.
710
12
    pub fn contained_by(&self, other: &ChannelMethod) -> bool {
711
        use ChannelMethod as CM;
712
12
        match (self, other) {
713
6
            (CM::Direct(our_addrs), CM::Direct(their_addrs)) => {
714
17
                our_addrs.iter().all(|a| their_addrs.contains(a))
715
            }
716
            #[cfg(feature = "pt-client")]
717
6
            (CM::Pluggable(our_target), CM::Pluggable(their_target)) => our_target == their_target,
718
            #[cfg(feature = "pt-client")]
719
            (_, _) => false,
720
        }
721
12
    }
722
}
723

            
724
/// An error that occurred while filtering addresses from a ChanMethod.
725
#[derive(Clone, Debug, thiserror::Error)]
726
pub enum RetainAddrsError {
727
    /// We removed all of the addresses from this method.
728
    #[error("All addresses were removed.")]
729
    NoAddrsLeft,
730
}
731

            
732
impl HasAddrs for PtTargetAddr {
733
8
    fn addrs(&self) -> &[SocketAddr] {
734
8
        match self {
735
4
            PtTargetAddr::IpPort(sockaddr) => slice::from_ref(sockaddr),
736
4
            PtTargetAddr::HostPort(..) | PtTargetAddr::None => &[],
737
        }
738
8
    }
739
}
740

            
741
impl HasAddrs for ChannelMethod {
742
2156
    fn addrs(&self) -> &[SocketAddr] {
743
2156
        match self {
744
2156
            ChannelMethod::Direct(addrs) => addrs,
745
            #[cfg(feature = "pt-client")]
746
            ChannelMethod::Pluggable(pt) => pt.addr.addrs(),
747
        }
748
2156
    }
749
}
750

            
751
#[cfg(test)]
752
mod test {
753
    // @@ begin test lint list maintained by maint/add_warning @@
754
    #![allow(clippy::bool_assert_comparison)]
755
    #![allow(clippy::clone_on_copy)]
756
    #![allow(clippy::dbg_macro)]
757
    #![allow(clippy::mixed_attributes_style)]
758
    #![allow(clippy::print_stderr)]
759
    #![allow(clippy::print_stdout)]
760
    #![allow(clippy::single_char_pattern)]
761
    #![allow(clippy::unwrap_used)]
762
    #![allow(clippy::unchecked_duration_subtraction)]
763
    #![allow(clippy::useless_vec)]
764
    #![allow(clippy::needless_pass_by_value)]
765
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
766
    use super::*;
767

            
768
    #[test]
769
    fn builtin() {
770
        assert!(TransportId::default().is_builtin());
771
        assert_eq!(
772
            TransportId::default(),
773
            "<none>".parse().expect("Couldn't parse default ID")
774
        );
775
    }
776

            
777
    #[test]
778
    #[cfg(not(feature = "pt-client"))]
779
    fn nosupport() {
780
        // We should get this error whenever we parse a non-default PT and we have no PT support.
781
        assert!(matches!(
782
            TransportId::from_str("obfs4"),
783
            Err(TransportIdError::NoSupport)
784
        ));
785
    }
786

            
787
    #[test]
788
    #[cfg(feature = "pt-client")]
789
    fn wellformed() {
790
        for id in &["snowflake", "obfs4", "_ohai", "Z", "future_WORK2"] {
791
            assert!(is_well_formed_id(id));
792
        }
793

            
794
        for id in &[" ", "Mölm", "12345", ""] {
795
            assert!(!is_well_formed_id(id));
796
        }
797
    }
798

            
799
    #[test]
800
    #[cfg(feature = "pt-client")]
801
    fn parsing() {
802
        let obfs = TransportId::from_str("obfs4").unwrap();
803
        let dflt = TransportId::default();
804
        let dflt2 = TransportId::from_str("<none>").unwrap();
805
        let dflt3 = TransportId::from_str("-").unwrap();
806
        let dflt4 = TransportId::from_str("").unwrap();
807
        let dflt5 = TransportId::from_str("bridge").unwrap();
808
        let snow = TransportId::from_str("snowflake").unwrap();
809
        let obfs_again = TransportId::from_str("obfs4").unwrap();
810

            
811
        assert_eq!(obfs, obfs_again);
812
        assert_eq!(dflt, dflt2);
813
        assert_eq!(dflt, dflt3);
814
        assert_eq!(dflt, dflt4);
815
        assert_eq!(dflt, dflt5);
816
        assert_ne!(snow, obfs);
817
        assert_ne!(snow, dflt);
818

            
819
        assert_eq!(dflt.to_string(), "-");
820

            
821
        assert!(matches!(
822
            TransportId::from_str("12345"),
823
            Err(TransportIdError::BadId(_))
824
        ));
825
        assert!(matches!(
826
            TransportId::from_str("Bridge"),
827
            Err(TransportIdError::BadId(_))
828
        ));
829
    }
830

            
831
    #[test]
832
    fn addr() {
833
        let chk_bridge_addr = |a: &PtTargetAddr, addr: &str| {
834
            let ba: BridgeAddr = addr.parse().unwrap();
835
            assert_eq!(&ba.to_string(), addr);
836

            
837
            assert_eq!(&PtTargetAddr::from(Some(ba.clone())), a);
838
            let reba: Option<BridgeAddr> = a.clone().into();
839
            assert_eq!(reba.as_ref(), Some(&ba));
840
        };
841

            
842
        for addr in &["1.2.3.4:555", "[::1]:9999"] {
843
            let a: PtTargetAddr = addr.parse().unwrap();
844
            assert_eq!(&a.to_string(), addr);
845

            
846
            let sa: SocketAddr = addr.parse().unwrap();
847
            assert_eq!(a.addrs(), &[sa]);
848

            
849
            chk_bridge_addr(&a, addr);
850
        }
851

            
852
        for addr in &["www.example.com:9100", "-"] {
853
            let a: PtTargetAddr = addr.parse().unwrap();
854
            assert_eq!(&a.to_string(), addr);
855
            assert_eq!(a.addrs(), &[]);
856

            
857
            if a == PtTargetAddr::None {
858
                let e = BridgeAddr::from_str(addr).unwrap_err();
859
                assert!(matches!(e, BridgeAddrError::BadAddress(_)));
860
            } else {
861
                chk_bridge_addr(&a, addr);
862
            }
863
        }
864

            
865
        for addr in &["foobar", "<<<>>>"] {
866
            let e = PtTargetAddr::from_str(addr).unwrap_err();
867
            assert!(matches!(e, BridgeAddrError::BadAddress(_)));
868

            
869
            let e = BridgeAddr::from_str(addr).unwrap_err();
870
            assert!(matches!(e, BridgeAddrError::BadAddress(_)));
871
        }
872
    }
873

            
874
    #[test]
875
    fn transport_id() {
876
        let id1: TransportId = "<none>".parse().unwrap();
877
        assert!(id1.is_builtin());
878
        assert_eq!(id1.to_string(), "-".to_string());
879

            
880
        #[cfg(feature = "pt-client")]
881
        {
882
            let id2: TransportId = "obfs4".parse().unwrap();
883
            assert_ne!(id2, id1);
884
            assert!(!id2.is_builtin());
885
            assert_eq!(id2.to_string(), "obfs4");
886

            
887
            assert!(matches!(
888
                TransportId::from_str("==="),
889
                Err(TransportIdError::BadId(_))
890
            ));
891
        }
892

            
893
        #[cfg(not(feature = "pt-client"))]
894
        {
895
            assert!(matches!(
896
                TransportId::from_str("obfs4"),
897
                Err(TransportIdError::NoSupport)
898
            ));
899
        }
900
    }
901

            
902
    #[test]
903
    fn settings() {
904
        let s = PtTargetSettings::try_from(vec![]).unwrap();
905
        assert_eq!(Vec::<_>::from(s), vec![]);
906

            
907
        let v = vec![("abc".into(), "def".into()), ("ghi".into(), "jkl".into())];
908
        let s = PtTargetSettings::try_from(v.clone()).unwrap();
909
        assert_eq!(Vec::<_>::from(s), v);
910

            
911
        let v = vec![("a=b".into(), "def".into())];
912
        let s = PtTargetSettings::try_from(v);
913
        assert!(matches!(s, Err(PtTargetInvalidSetting::Key(_))));
914

            
915
        let v = vec![("abc".into(), "d ef".into())];
916
        let s = PtTargetSettings::try_from(v);
917
        assert!(matches!(s, Err(PtTargetInvalidSetting::Value(_))));
918
    }
919

            
920
    #[test]
921
    fn chanmethod_direct() {
922
        let a1 = "127.0.0.1:8080".parse().unwrap();
923
        let a2 = "127.0.0.2:8181".parse().unwrap();
924
        let a3 = "127.0.0.3:8282".parse().unwrap();
925

            
926
        let m = ChannelMethod::Direct(vec![a1, a2]);
927
        assert_eq!(m.socket_addrs(), Some(&[a1, a2][..]));
928
        assert_eq!((m.target_addr()), Some(PtTargetAddr::IpPort(a1)));
929
        assert!(m.is_direct());
930
        assert_eq!(m.transport_id(), TransportId::default());
931

            
932
        let m2 = ChannelMethod::Direct(vec![a1, a2, a3]);
933
        assert!(m.contained_by(&m));
934
        assert!(m.contained_by(&m2));
935
        assert!(!m2.contained_by(&m));
936

            
937
        let mut m3 = m2.clone();
938
        m3.retain_addrs(|a| a.port() != 8282).unwrap();
939
        assert_eq!(m3, m);
940
        assert_ne!(m3, m2);
941
    }
942

            
943
    #[test]
944
    #[cfg(feature = "pt-client")]
945
    fn chanmethod_pt() {
946
        use itertools::Itertools;
947

            
948
        let transport = "giraffe".parse().unwrap();
949
        let addr1 = PtTargetAddr::HostPort("pt.example.com".into(), 1234);
950
        let target1 = PtTarget::new("giraffe".parse().unwrap(), addr1.clone());
951
        let m1 = ChannelMethod::Pluggable(target1);
952

            
953
        let addr2 = PtTargetAddr::IpPort("127.0.0.1:567".parse().unwrap());
954
        let target2 = PtTarget::new("giraffe".parse().unwrap(), addr2.clone());
955
        let m2 = ChannelMethod::Pluggable(target2);
956

            
957
        let addr3 = PtTargetAddr::None;
958
        let target3 = PtTarget::new("giraffe".parse().unwrap(), addr3.clone());
959
        let m3 = ChannelMethod::Pluggable(target3);
960

            
961
        assert_eq!(m1.socket_addrs(), None);
962
        assert_eq!(
963
            m2.socket_addrs(),
964
            Some(&["127.0.0.1:567".parse().unwrap()][..])
965
        );
966
        assert_eq!(m3.socket_addrs(), None);
967

            
968
        assert_eq!(m1.target_addr(), Some(addr1));
969
        assert_eq!(m2.target_addr(), Some(addr2));
970
        assert_eq!(m3.target_addr(), Some(addr3));
971

            
972
        assert!(!m1.is_direct());
973
        assert!(!m2.is_direct());
974
        assert!(!m3.is_direct());
975

            
976
        assert_eq!(m1.transport_id(), transport);
977
        assert_eq!(m2.transport_id(), transport);
978
        assert_eq!(m3.transport_id(), transport);
979

            
980
        for v in [&m1, &m2, &m3].iter().combinations(2) {
981
            let first = v[0];
982
            let second = v[1];
983
            assert_eq!(first.contained_by(second), first == second);
984
        }
985

            
986
        let mut m1new = m1.clone();
987
        let mut m2new = m2.clone();
988
        let mut m3new = m3.clone();
989
        // this will retain the IpPort target, and ignore the other targets.
990
        m1new.retain_addrs(|a| a.port() == 567).unwrap();
991
        m2new.retain_addrs(|a| a.port() == 567).unwrap();
992
        m3new.retain_addrs(|a| a.port() == 567).unwrap();
993
        assert_eq!(m1new, m1);
994
        assert_eq!(m2new, m2);
995
        assert_eq!(m3new, m3);
996

            
997
        // But if we try to remove the ipport target, we get an error.
998
        assert!(matches!(
999
            m2new.retain_addrs(|a| a.port() == 999),
            Err(RetainAddrsError::NoAddrsLeft)
        ));
    }
}