mirror of
https://github.com/morgan9e/UxPlay
synced 2026-04-14 00:04:13 +09:00
add suppport for user access control by -pw password (HTML digest)
This commit is contained in:
57
README.html
57
README.html
@@ -1,24 +1,26 @@
|
|||||||
<h1
|
<h1
|
||||||
id="uxplay-1.71-airplay-mirror-and-airplay-audio-server-for-linux-macos-and-unix-now-also-runs-on-windows.">UxPlay
|
id="uxplay-1.72-beta-airplay-mirror-and-airplay-audio-server-for-linux-macos-and-unix-now-also-runs-on-windows.">UxPlay
|
||||||
1.71: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix
|
1.72 (beta): AirPlay-Mirror and AirPlay-Audio server for Linux, macOS,
|
||||||
(now also runs on Windows).</h1>
|
and Unix (now also runs on Windows).</h1>
|
||||||
<h3
|
<h3
|
||||||
id="now-developed-at-the-github-site-httpsgithub.comfdh2uxplay-where-all-user-issues-should-be-posted-and-latest-versions-can-be-found."><strong>Now
|
id="now-developed-at-the-github-site-httpsgithub.comfdh2uxplay-where-all-user-issues-should-be-posted-and-latest-versions-can-be-found."><strong>Now
|
||||||
developed at the GitHub site <a href="https://github.com/FDH2/UxPlay"
|
developed at the GitHub site <a href="https://github.com/FDH2/UxPlay"
|
||||||
class="uri">https://github.com/FDH2/UxPlay</a> (where ALL user issues
|
class="uri">https://github.com/FDH2/UxPlay</a> (where ALL user issues
|
||||||
should be posted, and latest versions can be found).</strong></h3>
|
should be posted, and latest versions can be found).</strong></h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><em><strong>NEW in v1.71</strong>: Support for (YouTube) HLS (HTTP
|
<li><p><em><strong>NEW in v1.72</strong>: Improved Support for (YouTube)
|
||||||
Live Streaming) video with the new “-hls” option.</em> <strong>Only
|
HLS (HTTP Live Streaming) video with the new “-hls” option.</em>
|
||||||
streaming from the YouTube iOS app (in "m3u8" protocol) is currently
|
<strong>Only streaming from the YouTube iOS app (in "m3u8" protocol) is
|
||||||
supported</strong>: (streaming using the AirPlay icon in a browser
|
currently supported</strong>: (streaming using the AirPlay icon in a
|
||||||
window is <strong>not</strong> yet supported).Click on the airplay icon
|
browser window is <strong>not</strong> yet supported).Click on the
|
||||||
in the YouTube app to stream video. (You may need to wait until
|
airplay icon in the YouTube app to stream video. <strong>Please report
|
||||||
advertisements have finished or been skipped before clicking the YouTube
|
any issues with this new feature of UxPlay</strong>.</p>
|
||||||
airplay icon.) <strong>Please report any issues with this new feature of
|
<p><em>The default video player for HLS is GStreamer playbin v3: use
|
||||||
UxPlay</strong>. <em>The default video player for HLS is GStreamer
|
“-hls 2” to revert to playbin v2 if some videos fail to play</em>.</p>
|
||||||
playbin v3: use “-hls 2” to revert to playbin v2 if some videos fail to
|
<ul>
|
||||||
play</em>.</li>
|
<li>added support for setting a password (as an alternative to on-screen
|
||||||
|
pin codes) to control client access (-pw option)</li>
|
||||||
|
</ul></li>
|
||||||
</ul>
|
</ul>
|
||||||
<h2 id="highlights">Highlights:</h2>
|
<h2 id="highlights">Highlights:</h2>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -507,8 +509,12 @@ option: see “<code>-pin</code>” and “<code>-reg</code>” in <a
|
|||||||
href="#usage">Usage</a> for details, if you wish to use it. <em>Some
|
href="#usage">Usage</a> for details, if you wish to use it. <em>Some
|
||||||
clients with MDM (Mobile Device Management, often present on
|
clients with MDM (Mobile Device Management, often present on
|
||||||
employer-owned devices) are required to use pin-authentication: UxPlay
|
employer-owned devices) are required to use pin-authentication: UxPlay
|
||||||
will provide this even when running without the pin
|
will provide this even when running without the pin option.</em>
|
||||||
option.</em></p></li>
|
Password authentication (-pw <em>pwd</em>)is also offered as an
|
||||||
|
alternative solution to pin codes: users need to know the password
|
||||||
|
<em>pwd</em> and enter it on their iOS/macOS device to access UxPlay,
|
||||||
|
when prompted (if <em>pwd</em> is not set, a displayed random pin code
|
||||||
|
must be entered at <strong>each</strong> new connection.)</p></li>
|
||||||
<li><p>By default, UxPlay is locked to its current client until that
|
<li><p>By default, UxPlay is locked to its current client until that
|
||||||
client drops the connection; since UxPlay-1.58, the option
|
client drops the connection; since UxPlay-1.58, the option
|
||||||
<code>-nohold</code> modifies this behavior so that when a new client
|
<code>-nohold</code> modifies this behavior so that when a new client
|
||||||
@@ -999,6 +1005,17 @@ key (base-64 format), Device ID, and Device name; commenting out (with
|
|||||||
options -restrict, -block, -allow for more ways to control client
|
options -restrict, -block, -allow for more ways to control client
|
||||||
access). <em>(Add a line “reg” in the startup file if you wish to use
|
access). <em>(Add a line “reg” in the startup file if you wish to use
|
||||||
this feature.)</em></p>
|
this feature.)</em></p>
|
||||||
|
<p><strong>-pw</strong> [<em>pwd</em>]. (since 1.72). As an alternative
|
||||||
|
to -pin, client access can be controlled with a password set when uxplay
|
||||||
|
starts (set it in the .uxplay startup file, where it is stored as
|
||||||
|
cleartext.) All users must then know this password. This uses HTTP md5
|
||||||
|
Digest authentication, which is now regarded as providing weak security,
|
||||||
|
but it is only used to validate the uxplay password, and no user
|
||||||
|
credentials are exposed. _Note: -pin and -pw are alternatives: if both
|
||||||
|
are specified at startup, the earlier of these two options is discarded.
|
||||||
|
If <em>pwd</em> is not specified, a random 4-digit pin code is
|
||||||
|
displayed, and must be entered on the client at <strong>each</strong>
|
||||||
|
new conenction.</p>
|
||||||
<p><strong>-vsync [x]</strong> (In Mirror mode:) this option
|
<p><strong>-vsync [x]</strong> (In Mirror mode:) this option
|
||||||
(<strong>now the default</strong>) uses timestamps to synchronize audio
|
(<strong>now the default</strong>) uses timestamps to synchronize audio
|
||||||
with video on the server, with an optional audio delay in (decimal)
|
with video on the server, with an optional audio delay in (decimal)
|
||||||
@@ -1627,9 +1644,15 @@ an AppleTV6,2 with sourceVersion 380.20.1 (an AppleTV 4K 1st gen,
|
|||||||
introduced 2017, running tvOS 12.2.1), so it does not seem to matter
|
introduced 2017, running tvOS 12.2.1), so it does not seem to matter
|
||||||
what version UxPlay claims to be.</p>
|
what version UxPlay claims to be.</p>
|
||||||
<h1 id="changelog">Changelog</h1>
|
<h1 id="changelog">Changelog</h1>
|
||||||
|
<p>1.72 2025-05-15. Improved HLS Live Streaming (YouTube) support. Add
|
||||||
|
support for password (HTTP Digest Authentication, -pw option) as
|
||||||
|
alternative to on-screen pin codes.</p>
|
||||||
<p>1.72 2024-04-22 Add requested options -md <filename> to output
|
<p>1.72 2024-04-22 Add requested options -md <filename> to output
|
||||||
audio metadata text to a file for possible display (complements -ca
|
audio metadata text to a file for possible display (complements -ca
|
||||||
option), and -vol <v> option to set initial audio-streaming volume.</p>
|
option), and -vol <v> option to set initial audio-streaming volume. Add
|
||||||
|
support password user access control with HTTP digest Authentication
|
||||||
|
(-pw [pwd]). If no pwd is set, a random pin is displayed for entry at
|
||||||
|
each new connection.</p>
|
||||||
<p>1.71 2024-12-13 Add support for HTTP Live Streaming (HLS), initially
|
<p>1.71 2024-12-13 Add support for HTTP Live Streaming (HLS), initially
|
||||||
only for YouTube movies. Fix issue with NTP timeout on Windows.</p>
|
only for YouTube movies. Fix issue with NTP timeout on Windows.</p>
|
||||||
<p>1.70 2024-10-04 Add support for 4K (h265) video (resolution 3840 x
|
<p>1.70 2024-10-04 Add support for 4K (h265) video (resolution 3840 x
|
||||||
|
|||||||
41
README.md
41
README.md
@@ -1,18 +1,23 @@
|
|||||||
# UxPlay 1.71: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows).
|
# UxPlay 1.72 (beta): AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows).
|
||||||
|
|
||||||
### **Now developed at the GitHub site <https://github.com/FDH2/UxPlay> (where ALL user issues should be posted, and latest versions can be found).**
|
### **Now developed at the GitHub site <https://github.com/FDH2/UxPlay> (where ALL user issues should be posted, and latest versions can be found).**
|
||||||
|
|
||||||
- ***NEW in v1.71**: Support for (YouTube) HLS (HTTP Live Streaming)
|
- ***NEW in v1.72**: Improved Support for (YouTube) HLS (HTTP Live Streaming)
|
||||||
video with the new "-hls" option.* **Only streaming from the YouTube iOS app
|
video with the new "-hls" option.* **Only streaming from the YouTube iOS app
|
||||||
(in \"m3u8\" protocol) is currently supported**: (streaming using the AirPlay icon in a browser window
|
(in \"m3u8\" protocol) is currently supported**: (streaming using the AirPlay icon in a browser window
|
||||||
is **not** yet supported).Click on the airplay icon in the
|
is **not** yet supported).Click on the airplay icon in the
|
||||||
YouTube app to stream video. (You may need to wait until
|
YouTube app to stream video.
|
||||||
advertisements have finished or been skipped before clicking the
|
**Please report any issues with this new feature of UxPlay**.
|
||||||
YouTube airplay icon.) **Please report any issues with this new
|
|
||||||
feature of UxPlay**. _The default video player for HLS is
|
_The default video player for HLS is
|
||||||
GStreamer playbin v3: use "-hls 2" to revert to playbin v2 if
|
GStreamer playbin v3: use "-hls 2" to revert to playbin v2 if
|
||||||
some videos fail to play_.
|
some videos fail to play_.
|
||||||
|
|
||||||
|
* added support for setting a password (as an alternative to on-screen
|
||||||
|
pin codes) to control client access (-pw option); added support for
|
||||||
|
setting initial audion volume (--vol option), and output of audio-mode
|
||||||
|
metatdate to file (for display, -md option).
|
||||||
|
|
||||||
## Highlights:
|
## Highlights:
|
||||||
|
|
||||||
- GPLv3, open source.
|
- GPLv3, open source.
|
||||||
@@ -499,7 +504,11 @@ below for help with this or other problems.
|
|||||||
[Usage](#usage) for details, if you wish to use it. *Some clients
|
[Usage](#usage) for details, if you wish to use it. *Some clients
|
||||||
with MDM (Mobile Device Management, often present on employer-owned
|
with MDM (Mobile Device Management, often present on employer-owned
|
||||||
devices) are required to use pin-authentication: UxPlay will provide
|
devices) are required to use pin-authentication: UxPlay will provide
|
||||||
this even when running without the pin option.*
|
this even when running without the pin option.* Password authentication
|
||||||
|
(-pw _pwd_)is also offered as an alternative solution to pin codes:
|
||||||
|
users need to know the password _pwd_ and enter it on their iOS/macOS device
|
||||||
|
to access UxPlay, when prompted (if _pwd_ is not set, a displayed random
|
||||||
|
pin code must be entered at **each** new connection.)
|
||||||
|
|
||||||
- By default, UxPlay is locked to its current client until that client
|
- By default, UxPlay is locked to its current client until that client
|
||||||
drops the connection; since UxPlay-1.58, the option `-nohold`
|
drops the connection; since UxPlay-1.58, the option `-nohold`
|
||||||
@@ -1006,6 +1015,17 @@ deregisters the corresponding client (see options -restrict, -block,
|
|||||||
-allow for more ways to control client access). *(Add a line "reg" in
|
-allow for more ways to control client access). *(Add a line "reg" in
|
||||||
the startup file if you wish to use this feature.)*
|
the startup file if you wish to use this feature.)*
|
||||||
|
|
||||||
|
**-pw** [*pwd*]. (since 1.72). As an alternative to -pin, client access
|
||||||
|
can be controlled with a password set when uxplay starts (set it in
|
||||||
|
the .uxplay startup file, where it is stored as cleartext.) All users must
|
||||||
|
then know this password. This uses HTTP md5 Digest authentication,
|
||||||
|
which is now regarded as providing weak security, but it is only used to
|
||||||
|
validate the uxplay password, and no user credentials are exposed. _Note:
|
||||||
|
-pin and -pw are alternatives: if both are specified at startup, the
|
||||||
|
earlier of these two options is discarded. If *pwd* is not specified,
|
||||||
|
a random 4-digit pin code is displayed, and must be entered on the client
|
||||||
|
at **each** new conenction.
|
||||||
|
|
||||||
**-vsync \[x\]** (In Mirror mode:) this option (**now the default**)
|
**-vsync \[x\]** (In Mirror mode:) this option (**now the default**)
|
||||||
uses timestamps to synchronize audio with video on the server, with an
|
uses timestamps to synchronize audio with video on the server, with an
|
||||||
optional audio delay in (decimal) milliseconds (*x* = "20.5" means
|
optional audio delay in (decimal) milliseconds (*x* = "20.5" means
|
||||||
@@ -1671,10 +1691,15 @@ introduced 2017, running tvOS 12.2.1), so it does not seem to matter
|
|||||||
what version UxPlay claims to be.
|
what version UxPlay claims to be.
|
||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
1.72 2025-05-15. Improved HLS Live Streaming (YouTube) support.
|
||||||
|
Add support for password (HTTP Digest Authentication, -pw option) as
|
||||||
|
alternative to on-screen pin codes.
|
||||||
|
|
||||||
1.72 2024-04-22 Add requested options -md \<filename\> to output audio
|
1.72 2024-04-22 Add requested options -md \<filename\> to output audio
|
||||||
metadata text to a file for possible display (complements -ca option),
|
metadata text to a file for possible display (complements -ca option),
|
||||||
and -vol <v> option to set initial audio-streaming volume.
|
and -vol <v> option to set initial audio-streaming volume. Add support
|
||||||
|
password user access control with HTTP digest Authentication (-pw [pwd]).
|
||||||
|
If no pwd is set, a random pin is displayed for entry at each new connection.
|
||||||
|
|
||||||
1.71 2024-12-13 Add support for HTTP Live Streaming (HLS), initially
|
1.71 2024-12-13 Add support for HTTP Live Streaming (HLS), initially
|
||||||
only for YouTube movies. Fix issue with NTP timeout on Windows.
|
only for YouTube movies. Fix issue with NTP timeout on Windows.
|
||||||
|
|||||||
49
README.txt
49
README.txt
@@ -1,17 +1,19 @@
|
|||||||
# UxPlay 1.71: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows).
|
# UxPlay 1.72 (beta): AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows).
|
||||||
|
|
||||||
### **Now developed at the GitHub site <https://github.com/FDH2/UxPlay> (where ALL user issues should be posted, and latest versions can be found).**
|
### **Now developed at the GitHub site <https://github.com/FDH2/UxPlay> (where ALL user issues should be posted, and latest versions can be found).**
|
||||||
|
|
||||||
- ***NEW in v1.71**: Support for (YouTube) HLS (HTTP Live Streaming)
|
- ***NEW in v1.72**: Improved Support for (YouTube) HLS (HTTP Live
|
||||||
video with the new "-hls" option.* **Only streaming from the YouTube
|
Streaming) video with the new "-hls" option.* **Only streaming from
|
||||||
iOS app (in \"m3u8\" protocol) is currently supported**: (streaming
|
the YouTube iOS app (in \"m3u8\" protocol) is currently supported**:
|
||||||
using the AirPlay icon in a browser window is **not** yet
|
(streaming using the AirPlay icon in a browser window is **not** yet
|
||||||
supported).Click on the airplay icon in the YouTube app to stream
|
supported).Click on the airplay icon in the YouTube app to stream
|
||||||
video. (You may need to wait until advertisements have finished or
|
video. **Please report any issues with this new feature of UxPlay**.
|
||||||
been skipped before clicking the YouTube airplay icon.) **Please
|
|
||||||
report any issues with this new feature of UxPlay**. *The default
|
*The default video player for HLS is GStreamer playbin v3: use "-hls
|
||||||
video player for HLS is GStreamer playbin v3: use "-hls 2" to revert
|
2" to revert to playbin v2 if some videos fail to play*.
|
||||||
to playbin v2 if some videos fail to play*.
|
|
||||||
|
- added support for setting a password (as an alternative to
|
||||||
|
on-screen pin codes) to control client access (-pw option)
|
||||||
|
|
||||||
## Highlights:
|
## Highlights:
|
||||||
|
|
||||||
@@ -499,7 +501,12 @@ below for help with this or other problems.
|
|||||||
[Usage](#usage) for details, if you wish to use it. *Some clients
|
[Usage](#usage) for details, if you wish to use it. *Some clients
|
||||||
with MDM (Mobile Device Management, often present on employer-owned
|
with MDM (Mobile Device Management, often present on employer-owned
|
||||||
devices) are required to use pin-authentication: UxPlay will provide
|
devices) are required to use pin-authentication: UxPlay will provide
|
||||||
this even when running without the pin option.*
|
this even when running without the pin option.* Password
|
||||||
|
authentication (-pw *pwd*)is also offered as an alternative solution
|
||||||
|
to pin codes: users need to know the password *pwd* and enter it on
|
||||||
|
their iOS/macOS device to access UxPlay, when prompted (if *pwd* is
|
||||||
|
not set, a displayed random pin code must be entered at **each** new
|
||||||
|
connection.)
|
||||||
|
|
||||||
- By default, UxPlay is locked to its current client until that client
|
- By default, UxPlay is locked to its current client until that client
|
||||||
drops the connection; since UxPlay-1.58, the option `-nohold`
|
drops the connection; since UxPlay-1.58, the option `-nohold`
|
||||||
@@ -1007,6 +1014,17 @@ deregisters the corresponding client (see options -restrict, -block,
|
|||||||
-allow for more ways to control client access). *(Add a line "reg" in
|
-allow for more ways to control client access). *(Add a line "reg" in
|
||||||
the startup file if you wish to use this feature.)*
|
the startup file if you wish to use this feature.)*
|
||||||
|
|
||||||
|
**-pw** \[*pwd*\]. (since 1.72). As an alternative to -pin, client
|
||||||
|
access can be controlled with a password set when uxplay starts (set it
|
||||||
|
in the .uxplay startup file, where it is stored as cleartext.) All users
|
||||||
|
must then know this password. This uses HTTP md5 Digest authentication,
|
||||||
|
which is now regarded as providing weak security, but it is only used to
|
||||||
|
validate the uxplay password, and no user credentials are exposed.
|
||||||
|
\_Note: -pin and -pw are alternatives: if both are specified at startup,
|
||||||
|
the earlier of these two options is discarded. If *pwd* is not
|
||||||
|
specified, a random 4-digit pin code is displayed, and must be entered
|
||||||
|
on the client at **each** new conenction.
|
||||||
|
|
||||||
**-vsync \[x\]** (In Mirror mode:) this option (**now the default**)
|
**-vsync \[x\]** (In Mirror mode:) this option (**now the default**)
|
||||||
uses timestamps to synchronize audio with video on the server, with an
|
uses timestamps to synchronize audio with video on the server, with an
|
||||||
optional audio delay in (decimal) milliseconds (*x* = "20.5" means
|
optional audio delay in (decimal) milliseconds (*x* = "20.5" means
|
||||||
@@ -1672,9 +1690,16 @@ what version UxPlay claims to be.
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
1.72 2025-05-15. Improved HLS Live Streaming (YouTube) support. Add
|
||||||
|
support for password (HTTP Digest Authentication, -pw option) as
|
||||||
|
alternative to on-screen pin codes.
|
||||||
|
|
||||||
1.72 2024-04-22 Add requested options -md \<filename\> to output audio
|
1.72 2024-04-22 Add requested options -md \<filename\> to output audio
|
||||||
metadata text to a file for possible display (complements -ca option),
|
metadata text to a file for possible display (complements -ca option),
|
||||||
and -vol `<v>`{=html} option to set initial audio-streaming volume.
|
and -vol `<v>`{=html} option to set initial audio-streaming volume. Add
|
||||||
|
support password user access control with HTTP digest Authentication
|
||||||
|
(-pw \[pwd\]). If no pwd is set, a random pin is displayed for entry at
|
||||||
|
each new connection.
|
||||||
|
|
||||||
1.71 2024-12-13 Add support for HTTP Live Streaming (HLS), initially
|
1.71 2024-12-13 Add support for HTTP Live Streaming (HLS), initially
|
||||||
only for YouTube movies. Fix issue with NTP timeout on Windows.
|
only for YouTube movies. Fix issue with NTP timeout on Windows.
|
||||||
|
|||||||
60
lib/crypto.c
60
lib/crypto.c
@@ -507,6 +507,8 @@ void ed25519_key_destroy(ed25519_key_t *key) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// SHA 512
|
// SHA 512
|
||||||
|
|
||||||
struct sha_ctx_s {
|
struct sha_ctx_s {
|
||||||
@@ -540,7 +542,6 @@ void sha_final(sha_ctx_t *ctx, uint8_t *out, unsigned int *len) {
|
|||||||
void sha_reset(sha_ctx_t *ctx) {
|
void sha_reset(sha_ctx_t *ctx) {
|
||||||
if (!EVP_MD_CTX_reset(ctx->digest_ctx) ||
|
if (!EVP_MD_CTX_reset(ctx->digest_ctx) ||
|
||||||
!EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha512(), NULL)) {
|
!EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha512(), NULL)) {
|
||||||
|
|
||||||
handle_error(__func__);
|
handle_error(__func__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -552,6 +553,63 @@ void sha_destroy(sha_ctx_t *ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MD5
|
||||||
|
struct md5_ctx_s {
|
||||||
|
EVP_MD_CTX *digest_ctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
md5_ctx_t *md5_init() {
|
||||||
|
md5_ctx_t *ctx = malloc(sizeof(md5_ctx_t));
|
||||||
|
assert(ctx != NULL);
|
||||||
|
ctx->digest_ctx = EVP_MD_CTX_new();
|
||||||
|
assert(ctx->digest_ctx != NULL);
|
||||||
|
|
||||||
|
if (!EVP_DigestInit_ex(ctx->digest_ctx, EVP_md5(), NULL)) {
|
||||||
|
handle_error(__func__);
|
||||||
|
}
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void md5_update(md5_ctx_t *ctx, const uint8_t *in, int len) {
|
||||||
|
if (!EVP_DigestUpdate(ctx->digest_ctx, in, len)) {
|
||||||
|
handle_error(__func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void md5_final(md5_ctx_t *ctx, uint8_t *out, unsigned int *len) {
|
||||||
|
if (!EVP_DigestFinal_ex(ctx->digest_ctx, out, len)) {
|
||||||
|
handle_error(__func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void md5_reset(md5_ctx_t *ctx) {
|
||||||
|
if (!EVP_MD_CTX_reset(ctx->digest_ctx) ||
|
||||||
|
!EVP_DigestInit_ex(ctx->digest_ctx, EVP_md5(), NULL)) {
|
||||||
|
|
||||||
|
handle_error(__func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void md5_destroy(md5_ctx_t *ctx) {
|
||||||
|
if (ctx) {
|
||||||
|
EVP_MD_CTX_free(ctx->digest_ctx);
|
||||||
|
free(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MD5_DIGEST_LENGTH 16
|
||||||
|
char *get_md5(char *string) {
|
||||||
|
unsigned char hash[MD5_DIGEST_LENGTH];
|
||||||
|
md5_ctx_t *ctx = NULL;
|
||||||
|
ctx = md5_init();
|
||||||
|
md5_update(ctx, (const unsigned char *) string, strlen(string));
|
||||||
|
md5_final(ctx, hash, NULL);
|
||||||
|
md5_destroy(ctx);
|
||||||
|
ctx = NULL;
|
||||||
|
char *result_str = utils_hex_to_string(hash, MD5_DIGEST_LENGTH);
|
||||||
|
return result_str; //must free result_str after use
|
||||||
|
}
|
||||||
|
|
||||||
int get_random_bytes(unsigned char *buf, int num) {
|
int get_random_bytes(unsigned char *buf, int num) {
|
||||||
return RAND_bytes(buf, num);
|
return RAND_bytes(buf, num);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,15 @@ void sha_final(sha_ctx_t *ctx, uint8_t *out, unsigned int *len);
|
|||||||
void sha_reset(sha_ctx_t *ctx);
|
void sha_reset(sha_ctx_t *ctx);
|
||||||
void sha_destroy(sha_ctx_t *ctx);
|
void sha_destroy(sha_ctx_t *ctx);
|
||||||
|
|
||||||
|
//MD5
|
||||||
|
typedef struct md5_ctx_s md5_ctx_t;
|
||||||
|
md5_ctx_t *md5_init();
|
||||||
|
void md5_update(md5_ctx_t *ctx, const uint8_t *in, int len);
|
||||||
|
void md5_final(md5_ctx_t *ctx, uint8_t *out, unsigned int *len);
|
||||||
|
void md5_reset(md5_ctx_t *ctx);
|
||||||
|
void md5_destroy(md5_ctx_t *ctx);
|
||||||
|
char *get_md5(char *string);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
47
lib/dnssd.c
47
lib/dnssd.c
@@ -151,17 +151,21 @@ struct dnssd_s {
|
|||||||
uint32_t features1;
|
uint32_t features1;
|
||||||
uint32_t features2;
|
uint32_t features2;
|
||||||
|
|
||||||
unsigned char require_pw;
|
unsigned char pin_pw;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dnssd_t *
|
dnssd_t *
|
||||||
dnssd_init(const char* name, int name_len, const char* hw_addr, int hw_addr_len, int *error, int require_pw)
|
dnssd_init(const char* name, int name_len, const char* hw_addr, int hw_addr_len, int *error, unsigned char pin_pw)
|
||||||
{
|
{
|
||||||
dnssd_t *dnssd;
|
dnssd_t *dnssd;
|
||||||
char *end;
|
char *end;
|
||||||
unsigned long features;
|
unsigned long features;
|
||||||
|
/* pin_pw = 0: no pin or password
|
||||||
|
1: use onscreen pin for client access control
|
||||||
|
2: require password for client accress control.
|
||||||
|
*/
|
||||||
|
|
||||||
if (error) *error = DNSSD_ERROR_NOERROR;
|
if (error) *error = DNSSD_ERROR_NOERROR;
|
||||||
|
|
||||||
@@ -171,7 +175,7 @@ dnssd_init(const char* name, int name_len, const char* hw_addr, int hw_addr_len,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dnssd->require_pw = (unsigned char) require_pw;
|
dnssd->pin_pw = pin_pw;
|
||||||
|
|
||||||
features = strtoul(FEATURES_1, &end, 16);
|
features = strtoul(FEATURES_1, &end, 16);
|
||||||
if (!end || (features & 0xFFFFFFFF) != features) {
|
if (!end || (features & 0xFFFFFFFF) != features) {
|
||||||
@@ -302,10 +306,19 @@ dnssd_register_raop(dnssd_t *dnssd, unsigned short port)
|
|||||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "am", strlen(GLOBAL_MODEL), GLOBAL_MODEL);
|
dnssd->TXTRecordSetValue(&dnssd->raop_record, "am", strlen(GLOBAL_MODEL), GLOBAL_MODEL);
|
||||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "md", strlen(RAOP_MD), RAOP_MD);
|
dnssd->TXTRecordSetValue(&dnssd->raop_record, "md", strlen(RAOP_MD), RAOP_MD);
|
||||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "rhd", strlen(RAOP_RHD), RAOP_RHD);
|
dnssd->TXTRecordSetValue(&dnssd->raop_record, "rhd", strlen(RAOP_RHD), RAOP_RHD);
|
||||||
if (dnssd->require_pw) {
|
switch (dnssd->pin_pw) {
|
||||||
|
case 2:
|
||||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "pw", strlen("true"), "true");
|
dnssd->TXTRecordSetValue(&dnssd->raop_record, "pw", strlen("true"), "true");
|
||||||
} else {
|
dnssd->TXTRecordSetValue(&dnssd->raop_record, "sf", 4, "0x84");
|
||||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "pw", strlen("false"), "false");
|
break;
|
||||||
|
case 1:
|
||||||
|
dnssd->TXTRecordSetValue(&dnssd->raop_record, "pw", strlen("true"), "true");
|
||||||
|
dnssd->TXTRecordSetValue(&dnssd->raop_record, "sf", 3, "0x8c");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dnssd->TXTRecordSetValue(&dnssd->raop_record, "pw", strlen("true"), "false");
|
||||||
|
dnssd->TXTRecordSetValue(&dnssd->raop_record, "sf", strlen(RAOP_SF), RAOP_SF);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "sr", strlen(RAOP_SR), RAOP_SR);
|
dnssd->TXTRecordSetValue(&dnssd->raop_record, "sr", strlen(RAOP_SR), RAOP_SR);
|
||||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "ss", strlen(RAOP_SS), RAOP_SS);
|
dnssd->TXTRecordSetValue(&dnssd->raop_record, "ss", strlen(RAOP_SS), RAOP_SS);
|
||||||
@@ -361,18 +374,26 @@ dnssd_register_airplay(dnssd_t *dnssd, unsigned short port)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// flags is a string representing a 20-bit flag (up to 3 hex digits)
|
||||||
dnssd->TXTRecordCreate(&dnssd->airplay_record, 0, NULL);
|
dnssd->TXTRecordCreate(&dnssd->airplay_record, 0, NULL);
|
||||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "deviceid", strlen(device_id), device_id);
|
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "deviceid", strlen(device_id), device_id);
|
||||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "features", strlen(features), features);
|
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "features", strlen(features), features);
|
||||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "flags", strlen(AIRPLAY_FLAGS), AIRPLAY_FLAGS);
|
switch (dnssd->pin_pw) {
|
||||||
|
case 1: // display onscreen pin
|
||||||
|
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pw", strlen("true"), "true");
|
||||||
|
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "flags", 3, "0x4");
|
||||||
|
break;
|
||||||
|
case 2: // require password
|
||||||
|
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pw", strlen("true"), "true");
|
||||||
|
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "flags", 3, "0x4");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pw", strlen("false"), "false");
|
||||||
|
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "flags", 3, "0x4");
|
||||||
|
break;
|
||||||
|
}
|
||||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "model", strlen(GLOBAL_MODEL), GLOBAL_MODEL);
|
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "model", strlen(GLOBAL_MODEL), GLOBAL_MODEL);
|
||||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pk", strlen(dnssd->pk), dnssd->pk);
|
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pk", strlen(dnssd->pk), dnssd->pk);
|
||||||
if (dnssd->require_pw) {
|
|
||||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pw", strlen("true"), "true");
|
|
||||||
} else {
|
|
||||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pw", strlen("false"), "false");
|
|
||||||
}
|
|
||||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pi", strlen(AIRPLAY_PI), AIRPLAY_PI);
|
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pi", strlen(AIRPLAY_PI), AIRPLAY_PI);
|
||||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "srcvers", strlen(AIRPLAY_SRCVERS), AIRPLAY_SRCVERS);
|
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "srcvers", strlen(AIRPLAY_SRCVERS), AIRPLAY_SRCVERS);
|
||||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "vv", strlen(AIRPLAY_VV), AIRPLAY_VV);
|
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "vv", strlen(AIRPLAY_VV), AIRPLAY_VV);
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ extern "C" {
|
|||||||
|
|
||||||
typedef struct dnssd_s dnssd_t;
|
typedef struct dnssd_s dnssd_t;
|
||||||
|
|
||||||
DNSSD_API dnssd_t *dnssd_init(const char *name, int name_len, const char *hw_addr, int hw_addr_len, int *error, int require_pw);
|
DNSSD_API dnssd_t *dnssd_init(const char *name, int name_len, const char *hw_addr, int hw_addr_len, int *error, unsigned char pin_pw);
|
||||||
|
|
||||||
DNSSD_API int dnssd_register_raop(dnssd_t *dnssd, unsigned short port);
|
DNSSD_API int dnssd_register_raop(dnssd_t *dnssd, unsigned short port);
|
||||||
DNSSD_API int dnssd_register_airplay(dnssd_t *dnssd, unsigned short port);
|
DNSSD_API int dnssd_register_airplay(dnssd_t *dnssd, unsigned short port);
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
#define RAOP_VN "65537"
|
#define RAOP_VN "65537"
|
||||||
|
|
||||||
#define AIRPLAY_SRCVERS GLOBAL_VERSION /*defined in global.h */
|
#define AIRPLAY_SRCVERS GLOBAL_VERSION /*defined in global.h */
|
||||||
#define AIRPLAY_FLAGS "0x4"
|
#define AIRPLAY_FLAGS "0x84"
|
||||||
#define AIRPLAY_VV "2"
|
#define AIRPLAY_VV "2"
|
||||||
#define AIRPLAY_PI "2e388006-13ba-4041-9a67-25dd4a43d536"
|
#define AIRPLAY_PI "2e388006-13ba-4041-9a67-25dd4a43d536"
|
||||||
|
|
||||||
|
|||||||
163
lib/pairing.c
163
lib/pairing.c
@@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@@ -195,6 +196,168 @@ pairing_session_get_public_key(pairing_session_t *session, unsigned char ecdh_ke
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
pairing_session_make_nonce(pairing_session_t *session, uint64_t *local_time, const char *client_data, unsigned char *nonce, int len) {
|
||||||
|
unsigned char hash[SHA512_DIGEST_LENGTH];
|
||||||
|
if (len > sizeof(hash)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!client_data || !local_time || !session || !nonce || len <= 0) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
sha_ctx_t *ctx = sha_init();
|
||||||
|
sha_update(ctx, (const unsigned char *) local_time, sizeof(uint64_t));
|
||||||
|
sha_update(ctx, (const unsigned char *) client_data, strlen(client_data));
|
||||||
|
sha_update(ctx, (const unsigned char *) session->ed_ours, ED25519_KEY_SIZE);
|
||||||
|
sha_final(ctx, hash, NULL);
|
||||||
|
sha_destroy(ctx);
|
||||||
|
memcpy(nonce, hash, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
char *get_token(char **cursor, char *token_name, char start_char, char end_char) {
|
||||||
|
char *ptr = *cursor;
|
||||||
|
|
||||||
|
ptr = strstr(ptr, token_name);
|
||||||
|
if (!ptr) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ptr += strlen(token_name);
|
||||||
|
ptr = strchr(ptr, start_char);
|
||||||
|
if (!ptr) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *token = ++ptr;
|
||||||
|
ptr = strchr(ptr, end_char);
|
||||||
|
if (!ptr) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(ptr++) = '\0';
|
||||||
|
*cursor = ptr;
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#define test_digest
|
||||||
|
bool
|
||||||
|
pairing_digest_verify(const char *method, const char * authorization, const char *password) {
|
||||||
|
/* RFC 2617 HTTP md5 Digest password authentication */
|
||||||
|
|
||||||
|
char *sentence = (char *) calloc(strlen(authorization) + 1, sizeof(char));
|
||||||
|
strncpy(sentence, authorization, strlen(authorization));
|
||||||
|
char *username = NULL;
|
||||||
|
char *realm = NULL;
|
||||||
|
char *nonce = NULL;
|
||||||
|
char *uri = NULL;
|
||||||
|
char *qop = NULL;
|
||||||
|
char *nc = NULL;
|
||||||
|
char *cnonce = NULL;
|
||||||
|
char *response = NULL;
|
||||||
|
|
||||||
|
char *cursor = sentence;
|
||||||
|
const char *pwd = password;
|
||||||
|
const char *mthd = method;
|
||||||
|
char *raw;
|
||||||
|
int len;
|
||||||
|
bool authenticated;
|
||||||
|
|
||||||
|
#ifdef test_digest
|
||||||
|
char testauth[] = "Digest username=\"Mufasa\","
|
||||||
|
"realm=\"testrealm@host.com\","
|
||||||
|
"nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
|
||||||
|
"uri=\"/dir/index.html\","
|
||||||
|
"qop=auth,"
|
||||||
|
"nc=00000001,"
|
||||||
|
"cnonce=\"0a4f113b\","
|
||||||
|
"response=\"6629fae49393a05397450978507c4ef1\","
|
||||||
|
"opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""
|
||||||
|
;
|
||||||
|
pwd = "Circle Of Life";
|
||||||
|
mthd = "GET";
|
||||||
|
cursor = testauth;
|
||||||
|
|
||||||
|
char HA1[] = "939e7578ed9e3c518a452acee763bce9";
|
||||||
|
char HA2[] = "39aff3a2bab6126f332b942af96d3366";
|
||||||
|
#endif
|
||||||
|
username = get_token(&cursor, "username", '\"', '\"');
|
||||||
|
realm = get_token(&cursor, "realm", '\"', '\"');
|
||||||
|
nonce = get_token(&cursor,"nonce", '\"', '\"');
|
||||||
|
uri = get_token(&cursor,"uri", '\"', '\"');
|
||||||
|
qop = get_token(&cursor, "qop", '=', ',');
|
||||||
|
if (qop) {
|
||||||
|
nc = get_token(&cursor, "nc", '=', ',');
|
||||||
|
cnonce = get_token(&cursor, "cnonce", '\"', '\"');
|
||||||
|
}
|
||||||
|
response = get_token(&cursor, "response", '\"', '\"');
|
||||||
|
|
||||||
|
#ifdef test_digest
|
||||||
|
printf("username: [%s] realm: [%s]\n", username, realm);
|
||||||
|
printf("nonce: [%s]\n", nonce);
|
||||||
|
printf("method: [%s]\n", mthd);
|
||||||
|
printf("uri: [%s]\n", uri);
|
||||||
|
if (qop) {
|
||||||
|
printf("qop: [%s], nc=[%s], cnonce: [%s]\n", qop, nc, cnonce);
|
||||||
|
}
|
||||||
|
printf("response: [%s]\n", response);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* H1 = H(username : realm : password ) */
|
||||||
|
len = strlen(username) + strlen(realm) + strlen(pwd) + 3;
|
||||||
|
raw = (char *) calloc(len, sizeof(char));
|
||||||
|
snprintf(raw, len, "%s:%s:%s", username, realm, pwd);
|
||||||
|
char *hash1 = get_md5(raw);
|
||||||
|
free (raw);
|
||||||
|
|
||||||
|
#ifdef test_digest
|
||||||
|
printf("hash1: should be %s, was: %s\n ", HA1, hash1_str);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* H2 = H(method : uri) */
|
||||||
|
len = strlen(mthd) + strlen(uri) + 2;
|
||||||
|
raw = (char *) calloc(len, sizeof(char));
|
||||||
|
snprintf(raw, len, "%s:%s", mthd, uri);
|
||||||
|
char *hash2 = get_md5(raw);
|
||||||
|
free (raw);
|
||||||
|
|
||||||
|
#ifdef test_digest
|
||||||
|
printf("hash2: should be %s, was: %s\n", HA2, hash2_str);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* result = H(H1 : nonce (or nonce:nc:cnonce:qop) : H2) */
|
||||||
|
len = strlen(hash1) + strlen(nonce) + strlen(hash2) + 3;
|
||||||
|
if (qop) {
|
||||||
|
len += strlen(nc) + strlen(cnonce) + strlen(qop) + 3;
|
||||||
|
raw = (char *) calloc(len, sizeof(char));
|
||||||
|
snprintf(raw, len, "%s:%s:%s:%s:%s:%s", hash1, nonce, nc, cnonce, qop, hash2);
|
||||||
|
} else {
|
||||||
|
raw = (char *) calloc(len, sizeof(char));
|
||||||
|
snprintf(raw, len, "%s:%s:%s", hash1, nonce, hash2);
|
||||||
|
}
|
||||||
|
free (hash1);
|
||||||
|
free (hash2);
|
||||||
|
char *result = get_md5(raw);
|
||||||
|
free (raw);
|
||||||
|
authenticated = (strcmp(result,response) ? false : true);
|
||||||
|
|
||||||
|
#ifdef test_digest
|
||||||
|
printf("result: should be %s, was: %s, authenticated is %s\n", response, result, (authenticated ? "true" : "false"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
free (result);
|
||||||
|
free(sentence);
|
||||||
|
|
||||||
|
#ifdef test_digest
|
||||||
|
exit(0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return authenticated;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
pairing_session_get_signature(pairing_session_t *session, unsigned char signature[PAIRING_SIG_SIZE])
|
pairing_session_get_signature(pairing_session_t *session, unsigned char signature[PAIRING_SIG_SIZE])
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -62,4 +62,7 @@ int srp_confirm_pair_setup(pairing_session_t *session, pairing_t *pairing, unsig
|
|||||||
unsigned char *auth_tag);
|
unsigned char *auth_tag);
|
||||||
void access_client_session_data(pairing_session_t *session, char **username, char **client_pk, bool *setup);
|
void access_client_session_data(pairing_session_t *session, char **username, char **client_pk, bool *setup);
|
||||||
void ed25519_pk_to_base64(const unsigned char *pk, char **pk64);
|
void ed25519_pk_to_base64(const unsigned char *pk, char **pk64);
|
||||||
|
int pairing_session_make_nonce(pairing_session_t *session, uint64_t *local_time, const char *client_data, unsigned char *nonce, int len);
|
||||||
|
bool pairing_digest_verify(const char *method, const char * authorization, const char *password);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
17
lib/raop.c
17
lib/raop.c
@@ -77,6 +77,10 @@ struct raop_s {
|
|||||||
|
|
||||||
/* activate support for HLS live streaming */
|
/* activate support for HLS live streaming */
|
||||||
bool hls_support;
|
bool hls_support;
|
||||||
|
|
||||||
|
/* used in digest authentication */
|
||||||
|
char *nonce;
|
||||||
|
char * random_pw;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct raop_conn_s {
|
struct raop_conn_s {
|
||||||
@@ -99,7 +103,7 @@ struct raop_conn_s {
|
|||||||
connection_type_t connection_type;
|
connection_type_t connection_type;
|
||||||
|
|
||||||
char *client_session_id;
|
char *client_session_id;
|
||||||
|
bool authenticated;
|
||||||
bool have_active_remote;
|
bool have_active_remote;
|
||||||
};
|
};
|
||||||
typedef struct raop_conn_s raop_conn_t;
|
typedef struct raop_conn_s raop_conn_t;
|
||||||
@@ -159,6 +163,7 @@ conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remot
|
|||||||
conn->client_session_id = NULL;
|
conn->client_session_id = NULL;
|
||||||
conn->airplay_video = NULL;
|
conn->airplay_video = NULL;
|
||||||
|
|
||||||
|
conn->authenticated = false;
|
||||||
|
|
||||||
conn->have_active_remote = false;
|
conn->have_active_remote = false;
|
||||||
|
|
||||||
@@ -579,6 +584,7 @@ raop_init(raop_callbacks_t *callbacks) {
|
|||||||
|
|
||||||
raop->hls_support = false;
|
raop->hls_support = false;
|
||||||
|
|
||||||
|
raop->nonce = NULL;
|
||||||
return raop;
|
return raop;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,7 +608,7 @@ raop_init2(raop_t *raop, int nohold, const char *device_id, const char *keyfile)
|
|||||||
#else
|
#else
|
||||||
unsigned char public_key[ED25519_KEY_SIZE];
|
unsigned char public_key[ED25519_KEY_SIZE];
|
||||||
pairing_get_public_key(pairing, public_key);
|
pairing_get_public_key(pairing, public_key);
|
||||||
char *pk_str = utils_pk_to_string(public_key, ED25519_KEY_SIZE);
|
char *pk_str = utils_hex_to_string(public_key, ED25519_KEY_SIZE);
|
||||||
strncpy(raop->pk_str, (const char *) pk_str, 2*ED25519_KEY_SIZE);
|
strncpy(raop->pk_str, (const char *) pk_str, 2*ED25519_KEY_SIZE);
|
||||||
free(pk_str);
|
free(pk_str);
|
||||||
#endif
|
#endif
|
||||||
@@ -638,6 +644,13 @@ raop_destroy(raop_t *raop) {
|
|||||||
pairing_destroy(raop->pairing);
|
pairing_destroy(raop->pairing);
|
||||||
httpd_destroy(raop->httpd);
|
httpd_destroy(raop->httpd);
|
||||||
logger_destroy(raop->logger);
|
logger_destroy(raop->logger);
|
||||||
|
if (raop->nonce) {
|
||||||
|
free(raop->nonce);
|
||||||
|
}
|
||||||
|
if (raop->random_pw) {
|
||||||
|
free(raop->random_pw);
|
||||||
|
}
|
||||||
|
|
||||||
free(raop);
|
free(raop);
|
||||||
|
|
||||||
/* Cleanup the network */
|
/* Cleanup the network */
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ struct raop_callbacks_s {
|
|||||||
void (*display_pin) (void *cls, char * pin);
|
void (*display_pin) (void *cls, char * pin);
|
||||||
void (*register_client) (void *cls, const char *device_id, const char *pk_str, const char *name);
|
void (*register_client) (void *cls, const char *device_id, const char *pk_str, const char *name);
|
||||||
bool (*check_register) (void *cls, const char *pk_str);
|
bool (*check_register) (void *cls, const char *pk_str);
|
||||||
|
const char* (*passwd) (void *cls, int *len);
|
||||||
void (*export_dacp) (void *cls, const char *active_remote, const char *dacp_id);
|
void (*export_dacp) (void *cls, const char *active_remote, const char *dacp_id);
|
||||||
int (*video_set_codec)(void *cls, video_codec_t codec);
|
int (*video_set_codec)(void *cls, video_codec_t codec);
|
||||||
/* for HLS video player controls */
|
/* for HLS video player controls */
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <plist/plist.h>
|
#include <plist/plist.h>
|
||||||
#define AUDIO_SAMPLE_RATE 44100 /* all supported AirPlay audio format use this sample rate */
|
#define AUDIO_SAMPLE_RATE 44100 /* all supported AirPlay audio format use this sample rate */
|
||||||
#define SECOND_IN_USECS 1000000
|
#define SECOND_IN_USECS 1000000
|
||||||
|
#define SECOND_IN_NSECS 1000000000
|
||||||
|
|
||||||
typedef void (*raop_handler_t)(raop_conn_t *, http_request_t *,
|
typedef void (*raop_handler_t)(raop_conn_t *, http_request_t *,
|
||||||
http_response_t *, char **, int *);
|
http_response_t *, char **, int *);
|
||||||
@@ -36,6 +37,33 @@ raop_handler_info(raop_conn_t *conn,
|
|||||||
{
|
{
|
||||||
assert(conn->raop->dnssd);
|
assert(conn->raop->dnssd);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* initial GET/info request sends plist with string "txtAirPlay" */
|
||||||
|
bool txtAirPlay = false;
|
||||||
|
const char* content_type = NULL;
|
||||||
|
content_type = http_request_get_header(request, "Content-Type");
|
||||||
|
if (content_type && strstr(content_type, "application/x-apple-binary-plist")) {
|
||||||
|
char *qualifier_string = NULL;
|
||||||
|
const char *data = NULL;
|
||||||
|
int data_len = 0;
|
||||||
|
data = http_request_get_data(request, &data_len);
|
||||||
|
//parsing bplist
|
||||||
|
plist_t req_root_node = NULL;
|
||||||
|
plist_from_bin(data, data_len, &req_root_node);
|
||||||
|
plist_t req_qualifier_node = plist_dict_get_item(req_root_node, "qualifier");
|
||||||
|
if (PLIST_IS_ARRAY(req_qualifier_node)) {
|
||||||
|
plist_t req_string_node = plist_array_get_item(req_qualifier_node, 0);
|
||||||
|
plist_get_string_val(req_string_node, &qualifier_string);
|
||||||
|
}
|
||||||
|
if (qualifier_string && !strcmp(qualifier_string, "txtAirPlay")) {
|
||||||
|
printf("qualifier: %s\n", qualifier_string);
|
||||||
|
txtAirPlay = true;
|
||||||
|
}
|
||||||
|
if (qualifier_string) {
|
||||||
|
free(qualifier_string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
plist_t res_node = plist_new_dict();
|
plist_t res_node = plist_new_dict();
|
||||||
|
|
||||||
/* deviceID is the physical hardware address, and will not change */
|
/* deviceID is the physical hardware address, and will not change */
|
||||||
@@ -555,6 +583,81 @@ raop_handler_setup(raop_conn_t *conn,
|
|||||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "SETUP 1");
|
logger_log(conn->raop->logger, LOGGER_DEBUG, "SETUP 1");
|
||||||
|
|
||||||
// First setup
|
// First setup
|
||||||
|
|
||||||
|
/* RFC2617 Digest authentication (md5 hash) of uxplay client-access password, if set */
|
||||||
|
if (!conn->authenticated && conn->raop->callbacks.passwd) {
|
||||||
|
int len;
|
||||||
|
const char *password = conn->raop->callbacks.passwd(conn->raop->callbacks.cls, &len);
|
||||||
|
// len = -1 means use a random password for this connection
|
||||||
|
if (len == -1 && !conn->raop->random_pw) {
|
||||||
|
// get and store 4 random digits
|
||||||
|
int pin_4 = random_pin();
|
||||||
|
if (pin_4 < 0) {
|
||||||
|
logger_log(conn->raop->logger, LOGGER_ERR, "Failed to generate random pin");
|
||||||
|
}
|
||||||
|
char pin[6] = {'\0'};
|
||||||
|
snprintf(pin, 5, "%04u", pin_4 % 10000);
|
||||||
|
printf("*** set new pin = [%s]\n", pin);
|
||||||
|
conn->raop->random_pw = strndup((const char *) pin, 6);
|
||||||
|
printf("*** stored new pin = [%s]\n", conn->raop->random_pw);
|
||||||
|
}
|
||||||
|
if (len == -1 && conn->raop->callbacks.display_pin) {
|
||||||
|
char *pin = conn->raop->random_pw;
|
||||||
|
assert(pin);
|
||||||
|
conn->raop->callbacks.display_pin(conn->raop->callbacks.cls, pin);
|
||||||
|
logger_log(conn->raop->logger, LOGGER_INFO, "*** CLIENT MUST NOW ENTER PIN = \"%s\" AS AIRPLAY PASSWORD", pin);
|
||||||
|
password = (const char *) pin;
|
||||||
|
}
|
||||||
|
if (len && !conn->authenticated) {
|
||||||
|
char nonce_string[33] = { '\0' };
|
||||||
|
//bool stale = false; //not implemented
|
||||||
|
const char *authorization = NULL;
|
||||||
|
authorization = http_request_get_header(request, "Authorization");
|
||||||
|
if (authorization) {
|
||||||
|
char *ptr = strstr(authorization, "nonce=\"") + strlen("nonce=\"");
|
||||||
|
strncpy(nonce_string, ptr, 32);
|
||||||
|
const char *method = http_request_get_method(request);
|
||||||
|
conn->authenticated = pairing_digest_verify(method, authorization, password);
|
||||||
|
if (conn->authenticated) {
|
||||||
|
printf("initial authenticatication OK\n");
|
||||||
|
conn->authenticated = conn->authenticated && !strcmp(nonce_string, conn->raop->nonce);
|
||||||
|
if (!conn->authenticated) {
|
||||||
|
logger_log(conn->raop->logger, LOGGER_INFO, "authentication rejected (nonce mismatch) %s %s",
|
||||||
|
nonce_string, conn->raop->nonce);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (conn->authenticated && conn->raop->random_pw) {
|
||||||
|
printf("*********free random_pw\n");
|
||||||
|
free (conn->raop->random_pw);
|
||||||
|
conn->raop->random_pw = NULL;
|
||||||
|
}
|
||||||
|
if (conn->raop->nonce) {
|
||||||
|
free(conn->raop->nonce);
|
||||||
|
conn->raop->nonce = NULL;
|
||||||
|
}
|
||||||
|
logger_log(conn->raop->logger, LOGGER_INFO, "Client authentication %s", (conn->authenticated ? "success" : "failure"));
|
||||||
|
}
|
||||||
|
if (!conn->authenticated) {
|
||||||
|
/* create a nonce */
|
||||||
|
const char *url = http_request_get_url(request);
|
||||||
|
unsigned char nonce[16] = { '\0' };
|
||||||
|
int len = 16;
|
||||||
|
uint64_t now = raop_ntp_get_local_time();
|
||||||
|
assert (!pairing_session_make_nonce(conn->session, &now, url, nonce, len));
|
||||||
|
if (conn->raop->nonce) {
|
||||||
|
free(conn->raop->nonce);
|
||||||
|
}
|
||||||
|
conn->raop->nonce = utils_hex_to_string(nonce, len);
|
||||||
|
char response_text[80] = "Digest realm=\"raop\", nonce=\"";
|
||||||
|
strncat(response_text, conn->raop->nonce, strlen(conn->raop->nonce));
|
||||||
|
strncat(response_text, "\"", 1);
|
||||||
|
http_response_init(response, "RTSP/1.0", 401, "Unauthorized");
|
||||||
|
http_response_add_header(response, "WWW-Authenticate", response_text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
char* eiv = NULL;
|
char* eiv = NULL;
|
||||||
uint64_t eiv_len = 0;
|
uint64_t eiv_len = 0;
|
||||||
|
|
||||||
@@ -1047,7 +1150,7 @@ raop_handler_teardown(raop_conn_t *conn,
|
|||||||
uint64_t val;
|
uint64_t val;
|
||||||
int count = plist_array_get_size(req_streams_node);
|
int count = plist_array_get_size(req_streams_node);
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
plist_t req_stream_node = plist_array_get_item(req_streams_node,0);
|
plist_t req_stream_node = plist_array_get_item(req_streams_node,i);
|
||||||
plist_t req_stream_type_node = plist_dict_get_item(req_stream_node, "type");
|
plist_t req_stream_type_node = plist_dict_get_item(req_stream_node, "type");
|
||||||
plist_get_uint_val(req_stream_type_node, &val);
|
plist_get_uint_val(req_stream_type_node, &val);
|
||||||
if (val == 96) {
|
if (val == 96) {
|
||||||
|
|||||||
12
lib/utils.c
12
lib/utils.c
@@ -186,14 +186,14 @@ char *utils_parse_hex(const char *str, int str_len, int *data_len) {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *utils_pk_to_string(const unsigned char *pk, int pk_len) {
|
char *utils_hex_to_string(const unsigned char *hex, int hex_len) {
|
||||||
char *pk_str = (char *) malloc(2*pk_len + 1);
|
char *hex_str = (char *) malloc(2*hex_len + 1);
|
||||||
char* pos = pk_str;
|
char* pos = hex_str;
|
||||||
for (int i = 0; i < pk_len; i++) {
|
for (int i = 0; i < hex_len; i++) {
|
||||||
snprintf(pos, 3, "%2.2x", *(pk + i));
|
snprintf(pos, 3, "%2.2x", *(hex + i));
|
||||||
pos +=2;
|
pos +=2;
|
||||||
}
|
}
|
||||||
return pk_str;
|
return hex_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *utils_data_to_string(const unsigned char *data, int datalen, int chars_per_line) {
|
char *utils_data_to_string(const unsigned char *data, int datalen, int chars_per_line) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ int utils_read_file(char **dst, const char *pemstr);
|
|||||||
int utils_hwaddr_raop(char *str, int strlen, const char *hwaddr, int hwaddrlen);
|
int utils_hwaddr_raop(char *str, int strlen, const char *hwaddr, int hwaddrlen);
|
||||||
int utils_hwaddr_airplay(char *str, int strlen, const char *hwaddr, int hwaddrlen);
|
int utils_hwaddr_airplay(char *str, int strlen, const char *hwaddr, int hwaddrlen);
|
||||||
char *utils_parse_hex(const char *str, int str_len, int *data_len);
|
char *utils_parse_hex(const char *str, int str_len, int *data_len);
|
||||||
char *utils_pk_to_string(const unsigned char *pk, int pk_len);
|
char *utils_hex_to_string(const unsigned char *hex, int hex_len);
|
||||||
char *utils_data_to_string(const unsigned char *data, int datalen, int chars_per_line);
|
char *utils_data_to_string(const unsigned char *data, int datalen, int chars_per_line);
|
||||||
char *utils_data_to_text(const char *data, int datalen);
|
char *utils_data_to_text(const char *data, int datalen);
|
||||||
void ntp_timestamp_to_time(uint64_t ntp_timestamp, char *timestamp, size_t maxsize);
|
void ntp_timestamp_to_time(uint64_t ntp_timestamp, char *timestamp, size_t maxsize);
|
||||||
|
|||||||
7
uxplay.1
7
uxplay.1
@@ -27,6 +27,13 @@ UxPlay 1.71: An open\-source AirPlay mirroring (+ audio streaming) server:
|
|||||||
.IP
|
.IP
|
||||||
client pin-registration; (option: use file "fn" for this)
|
client pin-registration; (option: use file "fn" for this)
|
||||||
.TP
|
.TP
|
||||||
|
\fB\-pw\fI [pwd]\fR Require use of password "pwd" to control client access.
|
||||||
|
.IP
|
||||||
|
(with no \fIpwd\fR, pin entry is required at \fIeach\fR connection.)
|
||||||
|
|
||||||
|
.IP
|
||||||
|
(option "-pw" after "-pin" overrides it, and vice versa.)
|
||||||
|
.TP
|
||||||
\fB\-vsync\fI[x]\fR Mirror mode: sync audio to video using timestamps (default)
|
\fB\-vsync\fI[x]\fR Mirror mode: sync audio to video using timestamps (default)
|
||||||
.IP
|
.IP
|
||||||
\fIx\fR is optional audio delay: millisecs, decimal, can be neg.
|
\fIx\fR is optional audio delay: millisecs, decimal, can be neg.
|
||||||
|
|||||||
52
uxplay.cpp
52
uxplay.cpp
@@ -65,7 +65,7 @@
|
|||||||
#include "renderers/video_renderer.h"
|
#include "renderers/video_renderer.h"
|
||||||
#include "renderers/audio_renderer.h"
|
#include "renderers/audio_renderer.h"
|
||||||
|
|
||||||
#define VERSION "1.71"
|
#define VERSION "1.712beta"
|
||||||
|
|
||||||
#define SECOND_IN_USECS 1000000
|
#define SECOND_IN_USECS 1000000
|
||||||
#define SECOND_IN_NSECS 1000000000UL
|
#define SECOND_IN_NSECS 1000000000UL
|
||||||
@@ -74,6 +74,7 @@
|
|||||||
#define LOWEST_ALLOWED_PORT 1024
|
#define LOWEST_ALLOWED_PORT 1024
|
||||||
#define HIGHEST_PORT 65535
|
#define HIGHEST_PORT 65535
|
||||||
#define MISSED_FEEDBACK_LIMIT 15
|
#define MISSED_FEEDBACK_LIMIT 15
|
||||||
|
#define MIN_PASSWORD_LENGTH 4
|
||||||
#define DEFAULT_PLAYBIN_VERSION 3
|
#define DEFAULT_PLAYBIN_VERSION 3
|
||||||
#define BT709_FIX "capssetter caps=\"video/x-h264, colorimetry=bt709\""
|
#define BT709_FIX "capssetter caps=\"video/x-h264, colorimetry=bt709\""
|
||||||
#define SRGB_FIX " ! video/x-raw,colorimetry=sRGB,format=RGB ! "
|
#define SRGB_FIX " ! video/x-raw,colorimetry=sRGB,format=RGB ! "
|
||||||
@@ -143,7 +144,9 @@ static std::vector<std::string> allowed_clients;
|
|||||||
static std::vector<std::string> blocked_clients;
|
static std::vector<std::string> blocked_clients;
|
||||||
static bool restrict_clients;
|
static bool restrict_clients;
|
||||||
static bool setup_legacy_pairing = false;
|
static bool setup_legacy_pairing = false;
|
||||||
static bool require_password = false;
|
static unsigned char pin_pw = 0; /* 0: no client access control; 1: onscreen pin ; 2: require password (same password for all clients) 3: random pw*/
|
||||||
|
static std::string password = "";
|
||||||
|
static guint min_password_length = MIN_PASSWORD_LENGTH;
|
||||||
static unsigned short pin = 0;
|
static unsigned short pin = 0;
|
||||||
static std::string keyfile = "";
|
static std::string keyfile = "";
|
||||||
static std::string mac_address = "";
|
static std::string mac_address = "";
|
||||||
@@ -665,6 +668,9 @@ static void print_info (char *name) {
|
|||||||
printf(" default pin is random: optionally use fixed pin xxxx\n");
|
printf(" default pin is random: optionally use fixed pin xxxx\n");
|
||||||
printf("-reg [fn] Keep a register in $HOME/.uxplay.register to verify returning\n");
|
printf("-reg [fn] Keep a register in $HOME/.uxplay.register to verify returning\n");
|
||||||
printf(" client pin-registration; (option: use file \"fn\" for this)\n");
|
printf(" client pin-registration; (option: use file \"fn\" for this)\n");
|
||||||
|
printf("-pw [pwd] Require use of password to control client access;\n");
|
||||||
|
printf(" (with no pwd, pin entry is required at *each* connection.)\n");
|
||||||
|
printf(" (option \"-pw\" after \"-pin\" overrides it, and vice versa)\n");
|
||||||
printf("-vsync [x]Mirror mode: sync audio to video using timestamps (default)\n");
|
printf("-vsync [x]Mirror mode: sync audio to video using timestamps (default)\n");
|
||||||
printf(" x is optional audio delay: millisecs, decimal, can be neg.\n");
|
printf(" x is optional audio delay: millisecs, decimal, can be neg.\n");
|
||||||
printf("-vsync no Switch off audio/(server)video timestamp synchronization \n");
|
printf("-vsync no Switch off audio/(server)video timestamp synchronization \n");
|
||||||
@@ -1189,7 +1195,7 @@ static void parse_arguments (int argc, char *argv[]) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
} else if (arg == "-pin") {
|
} else if (arg == "-pin") {
|
||||||
setup_legacy_pairing = true;
|
setup_legacy_pairing = true;
|
||||||
require_password = true;
|
pin_pw = 1;
|
||||||
if (i < argc - 1 && *argv[i+1] != '-') {
|
if (i < argc - 1 && *argv[i+1] != '-') {
|
||||||
unsigned int n = 9999;
|
unsigned int n = 9999;
|
||||||
if (!get_value(argv[++i], &n)) {
|
if (!get_value(argv[++i], &n)) {
|
||||||
@@ -1224,6 +1230,19 @@ static void parse_arguments (int argc, char *argv[]) {
|
|||||||
keyfile.erase();
|
keyfile.erase();
|
||||||
keyfile.append("0");
|
keyfile.append("0");
|
||||||
}
|
}
|
||||||
|
} else if (arg == "-pw") {
|
||||||
|
setup_legacy_pairing = false;
|
||||||
|
if (!option_has_value(i, argc, arg, argv[i+1])) {
|
||||||
|
pin_pw = 3;
|
||||||
|
} else if (i < argc - 1 && *argv[i+1] != '-') {
|
||||||
|
password.erase();
|
||||||
|
password.append(argv[++i]);
|
||||||
|
pin_pw = 2;
|
||||||
|
if (password.size() < min_password_length) {
|
||||||
|
fprintf(stderr, "invalid client-access password \"%s\": length must be at least %u characters\n", password.c_str(), min_password_length);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (arg == "-dacp") {
|
} else if (arg == "-dacp") {
|
||||||
dacpfile.erase();
|
dacpfile.erase();
|
||||||
if (i < argc - 1 && *argv[i+1] != '-') {
|
if (i < argc - 1 && *argv[i+1] != '-') {
|
||||||
@@ -1504,12 +1523,16 @@ static void stop_dnssd() {
|
|||||||
|
|
||||||
static int start_dnssd(std::vector<char> hw_addr, std::string name) {
|
static int start_dnssd(std::vector<char> hw_addr, std::string name) {
|
||||||
int dnssd_error;
|
int dnssd_error;
|
||||||
int require_pw = (require_password ? 1 : 0);
|
|
||||||
if (dnssd) {
|
if (dnssd) {
|
||||||
LOGE("start_dnssd error: dnssd != NULL");
|
LOGE("start_dnssd error: dnssd != NULL");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
dnssd = dnssd_init(name.c_str(), strlen(name.c_str()), hw_addr.data(), hw_addr.size(), &dnssd_error, require_pw);
|
/* pin_pw controls client access
|
||||||
|
pin_pw = 1: client must enter pin displayed onscreen (first access only)
|
||||||
|
= 2: client must enter password (same password for all clients)
|
||||||
|
= 0: no access control
|
||||||
|
*/
|
||||||
|
dnssd = dnssd_init(name.c_str(), strlen(name.c_str()), hw_addr.data(), hw_addr.size(), &dnssd_error, pin_pw);
|
||||||
if (dnssd_error) {
|
if (dnssd_error) {
|
||||||
LOGE("Could not initialize dnssd library!: error %d", dnssd_error);
|
LOGE("Could not initialize dnssd library!: error %d", dnssd_error);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -1665,6 +1688,15 @@ extern "C" void display_pin(void *cls, char *pin) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" const char *passwd(void *cls, int *len){
|
||||||
|
if (pin_pw == 3) {
|
||||||
|
*len = -1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
*len = password.size();
|
||||||
|
return password.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" void export_dacp(void *cls, const char *active_remote, const char *dacp_id) {
|
extern "C" void export_dacp(void *cls, const char *active_remote, const char *dacp_id) {
|
||||||
if (dacpfile.length()) {
|
if (dacpfile.length()) {
|
||||||
FILE *fp = fopen(dacpfile.c_str(), "w");
|
FILE *fp = fopen(dacpfile.c_str(), "w");
|
||||||
@@ -1791,6 +1823,7 @@ extern "C" void video_process (void *cls, raop_ntp_t *ntp, video_decode_struct *
|
|||||||
data->ntp_time_remote = data->ntp_time_remote + remote_clock_offset;
|
data->ntp_time_remote = data->ntp_time_remote + remote_clock_offset;
|
||||||
pts_mismatch = video_renderer_render_buffer(data->data, &(data->data_len), &(data->nal_count), &(data->ntp_time_remote));
|
pts_mismatch = video_renderer_render_buffer(data->data, &(data->data_len), &(data->nal_count), &(data->ntp_time_remote));
|
||||||
if (pts_mismatch) {
|
if (pts_mismatch) {
|
||||||
|
LOGI("adjust timestamps by %8.6f secs", (double) (pts_mismatch / SECOND_IN_NSECS));
|
||||||
remote_clock_offset += pts_mismatch;
|
remote_clock_offset += pts_mismatch;
|
||||||
}
|
}
|
||||||
count++;
|
count++;
|
||||||
@@ -2096,6 +2129,7 @@ static int start_raop_server (unsigned short display[5], unsigned short tcp[3],
|
|||||||
raop_cbs.display_pin = display_pin;
|
raop_cbs.display_pin = display_pin;
|
||||||
raop_cbs.register_client = register_client;
|
raop_cbs.register_client = register_client;
|
||||||
raop_cbs.check_register = check_register;
|
raop_cbs.check_register = check_register;
|
||||||
|
raop_cbs.passwd = passwd;
|
||||||
raop_cbs.export_dacp = export_dacp;
|
raop_cbs.export_dacp = export_dacp;
|
||||||
raop_cbs.video_reset = video_reset;
|
raop_cbs.video_reset = video_reset;
|
||||||
raop_cbs.video_set_codec = video_set_codec;
|
raop_cbs.video_set_codec = video_set_codec;
|
||||||
@@ -2130,7 +2164,7 @@ static int start_raop_server (unsigned short display[5], unsigned short tcp[3],
|
|||||||
|
|
||||||
if (show_client_FPS_data) raop_set_plist(raop, "clientFPSdata", 1);
|
if (show_client_FPS_data) raop_set_plist(raop, "clientFPSdata", 1);
|
||||||
if (audiodelay >= 0) raop_set_plist(raop, "audio_delay_micros", audiodelay);
|
if (audiodelay >= 0) raop_set_plist(raop, "audio_delay_micros", audiodelay);
|
||||||
if (require_password) raop_set_plist(raop, "pin", (int) pin);
|
if (pin_pw == 1) raop_set_plist(raop, "pin", (int) pin);
|
||||||
if (hls_support) raop_set_plist(raop, "hls", 1);
|
if (hls_support) raop_set_plist(raop, "hls", 1);
|
||||||
|
|
||||||
/* network port selection (ports listed as "0" will be dynamically assigned) */
|
/* network port selection (ports listed as "0" will be dynamically assigned) */
|
||||||
@@ -2354,7 +2388,7 @@ int main (int argc, char *argv[]) {
|
|||||||
video_converter.append(option);
|
video_converter.append(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (require_password && registration_list) {
|
if (pin_pw == 1 && registration_list) {
|
||||||
if (pairing_register == "") {
|
if (pairing_register == "") {
|
||||||
const char * homedir = get_homedir();
|
const char * homedir = get_homedir();
|
||||||
if (homedir) {
|
if (homedir) {
|
||||||
@@ -2365,7 +2399,7 @@ int main (int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* read in public keys that were previously registered with pair-setup-pin */
|
/* read in public keys that were previously registered with pair-setup-pin */
|
||||||
if (require_password && registration_list && strlen(pairing_register.c_str())) {
|
if (pin_pw == 1 && registration_list && strlen(pairing_register.c_str())) {
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
std::string key;
|
std::string key;
|
||||||
int clients = 0;
|
int clients = 0;
|
||||||
@@ -2386,7 +2420,7 @@ int main (int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (require_password && keyfile == "0") {
|
if (pin_pw == 1 && keyfile == "0") {
|
||||||
const char * homedir = get_homedir();
|
const char * homedir = get_homedir();
|
||||||
if (homedir) {
|
if (homedir) {
|
||||||
keyfile.erase();
|
keyfile.erase();
|
||||||
|
|||||||
Reference in New Issue
Block a user