fix(bedrock): unsupported version disconnect (#3032)

This commit is contained in:
ZlordHUN
2026-08-25 08:10:45 +02:00
committed by GitHub
parent 67eaa91400
commit 37b82e6f3a
4 changed files with 87 additions and 11 deletions

View File

@@ -61,6 +61,26 @@ impl PacketWrite for CUnconnectedPong {
}
}
#[derive(PacketWrite)]
#[packet(0x19)]
pub struct CIncompatibleProtocolVersion {
protocol_version: u8,
magic: [u8; 16],
#[serial(big_endian)]
server_guid: u64,
}
impl CIncompatibleProtocolVersion {
#[must_use]
pub const fn new(protocol_version: u8, server_guid: u64) -> Self {
Self {
protocol_version,
magic: OFFLINE_MESSAGE_MAGIC,
server_guid,
}
}
}
pub struct ServerInfo<'a> {
pub motd: &'a str,
pub protocol: u32,
@@ -120,4 +140,16 @@ mod tests {
"MCPE;Pumpkin;2168;1.26.40;2;20;42;world;Creative;1;19132;19133;0;"
);
}
#[test]
fn encodes_raknet_incompatible_protocol_response() {
let mut response = Vec::new();
CIncompatibleProtocolVersion::new(12, 42)
.write(&mut response)
.unwrap();
assert_eq!(response[0], 12);
assert_eq!(response[1..17], OFFLINE_MESSAGE_MAGIC);
assert_eq!(response[17..], 42u64.to_be_bytes());
}
}

View File

@@ -6,13 +6,19 @@ impl BedrockClient {
&self,
packet: SRequestNetworkSettings,
server: &Server,
) {
if packet.protocol_version < CURRENT_BEDROCK_MC_PROTOCOL as i32 {
self.send_packet(&CPlayStatus::OutdatedClient).await;
return;
} else if packet.protocol_version > CURRENT_BEDROCK_MC_PROTOCOL as i32 {
self.send_packet(&CPlayStatus::OutdatedServer).await;
return;
) -> bool {
let status = match packet
.protocol_version
.cmp(&(CURRENT_BEDROCK_MC_PROTOCOL as i32))
{
std::cmp::Ordering::Less => Some(CPlayStatus::OutdatedClient),
std::cmp::Ordering::Greater => Some(CPlayStatus::OutdatedServer),
std::cmp::Ordering::Equal => None,
};
if let Some(status) = status {
self.send_packet(&status).await;
self.close().await;
return false;
}
self.version.store(BedrockMinecraftVersion::from_protocol(
@@ -35,5 +41,6 @@ impl BedrockClient {
))
.await;
self.set_compression(compression).await;
true
}
}

View File

@@ -596,7 +596,9 @@ impl BedrockClient {
continue;
}
};
self.handle_request_network_settings(packet, server).await;
if !self.handle_request_network_settings(packet, server).await {
return PacketHandlerResult::Stop;
}
}
SLogin::PACKET_ID => {
let packet = match SLogin::read(payload) {

View File

@@ -9,8 +9,8 @@ use bytes::Bytes;
use pumpkin_protocol::{
BClientPacket,
bedrock::status::{
CUnconnectedPong, OFFLINE_MESSAGE_MAGIC, SUnconnectedPing, SUnconnectedPingOpenConnections,
ServerInfo,
CIncompatibleProtocolVersion, CUnconnectedPong, OFFLINE_MESSAGE_MAGIC, SUnconnectedPing,
SUnconnectedPingOpenConnections, ServerInfo,
},
packet::Packet,
serial::PacketRead,
@@ -75,6 +75,8 @@ impl StatusResponder {
if is_status_packet(packet) {
trace!(%client, length, "Received Bedrock server-list status ping");
self.respond(server, &self.ipv4, packet, client).await
} else if let Some(client_protocol) = raknet_protocol_version(packet) {
self.reject_legacy_raknet(server, &self.ipv4, client, client_protocol).await
} else {
trace!(
%client,
@@ -91,11 +93,32 @@ impl StatusResponder {
result = self.ipv6.recv_from(&mut ipv6_buffer) => {
let (length, client) = result?;
trace!(%client, length, "Received Bedrock IPv6 server-list status packet");
self.respond(server, &self.ipv6, &ipv6_buffer[..length], client).await
let packet = &ipv6_buffer[..length];
if let Some(client_protocol) = raknet_protocol_version(packet) {
self.reject_legacy_raknet(server, &self.ipv6, client, client_protocol).await
} else {
self.respond(server, &self.ipv6, packet, client).await
}
}
}
}
async fn reject_legacy_raknet(
&self,
server: &Server,
socket: &UdpSocket,
client: SocketAddr,
client_protocol: u8,
) -> Result<(), Error> {
let server_protocol = client_protocol.saturating_add(1);
let packet = CIncompatibleProtocolVersion::new(server_protocol, server.server_guid);
let mut response = vec![CIncompatibleProtocolVersion::PACKET_ID as u8];
packet.write_packet(&mut response)?;
socket.send_to(&response, client).await?;
trace!(%client, client_protocol, server_protocol, "Rejected unsupported Bedrock RakNet connection");
Ok(())
}
async fn respond(
&self,
server: &Server,
@@ -125,6 +148,12 @@ fn is_status_packet(packet: &[u8]) -> bool {
&& packet.get(9..25) == Some(OFFLINE_MESSAGE_MAGIC.as_slice())
}
fn raknet_protocol_version(packet: &[u8]) -> Option<u8> {
(packet.first() == Some(&0x05) && packet.get(1..17) == Some(OFFLINE_MESSAGE_MAGIC.as_slice()))
.then(|| packet.get(17).copied())
.flatten()
}
fn ice_packet_kind(packet: &[u8]) -> &'static str {
if packet.len() >= 20 && packet.get(4..8) == Some(&[0x21, 0x12, 0xa4, 0x42]) {
"STUN"
@@ -231,6 +260,12 @@ mod tests {
stun_success[4..8].copy_from_slice(&[0x21, 0x12, 0xa4, 0x42]);
assert!(!is_status_packet(&stun_success));
assert_eq!(ice_packet_kind(&stun_success), "STUN");
let mut open_connection = [0; 18];
open_connection[0] = 0x05;
open_connection[1..17].copy_from_slice(&OFFLINE_MESSAGE_MAGIC);
open_connection[17] = 11;
assert_eq!(raknet_protocol_version(&open_connection), Some(11));
}
#[tokio::test]