1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![allow(clippy::needless_doctest_main)]
3use std::{
27 fmt::Display,
28 io::{Read, Write},
29};
30
31mod entity;
32#[cfg(feature = "async-futures")]
33#[cfg_attr(docsrs, doc(cfg(feature = "async-futures")))]
34pub mod futures;
35#[cfg(feature = "sync")]
36#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
37pub mod sync;
38#[cfg(feature = "async-tokio")]
39#[cfg_attr(docsrs, doc(cfg(feature = "async-tokio")))]
40pub mod tokio;
41
42pub use entity::*;
43
44#[derive(Debug)]
45pub enum Error {
47 Io(std::io::Error),
49 UnsupportedProtocol,
51}
52
53impl Display for Error {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 Self::Io(io) => io.fmt(f),
57 Self::UnsupportedProtocol => write!(f, "unsupported protocol"),
58 }
59 }
60}
61
62impl std::error::Error for Error {}
63
64impl From<std::io::Error> for Error {
65 fn from(error: std::io::Error) -> Self {
66 Self::Io(error)
67 }
68}
69
70pub type Result<T> = std::result::Result<T, Error>;
72
73fn build_latest_request(hostname: &str, port: u16) -> Result<Vec<u8>> {
74 let mut buffer = vec![
76 0x00, 0xff, 0xff, 0xff, 0xff,
78 0x0f, ];
80 write_varint(&mut buffer, hostname.len() as i32); buffer.extend_from_slice(hostname.as_bytes());
83 buffer.extend_from_slice(&[
84 (port >> 8) as u8,
85 (port & 0b1111_1111) as u8, 0x01, ]);
88 let mut full_buffer = vec![];
90 write_varint(&mut full_buffer, buffer.len() as i32); full_buffer.append(&mut buffer);
92 full_buffer.extend_from_slice(&[
93 1, 0x00, ]);
96 Ok(full_buffer)
97}
98
99fn decode_latest_response(buffer: &[u8]) -> Result<RawLatest> {
100 serde_json::from_slice(buffer).map_err(|_| Error::UnsupportedProtocol)
101}
102
103const LEGACY_REQUEST: [u8; 35] = [
104 0xfe, 0x01, 0xfa, 0x00, 0x0b, 0x00, 0x4d, 0x00, 0x43, 0x00, 0x7c, 0x00, 0x50, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x48,
109 0x00, 0x6f, 0x00, 0x73, 0x00, 0x74,
110 7, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
116
117fn decode_legacy(buffer: &[u8]) -> Result<String> {
118 if buffer.len() <= 3 || buffer[0] != 0xff {
119 return Err(Error::UnsupportedProtocol);
120 }
121 let utf16be: Vec<u16> = buffer[3..]
122 .chunks_exact(2)
123 .map(|chunk| ((chunk[0] as u16) << 8) | chunk[1] as u16)
124 .collect();
125 String::from_utf16(&utf16be).map_err(|_| Error::UnsupportedProtocol)
126}
127
128fn parse_legacy(s: &str, raw: Vec<u8>) -> Result<Response> {
129 let mut fields = s.split('\0');
130 let magic = fields.next().map(|s| s == "\u{00a7}\u{0031}");
131 let protocol = fields.next().and_then(|s| s.parse().ok());
132 let version = fields.next();
133 let motd = fields.next();
134 let players = fields.next().and_then(|s| s.parse().ok());
135 let max_players = fields.next().and_then(|s| s.parse().ok());
136 match (magic, protocol, version, motd, players, max_players) {
137 (
138 Some(true),
139 Some(protocol),
140 Some(version),
141 Some(motd),
142 Some(players),
143 Some(max_players),
144 ) => Ok(Response {
145 protocol,
146 enforces_secure_chat: None,
147 previews_chat: None,
148 version: version.to_string(),
149 description: Some(serde_json::Value::String(motd.to_string())),
150 online_players: players,
151 max_players,
152 favicon: None,
153 forge_data: None,
154 mod_info: None,
155 sample: None,
156 raw,
157 }),
158 _ => Err(Error::UnsupportedProtocol),
159 }
160}
161
162const LAST_SEVEN_BITS: i32 = 0b0111_1111;
164const NEXT_BYTE_EXISTS: u8 = 0b1000_0000;
165
166const SEVEN_BITS_SHIFT_MASK: i32 = 0x01_ff_ff_ff;
168
169fn write_varint(sink: &mut Vec<u8>, mut value: i32) {
170 loop {
171 let mut temp = (value & LAST_SEVEN_BITS) as u8;
172 value >>= 7;
174 value &= SEVEN_BITS_SHIFT_MASK;
175 if value != 0 {
176 temp |= NEXT_BYTE_EXISTS;
177 }
178 sink.push(temp);
179 if value == 0 {
180 break;
181 }
182 }
183}