mirror of
https://github.com/aramperes/nut-rs.git
synced 2025-09-09 13:38:30 -04:00
110 lines
3.6 KiB
Rust
110 lines
3.6 KiB
Rust
use crate::tokio::stream::ConnectionStream;
|
|
use crate::{Config, Error, Host, NutError, TcpHost};
|
|
use tokio::net::TcpStream;
|
|
|
|
/// An asynchronous NUT client, using Tokio.
|
|
pub struct Client {
|
|
/// The client configuration.
|
|
config: Config,
|
|
/// The client connection.
|
|
pub(crate) stream: ConnectionStream,
|
|
}
|
|
|
|
impl Client {
|
|
/// Connects to a remote NUT server using a blocking connection.
|
|
pub async fn new(config: &Config) -> crate::Result<Self> {
|
|
match &config.host {
|
|
Host::Tcp(host) => Self::new_tcp(config, host).await,
|
|
}
|
|
// TODO: Support Unix domain sockets
|
|
}
|
|
|
|
/// Connects to a remote NUT server using a blocking TCP connection.
|
|
async fn new_tcp(config: &Config, host: &TcpHost) -> crate::Result<Self> {
|
|
let tcp_stream = TcpStream::connect(&host.addr).await?;
|
|
let mut client = Client {
|
|
config: config.clone(),
|
|
stream: ConnectionStream::Tcp(tcp_stream).buffered(),
|
|
};
|
|
|
|
client = client.enable_ssl().await?;
|
|
|
|
Ok(client)
|
|
}
|
|
|
|
/// Authenticates to the given UPS device with the username and password set in the config.
|
|
pub async fn login(&mut self, ups_name: String) -> crate::Result<()> {
|
|
if let Some(auth) = self.config.auth.clone() {
|
|
// Pass username and check for 'OK'
|
|
self.set_username(auth.username).await?;
|
|
|
|
// Pass password and check for 'OK'
|
|
if let Some(password) = auth.password {
|
|
self.set_password(password).await?;
|
|
}
|
|
|
|
// Submit login
|
|
self.exec_login(ups_name).await
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "async-ssl")]
|
|
async fn enable_ssl(mut self) -> crate::Result<Self> {
|
|
if self.config.ssl {
|
|
self.exec_start_tls().await?;
|
|
|
|
// Initialize SSL configurations
|
|
let mut ssl_config = rustls::ClientConfig::new();
|
|
let dns_name: webpki::DNSName;
|
|
|
|
if self.config.ssl_insecure {
|
|
ssl_config
|
|
.dangerous()
|
|
.set_certificate_verifier(std::sync::Arc::new(
|
|
crate::ssl::InsecureCertificateValidator::new(&self.config),
|
|
));
|
|
|
|
dns_name = webpki::DNSNameRef::try_from_ascii_str("www.google.com")
|
|
.unwrap()
|
|
.to_owned();
|
|
} else {
|
|
// Try to get hostname as given (e.g. localhost can be used for strict SSL, but not 127.0.0.1)
|
|
let hostname = self
|
|
.config
|
|
.host
|
|
.hostname()
|
|
.ok_or(Error::Nut(NutError::SslInvalidHostname))?;
|
|
|
|
dns_name = webpki::DNSNameRef::try_from_ascii_str(&hostname)
|
|
.map_err(|_| Error::Nut(NutError::SslInvalidHostname))?
|
|
.to_owned();
|
|
|
|
ssl_config
|
|
.root_store
|
|
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
|
|
};
|
|
|
|
let config = tokio_rustls::TlsConnector::from(std::sync::Arc::new(ssl_config));
|
|
|
|
// Un-buffer to get back underlying stream
|
|
self.stream = self.stream.unbuffered();
|
|
|
|
// Upgrade to SSL
|
|
self.stream = self
|
|
.stream
|
|
.upgrade_ssl_client(config, dns_name.as_ref())
|
|
.await?;
|
|
|
|
// Re-buffer
|
|
self.stream = self.stream.buffered();
|
|
}
|
|
Ok(self)
|
|
}
|
|
|
|
#[cfg(not(feature = "async-ssl"))]
|
|
async fn enable_ssl(self) -> crate::Result<Self> {
|
|
Ok(self)
|
|
}
|
|
}
|