From 39df5454a2c108df1a1ed31beacae184fd8a3445 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sun, 12 May 2024 00:11:25 +0300 Subject: [PATCH] init commit?? --- Cargo.lock | 73 +++++++++++++ Cargo.toml | 15 +++ LICENSE | 13 +++ README.md | 1 + src/lib.rs | 305 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 37 +++++++ 6 files changed, 444 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e7c0baa --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,73 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "bytebuffer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7bfaf7cd08cacd74cdc6b521c37ac39cbc92692e5ab5c21ed5657a749b577c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "rust_mc_proto" +version = "0.1.0" +dependencies = [ + "bytebuffer", + "flate2", + "varint-rs", +] + +[[package]] +name = "varint-rs" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c62ce83 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rust_mc_proto" +description = "Minecraft packets protocol in pure rust" + +license-file = "LICENSE" + +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +varint-rs = "2.2.0" +flate2 = "1.0.30" +bytebuffer = "2.2.0" \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..07b7a81 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. \ No newline at end of file diff --git a/README.md b/README.md index 3bb3f21..fb4edda 100644 --- a/README.md +++ b/README.md @@ -1 +1,2 @@ # rust_mc_proto +Minecraft packets protocol in pure rust \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..eeaa318 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,305 @@ +use std::io::{Write, Read}; +use std::net::{SocketAddr, TcpStream, ToSocketAddrs}; +use bytebuffer::ByteBuffer; +use varint_rs::{VarintWriter, VarintReader}; +use flate2::{Compress, Compression, Decompress, FlushCompress, Status, FlushDecompress}; + +#[derive(Debug)] +pub enum ProtocolError { + AddressParseError, + DataRanOutError, + StringParseError, + StreamConnectError, + VarIntError, + ReadError, + WriteError, + ZlibError, + UnsignedShortError +} + +#[derive(Debug)] +pub struct Packet { + pub id: u8, + pub buffer: ByteBuffer +} + +impl VarintWriter for Packet { + type Error = ProtocolError; + + fn write(&mut self, byte: u8) -> Result<(), Self::Error> { + match Write::write(&mut self.buffer, &[byte]) { + Ok(_) => Ok(()), + Err(_) => Err(ProtocolError::WriteError) + } + } +} + +impl VarintReader for Packet { + type Error = ProtocolError; + + fn read(&mut self) -> Result { + let mut buf = vec![0; 1]; + + match Read::read(&mut self.buffer, &mut buf) { + Ok(i) => { + if i == 1 { + Ok(buf[0]) + } else { + Err(ProtocolError::ReadError) + } + }, + Err(_) => Err(ProtocolError::ReadError), + } + } +} + +impl Packet { + pub fn new(id: u8, buffer: ByteBuffer) -> Packet { + Packet { + id, + buffer + } + } + + pub fn from_bytes(id: u8, data: &[u8]) -> Packet { + Packet { + id, + buffer: ByteBuffer::from_bytes(data) + } + } + + pub fn empty(id: u8) -> Packet { + Packet { + id, + buffer: ByteBuffer::new() + } + } + + pub fn write_string(&mut self, s: String) -> Result<(), ProtocolError> { + let bytes = s.as_bytes(); + + self.write_usize_varint(bytes.len())?; + + for b in bytes { + self.write(*b)?; + } + + Ok(()) + } + + pub fn read_string(&mut self) -> Result { + let size = self.read_usize_varint()?; + let mut bytes: Vec = vec![0; size]; + + for _ in 0..size { + bytes.push(self.read()?); + } + + match String::from_utf8(bytes) { + Ok(i) => Ok(i), + Err(_) => Err(ProtocolError::StringParseError) + } + } + + pub fn write_unsigned_short(&mut self, short: u16) -> Result<(), ProtocolError> { + match self.buffer.write_all(&short.to_be_bytes()) { + Ok(_) => Ok(()), + Err(_) => Err(ProtocolError::UnsignedShortError), + } + } +} + +pub struct Connection { + pub stream: TcpStream, + pub addr: SocketAddr, + compress: bool, + compress_threashold: usize +} + +impl Connection { + pub fn build(addr: &str) -> Result { + let addr = match addr.to_socket_addrs() { + Ok(mut i) => { match i.next() { + Some(i) => { i }, + None => { return Err(ProtocolError::AddressParseError) }, + } }, + Err(_) => { return Err(ProtocolError::AddressParseError) }, + }; + + let stream: TcpStream = match TcpStream::connect(&addr) { + Ok(i) => i, + Err(_) => { return Err(ProtocolError::StreamConnectError) }, + }; + + Ok(Connection { + stream, + addr, + compress: false, + compress_threashold: 0 + }) + } + + pub fn new(addr: &str) -> Connection { + Self::build(addr).unwrap() + } + + pub fn set_compression(&mut self, threashold: usize) { + self.compress = true; + self.compress_threashold = threashold; + } + + pub fn read_packet(&mut self) -> Result { + if !self.compress { + let length = self.read_usize_varint()?; + + let packet_id = self.read_u8_varint()?; + let mut data: Vec = vec![0; length - 1]; + match self.stream.read_exact(&mut data) { + Ok(i) => i, + Err(_) => { return Err(ProtocolError::ReadError) }, + }; + + Ok(Packet::from_bytes(packet_id, &data)) + } else { + let packet_length = self.read_usize_varint()?; + let data_length = self.read_usize_varint()?; + + if data_length == 0 { + let mut data: Vec = vec![0; packet_length - 1]; + match self.stream.read_exact(&mut data) { + Ok(i) => i, + Err(_) => { return Err(ProtocolError::ReadError) }, + }; + + let mut data_buf = ByteBuffer::from_vec(decompress_zlib(&data)?); + + let packet_id = match data_buf.read_u8_varint() { + Ok(i) => i, + Err(_) => { return Err(ProtocolError::VarIntError) }, + }; + let mut data: Vec = vec![0; data_length - 1]; + match data_buf.read_exact(&mut data) { + Ok(i) => i, + Err(_) => { return Err(ProtocolError::ReadError) }, + }; + + Ok(Packet::from_bytes(packet_id, &data)) + } else { + let packet_id = self.read_u8_varint()?; + let mut data: Vec = vec![0; data_length - 1]; + match self.stream.read_exact(&mut data) { + Ok(i) => i, + Err(_) => { return Err(ProtocolError::ReadError) }, + }; + + Ok(Packet::from_bytes(packet_id, &data)) + } + } + } + + pub fn write_packet(&mut self, packet: &Packet) -> Result<(), ProtocolError> { + let mut buf = ByteBuffer::new(); + + if !self.compress { + match buf.write_usize_varint(packet.buffer.len() + 1) { + Ok(_) => {}, + Err(_) => { return Err(ProtocolError::WriteError) }, + }; + match buf.write_u8_varint(packet.id) { + Ok(_) => {}, + Err(_) => { return Err(ProtocolError::WriteError) }, + }; + match buf.write_all(packet.buffer.as_bytes()) { + Ok(_) => {}, + Err(_) => { return Err(ProtocolError::WriteError) }, + }; + } else { + let mut pack = ByteBuffer::new(); + + if packet.buffer.len() < self.compress_threashold { + pack.write_usize_varint(0).unwrap(); + pack.write_u8_varint(packet.id).unwrap(); + pack.write_all(packet.buffer.as_bytes()).unwrap(); + } else { + let mut data = ByteBuffer::new(); + + data.write_u8_varint(packet.id).unwrap(); + data.write_all(packet.buffer.as_bytes()).unwrap(); + + let data = compress_zlib(data.as_bytes())?; + + pack.write_usize_varint(packet.buffer.len() + 1).unwrap(); + pack.write_all(&data).unwrap(); + } + + buf.write_usize_varint(pack.len()).unwrap(); + buf.write_all(pack.as_bytes()).unwrap(); + } + + self.stream.write_all(buf.as_bytes()).unwrap(); + + Ok(()) + } + + pub fn close(&mut self) { + self.stream.shutdown(std::net::Shutdown::Both).unwrap(); + } +} + +impl VarintWriter for Connection { + type Error = ProtocolError; + + fn write(&mut self, byte: u8) -> Result<(), Self::Error> { + match Write::write(&mut self.stream, &[byte]) { + Ok(_) => Ok(()), + Err(_) => Err(ProtocolError::WriteError), + } + } +} + +impl VarintReader for Connection { + type Error = ProtocolError; + + fn read(&mut self) -> Result { + let mut buf = vec![0; 1]; + + match Read::read(&mut self.stream, &mut buf) { + Ok(i) => { + if i == 1 { + Ok(buf[0]) + } else { + Err(ProtocolError::ReadError) + } + }, + Err(_) => Err(ProtocolError::ReadError), + } + } +} + +fn compress_zlib(bytes: &[u8]) -> Result, ProtocolError> { + let mut compresser = Compress::new(Compression::best(), true); + let mut output: Vec = Vec::new(); + match compresser.compress_vec(&bytes, &mut output, FlushCompress::Finish) { + Ok(i) => { + match i { + Status::Ok => Ok(output), + _ => Err(ProtocolError::ZlibError) + } + }, + Err(_) => Err(ProtocolError::ZlibError) + } +} + +fn decompress_zlib(bytes: &[u8]) -> Result, ProtocolError> { + let mut compresser = Decompress::new(true); + let mut output: Vec = Vec::new(); + match compresser.decompress_vec(&bytes, &mut output, FlushDecompress::Finish) { + Ok(i) => { + match i { + Status::Ok => Ok(output), + _ => Err(ProtocolError::ZlibError) + } + }, + Err(_) => Err(ProtocolError::ZlibError) + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..eed82af --- /dev/null +++ b/src/main.rs @@ -0,0 +1,37 @@ +use rust_mc_proto::{Connection, Packet, ProtocolError}; +use varint_rs::{VarintReader, VarintWriter}; +use std::io::Write; + +fn send_handshake(conn: &mut Connection, + protocol_version: u16, + server_address: &str, + server_port: u16, + next_state: u8) -> Result<(), ProtocolError> { + let mut packet = Packet::empty(0x00); + packet.write_u16_varint(protocol_version)?; + packet.write_string(server_address.to_string())?; + packet.write_unsigned_short(server_port)?; + packet.write_u8_varint(next_state)?; + + packet.buffer.set_rpos(0); + packet.buffer.set_wpos(0); + + conn.write_packet(&packet) +} + +fn send_status_request(conn: &mut Connection) -> Result<(), ProtocolError> { + conn.write_packet(&Packet::empty(0x00)) +} + +fn read_status_response(conn: &mut Connection) -> Result { + let mut packet = conn.read_packet()?; + packet.read_string() +} + +fn main() { + let mut conn = Connection::new("localhost:25565"); + + send_handshake(&mut conn, 765, "localhost", 25565, 1).unwrap(); + send_status_request(&mut conn).unwrap(); + println!("{}", read_status_response(&mut conn).unwrap()); +}