| File: | build-analysis/../src/plugins/airplay/raop_client.c |
| Warning: | line 279, column 2 Value stored to 'size' is never read |
| 1 | #include <string.h> |
| 2 | #include <unistd.h> |
| 3 | |
| 4 | #include <openssl/rand.h> |
| 5 | #include <openssl/rsa.h> |
| 6 | #include <openssl/bio.h> |
| 7 | #include <openssl/engine.h> |
| 8 | #include <openssl/bn.h> |
| 9 | #include <openssl/aes.h> |
| 10 | |
| 11 | #include "raop_client.h" |
| 12 | #include "net_utils.h" |
| 13 | #include "rtsp.h" |
| 14 | |
| 15 | #define RAOP_RTSP_USER_AGENT"iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)" "iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)" |
| 16 | #define RAOP_DEFAULT_VOLUME-30 -30 |
| 17 | |
| 18 | #define RAOP_IO_UNDEFINED0x00 0x00 |
| 19 | #define RAOP_IO_RTSP_READ0x01 0x01 |
| 20 | #define RAOP_IO_RTSP_WRITE0x02 0x02 |
| 21 | #define RAOP_IO_STREAM_READ0x04 0x04 |
| 22 | #define RAOP_IO_STREAM_WRITE0x08 0x08 |
| 23 | |
| 24 | #define RAOP_RTSP_DISCONNECTED0x00 0x00 |
| 25 | #define RAOP_RTSP_CONNECTING0x01 0x01 |
| 26 | #define RAOP_RTSP_ANNOUNCE0x02 0x02 |
| 27 | #define RAOP_RTSP_SETUP0x04 0x04 |
| 28 | #define RAOP_RTSP_RECORD0x08 0x08 |
| 29 | #define RAOP_RTSP_SETPARAMS0x10 0x10 |
| 30 | #define RAOP_RTSP_FLUSH0x20 0x20 |
| 31 | #define RAOP_RTSP_CONNECTED0x40 0x40 |
| 32 | #define RAOP_RTSP_DONE0x80 0x80 |
| 33 | |
| 34 | typedef enum audio_jack_status { |
| 35 | AUDIO_JACK_CONNECTED, |
| 36 | AUDIO_JACK_DISCONNECTED |
| 37 | } audio_jack_status_t; |
| 38 | |
| 39 | typedef enum audio_jack_type { |
| 40 | AUDIO_JACK_ANALOG, |
| 41 | AUDIO_JACK_DIGITAL |
| 42 | } audio_jack_type_t; |
| 43 | |
| 44 | struct raop_client_struct { |
| 45 | /* endpoint addresses */ |
| 46 | gchar *apex_host; |
| 47 | gushort rtsp_port; |
| 48 | gushort stream_port; |
| 49 | gchar *cli_host; |
| 50 | |
| 51 | /* rtsp handler */ |
| 52 | RTSPConnection *rtsp_conn; |
| 53 | gchar *rtsp_url; |
| 54 | guint32 rtsp_state; |
| 55 | |
| 56 | /* data stream */ |
| 57 | gint stream_fd; |
| 58 | raop_client_stream_cb_t stream_cb; |
| 59 | |
| 60 | /* RTSP and stream channel state */ |
| 61 | gint io_state; |
| 62 | |
| 63 | /* RTSP session stuff */ |
| 64 | gchar session_id[10 + 1]; /* 4-byte base-10 */ |
| 65 | gchar client_id[16 + 1]; /* 8-byte base-16 */ |
| 66 | |
| 67 | /* audio settings */ |
| 68 | audio_jack_status_t jack_status; |
| 69 | audio_jack_type_t jack_type; |
| 70 | gdouble volume; |
| 71 | |
| 72 | /* crypto */ |
| 73 | guint8 aes_iv[16]; |
| 74 | guint8 aes_key_str[16]; |
| 75 | guint8 challenge[16]; |
| 76 | AES_KEY *aes_key; |
| 77 | |
| 78 | /* audio sample */ |
| 79 | guint8 sbuf[RAOP_ALAC_FRAME_SIZE4096 * 2 * 2 + 19]; /* 16-bit stereo */ |
| 80 | guint32 sbuf_size; |
| 81 | guint32 sbuf_offset; |
| 82 | }; |
| 83 | |
| 84 | static gint raop_client_stream_sample (raop_client_t *rc, guint16 *buf, guint32 len); |
| 85 | |
| 86 | /* Helper Functions */ |
| 87 | |
| 88 | /* write upto 8 bits at-a-time into a buffer */ |
| 89 | static void |
| 90 | write_bits (guint8 *buf, guint8 val, gint nbits, guint32 *offset) |
| 91 | { |
| 92 | int bit_offset = *offset % 8; |
| 93 | int byte_offset = *offset / 8; |
| 94 | int left = 8 - bit_offset; |
| 95 | |
| 96 | *offset += nbits; |
| 97 | if (nbits >= left) { |
| 98 | buf[byte_offset] |= (val >> (nbits - left)); |
| 99 | val = (val << left) >> left; |
| 100 | nbits -= left; |
| 101 | left = 8; |
| 102 | byte_offset++; |
| 103 | } |
| 104 | if (nbits && nbits < left) { |
| 105 | buf[byte_offset] |= (val << (left - nbits)); |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | static gint |
| 110 | raop_rsa_encrypt (guchar *text, gint len, guchar *res) |
| 111 | { |
| 112 | RSA *rsa; |
| 113 | size_t size; |
| 114 | static const guchar mod[] = { |
| 115 | 0xe7,0xd7,0x44,0xf2,0xa2,0xe2,0x78,0x8b,0x6c,0x1f,0x55,0xa0, |
| 116 | 0x8e,0xb7,0x5,0x44,0xa8,0xfa,0x79,0x45,0xaa,0x8b,0xe6,0xc6, |
| 117 | 0x2c,0xe5,0xf5,0x1c,0xbd,0xd4,0xdc,0x68,0x42,0xfe,0x3d,0x10, |
| 118 | 0x83,0xdd,0x2e,0xde,0xc1,0xbf,0xd4,0x25,0x2d,0xc0,0x2e,0x6f, |
| 119 | 0x39,0x8b,0xdf,0xe,0x61,0x48,0xea,0x84,0x85,0x5e,0x2e,0x44, |
| 120 | 0x2d,0xa6,0xd6,0x26,0x64,0xf6,0x74,0xa1,0xf3,0x4,0x92,0x9a, |
| 121 | 0xde,0x4f,0x68,0x93,0xef,0x2d,0xf6,0xe7,0x11,0xa8,0xc7,0x7a, |
| 122 | 0xd,0x91,0xc9,0xd9,0x80,0x82,0x2e,0x50,0xd1,0x29,0x22,0xaf, |
| 123 | 0xea,0x40,0xea,0x9f,0xe,0x14,0xc0,0xf7,0x69,0x38,0xc5,0xf3, |
| 124 | 0x88,0x2f,0xc0,0x32,0x3d,0xd9,0xfe,0x55,0x15,0x5f,0x51,0xbb, |
| 125 | 0x59,0x21,0xc2,0x1,0x62,0x9f,0xd7,0x33,0x52,0xd5,0xe2,0xef, |
| 126 | 0xaa,0xbf,0x9b,0xa0,0x48,0xd7,0xb8,0x13,0xa2,0xb6,0x76,0x7f, |
| 127 | 0x6c,0x3c,0xcf,0x1e,0xb4,0xce,0x67,0x3d,0x3,0x7b,0xd,0x2e, |
| 128 | 0xa3,0xc,0x5f,0xff,0xeb,0x6,0xf8,0xd0,0x8a,0xdd,0xe4,0x9, |
| 129 | 0x57,0x1a,0x9c,0x68,0x9f,0xef,0x10,0x72,0x88,0x55,0xdd,0x8c, |
| 130 | 0xfb,0x9a,0x8b,0xef,0x5c,0x89,0x43,0xef,0x3b,0x5f,0xaa,0x15, |
| 131 | 0xdd,0xe6,0x98,0xbe,0xdd,0xf3,0x59,0x96,0x3,0xeb,0x3e,0x6f, |
| 132 | 0x61,0x37,0x2b,0xb6,0x28,0xf6,0x55,0x9f,0x59,0x9a,0x78,0xbf, |
| 133 | 0x50,0x6,0x87,0xaa,0x7f,0x49,0x76,0xc0,0x56,0x2d,0x41,0x29, |
| 134 | 0x56,0xf8,0x98,0x9e,0x18,0xa6,0x35,0x5b,0xd8,0x15,0x97,0x82, |
| 135 | 0x5e,0xf,0xc8,0x75,0x34,0x3e,0xc7,0x82,0x11,0x76,0x25,0xcd |
| 136 | ,0xbf,0x98,0x44,0x7b}; |
| 137 | static const guchar exp[] = {0x01, 0x00, 0x01}; |
| 138 | BIGNUM *n, *e; |
| 139 | |
| 140 | rsa = RSA_new (); |
| 141 | n = BN_bin2bn (mod, 256, NULL((void*)0)); |
| 142 | e = BN_bin2bn (exp, 3, NULL((void*)0)); |
| 143 | if (!rsa || !n || !e) |
| 144 | goto err; |
| 145 | |
| 146 | #if OPENSSL_VERSION_NUMBER0x1000106fL < 0x10100000 |
| 147 | rsa->n = n; |
| 148 | rsa->e = e; |
| 149 | #else |
| 150 | if (!RSA_set0_key(rsa, n, e, NULL((void*)0))) |
| 151 | goto err; |
| 152 | #endif |
| 153 | |
| 154 | size = RSA_public_encrypt (len, text, res, rsa, RSA_PKCS1_OAEP_PADDING4); |
| 155 | |
| 156 | RSA_free (rsa); |
| 157 | return size; |
| 158 | err: |
| 159 | if (rsa) |
| 160 | RSA_free(rsa); |
| 161 | if (n) |
| 162 | BN_free(n); |
| 163 | if (e) |
| 164 | BN_free(e); |
| 165 | return 0; |
| 166 | } |
| 167 | |
| 168 | static void |
| 169 | raop_send_sample (raop_client_t *rc) |
| 170 | { |
| 171 | gint nwritten; |
| 172 | gint nleft = rc->sbuf_size - rc->sbuf_offset; |
| 173 | |
| 174 | if (nleft == 0) { |
| 175 | guchar buf[RAOP_ALAC_FRAME_SIZE4096 * 2 * 2]; |
| 176 | gint ret; |
| 177 | |
| 178 | ret = rc->stream_cb.func (rc->stream_cb.data, buf, sizeof (buf)); |
| 179 | if (ret > 0) |
| 180 | raop_client_stream_sample (rc, (guint16 *)buf, ret); |
| 181 | |
| 182 | nleft = rc->sbuf_size - rc->sbuf_offset; |
| 183 | } |
| 184 | /* XXX: check for error */ |
| 185 | nwritten = tcp_write (rc->stream_fd, (char *) rc->sbuf + rc->sbuf_offset, |
| 186 | nleft); |
| 187 | rc->sbuf_offset += nwritten; |
| 188 | } |
| 189 | |
| 190 | /* RTSP glue */ |
| 191 | |
| 192 | static gint |
| 193 | raop_rtsp_get_reply (raop_client_t *rc) |
| 194 | { |
| 195 | RTSPMessage response = { 0 }; |
| 196 | RTSPResult res; |
| 197 | gchar *ajstatus; |
| 198 | gchar **params; |
| 199 | |
| 200 | res = rtsp_connection_receive (rc->rtsp_conn, &response); |
| 201 | if (res != RTSP_OK) |
| 202 | return RAOP_EFAIL-1; |
| 203 | |
| 204 | res = rtsp_message_get_header (&response, RTSP_HDR_AUDIO_JACK_STATUS, |
| 205 | &ajstatus); |
| 206 | if (res == RTSP_OK) { |
| 207 | params = g_strsplit (ajstatus, "; ", -1); |
| 208 | if (!g_ascii_strncasecmp (params[0], "connected", strlen ("connected"))) { |
| 209 | rc->jack_status = AUDIO_JACK_CONNECTED; |
| 210 | } else { |
| 211 | rc->jack_status = AUDIO_JACK_DISCONNECTED; |
| 212 | } |
| 213 | if (!g_ascii_strncasecmp (params[1], "type=analog", strlen ("type=analog"))) { |
| 214 | rc->jack_type = AUDIO_JACK_ANALOG; |
| 215 | } else { |
| 216 | rc->jack_type = AUDIO_JACK_DIGITAL; |
| 217 | } |
| 218 | g_strfreev (params); |
| 219 | } |
| 220 | |
| 221 | if (rc->rtsp_state == RAOP_RTSP_SETUP0x04) { |
| 222 | gchar *transport; |
| 223 | gchar *port_str; |
| 224 | res = rtsp_message_get_header (&response, RTSP_HDR_TRANSPORT, |
| 225 | &transport); |
| 226 | if (res != RTSP_OK) |
| 227 | return RAOP_EFAIL-1; |
| 228 | |
| 229 | port_str = g_strrstr (transport, "server_port="); |
| 230 | rc->stream_port = strtol (port_str + strlen ("server_port="), NULL((void*)0), 10); |
| 231 | } |
| 232 | |
| 233 | return RAOP_EOK0; |
| 234 | } |
| 235 | |
| 236 | static gint |
| 237 | b64_encode_alloc (const guchar *data, int size, char **out) |
| 238 | { |
| 239 | BIO *mb,*b64b,*bio; |
| 240 | char *p; |
| 241 | |
| 242 | mb = BIO_new (BIO_s_mem ()); |
| 243 | b64b = BIO_new (BIO_f_base64 ()); |
| 244 | BIO_set_flags (b64b, BIO_FLAGS_BASE64_NO_NL0x100); |
| 245 | bio = BIO_push (b64b, mb); |
| 246 | |
| 247 | BIO_write (bio,data,size); |
| 248 | |
| 249 | (void) BIO_flush (bio)(int)BIO_ctrl(bio,11,0,((void*)0)); |
| 250 | size = BIO_ctrl (mb, BIO_CTRL_INFO3, 0, (char *)&p); |
| 251 | *out = g_malloc (size+1); |
| 252 | memcpy (*out, p, size); |
| 253 | (*out)[size] = 0; |
| 254 | BIO_free_all (bio); |
| 255 | return size; |
| 256 | } |
| 257 | |
| 258 | static gint |
| 259 | raop_rtsp_announce (raop_client_t *rc) |
| 260 | { |
| 261 | RTSPMessage request = { 0 }; |
| 262 | RTSPResult res; |
| 263 | guchar enc_aes_key[512]; |
| 264 | gchar *key; |
| 265 | gchar *iv; |
| 266 | gint size; |
| 267 | gchar *sdp_buf; |
| 268 | gchar *ac; |
| 269 | gint ret = RAOP_EOK0; |
| 270 | |
| 271 | size = raop_rsa_encrypt (rc->aes_key_str, 16, enc_aes_key); |
| 272 | if (size == 0) |
| 273 | return RAOP_EFAIL-1; |
| 274 | |
| 275 | size = b64_encode_alloc (enc_aes_key, size, &key); |
| 276 | g_strdelimit (key, "=", '\0'); |
| 277 | size = b64_encode_alloc (rc->aes_iv, 16, &iv); |
| 278 | g_strdelimit (iv, "=", '\0'); |
| 279 | size = b64_encode_alloc (rc->challenge, 16, &ac); |
Value stored to 'size' is never read | |
| 280 | g_strdelimit (ac, "=", '\0'); |
| 281 | |
| 282 | res = rtsp_message_init_request (RTSP_ANNOUNCE, rc->rtsp_url, &request); |
| 283 | rtsp_message_add_header (&request, RTSP_HDR_USER_AGENT, RAOP_RTSP_USER_AGENT"iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)"); |
| 284 | rtsp_message_add_header (&request, RTSP_HDR_CLIENT_INSTANCE, rc->client_id); |
| 285 | rtsp_message_add_header (&request, RTSP_HDR_APPLE_CHALLENGE, ac); |
| 286 | rtsp_message_add_header (&request, RTSP_HDR_CONTENT_TYPE, "application/sdp"); |
| 287 | |
| 288 | sdp_buf = g_strdup_printf ("v=0\r\n" |
| 289 | "o=iTunes %s 0 IN IP4 %s\r\n" |
| 290 | "s=iTunes\r\n" |
| 291 | "c=IN IP4 %s\r\n" |
| 292 | "t=0 0\r\n" |
| 293 | "m=audio 0 RTP/AVP 96\r\n" |
| 294 | "a=rtpmap:96 AppleLossless\r\n" |
| 295 | "a=fmtp:96 4096 0 16 40 10 14 2 255 0 0 44100\r\n" |
| 296 | "a=rsaaeskey:%s\r\n" |
| 297 | "a=aesiv:%s\r\n", |
| 298 | rc->session_id, rc->cli_host, |
| 299 | rc->apex_host, key, iv); |
| 300 | rtsp_message_set_body (&request, (guint8 *) sdp_buf, strlen (sdp_buf)); |
| 301 | res = rtsp_connection_send (rc->rtsp_conn, &request); |
| 302 | if (res != RTSP_OK) |
| 303 | ret = RAOP_EFAIL-1; |
| 304 | |
| 305 | g_free (key); |
| 306 | g_free (iv); |
| 307 | g_free (ac); |
| 308 | g_free (sdp_buf); |
| 309 | return ret; |
| 310 | } |
| 311 | |
| 312 | static gint |
| 313 | raop_rtsp_setup (raop_client_t *rc) |
| 314 | { |
| 315 | RTSPMessage request = { 0 }; |
| 316 | RTSPResult res; |
| 317 | |
| 318 | res = rtsp_message_init_request (RTSP_SETUP, rc->rtsp_url, &request); |
| 319 | rtsp_message_add_header (&request, RTSP_HDR_CLIENT_INSTANCE, rc->client_id); |
| 320 | rtsp_message_add_header (&request, RTSP_HDR_USER_AGENT, RAOP_RTSP_USER_AGENT"iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)"); |
| 321 | rtsp_message_add_header (&request, RTSP_HDR_TRANSPORT, "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");/* ;control_port=0;timing_port=0"; */ |
| 322 | |
| 323 | res = rtsp_connection_send (rc->rtsp_conn, &request); |
| 324 | return (res == RTSP_OK) ? RAOP_EOK0 : RAOP_EFAIL-1; |
| 325 | } |
| 326 | |
| 327 | static gint |
| 328 | raop_rtsp_record (raop_client_t *rc) |
| 329 | { |
| 330 | RTSPMessage request = { 0 }; |
| 331 | RTSPResult res; |
| 332 | |
| 333 | res = rtsp_message_init_request (RTSP_RECORD, rc->rtsp_url, &request); |
| 334 | rtsp_message_add_header (&request, RTSP_HDR_CLIENT_INSTANCE, rc->client_id); |
| 335 | rtsp_message_add_header (&request, RTSP_HDR_USER_AGENT, RAOP_RTSP_USER_AGENT"iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)"); |
| 336 | rtsp_message_add_header (&request, RTSP_HDR_RANGE, "npt=0-"); |
| 337 | rtsp_message_add_header (&request, RTSP_HDR_RTP_INFO, "seq=0;rtptime=0"); |
| 338 | res = rtsp_connection_send (rc->rtsp_conn, &request); |
| 339 | return (res == RTSP_OK) ? RAOP_EOK0 : RAOP_EFAIL-1; |
| 340 | } |
| 341 | |
| 342 | /* |
| 343 | * XXX: take seq and rtptime as args |
| 344 | */ |
| 345 | static gint |
| 346 | raop_rtsp_flush (raop_client_t *rc) |
| 347 | { |
| 348 | RTSPMessage request = { 0 }; |
| 349 | RTSPResult res; |
| 350 | |
| 351 | res = rtsp_message_init_request (RTSP_FLUSH, rc->rtsp_url, &request); |
| 352 | rtsp_message_add_header (&request, RTSP_HDR_CLIENT_INSTANCE, rc->client_id); |
| 353 | rtsp_message_add_header (&request, RTSP_HDR_USER_AGENT, RAOP_RTSP_USER_AGENT"iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)"); |
| 354 | rtsp_message_add_header (&request, RTSP_HDR_RANGE, "npt=0-"); |
| 355 | rtsp_message_add_header (&request, RTSP_HDR_RTP_INFO, "seq=0;rtptime=0"); |
| 356 | res = rtsp_connection_send (rc->rtsp_conn, &request); |
| 357 | return (res == RTSP_OK) ? RAOP_EOK0 : RAOP_EFAIL-1; |
| 358 | } |
| 359 | |
| 360 | static gint |
| 361 | raop_rtsp_set_params (raop_client_t *rc) |
| 362 | { |
| 363 | RTSPMessage request = { 0 }; |
| 364 | RTSPResult res; |
| 365 | gchar *volume; |
| 366 | |
| 367 | res = rtsp_message_init_request (RTSP_SET_PARAMETER, rc->rtsp_url, &request); |
| 368 | rtsp_message_add_header (&request, RTSP_HDR_CLIENT_INSTANCE, rc->client_id); |
| 369 | rtsp_message_add_header (&request, RTSP_HDR_USER_AGENT, RAOP_RTSP_USER_AGENT"iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)"); |
| 370 | |
| 371 | rtsp_message_add_header (&request, RTSP_HDR_CONTENT_TYPE, "text/parameters"); |
| 372 | volume = g_strdup_printf ("volume: %f\r\n", rc->volume); |
| 373 | rtsp_message_set_body (&request, (guint8 *) volume, strlen (volume)); |
| 374 | res = rtsp_connection_send (rc->rtsp_conn, &request); |
| 375 | |
| 376 | g_free (volume); |
| 377 | |
| 378 | return (res == RTSP_OK) ? RAOP_EOK0 : RAOP_EFAIL-1; |
| 379 | } |
| 380 | |
| 381 | static gint |
| 382 | raop_rtsp_teardown (raop_client_t *rc) |
| 383 | { |
| 384 | RTSPMessage request = { 0 }; |
| 385 | RTSPResult res; |
| 386 | |
| 387 | res = rtsp_message_init_request (RTSP_TEARDOWN, rc->rtsp_url, &request); |
| 388 | rtsp_message_add_header (&request, RTSP_HDR_CLIENT_INSTANCE, rc->client_id); |
| 389 | rtsp_message_add_header (&request, RTSP_HDR_USER_AGENT, RAOP_RTSP_USER_AGENT"iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)"); |
| 390 | res = rtsp_connection_send (rc->rtsp_conn, &request); |
| 391 | return (res == RTSP_OK) ? RAOP_EOK0 : RAOP_EFAIL-1; |
| 392 | } |
| 393 | |
| 394 | |
| 395 | /* Public API */ |
| 396 | |
| 397 | gint |
| 398 | raop_client_init (raop_client_t **client) |
| 399 | { |
| 400 | raop_client_t *rc; |
| 401 | guchar rand_buf[8 + 16]; |
| 402 | int ret; |
| 403 | |
| 404 | *client = (raop_client_t *) g_malloc (sizeof (raop_client_t)); |
| 405 | if (!*client) |
| 406 | return RAOP_ENOMEM-6; |
| 407 | rc = *client; |
| 408 | |
| 409 | /* XXX: use a better seed */ |
| 410 | RAND_seed (rc, sizeof (raop_client_t)); |
| 411 | memset ((void *) rc, 0, sizeof (raop_client_t)); |
| 412 | |
| 413 | rc->stream_fd = -1; |
| 414 | rc->io_state = RAOP_IO_UNDEFINED0x00; |
| 415 | rc->jack_status = AUDIO_JACK_DISCONNECTED; |
| 416 | rc->jack_type = AUDIO_JACK_ANALOG; |
| 417 | rc->volume = RAOP_DEFAULT_VOLUME-30; |
| 418 | |
| 419 | /* setup client_id, aes_key */ |
| 420 | ret = RAND_bytes (rand_buf, sizeof (rand_buf)); |
| 421 | if (ret <= 0) { |
| 422 | return RAOP_EFAIL-1; |
| 423 | } |
| 424 | g_snprintf (rc->client_id, 17, "%08X%08X", *((guint *) rand_buf), |
| 425 | *((guint *) (rand_buf + 4))); |
| 426 | |
| 427 | ret = RAND_bytes (rc->aes_key_str, sizeof (rc->aes_key_str)); |
| 428 | if (ret <= 0) { |
| 429 | return RAOP_EFAIL-1; |
| 430 | } |
| 431 | rc->aes_key = (AES_KEY *) g_malloc (sizeof (AES_KEY)); |
| 432 | AES_set_encrypt_key (rc->aes_key_str, 128, rc->aes_key); |
| 433 | |
| 434 | return RAOP_EOK0; |
| 435 | } |
| 436 | |
| 437 | gint |
| 438 | raop_client_connect (raop_client_t *rc, const gchar *host, gushort port) |
| 439 | { |
| 440 | guchar rand_buf[4]; |
| 441 | int ret; |
| 442 | int rtsp_fd; |
| 443 | |
| 444 | rc->apex_host = g_strdup (host); |
| 445 | rc->rtsp_port = port; |
| 446 | rc->sbuf_size = 0; |
| 447 | rc->sbuf_offset = 0; |
| 448 | |
| 449 | RAND_bytes (rand_buf, sizeof (rand_buf)); |
| 450 | g_snprintf (rc->session_id, 11, "%u", *((guint *) rand_buf)); |
| 451 | RAND_bytes (rc->aes_iv, sizeof (rc->aes_iv)); |
| 452 | RAND_bytes (rc->challenge, sizeof (rc->challenge)); |
| 453 | |
| 454 | rtsp_fd = tcp_open (); |
| 455 | if (rtsp_fd == -1) |
| 456 | return RAOP_ESYS-4; |
| 457 | ret = set_sock_nonblock (rtsp_fd); |
| 458 | if (ret == -1) |
| 459 | return RAOP_ESYS-4; |
| 460 | ret = tcp_connect (rtsp_fd, rc->apex_host, rc->rtsp_port); |
| 461 | if (ret == -1 && errno(*__errno_location ()) != EINPROGRESS115) |
| 462 | return RAOP_ESYS-4; |
| 463 | |
| 464 | /* XXX: do this after successful connect? */ |
| 465 | rc->cli_host = g_strdup (get_local_addr (rtsp_fd)); |
| 466 | rc->rtsp_url = g_strdup_printf ("rtsp://%s/%s", rc->cli_host, |
| 467 | rc->session_id); |
| 468 | rtsp_connection_create (rtsp_fd, &rc->rtsp_conn); |
| 469 | |
| 470 | rc->rtsp_state = RAOP_RTSP_CONNECTING0x01; |
| 471 | rc->io_state |= RAOP_IO_RTSP_WRITE0x02; |
| 472 | return RAOP_EOK0; |
| 473 | } |
| 474 | |
| 475 | |
| 476 | gint |
| 477 | raop_client_handle_io (raop_client_t *rc, int fd, GIOCondition cond) |
| 478 | { |
| 479 | gint rtsp_fd = rc->rtsp_conn->fd; |
| 480 | gint ret = RAOP_EOK0; |
| 481 | |
| 482 | if (fd < 0) |
| 483 | return RAOP_EINVAL-5; |
| 484 | |
| 485 | if (cond == G_IO_OUT ) { |
| 486 | /* send RTSP requests */ |
| 487 | if (fd == rtsp_fd) { |
| 488 | /* if pending answers return */ |
| 489 | if (rc->io_state & RAOP_IO_RTSP_READ0x01) |
| 490 | return RAOP_EPROTO-2; |
| 491 | /* XXX: options, challenge/response */ |
| 492 | if (rc->rtsp_state & RAOP_RTSP_CONNECTING0x01) { |
| 493 | ret = raop_rtsp_announce (rc); |
| 494 | if (ret != RAOP_EOK0) |
| 495 | return ret; |
| 496 | rc->rtsp_state = RAOP_RTSP_ANNOUNCE0x02; |
| 497 | } else if (rc->rtsp_state & RAOP_RTSP_ANNOUNCE0x02) { |
| 498 | ret = raop_rtsp_setup (rc); |
| 499 | if (ret != RAOP_EOK0) |
| 500 | return ret; |
| 501 | rc->rtsp_state = RAOP_RTSP_SETUP0x04; |
| 502 | } else if (rc->rtsp_state & RAOP_RTSP_SETUP0x04) { |
| 503 | ret = raop_rtsp_record (rc); |
| 504 | if (ret != RAOP_EOK0) |
| 505 | return ret; |
| 506 | rc->rtsp_state = RAOP_RTSP_RECORD0x08; |
| 507 | } else if (rc->rtsp_state & RAOP_RTSP_RECORD0x08) { |
| 508 | ret = raop_rtsp_set_params (rc); |
| 509 | if (ret != RAOP_EOK0) |
| 510 | return ret; |
| 511 | rc->rtsp_state = RAOP_RTSP_DONE0x80; |
| 512 | } else if (rc->rtsp_state & RAOP_RTSP_SETPARAMS0x10) { |
| 513 | ret = raop_rtsp_set_params (rc); |
| 514 | if (ret != RAOP_EOK0) |
| 515 | return ret; |
| 516 | rc->rtsp_state ^= RAOP_RTSP_SETPARAMS0x10; |
| 517 | } else if (rc->rtsp_state & RAOP_RTSP_FLUSH0x20) { |
| 518 | ret = raop_rtsp_flush (rc); |
| 519 | if (ret != RAOP_EOK0) |
| 520 | return ret; |
| 521 | rc->rtsp_state ^= RAOP_RTSP_FLUSH0x20; |
| 522 | } |
| 523 | rc->io_state ^= RAOP_IO_RTSP_WRITE0x02; |
| 524 | rc->io_state |= RAOP_IO_RTSP_READ0x01; |
| 525 | } else if (fd == rc->stream_fd) { /* stream data */ |
| 526 | raop_send_sample (rc); |
| 527 | } |
| 528 | } else if (cond == G_IO_IN) { |
| 529 | /* get RTSP replies */ |
| 530 | if (fd == rtsp_fd) { |
| 531 | /* shouldn't happen */ |
| 532 | if (rc->io_state & RAOP_IO_RTSP_WRITE0x02) |
| 533 | return RAOP_EPROTO-2; |
| 534 | ret = raop_rtsp_get_reply (rc); |
| 535 | if (ret != RAOP_EOK0) |
| 536 | return ret; |
| 537 | rc->io_state ^= RAOP_IO_RTSP_READ0x01; |
| 538 | if (rc->rtsp_state == RAOP_RTSP_DONE0x80) { |
| 539 | rc->stream_fd = tcp_open (); |
| 540 | if (rc->stream_fd == -1) |
| 541 | return RAOP_ESYS-4; |
| 542 | ret = set_sock_nonblock (rc->stream_fd); |
| 543 | if (ret == -1) |
| 544 | return RAOP_ESYS-4; |
| 545 | ret = tcp_connect (rc->stream_fd, rc->apex_host, |
| 546 | rc->stream_port); |
| 547 | if (ret == -1 && errno(*__errno_location ()) != EINPROGRESS115) |
| 548 | return RAOP_ESYS-4; |
| 549 | rc->io_state |= RAOP_IO_STREAM_WRITE0x08; |
| 550 | rc->io_state |= RAOP_IO_STREAM_READ0x04; |
| 551 | rc->rtsp_state = RAOP_RTSP_CONNECTED0x40; |
| 552 | } else if (rc->rtsp_state != RAOP_RTSP_CONNECTED0x40) { |
| 553 | rc->io_state |= RAOP_IO_RTSP_WRITE0x02; |
| 554 | } |
| 555 | } else if (fd == rc->stream_fd) { |
| 556 | char buf[56]; |
| 557 | /* read data sent by ApEx we don't know what |
| 558 | * it is, just read it for now, and doesn't |
| 559 | * even care about returnval */ |
| 560 | read (rc->stream_fd, buf, 56); |
| 561 | } |
| 562 | } else if (cond == G_IO_ERR) { |
| 563 | /* XXX */ |
| 564 | } |
| 565 | |
| 566 | return RAOP_EOK0; |
| 567 | } |
| 568 | |
| 569 | static gint |
| 570 | raop_client_stream_sample (raop_client_t *rc, guint16 *buf, guint32 len) |
| 571 | { |
| 572 | guint8 hdr[] = {0x24, 0x00, 0x00, 0x00, |
| 573 | 0xF0, 0xFF, 0x00, 0x00, |
| 574 | 0x00, 0x00, 0x00, 0x00, |
| 575 | 0x00, 0x00, 0x00, 0x00}; |
| 576 | int i; |
| 577 | short cnt; |
| 578 | guint8 *pbuf; |
| 579 | guint8 iv[16]; |
| 580 | guint32 offset = 0; |
| 581 | |
| 582 | cnt = len + 3 + 12; |
| 583 | cnt = GUINT16_TO_BE (cnt)((((guint16) ( (guint16) ((guint16) (cnt) >> 8) | (guint16 ) ((guint16) (cnt) << 8))))); |
| 584 | memcpy (hdr + 2, (void *) &cnt, sizeof (cnt)); |
| 585 | |
| 586 | memset (rc->sbuf, 0, sizeof (rc->sbuf)); |
| 587 | memcpy (rc->sbuf, hdr, sizeof (hdr)); |
| 588 | pbuf = rc->sbuf + sizeof (hdr); |
| 589 | |
| 590 | /* ALAC frame header */ |
| 591 | write_bits (pbuf, 1, 3, &offset); /* # of channels */ |
| 592 | write_bits (pbuf, 0, 4, &offset); /* output waiting? */ |
| 593 | write_bits (pbuf, 0, 4, &offset); /* unknown */ |
| 594 | write_bits (pbuf, 0, 8, &offset); /* unknown */ |
| 595 | write_bits (pbuf, 0, 1, &offset); /* sample size follows the header */ |
| 596 | write_bits (pbuf, 0, 2, &offset); /* unknown */ |
| 597 | write_bits (pbuf, 1, 1, &offset); /* uncompressed */ |
| 598 | |
| 599 | for (i = 0; i < len/2; i++) { |
| 600 | write_bits (pbuf, (buf[i]) >> 8, 8, &offset); |
| 601 | write_bits (pbuf, (buf[i]) & 0xff, 8, &offset); |
| 602 | } |
| 603 | memcpy (iv, rc->aes_iv, sizeof (iv)); |
| 604 | AES_cbc_encrypt (pbuf, pbuf, |
| 605 | (len + 3) / 16 * 16, |
| 606 | rc->aes_key, iv, TRUE(!(0))); |
| 607 | |
| 608 | rc->sbuf_size = len + 3 + sizeof (hdr); |
| 609 | rc->sbuf_offset = 0; |
| 610 | |
| 611 | return RAOP_EOK0; |
| 612 | } |
| 613 | |
| 614 | gint |
| 615 | raop_client_flush (raop_client_t *rc) |
| 616 | { |
| 617 | if (rc->rtsp_state & RAOP_RTSP_CONNECTED0x40) { |
| 618 | memset (rc->sbuf, 0, sizeof (rc->sbuf)); |
| 619 | rc->rtsp_state |= RAOP_RTSP_FLUSH0x20; |
| 620 | rc->io_state |= RAOP_IO_RTSP_WRITE0x02; |
| 621 | } |
| 622 | |
| 623 | return RAOP_EOK0; |
| 624 | } |
| 625 | |
| 626 | gint |
| 627 | raop_client_set_stream_cb (raop_client_t *rc, raop_client_stream_cb_func_t func, void *data) |
| 628 | { |
| 629 | rc->stream_cb.func = func; |
| 630 | rc->stream_cb.data = data; |
| 631 | return RAOP_EOK0; |
| 632 | } |
| 633 | |
| 634 | gint |
| 635 | raop_client_set_volume (raop_client_t *rc, gdouble volume) |
| 636 | { |
| 637 | rc->volume = volume; |
| 638 | if (rc->rtsp_state & RAOP_RTSP_CONNECTED0x40) { |
| 639 | rc->rtsp_state |= RAOP_RTSP_SETPARAMS0x10; |
| 640 | rc->io_state |= RAOP_IO_RTSP_WRITE0x02; |
| 641 | } |
| 642 | return RAOP_EOK0; |
| 643 | } |
| 644 | |
| 645 | gint |
| 646 | raop_client_get_volume (raop_client_t *rc, gdouble *volume) |
| 647 | { |
| 648 | *volume = rc->volume; |
| 649 | return RAOP_EOK0; |
| 650 | } |
| 651 | |
| 652 | gboolean |
| 653 | raop_client_can_read (raop_client_t *rc, gint fd) |
| 654 | { |
| 655 | gint rtsp_fd = rc->rtsp_conn->fd; |
| 656 | if (fd == rtsp_fd) { |
| 657 | return rc->io_state & RAOP_IO_RTSP_READ0x01; |
| 658 | } else if (fd == rc->stream_fd) { |
| 659 | return rc->io_state & RAOP_IO_STREAM_READ0x04; |
| 660 | } else { |
| 661 | return FALSE(0); |
| 662 | } |
| 663 | } |
| 664 | |
| 665 | gboolean |
| 666 | raop_client_can_write (raop_client_t *rc, gint fd) |
| 667 | { |
| 668 | gint rtsp_fd = rc->rtsp_conn->fd; |
| 669 | if (fd == rtsp_fd) { |
| 670 | return rc->io_state & RAOP_IO_RTSP_WRITE0x02; |
| 671 | } else if (fd == rc->stream_fd) { |
| 672 | return rc->io_state & RAOP_IO_STREAM_WRITE0x08; |
| 673 | } else { |
| 674 | return FALSE(0); |
| 675 | } |
| 676 | } |
| 677 | |
| 678 | gint |
| 679 | raop_client_rtsp_sock (raop_client_t *rc) |
| 680 | { |
| 681 | return rc->rtsp_conn->fd; |
| 682 | } |
| 683 | |
| 684 | gint |
| 685 | raop_client_stream_sock (raop_client_t *rc) |
| 686 | { |
| 687 | return rc->stream_fd; |
| 688 | } |
| 689 | |
| 690 | gint |
| 691 | raop_client_disconnect (raop_client_t *rc) |
| 692 | { |
| 693 | if (!rc) |
| 694 | return RAOP_EINVAL-5; |
| 695 | |
| 696 | raop_rtsp_teardown (rc); |
| 697 | close (rc->rtsp_conn->fd); |
| 698 | close (rc->stream_fd); |
| 699 | rc->rtsp_conn->fd = rc->stream_fd = -1; |
| 700 | rtsp_connection_free (rc->rtsp_conn); |
| 701 | rc->io_state = RAOP_IO_UNDEFINED0x00; |
| 702 | rc->rtsp_state = RAOP_RTSP_DISCONNECTED0x00; |
| 703 | g_free (rc->rtsp_url); |
| 704 | return RAOP_EOK0; |
| 705 | } |
| 706 | |
| 707 | gint |
| 708 | raop_client_destroy (raop_client_t *rc) |
| 709 | { |
| 710 | if (!rc) |
| 711 | return RAOP_EINVAL-5; |
| 712 | |
| 713 | g_free (rc->aes_key); |
| 714 | g_free (rc->apex_host); |
| 715 | g_free (rc->cli_host); |
| 716 | g_free (rc); |
| 717 | return RAOP_EOK0; |
| 718 | } |