Add basic raw packet parsing, hijack ping and status message

This commit is contained in:
timvisee 2021-11-07 14:18:40 +01:00
parent 3dd734cb0f
commit 923e172d0d
No known key found for this signature in database
GPG Key ID: B8DB720BC383E172
6 changed files with 550 additions and 20 deletions

280
Cargo.lock generated
View File

@ -2,6 +2,18 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "autocfg"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
[[package]]
name = "autocfg"
version = "1.0.1"
@ -14,6 +26,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.1.0"
@ -26,6 +44,42 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
"bitflags",
]
[[package]]
name = "crc32fast"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
dependencies = [
"cfg-if",
]
[[package]]
name = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "futures"
version = "0.3.17"
@ -80,7 +134,7 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb"
dependencies = [
"autocfg",
"autocfg 1.0.1",
"proc-macro-hack",
"proc-macro2",
"quote",
@ -105,7 +159,7 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481"
dependencies = [
"autocfg",
"autocfg 1.0.1",
"futures-channel",
"futures-core",
"futures-io",
@ -138,12 +192,24 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "libc"
version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lock_api"
version = "0.4.5"
@ -166,7 +232,9 @@ dependencies = [
name = "mc-idle"
version = "0.1.0"
dependencies = [
"bytes",
"futures",
"minecraft-protocol",
"tokio",
]
@ -176,6 +244,39 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "minecraft-protocol"
version = "0.1.0"
source = "git+https://github.com/eihwaz/minecraft-protocol?rev=09f95625eff272794590e1ef73b2a1b673f47f50#09f95625eff272794590e1ef73b2a1b673f47f50"
dependencies = [
"byteorder",
"minecraft-protocol-derive",
"named-binary-tag",
"serde",
"serde_json",
"uuid",
]
[[package]]
name = "minecraft-protocol-derive"
version = "0.0.0"
source = "git+https://github.com/eihwaz/minecraft-protocol?rev=09f95625eff272794590e1ef73b2a1b673f47f50#09f95625eff272794590e1ef73b2a1b673f47f50"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg 1.0.1",
]
[[package]]
name = "mio"
version = "0.7.14"
@ -198,6 +299,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "named-binary-tag"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d654702943d37d67f1491769ed46484c306f9b9d0d258348904bd63ffb101e8"
dependencies = [
"byteorder",
"flate2",
"linked-hash-map",
]
[[package]]
name = "ntapi"
version = "0.3.6"
@ -290,6 +402,121 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
dependencies = [
"autocfg 0.1.7",
"libc",
"rand_chacha",
"rand_core 0.4.2",
"rand_hc",
"rand_isaac",
"rand_jitter",
"rand_os",
"rand_pcg",
"rand_xorshift",
"winapi",
]
[[package]]
name = "rand_chacha"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
dependencies = [
"autocfg 0.1.7",
"rand_core 0.3.1",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rand_jitter"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
dependencies = [
"libc",
"rand_core 0.4.2",
"winapi",
]
[[package]]
name = "rand_os"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
dependencies = [
"cloudabi",
"fuchsia-cprng",
"libc",
"rand_core 0.4.2",
"rdrand",
"winapi",
]
[[package]]
name = "rand_pcg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
dependencies = [
"autocfg 0.1.7",
"rand_core 0.4.2",
]
[[package]]
name = "rand_xorshift"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
@ -299,12 +526,49 @@ dependencies = [
"bitflags",
]
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
@ -343,7 +607,7 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee"
dependencies = [
"autocfg",
"autocfg 1.0.1",
"bytes",
"libc",
"memchr",
@ -374,6 +638,16 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "uuid"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a"
dependencies = [
"rand",
"serde",
]
[[package]]
name = "winapi"
version = "0.3.9"

View File

@ -4,5 +4,7 @@ version = "0.1.0"
edition = "2018"
[dependencies]
bytes = "1.1"
futures = "0.3"
minecraft-protocol = { git = "https://github.com/eihwaz/minecraft-protocol", rev = "09f95625eff272794590e1ef73b2a1b673f47f50" }
tokio = { version = "1", features = ["full"] }

11
src/config.rs Normal file
View File

@ -0,0 +1,11 @@
/// Public address for users to connect to.
pub const ADDRESS_PUBLIC: &str = "127.0.0.1:9090";
/// Minecraft server address to proxy to.
pub const ADDRESS_PROXY: &str = "127.0.0.1:9091";
/// Server description shown when server is starting.
pub const LABEL_SERVER_SLEEPING: &str = "Server sleeping...\nJoin to start it up §c♥";
/// Server description shown when server is starting.
pub const LABEL_SERVER_STARTING: &str = "Server starting...\nPlease wait §c♥";

View File

@ -1,29 +1,40 @@
use tokio::io;
use tokio::io::AsyncWriteExt;
use tokio::net::{TcpListener, TcpStream};
#![allow(unused)]
pub mod config;
pub mod protocol;
pub mod types;
use futures::FutureExt;
use std::error::Error;
/// Public address for users to connect to.
const ADDRESS_PUBLIC: &str = "127.0.0.1:9090";
use bytes::BytesMut;
use futures::future::poll_fn;
use futures::FutureExt;
use minecraft_protocol::decoder::Decoder;
use minecraft_protocol::encoder::Encoder;
use minecraft_protocol::version::v1_14_4::status::{PingRequest, PingResponse};
use tokio::io;
use tokio::io::AsyncWriteExt;
use tokio::io::ReadBuf;
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::mpsc::unbounded_channel;
/// Minecraft server address to proxy to.
const ADDRESS_PROXY: &str = "127.0.0.1:9091";
use config::*;
use protocol::RawPacket;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
async fn main() -> Result<(), ()> {
println!("Public address: {}", ADDRESS_PUBLIC);
println!("Proxy address: {}", ADDRESS_PROXY);
// Listen for new connections
let listener = TcpListener::bind(ADDRESS_PUBLIC).await?;
// TODO: do not drop error here
let listener = TcpListener::bind(ADDRESS_PUBLIC).await.map_err(|_| ())?;
// Proxy all incomming connections
while let Ok((inbound, _)) = listener.accept().await {
let transfer = proxy(inbound, ADDRESS_PROXY.to_string()).map(|r| {
if let Err(e) = r {
println!("Failed to proxy: {}", e);
println!("Failed to proxy: {:?}", e);
}
});
@ -34,20 +45,179 @@ async fn main() -> Result<(), Box<dyn Error>> {
}
/// Proxy the given inbound stream to a target address.
async fn proxy(mut inbound: TcpStream, addr_target: String) -> Result<(), Box<dyn Error>> {
let mut outbound = TcpStream::connect(addr_target).await?;
// TODO: do not drop error here, return Box<dyn Error>
async fn proxy(mut inbound: TcpStream, addr_target: String) -> Result<(), ()> {
// TODO: do not drop error here
let mut outbound = TcpStream::connect(addr_target).await.map_err(|_| ())?;
let (mut ri, mut wi) = inbound.split();
let (mut ro, mut wo) = outbound.split();
let (client_send_queue, mut client_to_send) = unbounded_channel::<Vec<u8>>();
let client_to_server = async {
io::copy(&mut ri, &mut wo).await?;
wo.shutdown().await
// Wait for readable state
while ri.readable().await.is_ok() {
// Poll until we have data available
let mut poll_buf = [0; 10];
let mut poll_buf = ReadBuf::new(&mut poll_buf);
// TODO: do not drop error here!
let read = poll_fn(|cx| ri.poll_peek(cx, &mut poll_buf))
.await
.map_err(|_| ())?;
if read == 0 {
continue;
}
// TODO: remove
// eprintln!("READ {}", read);
// Read packet from socket
let mut buf = Vec::with_capacity(64);
// TODO: do not drop error here
let read = ri.try_read_buf(&mut buf).map_err(|_| ())?;
if read == 0 {
continue;
}
// PING PACKET TEST
eprintln!("PACKET {:?}", buf.as_slice());
match RawPacket::decode(buf.as_mut_slice()) {
Ok(packet) => {
eprintln!("PACKET ID: {}", packet.id);
eprintln!("PACKET DATA: {:?}", packet.data);
if packet.id == 0 {
// Catch status packet
eprintln!("PACKET STATUS");
use minecraft_protocol::data::chat::{Message, Payload};
use minecraft_protocol::data::server_status::*;
use minecraft_protocol::version::v1_14_4::status::*;
// Build status response
let server_status = ServerStatus {
version: ServerVersion {
name: String::from("1.16.5"),
protocol: 754,
},
description: Message::new(Payload::text(LABEL_SERVER_SLEEPING)),
players: OnlinePlayers {
online: 0,
max: 0,
sample: vec![],
},
};
let status_response = StatusResponse { server_status };
let mut vec = Vec::new();
status_response.encode(&mut vec).unwrap();
let status_packet = RawPacket::new(0, vec);
let response = status_packet.encode()?;
client_send_queue
.send(response)
.expect("failed to queue status response");
continue;
}
if packet.id == 1 {
// Catch ping packet
if let Ok(ping) = PingRequest::decode(&mut packet.data.as_slice()) {
eprintln!("PACKET PING: {}", ping.time);
let response = packet.encode()?;
client_send_queue
.send(response)
.expect("failed to queue ping response");
continue;
} else {
eprintln!("PACKET PING PARSE ERROR!");
}
}
}
Err(()) => eprintln!("ERROR PARSING PACKET"),
}
// Forward data to server
wo.write_all(&buf).await.expect("failed to write to server");
// io::copy(&mut ri, &mut wo).await?;
}
// io::copy(&mut ri, &mut wo).await?;
// TODO: do not drop error here
wo.shutdown().await.map_err(|_| ())
};
let server_to_client = async {
io::copy(&mut ro, &mut wi).await?;
wi.shutdown().await
// let proxy = io::copy(&mut ro, &mut wi);
// Server packts to send to client, add to client sending queue
let proxy = async {
// Wait for readable state
while ro.readable().await.is_ok() {
// Poll until we have data available
let mut poll_buf = [0; 10];
let mut poll_buf = ReadBuf::new(&mut poll_buf);
// TODO: do not drop error here
let read = poll_fn(|cx| ro.poll_peek(cx, &mut poll_buf))
.await
.map_err(|_| ())?;
if read == 0 {
continue;
}
// TODO: remove
// eprintln!("READ {}", read);
// Read packet from socket
let mut buf = Vec::with_capacity(64);
// TODO: do not drop error here
let read = ro.try_read_buf(&mut buf).map_err(|_| ())?;
if read == 0 {
continue;
}
assert_eq!(buf.len(), read);
client_send_queue.send(buf);
// Forward data to server
// TODO: do not drop error here
// wo.write_all(&buf).await.map_err(|_| ())?;
// io::copy(&mut ri, &mut wo).await?;
}
Ok(())
};
// Push client sending queue to client
let other = async {
loop {
let msg = poll_fn(|cx| client_to_send.poll_recv(cx))
.await
.expect("failed to poll_fn");
wi.write_all(msg.as_ref())
.await
.expect("failed to write to client");
}
Ok(())
};
tokio::try_join!(proxy, other)?;
// TODO: do not drop error here
wi.shutdown().await.map_err(|_| ())
};
tokio::try_join!(client_to_server, server_to_client)?;

44
src/protocol.rs Normal file
View File

@ -0,0 +1,44 @@
use crate::types;
/// Raw Minecraft packet.
///
/// Having a packet ID and a raw data byte array.
pub struct RawPacket {
/// Packet ID.
pub id: i32,
/// Packet data.
pub data: Vec<u8>,
}
impl RawPacket {
/// Construct new raw packet.
pub fn new(id: i32, data: Vec<u8>) -> Self {
Self { id, data }
}
/// Decode packet from raw buffer.
pub fn decode(mut buf: &mut [u8]) -> Result<Self, ()> {
// Read length
let (read, len) = types::read_var_int(buf)?;
buf = &mut buf[read..][..len as usize];
// Read packet ID, select buf
let (read, packet_id) = types::read_var_int(buf)?;
buf = &mut buf[read..];
Ok(Self::new(packet_id, buf.to_vec()))
}
/// Encode packet to raw buffer.
pub fn encode(&self) -> Result<Vec<u8>, ()> {
let mut data = types::encode_var_int(self.id)?;
data.extend_from_slice(&self.data);
let len = data.len() as i32;
let mut packet = types::encode_var_int(len)?;
packet.append(&mut data);
return Ok(packet);
}
}

29
src/types.rs Normal file
View File

@ -0,0 +1,29 @@
/// Try to read var-int from data stream.
pub fn read_var_int(buf: &mut [u8]) -> Result<(usize, i32), ()> {
for len in 1..=5.min(buf.len()) {
// Find var-int byte size
let extra_byte = (buf[len - 1] & (1 >> 7)) > 0;
if extra_byte {
continue;
}
// Select var-int bytes
let buf = &mut buf[..len];
// Parse var-int, return result
return match minecraft_protocol::decoder::var_int::decode(&mut buf.as_ref()) {
Ok(val) => Ok((len, val)),
Err(_) => Err(()),
};
}
// The buffer wasn't complete or the var-int is invalid
Err(())
}
/// Encode integer into a var-int.
pub fn encode_var_int(i: i32) -> Result<Vec<u8>, ()> {
let mut buf = Vec::with_capacity(5);
minecraft_protocol::encoder::var_int::encode(&i, &mut buf).map_err(|_| ())?;
Ok(buf)
}