Skip to content

Commit fd3afb6

Browse files
committed
Improve querying
1 parent 5eb7e04 commit fd3afb6

6 files changed

Lines changed: 146 additions & 88 deletions

File tree

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ opt-level = "z"
2424
strip = true
2525
lto = true
2626
codegen-units = 1
27-
panic = "abort"
27+
panic = "abort"

‎src/mode/bedrock.rs‎

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::analyze::{MotdInfo, StatusPayload};
2+
use crate::mode::QueryMode::BEDROCK;
23
use crate::mode::QueryModeHandler;
34
use crate::network::resolve::resolve_addr;
45
use crate::network::util;
@@ -9,13 +10,13 @@ use std::io::ErrorKind;
910
use std::net::SocketAddr;
1011
use std::time::Duration;
1112
use tokio::net::UdpSocket;
13+
use tokio::task::JoinSet;
1214
use tokio::time::timeout;
13-
use crate::mode::QueryMode::BEDROCK;
1415

1516
const MAGIC_HIGH: u64 = 0x00ffff00fefefefeu64;
1617
const MAGIC_LOW: u64 = 0xfdfdfdfd12345678u64;
1718

18-
async fn single_ip_check(addr: &SocketAddr) -> std::io::Result<StatusPayload> {
19+
async fn single_ip_check(addr: SocketAddr) -> std::io::Result<StatusPayload> {
1920
let timeout_time = Duration::from_secs(5);
2021
let socket = UdpSocket::bind("0.0.0.0:0").await?;
2122
socket.connect(addr).await?;
@@ -32,7 +33,7 @@ async fn single_ip_check(addr: &SocketAddr) -> std::io::Result<StatusPayload> {
3233

3334
let mut recv_buf = [0u8; 1024];
3435
let recv = timeout(timeout_time, socket.recv_from(&mut recv_buf)).await??;
35-
log::trace!("Received response");
36+
log::trace!("Received response from {}", addr);
3637

3738
let mut bytes = BytesMut::from(&recv_buf[..recv.0]);
3839
if bytes.get_u8() != 0x1C {
@@ -87,18 +88,31 @@ async fn single_ip_check(addr: &SocketAddr) -> std::io::Result<StatusPayload> {
8788
})
8889
}
8990

90-
async fn check_bedrock_server(addr_vec: &Vec<SocketAddr>) -> std::io::Result<StatusPayload> {
91+
async fn safe_ip_check(addr: SocketAddr) -> std::io::Result<StatusPayload> {
92+
match single_ip_check(addr).await {
93+
Ok(status) => Ok(status),
94+
Err(e) => {
95+
log::warn!("Failed to check available server ip {}: {}", addr, e);
96+
Err(e)
97+
}
98+
}
99+
}
100+
101+
async fn check_bedrock_server(addr_vec: Vec<SocketAddr>) -> std::io::Result<StatusPayload> {
102+
let mut set = JoinSet::new();
103+
91104
for addr in addr_vec {
92-
match single_ip_check(&addr).await {
93-
Ok(r) => {
94-
return Ok(r);
95-
}
96-
Err(e) => {
97-
log::warn!("Failed to check available server ip {}: {}", addr, e);
98-
continue;
99-
}
105+
set.spawn(safe_ip_check(addr));
106+
}
107+
108+
while let Some(join_res) = set.join_next().await {
109+
if let Ok(res) = join_res
110+
&& res.is_ok()
111+
{
112+
return res;
100113
}
101114
}
115+
102116
Err(std::io::Error::new(ErrorKind::NotFound, "No server found"))
103117
}
104118

@@ -107,8 +121,8 @@ pub struct BedrockQuery;
107121
#[async_trait]
108122
impl QueryModeHandler for BedrockQuery {
109123
async fn do_query(&self, addr: &str) -> std::io::Result<StatusPayload> {
110-
let mut res = resolve_addr(addr, 19132);
111-
let addrs = res.get_or_insert_default();
124+
let res = resolve_addr(addr, 19132);
125+
let addrs = res.unwrap_or(vec![]);
112126
check_bedrock_server(addrs).await
113127
}
114128
}

‎src/mode/java.rs‎

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use crate::analyze::{MotdInfo, PlayerInfo, StatusPayload};
2+
use crate::mode::QueryMode::JAVA;
23
use crate::mode::QueryModeHandler;
34
use crate::network::resolve::{resolve_addr, resolve_server_srv};
45
use crate::network::schema::{read_string, read_var_int_stream, write_var_int};
5-
use crate::network::util;
6+
use crate::network::util::{io_timeout, now_timestamp};
7+
use crate::util::make_tcp_socket;
68
use async_trait::async_trait;
79
use bytes::{Buf, BufMut, BytesMut};
810
use clap::Args;
@@ -11,22 +13,14 @@ use std::io::ErrorKind;
1113
use std::net::SocketAddr;
1214
use std::time::Duration;
1315
use tokio::io::{AsyncReadExt, AsyncWriteExt};
14-
use tokio::net::TcpSocket;
15-
use tokio::time::timeout;
16-
use crate::mode::QueryMode::JAVA;
16+
use tokio::task::JoinSet;
1717

1818
async fn single_ip_check(addr: &SocketAddr, protocol: i32) -> std::io::Result<StatusPayload> {
19-
let timeout_time = Duration::from_secs(5);
19+
let time = Duration::from_secs(5);
2020
let ip_str = addr.ip().to_string();
2121

22-
let socket = if addr.is_ipv4() {
23-
log::trace!("Using IPv4 socket to {}", addr);
24-
TcpSocket::new_v4()?
25-
} else {
26-
log::trace!("Using IPv6 socket to {}", addr);
27-
TcpSocket::new_v6()?
28-
};
29-
let mut stream = timeout(timeout_time, socket.connect(*addr)).await??;
22+
let socket = make_tcp_socket(addr)?;
23+
let mut stream = io_timeout(time, socket.connect(*addr), "Connection").await??;
3024

3125
let mut handshake = vec![0];
3226
write_var_int(&mut handshake, protocol); // protocol_version
@@ -44,9 +38,9 @@ async fn single_ip_check(addr: &SocketAddr, protocol: i32) -> std::io::Result<St
4438

4539
let handshake_recv_len = read_var_int_stream(&mut stream).await?;
4640
let mut handshake_recv = vec![0; handshake_recv_len as usize];
47-
timeout(timeout_time, stream.read_exact(&mut handshake_recv)).await??;
41+
io_timeout(time, stream.read_exact(&mut handshake_recv), "Handshake").await??;
4842
let mut recv_buf = BytesMut::from(handshake_recv.as_slice());
49-
log::trace!("Handshake received, length: {}", handshake_recv.len());
43+
log::trace!("Handshake received from {}, length: {}", addr, handshake_recv.len());
5044

5145
if recv_buf.remaining() == 0 || recv_buf.get_u8() != 0 {
5246
return Err(std::io::Error::new(
@@ -68,20 +62,20 @@ async fn single_ip_check(addr: &SocketAddr, protocol: i32) -> std::io::Result<St
6862
let mut decoded: Value = from_str(&json_str).map_err(crate::util::wrap_invalid)?;
6963

7064
stream.write(&[9, 1]).await?; // ping_request
71-
stream.write_i64(util::now_timestamp()).await?;
65+
stream.write_i64(now_timestamp()).await?;
7266
stream.flush().await?;
7367
log::trace!("Ping request sent");
7468

7569
let recv_pong = &mut [0; 10];
76-
timeout(timeout_time, stream.read_exact(recv_pong)).await??;
70+
io_timeout(time, stream.read_exact(recv_pong), "Ping receiving").await??;
7771
if recv_pong[0] != 9 || recv_pong[1] != 1 {
7872
return Err(std::io::Error::new(
7973
ErrorKind::InvalidData,
8074
"Invalid pong packet",
8175
));
8276
}
8377
let server_clock = i64::from_be_bytes(recv_pong[2..10].try_into().expect("Recv failed"));
84-
let diff = util::now_timestamp() - server_clock;
78+
let diff = now_timestamp() - server_clock;
8579
log::trace!("Got ping time: {}", diff);
8680

8781
let players = decoded["players"].take();
@@ -123,21 +117,34 @@ async fn single_ip_check(addr: &SocketAddr, protocol: i32) -> std::io::Result<St
123117
})
124118
}
125119

120+
async fn safe_ip_check(addr: SocketAddr, protocol: i32) -> std::io::Result<StatusPayload> {
121+
match single_ip_check(&addr, protocol).await {
122+
Ok(r) => Ok(r),
123+
Err(e) => {
124+
log::warn!("Failed to check available server ip {}: {}", addr, e);
125+
Err(e)
126+
}
127+
}
128+
}
129+
126130
async fn check_java_server(
127-
addr_vec: &Vec<SocketAddr>,
131+
addr_vec: Vec<SocketAddr>,
128132
protocol: i32,
129133
) -> std::io::Result<StatusPayload> {
134+
let mut set = JoinSet::new();
135+
130136
for addr in addr_vec {
131-
match single_ip_check(&addr, protocol).await {
132-
Ok(r) => {
133-
return Ok(r);
134-
}
135-
Err(e) => {
136-
log::warn!("Failed to check available server ip {}: {}", addr, e);
137-
continue;
138-
}
137+
set.spawn(safe_ip_check(addr, protocol));
138+
}
139+
140+
while let Some(join_res) = set.join_next().await {
141+
if let Ok(res) = join_res
142+
&& res.is_ok()
143+
{
144+
return res;
139145
}
140146
}
147+
141148
Err(std::io::Error::new(ErrorKind::NotFound, "No server found"))
142149
}
143150

@@ -155,19 +162,23 @@ pub struct JavaQuery<'a> {
155162
args: &'a JavaModeArgs,
156163
}
157164

165+
pub async fn add_srv(addr: &str, addresses: &mut Vec<SocketAddr>) {
166+
let srv_res = resolve_server_srv(addr).await;
167+
let srv = srv_res
168+
.iter()
169+
.filter_map(|addr| resolve_addr(addr, 25565))
170+
.flatten();
171+
addresses.splice(0..0, srv);
172+
}
173+
158174
#[async_trait]
159175
impl QueryModeHandler for JavaQuery<'_> {
160176
async fn do_query(&self, addr: &str) -> std::io::Result<StatusPayload> {
161-
let mut je_res = resolve_addr(addr, 25565);
162-
let je_address = je_res.get_or_insert_default();
177+
let je_res = resolve_addr(addr, 25565);
178+
let mut je_address = je_res.unwrap_or(vec![]);
163179

164180
if !self.args.no_srv {
165-
let srv_res = resolve_server_srv(addr).await;
166-
let srv = srv_res
167-
.iter()
168-
.filter_map(|addr| resolve_addr(addr, 25565))
169-
.flatten();
170-
je_address.splice(0..0, srv);
181+
add_srv(addr, &mut je_address).await;
171182
}
172183

173184
check_java_server(je_address, self.args.protocol).await

‎src/mode/legacy.rs‎

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,29 @@
11
use crate::analyze::*;
2+
use crate::mode::QueryMode::LEGACY;
23
use crate::mode::QueryModeHandler;
3-
use crate::mode::java::JavaModeArgs;
4-
use crate::network::resolve::{resolve_addr, resolve_server_srv};
4+
use crate::mode::java::{JavaModeArgs, add_srv};
5+
use crate::network::resolve::resolve_addr;
56
use crate::network::util;
7+
use crate::network::util::{io_timeout, now_timestamp};
8+
use crate::util::make_tcp_socket;
69
use async_trait::async_trait;
710
use bytes::BufMut;
811
use serde_json::json;
912
use std::io::ErrorKind;
1013
use std::net::SocketAddr;
1114
use std::time::Duration;
1215
use tokio::io::{AsyncReadExt, AsyncWriteExt};
13-
use tokio::net::TcpSocket;
14-
use tokio::time::timeout;
15-
use crate::mode::QueryMode::LEGACY;
16+
use tokio::task::JoinSet;
1617

1718
const LEGACY_HEADER: [u8; 27] = [
1819
0xFE, 0x01, 0xFA, 0x00, 0x0B, 0x00, 0x4D, 0x00, 0x43, 0x00, 0x7C, 0x00, 0x50, 0x00, 0x69, 0x00,
1920
0x6E, 0x00, 0x67, 0x00, 0x48, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x74,
2021
];
2122

2223
async fn single_ip_check(addr: &SocketAddr) -> std::io::Result<StatusPayload> {
23-
let timeout_time = Duration::from_secs(5);
24-
let socket = if addr.is_ipv4() {
25-
log::trace!("Using IPv4 socket to {}", addr);
26-
TcpSocket::new_v4()?
27-
} else {
28-
log::trace!("Using IPv6 socket to {}", addr);
29-
TcpSocket::new_v6()?
30-
};
31-
let mut stream = socket.connect(*addr).await?;
24+
let time = Duration::from_secs(5);
25+
let socket = make_tcp_socket(addr)?;
26+
let mut stream = io_timeout(time, socket.connect(*addr), "Connection").await??;
3227

3328
let ip_str = addr.ip().to_string();
3429
let utf16 = ip_str.encode_utf16().collect::<Vec<_>>();
@@ -43,13 +38,13 @@ async fn single_ip_check(addr: &SocketAddr) -> std::io::Result<StatusPayload> {
4338
buf.put_u16(0);
4439
buf.put_u16(addr.port());
4540

46-
let send_time = util::now_timestamp();
41+
let send_time = now_timestamp();
4742
stream.write_all(&buf).await?;
4843
log::trace!("Legacy query sent, packet length = {}", packet_len);
4944

5045
let mut recv_buffer = [0u8; 3];
51-
timeout(timeout_time, stream.read_exact(&mut recv_buffer)).await??;
52-
let ping = util::now_timestamp() - send_time;
46+
io_timeout(time, stream.read_exact(&mut recv_buffer), "Handshake").await??;
47+
let ping = now_timestamp() - send_time;
5348
log::trace!("Legacy query received, ping = {}", ping);
5449
if recv_buffer[0] != 0xFF {
5550
return Err(std::io::Error::new(
@@ -67,7 +62,7 @@ async fn single_ip_check(addr: &SocketAddr) -> std::io::Result<StatusPayload> {
6762
.map(|a| u16::from_be_bytes([a[0], a[1]]))
6863
.collect::<Vec<_>>();
6964
let str = String::from_utf16(&u16buf).map_err(crate::util::wrap_invalid)?;
70-
log::trace!("Legacy query received: {}", str);
65+
log::trace!("Legacy query received from {}: {}", addr, str);
7166

7267
if str.starts_with("\u{00A7}1\0") {
7368
log::debug!("Legacy query version 1");
@@ -122,18 +117,31 @@ async fn single_ip_check(addr: &SocketAddr) -> std::io::Result<StatusPayload> {
122117
}
123118
}
124119

125-
async fn check_legacy_server(addr_vec: &Vec<SocketAddr>) -> std::io::Result<StatusPayload> {
120+
async fn safe_ip_check(addr: SocketAddr) -> std::io::Result<StatusPayload> {
121+
match single_ip_check(&addr).await {
122+
Ok(r) => Ok(r),
123+
Err(e) => {
124+
log::warn!("Failed to check available server ip {}: {}", addr, e);
125+
Err(e)
126+
}
127+
}
128+
}
129+
130+
async fn check_legacy_server(addr_vec: Vec<SocketAddr>) -> std::io::Result<StatusPayload> {
131+
let mut set = JoinSet::new();
132+
126133
for addr in addr_vec {
127-
match single_ip_check(&addr).await {
128-
Ok(r) => {
129-
return Ok(r);
130-
}
131-
Err(e) => {
132-
log::warn!("Failed to check available server ip {}: {}", addr, e);
133-
continue;
134-
}
134+
set.spawn(safe_ip_check(addr));
135+
}
136+
137+
while let Some(join_res) = set.join_next().await {
138+
if let Ok(res) = join_res
139+
&& res.is_ok()
140+
{
141+
return res;
135142
}
136143
}
144+
137145
Err(std::io::Error::new(ErrorKind::NotFound, "No server found"))
138146
}
139147

@@ -144,16 +152,11 @@ pub struct LegacyQuery<'a> {
144152
#[async_trait]
145153
impl QueryModeHandler for LegacyQuery<'_> {
146154
async fn do_query(&self, addr: &str) -> std::io::Result<StatusPayload> {
147-
let mut res = resolve_addr(addr, 25565);
148-
let addrs = res.get_or_insert_default();
155+
let res = resolve_addr(addr, 25565);
156+
let mut addrs = res.unwrap_or(vec![]);
149157

150158
if !self.args.no_srv {
151-
let srv_res = resolve_server_srv(addr).await;
152-
let srv = srv_res
153-
.iter()
154-
.filter_map(|addr| resolve_addr(addr, 25565))
155-
.flatten();
156-
addrs.splice(0..0, srv);
159+
add_srv(addr, &mut addrs).await;
157160
}
158161

159162
check_legacy_server(addrs).await

0 commit comments

Comments
 (0)