mirror of
https://github.com/italicsjenga/valence.git
synced 2025-01-27 05:56:33 +11:00
Packet filtering (#67)
* Add `PacketName` * Fix inefficiency in codec.rs * Add regex argument to packet_inspector * Reorder imports * Make the regex arg optional * Update README
This commit is contained in:
parent
7c23d7a086
commit
0a75f103aa
5 changed files with 67 additions and 17 deletions
|
@ -10,3 +10,4 @@ clap = { version = "3.2.8", features = ["derive"] }
|
|||
tokio = { version = "1", features = ["full"] }
|
||||
anyhow = "1"
|
||||
chrono = "0.4.19"
|
||||
regex = "1.6.0"
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
The packet inspector is a very simple Minecraft proxy for viewing the contents of packets as they are sent/received.
|
||||
It uses Valence's protocol facilities to print packet contents.
|
||||
This was made for three purposes:
|
||||
|
||||
- Check that packets between Valence and client are matching your expectations.
|
||||
- Check that packets between vanilla server and client are parsed correctly by Valence.
|
||||
- Understand how the protocol works between the vanilla server and client.
|
||||
|
@ -14,6 +15,7 @@ First, start a server
|
|||
```sh
|
||||
cargo r -r --example conway
|
||||
```
|
||||
|
||||
In a separate terminal, start the packet inspector.
|
||||
|
||||
```sh
|
||||
|
@ -22,7 +24,19 @@ cargo r -r -p packet_inspector -- 127.0.0.1:25566 127.0.0.1:25565
|
|||
|
||||
The client must connect to `localhost:25566`. You should see the packets in `stdout`.
|
||||
|
||||
If you only want to see errors, direct `stdout` elsewhere.
|
||||
The third argument to the packet inspector is an optional regular expression compatible with
|
||||
the [regex](https://docs.rs/regex/latest/regex/) crate. Packets with names that match the regex are printed while those
|
||||
that don't are ignored. If the regex is not provided then the empty string is assumed and all packets are considered
|
||||
matching.
|
||||
|
||||
If you're only interested in packets `Foo`, `Bar`, and `Baz`, you can use a regex such as `^(Foo|Bar|Baz)$`.
|
||||
|
||||
```sh
|
||||
cargo r -r -p packet_inspector -- 127.0.0.1:25566 127.0.0.1:25565 '^(Foo|Bar|Baz)$'
|
||||
```
|
||||
|
||||
Packets are printed to `stdout` while errors are printed to `stderr`. If you only want to see errors in your terminal,
|
||||
direct `stdout` elsewhere.
|
||||
|
||||
```sh
|
||||
cargo r -r -p packet_inspector -- 127.0.0.1:25566 127.0.0.1:25565 > log.txt
|
||||
|
@ -31,17 +45,20 @@ cargo r -r -p packet_inspector -- 127.0.0.1:25566 127.0.0.1:25565 > log.txt
|
|||
## Quick start with Vanilla Server via Docker
|
||||
|
||||
Start the server
|
||||
```bash
|
||||
|
||||
```sh
|
||||
docker run -e EULA=TRUE -e ONLINE_MODE=false -d -p 25565:25565 --name mc itzg/minecraft-server
|
||||
```
|
||||
|
||||
View server logs
|
||||
```bash
|
||||
|
||||
```sh
|
||||
docker logs -f mc
|
||||
```
|
||||
|
||||
Server Rcon
|
||||
```bash
|
||||
|
||||
```sh
|
||||
docker exec -i mc rcon-cli
|
||||
```
|
||||
|
||||
|
@ -54,6 +71,7 @@ cargo r -r -p packet_inspector -- 127.0.0.1:25566 127.0.0.1:25565
|
|||
Open Minecraft and connect to `localhost:25566`.
|
||||
|
||||
Clean up
|
||||
|
||||
```
|
||||
docker stop mc
|
||||
docker rm mc
|
||||
|
|
|
@ -8,6 +8,7 @@ use std::{fmt, io};
|
|||
use anyhow::bail;
|
||||
use chrono::{DateTime, Utc};
|
||||
use clap::Parser;
|
||||
use regex::Regex;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
|
@ -20,7 +21,7 @@ use valence::protocol::packets::c2s::status::{QueryPing, QueryRequest};
|
|||
use valence::protocol::packets::s2c::login::{LoginSuccess, S2cLoginPacket};
|
||||
use valence::protocol::packets::s2c::play::S2cPlayPacket;
|
||||
use valence::protocol::packets::s2c::status::{QueryPong, QueryResponse};
|
||||
use valence::protocol::packets::{DecodePacket, EncodePacket};
|
||||
use valence::protocol::packets::{DecodePacket, EncodePacket, PacketName};
|
||||
use valence::protocol::{Encode, VarInt};
|
||||
|
||||
#[derive(Parser, Clone, Debug)]
|
||||
|
@ -29,26 +30,36 @@ struct Cli {
|
|||
/// The socket address to listen for connections on. This is the address
|
||||
/// clients should connect to.
|
||||
client: SocketAddr,
|
||||
/// The socket address the proxy will connect to.
|
||||
/// The socket address the proxy will connect to. This is the address of the
|
||||
/// server.
|
||||
server: SocketAddr,
|
||||
|
||||
/// The optional regular expression to use on packet names. Packet names
|
||||
/// matching the regex are printed while those that don't are ignored.
|
||||
///
|
||||
/// If no regex is provided, all packets are considered matching.
|
||||
regex: Option<Regex>,
|
||||
/// The maximum number of connections allowed to the proxy. By default,
|
||||
/// there is no limit.
|
||||
#[clap(short, long)]
|
||||
max_connections: Option<usize>,
|
||||
|
||||
/// Print a timestamp before each packet.
|
||||
#[clap(short, long)]
|
||||
timestamp: bool,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
fn print(&self, d: &impl fmt::Debug) {
|
||||
fn print(&self, p: &(impl fmt::Debug + PacketName)) {
|
||||
if let Some(r) = &self.regex {
|
||||
if !r.is_match(p.packet_name()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if self.timestamp {
|
||||
let now: DateTime<Utc> = Utc::now();
|
||||
println!("{now} {d:#?}");
|
||||
println!("{now} {p:#?}");
|
||||
} else {
|
||||
println!("{d:#?}");
|
||||
println!("{p:#?}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -200,10 +200,8 @@ impl<R: AsyncRead + Unpin> Decoder<R> {
|
|||
.context("decoding packet after decompressing")?;
|
||||
ensure!(
|
||||
decompressed.is_empty(),
|
||||
format!(
|
||||
"packet contents were not read completely, {} remaining bytes",
|
||||
decompressed.len()
|
||||
)
|
||||
"packet contents were not read completely ({} bytes remaining)",
|
||||
decompressed.len()
|
||||
);
|
||||
packet
|
||||
} else {
|
||||
|
|
|
@ -28,12 +28,18 @@ use crate::protocol::{
|
|||
};
|
||||
use crate::text::Text;
|
||||
|
||||
/// Provides the name of a packet for debugging purposes.
|
||||
pub trait PacketName {
|
||||
/// The name of this packet.
|
||||
fn packet_name(&self) -> &'static str;
|
||||
}
|
||||
|
||||
/// Trait for types that can be written to the Minecraft protocol as a complete
|
||||
/// packet.
|
||||
///
|
||||
/// A complete packet is one that starts with a `VarInt` packet ID, followed by
|
||||
/// the body of the packet.
|
||||
pub trait EncodePacket: fmt::Debug {
|
||||
pub trait EncodePacket: PacketName + fmt::Debug {
|
||||
/// Writes a packet to the Minecraft protocol, including its packet ID.
|
||||
fn encode_packet(&self, w: &mut impl Write) -> anyhow::Result<()>;
|
||||
}
|
||||
|
@ -43,7 +49,7 @@ pub trait EncodePacket: fmt::Debug {
|
|||
///
|
||||
/// A complete packet is one that starts with a `VarInt` packet ID, followed by
|
||||
/// the body of the packet.
|
||||
pub trait DecodePacket: Sized + fmt::Debug {
|
||||
pub trait DecodePacket: Sized + PacketName + fmt::Debug {
|
||||
/// Reads a packet from the Minecraft protocol, including its packet ID.
|
||||
fn decode_packet(r: &mut &[u8]) -> anyhow::Result<Self>;
|
||||
}
|
||||
|
@ -295,6 +301,12 @@ macro_rules! def_packet_group {
|
|||
}
|
||||
}
|
||||
|
||||
impl PacketName for $packet {
|
||||
fn packet_name(&self) -> &'static str {
|
||||
stringify!($packet)
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodePacket for $packet {
|
||||
fn encode_packet(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||
VarInt($id).encode(w).context("failed to write packet ID")?;
|
||||
|
@ -316,6 +328,16 @@ macro_rules! def_packet_group {
|
|||
}
|
||||
)*
|
||||
|
||||
impl PacketName for $group_name {
|
||||
fn packet_name(&self) -> &'static str {
|
||||
match self {
|
||||
$(
|
||||
Self::$packet(pkt) => pkt.packet_name(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DecodePacket for $group_name {
|
||||
fn decode_packet(r: &mut &[u8]) -> anyhow::Result<Self> {
|
||||
let packet_id = VarInt::decode(r)
|
||||
|
|
Loading…
Add table
Reference in a new issue