1+ use std:: io:: Read ;
2+ use std:: io:: Cursor ;
13use std:: net:: Ipv4Addr ;
24use std:: time:: Duration ;
5+ use flate2:: read:: ZlibDecoder ;
6+ use futures:: TryFutureExt ;
37use tokio:: net:: TcpStream ;
4- use tokio:: io:: AsyncWriteExt ;
8+ use tokio:: io:: { AsyncReadExt , AsyncWriteExt } ;
59use tokio:: time:: timeout;
610use crate :: minecraft:: Join ;
7- use crate :: minecraft:: utils:: { Handshake , write_varint, read_varint, prepend_length, MinecraftPacket , encode_string, encode_uuid} ;
11+ use crate :: minecraft:: utils:: { Handshake , write_varint, read_varint, prepend_length, MinecraftPacket , encode_string} ;
12+ use uuid:: Uuid ;
813
9- // TODO: Make this work
10- // It have every time the same error:
11- // [20:13:41 INFO]: /[0:0:0:0:0:0:0:1]:58067 lost connection: Internal Exception: io.netty.handler.codec.DecoderException: Failed to decode packet 'serverbound/minecraft:hello'
12- // I used this docs here https://minecraft.wiki/w/Java_Edition_protocol/FAQ#What's_the_normal_login_sequence_for_a_client?
1314pub async fn execute_join_check ( ip : Ipv4Addr , port : u16 , timeout_dur : Duration , username : & str , protocol : i32 ) -> Result < Join , String > {
1415 let mut stream = timeout ( timeout_dur, TcpStream :: connect ( ( ip, port) ) )
1516 . await
1617 . map_err ( |_| "Connect Timeout" ) ?
1718 . map_err ( |e| e. to_string ( ) ) ?;
1819
19- // Send handshake
2020 let handshake = Handshake {
2121 protocol,
2222 address : ip,
@@ -25,36 +25,140 @@ pub async fn execute_join_check(ip: Ipv4Addr, port: u16, timeout_dur: Duration,
2525 } . serialize ( ) ;
2626 stream. write_all ( & handshake) . await . map_err ( |e| e. to_string ( ) ) ?;
2727
28- // Login Start Packet
28+ // Login start packet
2929 let mut login_start = Vec :: new ( ) ;
3030 write_varint ( 0x00 , & mut login_start) ;
3131 login_start. extend ( encode_string ( username) ) ;
32- login_start. push ( 0x01 ) ;
33- // Used here 0x0U0
34- login_start. extend ( encode_uuid ( "00000000-0000-0000-0000-000000000000" ) ) ;
32+
33+ // The protocol needs an uuid after 1.17.3
34+ if protocol > 761 {
35+ let uuid = Uuid :: nil ( ) ;
36+ login_start. extend_from_slice ( uuid. as_bytes ( ) ) ;
37+ }
3538
3639 let final_login = prepend_length ( login_start) ;
3740 stream. write_all ( & final_login) . await . map_err ( |e| e. to_string ( ) ) ?;
3841 stream. flush ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
3942
40- let packet_length = read_varint ( & mut stream) . await ? as usize ;
41- let packet_id = read_varint ( & mut stream) . await ?;
43+ let result = timeout ( timeout_dur, async {
44+ let _packet_length = read_varint ( & mut stream) . await ?;
45+ let mut packet_id = read_varint ( & mut stream) . await ?;
46+
47+ if packet_id == 0x03 {
48+ let _threshold = read_varint ( & mut stream) . await ?;
49+
50+ let total_len = read_varint ( & mut stream) . await ? as usize ;
51+ let uncompressed_len = read_varint ( & mut stream) . await ? as usize ;
52+
53+ let mut packet_data = Vec :: new ( ) ;
54+ if uncompressed_len == 0 {
55+ let remaining = total_len - 1 ;
56+ let mut buf = vec ! [ 0u8 ; remaining] ;
57+ stream. read_exact ( & mut buf) . await . map_err ( |e| e. to_string ( ) ) ?;
58+ packet_data = buf;
59+ } else {
60+ let compressed_len = total_len - get_varint_size ( uncompressed_len as i32 ) ;
61+ let mut compressed_buf = vec ! [ 0u8 ; compressed_len] ;
62+ stream. read_exact ( & mut compressed_buf) . await . map_err ( |e| e. to_string ( ) ) ?;
63+
64+ let mut decoder = ZlibDecoder :: new ( & compressed_buf[ ..] ) ;
65+ decoder. read_to_end ( & mut packet_data) . map_err ( |e : std:: io:: Error | e. to_string ( ) ) ?;
66+ }
67+
68+ let mut cursor = Cursor :: new ( packet_data) ;
69+ packet_id = read_varint ( & mut cursor) . await ?;
70+
71+ return handle_packet ( packet_id, cursor) . await ;
72+ }
73+
74+ handle_packet_stream ( packet_id, & mut stream) . await
75+ } ) . await . map_err ( |_| "Timeout while reading answer" . to_string ( ) ) ??;
76+
77+ Ok ( result)
78+ }
79+
80+ async fn handle_packet_stream ( packet_id : i32 , stream : & mut TcpStream ) -> Result < Join , String > {
81+ match packet_id {
82+ 0x00 => {
83+ let reason_json = read_string_packet ( stream) . await ?;
84+ let plain_reason = crate :: minecraft:: utils:: parse_plain ( & reason_json) ;
85+ let lower_reason = plain_reason. to_lowercase ( ) ;
86+
87+ let online_mode_keywords = [
88+ "failed to verify" , "not authenticated" , "premium account" ,
89+ "authentication servers" , "encryption" , "online-mode" ,
90+ "requires mojang" , "requires microsoft"
91+ ] ;
4292
43- let packet_id = packet_id;
93+ let whitelist_keywords = [
94+ "whitelist" , "whitelisted" , "not allowed"
95+ ] ;
4496
45- Err ( format ! ( "Packet: 0x{:02X}" , packet_id) )
97+ let is_cracked = !online_mode_keywords. iter ( ) . any ( |& k| lower_reason. contains ( k) ) ;
98+ let is_whitelist = whitelist_keywords. iter ( ) . any ( |& k| lower_reason. contains ( k) ) ;
99+
100+ Ok ( Join {
101+ cracked : is_cracked,
102+ whitelist : is_whitelist,
103+ kick_message : Some ( plain_reason)
104+ } )
105+ }
106+ 0x01 => Ok ( Join { cracked : false , whitelist : false , kick_message : None } ) ,
107+ 0x02 => Ok ( Join { cracked : true , whitelist : false , kick_message : None } ) ,
108+ _ => Err ( format ! ( "Unknown packet: 0x{:02X}" , packet_id) ) ,
109+ }
46110}
47111
48- fn read_varint_from_slice ( slice : & mut & [ u8 ] ) -> Result < i32 , String > {
49- let mut res = 0 ;
50- let mut pos = 0 ;
51- while pos < 32 {
52- if slice. is_empty ( ) { return Err ( "Unexpected end of slice" . into ( ) ) ; }
53- let byte = slice[ 0 ] ;
54- * slice = & slice[ 1 ..] ;
55- res |= ( ( byte & 0x7F ) as i32 ) << pos;
56- if ( byte & 0x80 ) == 0 { return Ok ( res) ; }
57- pos += 7 ;
112+ async fn handle_packet ( packet_id : i32 , mut cursor : Cursor < Vec < u8 > > ) -> Result < Join , String > {
113+ match packet_id {
114+ 0x00 => {
115+ let len = read_varint ( & mut cursor) . await ? as usize ;
116+ let mut buf = vec ! [ 0u8 ; len] ;
117+
118+ AsyncReadExt :: read_exact ( & mut cursor, & mut buf) . map_err ( |e| e. to_string ( ) ) . await ?;
119+
120+ let reason_json = String :: from_utf8_lossy ( & buf) . to_string ( ) ;
121+ let plain_reason = crate :: minecraft:: utils:: parse_plain ( & reason_json) ;
122+ let lower_reason = plain_reason. to_lowercase ( ) ;
123+
124+ let online_mode_keywords = [
125+ "failed to verify" , "not authenticated" , "premium account" ,
126+ "authentication servers" , "encryption" , "online-mode" ,
127+ "requires mojang" , "requires microsoft"
128+ ] ;
129+
130+ let whitelist_keywords = [
131+ "whitelist" , "whitelisted" , "not allowed"
132+ ] ;
133+
134+ let is_cracked = !online_mode_keywords. iter ( ) . any ( |& k| lower_reason. contains ( k) ) ;
135+ let is_whitelist = whitelist_keywords. iter ( ) . any ( |& k| lower_reason. contains ( k) ) ;
136+
137+ Ok ( Join {
138+ cracked : is_cracked,
139+ whitelist : is_whitelist,
140+ kick_message : Some ( plain_reason)
141+ } )
142+ }
143+ 0x01 => Ok ( Join { cracked : false , whitelist : false , kick_message : None } ) ,
144+ 0x02 => Ok ( Join { cracked : true , whitelist : false , kick_message : None } ) ,
145+ _ => Err ( format ! ( "Unknown packet after decompression: 0x{:02X}" , packet_id) ) ,
146+ }
147+ }
148+
149+ fn get_varint_size ( mut value : i32 ) -> usize {
150+ let mut size = 0 ;
151+ loop {
152+ size += 1 ;
153+ if ( value as u32 & !0x7F ) == 0 { break ; }
154+ value = ( value as u32 >> 7 ) as i32 ;
58155 }
59- Err ( "VarInt too big" . into ( ) )
156+ size
157+ }
158+
159+ async fn read_string_packet ( stream : & mut TcpStream ) -> Result < String , String > {
160+ let len = read_varint ( stream) . await ?;
161+ let mut buf = vec ! [ 0u8 ; len as usize ] ;
162+ stream. read_exact ( & mut buf) . await . map_err ( |e| e. to_string ( ) ) ?;
163+ Ok ( String :: from_utf8_lossy ( & buf) . to_string ( ) )
60164}
0 commit comments