free-librespot/core/src/connection/mod.rs
2020-01-29 09:45:06 +11:00

131 lines
4.3 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_codec::Framed;
use tokio_core::net::TcpStream;
use tokio_core::reactor::Handle;
use url::Url;
use crate::authentication::Credentials;
use crate::version;
use crate::proxytunnel;
pub type Transport = Framed<TcpStream, APCodec>;
pub fn connect(
addr: String,
handle: &Handle,
proxy: &Option<Url>,
) -> Box<dyn Future<Item = Transport, Error = io::Error>> {
let (addr, connect_url) = match *proxy {
Some(ref url) => {
info!("Using proxy \"{}\"", url);
match url.to_socket_addrs().and_then(|mut iter| {
iter.next().ok_or(io::Error::new(
io::ErrorKind::NotFound,
"Can't resolve proxy server address",
))
}) {
Ok(socket_addr) => (socket_addr, Some(addr)),
Err(error) => return Box::new(futures::future::err(error)),
}
}
None => {
match addr.to_socket_addrs().and_then(|mut iter| {
iter.next().ok_or(io::Error::new(
io::ErrorKind::NotFound,
"Can't resolve server address",
))
}) {
Ok(socket_addr) => (socket_addr, None),
Err(error) => return Box::new(futures::future::err(error)),
}
}
};
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<dyn Future<Item = (Transport, Credentials), Error = io::Error>> {
use crate::protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os};
use crate::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"),
}),
)
}