| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- #pragma once
- #include <stdint.h>
- #include <stddef.h>
- #include <stdbool.h>
- #include "base64.h"
- /**
- * Simple plist builder for AirPlay
- * Builds XML plist format (easier to debug, iOS accepts it)
- */
- typedef struct {
- char *buffer;
- size_t size;
- size_t capacity;
- } plist_t;
- /**
- * Initialize a plist builder
- */
- void plist_init(plist_t *p, char *buffer, size_t capacity);
- /**
- * Start XML plist document
- */
- void plist_begin(plist_t *p);
- /**
- * Start a dictionary
- */
- void plist_dict_begin(plist_t *p);
- /**
- * Add string to dictionary
- */
- void plist_dict_string(plist_t *p, const char *key, const char *value);
- /**
- * Add integer to dictionary
- */
- void plist_dict_int(plist_t *p, const char *key, int64_t value);
- /**
- * Add unsigned integer to dictionary
- */
- void plist_dict_uint(plist_t *p, const char *key, uint64_t value);
- /**
- * Add boolean to dictionary
- */
- void plist_dict_bool(plist_t *p, const char *key, bool value);
- /**
- * Add base64 data to dictionary
- */
- void plist_dict_data(plist_t *p, const char *key, const uint8_t *data,
- size_t len);
- /**
- * Add data as hex string (for pk field)
- */
- void plist_dict_data_hex(plist_t *p, const char *key, const uint8_t *data,
- size_t len);
- /**
- * End dictionary
- */
- void plist_dict_end(plist_t *p);
- /**
- * Start an array with key (inside dict)
- */
- void plist_dict_array_begin(plist_t *p, const char *key);
- /**
- * Start an array (standalone)
- */
- void plist_array_begin(plist_t *p);
- /**
- * End array
- */
- void plist_array_end(plist_t *p);
- /**
- * Add integer to array
- */
- void plist_array_int(plist_t *p, int64_t value);
- /**
- * End plist document
- * @return Total size of plist
- */
- size_t plist_end(plist_t *p);
- // ========================================
- // Binary plist parser (for AirPlay 2 SETUP)
- // ========================================
- /**
- * Find a data value by key in a binary plist
- * @param plist Binary plist data
- * @param plist_len Length of plist
- * @param key Key to search for (e.g., "ekey", "eiv")
- * @param out_data Output buffer for data value
- * @param out_capacity Capacity of output buffer
- * @param out_len Actual length of data found
- * @return true if found, false otherwise
- */
- bool bplist_find_data(const uint8_t *plist, size_t plist_len, const char *key,
- uint8_t *out_data, size_t out_capacity, size_t *out_len);
- /**
- * Find a data value by key anywhere in a binary plist
- * @param plist Binary plist data
- * @param plist_len Length of plist
- * @param key Key to search for (e.g., "ekey", "eiv")
- * @param out_data Output buffer for data value
- * @param out_capacity Capacity of output buffer
- * @param out_len Actual length of data found
- * @return true if found, false otherwise
- */
- bool bplist_find_data_deep(const uint8_t *plist, size_t plist_len,
- const char *key, uint8_t *out_data,
- size_t out_capacity, size_t *out_len);
- /**
- * Get number of stream entries in a binary plist "streams" array
- * @param plist Binary plist data
- * @param plist_len Length of plist
- * @param count Output stream count
- * @return true if streams array found and count read
- */
- bool bplist_get_streams_count(const uint8_t *plist, size_t plist_len,
- size_t *count);
- /**
- * Get stream details by index from a binary plist "streams" array
- * @param plist Binary plist data
- * @param plist_len Length of plist
- * @param index Stream index
- * @param type Stream type (e.g., 96)
- * @param ekey_len Length of ekey data if present
- * @param eiv_len Length of eiv data if present
- * @param shk_len Length of shk data if present
- * @return true if stream entry parsed
- */
- bool bplist_get_stream_info(const uint8_t *plist, size_t plist_len,
- size_t index, int64_t *type, size_t *ekey_len,
- size_t *eiv_len, size_t *shk_len);
- // Stream key debug info
- typedef struct {
- char key[64];
- uint8_t value_type; // See BPLIST_VALUE_*
- size_t value_len;
- int64_t int_value;
- } bplist_kv_info_t;
- #define BPLIST_VALUE_UNKNOWN 0
- #define BPLIST_VALUE_INT 1
- #define BPLIST_VALUE_DATA 2
- #define BPLIST_VALUE_STRING 3
- #define BPLIST_VALUE_UID 4
- #define BPLIST_VALUE_ARRAY 5
- #define BPLIST_VALUE_DICT 6
- /**
- * Get key/value info for a stream dict (debug helper)
- * @param plist Binary plist data
- * @param plist_len Length of plist
- * @param index Stream index
- * @param out Output array for key/value info
- * @param out_capacity Capacity of output array
- * @param out_count Number of items written
- * @return true if stream entry parsed
- */
- bool bplist_get_stream_kv_info(const uint8_t *plist, size_t plist_len,
- size_t index, bplist_kv_info_t *out,
- size_t out_capacity, size_t *out_count);
- /**
- * Find stream-specific crypto fields in a binary plist
- * @param plist Binary plist data
- * @param plist_len Length of plist
- * @param stream_type Stream "type" to match (e.g., 96 for audio)
- * @param ekey Output buffer for encrypted key (optional)
- * @param ekey_capacity Capacity of ekey buffer
- * @param ekey_len Length of ekey found
- * @param eiv Output buffer for IV (optional)
- * @param eiv_capacity Capacity of eiv buffer
- * @param eiv_len Length of eiv found
- * @param shk Output buffer for shared key (optional)
- * @param shk_capacity Capacity of shk buffer
- * @param shk_len Length of shk found
- * @return true if any crypto field was found for the stream, false otherwise
- */
- bool bplist_find_stream_crypto(const uint8_t *plist, size_t plist_len,
- int64_t stream_type, uint8_t *ekey,
- size_t ekey_capacity, size_t *ekey_len,
- uint8_t *eiv, size_t eiv_capacity,
- size_t *eiv_len, uint8_t *shk,
- size_t shk_capacity, size_t *shk_len);
- /**
- * Find an integer value by key in a binary plist
- * @param plist Binary plist data
- * @param plist_len Length of plist
- * @param key Key to search for
- * @param out_value Output for integer value
- * @return true if found, false otherwise
- */
- bool bplist_find_int(const uint8_t *plist, size_t plist_len, const char *key,
- int64_t *out_value);
- /**
- * Find a real/float value by key in a binary plist
- * Handles both real and integer values (converting int to double)
- * @param plist Binary plist data
- * @param plist_len Length of plist
- * @param key Key to search for
- * @param out_value Output for double value
- * @return true if found, false otherwise
- */
- bool bplist_find_real(const uint8_t *plist, size_t plist_len, const char *key,
- double *out_value);
- /**
- * Find a string value by key in a binary plist
- * @param plist Binary plist data
- * @param plist_len Length of plist
- * @param key Key to search for
- * @param out_str Output buffer for string value
- * @param out_capacity Capacity of output buffer
- * @return true if found, false otherwise
- */
- bool bplist_find_string(const uint8_t *plist, size_t plist_len, const char *key,
- char *out_str, size_t out_capacity);
- // ========================================
- // Binary plist builders (for AirPlay SETUP responses)
- // ========================================
- /**
- * Build initial SETUP response bplist (no streams array)
- * Returns eventPort and timingPort.
- * @param out Output buffer
- * @param capacity Buffer capacity
- * @param event_port Event port to include in response
- * @return Length of generated bplist, or 0 on error
- */
- size_t bplist_build_initial_setup(uint8_t *out, size_t capacity,
- uint16_t event_port);
- /**
- * Build stream SETUP response bplist (with streams array)
- * Returns streams[] array with type, dataPort, controlPort, audioBufferSize.
- * @param out Output buffer
- * @param capacity Buffer capacity
- * @param stream_type Stream type (96=realtime UDP, 103=buffered TCP)
- * @param data_port Data port to include
- * @param control_port Control port to include
- * @param audio_buffer_size Audio buffer size to advertise
- * @return Length of generated bplist, or 0 on error
- */
- size_t bplist_build_stream_setup(uint8_t *out, size_t capacity,
- int64_t stream_type, uint16_t data_port,
- uint16_t control_port,
- uint32_t audio_buffer_size);
- /**
- * Build feedback response bplist
- * Returns a streams array with type and sample rate for keepalive.
- * This response prevents iPhone from sending TEARDOWN during extended pause.
- * @param out Output buffer
- * @param capacity Buffer capacity
- * @param stream_type Stream type (103 for buffered audio)
- * @param sample_rate Sample rate (44100.0)
- * @return Length of generated bplist, or 0 on error
- */
- size_t bplist_build_feedback_response(uint8_t *out, size_t capacity,
- int64_t stream_type, double sample_rate);
- /**
- * Build /info response bplist.
- * Mirrors the XML /info response for RTSP clients that require binary plists.
- * @param out Output buffer
- * @param capacity Buffer capacity
- * @param device_id Device MAC string
- * @param device_name User-visible AirPlay device name
- * @param public_key HAP Ed25519 public key
- * @param public_key_len Public key length
- * @param features AirPlay feature bitmask
- * @param protocol_version AirPlay protocol version value ("vv")
- * @return Length of generated bplist, or 0 on error
- */
- size_t bplist_build_info_response(uint8_t *out, size_t capacity,
- const char *device_id,
- const char *device_name,
- const uint8_t *public_key,
- size_t public_key_len, uint64_t features,
- int64_t protocol_version);
|