Bug Summary

File:build-analysis/../src/plugins/airplay/raop_client.c
Warning:line 333, column 2
Value stored to 'res' is never read

Annotated Source Code

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
34typedef enum audio_jack_status {
35 AUDIO_JACK_CONNECTED,
36 AUDIO_JACK_DISCONNECTED
37} audio_jack_status_t;
38
39typedef enum audio_jack_type {
40 AUDIO_JACK_ANALOG,
41 AUDIO_JACK_DIGITAL
42} audio_jack_type_t;
43
44struct 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
84static 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 */
89static void
90write_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
109static gint
110raop_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;
158err:
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
168static void
169raop_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
192static gint
193raop_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
236static gint
237b64_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
258static gint
259raop_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);
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
312static gint
313raop_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
327static gint
328raop_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);
Value stored to 'res' is never read
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 */
345static gint
346raop_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
360static gint
361raop_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
381static gint
382raop_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
397gint
398raop_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
437gint
438raop_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
476gint
477raop_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
569static gint
570raop_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
614gint
615raop_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
626gint
627raop_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
634gint
635raop_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
645gint
646raop_client_get_volume (raop_client_t *rc, gdouble *volume)
647{
648 *volume = rc->volume;
649 return RAOP_EOK0;
650}
651
652gboolean
653raop_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
665gboolean
666raop_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
678gint
679raop_client_rtsp_sock (raop_client_t *rc)
680{
681 return rc->rtsp_conn->fd;
682}
683
684gint
685raop_client_stream_sock (raop_client_t *rc)
686{
687 return rc->stream_fd;
688}
689
690gint
691raop_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
707gint
708raop_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}