Add Support For Password-Protected Receivers, Supporting Bug Fixes - #26
Open
jamescoman wants to merge 5 commits into
Open
Add Support For Password-Protected Receivers, Supporting Bug Fixes#26jamescoman wants to merge 5 commits into
jamescoman wants to merge 5 commits into
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two independent fixes for mirroring to receivers on recent tvOS, plus two small
supporting changes.
Pair-verified sessions fail with
HTTP 400on SETUP.pair-verifydoes twothings 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 abare
400— no body, no explanation, and after ~40 ms of processing, whichmakes 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
401and aWWW-Authenticate: Digestheader. 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 apassword-protected receiver to work.
Commits
Add AppleTV14,1 without Homepod to tested devicesFix PIN prompt for receivers that use a fixed passwordairplay: log the SETUP plist under -debugairplay: advertise PTP timing for pair-verified sessions400fixairplay: answer HTTP Digest challenges from password-protected receivers401fixEach builds, vets and passes tests standalone.
Timing protocol
selectTimingProtocol()picks the clock from the session type: PTP whenpair-verified, NTP otherwise.
-timing-protocolforces 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
autodefault preserves exactly the current behaviour there. Anysetup 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
timingPeerInfoin the SETUP response — its addresses, itsClockID— and takes the clock master role. No new protocol machinery, no newsockets.
Digest auth
The challenge carries neither
qopnoralgorithm, so this is RFC 2069 digest:MD5(HA1:nonce:HA2). New-passwordflag, or$DOUBLETAKE_PASSWORD, which ispreferred because a command line is readable by other users via
ps. The envvar wins when both are set.
-auth-useroverrides the Digest username.Two design points worth calling out:
The retry lives inside
rtspRequest/httpRequestrather than at the ~24 callsites, 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
OPTIONSprobe — challenged, andside-effect free.
Verification
Against real hardware — AppleTV14,1, tvOS 26.5, AirTunes/950.7.1:
200, plus theexpected
401on theOPTIONSprobe, SETUP/RECORD/SET_PARAMETER/feedback allpre-authenticated, no warnings.
realm="airplay", usernameAirPlay,urias the absolute request URI —each confirmed by enumeration against the receiver rather than assumed. A
path-only
uriis rejected with401.400was reproduced on an unmodified build of8ccea5f, so it ispre-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 retryagainst a fake receiver, and timing-protocol selection.
Caveats
autodefaults are chosen toleave every other path unchanged.
-auth-user iTunesis offered for RAOP-style receivers advertisingrealm="raop"but is untested — onlyrealm="airplay"was available here.what this one does. A receiver expecting the sender to master would need real
PTP support.
AppleTV14,1was already listed as tested, so it presumably worked viatransient pairing before tvOS began enforcing AirPlay 2 timing validation.