rust_mc_status/protocol/
java_modern.rs1use std::io::Cursor;
28use std::time::Duration;
29
30use serde_json::Value;
31use tokio::io::{AsyncReadExt, AsyncWriteExt};
32use tokio::net::TcpStream;
33use tokio::time::timeout;
34#[cfg(feature = "proxy")]
35use tokio_socks::tcp::Socks5Stream;
36
37use crate::core::time::{start_timer, elapsed_ms};
38use crate::error::McError;
39use crate::models::{JavaStatus, PingResult, ServerData};
40use crate::proxy::ProxyConfig;
41#[cfg(feature = "proxy")]
42use crate::proxy::ProxyKind;
43use super::{PingProtocol, ResolvedTarget};
44
45const PROTOCOL_VERSION: i32 = 47;
46const READ_BUFFER_SIZE: usize = 4096;
47
48static STATUS_REQUEST: &[u8] = &[0x00];
49
50#[derive(Debug, Clone, Copy, Default)]
56pub struct JavaModernProtocol;
57
58impl PingProtocol for JavaModernProtocol {
59 fn name(&self) -> &'static str { "java-modern" }
60
61 async fn ping(
62 &self,
63 target: &ResolvedTarget,
64 tout: Duration,
65 proxy: Option<&ProxyConfig>,
66 ) -> Result<PingResult, McError> {
67 let start = start_timer();
68 let mut stream = connect_tcp(target, proxy, tout).await?;
69 stream.set_nodelay(true).map_err(McError::Io)?;
70
71 send_packet(&mut stream, &build_handshake(&target.hostname, target.addr.port()), tout).await?;
72 send_packet(&mut stream, STATUS_REQUEST, tout).await?;
73
74 let (json, latency) = read_response(stream, start, tout).await?;
75 let data = ServerData::Java(JavaStatus::from_json(json)?);
76 Ok(PingResult::new(data, latency))
77 }
78}
79
80async fn connect_tcp(
83 target: &ResolvedTarget,
84 proxy: Option<&ProxyConfig>,
85 tout: Duration,
86) -> Result<TcpStream, McError> {
87 #[cfg(feature = "proxy")]
88 if let Some(cfg) = proxy {
89 let proxy_addr = format!("{}:{}", cfg.addr().host, cfg.addr().port);
90 return match cfg.kind() {
91 ProxyKind::Socks5 => {
93 let target_addr = (target.hostname.as_str(), target.addr.port());
94 let stream = match cfg.auth() {
95 None => {
96 timeout(tout, Socks5Stream::connect(proxy_addr.as_str(), target_addr))
97 .await
98 .map_err(|_| McError::timeout())?
99 .map_err(|e| McError::proxy_error(e.to_string()))?
100 }
101 Some(auth) => {
102 timeout(
103 tout,
104 Socks5Stream::connect_with_password(
105 proxy_addr.as_str(),
106 target_addr,
107 &auth.username,
108 &auth.password,
109 ),
110 )
111 .await
112 .map_err(|_| McError::timeout())?
113 .map_err(|e| McError::proxy_error(e.to_string()))?
114 }
115 };
116 Ok(stream.into_inner())
117 }
118
119 ProxyKind::Http => {
121 crate::proxy::http::connect(
122 &proxy_addr,
123 &target.hostname,
124 target.addr.port(),
125 cfg.auth(),
126 tout,
127 )
128 .await
129 }
130 };
131 }
132
133 let _ = proxy;
135 timeout(tout, TcpStream::connect(target.addr))
136 .await
137 .map_err(|_| McError::timeout())?
138 .map_err(|e| McError::connection(e.to_string()))
139}
140
141async fn send_packet(stream: &mut TcpStream, data: &[u8], tout: Duration) -> Result<(), McError> {
144 let mut packet = Vec::with_capacity(data.len() + 5);
145 write_varint(&mut packet, data.len() as i32);
146 packet.extend_from_slice(data);
147 timeout(tout, stream.write_all(&packet))
148 .await
149 .map_err(|_| McError::timeout())?
150 .map_err(McError::Io)
151}
152
153async fn read_response(
154 mut stream: TcpStream,
155 start: tokio::time::Instant,
156 tout: Duration,
157) -> Result<(Value, f64), McError> {
158 let mut resp = Vec::new();
159 let mut buf = [0u8; READ_BUFFER_SIZE];
160 let mut expected = None;
161
162 loop {
163 let n = timeout(tout, stream.read(&mut buf))
164 .await
165 .map_err(|_| McError::timeout())?
166 .map_err(McError::Io)?;
167 if n == 0 { break; }
168 resp.extend_from_slice(&buf[..n]);
169
170 if expected.is_none() && resp.len() >= 5 {
171 let mut cur = Cursor::new(&resp);
172 if let Ok(len) = read_varint(&mut cur) {
173 expected = Some(cur.position() as usize + len as usize);
174 }
175 }
176 if expected.is_some_and(|e| resp.len() >= e) { break; }
177 }
178
179 if resp.is_empty() {
180 return Err(McError::invalid_response("empty response"));
181 }
182
183 let mut cur = Cursor::new(&resp);
184 let _pkt_len = read_varint(&mut cur)?;
185 if read_varint(&mut cur)? != 0x00 {
186 return Err(McError::invalid_response("unexpected packet id"));
187 }
188 let json_len = read_varint(&mut cur)? as usize;
189 let pos = cur.position() as usize;
190 let end = pos
191 .checked_add(json_len)
192 .filter(|&e| e <= resp.len())
193 .ok_or_else(|| McError::invalid_response("json length exceeds packet"))?;
194
195 let json = serde_json::from_slice(&resp[pos..end]).map_err(|e| McError::from(crate::error::ProtocolError::Json(e)))?;
196 let latency = elapsed_ms(start);
197 Ok((json, latency))
198}
199
200fn build_handshake(host: &str, port: u16) -> Vec<u8> {
201 let mut buf = Vec::with_capacity(7 + host.len());
202 write_varint(&mut buf, 0x00);
203 write_varint(&mut buf, PROTOCOL_VERSION);
204 write_varint(&mut buf, host.len() as i32);
205 buf.extend_from_slice(host.as_bytes());
206 buf.extend_from_slice(&port.to_be_bytes());
207 write_varint(&mut buf, 1);
208 buf
209}
210
211pub(crate) fn write_varint(buf: &mut Vec<u8>, v: i32) {
214 let mut v = v as u32;
215 while v >= 0x80 {
216 buf.push((v as u8 & 0x7F) | 0x80);
217 v >>= 7;
218 }
219 buf.push(v as u8);
220}
221
222pub(crate) fn read_varint<R: std::io::Read>(r: &mut R) -> Result<i32, McError> {
223 let (mut res, mut shift) = (0i32, 0u32);
224 loop {
225 let mut b = [0u8; 1];
226 r.read_exact(&mut b)
227 .map_err(|e| McError::invalid_response(e.to_string()))?;
228 let val = b[0] as i32;
229 res |= (val & 0x7F) << shift;
230 shift += 7;
231 if shift > 35 {
232 return Err(McError::invalid_response("varint too long"));
233 }
234 if val & 0x80 == 0 { break; }
235 }
236 Ok(res)
237}