blob: e030f75854291782e161b59234270f6eb3c8335b [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//! Ding-Sun RPC (dsrpc) for MesaTEE-SGX
//!
//! MesaTEE-SGX uses direct ECALL/OCALL interfaces on local, and
//! socket for remote.
use serde::{de::DeserializeOwned, Serialize};
use sgx_types::c_int;
use std::io::{self, Read, Write};
use std::marker::PhantomData;
use std::net::TcpStream;
use crate::rpc::{EnclaveService, RpcClient, RpcServer};
#[cfg(not(feature = "mesalock_sgx"))]
use std::os::unix::io::FromRawFd;
use crate::Result;
// TODO: make the following comments into doc comments starting with `//!`.
// Sgx Socket RPC always depends on a socket which is always managed by fd.
// Before launching a Sgx Socket server, one should run a thread pool and
// properly run a "accept loop" like before. On accept, use `into_raw_fd` to
// pass the fd to the enclave.
//
// ```
// println!("Running as server...");
// let listener = TcpListener::bind("0.0.0.0:3443").unwrap();
// match listener.accept() {
// Ok((socket, addr)) => {
// println!("new client from {:?}", addr);
// let mut retval = sgx_status_t::SGX_SUCCESS;
// let result = unsafe {
// run_server(enclave.geteid(), &mut retval, socket.into_raw_fd(), sign_type)
// };
// match result {
// sgx_status_t::SGX_SUCCESS => {
// println!("ECALL success!");
// },
// _ => {
// println!("[-] ECALL Enclave Failed {}!", result.as_str());
// return;
// }
// }
// }
// Err(e) => println!("couldn't get client: {:?}", e),
// }
//```
// `into_raw_fd` consumes the stream. So socket would **not** be dropped on
// leaving its scope, and **not** triggers its dtor which closes the connection.
// In MesaTEE, the stream is re-constructed in an enclave by `from_raw_fd`. So
// the dtor is invoked when the in-enclave stream leaves its scope. Pairing
// the `into_raw_fd` and `from_raw_fd` is **required**.
pub struct PipeConfig {
fd: c_int,
}
impl PipeConfig {
pub fn new(fd: c_int) -> Self {
PipeConfig { fd }
}
pub fn get(&self) -> c_int {
self.fd
}
}
pub type PipeClientConfig = PipeConfig;
// Pipe stores the `TcpStream` created from the fd comes from Config.
pub struct Pipe<U, V, X> {
inner: TcpStream,
u: PhantomData<U>,
v: PhantomData<V>,
x: PhantomData<X>,
}
impl<U, V, X> Read for Pipe<U, V, X> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}
}
impl<U, V, X> Write for Pipe<U, V, X> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
impl<U, V, X> RpcServer<U, V, X> for Pipe<U, V, X>
where
U: DeserializeOwned + std::fmt::Debug,
V: Serialize + std::fmt::Debug,
X: EnclaveService<U, V>,
{
type Config = PipeConfig;
// type X = Box<EnclaveService<U, V>>;
// The `TcpStream::new()` function is different from Rust's design.
// The SGX version takes a fd as a input and return an `Option`.
fn start(config: &Self::Config) -> Result<Self> {
Ok(Pipe {
#[cfg(feature = "mesalock_sgx")]
inner: TcpStream::new(config.get())?,
#[cfg(not(feature = "mesalock_sgx"))]
inner: unsafe { TcpStream::from_raw_fd(config.get()) },
u: PhantomData::<U>,
v: PhantomData::<V>,
x: PhantomData::<X>,
})
}
// Use default impplementation
// fn serve(&mut self, mut s: X) -> Result<()>;
}
pub struct PipeClient<U, V> {
inner: TcpStream,
u: PhantomData<U>,
v: PhantomData<V>,
}
impl<U, V> Read for PipeClient<U, V> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}
}
impl<U, V> Write for PipeClient<U, V> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
impl<U, V> RpcClient<U, V> for PipeClient<U, V>
where
U: Serialize,
V: DeserializeOwned,
{
type Config = PipeClientConfig;
fn open(config: Self::Config) -> Result<Self> {
Ok(PipeClient {
#[cfg(feature = "mesalock_sgx")]
inner: TcpStream::new(config.get())?,
#[cfg(not(feature = "mesalock_sgx"))]
inner: unsafe { TcpStream::from_raw_fd(config.get()) },
u: PhantomData::<U>,
v: PhantomData::<V>,
})
}
// Use default implementation
// fn invoke(&mut self, input: U) -> Result<V>;
}