Skip to content

Commit 750e05f

Browse files
feature: full client code refactor
1 parent fc7dc7f commit 750e05f

File tree

19 files changed

+253
-234
lines changed

19 files changed

+253
-234
lines changed

src/client.rs

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
use crate::{
2+
byte_buf_utils::read_varint,
3+
config::{self, get_config},
4+
generators::{generate_code, generate_verify_token},
5+
handlers::{
6+
encryption_response::handle_encryption, handshake::handle_handshake,
7+
login_start::handle_login_start,
8+
},
9+
map::get_map,
10+
mojang,
11+
responses::{
12+
disconnect::send_disconnect, encryption::send_encryption, ping::handle_ping,
13+
status::send_status,
14+
},
15+
};
16+
use aes::Aes128;
17+
use anyhow::{anyhow, Result};
18+
use bytes::{BufMut, BytesMut};
19+
use cfb8::Encryptor;
20+
use std::{sync::Arc, time::Duration};
21+
use tokio::net::TcpStream;
22+
use tracing::{debug, info};
23+
use uuid::Uuid;
24+
25+
#[derive(Debug)]
26+
pub enum NextStateEnum {
27+
Status,
28+
Login,
29+
Unknown,
30+
}
31+
32+
#[derive(Debug)]
33+
pub struct Session {
34+
pub server_id: String,
35+
pub proto_ver: Option<usize>,
36+
pub next_state: NextStateEnum,
37+
pub nickname: Option<String>,
38+
pub uuid: Option<Uuid>,
39+
pub secret: Option<Vec<u8>>, // Shared secret,
40+
pub verify_token: [u8; 4],
41+
pub cipher: Option<Encryptor<Aes128>>,
42+
}
43+
44+
impl Session {
45+
pub async fn new() -> Self {
46+
let config = get_config().await;
47+
48+
Self {
49+
server_id: config.server.config.server_name.clone(),
50+
proto_ver: None,
51+
next_state: NextStateEnum::Unknown,
52+
nickname: None,
53+
uuid: None,
54+
secret: None,
55+
verify_token: generate_verify_token(),
56+
cipher: None,
57+
}
58+
}
59+
}
60+
61+
pub struct MinecraftClient {
62+
pub session: Session,
63+
pub buffer: BytesMut,
64+
pub config: &'static config::types::Config,
65+
pub stream: TcpStream,
66+
pub keys: Arc<rsa::RsaPrivateKey>,
67+
}
68+
69+
impl MinecraftClient {
70+
pub async fn new(stream: TcpStream, keys: Arc<rsa::RsaPrivateKey>) -> Self {
71+
Self {
72+
session: Session::new().await,
73+
buffer: BytesMut::new(),
74+
config: get_config().await,
75+
stream,
76+
keys,
77+
}
78+
}
79+
80+
pub async fn run(&mut self) -> Result<()> {
81+
loop {
82+
let mut temp_buf = vec![0; 1024];
83+
self.stream.readable().await?;
84+
85+
match self.stream.try_read(&mut temp_buf) {
86+
Ok(0) => {
87+
debug!("No more data to read. Exiting...");
88+
break; // if client disconnected
89+
}
90+
Ok(n) => {
91+
self.buffer.put_slice(&temp_buf[..n]);
92+
self.handle_packet().await?;
93+
}
94+
Err(_) => {}
95+
}
96+
self.buffer.clear();
97+
}
98+
Ok(())
99+
}
100+
101+
async fn handle_packet(&mut self) -> Result<()> {
102+
while self.packet_available() {
103+
let packet_id = read_varint(&mut self.buffer)?;
104+
debug!("Received packet: {}", packet_id);
105+
106+
match packet_id {
107+
0x00 => self.handle_packet_0().await?,
108+
0x01 => self.handle_packet_1().await?,
109+
_ => return Err(anyhow!("Received unknown packet")),
110+
}
111+
}
112+
113+
Ok(())
114+
}
115+
116+
async fn handle_packet_0(&mut self) -> Result<()> {
117+
match self.session.next_state {
118+
NextStateEnum::Status => send_status(&mut self.stream, &mut self.session).await?, // Send status response
119+
NextStateEnum::Login => {
120+
// Handle login start
121+
handle_login_start(&mut self.session, &mut self.buffer)?;
122+
send_encryption(&mut self.stream, self.keys.clone(), &mut self.session).await?;
123+
}
124+
NextStateEnum::Unknown => handle_handshake(&mut self.session, &mut self.buffer)?, // Handle handshake
125+
}
126+
127+
Ok(())
128+
}
129+
130+
async fn handle_packet_1(&mut self) -> Result<()> {
131+
match self.session.next_state {
132+
NextStateEnum::Status => {
133+
// Handle ping request
134+
handle_ping(&mut self.stream, &mut self.buffer).await?
135+
}
136+
NextStateEnum::Login => self.handle_encryption_response().await?,
137+
NextStateEnum::Unknown => return Err(anyhow!("Received unknown packet")),
138+
}
139+
Ok(())
140+
}
141+
142+
async fn handle_encryption_response(&mut self) -> Result<()> {
143+
debug!("Received encryption response");
144+
handle_encryption(&mut self.session, &mut self.buffer, self.keys.clone())?;
145+
let player_data = mojang::join(&mut self.session, self.keys.clone()).await?;
146+
147+
if player_data.is_none() {
148+
debug!("Mojang API error");
149+
send_disconnect(
150+
&mut self.stream,
151+
&mut self.session,
152+
self.config.messages.bad_session.clone(),
153+
)
154+
.await?;
155+
return Err(anyhow!("Mojang API error"));
156+
}
157+
158+
let player_data = player_data.unwrap();
159+
let map = get_map().await;
160+
let code = generate_code(6); // Generate 6-digit code
161+
162+
// Insert client data into hash map
163+
map.insert(
164+
code.clone(),
165+
player_data.clone(),
166+
Duration::from_secs(self.config.api.code_life_time),
167+
)
168+
.await;
169+
170+
// Disconnect client with code
171+
send_disconnect(
172+
&mut self.stream,
173+
&mut self.session,
174+
self.config
175+
.messages
176+
.success
177+
.replace("{{NAME}}", &player_data.name)
178+
.replace("{{UUID}}", &player_data.id)
179+
.replace("{{CODE}}", &code),
180+
)
181+
.await?;
182+
183+
info!("Created code {} for {}", code, player_data.name);
184+
Ok(())
185+
}
186+
187+
fn packet_available(&mut self) -> bool {
188+
if self.buffer.is_empty() {
189+
return false;
190+
}
191+
// Read packet length
192+
read_varint(&mut self.buffer)
193+
.map(|x| self.buffer.len() >= x)
194+
.unwrap_or(false)
195+
}
196+
}

src/client_sessions.rs

Lines changed: 0 additions & 41 deletions
This file was deleted.

src/config/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
pub mod server_icon;
2-
mod types;
2+
pub mod types;
33

44
use anyhow::Result;
55
use std::sync::OnceLock;
@@ -14,13 +14,13 @@ pub async fn load(path: &str) -> Result<()> {
1414
.await
1515
.expect("Couldn't load config file");
1616

17-
let mut config: types::Config = match toml::from_str(&file) {
17+
let mut config: types::Config = match toml::from_str(&file) {
1818
Ok(config) => config,
1919
Err(err) => {
2020
panic!("Couldn't parse config: {}", err.message())
2121
}
2222
};
23-
23+
2424
//let mut config: types::Config = toml::from_str(&file);
2525
match server_icon::load(&config.server.status.icon_path).await {
2626
Ok(base64) => config.image = Some(format!("data:image/png;base64,{}", base64)),

src/encryption.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1+
use crate::client::Session;
12
use bytes::BytesMut;
23

3-
use crate::client_sessions::Session;
4-
54
pub fn encrypt_packet(data: &mut BytesMut, session: &mut Session) {
65
use aes::cipher::{generic_array::GenericArray, BlockEncryptMut, BlockSizeUser};
76
use aes::Aes128;

src/generators/keys.rs

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/generators/mod.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,19 @@
1-
pub mod keys;
1+
use rand::{rngs::OsRng, Rng};
2+
use rsa::RsaPrivateKey;
3+
4+
pub fn generate_key_pair() -> RsaPrivateKey {
5+
let bits = 1024;
6+
RsaPrivateKey::new(&mut OsRng, bits).expect("failed to generate a key")
7+
}
8+
9+
pub fn generate_verify_token() -> [u8; 4] {
10+
let mut token = [0u8; 4];
11+
rand::thread_rng().fill(&mut token);
12+
token
13+
}
14+
15+
pub fn generate_code(len: u8) -> String {
16+
(0..len)
17+
.map(|_| rand::thread_rng().gen_range(0..=9).to_string())
18+
.collect()
19+
}

0 commit comments

Comments
 (0)