feat: added mTLS auth for beacons
This commit is contained in:
parent
f9ff9f266a
commit
75b53f7191
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -487,9 +487,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.9.0"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "camino"
|
name = "camino"
|
||||||
@ -2983,9 +2983,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.22"
|
version = "0.23.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7"
|
checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lc-rs",
|
"aws-lc-rs",
|
||||||
"log",
|
"log",
|
||||||
@ -3370,7 +3370,10 @@ name = "sparse-beacon"
|
|||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"bytes",
|
||||||
"futures",
|
"futures",
|
||||||
|
"http",
|
||||||
|
"http-body-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper-rustls",
|
"hyper-rustls",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
@ -3379,6 +3382,7 @@ dependencies = [
|
|||||||
"pcap-sys",
|
"pcap-sys",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"rand 0.9.0",
|
"rand 0.9.0",
|
||||||
|
"rustls",
|
||||||
"simple_logger",
|
"simple_logger",
|
||||||
"smoltcp",
|
"smoltcp",
|
||||||
"sparse-actions",
|
"sparse-actions",
|
||||||
@ -3396,7 +3400,9 @@ dependencies = [
|
|||||||
"axum",
|
"axum",
|
||||||
"axum-server",
|
"axum-server",
|
||||||
"futures",
|
"futures",
|
||||||
|
"rcgen",
|
||||||
"rustls",
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use libc::{c_char, c_int, c_uchar, c_uint, c_ushort, c_void};
|
use libc::{c_char, c_int, c_uchar, c_uint, c_ushort};
|
||||||
|
|
||||||
pub const DLT_NULL: i32 = 0;
|
pub const DLT_NULL: i32 = 0;
|
||||||
pub const DLT_EN10MB: i32 = 1;
|
pub const DLT_EN10MB: i32 = 1;
|
||||||
@ -161,17 +161,6 @@ extern "C" {
|
|||||||
mask: u32,
|
mask: u32,
|
||||||
) -> c_int;
|
) -> c_int;
|
||||||
pub fn pcap_setfilter(dev: *mut PcapDev, fp: *const BpfProgram) -> c_int;
|
pub fn pcap_setfilter(dev: *mut PcapDev, fp: *const BpfProgram) -> c_int;
|
||||||
pub fn pcap_loop(
|
|
||||||
p: *mut PcapDev,
|
|
||||||
cnt: c_int,
|
|
||||||
callback: unsafe extern "C" fn(
|
|
||||||
user: *mut c_void,
|
|
||||||
header: *const PktHeader,
|
|
||||||
data: *const u8,
|
|
||||||
),
|
|
||||||
user: *mut c_void,
|
|
||||||
) -> c_int;
|
|
||||||
pub fn pcap_breakloop(p: *mut PcapDev);
|
|
||||||
pub fn pcap_sendpacket(p: *mut PcapDev, buf: *const c_uchar, size: c_int) -> c_int;
|
pub fn pcap_sendpacket(p: *mut PcapDev, buf: *const c_uchar, size: c_int) -> c_int;
|
||||||
pub fn pcap_setnonblock(dev: *mut PcapDev, nonblock: c_int, errbuf: *mut c_char) -> c_int;
|
pub fn pcap_setnonblock(dev: *mut PcapDev, nonblock: c_int, errbuf: *mut c_char) -> c_int;
|
||||||
pub fn pcap_get_selectable_fd(p: *mut PcapDev) -> c_int;
|
pub fn pcap_get_selectable_fd(p: *mut PcapDev) -> c_int;
|
||||||
|
|||||||
@ -122,32 +122,19 @@ pub struct BpfProgram {}
|
|||||||
pub struct Interface {
|
pub struct Interface {
|
||||||
dev_name: CString,
|
dev_name: CString,
|
||||||
dev: *mut ffi::PcapDev,
|
dev: *mut ffi::PcapDev,
|
||||||
absorbed: bool,
|
|
||||||
nonblocking: bool,
|
nonblocking: bool,
|
||||||
state: State,
|
state: State,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Interface {
|
impl Drop for Interface {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if !self.absorbed {
|
|
||||||
unsafe { ffi::pcap_close(self.dev) };
|
unsafe { ffi::pcap_close(self.dev) };
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Interface {}
|
unsafe impl Send for Interface {}
|
||||||
unsafe impl Sync for Interface {}
|
unsafe impl Sync for Interface {}
|
||||||
|
|
||||||
struct ListenHandler<'a, F>
|
|
||||||
where
|
|
||||||
F: FnMut(&Interface, packets::EthernetPkt) -> error::Result<bool>,
|
|
||||||
{
|
|
||||||
packet_handler: F,
|
|
||||||
break_on_fail: bool,
|
|
||||||
fail_error: Option<error::Error>,
|
|
||||||
interface: &'a Interface,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interface {
|
impl Interface {
|
||||||
pub fn new(name: &str) -> error::Result<Interface> {
|
pub fn new(name: &str) -> error::Result<Interface> {
|
||||||
let mut errbuf = [0i8; ffi::PCAP_ERRBUF_SIZE];
|
let mut errbuf = [0i8; ffi::PCAP_ERRBUF_SIZE];
|
||||||
@ -162,7 +149,6 @@ impl Interface {
|
|||||||
Ok(Interface {
|
Ok(Interface {
|
||||||
dev_name,
|
dev_name,
|
||||||
dev,
|
dev,
|
||||||
absorbed: false,
|
|
||||||
nonblocking: false,
|
nonblocking: false,
|
||||||
state: State::Disabled,
|
state: State::Disabled,
|
||||||
})
|
})
|
||||||
@ -263,7 +249,6 @@ impl Interface {
|
|||||||
Err(unsafe { ffi::pcap_geterr(self.dev) })?;
|
Err(unsafe { ffi::pcap_geterr(self.dev) })?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.absorbed = true;
|
|
||||||
self.state = State::Activated;
|
self.state = State::Activated;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -390,85 +375,6 @@ impl Interface {
|
|||||||
Ok(packets::EthernetPkt { data: rdata }.to_owned())
|
Ok(packets::EthernetPkt { data: rdata }.to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen<F>(
|
|
||||||
&self,
|
|
||||||
packet_handler: F,
|
|
||||||
break_on_fail: bool,
|
|
||||||
packet_count: i32,
|
|
||||||
) -> error::Result<(Option<error::Error>, i32)>
|
|
||||||
where
|
|
||||||
F: FnMut(&Interface, packets::EthernetPkt) -> error::Result<bool>,
|
|
||||||
{
|
|
||||||
if self.state == State::Listening {
|
|
||||||
return Err(error::Error::IncorrectDeviceState(
|
|
||||||
State::Activated,
|
|
||||||
self.state,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn cback<F>(
|
|
||||||
user: *mut libc::c_void,
|
|
||||||
header: *const ffi::PktHeader,
|
|
||||||
data: *const u8,
|
|
||||||
) where
|
|
||||||
F: FnMut(&Interface, packets::EthernetPkt) -> error::Result<bool>,
|
|
||||||
{
|
|
||||||
let info = &mut *(user as *mut ListenHandler<F>);
|
|
||||||
|
|
||||||
let data = slice::from_raw_parts(data, (*header).caplen as usize);
|
|
||||||
|
|
||||||
if data.len() < 14 {
|
|
||||||
eprintln!(
|
|
||||||
" * Failed to get full packet, captured {} bytes",
|
|
||||||
data.len()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = (info.packet_handler)(info.interface, packets::EthernetPkt { data });
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!(" * Packet handle error: {:?}", e);
|
|
||||||
if info.break_on_fail {
|
|
||||||
info.fail_error = Some(e);
|
|
||||||
ffi::pcap_breakloop(info.interface.dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(b) => {
|
|
||||||
if b {
|
|
||||||
ffi::pcap_breakloop(info.interface.dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let interface = Interface {
|
|
||||||
dev_name: self.dev_name.clone(),
|
|
||||||
dev: self.dev,
|
|
||||||
absorbed: true,
|
|
||||||
nonblocking: self.nonblocking,
|
|
||||||
state: State::Listening,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut info = ListenHandler::<F> {
|
|
||||||
packet_handler,
|
|
||||||
break_on_fail,
|
|
||||||
fail_error: None,
|
|
||||||
interface: &interface,
|
|
||||||
};
|
|
||||||
|
|
||||||
let count = unsafe {
|
|
||||||
ffi::pcap_loop(
|
|
||||||
self.dev,
|
|
||||||
packet_count,
|
|
||||||
cback::<F>,
|
|
||||||
&mut info as *mut ListenHandler<F> as *mut libc::c_void,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((info.fail_error, count))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub fn get_wait_ready_callback(&self) -> WaitHandle {
|
pub fn get_wait_ready_callback(&self) -> WaitHandle {
|
||||||
let handle = unsafe { ffi::pcap_getevent(self.dev) };
|
let handle = unsafe { ffi::pcap_getevent(self.dev) };
|
||||||
|
|||||||
@ -14,12 +14,16 @@ tracing = "0.1.41"
|
|||||||
rand = "0.9.0"
|
rand = "0.9.0"
|
||||||
pin-project = "1.1.9"
|
pin-project = "1.1.9"
|
||||||
hyper-util = { version = "0.1.10", features = ["client", "client-legacy", "http1", "http2", "service", "tokio"] }
|
hyper-util = { version = "0.1.10", features = ["client", "client-legacy", "http1", "http2", "service", "tokio"] }
|
||||||
hyper-rustls = { version = "0.27.5", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
|
hyper-rustls = { version = "0.27.5", default-features = false, features = ["http1", "http2", "native-tokio", "ring"] }
|
||||||
|
rustls = { version = "0.23.23", default-features = false, features = ["ring", "std"] }
|
||||||
tower-service = "0.3.3"
|
tower-service = "0.3.3"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
simple_logger = "5.0.0"
|
simple_logger = "5.0.0"
|
||||||
|
http = "1.2.0"
|
||||||
|
|
||||||
pcap-sys = { version = "0.1.0", path = "../pcap-sys" }
|
pcap-sys = { version = "0.1.0", path = "../pcap-sys" }
|
||||||
sparse-actions = { version = "2.0.0", path = "../sparse-actions" }
|
sparse-actions = { version = "2.0.0", path = "../sparse-actions" }
|
||||||
packets = { version = "0.1.0", path = "../packets" }
|
packets = { version = "0.1.0", path = "../packets" }
|
||||||
nl-sys = { version = "0.1.0", path = "../nl-sys" }
|
nl-sys = { version = "0.1.0", path = "../nl-sys" }
|
||||||
|
bytes = "1.10.0"
|
||||||
|
http-body-util = "0.1.2"
|
||||||
|
|||||||
@ -1,313 +1,51 @@
|
|||||||
use std::{
|
use std::{future::Future, pin::Pin, task::{self, Poll}};
|
||||||
future::Future,
|
|
||||||
net::Ipv4Addr,
|
|
||||||
pin::Pin,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
use futures::ready;
|
use futures::stream::StreamExt;
|
||||||
use smoltcp::{
|
use http::Uri;
|
||||||
iface::{Config, Interface, SocketHandle, SocketSet},
|
use http_body_util::{Empty, BodyExt};
|
||||||
socket::tcp::{RecvError, SendError, Socket, SocketBuffer, State},
|
use hyper::Request;
|
||||||
time::Instant,
|
use hyper_util::{client::legacy::Client, rt::{TokioExecutor, TokioIo}};
|
||||||
wire::{EthernetAddress, IpCidr, Ipv4Address},
|
use rustls::RootCertStore;
|
||||||
};
|
use tower_service::Service;
|
||||||
use tokio::{
|
|
||||||
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
|
|
||||||
task::{spawn, spawn_blocking, JoinHandle},
|
|
||||||
};
|
|
||||||
|
|
||||||
use sparse_actions::payload_types::Parameters;
|
use sparse_actions::payload_types::Parameters;
|
||||||
|
|
||||||
use crate::{adapter, error};
|
use crate::{adapter, error, tcp::{self, setup_network}};
|
||||||
|
|
||||||
pub struct NetInterfaceHandle {
|
#[derive(Clone)]
|
||||||
net: Arc<Mutex<(SocketSet<'static>, crate::socket::RawSocket, Interface)>>,
|
pub struct ServerConnector<T>
|
||||||
tcp_handle: SocketHandle,
|
|
||||||
|
|
||||||
background_process: JoinHandle<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for NetInterfaceHandle {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.background_process.abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncRead for NetInterfaceHandle {
|
|
||||||
fn poll_read(
|
|
||||||
self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
tbuf: &mut tokio::io::ReadBuf<'_>,
|
|
||||||
) -> Poll<Result<(), std::io::Error>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
let Ok(mut inner) = this.net.lock() else {
|
|
||||||
return Poll::Ready(Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::BrokenPipe,
|
|
||||||
"mutex for tcp connection is poisoned",
|
|
||||||
)));
|
|
||||||
};
|
|
||||||
let (ref mut s_guard, _, _) = *inner;
|
|
||||||
|
|
||||||
let socket = s_guard.get_mut::<Socket>(this.tcp_handle);
|
|
||||||
|
|
||||||
let has_data = socket.can_recv();
|
|
||||||
while socket.can_recv() {
|
|
||||||
let buf = match socket.recv(|buf| (buf.len(), buf.to_vec())) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(RecvError::InvalidState) => {
|
|
||||||
return Poll::Ready(Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::NetworkDown,
|
|
||||||
"received InvalidState from smoltcp",
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
Err(RecvError::Finished) => {
|
|
||||||
return Poll::Ready(Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::BrokenPipe,
|
|
||||||
"tried reading from finished connection",
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
tbuf.put_slice(&buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.register_recv_waker(cx.waker());
|
|
||||||
|
|
||||||
if has_data {
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
} else {
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncWrite for NetInterfaceHandle {
|
|
||||||
fn poll_write(
|
|
||||||
self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
src: &[u8],
|
|
||||||
) -> Poll<std::io::Result<usize>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
let Ok(mut inner) = this.net.lock() else {
|
|
||||||
return Poll::Ready(Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::BrokenPipe,
|
|
||||||
"mutex for tcp connection is poisoned",
|
|
||||||
)));
|
|
||||||
};
|
|
||||||
let (ref mut s_guard, _, _) = *inner;
|
|
||||||
|
|
||||||
let socket = s_guard.get_mut::<Socket>(this.tcp_handle);
|
|
||||||
|
|
||||||
socket.register_send_waker(cx.waker());
|
|
||||||
|
|
||||||
if socket.can_send() {
|
|
||||||
let to_send = socket.send_capacity().min(src.len());
|
|
||||||
match socket.send_slice(&src[..to_send]) {
|
|
||||||
Ok(s) => Poll::Ready(Ok(s)),
|
|
||||||
Err(SendError::InvalidState) => {
|
|
||||||
return Poll::Ready(Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::NetworkDown,
|
|
||||||
"received InvalidState from smoltcp",
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
let Ok(mut inner) = this.net.lock() else {
|
|
||||||
return Poll::Ready(Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::BrokenPipe,
|
|
||||||
"mutex for tcp connection is poisoned",
|
|
||||||
)));
|
|
||||||
};
|
|
||||||
let (ref mut s_guard, _, _) = *inner;
|
|
||||||
|
|
||||||
let socket = s_guard.get_mut::<Socket>(this.tcp_handle);
|
|
||||||
socket.close();
|
|
||||||
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn setup_network<T>(
|
|
||||||
adapter: &T,
|
|
||||||
parameters: &Parameters,
|
|
||||||
) -> Result<NetInterfaceHandle, error::BeaconError>
|
|
||||||
where
|
where
|
||||||
T: adapter::BeaconAdapter + Clone + Send + 'static,
|
T: adapter::BeaconAdapter + Clone + Send + 'static
|
||||||
{
|
{
|
||||||
let net_info = tokio::task::spawn_blocking({
|
adapter: T,
|
||||||
let adapter = adapter.clone();
|
parameters: Parameters
|
||||||
move || adapter.networking_info()
|
}
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
let (interface, gateway_ip, mac_address, source_ip, netmask) = match unsafe {
|
impl<T> Service<Uri> for ServerConnector<T>
|
||||||
parameters.source_ip.custom_networking.mode
|
where
|
||||||
} {
|
T: adapter::BeaconAdapter + Clone + Send + Sync + 'static
|
||||||
0 => {
|
{
|
||||||
// custom networking
|
type Response = TokioIo<tcp::NetInterfaceHandle>;
|
||||||
let interface_name = unsafe {
|
type Error = error::BeaconError;
|
||||||
¶meters.source_ip.custom_networking.interface
|
type Future = Pin<Box<
|
||||||
[..parameters.source_ip.custom_networking.interface_len as usize]
|
dyn Future<Output = Result<Self::Response, Self::Error>> + Send
|
||||||
};
|
>>;
|
||||||
|
|
||||||
let interface = if interface_name.is_empty() {
|
fn poll_ready(&mut self, _: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
let Some(default_route) = net_info.routes.iter().find(|r| r.network.1 == 0) else {
|
Poll::Ready(Ok(()))
|
||||||
return Err(error::BeaconError::NoDefaultRoute);
|
|
||||||
};
|
|
||||||
|
|
||||||
&net_info.interfaces[default_route.interface_index]
|
|
||||||
} else {
|
|
||||||
net_info
|
|
||||||
.interfaces
|
|
||||||
.iter()
|
|
||||||
.find(|intf| intf.name == interface_name)
|
|
||||||
.ok_or(error::BeaconError::NoDefaultRoute)?
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
(
|
|
||||||
interface,
|
|
||||||
Ipv4Addr::new(
|
|
||||||
parameters.source_ip.custom_networking.gateway.a,
|
|
||||||
parameters.source_ip.custom_networking.gateway.b,
|
|
||||||
parameters.source_ip.custom_networking.gateway.c,
|
|
||||||
parameters.source_ip.custom_networking.gateway.d,
|
|
||||||
),
|
|
||||||
parameters.source_ip.custom_networking.source_mac.clone(),
|
|
||||||
Ipv4Addr::new(
|
|
||||||
parameters.source_ip.custom_networking.source_ip.a,
|
|
||||||
parameters.source_ip.custom_networking.source_ip.b,
|
|
||||||
parameters.source_ip.custom_networking.source_ip.c,
|
|
||||||
parameters.source_ip.custom_networking.source_ip.d,
|
|
||||||
),
|
|
||||||
parameters.source_ip.custom_networking.netmask as u8,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
// host networking
|
|
||||||
let Some(default_route) = net_info.routes.iter().find(|r| r.network.1 == 0) else {
|
|
||||||
return Err(error::BeaconError::NoDefaultRoute);
|
|
||||||
};
|
|
||||||
|
|
||||||
let default_route_if = &net_info.interfaces[default_route.interface_index];
|
|
||||||
|
|
||||||
(
|
|
||||||
default_route_if,
|
|
||||||
default_route.gateway.0,
|
|
||||||
default_route_if.mac_addr.clone(),
|
|
||||||
unsafe {
|
|
||||||
Ipv4Addr::new(
|
|
||||||
parameters.source_ip.use_host_networking.source_ip.a,
|
|
||||||
parameters.source_ip.use_host_networking.source_ip.b,
|
|
||||||
parameters.source_ip.use_host_networking.source_ip.c,
|
|
||||||
parameters.source_ip.use_host_networking.source_ip.d,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
default_route.gateway.1,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => panic!("Corrupted parameters present!"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let go_promisc = mac_address != [0, 0, 0, 0, 0, 0];
|
|
||||||
let mac_address = Some(mac_address)
|
|
||||||
.filter(|smac| smac != &[0, 0, 0, 0, 0, 0])
|
|
||||||
.unwrap_or(interface.mac_addr);
|
|
||||||
|
|
||||||
let local_port = 49152 + rand::random::<u16>() % 16384;
|
|
||||||
let mut device = crate::socket::RawSocket::new::<T>(interface, go_promisc, local_port)?;
|
|
||||||
|
|
||||||
let mut config = Config::new(EthernetAddress(mac_address).into());
|
|
||||||
config.random_seed = rand::random();
|
|
||||||
|
|
||||||
let mut iface = Interface::new(config, &mut device, Instant::now());
|
|
||||||
iface.update_ip_addrs(|addrs| {
|
|
||||||
addrs
|
|
||||||
.push(IpCidr::new(source_ip.into(), netmask))
|
|
||||||
.expect("could not add new IP address");
|
|
||||||
});
|
|
||||||
iface
|
|
||||||
.routes_mut()
|
|
||||||
.add_default_ipv4_route(gateway_ip.into())
|
|
||||||
.expect("did not expect route table to be full");
|
|
||||||
|
|
||||||
let tcp_rx_buffer = SocketBuffer::new(vec![0; 8192]);
|
|
||||||
let tcp_tx_buffer = SocketBuffer::new(vec![0; 8192]);
|
|
||||||
let tcp_socket = Socket::new(tcp_rx_buffer, tcp_tx_buffer);
|
|
||||||
|
|
||||||
let mut sockets = SocketSet::new(vec![]);
|
|
||||||
let tcp_handle = sockets.add(tcp_socket);
|
|
||||||
|
|
||||||
let mut active = false;
|
|
||||||
let ready_wait = device.get_ready_wait_callback();
|
|
||||||
|
|
||||||
let destination = (
|
|
||||||
Ipv4Address::new(
|
|
||||||
parameters.destination_ip.a,
|
|
||||||
parameters.destination_ip.b,
|
|
||||||
parameters.destination_ip.c,
|
|
||||||
parameters.destination_ip.d,
|
|
||||||
),
|
|
||||||
8080, //parameters.destination_port,
|
|
||||||
);
|
|
||||||
|
|
||||||
while !active {
|
|
||||||
let timestamp = Instant::now();
|
|
||||||
iface.poll(timestamp, &mut device, &mut sockets);
|
|
||||||
|
|
||||||
let cx = iface.context();
|
|
||||||
|
|
||||||
let socket = sockets.get_mut::<Socket>(tcp_handle);
|
|
||||||
if !socket.is_active() {
|
|
||||||
socket.connect(cx, destination, local_port)?;
|
|
||||||
}
|
|
||||||
active = socket.is_active() && socket.state() == State::Established;
|
|
||||||
|
|
||||||
ready_wait.wait(iface.poll_delay(timestamp, &sockets).map(Into::into))?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let net = Arc::new(Mutex::new((sockets, device, iface)));
|
fn call(&mut self, _: Uri) -> Self::Future {
|
||||||
|
Box::pin({
|
||||||
let background_process = spawn({
|
let adapter = self.adapter.clone();
|
||||||
let net = Arc::clone(&net);
|
let params = self.parameters.clone();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
loop {
|
setup_network(adapter, params)
|
||||||
let delay = {
|
.await
|
||||||
let Ok(mut guard) = net.lock() else {
|
.map(TokioIo::new)
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let (ref mut s_guard, ref mut d_guard, ref mut i_guard) = *guard;
|
|
||||||
|
|
||||||
let timestamp = Instant::now();
|
|
||||||
i_guard.poll(timestamp, d_guard, s_guard);
|
|
||||||
|
|
||||||
i_guard.poll_delay(timestamp, s_guard)
|
|
||||||
};
|
|
||||||
|
|
||||||
let _ = ready_wait.wait(delay.map(Into::into));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(NetInterfaceHandle {
|
|
||||||
net,
|
|
||||||
tcp_handle,
|
|
||||||
|
|
||||||
background_process,
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn perform_callback<T>(
|
pub async fn perform_callback<T>(
|
||||||
@ -315,23 +53,57 @@ pub async fn perform_callback<T>(
|
|||||||
parameters: &Parameters,
|
parameters: &Parameters,
|
||||||
) -> Result<(), error::BeaconError>
|
) -> Result<(), error::BeaconError>
|
||||||
where
|
where
|
||||||
T: adapter::BeaconAdapter + Clone + Send + 'static,
|
T: adapter::BeaconAdapter + Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
println!("Attempting net connection...");
|
let server_cert = rustls::pki_types::CertificateDer::from(
|
||||||
let mut net_handle = setup_network(adapter, parameters).await?;
|
parameters.pubkey_cert[..parameters.pubkey_cert_size as usize].to_owned()
|
||||||
println!("Got connection!");
|
);
|
||||||
|
|
||||||
let mut buffer = vec![0u8; 4096];
|
let client_cert = rustls::pki_types::CertificateDer::from(
|
||||||
|
parameters.client_cert[..parameters.client_cert_length as usize].to_owned()
|
||||||
|
);
|
||||||
|
|
||||||
net_handle.write(&*b"Hello there\n").await?;
|
let client_key = rustls::pki_types::PrivateKeyDer::try_from(
|
||||||
|
parameters.client_key[..parameters.client_key_length as usize].to_owned()
|
||||||
|
)
|
||||||
|
.map_err(|_| rustls::Error::InvalidCertificate(
|
||||||
|
rustls::CertificateError::BadEncoding
|
||||||
|
))?;
|
||||||
|
|
||||||
while let Ok(v) = net_handle.read(&mut buffer).await {
|
let mut root_store = RootCertStore::empty();
|
||||||
println!("Received {v} bytes: {:?}", &buffer[..v]);
|
root_store.add(server_cert.clone())?;
|
||||||
|
|
||||||
net_handle.write(&buffer[..v]).await?;
|
let tls_config = rustls::ClientConfig::builder()
|
||||||
|
.with_root_certificates(root_store)
|
||||||
|
.with_client_auth_cert(
|
||||||
|
vec![client_cert, server_cert],
|
||||||
|
client_key
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let https = hyper_rustls::HttpsConnectorBuilder::new()
|
||||||
|
.with_tls_config(tls_config)
|
||||||
|
.https_only()
|
||||||
|
.enable_http1()
|
||||||
|
.enable_http2()
|
||||||
|
.wrap_connector(ServerConnector {
|
||||||
|
adapter: adapter.clone(),
|
||||||
|
parameters: parameters.clone()
|
||||||
|
});
|
||||||
|
|
||||||
|
let client = Client::builder(TokioExecutor::new())
|
||||||
|
.build(https);
|
||||||
|
|
||||||
|
for _ in 1..5 {
|
||||||
|
let req = Request::builder()
|
||||||
|
.uri("https://sparse.com/hidden_sparse/test".parse::<hyper::Uri>()?)
|
||||||
|
.body(Empty::<bytes::Bytes>::new())?;
|
||||||
|
let resp = client.request(req).await?;
|
||||||
|
|
||||||
|
println!("{:?} {:?}", resp.version(), resp.status());
|
||||||
|
let body = resp.into_body();
|
||||||
|
let body = body.collect().await;
|
||||||
|
println!("{:?}", body);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Finishing connection");
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,4 +16,12 @@ pub enum BeaconError {
|
|||||||
Connect(#[from] smoltcp::socket::tcp::ConnectError),
|
Connect(#[from] smoltcp::socket::tcp::ConnectError),
|
||||||
#[error("netlink error")]
|
#[error("netlink error")]
|
||||||
Nl(#[from] nl_sys::error::Error),
|
Nl(#[from] nl_sys::error::Error),
|
||||||
|
#[error("http comms error")]
|
||||||
|
Http(#[from] http::Error),
|
||||||
|
#[error("uri parse error")]
|
||||||
|
InvalidUri(#[from] http::uri::InvalidUri),
|
||||||
|
#[error("hyper http error")]
|
||||||
|
HyperError(#[from] hyper_util::client::legacy::Error),
|
||||||
|
#[error("rustls")]
|
||||||
|
Rustls(#[from] rustls::Error),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,16 @@
|
|||||||
use sparse_actions::payload_types::Parameters;
|
use sparse_actions::payload_types::Parameters;
|
||||||
|
|
||||||
pub mod adapter;
|
|
||||||
mod callback;
|
mod callback;
|
||||||
pub mod error;
|
|
||||||
mod socket;
|
mod socket;
|
||||||
|
mod tcp;
|
||||||
|
|
||||||
|
pub mod adapter;
|
||||||
|
pub mod error;
|
||||||
pub use error::BeaconError;
|
pub use error::BeaconError;
|
||||||
|
|
||||||
pub async fn run_beacon_step<A>(host_adapter: A, params: Parameters) -> Result<(), BeaconError>
|
pub async fn run_beacon_step<A>(host_adapter: A, params: Parameters) -> Result<(), BeaconError>
|
||||||
where
|
where
|
||||||
A: adapter::BeaconAdapter + Clone + Send + 'static,
|
A: adapter::BeaconAdapter + Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
callback::perform_callback(&host_adapter, ¶ms).await?;
|
callback::perform_callback(&host_adapter, ¶ms).await?;
|
||||||
|
|
||||||
|
|||||||
328
sparse-beacon/src/tcp.rs
Normal file
328
sparse-beacon/src/tcp.rs
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
use std::{
|
||||||
|
net::Ipv4Addr,
|
||||||
|
pin::Pin,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
|
||||||
|
use smoltcp::{
|
||||||
|
iface::{Config, Interface, SocketHandle, SocketSet},
|
||||||
|
socket::tcp::{RecvError, SendError, Socket, SocketBuffer, State},
|
||||||
|
time::Instant,
|
||||||
|
wire::{EthernetAddress, IpCidr, Ipv4Address},
|
||||||
|
};
|
||||||
|
use tokio::{
|
||||||
|
io::{AsyncRead, AsyncWrite},
|
||||||
|
task::{spawn_blocking, JoinHandle},
|
||||||
|
sync::broadcast
|
||||||
|
};
|
||||||
|
use hyper_util::client::legacy::connect;
|
||||||
|
|
||||||
|
use sparse_actions::payload_types::Parameters;
|
||||||
|
|
||||||
|
use crate::{adapter, error};
|
||||||
|
|
||||||
|
pub struct NetInterfaceHandle {
|
||||||
|
net: Arc<Mutex<(SocketSet<'static>, crate::socket::RawSocket, Interface)>>,
|
||||||
|
tcp_handle: SocketHandle,
|
||||||
|
|
||||||
|
close_background: broadcast::Sender<()>,
|
||||||
|
background_process: JoinHandle<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for NetInterfaceHandle {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("Running drop for net interface handle; {} copies exist", Arc::strong_count(&self.net));
|
||||||
|
let _ = self.close_background.send(());
|
||||||
|
self.background_process.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncRead for NetInterfaceHandle {
|
||||||
|
fn poll_read(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
tbuf: &mut tokio::io::ReadBuf<'_>,
|
||||||
|
) -> Poll<Result<(), std::io::Error>> {
|
||||||
|
let this = self.get_mut();
|
||||||
|
let Ok(mut inner) = this.net.lock() else {
|
||||||
|
return Poll::Ready(Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::BrokenPipe,
|
||||||
|
"mutex for tcp connection is poisoned",
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
let (ref mut s_guard, _, _) = *inner;
|
||||||
|
|
||||||
|
let socket = s_guard.get_mut::<Socket>(this.tcp_handle);
|
||||||
|
|
||||||
|
let has_data = socket.can_recv();
|
||||||
|
while socket.can_recv() {
|
||||||
|
let buf = match socket.recv(|buf| (buf.len(), buf.to_vec())) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(RecvError::InvalidState) => {
|
||||||
|
return Poll::Ready(Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::NetworkDown,
|
||||||
|
"received InvalidState from smoltcp",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Err(RecvError::Finished) => {
|
||||||
|
return Poll::Ready(Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::BrokenPipe,
|
||||||
|
"tried reading from finished connection",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
tbuf.put_slice(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.register_recv_waker(cx.waker());
|
||||||
|
|
||||||
|
if has_data {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncWrite for NetInterfaceHandle {
|
||||||
|
fn poll_write(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
src: &[u8],
|
||||||
|
) -> Poll<std::io::Result<usize>> {
|
||||||
|
let this = self.get_mut();
|
||||||
|
let Ok(mut inner) = this.net.lock() else {
|
||||||
|
return Poll::Ready(Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::BrokenPipe,
|
||||||
|
"mutex for tcp connection is poisoned",
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
let (ref mut s_guard, _, _) = *inner;
|
||||||
|
|
||||||
|
let socket = s_guard.get_mut::<Socket>(this.tcp_handle);
|
||||||
|
|
||||||
|
socket.register_send_waker(cx.waker());
|
||||||
|
|
||||||
|
if socket.can_send() {
|
||||||
|
let to_send = socket.send_capacity().min(src.len());
|
||||||
|
match socket.send_slice(&src[..to_send]) {
|
||||||
|
Ok(s) => Poll::Ready(Ok(s)),
|
||||||
|
Err(SendError::InvalidState) => {
|
||||||
|
return Poll::Ready(Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::NetworkDown,
|
||||||
|
"received InvalidState from smoltcp",
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
|
||||||
|
let this = self.get_mut();
|
||||||
|
let Ok(mut inner) = this.net.lock() else {
|
||||||
|
return Poll::Ready(Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::BrokenPipe,
|
||||||
|
"mutex for tcp connection is poisoned",
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
let (ref mut s_guard, _, _) = *inner;
|
||||||
|
|
||||||
|
let socket = s_guard.get_mut::<Socket>(this.tcp_handle);
|
||||||
|
socket.close();
|
||||||
|
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl connect::Connection for NetInterfaceHandle {
|
||||||
|
fn connected(&self) -> connect::Connected {
|
||||||
|
connect::Connected::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn setup_network<T>(
|
||||||
|
adapter: T,
|
||||||
|
parameters: Parameters,
|
||||||
|
) -> Result<NetInterfaceHandle, error::BeaconError>
|
||||||
|
where
|
||||||
|
T: adapter::BeaconAdapter + Clone + Send + 'static,
|
||||||
|
{
|
||||||
|
let net_info = tokio::task::spawn_blocking({
|
||||||
|
let adapter = adapter.clone();
|
||||||
|
move || adapter.networking_info()
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
let (interface, gateway_ip, mac_address, source_ip, netmask) = match unsafe {
|
||||||
|
parameters.source_ip.custom_networking.mode
|
||||||
|
} {
|
||||||
|
0 => {
|
||||||
|
// custom networking
|
||||||
|
let interface_name = unsafe {
|
||||||
|
¶meters.source_ip.custom_networking.interface
|
||||||
|
[..parameters.source_ip.custom_networking.interface_len as usize]
|
||||||
|
};
|
||||||
|
|
||||||
|
let interface = if interface_name.is_empty() {
|
||||||
|
let Some(default_route) = net_info.routes.iter().find(|r| r.network.1 == 0) else {
|
||||||
|
return Err(error::BeaconError::NoDefaultRoute);
|
||||||
|
};
|
||||||
|
|
||||||
|
&net_info.interfaces[default_route.interface_index]
|
||||||
|
} else {
|
||||||
|
net_info
|
||||||
|
.interfaces
|
||||||
|
.iter()
|
||||||
|
.find(|intf| intf.name == interface_name)
|
||||||
|
.ok_or(error::BeaconError::NoDefaultRoute)?
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
(
|
||||||
|
interface,
|
||||||
|
Ipv4Addr::new(
|
||||||
|
parameters.source_ip.custom_networking.gateway.a,
|
||||||
|
parameters.source_ip.custom_networking.gateway.b,
|
||||||
|
parameters.source_ip.custom_networking.gateway.c,
|
||||||
|
parameters.source_ip.custom_networking.gateway.d,
|
||||||
|
),
|
||||||
|
parameters.source_ip.custom_networking.source_mac.clone(),
|
||||||
|
Ipv4Addr::new(
|
||||||
|
parameters.source_ip.custom_networking.source_ip.a,
|
||||||
|
parameters.source_ip.custom_networking.source_ip.b,
|
||||||
|
parameters.source_ip.custom_networking.source_ip.c,
|
||||||
|
parameters.source_ip.custom_networking.source_ip.d,
|
||||||
|
),
|
||||||
|
parameters.source_ip.custom_networking.netmask as u8,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
// host networking
|
||||||
|
let Some(default_route) = net_info.routes.iter().find(|r| r.network.1 == 0) else {
|
||||||
|
return Err(error::BeaconError::NoDefaultRoute);
|
||||||
|
};
|
||||||
|
|
||||||
|
let default_route_if = &net_info.interfaces[default_route.interface_index];
|
||||||
|
|
||||||
|
(
|
||||||
|
default_route_if,
|
||||||
|
default_route.gateway.0,
|
||||||
|
default_route_if.mac_addr.clone(),
|
||||||
|
unsafe {
|
||||||
|
Ipv4Addr::new(
|
||||||
|
parameters.source_ip.use_host_networking.source_ip.a,
|
||||||
|
parameters.source_ip.use_host_networking.source_ip.b,
|
||||||
|
parameters.source_ip.use_host_networking.source_ip.c,
|
||||||
|
parameters.source_ip.use_host_networking.source_ip.d,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
default_route.gateway.1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => panic!("Corrupted parameters present!"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let go_promisc = mac_address != [0, 0, 0, 0, 0, 0];
|
||||||
|
let mac_address = Some(mac_address)
|
||||||
|
.filter(|smac| smac != &[0, 0, 0, 0, 0, 0])
|
||||||
|
.unwrap_or(interface.mac_addr);
|
||||||
|
|
||||||
|
let local_port = 49152 + rand::random::<u16>() % 16384;
|
||||||
|
let mut device = crate::socket::RawSocket::new::<T>(interface, go_promisc, local_port)?;
|
||||||
|
|
||||||
|
let mut config = Config::new(EthernetAddress(mac_address).into());
|
||||||
|
config.random_seed = rand::random();
|
||||||
|
|
||||||
|
let mut iface = Interface::new(config, &mut device, Instant::now());
|
||||||
|
iface.update_ip_addrs(|addrs| {
|
||||||
|
addrs
|
||||||
|
.push(IpCidr::new(source_ip.into(), netmask))
|
||||||
|
.expect("could not add new IP address");
|
||||||
|
});
|
||||||
|
iface
|
||||||
|
.routes_mut()
|
||||||
|
.add_default_ipv4_route(gateway_ip.into())
|
||||||
|
.expect("did not expect route table to be full");
|
||||||
|
|
||||||
|
let tcp_rx_buffer = SocketBuffer::new(vec![0; 8192]);
|
||||||
|
let tcp_tx_buffer = SocketBuffer::new(vec![0; 8192]);
|
||||||
|
let tcp_socket = Socket::new(tcp_rx_buffer, tcp_tx_buffer);
|
||||||
|
|
||||||
|
let mut sockets = SocketSet::new(vec![]);
|
||||||
|
let tcp_handle = sockets.add(tcp_socket);
|
||||||
|
|
||||||
|
let mut active = false;
|
||||||
|
let ready_wait = device.get_ready_wait_callback();
|
||||||
|
|
||||||
|
let (close_background, mut close_background_recv) = broadcast::channel(1);
|
||||||
|
|
||||||
|
let destination = (
|
||||||
|
Ipv4Address::new(
|
||||||
|
parameters.destination_ip.a,
|
||||||
|
parameters.destination_ip.b,
|
||||||
|
parameters.destination_ip.c,
|
||||||
|
parameters.destination_ip.d,
|
||||||
|
),
|
||||||
|
parameters.destination_port,
|
||||||
|
);
|
||||||
|
|
||||||
|
while !active {
|
||||||
|
let timestamp = Instant::now();
|
||||||
|
iface.poll(timestamp, &mut device, &mut sockets);
|
||||||
|
|
||||||
|
let cx = iface.context();
|
||||||
|
|
||||||
|
let socket = sockets.get_mut::<Socket>(tcp_handle);
|
||||||
|
if !socket.is_active() {
|
||||||
|
socket.connect(cx, destination, local_port)?;
|
||||||
|
}
|
||||||
|
active = socket.is_active() && socket.state() == State::Established;
|
||||||
|
|
||||||
|
ready_wait.wait(iface.poll_delay(timestamp, &sockets).map(Into::into))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let net = Arc::new(Mutex::new((sockets, device, iface)));
|
||||||
|
|
||||||
|
let background_process = spawn_blocking({
|
||||||
|
let net = Arc::clone(&net);
|
||||||
|
|
||||||
|
move || {
|
||||||
|
loop {
|
||||||
|
if close_background_recv.try_recv().is_ok() {
|
||||||
|
println!("Running drop for background thread; {} copies exist", Arc::strong_count(&net));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let delay = {
|
||||||
|
let Ok(mut guard) = net.lock() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let (ref mut s_guard, ref mut d_guard, ref mut i_guard) = *guard;
|
||||||
|
|
||||||
|
let timestamp = Instant::now();
|
||||||
|
i_guard.poll(timestamp, d_guard, s_guard);
|
||||||
|
|
||||||
|
i_guard.poll_delay(timestamp, s_guard)
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = ready_wait.wait(delay.map(Into::into));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(NetInterfaceHandle {
|
||||||
|
net,
|
||||||
|
tcp_handle,
|
||||||
|
|
||||||
|
close_background,
|
||||||
|
background_process,
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -15,3 +15,5 @@ serde = "1.0"
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
axum-server = { version = "^0.7", features = ["tokio-rustls", "tls-rustls"] }
|
axum-server = { version = "^0.7", features = ["tokio-rustls", "tls-rustls"] }
|
||||||
rustls = "0.23"
|
rustls = "0.23"
|
||||||
|
rcgen = "0.13.2"
|
||||||
|
rustls-pki-types = "1.11.0"
|
||||||
|
|||||||
@ -5,6 +5,8 @@ pub enum Error {
|
|||||||
TokioJoin(tokio::task::JoinError),
|
TokioJoin(tokio::task::JoinError),
|
||||||
Io(std::io::Error),
|
Io(std::io::Error),
|
||||||
Rustls(rustls::Error),
|
Rustls(rustls::Error),
|
||||||
|
Rcgen(rcgen::Error),
|
||||||
|
WebPki(rustls::client::VerifierBuilderError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Error {
|
impl std::fmt::Display for Error {
|
||||||
@ -25,6 +27,12 @@ impl std::fmt::Display for Error {
|
|||||||
Error::Rustls(err) => {
|
Error::Rustls(err) => {
|
||||||
write!(f, "rustls error: {err:?}")
|
write!(f, "rustls error: {err:?}")
|
||||||
}
|
}
|
||||||
|
Error::Rcgen(err) => {
|
||||||
|
write!(f, "rcgen error: {err:?}")
|
||||||
|
}
|
||||||
|
Error::WebPki(err) => {
|
||||||
|
write!(f, "webpki error: {err:?}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,6 +44,8 @@ impl std::error::Error for Error {
|
|||||||
Error::TokioJoin(err) => Some(err),
|
Error::TokioJoin(err) => Some(err),
|
||||||
Error::Io(err) => Some(err),
|
Error::Io(err) => Some(err),
|
||||||
Error::Rustls(err) => Some(err),
|
Error::Rustls(err) => Some(err),
|
||||||
|
Error::Rcgen(err) => Some(err),
|
||||||
|
Error::WebPki(err) => Some(err),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,3 +82,15 @@ impl From<rustls::Error> for Error {
|
|||||||
Self::Rustls(err)
|
Self::Rustls(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<rcgen::Error> for Error {
|
||||||
|
fn from(err: rcgen::Error) -> Self {
|
||||||
|
Self::Rcgen(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<rustls::client::VerifierBuilderError> for Error {
|
||||||
|
fn from(err: rustls::client::VerifierBuilderError) -> Self {
|
||||||
|
Self::WebPki(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use axum::routing::{Router, get, post};
|
use axum::routing::{Router, get, post};
|
||||||
|
use rcgen::{Certificate, CertificateParams, KeyPair};
|
||||||
|
use rustls::{server::WebPkiClientVerifier, RootCertStore};
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
@ -114,7 +116,29 @@ pub async fn start_listener(
|
|||||||
|
|
||||||
let hidden_app = Router::new().nest("/hidden_sparse", app);
|
let hidden_app = Router::new().nest("/hidden_sparse", app);
|
||||||
|
|
||||||
let keypair = match rustls::pki_types::PrivateKeyDer::try_from(listener.privkey.clone()) {
|
let ca_cert = rustls::pki_types::CertificateDer::from(listener.certificate.clone());
|
||||||
|
|
||||||
|
let (keypair, cert) = {
|
||||||
|
let ca_keypair = KeyPair::from_der_and_sign_algo(
|
||||||
|
&rustls_pki_types::PrivateKeyDer::try_from(&*listener.privkey)
|
||||||
|
.map_err(|_| rcgen::Error::CouldNotParseCertificate)?,
|
||||||
|
&rcgen::PKCS_ECDSA_P256_SHA256,
|
||||||
|
)?;
|
||||||
|
let ca_params = CertificateParams::from_ca_cert_der(&(*listener.certificate).into())
|
||||||
|
.map_err(|_| rcgen::Error::CouldNotParseCertificate)?;
|
||||||
|
let ca_cert = ca_params.self_signed(&ca_keypair)?;
|
||||||
|
|
||||||
|
let server_key = KeyPair::generate()?;
|
||||||
|
let Ok(server_params) = CertificateParams::new(
|
||||||
|
vec![listener.domain_name.clone()]
|
||||||
|
) else {
|
||||||
|
return Err(crate::error::Error::Generic(format!(
|
||||||
|
"Could not generate new server keychain"
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
let server_cert = server_params.signed_by(&server_key, &ca_cert, &ca_keypair)?;
|
||||||
|
|
||||||
|
let keypair = match rustls::pki_types::PrivateKeyDer::try_from(server_key.serialize_der()) {
|
||||||
Ok(pk) => pk,
|
Ok(pk) => pk,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(crate::error::Error::Generic(format!(
|
return Err(crate::error::Error::Generic(format!(
|
||||||
@ -122,10 +146,19 @@ pub async fn start_listener(
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let cert = rustls::pki_types::CertificateDer::from(listener.certificate.clone());
|
let cert = server_cert.into();
|
||||||
|
|
||||||
|
(keypair, cert)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut root_store = RootCertStore::empty();
|
||||||
|
root_store.add(ca_cert)?;
|
||||||
|
|
||||||
|
let client_verifier = WebPkiClientVerifier::builder(root_store.into())
|
||||||
|
.build()?;
|
||||||
|
|
||||||
let mut tls_config = rustls::ServerConfig::builder()
|
let mut tls_config = rustls::ServerConfig::builder()
|
||||||
.with_no_client_auth()
|
.with_client_cert_verifier(client_verifier)
|
||||||
.with_single_cert(vec![cert], keypair)?;
|
.with_single_cert(vec![cert], keypair)?;
|
||||||
tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
||||||
|
|
||||||
|
|||||||
@ -107,7 +107,7 @@ pub async fn add_template(
|
|||||||
let db = expect_context::<SqlitePool>();
|
let db = expect_context::<SqlitePool>();
|
||||||
|
|
||||||
let listener = sqlx::query!(
|
let listener = sqlx::query!(
|
||||||
"SELECT certificate, privkey FROM beacon_listener WHERE listener_id = ?",
|
"SELECT domain_name, certificate, privkey FROM beacon_listener WHERE listener_id = ?",
|
||||||
listener_id
|
listener_id
|
||||||
)
|
)
|
||||||
.fetch_one(&db)
|
.fetch_one(&db)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user