pub mod config; use anyhow::bail; use azalea_protocol::packets::handshake::ServerboundHandshakePacket; use azalea_protocol::packets::login::ServerboundLoginPacket; use azalea_protocol::packets::ConnectionProtocol; use azalea_protocol::read::read_packet; use azalea_protocol::write::write_packet; use bytes::BytesMut; use config::Config; use log::{error, info, warn}; use std::fs::read_to_string; use std::net::SocketAddr; use std::sync::Arc; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; use tokio::net::{TcpListener, TcpStream}; #[tokio::main] async fn main() { env_logger::init_from_env("LOG"); let config = Arc::new(serde_yaml::from_str::(&read_to_string("proxy.yaml").unwrap()).unwrap()); let listener = TcpListener::bind("127.0.0.1:25565").await.unwrap(); info!("listening"); loop { match listener.accept().await { Ok((sock, addr)) => { let config = config.clone(); tokio::spawn(async move { match handle_client(config, sock, addr).await { Ok(()) => (), Err(e) => warn!("{e}"), } }); } Err(e) => error!("{}", e), } } } async fn handle_client( config: Arc, sock: TcpStream, addr: SocketAddr, ) -> anyhow::Result<()> { info!("connection {addr}"); let mut buf = BytesMut::new(); let (mut downstream_reader, downstream_writer) = sock.into_split(); let handshake = read_packet::( &mut downstream_reader, &mut buf, None, &mut None, ) .await?; let upstream_handshake = match handshake { ServerboundHandshakePacket::ClientIntention(p) => { info!( "new client (version={}, intent={:?})", p.protocol_version, p.intention ); match p.intention { ConnectionProtocol::Status => { bail!("we dont support ping yet") } ConnectionProtocol::Login => {} _ => bail!("invalid intent"), } p } }; let login = read_packet::(&mut downstream_reader, &mut buf, None, &mut None) .await?; let upstream_login = match login { ServerboundLoginPacket::Hello(mut p) => { info!("client hello (username={:?})", p.username); let profile = config .whitelist .iter() .find(|e| e.token.as_ref().map_or(false, |e| e == &p.username)); match profile { Some(profile) => { warn!("auth as {}", profile.username); p.username = profile.username.clone(); } None => { warn!("no profile found, disconnecting client"); return Ok(()); } } p } ServerboundLoginPacket::Key(_) => bail!("key not supported"), ServerboundLoginPacket::CustomQuery(_) => bail!("custom query not supported"), }; let upstream = TcpStream::connect("127.0.0.1:25567").await?; let (upstream_reader, mut upstream_writer) = upstream.into_split(); write_packet( &ServerboundHandshakePacket::ClientIntention(upstream_handshake), &mut upstream_writer, None, &mut None, ) .await?; write_packet::( &ServerboundLoginPacket::Hello(upstream_login), &mut upstream_writer, None, &mut None, ) .await?; tokio::spawn(async move { connect(downstream_writer, upstream_reader).await.unwrap(); }); connect(upstream_writer, downstream_reader).await?; Ok(()) // for _ in 0..3 { // let a = read_packet::( // &mut upstream_reader, // &mut buf, // None, // &mut None, // ) // .await?; // debug!("login {a:?}"); // write_packet(&a, &mut downstream_writer, None, &mut None).await?; // } // tokio::spawn(async move { // let mut buf = BytesMut::new(); // loop { // let a = read_packet::( // &mut upstream_reader, // &mut buf, // None, // &mut None, // ) // .await // .unwrap(); // debug!("downstream {a:?}"); // write_packet(&a, &mut downstream_writer, None, &mut None) // .await // .unwrap(); // } // }); // loop { // let a = read_packet::( // &mut downstream_reader, // &mut buf, // None, // &mut None, // ) // .await?; // debug!("upstream {a:?}"); // write_packet(&a, &mut upstream_writer, None, &mut None).await?; // } } async fn connect(mut writer: OwnedWriteHalf, mut reader: OwnedReadHalf) -> anyhow::Result<()> { let mut buf = [0; 1024]; loop { let size = reader.read(&mut buf).await?; writer.write_all(&buf[..size]).await?; } }