diff --git a/README.html b/README.html index bfe24c3..2a5298c 100644 --- a/README.html +++ b/README.html @@ -1,6 +1,6 @@

UxPlay -1.67: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix +id="uxplay-1.68-airplay-mirror-and-airplay-audio-server-for-linux-macos-and-unix-now-also-runs-on-windows.">UxPlay +1.68: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows).

Now @@ -9,9 +9,13 @@ href="https://github.com/FDH2/UxPlay">https://github.com/FDH2/UxPlay (where ALL user issues should be posted, and latest versions can be found).

Highlights:

+

AirPlay volume-control attenuates volume (gain) by up to -30dB: the +range -30dB:0dB can be rescaled from Low:0, or +Low:High, using the option -db (“-db +Low” or “-db Low:High”), Low must be +negative. Rescaling is linear in decibels. The option +-taper provides a “tapered” AirPlay volume-control profile +some users may prefer.

The -vsync and -async options also allow an optional positive (or negative) audio-delay adjustment in milliseconds for fine-tuning : -vsync 20.5 delays audio relative to video by @@ -895,6 +911,14 @@ UxPlay startups. As long as this file is not deleted or moved, a client will not have to re-authenticate after an initial authentication. (Add a “pin” entry in the UxPlay startup file if you wish the UxPlay server to use this protocol).

+

-reg [filename]: (since v1.68). This option +maintains a list of previously-pin-registered clients in +$HOME/.uxplay.register (or optionally, in filename). Without +this option, returning clients claiming to be already pin-registered are +trusted and not checked. (This option may be useful if UxPlay is used in +a more public environment, to record client details; the register is +text, one line per client, with client’s public key (base-64 format), +Device ID, and Device name.)

-vsync [x] (In Mirror mode:) this option (now the default) uses timestamps to synchronize audio with video on the server, with an optional audio delay in (decimal) @@ -924,6 +948,24 @@ have any effect.

Audio-only mode, but this option may be useful as a command-line option to switch off a -async option set in a “uxplayrc” configuration file.

+

-db low[:high] Rescales the +AirPlay volume-control attenuation (gain) from -30dB:0dB to +low:0dB or low:high. The lower limit +low must be negative (attenuation); the upper limit +high can be either sign. (GStreamer restricts +volume-augmentation by high so that it cannot exceed +20dB). +The rescaling is “flat”, so that for -db -50:10, a change in Airplay +attenuation by -7dB is translated to a -7 x (60/30) = -14dB attenuation, +and the maximum volume (AirPlay 0dB) is a 10dB augmentation, and Airplay +-30dB would become -50dB. Note that the minimum AirPlay value (-30dB +exactly) is translated to “mute”.

+

-taper Provides a “tapered” Airplay volume-control +profile (matching the one called “dasl-tapering” in shairport-sync): +each time the length of the volume slider (or the number of steps above +mute, where 16 steps = full volume) is reduced by 50%, the perceived +volume is halved (a 10dB attenuation). (This is modified at low volumes, +to use the “untapered” volume if it is louder.)

-s wxh (e.g. -s 1920x1080 , which is the default ) sets the display resolution (width and height, in pixels). (This may be a request made to the AirPlay client, and perhaps will not be the final @@ -1107,13 +1149,27 @@ card, (more specifically, the MAC address used by the first active network interface detected) a random MAC address will be used even if option -m was not specified. (Note that a random MAC address will be different each time UxPlay is started).

-

-key [filename]: By default, the storage of -the Server private key is in the file $HOME/.uxplay.pem. Use the “-key -filename” option to change this location. This option should be -set in the UxPlay startup file as a line “key filename” (no -initial “-”), where filename is a full path. The filename -may be enclosed in quotes ("...."), (and must be, if the -filename has any blank spaces).

+

-key [filename]: This (more secure) option +for generating and storing a persistant public key (needed for the -pin +option) has been replaced by default with a (less secure) method which +generates a key from the server’s “device ID” (MAC address, which can be +changed with the -m option, conveniently as a startup file option). When +the -key option is used, a securely generated keypair is generated and +stored in $HOME/.uxplay.pem, if that file does not exist, +or read from it, if it exists. (Optionally, the key can be stored in +filename.) This method is more secure than the new default +method, (because the Device ID is broadcast in the DNS_SD announcement) +but still leaves the private key exposed to anyone who can access the +pem file. Because the default (but “less-secure”) “Device ID” method is +simpler, and security of client access to uxplay is unlikely to be an +important issue, the -key option is no longer recommended.

+

By default, the storage of the Server private key is in the file +$HOME/.uxplay.pem. Use the “-key filename” option to change +this location. This option should be set in the UxPlay startup file as a +line “key filename” (no initial “-”), where +filename is a full path. The filename may be enclosed in +quotes ("...."), (and must be, if the filename has any +blank spaces).

-dacp [filename]: Export current client DACP-ID and Active-Remote key to file: default is $HOME/.uxplay.dacp. (optionally can be changed to filename). Can be used by remote @@ -1463,6 +1519,14 @@ 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 what version UxPlay claims to be.

Changelog

+

1.68 2023-12-31 New simpler (default) method for generating a +persistent public key from the server MAC address (which can now be set +with the -m option). (The previous method is still available with -key +option). New option -reg to maintain a register of pin-authenticated +clients. Corrected volume-control: now interprets AirPlay volume range +-30dB:0dB as decibel gain attenuation, with new option -db low[:high] +for “flat” rescaling of the dB range. Add -taper option for a “tapered” +AirPlay volume-control profile.

1.67 2023-11-30 Add support for Apple-style one-time pin authentication of clients with option “-pin”: (uses SRP6a authentication protocol and public key persistence). Detection with error message of diff --git a/README.md b/README.md index 7827ae7..e674cbc 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,12 @@ -# UxPlay 1.67: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows). +# UxPlay 1.68: 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](https://github.com/FDH2/UxPlay) (where ALL user issues should be posted, and latest versions can be found). - * _**NEW in v1.67**: support for one-time Apple-style "pin" code client authentication ("client-server - pairing") when the option "-pin" is used._ + * _**NEW in v1.68**: Volume-control improvements, plus improved support for Apple-style one-time "pin" codes introduced in 1.67: a + register of pin-registered clients can now optionally be maintained to check returning clients; a simpler method for generating + a persistent public key (based on the MAC address, which can be set in the UxPlay startup file) is now the default. (The OpenSSL + "pem-file" method introduced in 1.67 is still available with the '-key" option.)_ + ## Highlights: @@ -375,10 +378,14 @@ help with this or other problems. * Since v 1.67, the UxPlay option "`-pin`" allows clients to "pair" with the UxPlay server the first time they connect to it, by entering a 4-digit pin code that is displayed on the UxPlay terminal. (This is optional, but sometimes required if the client is a -corporately-owned and -managed device with MDM Mobile Device Management.) Pairing occurs just once, is curently only -recorded in the client, and persists unless the -UxPlay public key (stored in $HOME/.uxplay.pem, or elsewhere if option `-key ` is used) is moved or deleted, after -which a new key is generated. (Non-Apple clients might not implement the persistence feature.) +corporately-owned and -managed device with MDM Mobile Device Management.) Pairing occurs just once, is currently only +recorded by the client unless the -reg option is used, and persists until the +UxPlay public key is changed. By default (since v1.68) the public key is now generated using the "Device ID", which is either the server's +hardware MAC address, or +can be set with the -m option (most conveniently using the startup option file). (Storage of a more securely-generated +persistent key as an OpenSSL "pem" file is still available with the -key option). For use of uxplay in a more public environment, a +list of previously-registered clients can (since v1.68) be optionally-maintained using the -reg option: without this +option, returning clients claiming to be registered are just trusted and not checked. * By default, UxPlay is locked to its current client until that client drops the connection; since UxPlay-1.58, the option `-nohold` modifies this @@ -408,6 +415,10 @@ if you want to follow the Apple Music lyrics on the client while listening to su delays the video on the client to match audio on the server, so leads to a slight delay before a pause or track-change initiated on the client takes effect on the audio played by the server. +AirPlay volume-control attenuates volume (gain) by up to -30dB: the range -30dB:0dB can be rescaled from _Low_:0, or _Low_:_High_, using the +option `-db` ("-db _Low_ " or "-db _Low_:_High_ "), _Low_ must be negative. Rescaling is linear in decibels. The option ```-taper``` provides a "tapered" AirPlay volume-control +profile some users may prefer. + The -vsync and -async options also allow an optional positive (or negative) audio-delay adjustment in _milliseconds_ for fine-tuning : `-vsync 20.5` delays audio relative to video by 0.0205 secs; a negative value advances it.) @@ -708,6 +719,12 @@ with "`#`" are treated as comments, and ignored. Command line options supersede client will not have to re-authenticate after an initial authentication. _(Add a "pin" entry in the UxPlay startup file if you wish the UxPlay server to use this protocol)._ +**-reg [_filename_]**: (since v1.68). This option maintains a list of previously-pin-registered clients in $HOME/.uxplay.register (or optionally, in _filename_). + Without this option, returning clients claiming to be already pin-registered are trusted and not checked. (This option may be useful if UxPlay is used + in a more public environment, to record client details; the register is text, one line per client, with client's public + key (base-64 format), Device ID, and Device name.) + + **-vsync [x]** (In Mirror mode:) this option (**now the default**) uses timestamps to synchronize audio with video on the server, with an optional audio delay in (decimal) milliseconds (_x_ = "20.5" means 0.0205 seconds delay: positive or negative delays less than a second are allowed.) It is needed on low-power systems such as Raspberry Pi without hardware @@ -728,6 +745,18 @@ using UxPlay as a second monitor for a mac computer, or monitoring a webcam; wit **-async no**. This is the still the default behavior in Audio-only mode, but this option may be useful as a command-line option to switch off a `-async` option set in a "uxplayrc" configuration file. +**-db _low_[:_high_]** Rescales the AirPlay volume-control attenuation (gain) from -30dB:0dB to _low_:0dB or _low_:_high_. The lower limit _low_ + must be negative (attenuation); the upper limit _high_ can be either sign. (GStreamer restricts volume-augmentation by _high_ so that it + cannot exceed +20dB). + The rescaling is "flat", so that for -db -50:10, a change in Airplay attenuation by -7dB is translated to a -7 x (60/30) = -14dB attenuation, + and the maximum volume (AirPlay 0dB) is a 10dB augmentation, and Airplay -30dB would become -50dB. Note that the minimum AirPlay value (-30dB exactly) + is translated to "mute". + +**-taper** Provides a "tapered" Airplay volume-control profile (matching the one called "dasl-tapering" + in [shairport-sync](https://github.com/mikebrady/shairport-sync)): each time the length of the + volume slider (or the number of steps above mute, where 16 steps = full volume) is reduced by 50%, the perceived volume is halved (a 10dB attenuation). + (This is modified at low volumes, to use the "untapered" volume if it is louder.) + **-s wxh** (e.g. -s 1920x1080 , which is the default ) sets the display resolution (width and height, in pixels). (This may be a request made to the AirPlay client, and perhaps will not @@ -891,7 +920,16 @@ which will not work if a firewall is running. a random MAC address will be used even if option **-m** was not specified. (Note that a random MAC address will be different each time UxPlay is started). -**-key [_filename_]**: By default, the storage of the Server private key is in the file $HOME/.uxplay.pem. Use +**-key [_filename_]**: This (more secure) option for generating and storing a persistant public key (needed for + the -pin option) has been replaced by default with a (less secure) method which generates a key from the server's "device ID" + (MAC address, which can be changed with the -m option, conveniently as a startup file option). + When the -key option is used, a securely generated keypair is generated and stored in `$HOME/.uxplay.pem`, if that file does not exist, + or read from it, if it exists. (Optionally, the key can be stored in _filename_.) This method is more secure than the new default method, + (because the Device ID is broadcast in the DNS_SD announcement) but still leaves the private key exposed to anyone who can access the pem file. + Because the default (but "less-secure") "Device ID" method is simpler, and security of client access to uxplay is unlikely to be an important issue, + the -key option is no longer recommended. + +By default, the storage of the Server private key is in the file $HOME/.uxplay.pem. Use the "-key _filename_" option to change this location. This option should be set in the UxPlay startup file as a line "`key filename`" (no initial "-"), where ``filename`` is a full path. The filename may be enclosed in quotes (`"...."`), (and must be, if the filename has any blank spaces). @@ -1156,6 +1194,13 @@ tvOS 12.2.1), so it does not seem to matter what version UxPlay claims to be. # Changelog +1.68 2023-12-31 New simpler (default) method for generating a persistent public key from the server MAC + address (which can now be set with the -m option). (The previous method is still available + with -key option). New option -reg to maintain a register of pin-authenticated clients. Corrected + volume-control: now interprets AirPlay volume range -30dB:0dB as decibel gain attenuation, + with new option -db low[:high] for "flat" rescaling of the dB range. Add -taper option for a "tapered" + AirPlay volume-control profile. + 1.67 2023-11-30 Add support for Apple-style one-time pin authentication of clients with option "-pin": (uses SRP6a authentication protocol and public key persistence). Detection with error message of (currently) unsupported H265 video when requesting high resolution over wired ethernet. diff --git a/README.txt b/README.txt index 1035f1e..c13bf72 100644 --- a/README.txt +++ b/README.txt @@ -1,10 +1,15 @@ -# UxPlay 1.67: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows). +# UxPlay 1.68: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows). ### Now developed at the GitHub site (where ALL user issues should be posted, and latest versions can be found). -- ***NEW in v1.67**: support for one-time Apple-style "pin" code - client authentication ("client-server pairing") when the option - "-pin" is used.* +- ***NEW in v1.68**: Volume-control improvements, plus improved + support for Apple-style one-time "pin" codes introduced in 1.67: a + register of pin-registered clients can now optionally be maintained + to check returning clients; a simpler method for generating a + persistent public key (based on the MAC address, which can be set in + the UxPlay startup file) is now the default. (The OpenSSL "pem-file" + method introduced in 1.67 is still available with the '-key" + option.)* ## Highlights: @@ -451,11 +456,18 @@ below for help with this or other problems. entering a 4-digit pin code that is displayed on the UxPlay terminal. (This is optional, but sometimes required if the client is a corporately-owned and -managed device with MDM Mobile Device - Management.) Pairing occurs just once, is curently only recorded in - the client, and persists unless the UxPlay public key (stored in - \$HOME/.uxplay.pem, or elsewhere if option `-key ` is - used) is moved or deleted, after which a new key is generated. - (Non-Apple clients might not implement the persistence feature.) + Management.) Pairing occurs just once, is currently only recorded by + the client unless the -reg option is used, and persists until the + UxPlay public key is changed. By default (since v1.68) the public + key is now generated using the "Device ID", which is either the + server's hardware MAC address, or can be set with the -m option + (most conveniently using the startup option file). (Storage of a + more securely-generated persistent key as an OpenSSL "pem" file is + still available with the -key option). For use of uxplay in a more + public environment, a list of previously-registered clients can + (since v1.68) be optionally-maintained using the -reg option: + without this option, returning clients claiming to be registered are + just trusted and not checked. - By default, UxPlay is locked to its current client until that client drops the connection; since UxPlay-1.58, the option `-nohold` @@ -498,6 +510,12 @@ helped to prevent this previously when timestamps were not being used.) slight delay before a pause or track-change initiated on the client takes effect on the audio played by the server. +AirPlay volume-control attenuates volume (gain) by up to -30dB: the +range -30dB:0dB can be rescaled from *Low*:0, or *Low*:*High*, using the +option `-db` ("-db *Low*" or "-db *Low*:*High*"), *Low* must be +negative. Rescaling is linear in decibels. The option `-taper` provides +a "tapered" AirPlay volume-control profile some users may prefer. + The -vsync and -async options also allow an optional positive (or negative) audio-delay adjustment in *milliseconds* for fine-tuning : `-vsync 20.5` delays audio relative to video by 0.0205 secs; a negative @@ -895,6 +913,14 @@ re-authenticate after an initial authentication. *(Add a "pin" entry in the UxPlay startup file if you wish the UxPlay server to use this protocol).* +**-reg \[*filename*\]**: (since v1.68). This option maintains a list of +previously-pin-registered clients in \$HOME/.uxplay.register (or +optionally, in *filename*). Without this option, returning clients +claiming to be already pin-registered are trusted and not checked. (This +option may be useful if UxPlay is used in a more public environment, to +record client details; the register is text, one line per client, with +client's public key (base-64 format), Device ID, and Device name.) + **-vsync \[x\]** (In Mirror mode:) this option (**now the default**) uses timestamps to synchronize audio with video on the server, with an optional audio delay in (decimal) milliseconds (*x* = "20.5" means @@ -925,6 +951,24 @@ changing this does not seem to have any effect*. mode, but this option may be useful as a command-line option to switch off a `-async` option set in a "uxplayrc" configuration file. +**-db *low*\[:*high*\]** Rescales the AirPlay volume-control attenuation +(gain) from -30dB:0dB to *low*:0dB or *low*:*high*. The lower limit +*low* must be negative (attenuation); the upper limit *high* can be +either sign. (GStreamer restricts volume-augmentation by *high* so that +it cannot exceed +20dB). The rescaling is "flat", so that for -db +-50:10, a change in Airplay attenuation by -7dB is translated to a -7 x +(60/30) = -14dB attenuation, and the maximum volume (AirPlay 0dB) is a +10dB augmentation, and Airplay -30dB would become -50dB. Note that the +minimum AirPlay value (-30dB exactly) is translated to "mute". + +**-taper** Provides a "tapered" Airplay volume-control profile (matching +the one called "dasl-tapering" in +[shairport-sync](https://github.com/mikebrady/shairport-sync)): each +time the length of the volume slider (or the number of steps above mute, +where 16 steps = full volume) is reduced by 50%, the perceived volume is +halved (a 10dB attenuation). (This is modified at low volumes, to use +the "untapered" volume if it is louder.) + **-s wxh** (e.g. -s 1920x1080 , which is the default ) sets the display resolution (width and height, in pixels). (This may be a request made to the AirPlay client, and perhaps will not be the final resolution you @@ -1125,12 +1169,27 @@ network interface detected) a random MAC address will be used even if option **-m** was not specified. (Note that a random MAC address will be different each time UxPlay is started). -**-key \[*filename*\]**: By default, the storage of the Server private -key is in the file \$HOME/.uxplay.pem. Use the "-key *filename*" option -to change this location. This option should be set in the UxPlay startup -file as a line "`key filename`" (no initial "-"), where `filename` is a -full path. The filename may be enclosed in quotes (`"...."`), (and must -be, if the filename has any blank spaces). +**-key \[*filename*\]**: This (more secure) option for generating and +storing a persistant public key (needed for the -pin option) has been +replaced by default with a (less secure) method which generates a key +from the server's "device ID" (MAC address, which can be changed with +the -m option, conveniently as a startup file option). When the -key +option is used, a securely generated keypair is generated and stored in +`$HOME/.uxplay.pem`, if that file does not exist, or read from it, if it +exists. (Optionally, the key can be stored in *filename*.) This method +is more secure than the new default method, (because the Device ID is +broadcast in the DNS_SD announcement) but still leaves the private key +exposed to anyone who can access the pem file. Because the default (but +"less-secure") "Device ID" method is simpler, and security of client +access to uxplay is unlikely to be an important issue, the -key option +is no longer recommended. + +By default, the storage of the Server private key is in the file +\$HOME/.uxplay.pem. Use the "-key *filename*" option to change this +location. This option should be set in the UxPlay startup file as a line +"`key filename`" (no initial "-"), where `filename` is a full path. The +filename may be enclosed in quotes (`"...."`), (and must be, if the +filename has any blank spaces). **-dacp \[*filename*\]**: Export current client DACP-ID and Active-Remote key to file: default is \$HOME/.uxplay.dacp. (optionally @@ -1497,6 +1556,15 @@ what version UxPlay claims to be. # Changelog +1.68 2023-12-31 New simpler (default) method for generating a persistent +public key from the server MAC address (which can now be set with the -m +option). (The previous method is still available with -key option). New +option -reg to maintain a register of pin-authenticated clients. +Corrected volume-control: now interprets AirPlay volume range -30dB:0dB +as decibel gain attenuation, with new option -db low\[:high\] for "flat" +rescaling of the dB range. Add -taper option for a "tapered" AirPlay +volume-control profile. + 1.67 2023-11-30 Add support for Apple-style one-time pin authentication of clients with option "-pin": (uses SRP6a authentication protocol and public key persistence). Detection with error message of (currently) diff --git a/lib/crypto.c b/lib/crypto.c index c1167d5..906a2fe 100644 --- a/lib/crypto.c +++ b/lib/crypto.c @@ -35,6 +35,8 @@ #include "utils.h" +#define SALT_PK "UxPlay-Persistent-Not-Secure-Public-Key" + struct aes_ctx_s { EVP_CIPHER_CTX *cipher_ctx; uint8_t key[AES_128_BLOCK_SIZE]; @@ -352,7 +354,7 @@ struct ed25519_key_s { EVP_PKEY *pkey; }; -ed25519_key_t *ed25519_key_generate(const char *keyfile, int *result) { +ed25519_key_t *ed25519_key_generate(const char *device_id, const char *keyfile, int *result) { ed25519_key_t *key; EVP_PKEY_CTX *pctx; BIO *bp; @@ -379,7 +381,15 @@ ed25519_key_t *ed25519_key_generate(const char *keyfile, int *result) { new_pk = true; } } else { - new_pk = true; + /* generate (insecure) persistent keypair using device_id */ + unsigned char hash[SHA512_DIGEST_LENGTH]; + char salt[] = SALT_PK; + sha_ctx_t *ctx = sha_init(); + sha_update(ctx, (const unsigned char *) salt, (unsigned int) strlen(salt)); + sha_update(ctx, (const unsigned char *) device_id, (unsigned int) strlen(device_id)); + sha_final(ctx, hash, NULL); + sha_destroy(ctx); + key->pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, (const unsigned char *) hash, ED25519_KEY_SIZE); } if (new_pk) { @@ -546,3 +556,27 @@ void sha_destroy(sha_ctx_t *ctx) { int get_random_bytes(unsigned char *buf, int num) { return RAND_bytes(buf, num); } +#include +void pk_to_base64(const unsigned char *pk, int pk_len, char *pk_base64, int len) { + memset(pk_base64, 0, len); + int len64 = (4 * (pk_len /3)) + (pk_len % 3 ? 4 : 0); + + assert (len > len64); + + BIO *b64 = BIO_new(BIO_f_base64()); + BIO *bio = BIO_new(BIO_s_mem()); + BUF_MEM *bufferPtr; + + + bio = BIO_push(b64, bio); + + BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); + BIO_write(bio, pk, pk_len); + BIO_flush(bio); + + BIO_get_mem_ptr(bio, &bufferPtr); + BIO_set_close(bio, BIO_NOCLOSE); + BIO_free_all(bio); + memcpy(pk_base64,(*bufferPtr).data, len64); +} + diff --git a/lib/crypto.h b/lib/crypto.h index 47ac30a..7bd2d5e 100644 --- a/lib/crypto.h +++ b/lib/crypto.h @@ -67,6 +67,7 @@ x25519_key_t *x25519_key_from_raw(const unsigned char data[X25519_KEY_SIZE]); void x25519_key_get_raw(unsigned char data[X25519_KEY_SIZE], const x25519_key_t *key); void x25519_key_destroy(x25519_key_t *key); int get_random_bytes(unsigned char *buf, int num); +void pk_to_base64(const unsigned char *pk, int pk_len, char *pk_base64, int len); void x25519_derive_secret(unsigned char secret[X25519_KEY_SIZE], const x25519_key_t *ours, const x25519_key_t *theirs); @@ -82,7 +83,7 @@ int gcm_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *pl typedef struct ed25519_key_s ed25519_key_t; -ed25519_key_t *ed25519_key_generate(const char * keyfile, int * result); +ed25519_key_t *ed25519_key_generate(const char *device_id, const char * keyfile, int * result); ed25519_key_t *ed25519_key_from_raw(const unsigned char data[ED25519_KEY_SIZE]); void ed25519_key_get_raw(unsigned char data[ED25519_KEY_SIZE], const ed25519_key_t *key); /* diff --git a/lib/pairing.c b/lib/pairing.c index 3df0b58..e60273c 100644 --- a/lib/pairing.c +++ b/lib/pairing.c @@ -84,7 +84,7 @@ derive_key_internal(pairing_session_t *session, const unsigned char *salt, unsig } pairing_t * -pairing_init_generate(const char * keyfile, int *result) +pairing_init_generate(const char *device_id, const char *keyfile, int *result) { pairing_t *pairing; *result = 0; @@ -93,7 +93,7 @@ pairing_init_generate(const char * keyfile, int *result) return NULL; } - pairing->ed = ed25519_key_generate(keyfile, result); + pairing->ed = ed25519_key_generate(device_id, keyfile, result); return pairing; } @@ -136,7 +136,7 @@ pairing_session_init(pairing_t *pairing) session->status = STATUS_INITIAL; session->srp = NULL; - + session->pair_setup = false; return session; } @@ -447,8 +447,20 @@ srp_confirm_pair_setup(pairing_session_t *session, pairing_t *pairing, return epk_len; } -void access_client_session_data(pairing_session_t *session, char **username, unsigned char **client_pk, bool *setup) { - *username = session->username; - *client_pk = session->client_pk; +void access_client_session_data(pairing_session_t *session, char **username, char **client_pk64, bool *setup) { + int len64 = 4 * (1 + (ED25519_KEY_SIZE / 3)) + 1; setup = &(session->pair_setup); + *username = session->username; + if (setup) { + *client_pk64 = (char *) malloc(len64); + pk_to_base64(session->client_pk, ED25519_KEY_SIZE, *client_pk64, len64); + } else { + *client_pk64 = NULL; + } +} + +void ed25519_pk_to_base64(const unsigned char *pk, char **pk64) { + int len64 = 4 * (1 + (ED25519_KEY_SIZE / 3)) + 1; + *pk64 = (char *) malloc(len64); + pk_to_base64(pk, ED25519_KEY_SIZE, *pk64, len64); } diff --git a/lib/pairing.h b/lib/pairing.h index eec1231..54db62b 100644 --- a/lib/pairing.h +++ b/lib/pairing.h @@ -36,7 +36,7 @@ typedef struct pairing_s pairing_t; typedef struct pairing_session_s pairing_session_t; -pairing_t *pairing_init_generate(const char * keyfile, int *result); +pairing_t *pairing_init_generate(const char *device_id, const char *keyfile, int *result); void pairing_get_public_key(pairing_t *pairing, unsigned char public_key[ED25519_KEY_SIZE]); pairing_session_t *pairing_session_init(pairing_t *pairing); @@ -60,5 +60,6 @@ int srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const uns int len_A, unsigned char *proof, int client_proof_len, int proof_len); int srp_confirm_pair_setup(pairing_session_t *session, pairing_t *pairing, unsigned char *epk, unsigned char *auth_tag); -void access_client_session_data(pairing_session_t *session, char **username, unsigned 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); #endif diff --git a/lib/raop.c b/lib/raop.c index c42f177..ef9b009 100644 --- a/lib/raop.c +++ b/lib/raop.c @@ -411,7 +411,7 @@ conn_destroy(void *ptr) { } raop_t * -raop_init(int max_clients, raop_callbacks_t *callbacks, const char* keyfile) { +raop_init(int max_clients, raop_callbacks_t *callbacks, const char *device_id, const char *keyfile) { raop_t *raop; pairing_t *pairing; httpd_t *httpd; @@ -443,7 +443,7 @@ raop_init(int max_clients, raop_callbacks_t *callbacks, const char* keyfile) { /* create a new public key for pairing */ int new_key; - pairing = pairing_init_generate(keyfile, &new_key); + pairing = pairing_init_generate(device_id, keyfile, &new_key); if (!pairing) { free(raop); return NULL; diff --git a/lib/raop.h b/lib/raop.h index ffbeab3..2888d3d 100644 --- a/lib/raop.h +++ b/lib/raop.h @@ -60,7 +60,7 @@ struct raop_callbacks_s { void (*video_report_size)(void *cls, float *width_source, float *height_source, float *width, float *height); void (*report_client_request) (void *cls, char *deviceid, char *model, char *name, bool *admit); void (*display_pin) (void *cls, char * pin); - void (*register_client) (void *cls, const char *device_id, const char *pk_str); + 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); void (*export_dacp) (void *cls, const char *active_remote, const char *dacp_id); }; @@ -68,7 +68,7 @@ typedef struct raop_callbacks_s raop_callbacks_t; raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const char *remote, int remote_addr_len, unsigned short timing_rport, timing_protocol_t *time_protocol); - RAOP_API raop_t *raop_init(int max_clients, raop_callbacks_t *callbacks, const char* keyfile); +RAOP_API raop_t *raop_init(int max_clients, raop_callbacks_t *callbacks, const char *device_id, const char *keyfile); RAOP_API void raop_set_log_level(raop_t *raop, int level); RAOP_API void raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls); RAOP_API int raop_set_plist(raop_t *raop, const char *plist_item, const int value); diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h index 679cb72..fff801b 100644 --- a/lib/raop_handlers.h +++ b/lib/raop_handlers.h @@ -341,15 +341,6 @@ raop_handler_pairsetup_pin(raop_conn_t *conn, logger_log(conn->raop->logger, LOGGER_ERR, "pair-pin-setup (step 3): client authentication failed\n"); goto authentication_failed; } else { - bool client_pair_setup; - char *client_device_id; - unsigned char *client_pk; - access_client_session_data(conn->session, &client_device_id, &client_pk, &client_pair_setup); - char * client_pk_str = utils_pk_to_string(client_pk, ED25519_KEY_SIZE); - if (conn->raop->callbacks.register_client) { - conn->raop->callbacks.register_client(conn->raop->callbacks.cls, client_device_id, client_pk_str); - } - free (client_pk_str); logger_log(conn->raop->logger, LOGGER_DEBUG, "pair-pin-setup success\n"); } pairing_session_set_setup_status(conn->session); @@ -442,12 +433,15 @@ raop_handler_pairverify(raop_conn_t *conn, logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ED25519 signature"); } if (register_check) { - char *pk_str = utils_pk_to_string((const unsigned char *)(data + 4 + X25519_KEY_SIZE), ED25519_KEY_SIZE); bool registered_client = true; - if (conn->raop->callbacks.check_register) { - registered_client = conn->raop->callbacks.check_register(conn->raop->callbacks.cls, pk_str); + if (conn->raop->callbacks.check_register) { + const unsigned char *pk = data + 4 + X25519_KEY_SIZE; + char *pk64; + ed25519_pk_to_base64(pk, &pk64); + registered_client = conn->raop->callbacks.check_register(conn->raop->callbacks.cls, pk64); + free (pk64); } - free (pk_str); + if (!registered_client) { return; } @@ -584,12 +578,32 @@ raop_handler_setup(raop_conn_t *conn, if (conn->raop->callbacks.report_client_request) { conn->raop->callbacks.report_client_request(conn->raop->callbacks.cls, deviceID, model, name, &admit_client); } - free (deviceID); - deviceID = NULL; - free (model); - model = NULL; - free (name); - name = NULL; + if (admit_client && deviceID && name && conn->raop->callbacks.register_client) { + bool pending_registration; + char *client_device_id; + char *client_pk; /* encoded as null-terminated base64 string*/ + access_client_session_data(conn->session, &client_device_id, &client_pk, &pending_registration); + if (pending_registration) { + if (client_pk && !strcmp(deviceID, client_device_id)) { + conn->raop->callbacks.register_client(conn->raop->callbacks.cls, client_device_id, client_pk, name); + } + } + if (client_pk) { + free (client_pk); + } + } + if (deviceID) { + free (deviceID); + deviceID = NULL; + } + if (model) { + free (model); + model = NULL; + } + if (name) { + free (name); + name = NULL; + } if (admit_client == false) { /* client is not authorized to connect */ plist_free(res_root_node); diff --git a/renderers/audio_renderer.h b/renderers/audio_renderer.h index b405dfe..232da28 100644 --- a/renderers/audio_renderer.h +++ b/renderers/audio_renderer.h @@ -37,7 +37,7 @@ void audio_renderer_init(logger_t *logger, const char* audiosink, const bool *au void audio_renderer_start(unsigned char* compression_type); void audio_renderer_stop(); void audio_renderer_render_buffer(unsigned char* data, int *data_len, unsigned short *seqnum, uint64_t *ntp_time); -void audio_renderer_set_volume(float volume); +void audio_renderer_set_volume(double volume); void audio_renderer_flush(); void audio_renderer_destroy(); diff --git a/renderers/audio_renderer_gstreamer.c b/renderers/audio_renderer_gstreamer.c index 2b131e1..c390e68 100644 --- a/renderers/audio_renderer_gstreamer.c +++ b/renderers/audio_renderer_gstreamer.c @@ -132,7 +132,7 @@ void audio_renderer_init(logger_t *render_logger, const char* audiosink, const b g_object_set(clock, "clock-type", GST_CLOCK_TYPE_REALTIME, NULL); logger = render_logger; - + aac = check_plugin_feature (avdec_aac); alac = check_plugin_feature (avdec_alac); @@ -355,12 +355,10 @@ void audio_renderer_render_buffer(unsigned char* data, int *data_len, unsigned s } } -void audio_renderer_set_volume(float volume) { - float avol; - if (fabs(volume) < 28) { - avol=floorf(((28-fabs(volume))/28)*10)/10; - g_object_set(renderer->volume, "volume", avol, NULL); - } +void audio_renderer_set_volume(double volume) { + volume = (volume > 10.0) ? 10.0 : volume; + volume = (volume < 0.0) ? 0.0 : volume; + g_object_set(renderer->volume, "volume", volume, NULL); } void audio_renderer_flush() { diff --git a/uxplay.1 b/uxplay.1 index 200e052..9a0d526 100644 --- a/uxplay.1 +++ b/uxplay.1 @@ -1,11 +1,11 @@ -.TH UXPLAY "1" "November 2023" "1.67" "User Commands" +.TH UXPLAY "1" "December 2023" "1.68" "User Commands" .SH NAME uxplay \- start AirPlay server .SH SYNOPSIS .B uxplay [\fI\,-n name\/\fR] [\fI\,-s wxh\/\fR] [\fI\,-p \/\fR[\fI\,n\/\fR]] [more \fI OPTIONS \/\fR ...] .SH DESCRIPTION -UxPlay 1.67: An open\-source AirPlay mirroring (+ audio streaming) server: +UxPlay 1.68: An open\-source AirPlay mirroring (+ audio streaming) server: .SH OPTIONS .TP .B @@ -17,6 +17,10 @@ UxPlay 1.67: An open\-source AirPlay mirroring (+ audio streaming) server: .IP without option, pin is random: optionally use fixed pin xxxx. .TP +\fB\-reg\fI [fn]\fR Keep a register in $HOME/.uxplay.register to verify returning +.IP + client pin-registration; (option: use file "fn" for this) +.TP \fB\-vsync\fI[x]\fR Mirror mode: sync audio to video using timestamps (default) .IP \fIx\fR is optional audio delay: millisecs, decimal, can be neg. @@ -27,6 +31,13 @@ UxPlay 1.67: An open\-source AirPlay mirroring (+ audio streaming) server: .TP \fB\-async\fR no Switch off audio/(client)video timestamp synchronization. .TP +\fB\-db\fI l[:h]\fR Set minumum volume attenuation to l dB (decibels, negative); +.IP + optional: set maximum to h dB (+ or -); default -30.0:0.0 +.PP +.TP +\fB\-taper\fR Use a "tapered" AirPlay volume-control profile. +.TP \fB\-s\fR wxh[@r]Set display resolution [refresh_rate] default 1920x1080[@60] .TP \fB\-o\fR Set display "overscanned" mode on (not usually needed) @@ -106,9 +117,13 @@ UxPlay 1.67: An open\-source AirPlay mirroring (+ audio streaming) server: .TP \fB\-r\fR {R|L} Rotate 90 degrees Right (cw) or Left (ccw) .TP -\fB\-m\fR Use random MAC address (use for concurrent UxPlay's) +\fB\-m\fI [mac]\fR Set MAC address (also Device ID); use for concurrent UxPlays +.IP + if mac xx:xx:xx:xx:xx:xx is not given, a random MAC is used. +.PP .TP -\fB\-key\fI fn \fR Store private key in file fn (Default: $HOME/.uxplay.pem) +\fB\-key\fI [fn]\fR Store private key in $HOME/.uxplay.pem (or in file "fn") +.PP .TP \fB\-dacp\fI [fn]\fRExport client DACP information to file $HOME/.uxplay.dacp .IP @@ -140,13 +155,9 @@ UxPlay 1.67: An open\-source AirPlay mirroring (+ audio streaming) server: \fB\-h\fR Displays help information .SH FILES -Private key stored (for persistence) in $HOME/.uxplay.pem (can be changed) -.TP -(transient) storage of client DACP info: $HOME/uxplay.dacp (can be changed) -.TP Options in one of $UXPLAYRC, or ~/.uxplayrc, or ~/.config/uxplayrc .TP -are applied first (command-line options may modify them). uxplayrc format: +are applied first (command-line options may modify them). Format: .TP one option per line,\fI no\fR initial "-"; lines beginning with "#" ignored. .SH diff --git a/uxplay.cpp b/uxplay.cpp index aafaa78..56be690 100644 --- a/uxplay.cpp +++ b/uxplay.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #ifdef _WIN32 /*modifications for Windows compilation */ #include @@ -60,7 +62,7 @@ #include "renderers/video_renderer.h" #include "renderers/audio_renderer.h" -#define VERSION "1.67" +#define VERSION "1.68" #define SECOND_IN_USECS 1000000 #define SECOND_IN_NSECS 1000000000UL @@ -132,6 +134,13 @@ static unsigned short pin = 0; static std::string keyfile = ""; static std::string mac_address = ""; static std::string dacpfile = ""; +static bool registration_list = false; +static std::string pairing_register = ""; +static std::vector registered_keys; +static double db_low = -30.0; +static double db_high = 0.0; +static bool taper_volume = true; + /* logging */ static void log(int level, const char* format, ...) { @@ -561,12 +570,17 @@ static void print_info (char *name) { printf("-n name Specify the network name of the AirPlay server\n"); printf("-nh Do not add \"@hostname\" at the end of AirPlay server name\n"); printf("-pin[xxxx]Use a 4-digit pin code to control client access (default: no)\n"); - printf(" without option, 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(" client pin-registration; (option: use file \"fn\" for this)\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("-vsync no Switch off audio/(server)video timestamp synchronization \n"); printf("-async [x]Audio-Only mode: sync audio to client video (default: no)\n"); printf("-async no Switch off audio/(client)video timestamp synchronization\n"); + printf("-db l[:h] Set minimum volume attenuation to l dB (decibels, negative);\n"); + printf(" optional: set maximum to h dB (+ or -) default: -30.0:0.0 dB\n"); + printf("-taper Use a \"tapered\" AirPlay volume-control profile\n"); printf("-s wxh[@r]Set display resolution [refresh_rate] default 1920x1080[@60]\n"); printf("-o Set display \"overscanned\" mode on (not usually needed)\n"); printf("-fs Full-screen (only works with X11, Wayland and VAAPI)\n"); @@ -607,8 +621,8 @@ static void print_info (char *name) { printf("-f {H|V|I}Horizontal|Vertical flip, or both=Inversion=rotate 180 deg\n"); printf("-r {R|L} Rotate 90 degrees Right (cw) or Left (ccw)\n"); printf("-m [mac] Set MAC address (also Device ID);use for concurrent UxPlays\n"); - printf(" if mac xx:xx:xx:xx:xx:xx is not given, a random mac is used\n"); - printf("-key Store private key in file (default:$HOME/.uxplay.pem)\n"); + printf(" if mac xx:xx:xx:xx:xx:xx is not given, a random MAC is used\n"); + printf("-key [fn] Store private key in $HOME/.uxplay.pem (or in file \"fn\")\n"); printf("-dacp [fn]Export client DACP information to file $HOME/.uxplay.dacp\n"); printf(" (option to use file \"fn\" instead); used for client remote\n"); printf("-vdmp [n] Dump h264 video output to \"fn.h264\"; fn=\"videodump\",change\n"); @@ -1030,7 +1044,7 @@ static void parse_arguments (int argc, char *argv[]) { continue; } } - fprintf(stderr, "invalid argument -al %s: must be a decimal time offset in seconds, range [0,10]\n" + fprintf(stderr, "invalid -al %s: value must be a decimal time offset in seconds, range [0,10]\n" "(like 5 or 4.8, which will be converted to a whole number of microseconds)\n", argv[i]); exit(1); } else if (arg == "-pin") { @@ -1044,6 +1058,17 @@ static void parse_arguments (int argc, char *argv[]) { } pin = n + 10000; } + } else if (arg == "-reg") { + registration_list = true; + pairing_register.erase(); + if (i < argc - 1 && *argv[i+1] != '-') { + pairing_register.append(argv[++i]); + const char * fn = pairing_register.c_str(); + if (!file_has_write_access(fn)) { + fprintf(stderr, "%s cannot be written to:\noption \"-key \" must be to a file with write access\n", fn); + exit(1); + } + } } else if (arg == "-key") { keyfile.erase(); if (i < argc - 1 && *argv[i+1] != '-') { @@ -1054,8 +1079,10 @@ static void parse_arguments (int argc, char *argv[]) { exit(1); } } else { - fprintf(stderr, "option \"-key \" requires a path to a file for persistent key storage\n"); - exit(1); + // fprintf(stderr, "option \"-key \" requires a path to a file for persistent key storage\n"); + // exit(1); + keyfile.erase(); + keyfile.append("0"); } } else if (arg == "-dacp") { dacpfile.erase(); @@ -1070,6 +1097,34 @@ static void parse_arguments (int argc, char *argv[]) { dacpfile.append(get_homedir()); dacpfile.append("/.uxplay.dacp"); } + } else if (arg == "-taper") { + taper_volume = true; + } else if (arg == "-db") { + bool db_bad = true; + double db1, db2; + char *start = NULL; + if ( i < argc -1) { + char *end1, *end2; + start = argv[i+1]; + db1 = strtod(start, &end1); + if (end1 > start && *end1 == ':') { + db2 = strtod(++end1, &end2); + if ( *end2 == '\0' && end2 > end1 && db1 < 0 && db1 < db2) { + db_bad = false; + } + } else if (*end1 =='\0' && end1 > start && db1 < 0 ) { + db_bad = false; + db2 = 0.0; + } + } + if (db_bad) { + fprintf(stderr, "invalid %s %s: db value must be \"low\" or \"low:high\", low < 0 and high > low are decibel gains\n", argv[i], start); + exit(1); + } + i++; + db_low = db1; + db_high = db2; + printf("db range %f:%f\n", db_low, db_high); } else { fprintf(stderr, "unknown option %s, stopping (for help use option \"-h\")\n",argv[i]); exit(1); @@ -1454,9 +1509,48 @@ extern "C" void video_flush (void *cls) { } extern "C" void audio_set_volume (void *cls, float volume) { - if (use_audio) { - audio_renderer_set_volume(volume); + double db, db_flat, frac, gst_volume; + if (!use_audio) { + return; } + /* convert from AirPlay dB volume in range {-30dB : 0dB}, to GStreamer volume */ + if (volume == -144.0f) { /* AirPlay "mute" signal */ + frac = 0.0; + } else if (volume < -30.0f) { + LOGE(" invalid AirPlay volume %f", volume); + frac = 0.0; + } else if (volume > 0.0f) { + LOGE(" invalid AirPlay volume %f", volume); + frac = 1.0; + } else if (volume == -30.0f) { + frac = 0.0; + } else if (volume == 0.0f) { + frac = 1.0; + } else { + frac = (double) ( (30.0f + volume) / 30.0f); + frac = (frac > 1.0) ? 1.0 : frac; + } + + /* frac is length of volume slider as fraction of max length */ + /* also (steps/16) where steps is number of discrete steps above mute (16 = full volume) */ + if (frac == 0.0) { + gst_volume = 0.0; + } else { + /* flat rescaling of decibel range from {-30dB : 0dB} to {db_low : db_high} */ + db_flat = db_low + (db_high-db_low) * frac; + if (taper_volume) { + /* taper the volume reduction by the (rescaled) Airplay {-30:0} range so each reduction of + * the remaining slider length by 50% reduces the perceived volume by 50% (-10dB gain) + * (This is the "dasl-tapering" scheme offered by shairport-sync) */ + db = db_high + 10.0 * (log10(frac) / log10(2.0)); + db = (db > db_flat) ? db : db_flat; + } else { + db = db_flat; + } + /* conversion from (gain) decibels to GStreamer's linear volume scale */ + gst_volume = pow(10.0, 0.05*db); + } + audio_renderer_set_volume(gst_volume); } extern "C" void audio_get_format (void *cls, unsigned char *ct, unsigned short *spf, bool *usingScreen, bool *isMedia, uint64_t *audioFormat) { @@ -1541,15 +1635,36 @@ extern "C" void audio_set_metadata(void *cls, const void *buffer, int buflen) { } } -extern "C" void register_client(void *cls, const char *device_id, const char *client_pk_str) { - /* pair-setup-pin client registration by the server is not implemented here, do nothing*/ - LOGD("registered new client: DeviceID = %s\nPK = \"%s\"", device_id, client_pk_str); +extern "C" void register_client(void *cls, const char *device_id, const char *client_pk, const char *client_name) { + if (!registration_list) { + /* we are not maintaining a list of registered clients */ + return; + } + LOGI("registered new client: %s DeviceID = %s PK = \n%s", client_name, device_id, client_pk); + registered_keys.push_back(client_pk); + if (strlen(pairing_register.c_str())) { + FILE *fp = fopen(pairing_register.c_str(), "a"); + if (fp) { + fprintf(fp, "%s,%s,%s\n", client_pk, device_id, client_name); + fclose(fp); + } + } } -extern "C" bool check_register(void *cls, const char *client_pk_str) { - /* pair-setup-pin client registration by the server is not implemented here, return "true"*/ - LOGD("register check returning client:\nPK = \"%s\"", client_pk_str); - return true; +extern "C" bool check_register(void *cls, const char *client_pk) { + if (!registration_list) { + /* we are not maintaining a list of registered clients */ + return true; + } + LOGD("check returning client's pairing registration"); + std::string pk = client_pk; + if (std::find(registered_keys.rbegin(), registered_keys.rend(), pk) != registered_keys.rend()) { + LOGD("registration found: PK=%s", client_pk); + return true; + } else { + LOGE("returning client's pairing registration not found: PK=%s", client_pk); + return false; + } } extern "C" void log_callback (void *cls, int level, const char *msg) { @@ -1600,7 +1715,7 @@ static int start_raop_server (unsigned short display[5], unsigned short tcp[3], raop_cbs.export_dacp = export_dacp; /* set max number of connections = 2 to protect against capture by new client */ - raop = raop_init(max_connections, &raop_cbs, keyfile.c_str()); + raop = raop_init(max_connections, &raop_cbs, mac_address.c_str(), keyfile.c_str()); if (raop == NULL) { LOGE("Error initializing raop!"); return -1; @@ -1825,14 +1940,46 @@ int main (int argc, char *argv[]) { video_parser.append(BT709_FIX); } - if (require_password && keyfile == "") { + if (require_password && registration_list) { + if (pairing_register == "") { + const char * homedir = get_homedir(); + if (homedir) { + pairing_register = homedir; + pairing_register.append("/.uxplay.register"); + } + } + } + + /* read in public keys that were previously registered with pair-setup-pin */ + if (require_password && registration_list && strlen(pairing_register.c_str())) { + size_t len = 0; + std::string key; + int clients = 0; + std::ifstream file(pairing_register); + if (file.is_open()) { + std::string line; + while (std::getline(file, line)) { + /*32 bytes pk -> base64 -> strlen(pk64) = 44 chars = line[0:43]; add '\0' at line[44] */ + line[44] = '\0'; + std::string pk = line.c_str(); + registered_keys.push_back(key.assign(pk)); + clients ++; + } + if (clients) { + LOGI("Register %s lists %d pin-registered clients", pairing_register.c_str(), clients); + } + file.close(); + } + } + + if (require_password && keyfile == "0") { const char * homedir = get_homedir(); if (homedir) { keyfile.erase(); keyfile = homedir; keyfile.append("/.uxplay.pem"); } else { - LOGE("could not determine $HOME: public key wiil no be saved, and so will not be persistent"); + LOGE("could not determine $HOME: public key wiil not be saved, and so will not be persistent"); } } @@ -1883,7 +2030,6 @@ int main (int argc, char *argv[]) { LOGI("using randomly-generated MAC address %s",mac_address.c_str()); } parse_hw_addr(mac_address, server_hw_addr); - mac_address.clear(); if (coverart_filename.length()) { LOGI("any AirPlay audio cover-art will be written to file %s",coverart_filename.c_str()); diff --git a/uxplay.spec b/uxplay.spec index 3cfda37..cb55a28 100644 --- a/uxplay.spec +++ b/uxplay.spec @@ -1,5 +1,5 @@ Name: uxplay -Version: 1.67 +Version: 1.68.1 Release: 1%{?dist} %global gittag v%{version} @@ -11,7 +11,7 @@ Source0: https://github.com/FDH2/UxPlay/archive/%{gittag}/%{name}-%{versi Packager: UxPlay maintainer -BuildRequires: cmake >= 3.4.1 +BuildRequires: cmake >= 3.5 BuildRequires: make BuildRequires: gcc BuildRequires: gcc-c++ @@ -135,7 +135,7 @@ cd build %{_docdir}/%{name}/llhttp/LICENSE-MIT %changelog -* Wed Nov 29 2023 UxPlay maintainer +* Fri Dec 29 2023 UxPlay maintainer Initial uxplay.spec: tested on Fedora 38, Rocky Linux 9.2, OpenSUSE Leap 15.5, Mageia 9, OpenMandriva ROME, PCLinuxOS -