File: | build-analysis/../src/plugins/airplay/raop_client.c |
Warning: | line 275, 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); |
Value stored to 'size' is never read | |
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 | |
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 | } |