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 pub obfs_lengths: bool,
31 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 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 pub fn random() -> Self {
63 Self {
64 key: rand::random(),
65 params: ObfsParams::default(),
66 }
67 }
68
69 pub fn random_with_params(params: ObfsParams) -> Self {
71 Self {
72 key: rand::random(),
73 params,
74 }
75 }
76
77 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#[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 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 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 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 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 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}