Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 37 additions & 11 deletions examples/connect_network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,7 @@ async fn main() {

let session = iwdrs::session::Session::new().await.unwrap();

let agent = Agent {
request_passphrase_fn: Box::new(move || {
let password = password.clone();
Box::pin(async {
match password {
Some(password) => Ok(password.clone()),
None => Err("No Password Provided".into()),
}
})
}),
};
let agent = PasswdAgent(password);
let _agent_manager = session.register_agent(agent).await.unwrap();

let station = session.stations().pop().unwrap();
Expand Down Expand Up @@ -57,3 +47,39 @@ async fn find_network<'a>(ssid: &str, networks: &'a [(Network, i16)]) -> Option<
}
None
}

struct PasswdAgent(Option<String>);

impl Agent for PasswdAgent {
fn request_passphrase(
&self,
_network: &Network,
) -> impl Future<Output = Result<String, iwdrs::error::agent::Canceled>> + Send {
std::future::ready(match self.0.as_ref() {
Some(passwd) => Ok(passwd.clone()),
None => Err(iwdrs::error::agent::Canceled {}),
})
}

fn request_private_key_passphrase(
&self,
_network: &Network,
) -> impl Future<Output = Result<String, iwdrs::error::agent::Canceled>> + Send {
std::future::ready(Err(iwdrs::error::agent::Canceled {}))
}

fn request_user_name_and_passphrase(
&self,
_network: &Network,
) -> impl Future<Output = Result<(String, String), iwdrs::error::agent::Canceled>> + Send {
std::future::ready(Err(iwdrs::error::agent::Canceled {}))
}

fn request_user_password(
&self,
_network: &Network,
_user_name: Option<&String>,
) -> impl Future<Output = Result<(String, String), iwdrs::error::agent::Canceled>> + Send {
std::future::ready(Err(iwdrs::error::agent::Canceled {}))
}
}
137 changes: 119 additions & 18 deletions src/agent.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::{future::Future, pin::Pin, sync::Arc};
use std::{future::Future, str::FromStr, sync::Arc};

use strum::EnumString;
use zbus::{Connection, Proxy, interface};
use zvariant::OwnedObjectPath;

use crate::{error::agent::Canceled, network::Network};

// AgentManager

#[derive(Debug, Clone)]
Expand All @@ -29,43 +32,141 @@ impl AgentManager {
.await
}

pub(crate) async fn register_agent(&self, agent: Agent) -> zbus::Result<()> {
pub(crate) async fn register_agent(&self, agent: impl Agent) -> zbus::Result<()> {
let proxy = self.proxy().await?;
proxy
.call_method("RegisterAgent", &(self.dbus_path))
.await?;

let interface = AgentInterface {
agent,
connection: self.connection.clone(),
};

self.connection
.object_server()
.at(self.dbus_path.clone(), agent)
.at(self.dbus_path.clone(), interface)
.await?;

Ok(())
}
}

// Agent
#[derive(Debug, EnumString)]
pub enum CancellationReason {
#[strum(serialize = "out-of-range")]
OutOfRange,
#[strum(serialize = "user-canceled")]
UserCanceled,
#[strum(serialize = "timed-out")]
Timeout,
#[strum(serialize = "shutdown")]
Shutdown,
}

pub trait Agent: Send + Sync + 'static {
/// This method gets called when the service daemon unregisters the agent. An agent can use it to do
/// cleanup tasks. There is no need to unregister the agent, because when this method gets called it has
/// already been unregistered.
fn release(&self) {}

///This method gets called when trying to connect to a network and passphrase is required.
fn request_passphrase(
&self,
network: &Network,
) -> impl Future<Output = Result<String, Canceled>> + Send;

/// This method gets called when connecting to a network that requires authentication using a
/// locally-stored encrypted private key file, to obtain that private key's encryption passphrase.
fn request_private_key_passphrase(
&self,
network: &Network,
) -> impl Future<Output = Result<String, Canceled>> + Send;

pub type RequestPassPhraseFn = Box<
dyn (Fn() -> Pin<Box<dyn Future<Output = Result<String, Box<dyn std::error::Error>>> + Send>>)
+ Send
+ Sync,
>;
/// This method gets called when connecting to a network that requires authentication using a
/// user name and password.
fn request_user_name_and_passphrase(
&self,
network: &Network,
) -> impl Future<Output = Result<(String, String), Canceled>> + Send;

/// This method gets called when connecting to a network that requires authentication with a
/// user password. The user name is optionally passed in the parameter.
fn request_user_password(
&self,
network: &Network,
user_name: Option<&String>,
) -> impl Future<Output = Result<(String, String), Canceled>> + Send;

/// This method gets called to indicate that the agent request failed before a reply was returned.
fn cancel(&self, _reason: CancellationReason) {}
}

struct AgentInterface<A> {
agent: A,
connection: Arc<Connection>,
}

pub struct Agent {
pub request_passphrase_fn: RequestPassPhraseFn,
impl<A> AgentInterface<A> {
fn get_network(&self, network_path: OwnedObjectPath) -> Network {
Network {
connection: self.connection.clone(),
dbus_path: network_path,
}
}
}

#[interface(name = "net.connman.iwd.Agent")]
impl Agent {
impl<A: Agent> AgentInterface<A> {
#[zbus(name = "Release")]
fn release(&self) {
self.agent.release();
}

#[zbus(name = "RequestPassphrase")]
async fn request_passphrase(
async fn request_passphrase(&self, network_path: OwnedObjectPath) -> zbus::fdo::Result<String> {
let network = self.get_network(network_path);
Ok(self.agent.request_passphrase(&network).await?)
}

#[zbus(name = "RequestPrivateKeyPassphrase")]
async fn request_private_key_passphrase(
&self,
_network_path: OwnedObjectPath,
network_path: OwnedObjectPath,
) -> zbus::fdo::Result<String> {
match (self.request_passphrase_fn)().await {
Ok(passphrase) => Ok(passphrase),
Err(e) => Err(zbus::fdo::Error::Failed(e.to_string())),
}
let network = self.get_network(network_path);
Ok(self.agent.request_private_key_passphrase(&network).await?)
}

#[zbus(name = "RequestUserNameAndPassword")]
async fn request_user_name_and_passphrase(
&self,
network_path: OwnedObjectPath,
) -> zbus::fdo::Result<(String, String)> {
let network = self.get_network(network_path);
Ok(self
.agent
.request_user_name_and_passphrase(&network)
.await?)
}

#[zbus(name = "RequestUserPassword")]
async fn request_user_password(
&self,
network_path: OwnedObjectPath,
user_name: zvariant::Optional<String>,
) -> zbus::fdo::Result<(String, String)> {
let network = self.get_network(network_path);
let user_name = user_name.as_ref();
Ok(self
.agent
.request_user_password(&network, user_name)
.await?)
}

#[zbus(name = "Cancel")]
fn cancel(&self, reason: String) {
let reason = CancellationReason::from_str(&reason).unwrap();
self.agent.cancel(reason);
}
}
1 change: 1 addition & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use strum::EnumMessage;
use thiserror::Error;

pub mod access_point;
pub mod agent;
pub mod network;
pub mod station;

Expand Down
18 changes: 18 additions & 0 deletions src/error/agent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::fmt::Display;

use thiserror::Error;

#[derive(Debug, Error)]
pub struct Canceled();

impl Display for Canceled {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Canceled")
}
}

impl From<Canceled> for zbus::fdo::Error {
fn from(value: Canceled) -> Self {
zbus::fdo::Error::Failed(value.to_string())
}
}
2 changes: 1 addition & 1 deletion src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ impl Session {
.collect()
}

pub async fn register_agent(&self, agent: Agent) -> zbus::Result<AgentManager> {
pub async fn register_agent(&self, agent: impl Agent) -> zbus::Result<AgentManager> {
let path =
OwnedObjectPath::try_from(format!("/iwdrs/agent/{}", Uuid::new_v4().as_simple()))?;
let agent_manager = AgentManager::new(self.connection.clone(), path);
Expand Down