Skip to main content

dvb_si/descriptors/ait/
transport_protocol.rs

1//! Transport Protocol Descriptor — ETSI TS 102 809 §5.3.6.1, Table 28
2//! (AIT tag 0x02).
3//!
4//! Carried in the AIT descriptor loops. Identifies the transport mechanism
5//! for an application via `protocol_id` + `transport_protocol_label` + selector
6//! bytes. The selector bytes are interpreted per `protocol_id`:
7//!
8//! - 0x0001: Object Carousel (Table 31)
9//! - 0x0003: Interaction / HTTP (Table 32)
10//! - other: Unknown (raw bytes preserved)
11
12use crate::descriptors::descriptor_body;
13use crate::error::{Error, Result};
14use alloc::vec::Vec;
15use dvb_common::{Parse, Serialize};
16
17/// Descriptor tag for transport_protocol_descriptor (AIT namespace).
18pub const TAG: u8 = 0x02;
19const HEADER_LEN: usize = 2;
20const PROTOCOL_ID_LEN: usize = 2;
21const LABEL_LEN: usize = 1;
22
23/// Protocol ID for Object Carousel — ETSI TS 102 809 Table 29.
24pub const PROTOCOL_ID_OBJECT_CAROUSEL: u16 = 0x0001;
25/// Protocol ID for HTTP interaction channel — ETSI TS 102 809 Table 29.
26pub const PROTOCOL_ID_HTTP: u16 = 0x0003;
27
28/// Decoded Object Carousel selector — Table 31.
29#[derive(Debug, Clone, PartialEq, Eq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize))]
31pub struct OcSelector {
32    /// 1-bit remote_connection flag.
33    pub remote_connection: bool,
34    /// Present only when `remote_connection` is true.
35    pub remote_connection_info: Option<OcRemoteConnection>,
36    /// Component tag.
37    pub component_tag: u8,
38}
39
40/// Remote connection fields inside the OC selector (Table 31).
41#[derive(Debug, Clone, PartialEq, Eq)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize))]
43pub struct OcRemoteConnection {
44    /// 16-bit original_network_id.
45    pub original_network_id: u16,
46    /// 16-bit transport_stream_id.
47    pub transport_stream_id: u16,
48    /// 16-bit service_id.
49    pub service_id: u16,
50}
51
52/// One URL entry in the HTTP interaction selector — Table 32.
53#[derive(Debug, Clone, PartialEq, Eq)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize))]
55pub struct HttpUrlEntry<'a> {
56    /// URL base bytes.
57    pub url_base: &'a [u8],
58    /// URL extension strings.
59    pub url_extensions: Vec<&'a [u8]>,
60}
61
62/// Decoded HTTP interaction selector — Table 32.
63#[derive(Debug, Clone, PartialEq, Eq)]
64#[cfg_attr(feature = "serde", derive(serde::Serialize))]
65pub struct HttpSelector<'a> {
66    /// URL entries.
67    pub urls: Vec<HttpUrlEntry<'a>>,
68}
69
70/// Typed selector decoded from the raw selector bytes by `protocol_id`.
71#[derive(Debug, Clone, PartialEq, Eq)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize))]
73#[non_exhaustive]
74pub enum SelectorKind<'a> {
75    /// Object Carousel selector (protocol_id 0x0001).
76    ObjectCarousel(OcSelector),
77    /// HTTP interaction selector (protocol_id 0x0003).
78    Http(HttpSelector<'a>),
79    /// Unknown protocol — raw selector bytes.
80    Unknown(&'a [u8]),
81}
82
83impl SelectorKind<'_> {
84    /// Spec name for the selector variant.
85    #[must_use]
86    pub fn name(&self) -> &'static str {
87        match self {
88            Self::ObjectCarousel(_) => "OBJECT_CAROUSEL",
89            Self::Http(_) => "HTTP",
90            Self::Unknown(_) => "UNKNOWN",
91        }
92    }
93}
94dvb_common::impl_spec_display!(SelectorKind<'_>);
95
96/// Transport Protocol Descriptor (AIT tag 0x02).
97#[derive(Debug, Clone, PartialEq, Eq)]
98#[cfg_attr(feature = "serde", derive(serde::Serialize))]
99pub struct TransportProtocolDescriptor<'a> {
100    /// 16-bit protocol_id (Table 29).
101    pub protocol_id: u16,
102    /// Transport protocol label.
103    pub transport_protocol_label: u8,
104    /// Raw selector bytes (after protocol_id + label). Use `selector()`
105    /// for typed decoding.
106    #[cfg_attr(feature = "serde", serde(borrow))]
107    pub selector_bytes: &'a [u8],
108}
109
110impl<'a> TransportProtocolDescriptor<'a> {
111    /// Decode the selector bytes according to `protocol_id`.
112    #[must_use]
113    pub fn selector(&self) -> SelectorKind<'a> {
114        match self.protocol_id {
115            PROTOCOL_ID_OBJECT_CAROUSEL => self.decode_oc_selector(),
116            PROTOCOL_ID_HTTP => self.decode_http_selector(),
117            _ => SelectorKind::Unknown(self.selector_bytes),
118        }
119    }
120
121    fn decode_oc_selector(&self) -> SelectorKind<'a> {
122        let b = self.selector_bytes;
123        if b.len() < 2 {
124            return SelectorKind::Unknown(b);
125        }
126        let remote_connection = (b[0] & 0x80) != 0;
127        let mut pos = 1;
128        let remote_connection_info = if remote_connection {
129            if pos + 6 > b.len() {
130                return SelectorKind::Unknown(b);
131            }
132            let info = OcRemoteConnection {
133                original_network_id: u16::from_be_bytes([b[pos], b[pos + 1]]),
134                transport_stream_id: u16::from_be_bytes([b[pos + 2], b[pos + 3]]),
135                service_id: u16::from_be_bytes([b[pos + 4], b[pos + 5]]),
136            };
137            pos += 6;
138            Some(info)
139        } else {
140            None
141        };
142        // component_tag must still be present; a malformed selector that
143        // declared remote_connection but stops before the component_tag
144        // (e.g. exactly 7 bytes) falls back to Unknown rather than panicking.
145        if pos >= b.len() {
146            return SelectorKind::Unknown(b);
147        }
148        let component_tag = b[pos];
149        SelectorKind::ObjectCarousel(OcSelector {
150            remote_connection,
151            remote_connection_info,
152            component_tag,
153        })
154    }
155
156    fn decode_http_selector(&self) -> SelectorKind<'a> {
157        let b = self.selector_bytes;
158        let mut urls = Vec::new();
159        let mut pos = 0;
160        while pos < b.len() {
161            let url_base_length = b[pos] as usize;
162            pos += 1;
163            if pos + url_base_length > b.len() {
164                break;
165            }
166            let base_end = pos + url_base_length;
167            let url_base = &b[pos..base_end];
168            pos = base_end;
169            if pos >= b.len() {
170                urls.push(HttpUrlEntry {
171                    url_base,
172                    url_extensions: Vec::new(),
173                });
174                break;
175            }
176            let url_extension_count = b[pos] as usize;
177            pos += 1;
178            let mut url_extensions = Vec::with_capacity(url_extension_count);
179            for _ in 0..url_extension_count {
180                if pos >= b.len() {
181                    break;
182                }
183                let ext_len = b[pos] as usize;
184                pos += 1;
185                let ext_end = pos + ext_len;
186                if ext_end > b.len() {
187                    break;
188                }
189                url_extensions.push(&b[pos..ext_end]);
190                pos = ext_end;
191            }
192            urls.push(HttpUrlEntry {
193                url_base,
194                url_extensions,
195            });
196        }
197        SelectorKind::Http(HttpSelector { urls })
198    }
199}
200
201impl<'a> Parse<'a> for TransportProtocolDescriptor<'a> {
202    type Error = crate::error::Error;
203    fn parse(bytes: &'a [u8]) -> Result<Self> {
204        let body = descriptor_body(
205            bytes,
206            TAG,
207            "TransportProtocolDescriptor",
208            "unexpected tag for transport_protocol_descriptor",
209        )?;
210        if body.len() < PROTOCOL_ID_LEN + LABEL_LEN {
211            return Err(Error::InvalidDescriptor {
212                tag: TAG,
213                reason: "transport_protocol_descriptor body shorter than minimum 3 bytes",
214            });
215        }
216        let protocol_id = u16::from_be_bytes([body[0], body[1]]);
217        let transport_protocol_label = body[2];
218        let selector_bytes = &body[PROTOCOL_ID_LEN + LABEL_LEN..];
219        Ok(Self {
220            protocol_id,
221            transport_protocol_label,
222            selector_bytes,
223        })
224    }
225}
226
227impl Serialize for TransportProtocolDescriptor<'_> {
228    type Error = crate::error::Error;
229    fn serialized_len(&self) -> usize {
230        HEADER_LEN + PROTOCOL_ID_LEN + LABEL_LEN + self.selector_bytes.len()
231    }
232
233    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
234        let len = self.serialized_len();
235        let body_len = len - HEADER_LEN;
236        if body_len > u8::MAX as usize {
237            return Err(Error::InvalidDescriptor {
238                tag: TAG,
239                reason: "transport_protocol_descriptor body exceeds 255 bytes",
240            });
241        }
242        if buf.len() < len {
243            return Err(Error::OutputBufferTooSmall {
244                need: len,
245                have: buf.len(),
246            });
247        }
248        buf[0] = TAG;
249        buf[1] = body_len as u8;
250        buf[2..4].copy_from_slice(&self.protocol_id.to_be_bytes());
251        buf[4] = self.transport_protocol_label;
252        buf[5..5 + self.selector_bytes.len()].copy_from_slice(self.selector_bytes);
253        Ok(len)
254    }
255}
256
257impl<'a> crate::traits::DescriptorDef<'a> for TransportProtocolDescriptor<'a> {
258    const TAG: u8 = TAG;
259    const NAME: &'static str = "TRANSPORT_PROTOCOL";
260}
261
262#[cfg(test)]
263mod tests {
264    use super::*;
265
266    /// Body: protocol_id(2) + label(1) + remote_conn_rfu(1) + component_tag(1) = 5.
267    fn build_oc_local() -> [u8; 7] {
268        [TAG, 5, 0x00, 0x01, 0x01, 0x00, 0xB4]
269    }
270
271    /// Body: protocol_id(2) + label(1) + remote_conn(1) + org_id(2) + ts_id(2) + service_id(2) + component_tag(1) = 11.
272    fn build_oc_remote() -> [u8; 13] {
273        [
274            TAG, 11, 0x00, 0x01, 0x01, 0x80, // remote_connection=1, rfu=0000000
275            0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x04, // component_tag
276        ]
277    }
278
279    /// Body: protocol_id(2) + label(1) + url_base_len(1) + "http"(4) + ext_count(1) + ext_len(1) + "/app"(4) = 14.
280    fn build_http() -> [u8; 16] {
281        [
282            TAG, 14, 0x00, 0x03, 0x01, 4, b'h', b't', b't', b'p', 1, // url_extension_count
283            4, b'/', b'a', b'p', b'p',
284        ]
285    }
286
287    #[test]
288    fn parse_oc_local_selector() {
289        let bytes = build_oc_local();
290        let d = TransportProtocolDescriptor::parse(&bytes).unwrap();
291        assert_eq!(d.protocol_id, PROTOCOL_ID_OBJECT_CAROUSEL);
292        assert_eq!(d.transport_protocol_label, 0x01);
293        assert_eq!(d.selector_bytes, &[0x00, 0xB4]);
294        match d.selector() {
295            SelectorKind::ObjectCarousel(oc) => {
296                assert!(!oc.remote_connection);
297                assert!(oc.remote_connection_info.is_none());
298                assert_eq!(oc.component_tag, 0xB4);
299            }
300            other => panic!("expected ObjectCarousel, got {other:?}"),
301        }
302    }
303
304    #[test]
305    fn parse_oc_remote_selector() {
306        let bytes = build_oc_remote();
307        let d = TransportProtocolDescriptor::parse(&bytes).unwrap();
308        match d.selector() {
309            SelectorKind::ObjectCarousel(oc) => {
310                assert!(oc.remote_connection);
311                let rc = oc.remote_connection_info.unwrap();
312                assert_eq!(rc.original_network_id, 1);
313                assert_eq!(rc.transport_stream_id, 2);
314                assert_eq!(rc.service_id, 3);
315                assert_eq!(oc.component_tag, 0x04);
316            }
317            other => panic!("expected ObjectCarousel, got {other:?}"),
318        }
319    }
320
321    #[test]
322    fn parse_http_selector() {
323        let bytes = build_http();
324        let d = TransportProtocolDescriptor::parse(&bytes).unwrap();
325        assert_eq!(d.protocol_id, PROTOCOL_ID_HTTP);
326        match d.selector() {
327            SelectorKind::Http(http) => {
328                assert_eq!(http.urls.len(), 1);
329                assert_eq!(http.urls[0].url_base, b"http");
330                assert_eq!(http.urls[0].url_extensions.len(), 1);
331                assert_eq!(http.urls[0].url_extensions[0], b"/app");
332            }
333            other => panic!("expected Http, got {other:?}"),
334        }
335    }
336
337    #[test]
338    fn oc_remote_selector_missing_component_tag_is_unknown_not_panic() {
339        // protocol_id=0x0001, label, then a 7-byte selector that declares
340        // remote_connection=1 (+6 bytes of triplet) but omits component_tag.
341        // selector() must return Unknown, not panic.
342        let bytes = [
343            TAG, 10, // body = 10
344            0x00, 0x01, // protocol_id = 0x0001 (OC)
345            0x01, // transport_protocol_label
346            0x80, // remote_connection=1
347            0x00, 0x01, 0x00, 0x02, 0x00, 0x03, // 6-byte triplet, then NO component_tag
348        ];
349        let d = TransportProtocolDescriptor::parse(&bytes).unwrap();
350        assert_eq!(d.selector_bytes.len(), 7); // flag(1) + triplet(6), component_tag missing
351        assert!(d.selector_bytes[0] & 0x80 != 0); // remote_connection set
352        assert!(matches!(d.selector(), SelectorKind::Unknown(_)));
353    }
354
355    #[test]
356    fn parse_unknown_protocol() {
357        let bytes = [TAG, 5, 0x01, 0x00, 0x02, 0xCA, 0xFE];
358        let d = TransportProtocolDescriptor::parse(&bytes).unwrap();
359        assert_eq!(d.protocol_id, 0x0100);
360        assert!(matches!(d.selector(), SelectorKind::Unknown(_)));
361    }
362
363    #[test]
364    fn serialize_round_trip_oc() {
365        let bytes = build_oc_local();
366        let d = TransportProtocolDescriptor::parse(&bytes).unwrap();
367        let mut buf = vec![0u8; d.serialized_len()];
368        d.serialize_into(&mut buf).unwrap();
369        assert_eq!(buf.as_slice(), &bytes[..]);
370        let re = TransportProtocolDescriptor::parse(&buf).unwrap();
371        assert_eq!(d, re);
372    }
373
374    #[test]
375    fn serialize_round_trip_http() {
376        let bytes = build_http();
377        let d = TransportProtocolDescriptor::parse(&bytes).unwrap();
378        let mut buf = vec![0u8; d.serialized_len()];
379        d.serialize_into(&mut buf).unwrap();
380        assert_eq!(buf.as_slice(), &bytes[..]);
381        let re = TransportProtocolDescriptor::parse(&buf).unwrap();
382        assert_eq!(d, re);
383    }
384
385    #[test]
386    fn serialize_round_trip_oc_remote() {
387        let bytes = build_oc_remote();
388        let d = TransportProtocolDescriptor::parse(&bytes).unwrap();
389        let mut buf = vec![0u8; d.serialized_len()];
390        d.serialize_into(&mut buf).unwrap();
391        assert_eq!(buf.as_slice(), &bytes[..]);
392    }
393}