Skip to main content

redevplugin_worker_sdk/
resource.rs

1use crate::error::{Error, Result};
2use serde::Deserialize;
3
4pub const IO_FLAG_EOF: u32 = 1 << 0;
5pub const IO_FLAG_TEXT: u32 = 1 << 1;
6pub const IO_FLAG_BINARY: u32 = 1 << 2;
7pub const IO_FLAG_MESSAGE_END: u32 = 1 << 3;
8pub const IO_FLAG_DATAGRAM_END: u32 = 1 << 4;
9pub const MAX_IO_CHUNK_BYTES: usize = 64 * 1024;
10
11#[link(wasm_import_module = "redevplugin.io")]
12unsafe extern "C" {
13    #[link_name = "rdp_call_v1"]
14    fn rdp_call_v1(
15        request_ptr: i32,
16        request_len: i32,
17        response_ptr: i32,
18        response_capacity: i32,
19    ) -> i32;
20    #[link_name = "rdp_read_v1"]
21    fn rdp_read_v1(
22        handle: i64,
23        destination_ptr: i32,
24        destination_capacity: i32,
25        flags_ptr: i32,
26    ) -> i32;
27    #[link_name = "rdp_write_v1"]
28    fn rdp_write_v1(handle: i64, source_ptr: i32, source_len: i32, flags: i32) -> i32;
29    #[link_name = "rdp_seek_v1"]
30    fn rdp_seek_v1(handle: i64, offset: i64, whence: i32) -> i64;
31    #[link_name = "rdp_close_v1"]
32    fn rdp_close_v1(handle: i64) -> i32;
33    #[link_name = "rdp_last_error_v1"]
34    fn rdp_last_error_v1(response_ptr: i32, response_capacity: i32) -> i32;
35}
36
37#[derive(Debug)]
38pub(crate) struct Handle {
39    value: Option<u64>,
40}
41
42impl Handle {
43    pub(crate) fn new(value: u64) -> Result<Self> {
44        if value == 0 || value > i64::MAX as u64 {
45            return Err(Error::internal("Host returned an invalid resource handle"));
46        }
47        Ok(Self { value: Some(value) })
48    }
49
50    pub fn id(&self) -> u64 {
51        self.value.unwrap_or(0)
52    }
53
54    pub(crate) fn disarm(&mut self) {
55        self.value = None;
56    }
57
58    pub(crate) fn read(&mut self, capacity: usize) -> Result<(Vec<u8>, u32)> {
59        if capacity == 0 || capacity > MAX_IO_CHUNK_BYTES {
60            return Err(Error::from_abi_status(-1));
61        }
62        let handle = self.require_open()?;
63        let mut bytes = vec![0_u8; capacity];
64        let mut flags = 0_u32;
65        let status = unsafe {
66            rdp_read_v1(
67                handle as i64,
68                bytes.as_mut_ptr() as i32,
69                bytes.len() as i32,
70                (&mut flags as *mut u32) as i32,
71            )
72        };
73        let written = status_result(status)?;
74        if written > bytes.len() {
75            return Err(Error::internal("Host returned an oversized read result"));
76        }
77        bytes.truncate(written);
78        Ok((bytes, flags))
79    }
80
81    pub(crate) fn write(&mut self, source: &[u8], flags: u32) -> Result<()> {
82        if source.len() > MAX_IO_CHUNK_BYTES {
83            return Err(Error::from_abi_status(-11));
84        }
85        let handle = self.require_open()?;
86        let status = unsafe {
87            rdp_write_v1(
88                handle as i64,
89                source.as_ptr() as i32,
90                source.len() as i32,
91                flags as i32,
92            )
93        };
94        let written = status_result(status)?;
95        if written != source.len() {
96            return Err(Error::internal("Host returned a forbidden partial write"));
97        }
98        Ok(())
99    }
100
101    pub(crate) fn seek(&mut self, offset: i64, whence: u32) -> Result<u64> {
102        let handle = self.require_open()?;
103        let status = unsafe { rdp_seek_v1(handle as i64, offset, whence as i32) };
104        if status < 0 {
105            return Err(last_error(status as i32));
106        }
107        Ok(status as u64)
108    }
109
110    pub(crate) fn close(&mut self) -> Result<()> {
111        let Some(handle) = self.value.take() else {
112            return Ok(());
113        };
114        let status = unsafe { rdp_close_v1(handle as i64) };
115        status_result(status).map(|_| ())
116    }
117
118    fn require_open(&self) -> Result<u64> {
119        self.value.ok_or_else(|| Error::from_abi_status(-5))
120    }
121}
122
123impl Drop for Handle {
124    fn drop(&mut self) {
125        let _ = self.close();
126    }
127}
128
129pub(crate) fn call_control_raw(request: &[u8]) -> Result<Vec<u8>> {
130    if request.is_empty() || request.len() > MAX_IO_CHUNK_BYTES {
131        return Err(Error::from_abi_status(-1));
132    }
133    let mut response = vec![0_u8; MAX_IO_CHUNK_BYTES];
134    let status = unsafe {
135        rdp_call_v1(
136            request.as_ptr() as i32,
137            request.len() as i32,
138            response.as_mut_ptr() as i32,
139            response.len() as i32,
140        )
141    };
142    let written = status_result(status)?;
143    if written > response.len() {
144        return Err(Error::internal(
145            "Host returned an oversized control response",
146        ));
147    }
148    response.truncate(written);
149    Ok(response)
150}
151
152fn status_result(status: i32) -> Result<usize> {
153    if status < 0 {
154        return Err(last_error(status));
155    }
156    usize::try_from(status).map_err(|_| Error::internal("ABI result does not fit usize"))
157}
158
159fn last_error(status: i32) -> Error {
160    let mut response = vec![0_u8; MAX_IO_CHUNK_BYTES];
161    let written = unsafe { rdp_last_error_v1(response.as_mut_ptr() as i32, response.len() as i32) };
162    if written > 0 && (written as usize) <= response.len() {
163        response.truncate(written as usize);
164        #[derive(Deserialize)]
165        struct LastError {
166            code: crate::error::ErrorCode,
167            message: String,
168            #[serde(default)]
169            retryable: bool,
170            #[serde(default)]
171            details: serde_json::Value,
172        }
173        if let Ok(error) = serde_json::from_slice::<LastError>(&response) {
174            return Error {
175                code: error.code,
176                message: error.message,
177                retryable: error.retryable,
178                details: error.details,
179            };
180        }
181    }
182    Error::from_abi_status(status)
183}