1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00

Add proxy example

This commit is contained in:
EightFactorial 2023-01-18 12:32:16 -08:00
parent 798cfc233e
commit 15a7085514
6 changed files with 384 additions and 10 deletions

166
Cargo.lock generated
View file

@ -97,7 +97,7 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"hermit-abi 0.1.19",
"libc",
"winapi",
]
@ -121,7 +121,7 @@ dependencies = [
"azalea-physics",
"azalea-protocol",
"azalea-world",
"env_logger",
"env_logger 0.10.0",
"futures",
"log",
"nohash-hasher",
@ -130,6 +130,8 @@ dependencies = [
"priority-queue",
"thiserror",
"tokio",
"tracing",
"tracing-subscriber",
"uuid",
]
@ -140,7 +142,7 @@ dependencies = [
"azalea-buf",
"azalea-crypto",
"chrono",
"env_logger",
"env_logger 0.9.3",
"log",
"num-bigint",
"reqwest",
@ -415,7 +417,7 @@ dependencies = [
"anyhow",
"azalea",
"azalea-protocol",
"env_logger",
"env_logger 0.9.3",
"parking_lot",
"rand",
"tokio",
@ -711,6 +713,40 @@ dependencies = [
"termcolor",
]
[[package]]
name = "env_logger"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
dependencies = [
"humantime",
"is-terminal",
"log",
"regex",
"termcolor",
]
[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "fastrand"
version = "1.8.0"
@ -928,6 +964,15 @@ dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]]
name = "http"
version = "0.2.8"
@ -1054,12 +1099,34 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "io-lifetimes"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
dependencies = [
"libc",
"windows-sys 0.42.0",
]
[[package]]
name = "ipnet"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745"
[[package]]
name = "is-terminal"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
dependencies = [
"hermit-abi 0.2.6",
"io-lifetimes",
"rustix",
"windows-sys 0.42.0",
]
[[package]]
name = "itertools"
version = "0.10.5"
@ -1108,6 +1175,12 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "lock_api"
version = "0.4.9"
@ -1217,6 +1290,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "num"
version = "0.4.0"
@ -1310,7 +1393,7 @@ version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
dependencies = [
"hermit-abi",
"hermit-abi 0.1.19",
"libc",
]
@ -1380,6 +1463,12 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -1650,6 +1739,20 @@ version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustix"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys 0.42.0",
]
[[package]]
name = "ryu"
version = "1.0.11"
@ -1768,6 +1871,15 @@ dependencies = [
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
dependencies = [
"lazy_static",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
@ -1888,6 +2000,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "thread_local"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
dependencies = [
"once_cell",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
@ -2004,6 +2125,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
dependencies = [
"lazy_static",
"log",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
dependencies = [
"nu-ansi-term",
"sharded-slab",
"smallvec",
"thread_local",
"tracing-core",
"tracing-log",
]
[[package]]
@ -2110,6 +2257,15 @@ name = "uuid"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
dependencies = [
"serde",
]
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"

View file

@ -125,7 +125,7 @@ impl<'de> Deserialize<'de> for GameProfile {
}
}
const FIELDS: &'static [&'static str] = &["id", "name", "properties"];
const FIELDS: &[&str] = &["id", "name", "properties"];
deserializer.deserialize_struct("GameProfile", FIELDS, GameProfileVisitor)
}
}

View file

@ -77,8 +77,8 @@ impl ChatPacket {
/// player-sent chat message, this will be None.
pub fn uuid(&self) -> Option<Uuid> {
match self {
ChatPacket::System(_) => return None,
ChatPacket::Player(m) => return Some(m.sender),
ChatPacket::System(_) => None,
ChatPacket::Player(m) => Some(m.sender),
}
}

View file

@ -18,7 +18,7 @@ use std::marker::PhantomData;
use std::net::SocketAddr;
use thiserror::Error;
use tokio::io::AsyncWriteExt;
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf, ReuniteError};
use tokio::net::TcpStream;
use uuid::Uuid;
@ -448,4 +448,9 @@ where
},
}
}
/// Convert from a `Connection` into a `TcpStream`. Useful for servers.
pub fn unwrap(self) -> Result<TcpStream, ReuniteError> {
self.reader.read_stream.reunite(self.writer.write_stream)
}
}

View file

@ -27,11 +27,13 @@ nohash-hasher = "0.2.0"
num-traits = "0.2.15"
parking_lot = { version = "^0.12.1", features = ["deadlock_detection"] }
priority-queue = "1.3.0"
tracing = "0.1.36"
tracing-subscriber = "0.3.15"
thiserror = "^1.0.37"
tokio = "^1.23.1"
uuid = "1.2.2"
[dev-dependencies]
anyhow = "^1.0.65"
env_logger = "^0.9.1"
env_logger = "^0.10.0"
tokio = "^1.23.1"

211
azalea/examples/proxy.rs Normal file
View file

@ -0,0 +1,211 @@
//! A `simple` server that gets login information and proxies connections.
//! After login all connections are encrypted and Azalea cannot read them.
use azalea_protocol::{
connect::Connection,
packets::{
handshake::{
client_intention_packet::ClientIntentionPacket, ClientboundHandshakePacket,
ServerboundHandshakePacket,
},
login::{serverbound_hello_packet::ServerboundHelloPacket, ServerboundLoginPacket},
status::{
clientbound_pong_response_packet::ClientboundPongResponsePacket,
clientbound_status_response_packet::{
ClientboundStatusResponsePacket, Players, Version,
},
ServerboundStatusPacket,
},
ConnectionProtocol,
},
read::ReadPacketError,
};
use futures::FutureExt;
use log::{error, info, warn};
use std::error::Error;
use tokio::{
io::{self, AsyncWriteExt},
net::{TcpListener, TcpStream},
};
use tracing::Level;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt().with_max_level(Level::INFO).init();
// Bind to an address and port
let listener = TcpListener::bind("127.0.0.1:25566").await?;
loop {
// When a connection is made, pass it off to another thread
let (stream, _) = listener.accept().await?;
tokio::spawn(handle_connection(stream));
}
}
async fn handle_connection(stream: TcpStream) -> anyhow::Result<()> {
stream.set_nodelay(true)?;
let ip = stream.peer_addr()?;
let mut conn: Connection<ServerboundHandshakePacket, ClientboundHandshakePacket> =
Connection::wrap(stream);
// The first packet sent from a client is the intent packet.
// This specifies whether the client is pinging
// the server or is going to join the game.
let intent = match conn.read().await {
Err(e) => {
let e = e.into();
error!("{e}");
return Err(e);
}
Ok(p) => match p {
ServerboundHandshakePacket::ClientIntention(i) => {
info!(
"New connection: {0}, Version {1}, {2:?}",
ip, i.protocol_version, i.intention
);
i
}
},
};
match intent.intention {
// If the client is pinging the proxy,
// reply with the information below.
ConnectionProtocol::Status => {
let mut conn = conn.status();
loop {
match conn.read().await {
Ok(p) => match p {
ServerboundStatusPacket::StatusRequest(_) => {
conn.write(
ClientboundStatusResponsePacket {
description: String::from("An Azalea Minecraft Proxy").into(),
favicon: None,
players: Players {
max: 1,
online: 0,
sample: Vec::new(),
},
version: Version {
name: String::from("1.19.3"),
protocol: 761,
},
previews_chat: Some(false),
enforces_secure_chat: Some(false),
}
.get(),
)
.await?;
}
ServerboundStatusPacket::PingRequest(p) => {
conn.write(ClientboundPongResponsePacket { time: p.time }.get())
.await?;
}
},
Err(e) => match *e {
ReadPacketError::ConnectionClosed => {
break;
}
e => {
error!("Error during status: {e}");
return Err(e.into());
}
},
}
}
}
// If the client intends to join the proxy,
// wait for them to send the `Hello` packet to
// log their username and uuid, then forward the
// connection along to the proxy target.
ConnectionProtocol::Login => {
let mut conn = conn.login();
loop {
match conn.read().await {
Ok(p) => match p {
ServerboundLoginPacket::Hello(hello) => {
let id = if let Some(id) = hello.profile_id {
id.to_string()
} else {
"".to_string()
};
info!("Player {0} logging in with uuid:{id}", hello.name);
tokio::spawn(
transfer(
conn.unwrap()?,
"127.0.0.1:25565".to_string(),
intent,
hello,
)
.map(|r| {
if let Err(e) = r {
error!("Failed to transfer; error={e}");
}
}),
);
break;
}
_ => {}
},
Err(e) => match *e {
ReadPacketError::ConnectionClosed => {
break;
}
e => {
error!("Error during login: {e}");
return Err(e.into());
}
},
}
}
}
_ => {
warn!("Client provided weird intent {intent:?}, closing connection");
return Ok(());
}
}
Ok(())
}
async fn transfer(
mut inbound: TcpStream,
proxy_addr: String,
intent: ClientIntentionPacket,
hello: ServerboundHelloPacket,
) -> Result<(), Box<dyn Error>> {
let outbound = TcpStream::connect(proxy_addr).await?;
let name = hello.name.clone();
outbound.set_nodelay(true)?;
// Repeat the intent and hello packet
// recieved earlier to the proxy target
let mut outbound_conn: Connection<ClientboundHandshakePacket, ServerboundHandshakePacket> =
Connection::wrap(outbound);
outbound_conn.write(intent.get()).await?;
let mut outbound_conn = outbound_conn.login();
outbound_conn.write(hello.get()).await?;
let mut outbound = outbound_conn.unwrap()?;
// Split the incoming and outgoing connections in
// halves and handle each pair on separate threads.
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)?;
info!("Player {name} left the game");
Ok(())
}