plist.h 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. #pragma once
  2. #include <stdint.h>
  3. #include <stddef.h>
  4. #include <stdbool.h>
  5. #include "base64.h"
  6. /**
  7. * Simple plist builder for AirPlay
  8. * Builds XML plist format (easier to debug, iOS accepts it)
  9. */
  10. typedef struct {
  11. char *buffer;
  12. size_t size;
  13. size_t capacity;
  14. } plist_t;
  15. /**
  16. * Initialize a plist builder
  17. */
  18. void plist_init(plist_t *p, char *buffer, size_t capacity);
  19. /**
  20. * Start XML plist document
  21. */
  22. void plist_begin(plist_t *p);
  23. /**
  24. * Start a dictionary
  25. */
  26. void plist_dict_begin(plist_t *p);
  27. /**
  28. * Add string to dictionary
  29. */
  30. void plist_dict_string(plist_t *p, const char *key, const char *value);
  31. /**
  32. * Add integer to dictionary
  33. */
  34. void plist_dict_int(plist_t *p, const char *key, int64_t value);
  35. /**
  36. * Add unsigned integer to dictionary
  37. */
  38. void plist_dict_uint(plist_t *p, const char *key, uint64_t value);
  39. /**
  40. * Add boolean to dictionary
  41. */
  42. void plist_dict_bool(plist_t *p, const char *key, bool value);
  43. /**
  44. * Add base64 data to dictionary
  45. */
  46. void plist_dict_data(plist_t *p, const char *key, const uint8_t *data,
  47. size_t len);
  48. /**
  49. * Add data as hex string (for pk field)
  50. */
  51. void plist_dict_data_hex(plist_t *p, const char *key, const uint8_t *data,
  52. size_t len);
  53. /**
  54. * End dictionary
  55. */
  56. void plist_dict_end(plist_t *p);
  57. /**
  58. * Start an array with key (inside dict)
  59. */
  60. void plist_dict_array_begin(plist_t *p, const char *key);
  61. /**
  62. * Start an array (standalone)
  63. */
  64. void plist_array_begin(plist_t *p);
  65. /**
  66. * End array
  67. */
  68. void plist_array_end(plist_t *p);
  69. /**
  70. * Add integer to array
  71. */
  72. void plist_array_int(plist_t *p, int64_t value);
  73. /**
  74. * End plist document
  75. * @return Total size of plist
  76. */
  77. size_t plist_end(plist_t *p);
  78. // ========================================
  79. // Binary plist parser (for AirPlay 2 SETUP)
  80. // ========================================
  81. /**
  82. * Find a data value by key in a binary plist
  83. * @param plist Binary plist data
  84. * @param plist_len Length of plist
  85. * @param key Key to search for (e.g., "ekey", "eiv")
  86. * @param out_data Output buffer for data value
  87. * @param out_capacity Capacity of output buffer
  88. * @param out_len Actual length of data found
  89. * @return true if found, false otherwise
  90. */
  91. bool bplist_find_data(const uint8_t *plist, size_t plist_len, const char *key,
  92. uint8_t *out_data, size_t out_capacity, size_t *out_len);
  93. /**
  94. * Find a data value by key anywhere in a binary plist
  95. * @param plist Binary plist data
  96. * @param plist_len Length of plist
  97. * @param key Key to search for (e.g., "ekey", "eiv")
  98. * @param out_data Output buffer for data value
  99. * @param out_capacity Capacity of output buffer
  100. * @param out_len Actual length of data found
  101. * @return true if found, false otherwise
  102. */
  103. bool bplist_find_data_deep(const uint8_t *plist, size_t plist_len,
  104. const char *key, uint8_t *out_data,
  105. size_t out_capacity, size_t *out_len);
  106. /**
  107. * Get number of stream entries in a binary plist "streams" array
  108. * @param plist Binary plist data
  109. * @param plist_len Length of plist
  110. * @param count Output stream count
  111. * @return true if streams array found and count read
  112. */
  113. bool bplist_get_streams_count(const uint8_t *plist, size_t plist_len,
  114. size_t *count);
  115. /**
  116. * Get stream details by index from a binary plist "streams" array
  117. * @param plist Binary plist data
  118. * @param plist_len Length of plist
  119. * @param index Stream index
  120. * @param type Stream type (e.g., 96)
  121. * @param ekey_len Length of ekey data if present
  122. * @param eiv_len Length of eiv data if present
  123. * @param shk_len Length of shk data if present
  124. * @return true if stream entry parsed
  125. */
  126. bool bplist_get_stream_info(const uint8_t *plist, size_t plist_len,
  127. size_t index, int64_t *type, size_t *ekey_len,
  128. size_t *eiv_len, size_t *shk_len);
  129. // Stream key debug info
  130. typedef struct {
  131. char key[64];
  132. uint8_t value_type; // See BPLIST_VALUE_*
  133. size_t value_len;
  134. int64_t int_value;
  135. } bplist_kv_info_t;
  136. #define BPLIST_VALUE_UNKNOWN 0
  137. #define BPLIST_VALUE_INT 1
  138. #define BPLIST_VALUE_DATA 2
  139. #define BPLIST_VALUE_STRING 3
  140. #define BPLIST_VALUE_UID 4
  141. #define BPLIST_VALUE_ARRAY 5
  142. #define BPLIST_VALUE_DICT 6
  143. /**
  144. * Get key/value info for a stream dict (debug helper)
  145. * @param plist Binary plist data
  146. * @param plist_len Length of plist
  147. * @param index Stream index
  148. * @param out Output array for key/value info
  149. * @param out_capacity Capacity of output array
  150. * @param out_count Number of items written
  151. * @return true if stream entry parsed
  152. */
  153. bool bplist_get_stream_kv_info(const uint8_t *plist, size_t plist_len,
  154. size_t index, bplist_kv_info_t *out,
  155. size_t out_capacity, size_t *out_count);
  156. /**
  157. * Find stream-specific crypto fields in a binary plist
  158. * @param plist Binary plist data
  159. * @param plist_len Length of plist
  160. * @param stream_type Stream "type" to match (e.g., 96 for audio)
  161. * @param ekey Output buffer for encrypted key (optional)
  162. * @param ekey_capacity Capacity of ekey buffer
  163. * @param ekey_len Length of ekey found
  164. * @param eiv Output buffer for IV (optional)
  165. * @param eiv_capacity Capacity of eiv buffer
  166. * @param eiv_len Length of eiv found
  167. * @param shk Output buffer for shared key (optional)
  168. * @param shk_capacity Capacity of shk buffer
  169. * @param shk_len Length of shk found
  170. * @return true if any crypto field was found for the stream, false otherwise
  171. */
  172. bool bplist_find_stream_crypto(const uint8_t *plist, size_t plist_len,
  173. int64_t stream_type, uint8_t *ekey,
  174. size_t ekey_capacity, size_t *ekey_len,
  175. uint8_t *eiv, size_t eiv_capacity,
  176. size_t *eiv_len, uint8_t *shk,
  177. size_t shk_capacity, size_t *shk_len);
  178. /**
  179. * Find an integer value by key in a binary plist
  180. * @param plist Binary plist data
  181. * @param plist_len Length of plist
  182. * @param key Key to search for
  183. * @param out_value Output for integer value
  184. * @return true if found, false otherwise
  185. */
  186. bool bplist_find_int(const uint8_t *plist, size_t plist_len, const char *key,
  187. int64_t *out_value);
  188. /**
  189. * Find a real/float value by key in a binary plist
  190. * Handles both real and integer values (converting int to double)
  191. * @param plist Binary plist data
  192. * @param plist_len Length of plist
  193. * @param key Key to search for
  194. * @param out_value Output for double value
  195. * @return true if found, false otherwise
  196. */
  197. bool bplist_find_real(const uint8_t *plist, size_t plist_len, const char *key,
  198. double *out_value);
  199. /**
  200. * Find a string value by key in a binary plist
  201. * @param plist Binary plist data
  202. * @param plist_len Length of plist
  203. * @param key Key to search for
  204. * @param out_str Output buffer for string value
  205. * @param out_capacity Capacity of output buffer
  206. * @return true if found, false otherwise
  207. */
  208. bool bplist_find_string(const uint8_t *plist, size_t plist_len, const char *key,
  209. char *out_str, size_t out_capacity);
  210. // ========================================
  211. // Binary plist builders (for AirPlay SETUP responses)
  212. // ========================================
  213. /**
  214. * Build initial SETUP response bplist (no streams array)
  215. * Returns eventPort and timingPort.
  216. * @param out Output buffer
  217. * @param capacity Buffer capacity
  218. * @param event_port Event port to include in response
  219. * @return Length of generated bplist, or 0 on error
  220. */
  221. size_t bplist_build_initial_setup(uint8_t *out, size_t capacity,
  222. uint16_t event_port);
  223. /**
  224. * Build stream SETUP response bplist (with streams array)
  225. * Returns streams[] array with type, dataPort, controlPort, audioBufferSize.
  226. * @param out Output buffer
  227. * @param capacity Buffer capacity
  228. * @param stream_type Stream type (96=realtime UDP, 103=buffered TCP)
  229. * @param data_port Data port to include
  230. * @param control_port Control port to include
  231. * @param audio_buffer_size Audio buffer size to advertise
  232. * @return Length of generated bplist, or 0 on error
  233. */
  234. size_t bplist_build_stream_setup(uint8_t *out, size_t capacity,
  235. int64_t stream_type, uint16_t data_port,
  236. uint16_t control_port,
  237. uint32_t audio_buffer_size);
  238. /**
  239. * Build feedback response bplist
  240. * Returns a streams array with type and sample rate for keepalive.
  241. * This response prevents iPhone from sending TEARDOWN during extended pause.
  242. * @param out Output buffer
  243. * @param capacity Buffer capacity
  244. * @param stream_type Stream type (103 for buffered audio)
  245. * @param sample_rate Sample rate (44100.0)
  246. * @return Length of generated bplist, or 0 on error
  247. */
  248. size_t bplist_build_feedback_response(uint8_t *out, size_t capacity,
  249. int64_t stream_type, double sample_rate);
  250. /**
  251. * Build /info response bplist.
  252. * Mirrors the XML /info response for RTSP clients that require binary plists.
  253. * @param out Output buffer
  254. * @param capacity Buffer capacity
  255. * @param device_id Device MAC string
  256. * @param device_name User-visible AirPlay device name
  257. * @param public_key HAP Ed25519 public key
  258. * @param public_key_len Public key length
  259. * @param features AirPlay feature bitmask
  260. * @param protocol_version AirPlay protocol version value ("vv")
  261. * @return Length of generated bplist, or 0 on error
  262. */
  263. size_t bplist_build_info_response(uint8_t *out, size_t capacity,
  264. const char *device_id,
  265. const char *device_name,
  266. const uint8_t *public_key,
  267. size_t public_key_len, uint64_t features,
  268. int64_t protocol_version);