mirror of
https://gitlab.com/fabinfra/fabaccess/sute.git
synced 2025-03-12 14:41:52 +01:00
120 lines
3.8 KiB
Rust
120 lines
3.8 KiB
Rust
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<A: AsyncToSocketAddrs>(addr: A) -> impl Future<Output=Api> {
|
|
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::<connection_capnp::message::Builder>().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<bool, io::Error> {
|
|
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<Output=()> {
|
|
let mut outer = capnp::message::Builder::new_default();
|
|
let mut builder = outer.init_root::<connection_capnp::message::Builder>()
|
|
.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<bool, io::Error> {
|
|
let message = capnp_futures::serialize::read_message(&mut self.stream, capnp::message::ReaderOptions::default()).await.unwrap().unwrap();
|
|
let m = message.get_root::<connection_capnp::message::Reader>().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);
|
|
}
|
|
}
|