Skip to main content

sipx_sip/
rel.rs

1//! Reliable provisional responses (RFC 3262): 100rel, `RSeq`, `RAck` and PRACK.
2//!
3//! An ordinary `180 Ringing` is fire-and-forget. Over UDP it is simply lost sometimes, and the
4//! caller hears nothing while the callee's phone rings — or, worse, an early-media answer never
5//! arrives and the call connects to silence. RFC 3262 makes a provisional response an
6//! acknowledged message: the UAS numbers it, retransmits it until a PRACK comes back, and gives
7//! up on the whole invitation if none ever does.
8//!
9//! Some carriers will not accept a call without it, which is the practical reason this exists.
10//!
11//! Everything here is pure: sequence numbers, the ordering rule, and the decision about whether
12//! a request may or must be answered reliably. The retransmission clock lives a layer up.
13
14use std::fmt;
15
16use crate::error::HeaderError;
17use crate::headers::grammar::{parse_u64, trim};
18use crate::message::TypedHeader;
19use crate::name::HeaderName;
20
21/// The option tag, in `Supported`, `Require` and `Unsupported` (RFC 3262 §8.1).
22pub const OPTION_TAG: &str = "100rel";
23
24/// The largest first sequence number the RFC allows.
25///
26/// §3: the first `RSeq` "MUST be between 1 and 2**31 - 1". The ceiling is not decoration — the
27/// field is 32 bits and "`RSeq` numbers MUST NOT wrap around", so starting in the lower half
28/// leaves 2^31 responses of headroom before the rule could be broken.
29pub const MAX_FIRST_RSEQ: u32 = i32::MAX as u32;
30
31/// The `RSeq` header (RFC 3262 §7.1).
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
33pub struct RSeq(pub u32);
34
35impl TypedHeader for RSeq {
36    const NAME: HeaderName = HeaderName::RSeq;
37
38    fn decode(value: &[u8]) -> Result<Self, HeaderError> {
39        let n = parse_u64(trim(value), "RSeq")?;
40        u32::try_from(n)
41            .map(Self)
42            .map_err(|_| HeaderError::OutOfRange { header: "RSeq" })
43    }
44}
45
46/// The `RAck` header (RFC 3262 §7.2): `response-num CSeq-num Method`.
47///
48/// All three parts are load-bearing. The response number says *which* provisional is being
49/// acknowledged, and the `CSeq` pair says which request it belonged to — without them a PRACK
50/// for a re-INVITE could be matched against a provisional from the original INVITE.
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct RAck {
53    /// The `RSeq` of the response being acknowledged.
54    pub rseq: u32,
55    /// The `CSeq` number of the request that response answered.
56    pub cseq: u32,
57    /// The method of that request.
58    pub method: Vec<u8>,
59}
60
61impl TypedHeader for RAck {
62    const NAME: HeaderName = HeaderName::RAck;
63
64    fn decode(value: &[u8]) -> Result<Self, HeaderError> {
65        let mut parts = trim(value)
66            .split(u8::is_ascii_whitespace)
67            .filter(|p| !p.is_empty());
68        let bad = || HeaderError::Syntax { header: "RAck" };
69        let rseq = parse_u64(parts.next().ok_or_else(bad)?, "RAck")?;
70        let cseq = parse_u64(parts.next().ok_or_else(bad)?, "RAck")?;
71        let method = parts.next().ok_or_else(bad)?.to_vec();
72        // Three fields exactly. A fourth means the value is not what this grammar describes,
73        // and guessing which three were meant is how a PRACK gets matched to the wrong
74        // response.
75        if parts.next().is_some() || method.is_empty() {
76            return Err(bad());
77        }
78        Ok(Self {
79            rseq: u32::try_from(rseq).map_err(|_| HeaderError::OutOfRange { header: "RAck" })?,
80            cseq: u32::try_from(cseq).map_err(|_| HeaderError::OutOfRange { header: "RAck" })?,
81            method,
82        })
83    }
84}
85
86impl fmt::Display for RAck {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        write!(
89            f,
90            "{} {} {}",
91            self.rseq,
92            self.cseq,
93            String::from_utf8_lossy(&self.method)
94        )
95    }
96}
97
98/// What a UAS must do about reliability for an incoming request.
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub enum Reliability {
101    /// The peer said nothing about 100rel. §3: the UAS "MUST NOT send the provisional response
102    /// reliably" — a peer that has not asked for `RSeq` will not send PRACK, and the response
103    /// would be retransmitted for 32 seconds and then fail the invitation.
104    Forbidden,
105    /// The peer supports it. Reliable provisionals are allowed, not required.
106    Permitted,
107    /// The peer put it in `Require`. Provisionals must be reliable.
108    Required,
109    /// The peer requires it and this side will not: refuse with `420 Bad Extension` and an
110    /// `Unsupported: 100rel` (§3).
111    Refuse,
112}
113
114/// What the peer said about 100rel in its request.
115///
116/// A struct rather than two `bool` arguments, because `supported` and `required` are the same
117/// type and mean nearly opposite things, and a caller that transposes them turns "may send
118/// reliably" into "must". That is not hypothetical: writing this module's own tests transposed
119/// them on the first attempt.
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub struct Offered {
122    /// `100rel` appeared in `Supported`.
123    pub supported: bool,
124    /// `100rel` appeared in `Require`.
125    pub required: bool,
126}
127
128impl Offered {
129    /// What a request's `Supported` and `Require` say about 100rel.
130    #[must_use]
131    pub fn in_request(request: &crate::message::Request) -> Self {
132        let has = |name: &HeaderName| {
133            request
134                .headers
135                .get_all(name)
136                .any(|header| contains_tag(&header.value()))
137        };
138        Self {
139            supported: has(&HeaderName::Supported),
140            required: has(&HeaderName::Require),
141        }
142    }
143}
144
145/// Whether a comma-separated option-tag list contains `100rel`.
146fn contains_tag(value: &[u8]) -> bool {
147    value
148        .split(|&b| b == b',')
149        .any(|tag| trim(tag).eq_ignore_ascii_case(OPTION_TAG.as_bytes()))
150}
151
152/// Decide the UAS side (RFC 3262 §3).
153///
154/// `enabled` is local policy. Refusing outright is better than accepting and then not honouring
155/// it: a caller that put 100rel in `Require` is waiting for an `RSeq` that will never come, and
156/// silence looks the same as a network fault.
157#[must_use]
158pub fn reliability(peer: Offered, enabled: bool) -> Reliability {
159    match (peer.required, peer.supported, enabled) {
160        (true, _, true) => Reliability::Required,
161        (true, _, false) => Reliability::Refuse,
162        (false, true, true) => Reliability::Permitted,
163        // Not offered, or offered to a side that has it switched off. Either way an ordinary
164        // unreliable provisional is the only correct thing to send.
165        (false, _, _) => Reliability::Forbidden,
166    }
167}
168
169/// The UAS's numbering of reliable provisionals within one transaction (RFC 3262 §3).
170#[derive(Debug, Clone, Copy)]
171pub struct Numbering {
172    next: u32,
173    outstanding: Option<u32>,
174}
175
176impl Numbering {
177    /// Start numbering at `first`, which must be in `1..=MAX_FIRST_RSEQ`.
178    ///
179    /// The value is supplied rather than generated here because this crate has no randomness
180    /// and wants none — a sans-IO core that reaches for an entropy source has stopped being
181    /// one. Out-of-range values are clamped into the legal window rather than rejected: the
182    /// caller cannot usefully handle a failure to pick a number, and a number outside the
183    /// window is a protocol violation this side would be committing.
184    #[must_use]
185    pub fn starting_at(first: u32) -> Self {
186        Self {
187            next: first.clamp(1, MAX_FIRST_RSEQ),
188            outstanding: None,
189        }
190    }
191
192    /// The number for the next reliable provisional, or `None` if one is still unacknowledged.
193    ///
194    /// §3: "The UAS MUST NOT send a second reliable provisional response until the first is
195    /// acknowledged." Enforced by returning nothing rather than by trusting the caller, because
196    /// the guarantee the mechanism sells — that the peer received these *in order* — is exactly
197    /// what a second unacknowledged response destroys.
198    pub fn allocate(&mut self) -> Option<u32> {
199        if self.outstanding.is_some() {
200            return None;
201        }
202        let rseq = self.next;
203        self.next = self.next.saturating_add(1);
204        self.outstanding = Some(rseq);
205        Some(rseq)
206    }
207
208    /// The response still waiting for a PRACK.
209    #[must_use]
210    pub fn outstanding(&self) -> Option<u32> {
211        self.outstanding
212    }
213
214    /// Whether this `RAck` acknowledges the outstanding response, and clear it if so.
215    ///
216    /// §3 defines a match as same dialog, and `RAck`'s three fields equal to the response's
217    /// `RSeq` and the request's `CSeq` number and method. The dialog is the caller's to check;
218    /// everything else is here.
219    pub fn acknowledge(&mut self, ack: &RAck, cseq: u32, method: &[u8]) -> bool {
220        let matches = self.outstanding == Some(ack.rseq)
221            && ack.cseq == cseq
222            && ack.method.eq_ignore_ascii_case(method);
223        if matches {
224            self.outstanding = None;
225        }
226        matches
227    }
228}
229
230/// The UAC's view of the reliable provisionals arriving for one request (RFC 3262 §4).
231#[derive(Debug, Clone, Copy, Default)]
232pub struct Sequence {
233    last: Option<u32>,
234}
235
236/// What the UAC should do with a reliable provisional it has just received.
237#[derive(Debug, Clone, Copy, PartialEq, Eq)]
238pub enum Received {
239    /// In order. Acknowledge it with a PRACK.
240    Acknowledge,
241    /// A retransmission of one already seen. §4: "retransmissions of that response MUST be
242    /// discarded" — and notably *not* re-PRACKed, so a lossy path does not turn one ringing
243    /// response into a stream of PRACKs.
244    Duplicate,
245    /// Out of order: a gap, so an earlier response has not arrived. §4 says such a response
246    /// "MUST NOT be acknowledged with a PRACK, and MUST NOT be processed further".
247    OutOfOrder,
248}
249
250impl Sequence {
251    /// Classify a reliable provisional.
252    pub fn accept(&mut self, rseq: u32) -> Received {
253        match self.last {
254            // §4: the sequence "MUST be initialized to the RSeq header field in the first
255            // reliable provisional response received", whatever that value happens to be.
256            None => {
257                self.last = Some(rseq);
258                Received::Acknowledge
259            }
260            Some(last) if rseq == last => Received::Duplicate,
261            Some(last) if rseq == last.saturating_add(1) => {
262                self.last = Some(rseq);
263                Received::Acknowledge
264            }
265            Some(_) => Received::OutOfOrder,
266        }
267    }
268
269    /// The highest in-order number seen.
270    #[must_use]
271    pub fn last(&self) -> Option<u32> {
272        self.last
273    }
274}
275
276#[cfg(test)]
277#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
278mod tests {
279    use super::*;
280
281    #[test]
282    fn the_option_tag_is_found_however_the_peer_spells_the_list() {
283        use crate::{Limits, Message, parse_datagram};
284        let request = |extra: &str| {
285            let text = format!(
286                "INVITE sip:b@example.com SIP/2.0\r\n\
287                 Via: SIP/2.0/UDP 192.0.2.1;branch=z9hG4bKx\r\n\
288                 To: <sip:b@example.com>\r\n\
289                 From: <sip:a@example.net>;tag=1\r\n\
290                 Call-ID: c\r\n\
291                 CSeq: 1 INVITE\r\n\
292                 {extra}\
293                 Content-Length: 0\r\n\r\n"
294            );
295            match parse_datagram(bytes::Bytes::from(text), &Limits::datagram()).expect("parses") {
296                Message::Request(r) => r,
297                Message::Response(_) => panic!("a request"),
298            }
299        };
300        // Comma-joined with other tags, on its own row, and in a different case: all the same
301        // list as far as RFC 3261 §7.3.1 is concerned.
302        assert!(Offered::in_request(&request("Supported: timer, 100rel, path\r\n")).supported);
303        assert!(Offered::in_request(&request("Supported: 100REL\r\n")).supported);
304        assert!(Offered::in_request(&request("Require: 100rel\r\n")).required);
305        // Not a substring match: `100relx` is a different tag.
306        assert!(!Offered::in_request(&request("Supported: 100relx\r\n")).supported);
307        assert!(!Offered::in_request(&request("Supported: timer\r\n")).supported);
308    }
309
310    #[test]
311    fn an_rack_carries_all_three_fields() {
312        let ack = RAck::decode(b"9021 314159 INVITE").expect("parses");
313        assert_eq!(ack.rseq, 9021);
314        assert_eq!(ack.cseq, 314_159);
315        assert_eq!(ack.method, b"INVITE");
316        assert_eq!(ack.to_string(), "9021 314159 INVITE");
317    }
318
319    #[test]
320    fn a_malformed_rack_is_rejected_rather_than_guessed_at() {
321        // Two fields, four fields, and a missing method. Each could be "read generously" into
322        // something, and each generous reading is a PRACK matched against a response it does
323        // not acknowledge.
324        for value in [
325            &b"9021 314159"[..],
326            &b"9021 314159 INVITE extra"[..],
327            &b"9021"[..],
328            &b""[..],
329        ] {
330            assert!(
331                RAck::decode(value).is_err(),
332                "{:?} should not parse",
333                String::from_utf8_lossy(value)
334            );
335        }
336    }
337
338    #[test]
339    fn the_uas_will_not_number_a_second_response_before_the_first_is_acknowledged() {
340        let mut numbering = Numbering::starting_at(500);
341        assert_eq!(numbering.allocate(), Some(500));
342        // §3: not until the first is acknowledged. Otherwise the UAS cannot be sure the peer
343        // received them in order, which is the entire guarantee.
344        assert_eq!(numbering.allocate(), None);
345
346        let ack = RAck {
347            rseq: 500,
348            cseq: 1,
349            method: b"INVITE".to_vec(),
350        };
351        assert!(numbering.acknowledge(&ack, 1, b"INVITE"));
352        // §3: "greater by exactly one".
353        assert_eq!(numbering.allocate(), Some(501));
354    }
355
356    #[test]
357    fn a_prack_for_another_request_does_not_acknowledge_this_one() {
358        let mut numbering = Numbering::starting_at(500);
359        numbering.allocate();
360        // Right RSeq, wrong CSeq: a PRACK for a re-INVITE's provisional would otherwise stop
361        // the retransmissions of the original INVITE's.
362        let wrong_cseq = RAck {
363            rseq: 500,
364            cseq: 2,
365            method: b"INVITE".to_vec(),
366        };
367        assert!(!numbering.acknowledge(&wrong_cseq, 1, b"INVITE"));
368        let wrong_method = RAck {
369            rseq: 500,
370            cseq: 1,
371            method: b"UPDATE".to_vec(),
372        };
373        assert!(!numbering.acknowledge(&wrong_method, 1, b"INVITE"));
374        assert_eq!(numbering.outstanding(), Some(500));
375    }
376
377    #[test]
378    fn a_first_rseq_outside_the_window_is_brought_into_it() {
379        assert_eq!(Numbering::starting_at(0).allocate(), Some(1));
380        assert_eq!(
381            Numbering::starting_at(u32::MAX).allocate(),
382            Some(MAX_FIRST_RSEQ)
383        );
384    }
385
386    #[test]
387    fn the_uac_acknowledges_in_order_and_discards_the_rest() {
388        let mut seen = Sequence::default();
389        // Whatever the first value is, it is the baseline — the RFC picks it at random.
390        assert_eq!(seen.accept(9021), Received::Acknowledge);
391        assert_eq!(seen.accept(9021), Received::Duplicate);
392        assert_eq!(seen.accept(9022), Received::Acknowledge);
393        // A gap means an earlier response is missing; PRACKing this one would tell the UAS
394        // something arrived in order when it did not.
395        assert_eq!(seen.accept(9024), Received::OutOfOrder);
396        assert_eq!(seen.last(), Some(9022));
397        // And the missing one, arriving late, is still in order.
398        assert_eq!(seen.accept(9023), Received::Acknowledge);
399    }
400
401    #[test]
402    fn a_peer_that_never_mentioned_100rel_gets_unreliable_provisionals() {
403        // §3: "If the request did not include either a Supported or Require header field
404        // indicating this feature, the UAS MUST NOT send the provisional response reliably."
405        // Sending one anyway means retransmitting for 32 seconds at a peer that will never
406        // PRACK, and then failing an invitation that was working.
407        assert_eq!(
408            reliability(
409                Offered {
410                    supported: false,
411                    required: false
412                },
413                true
414            ),
415            Reliability::Forbidden
416        );
417    }
418
419    #[test]
420    fn a_requirement_this_side_will_not_meet_is_refused_rather_than_ignored() {
421        let asked = Offered {
422            supported: true,
423            required: true,
424        };
425        let offered = Offered {
426            supported: true,
427            required: false,
428        };
429        assert_eq!(reliability(asked, false), Reliability::Refuse);
430        assert_eq!(reliability(asked, true), Reliability::Required);
431        assert_eq!(reliability(offered, true), Reliability::Permitted);
432        // Switched off locally, and only offered: nothing to refuse, nothing to do.
433        assert_eq!(reliability(offered, false), Reliability::Forbidden);
434    }
435}