Skip to main content

sipx_call/
call.rs

1//! Establishing a call: INVITE with an SDP offer, media bound to the answer, and BYE.
2
3use std::future::Future;
4use std::net::{IpAddr, SocketAddr};
5use std::pin::Pin;
6use std::sync::Arc;
7use std::time::Duration;
8
9use tokio::time::Instant;
10use tokio_util::sync::CancellationToken;
11
12use bytes::Bytes;
13use sipx_media::ice::{LocalDescription, Negotiation as IceNegotiation};
14use sipx_media::{Codec, Interrupt, MediaPort, MediaSession, Playback};
15use sipx_sdp::ice::{ComponentId, Credentials as IceCredentials};
16use sipx_sdp::{Capabilities, Connection, Direction, SessionDescription};
17use sipx_sip::build::{RequestBuilder, ResponseBuilder};
18use sipx_sip::session::{self, MinSe, SessionExpires};
19use sipx_sip::update::{self, Reception};
20use sipx_sip::{
21    HeaderName, HistoryInfo, Method, Reason, ReasonValue, Request, Response, StatusCode, Uri,
22};
23use sipx_transport::{Handle, Incoming, Target, TransportKind};
24
25pub use sipx_sip::auth::Credentials;
26
27use crate::dialog::{Dialog, strip_header_params};
28use crate::error::{Error, Result};
29use crate::event::{CallEvent, CallEvents, EndCause, EventSink};
30use crate::extension::{self, ApplicationRequest};
31use crate::identity::OutboundIdentityPolicy;
32use crate::media_policy::{Codecs, IcePolicy, Keying, MediaPolicy, MediaProfile, NegotiatedKeying};
33use crate::snapshot::{
34    DialogNotQuiescent, DialogPersistenceError, DialogRestoreContext, DialogSnapshot,
35    SessionSnapshot, SnapshotParts,
36};
37use crate::transfer::{
38    Referral, Replaces, Transfer, TransferState, is_terminated, parse_sipfrag, sipfrag,
39};
40
41/// 200 OK.
42///
43/// `StatusCode::new` is fallible because most codes come from the wire; this one is a literal
44/// that is always in range. Threading a `Result` out of every call site for it would mean
45/// inventing an error that can never happen — and the previous attempt reported it as "no
46/// final response to the INVITE", which would have been actively misleading.
47const OK: u16 = 200;
48
49pub(crate) fn ok_status() -> StatusCode {
50    StatusCode::new(OK).unwrap_or_else(|| unreachable!("200 is a valid status code"))
51}
52
53/// Queue the events construction already knows happened: `Ringing`, if the far end rang first,
54/// then `Answered` — every `Call` gets exactly this sequence at birth, on both the caller's and
55/// the callee's side, which is why both construction sites share it rather than repeating it.
56pub(crate) fn emit_construction_events(events: &EventSink, ringing: Option<bool>) {
57    if let Some(reliable) = ringing {
58        events.emit(CallEvent::Ringing { reliable });
59    }
60    events.emit(CallEvent::Answered);
61}
62
63/// A fresh token for a `Call-ID` or a `tag`.
64///
65/// Its own function rather than the user agent's digest `cnonce`: a dialog identifier is not an
66/// authentication nonce, and borrowing one ties this layer to the one that handles credentials
67/// for no reason beyond both wanting random hex.
68fn token_with_rng<R>(rng: &mut R) -> String
69where
70    R: rand::CryptoRng + ?Sized,
71{
72    let value = rand::RngCore::next_u64(rng);
73    format!("{value:016x}")
74}
75
76pub(crate) fn token() -> String {
77    token_with_rng(&mut rand::rng())
78}
79
80/// The two address roles of one media socket.
81///
82/// `advertised` is written into SDP and may be a public NAT mapping the host does not own.
83/// `bind` selects the local interface on which the RTP socket is opened. Passing an [`IpAddr`]
84/// keeps the historical behaviour by using it for both roles.
85///
86/// When ICE is enabled, these addresses are only the local gathering base and initial SDP
87/// default. A nominated ICE pair owns the live destination; symmetric RTP cannot replace it.
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub struct MediaAddress {
90    advertised: IpAddr,
91    bind: IpAddr,
92}
93
94impl MediaAddress {
95    /// Advertise and bind the same address.
96    #[must_use]
97    pub const fn new(advertised: IpAddr) -> Self {
98        Self {
99            advertised,
100            bind: advertised,
101        }
102    }
103
104    /// Bind RTP on `bind` while continuing to advertise the address passed to [`Self::new`].
105    ///
106    /// An unspecified bind address is valid. The advertised address must be reachable by the
107    /// peer; an unspecified one is refused before signalling. With ICE enabled,
108    /// the nominated pair takes precedence over symmetric-RTP source learning.
109    #[must_use]
110    pub const fn with_bind(mut self, bind: IpAddr) -> Self {
111        self.bind = bind;
112        self
113    }
114
115    /// The address serialized into SDP.
116    #[must_use]
117    pub const fn advertised(self) -> IpAddr {
118        self.advertised
119    }
120
121    /// The local address supplied to the RTP socket bind.
122    #[must_use]
123    pub const fn bind(self) -> IpAddr {
124        self.bind
125    }
126
127    fn validate(self) -> Result<Self> {
128        if self.advertised.is_unspecified() {
129            return Err(Error::UnspecifiedMediaAddress);
130        }
131        Ok(self)
132    }
133}
134
135impl From<IpAddr> for MediaAddress {
136    fn from(address: IpAddr) -> Self {
137        Self::new(address)
138    }
139}
140
141/// A call in progress.
142#[derive(Debug)]
143pub struct Call {
144    /// The dialog it runs in.
145    pub dialog: Dialog,
146    /// The successful final response that established this dialog.
147    ///
148    /// A caller retains the actual 2xx it received; an answerer records the 200 it sent. Keeping
149    /// this fact on the call lets applications report response-code distributions without
150    /// inventing `200` for a peer that answered with a different successful status.
151    initial_status: u16,
152    media: Arc<MediaSession>,
153    /// Replaced sessions whose workers have been stopped but not yet completely joined. A
154    /// cancelled renegotiation leaves this ownership in the call for retry or terminal cleanup.
155    retired_media: Vec<Arc<MediaSession>>,
156    endpoint: Handle,
157    /// Where in-dialog requests go: the peer's `Contact`, not where the INVITE was sent.
158    target: Target,
159    /// Set while a 2xx is still being retransmitted; cleared when the ACK arrives.
160    ack_stop: Option<CancellationToken>,
161    /// Completion of the successful-final-response retransmitter. Retained separately from its
162    /// stop signal so an ACK or terminal call path proves that the worker has actually exited.
163    ack_retransmission: Option<OwnedTask>,
164    /// Capabilities behind an offer sent in a re-INVITE's 2xx, awaiting its answer in the ACK.
165    delayed_offer: Option<Capabilities>,
166    ended: bool,
167    /// Where this side receives media, so a re-offer can name the same address.
168    media_address: IpAddr,
169    /// Where replacement media sockets bind during an in-dialog renegotiation.
170    media_bind_address: IpAddr,
171    /// The codec set this call was placed or answered with, so a re-offer offers the same
172    /// set — a re-INVITE that silently narrowed to G.711 would move an Opus call mid-call.
173    codecs: Codecs,
174    /// Named composition policy retained for renegotiation and diagnostics.
175    profile: MediaProfile,
176    /// What the running session negotiated, for comparison against a re-offer.
177    current: Negotiated,
178    /// The peer's ICE credentials as this side last saw them (RFC 8839 §4.4.1.1.1).
179    ///
180    /// The only thing a restart can be recognised against: a later offer restarts ICE when **both**
181    /// its `ice-ufrag` and its `ice-pwd` differ from these. `None` is a call that never ran ICE, or
182    /// one whose peer has not described it — and neither can restart something that never started.
183    peer_ice: Option<sipx_sdp::ice::Credentials>,
184    /// Whether the call is on hold, and which way.
185    hold: Direction,
186    /// Whether the media is encrypted.
187    encrypted: bool,
188    /// The initial keying policy, retained so a later offer cannot silently downgrade it.
189    keying: Keying,
190    /// A transfer the far end has asked for and we have not yet answered.
191    referral: Option<Referral>,
192    /// A transfer we asked for, and what has become of it.
193    transfer: Option<Transfer>,
194    /// The RFC 4028 session timer, if one was negotiated.
195    session: Option<SessionState>,
196    /// Whose turn it is to offer and to answer (RFC 3311 §5, RFC 3264).
197    ///
198    /// Idle here: a `Call` exists only once the INVITE's offer/answer has completed, so nothing
199    /// is outstanding at construction on either side.
200    negotiation: update::Negotiation,
201    /// Whether the peer's `Allow` listed UPDATE (RFC 3311 §4).
202    ///
203    /// Read from the message that introduced the peer — the INVITE for a UAS, the 2xx for a
204    /// UAC — and refreshed from any later one. It is the only permission there is: RFC 4028
205    /// §7.4 turns it into the choice between UPDATE and a re-INVITE for a session refresh, and
206    /// a refresh sent by a method the far end does not implement draws a 405 and tears down a
207    /// call that was working.
208    peer_allows_update: bool,
209    /// Where this call's events go (story `C-3`). Every state change below is emitted through
210    /// this at the point it happens, not reconstructed afterwards from the fields above.
211    events: EventSink,
212    /// The one receiver [`Self::events`] hands out, until it does.
213    events_rx: Option<CallEvents>,
214    /// The diversion history received on the request or final response that established this
215    /// call. Retained because the application cannot recover it after the transaction stream is
216    /// consumed by call setup.
217    history: Option<HistoryInfo>,
218    /// Digest credentials retained for authenticated requests originated inside this dialog.
219    dialog_credentials: Option<Credentials>,
220    /// Case-sensitive private method tokens the application explicitly admitted.
221    admitted_dialog_methods: Vec<Bytes>,
222}
223
224/// A call-owned task that cannot detach if the call is abandoned without explicit shutdown.
225#[derive(Debug)]
226struct OwnedTask(tokio::task::JoinHandle<()>);
227
228impl OwnedTask {
229    fn new(owner: tokio::task::JoinHandle<()>) -> Self {
230        Self(owner)
231    }
232
233    async fn joined(&mut self) {
234        // discard: the caller has already selected the task's terminal protocol outcome; this
235        // await is only the ownership barrier and a cancellation JoinError cannot change it.
236        let _ = (&mut self.0).await;
237    }
238}
239
240impl Drop for OwnedTask {
241    fn drop(&mut self) {
242        self.0.abort();
243    }
244}
245
246async fn cancel_and_join(stop: &CancellationToken, owner: &mut OwnedTask) {
247    stop.cancel();
248    owner.joined().await;
249}
250
251async fn until_cancelled<F: Future>(stop: &CancellationToken, operation: F) -> Option<F::Output> {
252    tokio::select! {
253        biased;
254        () = stop.cancelled() => None,
255        output = operation => Some(output),
256    }
257}
258
259trait Retirable {
260    async fn finish(&self);
261}
262
263impl Retirable for Arc<MediaSession> {
264    async fn finish(&self) {
265        self.shutdown().await;
266    }
267}
268
269async fn drain_retired<T: Retirable>(retired: &mut Vec<T>) {
270    while let Some(previous) = retired.last() {
271        previous.finish().await;
272        retired.pop();
273    }
274}
275
276/// The pure half of accepting a peer's in-dialog offer.
277///
278/// Kept as one value so [`Call::can_accept_offer`] and [`Call::renegotiate`] cannot drift: the
279/// coupling asks the first question before it changes its other leg, and the call later applies
280/// exactly the description that passed that check.
281struct PreparedRenegotiation {
282    offer: SessionDescription,
283    negotiated: Negotiated,
284    answer: SessionDescription,
285    direction: Direction,
286}
287
288/// Refuse a socket-ownership change until renegotiation can replace the media session atomically.
289fn preserve_rtcp_mode(current: sipx_sdp::RtcpMode, proposed: sipx_sdp::RtcpMode) -> Result<()> {
290    if current == proposed {
291        Ok(())
292    } else {
293        Err(Error::RtcpModeChange { current, proposed })
294    }
295}
296
297/// The mode one answer selected for its corresponding offered audio section.
298fn exchanged_rtcp_mode(
299    offer: &SessionDescription,
300    answer: &SessionDescription,
301) -> sipx_sdp::RtcpMode {
302    offer
303        .media
304        .iter()
305        .zip(&answer.media)
306        .find(|(offered, _)| offered.media == "audio")
307        .map_or(sipx_sdp::RtcpMode::Separate, |(offered, answered)| {
308            sipx_sdp::RtcpMode::from_exchange(offered, answered)
309        })
310}
311
312/// The RTCP shape this implementation will select when answering `offer`.
313fn answering_rtcp_mode(offer: &SessionDescription) -> sipx_sdp::RtcpMode {
314    offer
315        .media
316        .iter()
317        .find(|media| media.media == "audio" && !media.is_rejected())
318        .filter(|media| media.rtcp_mux())
319        .map_or(sipx_sdp::RtcpMode::Separate, |_| sipx_sdp::RtcpMode::Mux)
320}
321
322/// A negotiated session timer and the deadline it is currently counting down to.
323#[derive(Debug, Clone, Copy)]
324struct SessionState {
325    terms: session::Session,
326    /// When [`Call::on_session_deadline`] should be called.
327    ///
328    /// Held as an absolute instant rather than recomputed from "now" on each poll, so that a
329    /// call driven by a loop that also does other work cannot have its timer pushed back
330    /// indefinitely by its own busyness.
331    act_at: Instant,
332}
333
334impl SessionState {
335    fn armed(terms: session::Session) -> Self {
336        Self {
337            terms,
338            act_at: Instant::now() + terms.act_after(),
339        }
340    }
341}
342
343fn retired_media_snapshot_refusal(count: usize) -> Option<DialogNotQuiescent> {
344    (count != 0).then_some(DialogNotQuiescent::MediaCleanup)
345}
346
347impl Call {
348    /// Capture the bounded protocol state needed to continue this confirmed dialog.
349    ///
350    /// `now` is explicit and only the session timer's remaining duration is retained. Sockets,
351    /// endpoint handles, media sessions, tasks, transactions, credentials, keys, entropy and
352    /// process-local clock instants never enter [`DialogSnapshot`]. Capture refuses any call with
353    /// active work whose safe continuation would require one of those runtime values.
354    pub fn dialog_snapshot(
355        &self,
356        now: Instant,
357    ) -> std::result::Result<DialogSnapshot, DialogPersistenceError> {
358        if self.ended {
359            return Err(DialogPersistenceError::NotQuiescent(
360                DialogNotQuiescent::Ended,
361            ));
362        }
363        if self.ack_retransmission.is_some() {
364            return Err(DialogPersistenceError::NotQuiescent(
365                DialogNotQuiescent::AwaitingAck,
366            ));
367        }
368        if !self.negotiation.is_idle() {
369            return Err(DialogPersistenceError::NotQuiescent(
370                DialogNotQuiescent::OfferAnswer,
371            ));
372        }
373        if let Some(reason) = retired_media_snapshot_refusal(self.retired_media.len()) {
374            return Err(DialogPersistenceError::NotQuiescent(reason));
375        }
376        if self.referral.is_some() || self.transfer.is_some() {
377            return Err(DialogPersistenceError::NotQuiescent(
378                DialogNotQuiescent::Transfer,
379            ));
380        }
381        if self.media.runs_ice() {
382            return Err(DialogPersistenceError::NotQuiescent(
383                DialogNotQuiescent::Ice,
384            ));
385        }
386        let session = self
387            .session
388            .map(|state| {
389                let remaining = state
390                    .act_at
391                    .checked_duration_since(now)
392                    .filter(|remaining| !remaining.is_zero())
393                    .ok_or({
394                        DialogPersistenceError::SessionActionDue(if state.terms.we_refresh {
395                            crate::DialogSessionAction::Refresh
396                        } else {
397                            crate::DialogSessionAction::Expire
398                        })
399                    })?;
400                Ok(SessionSnapshot {
401                    interval: state.terms.interval,
402                    we_refresh: state.terms.we_refresh,
403                    remaining,
404                })
405            })
406            .transpose()?;
407
408        DialogSnapshot::from_parts(SnapshotParts {
409            role: self.dialog.role,
410            id: self.dialog.id.clone(),
411            local_party: strip_header_params(&self.dialog.local_uri),
412            remote_party: strip_header_params(&self.dialog.remote_uri),
413            remote_target: self.dialog.remote_target.clone(),
414            route_set: self.dialog.route_set.clone(),
415            local_cseq: self.dialog.local_cseq,
416            remote_cseq: self.dialog.remote_cseq,
417            protected_signalling: self.target.transport.is_secure(),
418            media_keying: self.negotiated_keying(),
419            media_profile: self.profile,
420            codecs: self.codecs,
421            codec: self.current.codec,
422            clock_rate: self.current.clock_rate,
423            payload_type: self.current.wire_payload_type(),
424            receive_payload_type: self.current.receive_wire_payload_type(),
425            dtmf_payload_type: self.current.dtmf,
426            rtcp_mode: self.current.rtcp_mode,
427            hold: self.hold,
428            peer_allows_update: self.peer_allows_update,
429            session,
430        })
431    }
432
433    /// Attach validated durable dialog state to fresh endpoint and media drivers.
434    ///
435    /// Restoration is synchronous and performs no I/O. Every snapshot and context invariant is
436    /// checked before handles are cloned or events are published, so a refusal creates no task or
437    /// transaction and leaves the borrowed context running exactly as supplied. Snapshot storage,
438    /// authorization, encryption at rest, distribution and single-owner election belong to the
439    /// host; a successful decode proves format validity, not permission to resume a call.
440    pub fn restore_dialog(
441        snapshot: &DialogSnapshot,
442        context: &DialogRestoreContext,
443    ) -> std::result::Result<Self, DialogPersistenceError> {
444        let session = snapshot.validate_restore(context)?;
445        // The only mutation in restoration, after every fallible snapshot/context check. It is
446        // an atomic one-owner claim rather than runtime work: no task, transaction, socket or
447        // media worker starts here, and a concurrent duplicate restore gets a typed refusal.
448        context.claim()?;
449        let (events, events_rx) = EventSink::new();
450        Ok(Self {
451            dialog: snapshot.dialog(),
452            initial_status: OK,
453            media: Arc::clone(&context.media),
454            retired_media: Vec::new(),
455            endpoint: context.endpoint.clone(),
456            target: context.target.clone(),
457            ack_stop: None,
458            ack_retransmission: None,
459            delayed_offer: None,
460            ended: false,
461            media_address: context.media_address.advertised(),
462            media_bind_address: context.media_address.bind(),
463            codecs: snapshot.codecs_value(),
464            profile: snapshot.media_profile_value(),
465            current: snapshot.negotiated(context.remote_media),
466            peer_ice: None,
467            // Validation proved the freshly built media driver carries the durable direction.
468            // Install the injected runtime fact so restoration never trusts snapshot state alone.
469            hold: context.direction,
470            encrypted: context.media.is_encrypted(),
471            keying: context.policy.keying,
472            referral: None,
473            transfer: None,
474            session: session.map(|(interval, we_refresh, act_at)| SessionState {
475                terms: session::Session {
476                    interval,
477                    we_refresh,
478                },
479                act_at,
480            }),
481            negotiation: update::Negotiation::idle(),
482            peer_allows_update: snapshot.peer_allows_update_value(),
483            // Application-owned extension admission and credentials are runtime policy, not
484            // durable dialog facts. The host must install them again after restoration.
485            dialog_credentials: None,
486            admitted_dialog_methods: Vec::new(),
487            events,
488            events_rx: Some(events_rx),
489            history: None,
490        })
491    }
492
493    /// Signal and join the successful-final-response retransmitter, if one is active.
494    ///
495    /// The handle stays in `self` while it is awaited. Cancelling the caller therefore leaves the
496    /// ownership intact for the next ACK or terminal path instead of detaching the retransmitter.
497    async fn stop_ack_retransmission(&mut self) {
498        if let Some(stop) = self.ack_stop.take() {
499            if let Some(owner) = self.ack_retransmission.as_mut() {
500                cancel_and_join(&stop, owner).await;
501            } else {
502                stop.cancel();
503            }
504        } else if let Some(owner) = self.ack_retransmission.as_mut() {
505            owner.joined().await;
506        }
507        self.ack_retransmission = None;
508    }
509
510    async fn reap_retired_media(&mut self) {
511        drain_retired(&mut self.retired_media).await;
512    }
513
514    /// The successful final response that established this call.
515    #[must_use]
516    pub fn initial_status(&self) -> u16 {
517        self.initial_status
518    }
519
520    /// The audio.
521    #[must_use]
522    pub fn media(&self) -> &MediaSession {
523        &self.media
524    }
525
526    /// A shared media handle for an owning actor that must move one operation into a bounded task.
527    ///
528    /// Most applications should use [`Self::media`]. This form exists for interactive owners that
529    /// must keep accepting control commands while a recording receives frames; sharing the session
530    /// does not share or clone the `Call`'s signalling state.
531    pub fn media_handle(&self) -> Arc<MediaSession> {
532        Arc::clone(&self.media)
533    }
534
535    /// Install or clear the application callback for peer RTCP quality reports.
536    ///
537    /// This is call-owned policy: it remains installed across an ordinary re-INVITE, a media
538    /// session replacement, and an ICE restart. The callback itself must return promptly; see
539    /// [`sipx_media::RtcpQualityHook`].
540    pub fn set_rtcp_quality_hook(&self, hook: Option<sipx_media::RtcpQualityHook>) {
541        self.media.set_rtcp_quality_hook(hook);
542    }
543
544    /// The peer RTCP quality callback currently attached to this call.
545    #[must_use]
546    pub fn rtcp_quality_hook(&self) -> Option<sipx_media::RtcpQualityHook> {
547        self.media.rtcp_quality_hook()
548    }
549
550    /// A response handle for a coupling that must answer glare while an outgoing request borrows
551    /// this call's dialog state.
552    pub(crate) fn responder(&self) -> Handle {
553        self.endpoint.clone()
554    }
555
556    /// Send a DTMF digit.
557    pub async fn send_digit(&self, digit: sipx_rtp::Digit, duration: Duration) -> bool {
558        self.media.send_digit(digit, duration).await
559    }
560
561    /// Send a string of digits, each held for `duration`.
562    ///
563    /// Characters that are not DTMF digits are skipped rather than rejected: a caller passing
564    /// a formatted number should not have to strip the spaces and dashes itself.
565    pub async fn send_digits(&self, digits: &str, duration: Duration) -> bool {
566        for c in digits.chars() {
567            let Some(digit) = sipx_rtp::Digit::from_char(c) else {
568                continue;
569            };
570            if !self.media.send_digit(digit, duration).await {
571                return false;
572            }
573        }
574        true
575    }
576
577    /// Take the next digit the far end pressed.
578    ///
579    /// The digit only; a caller that wants how long it was held can read [`Self::media`]'s own
580    /// [`MediaSession::recv_digit`], which this delegates to.
581    pub async fn recv_digit(&self) -> Option<sipx_rtp::Digit> {
582        self.media
583            .recv_digit()
584            .await
585            .map(|(digit, _duration)| digit)
586    }
587
588    /// Play a clip and wait for it, reporting on the event stream when it stops.
589    ///
590    /// Paced by the send loop, so this resolves when the audio has actually gone out rather than
591    /// when it was queued. Emits [`CallEvent::PlaybackFinished`] either way, with `completed`
592    /// saying which happened: the clip ran to the end, or something cut it short. A host driving
593    /// the call from its events needs that distinction — "the announcement finished" and "the
594    /// caller hung up during the announcement" lead to different next steps.
595    ///
596    /// The packet size is the session's own, so a clip plays correctly under a codec whose clock
597    /// is not 8 kHz without the caller knowing the rate.
598    ///
599    /// This is [`Self::start_playback`] awaited, with [`Interrupt::Never`] — the clip runs to its
600    /// end whatever the far end presses. A caller that wants to stop it, or wants a keypress to,
601    /// needs the handle. Cancel-on-drop, like [`MediaSession::play`]: abandoning this future — a
602    /// `timeout` that fires, a lost `select!` — stops the clip rather than leaving it playing.
603    pub async fn play(&self, samples: &[i16]) -> bool {
604        let playback = self
605            .media
606            .start_playback(samples.to_vec(), Interrupt::Never);
607        let end = playback.play_out().await;
608        // Emitted from here rather than from a watcher task, so a caller that awaits this call
609        // can read the event immediately afterwards instead of racing a spawn.
610        self.events.emit(CallEvent::PlaybackFinished {
611            playback: playback.id(),
612            completed: end.completed(),
613        });
614        end.completed()
615    }
616
617    /// Convert and play explicit linear PCM, reporting completion on the call event stream.
618    ///
619    /// # Errors
620    ///
621    /// Returns [`sipx_audio::PcmError`] before queuing audio when the format cannot be converted.
622    pub async fn play_pcm(
623        &self,
624        pcm: &sipx_audio::Pcm,
625    ) -> std::result::Result<bool, sipx_audio::PcmError> {
626        let playback = self.media.start_pcm_playback(pcm, Interrupt::Never)?;
627        let end = playback.play_out().await;
628        self.events.emit(CallEvent::PlaybackFinished {
629            playback: playback.id(),
630            completed: end.completed(),
631        });
632        Ok(end.completed())
633    }
634
635    /// Start a clip and hand back a handle to it, without waiting (`M-17`).
636    ///
637    /// The primitive under "play a prompt and collect digits": the caller goes on to read digits
638    /// while the audio plays, and can reach back through the handle to stop the prompt — or ask
639    /// for [`Interrupt::OnDigit`] and have the far end's first keypress stop it. That keypress is
640    /// **not** consumed by interrupting; it arrives at [`Self::recv_digit`] like any other, which
641    /// is what makes the application contract's `gather{prompt, interruptible}`
642    /// (`docs/specs/app-contract.md` §6.2) buildable rather than a menu that eats the first digit
643    /// of every PIN.
644    ///
645    /// Clips **queue**: a second playback started while one is running begins when that one ends.
646    /// See [`MediaSession::start_playback`] for why, and for what a clip queued while another is
647    /// stopping does. The bound on stopping is [`Playback::STOP_BOUND_PACKETS`] packets.
648    ///
649    /// Reports [`CallEvent::PlaybackFinished`] for this playback however it ends, without the
650    /// caller having to await the handle — a watcher task does it, so a fire-and-forget
651    /// announcement is still observable to a host driving the call from its events.
652    pub fn start_playback(&self, samples: Vec<i16>, interrupt: Interrupt) -> Playback {
653        let playback = self.media.start_playback(samples, interrupt);
654        let watcher = playback.clone();
655        let emitter = self.events.emitter();
656        tokio::spawn(async move {
657            let end = watcher.finished().await;
658            emitter.emit(CallEvent::PlaybackFinished {
659                playback: watcher.id(),
660                completed: end.completed(),
661            });
662        });
663        playback
664    }
665
666    /// Convert explicit linear PCM and start a controllable playback.
667    ///
668    /// # Errors
669    ///
670    /// Returns [`sipx_audio::PcmError`] before creating a playback when conversion is refused.
671    pub fn start_pcm_playback(
672        &self,
673        pcm: &sipx_audio::Pcm,
674        interrupt: Interrupt,
675    ) -> std::result::Result<Playback, sipx_audio::PcmError> {
676        let playback = self.media.start_pcm_playback(pcm, interrupt)?;
677        let watcher = playback.clone();
678        let emitter = self.events.emitter();
679        tokio::spawn(async move {
680            let end = watcher.finished().await;
681            emitter.emit(CallEvent::PlaybackFinished {
682                playback: watcher.id(),
683                completed: end.completed(),
684            });
685        });
686        Ok(playback)
687    }
688
689    /// Record until the far end goes quiet for `idle`, and report the result on the event stream.
690    ///
691    /// Emits [`CallEvent::RecordingFinished`] carrying how much audio was captured — measured
692    /// from the samples themselves and the session's clock rate, not by timing the call, so the
693    /// number describes the recording rather than how long this side waited for it. The trailing
694    /// `idle` silence is not part of it: it is how the end was detected, not something the far
695    /// end said.
696    pub async fn record_until_idle(&self, idle: Duration) -> Vec<i16> {
697        let samples = self.media.record_until_idle(idle).await;
698        self.finished_recording(samples)
699    }
700
701    /// Record until `samples` samples have arrived or `within` elapses, and report the result on
702    /// the event stream.
703    ///
704    /// The counted wait, for a caller that knows how much audio the far end was given;
705    /// [`MediaSession::record_at_least`] has the reasoning, and why `within` is a bound on
706    /// failure rather than a window to measure in. Emits the same
707    /// [`CallEvent::RecordingFinished`] as [`Self::record_until_idle`], measured the same way —
708    /// from the samples, not from how long this side waited for them.
709    pub async fn record_at_least(&self, samples: usize, within: Duration) -> Vec<i16> {
710        let samples = self.media.record_at_least(samples, within).await;
711        self.finished_recording(samples)
712    }
713
714    /// Announce a finished recording and hand it back.
715    ///
716    /// Shared by both recording verbs so the duration on the event cannot come to mean one thing
717    /// for one of them and something else for the other.
718    fn finished_recording(&self, samples: Vec<i16>) -> Vec<i16> {
719        let rate = u64::from(self.media.clock_rate()).max(1);
720        let duration = Duration::from_micros(samples.len() as u64 * 1_000_000 / rate);
721        self.events.emit(CallEvent::RecordingFinished { duration });
722        samples
723    }
724
725    /// Stop contributing audio to the far end, without telling it anything (story `M-18`).
726    ///
727    /// # Mute is not hold
728    ///
729    /// This is the distinction the whole verb exists for, and getting it wrong is how a call ends
730    /// up renegotiated when all that was wanted was a quiet microphone:
731    ///
732    /// | | `mute` | [`reinvite(Direction::SendOnly)`](Self::reinvite) |
733    /// |---|---|---|
734    /// | Signalling | none — no re-INVITE, nothing on the wire | a re-INVITE the far end must answer |
735    /// | The SDP direction | unchanged; the session is the one that was negotiated | changed, and that *is* the mechanism |
736    /// | What the far end knows | nothing; [`is_on_hold`](Self::is_on_hold) there is unaffected | that this call is on hold, and it may play its own hold music |
737    /// | The RTP stream | keeps flowing, carrying silence | governed by the new direction |
738    /// | Can fail | no | yes — the far end can refuse the renegotiation |
739    ///
740    /// Hold is a state two parties agree on. Mute is a decision one party makes about its own
741    /// microphone, and a far end that could tell the difference between a muted caller and a
742    /// silent one would be reading something it was never sent.
743    ///
744    /// # What it does and does not gate
745    ///
746    /// Outbound audio only, and it is a gate rather than a suppressor: [`Self::play`] still runs
747    /// and still resolves the same way, the packets still go out at the same pacing, and what the
748    /// far end decodes out of them is silence. Reception is untouched — [`Self::recv_digit`],
749    /// [`Self::record_until_idle`] and [`MediaSession::quality`] all keep working while muted —
750    /// and so is DTMF in the sending direction: [`Self::send_digits`] is an explicit act by this
751    /// endpoint, like a keypad tone on a handset, not something the microphone picked up.
752    ///
753    /// Emits [`CallEvent::Muted`] on the transition, and nothing when the call was already muted.
754    pub fn mute(&self) {
755        if !self.media.set_muted(true) {
756            self.events.emit(CallEvent::Muted);
757        }
758    }
759
760    /// Contribute audio to the far end again, undoing [`Self::mute`].
761    ///
762    /// Emits [`CallEvent::Unmuted`] on the transition, and nothing when the call was not muted.
763    /// Like [`Self::mute`] it sends nothing: there is no renegotiation to undo, because muting
764    /// never made one.
765    pub fn unmute(&self) {
766        if self.media.set_muted(false) {
767            self.events.emit(CallEvent::Unmuted);
768        }
769    }
770
771    /// Whether this side's outbound audio is muted.
772    ///
773    /// Local state, and a different question from [`Self::is_on_hold`], which reports what the
774    /// *far end* has signalled about the session.
775    #[must_use]
776    pub fn is_muted(&self) -> bool {
777        self.media.is_muted()
778    }
779
780    /// Whether the media is encrypted (RFC 3711).
781    ///
782    /// Worth asking, and worth being able to answer without a packet capture. A call whose
783    /// signalling is encrypted and whose audio is not looks identical from the outside to one
784    /// where both are — which is exactly the confusion that makes people believe `sips:` covers
785    /// the media. It does not.
786    #[must_use]
787    pub fn is_encrypted(&self) -> bool {
788        self.encrypted
789    }
790
791    /// The keying mechanism this established call actually negotiated.
792    ///
793    /// Unlike the initial [`Keying`] policy, the result contains no `Auto`: by confirmation the
794    /// compatibility choice has resolved to either plain RTP or SDES-SRTP.
795    #[must_use]
796    pub fn negotiated_keying(&self) -> NegotiatedKeying {
797        if !self.encrypted {
798            NegotiatedKeying::Plain
799        } else if self.keying == Keying::DtlsSrtp {
800            NegotiatedKeying::DtlsSrtp
801        } else {
802            NegotiatedKeying::Sdes
803        }
804    }
805
806    /// The named media profile this established call retained.
807    #[must_use]
808    pub const fn media_profile(&self) -> MediaProfile {
809        self.profile
810    }
811
812    /// Nominated-pair, generation, state, and bounded ingress facts for browser audio.
813    #[must_use]
814    pub fn browser_component(&self) -> Option<sipx_media::browser::BrowserComponentSnapshot> {
815        self.media.browser_component()
816    }
817
818    /// RTP payload type selected for sending the established audio codec.
819    #[must_use]
820    pub fn negotiated_payload_type(&self) -> u8 {
821        self.current.wire_payload_type()
822    }
823
824    /// RTP payload type accepted when receiving the established audio codec.
825    ///
826    /// Usually equal to [`Self::negotiated_payload_type`], but each SDP description may assign a
827    /// different dynamic number to the same format (RFC 3264 §6.1).
828    #[must_use]
829    pub fn negotiated_receive_payload_type(&self) -> u8 {
830        self.current.receive_wire_payload_type()
831    }
832
833    /// RTP clock rate selected for the established audio codec.
834    #[must_use]
835    pub fn negotiated_clock_rate(&self) -> u32 {
836        self.media.clock_rate()
837    }
838
839    /// Whether the call has ended, from either side.
840    #[must_use]
841    pub fn is_ended(&self) -> bool {
842        self.ended
843    }
844
845    /// This call's event stream (story `C-3`).
846    ///
847    /// `Some` the first time this is called, `None` every time after — there is exactly one
848    /// consumer, per the vision's "own it, don't share it" (principle 3), so the receiver is
849    /// handed out rather than cloned.
850    pub fn events(&mut self) -> Option<CallEvents> {
851        self.events_rx.take()
852    }
853
854    /// Retain credentials for authenticated requests originated inside this dialog.
855    ///
856    /// Outbound calls inherit [`DialOptions::credentials`]. This setter supplies the equivalent
857    /// policy for answered calls or rotates the credentials on an existing call.
858    pub fn set_dialog_credentials(&mut self, credentials: Credentials) {
859        self.dialog_credentials = Some(credentials);
860    }
861
862    /// Admit one private, case-sensitive method token to the application-owned dialog path.
863    ///
864    /// Known SIP methods are refused: their ownership is decided by the stack, not converted into
865    /// a private extension by application policy.
866    pub fn admit_dialog_method(&mut self, method: &Method) -> Result<()> {
867        let token = extension::validate_method_for_admission(method)?;
868        if !self.admitted_dialog_methods.contains(&token) {
869            self.admitted_dialog_methods.push(token);
870        }
871        Ok(())
872    }
873
874    /// Send an application-owned request inside this dialog.
875    ///
876    /// The dialog supplies the Request-URI, route set, identifiers and next `CSeq`. `headers` may
877    /// contain application fields such as `Content-Type`, but never routing, dialog, framing, or
878    /// authorization fields. A supported 401/407 challenge is retried once when dialog credentials
879    /// are available.
880    pub async fn send_dialog_request(
881        &mut self,
882        method: Method,
883        headers: &[sipx_sip::Header],
884        body: Bytes,
885    ) -> Result<Response> {
886        if self.ended {
887            return Err(Error::DialogEnded);
888        }
889        if !extension::application_owned(&method, &self.admitted_dialog_methods) {
890            return Err(Error::StackOwnedDialogMethod(method));
891        }
892        extension::validate_request_parts(headers, &body)?;
893
894        let credentials = self.dialog_credentials.clone();
895        let first = self
896            .send_application_attempt(&method, headers, body.clone(), None)
897            .await?;
898        if first.status.is_success() {
899            return Ok(first);
900        }
901
902        let failure = rejection(&first);
903        let Error::AuthenticationChallenge { challenge, .. } = failure else {
904            return Err(failure);
905        };
906        let Some(credentials) = credentials else {
907            return Err(Error::Rejected {
908                status: first.status.code(),
909                reason: String::from_utf8_lossy(&first.reason).into_owned(),
910            });
911        };
912        let cnonce = token();
913        let authorization = Authorization {
914            challenge: &challenge,
915            credentials: &credentials,
916            nonce_count: 1,
917            cnonce: &cnonce,
918        };
919        let response = self
920            .send_application_attempt(&method, headers, body, Some(&authorization))
921            .await?;
922        if !response.status.is_success() {
923            return Err(rejection(&response));
924        }
925        Ok(response)
926    }
927
928    async fn send_application_attempt(
929        &mut self,
930        method: &Method,
931        headers: &[sipx_sip::Header],
932        body: Bytes,
933        authorization: Option<&Authorization<'_>>,
934    ) -> Result<Response> {
935        let cseq = self.dialog.next_cseq();
936        let mut request = application_request(&self.dialog, method, cseq, headers, body)?;
937        if let Some(authorization) = authorization {
938            authorize_invite(&mut request, authorization)?;
939        }
940        let mut responses = self.endpoint.send(request, self.target.clone()).await?;
941        responses.final_response().await.ok_or(Error::NoResponse)
942    }
943
944    /// Feed an in-dialog request to the call.
945    ///
946    /// Returns whether it belonged here. Without this an incoming BYE reaches nothing and the
947    /// local media session goes on sending RTP into a call the far end has torn down — worse
948    /// than a call that never connects, because it does not stop.
949    pub async fn handle(&mut self, incoming: &Incoming) -> Result<bool> {
950        if !self.dialog.matches(&incoming.request) {
951            return Ok(false);
952        }
953
954        match incoming.request.method {
955            Method::Ack => {
956                // The 2xx got through; stop retransmitting it.
957                self.stop_ack_retransmission().await;
958                self.accept_delayed_offer_answer(incoming.request.body())
959                    .await?;
960                Ok(true)
961            }
962            // An INVITE inside an existing dialog is a re-INVITE: a renegotiation of the call
963            // already running, not a new one.
964            Method::Invite => {
965                self.on_reinvite(incoming).await?;
966                Ok(true)
967            }
968            // RFC 3311 §5.1: an in-dialog renegotiation that does not disturb any INVITE
969            // transaction. In a confirmed dialog that is mostly a session refresh (RFC 4028
970            // §7.4), but a peer may equally use it to move the media, and either way it has to
971            // be answered promptly — §5.2 gives the UAS no window in which to ask anybody.
972            Method::Update => {
973                self.on_update(incoming).await?;
974                Ok(true)
975            }
976            // A REFER is not answered here, and that is deliberate. Every other in-dialog
977            // request has one correct response; a REFER asks *may I place a call on your
978            // behalf*, and only the application knows whether it may. `accept_referral` and
979            // `refuse_referral` are the two answers, and until one is given the transferor is
980            // waiting — which is honest, because it is.
981            Method::Refer => {
982                self.on_refer(incoming).await?;
983                Ok(true)
984            }
985            Method::Notify => {
986                self.on_notify(incoming).await?;
987                Ok(true)
988            }
989            // RFC 3261 §11.2: OPTIONS may be sent inside a dialog, where it is the cheapest
990            // keep-alive there is. Answered here rather than left to the application because
991            // `sipx_sip::update::ALLOW` — the one list this stack advertises, and the one a 405
992            // from [`serve`] carries — names OPTIONS. An advertisement that is not true is worse
993            // than a narrower one: a peer that reads the list and is then refused has been told
994            // two different things by the same endpoint.
995            Method::Options => {
996                self.on_options(incoming).await?;
997                Ok(true)
998            }
999            Method::Bye => {
1000                // §12.2.2 applies to every in-dialog request, not only the ones that
1001                // renegotiate: a BYE from behind the current sequence number is a stale
1002                // duplicate, and honouring it ends a call that is still running.
1003                if self.out_of_order(&incoming.request) {
1004                    self.refuse(incoming, 500, "Server Internal Error").await?;
1005                    return Ok(true);
1006                }
1007                self.record_remote_cseq(&incoming.request);
1008
1009                self.media.stop();
1010                self.ended = true;
1011                // Nothing left to keep alive, and leaving an elapsed deadline armed would have
1012                // `session_deadline` keep returning a time in the past, spinning any loop that
1013                // selects on it.
1014                self.session = None;
1015                self.stop_ack_retransmission().await;
1016                // Emitted here, at the point `ended` actually flips, rather than after the 200
1017                // OK below — the call is over the moment the far end's BYE is accepted, whether
1018                // or not building or sending the response then succeeds.
1019                self.events.end(EndCause::RemoteBye);
1020                let responded: Result<()> = async {
1021                    let response =
1022                        ResponseBuilder::to_request(&incoming.request, ok_status(), "OK")?.build();
1023                    self.endpoint.respond(&incoming.key, response).await?;
1024                    Ok(())
1025                }
1026                .await;
1027                self.media.shutdown().await;
1028                self.reap_retired_media().await;
1029                responded?;
1030                Ok(true)
1031            }
1032            ref method if extension::application_owned(method, &self.admitted_dialog_methods) => {
1033                if self.out_of_order(&incoming.request) {
1034                    self.refuse(incoming, 500, "Server Internal Error").await?;
1035                    return Ok(true);
1036                }
1037                if incoming.request.body().len() > extension::MAX_APPLICATION_BODY {
1038                    self.refuse(incoming, 413, "Content Too Large").await?;
1039                    return Err(Error::ApplicationBodyTooLarge {
1040                        actual: incoming.request.body().len(),
1041                        limit: extension::MAX_APPLICATION_BODY,
1042                    });
1043                }
1044                if !incoming.request.body().is_empty()
1045                    && incoming
1046                        .request
1047                        .headers
1048                        .get(&HeaderName::ContentType)
1049                        .is_none()
1050                {
1051                    self.refuse(incoming, 415, "Unsupported Media Type").await?;
1052                    return Err(Error::ApplicationContentTypeRequired);
1053                }
1054                self.record_remote_cseq(&incoming.request);
1055                self.events
1056                    .emit(CallEvent::ApplicationRequest(ApplicationRequest::new(
1057                        self.endpoint.clone(),
1058                        incoming.key.clone(),
1059                        &incoming.request,
1060                    )?));
1061                Ok(true)
1062            }
1063            _ => Ok(false),
1064        }
1065    }
1066
1067    /// The negotiated session interval, and whether this side is the one refreshing it.
1068    ///
1069    /// `None` means no timer was agreed, so nothing will ever notice a far end that stops
1070    /// answering — worth being able to check, because that is a property of the *peer*, not of
1071    /// what this side asked for.
1072    #[must_use]
1073    pub fn session_interval(&self) -> Option<(Duration, bool)> {
1074        self.session
1075            .map(|state| (state.terms.interval, state.terms.we_refresh))
1076    }
1077
1078    /// The diversion history received while this call was established.
1079    #[must_use]
1080    pub fn history(&self) -> Option<&HistoryInfo> {
1081        self.history.as_ref()
1082    }
1083
1084    /// When [`Self::on_session_deadline`] next needs to be called, if a timer was negotiated.
1085    ///
1086    /// Returned as an instant rather than as a future on purpose. A future would borrow the
1087    /// call for as long as it was being awaited, which is exactly the borrow
1088    /// [`Self::handle`] needs in the other arm of the `select!` this is written for.
1089    #[must_use]
1090    pub fn session_deadline(&self) -> Option<Instant> {
1091        self.session.map(|state| state.act_at)
1092    }
1093
1094    /// Do whatever the session timer's deadline asked for (RFC 4028 §10).
1095    ///
1096    /// For the refresher that is an UPDATE or a re-INVITE — whichever the peer's `Allow` says
1097    /// it can take (§7.4); for the other side it is a BYE,
1098    /// because nothing arrived and the far end is presumed gone. Calling this early is harmless
1099    /// — it re-reads the deadline and does nothing if it has not passed.
1100    pub async fn on_session_deadline(&mut self) -> Result<()> {
1101        let Some(state) = self.session else {
1102            return Ok(());
1103        };
1104        if Instant::now() < state.act_at {
1105            return Ok(());
1106        }
1107        if !state.terms.we_refresh {
1108            // §10: the side that is not refreshing "SHOULD send a BYE to terminate the
1109            // session". The media stops with it — a half-torn-down call that keeps streaming
1110            // is the failure this whole mechanism exists to end, not a gentler version of it.
1111            self.end(EndCause::Timeout).await?;
1112            return Err(Error::SessionExpired);
1113        }
1114        match self.refresh_session().await {
1115            Ok(()) => Ok(()),
1116            // §10: a refresh that times out or draws a 408 or 481 means the dialog is gone at
1117            // the far end, and RFC 3261 §12.2.1.2 says to BYE. Any other failure is about the
1118            // refresh, not the call: a 491 glare or a 500 leaves the session running until the
1119            // deadline we do not move, so the next attempt is the retry.
1120            Err(Error::NoResponse) => {
1121                self.end(EndCause::Timeout).await?;
1122                Err(Error::SessionExpired)
1123            }
1124            Err(Error::Rejected { status, reason }) => {
1125                const REQUEST_TIMEOUT: u16 = 408;
1126                const NO_SUCH_DIALOG: u16 = 481;
1127                if status == REQUEST_TIMEOUT || status == NO_SUCH_DIALOG {
1128                    self.end(EndCause::Timeout).await?;
1129                    return Err(Error::SessionExpired);
1130                }
1131                // Push the retry out so a peer answering 500 to every refresh is not asked
1132                // again immediately for the rest of the session interval.
1133                self.rearm();
1134                Err(Error::Rejected { status, reason })
1135            }
1136            Err(other) => {
1137                self.rearm();
1138                Err(other)
1139            }
1140        }
1141    }
1142
1143    /// Restart the countdown, because the session was refreshed.
1144    fn rearm(&mut self) {
1145        if let Some(state) = self.session.as_mut() {
1146            state.act_at = Instant::now() + state.terms.act_after();
1147        }
1148    }
1149
1150    /// Whether the far end has put the call on hold.
1151    #[must_use]
1152    pub fn is_on_hold(&self) -> bool {
1153        !self.hold.receives()
1154    }
1155
1156    /// Renegotiate an established call from a re-INVITE.
1157    ///
1158    /// The rule that shapes this: **a renegotiation that fails must leave the call running.** A
1159    /// re-INVITE tries to change something about a call that already works, so answering 488
1160    /// and carrying on is right; tearing the call down because the new offer was unusable
1161    /// would lose a call that was fine a moment ago.
1162    async fn on_reinvite(&mut self, incoming: &Incoming) -> Result<()> {
1163        if self.out_of_order(&incoming.request) {
1164            return self.refuse(incoming, 500, "Server Internal Error").await;
1165        }
1166        self.record_remote_cseq(&incoming.request);
1167
1168        if incoming.request.body().is_empty() {
1169            return self.offer_in_reinvite_success(incoming).await;
1170        }
1171
1172        // §5.2 rule 2's other source, and the reason the spec names INVITE alongside UPDATE: a
1173        // re-INVITE's offer is one this side owes an answer to until it produces one.
1174        if crate::update::carries_offer(&incoming.request) {
1175            self.negotiation.received_offer();
1176        }
1177        let renegotiated = self.renegotiate(incoming.request.body()).await;
1178        // On every path out of here the debt is settled: a 488 kills the offer and a 2xx
1179        // answers it, and a failure to renegotiate at all leaves nothing to answer.
1180        self.negotiation.sent_answer();
1181        let Some(answer_sdp) = renegotiated? else {
1182            return self.refuse_unacceptable(incoming).await;
1183        };
1184
1185        // RFC 4028 §7.2: any re-INVITE inside the dialog refreshes the session, whether or not
1186        // it was sent for that reason. Only counting the ones that carry `Session-Expires`
1187        // would hang up on a peer that is demonstrably alive and talking to us.
1188        self.rearm();
1189
1190        // RFC 3261 §12.2.2: a re-INVITE is a target refresh request, so its `Contact` replaces
1191        // the dialog's remote target. Without this the BYE still goes to where the peer was
1192        // when the call started, and a peer that has moved can never be told it is over.
1193        self.dialog.refresh_target(&incoming.request.headers);
1194        self.target = in_dialog_target(
1195            &self.dialog,
1196            Target::new(incoming.source, incoming.transport),
1197        );
1198
1199        let response = ResponseBuilder::to_request(&incoming.request, ok_status(), "OK")?
1200            .header(
1201                HeaderName::Contact,
1202                Bytes::from(contact_for(&self.endpoint, self.target.transport)),
1203            )?
1204            .header(
1205                HeaderName::Allow,
1206                Bytes::from_static(update::ALLOW.as_bytes()),
1207            )?
1208            .header(
1209                HeaderName::ContentType,
1210                Bytes::from_static(b"application/sdp"),
1211            )?
1212            .body(Bytes::from(answer_sdp.to_string_sdp()))
1213            .build();
1214        self.endpoint
1215            .respond(&incoming.key, response.clone())
1216            .await?;
1217
1218        // RFC 3261 §13.3.1.4 applies to the 2xx of *any* INVITE, not only the first: it is
1219        // retransmitted until the ACK arrives. The server transaction deliberately absorbs
1220        // retransmitted INVITEs without answering them again (RFC 6026), so if the TU does not
1221        // resend, one lost 200 deadlocks the renegotiation until the peer's Timer B — a single
1222        // dropped packet breaking hold and resume for half a minute.
1223        self.stop_ack_retransmission().await;
1224        let ack_stop = CancellationToken::new();
1225        let ack_retransmission = tokio::spawn(retransmit_until_acked(
1226            self.endpoint.clone(),
1227            incoming.key.clone(),
1228            response,
1229            ack_stop.clone(),
1230        ));
1231        self.ack_stop = Some(ack_stop);
1232        self.ack_retransmission = Some(OwnedTask::new(ack_retransmission));
1233        Ok(())
1234    }
1235
1236    /// Put our offer in the 2xx to a bodyless re-INVITE (RFC 3261 §14.2).
1237    async fn offer_in_reinvite_success(&mut self, incoming: &Incoming) -> Result<()> {
1238        if self.profile == MediaProfile::BrowserAudio
1239            || self.encrypted
1240            || !self.negotiation.may_offer()
1241        {
1242            return self.refuse_unacceptable(incoming).await;
1243        }
1244
1245        let mut capabilities = self
1246            .codecs
1247            .capabilities(self.media_address, self.media.local_addr().port());
1248        if self.current.rtcp_mode == sipx_sdp::RtcpMode::Mux {
1249            capabilities = capabilities.with_rtcp_mux();
1250        }
1251        capabilities.direction = self.hold;
1252        capabilities.session_version = self
1253            .dialog
1254            .remote_cseq
1255            .map_or(u64::from(self.dialog.local_cseq), u64::from);
1256        let mut offer = offer_from(&capabilities);
1257        self.offer_ice(&mut offer, IceOffer::Continue).await;
1258
1259        // The request itself is a target refresh even though its offer was delayed.
1260        self.rearm();
1261        self.dialog.refresh_target(&incoming.request.headers);
1262        self.target = in_dialog_target(
1263            &self.dialog,
1264            Target::new(incoming.source, incoming.transport),
1265        );
1266
1267        let response = ResponseBuilder::to_request(&incoming.request, ok_status(), "OK")?
1268            .header(
1269                HeaderName::Contact,
1270                Bytes::from(contact_for(&self.endpoint, self.target.transport)),
1271            )?
1272            .header(
1273                HeaderName::Allow,
1274                Bytes::from_static(update::ALLOW.as_bytes()),
1275            )?
1276            .header(
1277                HeaderName::ContentType,
1278                Bytes::from_static(b"application/sdp"),
1279            )?
1280            .body(Bytes::from(offer.to_string_sdp()))
1281            .build();
1282
1283        self.negotiation.sent_offer();
1284        self.delayed_offer = Some(capabilities);
1285        if let Err(error) = self.endpoint.respond(&incoming.key, response.clone()).await {
1286            self.delayed_offer = None;
1287            self.negotiation.received_answer();
1288            return Err(error.into());
1289        }
1290
1291        self.stop_ack_retransmission().await;
1292        let ack_stop = CancellationToken::new();
1293        let ack_retransmission = tokio::spawn(retransmit_until_acked(
1294            self.endpoint.clone(),
1295            incoming.key.clone(),
1296            response,
1297            ack_stop.clone(),
1298        ));
1299        self.ack_stop = Some(ack_stop);
1300        self.ack_retransmission = Some(OwnedTask::new(ack_retransmission));
1301        Ok(())
1302    }
1303
1304    /// Settle the answer carried by the ACK of a delayed-offer re-INVITE.
1305    async fn accept_delayed_offer_answer(&mut self, body: &[u8]) -> Result<()> {
1306        let Some(offered) = self.delayed_offer.take() else {
1307            return Ok(());
1308        };
1309        // Clear the exchange on every path: ACK has no response with which to repair a malformed
1310        // answer, and retaining this flag would turn one peer error into permanent glare.
1311        self.negotiation.received_answer();
1312
1313        let answer = sipx_sdp::parse(&String::from_utf8_lossy(body))
1314            .map_err(|error| Error::Sdp(error.to_string()))?;
1315        let settled = settle_answer(&offered, &answer, self.codecs)?;
1316        preserve_rtcp_mode(self.current.rtcp_mode, settled.negotiated.rtcp_mode)?;
1317        self.accept_answer_ice(&answer).await;
1318        self.move_media_if_changed(settled.negotiated).await
1319    }
1320
1321    /// Apply an offer that arrived in-dialog, and produce the answer to send back.
1322    ///
1323    /// `None` means the description is unusable and the caller must refuse — 488 for a
1324    /// re-INVITE (`M-8`) and for an UPDATE (RFC 3311 §5.2), which is the same rule for the same
1325    /// reason: **a renegotiation that fails leaves the call running.** Both requests try to
1326    /// change something that already works, so refusing the change and keeping the session is
1327    /// right; tearing the call down because the new offer was unusable would lose a call that
1328    /// was fine a moment ago.
1329    ///
1330    /// Shared by the two paths because they ask exactly the same question of exactly the same
1331    /// session and differ only in what carries the answer back.
1332    fn prepare_renegotiation(&self, body: &[u8]) -> Option<PreparedRenegotiation> {
1333        if self.keying == Keying::DtlsSrtp {
1334            return None;
1335        }
1336        let offer = sipx_sdp::parse(&String::from_utf8_lossy(body)).ok()?;
1337        let mut negotiated = negotiated(&offer, self.codecs).ok()?;
1338
1339        let mut capabilities = self
1340            .codecs
1341            .capabilities(self.media_address, self.media.local_addr().port());
1342        if self.current.rtcp_mode == sipx_sdp::RtcpMode::Mux {
1343            capabilities = capabilities.with_rtcp_mux();
1344        }
1345        let answer = sipx_sdp::answer(&offer, &capabilities);
1346        if answer
1347            .media
1348            .iter()
1349            .all(sipx_sdp::MediaDescription::is_rejected)
1350        {
1351            return None;
1352        }
1353        let proposed_mode = exchanged_rtcp_mode(&offer, &answer);
1354        // A muxed session owns one receive socket. Answering an offer that removed mux while
1355        // silently retaining that owner would put the wire and the running state in disagreement.
1356        // Refuse the offer with 488; the typed error is shared with the outbound paths below.
1357        preserve_rtcp_mode(self.current.rtcp_mode, proposed_mode).ok()?;
1358        negotiated.rtcp_mode = proposed_mode;
1359        let direction = offer
1360            .media
1361            .iter()
1362            .find(|media| media.media == "audio" && !media.is_rejected())
1363            .map(sipx_sdp::MediaDescription::direction)?;
1364        Some(PreparedRenegotiation {
1365            offer,
1366            negotiated,
1367            answer,
1368            direction,
1369        })
1370    }
1371
1372    /// Whether this call can answer an in-dialog offer, without changing call or media state.
1373    ///
1374    /// The coupling uses this before opening an exchange on its other leg. Syntax alone is not
1375    /// enough: the source call may have no common codec or may use DTLS-SRTP, whose renegotiation
1376    /// this layer deliberately refuses.
1377    pub(crate) fn can_accept_offer(&self, body: &[u8]) -> Option<Direction> {
1378        self.prepare_renegotiation(body)
1379            .map(|prepared| prepared.direction)
1380    }
1381
1382    async fn renegotiate(&mut self, body: &[u8]) -> Result<Option<SessionDescription>> {
1383        let Some(mut prepared) = self.prepare_renegotiation(body) else {
1384            return Ok(None);
1385        };
1386        self.answer_ice(&prepared.offer, &mut prepared.answer).await;
1387
1388        // Hold is a direction, not a separate state: `sendonly` or `inactive` from the far end
1389        // means it will not play what we send.
1390        let was_on_hold = self.is_on_hold();
1391        self.hold = prepared.direction;
1392        // Emitted right where `hold` changes, not by polling it afterwards — a renegotiation
1393        // that does not change the direction (a keep-alive, say) must not report a hold that
1394        // never happened.
1395        match (was_on_hold, self.is_on_hold()) {
1396            (false, true) => self.events.emit(CallEvent::Hold),
1397            (true, false) => self.events.emit(CallEvent::Resumed),
1398            _ => {}
1399        }
1400
1401        self.move_media_if_changed(prepared.negotiated).await?;
1402        Ok(Some(prepared.answer))
1403    }
1404
1405    /// Give the running agent the ICE half of an answer to one of our later offers.
1406    ///
1407    /// The offering side's mirror of [`Self::answer_ice`], and it signals nothing: the answer is
1408    /// the end of this exchange, so what comes back from the agent has no description left to go
1409    /// into. What matters is that the agent hears it at all — a restart this side offered is only
1410    /// half a restart until the peer's new credentials and candidates arrive.
1411    async fn accept_answer_ice(&mut self, answer: &SessionDescription) {
1412        if !self.media.runs_ice() {
1413            return;
1414        }
1415        let peer = answer
1416            .media
1417            .first()
1418            .map_or(IceNegotiation::Absent, |audio| {
1419                sipx_media::ice::negotiate(answer, audio)
1420            });
1421        // Recorded for the same reason the initial exchange records it: the next offer from the
1422        // peer is a restart only if it differs from what was last seen, and an answer is a
1423        // description like any other.
1424        self.peer_ice_restarted(&peer);
1425        // discard: this is the media path, and M12's clause is about the signalling one — the
1426        // counters for a media session that could not apply a renegotiation are `M-32`, which is
1427        // why `sipx-media` is not in the guard's `CRATES`. Nothing signalling is lost here in any
1428        // case: the peer's ICE half was recorded on the line above, which is what the *next* offer
1429        // is compared against, and a renegotiation that does not take leaves the candidate pair
1430        // already carrying the call in use.
1431        let _ = self.media.renegotiate_ice(None, Some(&peer)).await;
1432    }
1433
1434    /// Put this side's ICE half into a later offer (RFC 8839 §4.4; `ice.md` §13.5).
1435    ///
1436    /// The offering counterpart of [`Self::answer_ice`], and it carries the same rule: a stream
1437    /// doing ICE restates its half in **every** subsequent offer, because §6 makes their absence
1438    /// mean this side has stopped. [`Self::restart_ice`] is the one caller that also draws new
1439    /// credentials, and drawing them is the whole of what it does — §4.4.1.1.1 says both values
1440    /// changing *is* the restart, so there is no second flag to set on the wire.
1441    async fn offer_ice(&mut self, offer: &mut SessionDescription, ice: IceOffer) {
1442        if !self.media.runs_ice() {
1443            return;
1444        }
1445        let local = match ice {
1446            IceOffer::Continue => None,
1447            IceOffer::Restart => fresh_ice_parameters(),
1448        };
1449        // No peer half: this is an offer, and the answer that responds to it comes back through
1450        // `Dialing`/`renegotiate` like any other.
1451        let Some(signalled) = self.media.renegotiate_ice(local, None).await else {
1452            return;
1453        };
1454        let Some(audio) = offer.media.first_mut() else {
1455            return;
1456        };
1457        audio
1458            .attributes
1459            .retain(|attribute| !is_ice_attribute(attribute));
1460        audio.attributes.extend(ice_attributes(&signalled));
1461    }
1462
1463    /// Put this side's ICE half into the answer to a later offer (RFC 8839 §4.4; `ice.md` §13.5).
1464    ///
1465    /// Three things happen here and they are one operation because they must not be reordered:
1466    /// the offer is read for the peer's half, this side takes new parameters when §4.4.1.1.1 says
1467    /// the offer is a restart, and both are handed to the running agent — which is what decides
1468    /// whether the session is rebuilt. What comes back is what this answer signals.
1469    ///
1470    /// **A stream doing ICE re-signals on every exchange**, not only on a restart. §6 makes the
1471    /// absence of `candidate` attributes mean the peer has stopped doing ICE, so an answer that
1472    /// dropped them mid-call would tell the far end to fall back to symmetric RTP on a path it had
1473    /// already agreed to check. Hold, resume, a codec change and a session refresh all come
1474    /// through here, and none of them is a restart.
1475    ///
1476    /// A call with no agent is left exactly as it was: no attributes, no round trip to a driver
1477    /// that does not exist.
1478    async fn answer_ice(&mut self, offer: &SessionDescription, answer: &mut SessionDescription) {
1479        if !self.media.runs_ice() {
1480            return;
1481        }
1482        let peer = offer.media.first().map_or(IceNegotiation::Absent, |audio| {
1483            sipx_media::ice::negotiate(offer, audio)
1484        });
1485        // §4.4.1.1.1 is a question about the *peer's* two credentials, and this side answers it
1486        // only to know whether to draw its own new ones. The agent asks it again for itself, from
1487        // the credentials it is actually keyed to; see `MediaSession::renegotiate_ice`.
1488        let local = self
1489            .peer_ice_restarted(&peer)
1490            .then(fresh_ice_parameters)
1491            .flatten();
1492        let Some(signalled) = self.media.renegotiate_ice(local, Some(&peer)).await else {
1493            return;
1494        };
1495        let Some(audio) = answer.media.first_mut() else {
1496            return;
1497        };
1498        audio
1499            .attributes
1500            .retain(|attribute| !is_ice_attribute(attribute));
1501        audio.attributes.extend(ice_attributes(&signalled));
1502    }
1503
1504    /// Whether this offer restarts ICE (RFC 8839 §4.4.1.1.1).
1505    ///
1506    /// **Both** credentials changed, and only both. One alone is not a restart, which is the case
1507    /// the rule is worded to exclude: a peer may legitimately re-send a description with one value
1508    /// re-derived and the other unchanged, and treating that as a restart would tear down a
1509    /// working session for nothing.
1510    ///
1511    /// The comparison is against what this side last *saw*, which is why it is recorded here
1512    /// rather than derived from the SDP twice — "the same value moving between the session level
1513    /// and the media level is not a restart" is only true if what is compared is the effective
1514    /// value for the stream, which is what [`sipx_media::ice::negotiate`] resolves.
1515    fn peer_ice_restarted(&mut self, peer: &IceNegotiation) -> bool {
1516        let IceNegotiation::Ice { credentials, .. } = peer else {
1517            return false;
1518        };
1519        let restarted = self.peer_ice.as_ref().is_some_and(|seen| {
1520            seen.ufrag() != credentials.ufrag() && seen.pwd() != credentials.pwd()
1521        });
1522        self.peer_ice = Some(credentials.clone());
1523        restarted
1524    }
1525
1526    /// Answer an UPDATE that arrived in this dialog (RFC 3311 §5.2).
1527    ///
1528    /// The three refusals are three different answers, and the difference is the point: 491
1529    /// means the two sides collided and both should wait a randomised interval before trying
1530    /// again; a 500 with `Retry-After` means the request was well formed and simply early. A
1531    /// peer told the wrong one either backs off when it did not need to or retries straight
1532    /// into the same wall.
1533    ///
1534    /// Whichever it is, **the dialog survives** — including the 488 for a description this side
1535    /// cannot use. Every one of these is about a change that will not happen, not about the
1536    /// session that is already running.
1537    async fn on_update(&mut self, incoming: &Incoming) -> Result<()> {
1538        if self.out_of_order(&incoming.request) {
1539            return self.refuse(incoming, 500, "Server Internal Error").await;
1540        }
1541        self.record_remote_cseq(&incoming.request);
1542
1543        let has_offer = crate::update::carries_offer(&incoming.request);
1544        if let Reception::Refuse(refusal) = self.negotiation.receive(has_offer) {
1545            return crate::update::refuse(&self.endpoint, incoming, refusal).await;
1546        }
1547
1548        let mut builder = ResponseBuilder::to_request(&incoming.request, ok_status(), "OK")?
1549            .header(
1550                HeaderName::Contact,
1551                Bytes::from(contact_for(&self.endpoint, self.target.transport)),
1552            )?
1553            .header(
1554                HeaderName::Allow,
1555                Bytes::from_static(update::ALLOW.as_bytes()),
1556            )?;
1557
1558        if has_offer {
1559            // §5.2: the UAS "MUST adjust the session parameters accordingly and generate an
1560            // answer in the 2xx response".
1561            //
1562            // The result is captured rather than propagated with `?`, because `renegotiate`
1563            // can fail on something that has nothing to do with the peer — a media port that
1564            // will not bind. Returning through the `?` would leave this UPDATE forever in
1565            // progress and the offer forever owed, and every later UPDATE on the dialog would
1566            // draw §5.2's "you are too early" for a transaction nobody is waiting on.
1567            let renegotiated = self.renegotiate(incoming.request.body()).await;
1568            let Some(answer_sdp) = renegotiated.inspect_err(|_| self.negotiation.answered())?
1569            else {
1570                // The offer is dead, so nothing is owed for it any more — and this is a final
1571                // response, so no UPDATE is in progress either.
1572                self.negotiation.answered();
1573                return self.refuse_unacceptable(incoming).await;
1574            };
1575            builder = builder
1576                .header(
1577                    HeaderName::ContentType,
1578                    Bytes::from_static(b"application/sdp"),
1579                )?
1580                .body(Bytes::from(answer_sdp.to_string_sdp()));
1581        }
1582
1583        // RFC 4028 §7.4: an UPDATE refreshes the session whether or not it was sent for that
1584        // reason, so the 2xx names the terms in force and the deadline moves. Only counting the
1585        // ones that carry `Session-Expires` would hang up on a peer that is demonstrably alive.
1586        if let Some(state) = self.session {
1587            let expires = SessionExpires {
1588                interval: state.terms.interval,
1589                refresher: Some(if state.terms.we_refresh {
1590                    session::Refresher::Uas
1591                } else {
1592                    session::Refresher::Uac
1593                }),
1594            };
1595            builder = builder
1596                .header(HeaderName::SessionExpires, Bytes::from(expires.to_string()))?
1597                .header(HeaderName::Supported, Bytes::from_static(b"timer"))?;
1598        }
1599
1600        // §5.1: UPDATE is a target refresh request, so its `Contact` replaces the dialog's
1601        // remote target — the same rule RFC 3261 §12.2.2 gives a re-INVITE, and for the same
1602        // reason: without it the BYE goes to where the peer used to be.
1603        self.dialog.refresh_target(&incoming.request.headers);
1604        self.target = in_dialog_target(
1605            &self.dialog,
1606            Target::new(incoming.source, incoming.transport),
1607        );
1608        self.peer_allows_update = update::peer_allows(&incoming.request.headers);
1609
1610        let sent = self.endpoint.respond(&incoming.key, builder.build()).await;
1611        // Cleared whether or not the response got out. A send that failed will not be retried
1612        // here, so leaving the exchange open would answer every later UPDATE on this dialog
1613        // with §5.2's "you are too early" — permanently, for a transaction nobody is waiting
1614        // on any more.
1615        self.negotiation.answered();
1616        sent?;
1617        self.rearm();
1618        Ok(())
1619    }
1620
1621    /// Renegotiate this call with an UPDATE (RFC 3311).
1622    ///
1623    /// [`Self::reinvite`] remains the right way to renegotiate a *confirmed* dialog — §5.1
1624    /// recommends it, because an UPDATE must be answered promptly and leaves the far end no
1625    /// window in which to ask a user whether the change is acceptable. This is here for the
1626    /// cases where that does not apply: a peer that asked for UPDATE, or a change that nobody
1627    /// would be asked about.
1628    ///
1629    /// Refuses locally rather than putting an illegal request on the wire when an offer of ours
1630    /// is unanswered or one of theirs is unanswered by us (§5.1, RFC 3264): the far end would
1631    /// answer 491 or 500 and the round trip would have told us only what we already knew.
1632    pub async fn update(&mut self, direction: Direction) -> Result<()> {
1633        if self.profile == MediaProfile::BrowserAudio {
1634            return Err(sipx_sdp::browser_audio::ProfileError::ProfileRemoved.into());
1635        }
1636        if self.keying == Keying::DtlsSrtp {
1637            return Err(Error::DtlsRenegotiation);
1638        }
1639        if !self.negotiation.may_offer() {
1640            return Err(Error::Rejected {
1641                status: sipx_sip::update::Refusal::Glare.status(),
1642                reason: "an offer is already outstanding on this dialog".to_owned(),
1643            });
1644        }
1645
1646        let mut capabilities = self
1647            .codecs
1648            .capabilities(self.media_address, self.media.local_addr().port());
1649        if self.current.rtcp_mode == sipx_sdp::RtcpMode::Mux {
1650            capabilities = capabilities.with_rtcp_mux();
1651        }
1652        capabilities.direction = direction;
1653        // As for a re-INVITE: the version must increase with each modified offer, so the far
1654        // end can tell a changed description from a repeated one.
1655        capabilities.session_version = u64::from(self.dialog.local_cseq.saturating_add(1));
1656        let offer = offer_from(&capabilities);
1657
1658        let (builder, routes) =
1659            crate::update::request(&self.endpoint, &mut self.dialog, &self.target, Some(&offer))?;
1660        let request = crate::update::finish(builder, &routes)?;
1661
1662        self.negotiation.sent_offer();
1663        let response = crate::update::send(&self.endpoint, request, self.target.clone()).await;
1664        // Whatever came back closed the exchange: a 2xx carries the answer, and a failure means
1665        // there will never be one. Leaving the flag set would refuse every later offer of ours.
1666        self.negotiation.received_answer();
1667        let response = response?;
1668        if !response.status.is_success() {
1669            return Err(crate::update::rejected(&response));
1670        }
1671
1672        self.dialog.refresh_target(&response.headers);
1673        self.target = in_dialog_target(&self.dialog, self.target.clone());
1674        self.peer_allows_update = update::peer_allows(&response.headers);
1675
1676        if let Ok(answer) = sipx_sdp::parse(&String::from_utf8_lossy(response.body())) {
1677            // The answer's ICE half, before the codec comparison: on a restart it carries the
1678            // peer's new credentials and candidates, and an agent that is not told about them
1679            // checks a path nobody is answering on. On an ordinary re-offer it is the same half
1680            // again, which the agent merges (RFC 8839 §4.2) rather than replaces — so a
1681            // re-answer cannot silence ICE on a call that is working.
1682            if let Ok(renegotiated) = negotiated(&answer, self.codecs) {
1683                preserve_rtcp_mode(self.current.rtcp_mode, renegotiated.rtcp_mode)?;
1684                // Do not let an answer that failed the mode guard mutate the running ICE
1685                // generation. Socket ownership and candidate state move together or neither does.
1686                self.accept_answer_ice(&answer).await;
1687                self.move_media_if_changed(renegotiated).await?;
1688            }
1689        }
1690        self.hold = direction;
1691        self.adopt_session(&response);
1692        self.rearm();
1693        Ok(())
1694    }
1695
1696    /// Whether an in-dialog request arrived out of order.
1697    ///
1698    /// RFC 3261 §12.2.2 rejects a request behind the dialog's sequence number with a 500
1699    /// rather than applying it, and §12.2.1.1 requires each new in-dialog request to
1700    /// *increment* the number — so a repeat of the current one is a duplicate that has escaped
1701    /// the transaction layer's absorption window, not a fresh request, and is refused on the
1702    /// same grounds. This is not only the re-INVITE case: a stale BYE honoured here ends a
1703    /// call that a later request has already changed.
1704    fn out_of_order(&self, request: &Request) -> bool {
1705        self.dialog.is_out_of_order(request)
1706    }
1707
1708    /// Record the sequence number of an in-dialog request this side has accepted.
1709    fn record_remote_cseq(&mut self, request: &Request) {
1710        self.dialog.record_remote_cseq(request);
1711    }
1712
1713    /// Refuse a renegotiation with 488, saying why (RFC 3311 §5.2, RFC 3261 §20.43).
1714    ///
1715    /// The `Warning` is a SHOULD, and it is the difference between a peer that can log why its
1716    /// renegotiation was refused and one that can only log that it was.
1717    async fn refuse_unacceptable(&self, incoming: &Incoming) -> Result<()> {
1718        let status = StatusCode::new(488).unwrap_or_else(ok_status);
1719        let response =
1720            ResponseBuilder::to_request(&incoming.request, status, "Not Acceptable Here")?
1721                .header(
1722                    HeaderName::Warning,
1723                    Bytes::from(crate::update::warning(&self.endpoint)),
1724                )?
1725                .build();
1726        self.endpoint.respond(&incoming.key, response).await?;
1727        Ok(())
1728    }
1729
1730    /// Answer an in-dialog OPTIONS (RFC 3261 §11.2).
1731    ///
1732    /// The point of OPTIONS is the capability list, so a 200 with an empty `Allow` is a wasted
1733    /// exchange: the peer asked what we can do and learned nothing. No `Contact` and no session
1734    /// description — §11.2 allows a description here, and sending one would be an offer nobody
1735    /// asked for inside a call that already has one.
1736    async fn on_options(&mut self, incoming: &Incoming) -> Result<()> {
1737        // §12.2.2 applies to every in-dialog request, this one included. Going through the
1738        // dialog's own guard rather than past it is the point: a path that keeps its own copy of
1739        // the rule is a path the rule can be forgotten on.
1740        if self.out_of_order(&incoming.request) {
1741            return self.refuse(incoming, 500, "Server Internal Error").await;
1742        }
1743        self.record_remote_cseq(&incoming.request);
1744
1745        let response = ResponseBuilder::to_request(&incoming.request, ok_status(), "OK")?
1746            .header(
1747                HeaderName::Allow,
1748                Bytes::from_static(update::ALLOW.as_bytes()),
1749            )?
1750            .header(HeaderName::Accept, Bytes::from_static(b"application/sdp"))?
1751            .build();
1752        self.endpoint.respond(&incoming.key, response).await?;
1753        Ok(())
1754    }
1755
1756    /// Answer a request that reached this call and that [`Self::handle`] did not claim.
1757    ///
1758    /// Three different things bring one here, and they are three different answers — collapsing
1759    /// them was a real defect, not an untidiness. The first version answered 481 to everything
1760    /// that failed [`Dialog::matches`](crate::Dialog::matches), and `matches` is false for any
1761    /// request with no `To` tag: so a bare INVITE or CANCEL reaching a one-call [`serve`] drew
1762    /// RFC 3261 §12.2.2's "the dialog you named does not exist" for a request that named no
1763    /// dialog at all.
1764    ///
1765    /// - **It matches this dialog**, but the method is one this call does not implement:
1766    ///   §8.2.1's **405**, with the `Allow` that section makes mandatory.
1767    /// - **It names a dialog that is not this one** — it carries a `To` tag, or its method
1768    ///   exists only inside a dialog: §12.2.2's **481**.
1769    /// - **It names no dialog**, so it is a new exchange arriving where exactly one call is
1770    ///   being served: **486 Busy Here** for an INVITE (§21.4.24 — "not willing or able to take
1771    ///   additional calls", which is precisely the one-call contract), and 405 for anything
1772    ///   else. A dispatcher is the answer to wanting more than one call here, and the 486 says
1773    ///   so in the only vocabulary the peer has.
1774    ///
1775    /// An ACK gets nothing, because SIP has no response to one.
1776    ///
1777    /// Failures are logged rather than returned. This exists so nothing is discarded in silence
1778    /// (`T-19`, story `C-4`), and handing the caller an error to ignore would put the silence
1779    /// back one level up.
1780    pub(crate) async fn refuse_unclaimed(&self, incoming: &Incoming) {
1781        // There is no response to an ACK, and an ACK for a 2xx is a transaction of its own
1782        // (RFC 3261 §17.1.1.3). Nothing to send; a stray one is still worth a line.
1783        if incoming.request.method == Method::Ack {
1784            tracing::debug!("an ACK reached a call that did not claim it");
1785            return;
1786        }
1787        let request = &incoming.request;
1788        let (code, reason) = if self.dialog.matches(request) {
1789            (405u16, "Method Not Allowed")
1790        } else if crate::dialog::to_tag(&request.headers).is_some()
1791            || crate::dispatch::dialog_only(&request.method)
1792        {
1793            (481, "Call/Transaction Does Not Exist")
1794        } else if request.method == Method::Invite {
1795            (486, "Busy Here")
1796        } else {
1797            (405, "Method Not Allowed")
1798        };
1799        let Some(status) = StatusCode::new(code) else {
1800            return;
1801        };
1802        let allow = code == 405;
1803        let built =
1804            ResponseBuilder::to_request(&incoming.request, status, reason).and_then(|builder| {
1805                if allow {
1806                    builder.header(
1807                        HeaderName::Allow,
1808                        Bytes::from_static(update::ALLOW.as_bytes()),
1809                    )
1810                } else {
1811                    Ok(builder)
1812                }
1813            });
1814        match built {
1815            Ok(builder) => {
1816                // discard: the refusal is lost and **this loss reaches no counter**, which is
1817                // stated rather than papered over. `DiscardCounts::unanswered` is not it: that is
1818                // bumped only by the driver's 180 s sweep over transactions still handed over, so
1819                // it covers a `respond` that was never called and not one that was called and
1820                // failed — and on `Error::NoTransaction` there is no transaction left to sweep at
1821                // all. What bounds the damage is the peer: it retransmits the request and its own
1822                // transaction times out, so nothing hangs. Closing this needs a counter for
1823                // responses the endpoint could not send, which is a change to
1824                // `sipx_transport::Handle::respond`'s contract rather than to this call site.
1825                if let Err(error) = self.endpoint.respond(&incoming.key, builder.build()).await {
1826                    tracing::warn!(%error, code, "could not refuse an unclaimed request");
1827                }
1828            }
1829            // discard: the same loss one step earlier and with the same downstream count — a
1830            // refusal that cannot be built is a refusal that is not sent.
1831            Err(error) => tracing::warn!(%error, code, "could not build the refusal"),
1832        }
1833    }
1834
1835    /// Refuse a renegotiation without ending the call.
1836    pub(crate) async fn refuse(
1837        &self,
1838        incoming: &Incoming,
1839        code: u16,
1840        reason: impl Into<Bytes>,
1841    ) -> Result<()> {
1842        Self::refuse_with(&self.endpoint, incoming, code, reason).await
1843    }
1844
1845    /// Refuse through a cloned endpoint while the owning call is driving an outgoing exchange.
1846    pub(crate) async fn refuse_with(
1847        endpoint: &Handle,
1848        incoming: &Incoming,
1849        code: u16,
1850        reason: impl Into<Bytes>,
1851    ) -> Result<()> {
1852        let status = StatusCode::new(code).unwrap_or_else(ok_status);
1853        let response = ResponseBuilder::to_request(&incoming.request, status, reason)?.build();
1854        endpoint.respond(&incoming.key, response).await?;
1855        Ok(())
1856    }
1857
1858    /// Rebuild the media session, but only if where or how the media flows actually changed.
1859    ///
1860    /// Restarting an unchanged session would drop packets for no reason on every re-INVITE, and
1861    /// some peers send one every thirty seconds as a keep-alive.
1862    async fn move_media_if_changed(&mut self, to: Negotiated) -> Result<()> {
1863        self.reap_retired_media().await;
1864        // The payload type is the codec's number on the wire: a re-offer can move Opus from
1865        // 111 to 96 and leave the codec unchanged, and a session not rebuilt for that goes on
1866        // sending on the number the far end just reassigned.
1867        //
1868        // Compared as the *wire* number, not as the raw `Option`. A peer may add or drop the
1869        // redundant `a=rtpmap:0 PCMU/8000` between two descriptions of the same static codec, and
1870        // `Some(0)` against `None` would read as a change when nothing changed — rebuilding the
1871        // session, and dropping audio, on a re-INVITE that only reworded the SDP.
1872        if to.remote != self.current.remote
1873            || to.codec != self.current.codec
1874            || to.clock_rate != self.current.clock_rate
1875            || to.wire_payload_type() != self.current.wire_payload_type()
1876            || to.receive_wire_payload_type() != self.current.receive_wire_payload_type()
1877            || to.rtcp_mode != self.current.rtcp_mode
1878        {
1879            let port = MediaPort::bind(SocketAddr::new(self.media_bind_address, 0))
1880                .await
1881                .map_err(Error::Io)?;
1882            let replacement = port.start(to.media_config())?;
1883            // Mute is a property of the call, not of the session that happens to be carrying it
1884            // (`M-18`). Without this a re-INVITE that moves the media — the far end changing
1885            // address or codec, which this side did not ask for and cannot refuse — unmutes the
1886            // call behind the application's back.
1887            replacement.set_muted(self.media.is_muted());
1888            replacement.set_rtcp_quality_hook(self.media.rtcp_quality_hook());
1889            let previous = std::mem::replace(&mut self.media, Arc::new(replacement));
1890            self.retired_media.push(previous);
1891            self.reap_retired_media().await;
1892        }
1893        self.current = to;
1894        Ok(())
1895    }
1896
1897    /// Restart ICE on this call (RFC 8445 §9, RFC 8839 §4.4.1.1.1; `ice.md` §13.5).
1898    ///
1899    /// Sends a re-INVITE whose offer carries **new** `ice-ufrag` and `ice-pwd` for this stream,
1900    /// which is the entire signal — the peer reads both having changed and begins a new ICE
1901    /// session. Everything else about the call is unchanged, including its direction, so a
1902    /// restart does not resume a call that was on hold.
1903    ///
1904    /// Media keeps flowing on the pair the finished session selected until the new one selects its
1905    /// own. That is what makes a restart usable in the situation it exists for: the path has become
1906    /// doubtful, not yet unusable, and going silent while checks converge would turn a recoverable
1907    /// call into a dropped one.
1908    ///
1909    /// A call not running ICE is left alone and reports success. There is nothing to restart, and
1910    /// making the caller distinguish "no ICE" from "restart failed" would push the check to every
1911    /// call site.
1912    ///
1913    /// # Errors
1914    ///
1915    /// Returns [`Error`] when the re-INVITE cannot be built or sent, or when the far end refuses
1916    /// it — the same failures as any other renegotiation, and like them it leaves the call running.
1917    pub async fn restart_ice(&mut self) -> Result<()> {
1918        if !self.media.runs_ice() {
1919            return Ok(());
1920        }
1921        self.reoffer(self.hold, IceOffer::Restart).await
1922    }
1923
1924    /// Send a re-INVITE renegotiating this call.
1925    ///
1926    /// `direction` puts the call on hold (`SendOnly` or `Inactive`) or takes it off
1927    /// (`SendRecv`).
1928    ///
1929    /// Note what hold is **not**: RFC 8839 §4.4.1.1.1 makes `c=0.0.0.0` imply an ICE restart, so a
1930    /// hold spelled with a null connection address would restart ICE on every mute. Hold here is a
1931    /// direction and nothing else (RFC 3264), which is what it has always been, and this is the
1932    /// story that makes that a decision rather than an accident.
1933    pub async fn reinvite(&mut self, direction: Direction) -> Result<()> {
1934        self.reoffer(direction, IceOffer::Continue).await
1935    }
1936
1937    /// The re-INVITE both public entry points send, and the one place their difference lives.
1938    ///
1939    /// `ice` is a parameter rather than a field on [`Call`] because it is a property of *this*
1940    /// offer and of nothing else. Held as state it would be a fourth `bool` on a struct that
1941    /// already has three — which is what `clippy::struct_excessive_bools` objects to, and the
1942    /// objection is right: a flag set before a call and cleared after it is a state machine
1943    /// written in the hardest way to read.
1944    async fn reoffer(&mut self, direction: Direction, ice: IceOffer) -> Result<()> {
1945        if self.profile == MediaProfile::BrowserAudio {
1946            return Err(sipx_sdp::browser_audio::ProfileError::ProfileRemoved.into());
1947        }
1948        if self.keying == Keying::DtlsSrtp {
1949            return Err(Error::DtlsRenegotiation);
1950        }
1951        let (local, remote) = self.dialog.local_and_remote();
1952        let cseq = self.dialog.next_cseq();
1953
1954        let mut capabilities = self
1955            .codecs
1956            .capabilities(self.media_address, self.media.local_addr().port());
1957        if self.current.rtcp_mode == sipx_sdp::RtcpMode::Mux {
1958            capabilities = capabilities.with_rtcp_mux();
1959        }
1960        capabilities.direction = direction;
1961        // The session version must increase with each modified offer, so the far end can tell
1962        // a changed description from a repeated one.
1963        capabilities.session_version = u64::from(cseq);
1964        let mut offer = offer_from(&capabilities);
1965        self.offer_ice(&mut offer, ice).await;
1966
1967        let (uri, routes) = self.dialog.request_target();
1968        let builder = RequestBuilder::new(Method::Invite, uri)
1969            .header(HeaderName::To, Bytes::from(remote))?
1970            .header(HeaderName::From, Bytes::from(local))?
1971            .header(
1972                HeaderName::CallId,
1973                Bytes::from(self.dialog.id.call_id.clone()),
1974            )?
1975            .cseq(cseq, &Method::Invite)?
1976            .header(
1977                HeaderName::Contact,
1978                Bytes::from(contact_for(&self.endpoint, self.target.transport)),
1979            )?
1980            .header(
1981                HeaderName::Allow,
1982                Bytes::from_static(update::ALLOW.as_bytes()),
1983            )?
1984            .header(
1985                HeaderName::ContentType,
1986                Bytes::from_static(b"application/sdp"),
1987            )?
1988            .max_forwards(70)
1989            .body(Bytes::from(offer.to_string_sdp()));
1990
1991        // RFC 4028 §7.4: a refresh names the current interval and the current refresher, so
1992        // that proxies on the path can see the value in force and object to it. Any re-INVITE
1993        // refreshes the session (§7.2), so these go on every one rather than only on the ones
1994        // sent because the timer asked.
1995        let mut builder = builder.header(HeaderName::Supported, Bytes::from_static(b"timer"))?;
1996        if let Some(state) = self.session {
1997            let expires = SessionExpires {
1998                interval: state.terms.interval,
1999                refresher: Some(if state.terms.we_refresh {
2000                    session::Refresher::Uac
2001                } else {
2002                    session::Refresher::Uas
2003                }),
2004            };
2005            builder = builder
2006                .header(HeaderName::SessionExpires, Bytes::from(expires.to_string()))?
2007                .header(
2008                    HeaderName::MinSe,
2009                    Bytes::from(session::ABSOLUTE_MIN_INTERVAL.as_secs().to_string()),
2010                )?;
2011        }
2012
2013        let request = add_routes(builder, &routes)?.build();
2014        // RFC 3311 §5.2 rule 2 names an offer sent "in an UPDATE, PRACK or INVITE", and this is
2015        // the INVITE case: the offer is outstanding for as long as the response takes. Marked
2016        // and cleared around the whole exchange, so a failure cannot leave the flag set and
2017        // refuse every later offer of ours.
2018        self.negotiation.sent_offer();
2019        let exchange = async {
2020            let mut responses = self.endpoint.send(request, self.target.clone()).await?;
2021            responses.final_response().await.ok_or(Error::NoResponse)
2022        }
2023        .await;
2024        self.negotiation.received_answer();
2025        let response = exchange?;
2026
2027        if !response.status.is_success() {
2028            // The far end refused the change. The call it refused to change is still running,
2029            // so this is an error about the renegotiation, not about the call.
2030            const INTERVAL_TOO_SMALL: u16 = 422;
2031            if response.status.code() == INTERVAL_TOO_SMALL
2032                && let Some(required) = required_interval(&response)
2033                && let Some(state) = self.session.as_mut()
2034            {
2035                // §10: only a 2xx extends the expiration, so adopting the longer interval does
2036                // *not* buy time — the refresh still has to succeed before the deadline that
2037                // was already running. The next attempt is the one that must land.
2038                state.terms.interval = required.max(session::ABSOLUTE_MIN_INTERVAL);
2039            }
2040            return Err(Error::Rejected {
2041                status: response.status.code(),
2042                reason: String::from_utf8_lossy(&response.reason).into_owned(),
2043            });
2044        }
2045
2046        // RFC 3261 §12.2.1.2: the 2xx to a target refresh request refreshes the target here
2047        // too, and it must be applied before the ACK — which is itself an in-dialog request
2048        // and belongs at the peer's new location.
2049        self.dialog.refresh_target(&response.headers);
2050        self.target = in_dialog_target(&self.dialog, self.target.clone());
2051
2052        send_ack(&self.endpoint, &self.dialog, self.target.clone()).await?;
2053
2054        if let Ok(answer) = sipx_sdp::parse(&String::from_utf8_lossy(response.body())) {
2055            // The answer's ICE half, before the codec comparison: on a restart it carries the
2056            // peer's new credentials and candidates, and an agent that is not told about them
2057            // checks a path nobody is answering on. On an ordinary re-offer it is the same half
2058            // again, which the agent merges (RFC 8839 §4.2) rather than replaces — so a
2059            // re-answer cannot silence ICE on a call that is working.
2060            if let Ok(settled) = settle_answer(&capabilities, &answer, self.codecs) {
2061                preserve_rtcp_mode(self.current.rtcp_mode, settled.negotiated.rtcp_mode)?;
2062                self.accept_answer_ice(&answer).await;
2063                self.move_media_if_changed(settled.negotiated).await?;
2064            }
2065        }
2066        self.hold = direction;
2067        // §7.2: the session expiration is measured from the 2xx, and a re-INVITE sent for any
2068        // other reason refreshes it just the same.
2069        self.adopt_session(&response);
2070        self.rearm();
2071        Ok(())
2072    }
2073
2074    /// Take the session terms from the 2xx to a refresh we sent (RFC 4028 §7.2).
2075    ///
2076    /// Shared by the re-INVITE and the UPDATE paths: §7.2 measures the expiration from the 2xx
2077    /// and says nothing about which request drew it, so reading it in two places would be two
2078    /// chances to read it differently.
2079    fn adopt_session(&mut self, response: &Response) {
2080        if let Some(agreed) = session::adopt(
2081            response
2082                .headers
2083                .typed::<SessionExpires>()
2084                .and_then(std::result::Result::ok),
2085            self.session.map(|state| state.terms.interval),
2086        ) && let Some(state) = self.session.as_mut()
2087        {
2088            state.terms = agreed;
2089        }
2090    }
2091
2092    /// Refresh the session, by whichever method the peer allows (RFC 4028 §7.4).
2093    ///
2094    /// > "If a UAC knows that its peer supports the UPDATE method, it is RECOMMENDED that
2095    /// > UPDATE be used instead of a re-INVITE."
2096    ///
2097    /// It is only *known* from the peer's `Allow` (RFC 3311 §4), so that is what decides.
2098    /// Guessing the other way costs a working call: a refresh the far end answers 405 is a
2099    /// refresh that never happens, and the deadline behind it hangs up on a peer that is alive.
2100    ///
2101    /// The UPDATE carries **no body**. A refresh changes nothing — the description in force
2102    /// stays in force — and re-offering it would put a liveness check under §5.2's offer/answer
2103    /// rules, where it could be refused 491 or 500 for a reason that has nothing to do with
2104    /// whether the far end is still there.
2105    async fn refresh_session(&mut self) -> Result<()> {
2106        if !self.peer_allows_update {
2107            return self.reinvite(self.hold).await;
2108        }
2109
2110        let (mut builder, routes) =
2111            crate::update::request(&self.endpoint, &mut self.dialog, &self.target, None)?;
2112        // §7.4: a refresh names the interval and the refresher in force, so proxies on the path
2113        // can see the value and object to it. `Min-SE` is this side's own floor, and it is a
2114        // defence rather than a courtesy (§11.2).
2115        builder = builder.header(HeaderName::Supported, Bytes::from_static(b"timer"))?;
2116        if let Some(state) = self.session {
2117            let expires = SessionExpires {
2118                interval: state.terms.interval,
2119                refresher: Some(if state.terms.we_refresh {
2120                    session::Refresher::Uac
2121                } else {
2122                    session::Refresher::Uas
2123                }),
2124            };
2125            builder = builder
2126                .header(HeaderName::SessionExpires, Bytes::from(expires.to_string()))?
2127                .header(
2128                    HeaderName::MinSe,
2129                    Bytes::from(session::ABSOLUTE_MIN_INTERVAL.as_secs().to_string()),
2130                )?;
2131        }
2132
2133        let request = crate::update::finish(builder, &routes)?;
2134        let response = crate::update::send(&self.endpoint, request, self.target.clone()).await?;
2135
2136        if !response.status.is_success() {
2137            const INTERVAL_TOO_SMALL: u16 = 422;
2138            if response.status.code() == INTERVAL_TOO_SMALL
2139                && let Some(required) = required_interval(&response)
2140                && let Some(state) = self.session.as_mut()
2141            {
2142                // As on the re-INVITE path: only a 2xx extends the expiration, so adopting the
2143                // longer interval does not buy time. The next attempt is the one that has to
2144                // land, and it has to land before the deadline that is already running.
2145                state.terms.interval = required.max(session::ABSOLUTE_MIN_INTERVAL);
2146            }
2147            return Err(crate::update::rejected(&response));
2148        }
2149
2150        self.dialog.refresh_target(&response.headers);
2151        self.target = in_dialog_target(&self.dialog, self.target.clone());
2152        self.adopt_session(&response);
2153        self.rearm();
2154        Ok(())
2155    }
2156
2157    /// Ask the far end to transfer this call to `target` (RFC 3515).
2158    ///
2159    /// Returns once the transferee has accepted the *request*, which is not the same as the
2160    /// transfer having worked: a `202 Accepted` means "I will try". What became of it arrives
2161    /// afterwards, as NOTIFY, and shows up in [`Self::transfer`]. Reporting success here would
2162    /// tell a user their call was handed over when it may have been refused or rung out.
2163    pub async fn refer(&mut self, target: &Uri) -> Result<()> {
2164        let refer_to = String::from_utf8_lossy(&target.to_bytes()).into_owned();
2165        self.refer_to_raw(&refer_to).await
2166    }
2167
2168    /// Ask the far end to replace `other` with a call to this one's peer (RFC 3891 + 3515).
2169    ///
2170    /// The attended half of a transfer. Where a blind transfer says "call this number", this
2171    /// says "call this number, and when you get through, take the place of the call I already
2172    /// have with them" — which is what makes the handover seamless rather than a second ring.
2173    pub async fn refer_attended(&mut self, other: &Call) -> Result<()> {
2174        let replaces = Replaces {
2175            call_id: other.dialog.id.call_id.clone(),
2176            // From the point of view of the party that will receive the eventual INVITE, our
2177            // *remote* tag on `other` is that party's own local tag. Writing our own tag here
2178            // produces a header that names nothing and a transfer that always fails.
2179            to_tag: other.dialog.id.remote_tag.clone(),
2180            from_tag: other.dialog.id.local_tag.clone(),
2181            early_only: false,
2182        };
2183        let target = String::from_utf8_lossy(&other.dialog.remote_target.to_bytes()).into_owned();
2184        // `?` separates a URI from the headers it asks to be put in the request built from it
2185        // (RFC 3261 §19.1.1), and `Replaces` is one of those headers.
2186        let refer_to = format!(
2187            "{target}?Replaces={}",
2188            escape_uri_header(&replaces.to_header())
2189        );
2190        self.refer_to_raw(&refer_to).await
2191    }
2192
2193    /// Send a REFER whose `Refer-To` is this text.
2194    async fn refer_to_raw(&mut self, refer_to: &str) -> Result<()> {
2195        let (local, remote) = self.dialog.local_and_remote();
2196        let cseq = self.dialog.next_cseq();
2197
2198        let (uri, routes) = self.dialog.request_target();
2199        let builder = RequestBuilder::new(Method::Refer, uri)
2200            .header(HeaderName::To, Bytes::from(remote))?
2201            .header(HeaderName::From, Bytes::from(local.clone()))?
2202            .header(
2203                HeaderName::CallId,
2204                Bytes::from(self.dialog.id.call_id.clone()),
2205            )?
2206            .cseq(cseq, &Method::Refer)?
2207            .header(
2208                HeaderName::Contact,
2209                Bytes::from(contact_for(&self.endpoint, self.target.transport)),
2210            )?
2211            .header(HeaderName::ReferTo, Bytes::from(format!("<{refer_to}>")))?
2212            // RFC 3892. The transferee is being asked to call a stranger on our say-so; saying
2213            // who we are is the only basis it has for deciding whether to.
2214            .header(
2215                HeaderName::ReferredBy,
2216                Bytes::from(strip_header_params(&local)),
2217            )?
2218            .max_forwards(70);
2219
2220        let request = add_routes(builder, &routes)?.build();
2221        let mut responses = self.endpoint.send(request, self.target.clone()).await?;
2222        let response = responses.final_response().await.ok_or(Error::NoResponse)?;
2223
2224        if !response.status.is_success() {
2225            return Err(Error::Rejected {
2226                status: response.status.code(),
2227                reason: String::from_utf8_lossy(&response.reason).into_owned(),
2228            });
2229        }
2230
2231        // Nothing is known yet beyond "it was taken on". The first NOTIFY replaces this.
2232        self.transfer = Some(Transfer {
2233            state: TransferState::Trying,
2234            finished: false,
2235        });
2236        Ok(())
2237    }
2238
2239    /// The transfer the far end has asked for, if it has asked and we have not answered.
2240    #[must_use]
2241    pub fn referral(&self) -> Option<&Referral> {
2242        self.referral.as_ref()
2243    }
2244
2245    /// A transfer we asked for, and what has become of it. `None` if we asked for none.
2246    #[must_use]
2247    pub fn transfer(&self) -> Option<&Transfer> {
2248        self.transfer.as_ref()
2249    }
2250
2251    /// Accept the transfer, place the call, and report the outcome (RFC 3515 §2.4.5).
2252    ///
2253    /// `target` is where to *send* the new INVITE; the `Refer-To` URI is what goes in it. The
2254    /// two are separate for the same reason they are separate in [`dial`]: resolving a URI to
2255    /// an address is RFC 3263's job and lives in the transport, not here.
2256    ///
2257    /// The original call is left running. Whether to hang up on the transferor is a policy
2258    /// decision — a blind transfer usually ends it, an attended one does not — and it belongs
2259    /// to whoever is making that decision, not to this function.
2260    pub async fn accept_referral(&mut self, target: Target, options: &DialOptions) -> Result<Call> {
2261        let Some(referral) = self.referral.take() else {
2262            return Err(Error::NoReferral);
2263        };
2264
2265        let accepted = ResponseBuilder::to_request(
2266            &referral.request,
2267            StatusCode::new(202).unwrap_or_else(|| unreachable!("202 is a valid status code")),
2268            "Accepted",
2269        )?
2270        .build();
2271        self.endpoint.respond(&referral.key, accepted).await?;
2272
2273        // "I am trying", straight away. RFC 3515 §2.4.4 asks for an immediate NOTIFY so the
2274        // transferor knows the subscription exists before anything can go wrong with the call.
2275        self.notify_transfer(&referral, 100, "Trying", false)
2276            .await?;
2277
2278        let placed = dial(&self.endpoint, target, &referral.target, options).await;
2279
2280        let (status, reason) = match &placed {
2281            Ok(_) => (200, "OK".to_owned()),
2282            Err(Error::Rejected { status, reason }) => (*status, reason.clone()),
2283            // Anything else never reached the target at all. 503 is what a proxy would say for
2284            // the same situation, and it tells the transferor something true.
2285            Err(_) => (503, "Service Unavailable".to_owned()),
2286        };
2287        // Terminating, whether it worked or not. A transferee that reports the outcome and then
2288        // says nothing leaves a subscription open on both sides for a transfer that is over.
2289        self.notify_transfer(&referral, status, &reason, true)
2290            .await?;
2291
2292        placed
2293    }
2294
2295    /// Refuse the transfer (RFC 3515 §2.4.2).
2296    ///
2297    /// No subscription is created by a REFER that was not accepted, so nothing further is owed
2298    /// and no NOTIFY is sent. The transferor learns the outcome from the status, which is why
2299    /// it should be one they can act on — 603 for "no", 488 for "not that target".
2300    pub async fn refuse_referral(&mut self, status: u16, reason: &'static str) -> Result<()> {
2301        let Some(referral) = self.referral.take() else {
2302            return Err(Error::NoReferral);
2303        };
2304        let code = StatusCode::new(status).ok_or(Error::NoReferral)?;
2305        let response = ResponseBuilder::to_request(&referral.request, code, reason)?.build();
2306        self.endpoint.respond(&referral.key, response).await?;
2307        Ok(())
2308    }
2309
2310    /// Note a REFER, or refuse one that cannot be honoured whatever the application thinks.
2311    async fn on_refer(&mut self, incoming: &Incoming) -> Result<()> {
2312        let sequence = incoming
2313            .request
2314            .headers
2315            .typed::<sipx_sip::headers::CSeq>()
2316            .and_then(std::result::Result::ok)
2317            .map_or(0, |cseq| cseq.sequence);
2318
2319        let refer_to = incoming.request.headers.value(&HeaderName::ReferTo);
2320        let target = refer_to.as_deref().and_then(|value| {
2321            let text = String::from_utf8_lossy(value);
2322            Uri::parse(Bytes::from(unbracket(text.trim()))).ok()
2323        });
2324
2325        let Some(target) = target else {
2326            // A missing or unparseable `Refer-To` is not a decision for the application: there
2327            // is nowhere to transfer to, and 400 says exactly that.
2328            self.refuse_now(incoming, 400, "Bad Request").await?;
2329            return Ok(());
2330        };
2331
2332        // An attended transfer's `Refer-To` carries a `Replaces` (RFC 3891 + 3515), built by
2333        // `refer_attended` above as a URI header parameter. A substring check on the raw value
2334        // rather than a full URI-header parse: `Uri` does not expose its header component, and
2335        // this only has to distinguish "asks to replace a dialog" from "does not", not validate
2336        // one.
2337        let attended = refer_to.as_deref().is_some_and(contains_replaces);
2338
2339        self.referral = Some(Referral {
2340            target: target.clone(),
2341            referred_by: incoming
2342                .request
2343                .headers
2344                .value(&HeaderName::ReferredBy)
2345                .map(|value| String::from_utf8_lossy(&value).into_owned()),
2346            event_id: sequence,
2347            key: incoming.key.clone(),
2348            request: incoming.request.clone(),
2349        });
2350        self.events
2351            .emit(CallEvent::TransferRequested { target, attended });
2352        Ok(())
2353    }
2354
2355    /// Take in what the transferee says about a transfer we asked for.
2356    async fn on_notify(&mut self, incoming: &Incoming) -> Result<()> {
2357        // Answered first and unconditionally. A NOTIFY we do not understand is still a request
2358        // that must not be left to time out, and the subscription is ours whether or not this
2359        // particular notification made sense.
2360        let ok = ResponseBuilder::to_request(&incoming.request, ok_status(), "OK")?.build();
2361        self.endpoint.respond(&incoming.key, ok).await?;
2362
2363        let is_refer = incoming
2364            .request
2365            .headers
2366            .value(&HeaderName::Event)
2367            .is_some_and(|value| {
2368                String::from_utf8_lossy(&value)
2369                    .split(';')
2370                    .next()
2371                    .unwrap_or("")
2372                    .trim()
2373                    .eq_ignore_ascii_case("refer")
2374            });
2375        if !is_refer {
2376            return Ok(());
2377        }
2378
2379        let finished = incoming
2380            .request
2381            .headers
2382            .value(&HeaderName::SubscriptionState)
2383            .is_some_and(|value| is_terminated(&value));
2384
2385        let state = parse_sipfrag(incoming.request.body())
2386            .map(|(status, reason)| TransferState::from_status(status, &reason));
2387
2388        let transfer = self.transfer.get_or_insert(Transfer {
2389            state: TransferState::Trying,
2390            finished: false,
2391        });
2392        if let Some(state) = state {
2393            transfer.state = state.clone();
2394            self.events.emit(CallEvent::TransferProgress(state));
2395        }
2396        // Once terminated, always terminated: a stray notification afterwards must not reopen a
2397        // subscription the transferee has already closed.
2398        transfer.finished |= finished;
2399        Ok(())
2400    }
2401
2402    /// Report progress on a transfer we accepted.
2403    async fn notify_transfer(
2404        &mut self,
2405        referral: &Referral,
2406        status: u16,
2407        reason: &str,
2408        terminate: bool,
2409    ) -> Result<()> {
2410        let (local, remote) = self.dialog.local_and_remote();
2411        let cseq = self.dialog.next_cseq();
2412        let subscription = if terminate {
2413            // `noresource` is the reason RFC 6665 §4.1.3 gives for "the thing you subscribed to
2414            // no longer exists", which is what a finished transfer is.
2415            "terminated;reason=noresource".to_owned()
2416        } else {
2417            "active;expires=60".to_owned()
2418        };
2419
2420        let (uri, routes) = self.dialog.request_target();
2421        let builder = RequestBuilder::new(Method::Notify, uri)
2422            .header(HeaderName::To, Bytes::from(remote))?
2423            .header(HeaderName::From, Bytes::from(local))?
2424            .header(
2425                HeaderName::CallId,
2426                Bytes::from(self.dialog.id.call_id.clone()),
2427            )?
2428            .cseq(cseq, &Method::Notify)?
2429            .header(
2430                HeaderName::Contact,
2431                Bytes::from(contact_for(&self.endpoint, self.target.transport)),
2432            )?
2433            // The `id` ties this to the REFER that created the subscription, so a transferor
2434            // with two transfers in flight can tell which one this is about (RFC 3515 §2.4.4).
2435            .header(
2436                HeaderName::Event,
2437                Bytes::from(format!("refer;id={}", referral.event_id)),
2438            )?
2439            .header(HeaderName::SubscriptionState, Bytes::from(subscription))?
2440            .header(
2441                HeaderName::ContentType,
2442                Bytes::from_static(b"message/sipfrag;version=2.0"),
2443            )?
2444            .max_forwards(70)
2445            .body(Bytes::from(sipfrag(status, reason)));
2446
2447        let request = add_routes(builder, &routes)?.build();
2448        let mut responses = self.endpoint.send(request, self.target.clone()).await?;
2449        // A NOTIFY the transferor never answers does not undo the transfer; the call it asked
2450        // for has already happened either way.
2451        //
2452        // discard: nothing is thrown away that anyone could act on. The NOTIFY itself was handed
2453        // over — one the endpoint could not put on the wire is counted at the transmit by
2454        // `sipx_transport::UnsentCounts` — and what is dropped here is only *waiting* for its
2455        // answer. The bound bounds a failure (`X-29`): the transfer's outcome does not depend on
2456        // the reply arriving.
2457        let _ = tokio::time::timeout(Duration::from_secs(2), responses.final_response()).await;
2458        Ok(())
2459    }
2460
2461    /// Refuse a request outright, without involving the application.
2462    async fn refuse_now(
2463        &mut self,
2464        incoming: &Incoming,
2465        status: u16,
2466        reason: &'static str,
2467    ) -> Result<()> {
2468        let Some(code) = StatusCode::new(status) else {
2469            return Ok(());
2470        };
2471        let response = ResponseBuilder::to_request(&incoming.request, code, reason)?.build();
2472        self.endpoint.respond(&incoming.key, response).await?;
2473        Ok(())
2474    }
2475
2476    /// End the call, for `cause`, which is emitted the moment `ended` flips — before the BYE is
2477    /// even built, so a call is reported over regardless of whether transmitting the BYE then
2478    /// succeeds. Shared by [`Self::hang_up`] (this side decided to end it) and the session-timer
2479    /// path in [`Self::on_session_deadline`] (the far end stopped answering), so both go through
2480    /// exactly the same teardown and the event this emits cannot drift from which one happened.
2481    ///
2482    /// Anything still queued is sent first, then the media stops, then the BYE goes out.
2483    /// Stopping first would discard the tail of whatever was playing — the last word of a
2484    /// clip, the last digit of a PIN — because sending is paced and the queue outlives the
2485    /// call by however much is left in it.
2486    async fn end(&mut self, cause: EndCause) -> Result<()> {
2487        let reason = match cause {
2488            EndCause::Timeout => request_timeout_reason(),
2489            _ => normal_clearing_reason(),
2490        };
2491        self.end_with_reason(cause, &reason).await
2492    }
2493
2494    async fn begin_end(
2495        &mut self,
2496        cause: EndCause,
2497        reason: &ReasonValue,
2498    ) -> Result<Option<(Request, u32)>> {
2499        if self.ended {
2500            self.finish_media_ownership().await;
2501            return Ok(None);
2502        }
2503        self.media.flush(Duration::from_secs(5)).await;
2504        self.media.stop();
2505        self.ended = true;
2506        self.session = None;
2507        self.stop_ack_retransmission().await;
2508        self.events.end(cause);
2509
2510        let cseq = self.dialog.next_cseq();
2511        match bye_request(&self.dialog, cseq, reason) {
2512            Ok(bye) => Ok(Some((bye, cseq))),
2513            Err(error) => {
2514                self.finish_media_ownership().await;
2515                Err(error)
2516            }
2517        }
2518    }
2519
2520    async fn finish_media_ownership(&mut self) {
2521        self.stop_ack_retransmission().await;
2522        self.media.shutdown().await;
2523        self.reap_retired_media().await;
2524    }
2525
2526    async fn end_with_reason(&mut self, cause: EndCause, reason: &ReasonValue) -> Result<()> {
2527        let Some((bye, _)) = self.begin_end(cause, reason).await? else {
2528            return Ok(());
2529        };
2530        let sent: Result<()> = async {
2531            let mut responses = self.endpoint.send(bye, self.target.clone()).await?;
2532            // A BYE that is never answered still ends the call locally: the alternative is a call
2533            // that cannot be hung up because the far end has already gone.
2534            //
2535            // discard: the BYE was handed over, and one the endpoint could not put on the wire is
2536            // counted at the transmit as `sipx_transport::UnsentCounts::bye` — the number an operator
2537            // asking "why did that call linger" needs. What is dropped here is only waiting for the
2538            // 200, and this side has already ended the call either way. The bound bounds a failure
2539            // (`X-29`).
2540            let _ = tokio::time::timeout(Duration::from_secs(2), responses.final_response()).await;
2541            Ok(())
2542        }
2543        .await;
2544        self.finish_media_ownership().await;
2545        sent
2546    }
2547
2548    /// End the call because this side decided to.
2549    pub async fn hang_up(&mut self) -> Result<()> {
2550        self.end(EndCause::LocalHangup).await
2551    }
2552
2553    /// End the call and return the valid final response to the originated BYE.
2554    ///
2555    /// Unlike [`Self::hang_up`], this is an evidence-producing teardown: `within` bounds failure,
2556    /// and success requires the final response to name this exact dialog and the BYE's exact
2557    /// `CSeq`.
2558    /// A valid non-2xx is returned as [`Error::Rejected`], while a mismatched response is
2559    /// [`Error::InvalidDialogResponse`].
2560    pub async fn hang_up_observed(&mut self, within: Duration) -> Result<u16> {
2561        let reason = normal_clearing_reason();
2562        let Some((bye, cseq)) = self.begin_end(EndCause::LocalHangup, &reason).await? else {
2563            return Err(Error::InvalidDialogResponse);
2564        };
2565        let observed = async {
2566            let mut responses = self.endpoint.send(bye, self.target.clone()).await?;
2567            // Fixed duration bounds a failed teardown; the final response is the happens-before.
2568            let response = tokio::time::timeout(within, responses.final_response())
2569                .await
2570                .map_err(|_| Error::SignallingTeardownTimeout(within))?
2571                .ok_or(Error::SignallingTeardownTimeout(within))?;
2572            if !crate::signalling::response_matches_dialog(&response, &self.dialog, cseq) {
2573                return Err(Error::InvalidDialogResponse);
2574            }
2575            let status = response.status.code();
2576            if !response.status.is_success() {
2577                return Err(Error::Rejected {
2578                    status,
2579                    reason: String::from_utf8_lossy(&response.reason).into_owned(),
2580                });
2581            }
2582            Ok(status)
2583        }
2584        .await;
2585        self.finish_media_ownership().await;
2586        observed
2587    }
2588
2589    /// End the call with an explicit protocol cause.
2590    ///
2591    /// This is the coupled-leg shape from RFC 3326 §3.1: a controller which knows the winning
2592    /// response can tell the other dialog why it is being ended instead of reducing every
2593    /// teardown to a local hangup.
2594    pub async fn hang_up_with_reason(&mut self, reason: ReasonValue) -> Result<()> {
2595        self.end_with_reason(EndCause::LocalHangup, &reason).await
2596    }
2597}
2598
2599/// Drive a call until it ends, honouring its session timer.
2600///
2601/// The loop a call needs is not just "read the next message": a session timer is a deadline,
2602/// and a call that only ever wakes on incoming traffic can never notice that no traffic has
2603/// arrived. This is that loop, written once so that the RFC 4028 half of it is not something
2604/// every caller has to remember.
2605///
2606/// Returns when the far end hangs up, or [`Error::SessionExpired`] when it stops answering.
2607///
2608/// # One call, or one of many
2609///
2610/// This is **the one-call convenience over [`Dispatcher`](crate::Dispatcher)** (story `C-4`), and
2611/// the receiver it takes is what makes it both things at once. Handed the endpoint's own
2612/// `Receiver<Incoming>` it is the single-call program it has always been; handed an inbox a
2613/// dispatcher routed, it drives one call of any number on the same endpoint. There is no second
2614/// loop for the many-call case, which is the point — a hand-rolled demultiplexer beside this one
2615/// is a fresh chance to drop an ACK.
2616///
2617/// The one-call form claims the whole endpoint, so it is right only when this is the only call
2618/// on it. Anything else arriving there is not this call's, and is answered as such below.
2619///
2620/// # Nothing is discarded
2621///
2622/// A request [`Call::handle`] does not claim is **answered**, not dropped: 405 with `Allow` when
2623/// it belongs to this dialog but names a method this call does not implement (RFC 3261 §8.2.1),
2624/// 481 when it names a dialog that is not this one (§12.2.2), 486 for a second INVITE arriving
2625/// where one call is being served (§21.4.24), and nothing at all for an ACK, which SIP has no
2626/// response to. This used to be a silent drop, and it is the call-layer twin of what `T-19`
2627/// removed at the transport layer.
2628pub async fn serve(
2629    call: &mut Call,
2630    incoming: &mut tokio::sync::mpsc::Receiver<Incoming>,
2631) -> Result<()> {
2632    while !call.is_ended() {
2633        let deadline = call.session_deadline();
2634        tokio::select! {
2635            message = incoming.recv() => match message {
2636                Some(message) => {
2637                    if !call.handle(&message).await? {
2638                        call.refuse_unclaimed(&message).await;
2639                    }
2640                }
2641                // The endpoint has shut down. The call cannot be worked any further, and
2642                // pretending otherwise would spin on a closed channel.
2643                None => return Ok(()),
2644            },
2645            () = sleep_until(deadline) => call.on_session_deadline().await?,
2646            // DTMF arrives over RTP, not signalling, so nothing above ever sees it — this is
2647            // the one place a digit becomes a `CallEvent`. Read fresh from `call.media()` on
2648            // every pass rather than once outside the loop, so a re-INVITE that moves the
2649            // media session (`move_media_if_changed`) is followed automatically: the next
2650            // iteration's future is built against whichever session is current.
2651            digit = call.media().recv_digit() => {
2652                if let Some((digit, duration)) = digit {
2653                    call.events.emit(CallEvent::Dtmf { digit, duration });
2654                }
2655            }
2656        }
2657    }
2658    Ok(())
2659}
2660
2661/// Wait for a deadline, or forever if there is none.
2662///
2663/// A free function rather than a method so that it borrows nothing: a future that borrowed the
2664/// call would collide with the `&mut` the other arm of the `select!` needs.
2665pub(crate) async fn sleep_until(deadline: Option<Instant>) {
2666    match deadline {
2667        Some(at) => tokio::time::sleep_until(at).await,
2668        None => std::future::pending().await,
2669    }
2670}
2671
2672/// Percent-escape a value going into a URI header field.
2673///
2674/// A `Replaces` value contains `;` and `=`, both of which end a URI header in the grammar of
2675/// RFC 3261 §19.1.1. Left unescaped, the `Refer-To` would be truncated at the first semicolon,
2676/// the transferee would place an ordinary call, and the transfer would appear to work while the
2677/// original call was never replaced.
2678fn escape_uri_header(value: &str) -> String {
2679    let mut out = String::with_capacity(value.len());
2680    for byte in value.bytes() {
2681        match byte {
2682            b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' | b'@' => {
2683                out.push(byte as char);
2684            }
2685            _ => {
2686                use std::fmt::Write as _;
2687                // discard: nothing can be lost. `write!` into a `String` returns `fmt::Error`
2688                // only if the formatter itself fails, and `String`'s never does — there is no
2689                // I/O and no allocation failure path to report.
2690                let _ = write!(out, "%{byte:02X}");
2691            }
2692        }
2693    }
2694    out
2695}
2696
2697/// Whether a `Refer-To` value carries a `Replaces` header parameter — RFC 3891's marker of an
2698/// attended transfer, as [`Call::refer_attended`] builds it (`<target>?Replaces=...`).
2699///
2700/// A substring check rather than a full URI-header parse: `Uri` does not expose its header
2701/// component, and telling "asks to replace a dialog" from "does not" is all this needs to do.
2702fn contains_replaces(value: &[u8]) -> bool {
2703    String::from_utf8_lossy(value)
2704        .to_ascii_lowercase()
2705        .contains("replaces=")
2706}
2707
2708/// Strip the angle brackets a `Refer-To` almost always carries.
2709///
2710/// `Refer-To: <sip:x@y>` and `Refer-To: sip:x@y` are both legal; only the first can carry URI
2711/// parameters unambiguously, so it is the one everything sends. Any display name before the
2712/// bracket goes with them.
2713fn unbracket(value: &str) -> String {
2714    match (value.find('<'), value.rfind('>')) {
2715        (Some(open), Some(close)) if close > open => value
2716            .get(open + 1..close)
2717            .unwrap_or(value)
2718            .trim()
2719            .to_owned(),
2720        _ => value.to_owned(),
2721    }
2722}
2723
2724/// Add the dialog's route set, in order, as `Route` headers.
2725///
2726/// Without these, a request through a Record-Routing proxy — which is to say almost any real
2727/// deployment — is addressed straight at the peer's `Contact`, which the proxy will not relay
2728/// and the peer may not be reachable at. The call establishes and cannot be ended.
2729pub(crate) fn add_routes(
2730    mut builder: RequestBuilder,
2731    routes: &[String],
2732) -> std::result::Result<RequestBuilder, sipx_sip::error::BuildError> {
2733    for route in routes {
2734        builder = builder.header(HeaderName::Route, Bytes::from(route.clone()))?;
2735    }
2736    Ok(builder)
2737}
2738
2739/// Build one application-owned request entirely from live dialog state.
2740fn application_request(
2741    dialog: &Dialog,
2742    method: &Method,
2743    cseq: u32,
2744    headers: &[sipx_sip::Header],
2745    body: Bytes,
2746) -> Result<Request> {
2747    let (local, remote) = dialog.local_and_remote();
2748    let (uri, routes) = dialog.request_target();
2749    let mut builder = RequestBuilder::new(method.clone(), uri)
2750        .header(HeaderName::To, Bytes::from(remote))?
2751        .header(HeaderName::From, Bytes::from(local))?
2752        .header(HeaderName::CallId, Bytes::from(dialog.id.call_id.clone()))?
2753        .cseq(cseq, method)?
2754        .max_forwards(70);
2755    for header in headers {
2756        builder = builder.header(
2757            header.name().clone(),
2758            Bytes::copy_from_slice(header.raw_value()),
2759        )?;
2760    }
2761    Ok(add_routes(builder, &routes)?.body(body).build())
2762}
2763
2764fn bye_request(dialog: &Dialog, cseq: u32, reason: &ReasonValue) -> Result<Request> {
2765    let (local, remote) = dialog.local_and_remote();
2766    let (uri, routes) = dialog.request_target();
2767    let builder = RequestBuilder::new(Method::Bye, uri)
2768        .header(HeaderName::To, Bytes::from(remote))?
2769        .header(HeaderName::From, Bytes::from(local))?
2770        .header(HeaderName::CallId, Bytes::from(dialog.id.call_id.clone()))?
2771        .header(HeaderName::Reason, Reason::from(reason.clone()).to_bytes())?
2772        .cseq(cseq, &Method::Bye)?
2773        .max_forwards(70);
2774    Ok(add_routes(builder, &routes)?.build())
2775}
2776
2777/// How a call is placed.
2778#[derive(Debug, Clone)]
2779pub struct DialOptions {
2780    /// Our own address of record.
2781    pub from: String,
2782    /// Where this side receives media.
2783    pub media_address: IpAddr,
2784    /// The local interface on which the media socket is opened.
2785    ///
2786    /// This defaults to [`Self::media_address`] when constructed with [`Self::new`]. Set it
2787    /// independently when the SDP address is a public mapping which is not locally bindable. ICE
2788    /// nomination still owns the eventual media path when ICE is enabled; ordinary RTP cannot
2789    /// override that result.
2790    ///
2791    /// # Beta API migration
2792    ///
2793    /// Adding this public field deliberately breaks external `DialOptions` struct literals and
2794    /// exhaustive patterns. Add `media_bind_address` (normally equal to `media_address`) or move
2795    /// to [`Self::new`] and the builder methods. Constructor-based callers remain compatible.
2796    pub media_bind_address: IpAddr,
2797    /// Direction advertised by the initial SDP offer.
2798    ///
2799    /// `SendRecv` is the ordinary endpoint default. A two-dialog owner uses this to map the
2800    /// source leg's initial offer onto fresh SDP for its target leg without copying endpoint
2801    /// addresses, ports or key material.
2802    pub initial_direction: Direction,
2803    /// How long to wait for an answer before giving up and cancelling.
2804    ///
2805    /// `None` waits as long as the transaction layer does — 64·T1, or 32 seconds with the
2806    /// default constants. A bound *here* rather than around the call is what makes giving up
2807    /// correct: dropping the future partway through leaves the far end believing it is in a
2808    /// call, and only code inside the exchange can send the CANCEL that stops it.
2809    pub timeout: Option<Duration>,
2810    /// Ask for an RFC 4028 session timer of this length.
2811    ///
2812    /// `None` is the default and means no timer is requested. That is not the same as no timer
2813    /// being *run*: a far end that asks for one gets it, because refusing to refresh a session
2814    /// the peer is timing would have it hang up on a call that is working.
2815    pub session_expires: Option<Duration>,
2816    /// The pre-loaded route set to put on the INVITE, outermost proxy first (RFC 3608 §6.1).
2817    ///
2818    /// Empty by default: the INVITE goes to `target` and no further. Set it from a registrar's
2819    /// `Service-Route` — `UserAgent::service_route().rendered()` produces exactly this — when the
2820    /// registration says outbound requests must traverse proxies. Without it, a call placed
2821    /// through a registration reaches a proxy holding no state for it. This field serializes the
2822    /// `Route` headers; the application must resolve the outer hop and supply that transport
2823    /// destination as the [`Target`] passed to [`dial`]. The call layer does not resolve a Route
2824    /// URI or override the caller's target.
2825    pub service_route: Vec<String>,
2826    /// Application-supplied fields on the initial INVITE.
2827    ///
2828    /// Values have already passed [`sipx_sip::Header::build`]'s line-injection checks. The call
2829    /// layer retains them in the options so authentication and session-timer retries send the
2830    /// same request metadata as the first attempt. Applications remain responsible for refusing
2831    /// stack-owned routing and dialog fields before constructing these values.
2832    pub headers: Vec<sipx_sip::Header>,
2833    /// The media policy for this call.
2834    ///
2835    /// The default is G.711, no ICE. In particular, enabling a crate feature never changes what
2836    /// goes on the wire without an application selecting it.
2837    pub media: MediaPolicy,
2838    /// Credentials to answer a 401 or 407 during this call attempt.
2839    ///
2840    /// Owned by the application and retained only in the options it passes. Their `Debug`
2841    /// representation redacts the password, and the call path never logs an authorization value.
2842    /// [`dial`] and [`dial_once`] perform the bounded retry; [`dial_early`] surfaces
2843    /// [`Error::AuthenticationChallenge`] because its handle names the original INVITE.
2844    pub credentials: Option<Credentials>,
2845    /// Authentication service selected for this call's initial INVITE attempts.
2846    ///
2847    /// `None` is the wire-compatible default: no `Date` or `Identity` is added and no authority,
2848    /// credential, or time input is consulted. The policy owns those explicit caller inputs.
2849    pub identity: Option<OutboundIdentityPolicy>,
2850}
2851
2852impl DialOptions {
2853    /// Options for a call from an address of record.
2854    #[must_use]
2855    pub fn new(from: impl Into<String>, media_address: IpAddr) -> Self {
2856        Self {
2857            from: from.into(),
2858            media_address,
2859            media_bind_address: media_address,
2860            initial_direction: Direction::SendRecv,
2861            timeout: None,
2862            session_expires: None,
2863            service_route: Vec::new(),
2864            headers: Vec::new(),
2865            media: MediaPolicy::default(),
2866            credentials: None,
2867            identity: None,
2868        }
2869    }
2870
2871    /// Offer these codecs, most preferred first.
2872    ///
2873    /// [`Codecs::Opus`] puts Opus ahead of the G.711 pair in the offer; the far end's answer
2874    /// decides what the call carries, and a peer without Opus still gets G.711.
2875    #[must_use]
2876    pub fn with_codecs(mut self, codecs: Codecs) -> Self {
2877        self.media.codecs = codecs;
2878        self
2879    }
2880
2881    /// Advertise this direction in the initial offer.
2882    #[must_use]
2883    pub const fn with_initial_direction(mut self, direction: Direction) -> Self {
2884        self.initial_direction = direction;
2885        self
2886    }
2887
2888    /// Key this call with the selected mechanism.
2889    #[must_use]
2890    pub fn with_keying(mut self, keying: Keying) -> Self {
2891        self.media.keying = keying;
2892        self
2893    }
2894
2895    /// Use this complete media policy for the call.
2896    #[must_use]
2897    pub fn with_media_policy(mut self, media: MediaPolicy) -> Self {
2898        self.media = media;
2899        self
2900    }
2901
2902    /// Bind RTP on this local address without changing the address advertised in SDP.
2903    #[must_use]
2904    pub const fn with_media_bind_address(mut self, address: IpAddr) -> Self {
2905        self.media_bind_address = address;
2906        self
2907    }
2908
2909    /// Traverse these proxies on the way out, outermost first (RFC 3608).
2910    ///
2911    /// The values are `Route` header values — `<sip:proxy.example;lr>` — which is what
2912    /// `ServiceRoute::rendered` returns. Order is normative: §6.1 requires a UA that exercises a
2913    /// service route to preserve the order the registrar listed. This only serializes headers:
2914    /// resolve the outer hop in the application and pass that address as the `target` to [`dial`].
2915    #[must_use]
2916    pub fn with_service_route(mut self, hops: Vec<String>) -> Self {
2917        self.service_route = hops;
2918        self
2919    }
2920
2921    /// Add a validated application-owned field to every initial INVITE attempt.
2922    #[must_use]
2923    pub fn with_header(mut self, header: sipx_sip::Header) -> Self {
2924        self.headers.push(header);
2925        self
2926    }
2927
2928    /// Answer a digest challenge with these credentials (RFC 3261 §22).
2929    #[must_use]
2930    pub fn with_credentials(mut self, credentials: Credentials) -> Self {
2931        self.credentials = Some(credentials);
2932        self
2933    }
2934
2935    /// Sign every initial INVITE attempt with this caller-owned authentication policy.
2936    #[must_use]
2937    pub fn with_identity(mut self, identity: OutboundIdentityPolicy) -> Self {
2938        self.identity = Some(identity);
2939        self
2940    }
2941
2942    /// Detect a far end that vanishes, by refreshing the session on this interval (RFC 4028).
2943    ///
2944    /// Without this, a peer that loses power leaves the call up forever: there is no BYE, the
2945    /// socket never closes, and nothing else in SIP notices. The interval is raised to the
2946    /// RFC's ninety-second floor if it is shorter, because a shorter one is an amplification
2947    /// vector rather than a configuration choice.
2948    #[must_use]
2949    pub fn with_session_timer(mut self, interval: Duration) -> Self {
2950        self.session_expires = Some(interval.max(session::ABSOLUTE_MIN_INTERVAL));
2951        self
2952    }
2953
2954    /// Give up after this long.
2955    #[must_use]
2956    pub fn with_timeout(mut self, timeout: Duration) -> Self {
2957        self.timeout = Some(timeout);
2958        self
2959    }
2960}
2961
2962/// Place a call.
2963/// What this side offers, and the description that carries it.
2964///
2965/// A key is offered only when the transport protects it: SDES puts the master key in the SDP
2966/// body, so offering one over cleartext SIP publishes it (RFC 4568 §7.1).
2967///
2968/// Both the receive address and the codec set come from the caller's [`DialOptions`], so they are
2969/// taken as one rather than passed apart: they are two halves of the same decision about what this
2970/// side is offering, and splitting them invites a call site that reads the set from somewhere else.
2971#[derive(Debug)]
2972enum PendingKeying {
2973    Sdes,
2974    #[cfg(feature = "dtls")]
2975    Dtls(sipx_media::dtls::openssl::Identity),
2976}
2977
2978/// Refuse an impossible named profile before binding, gathering, certificate creation, or SIP I/O.
2979fn validate_profile_preflight(policy: MediaPolicy, transport: TransportKind) -> Result<()> {
2980    if policy.profile == MediaProfile::Standard {
2981        return Ok(());
2982    }
2983    if !cfg!(feature = "opus") {
2984        return Err(sipx_sdp::browser_audio::ProfileError::OpusUnavailable.into());
2985    }
2986    if !cfg!(feature = "dtls") {
2987        return Err(Error::DtlsUnavailable);
2988    }
2989    if transport != TransportKind::Wss {
2990        return Err(sipx_sdp::browser_audio::ProfileError::InsecureSignalling.into());
2991    }
2992    if policy.ice == IcePolicy::Disabled {
2993        return Err(sipx_sdp::browser_audio::ProfileError::IceRequired.into());
2994    }
2995    if policy.keying != Keying::DtlsSrtp {
2996        return Err(sipx_sdp::browser_audio::ProfileError::WeakerMedia.into());
2997    }
2998    #[cfg(feature = "opus")]
2999    if policy.codecs != Codecs::Opus {
3000        return if policy.codecs.carries(Codec::Opus) {
3001            Err(sipx_sdp::browser_audio::ProfileError::CodecSetIncomplete.into())
3002        } else {
3003            Err(sipx_sdp::browser_audio::ProfileError::OpusUnavailable.into())
3004        };
3005    }
3006    Ok(())
3007}
3008
3009/// Build the capabilities selected by policy and retain anything the later handshake needs.
3010fn media_capabilities(
3011    policy: MediaPolicy,
3012    address: IpAddr,
3013    port: u16,
3014    secure_signalling: bool,
3015) -> Result<(Capabilities, PendingKeying)> {
3016    if policy.profile == MediaProfile::Standard
3017        && policy.keying == Keying::DtlsSrtp
3018        && policy.ice != IcePolicy::Disabled
3019    {
3020        return Err(Error::Sdp(
3021            "DTLS-SRTP cannot yet be combined with ICE on one media port".to_owned(),
3022        ));
3023    }
3024    // Offer mux on every ordinary audio exchange. A peer that omits it in the answer selects the
3025    // established adjacent-port fallback; no retry or second offer is needed (RFC 5761 §5.1.1).
3026    let capabilities = policy.codecs.capabilities(address, port).with_rtcp_mux();
3027    match policy.keying {
3028        Keying::Auto => Ok((
3029            capabilities.with_srtp(secure_signalling),
3030            PendingKeying::Sdes,
3031        )),
3032        Keying::Plain => Ok((capabilities, PendingKeying::Sdes)),
3033        Keying::Sdes => {
3034            if !secure_signalling {
3035                return Err(Error::Sdp(
3036                    "SDES-SRTP requires protected signalling".to_owned(),
3037                ));
3038            }
3039            Ok((capabilities.with_srtp(true), PendingKeying::Sdes))
3040        }
3041        Keying::DtlsSrtp => {
3042            #[cfg(feature = "dtls")]
3043            {
3044                let identity = sipx_media::dtls::openssl::Identity::generate()
3045                    .map_err(|error| Error::Dtls(error.to_string()))?;
3046                let fingerprint = identity
3047                    .fingerprint()
3048                    .map_err(|error| Error::Dtls(error.to_string()))?;
3049                Ok((
3050                    capabilities.with_dtls_srtp(fingerprint),
3051                    PendingKeying::Dtls(identity),
3052                ))
3053            }
3054            #[cfg(not(feature = "dtls"))]
3055            {
3056                Err(Error::DtlsUnavailable)
3057            }
3058        }
3059    }
3060}
3061
3062async fn offered_media(
3063    options: &DialOptions,
3064    port: &MediaPort,
3065    transport: TransportKind,
3066) -> Result<(
3067    Capabilities,
3068    SessionDescription,
3069    Option<LocalDescription>,
3070    PendingKeying,
3071)> {
3072    let local_ice = match options.media.gathering(true)? {
3073        // An initial offer has not settled mux yet, so retain component 2 and its `a=rtcp`
3074        // destination for RFC 5761's no-second-exchange fallback.
3075        Some(gathering) => {
3076            let rtcp_mode = if options.media.profile == MediaProfile::BrowserAudio {
3077                sipx_sdp::RtcpMode::Mux
3078            } else {
3079                sipx_sdp::RtcpMode::Separate
3080            };
3081            Some(port.gather_with_rtcp_mode(&gathering, rtcp_mode).await)
3082        }
3083        None => None,
3084    };
3085    let advertised = local_ice
3086        .as_ref()
3087        .and_then(|local| local.default_destination(ComponentId::RTP))
3088        .unwrap_or_else(|| SocketAddr::new(options.media_address, port.local_addr().port()));
3089    let (capabilities, keying) = media_capabilities(
3090        options.media,
3091        advertised.ip(),
3092        advertised.port(),
3093        transport.is_secure(),
3094    )?;
3095    let mut capabilities = capabilities;
3096    capabilities.direction = options.initial_direction;
3097    let offer = if options.media.profile == MediaProfile::BrowserAudio {
3098        let local = local_ice
3099            .as_ref()
3100            .ok_or(sipx_sdp::browser_audio::ProfileError::IceRequired)?;
3101        let fingerprint = capabilities
3102            .dtls()
3103            .cloned()
3104            .ok_or(sipx_sdp::browser_audio::ProfileError::FingerprintRequired)?;
3105        sipx_sdp::browser_audio::offer(&sipx_sdp::browser_audio::BrowserAudioLocal {
3106            address: advertised.ip(),
3107            port: advertised.port(),
3108            session_id: capabilities.session_id,
3109            session_version: capabilities.session_version,
3110            direction: options.initial_direction,
3111            ice: local.credentials().clone(),
3112            candidates: local.candidates().to_vec(),
3113            fingerprint,
3114            setup: sipx_sdp::fingerprint::SetupCapabilities::both(),
3115        })?
3116    } else {
3117        let mut offer = offer_from(&capabilities);
3118        if let Some(local) = &local_ice {
3119            add_ice(&mut offer, local, &[]);
3120        }
3121        offer
3122    };
3123    Ok((capabilities, offer, local_ice, keying))
3124}
3125
3126/// Put one gathered local description into the audio stream it belongs to.
3127fn add_ice(
3128    description: &mut SessionDescription,
3129    local: &LocalDescription,
3130    additional: &[sipx_sdp::Attribute],
3131) {
3132    let Some(default) = local.default_destination(ComponentId::RTP) else {
3133        return;
3134    };
3135    description.connection = Some(Connection::new(default.ip()));
3136    if let Some(audio) = description.media.first_mut() {
3137        audio.port = default.port();
3138        if let Some(control) = local.default_destination(ComponentId::RTCP) {
3139            let address_type = if control.is_ipv6() { "IP6" } else { "IP4" };
3140            audio.attributes.push(sipx_sdp::Attribute::valued(
3141                "rtcp",
3142                format!("{} IN {address_type} {}", control.port(), control.ip()),
3143            ));
3144        }
3145        audio.attributes.extend(local.attributes());
3146        audio.attributes.extend_from_slice(additional);
3147    }
3148}
3149
3150/// The `a=` names RFC 8839 §5 gives ICE, so a later description can replace its own half.
3151///
3152/// Replaced rather than appended: `sipx_sdp::answer` copies the stream it is answering, so an
3153/// answer built from an offer that carried ICE starts out holding the *peer's* `ice-ufrag`,
3154/// `ice-pwd` and candidates. Extending that with ours would produce a description claiming both
3155/// sets, and a peer reading the first `ice-ufrag` it finds would key its checks to its own
3156/// credentials.
3157const ICE_ATTRIBUTES: &[&str] = &[
3158    "ice-ufrag",
3159    "ice-pwd",
3160    "ice-options",
3161    "ice-lite",
3162    "ice-pacing",
3163    "candidate",
3164    "remote-candidates",
3165];
3166
3167/// Whether an attribute is one of the ICE names a later description restates.
3168///
3169/// `ice-mismatch` is deliberately **not** here. RFC 8839 §5.3 makes it a statement about the
3170/// exchange rather than a parameter of this side's ICE session, and a stream that carries it is
3171/// one ICE is not running for at all.
3172fn is_ice_attribute(attribute: &sipx_sdp::Attribute) -> bool {
3173    ICE_ATTRIBUTES
3174        .iter()
3175        .any(|name| attribute.name.eq_ignore_ascii_case(name))
3176}
3177
3178/// This side's ICE half for a later offer or answer (RFC 8839 §4.4; `ice.md` §13.5).
3179///
3180/// The same three lines an initial description carries, from the agent rather than from the
3181/// gathering that has long since finished — `ice2` included, because §13.5's re-signalling has to
3182/// restate the whole half and a peer that stopped seeing `ice-options` would read it as a change.
3183fn ice_attributes(local: &sipx_media::ice::Local) -> Vec<sipx_sdp::Attribute> {
3184    let mut attributes = vec![
3185        sipx_sdp::Attribute::valued("ice-ufrag", local.credentials.ufrag()),
3186        sipx_sdp::Attribute::valued("ice-pwd", local.credentials.pwd()),
3187        sipx_sdp::Attribute::valued("ice-options", sipx_sdp::ice::ICE2),
3188    ];
3189    attributes.extend(
3190        local
3191            .candidates
3192            .iter()
3193            .map(|candidate| sipx_sdp::Attribute::valued("candidate", candidate.to_value())),
3194    );
3195    attributes
3196}
3197
3198/// What a later offer says about the ICE session already running (RFC 8839 §4.4; `ice.md` §13.5).
3199///
3200/// Two variants and not a `bool`, because the wire difference between them is not a flag: a
3201/// continuing offer restates the credentials in force, and a restart states new ones. §4.4.1.1.1
3202/// makes *that change* the entire signal, so there is nothing else for either variant to set.
3203#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3204enum IceOffer {
3205    /// Restate this side's half unchanged — hold, resume, a codec change, a session refresh.
3206    Continue,
3207    /// Draw new credentials and a new tiebreaker, which is what begins a new ICE session.
3208    Restart,
3209}
3210
3211/// The peer's ICE credentials as the description in this body states them, for [`Call::peer_ice`].
3212///
3213/// Read from the message that completed the initial exchange — the 2xx for a caller, the INVITE
3214/// for a callee — because a restart is only ever recognisable as a *change* from what was last
3215/// seen, and a call that recorded nothing would read the peer's first re-offer as one.
3216///
3217/// `None` for a description with no ICE, which is the ordinary call: nothing that arrives later can
3218/// restart a session that never began.
3219fn peer_ice_credentials(body: &[u8]) -> Option<sipx_sdp::ice::Credentials> {
3220    let description = sipx_sdp::parse(&String::from_utf8_lossy(body)).ok()?;
3221    let audio = description.media.first()?;
3222    match sipx_media::ice::negotiate(&description, audio) {
3223        IceNegotiation::Ice { credentials, .. } => Some(credentials),
3224        IceNegotiation::Absent | IceNegotiation::Mismatch => None,
3225    }
3226}
3227
3228/// Credentials and a tiebreaker for a new ICE session (RFC 8839 §5.4, RFC 8445 §7.1.3).
3229///
3230/// Drawn per session and never reused, for the reason `ice.md` §13.4 gives about the initial
3231/// exchange and which applies unchanged to a restart: credentials that outlive the session they
3232/// authenticated make one session's checks valid in another. A tiebreaker carried across would
3233/// resolve a role conflict the way the *previous* session resolved it.
3234///
3235/// `None` when credentials could not be built — the same failure `MediaPolicy::gathering` reports
3236/// on the initial exchange, from the same generator, so it is not reachable in practice. It
3237/// degrades rather than failing the renegotiation: the agent keeps the credentials it has, the
3238/// answer restates them, and the peer keys its new session's checks to those. RFC 8839 §4.4.1.1.1
3239/// asks the answerer for new ones; reusing them is worse than complying and much better than
3240/// refusing a re-offer on a call that is working.
3241fn fresh_ice_parameters() -> Option<(sipx_sdp::ice::Credentials, u64)> {
3242    let credentials = IceCredentials::new(token(), format!("{}{}", token(), token()))?;
3243    Some((credentials, rand::random()))
3244}
3245
3246/// The INVITE that opens a call.
3247///
3248/// Its own function only because `dial` had grown past the point where the interesting part —
3249/// what happens to the *response* — was visible among the header construction.
3250/// What identifies the dialog an INVITE is trying to create.
3251///
3252/// Held apart from the message so a retry can keep it. RFC 4028 §7.3 says a request re-sent
3253/// after a `422` "SHOULD have the same value as the Call-ID, To, and From of the previous
3254/// request" — a fresh identity would look to the far end like a second, unrelated call attempt
3255/// rather than the answer to the counter-offer it just made.
3256#[derive(Debug, Clone)]
3257struct Identity {
3258    call_id: String,
3259    from_tag: String,
3260    cseq: u32,
3261}
3262
3263impl Identity {
3264    fn fresh() -> Self {
3265        Self {
3266            call_id: format!("{}@sipx", token()),
3267            from_tag: token(),
3268            cseq: 1,
3269        }
3270    }
3271
3272    /// The same dialog, one transaction later.
3273    fn again(&self) -> Self {
3274        Self {
3275            cseq: self.cseq.saturating_add(1),
3276            ..self.clone()
3277        }
3278    }
3279}
3280
3281/// Everything that goes into the INVITE besides where it is being sent.
3282struct Invitation<'a> {
3283    to: &'a Uri,
3284    from: &'a str,
3285    via: &'a str,
3286    offer: Option<&'a SessionDescription>,
3287    session_expires: Option<Duration>,
3288    identity: &'a Identity,
3289    /// The pre-loaded route set, outermost first (RFC 3608 §6.1).
3290    service_route: &'a [String],
3291    /// Validated application-owned fields, preserved across retries.
3292    headers: &'a [sipx_sip::Header],
3293}
3294
3295fn build_invite(
3296    endpoint: &Handle,
3297    target: &Target,
3298    invitation: &Invitation<'_>,
3299) -> Result<Request> {
3300    let &Invitation {
3301        to,
3302        from,
3303        via,
3304        offer,
3305        session_expires,
3306        identity,
3307        service_route,
3308        headers,
3309    } = invitation;
3310    let Identity {
3311        call_id,
3312        from_tag,
3313        cseq,
3314    } = identity;
3315    let mut builder = RequestBuilder::new(Method::Invite, to.clone())
3316        .header(HeaderName::Via, Bytes::from(via.to_owned()))?
3317        .header(
3318            HeaderName::To,
3319            Bytes::from(format!("<{}>", String::from_utf8_lossy(&to.to_bytes()))),
3320        )?
3321        .header(
3322            HeaderName::From,
3323            Bytes::from(format!("{from};tag={from_tag}")),
3324        )?
3325        .header(HeaderName::CallId, Bytes::from(call_id.clone()))?
3326        .cseq(*cseq, &Method::Invite)?
3327        .header(
3328            HeaderName::Contact,
3329            Bytes::from(contact_for(endpoint, target.transport)),
3330        )?
3331        .max_forwards(70);
3332    if let Some(offer) = offer {
3333        builder = builder
3334            .header(
3335                HeaderName::ContentType,
3336                Bytes::from_static(b"application/sdp"),
3337            )?
3338            .body(Bytes::from(offer.to_string_sdp()));
3339    }
3340
3341    // One `Supported` row listing everything this side can do. Both tags are statements of
3342    // capability rather than requests: `timer` tells a far end that wants liveness detection
3343    // that it may have it, and `100rel` (RFC 3262 §4) is what permits the far end to send a
3344    // reliable provisional at all — §3 forbids it outright if we stay quiet, which means a
3345    // silent UAC gets unreliable ringing even from a UAS that would rather not send it.
3346    builder = builder.header(
3347        HeaderName::Supported,
3348        Bytes::from_static(b"timer, 100rel, histinfo"),
3349    )?;
3350    builder = builder.header(
3351        HeaderName::HistoryInfo,
3352        HistoryInfo::initial(to.clone()).to_bytes(),
3353    )?;
3354    // RFC 3311 §4: "A UAC compliant to this specification SHOULD also include an Allow header
3355    // field in the INVITE request, listing the method UPDATE." It is the only way the far end
3356    // is permitted to decide it may renegotiate the early session or refresh with an UPDATE
3357    // rather than a re-INVITE, so leaving it off does not merely omit a courtesy — it silently
3358    // forces every peer onto the heavier method for the life of the dialog.
3359    builder = builder.header(
3360        HeaderName::Allow,
3361        Bytes::from_static(update::ALLOW.as_bytes()),
3362    )?;
3363    if let Some(interval) = session_expires {
3364        // No `refresher` parameter. RFC 4028 Table 2 row 4 lets the UAS choose when the UAC
3365        // has not, and the UAS is the side that knows whether it is behind a NAT or a proxy
3366        // that cares. Naming ourselves would override a better-informed decision.
3367        let expires = SessionExpires {
3368            interval,
3369            refresher: None,
3370        };
3371        builder = builder
3372            .header(HeaderName::SessionExpires, Bytes::from(expires.to_string()))?
3373            .header(
3374                HeaderName::MinSe,
3375                Bytes::from(session::ABSOLUTE_MIN_INTERVAL.as_secs().to_string()),
3376            )?;
3377    }
3378    for header in headers {
3379        builder = builder.header(
3380            header.name().clone(),
3381            Bytes::copy_from_slice(header.raw_value()),
3382        )?;
3383    }
3384    // Pre-loaded Route, before the request goes anywhere. RFC 3608 §6.1 has the service route
3385    // used "as a preloaded Route header field in outgoing initial requests", and §6.1 requires
3386    // the order preserved — which `add_routes` does by appending in sequence. This is the only
3387    // place the INVITE can acquire it: a Route added after the transaction is created would not
3388    // be on the message the far end matched.
3389    let builder = add_routes(builder, service_route)?;
3390    Ok(builder.build())
3391}
3392
3393/// One digest answer to attach to a retried INVITE.
3394struct Authorization<'a> {
3395    challenge: &'a sipx_sip::auth::Challenge,
3396    credentials: &'a Credentials,
3397    nonce_count: u32,
3398    cnonce: &'a str,
3399}
3400
3401/// Add the header a 401 or 407 asked for, covering this request's method and URI.
3402fn authorize_invite(request: &mut Request, authorization: &Authorization<'_>) -> Result<()> {
3403    let uri = String::from_utf8_lossy(&request.uri.to_bytes()).into_owned();
3404    let method = String::from_utf8_lossy(request.method.as_bytes()).into_owned();
3405    let value = sipx_sip::auth::respond(
3406        authorization.challenge,
3407        authorization.credentials,
3408        &method,
3409        &uri,
3410        authorization.nonce_count,
3411        authorization.cnonce,
3412    );
3413    request.headers.push(sipx_sip::Header::build(
3414        authorization.challenge.response_header(),
3415        Bytes::from(value),
3416    )?);
3417    Ok(())
3418}
3419
3420/// The `Min-SE` a `422` demands, if it named one (RFC 4028 §6).
3421fn required_interval(response: &Response) -> Option<Duration> {
3422    response
3423        .headers
3424        .typed::<MinSe>()
3425        .and_then(std::result::Result::ok)
3426        .map(|min| min.0)
3427}
3428
3429/// Place a call, retrying once if the far end refuses the session interval.
3430///
3431/// RFC 4028 §7.3: a `422` is not a refusal of the call, it is a counter-offer of an interval.
3432/// Retrying is bounded to a single attempt on purpose — a peer that answers 422 to its own
3433/// stated minimum is broken, and a loop there is an outbound call flood.
3434pub async fn dial(
3435    endpoint: &Handle,
3436    target: Target,
3437    to: &Uri,
3438    options: &DialOptions,
3439) -> Result<Call> {
3440    dial_retrying(endpoint, target, to, options, true, None).await
3441}
3442
3443/// Place a call until it completes or `cancelled` resolves.
3444///
3445/// This has the same bounded authentication and session-interval retries as [`dial`]. If
3446/// cancellation wins while an INVITE is outstanding, the invitation is withdrawn before this
3447/// returns, including the ACK-then-BYE race when a successful final response was already in
3448/// flight. The returned [`Error::Cancelled`] distinguishes that local stop from a peer refusal.
3449pub async fn dial_until<F>(
3450    endpoint: &Handle,
3451    target: Target,
3452    to: &Uri,
3453    options: &DialOptions,
3454    cancelled: F,
3455) -> Result<Call>
3456where
3457    F: Future<Output = ()> + Send,
3458{
3459    tokio::pin!(cancelled);
3460    let cancelled: Pin<&mut (dyn Future<Output = ()> + Send)> = cancelled.as_mut();
3461    dial_retrying(endpoint, target, to, options, true, Some(cancelled)).await
3462}
3463
3464/// Place a call, surfacing a `422` instead of retrying it.
3465///
3466/// [`dial`] is this plus one retry, and is what almost everything wants. This is here for a
3467/// caller that would rather decide for itself what to do about an interval it does not like —
3468/// a gateway with a policy about how often it is willing to be woken, say.
3469pub async fn dial_once(
3470    endpoint: &Handle,
3471    target: Target,
3472    to: &Uri,
3473    options: &DialOptions,
3474) -> Result<Call> {
3475    dial_retrying(endpoint, target, to, options, false, None).await
3476}
3477
3478type Cancelled<'a> = Pin<&'a mut (dyn Future<Output = ()> + Send)>;
3479
3480/// Drive the two bounded retry reasons an initial INVITE has: authentication and session interval.
3481async fn dial_retrying(
3482    endpoint: &Handle,
3483    target: Target,
3484    to: &Uri,
3485    options: &DialOptions,
3486    retry_interval: bool,
3487    mut cancelled: Option<Cancelled<'_>>,
3488) -> Result<Call> {
3489    let credentials = options.credentials.clone();
3490    let mut attempted = options.clone();
3491    let mut identity = Identity::fresh();
3492    let mut challenge: Option<Box<sipx_sip::auth::Challenge>> = None;
3493    let mut nonce_use: Option<(String, u32)> = None;
3494    let mut stale_retried = false;
3495    let mut interval_retried = false;
3496
3497    loop {
3498        let cnonce = token();
3499        let authorization =
3500            challenge
3501                .as_deref()
3502                .zip(credentials.as_ref())
3503                .map(|(challenge, credentials)| Authorization {
3504                    challenge,
3505                    credentials,
3506                    nonce_count: nonce_count_for(&mut nonce_use, &challenge.nonce),
3507                    cnonce: &cnonce,
3508                });
3509        let result = dial_with(
3510            endpoint,
3511            target.clone(),
3512            to,
3513            &attempted,
3514            &identity,
3515            authorization.as_ref(),
3516            &mut cancelled,
3517        )
3518        .await;
3519
3520        match result {
3521            Err(Error::AuthenticationChallenge {
3522                status,
3523                reason,
3524                challenge: received,
3525            }) => {
3526                let rejected = || Error::Rejected {
3527                    status,
3528                    reason: reason.clone(),
3529                };
3530                if credentials.is_none() {
3531                    return Err(rejected());
3532                }
3533                if challenge.is_none() {
3534                    challenge = Some(received);
3535                } else if received.stale && !stale_retried {
3536                    stale_retried = true;
3537                    challenge = Some(received);
3538                } else {
3539                    return Err(rejected());
3540                }
3541            }
3542            Err(Error::IntervalTooBrief(required)) if retry_interval && !interval_retried => {
3543                interval_retried = true;
3544                attempted.session_expires = Some(required.max(session::ABSOLUTE_MIN_INTERVAL));
3545            }
3546            other => return other,
3547        }
3548        identity = identity.again();
3549    }
3550}
3551
3552/// The count of requests sent under `nonce`, starting over when the nonce changes.
3553fn nonce_count_for(nonce_use: &mut Option<(String, u32)>, nonce: &str) -> u32 {
3554    let count = match nonce_use {
3555        Some((last, count)) if last == nonce => count.saturating_add(1),
3556        _ => 1,
3557    };
3558    *nonce_use = Some((nonce.to_owned(), count));
3559    count
3560}
3561
3562/// Bind the media port and build the INVITE that will advertise it.
3563///
3564/// Split out of `dial_with` only for length: what is interesting there is what happens to the
3565/// *response*, and it was buried under header construction.
3566async fn open_invitation(
3567    endpoint: &Handle,
3568    target: &Target,
3569    to: &Uri,
3570    options: &DialOptions,
3571    identity: &Identity,
3572    authorization: Option<&Authorization<'_>>,
3573) -> Result<(
3574    MediaPort,
3575    Capabilities,
3576    Option<LocalDescription>,
3577    PendingKeying,
3578    Request,
3579)> {
3580    validate_profile_preflight(options.media, target.transport)?;
3581    MediaAddress::new(options.media_address)
3582        .with_bind(options.media_bind_address)
3583        .validate()?;
3584    // The offer has to name the port audio will arrive on, and only a bound socket knows it.
3585    // So the port is bound now and the session started once the answer says where and in what.
3586    let port = MediaPort::bind(SocketAddr::new(options.media_bind_address, 0))
3587        .await
3588        .map_err(Error::Io)?;
3589
3590    let (capabilities, offer, ice, keying) =
3591        offered_media(options, &port, target.transport).await?;
3592
3593    // The `Via` is built here rather than left to the transport, because a CANCEL has to carry
3594    // the *same* branch as the INVITE it cancels — that identity is what matches the two at the
3595    // far end (RFC 3261 §9.1). Letting the transport generate it would leave this layer unable
3596    // to name the transaction it started.
3597    let via = format!(
3598        "SIP/2.0/{} {};rport;branch={}",
3599        target.transport.as_str(),
3600        endpoint.sent_by_for(target.transport),
3601        sipx_transport::new_branch()
3602    );
3603
3604    let mut invite = build_invite(
3605        endpoint,
3606        target,
3607        &Invitation {
3608            to,
3609            from: options.from.as_str(),
3610            via: &via,
3611            offer: Some(&offer),
3612            session_expires: options.session_expires,
3613            identity,
3614            service_route: &options.service_route,
3615            headers: &options.headers,
3616        },
3617    )?;
3618    if let Some(authorization) = authorization {
3619        authorize_invite(&mut invite, authorization)?;
3620    }
3621    if let Some(identity) = &options.identity {
3622        identity.sign(&mut invite)?;
3623    }
3624    Ok((port, capabilities, ice, keying, invite))
3625}
3626
3627/// Build the RFC 3262 delayed-offer form of an INVITE.
3628///
3629/// No media socket is bound yet because the remote offer determines whether there is a session
3630/// to answer. The socket is created when that offer arrives in a reliable provisional, before
3631/// its answer is placed in PRACK.
3632fn open_offerless_invitation(
3633    endpoint: &Handle,
3634    target: &Target,
3635    to: &Uri,
3636    options: &DialOptions,
3637    identity: &Identity,
3638) -> Result<Request> {
3639    MediaAddress::new(options.media_address)
3640        .with_bind(options.media_bind_address)
3641        .validate()?;
3642    let via = format!(
3643        "SIP/2.0/{} {};rport;branch={}",
3644        target.transport.as_str(),
3645        endpoint.sent_by_for(target.transport),
3646        sipx_transport::new_branch()
3647    );
3648    let mut invite = build_invite(
3649        endpoint,
3650        target,
3651        &Invitation {
3652            to,
3653            from: options.from.as_str(),
3654            via: &via,
3655            offer: None,
3656            session_expires: options.session_expires,
3657            identity,
3658            service_route: &options.service_route,
3659            headers: &options.headers,
3660        },
3661    )?;
3662    if let Some(identity) = &options.identity {
3663        identity.sign(&mut invite)?;
3664    }
3665    Ok(invite)
3666}
3667
3668/// Take back an invitation the caller has stopped waiting for.
3669///
3670/// Split out of `dial_with` for length, but it is the part with all the hazards in it, so it
3671/// keeps its own name: everything here is about not leaving the far end in a call.
3672async fn withdraw(
3673    endpoint: &Handle,
3674    invite: &Request,
3675    target: Target,
3676    responses: &mut sipx_transport::Responses,
3677    reason: &ReasonValue,
3678) {
3679    // Giving up is not just ceasing to wait. The far end is ringing and has been told
3680    // nothing; without a CANCEL it goes on ringing, and someone answering afterwards
3681    // ends up in a call with a party that has left.
3682    //
3683    // The transport operation owns §9.1's race: it waits until the exact INVITE has a provisional,
3684    // or returns the final/timeout/transport event that won instead. Events it observes remain on
3685    // `responses`, so the crossing-2xx safeguard below sees the same transaction history.
3686    let grace = tokio::time::Instant::now() + Duration::from_secs(2);
3687    match tokio::time::timeout_at(
3688        grace,
3689        Box::pin(endpoint.cancel_invite(responses, Some(Reason::from(reason.clone())))),
3690    )
3691    .await
3692    {
3693        Ok(Ok(sipx_transport::CancelInviteOutcome::Sent(_cancellation))) => {}
3694        Ok(Ok(sipx_transport::CancelInviteOutcome::FinalResponse { response, .. })) => {
3695            if response.status.is_success() {
3696                ack_then_bye(endpoint, invite, &response, target).await;
3697            }
3698            return;
3699        }
3700        Ok(Ok(_)) | Err(_) => return,
3701        // The loss is counted where transport output is attempted. This is already the giving-up
3702        // path; its remaining remedy is still to catch a crossing 2xx and ACK-then-BYE it.
3703        Ok(Err(error)) => tracing::debug!(%error, "could not create CANCEL transaction"),
3704    }
3705
3706    // CANCEL cannot close the race it exists to manage: a 200 already in flight
3707    // arrives anyway, and RFC 3261 §15 says a UAC that will not proceed must
3708    // acknowledge it and then hang up rather than leave it unanswered.
3709    while let Ok(Some(event)) = tokio::time::timeout_at(grace, responses.next()).await {
3710        let sipx_sip::transaction::TuEvent::Response(late) = event else {
3711            continue;
3712        };
3713        if !late.status.is_final() {
3714            continue;
3715        }
3716        if late.status.is_success() {
3717            ack_then_bye(endpoint, invite, &late, target.clone()).await;
3718        }
3719        break;
3720    }
3721}
3722
3723/// Acknowledge a 2xx this side will not proceed with, and hang the dialog up (RFC 3261 §15).
3724///
3725/// Both callers reach it having already put a 2xx beyond recall: [`withdraw`], where a CANCEL lost
3726/// its race, and [`dial_with`], where establishing the call failed after the far end already
3727/// believed one existed. Neither may simply walk away — an unacknowledged 2xx is retransmitted for
3728/// 32 seconds and then streamed at a port this side has closed.
3729///
3730/// Every step is best-effort by design. This runs on a path that is already failing, and a BYE
3731/// that cannot be built or sent must not mask the error that brought us here.
3732async fn ack_then_bye(endpoint: &Handle, invite: &Request, response: &Response, target: Target) {
3733    let Some(dialog) = Dialog::from_response(invite, response) else {
3734        return;
3735    };
3736    let in_dialog = in_dialog_target(&dialog, target);
3737    // discard: counted as `sipx_transport::UnsentCounts::ack`. An ACK for a 2xx that does not go
3738    // out is the worst of the three — it has no transaction to retry it (RFC 3261 §13.2.2.4), so
3739    // the far end retransmits its 2xx for thirty-two seconds and then streams at a port this side
3740    // has closed. This one goes out through `send_directly`, so the `Result` dropped here *does*
3741    // report the transmit; it is dropped because the BYE below is the only remedy and is attempted
3742    // whether or not the ACK landed, and returning the error would only mask the failure that
3743    // brought us here.
3744    let _ = send_ack(endpoint, &dialog, in_dialog.clone()).await;
3745    if let Ok(bye) = bye_request(
3746        &dialog,
3747        dialog.local_cseq.saturating_add(1),
3748        &normal_clearing_reason(),
3749    ) {
3750        // discard: counted as `sipx_transport::UnsentCounts::bye` — the number an operator asking
3751        // "why did that call linger" needs, because a BYE that does not reach the wire leaves a
3752        // dialog up at the far end that no timer reaps unless RFC 4028 session timers happen to be
3753        // running. The count is taken at the transmit and not from this `Result`, which reports
3754        // only that the transaction was created. Nothing here can retry it: this is the failure
3755        // path itself.
3756        let _ = endpoint.send(bye, in_dialog).await;
3757    }
3758}
3759
3760/// What a non-2xx final response means to the caller.
3761///
3762/// RFC 4028 §6's 422 is separated out because it is the one rejection that is *actionable*: it
3763/// names the interval the far end would accept, so a caller can retry with it rather than only
3764/// learn that it failed. Every other status is reported as it arrived.
3765fn rejection(response: &Response) -> Error {
3766    const INTERVAL_TOO_SMALL: u16 = 422;
3767    if response.status.code() == INTERVAL_TOO_SMALL
3768        && let Some(required) = required_interval(response)
3769    {
3770        return Error::IntervalTooBrief(required);
3771    }
3772    if matches!(response.status.code(), 401 | 407) {
3773        let from_proxy = response.status.code() == 407;
3774        let header = if from_proxy {
3775            HeaderName::ProxyAuthenticate
3776        } else {
3777            HeaderName::WwwAuthenticate
3778        };
3779        let challenges = response
3780            .headers
3781            .get_all(&header)
3782            .filter_map(|header| sipx_sip::auth::Challenge::parse(&header.value(), from_proxy))
3783            .collect();
3784        if let Some(challenge) = sipx_sip::auth::strongest(challenges) {
3785            return Error::AuthenticationChallenge {
3786                status: response.status.code(),
3787                reason: String::from_utf8_lossy(&response.reason).into_owned(),
3788                challenge: Box::new(challenge),
3789            };
3790        }
3791    }
3792    Error::Rejected {
3793        status: response.status.code(),
3794        reason: String::from_utf8_lossy(&response.reason).into_owned(),
3795    }
3796}
3797
3798#[allow(
3799    clippy::too_many_lines,
3800    reason = "the establishment sequence keeps every post-2xx path visibly ACK-safe"
3801)]
3802async fn dial_with(
3803    endpoint: &Handle,
3804    target: Target,
3805    to: &Uri,
3806    options: &DialOptions,
3807    identity: &Identity,
3808    authorization: Option<&Authorization<'_>>,
3809    cancelled: &mut Option<Cancelled<'_>>,
3810) -> Result<Call> {
3811    let media_address = options.media_address;
3812    let (port, capabilities, ice, keying, invite) =
3813        open_invitation(endpoint, &target, to, options, identity, authorization).await?;
3814
3815    let mut responses = endpoint.send(invite.clone(), target.clone()).await?;
3816
3817    let mut acknowledging = Acknowledging {
3818        endpoint,
3819        invite: &invite,
3820        target: &target,
3821        capabilities: &capabilities,
3822        seen: sipx_sip::rel::Sequence::default(),
3823    };
3824    let (response, ringing) = match await_final(
3825        &mut responses,
3826        options.timeout,
3827        &mut acknowledging,
3828        cancelled,
3829    )
3830    .await
3831    {
3832        Waited::Final { response, ringing } => (response, ringing),
3833        Waited::Gone => return Err(Error::NoResponse),
3834        Waited::Transport(error) => return Err(Error::Transport(error)),
3835        Waited::GaveUp => {
3836            withdraw(
3837                endpoint,
3838                &invite,
3839                target.clone(),
3840                &mut responses,
3841                &request_timeout_reason(),
3842            )
3843            .await;
3844            return Err(Error::Cancelled(options.timeout.unwrap_or(Duration::ZERO)));
3845        }
3846        Waited::Cancelled => {
3847            withdraw(
3848                endpoint,
3849                &invite,
3850                target.clone(),
3851                &mut responses,
3852                &normal_clearing_reason(),
3853            )
3854            .await;
3855            return Err(Error::Cancelled(Duration::ZERO));
3856        }
3857    };
3858
3859    if !response.status.is_success() {
3860        // A non-2xx is acknowledged by the transaction layer itself, so there is nothing to
3861        // send here — only a media port to release, which happens when `port` drops.
3862        return Err(rejection(&response));
3863    }
3864
3865    // From here the far end believes a dialog exists, so *every* path must acknowledge.
3866    // Returning an error without one leaves it retransmitting its 200 for 32 seconds and then
3867    // streaming media at a port we have closed.
3868    // Where in-dialog requests go if the 2xx carries no `Contact` to refresh the target with.
3869    let fallback = target.clone();
3870    match establish(
3871        &invite,
3872        &response,
3873        fallback,
3874        port,
3875        ice,
3876        &capabilities,
3877        options,
3878    ) {
3879        Ok((dialog, port, in_dialog, settled, ice, answer)) => {
3880            let ack = build_ack(endpoint, &dialog, &in_dialog)?;
3881            endpoint
3882                .send_directly(ack.clone(), in_dialog.clone())
3883                .await?;
3884            // The stream stays open rather than being dropped here: a retransmitted 2xx means
3885            // this ACK was lost and RFC 3261 §13.2.2.4 requires another (see
3886            // `reack_retransmitted_2xx`).
3887            tokio::spawn(reack_retransmitted_2xx(
3888                endpoint.clone(),
3889                responses,
3890                ack,
3891                in_dialog.clone(),
3892            ));
3893            // RFC 5763 peers are allowed to wait for the SIP exchange to complete before opening
3894            // the media connection. In particular, the ACK must be on the wire before a selected
3895            // DTLS handshake can wait for that peer, or two correct endpoints can deadlock.
3896            let (media, settled) = match key_and_start(
3897                port,
3898                ice,
3899                settled,
3900                keying,
3901                &answer,
3902                false,
3903                options.media.profile,
3904            )
3905            .await
3906            {
3907                Ok(started) => started,
3908                Err(error) => {
3909                    // The 2xx was already acknowledged, so RFC 3261 §15 tears down the dialog
3910                    // whose selected media path could not be keyed. The transport counts an
3911                    // unsent BYE; the result is discarded so it cannot mask the DTLS error.
3912                    if let Ok(bye) = bye_request(
3913                        &dialog,
3914                        dialog.local_cseq.saturating_add(1),
3915                        &normal_clearing_reason(),
3916                    ) {
3917                        // discard: the original DTLS failure is the cause returned to the
3918                        // caller; a best-effort teardown failure must not replace it.
3919                        let _ = endpoint.send(bye, in_dialog).await;
3920                    }
3921                    return Err(error);
3922                }
3923            };
3924            // Emitted at construction — the earliest point this call has a stream anyone could
3925            // read from — from what was actually observed while waiting for the final response,
3926            // not reconstructed later from anything left lying around.
3927            let (events, events_rx) = EventSink::new();
3928            emit_construction_events(&events, ringing);
3929            Ok(Call {
3930                dialog,
3931                initial_status: response.status.code(),
3932                media: Arc::new(media),
3933                retired_media: Vec::new(),
3934                endpoint: endpoint.clone(),
3935                target: in_dialog,
3936                ack_stop: None,
3937                ack_retransmission: None,
3938                delayed_offer: None,
3939                ended: false,
3940                media_address,
3941                media_bind_address: options.media_bind_address,
3942                codecs: options.media.codecs,
3943                profile: options.media.profile,
3944                current: settled.negotiated,
3945                peer_ice: peer_ice_credentials(response.body()),
3946                encrypted: options.media.profile == MediaProfile::BrowserAudio
3947                    || settled.srtp.is_some(),
3948                keying: options.media.keying,
3949                hold: Direction::SendRecv,
3950                referral: None,
3951                transfer: None,
3952                session: session::adopt(
3953                    response
3954                        .headers
3955                        .typed::<SessionExpires>()
3956                        .and_then(std::result::Result::ok),
3957                    options.session_expires,
3958                )
3959                .map(SessionState::armed),
3960                negotiation: update::Negotiation::idle(),
3961                // From the 2xx, which RFC 3311 §4 asks to carry it. A dialog that reaches here
3962                // has completed one offer/answer exchange, so nothing is outstanding either way.
3963                peer_allows_update: update::peer_allows(&response.headers),
3964                events,
3965                events_rx: Some(events_rx),
3966                history: HistoryInfo::from_headers(&response.headers)
3967                    .and_then(std::result::Result::ok),
3968                dialog_credentials: options.credentials.clone(),
3969                admitted_dialog_methods: Vec::new(),
3970            })
3971        }
3972        Err(error) => {
3973            // RFC 3261 §15: a UAC that cannot proceed after a 2xx acknowledges it and then
3974            // sends BYE. Walking away silently is what leaves the far end streaming.
3975            ack_then_bye(endpoint, &invite, &response, target).await;
3976            Err(error)
3977        }
3978    }
3979}
3980
3981/// Everything after a 2xx that can fail, kept together so the caller can ACK on either path.
3982///
3983/// `offered` and `options` are taken whole rather than as the two fields read out of them, because
3984/// both are the same question asked twice — what this side put in the offer — and a call site that
3985/// passed a crypto list from one place and a codec set from another could pass two that disagree.
3986fn establish(
3987    invite: &Request,
3988    response: &Response,
3989    fallback: Target,
3990    port: MediaPort,
3991    ice: Option<LocalDescription>,
3992    offered: &Capabilities,
3993    options: &DialOptions,
3994) -> Result<(
3995    Dialog,
3996    MediaPort,
3997    Target,
3998    Settled,
3999    Option<LocalDescription>,
4000    SessionDescription,
4001)> {
4002    let answer = sipx_sdp::parse(&String::from_utf8_lossy(response.body()))
4003        .map_err(|error| Error::Sdp(error.to_string()))?;
4004    validate_establishment_answer(options.media.profile, invite.body(), &answer)?;
4005    let settled = settle_answer(offered, &answer, options.media.codecs)?;
4006    let dialog = Dialog::from_response(invite, response).ok_or(Error::NoDialog)?;
4007    let target = in_dialog_target(&dialog, fallback);
4008    let ice = match ice {
4009        Some(mut local) => {
4010            let negotiation = answer
4011                .media
4012                .first()
4013                .map_or(IceNegotiation::Absent, |audio| {
4014                    sipx_media::ice::negotiate(&answer, audio)
4015                });
4016            local.accept(&negotiation);
4017            Some(local)
4018        }
4019        None => None,
4020    };
4021    Ok((dialog, port, target, settled, ice, answer))
4022}
4023
4024/// Hold the named profile boundary ahead of every stateful part of answer application.
4025///
4026/// The generic codec settlement below this function is intentionally more permissive than the
4027/// browser-audio contract. Running the complete exchange validator here keeps an invalid answer
4028/// from reaching `LocalDescription::accept`, ACK transmission, ICE checks, or DTLS setup.
4029fn validate_establishment_answer(
4030    profile: MediaProfile,
4031    offered: &[u8],
4032    answer: &SessionDescription,
4033) -> Result<()> {
4034    if profile == MediaProfile::BrowserAudio {
4035        let offered = sipx_sdp::parse(&String::from_utf8_lossy(offered))
4036            .map_err(|error| Error::Sdp(error.to_string()))?;
4037        // discard: the complete relation is checked here; the generic settlement immediately
4038        // below derives the retained codec/ICE facts from this same validated answer.
4039        let _ = sipx_sdp::browser_audio::validate_answer(
4040            &offered,
4041            answer,
4042            sipx_sdp::fingerprint::SetupCapabilities::both(),
4043        )?;
4044    }
4045    Ok(())
4046}
4047
4048/// What the far end's answer to *our* offer settles.
4049///
4050/// The calling side's counterpart of [`Early::settle`], and the reason it is a function is that
4051/// an answer can now reach us in two places: the 200 that [`establish`] reads, and — once
4052/// [`dial_early`] exists — the reliable provisional that makes an early dialog renegotiable at
4053/// all (RFC 3262 §5). There is no port to bind on either path, because ours was bound before the
4054/// INVITE named it.
4055fn settle_answer(
4056    offered: &Capabilities,
4057    answer: &SessionDescription,
4058    codecs: Codecs,
4059) -> Result<Settled> {
4060    // Both halves or neither, *and* the two halves have to be the ones the two ends agreed on:
4061    // a stream keyed at one end only is a call that connects and carries silence, and one keyed
4062    // on an answer that echoed a tag nobody sent is a call encrypted to nothing. Neither is
4063    // worth having, so both come back as `Error::Sdp` rather than as a quietly plain call.
4064    let answered = answered_crypto(answer);
4065    let mut negotiated = negotiated(answer, codecs)?;
4066    let local_offer = offer_from(offered);
4067    let local_audio = local_offer
4068        .media
4069        .iter()
4070        .find(|media| media.media == "audio" && !media.is_rejected())
4071        .ok_or(Error::NoCommonCodec)?;
4072    negotiated.receive_payload_type = local_audio
4073        .formats
4074        .iter()
4075        .find_map(|format| {
4076            let (codec, payload_type, clock_rate) = codec_of(local_audio, format)?;
4077            (codec == negotiated.codec && clock_rate == negotiated.clock_rate)
4078                .then_some(payload_type)
4079        })
4080        .ok_or(Error::NoCommonCodec)?;
4081    let answered_mux = answer
4082        .media
4083        .iter()
4084        .find(|media| media.media == "audio" && !media.is_rejected())
4085        .is_some_and(sipx_sdp::MediaDescription::rtcp_mux);
4086    negotiated.rtcp_mode = if offered.rtcp_mux && answered_mux {
4087        sipx_sdp::RtcpMode::Mux
4088    } else {
4089        sipx_sdp::RtcpMode::Separate
4090    };
4091    Ok(Settled {
4092        negotiated,
4093        srtp: srtp_keys(offered.crypto.as_slice(), answered.as_ref())?,
4094    })
4095}
4096
4097/// Resolve the local handshake role from the peer description using SDP's shared level fallback.
4098fn dtls_local_setup(
4099    peer_description: &SessionDescription,
4100    local_is_answerer: bool,
4101) -> Result<sipx_sdp::fingerprint::Setup> {
4102    let audio = peer_description.media.first().ok_or(Error::NoCommonCodec)?;
4103    let peer_setup = sipx_sdp::answer::setup_of(peer_description, audio);
4104    let roles = sipx_sdp::fingerprint::SetupCapabilities::both();
4105    if local_is_answerer {
4106        roles
4107            .answer_to(peer_setup.unwrap_or(sipx_sdp::fingerprint::Setup::ActPass))
4108            .map_err(Error::from)
4109    } else {
4110        roles.from_answer(peer_setup).map_err(Error::from)
4111    }
4112}
4113
4114/// Reject an unusable DTLS offer before binding or gathering for its answer.
4115fn validate_dtls_offer_setup(offer: &SessionDescription, policy: MediaPolicy) -> Result<()> {
4116    if policy.keying == Keying::DtlsSrtp {
4117        // discard: validation is the side effect; the selected role is resolved again when the
4118        // handshake starts, after the successful answer has been transmitted.
4119        let _ = dtls_local_setup(offer, true)?;
4120    }
4121    Ok(())
4122}
4123
4124/// Complete selected keying and only then start the media workers on the same bound port.
4125#[cfg_attr(not(feature = "dtls"), allow(unused_mut, clippy::unused_async))]
4126async fn key_and_start(
4127    port: MediaPort,
4128    ice: Option<LocalDescription>,
4129    mut settled: Settled,
4130    keying: PendingKeying,
4131    peer_description: &SessionDescription,
4132    local_is_answerer: bool,
4133    profile: MediaProfile,
4134) -> Result<(MediaSession, Settled)> {
4135    #[cfg(not(feature = "dtls"))]
4136    // discard: these inputs select DTLS roles only; the feature-off build has no such branch.
4137    let _ = (peer_description, local_is_answerer, profile);
4138    #[cfg(feature = "dtls")]
4139    if profile == MediaProfile::BrowserAudio {
4140        let remote_role = if local_is_answerer {
4141            sipx_sdp::browser_audio::BrowserAudioRole::Offerer
4142        } else {
4143            sipx_sdp::browser_audio::BrowserAudioRole::Answerer
4144        };
4145        let remote = sipx_sdp::browser_audio::validate(peer_description, remote_role)?;
4146        if !local_is_answerer
4147            && remote.payloads
4148                != (sipx_sdp::browser_audio::BrowserAudioPayloads {
4149                    opus: 111,
4150                    pcmu: 0,
4151                    pcma: 8,
4152                    comfort_noise: 13,
4153                    telephone_event: 101,
4154                })
4155        {
4156            return Err(sipx_sdp::browser_audio::ProfileError::CodecSetIncomplete.into());
4157        }
4158        let local = ice.ok_or(sipx_sdp::browser_audio::ProfileError::IceRequired)?;
4159        let PendingKeying::Dtls(identity) = keying else {
4160            return Err(sipx_sdp::browser_audio::ProfileError::WeakerMedia.into());
4161        };
4162        let local_setup = dtls_local_setup(peer_description, local_is_answerer)?;
4163        let role = match local_setup {
4164            sipx_sdp::fingerprint::Setup::Active => sipx_media::dtls::Role::Client,
4165            sipx_sdp::fingerprint::Setup::Passive => sipx_media::dtls::Role::Server,
4166            _ => return Err(sipx_sdp::browser_audio::ProfileError::SetupRole.into()),
4167        };
4168        let media = port
4169            .start_browser_audio(
4170                settled.media_config(),
4171                local,
4172                0,
4173                identity,
4174                role,
4175                remote.fingerprint,
4176                Duration::from_secs(5),
4177            )
4178            .await
4179            .map_err(browser_start_error)?;
4180        return Ok((media, settled));
4181    }
4182    match keying {
4183        PendingKeying::Sdes => {}
4184        #[cfg(feature = "dtls")]
4185        PendingKeying::Dtls(identity) => {
4186            let audio = peer_description.media.first().ok_or(Error::NoCommonCodec)?;
4187            let fingerprint = audio
4188                .fingerprint()
4189                .or_else(|| peer_description.fingerprint())
4190                .ok_or_else(|| Error::Sdp("the DTLS peer supplied no fingerprint".to_owned()))?;
4191            let local_setup = dtls_local_setup(peer_description, local_is_answerer)?;
4192            let role = match local_setup {
4193                sipx_sdp::fingerprint::Setup::Active => sipx_media::dtls::Role::Client,
4194                sipx_sdp::fingerprint::Setup::Passive => sipx_media::dtls::Role::Server,
4195                _ => {
4196                    return Err(Error::Sdp(
4197                        "the DTLS exchange did not select active or passive".to_owned(),
4198                    ));
4199                }
4200            };
4201            let remote = settled.negotiated.remote;
4202            let (keyed, keys) = port
4203                .key_with_dtls(identity, remote, role, fingerprint, Duration::from_secs(5))
4204                .await
4205                .map_err(|error| Error::Dtls(error.to_string()))?;
4206            settled.srtp = Some(keys);
4207            let media = match ice {
4208                Some(local) => keyed.start_with_ice(settled.media_config(), local)?,
4209                None => keyed.start(settled.media_config())?,
4210            };
4211            return Ok((media, settled));
4212        }
4213    }
4214    let media = match ice {
4215        Some(local) => port.start_with_ice(settled.media_config(), local)?,
4216        None => port.start(settled.media_config())?,
4217    };
4218    Ok((media, settled))
4219}
4220
4221#[cfg(feature = "dtls")]
4222fn browser_start_error(error: sipx_media::browser::BrowserStartError) -> Error {
4223    use sipx_media::browser::BrowserStartError;
4224    match error {
4225        BrowserStartError::IceFailed | BrowserStartError::IceStopped => {
4226            sipx_sdp::browser_audio::ProfileError::NoNominatedPair.into()
4227        }
4228        BrowserStartError::RtcpMuxRequired => {
4229            sipx_sdp::browser_audio::ProfileError::RtcpMuxRequired.into()
4230        }
4231        BrowserStartError::DtlsTimeout => sipx_sdp::browser_audio::ProfileError::DtlsTimeout.into(),
4232        BrowserStartError::Dtls(sipx_media::dtls::Error::FingerprintMismatch) => {
4233            sipx_sdp::browser_audio::ProfileError::FingerprintMismatch.into()
4234        }
4235        BrowserStartError::Dtls(sipx_media::dtls::Error::NoProfile) => {
4236            sipx_sdp::browser_audio::ProfileError::NoSrtpProfile.into()
4237        }
4238        BrowserStartError::Setup(error) => Error::Media(error),
4239        other => Error::Dtls(other.to_string()),
4240    }
4241}
4242
4243/// Answer an incoming INVITE.
4244///
4245/// The 200 OK is retransmitted until the ACK arrives, which is the transaction user's job:
4246/// `sipx-sip`'s server transaction moves to `Accepted` and absorbs retransmissions of the
4247/// *request*, but it does not resend the response. Over UDP one lost 200 means the caller
4248/// gives up while this side holds an established call, so this is not optional.
4249///
4250/// Answers from the default codec set, [`Codecs::G711`]. [`answer_with`] takes a selection.
4251pub async fn answer(endpoint: &Handle, incoming: &Incoming, media_address: IpAddr) -> Result<Call> {
4252    answer_at(endpoint, incoming, MediaAddress::new(media_address)).await
4253}
4254
4255/// [`answer`] with independent advertised and bound media addresses.
4256pub async fn answer_at(
4257    endpoint: &Handle,
4258    incoming: &Incoming,
4259    media_address: MediaAddress,
4260) -> Result<Call> {
4261    answer_tagged(
4262        endpoint,
4263        incoming,
4264        media_address,
4265        &token(),
4266        None,
4267        MediaPolicy::default(),
4268        &[],
4269    )
4270    .await
4271}
4272
4273/// [`answer`], from a chosen codec set rather than the default one (`M-30`).
4274///
4275/// The answering counterpart of [`DialOptions::with_codecs`]. `codecs` bounds what the answer may
4276/// settle on, and no more than that: RFC 3264 §6.1 gives the *order* to the offerer, so a caller
4277/// offering G.711 first is answered G.711 first even from [`Codecs::Opus`]. What the selection
4278/// decides is whether Opus is on the table at all — an offer of it answered from
4279/// [`Codecs::G711`] is answered G.711, because a call must never settle on a codec the
4280/// application did not ask to carry.
4281pub async fn answer_with(
4282    endpoint: &Handle,
4283    incoming: &Incoming,
4284    media_address: IpAddr,
4285    codecs: Codecs,
4286) -> Result<Call> {
4287    answer_with_policy_at(
4288        endpoint,
4289        incoming,
4290        MediaAddress::new(media_address),
4291        MediaPolicy::default().with_codecs(codecs),
4292    )
4293    .await
4294}
4295
4296/// Answer using one coherent codec, security and ICE policy.
4297pub async fn answer_with_policy(
4298    endpoint: &Handle,
4299    incoming: &Incoming,
4300    media_address: IpAddr,
4301    policy: MediaPolicy,
4302) -> Result<Call> {
4303    answer_with_policy_at(endpoint, incoming, MediaAddress::new(media_address), policy).await
4304}
4305
4306/// [`answer_with_policy`] with independent advertised and bound media addresses.
4307pub async fn answer_with_policy_at(
4308    endpoint: &Handle,
4309    incoming: &Incoming,
4310    media_address: MediaAddress,
4311    policy: MediaPolicy,
4312) -> Result<Call> {
4313    answer_tagged(
4314        endpoint,
4315        incoming,
4316        media_address,
4317        &token(),
4318        None,
4319        policy,
4320        &[],
4321    )
4322    .await
4323}
4324
4325/// Answer using one coherent media policy and validated application-owned response fields.
4326pub async fn answer_with_policy_and_headers(
4327    endpoint: &Handle,
4328    incoming: &Incoming,
4329    media_address: IpAddr,
4330    policy: MediaPolicy,
4331    headers: &[sipx_sip::Header],
4332) -> Result<Call> {
4333    answer_with_policy_and_headers_at(
4334        endpoint,
4335        incoming,
4336        MediaAddress::new(media_address),
4337        policy,
4338        headers,
4339    )
4340    .await
4341}
4342
4343/// [`answer_with_policy_and_headers`] with independent advertised and bound media addresses.
4344pub async fn answer_with_policy_and_headers_at(
4345    endpoint: &Handle,
4346    incoming: &Incoming,
4347    media_address: MediaAddress,
4348    policy: MediaPolicy,
4349    headers: &[sipx_sip::Header],
4350) -> Result<Call> {
4351    answer_tagged(
4352        endpoint,
4353        incoming,
4354        media_address,
4355        &token(),
4356        None,
4357        policy,
4358        headers,
4359    )
4360    .await
4361}
4362
4363/// The same, with the `To` tag chosen by the caller rather than freshly minted.
4364///
4365/// [`Invitation::answer`](crate::Invitation::answer) uses it so that every response this stack
4366/// sends about one invitation carries one tag — the `200` accepting it, and the `200` that
4367/// [`Dispatcher`](crate::Dispatcher) sends for a CANCEL that arrives too late to stop it. RFC 3261
4368/// §9.2 asks for exactly that agreement ("the `To` tag of the response to the CANCEL and the `To`
4369/// tag in the response to the original request SHOULD be the same"), and it can only be honoured
4370/// by whoever owns both, which is the invitation.
4371pub(crate) async fn answer_tagged(
4372    endpoint: &Handle,
4373    incoming: &Incoming,
4374    media_address: MediaAddress,
4375    tag: &str,
4376    claim: Option<Claim<'_>>,
4377    policy: MediaPolicy,
4378    headers: &[sipx_sip::Header],
4379) -> Result<Call> {
4380    // Ahead of the claim, deliberately: an offer that cannot be read fails here with nothing
4381    // sent, and an invitation that was never taken is one a CANCEL can still end.
4382    let offer = sipx_sdp::parse(&String::from_utf8_lossy(incoming.request.body()))
4383        .map_err(|error| Error::Sdp(error.to_string()))?;
4384    // No provisional was sent on this path, so there is nothing to report as `Ringing`.
4385    answer_negotiated(
4386        endpoint,
4387        incoming,
4388        media_address,
4389        offer,
4390        tag,
4391        None,
4392        claim,
4393        policy,
4394        headers,
4395    )
4396    .await
4397}
4398
4399/// The media an invitation has bound, and what the far end has said about it.
4400///
4401/// An enum rather than two `Option`s because exactly one is true at a time, and the difference
4402/// between them is the whole of RFC 3311 §5.1's precondition: a session that has been offered
4403/// and not answered may not be renegotiated, and one that has been answered may.
4404#[derive(Debug)]
4405enum EarlyMedia {
4406    /// Bound, and named in the INVITE's offer. The far end has not answered it yet.
4407    Offered(MediaPort),
4408    /// The INVITE carried no offer. A reliable provisional may supply one for this side to
4409    /// answer in PRACK (RFC 3262 section 5).
4410    WaitingForOffer,
4411    /// Answered in a reliable provisional (RFC 3262 §5), and renegotiable from here.
4412    Answered(Box<Early>),
4413}
4414
4415/// An invitation this side has placed, which the far end has not yet answered.
4416///
4417/// The calling side's counterpart of [`Ringing`](crate::Ringing), and the reason it is a separate
4418/// entry point is that [`dial`] cannot be both. `dial` waits for the final response inside
4419/// itself, which is what almost every application wants and is why its signature is unchanged;
4420/// but an application that wants to do anything *while* the far end rings has to hold the early
4421/// dialog, and before this there was no moment at which it could.
4422///
4423/// What it holds is what the eventual [`Call`] will need: the INVITE's still-open response
4424/// stream, the media port bound before the offer named it, and — once a provisional creates one —
4425/// the dialog itself. [`Self::answered`] hands all three over rather than rebuilding them, which
4426/// matters most for the dialog: its sequence space already carries the PRACK and any UPDATE sent
4427/// while ringing, and a dialog built afresh from the 2xx would restart that space at the INVITE's
4428/// own number, putting the first BYE behind a request the far end has already seen (RFC 3261
4429/// §12.2.1.1).
4430///
4431/// **Nothing happens on its own.** A `Dialing` dropped without [`Self::answered`] or
4432/// [`Self::cancel`] leaves the far end ringing, exactly as a [`Call`] dropped without
4433/// [`Call::hang_up`] leaves the far end in a call. The discipline is the application's: making it
4434/// implicit would mean withdrawing an invitation from a destructor that cannot await the CANCEL
4435/// it sends, nor the `200` that may cross it.
4436#[derive(Debug)]
4437pub struct Dialing {
4438    endpoint: Handle,
4439    /// The INVITE itself. A CANCEL must repeat its identity, and a PRACK its sequence number.
4440    invite: Request,
4441    /// Where the INVITE was sent, and the fallback for in-dialog requests.
4442    target: Target,
4443    /// Where in-dialog requests go, once a `Contact` has said somewhere better.
4444    in_dialog: Target,
4445    /// The INVITE transaction, still open. `None` once [`Self::answered`] has handed it to
4446    /// `reack_retransmitted_2xx`, which is the only other thing entitled to read from it.
4447    responses: Option<sipx_transport::Responses>,
4448    /// The early dialog a provisional established (RFC 3261 §12.1.1).
4449    dialog: Option<Dialog>,
4450    /// Which reliable provisionals have been acknowledged (RFC 3262 §4).
4451    seen: sipx_sip::rel::Sequence,
4452    /// `None` only after [`Self::answered`] has handed the port to the [`Call`].
4453    media: Option<EarlyMedia>,
4454    /// A delayed offer prepared for the target leg but not `PRACKed` until the source answers it.
4455    coupled_prack: Option<CoupledPrack>,
4456    /// A final response that crossed the held PRACK; confirmed only after that PRACK leaves.
4457    coupled_final: Option<Box<Response>>,
4458    /// The ICE agent gathered for the same port, retained until an answer supplies its peer half.
4459    ice: Option<LocalDescription>,
4460    /// What the INVITE offered, kept because an SRTP answer has to be paired with the offer it
4461    /// answers and because a later UPDATE offers from the same starting point.
4462    capabilities: Option<Capabilities>,
4463    negotiation: update::Negotiation,
4464    peer_allows_update: bool,
4465    /// The direction the last UPDATE from this side set, carried into the [`Call`] so that an
4466    /// invitation put on hold before it was answered is answered on hold.
4467    hold: Direction,
4468    /// Whether anything past a bare `100 Trying` arrived, and whether it was reliable — the
4469    /// same thing [`Waited::Final`] carries, and for the same reason.
4470    ringing: Option<bool>,
4471    /// When to stop waiting, counted from when the INVITE went out rather than from each call
4472    /// to [`Self::answered`] — the far end is ringing against one deadline, not a fresh one per
4473    /// method call.
4474    deadline: Option<tokio::time::Instant>,
4475    options: DialOptions,
4476    /// A final response that arrived before the application was handed anything.
4477    ///
4478    /// A far end that goes straight from the INVITE to a `200` never gives its caller an early
4479    /// dialog. That is not a failure — the call is perfectly good — so it is completed here and
4480    /// [`Self::answered`] hands it over at once. Completed rather than parked, because a `2xx`
4481    /// held while an application decides what to do with a handle is a `2xx` the far end is
4482    /// retransmitting (RFC 3261 §13.2.2.4).
4483    answered_already: Option<Box<Call>>,
4484    /// The event stream begins with this app-visible attempt, before a `Call` exists.
4485    events: Option<EventSink>,
4486    /// Handed out once by [`Self::events`], or moved into the confirmed [`Call`].
4487    events_rx: Option<CallEvents>,
4488}
4489
4490/// What one read from the INVITE transaction produced.
4491enum Arrived {
4492    /// A provisional response.
4493    Provisional(Box<Response>),
4494    /// A final response.
4495    Final(Box<Response>),
4496    /// The deadline passed.
4497    GaveUp,
4498    /// The transaction ended without a final response.
4499    Gone,
4500}
4501
4502/// One early-dial event surfaced to the owning two-dialog coupling.
4503pub(crate) enum CouplingDialEvent {
4504    /// A provisional was consumed and any required PRACK was sent.
4505    Progress,
4506    /// A reliable provisional carried an offer whose PRACK is held for the source leg's answer.
4507    ReliableOffer(Direction),
4508    /// The outbound invitation confirmed.
4509    Answered(Box<Call>),
4510    /// One routed in-dialog request arrived, or that route closed.
4511    Incoming(Box<Option<Incoming>>),
4512}
4513
4514#[derive(Debug)]
4515struct CoupledPrack {
4516    response: Box<Response>,
4517    rseq: u32,
4518    answer: SessionDescription,
4519}
4520
4521/// Place a call and get the early dialog, rather than waiting for the call itself.
4522///
4523/// [`dial`] and [`dial_once`] wait for the final response and hand back a [`Call`]; this hands
4524/// back a [`Dialing`] as soon as the far end has established a dialog, so the application can act
4525/// while it rings — renegotiate the session with an UPDATE (RFC 3311 §5.1), answer one, or read
4526/// the description a provisional carried. [`Dialing::answered`] then waits for the call exactly
4527/// as `dial` would have.
4528///
4529/// It returns as soon as *a dialog* exists, which is not the same as an answered session: a far
4530/// end that rings `180` with no body has established a dialog and described nothing.
4531/// [`Dialing::has_early_session`] is what distinguishes them, and it is what
4532/// [`Dialing::update`] requires.
4533///
4534/// Unlike [`dial`] there is no retry on a `422`. The retry is a *second* INVITE, and the handle
4535/// an application would be holding names the first; [`Error::IntervalTooBrief`] comes back from
4536/// [`Dialing::answered`] instead, as it does from [`dial_once`].
4537///
4538/// # Errors
4539///
4540/// Fails if the INVITE cannot be built or sent, if the deadline passes before any dialog is
4541/// established — in which case the invitation is withdrawn first, so the far end stops ringing —
4542/// or if the transaction ends with no response at all.
4543///
4544/// It also fails if a reliable provisional answers our offer with a description that cannot be
4545/// used: [`Error::Sdp`] for a body that does not parse or an `a=crypto` that fails RFC 4568
4546/// §5.1.3's check, [`Error::NoCommonCodec`] for one that cannot be negotiated. The invitation is
4547/// withdrawn with a CANCEL first (RFC 3261 §9.1), so no handle to a dead invitation comes back. A
4548/// provisional carrying *no* description is not this case and is not an error.
4549pub async fn dial_early(
4550    endpoint: &Handle,
4551    target: Target,
4552    to: &Uri,
4553    options: &DialOptions,
4554) -> Result<Dialing> {
4555    let mut dialing = begin_dial_early(endpoint, target, to, options).await?;
4556    dialing.reach_early_dialog().await?;
4557    Ok(dialing)
4558}
4559
4560/// Place a call until an early dialog exists or `cancelled` resolves.
4561///
4562/// This is the cancellation-safe early-dialog counterpart of [`dial_until`]. If cancellation
4563/// wins after the INVITE has left, the invitation is withdrawn before this returns, including
4564/// the ACK-then-BYE cleanup when a successful final response crossed the cancellation. A caller
4565/// that continues waiting for confirmation can use [`Dialing::answered_until`] with the same
4566/// cancellation signal.
4567///
4568/// # Errors
4569///
4570/// The same setup and early-dialog errors as [`dial_early`]. Local cancellation returns
4571/// [`Error::Cancelled`] after the outstanding invitation has been cleaned up.
4572pub async fn dial_early_until<F>(
4573    endpoint: &Handle,
4574    target: Target,
4575    to: &Uri,
4576    options: &DialOptions,
4577    cancelled: F,
4578) -> Result<Dialing>
4579where
4580    F: Future<Output = ()> + Send,
4581{
4582    let mut dialing = begin_dial_early(endpoint, target, to, options).await?;
4583    tokio::pin!(cancelled);
4584    tokio::select! {
4585        biased;
4586        () = cancelled.as_mut() => {
4587            dialing.give_up().await;
4588            Err(Error::Cancelled(Duration::ZERO))
4589        }
4590        result = dialing.reach_early_dialog() => {
4591            result?;
4592            Ok(dialing)
4593        }
4594    }
4595}
4596
4597async fn begin_dial_early(
4598    endpoint: &Handle,
4599    target: Target,
4600    to: &Uri,
4601    options: &DialOptions,
4602) -> Result<Dialing> {
4603    if options.media.keying == Keying::DtlsSrtp {
4604        return Err(Error::DtlsEarlyMedia);
4605    }
4606    let (port, capabilities, ice, _keying, invite) =
4607        open_invitation(endpoint, &target, to, options, &Identity::fresh(), None).await?;
4608    let responses = endpoint.send(invite.clone(), target.clone()).await?;
4609
4610    let (events, events_rx) = EventSink::new();
4611    Ok(Dialing {
4612        endpoint: endpoint.clone(),
4613        in_dialog: target.clone(),
4614        invite,
4615        target,
4616        responses: Some(responses),
4617        dialog: None,
4618        seen: sipx_sip::rel::Sequence::default(),
4619        media: Some(EarlyMedia::Offered(port)),
4620        coupled_prack: None,
4621        coupled_final: None,
4622        ice,
4623        capabilities: Some(capabilities),
4624        // RFC 3264: the INVITE carried our offer, so an exchange is open until the far end
4625        // answers it — which before the 200 can only happen in a reliable provisional.
4626        negotiation: update::Negotiation::offering(),
4627        peer_allows_update: false,
4628        hold: Direction::SendRecv,
4629        ringing: None,
4630        deadline: options
4631            .timeout
4632            .map(|limit| tokio::time::Instant::now() + limit),
4633        options: options.clone(),
4634        answered_already: None,
4635        events: Some(events),
4636        events_rx: Some(events_rx),
4637    })
4638}
4639
4640/// Place an offerless INVITE and answer an offer from a reliable provisional in PRACK.
4641///
4642/// This is RFC 3262 section 5's delayed-offer shape. An SDP-bearing reliable provisional is not
4643/// merely observed: it is answered on the same dialog sequence and the resulting media is retained
4644/// through confirmation. Like [`dial_early`], this function can return for a bodiless provisional;
4645/// [`Dialing::has_early_session`] distinguishes that case from a negotiated early session.
4646///
4647/// # Errors
4648///
4649/// The same transport, timeout and final-response errors as [`dial_early`], plus the SDP or media
4650/// error produced while answering the provisional offer. DTLS-SRTP is refused because its active
4651/// handshake cannot be started safely before the final response on this path.
4652pub async fn dial_early_without_offer(
4653    endpoint: &Handle,
4654    target: Target,
4655    to: &Uri,
4656    options: &DialOptions,
4657) -> Result<Dialing> {
4658    if options.media.keying == Keying::DtlsSrtp {
4659        return Err(Error::DtlsEarlyMedia);
4660    }
4661    let identity = Identity::fresh();
4662    let invite = open_offerless_invitation(endpoint, &target, to, options, &identity)?;
4663    let responses = endpoint.send(invite.clone(), target.clone()).await?;
4664    let (events, events_rx) = EventSink::new();
4665    let mut dialing = Dialing {
4666        endpoint: endpoint.clone(),
4667        in_dialog: target.clone(),
4668        invite,
4669        target,
4670        responses: Some(responses),
4671        dialog: None,
4672        seen: sipx_sip::rel::Sequence::default(),
4673        media: Some(EarlyMedia::WaitingForOffer),
4674        coupled_prack: None,
4675        coupled_final: None,
4676        ice: None,
4677        capabilities: None,
4678        negotiation: update::Negotiation::idle(),
4679        peer_allows_update: false,
4680        hold: Direction::SendRecv,
4681        ringing: None,
4682        deadline: options
4683            .timeout
4684            .map(|limit| tokio::time::Instant::now() + limit),
4685        options: options.clone(),
4686        answered_already: None,
4687        events: Some(events),
4688        events_rx: Some(events_rx),
4689    };
4690    dialing.reach_early_dialog().await?;
4691    Ok(dialing)
4692}
4693
4694pub(crate) async fn dial_early_without_offer_for_coupling(
4695    endpoint: &Handle,
4696    target: Target,
4697    to: &Uri,
4698    options: &DialOptions,
4699) -> Result<(Dialing, Option<Direction>)> {
4700    if options.media.keying == Keying::DtlsSrtp {
4701        return Err(Error::DtlsEarlyMedia);
4702    }
4703    let identity = Identity::fresh();
4704    let invite = open_offerless_invitation(endpoint, &target, to, options, &identity)?;
4705    let responses = endpoint.send(invite.clone(), target.clone()).await?;
4706    let (events, events_rx) = EventSink::new();
4707    let mut dialing = Dialing {
4708        endpoint: endpoint.clone(),
4709        in_dialog: target.clone(),
4710        invite,
4711        target,
4712        responses: Some(responses),
4713        dialog: None,
4714        seen: sipx_sip::rel::Sequence::default(),
4715        media: Some(EarlyMedia::WaitingForOffer),
4716        coupled_prack: None,
4717        coupled_final: None,
4718        ice: None,
4719        capabilities: None,
4720        negotiation: update::Negotiation::idle(),
4721        peer_allows_update: false,
4722        hold: Direction::SendRecv,
4723        ringing: None,
4724        deadline: options
4725            .timeout
4726            .map(|limit| tokio::time::Instant::now() + limit),
4727        options: options.clone(),
4728        answered_already: None,
4729        events: Some(events),
4730        events_rx: Some(events_rx),
4731    };
4732    let direction = dialing.reach_early_dialog_for_coupling().await?;
4733    Ok((dialing, direction))
4734}
4735
4736impl Dialing {
4737    /// The early dialog, once a provisional has established one (RFC 3261 §12.1.1).
4738    ///
4739    /// Exposed read-only because `C-2` will want to know *which* dialog a provisional's media
4740    /// belongs to — with forking, one invitation can produce several — without this handle
4741    /// having to guess in advance what it will be asked.
4742    #[must_use]
4743    pub fn dialog(&self) -> Option<&Dialog> {
4744        self.dialog
4745            .as_ref()
4746            .or_else(|| self.answered_already.as_ref().map(|call| &call.dialog))
4747    }
4748
4749    /// Whether the far end has answered this invitation's offer, in a reliable provisional.
4750    ///
4751    /// The precondition for [`Self::update`], and worth reading as the question RFC 3311 §5.1
4752    /// actually asks: not "is there a dialog" but "is there an offer/answer exchange still
4753    /// open". A `180` with no body establishes the first and does nothing about the second.
4754    #[must_use]
4755    pub fn has_early_session(&self) -> bool {
4756        matches!(self.media, Some(EarlyMedia::Answered(_)))
4757    }
4758
4759    /// The running early-media session, once a reliable provisional answered the INVITE offer.
4760    ///
4761    /// `None` for a bodiless provisional and before an answer arrives. When this is `Some`, the
4762    /// same session is moved into the [`Call`] returned by [`Self::answered`].
4763    #[must_use]
4764    pub fn media(&self) -> Option<&MediaSession> {
4765        match self.media.as_ref() {
4766            Some(EarlyMedia::Answered(early)) => Some(&early.media),
4767            _ => self.answered_already.as_ref().map(|call| call.media()),
4768        }
4769    }
4770
4771    /// This attempt's event stream, continuing on the confirmed call.
4772    ///
4773    /// Handed out once. A reliable provisional that starts media queues
4774    /// [`CallEvent::EarlyMediaStarted`] before this method can return it; the same receiver later
4775    /// observes [`CallEvent::Answered`] without being replaced at confirmation.
4776    pub fn events(&mut self) -> Option<CallEvents> {
4777        self.events_rx.take().or_else(|| {
4778            self.answered_already
4779                .as_mut()
4780                .and_then(|call| call.events())
4781        })
4782    }
4783
4784    /// Drive this invitation until early media starts or a final response arrives.
4785    ///
4786    /// [`dial_early`] returns on the first early dialog, which may be a bodiless `180`; a later
4787    /// reliable `183` can still answer the offer. This method keeps the handle in the
4788    /// application's ownership while reading through those later provisionals. `true` means
4789    /// [`Self::media`] is now available and [`CallEvent::EarlyMediaStarted`] has been emitted.
4790    /// `false` means the invitation reached a final response first; [`Self::answered`] then hands
4791    /// back the already-completed call (or its final error).
4792    ///
4793    /// # Errors
4794    ///
4795    /// The same provisional, final-refusal, timeout, cancellation, and transaction errors as
4796    /// [`Self::answered`]. `false` reports a successful final response with no early-media phase;
4797    /// the already-completed call is retained for [`Self::answered`].
4798    pub async fn wait_for_early_media(&mut self) -> Result<bool> {
4799        if self.has_early_session() {
4800            return Ok(true);
4801        }
4802        if self.answered_already.is_some() {
4803            return Ok(false);
4804        }
4805        loop {
4806            match self.next_response().await {
4807                Arrived::Provisional(response) => {
4808                    if let Err(error) = self.observe(&response).await {
4809                        return Err(self.abandon(error).await);
4810                    }
4811                    if self.has_early_session() {
4812                        return Ok(true);
4813                    }
4814                }
4815                Arrived::Final(response) => {
4816                    let call = self.confirm(*response).await?;
4817                    self.answered_already = Some(Box::new(call));
4818                    return Ok(false);
4819                }
4820                Arrived::GaveUp => {
4821                    self.give_up().await;
4822                    return Err(Error::Cancelled(
4823                        self.options.timeout.unwrap_or(Duration::ZERO),
4824                    ));
4825                }
4826                Arrived::Gone => return Err(Error::NoResponse),
4827            }
4828        }
4829    }
4830
4831    /// Whether the far end has said it accepts UPDATE (RFC 3311 §4).
4832    ///
4833    /// Advisory, not enforced: §4 says a UAS "SHOULD" list it, and refusing to send on a peer
4834    /// that merely omitted the header would fail calls that would have worked. Worth checking
4835    /// before [`Self::update`] if a `405` would be more expensive than not trying.
4836    #[must_use]
4837    pub fn peer_allows_update(&self) -> bool {
4838        self.peer_allows_update
4839    }
4840
4841    /// Renegotiate the early session from this side (RFC 3311 §5.1).
4842    ///
4843    /// One implementation of §5.1, shared with [`Ringing::update`](crate::Ringing::update): the
4844    /// RFC makes UPDATE something either end may send, so there is one body of rules and two
4845    /// callers rather than a copy per role.
4846    ///
4847    /// # Errors
4848    ///
4849    /// [`Error::NoEarlySession`] if the far end has not answered our offer yet
4850    /// ([`Self::has_early_session`]); [`Error::NoDialog`] if no provisional established one; and
4851    /// [`Error::Rejected`] if the far end refuses, including the `491` of an offer that crossed
4852    /// one of ours.
4853    pub async fn update(&mut self, direction: Direction) -> Result<()> {
4854        let Some(early) = self.early_dialog() else {
4855            return Err(Error::NoDialog);
4856        };
4857        crate::update::offer(early, direction).await?;
4858        self.hold = direction;
4859        Ok(())
4860    }
4861
4862    /// Answer an UPDATE that arrived in this early dialog (RFC 3311 §5.2).
4863    ///
4864    /// Returns whether it was one for this dialog, so an application with one inbox can offer
4865    /// everything it receives and act on what is left. The refusals are the same three the
4866    /// answering side gives, because they are the same code.
4867    ///
4868    /// # Errors
4869    ///
4870    /// Fails only if the response could not be built or sent. A *refusal* is a successful call:
4871    /// §5.2's 488 and 500 are responses this stack sends deliberately, not errors here.
4872    pub async fn on_update(&mut self, incoming: &Incoming) -> Result<bool> {
4873        let Some(early) = self.early_dialog() else {
4874            return Ok(false);
4875        };
4876        crate::update::receive(early, incoming).await
4877    }
4878
4879    /// Advance either the INVITE transaction or its routed early-dialog inbox once.
4880    ///
4881    /// Kept crate-private for [`crate::coupling::EarlyCoupling`]: unlike [`Self::answered`], this
4882    /// does not consume the dialing handle or hold it across every provisional. The coupling can
4883    /// therefore service UPDATEs from either pending leg and observe cancellation while the
4884    /// outbound final response is still outstanding.
4885    pub(crate) async fn coupling_step(
4886        &mut self,
4887        incoming: &mut tokio::sync::mpsc::Receiver<Incoming>,
4888    ) -> Result<CouplingDialEvent> {
4889        if let Some(call) = self.answered_already.take() {
4890            return Ok(CouplingDialEvent::Answered(call));
4891        }
4892        if self.coupled_prack.is_none()
4893            && let Some(response) = self.coupled_final.take()
4894        {
4895            return self
4896                .confirm(*response)
4897                .await
4898                .map(Box::new)
4899                .map(CouplingDialEvent::Answered);
4900        }
4901        tokio::select! {
4902            request = incoming.recv() => Ok(CouplingDialEvent::Incoming(Box::new(request))),
4903            arrived = self.next_response() => match arrived {
4904                Arrived::Provisional(response) => {
4905                    if self.is_coupled_offer_candidate(&response) {
4906                        return match self.stage_coupled_offer(*response).await {
4907                            Ok(direction) => Ok(CouplingDialEvent::ReliableOffer(direction)),
4908                            Err(error) => Err(self.abandon(error).await),
4909                        };
4910                    }
4911                    if let Err(error) = self.observe(&response).await {
4912                        return Err(self.abandon(error).await);
4913                    }
4914                    Ok(CouplingDialEvent::Progress)
4915                }
4916                Arrived::Final(response) => {
4917                    if self.coupled_prack.is_some() {
4918                        self.coupled_final = Some(response);
4919                        Ok(CouplingDialEvent::Progress)
4920                    } else {
4921                        self.confirm(*response)
4922                            .await
4923                            .map(Box::new)
4924                            .map(CouplingDialEvent::Answered)
4925                    }
4926                },
4927                Arrived::GaveUp => {
4928                    self.give_up().await;
4929                    Err(Error::Cancelled(
4930                        self.options.timeout.unwrap_or(Duration::ZERO),
4931                    ))
4932                }
4933                Arrived::Gone => Err(Error::NoResponse),
4934            }
4935        }
4936    }
4937
4938    pub(crate) async fn complete_coupled_prack(&mut self) -> Result<()> {
4939        let Some(pending) = self.coupled_prack.take() else {
4940            return Err(Error::NoDialog);
4941        };
4942        self.acknowledge(&pending.response, pending.rseq, Some(pending.answer))
4943            .await
4944    }
4945
4946    /// Wait for the invitation to be answered, and take the call it becomes.
4947    ///
4948    /// Consuming, because everything it needs moves into the [`Call`]. Provisionals that arrive
4949    /// while waiting are handled exactly as they were before it returned — `PRACK`ed, and read for
4950    /// the answer that makes the session renegotiable — so an application that calls this
4951    /// immediately is in the same position as one that had called [`dial`].
4952    ///
4953    /// # Errors
4954    ///
4955    /// [`Error::Rejected`] if the far end declined, [`Error::IntervalTooBrief`] for a `422`
4956    /// (see [`dial_early`] on why it is not retried), [`Error::Cancelled`] if the deadline
4957    /// passed — the invitation is withdrawn first — and [`Error::NoResponse`] if the
4958    /// transaction ended without a final response.
4959    ///
4960    /// And, from a *provisional* rather than from the answer: [`Error::Sdp`] or
4961    /// [`Error::NoCommonCodec`] if a reliable provisional answers our offer with a description
4962    /// that cannot be used (RFC 3262 §5). That one withdraws the invitation with a CANCEL (RFC
4963    /// 3261 §9.1) rather than waiting for a 2xx to fail on, because a far end that answered no
4964    /// offer of ours may never send one.
4965    pub async fn answered(mut self) -> Result<Call> {
4966        self.drive_answered(None).await
4967    }
4968
4969    /// Wait for confirmation until `cancelled` resolves.
4970    ///
4971    /// Cancellation withdraws the owned invitation before returning, including ACK-then-BYE
4972    /// cleanup for a successful final response already in flight. This closes the ownership gap
4973    /// between [`dial_early_until`] returning an early handle and the final answer arriving.
4974    ///
4975    /// # Errors
4976    ///
4977    /// The same errors as [`Self::answered`]. Local cancellation returns [`Error::Cancelled`]
4978    /// after cleanup completes.
4979    pub async fn answered_until<F>(mut self, cancelled: F) -> Result<Call>
4980    where
4981        F: Future<Output = ()> + Send,
4982    {
4983        tokio::pin!(cancelled);
4984        let cancelled: Pin<&mut (dyn Future<Output = ()> + Send)> = cancelled.as_mut();
4985        self.drive_answered(Some(cancelled)).await
4986    }
4987
4988    async fn drive_answered(&mut self, mut cancelled: Option<Cancelled<'_>>) -> Result<Call> {
4989        if let Some(call) = self.answered_already.take() {
4990            return Ok(*call);
4991        }
4992        loop {
4993            let arrived = match cancelled.as_mut() {
4994                None => self.next_response().await,
4995                Some(cancelled) => {
4996                    tokio::select! {
4997                        biased;
4998                        () = cancelled.as_mut() => {
4999                            self.give_up().await;
5000                            return Err(Error::Cancelled(Duration::ZERO));
5001                        }
5002                        arrived = self.next_response() => arrived,
5003                    }
5004                }
5005            };
5006            match arrived {
5007                Arrived::Provisional(response) => {
5008                    if let Err(error) = self.observe(&response).await {
5009                        return Err(self.abandon(error).await);
5010                    }
5011                }
5012                Arrived::Final(response) => return self.confirm(*response).await,
5013                Arrived::GaveUp => {
5014                    self.give_up().await;
5015                    return Err(Error::Cancelled(
5016                        self.options.timeout.unwrap_or(Duration::ZERO),
5017                    ));
5018                }
5019                Arrived::Gone => return Err(Error::NoResponse),
5020            }
5021        }
5022    }
5023
5024    /// Give up on the invitation, and make sure the far end stops ringing (RFC 3261 §9.1, §15).
5025    ///
5026    /// The counterpart of [`Self::answered`], and the reason both consume the handle. A `200`
5027    /// that crosses the CANCEL is acknowledged and then hung up, which §15 requires and a CANCEL
5028    /// cannot do on its own.
5029    pub async fn cancel(mut self) {
5030        self.give_up().await;
5031    }
5032
5033    /// Cancel this invitation with an explicit protocol cause.
5034    ///
5035    /// A SIP 200 reason represents the RFC 3326 §3.1 case where another coupled or forked leg
5036    /// completed the call; other valid SIP and Q.850 causes are retained unchanged.
5037    pub async fn cancel_with_reason(mut self, reason: ReasonValue) {
5038        self.give_up_with_reason(&reason).await;
5039    }
5040
5041    /// The early dialog's mutable parts, borrowed for one UPDATE.
5042    ///
5043    /// `None` before any provisional has established a dialog, which is a peer there is nothing
5044    /// to send an in-dialog request *to*.
5045    fn early_dialog(&mut self) -> Option<crate::update::EarlyDialog<'_>> {
5046        Some(crate::update::EarlyDialog {
5047            endpoint: &self.endpoint,
5048            dialog: self.dialog.as_mut()?,
5049            target: &mut self.in_dialog,
5050            negotiation: &mut self.negotiation,
5051            peer_allows: &mut self.peer_allows_update,
5052            early: match self.media.as_mut() {
5053                Some(EarlyMedia::Answered(early)) => Some(early),
5054                _ => None,
5055            },
5056        })
5057    }
5058
5059    /// Read responses until a dialog exists, or the invitation is over before one did.
5060    async fn reach_early_dialog(&mut self) -> Result<()> {
5061        loop {
5062            match self.next_response().await {
5063                Arrived::Provisional(response) => {
5064                    if let Err(error) = self.observe(&response).await {
5065                        return Err(self.abandon(error).await);
5066                    }
5067                    if self.dialog.is_some() {
5068                        return Ok(());
5069                    }
5070                }
5071                Arrived::Final(response) => {
5072                    let call = self.confirm(*response).await?;
5073                    self.answered_already = Some(Box::new(call));
5074                    return Ok(());
5075                }
5076                Arrived::GaveUp => {
5077                    self.give_up().await;
5078                    return Err(Error::Cancelled(
5079                        self.options.timeout.unwrap_or(Duration::ZERO),
5080                    ));
5081                }
5082                Arrived::Gone => return Err(Error::NoResponse),
5083            }
5084        }
5085    }
5086
5087    async fn reach_early_dialog_for_coupling(&mut self) -> Result<Option<Direction>> {
5088        loop {
5089            match self.next_response().await {
5090                Arrived::Provisional(response) => {
5091                    if self.is_coupled_offer_candidate(&response) {
5092                        return match self.stage_coupled_offer(*response).await {
5093                            Ok(direction) => Ok(Some(direction)),
5094                            Err(error) => Err(self.abandon(error).await),
5095                        };
5096                    }
5097                    if let Err(error) = self.observe(&response).await {
5098                        return Err(self.abandon(error).await);
5099                    }
5100                    if self.dialog.is_some() {
5101                        return Ok(None);
5102                    }
5103                }
5104                Arrived::Final(response) => {
5105                    let call = self.confirm(*response).await?;
5106                    self.answered_already = Some(Box::new(call));
5107                    return Ok(None);
5108                }
5109                Arrived::GaveUp => {
5110                    self.give_up().await;
5111                    return Err(Error::Cancelled(
5112                        self.options.timeout.unwrap_or(Duration::ZERO),
5113                    ));
5114                }
5115                Arrived::Gone => return Err(Error::NoResponse),
5116            }
5117        }
5118    }
5119
5120    /// One response from the INVITE transaction, bounded by the invitation's own deadline.
5121    async fn next_response(&mut self) -> Arrived {
5122        let deadline = self.deadline;
5123        let Some(responses) = self.responses.as_mut() else {
5124            return Arrived::Gone;
5125        };
5126        loop {
5127            let event = match deadline {
5128                None => responses.next().await,
5129                Some(deadline) => match tokio::time::timeout_at(deadline, responses.next()).await {
5130                    Ok(event) => event,
5131                    Err(_elapsed) => return Arrived::GaveUp,
5132                },
5133            };
5134            match event {
5135                Some(sipx_sip::transaction::TuEvent::Response(response)) => {
5136                    return if response.status.is_final() {
5137                        Arrived::Final(response)
5138                    } else {
5139                        Arrived::Provisional(response)
5140                    };
5141                }
5142                Some(_) => {}
5143                None => return Arrived::Gone,
5144            }
5145        }
5146    }
5147
5148    /// Fold a provisional into the early dialog: the dialog it may create, the answer it may
5149    /// carry, and the PRACK it may require.
5150    ///
5151    /// # Errors
5152    ///
5153    /// Whatever [`Self::adopt_early_answer`] refused. It is the only fatal thing a provisional can
5154    /// do: everything else here is either optional (a dialog it did not establish, an `Allow` it
5155    /// did not carry) or recoverable (a PRACK that did not get through).
5156    async fn observe(&mut self, response: &Response) -> Result<()> {
5157        if !self.observe_metadata(response) {
5158            return Ok(());
5159        }
5160        let reliable = crate::rel::reliable_sequence(response);
5161
5162        if let Some(rseq) = reliable {
5163            // RFC 3262 §5: an answer may only travel in a reliable provisional, so this is the
5164            // only place before the 200 where our INVITE's offer can be closed out. An
5165            // unreliable provisional carrying a description is not one — §5 forbids it, and one
5166            // lost leaves the two sides disagreeing about what is in force with no way to
5167            // notice — so it is ignored rather than adopted.
5168            //
5169            // Held rather than propagated on the spot: the provisional is acknowledged first even
5170            // when its description is refused. RFC 3262 §4 makes the PRACK a MUST for every
5171            // reliable provisional a UAC receives, and the far end retransmits until one arrives;
5172            // failing a beat earlier would leave it resending a response into a CANCEL that has
5173            // already gone.
5174            let adopted = if matches!(self.media, Some(EarlyMedia::WaitingForOffer))
5175                && !response.body().is_empty()
5176            {
5177                self.adopt_early_offer(response).await.map(Some)
5178            } else {
5179                self.adopt_early_answer(response).map(|()| None)
5180            };
5181            if let Some(dialog) = self.dialog.as_mut() {
5182                dialog.refresh_target(&response.headers);
5183            }
5184            // A failure is logged rather than fatal, for `await_final`'s reason: the invitation
5185            // is still running, and abandoning a ringing call because one PRACK did not get
5186            // through is a worse outcome than the unreliability it was fixing.
5187            let prack_answer = adopted.as_ref().ok().and_then(Clone::clone);
5188            if let Err(error) = self.acknowledge(response, rseq, prack_answer).await {
5189                tracing::debug!(%error, "could not acknowledge a reliable provisional");
5190            }
5191            adopted?;
5192        }
5193        Ok(())
5194    }
5195
5196    fn is_coupled_offer_candidate(&self, response: &Response) -> bool {
5197        matches!(self.media, Some(EarlyMedia::WaitingForOffer))
5198            && !response.body().is_empty()
5199            && crate::rel::reliable_sequence(response).is_some()
5200    }
5201
5202    async fn stage_coupled_offer(&mut self, response: Response) -> Result<Direction> {
5203        if !self.observe_metadata(&response) {
5204            return Err(Error::NoDialog);
5205        }
5206        let rseq = crate::rel::reliable_sequence(&response).ok_or(Error::NoDialog)?;
5207        let offer = sipx_sdp::parse(&String::from_utf8_lossy(response.body()))
5208            .map_err(|error| Error::Sdp(error.to_string()))?;
5209        let direction = offer
5210            .media
5211            .iter()
5212            .find(|media| media.media == "audio" && !media.is_rejected())
5213            .map(sipx_sdp::MediaDescription::direction)
5214            .ok_or_else(|| {
5215                Error::Sdp("the reliable provisional carried no usable audio offer".to_owned())
5216            })?;
5217        let answer = self.adopt_early_offer(&response).await?;
5218        if let Some(dialog) = self.dialog.as_mut() {
5219            dialog.refresh_target(&response.headers);
5220        }
5221        self.coupled_prack = Some(CoupledPrack {
5222            response: Box::new(response),
5223            rseq,
5224            answer,
5225        });
5226        Ok(direction)
5227    }
5228
5229    fn observe_metadata(&mut self, response: &Response) -> bool {
5230        const TRYING: u16 = 100;
5231
5232        let reliable = crate::rel::reliable_sequence(response);
5233        if response.status.code() > TRYING && self.ringing.is_none() {
5234            let is_reliable = reliable.is_some();
5235            self.ringing = Some(is_reliable);
5236            if let Some(events) = self.events.as_ref() {
5237                events.emit(CallEvent::Ringing {
5238                    reliable: is_reliable,
5239                });
5240            }
5241        }
5242
5243        if self.dialog.is_none() {
5244            if let Some(dialog) = Dialog::from_response(&self.invite, response) {
5245                self.in_dialog = in_dialog_target(&dialog, self.target.clone());
5246                self.dialog = Some(dialog);
5247            }
5248        } else if !self.belongs(response) {
5249            return false;
5250        }
5251
5252        if update::peer_allows(&response.headers) {
5253            self.peer_allows_update = true;
5254        }
5255        true
5256    }
5257
5258    /// Answer a reliable provisional's offer for an offerless INVITE.
5259    async fn adopt_early_offer(&mut self, response: &Response) -> Result<SessionDescription> {
5260        if response.body().is_empty() {
5261            return Err(Error::Sdp(
5262                "an offerless INVITE received a reliable provisional with no offer".to_owned(),
5263            ));
5264        }
5265        let offer = sipx_sdp::parse(&String::from_utf8_lossy(response.body()))
5266            .map_err(|error| Error::Sdp(error.to_string()))?;
5267        let (early, answer) = Early::settle(
5268            MediaAddress::new(self.options.media_address)
5269                .with_bind(self.options.media_bind_address),
5270            self.target.transport.is_secure(),
5271            &offer,
5272            self.options.media,
5273        )
5274        .await?;
5275        self.media = Some(EarlyMedia::Answered(Box::new(early)));
5276        self.negotiation.sent_answer();
5277        if let Some(events) = self.events.as_ref() {
5278            events.emit(CallEvent::EarlyMediaStarted);
5279        }
5280        Ok(answer)
5281    }
5282
5283    /// Whether a response belongs to the dialog this handle holds.
5284    fn belongs(&self, response: &Response) -> bool {
5285        self.dialog.as_ref().is_none_or(|dialog| {
5286            Dialog::from_response(&self.invite, response)
5287                .is_none_or(|fresh| fresh.id.remote_tag == dialog.id.remote_tag)
5288        })
5289    }
5290
5291    /// Take the answer to our INVITE's offer out of a reliable provisional (RFC 3262 §5).
5292    ///
5293    /// This is what makes the early dialog renegotiable at all, and it is the calling side's
5294    /// mirror of [`ring_early`](crate::ring_early).
5295    ///
5296    /// `Ok(())` covers two shapes, and the difference matters. A provisional that carries **no
5297    /// description** is ordinary — a `180` establishes a dialog and answers nothing — and one that
5298    /// arrives after the exchange has already closed is a repeat of an answer we took the first
5299    /// time. Neither is a failure and neither is reported.
5300    ///
5301    /// # Errors
5302    ///
5303    /// A description that *is* there and cannot be used: [`Error::Sdp`] for a body that does not
5304    /// parse or an `a=crypto` that fails RFC 4568 §5.1.3's check, [`Error::NoCommonCodec`] for one
5305    /// that cannot be negotiated. Before `S-25` all three returned `()` and left a `debug` line,
5306    /// so they were indistinguishable from each other and from the silent cases above — and for a
5307    /// caller that never receives a 2xx, indistinguishable from nothing having happened.
5308    ///
5309    /// The session is left where it was on either path — still `Offered`, so the exchange stays
5310    /// open and [`Self::update`] keeps refusing, which is the truthful state. What changes is that
5311    /// the refusal now reaches [`Self::observe`], which withdraws the invitation over it.
5312    fn adopt_early_answer(&mut self, response: &Response) -> Result<()> {
5313        if !matches!(self.media, Some(EarlyMedia::Offered(_))) || response.body().is_empty() {
5314            return Ok(());
5315        }
5316        // Parsed and settled *before* the port is moved out, so that a failure on either step
5317        // leaves `media` exactly as it was rather than emptied.
5318        let answer = sipx_sdp::parse(&String::from_utf8_lossy(response.body()))
5319            .map_err(|error| Error::Sdp(error.to_string()))?;
5320        // The same vocabulary the 2xx path uses: `settle_from` runs this exact function on the
5321        // final response, and a refusal that arrived early is the same refusal. Naming it
5322        // differently here would ask an application to match on two errors for one fault.
5323        //
5324        // `M-30` added the selected codec set to this call. It widens what can be refused here:
5325        // an early answer naming a codec outside the set now fails where it previously could
5326        // not, and `S-25` turns that failure into a CANCEL. Our own offer only names codecs in
5327        // the set, so a conformant answer cannot trip it — an answer that does is naming
5328        // something we never offered, which is exactly what `S-25` exists to refuse rather than
5329        // hang on.
5330        let Some(capabilities) = self.capabilities.clone() else {
5331            return Err(Error::NoDialog);
5332        };
5333        let settled = settle_answer(&capabilities, &answer, self.options.media.codecs)?;
5334        self.accept_remote_ice(&answer);
5335        let Some(EarlyMedia::Offered(port)) = self.media.take() else {
5336            return Ok(());
5337        };
5338        let media = match self.ice.take() {
5339            Some(local) => port.start_with_ice(settled.media_config(), local)?,
5340            None => port.start(settled.media_config())?,
5341        };
5342        self.media = Some(EarlyMedia::Answered(Box::new(Early {
5343            media,
5344            capabilities,
5345            settled,
5346            media_address: self.options.media_address,
5347            media_bind_address: self.options.media_bind_address,
5348            codecs: self.options.media.codecs,
5349            keying: self.options.media.keying,
5350        })));
5351        self.negotiation.received_answer();
5352        if let Some(events) = self.events.as_ref() {
5353            events.emit(CallEvent::EarlyMediaStarted);
5354        }
5355        Ok(())
5356    }
5357
5358    /// Withdraw the invitation because a reliable provisional's description cannot be used.
5359    ///
5360    /// **CANCEL, not ACK-then-BYE** — this is the failure mode `S-25` had to choose, and it is
5361    /// chosen by where we are rather than by what went wrong. RFC 3261 §9.1 withdraws an
5362    /// invitation that has only been answered provisionally; there is no final response here to
5363    /// acknowledge, so the ACK-then-BYE that [`Self::confirm`] performs after a 2xx (§15) has
5364    /// nothing to attach itself to. [`Self::give_up`] is already that request, sent through
5365    /// [`withdraw`], which also covers the one case a CANCEL cannot: a `200` that crossed it is
5366    /// acknowledged and hung up, because by then §15 *does* apply.
5367    ///
5368    /// The alternative considered and rejected was to carry on and let the 2xx fail — which is
5369    /// what happened before this story. It fails safely (nothing is keyed on a refused answer,
5370    /// and `settle_from` re-runs the same check) but it is not a report: a far end that answers
5371    /// no offer of ours will not send a 2xx to fail on, and the caller's only outcome was
5372    /// [`Error::Cancelled`] when its own deadline passed.
5373    async fn abandon(&mut self, error: Error) -> Error {
5374        self.give_up().await;
5375        error
5376    }
5377
5378    /// PRACK a reliable provisional through the early dialog itself (RFC 3262 §4).
5379    ///
5380    /// Through *the* dialog, not a copy of it. The PRACK is an in-dialog request and takes the
5381    /// next number in this side's own sequence space (RFC 3261 §12.2.1.1); the `dial` path
5382    /// builds a throwaway `Dialog` per acknowledgement because it keeps none, which restarts
5383    /// that space at the INVITE's number every time. Here an UPDATE may follow, and it would
5384    /// then reuse the PRACK's number.
5385    async fn acknowledge(
5386        &mut self,
5387        response: &Response,
5388        rseq: u32,
5389        answer: Option<SessionDescription>,
5390    ) -> Result<()> {
5391        // §4: out of order means an earlier one is missing, and a duplicate has already been
5392        // acknowledged. Neither is PRACKed.
5393        if self.seen.accept(rseq) != sipx_sip::rel::Received::Acknowledge {
5394            return Ok(());
5395        }
5396        let dialog = self.dialog.as_mut().ok_or(Error::NoDialog)?;
5397        let invite_cseq = self
5398            .invite
5399            .headers
5400            .typed::<sipx_sip::CSeq>()
5401            .and_then(std::result::Result::ok)
5402            .map_or(1, |cseq| cseq.sequence);
5403        let body = match answer {
5404            Some(answer) => Some(answer),
5405            None => self.capabilities.as_ref().and_then(|capabilities| {
5406                crate::rel::prack_body(
5407                    !self.invite.body().is_empty(),
5408                    response.body(),
5409                    capabilities,
5410                )
5411            }),
5412        };
5413        crate::rel::send_prack(
5414            &self.endpoint,
5415            dialog,
5416            &self.in_dialog,
5417            rseq,
5418            invite_cseq,
5419            body,
5420        )
5421        .await
5422    }
5423
5424    /// Turn a final response into a [`Call`], or into the error it describes.
5425    async fn confirm(&mut self, response: Response) -> Result<Call> {
5426        if !response.status.is_success() {
5427            // A non-2xx is acknowledged by the transaction layer itself, so there is nothing to
5428            // send here — only a media port to release, which happens when this is dropped.
5429            return Err(rejection(&response));
5430        }
5431
5432        // From here the far end believes a dialog exists, so *every* path must acknowledge.
5433        // Returning an error without one leaves it retransmitting its 200 for 32 seconds and
5434        // then streaming media at a port we have closed.
5435        match self.accept(&response) {
5436            Ok((dialog, media, settled)) => {
5437                let ack = build_ack(&self.endpoint, &dialog, &self.in_dialog)?;
5438                self.endpoint
5439                    .send_directly(ack.clone(), self.in_dialog.clone())
5440                    .await?;
5441                // The stream stays open rather than being dropped: a retransmitted 2xx means
5442                // this ACK was lost and RFC 3261 §13.2.2.4 requires another.
5443                if let Some(responses) = self.responses.take() {
5444                    tokio::spawn(reack_retransmitted_2xx(
5445                        self.endpoint.clone(),
5446                        responses,
5447                        ack,
5448                        self.in_dialog.clone(),
5449                    ));
5450                }
5451                let Some(events) = self.events.take() else {
5452                    return Err(Error::NoDialog);
5453                };
5454                events.emit(CallEvent::Answered);
5455                let events_rx = self.events_rx.take();
5456                Ok(Call {
5457                    dialog,
5458                    initial_status: response.status.code(),
5459                    media: Arc::new(media),
5460                    retired_media: Vec::new(),
5461                    endpoint: self.endpoint.clone(),
5462                    target: self.in_dialog.clone(),
5463                    ack_stop: None,
5464                    ack_retransmission: None,
5465                    delayed_offer: None,
5466                    ended: false,
5467                    media_address: self.options.media_address,
5468                    media_bind_address: self.options.media_bind_address,
5469                    codecs: self.options.media.codecs,
5470                    profile: self.options.media.profile,
5471                    current: settled.negotiated,
5472                    peer_ice: peer_ice_credentials(response.body()),
5473                    encrypted: self.options.media.profile == MediaProfile::BrowserAudio
5474                        || settled.srtp.is_some(),
5475                    keying: self.options.media.keying,
5476                    hold: self.hold,
5477                    referral: None,
5478                    transfer: None,
5479                    session: session::adopt(
5480                        response
5481                            .headers
5482                            .typed::<SessionExpires>()
5483                            .and_then(std::result::Result::ok),
5484                        self.options.session_expires,
5485                    )
5486                    .map(SessionState::armed),
5487                    negotiation: self.negotiation,
5488                    peer_allows_update: self.peer_allows_update
5489                        || update::peer_allows(&response.headers),
5490                    events,
5491                    events_rx,
5492                    history: HistoryInfo::from_headers(&response.headers)
5493                        .and_then(std::result::Result::ok),
5494                    dialog_credentials: self.options.credentials.clone(),
5495                    admitted_dialog_methods: Vec::new(),
5496                })
5497            }
5498            Err(error) => {
5499                // RFC 3261 §15: a UAC that cannot proceed after a 2xx acknowledges it and then
5500                // sends BYE. Walking away silently is what leaves the far end streaming.
5501                let dialog = self
5502                    .dialog
5503                    .take()
5504                    .or_else(|| Dialog::from_response(&self.invite, &response));
5505                if let Some(dialog) = dialog {
5506                    let in_dialog = in_dialog_target(&dialog, self.target.clone());
5507                    // discard: counted as `sipx_transport::UnsentCounts::ack`, exactly as in
5508                    // `ack_then_bye` — the same two sends on the same failing path, written inline
5509                    // here only because this one already holds the dialog. Via `send_directly`, so
5510                    // this `Result` does report the transmit.
5511                    let _ = send_ack(&self.endpoint, &dialog, in_dialog.clone()).await;
5512                    if let Ok(bye) = bye_request(
5513                        &dialog,
5514                        dialog.local_cseq.saturating_add(1),
5515                        &normal_clearing_reason(),
5516                    ) {
5517                        // discard: counted as `sipx_transport::UnsentCounts::bye`, at the
5518                        // transmit rather than from this `Result`, which reports only that the
5519                        // transaction was created. It is dropped so that the error which brought
5520                        // us into this branch is the one the caller is given, rather than being
5521                        // masked by a teardown failure.
5522                        let _ = self.endpoint.send(bye, in_dialog).await;
5523                    }
5524                }
5525                Err(error)
5526            }
5527        }
5528    }
5529
5530    /// Everything after a 2xx that can fail, kept together so [`Self::confirm`] can ACK either way.
5531    ///
5532    /// [`establish`] is the same step for [`dial`], and this is not it: nothing here is rebuilt.
5533    /// The dialog is the one the provisional created, and when the early session was already
5534    /// answered the description in force is what *it* settled — including anything an UPDATE has
5535    /// changed since.
5536    fn accept(&mut self, response: &Response) -> Result<(Dialog, MediaSession, Settled)> {
5537        // A 2xx bearing a different `To` tag is a different dialog — a forked branch won — and
5538        // the early one it did not confirm has nothing to contribute to it.
5539        let confirms_early = self.belongs(response);
5540        let fresh = Dialog::from_response(&self.invite, response);
5541        let mut dialog = match (confirms_early, self.dialog.take(), fresh) {
5542            // The early dialog, its sequence space intact. Two ways to get here and one answer:
5543            // the usual one, where the 2xx confirms what the provisional established; and a 2xx
5544            // carrying no usable `To` tag or `Contact`, which establishes no dialog of its own
5545            // but does not unmake the one a provisional already did.
5546            (true, Some(early), _) | (_, Some(early), None) => early,
5547            // A forked branch won: this 2xx names a dialog that is not the early one.
5548            (_, _, Some(fresh)) => fresh,
5549            (_, None, None) => return Err(Error::NoDialog),
5550        };
5551        dialog.refresh_target(&response.headers);
5552        self.in_dialog = in_dialog_target(&dialog, self.target.clone());
5553
5554        let (media, settled) = match self.media.take() {
5555            // The answer arrived in a provisional, and any UPDATE since settled its own. So the
5556            // 2xx's body is *not* read: at this point it can only be a repeat of the answer or,
5557            // worse, a description that undoes the renegotiation. `answer_early` sends no body
5558            // in this exact case, and for the same reason.
5559            Some(EarlyMedia::Answered(early)) if confirms_early => (early.media, early.settled),
5560            Some(EarlyMedia::Answered(early)) => {
5561                // This 2xx confirmed a different fork from the early dialog the handle names.
5562                // Never attach the losing branch's running stream to the winner. Multi-branch
5563                // selection is application policy; until this handle can represent both, the
5564                // honest outcome is to tear the loser down and ACK-then-BYE the unrepresented
5565                // winner through `confirm`'s error path.
5566                drop(early);
5567                return Err(Error::NoDialog);
5568            }
5569            Some(EarlyMedia::Offered(port)) => {
5570                let settled = self.settle_from(response)?;
5571                let media = match self.ice.take() {
5572                    Some(local) => port.start_with_ice(settled.media_config(), local)?,
5573                    None => port.start(settled.media_config())?,
5574                };
5575                (media, settled)
5576            }
5577            Some(EarlyMedia::WaitingForOffer) => return Err(Error::NoEarlySession),
5578            None => return Err(Error::NoDialog),
5579        };
5580        Ok((dialog, media, settled))
5581    }
5582
5583    /// Read the answer out of the 2xx, for the case where no provisional carried one.
5584    fn settle_from(&mut self, response: &Response) -> Result<Settled> {
5585        let answer = sipx_sdp::parse(&String::from_utf8_lossy(response.body()))
5586            .map_err(|error| Error::Sdp(error.to_string()))?;
5587        let Some(capabilities) = self.capabilities.as_ref() else {
5588            return Err(Error::NoEarlySession);
5589        };
5590        let settled = settle_answer(capabilities, &answer, self.options.media.codecs)?;
5591        self.accept_remote_ice(&answer);
5592        // Our INVITE's offer is answered here rather than in a provisional, so the exchange
5593        // closes now. Without this the first UPDATE on the confirmed call would be refused as
5594        // glare against an offer that has in fact been answered.
5595        self.negotiation.received_answer();
5596        Ok(settled)
5597    }
5598
5599    /// Give the gathered agent the answer to the offer that created it.
5600    fn accept_remote_ice(&mut self, answer: &SessionDescription) {
5601        let negotiation = answer
5602            .media
5603            .first()
5604            .map_or(IceNegotiation::Absent, |audio| {
5605                sipx_media::ice::negotiate(answer, audio)
5606            });
5607        if let Some(local) = self.ice.as_mut() {
5608            local.accept(&negotiation);
5609        }
5610    }
5611
5612    /// Take back the invitation, whatever state it is in.
5613    async fn give_up(&mut self) {
5614        self.give_up_with_reason(&normal_clearing_reason()).await;
5615    }
5616
5617    async fn give_up_with_reason(&mut self, reason: &ReasonValue) {
5618        if let Some(response) = self.coupled_final.take() {
5619            if response.status.is_success() {
5620                ack_then_bye(&self.endpoint, &self.invite, &response, self.target.clone()).await;
5621            }
5622            return;
5623        }
5624        let Some(responses) = self.responses.as_mut() else {
5625            return;
5626        };
5627        withdraw(
5628            &self.endpoint,
5629            &self.invite,
5630            self.target.clone(),
5631            responses,
5632            reason,
5633        )
5634        .await;
5635    }
5636}
5637
5638/// The `200` that carries the answer, with the session-timer headers the negotiation settled on.
5639///
5640/// `agreed` is [`negotiate_session`]'s outcome: `None` when neither side asked for RFC 4028's
5641/// refresh, and otherwise the interval and refresher this answer commits to. `Require: timer` goes
5642/// on only when the negotiation said so — a 2xx that requires an extension the offer did not
5643/// support is a call the caller must reject.
5644///
5645/// # Errors
5646///
5647/// Returns [`Error`] when a header value cannot be built.
5648fn ok_with_answer(
5649    endpoint: &Handle,
5650    incoming: &Incoming,
5651    to_with_tag: &str,
5652    answer: &SessionDescription,
5653    agreed: Option<session::Accepted>,
5654    headers: &[sipx_sip::Header],
5655) -> Result<Response> {
5656    let mut response = ResponseBuilder::to_request(&incoming.request, ok_status(), "OK")?
5657        .set_header(&HeaderName::To, Bytes::from(to_with_tag.to_owned()))?
5658        .header(
5659            HeaderName::Contact,
5660            Bytes::from(contact_for(endpoint, incoming.transport)),
5661        )?
5662        // RFC 3311 §4: the 2xx "SHOULD contain an Allow header field listing the UPDATE
5663        // method". This is where a UAC learns it, and RFC 4028 §7.4 then reads it to decide
5664        // whether a session refresh may be an UPDATE.
5665        .header(
5666            HeaderName::Allow,
5667            Bytes::from_static(update::ALLOW.as_bytes()),
5668        )?
5669        .header(
5670            HeaderName::ContentType,
5671            Bytes::from_static(b"application/sdp"),
5672        )?
5673        .body(Bytes::from(answer.to_string_sdp()));
5674
5675    if let Some(accepted) = agreed {
5676        let expires = SessionExpires {
5677            interval: accepted.interval,
5678            refresher: Some(accepted.refresher),
5679        };
5680        response = response
5681            .header(HeaderName::SessionExpires, Bytes::from(expires.to_string()))?
5682            .header(HeaderName::Supported, Bytes::from_static(b"timer"))?;
5683        if accepted.require {
5684            response = response.header(HeaderName::Require, Bytes::from_static(b"timer"))?;
5685        }
5686    }
5687    Ok(add_response_headers(response, headers)?.build())
5688}
5689
5690fn add_response_headers(
5691    mut response: ResponseBuilder,
5692    headers: &[sipx_sip::Header],
5693) -> std::result::Result<ResponseBuilder, sipx_sip::error::BuildError> {
5694    for header in headers {
5695        response = response.header(
5696            header.name().clone(),
5697            Bytes::copy_from_slice(header.raw_value()),
5698        )?;
5699    }
5700    Ok(response)
5701}
5702
5703/// Read the offer's ICE half and gather for the answer if the policy selects it (`ice.md` §13.4).
5704///
5705/// One function for the three answering paths — the free answer functions, the dispatcher's
5706/// invitation and [`Early::settle`] — because "when does an answerer gather?" is one rule and
5707/// three copies of it are three chances to disagree. Two of them already did: one asked
5708/// `matches!(.., Ice { .. })` and one asked [`IceNegotiation::runs_ice`], which are the same
5709/// question spelled two ways until one of them acquires a case the other lacks.
5710///
5711/// Gathering is deliberately *after* reading the peer's half. A policy selecting ICE never means
5712/// "require the peer to implement it" (`ice.md` §13.4), so an offer carrying no candidate costs no
5713/// gathering, no STUN transaction and no timer.
5714///
5715/// # Errors
5716///
5717/// Returns [`Error`] when the policy cannot produce a gathering configuration.
5718async fn answer_gathering(
5719    port: &MediaPort,
5720    offer: &SessionDescription,
5721    policy: MediaPolicy,
5722) -> Result<(IceNegotiation, Option<LocalDescription>)> {
5723    let remote = offer.media.first().map_or(IceNegotiation::Absent, |audio| {
5724        sipx_media::ice::negotiate(offer, audio)
5725    });
5726    if !remote.runs_ice() {
5727        return Ok((remote, None));
5728    }
5729    let local = match policy.gathering(false)? {
5730        Some(gathering) => Some(
5731            port.gather_with_rtcp_mode(&gathering, answering_rtcp_mode(offer))
5732                .await,
5733        ),
5734        None => None,
5735    };
5736    Ok((remote, local))
5737}
5738
5739/// A session that has been described and answered, but not yet accepted.
5740///
5741/// What an early dialog needs in order to be renegotiable at all. RFC 3311 §5.1 will not let an
5742/// UPDATE carry an offer while an offer/answer exchange is open, so before the 200 there is
5743/// exactly one way to make one legal: the answer to the INVITE's offer travels in a reliable
5744/// provisional (RFC 3262 §5), and this is what that answer settled on.
5745///
5746/// The media port is bound here and handed to the eventual [`Call`] rather than bound again,
5747/// because the answer already told the far end which port to send to. Binding a second one
5748/// would make the 200 contradict the 183 for no reason.
5749#[derive(Debug)]
5750pub(crate) struct Early {
5751    pub(crate) media: MediaSession,
5752    pub(crate) capabilities: Capabilities,
5753    pub(crate) settled: Settled,
5754    pub(crate) media_address: IpAddr,
5755    pub(crate) media_bind_address: IpAddr,
5756    /// The codec set the provisional's answer was built from, kept because the exchange is not
5757    /// over: an UPDATE may reoffer before the 200, and it has to be answered from the same set
5758    /// rather than from the default one.
5759    pub(crate) codecs: Codecs,
5760    /// The exact application policy retained when the provisional becomes a confirmed call.
5761    pub(crate) keying: Keying,
5762}
5763
5764/// A media offer placed in a reliable provisional and awaiting its answer in PRACK.
5765#[derive(Debug)]
5766pub(crate) struct EarlyOffer {
5767    port: MediaPort,
5768    capabilities: Capabilities,
5769    offer: SessionDescription,
5770    ice: Option<LocalDescription>,
5771    keying: PendingKeying,
5772    policy_keying: Keying,
5773    media_address: MediaAddress,
5774    codecs: Codecs,
5775}
5776
5777impl EarlyOffer {
5778    pub(crate) async fn bind(
5779        media_address: MediaAddress,
5780        secure: bool,
5781        direction: Direction,
5782        policy: MediaPolicy,
5783    ) -> Result<Self> {
5784        let media_address = media_address.validate()?;
5785        if policy.keying == Keying::DtlsSrtp {
5786            return Err(Error::DtlsEarlyMedia);
5787        }
5788        let port = MediaPort::bind(SocketAddr::new(media_address.bind(), 0))
5789            .await
5790            .map_err(Error::Io)?;
5791        let local_ice = match policy.gathering(true)? {
5792            // As with the ordinary initial offer, mux is not settled until the answer arrives.
5793            Some(gathering) => Some(
5794                port.gather_with_rtcp_mode(&gathering, sipx_sdp::RtcpMode::Separate)
5795                    .await,
5796            ),
5797            None => None,
5798        };
5799        let advertised = local_ice
5800            .as_ref()
5801            .and_then(|local| local.default_destination(ComponentId::RTP))
5802            .unwrap_or_else(|| {
5803                SocketAddr::new(media_address.advertised(), port.local_addr().port())
5804            });
5805        let (mut capabilities, keying) =
5806            media_capabilities(policy, advertised.ip(), advertised.port(), secure)?;
5807        capabilities.direction = direction;
5808        let mut offer = offer_from(&capabilities);
5809        if let Some(local) = &local_ice {
5810            add_ice(&mut offer, local, &[]);
5811        }
5812        Ok(Self {
5813            port,
5814            capabilities,
5815            offer,
5816            ice: local_ice,
5817            keying,
5818            policy_keying: policy.keying,
5819            media_address,
5820            codecs: policy.codecs,
5821        })
5822    }
5823
5824    pub(crate) fn description(&self) -> &SessionDescription {
5825        &self.offer
5826    }
5827
5828    pub(crate) async fn settle(mut self, answer: &SessionDescription) -> Result<Early> {
5829        let settled = settle_answer(&self.capabilities, answer, self.codecs)?;
5830        if let Some(local) = self.ice.as_mut() {
5831            let remote = answer
5832                .media
5833                .first()
5834                .map_or(IceNegotiation::Absent, |audio| {
5835                    sipx_media::ice::negotiate(answer, audio)
5836                });
5837            local.accept(&remote);
5838        }
5839        let (media, settled) = key_and_start(
5840            self.port,
5841            self.ice,
5842            settled,
5843            self.keying,
5844            answer,
5845            false,
5846            MediaProfile::Standard,
5847        )
5848        .await?;
5849        Ok(Early {
5850            media,
5851            capabilities: self.capabilities,
5852            settled,
5853            media_address: self.media_address.advertised(),
5854            media_bind_address: self.media_address.bind(),
5855            codecs: self.codecs,
5856            keying: self.policy_keying,
5857        })
5858    }
5859}
5860
5861impl Early {
5862    /// Bind a port and answer `offer` with it.
5863    pub(crate) async fn settle(
5864        media_address: MediaAddress,
5865        secure: bool,
5866        offer: &SessionDescription,
5867        policy: MediaPolicy,
5868    ) -> Result<(Self, SessionDescription)> {
5869        let media_address = media_address.validate()?;
5870        if policy.keying == Keying::DtlsSrtp {
5871            return Err(Error::DtlsEarlyMedia);
5872        }
5873        let negotiated = negotiated(offer, policy.codecs)?;
5874        let port = MediaPort::bind(SocketAddr::new(media_address.bind(), 0))
5875            .await
5876            .map_err(Error::Io)?;
5877        let (remote_ice, mut local_ice) = answer_gathering(&port, offer, policy).await?;
5878        let advertised = local_ice
5879            .as_ref()
5880            .and_then(|local| local.default_destination(ComponentId::RTP))
5881            .unwrap_or_else(|| {
5882                SocketAddr::new(media_address.advertised(), port.local_addr().port())
5883            });
5884        let (capabilities, _keying) =
5885            media_capabilities(policy, advertised.ip(), advertised.port(), secure)?;
5886        let mut answer = sipx_sdp::answer(offer, &capabilities);
5887        if let Some(local) = local_ice.as_mut() {
5888            local.accept(&remote_ice);
5889            add_ice(&mut answer, local, &remote_ice.answer_attributes());
5890        } else if policy.ice != IcePolicy::Disabled
5891            && let Some(audio) = answer.media.first_mut()
5892        {
5893            audio.attributes.extend(remote_ice.answer_attributes());
5894        }
5895        if answer
5896            .media
5897            .iter()
5898            .all(sipx_sdp::MediaDescription::is_rejected)
5899        {
5900            return Err(Error::NoCommonCodec);
5901        }
5902        let settled = Settled {
5903            negotiated,
5904            srtp: srtp_keys_answering(capabilities.crypto.as_ref(), offer_crypto(offer)),
5905        };
5906        let media = match local_ice {
5907            Some(local) => port.start_with_ice(settled.media_config(), local)?,
5908            None => port.start(settled.media_config())?,
5909        };
5910        Ok((
5911            Self {
5912                media,
5913                capabilities,
5914                settled,
5915                media_address: media_address.advertised(),
5916                media_bind_address: media_address.bind(),
5917                codecs: policy.codecs,
5918                keying: policy.keying,
5919            },
5920            answer,
5921        ))
5922    }
5923
5924    /// Take the far end's answer to an offer *we* made, which moves only where we send.
5925    ///
5926    /// Nothing is owed back for an answer, so unlike [`Self::reanswer`] this produces no
5927    /// description. An answer that cannot be read leaves the session where it was: the far end
5928    /// accepted something, and guessing which of our formats it meant is worse than keeping
5929    /// what the last completed exchange settled.
5930    pub(crate) async fn adopt_answer(&mut self, answer: &SessionDescription) {
5931        if let Ok(negotiated) = negotiated(answer, self.codecs) {
5932            let settled = Settled {
5933                negotiated,
5934                srtp: self.settled.srtp.clone(),
5935            };
5936            // A failed replacement leaves the working early stream in place. The UPDATE itself
5937            // was usable, so turning a local socket failure into a peer refusal would describe
5938            // the wrong fault; the eventual call still confirms the last session that ran.
5939            // discard: the peer has already answered our UPDATE, so there is no signalling
5940            // response left to change; the still-running media session is the safe fallback.
5941            let _ = self.replace_media(settled).await;
5942        }
5943    }
5944
5945    /// Answer a *later* offer — one that arrived in an UPDATE — on the port already bound.
5946    ///
5947    /// `None` means the description is unusable, and the caller refuses 488 while the early
5948    /// dialog carries on: the same rule a re-INVITE gets in `M-8`, for the same reason.
5949    ///
5950    /// The port does not move. Our own receive address was published in the answer the peer
5951    /// already has, and changing it because *their* description changed would ask them to
5952    /// renegotiate again to learn where we went.
5953    pub(crate) async fn reanswer(
5954        &mut self,
5955        offer: &SessionDescription,
5956    ) -> Option<SessionDescription> {
5957        let negotiated = negotiated(offer, self.codecs).ok()?;
5958        let answer = sipx_sdp::answer(offer, &self.capabilities);
5959        if answer
5960            .media
5961            .iter()
5962            .all(sipx_sdp::MediaDescription::is_rejected)
5963        {
5964            return None;
5965        }
5966        let settled = Settled {
5967            negotiated,
5968            srtp: srtp_keys_answering(self.capabilities.crypto.as_ref(), offer_crypto(offer)),
5969        };
5970        self.replace_media(settled).await.ok()?;
5971        Some(answer)
5972    }
5973
5974    /// Apply an early UPDATE to the session that is already running.
5975    ///
5976    /// This is the same transition [`Call::move_media_if_changed`] performs for a confirmed
5977    /// dialog, but it happens at UPDATE time rather than being deferred to the INVITE's 2xx. The
5978    /// resulting session is then the one confirmation moves into `Call`, so answer time itself
5979    /// still neither rebinds nor leaves a gap.
5980    async fn replace_media(&mut self, settled: Settled) -> Result<()> {
5981        let to = settled.negotiated;
5982        let changed = to.remote != self.settled.negotiated.remote
5983            || to.codec != self.settled.negotiated.codec
5984            || to.wire_payload_type() != self.settled.negotiated.wire_payload_type()
5985            || settled.is_encrypted() != self.settled.is_encrypted();
5986        if changed && !self.media.reconfigure(settled.media_config()).await? {
5987            return Err(Error::Sdp(
5988                "an ICE-backed early session cannot change its media format in place".to_owned(),
5989            ));
5990        }
5991        self.settled = settled;
5992        Ok(())
5993    }
5994}
5995
5996/// Answer an INVITE that has already been rung (RFC 3262).
5997///
5998/// The tag comes from the [`Ringing`](crate::Ringing) rather than being fresh, and that is the
5999/// whole reason this exists. A provisional that established a dialog has already told the caller
6000/// what this side's tag is (RFC 3261 §12.1.1); a 200 with a different one creates a *second*
6001/// dialog. The caller ACKs the dialog it knows about, this side waits for an ACK to the other,
6002/// and the 200 is retransmitted for 32 seconds into a call that is actually up.
6003///
6004/// Answers from the default codec set, [`Codecs::G711`]. [`answer_ringing_with`] takes a
6005/// selection.
6006pub async fn answer_ringing(
6007    endpoint: &Handle,
6008    incoming: &Incoming,
6009    media_address: IpAddr,
6010    ringing: &crate::Ringing,
6011) -> Result<Call> {
6012    answer_ringing_with(
6013        endpoint,
6014        incoming,
6015        media_address,
6016        ringing,
6017        Codecs::default(),
6018    )
6019    .await
6020}
6021
6022/// [`answer_ringing`], from a chosen codec set rather than the default one (`M-30`).
6023///
6024/// The selection is made here rather than at [`ring`](crate::ring) because `ring` sends a
6025/// bodiless provisional: nothing about the session has been said yet when it goes out, so the
6026/// answer this builds is still the first one. That is exactly what separates this from
6027/// [`answer_early`], where the answer left in the 183 and the choice had to be made with it.
6028pub async fn answer_ringing_with(
6029    endpoint: &Handle,
6030    incoming: &Incoming,
6031    media_address: IpAddr,
6032    ringing: &crate::Ringing,
6033    codecs: Codecs,
6034) -> Result<Call> {
6035    answer_ringing_with_policy(
6036        endpoint,
6037        incoming,
6038        media_address,
6039        ringing,
6040        MediaPolicy::default().with_codecs(codecs),
6041    )
6042    .await
6043}
6044
6045/// [`answer_ringing`], using one coherent codec and ICE policy.
6046pub async fn answer_ringing_with_policy(
6047    endpoint: &Handle,
6048    incoming: &Incoming,
6049    media_address: IpAddr,
6050    ringing: &crate::Ringing,
6051    policy: MediaPolicy,
6052) -> Result<Call> {
6053    answer_ringing_with_policy_at(
6054        endpoint,
6055        incoming,
6056        MediaAddress::new(media_address),
6057        ringing,
6058        policy,
6059    )
6060    .await
6061}
6062
6063/// [`answer_ringing_with_policy`] with independent advertised and bound media addresses.
6064pub async fn answer_ringing_with_policy_at(
6065    endpoint: &Handle,
6066    incoming: &Incoming,
6067    media_address: MediaAddress,
6068    ringing: &crate::Ringing,
6069    policy: MediaPolicy,
6070) -> Result<Call> {
6071    // RFC 3262 §3 and §5: a 2xx must not go out while a reliable provisional carrying a session
6072    // description is unacknowledged. This path never puts a description in one — `ring` sends a
6073    // bodiless provisional, and `ring_early` is the entry point that does, where
6074    // [`answer_early`] enforces the MUST. What is left here is the weaker concern: answering
6075    // before the PRACK means retransmitting a `180` at a caller that has moved on, and the
6076    // ringing is stopped either way when `Ringing` drops.
6077    if !ringing.is_acknowledged() {
6078        tracing::debug!("answering before the reliable provisional was acknowledged");
6079    }
6080    let offer = sipx_sdp::parse(&String::from_utf8_lossy(incoming.request.body()))
6081        .map_err(|error| Error::Sdp(error.to_string()))?;
6082    answer_negotiated(
6083        endpoint,
6084        incoming,
6085        media_address,
6086        offer,
6087        ringing.tag(),
6088        Some(ringing.is_reliable()),
6089        None,
6090        policy,
6091        &[],
6092    )
6093    .await
6094}
6095
6096/// Answer an INVITE that was rung with [`crate::rel::ring_early`].
6097///
6098/// The counterpart of [`answer_ringing`] for a dialog whose offer/answer already completed in
6099/// the provisional. Three things follow from that and none is optional:
6100///
6101/// - **The provisional must already be acknowledged.** RFC 3262 §5 is a MUST: a UAS that put a
6102///   session description in a reliable provisional delays the 2xx until that provisional is
6103///   acknowledged. So this returns [`Error::UnacknowledgedProvisional`] rather than answering, and
6104///   the caller keeps feeding messages to [`Ringing::on_prack`](crate::Ringing::on_prack) until
6105///   [`Ringing::is_acknowledged`](crate::Ringing::is_acknowledged) is true. It cannot wait on
6106///   the caller's behalf: the PRACK arrives on the application's own inbox, and this holds the
6107///   `&mut` that handling it would need.
6108/// - **The 200 carries no session description.** There is nothing left to say: the offer was
6109///   answered in the 183, and anything an UPDATE renegotiated afterwards was answered in its own
6110///   2xx. Repeating the last answer here would be a second answer to the INVITE's offer, and
6111///   repeating the *first* one would silently undo the renegotiation. That is only safe
6112///   *because* of the rule above — the PRACK is proof the caller holds the answer, and without
6113///   it a lost 183 would leave the caller in a confirmed dialog with no description at all.
6114/// - **The media port is the one the provisional named**, not a fresh one, because that is the
6115///   port the far end has already been told to send to.
6116///
6117/// The `Ringing` is borrowed mutably and emptied rather than consumed, because it owns the
6118/// retransmission of the provisional and must go on owning it until it is dropped.
6119pub async fn answer_early(
6120    endpoint: &Handle,
6121    incoming: &Incoming,
6122    ringing: &mut crate::Ringing,
6123) -> Result<Call> {
6124    if !ringing.is_acknowledged() {
6125        return Err(Error::UnacknowledgedProvisional);
6126    }
6127
6128    // Before anything is taken out of the `Ringing`. A 422 leaves here through the `?`, and it
6129    // is a counter-offer rather than a failure — the caller is expected to be rung again — so
6130    // it must not cost the bound port and the session the early exchange settled.
6131    let agreed = negotiate_session(endpoint, incoming).await?;
6132
6133    let (early, dialog, negotiation, peer_allows_update) = ringing.take_early()?;
6134    let target = in_dialog_target(&dialog, Target::new(incoming.source, incoming.transport));
6135
6136    let to_with_tag = {
6137        let existing = incoming
6138            .request
6139            .headers
6140            .value(&HeaderName::To)
6141            .map(|value| String::from_utf8_lossy(&value).into_owned())
6142            .unwrap_or_default();
6143        format!("{};tag={}", strip_header_params(&existing), ringing.tag())
6144    };
6145
6146    let mut response = ResponseBuilder::to_request(&incoming.request, ok_status(), "OK")?
6147        .set_header(&HeaderName::To, Bytes::from(to_with_tag))?
6148        .header(
6149            HeaderName::Contact,
6150            Bytes::from(contact_for(endpoint, incoming.transport)),
6151        )?
6152        .header(
6153            HeaderName::Allow,
6154            Bytes::from_static(update::ALLOW.as_bytes()),
6155        )?;
6156    if let Some(accepted) = agreed {
6157        let expires = SessionExpires {
6158            interval: accepted.interval,
6159            refresher: Some(accepted.refresher),
6160        };
6161        response = response
6162            .header(HeaderName::SessionExpires, Bytes::from(expires.to_string()))?
6163            .header(HeaderName::Supported, Bytes::from_static(b"timer"))?;
6164        if accepted.require {
6165            response = response.header(HeaderName::Require, Bytes::from_static(b"timer"))?;
6166        }
6167    }
6168    let response = response.build();
6169
6170    let media = early.media;
6171    endpoint.respond(&incoming.key, response.clone()).await?;
6172
6173    let ack_stop = CancellationToken::new();
6174    let ack_retransmission = OwnedTask::new(tokio::spawn(retransmit_until_acked(
6175        endpoint.clone(),
6176        incoming.key.clone(),
6177        response,
6178        ack_stop.clone(),
6179    )));
6180
6181    let (events, events_rx) = EventSink::new();
6182    emit_construction_events(&events, Some(ringing.is_reliable()));
6183
6184    Ok(Call {
6185        dialog,
6186        initial_status: OK,
6187        media: Arc::new(media),
6188        retired_media: Vec::new(),
6189        endpoint: endpoint.clone(),
6190        target,
6191        ack_stop: Some(ack_stop),
6192        ack_retransmission: Some(ack_retransmission),
6193        delayed_offer: None,
6194        ended: false,
6195        media_address: early.media_address,
6196        media_bind_address: early.media_bind_address,
6197        codecs: early.codecs,
6198        profile: MediaProfile::Standard,
6199        current: early.settled.negotiated,
6200        peer_ice: peer_ice_credentials(incoming.request.body()),
6201        hold: Direction::SendRecv,
6202        encrypted: early.settled.is_encrypted(),
6203        keying: early.keying,
6204        referral: None,
6205        transfer: None,
6206        session: agreed.map(|accepted| {
6207            SessionState::armed(session::Session {
6208                interval: accepted.interval,
6209                we_refresh: accepted.refresher == session::Refresher::Uas,
6210            })
6211        }),
6212        negotiation,
6213        peer_allows_update,
6214        events,
6215        events_rx: Some(events_rx),
6216        history: HistoryInfo::from_headers(&incoming.request.headers)
6217            .and_then(std::result::Result::ok),
6218        dialog_credentials: None,
6219        admitted_dialog_methods: Vec::new(),
6220    })
6221}
6222
6223/// Settle the RFC 4028 session timer for an incoming INVITE, refusing it if it asks for too
6224/// little.
6225///
6226/// Sends the `422` itself, because the refusal has to carry the floor and the only thing that
6227/// knows the floor is the negotiation. Returning "too brief" and leaving the caller to build
6228/// the response would make the one header that makes a 422 useful optional.
6229async fn negotiate_session(
6230    endpoint: &Handle,
6231    incoming: &Incoming,
6232) -> Result<Option<session::Accepted>> {
6233    Ok(
6234        match session::answer(
6235            incoming
6236                .request
6237                .headers
6238                .typed::<sipx_sip::headers::misc::Supported>()
6239                .and_then(std::result::Result::ok)
6240                .is_some_and(|s| s.contains(session::OPTION_TAG)),
6241            incoming
6242                .request
6243                .headers
6244                .typed::<SessionExpires>()
6245                .and_then(std::result::Result::ok),
6246            incoming
6247                .request
6248                .headers
6249                .typed::<MinSe>()
6250                .and_then(std::result::Result::ok)
6251                .map(|min| min.0),
6252            session::ABSOLUTE_MIN_INTERVAL,
6253        ) {
6254            session::Answer::TooBrief(floor) => {
6255                // RFC 4028 §6: the 422 has to carry the minimum, or the caller learns only that it
6256                // was wrong and not what would be right, and retries the same interval forever.
6257                const INTERVAL_TOO_SMALL: u16 = 422;
6258                let status = StatusCode::new(INTERVAL_TOO_SMALL)
6259                    .unwrap_or_else(|| unreachable!("422 is a valid status code"));
6260                let refusal = ResponseBuilder::to_request(
6261                    &incoming.request,
6262                    status,
6263                    "Session Interval Too Small",
6264                )?
6265                .header(HeaderName::MinSe, Bytes::from(floor.as_secs().to_string()))?
6266                .build();
6267                endpoint.respond(&incoming.key, refusal).await?;
6268                return Err(Error::IntervalTooBrief(floor));
6269            }
6270            session::Answer::None => None,
6271            session::Answer::Accept(accepted) => Some(accepted),
6272        },
6273    )
6274}
6275
6276/// Called immediately before the `200` is handed to the transport, to take the invitation.
6277///
6278/// The dispatcher's hook into a path that otherwise knows nothing about invitations. Returning
6279/// `Err` aborts the answer with nothing sent; returning `Ok` means no later CANCEL may draw a
6280/// `487`, because a `200` is about to be on the wire. [`answer_negotiated`] documents why it is
6281/// invoked exactly where it is.
6282///
6283/// `Send + Sync` so that `&Claim` is `Send` and the futures carrying one stay spawnable, which
6284/// `an_answer_future_is_spawnable` holds to.
6285pub(crate) type Claim<'a> = &'a (dyn Fn() -> Result<()> + Send + Sync);
6286
6287/// Answer an INVITE whose offer has already been parsed.
6288///
6289/// `reliable_ringing` is `Some` exactly when this side rang first (via [`crate::rel::ring`]):
6290/// `Some(reliable)` reports whether that provisional was 100rel-acknowledged, and `None` (the
6291/// [`answer`] path) means there is no ringing to report at all.
6292///
6293/// `claim` is the dispatcher's, and is invoked at one specific line below; see [`Claim`].
6294///
6295/// `codecs` is what this side is willing to carry. It bounds the answer at both ends of the one
6296/// exchange: [`negotiated`] may not settle outside it, and [`Codecs::capabilities`] builds the
6297/// answer from it — so the codec the session starts on is always one the answer named.
6298// Eight, and every one of them is a distinct fact about *this* answer that the caller holds and
6299// this does not. Bundling them into a struct would be a struct with one construction site per
6300// caller and no behaviour, which moves the argument list rather than shortening it.
6301#[allow(clippy::too_many_arguments)]
6302#[allow(
6303    clippy::too_many_lines,
6304    reason = "the answer lifecycle remains in wire order; custom fields add one retained input"
6305)]
6306async fn answer_negotiated(
6307    endpoint: &Handle,
6308    incoming: &Incoming,
6309    media_address: MediaAddress,
6310    offer: SessionDescription,
6311    tag: &str,
6312    reliable_ringing: Option<bool>,
6313    claim: Option<Claim<'_>>,
6314    policy: MediaPolicy,
6315    headers: &[sipx_sip::Header],
6316) -> Result<Call> {
6317    validate_profile_preflight(policy, incoming.transport)?;
6318    let media_address = media_address.validate()?;
6319    if policy.profile == MediaProfile::BrowserAudio {
6320        sipx_sdp::browser_audio::validate(
6321            &offer,
6322            sipx_sdp::browser_audio::BrowserAudioRole::Offerer,
6323        )?;
6324    }
6325    validate_dtls_offer_setup(&offer, policy)?;
6326    let negotiated = negotiated(&offer, policy.codecs)?;
6327
6328    // The port is bound before the session starts, because the answer has to name it *and* the
6329    // session has to be created with the keys that answer settles on. Starting the session first
6330    // — as this did — leaves nowhere to put them.
6331    let port = MediaPort::bind(SocketAddr::new(media_address.bind(), 0))
6332        .await
6333        .map_err(Error::Io)?;
6334
6335    let (remote_ice, mut local_ice) = answer_gathering(&port, &offer, policy).await?;
6336    let advertised = local_ice
6337        .as_ref()
6338        .and_then(|local| local.default_destination(ComponentId::RTP))
6339        .unwrap_or_else(|| SocketAddr::new(media_address.advertised(), port.local_addr().port()));
6340    let (capabilities, keying) = media_capabilities(
6341        policy,
6342        advertised.ip(),
6343        advertised.port(),
6344        incoming.transport.is_secure(),
6345    )?;
6346    let mut answer_sdp = if policy.profile == MediaProfile::BrowserAudio {
6347        let local = local_ice
6348            .as_ref()
6349            .ok_or(sipx_sdp::browser_audio::ProfileError::IceRequired)?;
6350        let fingerprint = capabilities
6351            .dtls()
6352            .cloned()
6353            .ok_or(sipx_sdp::browser_audio::ProfileError::FingerprintRequired)?;
6354        sipx_sdp::browser_audio::answer(
6355            &offer,
6356            &sipx_sdp::browser_audio::BrowserAudioLocal {
6357                address: advertised.ip(),
6358                port: advertised.port(),
6359                session_id: capabilities.session_id,
6360                session_version: capabilities.session_version,
6361                direction: capabilities.direction,
6362                ice: local.credentials().clone(),
6363                candidates: local.candidates().to_vec(),
6364                fingerprint,
6365                setup: sipx_sdp::fingerprint::SetupCapabilities::both(),
6366            },
6367        )?
6368    } else {
6369        sipx_sdp::answer(&offer, &capabilities)
6370    };
6371    if let Some(local) = local_ice.as_mut() {
6372        local.accept(&remote_ice);
6373        if policy.profile == MediaProfile::Standard {
6374            add_ice(&mut answer_sdp, local, &remote_ice.answer_attributes());
6375        }
6376    } else if policy.ice != IcePolicy::Disabled
6377        && let Some(audio) = answer_sdp.media.first_mut()
6378    {
6379        audio.attributes.extend(remote_ice.answer_attributes());
6380    }
6381    if answer_sdp
6382        .media
6383        .iter()
6384        .all(sipx_sdp::MediaDescription::is_rejected)
6385    {
6386        return Err(Error::NoCommonCodec);
6387    }
6388
6389    // Our key from the answer we just built, theirs from the offer we were sent.
6390    let settled = Settled {
6391        negotiated,
6392        srtp: srtp_keys_answering(capabilities.crypto.as_ref(), offer_crypto(&offer)),
6393    };
6394    let to_with_tag = {
6395        let existing = incoming
6396            .request
6397            .headers
6398            .value(&HeaderName::To)
6399            .map(|value| String::from_utf8_lossy(&value).into_owned())
6400            .unwrap_or_default();
6401        format!("{};tag={tag}", strip_header_params(&existing))
6402    };
6403
6404    let agreed = negotiate_session(endpoint, incoming).await?;
6405
6406    let response = ok_with_answer(
6407        endpoint,
6408        incoming,
6409        &to_with_tag,
6410        &answer_sdp,
6411        agreed,
6412        headers,
6413    )?;
6414
6415    // Before the 200, not after. An INVITE with no usable `Contact` cannot form a dialog
6416    // (RFC 3261 §12.1.1), and answering first would put a 2xx on the wire for a call this side
6417    // is then unable to hold: the caller ACKs, believes it has a confirmed dialog, and streams
6418    // media at an endpoint that has forgotten it and can never send the BYE.
6419    let dialog = Dialog::from_request(&incoming.request, tag).ok_or(Error::NoDialog)?;
6420    let target = in_dialog_target(&dialog, Target::new(incoming.source, incoming.transport));
6421
6422    // The last thing before the `200` leaves, and that placement is the whole contract.
6423    //
6424    // Taking the invitation *early* would be simpler, but every fallible step above — parsing the
6425    // offer, binding the port, negotiating the session, building the response, forming the dialog
6426    // — can return `Err` with nothing on the wire. An invitation taken by one of those failures
6427    // is one no CANCEL can ever end: the CANCEL draws its `200`, the `487` is suppressed because
6428    // the invitation looks answered, and the INVITE transaction is left without a final response
6429    // for the caller's Timer B to resolve.
6430    //
6431    // Taking it *here* keeps the guarantee the early claim was for. From this line on, the only
6432    // fallible expression is `respond` itself; everything after it is infallible. So a CANCEL
6433    // arriving from now on finds the invitation taken and correctly sends no `487` behind the
6434    // `200` — and a CANCEL arriving a moment earlier is honoured in full, which is what it is
6435    // owed, because nothing has been sent yet.
6436    //
6437    // `respond` failing is the one case that stays claimed. That is deliberate: a stream
6438    // transport can write part of a response before erroring, so "it failed" is not proof that
6439    // nothing reached the caller, and a `487` chasing a `200` is the worse of the two outcomes.
6440    if let Some(claim) = claim {
6441        claim()?;
6442    }
6443
6444    endpoint.respond(&incoming.key, response.clone()).await?;
6445
6446    let ack_stop = CancellationToken::new();
6447    let mut ack_retransmission = OwnedTask::new(tokio::spawn(retransmit_until_acked(
6448        endpoint.clone(),
6449        incoming.key.clone(),
6450        response,
6451        ack_stop.clone(),
6452    )));
6453
6454    // The answer must leave before an active answerer sends ClientHello. A caller is permitted to
6455    // wait for the final SDP (and then its ACK) before opening the media path.
6456    let started = Box::pin(key_and_start(
6457        port,
6458        local_ice,
6459        settled,
6460        keying,
6461        &offer,
6462        true,
6463        policy.profile,
6464    ))
6465    .await;
6466    let (media, settled) = match started {
6467        Ok(started) => started,
6468        Err(error) => {
6469            cancel_and_join(&ack_stop, &mut ack_retransmission).await;
6470            return Err(error);
6471        }
6472    };
6473
6474    // As in `dial_with`: emitted at construction, from what was actually observed (ringing
6475    // first, if this path came through it) rather than recomputed afterwards.
6476    let (events, events_rx) = EventSink::new();
6477    emit_construction_events(&events, reliable_ringing);
6478
6479    Ok(Call {
6480        dialog,
6481        initial_status: OK,
6482        media: Arc::new(media),
6483        retired_media: Vec::new(),
6484        endpoint: endpoint.clone(),
6485        target,
6486        ack_stop: Some(ack_stop),
6487        ack_retransmission: Some(ack_retransmission),
6488        delayed_offer: None,
6489        ended: false,
6490        media_address: media_address.advertised(),
6491        media_bind_address: media_address.bind(),
6492        codecs: policy.codecs,
6493        profile: policy.profile,
6494        current: settled.negotiated,
6495        peer_ice: peer_ice_credentials(incoming.request.body()),
6496        hold: Direction::SendRecv,
6497        encrypted: policy.profile == MediaProfile::BrowserAudio || settled.srtp.is_some(),
6498        keying: policy.keying,
6499        referral: None,
6500        transfer: None,
6501        session: agreed.map(|accepted| {
6502            SessionState::armed(session::Session {
6503                interval: accepted.interval,
6504                we_refresh: accepted.refresher == session::Refresher::Uas,
6505            })
6506        }),
6507        negotiation: update::Negotiation::idle(),
6508        // From the INVITE, which RFC 3311 §4 asks a compliant UAC to put it on.
6509        peer_allows_update: update::peer_allows(&incoming.request.headers),
6510        events,
6511        events_rx: Some(events_rx),
6512        history: HistoryInfo::from_headers(&incoming.request.headers)
6513            .and_then(std::result::Result::ok),
6514        dialog_credentials: None,
6515        admitted_dialog_methods: Vec::new(),
6516    })
6517}
6518
6519/// Resend a 2xx on the T1 backoff until the ACK arrives or 64·T1 has passed.
6520async fn retransmit_until_acked(
6521    endpoint: Handle,
6522    key: sipx_sip::transaction::TransactionKey,
6523    response: Response,
6524    stop: CancellationToken,
6525) {
6526    let t1 = Duration::from_millis(500);
6527    let mut interval = t1;
6528    let mut elapsed = Duration::ZERO;
6529    let give_up = t1 * 64;
6530
6531    loop {
6532        if until_cancelled(&stop, tokio::time::sleep(interval))
6533            .await
6534            .is_none()
6535        {
6536            return;
6537        }
6538        elapsed += interval;
6539        if elapsed >= give_up {
6540            tracing::warn!("no ACK for our 2xx after 64*T1; giving up");
6541            return;
6542        }
6543        let Some(sent) = until_cancelled(&stop, endpoint.respond(&key, response.clone())).await
6544        else {
6545            return;
6546        };
6547        if sent.is_err() {
6548            return;
6549        }
6550        // Doubling capped at T2, exactly as the INVITE client transaction retransmits.
6551        interval = (interval * 2).min(Duration::from_secs(4));
6552    }
6553}
6554
6555/// What waiting for a final response ended in.
6556enum Waited {
6557    /// A final response arrived.
6558    Final {
6559        /// The response itself.
6560        response: Response,
6561        /// Whether a provisional counting as *ringing* — anything past a bare `100 Trying` —
6562        /// was seen first, and whether it was reliable (RFC 3262). `None` when the far end
6563        /// went straight to the final response, which is the one time no `CallEvent::Ringing`
6564        /// belongs on the eventual call's event stream.
6565        ringing: Option<bool>,
6566    },
6567    /// The deadline passed.
6568    GaveUp,
6569    /// The owner asked this attempt to stop.
6570    Cancelled,
6571    /// The transaction ended without a final response.
6572    Gone,
6573    /// The selected transport could not be established or used.
6574    Transport(sipx_transport::Error),
6575}
6576
6577/// Wait for the final response to an INVITE.
6578///
6579/// The transport response stream retains the provisional observation that RFC 3261 §9.1 needs
6580/// if this wait ends in local cancellation.
6581/// What a UAC needs in order to acknowledge a reliable provisional while it waits.
6582struct Acknowledging<'a> {
6583    endpoint: &'a Handle,
6584    invite: &'a Request,
6585    target: &'a Target,
6586    capabilities: &'a Capabilities,
6587    seen: sipx_sip::rel::Sequence,
6588}
6589
6590async fn await_final(
6591    responses: &mut sipx_transport::Responses,
6592    limit: Option<Duration>,
6593    acknowledging: &mut Acknowledging<'_>,
6594    cancelled: &mut Option<Cancelled<'_>>,
6595) -> Waited {
6596    let deadline = limit.map(|limit| tokio::time::Instant::now() + limit);
6597    let mut ringing = None;
6598    loop {
6599        let event = match (deadline, cancelled.as_mut()) {
6600            (None, None) => responses.next().await,
6601            (Some(deadline), None) => {
6602                match tokio::time::timeout_at(deadline, responses.next()).await {
6603                    Ok(event) => event,
6604                    Err(_elapsed) => return Waited::GaveUp,
6605                }
6606            }
6607            (None, Some(cancelled)) => {
6608                tokio::select! {
6609                    biased;
6610                    () = cancelled.as_mut() => return Waited::Cancelled,
6611                    event = responses.next() => event,
6612                }
6613            }
6614            (Some(deadline), Some(cancelled)) => {
6615                tokio::select! {
6616                    biased;
6617                    () = cancelled.as_mut() => return Waited::Cancelled,
6618                    () = tokio::time::sleep_until(deadline) => return Waited::GaveUp,
6619                    event = responses.next() => event,
6620                }
6621            }
6622        };
6623        match event {
6624            Some(sipx_sip::transaction::TuEvent::Response(response)) => {
6625                if response.status.is_final() {
6626                    return Waited::Final {
6627                        response: *response,
6628                        ringing,
6629                    };
6630                }
6631                // A bare `100 Trying` only acknowledges that the request arrived (RFC 3261
6632                // §17.2.1); it is not the far end's phone ringing, and 100rel does not apply
6633                // to it either (RFC 3262 §3), so it is excluded from what `ringing` tracks.
6634                if response.status.code() > 100 {
6635                    ringing = Some(crate::rel::reliable_sequence(&response).is_some());
6636                }
6637                // RFC 3262 §4. A failure here is logged rather than fatal: the invitation is
6638                // still running, and abandoning a ringing call because one PRACK did not get
6639                // through would be a worse outcome than the unreliability it was fixing.
6640                if let Err(error) = acknowledge(&response, acknowledging).await {
6641                    tracing::debug!(%error, "could not acknowledge a reliable provisional");
6642                }
6643            }
6644            Some(sipx_sip::transaction::TuEvent::TransportError) => {
6645                // Preserve TLS verification failures because treating a rejected certificate as
6646                // "no response" hides the security decision the caller must act on. Other send
6647                // failures retain the call API's established NoResponse behavior; changing those
6648                // exit semantics is outside this transport-selection story.
6649                if let Some(error @ sipx_transport::Error::Tls(_)) =
6650                    responses.take_transport_error()
6651                {
6652                    return Waited::Transport(error);
6653                }
6654                return Waited::Gone;
6655            }
6656            Some(_) => {}
6657            None => return Waited::Gone,
6658        }
6659    }
6660}
6661
6662/// PRACK a reliable provisional, if that is what this is (RFC 3262 §4).
6663async fn acknowledge(response: &Response, ctx: &mut Acknowledging<'_>) -> Result<()> {
6664    let Some(rseq) = crate::rel::reliable_sequence(response) else {
6665        return Ok(());
6666    };
6667    // §4: out of order means an earlier one is missing, and a duplicate has already been
6668    // acknowledged. Neither is PRACKed — re-acknowledging a retransmission would turn one lost
6669    // packet into a stream of PRACKs, and acknowledging a gap would tell the UAS that
6670    // everything up to this number arrived when it did not.
6671    if ctx.seen.accept(rseq) != sipx_sip::rel::Received::Acknowledge {
6672        return Ok(());
6673    }
6674
6675    // §4: "The provisional response MUST establish a dialog if one is not yet created." The
6676    // PRACK is an in-dialog request and has nowhere to go without it.
6677    let mut dialog = Dialog::from_response(ctx.invite, response).ok_or(Error::NoDialog)?;
6678    let target = in_dialog_target(&dialog, ctx.target.clone());
6679    let invite_cseq = ctx
6680        .invite
6681        .headers
6682        .typed::<sipx_sip::CSeq>()
6683        .and_then(std::result::Result::ok)
6684        .map_or(1, |cseq| cseq.sequence);
6685
6686    let body = crate::rel::prack_body(
6687        !ctx.invite.body().is_empty(),
6688        response.body(),
6689        ctx.capabilities,
6690    );
6691    crate::rel::send_prack(ctx.endpoint, &mut dialog, &target, rseq, invite_cseq, body).await
6692}
6693
6694fn normal_clearing_reason() -> ReasonValue {
6695    ReasonValue::q850(16, Some(b"Normal call clearing".to_vec()))
6696}
6697
6698fn request_timeout_reason() -> ReasonValue {
6699    // A constant defined by the SIP status-code space; construction cannot fail.
6700    StatusCode::new(408).map_or_else(
6701        || ReasonValue::q850(102, Some(b"Recovery on timer expiry".to_vec())),
6702        |status| ReasonValue::sip(status, Some(b"Request Timeout".to_vec())),
6703    )
6704}
6705
6706/// Acknowledge a 2xx (RFC 3261 §13.2.2.4).
6707///
6708/// This ACK is not part of the INVITE transaction and has no transaction of its own: it is
6709/// "passed to the transport layer directly for transmission", carries a *new* branch because
6710/// any proxy must treat it as a new request, and is resent only when a retransmitted 2xx
6711/// arrives. Handing it to the transaction layer instead earns it the retransmission timers of
6712/// a non-INVITE request — a stream of duplicate ACKs toward a response that will never come,
6713/// and a spurious timeout 32 seconds into a call that is up and talking.
6714async fn send_ack(endpoint: &Handle, dialog: &Dialog, target: Target) -> Result<()> {
6715    let ack = build_ack(endpoint, dialog, &target)?;
6716    endpoint.send_directly(ack, target).await?;
6717    Ok(())
6718}
6719
6720/// Keep acknowledging a 2xx for as long as the far end keeps retransmitting it.
6721///
6722/// RFC 3261 §13.2.2.4: the UAC core "MUST generate an ACK for each 2xx received", and a
6723/// retransmitted 2xx says the previous ACK never arrived. Nobody else can do this — the INVITE
6724/// transaction has already passed the response up, and RFC 6026's `Accepted` state exists so
6725/// that the retransmission still has somewhere to arrive. Dropping the stream after the first
6726/// answer leaves the far end retransmitting for 64*T1 and then tearing down, from its side, a
6727/// call this side believes is established and is already sending audio into.
6728///
6729/// The same ACK goes out each time, rather than a freshly built one: it acknowledges one
6730/// response, and a new branch on every repeat would present each as a new request.
6731async fn reack_retransmitted_2xx(
6732    endpoint: Handle,
6733    mut responses: sipx_transport::Responses,
6734    ack: Request,
6735    target: Target,
6736) {
6737    while let Some(event) = responses.next().await {
6738        if let sipx_sip::transaction::TuEvent::Response(response) = event
6739            && response.status.is_success()
6740            && endpoint
6741                .send_directly(ack.clone(), target.clone())
6742                .await
6743                .is_err()
6744        {
6745            return;
6746        }
6747    }
6748}
6749
6750fn build_ack(endpoint: &Handle, dialog: &Dialog, target: &Target) -> Result<Request> {
6751    let (local, remote) = dialog.local_and_remote();
6752    let (uri, routes) = dialog.request_target();
6753    let via = format!(
6754        "SIP/2.0/{} {};rport;branch={}",
6755        target.transport.as_str(),
6756        endpoint.sent_by_for(target.transport),
6757        sipx_transport::new_branch()
6758    );
6759    let ack = RequestBuilder::new(Method::Ack, uri)
6760        .header(HeaderName::Via, Bytes::from(via))?
6761        .header(HeaderName::To, Bytes::from(remote))?
6762        .header(HeaderName::From, Bytes::from(local))?
6763        .header(HeaderName::CallId, Bytes::from(dialog.id.call_id.clone()))?
6764        // The ACK for a 2xx carries the INVITE's sequence number, not a new one: it
6765        // acknowledges that request rather than being one of its own.
6766        .cseq(dialog.local_cseq, &Method::Ack)?
6767        .max_forwards(70);
6768    Ok(add_routes(ack, &routes)?.build())
6769}
6770
6771/// Where in-dialog requests go.
6772///
6773/// RFC 3261 §12.2.1.1: the peer's `Contact`, not the address the INVITE was sent to. Those
6774/// differ whenever a redirect, a B2BUA or a load balancer is involved, and using the original
6775/// address means the ACK and the BYE reach the wrong element.
6776///
6777/// A `Contact` naming a hostname would need resolution, which this layer does not do; the
6778/// address the exchange arrived from is the honest fallback, and behind a NAT it is the only
6779/// one that works.
6780pub(crate) fn in_dialog_target(dialog: &Dialog, fallback: Target) -> Target {
6781    // Over a WebSocket or QUIC the `Contact` is not consulted at all. RFC 7118 §5.2: the peer has no
6782    // listening port, its `Contact` names something that will never resolve, and the connection
6783    // the dialog was established on is the only way to reach it. This is the RFC 5923 rule for
6784    // stream transports made absolute — there is no fallback because there is nowhere to fall
6785    // back to, and honouring a `Contact` here would send the BYE to an address that either does
6786    // not answer or belongs to somebody else.
6787    if matches!(
6788        fallback.transport,
6789        TransportKind::Ws | TransportKind::Wss | TransportKind::Quic
6790    ) {
6791        return fallback;
6792    }
6793
6794    // The first hop, which is the remote target only when the dialog has no route set. With
6795    // one they are different elements, and RFC 3261 §12.2.1.1 hands the request to the former.
6796    let hop = dialog.hop();
6797    let Some(sipx_sip::Host::Ip(ip)) = hop.host() else {
6798        return fallback;
6799    };
6800    let transport = hop
6801        .transport()
6802        .and_then(TransportKind::parse)
6803        .unwrap_or(fallback.transport);
6804    let port = hop.port().unwrap_or_else(|| transport.default_port());
6805    Target::new(SocketAddr::new(*ip, port), transport)
6806}
6807
6808pub(crate) fn offer_from(capabilities: &Capabilities) -> SessionDescription {
6809    let mut sdp = SessionDescription::new(
6810        capabilities.address,
6811        capabilities.session_id,
6812        capabilities.session_version,
6813    );
6814    let mut audio = sipx_sdp::MediaDescription::audio(
6815        capabilities.audio_port,
6816        capabilities.audio_formats.clone(),
6817    );
6818    for (payload, mapping) in &capabilities.rtpmaps {
6819        audio.attributes.push(sipx_sdp::Attribute::valued(
6820            "rtpmap",
6821            format!("{payload} {mapping}"),
6822        ));
6823    }
6824    // The key, and the protocol that matches it. Offering `a=crypto` under `RTP/AVP` asks for a
6825    // stream that is encrypted and declared not to be; offering `RTP/SAVP` with no key asks for
6826    // encryption with nothing to key it. Both come from the same place, so neither can drift.
6827    if let Some(crypto) = &capabilities.crypto {
6828        capabilities.protocol().clone_into(&mut audio.protocol);
6829        audio
6830            .attributes
6831            .push(sipx_sdp::Attribute::valued("crypto", crypto.to_value()));
6832    }
6833    // The same rule for DTLS-SRTP, with the fingerprint in place of the key: `UDP/TLS/RTP/SAVP`
6834    // and an `a=fingerprint` come from one place so a stream cannot claim one and carry the
6835    // other. RFC 5763 §5 requires the *offerer* to say `actpass` and let the answerer choose.
6836    if let Some(fingerprint) = capabilities.dtls() {
6837        capabilities.protocol().clone_into(&mut audio.protocol);
6838        audio.attributes.push(sipx_sdp::Attribute::valued(
6839            "fingerprint",
6840            fingerprint.to_value(),
6841        ));
6842        audio.attributes.push(sipx_sdp::Attribute::valued(
6843            "setup",
6844            sipx_sdp::fingerprint::Setup::ActPass.as_str().to_owned(),
6845        ));
6846    }
6847    if capabilities.rtcp_mux {
6848        audio.attributes.push(sipx_sdp::Attribute::flag("rtcp-mux"));
6849    }
6850    audio.set_direction(capabilities.direction);
6851    sdp.media.push(audio);
6852    sdp
6853}
6854
6855/// What negotiation settled on.
6856#[derive(Debug, Clone, Copy)]
6857pub(crate) struct Negotiated {
6858    pub(crate) remote: SocketAddr,
6859    pub(crate) codec: Codec,
6860    /// RTP clock rate of this exact format.
6861    ///
6862    /// Usually fixed by the codec. L16 can be negotiated at more than one rate, so retaining the
6863    /// format's rate is what keeps packet sizing, resampling, and RTP timestamps in agreement.
6864    pub(crate) clock_rate: u32,
6865    /// The payload type to send `codec` with, when the description gave it a number.
6866    ///
6867    /// `None` only for a bare static type matched by number. Anything an rtpmap touched —
6868    /// Opus always, a remapped static possibly — has no number of its own that means anything:
6869    /// 111 is convention, and what the far end listens for is the number *it* assigned.
6870    pub(crate) payload_type: Option<u8>,
6871    /// The payload type our description assigned to packets arriving for this codec.
6872    ///
6873    /// Separate from [`Self::payload_type`], which belongs to the peer's description and is the
6874    /// number used for sending. They are usually equal but dynamic assignments are directional.
6875    pub(crate) receive_payload_type: Option<u8>,
6876    /// The payload type the far end uses for `telephone-event`, if it offered one.
6877    ///
6878    /// Taken from the description rather than assumed, because it is a *dynamic* type: 101 is
6879    /// what sipx offers, not what everyone uses, and assuming it would send keypresses on
6880    /// whatever the far end put that number to.
6881    pub(crate) dtmf: Option<u8>,
6882    /// Whether RTCP shares the RTP port or uses its adjacent control port.
6883    pub(crate) rtcp_mode: sipx_sdp::RtcpMode,
6884}
6885
6886/// What negotiation settled on, plus the keys — which are not `Copy` and do not belong in a
6887/// type that is.
6888#[derive(Debug, Clone)]
6889pub(crate) struct Settled {
6890    pub(crate) negotiated: Negotiated,
6891    srtp: Option<sipx_media::SrtpKeys>,
6892}
6893
6894impl Negotiated {
6895    /// The number this codec actually goes out with: the one the description assigned, or the
6896    /// codec's own when it is a static type nothing remapped.
6897    ///
6898    /// Mirrors [`sipx_media::Config::wire_payload_type`], which is what the session reads — so this
6899    /// is the value to compare when asking whether the wire changed. The raw [`Self::payload_type`]
6900    /// is not: `Some(0)` and `None` are two descriptions of PCMU and the same byte on the wire.
6901    fn wire_payload_type(&self) -> u8 {
6902        self.payload_type
6903            .unwrap_or_else(|| self.codec.payload_type())
6904    }
6905
6906    fn receive_wire_payload_type(&self) -> u8 {
6907        self.receive_payload_type
6908            .unwrap_or_else(|| self.codec.payload_type())
6909    }
6910
6911    fn media_config(self) -> sipx_media::Config {
6912        let mut config = sipx_media::Config::new(self.remote, self.codec);
6913        config.clock_rate = self.clock_rate;
6914        config.payload_type = self.payload_type;
6915        config.receive_payload_type = self.receive_payload_type;
6916        config.dtmf_payload_type = self.dtmf;
6917        config.rtcp_mode = self.rtcp_mode;
6918        config
6919    }
6920}
6921
6922impl Settled {
6923    /// Whether both halves of the keying are present, so the media is actually encrypted.
6924    pub(crate) fn is_encrypted(&self) -> bool {
6925        self.srtp.is_some()
6926    }
6927
6928    pub(crate) fn media_config(&self) -> sipx_media::Config {
6929        let mut config = self.negotiated.media_config();
6930        config.srtp.clone_from(&self.srtp);
6931        config
6932    }
6933}
6934
6935/// The keys an answer to *our* offer settles on, once it has been checked against what we sent.
6936///
6937/// RFC 4568 §5.1.3 makes the check a MUST on the offerer, and this is the only place a call can
6938/// run it: [`sipx_media::SrtpKeys::from_answer`] is the sole route from an answer to keys, and it
6939/// returns which of *our* offers the answer accepted, so the half we key with is the half we sent
6940/// rather than whichever one happened to be first. `docs/specs/srtp.md` §5.4.
6941///
6942/// `offered` is a slice and not one attribute because that is what the check takes. sipx offers
6943/// exactly one today, and a function that quietly assumed so would have to be found again the day
6944/// it offers two.
6945///
6946/// `Ok(None)` means this side offered no key at all — a plain call, which is the only case where
6947/// the absence of an `a=crypto` in the answer is not a failure. When we did offer, an answer
6948/// carrying nothing usable is refused: that is the shape "a suite that was never offered" arrives
6949/// in, since [`sipx_sdp::crypto::Crypto::parse`] refuses a suite sipx cannot key.
6950///
6951/// # Errors
6952///
6953/// [`Error::Sdp`] when the answer accepted a tag and suite this side never offered, or carried no
6954/// key. Not `None`: dropping to an unencrypted call would hand the user an insecure call presented
6955/// as a secure one, and dropping the stream would end the call with nothing anyone can act on.
6956pub(crate) fn srtp_keys(
6957    offered: &[sipx_sdp::crypto::Crypto],
6958    answered: Option<&sipx_sdp::crypto::Crypto>,
6959) -> Result<Option<sipx_media::SrtpKeys>> {
6960    if offered.is_empty() {
6961        // Nothing was offered, so there is nothing to verify and no local half to key with. An
6962        // answer cannot introduce SDES the offer did not ask for (RFC 4568 §5.1.2).
6963        return Ok(None);
6964    }
6965    sipx_media::SrtpKeys::from_answer(offered, answered)
6966        .map(Some)
6967        .map_err(|error| Error::Sdp(error.to_string()))
6968}
6969
6970/// Pair the key we are *answering* with against the far end's offered one.
6971///
6972/// The other side of [`srtp_keys`], and deliberately not the same function. §5.1.3's check is the
6973/// offerer's: here this side chose the attribute and echoed its tag ([`sipx_sdp::answer`], RFC
6974/// 4568 §5.1.2), so there is nothing to verify — only two halves to put together.
6975///
6976/// `None` unless *both* are present. One key is not a session: a stream keyed at one end only
6977/// is a stream the other end cannot read, and treating a half-offer as success would produce a
6978/// call that connects and carries silence.
6979pub(crate) fn srtp_keys_answering(
6980    ours: Option<&sipx_sdp::crypto::Crypto>,
6981    theirs: Option<sipx_sdp::crypto::Crypto>,
6982) -> Option<sipx_media::SrtpKeys> {
6983    let (ours, theirs) = (ours?, theirs?);
6984    Some(sipx_media::SrtpKeys {
6985        local: (ours.master_key().to_vec(), ours.master_salt().to_vec()),
6986        remote: (theirs.master_key().to_vec(), theirs.master_salt().to_vec()),
6987    })
6988}
6989
6990/// The keying the far end offered, from its description. Same shape as the answered one; named
6991/// separately because reading it from an *offer* and from an *answer* are different moments.
6992pub(crate) fn offer_crypto(sdp: &SessionDescription) -> Option<sipx_sdp::crypto::Crypto> {
6993    answered_crypto(sdp)
6994}
6995
6996/// The keying the far end answered with, from its description.
6997fn answered_crypto(sdp: &SessionDescription) -> Option<sipx_sdp::crypto::Crypto> {
6998    sdp.media
6999        .iter()
7000        .find(|m| m.media == "audio" && !m.is_rejected())?
7001        .crypto()
7002}
7003
7004/// The payload type carrying `telephone-event`, per the description's own rtpmaps.
7005fn telephone_event_payload_type(audio: &sipx_sdp::MediaDescription) -> Option<u8> {
7006    audio.formats.iter().find_map(|format| {
7007        let mapping = audio.rtpmap(format)?;
7008        let encoding = mapping.split('/').next().unwrap_or(mapping);
7009        encoding
7010            .eq_ignore_ascii_case("telephone-event")
7011            .then(|| format.parse::<u8>().ok())
7012            .flatten()
7013    })
7014}
7015
7016/// Where to send media, and in what codec, from a description.
7017///
7018/// `codecs` is the set this side offered or answered from: negotiation may only settle on a
7019/// codec the application selected, so an Opus offer answered from a G.711 set settles on
7020/// G.711, not on a codec the answer never named.
7021pub(crate) fn negotiated(sdp: &SessionDescription, codecs: Codecs) -> Result<Negotiated> {
7022    let audio = sdp
7023        .media
7024        .iter()
7025        .find(|m| m.media == "audio" && !m.is_rejected())
7026        .ok_or(Error::NoCommonCodec)?;
7027
7028    // A stream marked `inactive` carries nothing in either direction. Treating it as a working
7029    // call means holding a media session open for audio that will never come.
7030    if audio.direction() == Direction::Inactive {
7031        return Err(Error::NoCommonCodec);
7032    }
7033
7034    let address = sdp.address_for(audio).ok_or(Error::NoCommonCodec)?;
7035
7036    // The first format both sides can carry. The list is already in the offerer's preference
7037    // order, so the first playable one is the one to use. Playable is judged by what the
7038    // format's rtpmap says, never by a dynamic number alone — which is also the reason
7039    // `Codec::from_payload_type` deliberately never returns Opus: 111 is Opus here only because
7040    // this description said so.
7041    //
7042    // `sipx_sdp::answer` decides the same question when it builds the answer that goes on the
7043    // wire, and the two *must* agree: this settles what the session sends, and the answer is what
7044    // the far end was told to expect. They now agree by construction — both ask
7045    // `sipx_sdp::rtpmap::same_format` whether an offered rtpmap names a format this side has, so
7046    // there is one rule rather than two readings of it (`M-31`). What is left of the difference is
7047    // deliberate and one-directional: the answer also names `telephone-event`, which is not a
7048    // codec to settle on. `the_answer_and_the_negotiated_codec_agree` holds the agreement over a
7049    // table of offers, so this paragraph is a claim with a test under it rather than a hope.
7050    //
7051    // `carries` is part of the search and not a test applied to its result. Rejecting afterwards
7052    // would stop at the offerer's first choice and refuse the whole description if that one
7053    // format is outside our set — so an Opus-first offer reaching a G.711 call would come back
7054    // `NoCommonCodec` while the answer this side builds happily names the PCMU further down the
7055    // same list.
7056    let (codec, payload_type, clock_rate) = audio
7057        .formats
7058        .iter()
7059        .find_map(|format| {
7060            codec_of(audio, format)
7061                .filter(|(codec, _, clock_rate)| codecs.carries_format(*codec, *clock_rate))
7062        })
7063        .ok_or(Error::NoCommonCodec)?;
7064
7065    Ok(Negotiated {
7066        remote: SocketAddr::new(address, audio.port),
7067        codec,
7068        clock_rate,
7069        payload_type,
7070        receive_payload_type: payload_type,
7071        dtmf: telephone_event_payload_type(audio),
7072        // On the answering side this is the offer's request, which sipx accepts. On the offering
7073        // side `settle_answer` additionally requires that this side actually offered the flag.
7074        rtcp_mode: if audio.rtcp_mux() {
7075            sipx_sdp::RtcpMode::Mux
7076        } else {
7077            sipx_sdp::RtcpMode::Separate
7078        },
7079    })
7080}
7081
7082/// The codec a format names, and the payload type to put on the wire for it.
7083///
7084/// A format with an rtpmap is matched by the map: RFC 8866 §6.6 makes it authoritative even
7085/// for a static number, which is how an offer of `8` meaning iLBC is not read as PCMA. The
7086/// number is then *dynamic in meaning* — the map could have hung any name on it — so it goes
7087/// home with the codec rather than being reassumed from [`Codec::payload_type`]. Only a bare
7088/// static type, with no map at all, is matched by number.
7089fn codec_of(audio: &sipx_sdp::MediaDescription, format: &str) -> Option<(Codec, Option<u8>, u32)> {
7090    let payload = format.parse::<u8>().ok()?;
7091    if let Some(rtpmap) = audio.rtpmap(format) {
7092        return codec_format(rtpmap).map(|(codec, clock_rate)| (codec, Some(payload), clock_rate));
7093    }
7094    Codec::from_payload_type(payload).map(|codec| (codec, None, codec.clock_rate()))
7095}
7096
7097/// The codec and RTP clock an rtpmap names.
7098///
7099/// L16 is special only in having more than one supported rate. Its name and mono channel count
7100/// are still parsed by SDP's shared format reader; policy decides below whether the exact rate
7101/// was offered locally.
7102fn codec_format(rtpmap: &str) -> Option<(Codec, u32)> {
7103    let parsed = sipx_sdp::rtpmap::Rtpmap::parse(rtpmap).ok()?;
7104    if parsed.encoding().eq_ignore_ascii_case("L16") && parsed.channels() == 1 {
7105        return Some((Codec::L16, parsed.clock_rate()));
7106    }
7107    codec_named(rtpmap).map(|codec| (codec, codec.clock_rate()))
7108}
7109
7110/// The codec an rtpmap value names, if it is one we carry.
7111///
7112/// **The matching rule is not written here.** [`sipx_sdp::rtpmap::same_format`] decides whether two
7113/// `a=rtpmap` values name the same format, and this asks it once per codec sipx can run, against
7114/// the value that codec is offered with. It used to be written out a second time in this function,
7115/// with the clock rate parsed to a `u32` where [`sipx_sdp::answer`] compared the same field as
7116/// text — so the answer on the wire and the codec the session was built with could name different
7117/// formats for one offer (`M-31`).
7118///
7119/// `sipx-sdp` is the authority and not this crate, because the dependency only runs one way:
7120/// [`sipx_sdp::answer`] builds the answer sipx sends and cannot call up into `sipx-call`, so the
7121/// only arrangement in which one implementation serves both is the lower crate holding it. What
7122/// stays here is the half `sipx-sdp` must not learn — which rtpmaps sipx has a codec for, and
7123/// which codecs the application selected.
7124///
7125/// The order of the search does not matter: the values in [`carried`] are distinct formats, so an
7126/// rtpmap matches at most one of them. Preference order is the *offerer's*, and it is applied by
7127/// [`negotiated`] walking `m=`'s format list.
7128fn codec_named(rtpmap: &str) -> Option<Codec> {
7129    carried()
7130        .iter()
7131        .copied()
7132        .find(|&codec| sipx_sdp::rtpmap::same_format(rtpmap, offered_rtpmap(codec)))
7133}
7134
7135/// Every codec sipx can run, and can therefore read out of an rtpmap.
7136///
7137/// Omitting a new [`Codec`] variant here means it is simply never named by an offer, which is the
7138/// safe direction to fail in — the same reasoning as [`Codecs::carries`]. The exhaustive match in
7139/// [`offered_rtpmap`] is what forces someone to decide.
7140fn carried() -> &'static [Codec] {
7141    &[
7142        Codec::Pcmu,
7143        Codec::Pcma,
7144        Codec::L16,
7145        #[cfg(feature = "opus")]
7146        Codec::Opus,
7147    ]
7148}
7149
7150/// The `a=rtpmap` value sipx offers a codec with.
7151///
7152/// The same strings [`sipx_sdp::Capabilities::g711`] and [`sipx_sdp::Capabilities::with_opus`] put
7153/// on the wire, and they have to be: a codec whose value here disagreed with the one offered would
7154/// be a codec negotiation settles on and no answer ever names, which is the whole of `M-31`.
7155/// `the_answer_and_the_negotiated_codec_agree` is what holds the two together, rather than a
7156/// comment asking them to match.
7157///
7158/// RFC 7587 §7 fixes Opus's RTP clock at 48000 and its rtpmap channel count at 2 whatever the
7159/// audio actually is, so `opus/16000` is nothing we have however it is numbered.
7160const fn offered_rtpmap(codec: Codec) -> &'static str {
7161    match codec {
7162        Codec::Pcmu => "PCMU/8000",
7163        Codec::Pcma => "PCMA/8000",
7164        Codec::L16 => "L16/44100/1",
7165        #[cfg(feature = "opus")]
7166        Codec::Opus => "opus/48000/2",
7167    }
7168}
7169
7170/// The `Contact` this endpoint should advertise for a dialog on this transport.
7171///
7172/// Built from the endpoint's *advertised* address rather than its socket's local one. An
7173/// endpoint bound to `0.0.0.0` has a local address that means nothing to a peer, and behind a
7174/// NAT it is private — either way the peer stores it as the dialog's remote target and every
7175/// in-dialog request it sends becomes unroutable.
7176///
7177/// The transport matters because over a WebSocket there is no address to advertise at all: the
7178/// endpoint gives the invented name RFC 7118 §5.2 requires, and marks the URI with the same
7179/// transport token it puts in the `Via`, so a peer that does route on `Contact` knows not to
7180/// try `sip:` on port 5060.
7181#[must_use]
7182pub fn contact_for(endpoint: &Handle, transport: TransportKind) -> String {
7183    match transport {
7184        TransportKind::Ws | TransportKind::Wss | TransportKind::Quic => format!(
7185            "<sip:sipx@{};transport={}>",
7186            endpoint.sent_by_for(transport),
7187            transport.as_str().to_ascii_lowercase()
7188        ),
7189        _ => format!("<sip:sipx@{}>", endpoint.advertised()),
7190    }
7191}
7192
7193/// Answer an INVITE that asks to take the place of an existing call (RFC 3891).
7194///
7195/// The second half of an attended transfer: the transferor has spoken to the target, and hands
7196/// its original call over by telling one party to call the other with a `Replaces` header
7197/// naming the dialog to displace.
7198///
7199/// **The header must name `replaced`, all three fields of it.** A `Call-ID` travels in every
7200/// message of a dialog and is visible to every element on the path; the tags are random and
7201/// known only to the two parties. Accepting a match on the `Call-ID` alone — or trusting the
7202/// caller to have checked — turns this into a call-hijack primitive, so the check is here and
7203/// not in whoever calls it.
7204///
7205/// On success the replaced call is hung up and its media torn down. On failure the new INVITE
7206/// is refused and the existing call is left exactly as it was: a replacement that cannot be
7207/// honoured must not cost the user the call they already had.
7208///
7209/// Answers from the default codec set, [`Codecs::G711`]. [`answer_replacing_with`] takes a
7210/// selection.
7211pub async fn answer_replacing(
7212    endpoint: &Handle,
7213    incoming: &Incoming,
7214    media_address: IpAddr,
7215    replaced: &mut Call,
7216) -> Result<Call> {
7217    answer_replacing_with(
7218        endpoint,
7219        incoming,
7220        media_address,
7221        replaced,
7222        Codecs::default(),
7223    )
7224    .await
7225}
7226
7227/// [`answer_replacing`], from a chosen codec set rather than the default one (`M-30`).
7228///
7229/// `codecs` applies to the *replacement*, which is the only call being negotiated here. The one
7230/// being replaced is hung up, and nothing renegotiates it on the way out.
7231pub async fn answer_replacing_with(
7232    endpoint: &Handle,
7233    incoming: &Incoming,
7234    media_address: IpAddr,
7235    replaced: &mut Call,
7236    codecs: Codecs,
7237) -> Result<Call> {
7238    let Some(asked_for) = Replaces::of(&incoming.request) else {
7239        refuse_request(endpoint, incoming, 400, "Bad Request").await?;
7240        return Err(Error::NoReplaces);
7241    };
7242
7243    if !asked_for.matches(&replaced.dialog) {
7244        // 481, which RFC 3891 §3 asks for and which also gives nothing away: a caller guessing
7245        // tags gets the same answer whether the Call-ID was right or not, so there is nothing
7246        // to search.
7247        refuse_request(endpoint, incoming, 481, "Call/Transaction Does Not Exist").await?;
7248        return Err(Error::NoReplaces);
7249    }
7250
7251    // Answer first. If this fails the old call is untouched, which is the right way round:
7252    // hanging up first and then failing to answer would leave the user with no call at all.
7253    let taken_over = answer_with(endpoint, incoming, media_address, codecs).await?;
7254
7255    // Then end the one being replaced (RFC 3891 §3). Its media stops with it.
7256    //
7257    // discard: the BYE this sends is counted at the transmit as
7258    // `sipx_transport::UnsentCounts::bye` if the endpoint cannot put it on the wire. The `Result`
7259    // is discarded because the takeover has already succeeded on the line above and reporting a
7260    // teardown failure as the *transfer* failing would be false — the caller has the new call
7261    // either way, and `Call::end` has already marked the old one ended locally before the BYE was
7262    // ever built.
7263    let _ = replaced.hang_up().await;
7264
7265    Ok(taken_over)
7266}
7267
7268/// Refuse a request outright.
7269async fn refuse_request(
7270    endpoint: &Handle,
7271    incoming: &Incoming,
7272    status: u16,
7273    reason: &'static str,
7274) -> Result<()> {
7275    let Some(code) = StatusCode::new(status) else {
7276        return Ok(());
7277    };
7278    let response = ResponseBuilder::to_request(&incoming.request, code, reason)?.build();
7279    endpoint.respond(&incoming.key, response).await?;
7280    Ok(())
7281}
7282
7283#[cfg(test)]
7284#[allow(
7285    clippy::unwrap_used,
7286    clippy::expect_used,
7287    clippy::panic,
7288    clippy::indexing_slicing
7289)]
7290mod tests {
7291    use std::fmt::Write as _;
7292    use std::sync::atomic::{AtomicBool, Ordering};
7293    use std::task::Poll;
7294
7295    use super::*;
7296
7297    #[tokio::test]
7298    async fn successful_response_stop_before_first_poll_is_latched() {
7299        let stop = CancellationToken::new();
7300        stop.cancel();
7301        let operation_polled = Arc::new(AtomicBool::new(false));
7302        let observed = Arc::clone(&operation_polled);
7303        let operation = std::future::poll_fn(move |_context| {
7304            observed.store(true, Ordering::SeqCst);
7305            Poll::<()>::Pending
7306        });
7307
7308        assert!(until_cancelled(&stop, operation).await.is_none());
7309        assert!(
7310            !operation_polled.load(Ordering::SeqCst),
7311            "latched cancellation wins before the handoff is first polled"
7312        );
7313    }
7314
7315    #[tokio::test]
7316    async fn successful_response_stop_interrupts_a_pending_handoff() {
7317        let stop = CancellationToken::new();
7318        let worker_stop = stop.clone();
7319        let (polled_tx, polled_rx) = tokio::sync::oneshot::channel();
7320        let mut polled_tx = Some(polled_tx);
7321        let worker = tokio::spawn(async move {
7322            until_cancelled(
7323                &worker_stop,
7324                std::future::poll_fn(move |_context| {
7325                    if let Some(polled) = polled_tx.take() {
7326                        let _ = polled.send(());
7327                    }
7328                    Poll::<()>::Pending
7329                }),
7330            )
7331            .await
7332        });
7333        polled_rx.await.expect("the handoff is pending");
7334
7335        stop.cancel();
7336        assert_eq!(worker.await.expect("worker joins"), None);
7337    }
7338
7339    #[tokio::test]
7340    async fn answer_setup_failure_cancels_and_joins_its_retransmitter() {
7341        let stop = CancellationToken::new();
7342        let worker_stop = stop.clone();
7343        let finished = CancellationToken::new();
7344        let worker_finished = finished.clone();
7345        let mut owner = OwnedTask::new(tokio::spawn(async move {
7346            let _ = until_cancelled(&worker_stop, std::future::pending::<()>()).await;
7347            worker_finished.cancel();
7348        }));
7349
7350        cancel_and_join(&stop, &mut owner).await;
7351        assert!(
7352            finished.is_cancelled(),
7353            "setup failure returns only after the retransmitter exits"
7354        );
7355    }
7356
7357    struct TestRetired {
7358        entered: CancellationToken,
7359        release: CancellationToken,
7360    }
7361
7362    impl Retirable for TestRetired {
7363        async fn finish(&self) {
7364            self.entered.cancel();
7365            self.release.cancelled().await;
7366        }
7367    }
7368
7369    #[tokio::test]
7370    async fn cancelled_confirmed_replacement_retains_the_old_owner_for_retry() {
7371        let entered = CancellationToken::new();
7372        let release = CancellationToken::new();
7373        let mut retired = vec![TestRetired {
7374            entered: entered.clone(),
7375            release: release.clone(),
7376        }];
7377        let mut draining = Box::pin(drain_retired(&mut retired));
7378        tokio::select! {
7379            () = entered.cancelled() => {}
7380            () = &mut draining => panic!("retired generation completed before release"),
7381        }
7382        drop(draining);
7383        assert_eq!(retired.len(), 1, "cancellation preserved the old owner");
7384        assert_eq!(
7385            retired_media_snapshot_refusal(retired.len()),
7386            Some(DialogNotQuiescent::MediaCleanup),
7387            "snapshot capture refuses while cleanup ownership is retained"
7388        );
7389
7390        release.cancel();
7391        drain_retired(&mut retired).await;
7392        assert!(retired.is_empty(), "retry joined and removed the old owner");
7393        assert_eq!(retired_media_snapshot_refusal(retired.len()), None);
7394    }
7395
7396    /// M-49's pre-I/O boundary is pure: failure cannot have bound or gathered a socket.
7397    #[cfg(all(feature = "opus", feature = "dtls"))]
7398    #[test]
7399    fn browser_audio_preflight_is_fail_closed_before_io() {
7400        let policy = MediaPolicy::browser_audio();
7401        assert!(validate_profile_preflight(policy, TransportKind::Wss).is_ok());
7402        assert!(matches!(
7403            validate_profile_preflight(policy, TransportKind::Udp),
7404            Err(Error::Profile(
7405                sipx_sdp::browser_audio::ProfileError::InsecureSignalling
7406            ))
7407        ));
7408        assert!(matches!(
7409            validate_profile_preflight(policy.with_ice(IcePolicy::Disabled), TransportKind::Wss),
7410            Err(Error::Profile(
7411                sipx_sdp::browser_audio::ProfileError::IceRequired
7412            ))
7413        ));
7414        assert!(matches!(
7415            validate_profile_preflight(policy.with_keying(Keying::Plain), TransportKind::Wss),
7416            Err(Error::Profile(
7417                sipx_sdp::browser_audio::ProfileError::WeakerMedia
7418            ))
7419        ));
7420        let opus_only = Codecs::ordered(&[crate::CodecPreference::Opus]).expect("Opus build");
7421        assert!(matches!(
7422            validate_profile_preflight(policy.with_codecs(opus_only), TransportKind::Wss),
7423            Err(Error::Profile(
7424                sipx_sdp::browser_audio::ProfileError::CodecSetIncomplete
7425            ))
7426        ));
7427    }
7428
7429    #[cfg(not(feature = "opus"))]
7430    #[test]
7431    fn browser_audio_reports_missing_opus_as_a_typed_pre_io_error() {
7432        assert!(matches!(
7433            validate_profile_preflight(MediaPolicy::browser_audio(), TransportKind::Wss),
7434            Err(Error::Profile(
7435                sipx_sdp::browser_audio::ProfileError::OpusUnavailable
7436            ))
7437        ));
7438    }
7439
7440    #[cfg(all(feature = "opus", feature = "dtls"))]
7441    async fn browser_answer_fixture() -> (
7442        SessionDescription,
7443        SessionDescription,
7444        sipx_media::ice::LocalDescription,
7445        MediaPort,
7446    ) {
7447        let loopback: IpAddr = "127.0.0.1".parse().expect("loopback");
7448        let port = MediaPort::bind(SocketAddr::new(loopback, 0))
7449            .await
7450            .expect("offer port binds");
7451        let options = DialOptions::new("<sip:caller@example.invalid>", loopback)
7452            .with_media_policy(MediaPolicy::browser_audio());
7453        let (_capabilities, offer, local, _keying) =
7454            offered_media(&options, &port, TransportKind::Wss)
7455                .await
7456                .expect("browser offer gathers");
7457        let local = local.expect("browser offer retains ICE");
7458        let identity = sipx_media::dtls::openssl::Identity::generate().expect("answer identity");
7459        let fingerprint = identity.fingerprint().expect("answer fingerprint");
7460        let answer = sipx_sdp::browser_audio::answer(
7461            &offer,
7462            &sipx_sdp::browser_audio::BrowserAudioLocal {
7463                address: loopback,
7464                port: 40_000,
7465                session_id: 9_002,
7466                session_version: 1,
7467                direction: Direction::SendRecv,
7468                ice: sipx_sdp::ice::Credentials::new("peer", "peerPassword0123456789AB")
7469                    .expect("answer credentials"),
7470                candidates: vec![
7471                    sipx_sdp::ice::Candidate::parse(
7472                        "peer 1 UDP 2130706431 127.0.0.1 40000 typ host",
7473                    )
7474                    .expect("answer candidate"),
7475                ],
7476                fingerprint,
7477                setup: sipx_sdp::fingerprint::SetupCapabilities::both(),
7478            },
7479        )
7480        .expect("complete browser answer");
7481        (offer, answer, local, port)
7482    }
7483
7484    /// `M-49`: an incomplete final answer is refused at the call boundary before the retained ICE
7485    /// description accepts the peer half or any media owner can start.
7486    #[cfg(all(feature = "opus", feature = "dtls"))]
7487    #[tokio::test]
7488    async fn browser_answer_is_fully_validated_before_ice_state_changes() {
7489        let (offer, mut answer, local, _port) = browser_answer_fixture().await;
7490        answer.media[0]
7491            .attributes
7492            .retain(|attribute| attribute.name != "rtcp-mux");
7493        let ice_before = format!("{local:?}");
7494
7495        assert!(matches!(
7496            validate_establishment_answer(
7497                MediaProfile::BrowserAudio,
7498                offer.to_string_sdp().as_bytes(),
7499                &answer,
7500            ),
7501            Err(Error::Profile(
7502                sipx_sdp::browser_audio::ProfileError::RtcpMuxRequired
7503            ))
7504        ));
7505        assert_eq!(
7506            format!("{local:?}"),
7507            ice_before,
7508            "a refused answer did not reach LocalDescription::accept"
7509        );
7510    }
7511
7512    /// Generic codec negotiation permits an answer to change preference order; the named profile
7513    /// does not. The call boundary therefore uses the complete exchange validator, not only the
7514    /// parser for the answer in isolation.
7515    #[cfg(all(feature = "opus", feature = "dtls"))]
7516    #[tokio::test]
7517    async fn browser_answer_cannot_reorder_the_payloads_selected_by_the_offer() {
7518        let (offer, mut answer, local, _port) = browser_answer_fixture().await;
7519        answer.media[0].formats.swap(0, 1);
7520        let ice_before = format!("{local:?}");
7521
7522        assert!(matches!(
7523            validate_establishment_answer(
7524                MediaProfile::BrowserAudio,
7525                offer.to_string_sdp().as_bytes(),
7526                &answer,
7527            ),
7528            Err(Error::Profile(
7529                sipx_sdp::browser_audio::ProfileError::CodecSetIncomplete
7530            ))
7531        ));
7532        assert_eq!(
7533            format!("{local:?}"),
7534            ice_before,
7535            "a refused answer did not reach LocalDescription::accept"
7536        );
7537    }
7538
7539    /// The call boundary does not inherit the generic parser's extensible-candidate tolerance.
7540    /// Every browser-profile line must belong to the bounded host/server-reflexive set before the
7541    /// retained ICE generation is allowed to see any of them.
7542    #[cfg(all(feature = "opus", feature = "dtls"))]
7543    #[tokio::test]
7544    async fn browser_answer_rejects_every_unusable_candidate_before_ice_acceptance() {
7545        let (offer, answer, local, _port) = browser_answer_fixture().await;
7546        let ice_before = format!("{local:?}");
7547        for candidate in [
7548            "not a candidate",
7549            "relay 1 UDP 2130706430 127.0.0.1 40001 typ relay raddr 0.0.0.0 rport 9",
7550            "prflx 1 UDP 2130706429 127.0.0.1 40002 typ prflx raddr 0.0.0.0 rport 9",
7551        ] {
7552            let mut rejected = answer.clone();
7553            rejected.media[0]
7554                .attributes
7555                .push(sipx_sdp::Attribute::valued("candidate", candidate));
7556            assert!(matches!(
7557                validate_establishment_answer(
7558                    MediaProfile::BrowserAudio,
7559                    offer.to_string_sdp().as_bytes(),
7560                    &rejected,
7561                ),
7562                Err(Error::Profile(
7563                    sipx_sdp::browser_audio::ProfileError::IceRequired
7564                ))
7565            ));
7566            assert_eq!(
7567                format!("{local:?}"),
7568                ice_before,
7569                "candidate refusal changed retained ICE state: {candidate}"
7570            );
7571        }
7572    }
7573
7574    const IDENTIFIER_SAMPLE_SIZE: u64 = 4096;
7575
7576    fn bit_counts(values: impl IntoIterator<Item = u64>) -> [usize; 64] {
7577        let mut counts = [0; 64];
7578        for value in values {
7579            for (bit, count) in counts.iter_mut().enumerate() {
7580                *count += usize::from(value & (1_u64 << bit) != 0);
7581            }
7582        }
7583        counts
7584    }
7585
7586    /// Both reliable-provisional shapes use the same address pair: an offer in a 183 for an
7587    /// offerless INVITE, and an answer in a 183 for an INVITE carrying an offer.
7588    #[tokio::test]
7589    async fn early_media_binds_locally_and_advertises_the_chosen_address_in_both_roles() {
7590        let advertised: IpAddr = "198.51.100.44".parse().expect("valid");
7591        let bind: IpAddr = "127.0.0.1".parse().expect("valid");
7592        let addresses = MediaAddress::new(advertised).with_bind(bind);
7593
7594        let offered_early = EarlyOffer::bind(
7595            addresses,
7596            false,
7597            Direction::SendRecv,
7598            MediaPolicy::default(),
7599        )
7600        .await
7601        .expect("binds an early offer");
7602        assert_eq!(offered_early.port.local_addr().ip(), bind);
7603        assert_eq!(
7604            offered_early.description().connection,
7605            Some(Connection::new(advertised))
7606        );
7607
7608        let remote_offer = offered("0", &[]);
7609        let (answered_early, answer) =
7610            Early::settle(addresses, false, &remote_offer, MediaPolicy::default())
7611                .await
7612                .expect("binds an early answer");
7613        assert_eq!(answered_early.media.local_addr().ip(), bind);
7614        assert_eq!(answer.connection, Some(Connection::new(advertised)));
7615    }
7616
7617    /// RFC 3261 §19.3 makes a dialog tag a peer-visible identifier which must be hard to guess.
7618    /// Every hexadecimal position is sampled, rather than accepting a 64-bit-looking string whose
7619    /// high half is fixed by truncation or by a counter.
7620    #[test]
7621    fn dialog_tag_keeps_all_sixty_four_random_bits() {
7622        let values = (0..IDENTIFIER_SAMPLE_SIZE).map(|_| {
7623            let tag = token();
7624            assert_eq!(tag.len(), 16, "exactly 64 bits in hexadecimal");
7625            assert!(
7626                tag.bytes()
7627                    .all(|byte| byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase()),
7628                "the tag is canonical lowercase hexadecimal: {tag}"
7629            );
7630            u64::from_str_radix(&tag, 16).expect("the generator wrote hexadecimal")
7631        });
7632        for (bit, ones) in bit_counts(values).iter().copied().enumerate() {
7633            assert!(
7634                (1664..=2432).contains(&ones), // 128 positions * 2 * exp(-2 * 384^2 / 4096) < 1.4e-29.
7635                "dialog tag bit {bit} had {ones} ones in {IDENTIFIER_SAMPLE_SIZE} samples"
7636            );
7637        }
7638    }
7639
7640    /// `token_with_rng` makes the source property part of type checking, not an inference from a
7641    /// finite sample. A deterministic `RngCore` without `CryptoRng` cannot instantiate this call.
7642    #[test]
7643    fn dialog_tag_requires_a_cryptographic_rng_by_construction() {
7644        fn draw<R: rand::CryptoRng + ?Sized>(rng: &mut R) -> String {
7645            token_with_rng(rng)
7646        }
7647
7648        assert_eq!(draw(&mut rand::rng()).len(), 16);
7649    }
7650
7651    /// An audio description with the given formats and rtpmaps, as a peer would send it.
7652    fn offered(formats: &str, rtpmaps: &[&str]) -> SessionDescription {
7653        let mut body = format!(
7654            "v=0\r\n\
7655             o=- 1 1 IN IP4 192.0.2.1\r\n\
7656             s=-\r\n\
7657             c=IN IP4 192.0.2.1\r\n\
7658             t=0 0\r\n\
7659             m=audio 40000 RTP/AVP {formats}\r\n"
7660        );
7661        for rtpmap in rtpmaps {
7662            let _ = write!(body, "a=rtpmap:{rtpmap}\r\n");
7663        }
7664        sipx_sdp::parse(&body).expect("a description this test wrote")
7665    }
7666
7667    /// The default is the G.711 pair, in every build. The `opus` feature adds a variant to
7668    /// [`Codecs`]; it must never move which one `Default` produces, or turning the feature on to
7669    /// get the *option* of Opus would silently change what every existing call offers.
7670    #[test]
7671    fn the_default_codec_set_is_g711() {
7672        assert_eq!(Codecs::default(), Codecs::G711);
7673        let capabilities = Codecs::default().capabilities("192.0.2.9".parse().unwrap(), 40000);
7674        assert!(
7675            !capabilities
7676                .rtpmaps
7677                .iter()
7678                .any(|(_, value)| value.to_ascii_lowercase().contains("opus")),
7679            "the default offer names no Opus: {:?}",
7680            capabilities.rtpmaps
7681        );
7682    }
7683
7684    /// RFC 8866 §6.6 makes the rtpmap authoritative even over a static number. This is the rule
7685    /// that lets an Opus offer arrive at all — 111 means Opus only because the description said
7686    /// so — and the same rule refuses to read an offer of `8` remapped to something else as PCMA.
7687    #[test]
7688    fn a_format_is_read_from_its_rtpmap_and_not_from_its_number() {
7689        let remapped = offered("8 0", &["8 iLBC/8000", "0 PCMU/8000"]);
7690        let settled = negotiated(&remapped, Codecs::G711).expect("PCMU is common");
7691        assert_eq!(settled.codec, Codec::Pcmu);
7692        assert_eq!(
7693            settled.payload_type,
7694            Some(0),
7695            "the number the far end assigned travels with the codec"
7696        );
7697    }
7698
7699    /// A bare static type with no rtpmap at all is the one case matched by number, which is what
7700    /// keeps every G.711-only peer that sends `m=audio … 0 8` and nothing else working.
7701    #[test]
7702    fn a_bare_static_type_is_still_matched_by_number() {
7703        let settled = negotiated(&offered("0", &[]), Codecs::G711).expect("PCMU is static");
7704        assert_eq!(settled.codec, Codec::Pcmu);
7705        assert_eq!(
7706            settled.payload_type, None,
7707            "nothing named it, so nothing overrides `Codec::payload_type`"
7708        );
7709    }
7710
7711    /// M-43: RFC 3551 assigns mono L16 at 44.1 kHz to static payload 11. The adjacent payload
7712    /// 10 is stereo, which sipx's mono media surface deliberately does not claim.
7713    #[test]
7714    fn l16_static_payload_is_mono_at_forty_four_point_one_kilohertz() {
7715        let l16 = Codecs::ordered(&[crate::CodecPreference::L16]).expect("L16 selection");
7716        let settled = negotiated(&offered("11", &[]), l16).expect("static mono L16");
7717        assert_eq!(settled.codec, Codec::L16);
7718        assert_eq!(settled.payload_type, None);
7719        assert_eq!(settled.clock_rate, 44_100);
7720        assert!(matches!(
7721            negotiated(&offered("10", &[]), l16),
7722            Err(Error::NoCommonCodec)
7723        ));
7724    }
7725
7726    /// M-43: an L16 rate outside the static assignment is identified by rtpmap and its dynamic
7727    /// payload number travels with the session. Only rates this policy actually offers settle.
7728    #[test]
7729    fn l16_dynamic_payload_retains_its_explicit_clock_rate() {
7730        let l16 = Codecs::ordered(&[crate::CodecPreference::L16]).expect("L16 selection");
7731        let settled =
7732            negotiated(&offered("110", &["110 L16/8000/1"]), l16).expect("dynamic mono L16");
7733        assert_eq!(settled.codec, Codec::L16);
7734        assert_eq!(settled.payload_type, Some(110));
7735        assert_eq!(settled.clock_rate, 8_000);
7736
7737        assert!(
7738            matches!(
7739                negotiated(&offered("96", &["96 L16/16000/1"]), l16),
7740                Err(Error::NoCommonCodec)
7741            ),
7742            "an unoffered rate is not silently accepted"
7743        );
7744        assert!(
7745            matches!(
7746                negotiated(&offered("96", &["96 L16/8000/2"]), l16),
7747                Err(Error::NoCommonCodec)
7748            ),
7749            "the PCM API is mono"
7750        );
7751    }
7752
7753    /// Each SDP description owns its dynamic assignment. An 8 kHz L16 answer may send on 110
7754    /// while receiving on the 96 this side offered, with one shared negotiated clock.
7755    #[test]
7756    fn l16_answer_keeps_directional_dynamic_payload_assignments() {
7757        let l16 = Codecs::L16;
7758        let capabilities = l16.capabilities("192.0.2.9".parse().expect("address"), 40_000);
7759        let answer = offered("110", &["110 L16/8000/1"]);
7760        let settled = settle_answer(&capabilities, &answer, l16).expect("dynamic L16 answer");
7761        assert_eq!(settled.negotiated.codec, Codec::L16);
7762        assert_eq!(settled.negotiated.clock_rate, 8_000);
7763        assert_eq!(settled.negotiated.wire_payload_type(), 110);
7764        assert_eq!(settled.negotiated.receive_wire_payload_type(), 96);
7765    }
7766
7767    /// The clock rate and channel count are part of a format's identity (RFC 8866 §6.6), so a
7768    /// name sipx knows at a rate it does not is not a match.
7769    #[test]
7770    fn a_known_name_at_an_unknown_clock_rate_is_not_a_match() {
7771        assert_eq!(codec_named("PCMU/16000"), None);
7772        assert_eq!(codec_named("opus/16000/2"), None);
7773        assert_eq!(codec_named("PCMU/8000"), Some(Codec::Pcmu));
7774        assert_eq!(codec_named("pcma/8000"), Some(Codec::Pcma));
7775    }
7776
7777    /// The default build has no Opus, so an offer of it is not a codec that build can carry —
7778    /// and the offer is answered from what *is* common rather than refused. This is the promise
7779    /// the `opus` feature is off by default in order to make: `tests/opus.rs` is gated on the
7780    /// feature and cannot assert anything about the build that lacks it.
7781    #[cfg(not(feature = "opus"))]
7782    #[test]
7783    fn a_default_build_does_not_carry_an_offered_opus() {
7784        assert_eq!(codec_named("opus/48000/2"), None);
7785        let opus_first = offered("111 0", &["111 opus/48000/2", "0 PCMU/8000"]);
7786        let settled = negotiated(&opus_first, Codecs::G711).expect("G.711 is still offered");
7787        assert_eq!(settled.codec, Codec::Pcmu, "the first format sipx carries");
7788    }
7789
7790    /// Selecting a set is what puts a codec on the table, and negotiation may not step outside
7791    /// it. An Opus offer answered from [`Codecs::G711`] settles on G.711 — not because Opus is
7792    /// absent from the build, but because the answer this side builds never named it, and a
7793    /// session started on a codec no answer named sends packets the far end cannot place.
7794    #[cfg(feature = "opus")]
7795    #[test]
7796    fn negotiation_does_not_settle_outside_the_selected_set() {
7797        assert_eq!(codec_named("opus/48000/2"), Some(Codec::Opus));
7798        let opus_first = offered("111 0", &["111 opus/48000/2", "0 PCMU/8000"]);
7799
7800        let from_g711 = negotiated(&opus_first, Codecs::G711).expect("G.711 is still offered");
7801        assert_eq!(from_g711.codec, Codec::Pcmu);
7802
7803        let from_opus = negotiated(&opus_first, Codecs::Opus).expect("Opus is on the table");
7804        assert_eq!(from_opus.codec, Codec::Opus);
7805        assert_eq!(
7806            from_opus.payload_type,
7807            Some(111),
7808            "on the number this offer assigned, not on a number 111 means by itself"
7809        );
7810    }
7811
7812    /// A peer may spell a static type either way — `m=audio … 0` alone, or the same thing with a
7813    /// redundant `a=rtpmap:0 PCMU/8000` — and RFC 8866 §6.6 allows both for the same codec.
7814    ///
7815    /// So moving between the two spellings is not a *change*, and [`Call::move_media_if_changed`]
7816    /// must not rebuild the session for it: rebuilding costs an audible gap, and some peers
7817    /// re-INVITE every thirty seconds as a keep-alive. `negotiated` does record the difference —
7818    /// `Some(0)` against `None`, which is a true fact about what the description said — so the
7819    /// comparison is on [`Negotiated::wire_payload_type`], where the two collapse to the one byte
7820    /// that actually goes out.
7821    #[test]
7822    fn a_redundant_rtpmap_for_a_static_type_is_not_a_change() {
7823        let mapped = negotiated(&offered("0", &["0 PCMU/8000"]), Codecs::G711).expect("PCMU");
7824        let bare = negotiated(&offered("0", &[]), Codecs::G711).expect("PCMU");
7825
7826        assert_eq!(mapped.codec, bare.codec);
7827        assert_eq!(mapped.payload_type, Some(0), "the rtpmap named it");
7828        assert_eq!(bare.payload_type, None, "nothing named it");
7829        assert_eq!(
7830            mapped.wire_payload_type(),
7831            bare.wire_payload_type(),
7832            "the same byte goes on the wire either way, so the session must not move",
7833        );
7834    }
7835
7836    /// S-36 / RFC 3264 §6.1: the offer and answer may assign different dynamic numbers to the
7837    /// same format. The answer's number is what we send; the offer's remains what we receive.
7838    #[test]
7839    fn an_asymmetric_answer_keeps_each_directions_payload_number() {
7840        let address = "192.0.2.9".parse().expect("address");
7841        let mut capabilities = Capabilities::g711(address, 40_000);
7842        capabilities.audio_formats = vec!["111".to_owned()];
7843        capabilities.rtpmaps = vec![("111".to_owned(), "PCMU/8000".to_owned())];
7844        let answer = offered("96", &["96 PCMU/8000"]);
7845
7846        let settled = settle_answer(&capabilities, &answer, Codecs::G711).expect("same format");
7847        assert_eq!(settled.negotiated.wire_payload_type(), 96);
7848        assert_eq!(settled.negotiated.receive_wire_payload_type(), 111);
7849        let config = settled.media_config();
7850        assert_eq!(
7851            config.wire_payload_type(),
7852            96,
7853            "send with the peer's number"
7854        );
7855        assert_eq!(
7856            config.receive_wire_payload_type(),
7857            111,
7858            "receive with our number"
7859        );
7860    }
7861
7862    /// An *answer* naming a codec outside the selected set is refused, so nothing keys a session
7863    /// on it.
7864    ///
7865    /// Pinned separately from `negotiated` because of where the refusal lands rather than what it
7866    /// returns. It is a failure mode `M-30` adds to `settle_answer`, which had no codec opinion
7867    /// before; on this branch an early answer that trips it is swallowed by
7868    /// `Dialing::adopt_early_answer`, but that function propagates on `main` after `S-25`, so once
7869    /// the two are merged this same refusal ends the invitation over a CANCEL. That is a call
7870    /// termination neither branch produces alone, which is why the precondition is worth holding
7871    /// here rather than waiting for the merge to discover it.
7872    ///
7873    /// True in both feature configurations for two different reasons: with `opus` off no rtpmap can
7874    /// name Opus at all, and with it on `Codecs::G711` does not carry it.
7875    #[test]
7876    fn an_answer_outside_the_selected_set_is_refused() {
7877        let opus_only = offered("111", &["111 opus/48000/2"]);
7878        let capabilities =
7879            Capabilities::g711("127.0.0.1".parse().expect("loopback address"), 40_000);
7880        assert!(matches!(
7881            settle_answer(&capabilities, &opus_only, Codecs::G711),
7882            Err(Error::NoCommonCodec)
7883        ));
7884    }
7885
7886    #[test]
7887    fn the_initial_call_offer_requests_rtcp_mux() {
7888        let (capabilities, _keying) = media_capabilities(
7889            MediaPolicy::default(),
7890            "127.0.0.1".parse().expect("loopback address"),
7891            40_000,
7892            false,
7893        )
7894        .expect("default media capabilities");
7895        let offer = offer_from(&capabilities);
7896
7897        assert!(capabilities.rtcp_mux);
7898        assert!(offer.media.first().expect("audio offer").rtcp_mux());
7899    }
7900
7901    #[test]
7902    fn the_answer_settles_mux_or_the_separate_port_fallback_without_a_retry() {
7903        let capabilities =
7904            Capabilities::g711("127.0.0.1".parse().expect("loopback address"), 40_000)
7905                .with_rtcp_mux();
7906        let separate_answer = offered("0", &["0 PCMU/8000"]);
7907        let separate = settle_answer(&capabilities, &separate_answer, Codecs::G711)
7908            .expect("the answer remains usable");
7909        assert_eq!(separate.negotiated.rtcp_mode, sipx_sdp::RtcpMode::Separate);
7910
7911        let mut mux_answer = separate_answer;
7912        mux_answer
7913            .media
7914            .first_mut()
7915            .expect("audio answer")
7916            .attributes
7917            .push(sipx_sdp::Attribute::flag("rtcp-mux"));
7918        let mux = settle_answer(&capabilities, &mux_answer, Codecs::G711)
7919            .expect("the muxed answer remains usable");
7920        assert_eq!(mux.negotiated.rtcp_mode, sipx_sdp::RtcpMode::Mux);
7921
7922        let not_offered =
7923            Capabilities::g711("127.0.0.1".parse().expect("loopback address"), 40_000);
7924        let unasked = settle_answer(&not_offered, &mux_answer, Codecs::G711)
7925            .expect("an unasked attribute does not break the answer");
7926        assert_eq!(
7927            unasked.negotiated.rtcp_mode,
7928            sipx_sdp::RtcpMode::Separate,
7929            "an answer cannot negotiate a feature that was not offered"
7930        );
7931    }
7932
7933    /// A running one-port session cannot accept an in-dialog offer that drops mux while retaining
7934    /// its old socket owner. The same typed guard is used before inbound state is applied.
7935    #[test]
7936    fn an_inbound_reoffer_cannot_remove_the_running_mux_mode() {
7937        let offered_without_mux = offered("0", &["0 PCMU/8000"]);
7938        let answered_without_mux = offered("0", &["0 PCMU/8000"]);
7939        let proposed = exchanged_rtcp_mode(&offered_without_mux, &answered_without_mux);
7940
7941        assert!(matches!(
7942            preserve_rtcp_mode(sipx_sdp::RtcpMode::Mux, proposed),
7943            Err(Error::RtcpModeChange {
7944                current: sipx_sdp::RtcpMode::Mux,
7945                proposed: sipx_sdp::RtcpMode::Separate,
7946            })
7947        ));
7948    }
7949
7950    /// The outbound mirror: omission in an answer to a later offer is an explicit failure and
7951    /// leaves the established mux session in place instead of binding an unadvertised replacement.
7952    #[test]
7953    fn an_outbound_reoffer_answer_cannot_remove_the_running_mux_mode() {
7954        let answer_without_mux = offered("0", &["0 PCMU/8000"]);
7955        let renegotiated = negotiated(&answer_without_mux, Codecs::G711).expect("PCMU answer");
7956
7957        assert!(matches!(
7958            preserve_rtcp_mode(sipx_sdp::RtcpMode::Mux, renegotiated.rtcp_mode),
7959            Err(Error::RtcpModeChange {
7960                current: sipx_sdp::RtcpMode::Mux,
7961                proposed: sipx_sdp::RtcpMode::Separate,
7962            })
7963        ));
7964    }
7965
7966    /// Session-level RFC 4145 roles are resolved identically by both call roles, including the
7967    /// passive answer that makes the offerer the DTLS client.
7968    #[test]
7969    fn session_level_setup_selects_the_complementary_call_role() {
7970        let mut offer = offered("0", &["0 PCMU/8000"]);
7971        offer
7972            .attributes
7973            .push(sipx_sdp::Attribute::valued("setup", "actpass"));
7974        assert_eq!(
7975            dtls_local_setup(&offer, true).expect("answerer role"),
7976            sipx_sdp::fingerprint::Setup::Active
7977        );
7978
7979        for (answer, expected) in [
7980            ("active", sipx_sdp::fingerprint::Setup::Passive),
7981            ("passive", sipx_sdp::fingerprint::Setup::Active),
7982        ] {
7983            let mut description = offered("0", &["0 PCMU/8000"]);
7984            description
7985                .attributes
7986                .push(sipx_sdp::Attribute::valued("setup", answer));
7987            assert_eq!(
7988                dtls_local_setup(&description, false).expect("offerer role"),
7989                expected
7990            );
7991        }
7992    }
7993
7994    /// `SETUP-2` through the actual call/media boundary: a passive answer makes the sipx offerer
7995    /// run the DTLS client handshake, and a real server completes it with both fingerprints
7996    /// verified before the returned media session starts.
7997    #[cfg(feature = "dtls")]
7998    #[tokio::test]
7999    async fn a_passive_answer_wires_the_offerer_as_the_dtls_client() {
8000        let client_port = MediaPort::bind("127.0.0.1:0".parse().expect("client address"))
8001            .await
8002            .expect("binds client");
8003        let server_port = MediaPort::bind("127.0.0.1:0".parse().expect("server address"))
8004            .await
8005            .expect("binds server");
8006        let client_address = client_port.local_addr();
8007        let server_address = server_port.local_addr();
8008        let client_identity =
8009            sipx_media::dtls::openssl::Identity::generate().expect("client identity");
8010        let server_identity =
8011            sipx_media::dtls::openssl::Identity::generate().expect("server identity");
8012        let client_fingerprint = client_identity.fingerprint().expect("client fingerprint");
8013        let server_fingerprint = server_identity.fingerprint().expect("server fingerprint");
8014
8015        let mut passive_answer = offered("0", &["0 PCMU/8000"]);
8016        passive_answer.attributes.extend([
8017            sipx_sdp::Attribute::valued("setup", "passive"),
8018            sipx_sdp::Attribute::valued("fingerprint", server_fingerprint.to_value()),
8019        ]);
8020        let settled = Settled {
8021            negotiated: Negotiated {
8022                remote: server_address,
8023                codec: Codec::Pcmu,
8024                clock_rate: 8_000,
8025                payload_type: Some(0),
8026                receive_payload_type: Some(0),
8027                dtmf: None,
8028                rtcp_mode: sipx_sdp::RtcpMode::Separate,
8029            },
8030            srtp: None,
8031        };
8032        let handshake_bound = Duration::from_secs(5); // Bounds a failed handshake; not ordering.
8033        let server = server_port.key_with_dtls(
8034            server_identity,
8035            client_address,
8036            sipx_media::dtls::Role::Server,
8037            client_fingerprint,
8038            handshake_bound,
8039        );
8040        let client = key_and_start(
8041            client_port,
8042            None,
8043            settled,
8044            PendingKeying::Dtls(client_identity),
8045            &passive_answer,
8046            false,
8047            MediaProfile::Standard,
8048        );
8049
8050        let (server, client) = tokio::join!(server, client);
8051        let (_server_port, _server_keys) = server.expect("server handshake completes");
8052        let (client_session, client_settled) = client.expect("offerer handshake completes");
8053        assert!(client_settled.is_encrypted());
8054        client_session.stop();
8055    }
8056
8057    /// A `holdconn` DTLS offer is rejected by the call preflight before the answering path can
8058    /// bind a media port or send a successful response.
8059    #[test]
8060    fn a_holdconn_dtls_offer_is_a_typed_pre_response_refusal() {
8061        let mut offer = offered("0", &["0 PCMU/8000"]);
8062        offer
8063            .attributes
8064            .push(sipx_sdp::Attribute::valued("setup", "holdconn"));
8065        let policy = MediaPolicy::default().with_keying(Keying::DtlsSrtp);
8066
8067        assert!(matches!(
8068            validate_dtls_offer_setup(&offer, policy),
8069            Err(Error::DtlsSetup(
8070                sipx_sdp::fingerprint::SetupRoleError::UnresolvedOffer(
8071                    sipx_sdp::fingerprint::Setup::HoldConn
8072                )
8073            ))
8074        ));
8075    }
8076
8077    /// An initial mux offer retains component 2 and advertises its explicit `a=rtcp` destination,
8078    /// so an answer omitting mux can take the fallback without a second offer.
8079    #[tokio::test]
8080    async fn an_initial_mux_ice_offer_carries_the_control_fallback() {
8081        let loopback: IpAddr = "127.0.0.1".parse().expect("loopback");
8082        let port = MediaPort::bind(SocketAddr::new(loopback, 0))
8083            .await
8084            .expect("binds media");
8085        let options = DialOptions::new("<sip:caller@example.invalid>", loopback)
8086            .with_media_policy(MediaPolicy::default().with_ice(IcePolicy::Host));
8087        let (_capabilities, offer, local, _keying) =
8088            offered_media(&options, &port, TransportKind::Udp)
8089                .await
8090                .expect("gathers an offer");
8091        let local = local.expect("ICE description");
8092        let audio = offer.media.first().expect("audio offer");
8093
8094        assert!(audio.rtcp_mux(), "mux is offered");
8095        assert!(
8096            local
8097                .candidates()
8098                .iter()
8099                .any(|candidate| candidate.component == ComponentId::RTCP),
8100            "the initial offer retains the component-2 fallback"
8101        );
8102        assert!(
8103            audio.attribute("rtcp").is_some(),
8104            "the fallback control destination is explicit"
8105        );
8106    }
8107
8108    /// Once an answer agrees to mux, its local ICE half contains component 1 alone.
8109    #[tokio::test]
8110    async fn a_muxed_ice_answer_gathers_one_component() {
8111        let mut offer = offered("0", &["0 PCMU/8000"]);
8112        let audio = offer.media.first_mut().expect("audio offer");
8113        audio.attributes.extend([
8114            sipx_sdp::Attribute::flag("rtcp-mux"),
8115            sipx_sdp::Attribute::valued("ice-ufrag", "peer"),
8116            sipx_sdp::Attribute::valued("ice-pwd", "peerPassword0123456789AB"),
8117            sipx_sdp::Attribute::valued("candidate", "1 1 UDP 2130706431 192.0.2.1 40000 typ host"),
8118        ]);
8119        let port = MediaPort::bind("127.0.0.1:0".parse().expect("loopback"))
8120            .await
8121            .expect("binds media");
8122        let (_remote, local) = answer_gathering(
8123            &port,
8124            &offer,
8125            MediaPolicy::default().with_ice(IcePolicy::Host),
8126        )
8127        .await
8128        .expect("gathers answer");
8129        let local = local.expect("ICE description");
8130
8131        assert_eq!(local.candidates().len(), 1);
8132        assert_eq!(local.candidates()[0].component, ComponentId::RTP);
8133        assert_eq!(local.default_destination(ComponentId::RTCP), None);
8134    }
8135
8136    /// An offer with nothing sipx carries is refused rather than answered on a guess.
8137    #[test]
8138    fn an_offer_of_nothing_we_carry_has_no_common_codec() {
8139        let g729 = offered("18", &["18 G729/8000"]);
8140        assert!(matches!(
8141            negotiated(&g729, Codecs::G711),
8142            Err(Error::NoCommonCodec)
8143        ));
8144    }
8145
8146    /// One row of the agreement table: an offer, and the set the application selected.
8147    ///
8148    /// The property is in [`tests::the_answer_and_the_negotiated_codec_agree`]. The rows exist so
8149    /// it is held against a *class* of rtpmap spellings rather than the one spelling that happened
8150    /// to be found — `M-31` was filed because a fix aimed at `08000` alone would leave the shape
8151    /// in place.
8152    struct Agreement {
8153        /// Why this row is in the table. Quoted in every failure, because a table-driven
8154        /// assertion that only prints the values makes the reader guess what was being tested.
8155        why: &'static str,
8156        /// The `m=audio` format list, in the offerer's preference order.
8157        formats: &'static str,
8158        /// The offer's `a=rtpmap` attribute values.
8159        rtpmaps: &'static [&'static str],
8160        /// The set the application selected for this call.
8161        codecs: Codecs,
8162    }
8163
8164    /// The offers the agreement must hold over, in every build.
8165    ///
8166    /// Derived from `docs/specs/sdp-format-identity.md` §4.4's vectors. A `const` table rather than
8167    /// a function that builds one: it is data, and a hundred lines of data is not a hundred lines
8168    /// of control flow for anyone reading it — or for `clippy::too_many_lines`.
8169    const AGREEMENT_TABLE: &[Agreement] = &[
8170        Agreement {
8171            why: "a clock rate with a leading zero is the same rate — `08000` and `8000` are \
8172                      numerically equal and textually different, which is the split M-31 was \
8173                      filed for",
8174            formats: "0 8",
8175            rtpmaps: &["0 PCMU/08000", "8 PCMA/8000"],
8176            codecs: Codecs::G711,
8177        },
8178        Agreement {
8179            why: "the same split in the *channel* field, so a fix aimed at the clock rate \
8180                      alone does not close the story",
8181            formats: "0 8",
8182            rtpmaps: &["0 PCMU/8000/01", "8 PCMA/8000"],
8183            codecs: Codecs::G711,
8184        },
8185        Agreement {
8186            why: "an offer that puts a codec sipx does not carry first: both rules must skip \
8187                      it and settle further down the list, not refuse the stream",
8188            formats: "18 0",
8189            rtpmaps: &["18 G729/8000", "0 PCMU/8000"],
8190            codecs: Codecs::G711,
8191        },
8192        Agreement {
8193            why: "a dynamic number carrying a codec sipx does have — 96 means PCMU here only \
8194                      because this offer said so (RFC 8866 §6.6), and both rules must read the \
8195                      map rather than the number",
8196            formats: "96 0",
8197            rtpmaps: &["96 PCMU/8000", "0 PCMU/8000"],
8198            codecs: Codecs::G711,
8199        },
8200        Agreement {
8201            why: "a bare static type, the one case with no rtpmap for either rule to read",
8202            formats: "0",
8203            rtpmaps: &[],
8204            codecs: Codecs::G711,
8205        },
8206        Agreement {
8207            why: "mono spelled out where RFC 8866 §6.6 would have let it be implied",
8208            formats: "0",
8209            rtpmaps: &["0 PCMU/8000/1"],
8210            codecs: Codecs::G711,
8211        },
8212        Agreement {
8213            why: "stereo G.711 is a different format from mono G.711, and neither rule may \
8214                      settle on it",
8215            formats: "0 8",
8216            rtpmaps: &["0 PCMU/8000/2", "8 PCMA/8000"],
8217            codecs: Codecs::G711,
8218        },
8219        Agreement {
8220            why: "a signed clock rate is not a decimal digit string, so it identifies nothing for \
8221                  either rule. The *third* witness, and the one that was not predicted: \
8222                  `u32::from_str` accepts a leading `+`, so the parsing rule read `+8000` as 8000 \
8223                  while the textual one did not — the same split as a leading zero, arrived at from \
8224                  the other side. It is why the digits are checked in `sipx-sdp` rather than left \
8225                  to `from_str`, and note the single rule resolves it the *opposite* way from a \
8226                  leading zero: both callers decline it, and both settle on PCMA below",
8227            formats: "0 8",
8228            rtpmaps: &["0 PCMU/+8000", "8 PCMA/8000"],
8229            codecs: Codecs::G711,
8230        },
8231        Agreement {
8232            why: "a clock rate that overflows u32 — hostile input, and a non-match for both \
8233                      rules rather than a panic in either",
8234            formats: "0 8",
8235            rtpmaps: &["0 PCMU/99999999999999", "8 PCMA/8000"],
8236            codecs: Codecs::G711,
8237        },
8238        Agreement {
8239            why: "an rtpmap with no clock rate at all identifies nothing",
8240            formats: "0 8",
8241            rtpmaps: &["0 PCMU", "8 PCMA/8000"],
8242            codecs: Codecs::G711,
8243        },
8244        Agreement {
8245            why: "an empty clock rate is not zero and is not 8000",
8246            formats: "0 8",
8247            rtpmaps: &["0 PCMU/", "8 PCMA/8000"],
8248            codecs: Codecs::G711,
8249        },
8250        Agreement {
8251            why: "whitespace inside the value: a rate neither rule can read, and both must \
8252                      fail to read it the same way",
8253            formats: "0 8",
8254            rtpmaps: &["0 PCMU/ 8000", "8 PCMA/8000"],
8255            codecs: Codecs::G711,
8256        },
8257        Agreement {
8258            why: "a fourth field is outside RFC 8866 §6.6's grammar, so the value identifies \
8259                      nothing — and must do so for both rules rather than one silently ignoring it",
8260            formats: "0 8",
8261            rtpmaps: &["0 PCMU/8000/1/9", "8 PCMA/8000"],
8262            codecs: Codecs::G711,
8263        },
8264        Agreement {
8265            why: "an Opus-first offer reaching a call that selected G.711: the M-30 case, and \
8266                      true in both feature configurations — with `opus` off no rtpmap can name it, \
8267                      with it on the set does not carry it",
8268            formats: "111 0",
8269            rtpmaps: &["111 opus/48000/2", "0 PCMU/8000"],
8270            codecs: Codecs::G711,
8271        },
8272        Agreement {
8273            why: "a dynamic number with no rtpmap is uninterpretable whatever the number, so \
8274                      the stream is refused rather than guessed at",
8275            formats: "111",
8276            rtpmaps: &[],
8277            codecs: Codecs::G711,
8278        },
8279        Agreement {
8280            why: "a stream offering only telephone-event is not a call: the answer rejects it \
8281                      and negotiation must refuse it too",
8282            formats: "101",
8283            rtpmaps: &["101 telephone-event/8000"],
8284            codecs: Codecs::G711,
8285        },
8286        Agreement {
8287            why: "an offer of nothing sipx carries at all — both rules refuse, and the \
8288                      agreement holds on the refusing side as well",
8289            formats: "18",
8290            rtpmaps: &["18 G729/8000"],
8291            codecs: Codecs::G711,
8292        },
8293    ];
8294
8295    /// The rows that only exist when the `opus` feature is on, because [`Codecs::Opus`] does.
8296    ///
8297    /// Empty in the default build rather than absent, so the test body has no `cfg` in it and the
8298    /// two configurations run the same code over different data.
8299    #[cfg(feature = "opus")]
8300    const OPUS_AGREEMENT_TABLE: &[Agreement] = &[
8301        Agreement {
8302            why: "Opus on the set that carries it, on the number this offer assigned",
8303            formats: "111 0",
8304            rtpmaps: &["111 opus/48000/2", "0 PCMU/8000"],
8305            codecs: Codecs::Opus,
8306        },
8307        Agreement {
8308            why: "the leading-zero split on Opus's own clock rate, so the class is closed in the \
8309                  gated path too and not only for G.711",
8310            formats: "111 0",
8311            rtpmaps: &["111 opus/048000/2", "0 PCMU/8000"],
8312            codecs: Codecs::Opus,
8313        },
8314        Agreement {
8315            why: "Opus at a rate RFC 7587 §7 does not assign is nothing sipx has, whatever number \
8316                  is beside it",
8317            formats: "111 0",
8318            rtpmaps: &["111 opus/16000/2", "0 PCMU/8000"],
8319            codecs: Codecs::Opus,
8320        },
8321    ];
8322
8323    /// No Opus in this build, so no Opus rows. See [`OPUS_AGREEMENT_TABLE`].
8324    #[cfg(not(feature = "opus"))]
8325    const OPUS_AGREEMENT_TABLE: &[Agreement] = &[];
8326
8327    /// The answer sipx puts on the wire and the codec it configures the media session with must
8328    /// name the same format. **`M-31`'s failing-first test.**
8329    ///
8330    /// This is the assertion that fails while the two rules disagree: with the answer comparing an
8331    /// rtpmap clock rate as text and `codec_named` parsing it to `u32`, an offer of
8332    /// `a=rtpmap:0 PCMU/08000` settles on `Pcmu` at payload type 0 while the answer names only
8333    /// `8`. sipx would then send µ-law on a number the answer never offered *and* decode the
8334    /// peer's PCMA through a µ-law session — audible garbage rather than silence, with nothing in
8335    /// the stack reporting an error.
8336    ///
8337    /// The property is a biconditional, not a one-way check, because both halves are reachable
8338    /// defects: a codec the answer never named is a session the far end cannot read, and a stream
8339    /// the answer accepted while negotiation refused it is a call that fails after the 200 OK went
8340    /// out. `wire_payload_type` is the value compared because that is the byte that leaves —
8341    /// `Some(0)` and `None` are two descriptions of the same PCMU.
8342    #[test]
8343    fn the_answer_and_the_negotiated_codec_agree() {
8344        let local: IpAddr = "192.0.2.9".parse().expect("a literal address");
8345
8346        for row in AGREEMENT_TABLE.iter().chain(OPUS_AGREEMENT_TABLE) {
8347            let offer = offered(row.formats, row.rtpmaps);
8348            let answered = sipx_sdp::answer(&offer, &row.codecs.capabilities(local, 40000));
8349            let audio = answered
8350                .media
8351                .iter()
8352                .find(|stream| stream.media == "audio")
8353                .expect("the answer has one m= line per offered stream");
8354
8355            match negotiated(&offer, row.codecs) {
8356                Ok(settled) => {
8357                    assert!(
8358                        !audio.is_rejected(),
8359                        "{}: negotiation settled on {:?} while the answer rejected the stream",
8360                        row.why,
8361                        settled.codec,
8362                    );
8363                    let wire = settled.wire_payload_type().to_string();
8364                    assert!(
8365                        audio.formats.contains(&wire),
8366                        "{}: negotiation settled on {:?} at payload type {wire}, which the answer \
8367                         never named ({:?})",
8368                        row.why,
8369                        settled.codec,
8370                        audio.formats,
8371                    );
8372                }
8373                Err(error) => {
8374                    assert!(
8375                        audio.is_rejected(),
8376                        "{}: negotiation refused the stream ({error}) while the answer accepted it \
8377                         with formats {:?}",
8378                        row.why,
8379                        audio.formats,
8380                    );
8381                }
8382            }
8383        }
8384    }
8385}