Skip to main content

moqtap_codec/draft15/
fields.rs

1use crate::draft15::message::ControlMessage;
2use crate::fields::{FieldMap as Map, FieldValue as Value};
3use crate::kvp::{KeyValuePair, KvpValue};
4use crate::types::*;
5use crate::varint::VarInt;
6
7fn vi(v: u64) -> Value {
8    Value::Uint(v)
9}
10
11fn ns_to_json(ns: &TrackNamespace) -> Value {
12    Value::Array(
13        ns.0.iter().map(|e| Value::Text(String::from_utf8_lossy(e).into_owned())).collect(),
14    )
15}
16
17fn d15_setup_param_name(key: u64) -> Option<&'static str> {
18    match key {
19        0x01 => Some("path"),
20        0x02 => Some("max_request_id"),
21        0x03 => Some("authorization_token"),
22        0x04 => Some("max_auth_token_cache_size"),
23        0x05 => Some("authority"),
24        0x07 => Some("moqt_implementation"),
25        _ => None,
26    }
27}
28
29fn d15_msg_param_name(key: u64) -> Option<&'static str> {
30    match key {
31        0x02 => Some("delivery_timeout"),
32        0x03 => Some("authorization_token"),
33        0x04 => Some("max_cache_duration"),
34        0x08 => Some("expires"),
35        0x09 => Some("largest_object"),
36        0x0e => Some("publisher_priority"),
37        0x10 => Some("forward"),
38        0x20 => Some("subscriber_priority"),
39        0x21 => Some("subscription_filter"),
40        0x22 => Some("group_order"),
41        0x30 => Some("dynamic_groups"),
42        0x32 => Some("new_group_request"),
43        _ => None,
44    }
45}
46
47/// Render a SUBSCRIPTION FILTER (0x21) parameter value: a Filter Type and the
48/// Start Location and End Group that type promises.
49///
50/// # Nothing has checked that the value holds the fields its Filter Type names
51///
52/// 0x21 is an odd Type, so `KeyValuePair::decode` keeps whatever
53/// length-prefixed bytes arrived and the value reaches here unexamined. None of
54/// `decode_parameters`' own checks looks at the *contents* of a filter value,
55/// and `crate::dispatch::AnyControlMessage::fields` renders every message that
56/// decoded — so a peer's bytes reach this function directly. That is the chain
57/// `tests/hostile_parameter_values.rs` sets out in full.
58///
59/// The truncation that follows an AbsoluteStart or AbsoluteRange Filter Type is
60/// the nastier shape, because the value looks well formed right up to the point
61/// where it is not: the Filter Type decodes cleanly and the Start Location it
62/// promises is simply not there.
63///
64/// # What a value it cannot read renders as
65///
66/// The raw bytes, as `fields::params` and `decode_largest_object` do. Field
67/// extraction runs on a message that has already decoded, so it has no refusal
68/// to give: what a peer sent is what there is to show, and no read below may
69/// panic on it.
70fn decode_subscription_filter(bytes: &[u8]) -> Value {
71    let mut buf = bytes;
72    let Ok(filter_type) = VarInt::decode(&mut buf) else {
73        return Value::Bytes(bytes.to_vec());
74    };
75    let filter_type = filter_type.into_inner();
76    let mut obj = Map::new();
77    obj.insert("filter_type".into(), vi(filter_type));
78    match filter_type {
79        3 => {
80            // AbsoluteStart
81            let Ok(start_group) = VarInt::decode(&mut buf) else {
82                return Value::Bytes(bytes.to_vec());
83            };
84            let start_group = start_group.into_inner();
85            let Ok(start_object) = VarInt::decode(&mut buf) else {
86                return Value::Bytes(bytes.to_vec());
87            };
88            let start_object = start_object.into_inner();
89            obj.insert("start_group".into(), vi(start_group));
90            obj.insert("start_object".into(), vi(start_object));
91        }
92        4 => {
93            // AbsoluteRange
94            let Ok(start_group) = VarInt::decode(&mut buf) else {
95                return Value::Bytes(bytes.to_vec());
96            };
97            let start_group = start_group.into_inner();
98            let Ok(start_object) = VarInt::decode(&mut buf) else {
99                return Value::Bytes(bytes.to_vec());
100            };
101            let start_object = start_object.into_inner();
102            let Ok(end_group) = VarInt::decode(&mut buf) else {
103                return Value::Bytes(bytes.to_vec());
104            };
105            let end_group = end_group.into_inner();
106            obj.insert("start_group".into(), vi(start_group));
107            obj.insert("start_object".into(), vi(start_object));
108            obj.insert("end_group".into(), vi(end_group));
109        }
110        _ => {
111            // LatestGroup (1), LatestObject (2), or unknown — no extra fields
112        }
113    }
114    Value::Map(obj)
115}
116
117/// Render a draft-15 LARGEST_OBJECT (0x09) parameter value: a Group and an
118/// Object, as two varints.
119///
120/// # Nothing has checked that the value is two varints
121///
122/// Drafts 17 and later give 0x09 a `Location` encoding: their decoders read the
123/// two varints and re-serialise them into the stored value, so what reaches
124/// their extractor is two varints by construction. Draft-15 has no such table.
125/// 0x09 is an odd Type, so `KeyValuePair::decode` keeps whatever
126/// length-prefixed bytes arrived, and none of `decode_parameters`' four checks
127/// — duplicates, authorization tokens, varint value ranges, subscription
128/// filters — looks at 0x09. `KNOWN_VERSION_SPECIFIC_PARAMETERS` admits it and
129/// `check_parameter_scope` permits it on SUBSCRIBE_OK, so an eight-byte
130/// SUBSCRIBE_OK carrying `0x09` with an empty value reaches here.
131///
132/// An empty value fails the first read and a single `0x00` fails the *second*,
133/// which is the nastier of the two: the first varint decodes cleanly and the
134/// value looks well formed right up to the point where it is not.
135///
136/// # What a value it cannot read renders as
137///
138/// The raw bytes, as `fields::params` and this file's own
139/// `auth_token_to_json_d15` do. Field extraction runs on a message that has
140/// already decoded, so it has no refusal to give: what a peer sent is what
141/// there is to show.
142fn decode_largest_object(bytes: &[u8]) -> Value {
143    let mut buf = bytes;
144    let Ok(group) = VarInt::decode(&mut buf) else {
145        return Value::Bytes(bytes.to_vec());
146    };
147    let Ok(object) = VarInt::decode(&mut buf) else {
148        return Value::Bytes(bytes.to_vec());
149    };
150    let mut obj = Map::new();
151    obj.insert("group".into(), vi(group.into_inner()));
152    obj.insert("object".into(), vi(object.into_inner()));
153    Value::Map(obj)
154}
155
156/// Parse an authorization_token byte value into structured JSON.
157fn auth_token_to_json_d15(bytes: &[u8]) -> Value {
158    let mut buf = bytes;
159    let alias_type = match VarInt::decode(&mut buf) {
160        Ok(v) => v,
161        Err(_) => return Value::Bytes(bytes.to_vec()),
162    };
163    let at = alias_type.into_inner();
164    let mut o = Map::new();
165    o.insert("alias_type".into(), vi(at));
166    match at {
167        0 | 2 => {
168            if let Ok(ta) = VarInt::decode(&mut buf) {
169                o.insert("token_alias".into(), vi(ta.into_inner()));
170            }
171        }
172        1 => {
173            if let Ok(ta) = VarInt::decode(&mut buf) {
174                o.insert("token_alias".into(), vi(ta.into_inner()));
175            }
176            if let Ok(tt) = VarInt::decode(&mut buf) {
177                o.insert("token_type".into(), vi(tt.into_inner()));
178            }
179            o.insert("token_value".into(), Value::Bytes(buf.to_vec()));
180        }
181        _ => {
182            if let Ok(tt) = VarInt::decode(&mut buf) {
183                o.insert("token_type".into(), vi(tt.into_inner()));
184            }
185            o.insert("token_value".into(), Value::Bytes(buf.to_vec()));
186        }
187    }
188    Value::Map(o)
189}
190
191fn kvp_to_json_d15_inner(
192    params: &[KeyValuePair],
193    name_fn: fn(u64) -> Option<&'static str>,
194) -> Value {
195    crate::fields::kvp_entries(params, |key, value| {
196        let Some(name) = name_fn(key) else {
197            return (None, None);
198        };
199        let rendered = match (value, key) {
200            (KvpValue::Bytes(b), 0x21) => decode_subscription_filter(b),
201            (KvpValue::Bytes(b), 0x09) => decode_largest_object(b),
202            (KvpValue::Bytes(b), _) if name == "authorization_token" => auth_token_to_json_d15(b),
203            (KvpValue::Varint(v), _) => vi(v.into_inner()),
204            (KvpValue::Bytes(b), _) => Value::Text(String::from_utf8_lossy(b).into_owned()),
205        };
206        (Some(name), Some(rendered))
207    })
208}
209
210fn kvp_to_json_d15(params: &[KeyValuePair]) -> Value {
211    kvp_to_json_d15_inner(params, d15_msg_param_name)
212}
213
214pub(crate) fn kvp_to_json_d15_setup(params: &[KeyValuePair]) -> Value {
215    kvp_to_json_d15_inner(params, d15_setup_param_name)
216}
217
218/// This draft's field names for a decoded control message.
219///
220/// Keys are the names this draft gives its fields, in the order it defines
221/// them. An optional field the message did not carry is absent rather than
222/// zero.
223pub fn message_fields(msg: &ControlMessage) -> Map {
224    let obj = match msg {
225        ControlMessage::ClientSetup(m) => {
226            let mut o = Map::new();
227            o.insert("parameters".into(), kvp_to_json_d15_setup(&m.parameters));
228            o
229        }
230        ControlMessage::ServerSetup(m) => {
231            let mut o = Map::new();
232            o.insert("parameters".into(), kvp_to_json_d15_setup(&m.parameters));
233            o
234        }
235        ControlMessage::GoAway(m) => {
236            let mut o = Map::new();
237            o.insert(
238                "new_session_uri".into(),
239                Value::Text(String::from_utf8_lossy(&m.new_session_uri).into_owned()),
240            );
241            o
242        }
243        ControlMessage::MaxRequestId(m) => {
244            let mut o = Map::new();
245            o.insert("max_request_id".into(), vi(m.request_id.into_inner()));
246            o
247        }
248        ControlMessage::RequestsBlocked(m) => {
249            let mut o = Map::new();
250            o.insert("maximum_request_id".into(), vi(m.maximum_request_id.into_inner()));
251            o
252        }
253        ControlMessage::RequestOk(m) => {
254            let mut o = Map::new();
255            o.insert("request_id".into(), vi(m.request_id.into_inner()));
256            o.insert("parameters".into(), kvp_to_json_d15(&m.parameters));
257            o
258        }
259        ControlMessage::RequestError(m) => {
260            let mut o = Map::new();
261            o.insert("request_id".into(), vi(m.request_id.into_inner()));
262            o.insert("error_code".into(), vi(m.error_code.into_inner()));
263            o.insert(
264                "reason_phrase".into(),
265                Value::Text(String::from_utf8_lossy(&m.reason_phrase).into_owned()),
266            );
267            o
268        }
269        ControlMessage::Subscribe(m) => {
270            let mut o = Map::new();
271            o.insert("request_id".into(), vi(m.request_id.into_inner()));
272            o.insert("track_namespace".into(), ns_to_json(&m.track_namespace));
273            o.insert(
274                "track_name".into(),
275                Value::Text(String::from_utf8_lossy(&m.track_name).into_owned()),
276            );
277            o.insert("parameters".into(), kvp_to_json_d15(&m.parameters));
278            o
279        }
280        ControlMessage::SubscribeOk(m) => {
281            let mut o = Map::new();
282            o.insert("request_id".into(), vi(m.request_id.into_inner()));
283            o.insert("track_alias".into(), vi(m.track_alias.into_inner()));
284            o.insert("parameters".into(), kvp_to_json_d15(&m.parameters));
285            o
286        }
287        ControlMessage::SubscribeUpdate(m) => {
288            let mut o = Map::new();
289            o.insert("request_id".into(), vi(m.request_id.into_inner()));
290            o.insert("subscription_request_id".into(), vi(m.subscription_request_id.into_inner()));
291            o.insert("parameters".into(), kvp_to_json_d15(&m.parameters));
292            o
293        }
294        ControlMessage::Unsubscribe(m) => {
295            let mut o = Map::new();
296            o.insert("request_id".into(), vi(m.request_id.into_inner()));
297            o
298        }
299        ControlMessage::Publish(m) => {
300            let mut o = Map::new();
301            o.insert("request_id".into(), vi(m.request_id.into_inner()));
302            o.insert("track_namespace".into(), ns_to_json(&m.track_namespace));
303            o.insert(
304                "track_name".into(),
305                Value::Text(String::from_utf8_lossy(&m.track_name).into_owned()),
306            );
307            o.insert("track_alias".into(), vi(m.track_alias.into_inner()));
308            o.insert("parameters".into(), kvp_to_json_d15(&m.parameters));
309            o
310        }
311        ControlMessage::PublishOk(m) => {
312            let mut o = Map::new();
313            o.insert("request_id".into(), vi(m.request_id.into_inner()));
314            o.insert("parameters".into(), kvp_to_json_d15(&m.parameters));
315            o
316        }
317        ControlMessage::PublishDone(m) => {
318            let mut o = Map::new();
319            o.insert("request_id".into(), vi(m.request_id.into_inner()));
320            o.insert("status_code".into(), vi(m.status_code.into_inner()));
321            o.insert("stream_count".into(), vi(m.stream_count.into_inner()));
322            o.insert(
323                "reason_phrase".into(),
324                Value::Text(String::from_utf8_lossy(&m.reason_phrase).into_owned()),
325            );
326            o
327        }
328        ControlMessage::PublishNamespace(m) => {
329            let mut o = Map::new();
330            o.insert("request_id".into(), vi(m.request_id.into_inner()));
331            o.insert("track_namespace".into(), ns_to_json(&m.track_namespace));
332            o.insert("parameters".into(), kvp_to_json_d15(&m.parameters));
333            o
334        }
335        ControlMessage::PublishNamespaceDone(m) => {
336            let mut o = Map::new();
337            o.insert("track_namespace".into(), ns_to_json(&m.track_namespace));
338            o
339        }
340        ControlMessage::PublishNamespaceCancel(m) => {
341            let mut o = Map::new();
342            o.insert("track_namespace".into(), ns_to_json(&m.track_namespace));
343            o.insert("error_code".into(), vi(m.error_code.into_inner()));
344            o.insert(
345                "reason_phrase".into(),
346                Value::Text(String::from_utf8_lossy(&m.reason_phrase).into_owned()),
347            );
348            o
349        }
350        ControlMessage::SubscribeNamespace(m) => {
351            let mut o = Map::new();
352            o.insert("request_id".into(), vi(m.request_id.into_inner()));
353            o.insert("namespace_prefix".into(), ns_to_json(&m.namespace_prefix));
354            o.insert("parameters".into(), kvp_to_json_d15(&m.parameters));
355            o
356        }
357        ControlMessage::UnsubscribeNamespace(m) => {
358            let mut o = Map::new();
359            o.insert("request_id".into(), vi(m.request_id.into_inner()));
360            o
361        }
362        ControlMessage::TrackStatus(m) => {
363            let mut o = Map::new();
364            o.insert("request_id".into(), vi(m.request_id.into_inner()));
365            o.insert("track_namespace".into(), ns_to_json(&m.track_namespace));
366            o.insert(
367                "track_name".into(),
368                Value::Text(String::from_utf8_lossy(&m.track_name).into_owned()),
369            );
370            o.insert("parameters".into(), kvp_to_json_d15(&m.parameters));
371            o
372        }
373        ControlMessage::Fetch(m) => {
374            let mut o = Map::new();
375            o.insert("request_id".into(), vi(m.request_id.into_inner()));
376            o.insert("fetch_type".into(), vi(m.fetch_type as u64));
377            match &m.fetch_payload {
378                crate::draft15::message::FetchPayload::Standalone {
379                    track_namespace,
380                    track_name,
381                    start_group,
382                    start_object,
383                    end_group,
384                    end_object,
385                } => {
386                    o.insert("track_namespace".into(), ns_to_json(track_namespace));
387                    o.insert(
388                        "track_name".into(),
389                        Value::Text(String::from_utf8_lossy(track_name).into_owned()),
390                    );
391                    o.insert("start_group".into(), vi(start_group.into_inner()));
392                    o.insert("start_object".into(), vi(start_object.into_inner()));
393                    o.insert("end_group".into(), vi(end_group.into_inner()));
394                    o.insert("end_object".into(), vi(end_object.into_inner()));
395                }
396                crate::draft15::message::FetchPayload::Joining {
397                    joining_request_id,
398                    joining_start,
399                } => {
400                    o.insert("joining_request_id".into(), vi(joining_request_id.into_inner()));
401                    o.insert("joining_start".into(), vi(joining_start.into_inner()));
402                }
403            }
404            o.insert("parameters".into(), kvp_to_json_d15(&m.parameters));
405            o
406        }
407        ControlMessage::FetchOk(m) => {
408            let mut o = Map::new();
409            o.insert("request_id".into(), vi(m.request_id.into_inner()));
410            o.insert("end_of_track".into(), vi(m.end_of_track as u64));
411            o.insert("end_group".into(), vi(m.end_group.into_inner()));
412            o.insert("end_object".into(), vi(m.end_object.into_inner()));
413            o.insert("parameters".into(), kvp_to_json_d15(&m.parameters));
414            o
415        }
416        ControlMessage::FetchCancel(m) => {
417            let mut o = Map::new();
418            o.insert("request_id".into(), vi(m.request_id.into_inner()));
419            o
420        }
421    };
422    obj
423}