1 个不稳定版本
0.1.0 | 2024年5月4日 |
---|
#1349 in 网络编程
105KB
2K SLoC
Rust中的可插拔传输(PTRS)
PTRS是一个用于在Rust中编写可插拔传输的库。
⚠️ 🚧 警告:此crate仍在建设中 🚧 ⚠️
- 接口可能会随时更改
- 未准备好用于生产
- 不要将此用于任何安全关键的应用程序
库使用
(目前)此库围绕连接的抽象,将任何实现tokio::io:AsyncRead
+ tokio::io::AsyncWrite
+ Unpin + Send + Sync
的特质视为任何东西。这允许我们将可插拔传输的预期共享行为定义为这些Stream
的转换。
/// Future containing a generic result. We use this for functions that take
/// and/or return futures that will produce Read/Write tunnels once awaited.
pub type FutureResult<T, E> = Box<dyn Future<Output = Result<T, E>> + Send>;
/// Future containing a generic result, shorthand for ['FutureResult']. We use
/// this for functions that take and/or return futures that will produce
/// Read/Write tunnels once awaited.
pub(crate) type F<T, E> = FutureResult<T, E>;
/// Client Transport
pub trait ClientTransport<InRW, InErr>
where
InRW: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static,
{
type OutRW: AsyncRead + AsyncWrite + Send + Unpin;
type OutErr: std::error::Error + Send + Sync;
type Builder: ClientBuilder<InRW>;
/// Create a pluggable transport connection given a future that will return
/// a Read/Write object that can be used as the underlying socket for the
/// connection.
fn establish(self, input: Pin<F<InRW, InErr>>) -> Pin<F<Self::OutRW, Self::OutErr>>;
/// Create a connection for the pluggable transport client using the provided
/// (pre-existing/pre-connected) Read/Write object as the underlying socket.
fn wrap(self, io: InRW) -> Pin<F<Self::OutRW, Self::OutErr>>;
/// Returns a string identifier for this transport
fn method_name() -> String;
}
/// Server Transport
pub trait ServerTransport<InRW>
where
InRW: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static,
{
type OutRW: AsyncRead + AsyncWrite + Send + Unpin;
type OutErr: std::error::Error + Send + Sync;
type Builder: ServerBuilder<InRW>;
/// Create/accept a connection for the pluggable transport client using the
/// provided (pre-existing/pre-connected) Read/Write object as the
/// underlying socket.
fn reveal(self, io: InRW) -> Pin<F<Self::OutRW, Self::OutErr>>;
/// Returns a string identifier for this transport
fn method_name() -> String;
}
ptrs传输集成
有了这个抽象,将传输集成到异步Rust应用程序中变得相对简单,例如,集成标识传输(执行直接复制而不进行实际转换)可以这样做
/// TODO
客户端侧的集成也类似简单。
/// TODO
有关更深入的集成示例,请参阅lyrebird
crate中的二进制示例。
配置和状态
可以使用各自的构建器接口实现配置传输,这些接口需要options(...)
和statedir(...)
函数。有关示例实现,请参阅obfs4 transport
。
组合
由于ptrs接口封装了实现连接特性对象的对象,并返回实现相同抽象的特性和对象,因此可以在多个传输层之上进行封装。这样做的一个原因可能是为了有独立的可靠性、混淆和填充策略,这些策略可以相互组合。
let listener = tokio::net::TcpListener::bind("127.0.0.1:8009")
.await
.unwrap();
let (tcp_sock, _) = listener.accept().await.unwrap();
let pb: &BuilderS = &<Passthrough as PluggableTransport<TcpStream>>::server_builder();
let client1 = <BuilderS as ServerBuilder<TcpStream>>::build(pb);
let conn1 = client1.reveal(tcp_sock).await.unwrap();
let client2 = <BuilderS as ServerBuilder<TcpStream>>::build(pb);
let conn2 = client2.reveal(conn1).await.unwrap();
let client3 = <BuilderS as ServerBuilder<TcpStream>>::build(pb);
let mut sock = client3.reveal(conn2).await.unwrap();
let (mut r, mut w) = tokio::io::split(&mut sock);
_ = tokio::io::copy(&mut r, &mut w).await;
在客户端
let pb: &BuilderC = &<Passthrough as PluggableTransport<TcpStream>>::client_builder();
let client = <BuilderC as ClientBuilder<TcpStream>>::build(pb);
let conn_fut1 = client.establish(Box::pin(tcp_dial_fut));
let client = <BuilderC as ClientBuilder<TcpStream>>::build(pb);
let conn_fut2 = client.establish(Box::pin(conn_fut1));
let client = <BuilderC as ClientBuilder<TcpStream>>::build(pb);
let conn_fut3 = client.establish(Box::pin(conn_fut2));
let mut conn = conn_fut3.await?;
let msg = b"a man a plan a canal panama";
_ = conn.write(&msg[..]).await?;
实现传输
可以使用几种构建可插拔传输的结构,部分原因是没有任何一种结构在证明上优于其他结构。
obfs4传输是使用tokio_util::codec模型实现的。
注释/资源
虽然这与Tor可插拔传输系统相关并受到其启发,但本存储库的主要关注点是创建一个一致且有用的抽象,用于构建可插拔传输。有关Tor相关可插拔传输的更多信息,请参阅以下资源。
开源许可证
MIT和Apache-2.0双重授权是Rust语言社区目前接受的规范,自编译器和许多公共库以来一直被使用(见为什么是双重MIT/ASL2许可证?)。为了符合社区标准,ptrs使用双重MIT+Apache-2.0许可证。
贡献
欢迎贡献者、问题和拉取请求
依赖项
~5–12MB
~151K SLoC