sosistab/lib.rs
1//! # What is Sosistab?
2//!
3//! Sosistab is an unreliable, obfuscated datagram transport over UDP and TCP, designed to achieve high performance even in extremely bad networks. It is originally designed for [Geph](https://geph.io), a resilient anti-censorship VPN, but it can be used for reliable communication over radios, game networking, etc. It also comes with a QUIC-like multiplex protocol that implements multiple TCP-like reliable streams over the base sosistab layer. This multiplex protocol is ideal for applications requiring a mix of reliable and unreliable traffic. For example, VPNs might do signaling and authentication over reliable streams, while passing packets through unreliable datagrams.
4//!
5//! **NOTE**: Sosistab is still in *heavy* development. Expect significant breaking API changes before version 1.0 is released.
6//!
7//! # Features
8//!
9//! - State-of-the-art reliable streaming protocol with selective ACKs and BIC-based congestion control. Notably, it has better fairness *and* performance in modern networks than protocols like KCP that ape 1980s TCP specifications.
10//! - Strong, state-of-the-art (obfs4-like) obfuscation. Sosistab servers cannot be detected by active probing, and Sosistab traffic is reasonably indistinguishable from random. We also make a best-effort attempt at hiding side-channels through random padding.
11//! - Strong yet lightweight authenticated encryption with chacha20-poly1305
12//! - Deniable public-key encryption with triple-x25519, with servers having long-term public keys that must be provided out-of-band. Similar to decent encrypted transports like TLS and DTLS --- but not to the whole Shadowsocks/Vmess family of protocols --- different clients have different session keys and cannot spy on each other.
13//! - Reed-Solomon error correction that targets a certain application packet loss level. Intelligent autotuning and dynamic batch sizes make performance much better than other FEC-based tools like udpspeeder. This lets Sosistab turns high-bandwidth, high-loss links to medium-bandwidth, low-loss links, which is generally much more useful.
14//! - Avoids last-mile congestive collapse but works around lossy links. Shamelessly unfair in permanently congested WANs --- but that's really their problem, not yours. In any case, permanently congested WANs are observationally identical to lossy links, and any solution for the latter will cause unfairness in the former.
15//!
16//! # Use of async
17//!
18//! Sosistab uses the "futures" traits and the [smolscale] executor. In practice, this means that Sosistab is compatible with any executor, but unless your program uses [smolscale], Sosistab will run on a separate thread pool from the rest of your program. This comes with less overhead than you imagine, and generally it's fine to use Sosistab with e.g. async-std or Tokio.
19//!
20//! In the future, we will consider adding hyper-like traits to enable integration of Sosistab with other executors.
21
22mod buffer;
23mod pacer;
24pub use buffer::*;
25mod client;
26mod crypt;
27mod fec;
28mod listener;
29use bincode::Options;
30pub use client::*;
31pub use listener::*;
32use serde::de::DeserializeOwned;
33use smallvec::SmallVec;
34use std::{future::Future, pin::Pin, task::Poll};
35mod protocol;
36pub mod runtime;
37mod session;
38pub use session::*;
39mod backhaul;
40mod mux;
41pub use mux::*;
42mod tcp;
43use backhaul::*;
44mod recfilter;
45mod stats;
46pub use stats::*;
47
48pub use mux::pkt_trace::init_packet_tracing;
49
50pub(crate) type SVec<T> = SmallVec<[T; 16]>;
51
52/// Safely deserialize
53pub(crate) fn safe_deserialize<T: DeserializeOwned>(bts: &[u8]) -> bincode::Result<T> {
54 let my_options = bincode::DefaultOptions::new()
55 .with_fixint_encoding()
56 .allow_trailing_bytes()
57 .with_limit(bts.len() as u64);
58 my_options.deserialize(bts)
59}
60
61impl<T: Future> MyFutureExt for T {}
62
63/// Our own futures extension trait
64pub(crate) trait MyFutureExt: Future + Sized {
65 /// Returns a future that is pending unless a certain value is true. Useful to "turn off" a future based on a condition.
66 fn pending_unless(self, unless: bool) -> PendingUnless<Self> {
67 PendingUnless {
68 inner: self,
69 always_pending: !unless,
70 }
71 }
72}
73
74pub(crate) struct PendingUnless<T: Future> {
75 inner: T,
76 always_pending: bool,
77}
78
79impl<T: Future> Future for PendingUnless<T> {
80 type Output = T::Output;
81
82 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
83 if self.always_pending {
84 Poll::Pending
85 } else {
86 let inner = unsafe { self.map_unchecked_mut(|v| &mut v.inner) };
87 inner.poll(cx)
88 }
89 }
90}