use url::Url;
use sspi::NetworkRequest;
use sspi::network_client::*;
use sspi::{Error, ErrorKind, Result};
#[cfg(feature = "async")]
mod client_impl {
use core::future::Future;
use core::net::{IpAddr, Ipv4Addr};
use core::pin::Pin;
use super::*;
use futures_util::TryFutureExt;
use reqwest::Client;
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
use tokio::net::{TcpStream, UdpSocket};
pub struct ReqwestNetworkClient;
impl AsyncNetworkClient for ReqwestNetworkClient {
fn send<'a>(
&'a mut self,
network_request: &'a NetworkRequest,
) -> Pin<Box<dyn Future<Output = Result<Vec<u8>>> + 'a>> {
Box::pin(ReqwestNetworkClient::send(self, network_request))
}
}
impl ReqwestNetworkClient {
pub fn new() -> Self {
Self {}
}
}
impl Default for ReqwestNetworkClient {
fn default() -> Self {
Self::new()
}
}
impl ReqwestNetworkClient {
pub async fn send<'a>(&'a mut self, request: &'a NetworkRequest) -> Result<Vec<u8>> {
log::debug!("Sending SSPI network request to {}", request.url);
match &request.protocol {
NetworkProtocol::Tcp => self.send_tcp(&request.url, &request.data).await,
NetworkProtocol::Udp => self.send_udp(&request.url, &request.data).await,
NetworkProtocol::Http | NetworkProtocol::Https => {
self.send_http(&request.url, &request.data).await
}
}
}
async fn send_tcp(&self, url: &Url, data: &[u8]) -> sspi::Result<Vec<u8>> {
let addr = format!(
"{}:{}",
url.host_str().unwrap_or_default(),
url.port().unwrap_or(88)
);
let mut stream = TcpStream::connect(addr).await.map_err(|e| {
Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{:?}", e))
})?;
stream
.write(data)
.map_err(|e| Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{:?}", e)))
.await?;
let len = stream.read_u32().await.map_err(|e| {
Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{:?}", e))
})?;
let mut buf = vec![0; len as usize + 4];
buf[0..4].copy_from_slice(&(len.to_be_bytes()));
stream
.read_exact(&mut buf[4..])
.map_err(|e| Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{:?}", e)))
.await?;
Ok(buf)
}
async fn send_udp(&self, url: &Url, data: &[u8]) -> Result<Vec<u8>> {
let udp_socket = UdpSocket::bind((IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
.await
.map_err(|e| Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{e:?}")))?;
let addr = format!(
"{}:{}",
url.host_str().unwrap_or_default(),
url.port().unwrap_or(88)
);
udp_socket
.send_to(data, addr)
.await
.map_err(|e| Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{e:?}")))?;
let mut buf = vec![0; 0xbb80];
let n = udp_socket
.recv(&mut buf)
.await
.map_err(|e| Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{e:?}")))?;
let buf = &buf[0..n];
let mut reply_buf = Vec::with_capacity(n + 4);
let n = u32::try_from(n)
.map_err(|e| Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{e:?}")))?;
reply_buf.extend_from_slice(&n.to_be_bytes());
reply_buf.extend_from_slice(buf);
Ok(reply_buf)
}
async fn send_http(&mut self, url: &Url, data: &[u8]) -> Result<Vec<u8>> {
let client = Client::new();
let response = client
.post(url.clone())
.body(data.to_vec())
.send()
.await
.map_err(|e| {
Error::new(
ErrorKind::NoAuthenticatingAuthority,
format!("failed to send KDC request over proxy: {e:?}"),
)
})?
.error_for_status()
.map_err(|e| {
Error::new(
ErrorKind::NoAuthenticatingAuthority,
format!("KdcProxy: {e:?}"),
)
})?;
let body = response.bytes().await.map_err(|e| {
Error::new(
ErrorKind::NoAuthenticatingAuthority,
format!("failed to receive KDC response: {e:?}"),
)
})?;
let body = Vec::from(body);
Ok(body)
}
}
}
#[cfg(not(feature = "async"))]
mod client_impl {
use super::*;
use byteorder::{BigEndian, ReadBytesExt};
use reqwest::blocking::Client;
use std::io::{Read, Write};
use std::net::{IpAddr, Ipv4Addr, TcpStream, UdpSocket};
#[derive(Clone, Default)]
pub struct ReqwestNetworkClient;
impl ReqwestNetworkClient {
fn send_tcp(&self, url: &Url, data: &[u8]) -> Result<Vec<u8>> {
let addr = format!(
"{}:{}",
url.host_str().unwrap_or_default(),
url.port().unwrap_or(88)
);
let mut stream = TcpStream::connect(addr).map_err(|e| {
Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{:?}", e))
})?;
stream.write(data).map_err(|e| {
Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{:?}", e))
})?;
let len = stream.read_u32::<BigEndian>().map_err(|e| {
Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{:?}", e))
})?;
let mut buf = vec![0; len as usize + 4];
buf[0..4].copy_from_slice(&(len.to_be_bytes()));
stream.read_exact(&mut buf[4..]).map_err(|e| {
Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{:?}", e))
})?;
Ok(buf)
}
fn send_udp(&self, url: &Url, data: &[u8]) -> Result<Vec<u8>> {
let udp_socket = UdpSocket::bind((IpAddr::V4(Ipv4Addr::LOCALHOST), 0))?;
let addr = format!(
"{}:{}",
url.host_str().unwrap_or_default(),
url.port().unwrap_or(88)
);
udp_socket.send_to(data, addr)?;
let mut buf = vec![0; 0xbb80];
let n = udp_socket.recv(&mut buf)?;
let mut reply_buf = Vec::with_capacity(n + 4);
reply_buf.extend_from_slice(&(n as u32).to_be_bytes());
reply_buf.extend_from_slice(&buf[0..n]);
Ok(reply_buf)
}
fn send_http(&self, url: &Url, data: &[u8]) -> Result<Vec<u8>> {
let client = Client::new();
let response = client
.post(url.clone())
.body(data.to_vec())
.send()
.map_err(|err| match err {
err if err.to_string().to_lowercase().contains("certificate") => Error::new(
ErrorKind::CertificateUnknown,
format!("Invalid certificate data: {:?}", err),
),
_ => Error::new(
ErrorKind::NoAuthenticatingAuthority,
format!("Unable to send the data to the KDC Proxy: {:?}", err),
),
})?
.error_for_status()
.map_err(|err| {
Error::new(
ErrorKind::NoAuthenticatingAuthority,
format!("KDC Proxy: {err}"),
)
})?;
let body = response.bytes().map_err(|err| {
Error::new(
ErrorKind::NoAuthenticatingAuthority,
format!(
"Unable to read the response data from the KDC Proxy: {:?}",
err
),
)
})?;
let body = Vec::from(body);
Ok(body)
}
}
impl NetworkClient for ReqwestNetworkClient {
fn send(&self, request: &NetworkRequest) -> Result<Vec<u8>> {
match request.protocol {
NetworkProtocol::Tcp => self.send_tcp(&request.url, &request.data),
NetworkProtocol::Udp => self.send_udp(&request.url, &request.data),
NetworkProtocol::Http | NetworkProtocol::Https => {
self.send_http(&request.url, &request.data)
}
}
}
}
}
pub use client_impl::*;