mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
fix(bedrock): unsupported version disconnect (#3032)
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user