Skip to main content

sillad_sosistab3/
lib.rs

1use std::{
2    collections::VecDeque,
3    fmt::Debug,
4    io::{ErrorKind, Read, Write},
5    task::{Poll, ready},
6};
7
8use pin_project::pin_project;
9use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
10
11use serde::{Deserialize, Serialize};
12use sillad::Pipe;
13use state::State;
14
15mod dedup;
16pub mod dialer;
17mod handshake;
18pub mod listener;
19mod state;
20
21#[derive(Clone, Copy)]
22pub struct Cookie {
23    key: [u8; 32],
24    params: ObfsParams,
25}
26
27#[derive(Clone, Copy, Default, Deserialize, Serialize, Debug)]
28pub struct ObfsParams {
29    // whether or not to pad write lengths
30    pub obfs_lengths: bool,
31    // whether or not to add delays
32    pub obfs_timing: bool,
33}
34
35impl Debug for Cookie {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        format!(
38            "{}---{}",
39            hex::encode(self.key),
40            serde_json::to_string(&self.params).unwrap()
41        )
42        .fmt(f)
43    }
44}
45
46impl Cookie {
47    /// Derives a cookie from a string.
48    pub fn new(s: &str) -> Self {
49        let (cookie, params) = if let Some((a, b)) = s.split_once("---") {
50            (a, serde_json::from_str(b).unwrap_or_default())
51        } else {
52            (s, ObfsParams::default())
53        };
54        let derived_cookie = blake3::derive_key("cookie", cookie.as_bytes());
55        Self {
56            key: derived_cookie,
57            params,
58        }
59    }
60
61    /// Randomly generates a cookie.
62    pub fn random() -> Self {
63        Self {
64            key: rand::random(),
65            params: ObfsParams::default(),
66        }
67    }
68
69    /// Randomly create a cookie with the given parameters.
70    pub fn random_with_params(params: ObfsParams) -> Self {
71        Self {
72            key: rand::random(),
73            params,
74        }
75    }
76
77    /// Derives a key given the direction.
78    pub fn derive_key(&self, is_server: bool) -> [u8; 32] {
79        blake3::derive_key(if is_server { "server" } else { "client" }, &self.key)
80    }
81}
82
83/// An established sosistab3 connection.
84#[pin_project]
85pub struct SosistabPipe<P: Pipe> {
86    #[pin]
87    lower: P,
88    state: State,
89
90    read_buf: VecDeque<u8>,
91    read_closed: bool,
92    raw_read_buf: Vec<u8>,
93
94    to_write_buf: Vec<u8>,
95}
96
97impl<P: Pipe> SosistabPipe<P> {
98    fn new(lower: P, state: State) -> Self {
99        Self {
100            lower,
101            state,
102            read_buf: Default::default(),
103            read_closed: false,
104            raw_read_buf: Default::default(),
105            to_write_buf: Default::default(),
106        }
107    }
108}
109
110impl<P: Pipe> AsyncWrite for SosistabPipe<P> {
111    #[tracing::instrument(name = "sosistab_write", skip(self, cx, buf))]
112    fn poll_write(
113        self: std::pin::Pin<&mut Self>,
114        cx: &mut std::task::Context<'_>,
115        buf: &[u8],
116    ) -> Poll<std::io::Result<usize>> {
117        // This implementation here is technically incorrect, if the caller doesn't poll the *same* buffer until completion.
118        // But it seems like it's not possible to be technically correct without spawning a background thread and introducing an extra copy, and this is pretty hot code.
119
120        let mut this = self.project();
121        if this.to_write_buf.is_empty() {
122            this.state.encrypt(buf, this.to_write_buf);
123        }
124        loop {
125            tracing::trace!(bytes_to_write = this.to_write_buf.len(), "polling write");
126            let res = ready!(this.lower.as_mut().poll_write(cx, this.to_write_buf));
127            match res {
128                Ok(n) => {
129                    tracing::trace!(
130                        bytes_to_write = this.to_write_buf.len(),
131                        just_wrote = n,
132                        plain_n = buf.len(),
133                        "successfully wrote"
134                    );
135                    this.to_write_buf.drain(..n);
136                    if this.to_write_buf.is_empty() {
137                        tracing::trace!(
138                            bytes_to_write = this.to_write_buf.len(),
139                            just_wrote = n,
140                            "returning Ready from write"
141                        );
142                        return Poll::Ready(Ok(buf.len()));
143                    }
144                }
145                Err(err) => return Poll::Ready(Err(err)),
146            }
147        }
148    }
149
150    fn poll_flush(
151        self: std::pin::Pin<&mut Self>,
152        cx: &mut std::task::Context<'_>,
153    ) -> Poll<std::io::Result<()>> {
154        let mut this = self.project();
155        if !this.to_write_buf.is_empty() {
156            match ready!(this.lower.as_mut().poll_write(cx, this.to_write_buf)) {
157                Ok(n) => {
158                    this.to_write_buf.drain(..n);
159                    if !this.to_write_buf.is_empty() {
160                        return Poll::Pending;
161                    }
162                }
163                Err(err) => {
164                    return Poll::Ready(Err(err));
165                }
166            }
167        }
168        this.lower.poll_flush(cx)
169    }
170
171    fn poll_shutdown(
172        self: std::pin::Pin<&mut Self>,
173        cx: &mut std::task::Context<'_>,
174    ) -> Poll<std::io::Result<()>> {
175        self.project().lower.poll_shutdown(cx)
176    }
177}
178
179impl<P: Pipe> AsyncRead for SosistabPipe<P> {
180    #[tracing::instrument(name = "sosistab_read", skip(self, cx, buf))]
181    fn poll_read(
182        self: std::pin::Pin<&mut Self>,
183        cx: &mut std::task::Context<'_>,
184        buf: &mut ReadBuf<'_>,
185    ) -> std::task::Poll<std::io::Result<()>> {
186        let mut this = self.project();
187        loop {
188            if !this.read_buf.is_empty() || *this.read_closed {
189                tracing::trace!(buf_len = this.read_buf.len(), "reading from the read_buf");
190                // Drain as many decrypted bytes as the caller's buffer can hold.
191                // (When read_buf is empty and the stream is closed, this copies
192                // nothing, signaling EOF.)
193                let to_copy = this.read_buf.len().min(buf.remaining());
194                if to_copy > 0 {
195                    let dst = buf.initialize_unfilled_to(to_copy);
196                    // Reading from a VecDeque<u8> is infallible.
197                    let n = this.read_buf.read(&mut dst[..to_copy]).unwrap_or(0);
198                    buf.advance(n);
199                }
200                return Poll::Ready(Ok(()));
201            } else {
202                // Read raw ciphertext from the lower pipe into a scratch buffer.
203                let mut scratch = [0u8; 16384];
204                let mut scratch_buf = ReadBuf::new(&mut scratch);
205                let res = ready!(this.lower.as_mut().poll_read(cx, &mut scratch_buf));
206                match res {
207                    Err(e) => return Poll::Ready(Err(e)),
208                    Ok(()) => {
209                        let filled = scratch_buf.filled();
210                        let n = filled.len();
211                        if n == 0 {
212                            *this.read_closed = true;
213                            continue;
214                        }
215                        this.raw_read_buf.write_all(filled).unwrap();
216                        tracing::trace!(
217                            n,
218                            raw_buf_len = this.raw_read_buf.len(),
219                            buf_len = this.read_buf.len(),
220                            "read returned from lower"
221                        );
222                        // attempt to decrypt in order to fill the read_buf. we decrypt as many fragments as possible until we cannot decrypt anymore. at that point, we would need more fresh data to decrypt more.
223                        loop {
224                            match this.state.decrypt(this.raw_read_buf, &mut this.read_buf) {
225                                Ok(result) => {
226                                    tracing::trace!(
227                                        n,
228                                        raw_read_len = this.raw_read_buf.len(),
229                                        buf_len = this.read_buf.len(),
230                                        "decryption is successful"
231                                    );
232                                    this.raw_read_buf.drain(..result);
233                                }
234                                Err(err) => {
235                                    tracing::trace!(
236                                        n,
237                                        raw_read_len = this.raw_read_buf.len(),
238                                        buf_len = this.read_buf.len(),
239                                        "could not decrypt yet due to {:?}",
240                                        err
241                                    );
242                                    if err.kind() == ErrorKind::BrokenPipe {
243                                        return Poll::Ready(Err(err));
244                                    }
245                                    break;
246                                }
247                            }
248                        }
249                    }
250                }
251            }
252        }
253    }
254}
255
256impl<P: Pipe> Pipe for SosistabPipe<P> {
257    fn protocol(&self) -> &str {
258        "sosistab3"
259    }
260
261    fn remote_addr(&self) -> Option<&str> {
262        self.lower.remote_addr()
263    }
264
265    fn shared_secret(&self) -> Option<&[u8]> {
266        Some(self.state.shared_secret())
267    }
268}