Skip to content

Commit 52cfe1d

Browse files
committed
First commit
0 parents  commit 52cfe1d

File tree

6 files changed

+268
-0
lines changed

6 files changed

+268
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/target
2+
*.iml
3+
/.idea
4+
Cargo.lock

.rustfmt.toml

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use_try_shorthand = true

Cargo.toml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[package]
2+
name = "qtun"
3+
version = "0.1.0"
4+
authors = ["Max Lv <[email protected]>"]
5+
repository = "https://github.com/madeye/qtun"
6+
license = "MIT"
7+
edition = "2018"
8+
9+
[[bin]]
10+
name = "qtun-client"
11+
path = "src/client.rs"
12+
13+
[[bin]]
14+
name = "qtun-server"
15+
path = "src/server.rs"
16+
17+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
18+
19+
[dependencies]
20+
tokio = { version = "^0.2.7", features = ["full"] }
21+
bytes = "0.5"
22+
futures = "0.3"
23+
quinn = "0.6"
24+
structopt = "0.3"
25+
anyhow = "1.0"
26+
tracing = "0.1"

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# qtun
2+
3+
Yet another SIP003 plugin based on IETF-QUIC

src/client.rs

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use std::net::SocketAddr;
2+
use std::sync::Arc;
3+
4+
use tokio::net::{TcpListener, TcpStream};
5+
use tokio::prelude::*;
6+
7+
use anyhow::{anyhow, Result};
8+
use futures::future::try_join;
9+
use quinn::Endpoint;
10+
use structopt::{self, StructOpt};
11+
use tracing::info;
12+
13+
#[derive(StructOpt, Debug)]
14+
#[structopt(name = "qtun-client")]
15+
struct Opt {
16+
/// Address to listen on
17+
#[structopt(long = "local", default_value = "0.0.0.0:4433")]
18+
local: SocketAddr,
19+
/// Address to listen on
20+
#[structopt(long = "remote", default_value = "127.0.0.1:8138")]
21+
remote: SocketAddr,
22+
/// Override hostname used for certificate verification
23+
#[structopt(long = "host", default_value = "bing.com")]
24+
host: String,
25+
}
26+
27+
#[tokio::main]
28+
async fn main() -> Result<()> {
29+
let options = Opt::from_args();
30+
31+
let mut endpoint = quinn::Endpoint::builder();
32+
let client_config = quinn::ClientConfigBuilder::default();
33+
endpoint.default_client_config(client_config.build());
34+
35+
let (endpoint, _) = endpoint.bind(&"[::]:0".parse().unwrap())?;
36+
37+
let remote = Arc::<SocketAddr>::from(options.remote);
38+
let host = Arc::<String>::from(options.host);
39+
let endpoint = Arc::<Endpoint>::from(endpoint);
40+
41+
let mut listener = TcpListener::bind(options.local).await?;
42+
43+
while let Ok((inbound, _)) = listener.accept().await {
44+
info!("connection incoming");
45+
46+
let remote = Arc::clone(&remote);
47+
let host = Arc::clone(&host);
48+
let endpoint = Arc::clone(&endpoint);
49+
50+
let transfer = transfer(remote, host, endpoint, inbound);
51+
tokio::spawn(transfer);
52+
}
53+
54+
Ok(())
55+
}
56+
57+
async fn transfer(
58+
remote: Arc<SocketAddr>,
59+
host: Arc<String>,
60+
endpoint: Arc<Endpoint>,
61+
mut inbound: TcpStream,
62+
) -> Result<()> {
63+
let new_conn = endpoint
64+
.connect(&remote, &host)?
65+
.await
66+
.map_err(|e| anyhow!("failed to connect: {}", e))?;
67+
68+
let quinn::NewConnection {
69+
connection: conn, ..
70+
} = { new_conn };
71+
72+
let (mut ri, mut wi) = inbound.split();
73+
let (mut wo, mut ro) = conn
74+
.open_bi()
75+
.await
76+
.map_err(|e| anyhow!("failed to open stream: {}", e))?;
77+
78+
let client_to_server = io::copy(&mut ri, &mut wo);
79+
let server_to_client = io::copy(&mut ro, &mut wi);
80+
81+
try_join(client_to_server, server_to_client).await?;
82+
83+
Ok(())
84+
}

src/server.rs

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
use std::fs;
2+
use std::net::SocketAddr;
3+
use std::path::PathBuf;
4+
use std::sync::Arc;
5+
6+
use tokio::net::TcpStream;
7+
use tokio::prelude::*;
8+
9+
use anyhow::{Context, Result};
10+
use futures::future::try_join;
11+
use futures::{StreamExt, TryFutureExt};
12+
use structopt::{self, StructOpt};
13+
use tracing::{error, info};
14+
15+
#[derive(StructOpt, Debug)]
16+
#[structopt(name = "qtun-server")]
17+
struct Opt {
18+
/// TLS private key in PEM format
19+
#[structopt(
20+
parse(from_os_str),
21+
short = "k",
22+
long = "key",
23+
requires = "cert",
24+
default_value = "key.der"
25+
)]
26+
key: PathBuf,
27+
/// TLS certificate in PEM format
28+
#[structopt(
29+
parse(from_os_str),
30+
short = "c",
31+
long = "cert",
32+
requires = "key",
33+
default_value = "cert.der"
34+
)]
35+
cert: PathBuf,
36+
/// Enable stateless retries
37+
#[structopt(long = "stateless-retry")]
38+
stateless_retry: bool,
39+
/// Address to listen on
40+
#[structopt(long = "local", default_value = "0.0.0.0:4433")]
41+
local: SocketAddr,
42+
/// Address to listen on
43+
#[structopt(long = "remote", default_value = "127.0.0.1:8138")]
44+
remote: SocketAddr,
45+
}
46+
47+
#[tokio::main]
48+
async fn main() -> Result<()> {
49+
let options = Opt::from_args();
50+
51+
let mut transport_config = quinn::TransportConfig::default();
52+
transport_config.stream_window_uni(0);
53+
let mut server_config = quinn::ServerConfig::default();
54+
server_config.transport = Arc::new(transport_config);
55+
let mut server_config = quinn::ServerConfigBuilder::new(server_config);
56+
57+
if options.stateless_retry {
58+
server_config.use_stateless_retry(true);
59+
}
60+
61+
// load certificates
62+
let key_path = &options.key;
63+
let cert_path = &options.cert;
64+
let key = fs::read(key_path).context("failed to read private key")?;
65+
let key = if key_path.extension().map_or(false, |x| x == "der") {
66+
quinn::PrivateKey::from_der(&key)?
67+
} else {
68+
quinn::PrivateKey::from_pem(&key)?
69+
};
70+
let cert_chain = fs::read(cert_path).context("failed to read certificate chain")?;
71+
let cert_chain = if cert_path.extension().map_or(false, |x| x == "der") {
72+
quinn::CertificateChain::from_certs(quinn::Certificate::from_der(&cert_chain))
73+
} else {
74+
quinn::CertificateChain::from_pem(&cert_chain)?
75+
};
76+
server_config.certificate(cert_chain, key)?;
77+
78+
let mut endpoint = quinn::Endpoint::builder();
79+
endpoint.listen(server_config.build());
80+
81+
let remote = Arc::<SocketAddr>::from(options.remote);
82+
83+
let mut incoming = {
84+
let (endpoint, incoming) = endpoint.bind(&options.local)?;
85+
info!("listening on {}", endpoint.local_addr()?);
86+
incoming
87+
};
88+
89+
while let Some(conn) = incoming.next().await {
90+
info!("connection incoming");
91+
tokio::spawn(
92+
handle_connection(remote.clone(), conn).unwrap_or_else(move |e| {
93+
error!("connection failed: {reason}", reason = e.to_string())
94+
}),
95+
);
96+
}
97+
98+
Ok(())
99+
}
100+
101+
async fn handle_connection(remote: Arc<SocketAddr>, conn: quinn::Connecting) -> Result<()> {
102+
let quinn::NewConnection {
103+
connection: _,
104+
mut bi_streams,
105+
..
106+
} = conn.await?;
107+
108+
async {
109+
info!("established");
110+
111+
// Each stream initiated by the client constitutes a new request.
112+
while let Some(stream) = bi_streams.next().await {
113+
let stream = match stream {
114+
Err(quinn::ConnectionError::ApplicationClosed { .. }) => {
115+
info!("connection closed");
116+
return Ok(());
117+
}
118+
Err(e) => {
119+
return Err(e);
120+
}
121+
Ok(s) => s,
122+
};
123+
tokio::spawn(
124+
transfer(remote.clone(), stream)
125+
.unwrap_or_else(move |e| error!("failed: {reason}", reason = e.to_string())),
126+
);
127+
}
128+
Ok(())
129+
}
130+
.await?;
131+
132+
Ok(())
133+
}
134+
135+
async fn transfer(
136+
remote: Arc<SocketAddr>,
137+
inbound: (quinn::SendStream, quinn::RecvStream),
138+
) -> Result<()> {
139+
let mut outbound = TcpStream::connect(remote.as_ref()).await?;
140+
141+
let (mut wi, mut ri) = inbound;
142+
let (mut ro, mut wo) = outbound.split();
143+
144+
let client_to_server = io::copy(&mut ri, &mut wo);
145+
let server_to_client = io::copy(&mut ro, &mut wi);
146+
147+
try_join(client_to_server, server_to_client).await?;
148+
149+
Ok(())
150+
}

0 commit comments

Comments
 (0)