Browse Source

Refactor packet parsing/creating code.

wip
Titouan Rigoudy 9 years ago
parent
commit
b07fd93219
6 changed files with 287 additions and 98 deletions
  1. +10
    -0
      src/config.rs
  2. +6
    -6
      src/main.rs
  3. +71
    -55
      src/proto/connection.rs
  4. +118
    -0
      src/proto/message.rs
  5. +2
    -0
      src/proto/mod.rs
  6. +80
    -37
      src/server.rs

+ 10
- 0
src/config.rs View File

@ -0,0 +1,10 @@
pub const VER_MAJOR: u32 = 181;
pub const VER_MINOR: u32 = 100;
pub const USERNAME: &'static str = "abcdefgh";
// The password is not used for much, and sent unencrypted over the wire, so
// why not even check it in to git
pub const PASSWORD: &'static str = "ijklmnop";
pub const SERVER_HOST : &'static str = "server.slsknet.org";
pub const SERVER_PORT : u16 = 2242;

+ 6
- 6
src/main.rs View File

@ -1,5 +1,6 @@
mod server;
mod proto;
mod config;
#[macro_use] extern crate log;
extern crate mio;
@ -12,12 +13,9 @@ use std::net::ToSocketAddrs;
use mio::{EventLoop, EventSet, Handler, PollOpt, Token};
use mio::tcp::TcpStream;
use proto::Connection;
use proto::connection::Connection;
use server::ServerConnection;
const SERVER_HOST : &'static str = "server.slsknet.org";
const SERVER_PORT : u16 = 2242;
const SERVER_TOKEN : Token = Token(0);
#[derive(Debug)]
@ -67,8 +65,10 @@ fn connect(hostname: &str, port: u16) -> io::Result<TcpStream> {
}
fn main() {
let stream = connect(SERVER_HOST, SERVER_PORT).unwrap();
println!("Connected to {:?}", &stream);
let host = config::SERVER_HOST;
let port = config::SERVER_PORT;
let stream = connect(host, port).unwrap();
println!("Connected to {}:{}", host, port);
let mut event_loop = EventLoop::new().unwrap();


src/proto.rs → src/proto/connection.rs View File

@ -13,78 +13,81 @@ const MAX_MESSAGE_SIZE: usize = MAX_PACKET_SIZE - U32_SIZE;
const CODE_LOGIN: u32 = 1;
/*=========*
* MESSAGE *
*=========*/
#[derive(Debug, Clone, Copy)]
pub enum MessageCode {
Login,
Unknown(u32),
}
impl MessageCode {
fn to_u32(&self) -> u32 {
match *self {
MessageCode::Login => CODE_LOGIN,
MessageCode::Unknown(code) => code,
}
}
fn from_u32(code: u32) -> MessageCode {
match code {
CODE_LOGIN => MessageCode::Login,
_ => MessageCode::Unknown(code),
}
}
}
#[derive(Debug)]
pub struct Message {
code: MessageCode,
pub struct Packet {
cursor: usize,
bytes: Vec<u8>,
}
impl Message {
pub fn new(code: MessageCode) -> Message {
impl Packet {
pub fn new(code: u32) -> Self {
let mut bytes = Vec::new();
bytes.write_u32::<LittleEndian>(0).unwrap();
bytes.write_u32::<LittleEndian>(code.to_u32()).unwrap();
Message {
code: code,
bytes.write_u32::<LittleEndian>(code).unwrap();
Packet {
cursor: 2*U32_SIZE,
bytes: bytes,
}
}
fn from_raw_parts(bytes: Vec<u8>) -> Message {
let code_u32 = LittleEndian::read_u32(&bytes[U32_SIZE..2*U32_SIZE]);
Message {
code: MessageCode::from_u32(code_u32),
fn from_raw_parts(bytes: Vec<u8>) -> Self {
let size = LittleEndian::read_u32(&bytes[..U32_SIZE]) as usize;
assert!(size + U32_SIZE == bytes.len());
Packet {
cursor: U32_SIZE,
bytes: bytes,
}
}
pub fn code(&self) -> MessageCode {
self.code
}
// Writing convenience
pub fn write_str(&mut self, string: &str) -> io::Result<usize> {
try!(self.write_u32(string.len() as u32));
let n = try!(self.bytes.write(string.as_bytes()));
try!(self.write_uint(string.len() as u32));
let n = try!(self.write(string.as_bytes()));
Ok(n + U32_SIZE)
}
pub fn write_u32(&mut self, n: u32) -> io::Result<usize> {
match self.bytes.write_u32::<LittleEndian>(n) {
pub fn write_uint(&mut self, n: u32) -> io::Result<usize> {
match self.write_u32::<LittleEndian>(n) {
Ok(()) => Ok(U32_SIZE),
Err(e) => Err(io::Error::from(e))
}
}
pub fn write_bool(&mut self, b: bool) -> io::Result<usize> {
self.bytes.write(&[b as u8])
self.write(&[b as u8])
}
// Reading convenience
pub fn read_uint(&mut self) -> io::Result<u32> {
self.read_u32::<LittleEndian>().map_err(io::Error::from)
}
pub fn read_str(&mut self) -> io::Result<String> {
let len = try!(self.read_uint()) as usize;
let mut buffer = vec![0; len];
try!(self.read(&mut buffer));
let result = String::from_utf8(buffer);
match result {
Ok(string) => Ok(string),
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e.to_string())),
}
}
pub fn read_bool(&mut self) -> io::Result<bool> {
let mut buffer = vec![0; 1];
try!(self.read(&mut buffer));
match buffer[0] {
0 => Ok(false),
1 => Ok(true),
n => Err(io::Error::new(io::ErrorKind::InvalidInput,
format!("{} is not a boolean", n)))
}
}
pub fn finalize(mut self) -> Vec<u8> {
let bytes_len = (self.bytes.len() - U32_SIZE) as u32;
{
@ -95,7 +98,7 @@ impl Message {
}
}
impl io::Write for Message {
impl io::Write for Packet {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.bytes.write(buf)
}
@ -105,19 +108,32 @@ impl io::Write for Message {
}
}
impl io::Read for Packet {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut slice = &self.bytes[self.cursor..];
let result = slice.read(buf);
match result {
Ok(num_bytes_read) => self.cursor += num_bytes_read,
Err(_) => ()
}
result
}
}
/*======*
* PEER *
*======*/
pub trait Peer {
fn read_message(&mut self) -> Option<Message>;
fn write_message(&mut self, message: Message);
fn read_packet(&mut self) -> Option<Packet>;
fn write_packet(&mut self, packet: Packet);
}
#[derive(Debug, Clone, Copy)]
enum State {
ReadingLength,
ReadingMessage,
ReadingPacket,
}
#[derive(Debug)]
@ -161,25 +177,25 @@ impl<T: Peer> Connection<T> {
if message_len > MAX_MESSAGE_SIZE {
unimplemented!();
};
self.state = State::ReadingMessage;
self.state = State::ReadingPacket;
self.num_bytes_left = message_len;
self.buffer.extend(repeat(0).take(message_len));
},
State::ReadingMessage => {
State::ReadingPacket => {
self.state = State::ReadingLength;
self.num_bytes_left = U32_SIZE;
let new_buffer = vec![0;U32_SIZE];
let old_buffer = mem::replace(&mut self.buffer, new_buffer);
self.peer.write_message(Message::from_raw_parts(old_buffer));
self.peer.write_packet(Packet::from_raw_parts(old_buffer));
}
}
}
pub fn ready_to_write(&mut self, stream: &mut TcpStream) {
match self.peer.read_message() {
Some(message) => {
stream.write(&message.finalize()).unwrap();
match self.peer.read_packet() {
Some(packet) => {
stream.write(&packet.finalize()).unwrap();
()
},
None => (),

+ 118
- 0
src/proto/message.rs View File

@ -0,0 +1,118 @@
use std::io;
use std::net;
use crypto::md5::Md5;
use crypto::digest::Digest;
use proto::connection::Packet;
const VERSION_MAJOR: u32 = 181;
const VERSION_MINOR: u32 = 0;
const CODE_LOGIN: u32 = 1;
pub enum ServerRequest {
LoginRequest(LoginRequest),
}
impl ServerRequest {
pub fn to_packet(&self) -> io::Result<Packet> {
match *self {
ServerRequest::LoginRequest(ref request) => {
let mut packet = Packet::new(CODE_LOGIN);
try!(request.write_to_packet(&mut packet));
Ok(packet)
},
}
}
}
pub enum ServerResponse {
LoginResponse(LoginResponse),
UnknownResponse(u32, Packet),
}
impl ServerResponse {
pub fn from_packet(mut packet: Packet) -> io::Result<Self> {
let resp = match try!(packet.read_uint()) {
CODE_LOGIN => ServerResponse::LoginResponse(
try!(LoginResponse::from_packet(packet))),
code => ServerResponse::UnknownResponse(code, packet),
};
Ok(resp)
}
}
fn md5_str(string: &str) -> String {
let mut hasher = Md5::new();
hasher.input_str(string);
hasher.result_str()
}
pub struct LoginRequest {
username: String,
password: String,
major: u32,
minor: u32,
}
impl LoginRequest {
pub fn new(username: &str, password: &str, major: u32, minor: u32)
-> Result<Self, &'static str> {
if password.len() > 0 {
Ok(LoginRequest {
username: username.to_string(),
password: password.to_string(),
major: major,
minor: minor,
})
} else {
Err("Empty password")
}
}
pub fn write_to_packet(&self, packet: &mut Packet) -> io::Result<()> {
let userpass = String::new() + &self.username + &self.password;
let userpass_md5 = md5_str(&userpass);
try!(packet.write_str(&self.username));
try!(packet.write_str(&self.password));
try!(packet.write_uint(self.major));
try!(packet.write_str(&md5_str(&userpass)));
try!(packet.write_uint(self.minor));
Ok(())
}
}
pub enum LoginResponse {
LoginOk {
motd: String,
ip: net::Ipv4Addr,
password_md5_opt: Option<String>
},
LoginFail {
reason: String
},
}
impl LoginResponse {
pub fn from_packet(mut packet: Packet) -> io::Result<Self> {
let ok = try!(packet.read_bool());
let resp = if ok {
let motd = try!(packet.read_str()).to_string();
let ip = net::Ipv4Addr::from(try!(packet.read_uint()));
LoginResponse::LoginOk {
motd: motd,
ip: ip,
password_md5_opt: None
}
} else {
LoginResponse::LoginFail {
reason: try!(packet.read_str()).to_string()
}
};
Ok(resp)
}
}

+ 2
- 0
src/proto/mod.rs View File

@ -0,0 +1,2 @@
pub mod connection;
pub mod message;

+ 80
- 37
src/server.rs View File

@ -1,15 +1,15 @@
use crypto::md5::Md5;
use crypto::digest::Digest;
use std::io;
use std::net::Ipv4Addr;
use proto::{Message, MessageCode, Peer};
use proto::connection::{Packet, Peer};
use proto::message::{
LoginRequest,
LoginResponse,
ServerRequest,
ServerResponse,
};
const VER_MAJOR : u32 = 181;
const VER_MINOR : u32 = 100;
const USERNAME : &'static str = "abcdefgh";
// The password is not used for much, and sent unencrypted over the wire, so
// why not even check it in to git
const PASSWORD : &'static str = "ijklmnop";
use config;
#[derive(Debug, Clone, Copy)]
enum State {
@ -30,46 +30,89 @@ impl ServerConnection {
}
}
pub fn make_login_message(&mut self) -> Message {
let mut msg = Message::new(MessageCode::Login);
fn read_request(&mut self) -> Option<ServerRequest> {
match self.state {
State::NotLoggedIn => {
println!("Logging in...");
self.state = State::LoggingIn;
Some(ServerRequest::LoginRequest(LoginRequest::new(
config::USERNAME,
config::PASSWORD,
config::VER_MAJOR,
config::VER_MINOR,
).unwrap()))
},
_ => None
}
}
fn write_response(&mut self, response: ServerResponse) {
match response {
ServerResponse::LoginResponse(login) => {
self.handle_login(login);
},
ServerResponse::UnknownResponse(code, packet) => {
println!("Unknown packet code {}", code);
},
}
}
msg.write_str(USERNAME).unwrap();
msg.write_str(PASSWORD).unwrap();
msg.write_u32(VER_MAJOR).unwrap();
fn handle_login(&mut self, login: LoginResponse) -> io::Result<()> {
match self.state {
State::LoggingIn => {
match login {
LoginResponse::LoginOk { motd, ip, password_md5_opt } => {
self.state = State::LoggedIn;
let userpass = USERNAME.to_string() + PASSWORD;
msg.write_str(&Self::md5_str(&userpass)).unwrap();
println!("Login successful!");
println!("MOTD: \"{}\"", motd);
println!("IP address: {}", ip);
msg.write_u32(VER_MINOR).unwrap();
match password_md5_opt {
Some(password_md5) => {
println!("Password MD5: \"{}\"", password_md5);
println!(concat!(
"Connected to official server ",
"as official client"));
},
None => println!(concat!(
"Connected to official server ",
"as unofficial client")),
}
},
msg
}
LoginResponse::LoginFail { reason } => {
self.state = State::NotLoggedIn;
println!("Login failed!");
println!("Reason: {}", reason);
}
}
Ok(())
},
fn md5_str(string: &str) -> String {
let mut hasher = Md5::new();
hasher.input_str(string);
hasher.result_str()
_ => unimplemented!(),
}
}
}
impl Peer for ServerConnection {
fn read_message(&mut self) -> Option<Message> {
match self.state {
State::NotLoggedIn => {
println!("Logging in...");
self.state = State::LoggingIn;
Some(self.make_login_message())
fn read_packet(&mut self) -> Option<Packet> {
match self.read_request() {
Some(request) => {
match request.to_packet() {
Ok(packet) => Some(packet),
Err(e) => unimplemented!(),
}
},
_ => None
None => None
}
}
fn write_message(&mut self, message: Message) {
println!("write_message: {:?}", message);
match self.state {
State::LoggingIn => (),
_ => ()
fn write_packet(&mut self, mut packet: Packet) {
match ServerResponse::from_packet(packet) {
Ok(response) => self.write_response(response),
Err(e) => unimplemented!(),
}
}
}

Loading…
Cancel
Save