Create Rust project and add proxy functionality
This commit is contained in:
		
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -8,3 +8,8 @@ Cargo.lock
 | 
			
		||||
 | 
			
		||||
# These are backup files generated by rustfmt
 | 
			
		||||
**/*.rs.bk
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Added by cargo
 | 
			
		||||
 | 
			
		||||
/target
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "proxima-centauri"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
anyhow = "1.0.69"
 | 
			
		||||
axum = { version = "0.6.11", features = ["json"] }
 | 
			
		||||
serde = { version = "1.0.155", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0.94"
 | 
			
		||||
tokio = { version = "1.26.0", features = ["full"] }
 | 
			
		||||
tracing = "0.1.37"
 | 
			
		||||
tracing-subscriber = "0.3.16"
 | 
			
		||||
uuid = { version = "1.3.0", features = ["v4", "serde"] }
 | 
			
		||||
							
								
								
									
										149
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,149 @@
 | 
			
		||||
use axum::{http::StatusCode, Json};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use std::net::{IpAddr, SocketAddr};
 | 
			
		||||
use tokio::io::{self, AsyncWriteExt};
 | 
			
		||||
use tokio::net::{TcpListener, TcpStream};
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize, Serialize, Debug)]
 | 
			
		||||
pub struct ProxyCommand {
 | 
			
		||||
    command: Command,
 | 
			
		||||
    signature: [u8; 32],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize, Serialize, Debug)]
 | 
			
		||||
enum Command {
 | 
			
		||||
    New {
 | 
			
		||||
        incoming_port: u16,
 | 
			
		||||
        destination_port: u16,
 | 
			
		||||
        destination_ip: IpAddr,
 | 
			
		||||
        id: Uuid,
 | 
			
		||||
    },
 | 
			
		||||
    Modify {
 | 
			
		||||
        destionation_ip: IpAddr,
 | 
			
		||||
        id: Uuid,
 | 
			
		||||
    },
 | 
			
		||||
    Delete {
 | 
			
		||||
        id: Uuid,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize)]
 | 
			
		||||
pub struct ProxyResponse {
 | 
			
		||||
    message: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn root() -> &'static str {
 | 
			
		||||
    "Hello, World!"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn process_command(
 | 
			
		||||
    Json(payload): Json<ProxyCommand>,
 | 
			
		||||
) -> (StatusCode, Json<ProxyResponse>) {
 | 
			
		||||
    tracing::error!("Received payload: {:?}", payload);
 | 
			
		||||
    // TODO: verify signature
 | 
			
		||||
    match payload.command {
 | 
			
		||||
        Command::New {
 | 
			
		||||
            incoming_port,
 | 
			
		||||
            destination_port,
 | 
			
		||||
            destination_ip,
 | 
			
		||||
            id,
 | 
			
		||||
        } => {
 | 
			
		||||
            // TODO: add id to global proxy map
 | 
			
		||||
            add_proxy(
 | 
			
		||||
                incoming_port,
 | 
			
		||||
                SocketAddr::new(destination_ip, destination_port),
 | 
			
		||||
            )
 | 
			
		||||
            .await
 | 
			
		||||
            .unwrap(); // TODO: error propagation??
 | 
			
		||||
        }
 | 
			
		||||
        Command::Modify {
 | 
			
		||||
            destionation_ip,
 | 
			
		||||
            id,
 | 
			
		||||
        } => todo!(),
 | 
			
		||||
        Command::Delete { id } => todo!(),
 | 
			
		||||
    }
 | 
			
		||||
    (
 | 
			
		||||
        StatusCode::CREATED,
 | 
			
		||||
        Json(ProxyResponse {
 | 
			
		||||
            message: "Success".to_string(),
 | 
			
		||||
        }),
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn add_proxy(in_port: u16, destination: SocketAddr) -> anyhow::Result<()> {
 | 
			
		||||
    let listener = TcpListener::bind(("127.0.0.1", in_port)).await?;
 | 
			
		||||
 | 
			
		||||
    tracing::info!("proxying port {in_port} to {destination}");
 | 
			
		||||
 | 
			
		||||
    tokio::spawn(proxy(listener, destination));
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn proxy(listener: TcpListener, destination: SocketAddr) {
 | 
			
		||||
    while let Ok((inbound, _)) = listener.accept().await {
 | 
			
		||||
        let transfer = transfer(inbound, destination);
 | 
			
		||||
 | 
			
		||||
        tokio::spawn(transfer);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn transfer(mut inbound: TcpStream, destination: SocketAddr) -> anyhow::Result<()> {
 | 
			
		||||
    let mut outbound = TcpStream::connect(destination).await?;
 | 
			
		||||
 | 
			
		||||
    let (mut ri, mut wi) = inbound.split();
 | 
			
		||||
    let (mut ro, mut wo) = outbound.split();
 | 
			
		||||
 | 
			
		||||
    let client_to_server = async {
 | 
			
		||||
        io::copy(&mut ri, &mut wo).await?;
 | 
			
		||||
        wo.shutdown().await
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let server_to_client = async {
 | 
			
		||||
        io::copy(&mut ro, &mut wi).await?;
 | 
			
		||||
        wi.shutdown().await
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    tokio::try_join!(client_to_server, server_to_client)?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use std::net::Ipv4Addr;
 | 
			
		||||
 | 
			
		||||
    use crate::{Command, ProxyCommand};
 | 
			
		||||
    use serde::{Deserialize, Serialize};
 | 
			
		||||
    use uuid::uuid;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn serialize_proxy_command_new() {
 | 
			
		||||
        let proxy_command = ProxyCommand {
 | 
			
		||||
            command: Command::New {
 | 
			
		||||
                incoming_port: 5555,
 | 
			
		||||
                destination_port: 6666,
 | 
			
		||||
                destination_ip: std::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
 | 
			
		||||
                id: uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8"),
 | 
			
		||||
            },
 | 
			
		||||
            signature: [0u8; 32],
 | 
			
		||||
        };
 | 
			
		||||
        let expected = "{\"command\":{\"New\":{\"incoming_port\":5555,\"destination_port\":6666,\"\
 | 
			
		||||
                        destination_ip\":\"127.0.0.1\",\"id\":\"67e55044-10b1-426f-9247-bb680e5fe0c8\"}},\
 | 
			
		||||
                        \"signature\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}";
 | 
			
		||||
        assert_eq!(serde_json::to_string(&proxy_command).unwrap(), expected);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn serialize_proxy_command_delete() {
 | 
			
		||||
        let proxy_command = ProxyCommand {
 | 
			
		||||
            command: Command::Delete {
 | 
			
		||||
                id: uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8"),
 | 
			
		||||
            },
 | 
			
		||||
            signature: [0u8; 32],
 | 
			
		||||
        };
 | 
			
		||||
        let expected = "{\"command\":{\"Delete\":{\"id\":\"67e55044-10b1-426f-9247-bb680e5fe0c8\"}},\
 | 
			
		||||
                        \"signature\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}";
 | 
			
		||||
        assert_eq!(serde_json::to_string(&proxy_command).unwrap(), expected);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
use axum::{
 | 
			
		||||
    routing::{get, post},
 | 
			
		||||
    Router,
 | 
			
		||||
};
 | 
			
		||||
use proxima_centauri::{process_command, root};
 | 
			
		||||
use std::net::SocketAddr;
 | 
			
		||||
use tracing::Level;
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() {
 | 
			
		||||
    // initialize tracing
 | 
			
		||||
    let subscriber = tracing_subscriber::FmtSubscriber::builder()
 | 
			
		||||
        .with_max_level(Level::TRACE)
 | 
			
		||||
        .finish();
 | 
			
		||||
 | 
			
		||||
    tracing::subscriber::set_global_default(subscriber).unwrap();
 | 
			
		||||
 | 
			
		||||
    // build our application with a route
 | 
			
		||||
    let app = Router::new()
 | 
			
		||||
        // `GET /` goes to `root`
 | 
			
		||||
        .route("/", get(root))
 | 
			
		||||
        // `POST /command` goes to `process_command`
 | 
			
		||||
        .route("/command", post(process_command));
 | 
			
		||||
 | 
			
		||||
    // run our app with hyper
 | 
			
		||||
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
 | 
			
		||||
    tracing::debug!("listening  on {}", addr);
 | 
			
		||||
    axum::Server::bind(&addr)
 | 
			
		||||
        .serve(app.into_make_service())
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user