Initial Commit

This commit is contained in:
Ezra Barrow 2023-08-14 11:28:32 -05:00
commit a984b85e9a
9 changed files with 1495 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

1027
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

17
Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "sleepytunny"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = { version = "1.0.72", features = ["backtrace"] }
backoff = { version = "0.4.0", features = ["tokio"] }
base64 = "0.21.2"
color-eyre = "0.6.2"
quinn = { version = "0.10.2", features = [] }
rcgen = "0.11.1"
rustls = { version = "0.21.6", features = ["dangerous_configuration", "quic"] }
tokio = { version = "1.31.0", features = ["full"] }
tokio-tun = "0.9.0"

2
rust-toolchain.toml Normal file
View File

@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

22
src/bin/client.rs Normal file
View File

@ -0,0 +1,22 @@
/*
* --------------------
* THIS FILE IS LICENSED UNDER THE FOLLOWING TERMS
*
* this code may not be used for any purpose. be gay, do crime
*
* THE FOLLOWING MESSAGE IS NOT A LICENSE
*
* <barrow@tilde.team> wrote this file.
* by reading this text, you are reading "TRANS RIGHTS".
* this file and the content within it is the gay agenda.
* if we meet some day, and you think this stuff is worth it,
* you can buy me a beer, tea, or something stronger.
* -Ezra Barrow
* --------------------
*/
#[tokio::main]
async fn main() -> anyhow::Result<()> {
sleepytunny::client_main().await
}

22
src/bin/server.rs Normal file
View File

@ -0,0 +1,22 @@
/*
* --------------------
* THIS FILE IS LICENSED UNDER THE FOLLOWING TERMS
*
* this code may not be used for any purpose. be gay, do crime
*
* THE FOLLOWING MESSAGE IS NOT A LICENSE
*
* <barrow@tilde.team> wrote this file.
* by reading this text, you are reading "TRANS RIGHTS".
* this file and the content within it is the gay agenda.
* if we meet some day, and you think this stuff is worth it,
* you can buy me a beer, tea, or something stronger.
* -Ezra Barrow
* --------------------
*/
#[tokio::main]
async fn main() -> anyhow::Result<()> {
sleepytunny::server_main().await
}

122
src/encoder.rs Normal file
View File

@ -0,0 +1,122 @@
/*
* --------------------
* THIS FILE IS LICENSED UNDER THE FOLLOWING TERMS
*
* this code may not be used for any purpose. be gay, do crime
*
* THE FOLLOWING MESSAGE IS NOT A LICENSE
*
* <barrow@tilde.team> wrote this file.
* by reading this text, you are reading "TRANS RIGHTS".
* this file and the content within it is the gay agenda.
* if we meet some day, and you think this stuff is worth it,
* you can buy me a beer, tea, or something stronger.
* -Ezra Barrow
* --------------------
*/
use base64::encoded_len;
use base64::engine::general_purpose::STANDARD_NO_PAD as BASE64_ENGINE;
use base64::Engine;
use std::io;
use std::{
ops::{Div, Mul},
pin::Pin,
task::{ready, Poll},
};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
const INITIAL_BUF_SIZE: usize = 1024;
pub struct Encoder<T: AsyncRead + AsyncWrite> {
inner_read: tokio::io::ReadHalf<T>,
inner_write: tokio::io::WriteHalf<T>,
// actually, if both sides have the same internal BUF_SIZE, one of these should be len BUF_SIZE
read_buffer: Vec<u8>,
write_buffer: Vec<u8>,
}
impl<T: AsyncRead + AsyncWrite> Encoder<T> {
pub fn new(inner: T) -> Self {
let (inner_read, inner_write) = tokio::io::split(inner);
Self {
inner_read,
inner_write,
read_buffer: Vec::with_capacity(INITIAL_BUF_SIZE / 4 * 3),
write_buffer: Vec::with_capacity(INITIAL_BUF_SIZE / 3 * 4 + 4),
}
}
}
impl<T: AsyncRead + AsyncWrite> AsyncRead for Encoder<T> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> std::task::Poll<std::io::Result<()>> {
let remaining_octets = base64::decoded_len_estimate(buf.remaining());
// dbg!(
// buf.filled().len(),
// buf.remaining(),
// buf.remaining().div(4).mul(3),
// remaining_octets
// );
let (inner, mut bytebuf) = unsafe {
let s = self.get_unchecked_mut();
let inner = Pin::new_unchecked(&mut s.inner_read);
let buffer = &mut s.read_buffer;
if remaining_octets > buffer.len() {
buffer.resize(remaining_octets, 0);
}
(inner, ReadBuf::new(&mut buffer[..remaining_octets]))
};
ready!(inner.poll_read(cx, &mut bytebuf))?;
let base64_len = BASE64_ENGINE
.encode_slice(bytebuf.filled(), buf.initialize_unfilled())
.expect("read buffer too small somehow !");
buf.advance(base64_len);
Poll::Ready(Ok(()))
}
}
impl<T: AsyncRead + AsyncWrite> AsyncWrite for Encoder<T> {
fn poll_write(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, std::io::Error>> {
let (inner, bytebuf) = unsafe {
let s = self.get_unchecked_mut();
let inner = Pin::new_unchecked(&mut s.inner_write);
let buffer = &mut s.write_buffer;
buffer.clear();
(inner, buffer)
};
BASE64_ENGINE
.decode_vec(buf, bytebuf)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let decoded_bytes_written = ready!(inner.poll_write(cx, bytebuf))?;
let encoded_bytes_written = encoded_len(decoded_bytes_written, false).unwrap();
assert_eq!(encoded_bytes_written, buf.len());
Poll::Ready(Ok(encoded_bytes_written))
}
fn poll_flush(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Result<(), std::io::Error>> {
let inner = unsafe { self.map_unchecked_mut(|s| &mut s.inner_write) };
// this errors out for some reason
// let r = ready!(inner.poll_flush(cx)).unwrap();
Poll::Ready(Ok(()))
}
fn poll_shutdown(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Result<(), std::io::Error>> {
let inner = unsafe { self.map_unchecked_mut(|s| &mut s.inner_write) };
inner.poll_shutdown(cx)
}
}

123
src/lib.rs Normal file
View File

@ -0,0 +1,123 @@
/*
* --------------------
* THIS FILE IS LICENSED UNDER THE FOLLOWING TERMS
*
* this code may not be used for any purpose. be gay, do crime
*
* THE FOLLOWING MESSAGE IS NOT A LICENSE
*
* <barrow@tilde.team> wrote this file.
* by reading this text, you are reading "TRANS RIGHTS".
* this file and the content within it is the gay agenda.
* if we meet some day, and you think this stuff is worth it,
* you can buy me a beer, tea, or something stronger.
* -Ezra Barrow
* --------------------
*/
use anyhow::Context;
use std::{io, time::Duration};
use tokio::time::sleep;
use tokio_tun::Tun;
mod encoder;
use encoder::Encoder;
mod quic;
use quic::QuicModem;
fn is_timeout<T>(res: &anyhow::Result<T>) -> bool {
let e = match res {
Ok(_) => return false,
Err(e) => e,
};
dbg!(e);
if let Some(e) = e.downcast_ref::<quinn::ConnectionError>() {
if e == &quinn::ConnectionError::TimedOut {
return true;
}
}
if let Some(e) = e.downcast_ref::<io::Error>() {
use io::ErrorKind::*;
match e.kind() {
TimedOut | NotConnected => return true,
_ => {}
}
}
false
}
macro_rules! handle_timeout {
($result:expr) => {
handle_timeout!($result, {
eprintln!("timed out");
continue;
})
};
($result:expr, $block:expr) => {{
let _result = $result;
if is_timeout(&_result) {
$block
}
_result
}};
}
pub async fn client_main() -> anyhow::Result<()> {
let tun = Tun::builder()
.name("")
.tap(false)
.packet_info(true)
.address([10, 177, 233, 1].into())
.netmask([255, 255, 255, 0].into())
.up()
.try_build()?;
let mut tun = Encoder::new(tun);
let mut quic = QuicModem::new_client("10.177.1.7:9092")?;
let mut backoff: u64 = 1;
loop {
eprintln!("[client] attempting to connect...");
let mut conn = handle_timeout!(quic.connect().await, {
eprintln!("[client] timed out, waiting {} seconds...", backoff);
sleep(Duration::from_secs(backoff)).await;
backoff *= 2;
continue;
})?;
backoff = 1;
eprintln!("[client] connected: addr={}", conn.remote_address());
handle_timeout!(tokio::io::copy_bidirectional(&mut tun, &mut conn)
.await
.context("io error"))?;
}
}
pub async fn server_main() -> anyhow::Result<()> {
let tun = Tun::builder()
.name("")
.tap(false)
.packet_info(true)
.address([10, 177, 233, 2].into())
.netmask([255, 255, 255, 0].into())
.up()
.try_build()?;
let mut tun = Encoder::new(tun);
let mut quic = QuicModem::new_server("10.177.1.7:9092")?;
loop {
println!("[server] waiting for connection...");
let mut conn = handle_timeout!(quic.accept().await)?;
println!(
"[server] connection accepted: addr={}",
conn.remote_address()
);
handle_timeout!(tokio::io::copy_bidirectional(&mut tun, &mut conn)
.await
.context("io error"))?;
}
}

159
src/quic.rs Normal file
View File

@ -0,0 +1,159 @@
/*
* --------------------
* THIS FILE IS LICENSED UNDER THE FOLLOWING TERMS
*
* this code may not be used for any purpose. be gay, do crime
*
* THE FOLLOWING MESSAGE IS NOT A LICENSE
*
* <barrow@tilde.team> wrote this file.
* by reading this text, you are reading "TRANS RIGHTS".
* this file and the content within it is the gay agenda.
* if we meet some day, and you think this stuff is worth it,
* you can buy me a beer, tea, or something stronger.
* -Ezra Barrow
* --------------------
*/
use std::{
net::{SocketAddr, ToSocketAddrs},
sync::Arc,
time::Duration,
};
use anyhow::Context;
use tokio::{
io::{AsyncRead, AsyncWrite},
net::{TcpListener, TcpStream},
};
struct SkipServerVerification;
impl SkipServerVerification {
fn new() -> Arc<Self> {
Arc::new(Self)
}
}
impl rustls::client::ServerCertVerifier for SkipServerVerification {
fn verify_server_cert(
&self,
end_entity: &rustls::Certificate,
intermediates: &[rustls::Certificate],
server_name: &rustls::ServerName,
scts: &mut dyn Iterator<Item = &[u8]>,
ocsp_response: &[u8],
now: std::time::SystemTime,
) -> Result<rustls::client::ServerCertVerified, rustls::Error> {
Ok(rustls::client::ServerCertVerified::assertion())
}
}
fn configure_server() -> quinn::ServerConfig {
let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()]).unwrap();
let cert_der = cert.serialize_der().unwrap();
let priv_key = cert.serialize_private_key_der();
let priv_key = rustls::PrivateKey(priv_key);
let cert_chain = vec![rustls::Certificate(cert_der.clone())];
let mut server_config = quinn::ServerConfig::with_single_cert(cert_chain, priv_key).unwrap();
let transport_config = Arc::get_mut(&mut server_config.transport).unwrap();
transport_config
.max_concurrent_uni_streams(0u8.into())
// .max_idle_timeout(Some(Duration::from_secs(60).try_into().unwrap()))
.keep_alive_interval(Some(Duration::from_secs(3)));
server_config
}
fn configure_client() -> quinn::ClientConfig {
let crypto = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_custom_certificate_verifier(SkipServerVerification::new())
.with_no_client_auth();
quinn::ClientConfig::new(Arc::new(crypto))
}
pub struct QuicModem {
endpoint: quinn::Endpoint,
addr: SocketAddr,
}
impl QuicModem {
pub fn new_server(addr: impl ToSocketAddrs) -> anyhow::Result<Self> {
let addr = addr
.to_socket_addrs()?
.next()
.expect("bad server socketaddr");
let endpoint = quinn::Endpoint::server(configure_server(), addr)?;
eprintln!("initialized server");
Ok(Self { endpoint, addr })
}
pub fn new_client(addr: impl ToSocketAddrs) -> anyhow::Result<Self> {
let addr = addr
.to_socket_addrs()?
.next()
.expect("bad server socketaddr");
let mut endpoint = quinn::Endpoint::client("0.0.0.0:0".parse().unwrap())?;
endpoint.set_default_client_config(configure_client());
eprintln!("initialized client");
Ok(Self { endpoint, addr })
}
pub async fn accept(&mut self) -> anyhow::Result<QuicBiStream> {
let conn = self
.endpoint
.accept()
.await
.context("endpoint closed")?
.await?;
Ok(QuicBiStream::new(&conn, conn.open_bi().await?))
}
pub async fn connect(&mut self) -> anyhow::Result<QuicBiStream> {
let conn = self.endpoint.connect(self.addr, "localhost")?.await?;
Ok(QuicBiStream::new(&conn, conn.accept_bi().await?))
}
}
pub struct QuicBiStream {
conn: quinn::Connection,
send: quinn::SendStream,
recv: quinn::RecvStream,
}
impl QuicBiStream {
fn new(conn: &quinn::Connection, (send, recv): (quinn::SendStream, quinn::RecvStream)) -> Self {
Self {
conn: conn.clone(),
send,
recv,
}
}
pub fn remote_address(&self) -> SocketAddr {
self.conn.remote_address()
}
}
impl AsyncRead for QuicBiStream {
fn poll_read(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> std::task::Poll<std::io::Result<()>> {
unsafe { self.map_unchecked_mut(|s| &mut s.recv).poll_read(cx, buf) }
}
}
impl AsyncWrite for QuicBiStream {
fn poll_write(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> std::task::Poll<Result<usize, std::io::Error>> {
unsafe { self.map_unchecked_mut(|s| &mut s.send).poll_write(cx, buf) }
}
fn poll_flush(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), std::io::Error>> {
unsafe { self.map_unchecked_mut(|s| &mut s.send).poll_flush(cx) }
}
fn poll_shutdown(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), std::io::Error>> {
unsafe { self.map_unchecked_mut(|s| &mut s.send).poll_shutdown(cx) }
}
}