mod.rs 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. mod codec;
  2. mod handshake;
  3. pub use self::codec::APCodec;
  4. pub use self::handshake::handshake;
  5. use futures::{Future, Sink, Stream};
  6. use protobuf::{self, Message};
  7. use std::io;
  8. use std::net::ToSocketAddrs;
  9. use tokio_core::net::TcpStream;
  10. use tokio_core::reactor::Handle;
  11. use tokio_io::codec::Framed;
  12. use authentication::Credentials;
  13. use version;
  14. pub type Transport = Framed<TcpStream, APCodec>;
  15. pub fn connect<A: ToSocketAddrs>(
  16. addr: A,
  17. handle: &Handle,
  18. ) -> Box<Future<Item = Transport, Error = io::Error>> {
  19. let addr = addr.to_socket_addrs().unwrap().next().unwrap();
  20. let socket = TcpStream::connect(&addr, handle);
  21. let connection = socket.and_then(|socket| handshake(socket));
  22. Box::new(connection)
  23. }
  24. pub fn authenticate(
  25. transport: Transport,
  26. credentials: Credentials,
  27. device_id: String,
  28. ) -> Box<Future<Item = (Transport, Credentials), Error = io::Error>> {
  29. use protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os};
  30. let packet = protobuf_init!(ClientResponseEncrypted::new(), {
  31. login_credentials => {
  32. username: credentials.username,
  33. typ: credentials.auth_type,
  34. auth_data: credentials.auth_data,
  35. },
  36. system_info => {
  37. cpu_family: CpuFamily::CPU_UNKNOWN,
  38. os: Os::OS_UNKNOWN,
  39. system_information_string: format!("librespot_{}_{}", version::short_sha(), version::build_id()),
  40. device_id: device_id,
  41. },
  42. version_string: version::version_string(),
  43. });
  44. let cmd = 0xab;
  45. let data = packet.write_to_bytes().unwrap();
  46. Box::new(
  47. transport
  48. .send((cmd, data))
  49. .and_then(|transport| transport.into_future().map_err(|(err, _stream)| err))
  50. .and_then(|(packet, transport)| match packet {
  51. Some((0xac, data)) => {
  52. let welcome_data: APWelcome = protobuf::parse_from_bytes(data.as_ref()).unwrap();
  53. let reusable_credentials = Credentials {
  54. username: welcome_data.get_canonical_username().to_owned(),
  55. auth_type: welcome_data.get_reusable_auth_credentials_type(),
  56. auth_data: welcome_data.get_reusable_auth_credentials().to_owned(),
  57. };
  58. Ok((transport, reusable_credentials))
  59. }
  60. Some((0xad, _)) => panic!("Authentication failed"),
  61. Some((cmd, _)) => panic!("Unexpected packet {:?}", cmd),
  62. None => panic!("EOF"),
  63. }),
  64. )
  65. }