Skip to main content

tor_linkspec/
transport.rs

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
9use std::fmt::{self, Debug, Display};
10use std::net::SocketAddr;
11use std::slice;
12use std::str::FromStr;
13
14use itertools::Either;
15use safelog::Redactable;
16use serde::{Deserialize, Serialize};
17use thiserror::Error;
18
19use 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)]
33pub 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)]
39enum 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)]
67pub struct PtTransportName(String);
68
69impl FromStr for PtTransportName {
70    type Err = TransportIdError;
71
72    fn from_str(s: &str) -> Result<Self, Self::Err> {
73        s.to_string().try_into()
74    }
75}
76
77impl TryFrom<String> for PtTransportName {
78    type Error = TransportIdError;
79
80    fn try_from(s: String) -> Result<PtTransportName, Self::Error> {
81        if is_well_formed_id(&s) {
82            Ok(PtTransportName(s))
83        } else {
84            Err(TransportIdError::BadId(s))
85        }
86    }
87}
88
89impl Display for PtTransportName {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        Display::fmt(&self.0, f)
92    }
93}
94
95impl 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.
108const BUILT_IN_IDS: &[&str] = &["-", "", "bridge", "<none>"];
109
110impl FromStr for TransportId {
111    type Err = TransportIdError;
112
113    fn from_str(s: &str) -> Result<Self, Self::Err> {
114        if BUILT_IN_IDS.contains(&s) {
115            return Ok(TransportId(Inner::BuiltIn));
116        };
117
118        #[cfg(feature = "pt-client")]
119        {
120            let name: PtTransportName = s.parse()?;
121            Ok(TransportId(Inner::Pluggable(name)))
122        }
123
124        #[cfg(not(feature = "pt-client"))]
125        Err(TransportIdError::NoSupport)
126    }
127}
128
129impl Display for TransportId {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        match &self.0 {
132            Inner::BuiltIn => write!(f, "{}", BUILT_IN_IDS[0]),
133            #[cfg(feature = "pt-client")]
134            Inner::Pluggable(name) => write!(f, "{}", name),
135        }
136    }
137}
138
139#[cfg(feature = "pt-client")]
140impl From<PtTransportName> for TransportId {
141    fn from(name: PtTransportName) -> Self {
142        TransportId(Inner::Pluggable(name))
143    }
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_]*`.
151fn 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    let mut bytes = s.bytes();
155
156    if let Some(first) = bytes.next() {
157        (first.is_ascii_alphabetic() || first == b'_')
158            && bytes.all(|b| b.is_ascii_alphanumeric() || b == b'_')
159            && !s.eq_ignore_ascii_case("bridge")
160    } else {
161        false
162    }
163}
164
165/// An error related to parsing a TransportId.
166#[derive(Clone, Debug, thiserror::Error)]
167#[non_exhaustive]
168pub 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
179impl 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    pub fn is_builtin(&self) -> bool {
197        self.0 == Inner::BuiltIn
198    }
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    pub fn as_pluggable(&self) -> Option<&PtTransportName> {
206        match &self.0 {
207            Inner::BuiltIn => None,
208            #[cfg(feature = "pt-client")]
209            Inner::Pluggable(pt) => Some(pt),
210        }
211    }
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    pub fn into_pluggable(self) -> Option<PtTransportName> {
219        match self.0 {
220            Inner::BuiltIn => None,
221            #[cfg(feature = "pt-client")]
222            Inner::Pluggable(pt) => Some(pt),
223        }
224    }
225}
226
227/// This identifier is used to indicate no transport address.
228const 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]
244pub 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)]
275pub 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)]
283enum 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.
297impl BridgeAddr {
298    /// Create a new `BridgeAddr` referring to a numeric address and port
299    pub fn new_addr_from_sockaddr(sa: SocketAddr) -> Self {
300        BridgeAddr(BridgeAddrInner::IpPort(sa))
301    }
302
303    /// If this is a numeric address, return it as a `SocketAddr`
304    pub fn as_socketaddr(&self) -> Option<&SocketAddr> {
305        match &self.0 {
306            BridgeAddrInner::IpPort(sa) => Some(sa),
307            BridgeAddrInner::HostPort(..) => None,
308        }
309    }
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    pub fn as_host_port(&self) -> Option<(&str, u16)> {
318        match &self.0 {
319            BridgeAddrInner::IpPort(..) => None,
320            BridgeAddrInner::HostPort(hn, port) => Some((hn, *port)),
321        }
322    }
323}
324
325impl From<PtTargetAddr> for Option<BridgeAddr> {
326    fn from(pt: PtTargetAddr) -> Option<BridgeAddr> {
327        match pt {
328            PtTargetAddr::IpPort(sa) => Some(BridgeAddrInner::IpPort(sa)),
329            PtTargetAddr::HostPort(hn, p) => Some(BridgeAddrInner::HostPort(hn, p)),
330            PtTargetAddr::None => None,
331        }
332        .map(BridgeAddr)
333    }
334}
335impl From<Option<BridgeAddr>> for PtTargetAddr {
336    fn from(pt: Option<BridgeAddr>) -> PtTargetAddr {
337        match pt.map(|ba| ba.0) {
338            Some(BridgeAddrInner::IpPort(sa)) => PtTargetAddr::IpPort(sa),
339            Some(BridgeAddrInner::HostPort(hn, p)) => PtTargetAddr::HostPort(hn, p),
340            None => PtTargetAddr::None,
341        }
342    }
343}
344
345/// An error from parsing a [`BridgeAddr`] or [`PtTargetAddr`].
346#[derive(Clone, Debug, thiserror::Error)]
347#[non_exhaustive]
348pub 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
357impl FromStr for BridgeAddr {
358    type Err = BridgeAddrError;
359
360    fn from_str(s: &str) -> Result<Self, Self::Err> {
361        Ok(BridgeAddr(if let Ok(addr) = s.parse() {
362            BridgeAddrInner::IpPort(addr)
363        } else if let Some((name, port)) = s.rsplit_once(':') {
364            let port = port
365                .parse()
366                .map_err(|_| BridgeAddrError::BadAddress(s.to_string()))?;
367
368            BridgeAddrInner::HostPort(name.to_string(), port)
369        } else {
370            return Err(BridgeAddrError::BadAddress(s.to_string()));
371        }))
372    }
373}
374
375impl FromStr for PtTargetAddr {
376    type Err = BridgeAddrError;
377
378    fn from_str(s: &str) -> Result<Self, Self::Err> {
379        Ok(if s == NONE_ADDR {
380            PtTargetAddr::None
381        } else {
382            Some(BridgeAddr::from_str(s)?).into()
383        })
384    }
385}
386
387impl 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    fn as_bridge_ref(&self) -> Option<BridgeAddrInner<&SocketAddr, &str>> {
395        match self {
396            PtTargetAddr::IpPort(addr) => Some(BridgeAddrInner::IpPort(addr)),
397            PtTargetAddr::HostPort(host, port) => Some(BridgeAddrInner::HostPort(host, *port)),
398            PtTargetAddr::None => None,
399        }
400    }
401}
402
403impl<SA: Display, HN: Display> Display for BridgeAddrInner<SA, HN> {
404    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405        match self {
406            BridgeAddrInner::IpPort(addr) => write!(f, "{}", addr),
407            BridgeAddrInner::HostPort(host, port) => write!(f, "{}:{}", host, port),
408        }
409    }
410}
411
412// impl Display for BridgeAddr is done with derive_more, on the struct definition.
413
414impl Display for PtTargetAddr {
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        match self.as_bridge_ref() {
417            Some(b) => write!(f, "{}", b),
418            None => write!(f, "{}", NONE_ADDR),
419        }
420    }
421}
422
423impl<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
434impl 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
440impl 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)>")]
460pub struct PtTargetSettings {
461    /// A list of (key,value) pairs
462    settings: Vec<(String, String)>,
463}
464
465impl PtTargetSettings {
466    /// Return an error if `k,v` is not a valid setting.
467    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        if k.find(|c: char| c == '=' || c.is_whitespace()).is_some() {
473            return Err(PtTargetInvalidSetting::Key(k.to_string()));
474        }
475        if v.find(|c: char| c.is_whitespace()).is_some() {
476            return Err(PtTargetInvalidSetting::Value(v.to_string()));
477        }
478        Ok(())
479    }
480
481    /// Add `k,v` to this list of settings, if it is valid.
482    fn push_setting(
483        &mut self,
484        k: impl Into<String>,
485        v: impl Into<String>,
486    ) -> Result<(), PtTargetInvalidSetting> {
487        let k = k.into();
488        let v = v.into();
489        Self::check_setting(&k, &v)?;
490        self.settings.push((k, v));
491        Ok(())
492    }
493
494    /// Return the inner list of (key, value) pairs
495    pub fn into_inner(self) -> Vec<(String, String)> {
496        self.settings
497    }
498}
499
500impl TryFrom<Vec<(String, String)>> for PtTargetSettings {
501    type Error = PtTargetInvalidSetting;
502
503    fn try_from(settings: Vec<(String, String)>) -> Result<Self, Self::Error> {
504        for (k, v) in settings.iter() {
505            Self::check_setting(k, v)?;
506        }
507        Ok(Self { settings })
508    }
509}
510
511impl From<PtTargetSettings> for Vec<(String, String)> {
512    fn from(settings: PtTargetSettings) -> Self {
513        settings.settings
514    }
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)]
520pub 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]
533pub 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
549impl PtTarget {
550    /// Create a new `PtTarget` (with no settings)
551    pub fn new(transport: PtTransportName, addr: PtTargetAddr) -> Self {
552        PtTarget {
553            transport,
554            addr,
555            settings: Default::default(),
556        }
557    }
558
559    /// Add a setting (to be passed during the SOCKS handshake)
560    pub fn push_setting(
561        &mut self,
562        k: impl Into<String>,
563        v: impl Into<String>,
564    ) -> Result<(), PtTargetInvalidSetting> {
565        self.settings.push_setting(k, v)
566    }
567
568    /// Get the transport name
569    pub fn transport(&self) -> &PtTransportName {
570        &self.transport
571    }
572
573    /// Get the transport target address (or host and port)
574    pub fn addr(&self) -> &PtTargetAddr {
575        &self.addr
576    }
577
578    /// Iterate over the PT setting strings
579    pub fn settings(&self) -> impl Iterator<Item = (&str, &str)> {
580        self.settings.settings.iter().map(|(k, v)| (&**k, &**v))
581    }
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    pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
593        match self {
594            PtTarget {
595                addr: PtTargetAddr::IpPort(addr),
596                ..
597            } => Some(std::slice::from_ref(addr)),
598
599            _ => None,
600        }
601    }
602
603    /// Consume the `PtTarget` and return the component parts
604    pub fn into_parts(self) -> (PtTransportName, PtTargetAddr, PtTargetSettings) {
605        (self.transport, self.addr, self.settings)
606    }
607}
608
609/// The way to approach a single relay in order to open a channel.
610///
611/// For direct connections, this is simply an address.  For connections via a
612/// pluggable transport, this includes information about the transport, and any
613/// address and settings information that transport requires.
614#[derive(Clone, Debug, Eq, PartialEq, Hash)]
615#[non_exhaustive]
616pub enum ChannelMethod {
617    /// Connect to the relay directly at one of several addresses.
618    Direct(Vec<std::net::SocketAddr>),
619
620    /// Connect to a bridge relay via a pluggable transport.
621    #[cfg(feature = "pt-client")]
622    Pluggable(PtTarget),
623}
624
625impl ChannelMethod {
626    /// Return all the advertized socket addresses to which this method may connect.
627    ///
628    /// Returns `Some(&[])` if there is no way to connect to this target, and
629    /// `None` if this target does not use `SocketAddr` to connect
630    ///
631    /// NOTE that these are not necessarily an address to which you can open a
632    /// TCP connection! If this `ChannelMethod` is using a non-`Direct`
633    /// transport, then this address will be interpreted by that transport's
634    /// implementation.
635    pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
636        match self {
637            ChannelMethod::Direct(addr) => Some(addr.as_ref()),
638
639            #[cfg(feature = "pt-client")]
640            ChannelMethod::Pluggable(t) => t.socket_addrs(),
641        }
642    }
643
644    /// Return a BridgeAddr that this ChannelMethod uses.
645    //
646    // TODO this is kind of weird, what does Some(PtTargetAddr::None) mean?
647    pub fn target_addr(&self) -> Option<PtTargetAddr> {
648        match self {
649            ChannelMethod::Direct(addr) if !addr.is_empty() => Some(PtTargetAddr::IpPort(addr[0])),
650
651            #[cfg(feature = "pt-client")]
652            ChannelMethod::Pluggable(PtTarget { addr, .. }) => Some(addr.clone()),
653
654            _ => None,
655        }
656    }
657
658    /// Return the [`SocketAddr`] if this method is [`ChannelMethod::Direct`] and there is one and
659    /// only one available address.
660    ///
661    /// This is single address requirement is very important as this call is used during the
662    /// channel handshake on which we need the actual peer address we are connected to and not all
663    /// the possibilities.
664    ///
665    /// When connecting or accepting, that addres is put in the OwnedChanTarget and so this
666    /// extracts it.
667    pub fn unique_direct_addr(&self) -> Option<SocketAddr> {
668        match self {
669            Self::Direct(addrs) => match addrs.as_slice() {
670                [addr] => Some(*addr),
671                // Remember, more than one, we don't have a single address.
672                _ => None,
673            },
674            #[cfg(feature = "pt-client")]
675            _ => None,
676        }
677    }
678
679    /// Return true if this is a method for a direct connection.
680    pub fn is_direct(&self) -> bool {
681        matches!(self, ChannelMethod::Direct(_))
682    }
683
684    /// Return an identifier for the Transport to be used by this `ChannelMethod`.
685    pub fn transport_id(&self) -> TransportId {
686        match self {
687            ChannelMethod::Direct(_) => TransportId::default(),
688            #[cfg(feature = "pt-client")]
689            ChannelMethod::Pluggable(target) => target.transport().clone().into(),
690        }
691    }
692
693    ///
694    /// Change this `ChannelMethod` by removing every socket address that
695    /// does not satisfy `pred`.
696    ///
697    /// `Hostname` and `None` addresses are never removed.
698    ///
699    /// Return an error if we have removed every address.
700    pub fn retain_addrs<P>(&mut self, pred: P) -> Result<(), RetainAddrsError>
701    where
702        P: Fn(&std::net::SocketAddr) -> bool,
703    {
704        #[cfg(feature = "pt-client")]
705        use PtTargetAddr as Pt;
706
707        match self {
708            ChannelMethod::Direct(d) if d.is_empty() => {}
709            ChannelMethod::Direct(d) => {
710                d.retain(pred);
711                if d.is_empty() {
712                    return Err(RetainAddrsError::NoAddrsLeft);
713                }
714            }
715            #[cfg(feature = "pt-client")]
716            ChannelMethod::Pluggable(PtTarget { addr, .. }) => match addr {
717                Pt::IpPort(a) => {
718                    if !pred(a) {
719                        *addr = Pt::None;
720                        return Err(RetainAddrsError::NoAddrsLeft);
721                    }
722                }
723                Pt::HostPort(_, _) => {}
724                Pt::None => {}
725            },
726        }
727        Ok(())
728    }
729
730    /// Return true if every method to contact `self` is also a method to
731    /// contact `other`.
732    pub fn contained_by(&self, other: &ChannelMethod) -> bool {
733        use ChannelMethod as CM;
734        match (self, other) {
735            (CM::Direct(our_addrs), CM::Direct(their_addrs)) => {
736                our_addrs.iter().all(|a| their_addrs.contains(a))
737            }
738            #[cfg(feature = "pt-client")]
739            (CM::Pluggable(our_target), CM::Pluggable(their_target)) => our_target == their_target,
740            #[cfg(feature = "pt-client")]
741            (_, _) => false,
742        }
743    }
744}
745
746/// An error that occurred while filtering addresses from a ChanMethod.
747#[derive(Clone, Debug, thiserror::Error)]
748pub enum RetainAddrsError {
749    /// We removed all of the addresses from this method.
750    #[error("All addresses were removed.")]
751    NoAddrsLeft,
752}
753
754impl HasAddrs for PtTargetAddr {
755    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
756        match self {
757            PtTargetAddr::IpPort(sockaddr) => slice::from_ref(sockaddr),
758            PtTargetAddr::HostPort(..) | PtTargetAddr::None => &[],
759        }
760        .iter()
761        .copied()
762    }
763}
764
765impl HasAddrs for ChannelMethod {
766    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
767        let r = match self {
768            ChannelMethod::Direct(addrs) => Either::Left(addrs.iter().copied()),
769            #[cfg(feature = "pt-client")]
770            ChannelMethod::Pluggable(pt) => Either::Right(pt.addr.addrs()),
771        };
772
773        // Unfortunately, when pt-client is configured out, the compiler can't infer
774        // the type for Either::Right.  Ideally we'd use Void rather than iter::Empty
775        // but Void doesn't implement Iterator.
776        #[cfg(not(feature = "pt-client"))]
777        let _: &Either<_, std::iter::Empty<_>> = &r;
778
779        r
780    }
781}
782
783#[cfg(test)]
784mod test {
785    // @@ begin test lint list maintained by maint/add_warning @@
786    #![allow(clippy::bool_assert_comparison)]
787    #![allow(clippy::clone_on_copy)]
788    #![allow(clippy::dbg_macro)]
789    #![allow(clippy::mixed_attributes_style)]
790    #![allow(clippy::print_stderr)]
791    #![allow(clippy::print_stdout)]
792    #![allow(clippy::single_char_pattern)]
793    #![allow(clippy::unwrap_used)]
794    #![allow(clippy::unchecked_time_subtraction)]
795    #![allow(clippy::useless_vec)]
796    #![allow(clippy::needless_pass_by_value)]
797    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
798    use super::*;
799    use itertools::Itertools;
800
801    #[test]
802    fn builtin() {
803        assert!(TransportId::default().is_builtin());
804        assert_eq!(
805            TransportId::default(),
806            "<none>".parse().expect("Couldn't parse default ID")
807        );
808    }
809
810    #[test]
811    #[cfg(not(feature = "pt-client"))]
812    fn nosupport() {
813        // We should get this error whenever we parse a non-default PT and we have no PT support.
814        assert!(matches!(
815            TransportId::from_str("obfs4"),
816            Err(TransportIdError::NoSupport)
817        ));
818    }
819
820    #[test]
821    #[cfg(feature = "pt-client")]
822    fn wellformed() {
823        for id in &["snowflake", "obfs4", "_ohai", "Z", "future_WORK2"] {
824            assert!(is_well_formed_id(id));
825        }
826
827        for id in &[" ", "Mölm", "12345", ""] {
828            assert!(!is_well_formed_id(id));
829        }
830    }
831
832    #[test]
833    #[cfg(feature = "pt-client")]
834    fn parsing() {
835        let obfs = TransportId::from_str("obfs4").unwrap();
836        let dflt = TransportId::default();
837        let dflt2 = TransportId::from_str("<none>").unwrap();
838        let dflt3 = TransportId::from_str("-").unwrap();
839        let dflt4 = TransportId::from_str("").unwrap();
840        let dflt5 = TransportId::from_str("bridge").unwrap();
841        let snow = TransportId::from_str("snowflake").unwrap();
842        let obfs_again = TransportId::from_str("obfs4").unwrap();
843
844        assert_eq!(obfs, obfs_again);
845        assert_eq!(dflt, dflt2);
846        assert_eq!(dflt, dflt3);
847        assert_eq!(dflt, dflt4);
848        assert_eq!(dflt, dflt5);
849        assert_ne!(snow, obfs);
850        assert_ne!(snow, dflt);
851
852        assert_eq!(dflt.to_string(), "-");
853
854        assert!(matches!(
855            TransportId::from_str("12345"),
856            Err(TransportIdError::BadId(_))
857        ));
858        assert!(matches!(
859            TransportId::from_str("Bridge"),
860            Err(TransportIdError::BadId(_))
861        ));
862    }
863
864    #[test]
865    fn addr() {
866        let chk_bridge_addr = |a: &PtTargetAddr, addr: &str| {
867            let ba: BridgeAddr = addr.parse().unwrap();
868            assert_eq!(&ba.to_string(), addr);
869
870            assert_eq!(&PtTargetAddr::from(Some(ba.clone())), a);
871            let reba: Option<BridgeAddr> = a.clone().into();
872            assert_eq!(reba.as_ref(), Some(&ba));
873        };
874
875        for addr in &["1.2.3.4:555", "[::1]:9999"] {
876            let a: PtTargetAddr = addr.parse().unwrap();
877            assert_eq!(&a.to_string(), addr);
878
879            let sa: SocketAddr = addr.parse().unwrap();
880            assert_eq!(a.addrs().collect_vec(), &[sa]);
881
882            chk_bridge_addr(&a, addr);
883        }
884
885        for addr in &["www.example.com:9100", "-"] {
886            let a: PtTargetAddr = addr.parse().unwrap();
887            assert_eq!(&a.to_string(), addr);
888            assert_eq!(a.addrs().collect_vec(), &[]);
889
890            if a == PtTargetAddr::None {
891                let e = BridgeAddr::from_str(addr).unwrap_err();
892                assert!(matches!(e, BridgeAddrError::BadAddress(_)));
893            } else {
894                chk_bridge_addr(&a, addr);
895            }
896        }
897
898        for addr in &["foobar", "<<<>>>"] {
899            let e = PtTargetAddr::from_str(addr).unwrap_err();
900            assert!(matches!(e, BridgeAddrError::BadAddress(_)));
901
902            let e = BridgeAddr::from_str(addr).unwrap_err();
903            assert!(matches!(e, BridgeAddrError::BadAddress(_)));
904        }
905    }
906
907    #[test]
908    fn transport_id() {
909        let id1: TransportId = "<none>".parse().unwrap();
910        assert!(id1.is_builtin());
911        assert_eq!(id1.to_string(), "-".to_string());
912
913        #[cfg(feature = "pt-client")]
914        {
915            let id2: TransportId = "obfs4".parse().unwrap();
916            assert_ne!(id2, id1);
917            assert!(!id2.is_builtin());
918            assert_eq!(id2.to_string(), "obfs4");
919
920            assert!(matches!(
921                TransportId::from_str("==="),
922                Err(TransportIdError::BadId(_))
923            ));
924        }
925
926        #[cfg(not(feature = "pt-client"))]
927        {
928            assert!(matches!(
929                TransportId::from_str("obfs4"),
930                Err(TransportIdError::NoSupport)
931            ));
932        }
933    }
934
935    #[test]
936    fn settings() {
937        let s = PtTargetSettings::try_from(vec![]).unwrap();
938        assert_eq!(Vec::<_>::from(s), vec![]);
939
940        let v = vec![("abc".into(), "def".into()), ("ghi".into(), "jkl".into())];
941        let s = PtTargetSettings::try_from(v.clone()).unwrap();
942        assert_eq!(Vec::<_>::from(s), v);
943
944        let v = vec![("a=b".into(), "def".into())];
945        let s = PtTargetSettings::try_from(v);
946        assert!(matches!(s, Err(PtTargetInvalidSetting::Key(_))));
947
948        let v = vec![("abc".into(), "d ef".into())];
949        let s = PtTargetSettings::try_from(v);
950        assert!(matches!(s, Err(PtTargetInvalidSetting::Value(_))));
951    }
952
953    #[test]
954    fn chanmethod_direct() {
955        let a1 = "127.0.0.1:8080".parse().unwrap();
956        let a2 = "127.0.0.2:8181".parse().unwrap();
957        let a3 = "127.0.0.3:8282".parse().unwrap();
958
959        let m = ChannelMethod::Direct(vec![a1, a2]);
960        assert_eq!(m.socket_addrs(), Some(&[a1, a2][..]));
961        assert_eq!((m.target_addr()), Some(PtTargetAddr::IpPort(a1)));
962        assert!(m.is_direct());
963        assert_eq!(m.transport_id(), TransportId::default());
964
965        let m2 = ChannelMethod::Direct(vec![a1, a2, a3]);
966        assert!(m.contained_by(&m));
967        assert!(m.contained_by(&m2));
968        assert!(!m2.contained_by(&m));
969
970        let mut m3 = m2.clone();
971        m3.retain_addrs(|a| a.port() != 8282).unwrap();
972        assert_eq!(m3, m);
973        assert_ne!(m3, m2);
974    }
975
976    #[test]
977    #[cfg(feature = "pt-client")]
978    fn chanmethod_pt() {
979        use itertools::Itertools;
980
981        let transport = "giraffe".parse().unwrap();
982        let addr1 = PtTargetAddr::HostPort("pt.example.com".into(), 1234);
983        let target1 = PtTarget::new("giraffe".parse().unwrap(), addr1.clone());
984        let m1 = ChannelMethod::Pluggable(target1);
985
986        let addr2 = PtTargetAddr::IpPort("127.0.0.1:567".parse().unwrap());
987        let target2 = PtTarget::new("giraffe".parse().unwrap(), addr2.clone());
988        let m2 = ChannelMethod::Pluggable(target2);
989
990        let addr3 = PtTargetAddr::None;
991        let target3 = PtTarget::new("giraffe".parse().unwrap(), addr3.clone());
992        let m3 = ChannelMethod::Pluggable(target3);
993
994        assert_eq!(m1.socket_addrs(), None);
995        assert_eq!(
996            m2.socket_addrs(),
997            Some(&["127.0.0.1:567".parse().unwrap()][..])
998        );
999        assert_eq!(m3.socket_addrs(), None);
1000
1001        assert_eq!(m1.target_addr(), Some(addr1));
1002        assert_eq!(m2.target_addr(), Some(addr2));
1003        assert_eq!(m3.target_addr(), Some(addr3));
1004
1005        assert!(!m1.is_direct());
1006        assert!(!m2.is_direct());
1007        assert!(!m3.is_direct());
1008
1009        assert_eq!(m1.transport_id(), transport);
1010        assert_eq!(m2.transport_id(), transport);
1011        assert_eq!(m3.transport_id(), transport);
1012
1013        for v in [&m1, &m2, &m3].iter().combinations(2) {
1014            let first = v[0];
1015            let second = v[1];
1016            assert_eq!(first.contained_by(second), first == second);
1017        }
1018
1019        let mut m1new = m1.clone();
1020        let mut m2new = m2.clone();
1021        let mut m3new = m3.clone();
1022        // this will retain the IpPort target, and ignore the other targets.
1023        m1new.retain_addrs(|a| a.port() == 567).unwrap();
1024        m2new.retain_addrs(|a| a.port() == 567).unwrap();
1025        m3new.retain_addrs(|a| a.port() == 567).unwrap();
1026        assert_eq!(m1new, m1);
1027        assert_eq!(m2new, m2);
1028        assert_eq!(m3new, m3);
1029
1030        // But if we try to remove the ipport target, we get an error.
1031        assert!(matches!(
1032            m2new.retain_addrs(|a| a.port() == 999),
1033            Err(RetainAddrsError::NoAddrsLeft)
1034        ));
1035    }
1036}