Skip to main content

netget/server/capnp_rpc/
rpc.rs

1//! The RPC messages NetGet speaks (rpc.capnp, level 1 without promise resolution), shared by the
2//! server and the client. Offsets read from `capnp compile -ocapnp rpc.capnp`; booleans whose
3//! declared default is true are stored inverted, as the encoding requires.
4use super::layout::{Builder, Message, StructBuilder, StructReader, Target};
5use anyhow::{Context, Result};
6
7pub const MSG_UNIMPLEMENTED: u16 = 0;
8pub const MSG_ABORT: u16 = 1;
9pub const MSG_CALL: u16 = 2;
10pub const MSG_RETURN: u16 = 3;
11pub const MSG_FINISH: u16 = 4;
12pub const MSG_RESOLVE: u16 = 5;
13pub const MSG_RELEASE: u16 = 6;
14pub const MSG_BOOTSTRAP: u16 = 8;
15pub const MSG_DISEMBARGO: u16 = 13;
16
17/// Exception.Type.
18pub const EXC_FAILED: u16 = 0;
19pub const EXC_OVERLOADED: u16 = 1;
20pub const EXC_UNIMPLEMENTED: u16 = 3;
21
22pub fn exception_type(name: &str) -> Option<u16> {
23    match name {
24        "failed" => Some(EXC_FAILED),
25        "overloaded" => Some(EXC_OVERLOADED),
26        "disconnected" => Some(2),
27        "unimplemented" => Some(EXC_UNIMPLEMENTED),
28        _ => None,
29    }
30}
31
32pub fn exception_name(t: u16) -> &'static str {
33    match t {
34        0 => "failed",
35        1 => "overloaded",
36        2 => "disconnected",
37        3 => "unimplemented",
38        _ => "unknown",
39    }
40}
41
42/// Where a call is addressed.
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum CallTarget {
45    Imported(u32),
46    /// A promised answer, with how many pointer-field steps it takes into the results.
47    Promised {
48        question: u32,
49        steps: u32,
50    },
51}
52
53pub enum Returned<'a> {
54    /// The payload's content and the export ids named by its cap table.
55    Results {
56        content: Target<'a>,
57        caps: Vec<Option<u32>>,
58    },
59    Exception {
60        reason: String,
61        kind: u16,
62    },
63    Other,
64}
65
66pub enum Incoming<'a> {
67    Bootstrap {
68        question: u32,
69    },
70    Call {
71        question: u32,
72        target: CallTarget,
73        interface: u64,
74        method: u16,
75        content: Target<'a>,
76    },
77    Return {
78        answer: u32,
79        returned: Returned<'a>,
80    },
81    Finish {
82        question: u32,
83    },
84    Release,
85    Resolve,
86    Abort {
87        reason: String,
88    },
89    Unimplemented,
90    /// Anything else, by its union tag: answered with `unimplemented`.
91    Other(u16),
92}
93
94fn body<'a>(m: &StructReader<'a>) -> Result<StructReader<'a>> {
95    m.struct_field(0)?.context("message without a body")
96}
97
98pub fn decode(msg: &Message) -> Result<Incoming<'_>> {
99    let m = msg.root_struct()?;
100    let tag = m.u16(0);
101    Ok(match tag {
102        MSG_BOOTSTRAP => Incoming::Bootstrap {
103            question: body(&m)?.u32(0),
104        },
105        MSG_CALL => {
106            let c = body(&m)?;
107            let t = c.struct_field(0)?.context("call without a target")?;
108            let target = match t.u16(2) {
109                0 => CallTarget::Imported(t.u32(0)),
110                _ => {
111                    let p = t.struct_field(0)?.context("promised answer missing")?;
112                    CallTarget::Promised {
113                        question: p.u32(0),
114                        steps: p.list_field(0)?.map_or(0, |l| l.len),
115                    }
116                }
117            };
118            let content = match c.struct_field(1)? {
119                Some(payload) => payload.pointer(0)?,
120                None => Target::Null,
121            };
122            Incoming::Call {
123                question: c.u32(0),
124                target,
125                interface: c.u64(1),
126                method: c.u16(2),
127                content,
128            }
129        }
130        MSG_RETURN => {
131            let r = body(&m)?;
132            let returned = match r.u16(3) {
133                0 => {
134                    let payload = r.struct_field(0)?;
135                    let mut caps = Vec::new();
136                    if let Some(table) = payload
137                        .as_ref()
138                        .map(|p| p.list_field(1))
139                        .transpose()?
140                        .flatten()
141                    {
142                        for i in 0..table.len {
143                            let d = table.struct_at(i)?;
144                            caps.push(match d.u16(0) {
145                                1 | 2 => Some(d.u32(1)),
146                                _ => None,
147                            });
148                        }
149                    }
150                    Returned::Results {
151                        content: match payload {
152                            Some(p) => p.pointer(0)?,
153                            None => Target::Null,
154                        },
155                        caps,
156                    }
157                }
158                1 => {
159                    let e = r.struct_field(0)?;
160                    Returned::Exception {
161                        reason: e
162                            .as_ref()
163                            .map(|e| e.text(0))
164                            .transpose()?
165                            .flatten()
166                            .unwrap_or_default(),
167                        kind: e.map_or(0, |e| e.u16(2)),
168                    }
169                }
170                _ => Returned::Other,
171            };
172            Incoming::Return {
173                answer: r.u32(0),
174                returned,
175            }
176        }
177        MSG_FINISH => Incoming::Finish {
178            question: body(&m)?.u32(0),
179        },
180        MSG_RELEASE => Incoming::Release,
181        MSG_RESOLVE => Incoming::Resolve,
182        MSG_ABORT => Incoming::Abort {
183            reason: m
184                .struct_field(0)?
185                .map(|e| e.text(0))
186                .transpose()?
187                .flatten()
188                .unwrap_or_default(),
189        },
190        MSG_UNIMPLEMENTED => Incoming::Unimplemented,
191        other => Incoming::Other(other),
192    })
193}
194
195fn message(tag: u16) -> (Builder, StructBuilder) {
196    let (mut b, m) = Builder::with_root(1, 1);
197    b.set_u16(m, 0, tag);
198    (b, m)
199}
200
201pub fn bootstrap(question: u32) -> Result<Vec<u64>> {
202    let (mut b, m) = message(MSG_BOOTSTRAP);
203    let s = b.init_struct(m, 0, 1, 1)?;
204    b.set_u32(s, 0, question);
205    Ok(b.words)
206}
207
208/// A Return whose results are built by `fill`, which receives the Payload struct.
209pub fn return_results(
210    answer: u32,
211    fill: impl FnOnce(&mut Builder, StructBuilder) -> Result<()>,
212) -> Result<Vec<u64>> {
213    let (mut b, m) = message(MSG_RETURN);
214    let r = b.init_struct(m, 0, 2, 1)?;
215    b.set_u32(r, 0, answer);
216    // union tag 0 = results; releaseParamCaps keeps its default (true).
217    let payload = b.init_struct(r, 0, 0, 2)?;
218    fill(&mut b, payload)?;
219    Ok(b.words)
220}
221
222/// A Return whose results are the capability this side exports as `export`.
223pub fn return_capability(answer: u32, export: u32) -> Result<Vec<u64>> {
224    return_results(answer, |b, payload| {
225        b.set_capability(payload, 0, 0)?;
226        let descriptors = b.init_struct_list(payload, 1, 1, 1, 1)?;
227        b.set_u16(descriptors[0], 0, 1); // senderHosted
228        b.set_u32(descriptors[0], 1, export);
229        Ok(())
230    })
231}
232
233pub fn return_exception(answer: u32, reason: &str, kind: u16) -> Result<Vec<u64>> {
234    let (mut b, m) = message(MSG_RETURN);
235    let r = b.init_struct(m, 0, 2, 1)?;
236    b.set_u32(r, 0, answer);
237    b.set_u16(r, 3, 1);
238    let e = b.init_struct(r, 0, 1, 2)?;
239    b.set_text(e, 0, reason)?;
240    b.set_u16(e, 2, kind);
241    Ok(b.words)
242}
243
244/// A Call on an imported capability; `fill` receives the Payload struct.
245pub fn call(
246    question: u32,
247    import: u32,
248    interface: u64,
249    method: u16,
250    fill: impl FnOnce(&mut Builder, StructBuilder) -> Result<()>,
251) -> Result<Vec<u64>> {
252    let (mut b, m) = message(MSG_CALL);
253    let c = b.init_struct(m, 0, 3, 3)?;
254    b.set_u32(c, 0, question);
255    b.set_u16(c, 2, method);
256    b.set_u64(c, 1, interface);
257    let t = b.init_struct(c, 0, 1, 1)?;
258    b.set_u32(t, 0, import);
259    let payload = b.init_struct(c, 1, 0, 2)?;
260    fill(&mut b, payload)?;
261    Ok(b.words)
262}
263
264/// Finish a question. `release_result_caps` false keeps the capabilities its results carried
265/// (the bootstrap capability must survive its question); the field defaults to true, so false
266/// is stored as a set bit.
267pub fn finish(question: u32, release_result_caps: bool) -> Result<Vec<u64>> {
268    let (mut b, m) = message(MSG_FINISH);
269    let f = b.init_struct(m, 0, 1, 0)?;
270    b.set_u32(f, 0, question);
271    b.set_bool(f, 32, !release_result_caps);
272    Ok(b.words)
273}
274
275pub fn abort(reason: &str) -> Result<Vec<u64>> {
276    let (mut b, m) = message(MSG_ABORT);
277    let e = b.init_struct(m, 0, 1, 2)?;
278    b.set_text(e, 0, reason)?;
279    b.set_u16(e, 2, EXC_FAILED);
280    Ok(b.words)
281}
282
283/// `unimplemented`, echoing the message it answers.
284pub fn unimplemented(original: &Message) -> Result<Vec<u64>> {
285    let (mut b, m) = message(MSG_UNIMPLEMENTED);
286    let slot = Builder::pointer_slot_of(m, 0)?;
287    b.copy_into(slot, original.root()?)?;
288    Ok(b.words)
289}