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}