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 safelog::util::write_start_redacted;
17use serde::{Deserialize, Serialize};
18use thiserror::Error;
19
20use crate::HasAddrs;
21
22/// Identify a type of Transport.
23///
24/// If this crate is compiled with the `pt-client` feature, this type can
25/// support pluggable transports; otherwise, only the built-in transport type is
26/// supported.
27///
28/// This can be displayed as, or parsed from, a string.
29/// `"-"` is used to indicate the builtin transport,
30/// and `""` and `"bridge"` and `"<none>"` are also recognised for that.
31//
32// We recognise "bridge" as pluggable; "BRIDGE" is rejected as invalid.
33#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
34pub struct TransportId(Inner);
35
36/// Helper type to implement [`TransportId`].
37///
38/// This is a separate type so that TransportId can be opaque.
39#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
40enum Inner {
41    /// The built-in transport type.
42    #[default]
43    BuiltIn,
44
45    /// A pluggable transport type, specified by its name.
46    #[cfg(feature = "pt-client")]
47    Pluggable(PtTransportName),
48}
49
50/// The name of a Pluggable Transport protocol.
51///
52/// The name has been syntax-checked.
53///
54/// These names are used to identify the particular transport protocol, such as
55/// "obfs4" or "snowflake".  They match a name of a protocol that the transport
56/// binary knows how to provide to the name of a protocol that a bridge is
57/// configured to use.
58#[derive(
59    Debug,
60    Clone,
61    Default,
62    Eq,
63    PartialEq,
64    Hash,
65    serde_with::DeserializeFromStr,
66    serde_with::SerializeDisplay,
67)]
68pub struct PtTransportName(String);
69
70impl FromStr for PtTransportName {
71    type Err = TransportIdError;
72
73    fn from_str(s: &str) -> Result<Self, Self::Err> {
74        s.to_string().try_into()
75    }
76}
77
78impl TryFrom<String> for PtTransportName {
79    type Error = TransportIdError;
80
81    fn try_from(s: String) -> Result<PtTransportName, Self::Error> {
82        if is_well_formed_id(&s) {
83            Ok(PtTransportName(s))
84        } else {
85            Err(TransportIdError::BadId(s))
86        }
87    }
88}
89
90impl Display for PtTransportName {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        Display::fmt(&self.0, f)
93    }
94}
95
96impl AsRef<str> for PtTransportName {
97    fn as_ref(&self) -> &str {
98        &self.0
99    }
100}
101
102/// These identifiers are used to indicate the built-in transport.
103///
104/// When outputting string representations, the first (`"-"`) is used.
105//
106// Actual pluggable transport names are restricted to the syntax of C identifiers.
107// These strings are deliberately not in that syntax so as to avoid clashes.
108// `"bridge"` is likewise prohibited by the spec.
109const BUILT_IN_IDS: &[&str] = &["-", "", "bridge", "<none>"];
110
111impl FromStr for TransportId {
112    type Err = TransportIdError;
113
114    fn from_str(s: &str) -> Result<Self, Self::Err> {
115        if BUILT_IN_IDS.contains(&s) {
116            return Ok(TransportId(Inner::BuiltIn));
117        };
118
119        #[cfg(feature = "pt-client")]
120        {
121            let name: PtTransportName = s.parse()?;
122            Ok(TransportId(Inner::Pluggable(name)))
123        }
124
125        #[cfg(not(feature = "pt-client"))]
126        Err(TransportIdError::NoSupport)
127    }
128}
129
130impl Display for TransportId {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        match &self.0 {
133            Inner::BuiltIn => write!(f, "{}", BUILT_IN_IDS[0]),
134            #[cfg(feature = "pt-client")]
135            Inner::Pluggable(name) => write!(f, "{}", name),
136        }
137    }
138}
139
140#[cfg(feature = "pt-client")]
141impl From<PtTransportName> for TransportId {
142    fn from(name: PtTransportName) -> Self {
143        TransportId(Inner::Pluggable(name))
144    }
145}
146
147/// Return true if `s` is a well-formed transport ID.
148///
149/// According to the specification, a well-formed transport ID follows the same
150/// rules as a C99 identifier: It must follow the regular expression
151/// `[a-zA-Z_][a-zA-Z0-9_]*`.
152fn is_well_formed_id(s: &str) -> bool {
153    // It's okay to use a bytes iterator, since non-ascii strings are not
154    // allowed.
155    let mut bytes = s.bytes();
156
157    if let Some(first) = bytes.next() {
158        (first.is_ascii_alphabetic() || first == b'_')
159            && bytes.all(|b| b.is_ascii_alphanumeric() || b == b'_')
160            && !s.eq_ignore_ascii_case("bridge")
161    } else {
162        false
163    }
164}
165
166/// An error related to parsing a TransportId.
167#[derive(Clone, Debug, thiserror::Error)]
168#[non_exhaustive]
169pub enum TransportIdError {
170    /// Arti was compiled without client-side pluggable transport support, and
171    /// we tried to use a pluggable transport.
172    #[error("Not compiled with pluggable transport support")]
173    NoSupport,
174
175    /// Tried to parse a pluggable transport whose name was not well-formed.
176    #[error("{0:?} is not a valid pluggable transport ID")]
177    BadId(String),
178}
179
180impl TransportId {
181    /// Return a new `TransportId` referencing the builtin transport
182    ///
183    /// This is equivalent to the `Default` impl.
184    pub fn new_builtin() -> Self {
185        TransportId(Inner::BuiltIn)
186    }
187
188    /// Return a new `TransportId` referencing a pluggable transport
189    ///
190    /// This is equivalent to the `From<PtTransportName>` impl.
191    #[cfg(feature = "pt-client")]
192    pub fn new_pluggable(pt: PtTransportName) -> Self {
193        pt.into()
194    }
195
196    /// Return true if this is the built-in transport.
197    pub fn is_builtin(&self) -> bool {
198        self.0 == Inner::BuiltIn
199    }
200
201    /// Returns the pluggable transport name
202    ///
203    /// Or `None` if `self` doesn't specify a pluggable transport
204    /// (e.g. if it specifies the builtin transport).
205    #[cfg(feature = "pt-client")]
206    pub fn as_pluggable(&self) -> Option<&PtTransportName> {
207        match &self.0 {
208            Inner::BuiltIn => None,
209            #[cfg(feature = "pt-client")]
210            Inner::Pluggable(pt) => Some(pt),
211        }
212    }
213
214    /// Consumes this `TransportId` and returns the pluggable transport name
215    ///
216    /// Or `None` if `self` doesn't specify a pluggable transport
217    /// (e.g. if it specifies the builtin transport).
218    #[cfg(feature = "pt-client")]
219    pub fn into_pluggable(self) -> Option<PtTransportName> {
220        match self.0 {
221            Inner::BuiltIn => None,
222            #[cfg(feature = "pt-client")]
223            Inner::Pluggable(pt) => Some(pt),
224        }
225    }
226}
227
228/// This identifier is used to indicate no transport address.
229const NONE_ADDR: &str = "-";
230
231/// An address that an be passed to a pluggable transport to tell it where to
232/// connect (typically, to a bridge).
233///
234/// Not every transport accepts all kinds of addresses.
235///
236/// This is semantically very similar to `Option<BridgeAddr>`,
237/// but it has some of its own conversion methods and bespoke `FromStr` and `Display`.
238//
239// Implementations for `PtTargetAddr` are in terms of those for `BridgeAddr`
240// wheresoever possible, to ensure that they do not diverge in semantics.
241#[derive(
242    Clone, Debug, PartialEq, Eq, Hash, serde_with::DeserializeFromStr, serde_with::SerializeDisplay,
243)]
244#[non_exhaustive]
245pub enum PtTargetAddr {
246    /// An IP address and port for a Tor relay.
247    ///
248    /// This is the only address type supported by the BuiltIn transport.
249    IpPort(SocketAddr),
250    /// A hostname-and-port target address.  Some transports may support this.
251    HostPort(String, u16),
252    /// A completely absent target address.  Some transports support this.
253    None,
254}
255
256/// An address of a bridge, for use in configuration.
257///
258/// Contains precisely, either:
259///  * A hostname (as a string), plus a `u16` port; or
260///  * An (IPv4 or IPv6) socket address including port - i.e., a `SocketAddr`,
261///    or to put it another way, an IP address (v4 or v6) plus a `u16` port.
262///
263/// Hostnames which are not syntactically invalid Internet hostnames,
264/// and a port value of zero,
265/// *can* be represented within a `BridgeAddr`.
266#[derive(
267    Clone,
268    Debug,
269    PartialEq,
270    Eq,
271    Hash,
272    serde_with::DeserializeFromStr,
273    serde_with::SerializeDisplay,
274    derive_more::Display,
275)]
276pub struct BridgeAddr(BridgeAddrInner<SocketAddr, String>);
277
278/// `BridgeAddr` contents; type parameters allow use with references to avoid some copying
279///
280/// `SA` is always `SocketAddr` or `&SocketAddr`.
281///
282/// `HN` is always `String` or `&str`.
283#[derive(Clone, Debug, PartialEq, Eq, Hash)]
284enum BridgeAddrInner<SA, HN> {
285    /// An IP address and port for a bridge
286    IpPort(SA),
287    /// A hostname-and-port target address
288    HostPort(HN, u16),
289}
290
291// These methods have long slightly awkward names because we think
292// we may want to change their names and types later, and/or deprecate them.
293// So let's not use up the nice names now.
294//
295// TODO: decide on, and implement, a nicer API, or functions with nicer names.
296// TODO: add From/Into conversions for SocketAddr and maybe (String, u16).
297// TODO: consider providing constructor/accessor/deconstructor to/from Either.
298impl BridgeAddr {
299    /// Create a new `BridgeAddr` referring to a numeric address and port
300    pub fn new_addr_from_sockaddr(sa: SocketAddr) -> Self {
301        BridgeAddr(BridgeAddrInner::IpPort(sa))
302    }
303
304    /// If this is a numeric address, return it as a `SocketAddr`
305    pub fn as_socketaddr(&self) -> Option<&SocketAddr> {
306        match &self.0 {
307            BridgeAddrInner::IpPort(sa) => Some(sa),
308            BridgeAddrInner::HostPort(..) => None,
309        }
310    }
311
312    /// Create a new `BridgeAddr` referring to a numeric address and port
313    pub fn new_named_host_port(hostname: impl Into<String>, port: u16) -> Self {
314        BridgeAddr(BridgeAddrInner::HostPort(hostname.into(), port))
315    }
316
317    /// If this is a named host and port, return it as hostname string and port
318    pub fn as_host_port(&self) -> Option<(&str, u16)> {
319        match &self.0 {
320            BridgeAddrInner::IpPort(..) => None,
321            BridgeAddrInner::HostPort(hn, port) => Some((hn, *port)),
322        }
323    }
324}
325
326impl From<PtTargetAddr> for Option<BridgeAddr> {
327    fn from(pt: PtTargetAddr) -> Option<BridgeAddr> {
328        match pt {
329            PtTargetAddr::IpPort(sa) => Some(BridgeAddrInner::IpPort(sa)),
330            PtTargetAddr::HostPort(hn, p) => Some(BridgeAddrInner::HostPort(hn, p)),
331            PtTargetAddr::None => None,
332        }
333        .map(BridgeAddr)
334    }
335}
336impl From<Option<BridgeAddr>> for PtTargetAddr {
337    fn from(pt: Option<BridgeAddr>) -> PtTargetAddr {
338        match pt.map(|ba| ba.0) {
339            Some(BridgeAddrInner::IpPort(sa)) => PtTargetAddr::IpPort(sa),
340            Some(BridgeAddrInner::HostPort(hn, p)) => PtTargetAddr::HostPort(hn, p),
341            None => PtTargetAddr::None,
342        }
343    }
344}
345
346/// An error from parsing a [`BridgeAddr`] or [`PtTargetAddr`].
347#[derive(Clone, Debug, thiserror::Error)]
348#[non_exhaustive]
349pub enum BridgeAddrError {
350    /// We were compiled without support for addresses of this type.
351    #[error("Not compiled with pluggable transport support.")]
352    NoSupport,
353    /// We cannot parse this address.
354    #[error("Cannot parse {0:?} as an address.")]
355    BadAddress(String),
356}
357
358impl FromStr for BridgeAddr {
359    type Err = BridgeAddrError;
360
361    fn from_str(s: &str) -> Result<Self, Self::Err> {
362        Ok(BridgeAddr(if let Ok(addr) = s.parse() {
363            BridgeAddrInner::IpPort(addr)
364        } else if let Some((name, port)) = s.rsplit_once(':') {
365            let port = port
366                .parse()
367                .map_err(|_| BridgeAddrError::BadAddress(s.to_string()))?;
368
369            BridgeAddrInner::HostPort(name.to_string(), port)
370        } else {
371            return Err(BridgeAddrError::BadAddress(s.to_string()));
372        }))
373    }
374}
375
376impl FromStr for PtTargetAddr {
377    type Err = BridgeAddrError;
378
379    fn from_str(s: &str) -> Result<Self, Self::Err> {
380        Ok(if s == NONE_ADDR {
381            PtTargetAddr::None
382        } else {
383            Some(BridgeAddr::from_str(s)?).into()
384        })
385    }
386}
387
388impl PtTargetAddr {
389    /// Obtain an `Option<BridgeAddrInner>` containing references
390    ///
391    /// This is a useful helper for display-like implementations,
392    /// which can then implement for `PtTargetAddr` in terms of the impls for `BridgeAddrInner`.
393    ///
394    /// (See the code comment for `PtTargetAddr`.)
395    fn as_bridge_ref(&self) -> Option<BridgeAddrInner<&SocketAddr, &str>> {
396        match self {
397            PtTargetAddr::IpPort(addr) => Some(BridgeAddrInner::IpPort(addr)),
398            PtTargetAddr::HostPort(host, port) => Some(BridgeAddrInner::HostPort(host, *port)),
399            PtTargetAddr::None => None,
400        }
401    }
402}
403
404impl<SA: Display, HN: Display> Display for BridgeAddrInner<SA, HN> {
405    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406        match self {
407            BridgeAddrInner::IpPort(addr) => write!(f, "{}", addr),
408            BridgeAddrInner::HostPort(host, port) => write!(f, "{}:{}", host, port),
409        }
410    }
411}
412
413// impl Display for BridgeAddr is done with derive_more, on the struct definition.
414
415impl Display for PtTargetAddr {
416    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417        match self.as_bridge_ref() {
418            Some(b) => write!(f, "{}", b),
419            None => write!(f, "{}", NONE_ADDR),
420        }
421    }
422}
423
424impl<SA: Debug + Redactable, HN: Debug + Display + AsRef<str>> Redactable
425    for BridgeAddrInner<SA, HN>
426{
427    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
428        match self {
429            BridgeAddrInner::IpPort(a) => a.display_redacted(f),
430            BridgeAddrInner::HostPort(host, port) => {
431                write_start_redacted(f, host.as_ref(), 2, "…")?;
432                write!(f, ":{port}")
433            }
434        }
435    }
436}
437
438impl Redactable for BridgeAddr {
439    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
440        self.0.display_redacted(f)
441    }
442}
443
444impl Redactable for PtTargetAddr {
445    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
446        match self.as_bridge_ref() {
447            Some(b) => b.display_redacted(f),
448            None => write!(f, "{}", NONE_ADDR),
449        }
450    }
451}
452
453/// A set of options to be passed along to a pluggable transport along with a
454/// single target bridge relay.
455///
456/// These options typically describe aspects of the targeted bridge relay that
457/// are not included in its address and Tor keys, such as additional
458/// transport-specific keys or parameters.
459///
460/// This type is _not_ for settings that apply to _all_ of the connections over
461/// a transport.
462#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
463#[serde(into = "Vec<(String, String)>", try_from = "Vec<(String, String)>")]
464pub struct PtTargetSettings {
465    /// A list of (key,value) pairs
466    settings: Vec<(String, String)>,
467}
468
469impl PtTargetSettings {
470    /// Return an error if `k,v` is not a valid setting.
471    fn check_setting(k: &str, v: &str) -> Result<(), PtTargetInvalidSetting> {
472        // Unfortunately the spec is not very clear about the valid syntax.
473        // https://gitlab.torproject.org/tpo/core/torspec/-/issues/173
474        //
475        // For now we reject things that cannot be represented in a bridge line
476        if k.find(|c: char| c == '=' || c.is_whitespace()).is_some() {
477            return Err(PtTargetInvalidSetting::Key(k.to_string()));
478        }
479        if v.find(|c: char| c.is_whitespace()).is_some() {
480            return Err(PtTargetInvalidSetting::Value(v.to_string()));
481        }
482        Ok(())
483    }
484
485    /// Add `k,v` to this list of settings, if it is valid.
486    fn push_setting(
487        &mut self,
488        k: impl Into<String>,
489        v: impl Into<String>,
490    ) -> Result<(), PtTargetInvalidSetting> {
491        let k = k.into();
492        let v = v.into();
493        Self::check_setting(&k, &v)?;
494        self.settings.push((k, v));
495        Ok(())
496    }
497
498    /// Return the inner list of (key, value) pairs
499    pub fn into_inner(self) -> Vec<(String, String)> {
500        self.settings
501    }
502}
503
504impl TryFrom<Vec<(String, String)>> for PtTargetSettings {
505    type Error = PtTargetInvalidSetting;
506
507    fn try_from(settings: Vec<(String, String)>) -> Result<Self, Self::Error> {
508        for (k, v) in settings.iter() {
509            Self::check_setting(k, v)?;
510        }
511        Ok(Self { settings })
512    }
513}
514
515impl From<PtTargetSettings> for Vec<(String, String)> {
516    fn from(settings: PtTargetSettings) -> Self {
517        settings.settings
518    }
519}
520
521/// The set of information passed to the  pluggable transport subsystem in order
522/// to establish a connection to a bridge relay.
523#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
524pub struct PtTarget {
525    /// The transport to be used.
526    transport: PtTransportName,
527    /// The address of the bridge relay, if any.
528    addr: PtTargetAddr,
529    /// Any additional settings used by the transport.
530    #[serde(default)]
531    settings: PtTargetSettings,
532}
533
534/// Invalid PT parameter setting
535#[derive(Error, Clone, Debug, Eq, PartialEq)]
536#[non_exhaustive]
537pub enum PtTargetInvalidSetting {
538    /// Currently: the key contains whitespace or `=`
539    ///
540    /// Will probably be generated for a greater variety of values
541    /// when the spec is more nailed down.
542    #[error("key {0:?} has invalid or unsupported syntax")]
543    Key(String),
544
545    /// Currently: the value contains whitespace
546    ///
547    /// Will probably be generated for a greater variety of values
548    /// when the spec is more nailed down.
549    #[error("value {0:?} has invalid or unsupported syntax")]
550    Value(String),
551}
552
553impl PtTarget {
554    /// Create a new `PtTarget` (with no settings)
555    pub fn new(transport: PtTransportName, addr: PtTargetAddr) -> Self {
556        PtTarget {
557            transport,
558            addr,
559            settings: Default::default(),
560        }
561    }
562
563    /// Add a setting (to be passed during the SOCKS handshake)
564    pub fn push_setting(
565        &mut self,
566        k: impl Into<String>,
567        v: impl Into<String>,
568    ) -> Result<(), PtTargetInvalidSetting> {
569        self.settings.push_setting(k, v)
570    }
571
572    /// Get the transport name
573    pub fn transport(&self) -> &PtTransportName {
574        &self.transport
575    }
576
577    /// Get the transport target address (or host and port)
578    pub fn addr(&self) -> &PtTargetAddr {
579        &self.addr
580    }
581
582    /// Iterate over the PT setting strings
583    pub fn settings(&self) -> impl Iterator<Item = (&str, &str)> {
584        self.settings.settings.iter().map(|(k, v)| (&**k, &**v))
585    }
586
587    /// Return all the advertized socket addresses to which this target may
588    /// connect.
589    ///
590    /// Returns `Some(&[])` if there is no way to connect to this target, and
591    /// `None` if this target does not use `SocketAddr` to connect
592    ///
593    /// NOTE that these are not necessarily an address to which you can open a
594    /// TCP connection! The address will be interpreted by the implementation of
595    /// this pluggable transport.
596    pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
597        match self {
598            PtTarget {
599                addr: PtTargetAddr::IpPort(addr),
600                ..
601            } => Some(std::slice::from_ref(addr)),
602
603            _ => None,
604        }
605    }
606
607    /// Consume the `PtTarget` and return the component parts
608    pub fn into_parts(self) -> (PtTransportName, PtTargetAddr, PtTargetSettings) {
609        (self.transport, self.addr, self.settings)
610    }
611}
612
613impl Display for PtTarget {
614    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
615        write!(
616            f,
617            "{} @ {} with {:?}",
618            self.transport, self.addr, self.settings
619        )
620    }
621}
622
623/// The way to approach a single relay in order to open a channel.
624///
625/// For direct connections, this is simply an address.  For connections via a
626/// pluggable transport, this includes information about the transport, and any
627/// address and settings information that transport requires.
628#[derive(Clone, Debug, Eq, PartialEq, Hash)]
629#[non_exhaustive]
630pub enum ChannelMethod {
631    /// Connect to the relay directly at one of several addresses.
632    Direct(Vec<std::net::SocketAddr>),
633
634    /// Connect to a bridge relay via a pluggable transport.
635    #[cfg(feature = "pt-client")]
636    Pluggable(PtTarget),
637}
638
639impl ChannelMethod {
640    /// Return all the advertized socket addresses to which this method may connect.
641    ///
642    /// Returns `Some(&[])` if there is no way to connect to this target, and
643    /// `None` if this target does not use `SocketAddr` to connect
644    ///
645    /// NOTE that these are not necessarily an address to which you can open a
646    /// TCP connection! If this `ChannelMethod` is using a non-`Direct`
647    /// transport, then this address will be interpreted by that transport's
648    /// implementation.
649    pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
650        match self {
651            ChannelMethod::Direct(addr) => Some(addr.as_ref()),
652
653            #[cfg(feature = "pt-client")]
654            ChannelMethod::Pluggable(t) => t.socket_addrs(),
655        }
656    }
657
658    /// Return a BridgeAddr that this ChannelMethod uses.
659    //
660    // TODO this is kind of weird, what does Some(PtTargetAddr::None) mean?
661    pub fn target_addr(&self) -> Option<PtTargetAddr> {
662        match self {
663            ChannelMethod::Direct(addr) if !addr.is_empty() => Some(PtTargetAddr::IpPort(addr[0])),
664
665            #[cfg(feature = "pt-client")]
666            ChannelMethod::Pluggable(PtTarget { addr, .. }) => Some(addr.clone()),
667
668            _ => None,
669        }
670    }
671
672    /// Return the [`SocketAddr`] if this method is [`ChannelMethod::Direct`] and there is one and
673    /// only one available address.
674    ///
675    /// This is single address requirement is very important as this call is used during the
676    /// channel handshake on which we need the actual peer address we are connected to and not all
677    /// the possibilities.
678    ///
679    /// When connecting or accepting, that address is put in the OwnedChanTarget and so this
680    /// extracts it.
681    pub fn unique_direct_addr(&self) -> Option<SocketAddr> {
682        match self {
683            Self::Direct(addrs) => match addrs.as_slice() {
684                [addr] => Some(*addr),
685                // Remember, more than one, we don't have a single address.
686                _ => None,
687            },
688            #[cfg(feature = "pt-client")]
689            Self::Pluggable(_) => None,
690        }
691    }
692
693    /// Return true if this is a method for a direct connection.
694    pub fn is_direct(&self) -> bool {
695        matches!(self, ChannelMethod::Direct(_))
696    }
697
698    /// Return an identifier for the Transport to be used by this `ChannelMethod`.
699    pub fn transport_id(&self) -> TransportId {
700        match self {
701            ChannelMethod::Direct(_) => TransportId::default(),
702            #[cfg(feature = "pt-client")]
703            ChannelMethod::Pluggable(target) => target.transport().clone().into(),
704        }
705    }
706
707    ///
708    /// Change this `ChannelMethod` by removing every socket address that
709    /// does not satisfy `pred`.
710    ///
711    /// `Hostname` and `None` addresses are never removed.
712    ///
713    /// Return an error if we have removed every address.
714    pub fn retain_addrs<P>(&mut self, pred: P) -> Result<(), RetainAddrsError>
715    where
716        P: Fn(&std::net::SocketAddr) -> bool,
717    {
718        #[cfg(feature = "pt-client")]
719        use PtTargetAddr as Pt;
720
721        match self {
722            ChannelMethod::Direct(d) if d.is_empty() => {}
723            ChannelMethod::Direct(d) => {
724                d.retain(pred);
725                if d.is_empty() {
726                    return Err(RetainAddrsError::NoAddrsLeft);
727                }
728            }
729            #[cfg(feature = "pt-client")]
730            ChannelMethod::Pluggable(PtTarget { addr, .. }) => match addr {
731                Pt::IpPort(a) => {
732                    if !pred(a) {
733                        *addr = Pt::None;
734                        return Err(RetainAddrsError::NoAddrsLeft);
735                    }
736                }
737                Pt::HostPort(_, _) => {}
738                Pt::None => {}
739            },
740        }
741        Ok(())
742    }
743
744    /// Return true if every method to contact `self` is also a method to
745    /// contact `other`.
746    pub fn contained_by(&self, other: &ChannelMethod) -> bool {
747        use ChannelMethod as CM;
748        match (self, other) {
749            (CM::Direct(our_addrs), CM::Direct(their_addrs)) => {
750                our_addrs.iter().all(|a| their_addrs.contains(a))
751            }
752            #[cfg(feature = "pt-client")]
753            (CM::Pluggable(our_target), CM::Pluggable(their_target)) => our_target == their_target,
754            #[cfg(feature = "pt-client")]
755            (_, _) => false,
756        }
757    }
758}
759
760/// An error that occurred while filtering addresses from a ChanMethod.
761#[derive(Clone, Debug, thiserror::Error)]
762pub enum RetainAddrsError {
763    /// We removed all of the addresses from this method.
764    #[error("All addresses were removed.")]
765    NoAddrsLeft,
766}
767
768impl HasAddrs for PtTargetAddr {
769    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
770        match self {
771            PtTargetAddr::IpPort(sockaddr) => slice::from_ref(sockaddr),
772            PtTargetAddr::HostPort(..) | PtTargetAddr::None => &[],
773        }
774        .iter()
775        .copied()
776    }
777}
778
779impl HasAddrs for ChannelMethod {
780    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
781        let r = match self {
782            ChannelMethod::Direct(addrs) => Either::Left(addrs.iter().copied()),
783            #[cfg(feature = "pt-client")]
784            ChannelMethod::Pluggable(pt) => Either::Right(pt.addr.addrs()),
785        };
786
787        // Unfortunately, when pt-client is configured out, the compiler can't infer
788        // the type for Either::Right.  Ideally we'd use Void rather than iter::Empty
789        // but Void doesn't implement Iterator.
790        #[cfg(not(feature = "pt-client"))]
791        let _: &Either<_, std::iter::Empty<_>> = &r;
792
793        r
794    }
795}
796
797#[cfg(test)]
798mod test {
799    // @@ begin test lint list maintained by maint/add_warning @@
800    #![allow(clippy::bool_assert_comparison)]
801    #![allow(clippy::clone_on_copy)]
802    #![allow(clippy::dbg_macro)]
803    #![allow(clippy::mixed_attributes_style)]
804    #![allow(clippy::print_stderr)]
805    #![allow(clippy::print_stdout)]
806    #![allow(clippy::single_char_pattern)]
807    #![allow(clippy::unwrap_used)]
808    #![allow(clippy::unchecked_time_subtraction)]
809    #![allow(clippy::useless_vec)]
810    #![allow(clippy::needless_pass_by_value)]
811    #![allow(clippy::string_slice)] // See arti#2571
812    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
813    use super::*;
814    use itertools::Itertools;
815
816    #[test]
817    fn builtin() {
818        assert!(TransportId::default().is_builtin());
819        assert_eq!(
820            TransportId::default(),
821            "<none>".parse().expect("Couldn't parse default ID")
822        );
823    }
824
825    #[test]
826    #[cfg(not(feature = "pt-client"))]
827    fn nosupport() {
828        // We should get this error whenever we parse a non-default PT and we have no PT support.
829        assert!(matches!(
830            TransportId::from_str("obfs4"),
831            Err(TransportIdError::NoSupport)
832        ));
833    }
834
835    #[test]
836    #[cfg(feature = "pt-client")]
837    fn wellformed() {
838        for id in &["snowflake", "obfs4", "_ohai", "Z", "future_WORK2"] {
839            assert!(is_well_formed_id(id));
840        }
841
842        for id in &[" ", "Mölm", "12345", ""] {
843            assert!(!is_well_formed_id(id));
844        }
845    }
846
847    #[test]
848    #[cfg(feature = "pt-client")]
849    fn parsing() {
850        let obfs = TransportId::from_str("obfs4").unwrap();
851        let dflt = TransportId::default();
852        let dflt2 = TransportId::from_str("<none>").unwrap();
853        let dflt3 = TransportId::from_str("-").unwrap();
854        let dflt4 = TransportId::from_str("").unwrap();
855        let dflt5 = TransportId::from_str("bridge").unwrap();
856        let snow = TransportId::from_str("snowflake").unwrap();
857        let obfs_again = TransportId::from_str("obfs4").unwrap();
858
859        assert_eq!(obfs, obfs_again);
860        assert_eq!(dflt, dflt2);
861        assert_eq!(dflt, dflt3);
862        assert_eq!(dflt, dflt4);
863        assert_eq!(dflt, dflt5);
864        assert_ne!(snow, obfs);
865        assert_ne!(snow, dflt);
866
867        assert_eq!(dflt.to_string(), "-");
868
869        assert!(matches!(
870            TransportId::from_str("12345"),
871            Err(TransportIdError::BadId(_))
872        ));
873        assert!(matches!(
874            TransportId::from_str("Bridge"),
875            Err(TransportIdError::BadId(_))
876        ));
877    }
878
879    #[test]
880    fn addr() {
881        let chk_bridge_addr = |a: &PtTargetAddr, addr: &str| {
882            let ba: BridgeAddr = addr.parse().unwrap();
883            assert_eq!(&ba.to_string(), addr);
884
885            assert_eq!(&PtTargetAddr::from(Some(ba.clone())), a);
886            let reba: Option<BridgeAddr> = a.clone().into();
887            assert_eq!(reba.as_ref(), Some(&ba));
888        };
889
890        for addr in &["1.2.3.4:555", "[::1]:9999"] {
891            let a: PtTargetAddr = addr.parse().unwrap();
892            assert_eq!(&a.to_string(), addr);
893
894            let sa: SocketAddr = addr.parse().unwrap();
895            assert_eq!(a.addrs().collect_vec(), &[sa]);
896
897            chk_bridge_addr(&a, addr);
898        }
899
900        for addr in &["www.example.com:9100", "-"] {
901            let a: PtTargetAddr = addr.parse().unwrap();
902            assert_eq!(&a.to_string(), addr);
903            assert_eq!(a.addrs().collect_vec(), &[]);
904
905            if a == PtTargetAddr::None {
906                let e = BridgeAddr::from_str(addr).unwrap_err();
907                assert!(matches!(e, BridgeAddrError::BadAddress(_)));
908            } else {
909                chk_bridge_addr(&a, addr);
910            }
911        }
912
913        for addr in &["foobar", "<<<>>>"] {
914            let e = PtTargetAddr::from_str(addr).unwrap_err();
915            assert!(matches!(e, BridgeAddrError::BadAddress(_)));
916
917            let e = BridgeAddr::from_str(addr).unwrap_err();
918            assert!(matches!(e, BridgeAddrError::BadAddress(_)));
919        }
920    }
921
922    #[test]
923    fn transport_id() {
924        let id1: TransportId = "<none>".parse().unwrap();
925        assert!(id1.is_builtin());
926        assert_eq!(id1.to_string(), "-".to_string());
927
928        #[cfg(feature = "pt-client")]
929        {
930            let id2: TransportId = "obfs4".parse().unwrap();
931            assert_ne!(id2, id1);
932            assert!(!id2.is_builtin());
933            assert_eq!(id2.to_string(), "obfs4");
934
935            assert!(matches!(
936                TransportId::from_str("==="),
937                Err(TransportIdError::BadId(_))
938            ));
939        }
940
941        #[cfg(not(feature = "pt-client"))]
942        {
943            assert!(matches!(
944                TransportId::from_str("obfs4"),
945                Err(TransportIdError::NoSupport)
946            ));
947        }
948    }
949
950    #[test]
951    fn settings() {
952        let s = PtTargetSettings::try_from(vec![]).unwrap();
953        assert_eq!(Vec::<_>::from(s), vec![]);
954
955        let v = vec![("abc".into(), "def".into()), ("ghi".into(), "jkl".into())];
956        let s = PtTargetSettings::try_from(v.clone()).unwrap();
957        assert_eq!(Vec::<_>::from(s), v);
958
959        let v = vec![("a=b".into(), "def".into())];
960        let s = PtTargetSettings::try_from(v);
961        assert!(matches!(s, Err(PtTargetInvalidSetting::Key(_))));
962
963        let v = vec![("abc".into(), "d ef".into())];
964        let s = PtTargetSettings::try_from(v);
965        assert!(matches!(s, Err(PtTargetInvalidSetting::Value(_))));
966    }
967
968    #[test]
969    fn chanmethod_direct() {
970        let a1 = "127.0.0.1:8080".parse().unwrap();
971        let a2 = "127.0.0.2:8181".parse().unwrap();
972        let a3 = "127.0.0.3:8282".parse().unwrap();
973
974        let m = ChannelMethod::Direct(vec![a1, a2]);
975        assert_eq!(m.socket_addrs(), Some(&[a1, a2][..]));
976        assert_eq!((m.target_addr()), Some(PtTargetAddr::IpPort(a1)));
977        assert!(m.is_direct());
978        assert_eq!(m.transport_id(), TransportId::default());
979
980        let m2 = ChannelMethod::Direct(vec![a1, a2, a3]);
981        assert!(m.contained_by(&m));
982        assert!(m.contained_by(&m2));
983        assert!(!m2.contained_by(&m));
984
985        let mut m3 = m2.clone();
986        m3.retain_addrs(|a| a.port() != 8282).unwrap();
987        assert_eq!(m3, m);
988        assert_ne!(m3, m2);
989    }
990
991    #[test]
992    #[cfg(feature = "pt-client")]
993    fn chanmethod_pt() {
994        use itertools::Itertools;
995
996        let transport = "giraffe".parse().unwrap();
997        let addr1 = PtTargetAddr::HostPort("pt.example.com".into(), 1234);
998        let target1 = PtTarget::new("giraffe".parse().unwrap(), addr1.clone());
999        let m1 = ChannelMethod::Pluggable(target1);
1000
1001        let addr2 = PtTargetAddr::IpPort("127.0.0.1:567".parse().unwrap());
1002        let target2 = PtTarget::new("giraffe".parse().unwrap(), addr2.clone());
1003        let m2 = ChannelMethod::Pluggable(target2);
1004
1005        let addr3 = PtTargetAddr::None;
1006        let target3 = PtTarget::new("giraffe".parse().unwrap(), addr3.clone());
1007        let m3 = ChannelMethod::Pluggable(target3);
1008
1009        assert_eq!(m1.socket_addrs(), None);
1010        assert_eq!(
1011            m2.socket_addrs(),
1012            Some(&["127.0.0.1:567".parse().unwrap()][..])
1013        );
1014        assert_eq!(m3.socket_addrs(), None);
1015
1016        assert_eq!(m1.target_addr(), Some(addr1));
1017        assert_eq!(m2.target_addr(), Some(addr2));
1018        assert_eq!(m3.target_addr(), Some(addr3));
1019
1020        assert!(!m1.is_direct());
1021        assert!(!m2.is_direct());
1022        assert!(!m3.is_direct());
1023
1024        assert_eq!(m1.transport_id(), transport);
1025        assert_eq!(m2.transport_id(), transport);
1026        assert_eq!(m3.transport_id(), transport);
1027
1028        for v in [&m1, &m2, &m3].iter().combinations(2) {
1029            let first = v[0];
1030            let second = v[1];
1031            assert_eq!(first.contained_by(second), first == second);
1032        }
1033
1034        let mut m1new = m1.clone();
1035        let mut m2new = m2.clone();
1036        let mut m3new = m3.clone();
1037        // this will retain the IpPort target, and ignore the other targets.
1038        m1new.retain_addrs(|a| a.port() == 567).unwrap();
1039        m2new.retain_addrs(|a| a.port() == 567).unwrap();
1040        m3new.retain_addrs(|a| a.port() == 567).unwrap();
1041        assert_eq!(m1new, m1);
1042        assert_eq!(m2new, m2);
1043        assert_eq!(m3new, m3);
1044
1045        // But if we try to remove the ipport target, we get an error.
1046        assert!(matches!(
1047            m2new.retain_addrs(|a| a.port() == 999),
1048            Err(RetainAddrsError::NoAddrsLeft)
1049        ));
1050    }
1051}