Move AudioKeyManager to tokio

This commit is contained in:
Paul Lietar 2017-01-18 20:39:46 +00:00
parent d27063d5da
commit 855a7e87a7
8 changed files with 164 additions and 83 deletions

View file

@ -17,7 +17,7 @@ pub struct AudioDecrypt<T: io::Read> {
impl<T: io::Read> AudioDecrypt<T> { impl<T: io::Read> AudioDecrypt<T> {
pub fn new(key: AudioKey, reader: T) -> AudioDecrypt<T> { pub fn new(key: AudioKey, reader: T) -> AudioDecrypt<T> {
let cipher = aes::ctr(aes::KeySize::KeySize128, &key, AUDIO_AESIV); let cipher = aes::ctr(aes::KeySize::KeySize128, &key.0, AUDIO_AESIV);
AudioDecrypt { AudioDecrypt {
cipher: cipher, cipher: cipher,
key: key, key: key,
@ -45,7 +45,7 @@ impl<T: io::Read + io::Seek> io::Seek for AudioDecrypt<T> {
let iv = BigUint::from_bytes_be(AUDIO_AESIV) let iv = BigUint::from_bytes_be(AUDIO_AESIV)
.add(BigUint::from_u64(newpos / 16).unwrap()) .add(BigUint::from_u64(newpos / 16).unwrap())
.to_bytes_be(); .to_bytes_be();
self.cipher = aes::ctr(aes::KeySize::KeySize128, &self.key, &iv); self.cipher = aes::ctr(aes::KeySize::KeySize128, &self.key.0, &iv);
let buf = vec![0u8; skip as usize]; let buf = vec![0u8; skip as usize];
let mut buf2 = vec![0u8; skip as usize]; let mut buf2 = vec![0u8; skip as usize];

View file

@ -1,92 +1,68 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use eventual; use futures::sync::oneshot;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::{Cursor, Read, Write}; use std::io::Write;
use util::SeqGenerator;
use util::{SpotifyId, FileId}; use util::{SpotifyId, FileId};
use session::{Session, PacketHandler};
pub type AudioKey = [u8; 16]; #[derive(Debug,Hash,PartialEq,Eq,Copy,Clone)]
pub struct AudioKey(pub [u8; 16]);
#[derive(Debug,Hash,PartialEq,Eq,Copy,Clone)] #[derive(Debug,Hash,PartialEq,Eq,Copy,Clone)]
pub struct AudioKeyError; pub struct AudioKeyError;
#[derive(Debug,Hash,PartialEq,Eq,Copy,Clone)] type Result = ::std::result::Result<AudioKey, AudioKeyError>;
struct AudioKeyId(SpotifyId, FileId);
pub struct AudioKeyManager { component! {
next_seq: u32, AudioKeyManager : AudioKeyManagerInner {
pending: HashMap<u32, AudioKeyId>, sequence: SeqGenerator<u32> = SeqGenerator::new(0),
cache: HashMap<AudioKeyId, Vec<eventual::Complete<AudioKey, AudioKeyError>>>, pending: HashMap<u32, oneshot::Sender<Result>> = HashMap::new(),
}
} }
impl AudioKeyManager { impl AudioKeyManager {
pub fn new() -> AudioKeyManager { pub fn dispatch(&self, cmd: u8, data: Vec<u8>) {
AudioKeyManager { let seq = BigEndian::read_u32(&data[..4]);
next_seq: 1,
pending: HashMap::new(), let sender = self.lock(|inner| inner.pending.remove(&seq));
cache: HashMap::new(),
if let Some(sender) = sender {
match cmd {
0xd => {
let mut key = [0u8; 16];
key.copy_from_slice(&data[4..20]);
sender.complete(Ok(AudioKey(key)));
}
0xe => {
warn!("error audio key {:x} {:x}", data[4], data[5]);
sender.complete(Err(AudioKeyError));
}
_ => (),
}
} }
} }
fn send_key_request(&mut self, session: &Session, track: SpotifyId, file: FileId) -> u32 { pub fn request<'a>(&self, track: SpotifyId, file: FileId) -> oneshot::Receiver<Result> {
let seq = self.next_seq; let (tx, rx) = oneshot::channel();
self.next_seq += 1;
let seq = self.lock(move |inner| {
let seq = inner.sequence.get();
inner.pending.insert(seq, tx);
seq
});
self.send_key_request(seq, track, file);
rx
}
fn send_key_request<'a>(&self, seq: u32, track: SpotifyId, file: FileId) {
let mut data: Vec<u8> = Vec::new(); let mut data: Vec<u8> = Vec::new();
data.write(&file.0).unwrap(); data.write(&file.0).unwrap();
data.write(&track.to_raw()).unwrap(); data.write(&track.to_raw()).unwrap();
data.write_u32::<BigEndian>(seq).unwrap(); data.write_u32::<BigEndian>(seq).unwrap();
data.write_u16::<BigEndian>(0x0000).unwrap(); data.write_u16::<BigEndian>(0x0000).unwrap();
session.send_packet(0xc, data); self.session().send_packet(0xc, data)
seq
}
pub fn request(&mut self,
session: &Session,
track: SpotifyId,
file: FileId)
-> eventual::Future<AudioKey, AudioKeyError> {
let id = AudioKeyId(track, file);
self.cache
.get_mut(&id)
.map(|ref mut requests| {
let (tx, rx) = eventual::Future::pair();
requests.push(tx);
rx
})
.unwrap_or_else(|| {
let seq = self.send_key_request(session, track, file);
self.pending.insert(seq, id.clone());
let (tx, rx) = eventual::Future::pair();
self.cache.insert(id, vec![tx]);
rx
})
}
}
impl PacketHandler for AudioKeyManager {
fn handle(&mut self, cmd: u8, data: Vec<u8>, _session: &Session) {
let mut data = Cursor::new(data);
let seq = data.read_u32::<BigEndian>().unwrap();
if let Some(callbacks) = self.pending.remove(&seq).and_then(|id| self.cache.remove(&id)) {
if cmd == 0xd {
let mut key = [0u8; 16];
data.read_exact(&mut key).unwrap();
for cb in callbacks {
cb.complete(key);
}
} else if cmd == 0xe {
let error = AudioKeyError;
for cb in callbacks {
cb.fail(error);
}
}
}
} }
} }

View file

@ -56,7 +56,7 @@ impl Cache for DefaultCache {
value.and_then(|value| if value.len() == 16 { value.and_then(|value| if value.len() == 16 {
let mut result = [0u8; 16]; let mut result = [0u8; 16];
result.clone_from_slice(&value); result.clone_from_slice(&value);
Some(result) Some(AudioKey(result))
} else { } else {
None None
}) })
@ -73,7 +73,7 @@ impl Cache for DefaultCache {
key.extend_from_slice(&track.to_raw()); key.extend_from_slice(&track.to_raw());
key.extend_from_slice(&file.0); key.extend_from_slice(&file.0);
db.set(&key, &audio_key.as_ref()).unwrap(); db.set(&key, &audio_key.0.as_ref()).unwrap();
} }
xact.commit().unwrap(); xact.commit().unwrap();

29
src/component.rs Normal file
View file

@ -0,0 +1,29 @@
macro_rules! component {
($name:ident : $inner:ident { $($key:ident : $ty:ty = $value:expr,)* }) => {
#[derive(Clone)]
pub struct $name(::std::sync::Arc<($crate::session::SessionWeak, ::std::sync::Mutex<$inner>)>);
impl $name {
#[allow(dead_code)]
pub fn new(session: $crate::session::SessionWeak) -> $name {
$name(::std::sync::Arc::new((session, ::std::sync::Mutex::new($inner {
$($key : $value,)*
}))))
}
#[allow(dead_code)]
fn lock<F: FnOnce(&mut $inner) -> R, R>(&self, f: F) -> R {
let mut inner = (self.0).1.lock().expect("Mutex poisoned");
f(&mut inner)
}
#[allow(dead_code)]
fn session(&self) -> $crate::session::Session {
(self.0).0.upgrade()
}
}
struct $inner {
$($key : $ty,)*
}
}
}

View file

@ -49,6 +49,7 @@ extern crate portaudio;
#[cfg(feature = "libpulse-sys")] #[cfg(feature = "libpulse-sys")]
extern crate libpulse_sys; extern crate libpulse_sys;
#[macro_use] mod component;
pub mod album_cover; pub mod album_cover;
pub mod audio_backend; pub mod audio_backend;
pub mod audio_decrypt; pub mod audio_decrypt;

View file

@ -4,6 +4,7 @@ use std::sync::{mpsc, Mutex, Arc, MutexGuard};
use std::thread; use std::thread;
use std::io::{Read, Seek}; use std::io::{Read, Seek};
use vorbis; use vorbis;
use futures::Future;
use audio_decrypt::AudioDecrypt; use audio_decrypt::AudioDecrypt;
use audio_backend::Sink; use audio_backend::Sink;
@ -206,7 +207,7 @@ fn load_track(session: &Session, track_id: SpotifyId) -> Option<vorbis::Decoder<
} }
}; };
let key = session.audio_key(track.id, file_id).await().unwrap(); let key = session.audio_key().request(track.id, file_id).wait().unwrap().unwrap();
let audio_file = Subfile::new(AudioDecrypt::new(key, session.audio_file(file_id)), 0xa7); let audio_file = Subfile::new(AudioDecrypt::new(key, session.audio_file(file_id)), 0xa7);
let decoder = vorbis::Decoder::new(audio_file).unwrap(); let decoder = vorbis::Decoder::new(audio_file).unwrap();

View file

@ -5,23 +5,23 @@ use eventual::Future;
use eventual::Async; use eventual::Async;
use std::io::{self, Read, Cursor}; use std::io::{self, Read, Cursor};
use std::result::Result; use std::result::Result;
use std::sync::{Mutex, RwLock, Arc, mpsc}; use std::sync::{Mutex, RwLock, Arc, mpsc, Weak};
use std::str::FromStr; use std::str::FromStr;
use futures::Future as Future_; use futures::Future as Future_;
use futures::Stream; use futures::{Stream, BoxFuture};
use tokio_core::reactor::Handle; use tokio_core::reactor::Handle;
use album_cover::AlbumCover; use album_cover::AlbumCover;
use apresolve::apresolve_or_fallback; use apresolve::apresolve_or_fallback;
use audio_file::AudioFile; use audio_file::AudioFile;
use audio_key::{AudioKeyManager, AudioKey, AudioKeyError}; use audio_key::AudioKeyManager;
use authentication::Credentials; use authentication::Credentials;
use cache::Cache; use cache::Cache;
use connection::{self, adaptor}; use connection::{self, adaptor};
use mercury::{MercuryManager, MercuryRequest, MercuryResponse}; use mercury::{MercuryManager, MercuryRequest, MercuryResponse};
use metadata::{MetadataManager, MetadataRef, MetadataTrait}; use metadata::{MetadataManager, MetadataRef, MetadataTrait};
use stream::StreamManager; use stream::StreamManager;
use util::{SpotifyId, FileId, ReadSeek}; use util::{SpotifyId, FileId, ReadSeek, Lazy};
use stream; use stream;
@ -65,14 +65,18 @@ pub struct SessionInternal {
mercury: Mutex<MercuryManager>, mercury: Mutex<MercuryManager>,
metadata: Mutex<MetadataManager>, metadata: Mutex<MetadataManager>,
stream: Mutex<StreamManager>, stream: Mutex<StreamManager>,
audio_key: Mutex<AudioKeyManager>,
rx_connection: Mutex<adaptor::StreamAdaptor<(u8, Vec<u8>), io::Error>>, rx_connection: Mutex<adaptor::StreamAdaptor<(u8, Vec<u8>), io::Error>>,
tx_connection: Mutex<adaptor::SinkAdaptor<(u8, Vec<u8>)>>, tx_connection: Mutex<adaptor::SinkAdaptor<(u8, Vec<u8>)>>,
audio_key: Lazy<AudioKeyManager>,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Session(pub Arc<SessionInternal>); pub struct Session(pub Arc<SessionInternal>);
#[derive(Clone)]
pub struct SessionWeak(pub Weak<SessionInternal>);
pub fn device_id(name: &str) -> String { pub fn device_id(name: &str) -> String {
let mut h = Sha1::new(); let mut h = Sha1::new();
h.input_str(&name); h.input_str(&name);
@ -82,7 +86,7 @@ pub fn device_id(name: &str) -> String {
impl Session { impl Session {
pub fn connect(config: Config, credentials: Credentials, pub fn connect(config: Config, credentials: Credentials,
cache: Box<Cache + Send + Sync>, handle: Handle) cache: Box<Cache + Send + Sync>, handle: Handle)
-> Box<Future_<Item=(Session, Box<Future_<Item=(), Error=io::Error>>), Error=io::Error>> -> Box<Future_<Item=(Session, BoxFuture<(), io::Error>), Error=io::Error>>
{ {
let access_point = apresolve_or_fallback::<io::Error>(&handle); let access_point = apresolve_or_fallback::<io::Error>(&handle);
@ -108,7 +112,8 @@ impl Session {
} }
fn create(transport: connection::Transport, config: Config, fn create(transport: connection::Transport, config: Config,
cache: Box<Cache + Send + Sync>, username: String) -> (Session, Box<Future_<Item=(), Error=io::Error>>) cache: Box<Cache + Send + Sync>, username: String)
-> (Session, BoxFuture<(), io::Error>)
{ {
let transport = transport.map(|(cmd, data)| (cmd, data.as_ref().to_owned())); let transport = transport.map(|(cmd, data)| (cmd, data.as_ref().to_owned()));
let (tx, rx, task) = adaptor::adapt(transport); let (tx, rx, task) = adaptor::adapt(transport);
@ -127,12 +132,16 @@ impl Session {
mercury: Mutex::new(MercuryManager::new()), mercury: Mutex::new(MercuryManager::new()),
metadata: Mutex::new(MetadataManager::new()), metadata: Mutex::new(MetadataManager::new()),
stream: Mutex::new(StreamManager::new()), stream: Mutex::new(StreamManager::new()),
audio_key: Mutex::new(AudioKeyManager::new()),
audio_key: Lazy::new(),
})); }));
(session, task) (session, task)
} }
pub fn audio_key(&self) -> &AudioKeyManager {
self.0.audio_key.get(|| AudioKeyManager::new(self.weak()))
}
pub fn poll(&self) { pub fn poll(&self) {
let (cmd, data) = self.recv(); let (cmd, data) = self.recv();
@ -141,7 +150,7 @@ impl Session {
0x4 => self.send_packet(0x49, data), 0x4 => self.send_packet(0x49, data),
0x4a => (), 0x4a => (),
0x9 | 0xa => self.0.stream.lock().unwrap().handle(cmd, data, self), 0x9 | 0xa => self.0.stream.lock().unwrap().handle(cmd, data, self),
0xd | 0xe => self.0.audio_key.lock().unwrap().handle(cmd, data, self), 0xd | 0xe => self.audio_key().dispatch(cmd, data),
0x1b => { 0x1b => {
self.0.data.write().unwrap().country = String::from_utf8(data).unwrap(); self.0.data.write().unwrap().country = String::from_utf8(data).unwrap();
} }
@ -158,6 +167,7 @@ impl Session {
self.0.tx_connection.lock().unwrap().send((cmd, data)) self.0.tx_connection.lock().unwrap().send((cmd, data))
} }
/*
pub fn audio_key(&self, track: SpotifyId, file_id: FileId) -> Future<AudioKey, AudioKeyError> { pub fn audio_key(&self, track: SpotifyId, file_id: FileId) -> Future<AudioKey, AudioKeyError> {
self.0.cache self.0.cache
.get_audio_key(track, file_id) .get_audio_key(track, file_id)
@ -172,6 +182,7 @@ impl Session {
}) })
}) })
} }
*/
pub fn audio_file(&self, file_id: FileId) -> Box<ReadSeek> { pub fn audio_file(&self, file_id: FileId) -> Box<ReadSeek> {
self.0.cache self.0.cache
@ -241,6 +252,16 @@ impl Session {
pub fn device_id(&self) -> &str { pub fn device_id(&self) -> &str {
&self.config().device_id &self.config().device_id
} }
pub fn weak(&self) -> SessionWeak {
SessionWeak(Arc::downgrade(&self.0))
}
}
impl SessionWeak {
pub fn upgrade(&self) -> Session {
Session(self.0.upgrade().expect("Session died"))
}
} }
pub trait PacketHandler { pub trait PacketHandler {

View file

@ -1,6 +1,7 @@
use num::{BigUint, Integer, Zero, One}; use num::{BigUint, Integer, Zero, One};
use rand::{Rng, Rand}; use rand::{Rng, Rand};
use std::io; use std::io;
use std::mem;
use std::ops::{Mul, Rem, Shr}; use std::ops::{Mul, Rem, Shr};
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
@ -107,3 +108,55 @@ impl<'s> Iterator for StrChunks<'s> {
pub trait ReadSeek : ::std::io::Read + ::std::io::Seek { } pub trait ReadSeek : ::std::io::Read + ::std::io::Seek { }
impl <T: ::std::io::Read + ::std::io::Seek> ReadSeek for T { } impl <T: ::std::io::Read + ::std::io::Seek> ReadSeek for T { }
pub trait Seq {
fn next(&self) -> Self;
}
macro_rules! impl_seq {
($($ty:ty)*) => { $(
impl Seq for $ty {
fn next(&self) -> Self { *self + 1 }
}
)* }
}
impl_seq!(u8 u16 u32 u64 usize);
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct SeqGenerator<T: Seq>(T);
impl <T: Seq> SeqGenerator<T> {
pub fn new(value: T) -> Self {
SeqGenerator(value)
}
pub fn get(&mut self) -> T {
let value = self.0.next();
mem::replace(&mut self.0, value)
}
}
use std::sync::Mutex;
use std::cell::UnsafeCell;
pub struct Lazy<T>(Mutex<bool>, UnsafeCell<Option<T>>);
unsafe impl <T: Sync> Sync for Lazy<T> {}
unsafe impl <T: Send> Send for Lazy<T> {}
impl <T> Lazy<T> {
pub fn new() -> Lazy<T> {
Lazy(Mutex::new(false), UnsafeCell::new(None))
}
pub fn get<F: FnOnce() -> T>(&self, f: F) -> &T {
let mut inner = self.0.lock().unwrap();
if !*inner {
unsafe {
*self.1.get() = Some(f());
}
*inner = true;
}
unsafe { &*self.1.get() }.as_ref().unwrap()
}
}