diff --git a/README.md b/README.md index 7a131998..3b608beb 100644 --- a/README.md +++ b/README.md @@ -81,8 +81,8 @@ branch (`main`), please refer to the - **Common**: See [Overview of OP-TEE Rust Examples](https://teaclave.apache.org/trustzone-sdk-docs/overview-of-optee-rust-examples/). -- **`no-std`**: Excludes `test_serde`, `test_tcp_client`, `test_udp_socket`, - `test_message_passing_interface`, `test_tls_client`, `test_tls_server`. +- **`no-std`**: Excludes `test_serde`, `test_message_passing_interface`, + `test_tls_client`, `test_tls_server`. ## Quick Start with the OP-TEE Repo for QEMUv8 diff --git a/ci/ci.sh b/ci/ci.sh index c58a03f8..e10916f8 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -35,13 +35,13 @@ pushd ../tests ./test_signature_verification.sh ./test_supp_plugin.sh ./test_error_handling.sh +./test_tcp_client.sh +./test_udp_socket.sh # Run std only tests if [ "$STD" ]; then ./test_serde.sh ./test_message_passing_interface.sh - ./test_tcp_client.sh - ./test_udp_socket.sh ./test_tls_client.sh ./test_tls_server.sh ./test_eth_wallet.sh diff --git a/examples/tcp_client-rs/host/Cargo.toml b/examples/tcp_client-rs/host/Cargo.toml index c8f3610b..8c314d57 100644 --- a/examples/tcp_client-rs/host/Cargo.toml +++ b/examples/tcp_client-rs/host/Cargo.toml @@ -28,6 +28,7 @@ edition = "2018" libc = "0.2.48" proto = { path = "../proto" } optee-teec = { path = "../../../optee-teec" } +tiny_http = "0.12.0" [profile.release] lto = true diff --git a/examples/tcp_client-rs/host/Makefile b/examples/tcp_client-rs/host/Makefile index 7675483a..75a545f2 100644 --- a/examples/tcp_client-rs/host/Makefile +++ b/examples/tcp_client-rs/host/Makefile @@ -24,12 +24,7 @@ LINKER_CFG := target.$(TARGET).linker=\"$(CROSS_COMPILE)gcc\" OUT_DIR := $(CURDIR)/target/$(TARGET)/release -ifeq ($(STD),) -all: - @echo "Please \`export STD=y\` then rerun \`source environment\` to build the STD version" -else all: host strip -endif host: @cargo build --target $(TARGET_HOST) --release --config $(LINKER_CFG) diff --git a/examples/tcp_client-rs/host/src/main.rs b/examples/tcp_client-rs/host/src/main.rs index 75624db4..6a0acdf0 100644 --- a/examples/tcp_client-rs/host/src/main.rs +++ b/examples/tcp_client-rs/host/src/main.rs @@ -15,13 +15,33 @@ // specific language governing permissions and limitations // under the License. -use optee_teec::ParamNone; -use optee_teec::{Context, Operation, Session, Uuid}; -use proto::{Command, UUID}; +use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6}; +use std::thread; -fn tcp_client(session: &mut Session) -> optee_teec::Result<()> { - let mut operation = Operation::new(0, ParamNone, ParamNone, ParamNone, ParamNone); +use optee_teec::{Context, Operation, ParamType, Session, Uuid}; +use optee_teec::{ParamNone, ParamTmpRef, ParamValue}; +use proto::{Command, IpVersion, UUID}; + +fn tcp_client( + session: &mut Session, + address: &str, + port: u16, + ip_version: IpVersion, + host_name: &str, +) -> optee_teec::Result<()> { + println!("Test on: {}", address); + + let http_data = format!("GET / HTTP/1.0\r\nHost: {}\r\n\r\n", host_name); + let mut operation = Operation::new( + 0, + ParamTmpRef::new_input(address.as_bytes()), + ParamValue::new(port as u32, ip_version as u32, ParamType::ValueInput), + ParamTmpRef::new_input(http_data.as_bytes()), + ParamNone, + ); session.invoke_command(Command::Start as u32, &mut operation)?; + + println!("Success"); Ok(()) } @@ -30,8 +50,36 @@ fn main() -> optee_teec::Result<()> { let uuid = Uuid::parse_str(UUID).unwrap(); let mut session = ctx.open_session(uuid)?; - tcp_client(&mut session)?; + // test ipv4 + const IPV4_HOST: &str = "teaclave.apache.org"; + // Use the host directly to also check its domain name resolving capability. + tcp_client(&mut session, IPV4_HOST, 80, IpVersion::V4, IPV4_HOST)?; + + // test ipv6 + let addr = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0)); + let server = tiny_http::Server::http(addr).unwrap(); + let listen_addr = server.server_addr().to_ip().unwrap(); + let ip = listen_addr.ip().to_string(); + let port = listen_addr.port(); + + let child = thread::spawn(move || { + for request in server.incoming_requests() { + println!( + "received request! method: {:?}, url: {:?}, headers: {:?}", + request.method(), + request.url(), + request.headers() + ); + + let response = tiny_http::Response::from_string("hello world"); + request.respond(response).unwrap(); + break; + } + }); + // Use the IP address directly to ensure we're actually trying an IPv6 + // address. + tcp_client(&mut session, &ip, port, IpVersion::V6, &ip)?; + let _ = child.join(); - println!("Success"); Ok(()) } diff --git a/examples/tcp_client-rs/proto/Cargo.toml b/examples/tcp_client-rs/proto/Cargo.toml index b76ceb26..4a173c89 100644 --- a/examples/tcp_client-rs/proto/Cargo.toml +++ b/examples/tcp_client-rs/proto/Cargo.toml @@ -25,6 +25,7 @@ description = "Data structures and functions shared by host and TA." edition = "2018" [dependencies] +num_enum = { version = "0.7.3", default-features = false } [build-dependencies] uuid = { version = "1.8", default-features = false } diff --git a/examples/tcp_client-rs/proto/src/lib.rs b/examples/tcp_client-rs/proto/src/lib.rs index 7679b2dc..90346430 100644 --- a/examples/tcp_client-rs/proto/src/lib.rs +++ b/examples/tcp_client-rs/proto/src/lib.rs @@ -14,20 +14,22 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. +#![no_std] +use num_enum::{FromPrimitive, TryFromPrimitive, IntoPrimitive}; +#[derive(FromPrimitive, IntoPrimitive)] +#[repr(u32)] pub enum Command { Start, + #[default] Unknown, } -impl From for Command { - #[inline] - fn from(value: u32) -> Command { - match value { - 0 => Command::Start, - _ => Command::Unknown, - } - } +#[derive(TryFromPrimitive, IntoPrimitive)] +#[repr(u32)] +pub enum IpVersion { + V4 = 1, + V6 = 2, } pub const UUID: &str = &include_str!(concat!(env!("OUT_DIR"), "/uuid.txt")); diff --git a/examples/tcp_client-rs/ta/Cargo.toml b/examples/tcp_client-rs/ta/Cargo.toml index fce4cb49..5f71c197 100644 --- a/examples/tcp_client-rs/ta/Cargo.toml +++ b/examples/tcp_client-rs/ta/Cargo.toml @@ -28,6 +28,7 @@ edition = "2018" proto = { path = "../proto" } optee-utee-sys = { path = "../../../optee-utee/optee-utee-sys" } optee-utee = { path = "../../../optee-utee" } +cfg_block = "0.2.0" [build-dependencies] proto = { path = "../proto" } @@ -35,5 +36,5 @@ optee-utee-build = { path = "../../../optee-utee-build" } [profile.release] panic = "abort" -lto = false +lto = true opt-level = 1 diff --git a/examples/tcp_client-rs/ta/Makefile b/examples/tcp_client-rs/ta/Makefile index 42d92360..e69d8857 100644 --- a/examples/tcp_client-rs/ta/Makefile +++ b/examples/tcp_client-rs/ta/Makefile @@ -29,15 +29,12 @@ TA_SIGN_KEY ?= $(TA_DEV_KIT_DIR)/keys/default_ta.pem SIGN := $(TA_DEV_KIT_DIR)/scripts/sign_encrypt.py OUT_DIR := $(CURDIR)/target/$(TARGET)/release -ifeq ($(STD),) -all: - @echo "Please \`export STD=y\` then rerun \`source environment\` to build the STD version" -else +BUILDER = $(if $(STD),xargo,cargo) + all: ta strip sign -endif ta: - @xargo build --target $(TARGET) --release --config $(LINKER_CFG) + @$(BUILDER) build --target $(TARGET) --release --config $(LINKER_CFG) strip: ta @$(OBJCOPY) --strip-unneeded $(OUT_DIR)/ta $(OUT_DIR)/stripped_ta diff --git a/examples/tcp_client-rs/ta/src/main.rs b/examples/tcp_client-rs/ta/src/main.rs index bc3a2b85..ddd92931 100644 --- a/examples/tcp_client-rs/ta/src/main.rs +++ b/examples/tcp_client-rs/ta/src/main.rs @@ -15,16 +15,28 @@ // specific language governing permissions and limitations // under the License. +#![cfg_attr(not(target_os = "optee"), no_std)] #![no_main] +cfg_block::cfg_block! { + // In Teaclave, if target_os = "optee", the codes is compiled with std. + // Otherwise, no-std + if #[cfg(target_os = "optee")] { + use std::io::{Read, Write}; + } else { + extern crate alloc; + use optee_utee::net::{StdCompatConnect, StdCompatWrite, StdCompatRead}; + use alloc::vec::Vec; + use alloc::string::String; + } +} + use optee_utee::net::TcpStream; use optee_utee::{ ta_close_session, ta_create, ta_destroy, ta_invoke_command, ta_open_session, trace_println, }; use optee_utee::{Error, ErrorKind, Parameters, Result}; -use proto::Command; -use std::io::Read; -use std::io::Write; +use proto::{Command, IpVersion}; #[ta_create] fn create() -> Result<()> { @@ -49,35 +61,59 @@ fn destroy() { } #[ta_invoke_command] -fn invoke_command(cmd_id: u32, _params: &mut Parameters) -> Result<()> { +fn invoke_command(cmd_id: u32, params: &mut Parameters) -> Result<()> { trace_println!("[+] TA invoke command"); match Command::from(cmd_id) { Command::Start => { - tcp_client(); - Ok(()) + use core::convert::TryFrom; + + let mut param0 = unsafe { params.0.as_memref()? }; + let param1 = unsafe { params.1.as_value()? }; + let mut param2 = unsafe { params.2.as_memref()? }; + + let address = core::str::from_utf8(param0.buffer()).unwrap(); + let port = param1.a() as u16; + let ip_version = + IpVersion::try_from(param1.b()).map_err(|_| ErrorKind::BadParameters)?; + let http_data = param2.buffer(); + + tcp_client(address, port, ip_version, http_data) } _ => Err(Error::new(ErrorKind::BadParameters)), } } -fn tcp_client() { - let mut stream = TcpStream::connect("teaclave.apache.org", 80).unwrap(); - stream - .write_all(b"GET / HTTP/1.0\r\nHost: teaclave.apache.org\r\n\r\n") - .unwrap(); +fn tcp_client(address: &str, port: u16, ip_version: IpVersion, http_data: &[u8]) -> Result<()> { + let mut stream = match ip_version { + IpVersion::V4 => TcpStream::connect_v4(address, port), + IpVersion::V6 => TcpStream::connect_v6(address, port), + } + .map_err(|err| { + trace_println!("failed to connect to {}:{} due to {:?}", address, port, err); + ErrorKind::Generic + })?; + + stream.set_send_timeout_in_milli(60 * 1000); + stream.set_recv_timeout_in_milli(60 * 1000); + + stream.write_all(http_data).map_err(|err| { + trace_println!("failed to write_all due to {:?}", err); + ErrorKind::Generic + })?; let mut response = Vec::new(); let mut chunk = [0u8; 1024]; loop { match stream.read(&mut chunk) { Ok(0) => break, Ok(n) => response.extend_from_slice(&chunk[..n]), - Err(_) => { - trace_println!("Error"); - panic!(); + Err(err) => { + trace_println!("failed to read due to {:?}", err); + return Err(ErrorKind::Generic.into()); } } } trace_println!("{}", String::from_utf8_lossy(&response)); + Ok(()) } include!(concat!(env!("OUT_DIR"), "/user_ta_header.rs")); diff --git a/examples/udp_socket-rs/host/Makefile b/examples/udp_socket-rs/host/Makefile index 90b6d44c..734ed5cf 100644 --- a/examples/udp_socket-rs/host/Makefile +++ b/examples/udp_socket-rs/host/Makefile @@ -24,12 +24,7 @@ LINKER_CFG := target.$(TARGET).linker=\"$(CROSS_COMPILE)gcc\" OUT_DIR := $(CURDIR)/target/$(TARGET)/release -ifeq ($(STD),) -all: - @echo "Please \`export STD=y\` then rerun \`source environment\` to build the STD version" -else all: host strip -endif host: @cargo build --target $(TARGET_HOST) --release --config $(LINKER_CFG) diff --git a/examples/udp_socket-rs/host/src/main.rs b/examples/udp_socket-rs/host/src/main.rs index 5e1db599..62076cc7 100644 --- a/examples/udp_socket-rs/host/src/main.rs +++ b/examples/udp_socket-rs/host/src/main.rs @@ -14,28 +14,44 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. - -use optee_teec::ParamNone; -use optee_teec::{Context, Operation, Session, Uuid}; -use proto::{Command, UUID}; -use std::net::UdpSocket; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, UdpSocket}; use std::str; use std::thread; -fn udp_socket(session: &mut Session) -> optee_teec::Result<()> { - let mut operation = Operation::new(0, ParamNone, ParamNone, ParamNone, ParamNone); - session.invoke_command(Command::Start as u32, &mut operation)?; - Ok(()) -} +use optee_teec::{Context, Operation, Uuid}; +use optee_teec::{ParamNone, ParamTmpRef, ParamValue}; +use proto::{Command, IpVersion, UUID}; -fn main() -> optee_teec::Result<()> { - let socket = UdpSocket::bind("127.0.0.1:34254").unwrap(); +fn udp_socket(ip_version: IpVersion) { + let addr: SocketAddr = match ip_version { + IpVersion::V4 => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0)), + IpVersion::V6 => SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0)), + }; + let socket = UdpSocket::bind(addr).unwrap(); + let local_addr = socket.local_addr().unwrap(); + println!("Test on: {}", local_addr); - let mut ctx = Context::new()?; - let uuid = Uuid::parse_str(UUID).unwrap(); let child = thread::spawn(move || { + let mut ctx = Context::new().unwrap(); + let uuid = Uuid::parse_str(UUID).unwrap(); let mut session = ctx.open_session(uuid).unwrap(); - udp_socket(&mut session).unwrap(); + + let ip = local_addr.ip().to_string(); + let port = local_addr.port(); + let mut operation = Operation::new( + 0, + ParamTmpRef::new_input(ip.as_bytes()), + ParamValue::new( + port as u32, + ip_version as u32, + optee_teec::ParamType::ValueInput, + ), + ParamNone, + ParamNone, + ); + session + .invoke_command(Command::Start as u32, &mut operation) + .unwrap(); }); let mut buf = [0; 100]; @@ -47,5 +63,11 @@ fn main() -> optee_teec::Result<()> { let _ = child.join(); println!("Success"); +} + +fn main() -> optee_teec::Result<()> { + udp_socket(IpVersion::V4); + udp_socket(IpVersion::V6); + Ok(()) } diff --git a/examples/udp_socket-rs/proto/Cargo.toml b/examples/udp_socket-rs/proto/Cargo.toml index b76ceb26..4a173c89 100644 --- a/examples/udp_socket-rs/proto/Cargo.toml +++ b/examples/udp_socket-rs/proto/Cargo.toml @@ -25,6 +25,7 @@ description = "Data structures and functions shared by host and TA." edition = "2018" [dependencies] +num_enum = { version = "0.7.3", default-features = false } [build-dependencies] uuid = { version = "1.8", default-features = false } diff --git a/examples/udp_socket-rs/proto/src/lib.rs b/examples/udp_socket-rs/proto/src/lib.rs index 7679b2dc..88fa36b4 100644 --- a/examples/udp_socket-rs/proto/src/lib.rs +++ b/examples/udp_socket-rs/proto/src/lib.rs @@ -14,20 +14,22 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. +#![no_std] +use num_enum::{FromPrimitive, IntoPrimitive, TryFromPrimitive}; +#[derive(FromPrimitive, IntoPrimitive)] +#[repr(u32)] pub enum Command { - Start, + Start = 1, + #[default] Unknown, } -impl From for Command { - #[inline] - fn from(value: u32) -> Command { - match value { - 0 => Command::Start, - _ => Command::Unknown, - } - } +#[derive(TryFromPrimitive, IntoPrimitive)] +#[repr(u32)] +pub enum IpVersion { + V4 = 1, + V6 = 2, } pub const UUID: &str = &include_str!(concat!(env!("OUT_DIR"), "/uuid.txt")); diff --git a/examples/udp_socket-rs/ta/Cargo.toml b/examples/udp_socket-rs/ta/Cargo.toml index ba7d6d49..5f71c197 100644 --- a/examples/udp_socket-rs/ta/Cargo.toml +++ b/examples/udp_socket-rs/ta/Cargo.toml @@ -25,10 +25,10 @@ description = "An example of Rust OP-TEE TrustZone SDK." edition = "2018" [dependencies] -libc = { path = "../../../rust/libc" } proto = { path = "../proto" } optee-utee-sys = { path = "../../../optee-utee/optee-utee-sys" } optee-utee = { path = "../../../optee-utee" } +cfg_block = "0.2.0" [build-dependencies] proto = { path = "../proto" } @@ -36,5 +36,5 @@ optee-utee-build = { path = "../../../optee-utee-build" } [profile.release] panic = "abort" -lto = false +lto = true opt-level = 1 diff --git a/examples/udp_socket-rs/ta/Makefile b/examples/udp_socket-rs/ta/Makefile index 42d92360..e69d8857 100644 --- a/examples/udp_socket-rs/ta/Makefile +++ b/examples/udp_socket-rs/ta/Makefile @@ -29,15 +29,12 @@ TA_SIGN_KEY ?= $(TA_DEV_KIT_DIR)/keys/default_ta.pem SIGN := $(TA_DEV_KIT_DIR)/scripts/sign_encrypt.py OUT_DIR := $(CURDIR)/target/$(TARGET)/release -ifeq ($(STD),) -all: - @echo "Please \`export STD=y\` then rerun \`source environment\` to build the STD version" -else +BUILDER = $(if $(STD),xargo,cargo) + all: ta strip sign -endif ta: - @xargo build --target $(TARGET) --release --config $(LINKER_CFG) + @$(BUILDER) build --target $(TARGET) --release --config $(LINKER_CFG) strip: ta @$(OBJCOPY) --strip-unneeded $(OUT_DIR)/ta $(OUT_DIR)/stripped_ta diff --git a/examples/udp_socket-rs/ta/src/main.rs b/examples/udp_socket-rs/ta/src/main.rs index 867c9d86..c1877977 100644 --- a/examples/udp_socket-rs/ta/src/main.rs +++ b/examples/udp_socket-rs/ta/src/main.rs @@ -15,16 +15,29 @@ // specific language governing permissions and limitations // under the License. +#![cfg_attr(not(target_os = "optee"), no_std)] #![no_main] +cfg_block::cfg_block! { + // In Teaclave, if target_os = "optee", the codes is compiled with std. + // Otherwise, no-std + if #[cfg(target_os = "optee")] { + use std::io::{Read, Write}; + } else { + extern crate alloc; + use optee_utee::net::{StdCompatConnect, StdCompatWrite, StdCompatRead}; + use alloc::vec::Vec; + use alloc::string::String; + } +} + +use core::convert::TryFrom; use optee_utee::net::UdpSocket; use optee_utee::{ ta_close_session, ta_create, ta_destroy, ta_invoke_command, ta_open_session, trace_println, }; use optee_utee::{Error, ErrorKind, Parameters, Result}; -use proto::Command; -use std::io::Read; -use std::io::Write; +use proto::{Command, IpVersion}; #[ta_create] fn create() -> Result<()> { @@ -49,20 +62,41 @@ fn destroy() { } #[ta_invoke_command] -fn invoke_command(cmd_id: u32, _params: &mut Parameters) -> Result<()> { +fn invoke_command(cmd_id: u32, params: &mut Parameters) -> Result<()> { trace_println!("[+] TA invoke command"); match Command::from(cmd_id) { Command::Start => { - udp_socket(); - Ok(()) + let mut param0 = unsafe { params.0.as_memref()? }; + let param1 = unsafe { params.1.as_value()? }; + + let address = core::str::from_utf8(param0.buffer()).unwrap(); + let port = param1.a() as u16; + let ip_version = + IpVersion::try_from(param1.b()).map_err(|_| ErrorKind::BadParameters)?; + + udp_socket(address, port, ip_version) } _ => Err(Error::new(ErrorKind::BadParameters)), } } -fn udp_socket() { - let mut stream = UdpSocket::connect("127.0.0.1", 34254).unwrap(); - stream.write_all(b"[TA]: Hello, Teaclave!").unwrap(); +fn udp_socket(address: &str, port: u16, ip_version: IpVersion) -> Result<()> { + let mut stream = match ip_version { + IpVersion::V4 => UdpSocket::connect_v4(address, port), + IpVersion::V6 => UdpSocket::connect_v6(address, port), + } + .map_err(|err| { + trace_println!("failed to connect to {}:{} due to {:?}", address, port, err); + ErrorKind::Generic + })?; + + stream.set_send_timeout_in_milli(60 * 1000); + stream.set_recv_timeout_in_milli(60 * 1000); + + stream.write_all(b"[TA]: Hello, Teaclave!").map_err(|err| { + trace_println!("failed to write_all due to {:?}", err); + ErrorKind::Generic + })?; let mut response = Vec::new(); let mut chunk = [0u8; 1024]; @@ -74,13 +108,14 @@ fn udp_socket() { response.extend_from_slice(&chunk[..n]); break; } - Err(_) => { - trace_println!("Error"); - panic!(); + Err(err) => { + trace_println!("failed to read due to {:?}", err); + return Err(ErrorKind::Generic.into()); } } } trace_println!("{}", String::from_utf8_lossy(&response)); + Ok(()) } include!(concat!(env!("OUT_DIR"), "/user_ta_header.rs")); diff --git a/optee-utee/src/lib.rs b/optee-utee/src/lib.rs index 679339ce..af657784 100644 --- a/optee-utee/src/lib.rs +++ b/optee-utee/src/lib.rs @@ -64,6 +64,4 @@ pub mod time; pub mod arithmetical; pub mod extension; pub mod uuid; - -#[cfg(target_os = "optee")] pub mod net; diff --git a/optee-utee/src/net.rs b/optee-utee/src/net.rs deleted file mode 100644 index ea859b5a..00000000 --- a/optee-utee/src/net.rs +++ /dev/null @@ -1,361 +0,0 @@ -// 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. - -use optee_utee_sys as raw; -use std::io; -use std::io::ErrorKind; -use std::ptr; - -pub struct TcpStream { - pub handle: raw::TEE_iSocketHandle, -} - -impl TcpStream { - fn connect_with_ip_version( - address: &str, - port: u16, - ip_version: raw::TEE_ipSocket_ipVersion, - ) -> std::io::Result { - use std::ffi::CString; - unsafe { - let addr = match CString::new(address) { - Ok(addr) => addr, - Err(_) => return Err(io::Error::new(ErrorKind::Other, "Invalid address")), - }; - let mut handle: raw::TEE_iSocketHandle = ptr::null_mut(); - let mut protocol_error: u32 = 0; - let mut setup = raw::TEE_tcpSocket_Setup { - ipVersion: ip_version, - server_addr: addr.as_ptr() as _, - server_port: port, - }; - let ret = ((*raw::TEE_tcpSocket).open)( - &mut handle, - &mut setup as *mut raw::TEE_tcpSocket_Setup as _, - &mut protocol_error, - ); - match ret { - raw::TEE_SUCCESS => Ok(Self { handle }), - raw::TEE_ERROR_CANCEL => { - Err(io::Error::new(ErrorKind::Interrupted, "TEE_ERROR_CANCEL")) - } - raw::TEE_ERROR_OUT_OF_MEMORY => { - Err(io::Error::new(ErrorKind::Other, "TEE_ERROR_OUT_OF_MEMORY")) - } - raw::TEE_ERROR_BAD_PARAMETERS => { - Err(io::Error::new(ErrorKind::Other, "TEE_ERROR_BAD_PARAMETERS")) - } - raw::TEE_ISOCKET_ERROR_TIMEOUT => Err(io::Error::new( - ErrorKind::TimedOut, - "TEE_ISOCKET_ERROR_TIMEOUT", - )), - raw::TEE_ERROR_COMMUNICATION => Err(io::Error::new( - ErrorKind::ConnectionAborted, - "TEE_ERROR_COMMUNICATION", - )), - raw::TEE_ISOCKET_ERROR_PROTOCOL => Err(io::Error::new( - ErrorKind::Other, - "TEE_ISOCKET_ERROR_PROTOCOL", - )), - raw::TEE_ISOCKET_WARNING_PROTOCOL => Err(io::Error::new( - ErrorKind::Other, - format!("TEE_ISOCKET_WARNING_PROTOCOL: {}", protocol_error), - )), - _ => panic!("Unexpected return value"), - } - } - } - - pub fn connect_v4(address: &str, port: u16) -> std::io::Result { - Self::connect_with_ip_version(address, port, raw::TEE_ipSocket_ipVersion::TEE_IP_VERSION_4) - } - - pub fn connect_v6(address: &str, port: u16) -> std::io::Result { - Self::connect_with_ip_version(address, port, raw::TEE_ipSocket_ipVersion::TEE_IP_VERSION_4) - } - - pub fn connect(address: &str, port: u16) -> std::io::Result { - Self::connect_v4(address, port) - } -} - -impl Drop for TcpStream { - fn drop(&mut self) { - // Ignore any errors on close. - unsafe { - ((*raw::TEE_tcpSocket).close)(self.handle); - } - } -} - -impl std::io::Read for TcpStream { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - let mut length: u32 = buf.len() as _; - let ret = unsafe { - ((*raw::TEE_tcpSocket).recv)( - self.handle, - buf.as_mut_ptr() as _, - &mut length, - raw::TEE_TIMEOUT_INFINITE, - ) - }; - - match ret { - raw::TEE_SUCCESS => Ok(length as _), - raw::TEE_ERROR_CANCEL => { - Err(io::Error::new(ErrorKind::Interrupted, "TEE_ERROR_CANCEL")) - } - raw::TEE_ISOCKET_ERROR_TIMEOUT => Err(io::Error::new( - ErrorKind::TimedOut, - "TEE_ISOCKET_ERROR_TIMEOUT", - )), - raw::TEE_ERROR_COMMUNICATION => Err(io::Error::new( - ErrorKind::ConnectionAborted, - "TEE_ERROR_COMMUNICATION", - )), - raw::TEE_ISOCKET_ERROR_REMOTE_CLOSED => Err(io::Error::new( - ErrorKind::ConnectionAborted, - "TEE_ISOCKET_ERROR_REMOTE_CLOSED", - )), - raw::TEE_ISOCKET_ERROR_PROTOCOL => Err(io::Error::new( - ErrorKind::Other, - "TEE_ISOCKET_ERROR_PROTOCOL", - )), - raw::TEE_ISOCKET_WARNING_PROTOCOL => Err(io::Error::new( - ErrorKind::Other, - "TEE_ISOCKET_WARNING_PROTOCOL", - )), - _ => panic!("Unexpected return value"), - } - } -} - -impl std::io::Write for TcpStream { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - let mut length: u32 = buf.len() as _; - let ret = unsafe { - ((*raw::TEE_tcpSocket).send)( - self.handle, - buf.as_ptr() as *const u8 as _, - &mut length, - raw::TEE_TIMEOUT_INFINITE, - ) - }; - - match ret { - raw::TEE_SUCCESS => Ok(length as _), - raw::TEE_ERROR_CANCEL => { - Err(io::Error::new(ErrorKind::Interrupted, "TEE_ERROR_CANCEL")) - } - raw::TEE_ISOCKET_ERROR_TIMEOUT => Err(io::Error::new( - ErrorKind::TimedOut, - "TEE_ISOCKET_ERROR_TIMEOUT", - )), - raw::TEE_ISOCKET_ERROR_REMOTE_CLOSED => Err(io::Error::new( - ErrorKind::ConnectionAborted, - "TEE_ISOCKET_ERROR_REMOTE_CLOSED", - )), - raw::TEE_ISOCKET_ERROR_PROTOCOL => Err(io::Error::new( - ErrorKind::Other, - "TEE_ISOCKET_ERROR_PROTOCOL", - )), - raw::TEE_ISOCKET_WARNING_PROTOCOL => Err(io::Error::new( - ErrorKind::Other, - "TEE_ISOCKET_WARNING_PROTOCOL", - )), - raw::TEE_ISOCKET_ERROR_LARGE_BUFFER => Err(io::Error::new( - ErrorKind::Other, - "TEE_ISOCKET_ERROR_LARGE_BUFFER", - )), - _ => panic!("Unexpected return value"), - } - } - - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } -} - -pub struct UdpSocket { - pub handle: raw::TEE_iSocketHandle, -} - -impl UdpSocket { - fn connect_with_ip_version( - address: &str, - port: u16, - ip_version: raw::TEE_ipSocket_ipVersion, - ) -> std::io::Result { - use std::ffi::CString; - unsafe { - let addr = match CString::new(address) { - Ok(addr) => addr, - Err(_) => return Err(io::Error::new(ErrorKind::Other, "Invalid address")), - }; - let mut handle: raw::TEE_iSocketHandle = ptr::null_mut(); - let mut protocol_error: u32 = 0; - let mut setup = raw::TEE_udpSocket_Setup { - ipVersion: ip_version, - server_addr: addr.as_ptr() as _, - server_port: port, - }; - let ret = ((*raw::TEE_udpSocket).open)( - &mut handle, - &mut setup as *mut raw::TEE_udpSocket_Setup as _, - &mut protocol_error, - ); - match ret { - raw::TEE_SUCCESS => Ok(Self { handle }), - raw::TEE_ERROR_CANCEL => { - Err(io::Error::new(ErrorKind::Interrupted, "TEE_ERROR_CANCEL")) - } - raw::TEE_ERROR_OUT_OF_MEMORY => { - Err(io::Error::new(ErrorKind::Other, "TEE_ERROR_OUT_OF_MEMORY")) - } - raw::TEE_ERROR_BAD_PARAMETERS => { - Err(io::Error::new(ErrorKind::Other, "TEE_ERROR_BAD_PARAMETERS")) - } - raw::TEE_ISOCKET_ERROR_TIMEOUT => Err(io::Error::new( - ErrorKind::TimedOut, - "TEE_ISOCKET_ERROR_TIMEOUT", - )), - raw::TEE_ERROR_COMMUNICATION => Err(io::Error::new( - ErrorKind::ConnectionAborted, - "TEE_ERROR_COMMUNICATION", - )), - raw::TEE_ISOCKET_ERROR_PROTOCOL => Err(io::Error::new( - ErrorKind::Other, - "TEE_ISOCKET_ERROR_PROTOCOL", - )), - raw::TEE_ISOCKET_WARNING_PROTOCOL => Err(io::Error::new( - ErrorKind::Other, - format!("TEE_ISOCKET_WARNING_PROTOCOL: {}", protocol_error), - )), - _ => panic!("Unexpected return value"), - } - } - } - - pub fn connect_v4(address: &str, port: u16) -> std::io::Result { - Self::connect_with_ip_version(address, port, raw::TEE_ipSocket_ipVersion::TEE_IP_VERSION_4) - } - - pub fn connect_v6(address: &str, port: u16) -> std::io::Result { - Self::connect_with_ip_version(address, port, raw::TEE_ipSocket_ipVersion::TEE_IP_VERSION_4) - } - - pub fn connect(address: &str, port: u16) -> std::io::Result { - Self::connect_v4(address, port) - } -} - -impl Drop for UdpSocket { - fn drop(&mut self) { - // Ignore any errors on close. - unsafe { - ((*raw::TEE_udpSocket).close)(self.handle); - } - } -} - -impl std::io::Read for UdpSocket { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - let mut length: u32 = buf.len() as _; - let ret = unsafe { - ((*raw::TEE_udpSocket).recv)( - self.handle, - buf.as_mut_ptr() as _, - &mut length, - raw::TEE_TIMEOUT_INFINITE, - ) - }; - - match ret { - raw::TEE_SUCCESS => Ok(length as _), - raw::TEE_ERROR_CANCEL => { - Err(io::Error::new(ErrorKind::Interrupted, "TEE_ERROR_CANCEL")) - } - raw::TEE_ISOCKET_ERROR_TIMEOUT => Err(io::Error::new( - ErrorKind::TimedOut, - "TEE_ISOCKET_ERROR_TIMEOUT", - )), - raw::TEE_ERROR_COMMUNICATION => Err(io::Error::new( - ErrorKind::ConnectionAborted, - "TEE_ERROR_COMMUNICATION", - )), - raw::TEE_ISOCKET_ERROR_REMOTE_CLOSED => Err(io::Error::new( - ErrorKind::ConnectionAborted, - "TEE_ISOCKET_ERROR_REMOTE_CLOSED", - )), - raw::TEE_ISOCKET_ERROR_PROTOCOL => Err(io::Error::new( - ErrorKind::Other, - "TEE_ISOCKET_ERROR_PROTOCOL", - )), - raw::TEE_ISOCKET_WARNING_PROTOCOL => Err(io::Error::new( - ErrorKind::Other, - "TEE_ISOCKET_WARNING_PROTOCOL", - )), - _ => panic!("Unexpected return value"), - } - } -} - -impl std::io::Write for UdpSocket { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - let mut length: u32 = buf.len() as _; - let ret = unsafe { - ((*raw::TEE_udpSocket).send)( - self.handle, - buf.as_ptr() as *const u8 as _, - &mut length, - raw::TEE_TIMEOUT_INFINITE, - ) - }; - - match ret { - raw::TEE_SUCCESS => Ok(length as _), - raw::TEE_ERROR_CANCEL => { - Err(io::Error::new(ErrorKind::Interrupted, "TEE_ERROR_CANCEL")) - } - raw::TEE_ISOCKET_ERROR_TIMEOUT => Err(io::Error::new( - ErrorKind::TimedOut, - "TEE_ISOCKET_ERROR_TIMEOUT", - )), - raw::TEE_ISOCKET_ERROR_REMOTE_CLOSED => Err(io::Error::new( - ErrorKind::ConnectionAborted, - "TEE_ISOCKET_ERROR_REMOTE_CLOSED", - )), - raw::TEE_ISOCKET_ERROR_PROTOCOL => Err(io::Error::new( - ErrorKind::Other, - "TEE_ISOCKET_ERROR_PROTOCOL", - )), - raw::TEE_ISOCKET_WARNING_PROTOCOL => Err(io::Error::new( - ErrorKind::Other, - "TEE_ISOCKET_WARNING_PROTOCOL", - )), - raw::TEE_ISOCKET_ERROR_LARGE_BUFFER => Err(io::Error::new( - ErrorKind::Other, - "TEE_ISOCKET_ERROR_LARGE_BUFFER", - )), - _ => panic!("Unexpected return value"), - } - } - - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } -} diff --git a/optee-utee/src/net/error.rs b/optee-utee/src/net/error.rs new file mode 100644 index 00000000..b62ece2f --- /dev/null +++ b/optee-utee/src/net/error.rs @@ -0,0 +1,62 @@ +// 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. + +use optee_utee_sys as raw; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum SocketError { + ErrorProtocol(u32), + RemoteClosed, + Timeout, + OutOfResource, + WarningProtocol(u32), + LargeBuffer, + Hostname, + Tee(crate::ErrorKind), + Unknown(u32), +} + +impl SocketError { + pub fn from_raw_error(code: u32, protocol_error: u32) -> Self { + match code { + raw::TEE_ISOCKET_ERROR_PROTOCOL => Self::ErrorProtocol(protocol_error), + raw::TEE_ISOCKET_ERROR_REMOTE_CLOSED => Self::RemoteClosed, + raw::TEE_ISOCKET_ERROR_TIMEOUT => Self::Timeout, + raw::TEE_ISOCKET_ERROR_OUT_OF_RESOURCES => Self::OutOfResource, + raw::TEE_ISOCKET_ERROR_LARGE_BUFFER => Self::LargeBuffer, + raw::TEE_ISOCKET_WARNING_PROTOCOL => Self::WarningProtocol(protocol_error), + raw::TEE_ISOCKET_ERROR_HOSTNAME => Self::Hostname, + raw::TEE_ERROR_CANCEL + | raw::TEE_ERROR_COMMUNICATION + | raw::TEE_ERROR_OUT_OF_MEMORY + | raw::TEE_ERROR_BAD_PARAMETERS => Self::Tee(crate::Error::from_raw_error(code).kind()), + _ => Self::Unknown(code), + } + } +} + +impl core::fmt::Display for SocketError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From for SocketError { + fn from(value: crate::Error) -> Self { + Self::Tee(value.kind()) + } +} diff --git a/optee-utee/src/net/mod.rs b/optee-utee/src/net/mod.rs new file mode 100644 index 00000000..045453d8 --- /dev/null +++ b/optee-utee/src/net/mod.rs @@ -0,0 +1,30 @@ +// 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. + +mod error; +mod optee; +mod socket; + +pub use error::SocketError; +pub use optee::{Setup, TcpAdapter, TcpStream, UdpAdapter, UdpSocket}; +pub use socket::{Socket, SocketAdapter}; + +#[cfg(target_os = "optee")] +mod optee_std; + +mod optee_no_std; +pub use optee_no_std::{StdCompatConnect, StdCompatRead, StdCompatWrite}; diff --git a/optee-utee/src/net/optee.rs b/optee-utee/src/net/optee.rs new file mode 100644 index 00000000..b198319a --- /dev/null +++ b/optee-utee/src/net/optee.rs @@ -0,0 +1,202 @@ +// 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. + +use alloc::ffi::CString; +use core::ptr; +use optee_utee_sys as raw; + +use super::{Socket, SocketAdapter, SocketError}; + +/// A setup parameter used for OP-TEE. +pub struct Setup { + addr: CString, + port: u16, + version: raw::TEE_ipSocket_ipVersion, +} + +impl Setup { + pub(crate) fn new( + addr: &str, + port: u16, + version: raw::TEE_ipSocket_ipVersion, + ) -> crate::Result { + Ok(Self { + addr: CString::new(addr).map_err(|_| crate::ErrorKind::BadParameters)?, + port, + version, + }) + } + /// Construct a new IPv4 target parameter using the address and port. It + /// will return `BadParameters` if the address contains a `\0` character in + /// the middle. + pub fn new_v4(addr: &str, port: u16) -> crate::Result { + Self::new(addr, port, raw::TEE_ipSocket_ipVersion::TEE_IP_VERSION_4) + } + /// Construct a new IPv6 target parameter using the address and port. It + /// will return `BadParameters` if the address contains a `\0` character in + /// the middle. + pub fn new_v6(addr: &str, port: u16) -> crate::Result { + Self::new(addr, port, raw::TEE_ipSocket_ipVersion::TEE_IP_VERSION_6) + } +} + +/// An adapter for TCP sockets in OP-TEE. Typically, it is not used directly, +/// but can be employed for wrapper operations, such as traffic control within +/// the TEE. +pub struct TcpAdapter(raw::TEE_iSocketHandle); +/// An adapter for UDP sockets in OP-TEE. Typically, it is not used directly, +/// but can be employed for wrapper operations, such as traffic control within +/// the TEE. +pub struct UdpAdapter(raw::TEE_iSocketHandle); +/// A TcpStream that is compatible with OP-TEE. +pub type TcpStream = Socket; +/// A UdpSocket that is compatible with OP-TEE. +pub type UdpSocket = Socket; + +fn handle_socket_operation_error(handle: raw::TEE_iSocketHandle, code: u32) -> SocketError { + match code { + raw::TEE_ISOCKET_ERROR_PROTOCOL => { + let protocol_error = unsafe { ((*raw::TEE_tcpSocket).error)(handle) }; + SocketError::ErrorProtocol(protocol_error) + } + raw::TEE_ISOCKET_WARNING_PROTOCOL => { + let protocol_error = unsafe { ((*raw::TEE_tcpSocket).error)(handle) }; + SocketError::WarningProtocol(protocol_error) + } + _ => SocketError::from_raw_error(code, 0), + } +} + +impl SocketAdapter for TcpAdapter { + type Setup = Setup; + type Handle = Self; + + fn open(setup: Self::Setup) -> Result { + let mut handle: raw::TEE_iSocketHandle = ptr::null_mut(); + let mut protocol_error: u32 = 0; + let mut setup = raw::TEE_tcpSocket_Setup { + ipVersion: setup.version, + server_addr: setup.addr.as_ptr() as _, + server_port: setup.port, + }; + let ret = unsafe { + ((*raw::TEE_tcpSocket).open)( + &mut handle, + &mut setup as *mut raw::TEE_tcpSocket_Setup as _, + &mut protocol_error, + ) + }; + match ret { + raw::TEE_SUCCESS => Ok(Self(handle)), + _ => Err(SocketError::from_raw_error(ret, protocol_error)), + } + } + fn send(handle: &mut Self::Handle, buf: &[u8], timeout: u32) -> Result { + let mut length: u32 = buf.len() as _; + let ret = unsafe { + ((*raw::TEE_tcpSocket).send)( + handle.0, + buf.as_ptr() as *const u8 as _, + &mut length, + timeout, + ) + }; + match ret { + raw::TEE_SUCCESS => Ok(length as usize), + _ => Err(handle_socket_operation_error(handle.0, ret)), + } + } + fn recv(handle: &mut Self::Handle, buf: &mut [u8], timeout: u32) -> Result { + let mut length: u32 = buf.len() as _; + let ret = unsafe { + ((*raw::TEE_tcpSocket).recv)(handle.0, buf.as_mut_ptr() as _, &mut length, timeout) + }; + match ret { + raw::TEE_SUCCESS => Ok(length as usize), + _ => Err(handle_socket_operation_error(handle.0, ret)), + } + } +} + +impl Drop for TcpAdapter { + fn drop(&mut self) { + // Ignore any errors on close. + unsafe { + ((*raw::TEE_tcpSocket).close)(self.0); + } + } +} + +impl SocketAdapter for UdpAdapter { + type Setup = Setup; + type Handle = Self; + + fn open(setup: Self::Setup) -> Result { + let mut handle: raw::TEE_iSocketHandle = ptr::null_mut(); + let mut protocol_error: u32 = 0; + let mut setup = raw::TEE_udpSocket_Setup { + ipVersion: setup.version, + server_addr: setup.addr.as_ptr() as _, + server_port: setup.port, + }; + let ret = unsafe { + ((*raw::TEE_udpSocket).open)( + &mut handle, + &mut setup as *mut raw::TEE_udpSocket_Setup as _, + &mut protocol_error, + ) + }; + match ret { + raw::TEE_SUCCESS => Ok(Self(handle)), + _ => Err(SocketError::from_raw_error(ret, protocol_error)), + } + } + fn send(handle: &mut Self::Handle, buf: &[u8], timeout: u32) -> Result { + let mut length: u32 = buf.len() as _; + let ret = unsafe { + ((*raw::TEE_udpSocket).send)( + handle.0, + buf.as_ptr() as *const u8 as _, + &mut length, + timeout, + ) + }; + match ret { + raw::TEE_SUCCESS => Ok(length as usize), + _ => Err(handle_socket_operation_error(handle.0, ret)), + } + } + fn recv(handle: &mut Self::Handle, buf: &mut [u8], timeout: u32) -> Result { + let mut length: u32 = buf.len() as _; + let ret = unsafe { + ((*raw::TEE_udpSocket).recv)(handle.0, buf.as_mut_ptr() as _, &mut length, timeout) + }; + match ret { + raw::TEE_SUCCESS => Ok(length as usize), + _ => Err(handle_socket_operation_error(handle.0, ret)), + } + } +} + +impl Drop for UdpAdapter { + fn drop(&mut self) { + // Ignore any errors on close. + unsafe { + ((*raw::TEE_udpSocket).close)(self.0); + } + } +} diff --git a/optee-utee/src/net/optee_no_std.rs b/optee-utee/src/net/optee_no_std.rs new file mode 100644 index 00000000..dff4648b --- /dev/null +++ b/optee-utee/src/net/optee_no_std.rs @@ -0,0 +1,150 @@ +// 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. +use super::{Setup, Socket, SocketAdapter, SocketError}; + +/// A trait used for convenience; import it so that the code remains consistent +/// with the std version (with the only difference being the return error type). +/// +/// Take TcpStream as example: +/// ```no_run +/// use optee_utee::net::TcpStream; +/// +/// fn connect_without_compact_trait(host: &str, port: u16) -> Result { +/// let setup = Setup::new_v4(host, port)?; +/// TcpStream::open(setup) +/// } +/// +/// fn connect_with_compact_trait(host: &str, port: u16) -> Result { +/// use optee_utee::net::StdCompatConnect; +/// +/// TcpStream::connect_v4(host, port) +/// } +/// ``` +pub trait StdCompatConnect: Sized { + fn connect_v4(address: &str, port: u16) -> Result; + fn connect_v6(address: &str, port: u16) -> Result; + fn connect(address: &str, port: u16) -> Result { + Self::connect_v4(address, port) + } +} + +/// A trait used for convenience; import it so that the code remains consistent +/// with the std version (with the only difference being the return error type). +/// +/// Take TcpStream as example: +/// ```no_run +/// use optee_utee::net::TcpStream; +/// +/// fn write_without_compact_trait(stream: &mut Stream, mut buf: &[u8]) -> Result { +/// use optee_utee::ErrorKind; +/// +/// while !buf.is_empty() { +/// match stream.send(buf) { +/// Ok(0) => return Err(SocketError::Tee(ErrorKind::Generic)), +/// Ok(n) => buf = &buf[n..], +/// Err(e) => return Err(e), +/// } +/// } +/// Ok(()) +/// } +/// +/// fn write_with_compact_trait(stream: &mut Stream, buf: &[u8]) -> Result { +/// use optee_utee::net::StdCompatWrite; +/// +/// stream.write_all(buf) +/// } +/// ``` +pub trait StdCompatWrite { + fn write(&mut self, buf: &[u8]) -> Result; + fn write_all(&mut self, mut buf: &[u8]) -> Result<(), SocketError> { + while !buf.is_empty() { + match self.write(buf)? { + 0 => return Err(SocketError::Tee(crate::ErrorKind::Generic)), + n => buf = &buf[n..], + } + } + Ok(()) + } +} + +/// A trait used for convenience; import it so that the code remains consistent +/// with the std version (with the only difference being the return error type). +/// +/// Take TcpStream as example: +/// ```no_run +/// use optee_utee::net::TcpStream; +/// +/// fn read_without_compact_trait(stream: &mut Stream, mut buf: &mut [u8]) -> Result { +/// use optee_utee::ErrorKind; +/// +/// while !buf.is_empty() { +/// match stream.recv(buf) { +/// Ok(0) => break; +/// Ok(n) => buf = &mut buf[n..], +/// Err(e) => return Err(e), +/// } +/// } +/// if !buf.is_empty() { +/// return Err(SocketError::Tee(ErrorKind::Generic)); +/// } +/// Ok(()) +/// } +/// +/// fn read_with_compact_trait(stream: &mut Stream, buf: &mut [u8]) -> Result { +/// use optee_utee::net::StdCompatRead; +/// +/// stream.read_exact(buf) +/// } +/// ``` +pub trait StdCompatRead { + fn read(&mut self, buf: &mut [u8]) -> Result; + fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), SocketError> { + while !buf.is_empty() { + match self.read(buf)? { + 0 => break, + n => buf = &mut buf[n..], + } + } + if !buf.is_empty() { + return Err(SocketError::Tee(crate::ErrorKind::Generic)); + } + Ok(()) + } +} + +impl> StdCompatConnect for Socket { + fn connect_v4(address: &str, port: u16) -> Result { + let setup = Setup::new_v4(address, port)?; + Self::open(setup) + } + fn connect_v6(address: &str, port: u16) -> Result { + let setup = Setup::new_v6(address, port)?; + Self::open(setup) + } +} + +impl> StdCompatWrite for Socket { + fn write(&mut self, buf: &[u8]) -> Result { + self.send(buf) + } +} + +impl> StdCompatRead for Socket { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.recv(buf) + } +} diff --git a/optee-utee/src/net/optee_std.rs b/optee-utee/src/net/optee_std.rs new file mode 100644 index 00000000..e1aa2fbd --- /dev/null +++ b/optee-utee/src/net/optee_std.rs @@ -0,0 +1,146 @@ +// 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. + +use optee_utee_sys as raw; + +use super::optee::Setup; +use super::SocketError; +use super::{TcpStream, UdpSocket}; +use alloc::format; + +impl TcpStream { + fn connect_with_ip_version( + address: &str, + port: u16, + ip_version: raw::TEE_ipSocket_ipVersion, + ) -> std::io::Result { + let setup = Setup::new(address, port, ip_version) + .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Invalid address"))?; + Ok(Self::open(setup).map_err(|err| Into::::into(err))?) + } + pub fn connect_v4(address: &str, port: u16) -> std::io::Result { + Self::connect_with_ip_version(address, port, raw::TEE_ipSocket_ipVersion::TEE_IP_VERSION_4) + } + pub fn connect_v6(address: &str, port: u16) -> std::io::Result { + Self::connect_with_ip_version(address, port, raw::TEE_ipSocket_ipVersion::TEE_IP_VERSION_6) + } + pub fn connect(address: &str, port: u16) -> std::io::Result { + Self::connect_v4(address, port) + } +} + +impl std::io::Read for TcpStream { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + match self.recv(buf) { + Ok(v) => Ok(v as usize), + Err(err) => Err(Into::::into(err)), + } + } +} + +impl std::io::Write for TcpStream { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + match self.send(buf) { + Ok(v) => Ok(v as usize), + Err(err) => Err(Into::::into(err)), + } + } + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +impl UdpSocket { + fn connect_with_ip_version( + address: &str, + port: u16, + ip_version: raw::TEE_ipSocket_ipVersion, + ) -> std::io::Result { + let setup = Setup::new(address, port, ip_version) + .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Invalid address"))?; + Ok(Self::open(setup).map_err(|err| Into::::into(err))?) + } + pub fn connect_v4(address: &str, port: u16) -> std::io::Result { + Self::connect_with_ip_version(address, port, raw::TEE_ipSocket_ipVersion::TEE_IP_VERSION_4) + } + pub fn connect_v6(address: &str, port: u16) -> std::io::Result { + Self::connect_with_ip_version(address, port, raw::TEE_ipSocket_ipVersion::TEE_IP_VERSION_6) + } + pub fn connect(address: &str, port: u16) -> std::io::Result { + Self::connect_v4(address, port) + } +} + +impl std::io::Read for UdpSocket { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + match self.recv(buf) { + Ok(v) => Ok(v as usize), + Err(err) => Err(Into::::into(err)), + } + } +} + +impl std::io::Write for UdpSocket { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + match self.send(buf) { + Ok(v) => Ok(v as usize), + Err(err) => Err(Into::::into(err)), + } + } + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +// This is implemented to save developers from having to make numerous map_err +// calls. +impl Into for SocketError { + fn into(self) -> std::io::Error { + use std::io::{Error, ErrorKind}; + match self { + SocketError::ErrorProtocol(protocol_error) => Error::new( + ErrorKind::Other, + format!("TEE_ISOCKET_ERROR_PROTOCOL: 0x{:08X}", protocol_error), + ), + SocketError::RemoteClosed => Error::new( + ErrorKind::ConnectionAborted, + "TEE_ISOCKET_ERROR_REMOTE_CLOSED", + ), + SocketError::Timeout => Error::new(ErrorKind::TimedOut, "TEE_ISOCKET_ERROR_TIMEOUT"), + SocketError::OutOfResource => { + Error::new(ErrorKind::Other, "TEE_ISOCKET_ERROR_OUT_OF_RESOURCES") + } + SocketError::LargeBuffer => { + Error::new(ErrorKind::Other, "TEE_ISOCKET_ERROR_LARGE_BUFFER") + } + SocketError::WarningProtocol(protocol_error) => Error::new( + ErrorKind::Other, + format!("TEE_ISOCKET_WARNING_PROTOCOL: 0x{:08X}", protocol_error), + ), + SocketError::Hostname => Error::new(ErrorKind::Other, "TEE_ISOCKET_ERROR_HOSTNAME"), + SocketError::Tee(kind) => match kind { + crate::ErrorKind::OutOfMemory => { + Error::new(ErrorKind::OutOfMemory, "TEE_ERROR_OUT_OF_MEMORY") + } + _ => Error::new(ErrorKind::Other, kind.as_str()), + }, + SocketError::Unknown(code) => { + Error::new(ErrorKind::Other, format!("Unknown: {:08X}", code)) + } + } + } +} diff --git a/optee-utee/src/net/socket.rs b/optee-utee/src/net/socket.rs new file mode 100644 index 00000000..ac3d43b5 --- /dev/null +++ b/optee-utee/src/net/socket.rs @@ -0,0 +1,102 @@ +// 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. + +use optee_utee_sys as raw; + +use super::SocketError; +use core::time::Duration; + +/// A trait designed to accommodate various implementations of GP TEE Sockets +/// API. +/// +/// An implementation of this trait is responsible for handling all +/// protocol-related tasks, including but not limited to: +/// * Defining its own Setup type and using it to establish a new connection; +/// * Sending and receiving data over the connection, while managing protocol +/// errors (such as permitting certain warnings but raising others). +pub trait SocketAdapter: Sized { + type Setup; + type Handle; + + fn open(setup: Self::Setup) -> Result; + fn send(handle: &mut Self::Handle, buf: &[u8], timeout: u32) -> Result; + fn recv(handle: &mut Self::Handle, buf: &mut [u8], timeout: u32) -> Result; +} + +/// A struct used for socket operations. +pub struct Socket { + handle: T::Handle, + recv_timeout: u32, + send_timeout: u32, +} + +impl Socket { + /// create a new connection, and then sending and receiving data over it. + pub fn open(setup: T::Setup) -> Result { + let handle = T::open(setup)?; + Ok(Self { + handle, + recv_timeout: raw::TEE_TIMEOUT_INFINITE, + send_timeout: raw::TEE_TIMEOUT_INFINITE, + }) + } + /// set timeout of recv operation. + pub fn set_recv_timeout_in_milli(&mut self, milliseconds: u32) { + self.recv_timeout = milliseconds; + } + /// set timeout of send operation. + pub fn set_send_timeout_in_milli(&mut self, milliseconds: u32) { + self.send_timeout = milliseconds; + } + /// a wrapper of `set_recv_timeout_in_milli`, similar to `set_read_timeout` + /// in std::net::TcpStream, it will set timeout to `TEE_TIMEOUT_INFINITE` + /// if `Option::None` is provided. + pub fn set_recv_timeout(&mut self, dur: Option) -> crate::Result<()> { + let milliseconds = convert_duration_option_to_timeout(dur)?; + self.set_recv_timeout_in_milli(milliseconds); + Ok(()) + } + /// a wrapper of `set_send_timeout_in_milli`, similar to + /// `set_write_timeout` in std::net::TcpStream, it will set timeout to + /// `TEE_TIMEOUT_INFINITE` if `Option::None` is provided. + pub fn set_send_timeout(&mut self, dur: Option) -> crate::Result<()> { + let milliseconds = convert_duration_option_to_timeout(dur)?; + self.set_send_timeout_in_milli(milliseconds); + Ok(()) + } + /// send data, similar to `write` in `io::Write` + pub fn send(&mut self, buf: &[u8]) -> Result { + T::send(&mut self.handle, buf, self.send_timeout) + } + /// recv data, similar to `read` in `io::Read` + pub fn recv(&mut self, buf: &mut [u8]) -> Result { + T::recv(&mut self.handle, buf, self.recv_timeout) + } +} + +fn convert_duration_option_to_timeout(dur: Option) -> crate::Result { + match dur { + None => Ok(raw::TEE_TIMEOUT_INFINITE), + Some(v) => { + let milliseconds = v.as_millis(); + if milliseconds > (u32::MAX as u128) { + return Err(crate::ErrorKind::BadParameters.into()); + } + Ok(milliseconds as u32) + } + } +} diff --git a/tests/optee-qemuv8.sh b/tests/optee-qemuv8.sh index 0b8cd441..2a44e218 100755 --- a/tests/optee-qemuv8.sh +++ b/tests/optee-qemuv8.sh @@ -17,13 +17,6 @@ # specific language governing permissions and limitations # under the License. -# std examples: tcp_client, udp_socket, tls_client, tls_server needs the external network -if [ "$STD" ]; then - EXTERNAL_NETWORK_PARAMS=" \ - -netdev user,id=vmnic,hostfwd=:127.0.0.1:54433-:4433 \ - -device virtio-net-device,netdev=vmnic" -fi - cd $1 && ./qemu-system-aarch64 \ -nodefaults \ -nographic \ @@ -37,4 +30,6 @@ cd $1 && ./qemu-system-aarch64 \ -append 'console=ttyAMA0,38400 keep_bootcon root=/dev/vda2' \ -kernel Image -no-acpi \ -fsdev local,id=fsdev0,path=$(pwd)/../shared,security_model=none \ - -device virtio-9p-device,fsdev=fsdev0,mount_tag=host $EXTERNAL_NETWORK_PARAMS \ No newline at end of file + -device virtio-9p-device,fsdev=fsdev0,mount_tag=host \ + -netdev user,id=vmnic,hostfwd=:127.0.0.1:54433-:4433 \ + -device virtio-net-device,netdev=vmnic