mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-02-28 14:33:37 -08:00
feat(API): SupplyKeypair endpoint
This commit is contained in:
@@ -22,6 +22,10 @@ required-features = ["experiment_api", "internal_bin_gen_ipc_msg_types"]
|
|||||||
name = "api-integration-tests"
|
name = "api-integration-tests"
|
||||||
required-features = ["experiment_api", "internal_testing"]
|
required-features = ["experiment_api", "internal_testing"]
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "api-integration-tests-supply-keypair"
|
||||||
|
required-features = ["experiment_api", "internal_testing"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "handshake"
|
name = "handshake"
|
||||||
harness = false
|
harness = false
|
||||||
@@ -67,6 +71,7 @@ stacker = { workspace = true }
|
|||||||
serial_test = {workspace = true}
|
serial_test = {workspace = true}
|
||||||
procspawn = {workspace = true}
|
procspawn = {workspace = true}
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
rustix = {workspace = true}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
experiment_memfd_secret = ["rosenpass-wireguard-broker/experiment_memfd_secret"]
|
experiment_memfd_secret = ["rosenpass-wireguard-broker/experiment_memfd_secret"]
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
use std::{borrow::BorrowMut, collections::VecDeque, os::fd::OwnedFd};
|
use std::{borrow::BorrowMut, collections::VecDeque, os::fd::OwnedFd};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
use rosenpass_to::{ops::copy_slice, To};
|
use rosenpass_to::{ops::copy_slice, To};
|
||||||
|
use rosenpass_util::{fd::FdIo, functional::run, io::ReadExt, mem::DiscardResultExt};
|
||||||
|
|
||||||
use crate::app_server::AppServer;
|
use crate::{app_server::AppServer, protocol::BuildCryptoServer};
|
||||||
|
|
||||||
use super::Server as ApiServer;
|
use super::{supply_keypair_response_status, Server as ApiServer};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ApiHandler {
|
pub struct ApiHandler {
|
||||||
@@ -25,6 +27,42 @@ pub trait ApiHandlerContext {
|
|||||||
fn app_server_mut(&mut self) -> &mut AppServer;
|
fn app_server_mut(&mut self) -> &mut AppServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
#[error("Error in SupplyKeypair")]
|
||||||
|
struct SupplyKeypairError {
|
||||||
|
status: u128,
|
||||||
|
#[source]
|
||||||
|
cause: anyhow::Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
trait SupplyKeypairErrorExt<T> {
|
||||||
|
fn e_custom(self, status: u128) -> Result<T, SupplyKeypairError>;
|
||||||
|
fn einternal(self) -> Result<T, SupplyKeypairError>;
|
||||||
|
fn ealready_supplied(self) -> Result<T, SupplyKeypairError>;
|
||||||
|
fn einvalid_req(self) -> Result<T, SupplyKeypairError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E: Into<anyhow::Error>> SupplyKeypairErrorExt<T> for Result<T, E> {
|
||||||
|
fn e_custom(self, status: u128) -> Result<T, SupplyKeypairError> {
|
||||||
|
self.map_err(|e| SupplyKeypairError {
|
||||||
|
status,
|
||||||
|
cause: e.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn einternal(self) -> Result<T, SupplyKeypairError> {
|
||||||
|
self.e_custom(supply_keypair_response_status::INTERNAL_ERROR)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ealready_supplied(self) -> Result<T, SupplyKeypairError> {
|
||||||
|
self.e_custom(supply_keypair_response_status::KEYPAIR_ALREADY_SUPPLIED)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn einvalid_req(self) -> Result<T, SupplyKeypairError> {
|
||||||
|
self.e_custom(supply_keypair_response_status::INVALID_REQUEST)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> ApiServer for T
|
impl<T> ApiServer for T
|
||||||
where
|
where
|
||||||
T: ?Sized + ApiHandlerContext,
|
T: ?Sized + ApiHandlerContext,
|
||||||
@@ -39,4 +77,98 @@ where
|
|||||||
copy_slice(&req.echo).to(&mut res.echo);
|
copy_slice(&req.echo).to(&mut res.echo);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn supply_keypair(
|
||||||
|
&mut self,
|
||||||
|
req: &super::SupplyKeypairRequest,
|
||||||
|
req_fds: &mut VecDeque<OwnedFd>,
|
||||||
|
res: &mut super::SupplyKeypairResponse,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let outcome: Result<(), SupplyKeypairError> = run(|| {
|
||||||
|
// Acquire the file descriptors
|
||||||
|
let mut sk_io = FdIo(
|
||||||
|
req_fds
|
||||||
|
.front()
|
||||||
|
.context("First file descriptor, secret key, missing.")
|
||||||
|
.einvalid_req()?,
|
||||||
|
);
|
||||||
|
let mut pk_io = FdIo(
|
||||||
|
req_fds
|
||||||
|
.get(1)
|
||||||
|
.context("Second file descriptor, public key, missing.")
|
||||||
|
.einvalid_req()?,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Actually read the secrets
|
||||||
|
let mut sk = crate::protocol::SSk::zero();
|
||||||
|
sk_io.read_exact_til_end(sk.secret_mut()).einvalid_req()?;
|
||||||
|
|
||||||
|
let mut pk = crate::protocol::SPk::zero();
|
||||||
|
pk_io.read_exact_til_end(pk.borrow_mut()).einvalid_req()?;
|
||||||
|
|
||||||
|
// Retrieve the construction site
|
||||||
|
let construction_site = self.app_server_mut().crypto_site.borrow_mut();
|
||||||
|
|
||||||
|
// Retrieve the builder
|
||||||
|
use rosenpass_util::build::ConstructionSite as C;
|
||||||
|
let maybe_builder = match construction_site {
|
||||||
|
C::Builder(builder) => Some(builder),
|
||||||
|
C::Product(_) => None,
|
||||||
|
C::Void => {
|
||||||
|
return Err(anyhow::Error::msg("CryptoServer construction side is void"))
|
||||||
|
.einternal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Retrieve a reference to the keypair
|
||||||
|
let Some(BuildCryptoServer {
|
||||||
|
ref mut keypair, ..
|
||||||
|
}) = maybe_builder
|
||||||
|
else {
|
||||||
|
return Err(anyhow::Error::msg("CryptoServer already built")).ealready_supplied();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Supply the keypair to the CryptoServer
|
||||||
|
keypair
|
||||||
|
.insert(crate::protocol::Keypair { sk, pk })
|
||||||
|
.discard_result();
|
||||||
|
|
||||||
|
// Actually construct the CryptoServer
|
||||||
|
construction_site
|
||||||
|
.erect()
|
||||||
|
.map_err(|e| anyhow::Error::msg(format!("Error erecting the CryptoServer {e:?}")))
|
||||||
|
.einternal()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle errors
|
||||||
|
use supply_keypair_response_status as status;
|
||||||
|
let status = match outcome {
|
||||||
|
Ok(()) => status::OK,
|
||||||
|
Err(e) => {
|
||||||
|
let lvl = match e.status {
|
||||||
|
status::INTERNAL_ERROR => log::Level::Warn,
|
||||||
|
_ => log::Level::Debug,
|
||||||
|
};
|
||||||
|
|
||||||
|
log::log!(
|
||||||
|
lvl,
|
||||||
|
"Error while processing API Request.\n Request: {:?}\n Error: {:?}",
|
||||||
|
req,
|
||||||
|
e.cause
|
||||||
|
);
|
||||||
|
|
||||||
|
if e.status == status::INTERNAL_ERROR {
|
||||||
|
return Err(e.cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.status
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
res.payload.status = status;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use rosenpass_util::zerocopy::{RefMaker, ZerocopySliceExt};
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
PingRequest, PingResponse, RawMsgType, RefMakerRawMsgTypeExt, RequestMsgType, RequestRef,
|
PingRequest, PingResponse, RawMsgType, RefMakerRawMsgTypeExt, RequestMsgType, RequestRef,
|
||||||
ResponseMsgType, ResponseRef,
|
ResponseMsgType, ResponseRef, SupplyKeypairRequest, SupplyKeypairResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait ByteSliceRefExt: ByteSlice {
|
pub trait ByteSliceRefExt: ByteSlice {
|
||||||
@@ -111,6 +111,38 @@ pub trait ByteSliceRefExt: ByteSlice {
|
|||||||
fn ping_response_from_suffix(self) -> anyhow::Result<Ref<Self, PingResponse>> {
|
fn ping_response_from_suffix(self) -> anyhow::Result<Ref<Self, PingResponse>> {
|
||||||
self.zk_parse_suffix()
|
self.zk_parse_suffix()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn supply_keypair_request(self) -> anyhow::Result<Ref<Self, SupplyKeypairRequest>> {
|
||||||
|
self.zk_parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supply_keypair_request_from_prefix(self) -> anyhow::Result<Ref<Self, SupplyKeypairRequest>> {
|
||||||
|
self.zk_parse_prefix()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supply_keypair_request_from_suffix(self) -> anyhow::Result<Ref<Self, SupplyKeypairRequest>> {
|
||||||
|
self.zk_parse_suffix()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supply_keypair_response_maker(self) -> RefMaker<Self, SupplyKeypairResponse> {
|
||||||
|
self.zk_ref_maker()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supply_keypair_response(self) -> anyhow::Result<Ref<Self, SupplyKeypairResponse>> {
|
||||||
|
self.zk_parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supply_keypair_response_from_prefix(
|
||||||
|
self,
|
||||||
|
) -> anyhow::Result<Ref<Self, SupplyKeypairResponse>> {
|
||||||
|
self.zk_parse_prefix()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supply_keypair_response_from_suffix(
|
||||||
|
self,
|
||||||
|
) -> anyhow::Result<Ref<Self, SupplyKeypairResponse>> {
|
||||||
|
self.zk_parse_suffix()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: ByteSlice> ByteSliceRefExt for B {}
|
impl<B: ByteSlice> ByteSliceRefExt for B {}
|
||||||
|
|||||||
@@ -14,6 +14,13 @@ pub const PING_REQUEST: RawMsgType =
|
|||||||
pub const PING_RESPONSE: RawMsgType =
|
pub const PING_RESPONSE: RawMsgType =
|
||||||
RawMsgType::from_le_bytes(hex!("4ec7 f6f0 2bbc ba64 48f1 da14 c7cf 0260"));
|
RawMsgType::from_le_bytes(hex!("4ec7 f6f0 2bbc ba64 48f1 da14 c7cf 0260"));
|
||||||
|
|
||||||
|
// hash domain hash of: Rosenpass IPC API -> Rosenpass Protocol Server -> Supply Keypair Request
|
||||||
|
const SUPPLY_KEYPAIR_REQUEST: RawMsgType =
|
||||||
|
RawMsgType::from_le_bytes(hex!("ac91 a5a6 4f4b 21d0 ac7f 9b55 74f7 3529"));
|
||||||
|
// hash domain hash of: Rosenpass IPC API -> Rosenpass Protocol Server -> Supply Keypair Response
|
||||||
|
const SUPPLY_KEYPAIR_RESPONSE: RawMsgType =
|
||||||
|
RawMsgType::from_le_bytes(hex!("f2dc 49bd e261 5f10 40b7 3c16 ec61 edb9"));
|
||||||
|
|
||||||
pub trait MessageAttributes {
|
pub trait MessageAttributes {
|
||||||
fn message_size(&self) -> usize;
|
fn message_size(&self) -> usize;
|
||||||
}
|
}
|
||||||
@@ -21,17 +28,20 @@ pub trait MessageAttributes {
|
|||||||
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||||
pub enum RequestMsgType {
|
pub enum RequestMsgType {
|
||||||
Ping,
|
Ping,
|
||||||
|
SupplyKeypair,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||||
pub enum ResponseMsgType {
|
pub enum ResponseMsgType {
|
||||||
Ping,
|
Ping,
|
||||||
|
SupplyKeypair,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageAttributes for RequestMsgType {
|
impl MessageAttributes for RequestMsgType {
|
||||||
fn message_size(&self) -> usize {
|
fn message_size(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::Ping => std::mem::size_of::<super::PingRequest>(),
|
Self::Ping => std::mem::size_of::<super::PingRequest>(),
|
||||||
|
Self::SupplyKeypair => std::mem::size_of::<super::SupplyKeypairRequest>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,6 +50,7 @@ impl MessageAttributes for ResponseMsgType {
|
|||||||
fn message_size(&self) -> usize {
|
fn message_size(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::Ping => std::mem::size_of::<super::PingResponse>(),
|
Self::Ping => std::mem::size_of::<super::PingResponse>(),
|
||||||
|
Self::SupplyKeypair => std::mem::size_of::<super::SupplyKeypairResponse>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,6 +62,7 @@ impl TryFrom<RawMsgType> for RequestMsgType {
|
|||||||
use RequestMsgType as E;
|
use RequestMsgType as E;
|
||||||
Ok(match value {
|
Ok(match value {
|
||||||
self::PING_REQUEST => E::Ping,
|
self::PING_REQUEST => E::Ping,
|
||||||
|
self::SUPPLY_KEYPAIR_REQUEST => E::SupplyKeypair,
|
||||||
_ => return Err(InvalidApiMessageType(value)),
|
_ => return Err(InvalidApiMessageType(value)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -61,6 +73,7 @@ impl From<RequestMsgType> for RawMsgType {
|
|||||||
use RequestMsgType as E;
|
use RequestMsgType as E;
|
||||||
match val {
|
match val {
|
||||||
E::Ping => self::PING_REQUEST,
|
E::Ping => self::PING_REQUEST,
|
||||||
|
E::SupplyKeypair => self::SUPPLY_KEYPAIR_REQUEST,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,6 +85,7 @@ impl TryFrom<RawMsgType> for ResponseMsgType {
|
|||||||
use ResponseMsgType as E;
|
use ResponseMsgType as E;
|
||||||
Ok(match value {
|
Ok(match value {
|
||||||
self::PING_RESPONSE => E::Ping,
|
self::PING_RESPONSE => E::Ping,
|
||||||
|
self::SUPPLY_KEYPAIR_RESPONSE => E::SupplyKeypair,
|
||||||
_ => return Err(InvalidApiMessageType(value)),
|
_ => return Err(InvalidApiMessageType(value)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -82,6 +96,7 @@ impl From<ResponseMsgType> for RawMsgType {
|
|||||||
use ResponseMsgType as E;
|
use ResponseMsgType as E;
|
||||||
match val {
|
match val {
|
||||||
E::Ping => self::PING_RESPONSE,
|
E::Ping => self::PING_RESPONSE,
|
||||||
|
E::SupplyKeypair => self::SUPPLY_KEYPAIR_RESPONSE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,3 +95,89 @@ impl Message for PingResponse {
|
|||||||
self.msg_type = Self::MESSAGE_TYPE.into();
|
self.msg_type = Self::MESSAGE_TYPE.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(packed)]
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
|
||||||
|
pub struct SupplyKeypairRequestPayload {}
|
||||||
|
|
||||||
|
pub type SupplyKeypairRequest = RequestEnvelope<SupplyKeypairRequestPayload>;
|
||||||
|
|
||||||
|
impl Default for SupplyKeypairRequest {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SupplyKeypairRequest {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::from_payload(SupplyKeypairRequestPayload {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for SupplyKeypairRequest {
|
||||||
|
type Payload = SupplyKeypairRequestPayload;
|
||||||
|
type MessageClass = RequestMsgType;
|
||||||
|
const MESSAGE_TYPE: Self::MessageClass = RequestMsgType::SupplyKeypair;
|
||||||
|
|
||||||
|
fn from_payload(payload: Self::Payload) -> Self {
|
||||||
|
Self {
|
||||||
|
msg_type: Self::MESSAGE_TYPE.into(),
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup<B: ByteSliceMut>(buf: B) -> anyhow::Result<Ref<B, Self>> {
|
||||||
|
let mut r: Ref<B, Self> = buf.zk_zeroized()?;
|
||||||
|
r.init();
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self) {
|
||||||
|
self.msg_type = Self::MESSAGE_TYPE.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod supply_keypair_response_status {
|
||||||
|
pub const OK: u128 = 0;
|
||||||
|
pub const KEYPAIR_ALREADY_SUPPLIED: u128 = 1;
|
||||||
|
pub const INTERNAL_ERROR: u128 = 2;
|
||||||
|
pub const INVALID_REQUEST: u128 = 3;
|
||||||
|
pub const IO_ERROR: u128 = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed)]
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
|
||||||
|
pub struct SupplyKeypairResponsePayload {
|
||||||
|
pub status: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type SupplyKeypairResponse = ResponseEnvelope<SupplyKeypairResponsePayload>;
|
||||||
|
|
||||||
|
impl SupplyKeypairResponse {
|
||||||
|
pub fn new(status: u128) -> Self {
|
||||||
|
Self::from_payload(SupplyKeypairResponsePayload { status })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for SupplyKeypairResponse {
|
||||||
|
type Payload = SupplyKeypairResponsePayload;
|
||||||
|
type MessageClass = ResponseMsgType;
|
||||||
|
const MESSAGE_TYPE: Self::MessageClass = ResponseMsgType::SupplyKeypair;
|
||||||
|
|
||||||
|
fn from_payload(payload: Self::Payload) -> Self {
|
||||||
|
Self {
|
||||||
|
msg_type: Self::MESSAGE_TYPE.into(),
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup<B: ByteSliceMut>(buf: B) -> anyhow::Result<Ref<B, Self>> {
|
||||||
|
let mut r: Ref<B, Self> = buf.zk_zeroized()?;
|
||||||
|
r.init();
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self) {
|
||||||
|
self.msg_type = Self::MESSAGE_TYPE.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ impl<B: ByteSlice> RequestRef<B> {
|
|||||||
pub fn message_type(&self) -> RequestMsgType {
|
pub fn message_type(&self) -> RequestMsgType {
|
||||||
match self {
|
match self {
|
||||||
Self::Ping(_) => RequestMsgType::Ping,
|
Self::Ping(_) => RequestMsgType::Ping,
|
||||||
|
Self::SupplyKeypair(_) => RequestMsgType::SupplyKeypair,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,6 +49,9 @@ impl<B: ByteSlice> RequestRefMaker<B> {
|
|||||||
fn parse(self) -> anyhow::Result<RequestRef<B>> {
|
fn parse(self) -> anyhow::Result<RequestRef<B>> {
|
||||||
Ok(match self.msg_type {
|
Ok(match self.msg_type {
|
||||||
RequestMsgType::Ping => RequestRef::Ping(self.buf.ping_request()?),
|
RequestMsgType::Ping => RequestRef::Ping(self.buf.ping_request()?),
|
||||||
|
RequestMsgType::SupplyKeypair => {
|
||||||
|
RequestRef::SupplyKeypair(self.buf.supply_keypair_request()?)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +86,7 @@ impl<B: ByteSlice> RequestRefMaker<B> {
|
|||||||
|
|
||||||
pub enum RequestRef<B> {
|
pub enum RequestRef<B> {
|
||||||
Ping(Ref<B, PingRequest>),
|
Ping(Ref<B, PingRequest>),
|
||||||
|
SupplyKeypair(Ref<B, super::SupplyKeypairRequest>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B> RequestRef<B>
|
impl<B> RequestRef<B>
|
||||||
@@ -91,6 +96,7 @@ where
|
|||||||
pub fn bytes(&self) -> &[u8] {
|
pub fn bytes(&self) -> &[u8] {
|
||||||
match self {
|
match self {
|
||||||
Self::Ping(r) => r.bytes(),
|
Self::Ping(r) => r.bytes(),
|
||||||
|
Self::SupplyKeypair(r) => r.bytes(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,6 +108,7 @@ where
|
|||||||
pub fn bytes_mut(&mut self) -> &[u8] {
|
pub fn bytes_mut(&mut self) -> &[u8] {
|
||||||
match self {
|
match self {
|
||||||
Self::Ping(r) => r.bytes_mut(),
|
Self::Ping(r) => r.bytes_mut(),
|
||||||
|
Self::SupplyKeypair(r) => r.bytes_mut(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,10 +42,23 @@ impl ResponseMsg for PingResponse {
|
|||||||
type RequestMsg = PingRequest;
|
type RequestMsg = PingRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RequestMsg for super::SupplyKeypairRequest {
|
||||||
|
type ResponseMsg = super::SupplyKeypairResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseMsg for super::SupplyKeypairResponse {
|
||||||
|
type RequestMsg = super::SupplyKeypairRequest;
|
||||||
|
}
|
||||||
|
|
||||||
pub type PingPair<B1, B2> = (Ref<B1, PingRequest>, Ref<B2, PingResponse>);
|
pub type PingPair<B1, B2> = (Ref<B1, PingRequest>, Ref<B2, PingResponse>);
|
||||||
|
pub type SupplyKeypairPair<B1, B2> = (
|
||||||
|
Ref<B1, super::SupplyKeypairRequest>,
|
||||||
|
Ref<B2, super::SupplyKeypairResponse>,
|
||||||
|
);
|
||||||
|
|
||||||
pub enum RequestResponsePair<B1, B2> {
|
pub enum RequestResponsePair<B1, B2> {
|
||||||
Ping(PingPair<B1, B2>),
|
Ping(PingPair<B1, B2>),
|
||||||
|
SupplyKeypair(SupplyKeypairPair<B1, B2>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B1, B2> From<PingPair<B1, B2>> for RequestResponsePair<B1, B2> {
|
impl<B1, B2> From<PingPair<B1, B2>> for RequestResponsePair<B1, B2> {
|
||||||
@@ -54,6 +67,12 @@ impl<B1, B2> From<PingPair<B1, B2>> for RequestResponsePair<B1, B2> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<B1, B2> From<SupplyKeypairPair<B1, B2>> for RequestResponsePair<B1, B2> {
|
||||||
|
fn from(v: SupplyKeypairPair<B1, B2>) -> Self {
|
||||||
|
RequestResponsePair::SupplyKeypair(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<B1, B2> RequestResponsePair<B1, B2>
|
impl<B1, B2> RequestResponsePair<B1, B2>
|
||||||
where
|
where
|
||||||
B1: ByteSlice,
|
B1: ByteSlice,
|
||||||
@@ -66,6 +85,11 @@ where
|
|||||||
let res = ResponseRef::Ping(res.emancipate());
|
let res = ResponseRef::Ping(res.emancipate());
|
||||||
(req, res)
|
(req, res)
|
||||||
}
|
}
|
||||||
|
Self::SupplyKeypair((req, res)) => {
|
||||||
|
let req = RequestRef::SupplyKeypair(req.emancipate());
|
||||||
|
let res = ResponseRef::SupplyKeypair(res.emancipate());
|
||||||
|
(req, res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +114,11 @@ where
|
|||||||
let res = ResponseRef::Ping(res.emancipate_mut());
|
let res = ResponseRef::Ping(res.emancipate_mut());
|
||||||
(req, res)
|
(req, res)
|
||||||
}
|
}
|
||||||
|
Self::SupplyKeypair((req, res)) => {
|
||||||
|
let req = RequestRef::SupplyKeypair(req.emancipate_mut());
|
||||||
|
let res = ResponseRef::SupplyKeypair(res.emancipate_mut());
|
||||||
|
(req, res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ impl<B: ByteSlice> ResponseRef<B> {
|
|||||||
pub fn message_type(&self) -> ResponseMsgType {
|
pub fn message_type(&self) -> ResponseMsgType {
|
||||||
match self {
|
match self {
|
||||||
Self::Ping(_) => ResponseMsgType::Ping,
|
Self::Ping(_) => ResponseMsgType::Ping,
|
||||||
|
Self::SupplyKeypair(_) => ResponseMsgType::SupplyKeypair,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,6 +37,12 @@ impl<B> From<Ref<B, PingResponse>> for ResponseRef<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<B> From<Ref<B, super::SupplyKeypairResponse>> for ResponseRef<B> {
|
||||||
|
fn from(v: Ref<B, super::SupplyKeypairResponse>) -> Self {
|
||||||
|
Self::SupplyKeypair(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<B: ByteSlice> ResponseRefMaker<B> {
|
impl<B: ByteSlice> ResponseRefMaker<B> {
|
||||||
fn new(buf: B) -> anyhow::Result<Self> {
|
fn new(buf: B) -> anyhow::Result<Self> {
|
||||||
let msg_type = buf.deref().response_msg_type_from_prefix()?;
|
let msg_type = buf.deref().response_msg_type_from_prefix()?;
|
||||||
@@ -49,6 +56,9 @@ impl<B: ByteSlice> ResponseRefMaker<B> {
|
|||||||
fn parse(self) -> anyhow::Result<ResponseRef<B>> {
|
fn parse(self) -> anyhow::Result<ResponseRef<B>> {
|
||||||
Ok(match self.msg_type {
|
Ok(match self.msg_type {
|
||||||
ResponseMsgType::Ping => ResponseRef::Ping(self.buf.ping_response()?),
|
ResponseMsgType::Ping => ResponseRef::Ping(self.buf.ping_response()?),
|
||||||
|
ResponseMsgType::SupplyKeypair => {
|
||||||
|
ResponseRef::SupplyKeypair(self.buf.supply_keypair_response()?)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +93,7 @@ impl<B: ByteSlice> ResponseRefMaker<B> {
|
|||||||
|
|
||||||
pub enum ResponseRef<B> {
|
pub enum ResponseRef<B> {
|
||||||
Ping(Ref<B, PingResponse>),
|
Ping(Ref<B, PingResponse>),
|
||||||
|
SupplyKeypair(Ref<B, super::SupplyKeypairResponse>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B> ResponseRef<B>
|
impl<B> ResponseRef<B>
|
||||||
@@ -92,6 +103,7 @@ where
|
|||||||
pub fn bytes(&self) -> &[u8] {
|
pub fn bytes(&self) -> &[u8] {
|
||||||
match self {
|
match self {
|
||||||
Self::Ping(r) => r.bytes(),
|
Self::Ping(r) => r.bytes(),
|
||||||
|
Self::SupplyKeypair(r) => r.bytes(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,6 +115,7 @@ where
|
|||||||
pub fn bytes_mut(&mut self) -> &[u8] {
|
pub fn bytes_mut(&mut self) -> &[u8] {
|
||||||
match self {
|
match self {
|
||||||
Self::Ping(r) => r.bytes_mut(),
|
Self::Ping(r) => r.bytes_mut(),
|
||||||
|
Self::SupplyKeypair(r) => r.bytes_mut(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,13 @@ pub trait Server {
|
|||||||
res: &mut PingResponse,
|
res: &mut PingResponse,
|
||||||
) -> anyhow::Result<()>;
|
) -> anyhow::Result<()>;
|
||||||
|
|
||||||
|
fn supply_keypair(
|
||||||
|
&mut self,
|
||||||
|
req: &super::SupplyKeypairRequest,
|
||||||
|
req_fds: &mut VecDeque<OwnedFd>,
|
||||||
|
res: &mut super::SupplyKeypairResponse,
|
||||||
|
) -> anyhow::Result<()>;
|
||||||
|
|
||||||
fn dispatch<ReqBuf, ResBuf>(
|
fn dispatch<ReqBuf, ResBuf>(
|
||||||
&mut self,
|
&mut self,
|
||||||
p: &mut RequestResponsePair<ReqBuf, ResBuf>,
|
p: &mut RequestResponsePair<ReqBuf, ResBuf>,
|
||||||
@@ -21,6 +28,9 @@ pub trait Server {
|
|||||||
{
|
{
|
||||||
match p {
|
match p {
|
||||||
RequestResponsePair::Ping((req, res)) => self.ping(req, req_fds, res),
|
RequestResponsePair::Ping((req, res)) => self.ping(req, req_fds, res),
|
||||||
|
RequestResponsePair::SupplyKeypair((req, res)) => {
|
||||||
|
self.supply_keypair(req, req_fds, res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +52,11 @@ pub trait Server {
|
|||||||
res.init();
|
res.init();
|
||||||
RequestResponsePair::Ping((req, res))
|
RequestResponsePair::Ping((req, res))
|
||||||
}
|
}
|
||||||
|
RequestRef::SupplyKeypair(req) => {
|
||||||
|
let mut res = res.supply_keypair_response_from_prefix()?;
|
||||||
|
res.init();
|
||||||
|
RequestResponsePair::SupplyKeypair((req, res))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
self.dispatch(&mut pair, req_fds)?;
|
self.dispatch(&mut pair, req_fds)?;
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ fn main() -> Result<()> {
|
|||||||
vec![
|
vec![
|
||||||
Tree::Leaf("Ping Request".to_owned()),
|
Tree::Leaf("Ping Request".to_owned()),
|
||||||
Tree::Leaf("Ping Response".to_owned()),
|
Tree::Leaf("Ping Response".to_owned()),
|
||||||
|
Tree::Leaf("Supply Keypair Request".to_owned()),
|
||||||
|
Tree::Leaf("Supply Keypair Response".to_owned()),
|
||||||
],
|
],
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
|
|||||||
205
rosenpass/tests/api-integration-tests-supply-keypair.rs
Normal file
205
rosenpass/tests/api-integration-tests-supply-keypair.rs
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
use std::{
|
||||||
|
io::{BufRead, BufReader},
|
||||||
|
net::ToSocketAddrs,
|
||||||
|
os::unix::net::UnixStream,
|
||||||
|
process::Stdio,
|
||||||
|
thread::sleep,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{bail, Context};
|
||||||
|
use rosenpass::api::{self, supply_keypair_response_status};
|
||||||
|
use rosenpass_util::{
|
||||||
|
file::LoadValueB64,
|
||||||
|
length_prefix_encoding::{decoder::LengthPrefixDecoder, encoder::LengthPrefixEncoder},
|
||||||
|
};
|
||||||
|
use rosenpass_util::{mio::WriteWithFileDescriptors, zerocopy::ZerocopySliceExt};
|
||||||
|
use tempfile::TempDir;
|
||||||
|
use zerocopy::AsBytes;
|
||||||
|
|
||||||
|
use rosenpass::protocol::SymKey;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn api_integration_test() -> anyhow::Result<()> {
|
||||||
|
rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets();
|
||||||
|
|
||||||
|
let dir = TempDir::with_prefix("rosenpass-api-integration-test")?;
|
||||||
|
|
||||||
|
macro_rules! tempfile {
|
||||||
|
($($lst:expr),+) => {{
|
||||||
|
let mut buf = dir.path().to_path_buf();
|
||||||
|
$(buf.push($lst);)*
|
||||||
|
buf
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
let peer_a_endpoint = "[::1]:61424";
|
||||||
|
let peer_a_osk = tempfile!("a.osk");
|
||||||
|
let peer_b_osk = tempfile!("b.osk");
|
||||||
|
|
||||||
|
use rosenpass::config;
|
||||||
|
|
||||||
|
let peer_a_keypair = config::Keypair::new(tempfile!("a.pk"), tempfile!("a.sk"));
|
||||||
|
let peer_a = config::Rosenpass {
|
||||||
|
config_file_path: tempfile!("a.config"),
|
||||||
|
keypair: Some(peer_a_keypair.clone()),
|
||||||
|
listen: peer_a_endpoint.to_socket_addrs()?.collect(), // TODO: This could collide by accident
|
||||||
|
verbosity: config::Verbosity::Verbose,
|
||||||
|
api: api::config::ApiConfig {
|
||||||
|
listen_path: vec![tempfile!("a.sock")],
|
||||||
|
listen_fd: vec![],
|
||||||
|
stream_fd: vec![],
|
||||||
|
},
|
||||||
|
peers: vec![config::RosenpassPeer {
|
||||||
|
public_key: tempfile!("b.pk"),
|
||||||
|
key_out: Some(peer_a_osk.clone()),
|
||||||
|
endpoint: None,
|
||||||
|
pre_shared_key: None,
|
||||||
|
wg: None,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
let peer_b_keypair = config::Keypair::new(tempfile!("b.pk"), tempfile!("b.sk"));
|
||||||
|
let peer_b = config::Rosenpass {
|
||||||
|
config_file_path: tempfile!("b.config"),
|
||||||
|
keypair: None,
|
||||||
|
listen: vec![],
|
||||||
|
verbosity: config::Verbosity::Verbose,
|
||||||
|
api: api::config::ApiConfig {
|
||||||
|
listen_path: vec![tempfile!("b.sock")],
|
||||||
|
listen_fd: vec![],
|
||||||
|
stream_fd: vec![],
|
||||||
|
},
|
||||||
|
peers: vec![config::RosenpassPeer {
|
||||||
|
public_key: tempfile!("a.pk"),
|
||||||
|
key_out: Some(peer_b_osk.clone()),
|
||||||
|
endpoint: Some(peer_a_endpoint.to_owned()),
|
||||||
|
pre_shared_key: None,
|
||||||
|
wg: None,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate the keys
|
||||||
|
rosenpass::cli::testing::generate_and_save_keypair(
|
||||||
|
peer_a_keypair.secret_key.clone(),
|
||||||
|
peer_a_keypair.public_key.clone(),
|
||||||
|
)?;
|
||||||
|
rosenpass::cli::testing::generate_and_save_keypair(
|
||||||
|
peer_b_keypair.secret_key.clone(),
|
||||||
|
peer_b_keypair.public_key.clone(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Write the configuration files
|
||||||
|
peer_a.commit()?;
|
||||||
|
peer_b.commit()?;
|
||||||
|
|
||||||
|
// Start peer a
|
||||||
|
let proc_a = std::process::Command::new(env!("CARGO_BIN_EXE_rosenpass"))
|
||||||
|
.args([
|
||||||
|
"exchange-config",
|
||||||
|
peer_a.config_file_path.to_str().context("")?,
|
||||||
|
])
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
// Start peer b
|
||||||
|
let proc_b = std::process::Command::new(env!("CARGO_BIN_EXE_rosenpass"))
|
||||||
|
.args([
|
||||||
|
"exchange-config",
|
||||||
|
peer_b.config_file_path.to_str().context("")?,
|
||||||
|
])
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
// Acquire stdout
|
||||||
|
let mut out_a = BufReader::new(proc_a.stdout.context("")?).lines();
|
||||||
|
let mut out_b = BufReader::new(proc_b.stdout.context("")?).lines();
|
||||||
|
|
||||||
|
// Now connect to the peers
|
||||||
|
let api_path = peer_b.api.listen_path[0].as_path();
|
||||||
|
|
||||||
|
// Wait for the socket to be created
|
||||||
|
let attempt = 0;
|
||||||
|
while !api_path.exists() {
|
||||||
|
sleep(Duration::from_millis(200));
|
||||||
|
assert!(
|
||||||
|
attempt < 50,
|
||||||
|
"Api failed to be created even after 50 seconds"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let api = UnixStream::connect(api_path)?;
|
||||||
|
|
||||||
|
// Send SupplyKeypairRequest
|
||||||
|
{
|
||||||
|
use rustix::fs::{open, Mode, OFlags};
|
||||||
|
let sk = open(peer_b_keypair.secret_key, OFlags::RDONLY, Mode::empty())?;
|
||||||
|
let pk = open(peer_b_keypair.public_key, OFlags::RDONLY, Mode::empty())?;
|
||||||
|
|
||||||
|
let mut fds = vec![&sk, &pk].into();
|
||||||
|
let mut api = WriteWithFileDescriptors::<UnixStream, _, _, _>::new(&api, &mut fds);
|
||||||
|
LengthPrefixEncoder::from_message(api::SupplyKeypairRequest::new().as_bytes())
|
||||||
|
.write_all_to_stdio(&mut api)?;
|
||||||
|
assert!(fds.is_empty(), "Failed to write all file descriptors");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read response
|
||||||
|
{
|
||||||
|
//sleep(Duration::from_secs(10));
|
||||||
|
let mut decoder = LengthPrefixDecoder::new([0u8; api::MAX_RESPONSE_LEN]);
|
||||||
|
let res = decoder.read_all_from_stdio(api)?;
|
||||||
|
let res = res.zk_parse::<api::SupplyKeypairResponse>()?;
|
||||||
|
assert_eq!(
|
||||||
|
*res,
|
||||||
|
api::SupplyKeypairResponse::new(supply_keypair_response_status::OK)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the keys to successfully exchange a key
|
||||||
|
let mut attempt = 0;
|
||||||
|
loop {
|
||||||
|
let line_a = out_a.next().context("")??;
|
||||||
|
let line_b = out_b.next().context("")??;
|
||||||
|
|
||||||
|
let words_a = line_a.split(' ').collect::<Vec<_>>();
|
||||||
|
let words_b = line_b.split(' ').collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// FIXED FIXED PEER-ID FIXED FILENAME STATUS
|
||||||
|
// output-key peer KZqXTZ4l2aNnkJtLPhs4D8JxHTGmRSL9w3Qr+X8JxFk= key-file "client-A-osk" exchanged
|
||||||
|
let peer_a_id = words_b
|
||||||
|
.get(2)
|
||||||
|
.with_context(|| format!("Bad rosenpass output: `{line_b}`"))?;
|
||||||
|
let peer_b_id = words_a
|
||||||
|
.get(2)
|
||||||
|
.with_context(|| format!("Bad rosenpass output: `{line_a}`"))?;
|
||||||
|
assert_eq!(
|
||||||
|
line_a,
|
||||||
|
format!(
|
||||||
|
"output-key peer {peer_b_id} key-file \"{}\" exchanged",
|
||||||
|
peer_a_osk.to_str().context("")?
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
line_b,
|
||||||
|
format!(
|
||||||
|
"output-key peer {peer_a_id} key-file \"{}\" exchanged",
|
||||||
|
peer_b_osk.to_str().context("")?
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Read OSKs
|
||||||
|
let osk_a = SymKey::load_b64::<64, _>(peer_a_osk.clone())?;
|
||||||
|
let osk_b = SymKey::load_b64::<64, _>(peer_b_osk.clone())?;
|
||||||
|
match osk_a.secret() == osk_b.secret() {
|
||||||
|
true => break,
|
||||||
|
false if attempt > 10 => bail!("Peers did not produce a matching key even after ten attempts. Something is wrong with the key exchange!"),
|
||||||
|
false => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
attempt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
use std::{borrow::Borrow, io};
|
use std::{borrow::Borrow, io};
|
||||||
|
|
||||||
|
use anyhow::ensure;
|
||||||
|
|
||||||
pub trait IoErrorKind {
|
pub trait IoErrorKind {
|
||||||
fn io_error_kind(&self) -> io::ErrorKind;
|
fn io_error_kind(&self) -> io::ErrorKind;
|
||||||
}
|
}
|
||||||
@@ -108,3 +110,21 @@ impl<T: io::Read> ReadNonblockingWithBoringErrorsHandledExt for T {
|
|||||||
nonblocking_handle_io_errors(|| self.read(buf))
|
nonblocking_handle_io_errors(|| self.read(buf))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ReadExt {
|
||||||
|
fn read_exact_til_end(&mut self, buf: &mut [u8]) -> anyhow::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ReadExt for T
|
||||||
|
where
|
||||||
|
T: std::io::Read,
|
||||||
|
{
|
||||||
|
fn read_exact_til_end(&mut self, buf: &mut [u8]) -> anyhow::Result<()> {
|
||||||
|
self.read_exact(buf)?;
|
||||||
|
ensure!(
|
||||||
|
self.read(&mut [0u8; 8])? == 0,
|
||||||
|
"Read source longer than buffer"
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
pub mod b64;
|
pub mod b64;
|
||||||
pub mod build;
|
pub mod build;
|
||||||
|
pub mod controlflow;
|
||||||
pub mod fd;
|
pub mod fd;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod functional;
|
pub mod functional;
|
||||||
|
|||||||
@@ -2,11 +2,12 @@
|
|||||||
mod mio;
|
mod mio;
|
||||||
pub use mio::*;
|
pub use mio::*;
|
||||||
|
|
||||||
#[cfg(feature = "experiment_file_descriptor_passing")]
|
|
||||||
mod uds_recv_fd;
|
|
||||||
#[cfg(feature = "experiment_file_descriptor_passing")]
|
#[cfg(feature = "experiment_file_descriptor_passing")]
|
||||||
mod uds_send_fd;
|
mod uds_send_fd;
|
||||||
#[cfg(feature = "experiment_file_descriptor_passing")]
|
#[cfg(feature = "experiment_file_descriptor_passing")]
|
||||||
pub use uds_recv_fd::*;
|
|
||||||
#[cfg(feature = "experiment_file_descriptor_passing")]
|
|
||||||
pub use uds_send_fd::*;
|
pub use uds_send_fd::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "experiment_file_descriptor_passing")]
|
||||||
|
mod uds_recv_fd;
|
||||||
|
#[cfg(feature = "experiment_file_descriptor_passing")]
|
||||||
|
pub use uds_recv_fd::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user