obfs4/
pt.rs

1use crate::{
2    constants::*,
3    handshake::Obfs4NtorPublicKey,
4    proto::{Obfs4Stream, IAT},
5    Error, OBFS4_NAME,
6};
7use ptrs::{args::Args, FutureResult as F};
8
9use std::{
10    marker::PhantomData,
11    net::{SocketAddrV4, SocketAddrV6},
12    pin::Pin,
13    str::FromStr,
14    time::Duration,
15};
16
17use hex::FromHex;
18use ptrs::trace;
19use tokio::{
20    io::{AsyncRead, AsyncWrite},
21    net::TcpStream,
22};
23
24pub type Obfs4PT = Transport<TcpStream>;
25
26#[derive(Debug, Default)]
27pub struct Transport<T> {
28    _p: PhantomData<T>,
29}
30impl<T> Transport<T> {
31    pub const NAME: &'static str = OBFS4_NAME;
32}
33
34impl<T> ptrs::PluggableTransport<T> for Transport<T>
35where
36    T: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static,
37{
38    type ClientBuilder = crate::ClientBuilder;
39    type ServerBuilder = crate::ServerBuilder<T>;
40
41    fn name() -> String {
42        OBFS4_NAME.into()
43    }
44
45    fn client_builder() -> <Self as ptrs::PluggableTransport<T>>::ClientBuilder {
46        crate::ClientBuilder::default()
47    }
48
49    fn server_builder() -> <Self as ptrs::PluggableTransport<T>>::ServerBuilder {
50        crate::ServerBuilder::default()
51    }
52}
53
54impl<T> ptrs::ServerBuilder<T> for crate::ServerBuilder<T>
55where
56    T: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static,
57{
58    type ServerPT = crate::Server;
59    type Error = Error;
60    type Transport = Transport<T>;
61
62    fn build(&self) -> Self::ServerPT {
63        crate::ServerBuilder::build(self)
64    }
65
66    fn method_name() -> String {
67        OBFS4_NAME.into()
68    }
69
70    fn options(&mut self, opts: &Args) -> Result<&mut Self, Self::Error> {
71        // TODO: pass on opts
72
73        let state = Self::parse_state(None::<&str>, opts)?;
74        self.identity_keys = state.private_key;
75        self.iat_mode(state.iat_mode);
76        // self.drbg = state.drbg_seed; // TODO apply seed from args to server
77
78        trace!(
79            "node_pubkey: {}, node_id: {}, iat: {}",
80            hex::encode(self.identity_keys.pk.pk.as_bytes()),
81            hex::encode(self.identity_keys.pk.id.as_bytes()),
82            self.iat_mode,
83        );
84        Ok(self)
85    }
86
87    fn get_client_params(&self) -> String {
88        self.client_params()
89    }
90
91    fn statefile_location(&mut self, _path: &str) -> Result<&mut Self, Self::Error> {
92        Ok(self)
93    }
94
95    fn timeout(&mut self, _timeout: Option<Duration>) -> Result<&mut Self, Self::Error> {
96        Ok(self)
97    }
98
99    fn v4_bind_addr(&mut self, _addr: SocketAddrV4) -> Result<&mut Self, Self::Error> {
100        Ok(self)
101    }
102
103    fn v6_bind_addr(&mut self, _addr: SocketAddrV6) -> Result<&mut Self, Self::Error> {
104        Ok(self)
105    }
106}
107
108impl<T> ptrs::ClientBuilder<T> for crate::ClientBuilder
109where
110    T: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static,
111{
112    type ClientPT = crate::Client;
113    type Error = Error;
114    type Transport = Transport<T>;
115
116    fn method_name() -> String {
117        OBFS4_NAME.into()
118    }
119
120    /// Builds a new PtCommonParameters.
121    ///
122    /// **Errors**
123    /// If a required field has not been initialized.
124    fn build(&self) -> Self::ClientPT {
125        crate::ClientBuilder::build(self)
126    }
127
128    /// Pluggable transport attempts to parse and validate options from a string,
129    /// typically using ['parse_smethod_args'].
130    fn options(&mut self, opts: &Args) -> Result<&mut Self, Self::Error> {
131        let server_materials = match opts.retrieve(CERT_ARG) {
132            Some(cert_strs) => {
133                // The "new" (version >= 0.0.3) bridge lines use a unified "cert" argument
134                // for the Node ID and Public Key.
135                if cert_strs.is_empty() {
136                    return Err(format!("missing argument '{NODE_ID_ARG}'").into());
137                }
138                trace!("cert string: {}", &cert_strs);
139                let ntor_pk = Obfs4NtorPublicKey::from_str(&cert_strs)?;
140                let pk: [u8; NODE_PUBKEY_LENGTH] = *ntor_pk.pk.as_bytes();
141                let id: [u8; NODE_ID_LENGTH] = ntor_pk.id.as_bytes().try_into().unwrap();
142                (pk, id)
143            }
144            None => {
145                // The "old" style (version <= 0.0.2) bridge lines use separate Node ID
146                // and Public Key arguments in Base16 encoding and are a UX disaster.
147                let node_id_strs = opts
148                    .retrieve(NODE_ID_ARG)
149                    .ok_or(format!("missing argument '{NODE_ID_ARG}'"))?;
150                let id = <[u8; NODE_ID_LENGTH]>::from_hex(node_id_strs)
151                    .map_err(|e| format!("malformed node id: {e}"))?;
152
153                let public_key_strs = opts
154                    .retrieve(PUBLIC_KEY_ARG)
155                    .ok_or(format!("missing argument '{PUBLIC_KEY_ARG}'"))?;
156
157                let pk = <[u8; 32]>::from_hex(public_key_strs)
158                    .map_err(|e| format!("malformed public key: {e}"))?;
159                // Obfs4NtorPublicKey::new(pk, node_id)
160                (pk, id)
161            }
162        };
163
164        // IAT config is common across the two bridge line formats.
165        let iat_strs = opts
166            .retrieve(IAT_ARG)
167            .ok_or(format!("missing argument '{IAT_ARG}'"))?;
168        let iat_mode = IAT::from_str(&iat_strs)?;
169
170        self.with_node_pubkey(server_materials.0)
171            .with_node_id(server_materials.1)
172            .with_iat_mode(iat_mode);
173        trace!(
174            "node_pubkey: {}, node_id: {}, iat: {}",
175            hex::encode(self.station_pubkey),
176            hex::encode(self.station_id),
177            iat_mode
178        );
179
180        Ok(self)
181    }
182
183    /// A path where the launched PT can store state.
184    fn statefile_location(&mut self, _path: &str) -> Result<&mut Self, Self::Error> {
185        Ok(self)
186    }
187
188    /// The maximum time we should wait for a pluggable transport binary to
189    /// report successful initialization. If `None`, a default value is used.
190    fn timeout(&mut self, _timeout: Option<Duration>) -> Result<&mut Self, Self::Error> {
191        Ok(self)
192    }
193
194    /// An IPv4 address to bind outgoing connections to (if specified).
195    ///
196    /// Leaving this out will mean the PT uses a sane default.
197    fn v4_bind_addr(&mut self, _addr: SocketAddrV4) -> Result<&mut Self, Self::Error> {
198        Ok(self)
199    }
200
201    /// An IPv6 address to bind outgoing connections to (if specified).
202    ///
203    /// Leaving this out will mean the PT uses a sane default.
204    fn v6_bind_addr(&mut self, _addr: SocketAddrV6) -> Result<&mut Self, Self::Error> {
205        Ok(self)
206    }
207}
208
209/// Example wrapping transport that just passes the incoming connection future through
210/// unmodified as a proof of concept.
211impl<InRW, InErr> ptrs::ClientTransport<InRW, InErr> for crate::Client
212where
213    InRW: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static,
214    InErr: std::error::Error + Send + Sync + 'static,
215{
216    type OutRW = Obfs4Stream<InRW>;
217    type OutErr = Error;
218    type Builder = crate::ClientBuilder;
219
220    fn establish(self, input: Pin<F<InRW, InErr>>) -> Pin<F<Self::OutRW, Self::OutErr>> {
221        Box::pin(crate::Client::establish(self, input))
222    }
223
224    fn wrap(self, io: InRW) -> Pin<F<Self::OutRW, Self::OutErr>> {
225        Box::pin(crate::Client::wrap(self, io))
226    }
227
228    fn method_name() -> String {
229        OBFS4_NAME.into()
230    }
231}
232
233impl<InRW> ptrs::ServerTransport<InRW> for crate::Server
234where
235    InRW: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static,
236{
237    type OutRW = Obfs4Stream<InRW>;
238    type OutErr = Error;
239    type Builder = crate::ServerBuilder<InRW>;
240
241    /// Use something that can be accessed reference (Arc, Rc, etc.)
242    fn reveal(self, io: InRW) -> Pin<F<Self::OutRW, Self::OutErr>> {
243        Box::pin(crate::Server::wrap(self, io))
244    }
245
246    fn method_name() -> String {
247        OBFS4_NAME.into()
248    }
249}
250
251#[cfg(test)]
252mod test {
253    use super::*;
254
255    #[test]
256    fn check_name() {
257        let pt_name = <Obfs4PT as ptrs::PluggableTransport<TcpStream>>::name();
258        assert_eq!(pt_name, Obfs4PT::NAME);
259
260        let cb_name = <crate::ClientBuilder as ptrs::ClientBuilder<TcpStream>>::method_name();
261        assert_eq!(cb_name, Obfs4PT::NAME);
262
263        let sb_name =
264            <crate::ServerBuilder<TcpStream> as ptrs::ServerBuilder<TcpStream>>::method_name();
265        assert_eq!(sb_name, Obfs4PT::NAME);
266
267        let ct_name =
268            <crate::Client as ptrs::ClientTransport<TcpStream, crate::Error>>::method_name();
269        assert_eq!(ct_name, Obfs4PT::NAME);
270
271        let st_name = <crate::Server as ptrs::ServerTransport<TcpStream>>::method_name();
272        assert_eq!(st_name, Obfs4PT::NAME);
273    }
274}