Skip to content

Add Support For Password-Protected Receivers, Supporting Bug Fixes - #26

Open
jamescoman wants to merge 5 commits into
omarroth:mainfrom
jamescoman:push-yzzksuzywvmy
Open

Add Support For Password-Protected Receivers, Supporting Bug Fixes#26
jamescoman wants to merge 5 commits into
omarroth:mainfrom
jamescoman:push-yzzksuzywvmy

Conversation

@jamescoman

Copy link
Copy Markdown

Summary

Two independent fixes for mirroring to receivers on recent tvOS, plus two small
supporting changes.

Pair-verified sessions fail with HTTP 400 on SETUP. pair-verify does two
things at once: it encrypts the RTSP channel, and it makes the session an
AirPlay 2 session. doubletake handled the first but kept advertising the legacy
timingProtocol=NTP, and receivers that validate the second reject it with a
bare 400 — no body, no explanation, and after ~40 ms of processing, which
makes it read like a payload problem rather than a timing one. This affects
every session that uses saved credentials or -pair.

Password-protected receivers were unreachable. A receiver with "Require
Password" enabled answers SETUP with 401 and a WWW-Authenticate: Digest
header. Nothing in the codebase answered auth challenges, so mirroring to such a
receiver could not work at all.

These compound: using a password implies saved credentials, which implies
pair-verify, which hits the timing bug. Both fixes are needed for a
password-protected receiver to work.

Commits

commit what
Add AppleTV14,1 without Homepod to tested devices README only
Fix PIN prompt for receivers that use a fixed password prompt wording + full-line read
airplay: log the SETUP plist under -debug debugging aid
airplay: advertise PTP timing for pair-verified sessions the 400 fix
airplay: answer HTTP Digest challenges from password-protected receivers the 401 fix

Each builds, vets and passes tests standalone.

Timing protocol

selectTimingProtocol() picks the clock from the session type: PTP when
pair-verified, NTP otherwise. -timing-protocol forces either.

NTP still works for transient sessions and remains what they send. A
transient session is not an AirPlay 2 session, receivers do not validate it as
one, and the auto default preserves exactly the current behaviour there. Any
setup working today continues to work unchanged; this only alters what
pair-verified sessions advertise, and those are currently broken.

Declaring PTP does not require implementing an IEEE 1588 clock. The receiver
returns its own timingPeerInfo in the SETUP response — its addresses, its
ClockID — and takes the clock master role. No new protocol machinery, no new
sockets.

Digest auth

The challenge carries neither qop nor algorithm, so this is RFC 2069 digest:
MD5(HA1:nonce:HA2). New -password flag, or $DOUBLETAKE_PASSWORD, which is
preferred because a command line is readable by other users via ps. The env
var wins when both are set. -auth-user overrides the Digest username.

Two design points worth calling out:

The retry lives inside rtspRequest/httpRequest rather than at the ~24 call
sites, so every request is covered uniformly and inside the existing mutex.

SETUP cannot simply be retried. It registers stream state on the receiver
even when rejected, so answering its challenge by resending it fails with 400.
Challenges are therefore cached when seen and answered up front on later
requests, primed if necessary by an OPTIONS probe — challenged, and
side-effect free.

Verification

Against real hardware — AppleTV14,1, tvOS 26.5, AirTunes/950.7.1:

  • Full mirroring session with "Require Password" enabled: 13× 200, plus the
    expected 401 on the OPTIONS probe, SETUP/RECORD/SET_PARAMETER/feedback all
    pre-authenticated, no warnings.
  • realm="airplay", username AirPlay, uri as the absolute request URI —
    each confirmed by enumeration against the receiver rather than assumed. A
    path-only uri is rejected with 401.
  • The 400 was reproduced on an unmodified build of 8ccea5f, so it is
    pre-existing rather than introduced here.

Unit tests: challenge parsing (including the receiver's real base64 nonce and
commas inside quoted values), the digest computation against a vector computed
independently with md5sum, the retry decision table, an end-to-end retry
against a fake receiver, and timing-protocol selection.

Caveats

  • Only tested against one receiver model. The auto defaults are chosen to
    leave every other path unchanged.
  • -auth-user iTunes is offered for RAOP-style receivers advertising
    realm="raop" but is untested — only realm="airplay" was available here.
  • The PTP path relies on the receiver taking the clock master role, which is
    what this one does. A receiver expecting the sender to master would need real
    PTP support.
  • AppleTV14,1 was already listed as tested, so it presumably worked via
    transient pairing before tvOS began enforcing AirPlay 2 timing validation.

Receivers with "Require Password" enabled use a password you set rather than
a rotating onscreen code, so nothing appears on the TV and the old prompt sent
you looking for one. A failed pair-pin-start is likewise normal for those
receivers, so warn instead of exiting.

Also read the whole line: fmt.Scanln stops at the first space and silently
truncated any password containing one.
A receiver that rejects SETUP answers with a bare status and no body, so
there is nothing to go on without seeing what was actually sent. Dumps both
the setup plist and the audio stream descriptor in a stable key order, with
byte slices summarised, so two runs can be diffed.
pair-verify does two things at once: it encrypts the RTSP channel and it makes
the session an AirPlay 2 session. doubletake handled the first but kept
advertising the legacy NTP clock, and receivers that validate the second reject
that with a bare HTTP 400 on SETUP -- after enough processing delay to look
like a payload problem rather than a timing one. Transient sessions were
unaffected because they are not AirPlay 2 sessions.

Declaring PTP needs no local IEEE 1588 clock: the receiver returns its own
timingPeerInfo in the SETUP response and takes the clock master role.

Reproduced on AppleTV14,1 (tvOS 26.5, AirTunes/950.7.1), where it made every
password-protected session fail, since a password implies saved credentials
and therefore pair-verify.
A receiver with "Require Password" enabled challenges SETUP with 401 and a
WWW-Authenticate Digest header, which nothing answered, so mirroring to it was
impossible. Adds -password (or $DOUBLETAKE_PASSWORD, preferred since a command
line is visible in ps) and -auth-user.

The challenge carries no qop or algorithm, so this is RFC 2069 digest:
MD5(HA1:nonce:HA2). The retry lives inside rtspRequest/httpRequest rather than
at the 24 call sites, so every request is covered uniformly.

SETUP cannot simply be retried: it registers stream state on the receiver even
when rejected, and a second one fails with 400. Challenges are therefore cached
and answered up front, primed if needed by an OPTIONS probe, which is
challenged and side-effect free.

Verified against AppleTV14,1 (AirTunes/950.7.1): realm="airplay", username
"AirPlay", uri as the absolute request URI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant