fix: bedrock protocol version check
This commit is contained in:
parent
5847c90a44
commit
3c6f15d5eb
@ -1,6 +1,6 @@
|
|||||||
use std::io::{Error, Read, Write};
|
use std::io::{Error, Read, Write};
|
||||||
|
|
||||||
use crate::bedrock::{RAKNET_SPLIT, RakReliability};
|
use crate::bedrock::{MTU, RAKNET_SPLIT, RakReliability};
|
||||||
use crate::codec::u24;
|
use crate::codec::u24;
|
||||||
use crate::serial::{PacketRead, PacketWrite};
|
use crate::serial::{PacketRead, PacketWrite};
|
||||||
|
|
||||||
@ -68,15 +68,30 @@ impl Frame {
|
|||||||
|
|
||||||
pub fn read<R: Read>(reader: &mut R) -> Result<Vec<Self>, Error> {
|
pub fn read<R: Read>(reader: &mut R) -> Result<Vec<Self>, Error> {
|
||||||
let mut frames = Vec::new();
|
let mut frames = Vec::new();
|
||||||
|
let mut header_buf = [0u8; 1];
|
||||||
|
|
||||||
while let Ok(header) = u8::read(reader) {
|
while match reader.read(&mut header_buf) {
|
||||||
|
Ok(0) => false,
|
||||||
|
Ok(1) => true,
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
_ => unreachable!(),
|
||||||
|
} {
|
||||||
|
let header = header_buf[0];
|
||||||
let mut frame = Self::default();
|
let mut frame = Self::default();
|
||||||
|
|
||||||
let reliability_id = (header & 0xE0) >> 5;
|
let reliability_id = (header & 0xE0) >> 5;
|
||||||
let Some(reliability) = RakReliability::from_id(reliability_id) else {
|
let Some(reliability) = RakReliability::from_id(reliability_id) else {
|
||||||
return Err(Error::other("Invalid reliability"));
|
return Err(Error::other("Invalid reliability"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let split = (header & RAKNET_SPLIT) != 0;
|
let split = (header & RAKNET_SPLIT) != 0;
|
||||||
let length = u16::read_be(reader)? >> 3;
|
|
||||||
|
let bit_length = u16::read_be(reader)?;
|
||||||
|
let byte_length = (bit_length + 7) >> 3;
|
||||||
|
|
||||||
|
if byte_length > MTU as u16 {
|
||||||
|
return Err(Error::other("Frame payload length exceeds RakNet MTU"));
|
||||||
|
}
|
||||||
|
|
||||||
if reliability.is_reliable() {
|
if reliability.is_reliable() {
|
||||||
frame.reliable_number = u24::read(reader)?.0;
|
frame.reliable_number = u24::read(reader)?.0;
|
||||||
@ -98,8 +113,9 @@ impl Frame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
frame.reliability = reliability;
|
frame.reliability = reliability;
|
||||||
frame.payload = vec![0; length as usize];
|
frame.payload = vec![0; byte_length as usize];
|
||||||
reader.read_exact(&mut frame.payload)?;
|
reader.read_exact(&mut frame.payload)?;
|
||||||
|
|
||||||
frames.push(frame);
|
frames.push(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ pub mod packet_decoder;
|
|||||||
pub mod packet_encoder;
|
pub mod packet_encoder;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
|
pub const RAKNET_PROTOCOL_VERSION: u8 = 11;
|
||||||
pub const UDP_HEADER_SIZE: u16 = 28;
|
pub const UDP_HEADER_SIZE: u16 = 28;
|
||||||
pub const MTU: usize = 1400;
|
pub const MTU: usize = 1400;
|
||||||
|
|
||||||
|
|||||||
@ -129,11 +129,16 @@ impl SStatusRequest {
|
|||||||
.await
|
.await
|
||||||
.map_err(|_| DecodeError::UnexpectedEof)?;
|
.map_err(|_| DecodeError::UnexpectedEof)?;
|
||||||
|
|
||||||
let mut buf = [0u8; 4];
|
let position = packet.reader.position();
|
||||||
let is_full_request = match packet.reader.read(&mut buf).await {
|
let total_len = packet.reader.get_ref().len() as u64;
|
||||||
Ok(0) => false,
|
let remaining = total_len.saturating_sub(position);
|
||||||
Ok(4) => true,
|
|
||||||
// Any other byte count is malformed.
|
let is_full_request = match remaining {
|
||||||
|
0 => false,
|
||||||
|
4 => {
|
||||||
|
packet.reader.set_position(position + 4);
|
||||||
|
true
|
||||||
|
}
|
||||||
_ => return Err(DecodeError::MalformedPayload),
|
_ => return Err(DecodeError::MalformedPayload),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ use pumpkin_protocol::{
|
|||||||
codec::var_uint::VarUInt,
|
codec::var_uint::VarUInt,
|
||||||
};
|
};
|
||||||
use pumpkin_util::jwt::{AuthError, verify_chain};
|
use pumpkin_util::jwt::{AuthError, verify_chain};
|
||||||
use pumpkin_world::CURRENT_BEDROCK_MC_VERSION;
|
use pumpkin_world::{CURRENT_BEDROCK_MC_PROTOCOL, CURRENT_BEDROCK_MC_VERSION};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@ -49,7 +49,14 @@ struct CertificateChainPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BedrockClient {
|
impl BedrockClient {
|
||||||
pub async fn handle_request_network_settings(&self, _packet: SRequestNetworkSettings) {
|
pub async fn handle_request_network_settings(&self, packet: SRequestNetworkSettings) {
|
||||||
|
if packet.protocol_version < CURRENT_BEDROCK_MC_PROTOCOL as i32 {
|
||||||
|
self.send_game_packet(&CPlayStatus::OutdatedClient).await;
|
||||||
|
return;
|
||||||
|
} else if packet.protocol_version > CURRENT_BEDROCK_MC_PROTOCOL as i32 {
|
||||||
|
self.send_game_packet(&CPlayStatus::OutdatedServer).await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.send_game_packet(&CNetworkSettings::new(0, 0, false, 0, 0.0))
|
self.send_game_packet(&CNetworkSettings::new(0, 0, false, 0, 0.0))
|
||||||
.await;
|
.await;
|
||||||
self.set_compression(CompressionInfo::default()).await;
|
self.set_compression(CompressionInfo::default()).await;
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use pumpkin_protocol::bedrock::{
|
use pumpkin_protocol::bedrock::{
|
||||||
|
RAKNET_PROTOCOL_VERSION,
|
||||||
client::raknet::{
|
client::raknet::{
|
||||||
incompatible_protocol::CIncompatibleProtocolVersion,
|
incompatible_protocol::CIncompatibleProtocolVersion,
|
||||||
open_connection::{COpenConnectionReply1, COpenConnectionReply2},
|
open_connection::{COpenConnectionReply1, COpenConnectionReply2},
|
||||||
},
|
},
|
||||||
server::raknet::open_connection::{SOpenConnectionRequest1, SOpenConnectionRequest2},
|
server::raknet::open_connection::{SOpenConnectionRequest1, SOpenConnectionRequest2},
|
||||||
};
|
};
|
||||||
use pumpkin_world::CURRENT_BEDROCK_MC_PROTOCOL;
|
|
||||||
use tokio::net::UdpSocket;
|
use tokio::net::UdpSocket;
|
||||||
|
|
||||||
use crate::{net::bedrock::BedrockClient, server::Server};
|
use crate::{net::bedrock::BedrockClient, server::Server};
|
||||||
@ -19,12 +19,9 @@ impl BedrockClient {
|
|||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
socket: &UdpSocket,
|
socket: &UdpSocket,
|
||||||
) {
|
) {
|
||||||
if packet.protocol_version != CURRENT_BEDROCK_MC_PROTOCOL as u8 {
|
if packet.protocol_version != RAKNET_PROTOCOL_VERSION {
|
||||||
Self::send_offline_packet(
|
Self::send_offline_packet(
|
||||||
&CIncompatibleProtocolVersion::new(
|
&CIncompatibleProtocolVersion::new(RAKNET_PROTOCOL_VERSION, server.server_guid),
|
||||||
CURRENT_BEDROCK_MC_PROTOCOL as u8,
|
|
||||||
server.server_guid,
|
|
||||||
),
|
|
||||||
addr,
|
addr,
|
||||||
socket,
|
socket,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -97,7 +97,7 @@ impl RCONClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn poll(&mut self, server: &Arc<Server>, password: &str) -> Result<(), PacketError> {
|
async fn poll(&mut self, server: &Arc<Server>, password: &str) -> Result<(), PacketError> {
|
||||||
let Some(packet) = self.receive_packet().await? else {
|
let Some(packet) = self.receive_packet()? else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let config = &server.advanced_config.networking.rcon;
|
let config = &server.advanced_config.networking.rcon;
|
||||||
@ -178,7 +178,7 @@ impl RCONClient {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn receive_packet(&mut self) -> Result<Option<Packet>, PacketError> {
|
fn receive_packet(&mut self) -> Result<Option<Packet>, PacketError> {
|
||||||
Packet::deserialize(&mut self.incoming).await
|
Packet::deserialize(&mut self.incoming)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
use std::{
|
use std::string::FromUtf8Error;
|
||||||
io::{BufRead, Cursor},
|
|
||||||
string::FromUtf8Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::io::AsyncReadExt;
|
|
||||||
|
|
||||||
/// Client -> Server
|
/// Client -> Server
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
@ -57,10 +53,10 @@ impl ClientboundPacket {
|
|||||||
pub enum PacketError {
|
pub enum PacketError {
|
||||||
#[error("Invalid length")]
|
#[error("Invalid length")]
|
||||||
InvalidLength,
|
InvalidLength,
|
||||||
#[error("Failed to read packet")]
|
|
||||||
FailedRead(std::io::Error),
|
|
||||||
#[error("Dailed to send packet")]
|
#[error("Dailed to send packet")]
|
||||||
FailedSend(std::io::Error),
|
FailedSend(std::io::Error),
|
||||||
|
#[error("Missing packet lull terminator")]
|
||||||
|
MissingNullTerminator,
|
||||||
#[error("Invalid packet string body")]
|
#[error("Invalid packet string body")]
|
||||||
InvalidBody(FromUtf8Error),
|
InvalidBody(FromUtf8Error),
|
||||||
}
|
}
|
||||||
@ -74,36 +70,55 @@ pub struct Packet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Packet {
|
impl Packet {
|
||||||
pub async fn deserialize(incoming: &mut Vec<u8>) -> Result<Option<Self>, PacketError> {
|
pub fn deserialize(incoming: &mut Vec<u8>) -> Result<Option<Self>, PacketError> {
|
||||||
|
// We need at least 4 bytes to read the packet length header
|
||||||
if incoming.len() < 4 {
|
if incoming.len() < 4 {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mut buf = Cursor::new(&incoming);
|
|
||||||
let len = buf.read_i32_le().await.map_err(PacketError::FailedRead)? + 4;
|
|
||||||
if !(0..=1460).contains(&len) {
|
|
||||||
return Err(PacketError::InvalidLength);
|
|
||||||
}
|
|
||||||
let id = buf.read_i32_le().await.map_err(PacketError::FailedRead)?;
|
|
||||||
let ty = buf.read_i32_le().await.map_err(PacketError::FailedRead)?;
|
|
||||||
let mut payload = vec![];
|
|
||||||
let _ = buf
|
|
||||||
.read_until(b'\0', &mut payload)
|
|
||||||
.map_err(PacketError::FailedRead)?;
|
|
||||||
payload.pop();
|
|
||||||
buf.read_u8().await.map_err(PacketError::FailedRead)?;
|
|
||||||
if buf.position() != len as u64 {
|
|
||||||
return Err(PacketError::InvalidLength);
|
|
||||||
}
|
|
||||||
incoming.drain(0..len as usize);
|
|
||||||
|
|
||||||
let packet = Self {
|
// Read the 4-byte length header (little-endian)
|
||||||
|
let len_bytes: [u8; 4] = incoming[0..4].try_into().unwrap();
|
||||||
|
let size = i32::from_le_bytes(len_bytes);
|
||||||
|
|
||||||
|
// An RCON packet size includes: ID(4) + Type(4) + Body(n) + Null(1) + EmptyStringNull(1).
|
||||||
|
// Minimum size for an empty body is 10. Maximum MTU size is typically 1460.
|
||||||
|
if !(10..=1460).contains(&size) {
|
||||||
|
return Err(PacketError::InvalidLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total bytes we need in the buffer (length header + the payload size)
|
||||||
|
let total_packet_len = (size as usize) + 4;
|
||||||
|
|
||||||
|
// If the network hasn't delivered the full packet yet, return Ok(None)
|
||||||
|
if incoming.len() < total_packet_len {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have the full packet. Parse it synchronously using fixed slices.
|
||||||
|
let id = i32::from_le_bytes(incoming[4..8].try_into().unwrap());
|
||||||
|
let ty = i32::from_le_bytes(incoming[8..12].try_into().unwrap());
|
||||||
|
|
||||||
|
// Calculate body boundaries (starts after len, id, and ty -> 4+4+4 = 12)
|
||||||
|
// Ends 2 bytes before the total length (excluding the two trailing null bytes)
|
||||||
|
let body_start = 12;
|
||||||
|
let body_end = total_packet_len - 2;
|
||||||
|
|
||||||
|
if incoming[body_end] != 0 || incoming[body_end + 1] != 0 {
|
||||||
|
return Err(PacketError::MissingNullTerminator);
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload = &incoming[body_start..body_end];
|
||||||
|
let body = String::from_utf8(payload.to_vec()).map_err(PacketError::InvalidBody)?;
|
||||||
|
|
||||||
|
incoming.drain(0..total_packet_len);
|
||||||
|
|
||||||
|
Ok(Some(Self {
|
||||||
id,
|
id,
|
||||||
ptype: ServerboundPacket::from_i32(ty),
|
ptype: ServerboundPacket::from_i32(ty),
|
||||||
body: String::from_utf8(payload).map_err(PacketError::InvalidBody)?,
|
body,
|
||||||
};
|
}))
|
||||||
|
|
||||||
Ok(Some(packet))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_body(&self) -> &str {
|
pub fn get_body(&self) -> &str {
|
||||||
&self.body
|
&self.body
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user