use std::ffi::CStr; use futures::prelude::*; use smol::io; use smol::net::{TcpStream, AsyncToSocketAddrs}; use smol::LocalExecutor; use smol::Task; use rsasl::SASL; use capnp_rpc::{twoparty, RpcSystem, rpc_twoparty_capnp}; mod auth_capnp { include!(concat!(env!("OUT_DIR"), "/schema/auth_capnp.rs")); } mod connection_capnp { include!(concat!(env!("OUT_DIR"), "/schema/connection_capnp.rs")); } mod api_capnp { include!(concat!(env!("OUT_DIR"), "/schema/api_capnp.rs")); } const PLAIN: *const libc::c_char = b"PLAIN" as *const u8 as *const libc::c_char; pub struct Api { stream: TcpStream } impl Api { pub fn new(stream: TcpStream) -> Self { Self { stream } } pub fn connect(addr: A) -> impl Future { let f = async { let mut stream = TcpStream::connect(addr).await.unwrap(); println!("Doing a hecking connect!"); let mut api = Api::new(stream); api.handshake().await.unwrap(); if api.authenticate().await.unwrap() { println!("Authentication successful"); } else { println!("Authentication failed!"); } api }; f } async fn handshake(&mut self) -> Result<(), io::Error> { let host = "localhost"; let program = format!("{}-{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); let version = (0u32,1u32); let mut outer = capnp::message::Builder::new_default(); let mut builder = outer.init_root::().init_greet(); builder.set_host(host); builder.set_major(version.0); builder.set_minor(version.1); builder.set_program(program); capnp_futures::serialize::write_message(&mut self.stream, outer).await.unwrap(); println!("{}", program); Ok(()) } /// Authenticate to the server. Returns true on success, false on error async fn authenticate(&mut self) -> Result { let mut sasl = SASL::new().unwrap(); let plain = std::ffi::CString::new("PLAIN").unwrap(); let mut sess = sasl.client_start(&plain).unwrap(); sess.set_property(rsasl::Property::GSASL_AUTHID, b"testuser"); sess.set_property(rsasl::Property::GSASL_PASSWORD, b"testpass"); if let rsasl::Step::Done(data) = sess.step(&[]).unwrap() { self.send_authentication_request("PLAIN", Some(&data)).await; } else { println!("Sasl said moar data"); } Ok(self.receive_challenge().await?) } fn send_authentication_request(&mut self, mech: &str, init: Option<&[u8]>) -> impl Future { let mut outer = capnp::message::Builder::new_default(); let mut builder = outer.init_root::() .init_auth() .init_request(); builder.set_mechanism(mech); if let Some(data) = init { builder.init_initial_response().set_initial(data); } let stream = self.stream.clone(); capnp_futures::serialize::write_message(stream, outer).map(|r| r.unwrap()) } async fn receive_challenge(&mut self) -> Result { let message = capnp_futures::serialize::read_message(&mut self.stream, capnp::message::ReaderOptions::default()).await.unwrap().unwrap(); let m = message.get_root::().unwrap(); if let Ok(connection_capnp::message::Which::Auth(Ok(r))) = m.which() { if let Ok(auth_capnp::auth_message::Outcome(Ok(r))) = r.which() { if let Ok(auth_capnp::outcome::Result::Successful) = r.get_result() { return Ok(true); } } } return Ok(false); } }