mirror of
https://github.com/librespot-org/librespot.git
synced 2025-10-04 02:09:26 +02:00
Migrate core to tokio 1.0
This commit is contained in:
parent
efabb03631
commit
40e6355c34
16 changed files with 406 additions and 661 deletions
|
@ -1,14 +1,11 @@
|
|||
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
|
||||
use futures::{Async, Future, Poll};
|
||||
use hmac::{Hmac, Mac};
|
||||
use protobuf::{self, Message};
|
||||
use rand::thread_rng;
|
||||
use sha1::Sha1;
|
||||
use std::io::{self, Read};
|
||||
use std::marker::PhantomData;
|
||||
use tokio_codec::{Decoder, Framed};
|
||||
use tokio_io::io::{read_exact, write_all, ReadExact, Window, WriteAll};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use std::io;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
use tokio_util::codec::{Decoder, Framed};
|
||||
|
||||
use super::codec::APCodec;
|
||||
use crate::diffie_hellman::DHLocalKeys;
|
||||
|
@ -16,72 +13,33 @@ use crate::protocol;
|
|||
use crate::protocol::keyexchange::{APResponseMessage, ClientHello, ClientResponsePlaintext};
|
||||
use crate::util;
|
||||
|
||||
pub struct Handshake<T> {
|
||||
keys: DHLocalKeys,
|
||||
state: HandshakeState<T>,
|
||||
}
|
||||
|
||||
enum HandshakeState<T> {
|
||||
ClientHello(WriteAll<T, Vec<u8>>),
|
||||
APResponse(RecvPacket<T, APResponseMessage>),
|
||||
ClientResponse(Option<APCodec>, WriteAll<T, Vec<u8>>),
|
||||
}
|
||||
|
||||
pub fn handshake<T: AsyncRead + AsyncWrite>(connection: T) -> Handshake<T> {
|
||||
pub async fn handshake<T: AsyncRead + AsyncWrite + Unpin>(
|
||||
mut connection: T,
|
||||
) -> io::Result<Framed<T, APCodec>> {
|
||||
let local_keys = DHLocalKeys::random(&mut thread_rng());
|
||||
let client_hello = client_hello(connection, local_keys.public_key());
|
||||
let gc = local_keys.public_key();
|
||||
let mut accumulator = client_hello(&mut connection, gc).await?;
|
||||
let message: APResponseMessage = recv_packet(&mut connection, &mut accumulator).await?;
|
||||
let remote_key = message
|
||||
.get_challenge()
|
||||
.get_login_crypto_challenge()
|
||||
.get_diffie_hellman()
|
||||
.get_gs()
|
||||
.to_owned();
|
||||
|
||||
Handshake {
|
||||
keys: local_keys,
|
||||
state: HandshakeState::ClientHello(client_hello),
|
||||
}
|
||||
let shared_secret = local_keys.shared_secret(&remote_key);
|
||||
let (challenge, send_key, recv_key) = compute_keys(&shared_secret, &accumulator);
|
||||
let codec = APCodec::new(&send_key, &recv_key);
|
||||
|
||||
client_response(&mut connection, challenge).await?;
|
||||
|
||||
Ok(codec.framed(connection))
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite> Future for Handshake<T> {
|
||||
type Item = Framed<T, APCodec>;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, io::Error> {
|
||||
use self::HandshakeState::*;
|
||||
loop {
|
||||
self.state = match self.state {
|
||||
ClientHello(ref mut write) => {
|
||||
let (connection, accumulator) = try_ready!(write.poll());
|
||||
|
||||
let read = recv_packet(connection, accumulator);
|
||||
APResponse(read)
|
||||
}
|
||||
|
||||
APResponse(ref mut read) => {
|
||||
let (connection, message, accumulator) = try_ready!(read.poll());
|
||||
let remote_key = message
|
||||
.get_challenge()
|
||||
.get_login_crypto_challenge()
|
||||
.get_diffie_hellman()
|
||||
.get_gs()
|
||||
.to_owned();
|
||||
|
||||
let shared_secret = self.keys.shared_secret(&remote_key);
|
||||
let (challenge, send_key, recv_key) =
|
||||
compute_keys(&shared_secret, &accumulator);
|
||||
let codec = APCodec::new(&send_key, &recv_key);
|
||||
|
||||
let write = client_response(connection, challenge);
|
||||
ClientResponse(Some(codec), write)
|
||||
}
|
||||
|
||||
ClientResponse(ref mut codec, ref mut write) => {
|
||||
let (connection, _) = try_ready!(write.poll());
|
||||
let codec = codec.take().unwrap();
|
||||
let framed = codec.framed(connection);
|
||||
return Ok(Async::Ready(framed));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn client_hello<T: AsyncWrite>(connection: T, gc: Vec<u8>) -> WriteAll<T, Vec<u8>> {
|
||||
async fn client_hello<T>(connection: &mut T, gc: Vec<u8>) -> io::Result<Vec<u8>>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
{
|
||||
let mut packet = ClientHello::new();
|
||||
packet
|
||||
.mut_build_info()
|
||||
|
@ -106,13 +64,17 @@ fn client_hello<T: AsyncWrite>(connection: T, gc: Vec<u8>) -> WriteAll<T, Vec<u8
|
|||
|
||||
let mut buffer = vec![0, 4];
|
||||
let size = 2 + 4 + packet.compute_size();
|
||||
buffer.write_u32::<BigEndian>(size).unwrap();
|
||||
<Vec<u8> as WriteBytesExt>::write_u32::<BigEndian>(&mut buffer, size).unwrap();
|
||||
packet.write_to_vec(&mut buffer).unwrap();
|
||||
|
||||
write_all(connection, buffer)
|
||||
connection.write_all(&buffer[..]).await?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
fn client_response<T: AsyncWrite>(connection: T, challenge: Vec<u8>) -> WriteAll<T, Vec<u8>> {
|
||||
async fn client_response<T>(connection: &mut T, challenge: Vec<u8>) -> io::Result<()>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
{
|
||||
let mut packet = ClientResponsePlaintext::new();
|
||||
packet
|
||||
.mut_login_crypto_response()
|
||||
|
@ -123,70 +85,35 @@ fn client_response<T: AsyncWrite>(connection: T, challenge: Vec<u8>) -> WriteAll
|
|||
|
||||
let mut buffer = vec![];
|
||||
let size = 4 + packet.compute_size();
|
||||
buffer.write_u32::<BigEndian>(size).unwrap();
|
||||
<Vec<u8> as WriteBytesExt>::write_u32::<BigEndian>(&mut buffer, size).unwrap();
|
||||
packet.write_to_vec(&mut buffer).unwrap();
|
||||
|
||||
write_all(connection, buffer)
|
||||
connection.write_all(&buffer[..]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
enum RecvPacket<T, M: Message> {
|
||||
Header(ReadExact<T, Window<Vec<u8>>>, PhantomData<M>),
|
||||
Body(ReadExact<T, Window<Vec<u8>>>, PhantomData<M>),
|
||||
}
|
||||
|
||||
fn recv_packet<T: AsyncRead, M>(connection: T, acc: Vec<u8>) -> RecvPacket<T, M>
|
||||
async fn recv_packet<T, M>(connection: &mut T, acc: &mut Vec<u8>) -> io::Result<M>
|
||||
where
|
||||
T: Read,
|
||||
T: AsyncRead + Unpin,
|
||||
M: Message,
|
||||
{
|
||||
RecvPacket::Header(read_into_accumulator(connection, 4, acc), PhantomData)
|
||||
let header = read_into_accumulator(connection, 4, acc).await?;
|
||||
let size = BigEndian::read_u32(header) as usize;
|
||||
let data = read_into_accumulator(connection, size - 4, acc).await?;
|
||||
let message = protobuf::parse_from_bytes(data).unwrap();
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
impl<T: AsyncRead, M> Future for RecvPacket<T, M>
|
||||
where
|
||||
T: Read,
|
||||
M: Message,
|
||||
{
|
||||
type Item = (T, M, Vec<u8>);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, io::Error> {
|
||||
use self::RecvPacket::*;
|
||||
loop {
|
||||
*self = match *self {
|
||||
Header(ref mut read, _) => {
|
||||
let (connection, header) = try_ready!(read.poll());
|
||||
let size = BigEndian::read_u32(header.as_ref()) as usize;
|
||||
|
||||
let acc = header.into_inner();
|
||||
let read = read_into_accumulator(connection, size - 4, acc);
|
||||
RecvPacket::Body(read, PhantomData)
|
||||
}
|
||||
|
||||
Body(ref mut read, _) => {
|
||||
let (connection, data) = try_ready!(read.poll());
|
||||
let message = protobuf::parse_from_bytes(data.as_ref()).unwrap();
|
||||
|
||||
let acc = data.into_inner();
|
||||
return Ok(Async::Ready((connection, message, acc)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_into_accumulator<T: AsyncRead>(
|
||||
connection: T,
|
||||
async fn read_into_accumulator<'a, T: AsyncRead + Unpin>(
|
||||
connection: &mut T,
|
||||
size: usize,
|
||||
mut acc: Vec<u8>,
|
||||
) -> ReadExact<T, Window<Vec<u8>>> {
|
||||
acc: &'a mut Vec<u8>,
|
||||
) -> io::Result<&'a mut [u8]> {
|
||||
let offset = acc.len();
|
||||
acc.resize(offset + size, 0);
|
||||
|
||||
let mut window = Window::new(acc);
|
||||
window.set_start(offset);
|
||||
|
||||
read_exact(connection, window)
|
||||
connection.read_exact(&mut acc[offset..]).await?;
|
||||
Ok(&mut acc[offset..])
|
||||
}
|
||||
|
||||
fn compute_keys(shared_secret: &[u8], packets: &[u8]) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue