Initial Commit
This commit is contained in:
commit
a984b85e9a
|
@ -0,0 +1 @@
|
|||
/target
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
|
@ -0,0 +1,2 @@
|
|||
[toolchain]
|
||||
channel = "nightly"
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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"))?;
|
||||
}
|
||||
}
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue