Skip to content

Commit 121cf47

Browse files
refactor: Server code refactor
1 parent 22f5881 commit 121cf47

File tree

17 files changed

+262
-213
lines changed

17 files changed

+262
-213
lines changed

src/api/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
pub mod endpoint_handler;
22

33
use actix_web::{dev::Server, App, HttpServer};
4+
use anyhow::Result;
45
use std::net::SocketAddrV4;
56

67
/// Build HTTP server
7-
pub fn build_http_server(addr: SocketAddrV4) -> Server {
8+
pub fn build_http_server(addr: SocketAddrV4) -> Result<Server> {
89
let app = || App::new().service(endpoint_handler::code);
910

10-
HttpServer::new(app).bind(addr).unwrap().run()
11+
Ok(HttpServer::new(app).bind(addr)?.run())
1112
}

src/config/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
pub mod server_icon;
21
pub mod types;
32

43
use anyhow::Result;
4+
use base64_light::base64_encode_bytes;
55
use std::sync::OnceLock;
66
use tokio::fs;
77
use tracing::warn;
@@ -22,7 +22,7 @@ pub async fn load(path: &str) -> Result<()> {
2222
};
2323

2424
//let mut config: types::Config = toml::from_str(&file);
25-
match server_icon::load(&config.server.status.icon_path).await {
25+
match load_server_icon(&config.server.status.icon_path).await {
2626
Ok(base64) => config.image = Some(format!("data:image/png;base64,{}", base64)),
2727
Err(e) => warn!("Error loading server icon: {}", e),
2828
}
@@ -35,3 +35,11 @@ pub async fn load(path: &str) -> Result<()> {
3535
pub async fn get_config() -> &'static types::Config {
3636
CONFIG.get().expect("Config didn't loaded")
3737
}
38+
39+
/**
40+
Load server icon from file
41+
*/
42+
pub async fn load_server_icon(file_path: &str) -> anyhow::Result<String> {
43+
let file_content = fs::read(file_path).await?;
44+
Ok(base64_encode_bytes(&file_content))
45+
}

src/config/server_icon.rs

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

src/encryption.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
use crate::client::Session;
1+
use crate::server::MinecraftServer;
2+
use aes::cipher::AsyncStreamCipher;
23
use bytes::BytesMut;
34

4-
pub fn encrypt_packet(data: &mut BytesMut, session: &mut Session) {
5-
use aes::cipher::{generic_array::GenericArray, BlockEncryptMut, BlockSizeUser};
6-
use aes::Aes128;
5+
impl MinecraftServer {
6+
/**
7+
Encrypt minecraft packet in place
78
8-
if session.cipher.is_none() {
9-
return;
10-
}
9+
If session cipher is not set — skip the encryption
10+
*/
11+
pub fn encrypt_packet(&mut self, data: &mut BytesMut) {
12+
if self.session.cipher.is_none() {
13+
return;
14+
}
1115

12-
let mut cipher = session.cipher.clone().unwrap();
13-
for chunk in data.chunks_mut(cfb8::Encryptor::<Aes128>::block_size()) {
14-
let gen_arr = GenericArray::from_mut_slice(chunk);
15-
cipher.encrypt_block_mut(gen_arr);
16+
if let Some(cipher) = &mut self.session.cipher {
17+
cipher.clone().encrypt(data);
18+
}
1619
}
1720
}
Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,40 @@
1-
use std::sync::Arc;
2-
3-
use crate::{client::Session, packets::encryption_response::EncryptionResponsePacket};
1+
use crate::{packets::encryption_response::EncryptionResponsePacket, server::MinecraftServer};
42
use aes::{cipher::KeyIvInit, Aes128};
5-
use anyhow::Result;
6-
use bytes::BytesMut;
3+
use anyhow::{Error, Result};
4+
use rsa::Pkcs1v15Encrypt;
75

8-
use rsa::{Pkcs1v15Encrypt, RsaPrivateKey};
6+
impl MinecraftServer {
7+
/**
8+
Handle encryption response
99
10-
pub fn handle_encryption(
11-
session: &mut Session,
12-
buffer: &mut BytesMut,
13-
keys: Arc<rsa::RsaPrivateKey>,
14-
) -> Result<()> {
15-
let response = EncryptionResponsePacket::parse(buffer)?;
10+
Check verify tokens and set up the cipher
11+
*/
12+
pub fn handle_encryption(&mut self) -> Result<()> {
13+
let response = EncryptionResponsePacket::parse(&mut self.buffer)?;
1614

17-
// Decrypt client's keys
18-
let decrypted_secret = decrypt(&keys, &response.shared_secret)?;
19-
let decrypted_verify = decrypt(&keys, &response.verify_token)?;
15+
// Decrypt client's keys
16+
let decrypted_secret = self
17+
.keys
18+
.decrypt(Pkcs1v15Encrypt, &response.shared_secret)?;
19+
let decrypted_verify = self.keys.decrypt(Pkcs1v15Encrypt, &response.verify_token)?;
2020

21-
// Check tokens equality
22-
if decrypted_verify
23-
.iter()
24-
.zip(&session.verify_token)
25-
.filter(|&(a, b)| a == b)
26-
.count()
27-
!= decrypted_verify.len()
28-
{
29-
panic!("Verify tokens didn't match!");
30-
}
21+
// Check tokens equality
22+
if decrypted_verify
23+
.iter()
24+
.zip(&self.session.verify_token)
25+
.filter(|&(a, b)| a == b)
26+
.count()
27+
!= decrypted_verify.len()
28+
{
29+
return Err(Error::msg("Verify tokens didn't match!"));
30+
}
3131

32-
// Set up client cipher
33-
session.secret = Some(decrypted_secret.clone());
34-
session.cipher = Some(
35-
cfb8::Encryptor::<Aes128>::new_from_slices(
32+
// Set up client cipher
33+
self.session.secret = Some(decrypted_secret.clone());
34+
self.session.cipher = Some(cfb8::Encryptor::<Aes128>::new_from_slices(
3635
&decrypted_secret.clone(),
3736
&decrypted_secret.clone(),
38-
)
39-
.unwrap(),
40-
);
41-
Ok(())
42-
}
43-
44-
fn decrypt(private_key: &RsaPrivateKey, data: &Vec<u8>) -> Result<Vec<u8>, rsa::Error> {
45-
return private_key.decrypt(Pkcs1v15Encrypt, data);
37+
)?);
38+
Ok(())
39+
}
4640
}

src/handlers/handshake.rs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,35 @@
11
use anyhow::{Error, Result};
22

33
use crate::{
4-
client::{MinecraftClient, NextStateEnum},
54
packets::handshake::HandshakePacket,
6-
responses::disconnect::send_disconnect,
5+
server::{MinecraftServer, NextStateEnum},
76
};
87

9-
pub async fn handle_handshake(client: &mut MinecraftClient) -> Result<()> {
10-
let handshake = HandshakePacket::parse(&mut client.buffer)?;
8+
impl MinecraftServer {
9+
/**
10+
Handle handshake packet
1111
12-
client.session.next_state = match handshake.next_state {
13-
1 => NextStateEnum::Status,
14-
2 => NextStateEnum::Login,
15-
_ => NextStateEnum::Unknown,
16-
};
12+
Also check client's connection host
13+
*/
14+
pub async fn handle_handshake(&mut self) -> Result<()> {
15+
let handshake = HandshakePacket::parse(&mut self.buffer)?;
1716

18-
// Check client's connecting hostname
19-
client.session.proto_ver = Some(handshake.proto_ver);
20-
if let Some(server_ip) = &client.config.server.server_ip {
21-
if server_ip.ne(&handshake.server_addr) {
22-
send_disconnect(
23-
&mut client.stream,
24-
&mut client.session,
25-
client.config.messages.using_proxy.clone(),
26-
)
27-
.await?;
28-
return Err(Error::msg("Client using a proxy!"));
17+
self.session.next_state = match handshake.next_state {
18+
1 => NextStateEnum::Status,
19+
2 => NextStateEnum::Login,
20+
_ => NextStateEnum::Unknown,
21+
};
22+
23+
// Check client's connecting hostname
24+
self.session.proto_ver = Some(handshake.proto_ver);
25+
if let Some(server_ip) = &self.config.server.server_ip {
26+
if server_ip.ne(&handshake.server_addr) {
27+
self.send_disconnect(self.config.messages.using_proxy.clone())
28+
.await?;
29+
return Err(Error::msg("Client using a proxy!"));
30+
}
2931
}
30-
}
3132

32-
Ok(())
33+
Ok(())
34+
}
3335
}

src/handlers/login_start.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
use crate::{client::Session, packets::login_start::LoginStartPacket};
2-
use bytes::BytesMut;
1+
use crate::{packets::login_start::LoginStartPacket, server::MinecraftServer};
32

4-
pub fn handle_login_start(session: &mut Session, buff: &mut BytesMut) -> anyhow::Result<()> {
5-
let packet = LoginStartPacket::parse(buff)?;
6-
session.nickname = Some(packet.name);
7-
session.uuid = packet.uuid;
8-
Ok(())
3+
impl MinecraftServer {
4+
/**
5+
Handle login start packet from client
6+
*/
7+
pub fn handle_login_start(&mut self) -> anyhow::Result<()> {
8+
let packet = LoginStartPacket::parse(&mut self.buffer)?;
9+
self.session.nickname = Some(packet.name);
10+
self.session.uuid = packet.uuid;
11+
Ok(())
12+
}
913
}

src/main.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
mod api;
22
mod byte_buf_utils;
3-
mod client;
43
mod config;
54
mod encryption;
65
mod generators;
@@ -9,6 +8,7 @@ mod map;
98
mod mojang;
109
mod packets;
1110
mod responses;
11+
mod server;
1212

1313
use std::{
1414
net::{Ipv4Addr, SocketAddrV4},
@@ -18,8 +18,8 @@ use std::{
1818
};
1919

2020
use api::build_http_server;
21-
use client::MinecraftClient;
2221
use generators::generate_key_pair;
22+
use server::MinecraftServer;
2323
use tokio::{net::TcpListener, sync::Notify};
2424
use tokio::{signal, time::timeout};
2525
use tracing::{error, info};
@@ -54,7 +54,8 @@ async fn main() -> anyhow::Result<()> {
5454
let http = build_http_server(SocketAddrV4::new(
5555
Ipv4Addr::from_str(&config.api.addr)?,
5656
config.api.port,
57-
));
57+
))?;
58+
5859
tokio::spawn(async move { http.await });
5960
info!("API started on port {}", config.api.port);
6061

@@ -92,7 +93,7 @@ async fn main() -> anyhow::Result<()> {
9293
result = listener.accept() => {
9394
match result {
9495
Ok((stream, addr)) => {
95-
let mut client = MinecraftClient::new(stream, keys.clone()).await;
96+
let mut client = MinecraftServer::new(stream, keys.clone()).await?;
9697
info!("New connection from: {}", addr);
9798

9899
tokio::spawn(async move {

src/mojang/mod.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
pub mod response;
2-
use crate::{client::Session, config};
3-
use anyhow::Result;
2+
use crate::{config, server::Session};
3+
use anyhow::{Context, Result};
44
use num_bigint::BigInt;
55
use rsa::pkcs8::EncodePublicKey;
66
use sha1::{Digest, Sha1};
@@ -13,7 +13,13 @@ pub async fn join(
1313
) -> Result<Option<response::MojangResponse>> {
1414
let mut digest = Sha1::new();
1515
digest.update(session.server_id.as_bytes());
16-
digest.update(session.secret.clone().unwrap().as_slice());
16+
digest.update(
17+
session
18+
.secret
19+
.clone()
20+
.context("Session secret not set")?
21+
.as_slice(),
22+
);
1723
digest.update(keys.to_public_key().to_public_key_der()?.as_bytes());
1824
let hash = BigInt::from_signed_bytes_be(&digest.finalize()).to_str_radix(16);
1925

@@ -25,7 +31,13 @@ pub async fn join(
2531
.server
2632
.config
2733
.auth_url
28-
.replace("{{NAME}}", &session.nickname.clone().unwrap())
34+
.replace(
35+
"{{NAME}}",
36+
&session
37+
.nickname
38+
.clone()
39+
.context("Client's nickname not set")?,
40+
)
2941
.replace("{{HASH}}", &hash),
3042
)
3143
.await?;

src/packets/disconnect.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,31 @@ pub struct DisconnectPacket {
88
}
99

1010
impl DisconnectPacket {
11+
/**
12+
Create disconnect packet with provided reason
13+
*/
14+
pub fn with_reason(reason: String) -> Self {
15+
Self { reason }
16+
}
17+
18+
/**
19+
Create empty disconnect packet
20+
*/
21+
#[allow(dead_code)]
22+
pub fn new() -> Self {
23+
Self {
24+
reason: String::new(),
25+
}
26+
}
27+
28+
/**
29+
Build packet for sending over network
30+
*/
1131
pub fn build(&self) -> Result<BytesMut> {
1232
let mut buffer = BytesMut::new();
1333

1434
write_varint(&mut buffer, 0x00); // Packet id
15-
write_utf8(&mut buffer, &self.reason)?; // Server id
35+
write_utf8(&mut buffer, &format!(r#"{{"text":"{}"}}"#, self.reason))?; // Disconnect reason
1636

1737
Ok(add_size(&buffer))
1838
}

0 commit comments

Comments
 (0)