Browse Source

Start parsing messages with generic reusable code. Login works.

wip
Titouan Rigoudy 9 years ago
parent
commit
9e0923ddb9
6 changed files with 328 additions and 74 deletions
  1. +46
    -0
      Cargo.lock
  2. +4
    -3
      Cargo.toml
  3. +39
    -11
      src/main.rs
  4. +0
    -35
      src/message.rs
  5. +188
    -0
      src/proto.rs
  6. +51
    -25
      src/server.rs

+ 46
- 0
Cargo.lock View File

@ -5,6 +5,16 @@ dependencies = [
"byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-crypto 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "advapi32-sys"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -27,6 +37,15 @@ name = "cfg-if"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "gcc"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "kernel32-sys" name = "kernel32-sys"
version = "0.2.1" version = "0.2.1"
@ -103,6 +122,33 @@ dependencies = [
"libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "rand"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rust-crypto"
version = "0.2.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gcc 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-serialize"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.1.3" version = "0.1.3"


+ 4
- 3
Cargo.toml View File

@ -4,6 +4,7 @@ version = "0.1.0"
authors = ["letitz"] authors = ["letitz"]
[dependencies] [dependencies]
mio = "0.5"
log = "0.3.5"
byteorder = "0.4.2"
mio = "^0.5"
log = "^0.3.5"
byteorder = "^0.4.2"
rust-crypto = "^0.2.34"

+ 39
- 11
src/main.rs View File

@ -1,12 +1,18 @@
mod server; mod server;
mod message;
mod proto;
#[macro_use] extern crate log; #[macro_use] extern crate log;
extern crate mio; extern crate mio;
extern crate byteorder; extern crate byteorder;
extern crate crypto;
use std::io;
use std::net::ToSocketAddrs;
use mio::{EventLoop, EventSet, Handler, PollOpt, Token}; use mio::{EventLoop, EventSet, Handler, PollOpt, Token};
use mio::tcp::TcpStream;
use proto::Connection;
use server::ServerConnection; use server::ServerConnection;
const SERVER_HOST : &'static str = "server.slsknet.org"; const SERVER_HOST : &'static str = "server.slsknet.org";
@ -16,12 +22,17 @@ const SERVER_TOKEN : Token = Token(0);
#[derive(Debug)] #[derive(Debug)]
struct ConnectionHandler { struct ConnectionHandler {
server: ServerConnection,
server_conn: Connection<ServerConnection>,
server_stream: TcpStream,
} }
impl ConnectionHandler { impl ConnectionHandler {
fn new(server: ServerConnection) -> Self {
ConnectionHandler{ server: server }
fn new(server_conn: Connection<ServerConnection>, server_stream: TcpStream)
-> Self {
ConnectionHandler{
server_conn: server_conn,
server_stream: server_stream,
}
} }
} }
@ -33,25 +44,42 @@ impl Handler for ConnectionHandler {
token: Token, event_set: EventSet) { token: Token, event_set: EventSet) {
match token { match token {
SERVER_TOKEN => self.server.ready_to_read(),
SERVER_TOKEN =>
if event_set.is_readable() {
self.server_conn.ready_to_read(&mut self.server_stream)
} else {
self.server_conn.ready_to_write(&mut self.server_stream)
},
_ => unreachable!("Unknown token"), _ => unreachable!("Unknown token"),
} }
} }
} }
fn main() {
let server = ServerConnection::new(SERVER_HOST, SERVER_PORT).unwrap();
fn connect(hostname: &str, port: u16) -> io::Result<TcpStream> {
for sock_addr in try!((hostname, port).to_socket_addrs()) {
if let Ok(stream) = TcpStream::connect(&sock_addr) {
return Ok(stream)
}
}
Err(io::Error::new(io::ErrorKind::Other,
format!("Cannot connect to {}:{}", hostname, port)))
}
println!("Connected to {:?}", &server);
fn main() {
let stream = connect(SERVER_HOST, SERVER_PORT).unwrap();
println!("Connected to {:?}", &stream);
let mut event_loop = EventLoop::new().unwrap(); let mut event_loop = EventLoop::new().unwrap();
event_loop.register( event_loop.register(
server.stream(),
&stream,
SERVER_TOKEN, SERVER_TOKEN,
EventSet::readable(),
EventSet::readable() | EventSet::writable(),
PollOpt::edge()).unwrap(); PollOpt::edge()).unwrap();
event_loop.run(&mut ConnectionHandler::new(server)).unwrap();
let server_conn = Connection::new(ServerConnection::new());
let mut handler = ConnectionHandler::new(server_conn, stream);
event_loop.run(&mut handler).unwrap();
} }

+ 0
- 35
src/message.rs View File

@ -1,35 +0,0 @@
use std::io;
use std::io::Write;
use byteorder::{LittleEndian, WriteBytesExt};
const MAX_MESSAGE_SIZE : usize = 2048;
pub struct Message {
bytes: Vec<u8>,
}
impl Message {
pub fn new(msg_code: u32) -> Message {
let mut bytes = Vec::with_capacity(MAX_MESSAGE_SIZE);
bytes.write_u32::<LittleEndian>(msg_code);
Message{ bytes: bytes }
}
pub fn write_str(&mut self, string: &str) -> io::Result<usize> {
try!(self.write_u32::<LittleEndian>(string.len() as u32));
let n = try!(self.write(string.as_bytes()));
Ok(n + 4)
}
}
impl io::Write for Message {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.bytes.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.bytes.flush()
}
}

+ 188
- 0
src/proto.rs View File

@ -0,0 +1,188 @@
use std::iter::repeat;
use std::io;
use std::io::{Cursor, Read, Write};
use std::mem;
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt};
use mio::tcp::TcpStream;
const MAX_PACKET_SIZE: usize = 1 << 20; // 1 MiB
const U32_SIZE: usize = 4;
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,
bytes: Vec<u8>,
}
impl Message {
pub fn new(code: MessageCode) -> Message {
let mut bytes = Vec::new();
bytes.write_u32::<LittleEndian>(0).unwrap();
bytes.write_u32::<LittleEndian>(code.to_u32()).unwrap();
Message {
code: code,
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),
bytes: bytes,
}
}
pub fn code(&self) -> MessageCode {
self.code
}
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()));
Ok(n + U32_SIZE)
}
pub fn write_u32(&mut self, n: u32) -> io::Result<usize> {
match self.bytes.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])
}
pub fn finalize(mut self) -> Vec<u8> {
let bytes_len = (self.bytes.len() - U32_SIZE) as u32;
{
let mut first_word = &mut self.bytes[..4];
first_word.write_u32::<LittleEndian>(bytes_len).unwrap();
}
self.bytes
}
}
impl io::Write for Message {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.bytes.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.bytes.flush()
}
}
/*======*
* PEER *
*======*/
pub trait Peer {
fn read_message(&mut self) -> Option<Message>;
fn write_message(&mut self, message: Message);
}
#[derive(Debug, Clone, Copy)]
enum State {
ReadingLength,
ReadingMessage,
}
#[derive(Debug)]
pub struct Connection<T: Peer> {
state: State,
num_bytes_left: usize,
buffer: Vec<u8>,
peer: T,
}
impl<T: Peer> Connection<T> {
pub fn new(peer: T) -> Self {
Connection {
state: State::ReadingLength,
num_bytes_left: U32_SIZE,
buffer: vec![0; U32_SIZE],
peer: peer,
}
}
pub fn ready_to_read(&mut self, stream: &mut TcpStream) {
let offset = self.buffer.len() - self.num_bytes_left;
match stream.read(&mut self.buffer[offset..]) {
Ok(num_bytes_read) => {
assert!(num_bytes_read <= self.num_bytes_left);
self.num_bytes_left -= num_bytes_read;
},
Err(e) => error!("Could not read stream: {:?}", e),
}
if self.num_bytes_left > 0 {
return;
}
match self.state {
State::ReadingLength => {
let message_len =
LittleEndian::read_u32(&mut self.buffer) as usize;
if message_len > MAX_MESSAGE_SIZE {
unimplemented!();
};
self.state = State::ReadingMessage;
self.num_bytes_left = message_len;
self.buffer.extend(repeat(0).take(message_len));
},
State::ReadingMessage => {
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));
}
}
}
pub fn ready_to_write(&mut self, stream: &mut TcpStream) {
match self.peer.read_message() {
Some(message) => {
stream.write(&message.finalize()).unwrap();
()
},
None => (),
}
}
}

+ 51
- 25
src/server.rs View File

@ -1,12 +1,18 @@
use std::io;
use std::net::ToSocketAddrs;
use crypto::md5::Md5;
use crypto::digest::Digest;
use mio::tcp::TcpStream;
use proto::{Message, MessageCode, Peer};
use message::Message;
const VER_MAJOR : u32 = 181;
const VER_MINOR : u32 = 100;
#[derive(Debug)]
enum ServerState {
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";
#[derive(Debug, Clone, Copy)]
enum State {
NotLoggedIn, NotLoggedIn,
LoggingIn, LoggingIn,
LoggedIn, LoggedIn,
@ -14,36 +20,56 @@ enum ServerState {
#[derive(Debug)] #[derive(Debug)]
pub struct ServerConnection { pub struct ServerConnection {
stream: TcpStream,
state: ServerState,
state: State,
} }
impl ServerConnection { impl ServerConnection {
pub fn new(hostname: &str, port: u16) -> io::Result<Self> {
for sock_addr in try!((hostname, port).to_socket_addrs()) {
if let Ok(stream) = TcpStream::connect(&sock_addr) {
return Ok(ServerConnection {
stream: stream,
state: ServerState::NotLoggedIn,
})
}
pub fn new() -> Self {
ServerConnection {
state: State::NotLoggedIn,
} }
Err(io::Error::new(io::ErrorKind::Other,
format!("Cannot connect to {}:{}", hostname, port)))
} }
pub fn stream(&self) -> &TcpStream {
&self.stream
pub fn make_login_message(&mut self) -> Message {
let mut msg = Message::new(MessageCode::Login);
msg.write_str(USERNAME).unwrap();
msg.write_str(PASSWORD).unwrap();
msg.write_u32(VER_MAJOR).unwrap();
let userpass = USERNAME.to_string() + PASSWORD;
msg.write_str(&Self::md5_str(&userpass)).unwrap();
msg.write_u32(VER_MINOR).unwrap();
msg
} }
pub fn ready_to_read(&mut self) {
fn md5_str(string: &str) -> String {
let mut hasher = Md5::new();
hasher.input_str(string);
hasher.result_str()
}
}
impl Peer for ServerConnection {
fn read_message(&mut self) -> Option<Message> {
match self.state { match self.state {
_ => ()
State::NotLoggedIn => {
println!("Logging in...");
self.state = State::LoggingIn;
Some(self.make_login_message())
},
_ => None
} }
} }
pub fn login(&mut self) {
let msg = Message::new(1);
fn write_message(&mut self, message: Message) {
println!("write_message: {:?}", message);
match self.state {
State::LoggingIn => (),
_ => ()
}
} }
} }

Loading…
Cancel
Save