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 safelog::Redactable;
15use serde::{Deserialize, Serialize};
16use thiserror::Error;
17
18use 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)]
32pub 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)]
38enum 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    serde_with::DeserializeFromStr,
64    serde_with::SerializeDisplay,
65)]
66pub struct PtTransportName(String);
67
68impl FromStr for PtTransportName {
69    type Err = TransportIdError;
70
71    fn from_str(s: &str) -> Result<Self, Self::Err> {
72        s.to_string().try_into()
73    }
74}
75
76impl TryFrom<String> for PtTransportName {
77    type Error = TransportIdError;
78
79    fn try_from(s: String) -> Result<PtTransportName, Self::Error> {
80        if is_well_formed_id(&s) {
81            Ok(PtTransportName(s))
82        } else {
83            Err(TransportIdError::BadId(s))
84        }
85    }
86}
87
88impl Display for PtTransportName {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        Display::fmt(&self.0, f)
91    }
92}
93
94impl 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.
107const BUILT_IN_IDS: &[&str] = &["-", "", "bridge", "<none>"];
108
109impl FromStr for TransportId {
110    type Err = TransportIdError;
111
112    fn from_str(s: &str) -> Result<Self, Self::Err> {
113        if BUILT_IN_IDS.contains(&s) {
114            return Ok(TransportId(Inner::BuiltIn));
115        };
116
117        #[cfg(feature = "pt-client")]
118        {
119            let name: PtTransportName = s.parse()?;
120            Ok(TransportId(Inner::Pluggable(name)))
121        }
122
123        #[cfg(not(feature = "pt-client"))]
124        Err(TransportIdError::NoSupport)
125    }
126}
127
128impl Display for TransportId {
129    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130        match &self.0 {
131            Inner::BuiltIn => write!(f, "{}", BUILT_IN_IDS[0]),
132            #[cfg(feature = "pt-client")]
133            Inner::Pluggable(name) => write!(f, "{}", name),
134        }
135    }
136}
137
138#[cfg(feature = "pt-client")]
139impl From<PtTransportName> for TransportId {
140    fn from(name: PtTransportName) -> Self {
141        TransportId(Inner::Pluggable(name))
142    }
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_]*`.
150fn is_well_formed_id(s: &str) -> bool {
151    // It's okay to use a bytes iterator, since non-ascii strings are not
152    // allowed.
153    let mut bytes = s.bytes();
154
155    if let Some(first) = bytes.next() {
156        (first.is_ascii_alphabetic() || first == b'_')
157            && bytes.all(|b| b.is_ascii_alphanumeric() || b == b'_')
158            && !s.eq_ignore_ascii_case("bridge")
159    } else {
160        false
161    }
162}
163
164/// An error related to parsing a TransportId.
165#[derive(Clone, Debug, thiserror::Error)]
166#[non_exhaustive]
167pub 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
178impl 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    pub fn is_builtin(&self) -> bool {
196        self.0 == Inner::BuiltIn
197    }
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    pub fn as_pluggable(&self) -> Option<&PtTransportName> {
205        match &self.0 {
206            Inner::BuiltIn => None,
207            #[cfg(feature = "pt-client")]
208            Inner::Pluggable(pt) => Some(pt),
209        }
210    }
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    pub fn into_pluggable(self) -> Option<PtTransportName> {
218        match self.0 {
219            Inner::BuiltIn => None,
220            #[cfg(feature = "pt-client")]
221            Inner::Pluggable(pt) => Some(pt),
222        }
223    }
224}
225
226/// This identifier is used to indicate no transport address.
227const 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]
243pub 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)]
274pub 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)]
282enum 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.
296impl BridgeAddr {
297    /// Create a new `BridgeAddr` referring to a numeric address and port
298    pub fn new_addr_from_sockaddr(sa: SocketAddr) -> Self {
299        BridgeAddr(BridgeAddrInner::IpPort(sa))
300    }
301
302    /// If this is a numeric address, return it as a `SocketAddr`
303    pub fn as_socketaddr(&self) -> Option<&SocketAddr> {
304        match &self.0 {
305            BridgeAddrInner::IpPort(sa) => Some(sa),
306            BridgeAddrInner::HostPort(..) => None,
307        }
308    }
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    pub fn as_host_port(&self) -> Option<(&str, u16)> {
317        match &self.0 {
318            BridgeAddrInner::IpPort(..) => None,
319            BridgeAddrInner::HostPort(hn, port) => Some((hn, *port)),
320        }
321    }
322}
323
324impl From<PtTargetAddr> for Option<BridgeAddr> {
325    fn from(pt: PtTargetAddr) -> Option<BridgeAddr> {
326        match pt {
327            PtTargetAddr::IpPort(sa) => Some(BridgeAddrInner::IpPort(sa)),
328            PtTargetAddr::HostPort(hn, p) => Some(BridgeAddrInner::HostPort(hn, p)),
329            PtTargetAddr::None => None,
330        }
331        .map(BridgeAddr)
332    }
333}
334impl From<Option<BridgeAddr>> for PtTargetAddr {
335    fn from(pt: Option<BridgeAddr>) -> PtTargetAddr {
336        match pt.map(|ba| ba.0) {
337            Some(BridgeAddrInner::IpPort(sa)) => PtTargetAddr::IpPort(sa),
338            Some(BridgeAddrInner::HostPort(hn, p)) => PtTargetAddr::HostPort(hn, p),
339            None => PtTargetAddr::None,
340        }
341    }
342}
343
344/// An error from parsing a [`BridgeAddr`] or [`PtTargetAddr`].
345#[derive(Clone, Debug, thiserror::Error)]
346#[non_exhaustive]
347pub 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
356impl FromStr for BridgeAddr {
357    type Err = BridgeAddrError;
358
359    fn from_str(s: &str) -> Result<Self, Self::Err> {
360        Ok(BridgeAddr(if let Ok(addr) = s.parse() {
361            BridgeAddrInner::IpPort(addr)
362        } else if let Some((name, port)) = s.rsplit_once(':') {
363            let port = port
364                .parse()
365                .map_err(|_| BridgeAddrError::BadAddress(s.to_string()))?;
366
367            BridgeAddrInner::HostPort(name.to_string(), port)
368        } else {
369            return Err(BridgeAddrError::BadAddress(s.to_string()));
370        }))
371    }
372}
373
374impl FromStr for PtTargetAddr {
375    type Err = BridgeAddrError;
376
377    fn from_str(s: &str) -> Result<Self, Self::Err> {
378        Ok(if s == NONE_ADDR {
379            PtTargetAddr::None
380        } else {
381            Some(BridgeAddr::from_str(s)?).into()
382        })
383    }
384}
385
386impl 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    fn as_bridge_ref(&self) -> Option<BridgeAddrInner<&SocketAddr, &str>> {
394        match self {
395            PtTargetAddr::IpPort(addr) => Some(BridgeAddrInner::IpPort(addr)),
396            PtTargetAddr::HostPort(host, port) => Some(BridgeAddrInner::HostPort(host, *port)),
397            PtTargetAddr::None => None,
398        }
399    }
400}
401
402impl<SA: Display, HN: Display> Display for BridgeAddrInner<SA, HN> {
403    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404        match self {
405            BridgeAddrInner::IpPort(addr) => write!(f, "{}", addr),
406            BridgeAddrInner::HostPort(host, port) => write!(f, "{}:{}", host, port),
407        }
408    }
409}
410
411// impl Display for BridgeAddr is done with derive_more, on the struct definition.
412
413impl Display for PtTargetAddr {
414    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
415        match self.as_bridge_ref() {
416            Some(b) => write!(f, "{}", b),
417            None => write!(f, "{}", NONE_ADDR),
418        }
419    }
420}
421
422impl<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
433impl 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
439impl 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)>")]
459pub struct PtTargetSettings {
460    /// A list of (key,value) pairs
461    settings: Vec<(String, String)>,
462}
463
464impl PtTargetSettings {
465    /// Return an error if `k,v` is not a valid setting.
466    fn check_setting(k: &str, v: &str) -> Result<(), PtTargetInvalidSetting> {
467        // Unfortunately the spec is not very clear about the valid syntax.
468        // https://gitlab.torproject.org/tpo/core/torspec/-/issues/173
469        //
470        // For now we reject things that cannot be represented in a bridge line
471        if k.find(|c: char| c == '=' || c.is_whitespace()).is_some() {
472            return Err(PtTargetInvalidSetting::Key(k.to_string()));
473        }
474        if v.find(|c: char| c.is_whitespace()).is_some() {
475            return Err(PtTargetInvalidSetting::Value(v.to_string()));
476        }
477        Ok(())
478    }
479
480    /// Add `k,v` to this list of settings, if it is valid.
481    fn push_setting(
482        &mut self,
483        k: impl Into<String>,
484        v: impl Into<String>,
485    ) -> Result<(), PtTargetInvalidSetting> {
486        let k = k.into();
487        let v = v.into();
488        Self::check_setting(&k, &v)?;
489        self.settings.push((k, v));
490        Ok(())
491    }
492
493    /// Return the inner list of (key, value) pairs
494    pub fn into_inner(self) -> Vec<(String, String)> {
495        self.settings
496    }
497}
498
499impl TryFrom<Vec<(String, String)>> for PtTargetSettings {
500    type Error = PtTargetInvalidSetting;
501
502    fn try_from(settings: Vec<(String, String)>) -> Result<Self, Self::Error> {
503        for (k, v) in settings.iter() {
504            Self::check_setting(k, v)?;
505        }
506        Ok(Self { settings })
507    }
508}
509
510impl From<PtTargetSettings> for Vec<(String, String)> {
511    fn from(settings: PtTargetSettings) -> Self {
512        settings.settings
513    }
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)]
519pub 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]
532pub 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
548impl PtTarget {
549    /// Create a new `PtTarget` (with no settings)
550    pub fn new(transport: PtTransportName, addr: PtTargetAddr) -> Self {
551        PtTarget {
552            transport,
553            addr,
554            settings: Default::default(),
555        }
556    }
557
558    /// Add a setting (to be passed during the SOCKS handshake)
559    pub fn push_setting(
560        &mut self,
561        k: impl Into<String>,
562        v: impl Into<String>,
563    ) -> Result<(), PtTargetInvalidSetting> {
564        self.settings.push_setting(k, v)
565    }
566
567    /// Get the transport name
568    pub fn transport(&self) -> &PtTransportName {
569        &self.transport
570    }
571
572    /// Get the transport target address (or host and port)
573    pub fn addr(&self) -> &PtTargetAddr {
574        &self.addr
575    }
576
577    /// Iterate over the PT setting strings
578    pub fn settings(&self) -> impl Iterator<Item = (&str, &str)> {
579        self.settings.settings.iter().map(|(k, v)| (&**k, &**v))
580    }
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    pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
592        match self {
593            PtTarget {
594                addr: PtTargetAddr::IpPort(addr),
595                ..
596            } => Some(std::slice::from_ref(addr)),
597
598            _ => None,
599        }
600    }
601
602    /// Consume the `PtTarget` and return the component parts
603    pub fn into_parts(self) -> (PtTransportName, PtTargetAddr, PtTargetSettings) {
604        (self.transport, self.addr, self.settings)
605    }
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]
615pub 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
624impl 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    pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
635        match self {
636            ChannelMethod::Direct(addr) => Some(addr.as_ref()),
637
638            #[cfg(feature = "pt-client")]
639            ChannelMethod::Pluggable(t) => t.socket_addrs(),
640        }
641    }
642
643    /// Return a BridgeAddr that this ChannelMethod uses.
644    //
645    // TODO this is kind of weird, what does Some(PtTargetAddr::None) mean?
646    pub fn target_addr(&self) -> Option<PtTargetAddr> {
647        match self {
648            ChannelMethod::Direct(addr) if !addr.is_empty() => Some(PtTargetAddr::IpPort(addr[0])),
649
650            #[cfg(feature = "pt-client")]
651            ChannelMethod::Pluggable(PtTarget { addr, .. }) => Some(addr.clone()),
652
653            _ => None,
654        }
655    }
656
657    /// Return true if this is a method for a direct connection.
658    pub fn is_direct(&self) -> bool {
659        matches!(self, ChannelMethod::Direct(_))
660    }
661
662    /// Return an identifier for the Transport to be used by this `ChannelMethod`.
663    pub fn transport_id(&self) -> TransportId {
664        match self {
665            ChannelMethod::Direct(_) => TransportId::default(),
666            #[cfg(feature = "pt-client")]
667            ChannelMethod::Pluggable(target) => target.transport().clone().into(),
668        }
669    }
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    pub fn retain_addrs<P>(&mut self, pred: P) -> Result<(), RetainAddrsError>
679    where
680        P: Fn(&std::net::SocketAddr) -> bool,
681    {
682        #[cfg(feature = "pt-client")]
683        use PtTargetAddr as Pt;
684
685        match self {
686            ChannelMethod::Direct(d) if d.is_empty() => {}
687            ChannelMethod::Direct(d) => {
688                d.retain(pred);
689                if d.is_empty() {
690                    return Err(RetainAddrsError::NoAddrsLeft);
691                }
692            }
693            #[cfg(feature = "pt-client")]
694            ChannelMethod::Pluggable(PtTarget { addr, .. }) => match addr {
695                Pt::IpPort(a) => {
696                    if !pred(a) {
697                        *addr = Pt::None;
698                        return Err(RetainAddrsError::NoAddrsLeft);
699                    }
700                }
701                Pt::HostPort(_, _) => {}
702                Pt::None => {}
703            },
704        }
705        Ok(())
706    }
707
708    /// Return true if every method to contact `self` is also a method to
709    /// contact `other`.
710    pub fn contained_by(&self, other: &ChannelMethod) -> bool {
711        use ChannelMethod as CM;
712        match (self, other) {
713            (CM::Direct(our_addrs), CM::Direct(their_addrs)) => {
714                our_addrs.iter().all(|a| their_addrs.contains(a))
715            }
716            #[cfg(feature = "pt-client")]
717            (CM::Pluggable(our_target), CM::Pluggable(their_target)) => our_target == their_target,
718            #[cfg(feature = "pt-client")]
719            (_, _) => false,
720        }
721    }
722}
723
724/// An error that occurred while filtering addresses from a ChanMethod.
725#[derive(Clone, Debug, thiserror::Error)]
726pub enum RetainAddrsError {
727    /// We removed all of the addresses from this method.
728    #[error("All addresses were removed.")]
729    NoAddrsLeft,
730}
731
732impl HasAddrs for PtTargetAddr {
733    fn addrs(&self) -> &[SocketAddr] {
734        match self {
735            PtTargetAddr::IpPort(sockaddr) => slice::from_ref(sockaddr),
736            PtTargetAddr::HostPort(..) | PtTargetAddr::None => &[],
737        }
738    }
739}
740
741impl HasAddrs for ChannelMethod {
742    fn addrs(&self) -> &[SocketAddr] {
743        match self {
744            ChannelMethod::Direct(addrs) => addrs,
745            #[cfg(feature = "pt-client")]
746            ChannelMethod::Pluggable(pt) => pt.addr.addrs(),
747        }
748    }
749}
750
751#[cfg(test)]
752mod 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),
1000            Err(RetainAddrsError::NoAddrsLeft)
1001        ));
1002    }
1003}