mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-06-01 09:42:41 +00:00
[Protocol] Fix Minecraft Java query not being able to specify a hostname (#91)
* Make initial fix * Fix imports * Rename query_address to hostname and add this to the mc example * Fix master_querant example not compiling * Add extra safety on converting strings to Minecraft Varint strings * Add docs to RequestSettings * Fix formatting
This commit is contained in:
parent
211cd5fd5f
commit
a56ca45de6
8 changed files with 118 additions and 28 deletions
|
|
@ -126,7 +126,7 @@ fn main() -> GDResult<()> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
"mc" => println!("{:#?}", mc::query(ip, port)?),
|
"mc" => println!("{:#?}", mc::query(ip, port)?),
|
||||||
"mc_java" => println!("{:#?}", mc::query_java(ip, port)?),
|
"mc_java" => println!("{:#?}", mc::query_java(ip, port, None)?),
|
||||||
"mc_bedrock" => println!("{:#?}", mc::query_bedrock(ip, port)?),
|
"mc_bedrock" => println!("{:#?}", mc::query_bedrock(ip, port)?),
|
||||||
"mc_legacy" => println!("{:#?}", mc::query_legacy(ip, port)?),
|
"mc_legacy" => println!("{:#?}", mc::query_legacy(ip, port)?),
|
||||||
"mc_legacy_vb1_8" => {
|
"mc_legacy_vb1_8" => {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,28 @@
|
||||||
use gamedig::games::mc;
|
use gamedig::games::mc;
|
||||||
|
use gamedig::protocols::minecraft::RequestSettings;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// or Some(<port>), None is the default protocol port (which is 25565 for java
|
// or Some(<port>), None is the default protocol port (which is 25565 for java
|
||||||
// and 19132 for bedrock)
|
// and 19132 for bedrock)
|
||||||
let response = mc::query(&"127.0.0.1".parse().unwrap(), None);
|
let response = mc::query(&"127.0.0.1".parse().unwrap(), None);
|
||||||
|
// This will fail if no server is available locally!
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Err(error) => println!("Couldn't query, error: {}", error),
|
||||||
|
Ok(r) => println!("{:#?}", r),
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is an example to query a server with a hostname to be specified in the
|
||||||
|
// packet. Passing -1 on the protocol_version means anything, note that
|
||||||
|
// an invalid value here might result in server not responding.
|
||||||
|
let response = mc::query_java(
|
||||||
|
&"209.222.114.62".parse().unwrap(),
|
||||||
|
Some(25565),
|
||||||
|
Some(RequestSettings {
|
||||||
|
hostname: "mc.hypixel.net".to_string(),
|
||||||
|
protocol_version: -1,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
match response {
|
match response {
|
||||||
Err(error) => println!("Couldn't query, error: {}", error),
|
Err(error) => println!("Couldn't query, error: {}", error),
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::protocols::minecraft::RequestSettings;
|
||||||
use crate::{
|
use crate::{
|
||||||
protocols::minecraft::{self, BedrockResponse, JavaResponse, LegacyGroup},
|
protocols::minecraft::{self, BedrockResponse, JavaResponse, LegacyGroup},
|
||||||
GDErrorKind,
|
GDErrorKind,
|
||||||
|
|
@ -8,7 +9,7 @@ use std::net::{IpAddr, SocketAddr};
|
||||||
/// Query with all the protocol variants one by one (Java -> Bedrock -> Legacy
|
/// Query with all the protocol variants one by one (Java -> Bedrock -> Legacy
|
||||||
/// (1.6 -> 1.4 -> Beta 1.8)).
|
/// (1.6 -> 1.4 -> Beta 1.8)).
|
||||||
pub fn query(address: &IpAddr, port: Option<u16>) -> GDResult<JavaResponse> {
|
pub fn query(address: &IpAddr, port: Option<u16>) -> GDResult<JavaResponse> {
|
||||||
if let Ok(response) = query_java(address, port) {
|
if let Ok(response) = query_java(address, port, None) {
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,8 +25,16 @@ pub fn query(address: &IpAddr, port: Option<u16>) -> GDResult<JavaResponse> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query a Java Server.
|
/// Query a Java Server.
|
||||||
pub fn query_java(address: &IpAddr, port: Option<u16>) -> GDResult<JavaResponse> {
|
pub fn query_java(
|
||||||
minecraft::query_java(&SocketAddr::new(*address, port_or_java_default(port)), None)
|
address: &IpAddr,
|
||||||
|
port: Option<u16>,
|
||||||
|
request_settings: Option<RequestSettings>,
|
||||||
|
) -> GDResult<JavaResponse> {
|
||||||
|
minecraft::query_java(
|
||||||
|
&SocketAddr::new(*address, port_or_java_default(port)),
|
||||||
|
None,
|
||||||
|
request_settings,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query a (Java) Legacy Server (1.6 -> 1.4 -> Beta 1.8).
|
/// Query a (Java) Legacy Server (1.6 -> 1.4 -> Beta 1.8).
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ pub fn query_with_timeout(
|
||||||
Protocol::Minecraft(version) => {
|
Protocol::Minecraft(version) => {
|
||||||
match version {
|
match version {
|
||||||
Some(protocols::minecraft::Server::Java) => {
|
Some(protocols::minecraft::Server::Java) => {
|
||||||
protocols::minecraft::query_java(&socket_addr, timeout_settings).map(Box::new)?
|
protocols::minecraft::query_java(&socket_addr, timeout_settings, None).map(Box::new)?
|
||||||
}
|
}
|
||||||
Some(protocols::minecraft::Server::Bedrock) => {
|
Some(protocols::minecraft::Server::Bedrock) => {
|
||||||
protocols::minecraft::query_bedrock(&socket_addr, timeout_settings).map(Box::new)?
|
protocols::minecraft::query_bedrock(&socket_addr, timeout_settings).map(Box::new)?
|
||||||
|
|
|
||||||
|
|
@ -11,33 +11,28 @@ use crate::{
|
||||||
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
use crate::protocols::minecraft::{as_string, RequestSettings};
|
||||||
use byteorder::LittleEndian;
|
use byteorder::LittleEndian;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
const PAYLOAD: [u8; 17] = [
|
|
||||||
//Packet ID (0)
|
|
||||||
0x00,
|
|
||||||
//Protocol Version (-1 to determine version)
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0x0F,
|
|
||||||
//Server address (can be anything)
|
|
||||||
0x07, 0x47, 0x61, 0x6D, 0x65, 0x44, 0x69, 0x67,
|
|
||||||
//Server port (can be anything)
|
|
||||||
0x00, 0x00,
|
|
||||||
//Next state (1 for status)
|
|
||||||
0x01
|
|
||||||
];
|
|
||||||
|
|
||||||
pub struct Java {
|
pub struct Java {
|
||||||
socket: TcpSocket,
|
socket: TcpSocket,
|
||||||
|
request_settings: RequestSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Java {
|
impl Java {
|
||||||
fn new(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<Self> {
|
fn new(
|
||||||
|
address: &SocketAddr,
|
||||||
|
timeout_settings: Option<TimeoutSettings>,
|
||||||
|
request_settings: Option<RequestSettings>,
|
||||||
|
) -> GDResult<Self> {
|
||||||
let socket = TcpSocket::new(address)?;
|
let socket = TcpSocket::new(address)?;
|
||||||
socket.apply_timeout(timeout_settings)?;
|
socket.apply_timeout(timeout_settings)?;
|
||||||
|
|
||||||
Ok(Self { socket })
|
Ok(Self {
|
||||||
|
socket,
|
||||||
|
request_settings: request_settings.unwrap_or_default(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send(&mut self, data: Vec<u8>) -> GDResult<()> {
|
fn send(&mut self, data: Vec<u8>) -> GDResult<()> {
|
||||||
|
|
@ -57,7 +52,24 @@ impl Java {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_handshake(&mut self) -> GDResult<()> {
|
fn send_handshake(&mut self) -> GDResult<()> {
|
||||||
self.send(PAYLOAD.to_vec())?;
|
let handshake_payload = [
|
||||||
|
&[
|
||||||
|
// Packet ID (0)
|
||||||
|
0x00,
|
||||||
|
], // Protocol Version (-1 to determine version)
|
||||||
|
as_varint(self.request_settings.protocol_version).as_slice(),
|
||||||
|
// Server address (can be anything)
|
||||||
|
as_string(&self.request_settings.hostname)?.as_slice(),
|
||||||
|
// Server port (can be anything)
|
||||||
|
&self.socket.port().to_le_bytes(),
|
||||||
|
&[
|
||||||
|
// Next state (1 for status)
|
||||||
|
0x01,
|
||||||
|
],
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
|
self.send(handshake_payload)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -143,7 +155,11 @@ impl Java {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<JavaResponse> {
|
pub fn query(
|
||||||
Self::new(address, timeout_settings)?.get_info()
|
address: &SocketAddr,
|
||||||
|
timeout_settings: Option<TimeoutSettings>,
|
||||||
|
request_settings: Option<RequestSettings>,
|
||||||
|
) -> GDResult<JavaResponse> {
|
||||||
|
Self::new(address, timeout_settings, request_settings)?.get_info()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::protocols::minecraft::types::RequestSettings;
|
||||||
use crate::{
|
use crate::{
|
||||||
protocols::minecraft::{
|
protocols::minecraft::{
|
||||||
protocol::{
|
protocol::{
|
||||||
|
|
@ -26,7 +27,7 @@ mod legacy_v1_6;
|
||||||
/// Queries a Minecraft server with all the protocol variants one by one (Java
|
/// Queries a Minecraft server with all the protocol variants one by one (Java
|
||||||
/// -> Bedrock -> Legacy (1.6 -> 1.4 -> Beta 1.8)).
|
/// -> Bedrock -> Legacy (1.6 -> 1.4 -> Beta 1.8)).
|
||||||
pub fn query(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<JavaResponse> {
|
pub fn query(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<JavaResponse> {
|
||||||
if let Ok(response) = query_java(address, timeout_settings.clone()) {
|
if let Ok(response) = query_java(address, timeout_settings.clone(), None) {
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,8 +43,12 @@ pub fn query(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) ->
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query a Java Server.
|
/// Query a Java Server.
|
||||||
pub fn query_java(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<JavaResponse> {
|
pub fn query_java(
|
||||||
Java::query(address, timeout_settings)
|
address: &SocketAddr,
|
||||||
|
timeout_settings: Option<TimeoutSettings>,
|
||||||
|
request_settings: Option<RequestSettings>,
|
||||||
|
) -> GDResult<JavaResponse> {
|
||||||
|
Java::query(address, timeout_settings, request_settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query a (Java) Legacy Server (1.6 -> 1.4 -> Beta 1.8).
|
/// Query a (Java) Legacy Server (1.6 -> 1.4 -> Beta 1.8).
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
types::{CommonPlayer, CommonResponse, GenericPlayer},
|
types::{CommonPlayer, CommonResponse, GenericPlayer},
|
||||||
GenericResponse,
|
GenericResponse,
|
||||||
},
|
},
|
||||||
GDErrorKind::{PacketBad, UnknownEnumCast},
|
GDErrorKind::{InvalidInput, PacketBad, UnknownEnumCast},
|
||||||
GDResult,
|
GDResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -88,6 +88,28 @@ pub struct JavaResponse {
|
||||||
pub server_type: Server,
|
pub server_type: Server,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Java-only additional request settings.
|
||||||
|
pub struct RequestSettings {
|
||||||
|
/// Some Minecraft servers do not respond as expected if this
|
||||||
|
/// isn't a specific value, `mc.hypixel.net` is an example.
|
||||||
|
pub hostname: String,
|
||||||
|
/// Specifies the client [protocol version number](https://wiki.vg/Protocol_version_numbers),
|
||||||
|
/// `-1` means anything.
|
||||||
|
pub protocol_version: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RequestSettings {
|
||||||
|
/// `hostname`: "gamedig"
|
||||||
|
///
|
||||||
|
/// `protocol_version`: -1
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
hostname: "gamedig".to_string(),
|
||||||
|
protocol_version: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CommonResponse for JavaResponse {
|
impl CommonResponse for JavaResponse {
|
||||||
fn as_original(&self) -> GenericResponse { GenericResponse::Minecraft(VersionedResponse::Java(self)) }
|
fn as_original(&self) -> GenericResponse { GenericResponse::Minecraft(VersionedResponse::Java(self)) }
|
||||||
|
|
||||||
|
|
@ -238,3 +260,14 @@ pub(crate) fn get_string<B: ByteOrder>(buffer: &mut Buffer<B>) -> GDResult<Strin
|
||||||
|
|
||||||
String::from_utf8(text).map_err(|e| PacketBad.context(e))
|
String::from_utf8(text).map_err(|e| PacketBad.context(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_string(value: &str) -> GDResult<Vec<u8>> {
|
||||||
|
let length = value
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|e| InvalidInput.context(e))?;
|
||||||
|
let mut buf = as_varint(length);
|
||||||
|
buf.extend(value.as_bytes());
|
||||||
|
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,20 @@ pub trait Socket {
|
||||||
|
|
||||||
fn send(&mut self, data: &[u8]) -> GDResult<()>;
|
fn send(&mut self, data: &[u8]) -> GDResult<()>;
|
||||||
fn receive(&mut self, size: Option<usize>) -> GDResult<Vec<u8>>;
|
fn receive(&mut self, size: Option<usize>) -> GDResult<Vec<u8>>;
|
||||||
|
|
||||||
|
fn port(&self) -> u16;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TcpSocket {
|
pub struct TcpSocket {
|
||||||
socket: net::TcpStream,
|
socket: net::TcpStream,
|
||||||
|
address: SocketAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Socket for TcpSocket {
|
impl Socket for TcpSocket {
|
||||||
fn new(address: &SocketAddr) -> GDResult<Self> {
|
fn new(address: &SocketAddr) -> GDResult<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
socket: net::TcpStream::connect(address).map_err(|e| SocketConnect.context(e))?,
|
socket: net::TcpStream::connect(address).map_err(|e| SocketConnect.context(e))?,
|
||||||
|
address: *address,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,6 +58,8 @@ impl Socket for TcpSocket {
|
||||||
|
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn port(&self) -> u16 { self.address.port() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UdpSocket {
|
pub struct UdpSocket {
|
||||||
|
|
@ -96,6 +102,8 @@ impl Socket for UdpSocket {
|
||||||
|
|
||||||
Ok(buf[.. number_of_bytes_received].to_vec())
|
Ok(buf[.. number_of_bytes_received].to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn port(&self) -> u16 { self.address.port() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue