mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-28 17:21:52 +00:00
3a14e9a6be
Use the url crate to handle proxies to make sure they conform to a proper format.
103 lines
3.4 KiB
Rust
103 lines
3.4 KiB
Rust
mod codec;
|
|
mod handshake;
|
|
|
|
pub use self::codec::APCodec;
|
|
pub use self::handshake::handshake;
|
|
|
|
use futures::{Future, Sink, Stream};
|
|
use protobuf::{self, Message};
|
|
use std::io;
|
|
use std::net::ToSocketAddrs;
|
|
use tokio_core::net::TcpStream;
|
|
use tokio_core::reactor::Handle;
|
|
use tokio_io::codec::Framed;
|
|
use url::Url;
|
|
|
|
use authentication::Credentials;
|
|
use version;
|
|
|
|
use proxytunnel;
|
|
|
|
pub type Transport = Framed<TcpStream, APCodec>;
|
|
|
|
pub fn connect(
|
|
addr: String,
|
|
handle: &Handle,
|
|
proxy: &Option<Url>,
|
|
) -> Box<Future<Item = Transport, Error = io::Error>> {
|
|
let (addr, connect_url) = match *proxy {
|
|
Some(ref url) => {
|
|
info!("Using proxy \"{}\"", url);
|
|
(url.to_socket_addrs().unwrap().next().unwrap(), Some(addr))
|
|
}
|
|
None => (addr.to_socket_addrs().unwrap().next().unwrap(), None),
|
|
};
|
|
|
|
let socket = TcpStream::connect(&addr, handle);
|
|
if let Some(connect_url) = connect_url {
|
|
let connection = socket
|
|
.and_then(move |socket| proxytunnel::connect(socket, &connect_url).and_then(handshake));
|
|
Box::new(connection)
|
|
} else {
|
|
let connection = socket.and_then(handshake);
|
|
Box::new(connection)
|
|
}
|
|
}
|
|
|
|
pub fn authenticate(
|
|
transport: Transport,
|
|
credentials: Credentials,
|
|
device_id: String,
|
|
) -> Box<Future<Item = (Transport, Credentials), Error = io::Error>> {
|
|
use protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os};
|
|
use protocol::keyexchange::APLoginFailed;
|
|
|
|
let mut packet = ClientResponseEncrypted::new();
|
|
packet.mut_login_credentials().set_username(credentials.username);
|
|
packet.mut_login_credentials().set_typ(credentials.auth_type);
|
|
packet
|
|
.mut_login_credentials()
|
|
.set_auth_data(credentials.auth_data);
|
|
packet.mut_system_info().set_cpu_family(CpuFamily::CPU_UNKNOWN);
|
|
packet.mut_system_info().set_os(Os::OS_UNKNOWN);
|
|
packet.mut_system_info().set_system_information_string(format!(
|
|
"librespot_{}_{}",
|
|
version::short_sha(),
|
|
version::build_id()
|
|
));
|
|
packet.mut_system_info().set_device_id(device_id);
|
|
packet.set_version_string(version::version_string());
|
|
|
|
let cmd = 0xab;
|
|
let data = packet.write_to_bytes().unwrap();
|
|
|
|
Box::new(
|
|
transport
|
|
.send((cmd, data))
|
|
.and_then(|transport| transport.into_future().map_err(|(err, _stream)| err))
|
|
.and_then(|(packet, transport)| match packet {
|
|
Some((0xac, data)) => {
|
|
let welcome_data: APWelcome = protobuf::parse_from_bytes(data.as_ref()).unwrap();
|
|
|
|
let reusable_credentials = Credentials {
|
|
username: welcome_data.get_canonical_username().to_owned(),
|
|
auth_type: welcome_data.get_reusable_auth_credentials_type(),
|
|
auth_data: welcome_data.get_reusable_auth_credentials().to_owned(),
|
|
};
|
|
|
|
Ok((transport, reusable_credentials))
|
|
}
|
|
|
|
Some((0xad, data)) => {
|
|
let error_data: APLoginFailed = protobuf::parse_from_bytes(data.as_ref()).unwrap();
|
|
panic!(
|
|
"Authentication failed with reason: {:?}",
|
|
error_data.get_error_code()
|
|
)
|
|
}
|
|
|
|
Some((cmd, _)) => panic!("Unexpected packet {:?}", cmd),
|
|
None => panic!("EOF"),
|
|
}),
|
|
)
|
|
}
|