]> git.sesse.net Git - vlc/blob - modules/stream_out/raop.c
aout: add wait parameter to aout_DecFlush()
[vlc] / modules / stream_out / raop.c
1 /*****************************************************************************
2  * raop.c: Remote Audio Output Protocol streaming support
3  *****************************************************************************
4  * Copyright (C) 2008 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Author: Michael Hanselmann
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <assert.h>
32
33 #include <gcrypt.h>
34
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_sout.h>
38 #include <vlc_block.h>
39 #include <vlc_network.h>
40 #include <vlc_strings.h>
41 #include <vlc_charset.h>
42 #include <vlc_fs.h>
43 #include <vlc_gcrypt.h>
44 #include <vlc_es.h>
45 #include <vlc_http.h>
46 #include <vlc_memory.h>
47
48 #define RAOP_PORT 5000
49 #define RAOP_USER_AGENT "VLC " VERSION
50
51
52 static const char ps_raop_rsa_pubkey[] =
53     "\xe7\xd7\x44\xf2\xa2\xe2\x78\x8b\x6c\x1f\x55\xa0\x8e\xb7\x05\x44"
54     "\xa8\xfa\x79\x45\xaa\x8b\xe6\xc6\x2c\xe5\xf5\x1c\xbd\xd4\xdc\x68"
55     "\x42\xfe\x3d\x10\x83\xdd\x2e\xde\xc1\xbf\xd4\x25\x2d\xc0\x2e\x6f"
56     "\x39\x8b\xdf\x0e\x61\x48\xea\x84\x85\x5e\x2e\x44\x2d\xa6\xd6\x26"
57     "\x64\xf6\x74\xa1\xf3\x04\x92\x9a\xde\x4f\x68\x93\xef\x2d\xf6\xe7"
58     "\x11\xa8\xc7\x7a\x0d\x91\xc9\xd9\x80\x82\x2e\x50\xd1\x29\x22\xaf"
59     "\xea\x40\xea\x9f\x0e\x14\xc0\xf7\x69\x38\xc5\xf3\x88\x2f\xc0\x32"
60     "\x3d\xd9\xfe\x55\x15\x5f\x51\xbb\x59\x21\xc2\x01\x62\x9f\xd7\x33"
61     "\x52\xd5\xe2\xef\xaa\xbf\x9b\xa0\x48\xd7\xb8\x13\xa2\xb6\x76\x7f"
62     "\x6c\x3c\xcf\x1e\xb4\xce\x67\x3d\x03\x7b\x0d\x2e\xa3\x0c\x5f\xff"
63     "\xeb\x06\xf8\xd0\x8a\xdd\xe4\x09\x57\x1a\x9c\x68\x9f\xef\x10\x72"
64     "\x88\x55\xdd\x8c\xfb\x9a\x8b\xef\x5c\x89\x43\xef\x3b\x5f\xaa\x15"
65     "\xdd\xe6\x98\xbe\xdd\xf3\x59\x96\x03\xeb\x3e\x6f\x61\x37\x2b\xb6"
66     "\x28\xf6\x55\x9f\x59\x9a\x78\xbf\x50\x06\x87\xaa\x7f\x49\x76\xc0"
67     "\x56\x2d\x41\x29\x56\xf8\x98\x9e\x18\xa6\x35\x5b\xd8\x15\x97\x82"
68     "\x5e\x0f\xc8\x75\x34\x3e\xc7\x82\x11\x76\x25\xcd\xbf\x98\x44\x7b";
69
70 static const char ps_raop_rsa_exp[] = "\x01\x00\x01";
71
72 static const char psz_delim_space[] = " ";
73 static const char psz_delim_colon[] = ":";
74 static const char psz_delim_equal[] = "=";
75 static const char psz_delim_semicolon[] = ";";
76
77
78 /*****************************************************************************
79  * Prototypes
80  *****************************************************************************/
81 static int Open( vlc_object_t * );
82 static void Close( vlc_object_t * );
83
84 static sout_stream_id_sys_t *Add( sout_stream_t *, const es_format_t * );
85 static void Del( sout_stream_t *, sout_stream_id_sys_t * );
86 static int Send( sout_stream_t *, sout_stream_id_sys_t *, block_t* );
87
88 static int VolumeCallback( vlc_object_t *p_this, char const *psz_cmd,
89                            vlc_value_t oldval, vlc_value_t newval,
90                            void *p_data );
91
92 typedef enum
93 {
94     JACK_TYPE_NONE = 0,
95     JACK_TYPE_ANALOG,
96     JACK_TYPE_DIGITAL,
97 } jack_type_t;
98
99 struct sout_stream_sys_t
100 {
101     /* Input parameters */
102     char *psz_host;
103     char *psz_password;
104     int i_volume;
105
106     /* Plugin status */
107     sout_stream_id_sys_t *p_audio_stream;
108     bool b_alac_warning;
109     bool b_volume_callback;
110
111     /* Connection state */
112     int i_control_fd;
113     int i_stream_fd;
114
115     uint8_t ps_aes_key[16];
116     uint8_t ps_aes_iv[16];
117     gcry_cipher_hd_t aes_ctx;
118
119     char *psz_url;
120     char *psz_client_instance;
121     char *psz_session;
122     char *psz_last_status_line;
123
124     int i_cseq;
125     int i_server_port;
126     int i_audio_latency;
127     int i_jack_type;
128
129     http_auth_t auth;
130
131     /* Send buffer */
132     size_t i_sendbuf_len;
133     uint8_t *p_sendbuf;
134 };
135
136 struct sout_stream_id_sys_t
137 {
138     es_format_t fmt;
139 };
140
141
142 /*****************************************************************************
143  * Module descriptor
144  *****************************************************************************/
145 #define SOUT_CFG_PREFIX "sout-raop-"
146
147 #define HOST_TEXT N_("Host")
148 #define HOST_LONGTEXT N_("Hostname or IP address of target device")
149
150 #define VOLUME_TEXT N_("Volume")
151 #define VOLUME_LONGTEXT N_("Output volume for analog output: 0 for silence, " \
152                            "1..255 from almost silent to very loud.")
153
154 #define PASSWORD_TEXT N_("Password")
155 #define PASSWORD_LONGTEXT N_("Password for target device.")
156
157 #define PASSWORD_FILE_TEXT N_("Password file")
158 #define PASSWORD_FILE_LONGTEXT N_("Read password for target device from file.")
159
160 vlc_module_begin();
161     set_shortname( N_("RAOP") )
162     set_description( N_("Remote Audio Output Protocol stream output") )
163     set_capability( "sout stream", 0 )
164     add_shortcut( "raop" )
165     set_category( CAT_SOUT )
166     set_subcategory( SUBCAT_SOUT_STREAM )
167     add_string( SOUT_CFG_PREFIX "host", "",
168                 HOST_TEXT, HOST_LONGTEXT, false )
169     add_password( SOUT_CFG_PREFIX "password", NULL,
170                   PASSWORD_TEXT, PASSWORD_LONGTEXT, false )
171     add_loadfile( SOUT_CFG_PREFIX "password-file", NULL,
172               PASSWORD_FILE_TEXT, PASSWORD_FILE_LONGTEXT, false )
173     add_integer_with_range( SOUT_CFG_PREFIX "volume", 100, 0, 255,
174                             VOLUME_TEXT, VOLUME_LONGTEXT, false )
175     set_callbacks( Open, Close )
176 vlc_module_end()
177
178 static const char *const ppsz_sout_options[] = {
179     "host",
180     "password",
181     "password-file",
182     "volume",
183     NULL
184 };
185
186
187 /*****************************************************************************
188  * Utilities:
189  *****************************************************************************/
190 static void FreeSys( vlc_object_t *p_this, sout_stream_sys_t *p_sys )
191 {
192     sout_stream_t *p_stream = (sout_stream_t*)p_this;
193
194     if ( p_sys->i_control_fd >= 0 )
195         net_Close( p_sys->i_control_fd );
196     if ( p_sys->i_stream_fd >= 0 )
197         net_Close( p_sys->i_stream_fd );
198     if ( p_sys->b_volume_callback )
199         var_DelCallback( p_stream, SOUT_CFG_PREFIX "volume",
200                          VolumeCallback, NULL );
201
202     gcry_cipher_close( p_sys->aes_ctx );
203
204     free( p_sys->p_sendbuf );
205     free( p_sys->psz_host );
206     free( p_sys->psz_password );
207     free( p_sys->psz_url );
208     free( p_sys->psz_session );
209     free( p_sys->psz_client_instance );
210     free( p_sys->psz_last_status_line );
211     free( p_sys );
212 }
213
214 static void FreeId( sout_stream_id_sys_t *id )
215 {
216     free( id );
217 }
218
219 static void RemoveBase64Padding( char *str )
220 {
221     char *ps_pos = strchr( str, '=' );
222     if ( ps_pos != NULL )
223         *ps_pos = '\0';
224 }
225
226 static int CheckForGcryptErrorWithLine( sout_stream_t *p_stream,
227                                         gcry_error_t i_gcrypt_err,
228                                         unsigned int i_line )
229 {
230     if ( i_gcrypt_err != GPG_ERR_NO_ERROR )
231     {
232         msg_Err( p_stream, "gcrypt error (line %d): %s", i_line,
233                  gpg_strerror( i_gcrypt_err ) );
234         return 1;
235     }
236
237     return 0;
238 }
239
240 /* Wrapper to pass line number for easier debugging */
241 #define CheckForGcryptError( p_this, i_gcrypt_err ) \
242     CheckForGcryptErrorWithLine( p_this, i_gcrypt_err, __LINE__ )
243
244 /* MGF1 is specified in RFC2437, section 10.2.1. Variables are named after the
245  * specification.
246  */
247 static int MGF1( vlc_object_t *p_this,
248                  unsigned char *mask, size_t l,
249                  const unsigned char *Z, const size_t zLen,
250                  const int Hash )
251 {
252     sout_stream_t *p_stream = (sout_stream_t*)p_this;
253     gcry_error_t i_gcrypt_err;
254     gcry_md_hd_t md_handle = NULL;
255     unsigned int hLen;
256     unsigned char *ps_md;
257     uint32_t counter = 0;
258     uint8_t C[4];
259     size_t i_copylen;
260     int i_err = VLC_SUCCESS;
261
262     assert( mask != NULL );
263     assert( Z != NULL );
264
265     hLen = gcry_md_get_algo_dlen( Hash );
266
267     i_gcrypt_err = gcry_md_open( &md_handle, Hash, 0 );
268     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
269     {
270         i_err = VLC_EGENERIC;
271         goto error;
272     }
273
274     while ( l > 0 )
275     {
276         /* 3. For counter from 0 to \lceil{l / hLen}\rceil-1, do the following:
277          * a. Convert counter to an octet string C of length 4 with the
278          *    primitive I2OSP: C = I2OSP (counter, 4)
279          */
280         C[0] = (counter >> 24) & 0xff;
281         C[1] = (counter >> 16) & 0xff;
282         C[2] = (counter >> 8) & 0xff;
283         C[3] = counter & 0xff;
284         ++counter;
285
286         /* b. Concatenate the hash of the seed Z and C to the octet string T:
287          *    T = T || Hash (Z || C)
288          */
289         gcry_md_reset( md_handle );
290         gcry_md_write( md_handle, Z, zLen );
291         gcry_md_write( md_handle, C, 4 );
292         ps_md = gcry_md_read( md_handle, Hash );
293
294         /* 4. Output the leading l octets of T as the octet string mask. */
295         i_copylen = __MIN( l, hLen );
296         memcpy( mask, ps_md, i_copylen );
297         mask += i_copylen;
298         l -= i_copylen;
299     }
300
301 error:
302     gcry_md_close( md_handle );
303
304     return i_err;
305 }
306
307 /* EME-OAEP-ENCODE is specified in RFC2437, section 9.1.1.1. Variables are
308  * named after the specification.
309  */
310 static int AddOaepPadding( vlc_object_t *p_this,
311                            unsigned char *EM, const size_t emLenWithPrefix,
312                            const unsigned char *M, const size_t mLen,
313                            const unsigned char *P, const size_t pLen )
314 {
315     const int Hash = GCRY_MD_SHA1;
316     const unsigned int hLen = gcry_md_get_algo_dlen( Hash );
317     unsigned char *seed = NULL;
318     unsigned char *DB = NULL;
319     unsigned char *dbMask = NULL;
320     unsigned char *seedMask = NULL;
321     size_t emLen;
322     size_t psLen;
323     size_t i;
324     int i_err = VLC_SUCCESS;
325
326     /* Space for 0x00 prefix in EM. */
327     emLen = emLenWithPrefix - 1;
328
329     /* Step 2:
330      * If ||M|| > emLen-2hLen-1 then output "message too long" and stop.
331      */
332     if ( mLen > (emLen - (2 * hLen) - 1) )
333     {
334         msg_Err( p_this , "Message too long" );
335         goto error;
336     }
337
338     /* Step 3:
339      * Generate an octet string PS consisting of emLen-||M||-2hLen-1 zero
340      * octets. The length of PS may be 0.
341      */
342     psLen = emLen - mLen - (2 * hLen) - 1;
343
344     /*
345      * Step 5:
346      * Concatenate pHash, PS, the message M, and other padding to form a data
347      * block DB as: DB = pHash || PS || 01 || M
348      */
349     DB = calloc( 1, hLen + psLen + 1 + mLen );
350     dbMask = calloc( 1, emLen - hLen );
351     seedMask = calloc( 1, hLen );
352
353     if ( DB == NULL || dbMask == NULL || seedMask == NULL )
354     {
355         i_err = VLC_ENOMEM;
356         goto error;
357     }
358
359     /* Step 4:
360      * Let pHash = Hash(P), an octet string of length hLen.
361      */
362     gcry_md_hash_buffer( Hash, DB, P, pLen );
363
364     /* Step 3:
365      * Generate an octet string PS consisting of emLen-||M||-2hLen-1 zero
366      * octets. The length of PS may be 0.
367      */
368     memset( DB + hLen, 0, psLen );
369
370     /* Step 5:
371      * Concatenate pHash, PS, the message M, and other padding to form a data
372      * block DB as: DB = pHash || PS || 01 || M
373      */
374     DB[hLen + psLen] = 0x01;
375     memcpy( DB + hLen + psLen + 1, M, mLen );
376
377     /* Step 6:
378      * Generate a random octet string seed of length hLen
379      */
380     seed = gcry_random_bytes( hLen, GCRY_STRONG_RANDOM );
381     if ( seed == NULL )
382     {
383         i_err = VLC_ENOMEM;
384         goto error;
385     }
386
387     /* Step 7:
388      * Let dbMask = MGF(seed, emLen-hLen).
389      */
390     i_err = MGF1( p_this, dbMask, emLen - hLen, seed, hLen, Hash );
391     if ( i_err != VLC_SUCCESS )
392         goto error;
393
394     /* Step 8:
395      * Let maskedDB = DB \xor dbMask.
396      */
397     for ( i = 0; i < (emLen - hLen); ++i )
398         DB[i] ^= dbMask[i];
399
400     /* Step 9:
401      * Let seedMask = MGF(maskedDB, hLen).
402      */
403     i_err = MGF1( p_this, seedMask, hLen, DB, emLen - hLen, Hash );
404     if ( i_err != VLC_SUCCESS )
405         goto error;
406
407     /* Step 10:
408      * Let maskedSeed = seed \xor seedMask.
409      */
410     for ( i = 0; i < hLen; ++i )
411         seed[i] ^= seedMask[i];
412
413     /* Step 11:
414      * Let EM = maskedSeed || maskedDB.
415      */
416     assert( (1 + hLen + (hLen + psLen + 1 + mLen)) == emLenWithPrefix );
417     EM[0] = 0x00;
418     memcpy( EM + 1, seed, hLen );
419     memcpy( EM + 1 + hLen, DB, hLen + psLen + 1 + mLen );
420
421     /* Step 12:
422      * Output EM.
423      */
424
425 error:
426     free( DB );
427     free( dbMask );
428     free( seedMask );
429     free( seed );
430
431     return i_err;
432 }
433
434 static int EncryptAesKeyBase64( vlc_object_t *p_this, char **result )
435 {
436     sout_stream_t *p_stream = (sout_stream_t*)p_this;
437     sout_stream_sys_t *p_sys = p_stream->p_sys;
438     gcry_error_t i_gcrypt_err;
439     gcry_sexp_t sexp_rsa_params = NULL;
440     gcry_sexp_t sexp_input = NULL;
441     gcry_sexp_t sexp_encrypted = NULL;
442     gcry_sexp_t sexp_token_a = NULL;
443     gcry_mpi_t mpi_pubkey = NULL;
444     gcry_mpi_t mpi_exp = NULL;
445     gcry_mpi_t mpi_input = NULL;
446     gcry_mpi_t mpi_output = NULL;
447     unsigned char ps_padded_key[256];
448     unsigned char *ps_value;
449     size_t i_value_size;
450     int i_err;
451
452     /* Add RSA-OAES-SHA1 padding */
453     i_err = AddOaepPadding( p_this,
454                             ps_padded_key, sizeof( ps_padded_key ),
455                             p_sys->ps_aes_key, sizeof( p_sys->ps_aes_key ),
456                             NULL, 0 );
457     if ( i_err != VLC_SUCCESS )
458         goto error;
459     i_err = VLC_EGENERIC;
460
461     /* Read public key */
462     i_gcrypt_err = gcry_mpi_scan( &mpi_pubkey, GCRYMPI_FMT_USG,
463                                   ps_raop_rsa_pubkey,
464                                   sizeof( ps_raop_rsa_pubkey ) - 1, NULL );
465     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
466         goto error;
467
468     /* Read exponent */
469     i_gcrypt_err = gcry_mpi_scan( &mpi_exp, GCRYMPI_FMT_USG, ps_raop_rsa_exp,
470                                   sizeof( ps_raop_rsa_exp ) - 1, NULL );
471     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
472         goto error;
473
474     /* If the input data starts with a set bit (0x80), gcrypt thinks it's a
475      * signed integer and complains. Prefixing it with a zero byte (\0)
476      * works, but involves more work. Converting it to an MPI in our code is
477      * cleaner.
478      */
479     i_gcrypt_err = gcry_mpi_scan( &mpi_input, GCRYMPI_FMT_USG,
480                                   ps_padded_key, sizeof( ps_padded_key ),
481                                   NULL);
482     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
483         goto error;
484
485     /* Build S-expression with RSA parameters */
486     i_gcrypt_err = gcry_sexp_build( &sexp_rsa_params, NULL,
487                                     "(public-key(rsa(n %m)(e %m)))",
488                                     mpi_pubkey, mpi_exp );
489     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
490         goto error;
491
492     /* Build S-expression for data */
493     i_gcrypt_err = gcry_sexp_build( &sexp_input, NULL, "(data(value %m))",
494                                     mpi_input );
495     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
496         goto error;
497
498     /* Encrypt data */
499     i_gcrypt_err = gcry_pk_encrypt( &sexp_encrypted, sexp_input,
500                                     sexp_rsa_params );
501     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
502         goto error;
503
504     /* Extract encrypted data */
505     sexp_token_a = gcry_sexp_find_token( sexp_encrypted, "a", 0 );
506     if ( !sexp_token_a )
507     {
508         msg_Err( p_this , "Token 'a' not found in result S-expression" );
509         goto error;
510     }
511
512     mpi_output = gcry_sexp_nth_mpi( sexp_token_a, 1, GCRYMPI_FMT_USG );
513     if ( !mpi_output )
514     {
515         msg_Err( p_this, "Unable to extract MPI from result" );
516         goto error;
517     }
518
519     /* Copy encrypted data into char array */
520     i_gcrypt_err = gcry_mpi_aprint( GCRYMPI_FMT_USG, &ps_value, &i_value_size,
521                                     mpi_output );
522     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
523     {
524         goto error;
525     }
526
527     /* Encode in Base64 */
528     *result = vlc_b64_encode_binary( ps_value, i_value_size );
529     i_err = VLC_SUCCESS;
530
531 error:
532     gcry_sexp_release( sexp_rsa_params );
533     gcry_sexp_release( sexp_input );
534     gcry_sexp_release( sexp_encrypted );
535     gcry_sexp_release( sexp_token_a );
536     gcry_mpi_release( mpi_pubkey );
537     gcry_mpi_release( mpi_exp );
538     gcry_mpi_release( mpi_input );
539     gcry_mpi_release( mpi_output );
540
541     return i_err;
542 }
543
544 static char *ReadPasswordFile( vlc_object_t *p_this, const char *psz_path )
545 {
546     FILE *p_file = NULL;
547     char *psz_password = NULL;
548     char *psz_newline;
549     char ps_buffer[256];
550
551     p_file = vlc_fopen( psz_path, "rt" );
552     if ( p_file == NULL )
553     {
554         msg_Err( p_this, "Unable to open password file '%s': %s", psz_path,
555                  vlc_strerror_c(errno) );
556         goto error;
557     }
558
559     /* Read one line only */
560     if ( fgets( ps_buffer, sizeof( ps_buffer ), p_file ) == NULL )
561     {
562         if ( ferror( p_file ) )
563         {
564             msg_Err( p_this, "Error reading '%s': %s", psz_path,
565                      vlc_strerror_c(errno) );
566             goto error;
567         }
568
569         /* Nothing was read, but there was no error either. Maybe the file is
570          * empty. Not all implementations of fgets(3) write \0 to the output
571          * buffer in this case.
572          */
573         ps_buffer[0] = '\0';
574
575     } else {
576         /* Replace first newline with '\0' */
577         psz_newline = strchr( ps_buffer, '\n' );
578         if ( psz_newline != NULL )
579             *psz_newline = '\0';
580     }
581
582     if ( *ps_buffer == '\0' ) {
583         msg_Err( p_this, "No password could be read from '%s'", psz_path );
584         goto error;
585     }
586
587     psz_password = strdup( ps_buffer );
588
589 error:
590     if ( p_file != NULL )
591         fclose( p_file );
592
593     return psz_password;
594 }
595
596 /* Splits the value of a received header.
597  *
598  * Example: "Transport: RTP/AVP/TCP;unicast;mode=record;server_port=6000"
599  */
600 static int SplitHeader( char **ppsz_next, char **ppsz_name,
601                         char **ppsz_value )
602 {
603     /* Find semicolon (separator between assignments) */
604     *ppsz_name = strsep( ppsz_next, psz_delim_semicolon );
605     if ( *ppsz_name )
606     {
607         /* Skip spaces */
608         *ppsz_name += strspn( *ppsz_name, psz_delim_space );
609
610         /* Get value */
611         *ppsz_value = *ppsz_name;
612         strsep( ppsz_value, psz_delim_equal );
613     }
614     else
615         *ppsz_value = NULL;
616
617     return !!*ppsz_name;
618 }
619
620 static void FreeHeader( void *p_value, void *p_data )
621 {
622     VLC_UNUSED( p_data );
623     free( p_value );
624 }
625
626 static int ReadStatusLine( vlc_object_t *p_this )
627 {
628     sout_stream_t *p_stream = (sout_stream_t*)p_this;
629     sout_stream_sys_t *p_sys = p_stream->p_sys;
630     char *psz_line = NULL;
631     char *psz_token;
632     char *psz_next;
633     int i_result = VLC_EGENERIC;
634
635     p_sys->psz_last_status_line = net_Gets( p_this, p_sys->i_control_fd,
636                                             NULL );
637     if ( !p_sys->psz_last_status_line )
638         goto error;
639
640     /* Create working copy */
641     psz_line = strdup( p_sys->psz_last_status_line );
642     psz_next = psz_line;
643
644     /* Protocol field */
645     psz_token = strsep( &psz_next, psz_delim_space );
646     if ( !psz_token || strncmp( psz_token, "RTSP/1.", 7 ) != 0 )
647     {
648         msg_Err( p_this, "Unknown protocol (%s)",
649                  p_sys->psz_last_status_line );
650         goto error;
651     }
652
653     /* Status field */
654     psz_token = strsep( &psz_next, psz_delim_space );
655     if ( !psz_token )
656     {
657         msg_Err( p_this, "Request failed (%s)",
658                  p_sys->psz_last_status_line );
659         goto error;
660     }
661
662     i_result = atoi( psz_token );
663
664 error:
665     free( psz_line );
666
667     return i_result;
668 }
669
670 static int ReadHeader( vlc_object_t *p_this,
671                        vlc_dictionary_t *p_resp_headers,
672                        int *done )
673 {
674     sout_stream_t *p_stream = (sout_stream_t*)p_this;
675     sout_stream_sys_t *p_sys = p_stream->p_sys;
676     char *psz_original = NULL;
677     char *psz_line = NULL;
678     char *psz_token;
679     char *psz_next;
680     char *psz_name;
681     char *psz_value;
682     int i_err = VLC_SUCCESS;
683
684     psz_line = net_Gets( p_this, p_sys->i_control_fd, NULL );
685     if ( !psz_line )
686     {
687         i_err = VLC_EGENERIC;
688         goto error;
689     }
690
691     /* Empty line for response end */
692     if ( psz_line[0] == '\0' )
693         *done = 1;
694     else
695     {
696         psz_original = strdup( psz_line );
697         psz_next = psz_line;
698
699         psz_token = strsep( &psz_next, psz_delim_colon );
700         if ( !psz_token || psz_next[0] != ' ' )
701         {
702             msg_Err( p_this, "Invalid header format (%s)", psz_original );
703             i_err = VLC_EGENERIC;
704             goto error;
705         }
706
707         psz_name = psz_token;
708         psz_value = psz_next + 1;
709
710         vlc_dictionary_insert( p_resp_headers, psz_name, strdup( psz_value ) );
711     }
712
713 error:
714     free( psz_original );
715     free( psz_line );
716
717     return i_err;
718 }
719
720 static int WriteAuxHeaders( vlc_object_t *p_this,
721                             vlc_dictionary_t *p_req_headers )
722 {
723     sout_stream_t *p_stream = (sout_stream_t*)p_this;
724     sout_stream_sys_t *p_sys = p_stream->p_sys;
725     char **ppsz_keys = NULL;
726     char *psz_key;
727     char *psz_value;
728     int i_err = VLC_SUCCESS;
729     int i_rc;
730     size_t i;
731
732     ppsz_keys = vlc_dictionary_all_keys( p_req_headers );
733     for ( i = 0; ppsz_keys[i]; ++i )
734     {
735         psz_key = ppsz_keys[i];
736         psz_value = vlc_dictionary_value_for_key( p_req_headers, psz_key );
737
738         i_rc = net_Printf( p_this, p_sys->i_control_fd, NULL,
739                            "%s: %s\r\n", psz_key, psz_value );
740         if ( i_rc < 0 )
741         {
742             i_err = VLC_EGENERIC;
743             goto error;
744         }
745     }
746
747 error:
748     for ( i = 0; ppsz_keys[i]; ++i )
749         free( ppsz_keys[i] );
750     free( ppsz_keys );
751
752     return i_err;
753 }
754
755 static int SendRequest( vlc_object_t *p_this, const char *psz_method,
756                         const char *psz_content_type, const char *psz_body,
757                         vlc_dictionary_t *p_req_headers )
758 {
759     sout_stream_t *p_stream = (sout_stream_t*)p_this;
760     sout_stream_sys_t *p_sys = p_stream->p_sys;
761     const unsigned char psz_headers_end[] = "\r\n";
762     size_t i_body_length = 0;
763     int i_err = VLC_SUCCESS;
764     int i_rc;
765
766     i_rc = net_Printf( p_this, p_sys->i_control_fd, NULL,
767                        "%s %s RTSP/1.0\r\n"
768                        "User-Agent: " RAOP_USER_AGENT "\r\n"
769                        "Client-Instance: %s\r\n"
770                        "CSeq: %d\r\n",
771                        psz_method, p_sys->psz_url,
772                        p_sys->psz_client_instance,
773                        ++p_sys->i_cseq );
774     if ( i_rc < 0 )
775     {
776         i_err = VLC_EGENERIC;
777         goto error;
778     }
779
780     if ( psz_content_type )
781     {
782         i_rc = net_Printf( p_this, p_sys->i_control_fd, NULL,
783                            "Content-Type: %s\r\n", psz_content_type );
784         if ( i_rc < 0 )
785         {
786             i_err = VLC_ENOMEM;
787             goto error;
788         }
789     }
790
791     if ( psz_body )
792     {
793         i_body_length = strlen( psz_body );
794
795         i_rc = net_Printf( p_this, p_sys->i_control_fd, NULL,
796                            "Content-Length: %u\r\n",
797                            (unsigned int)i_body_length );
798         if ( i_rc < 0 )
799         {
800             i_err = VLC_ENOMEM;
801             goto error;
802         }
803     }
804
805     i_err = WriteAuxHeaders( p_this, p_req_headers );
806     if ( i_err != VLC_SUCCESS )
807         goto error;
808
809     i_rc = net_Write( p_this, p_sys->i_control_fd, NULL,
810                       psz_headers_end, sizeof( psz_headers_end ) - 1 );
811     if ( i_rc < 0 )
812     {
813         i_err = VLC_ENOMEM;
814         goto error;
815     }
816
817     if ( psz_body )
818         net_Write( p_this, p_sys->i_control_fd, NULL,
819                    psz_body, i_body_length );
820
821 error:
822     return i_err;
823 }
824
825 static int ParseAuthenticateHeader( vlc_object_t *p_this,
826                                     vlc_dictionary_t *p_resp_headers )
827 {
828     sout_stream_t *p_stream = (sout_stream_t*)p_this;
829     sout_stream_sys_t *p_sys = p_stream->p_sys;
830     char *psz_auth;
831     int i_err = VLC_SUCCESS;
832
833     psz_auth = vlc_dictionary_value_for_key( p_resp_headers,
834                                              "WWW-Authenticate" );
835     if ( psz_auth == NULL )
836     {
837         msg_Err( p_this, "HTTP 401 response missing "
838                          "WWW-Authenticate header" );
839         i_err = VLC_EGENERIC;
840         goto error;
841     }
842
843     http_auth_ParseWwwAuthenticateHeader( p_this, &p_sys->auth, psz_auth );
844
845 error:
846     return i_err;
847 }
848
849 static int ExecRequest( vlc_object_t *p_this, const char *psz_method,
850                         const char *psz_content_type, const char *psz_body,
851                         vlc_dictionary_t *p_req_headers,
852                         vlc_dictionary_t *p_resp_headers )
853 {
854     sout_stream_t *p_stream = (sout_stream_t*)p_this;
855     sout_stream_sys_t *p_sys = p_stream->p_sys;
856     char *psz_authorization = NULL;
857     int headers_done;
858     int i_err = VLC_SUCCESS;
859     int i_status;
860     int i_auth_state;
861
862     if ( p_sys->i_control_fd < 0 )
863     {
864         msg_Err( p_this, "Control connection not open" );
865         i_err = VLC_EGENERIC;
866         goto error;
867     }
868
869     i_auth_state = 0;
870     while ( 1 )
871     {
872         /* Send header only when Digest authentication is used */
873         if ( p_sys->psz_password != NULL && p_sys->auth.psz_nonce != NULL )
874         {
875             FREENULL( psz_authorization );
876
877             psz_authorization =
878                 http_auth_FormatAuthorizationHeader( p_this, &p_sys->auth,
879                                                      psz_method,
880                                                      p_sys->psz_url, "",
881                                                      p_sys->psz_password );
882             if ( psz_authorization == NULL )
883             {
884                 i_err = VLC_EGENERIC;
885                 goto error;
886             }
887
888             vlc_dictionary_insert( p_req_headers, "Authorization",
889                                    psz_authorization );
890         }
891
892         /* Send request */
893         i_err = SendRequest( p_this, psz_method, psz_content_type, psz_body,
894                              p_req_headers);
895         if ( i_err != VLC_SUCCESS )
896             goto error;
897
898         /* Read status line */
899         i_status = ReadStatusLine( p_this );
900         if ( i_status < 0 )
901         {
902             i_err = i_status;
903             goto error;
904         }
905
906         vlc_dictionary_clear( p_resp_headers, FreeHeader, NULL );
907
908         /* Read headers */
909         headers_done = 0;
910         while ( !headers_done )
911         {
912             i_err = ReadHeader( p_this, p_resp_headers, &headers_done );
913             if ( i_err != VLC_SUCCESS )
914                 goto error;
915         }
916
917         if ( i_status == 200 )
918             /* Request successful */
919             break;
920         else if ( i_status == 401 )
921         {
922             /* Authorization required */
923             if ( i_auth_state == 1 || p_sys->psz_password == NULL )
924             {
925                 msg_Err( p_this, "Access denied, password invalid" );
926                 i_err = VLC_EGENERIC;
927                 goto error;
928             }
929
930             i_err = ParseAuthenticateHeader( p_this, p_resp_headers );
931             if ( i_err != VLC_SUCCESS )
932                 goto error;
933
934             i_auth_state = 1;
935         }
936         else
937         {
938             msg_Err( p_this, "Request failed (%s), status is %d",
939                      p_sys->psz_last_status_line, i_status );
940             i_err = VLC_EGENERIC;
941             goto error;
942         }
943     }
944
945 error:
946     FREENULL( p_sys->psz_last_status_line );
947     free( psz_authorization );
948
949     return i_err;
950 }
951
952 static int AnnounceSDP( vlc_object_t *p_this, char *psz_local,
953                         uint32_t i_session_id )
954 {
955     sout_stream_t *p_stream = (sout_stream_t*)p_this;
956     sout_stream_sys_t *p_sys = p_stream->p_sys;
957     vlc_dictionary_t req_headers;
958     vlc_dictionary_t resp_headers;
959     unsigned char ps_sac[16];
960     char *psz_sdp = NULL;
961     char *psz_sac_base64 = NULL;
962     char *psz_aes_key_base64 = NULL;
963     char *psz_aes_iv_base64 = NULL;
964     int i_err = VLC_SUCCESS;
965     int i_rc;
966
967     vlc_dictionary_init( &req_headers, 0 );
968     vlc_dictionary_init( &resp_headers, 0 );
969
970     /* Encrypt AES key and encode it in Base64 */
971     i_rc = EncryptAesKeyBase64( p_this, &psz_aes_key_base64 );
972     if ( i_rc != VLC_SUCCESS || psz_aes_key_base64 == NULL )
973     {
974         i_err = VLC_EGENERIC;
975         goto error;
976     }
977     RemoveBase64Padding( psz_aes_key_base64 );
978
979     /* Encode AES IV in Base64 */
980     psz_aes_iv_base64 = vlc_b64_encode_binary( p_sys->ps_aes_iv,
981                                                sizeof( p_sys->ps_aes_iv ) );
982     if ( psz_aes_iv_base64 == NULL )
983     {
984         i_err = VLC_EGENERIC;
985         goto error;
986     }
987     RemoveBase64Padding( psz_aes_iv_base64 );
988
989     /* Random bytes for Apple-Challenge header */
990     gcry_randomize( ps_sac, sizeof( ps_sac ), GCRY_STRONG_RANDOM );
991
992     psz_sac_base64 = vlc_b64_encode_binary( ps_sac, sizeof( ps_sac ) );
993     if ( psz_sac_base64 == NULL )
994     {
995         i_err = VLC_EGENERIC;
996         goto error;
997     }
998     RemoveBase64Padding( psz_sac_base64 );
999
1000     /* Build SDP
1001      * Note: IPv6 addresses also use "IP4". Make sure not to include the
1002      * scope ID.
1003      */
1004     i_rc = asprintf( &psz_sdp,
1005                      "v=0\r\n"
1006                      "o=iTunes %u 0 IN IP4 %s\r\n"
1007                      "s=iTunes\r\n"
1008                      "c=IN IP4 %s\r\n"
1009                      "t=0 0\r\n"
1010                      "m=audio 0 RTP/AVP 96\r\n"
1011                      "a=rtpmap:96 AppleLossless\r\n"
1012                      "a=fmtp:96 4096 0 16 40 10 14 2 255 0 0 44100\r\n"
1013                      "a=rsaaeskey:%s\r\n"
1014                      "a=aesiv:%s\r\n",
1015                      i_session_id, psz_local, p_sys->psz_host,
1016                      psz_aes_key_base64, psz_aes_iv_base64 );
1017
1018     if ( i_rc < 0 )
1019     {
1020         i_err = VLC_ENOMEM;
1021         goto error;
1022     }
1023
1024     /* Build and send request */
1025     vlc_dictionary_insert( &req_headers, "Apple-Challenge", psz_sac_base64 );
1026
1027     i_err = ExecRequest( p_this, "ANNOUNCE", "application/sdp", psz_sdp,
1028                          &req_headers, &resp_headers);
1029     if ( i_err != VLC_SUCCESS )
1030         goto error;
1031
1032 error:
1033     vlc_dictionary_clear( &req_headers, NULL, NULL );
1034     vlc_dictionary_clear( &resp_headers, FreeHeader, NULL );
1035
1036     free( psz_sdp );
1037     free( psz_sac_base64 );
1038     free( psz_aes_key_base64 );
1039     free( psz_aes_iv_base64 );
1040
1041     return i_err;
1042 }
1043
1044 static int SendSetup( vlc_object_t *p_this )
1045 {
1046     sout_stream_t *p_stream = (sout_stream_t*)p_this;
1047     sout_stream_sys_t *p_sys = p_stream->p_sys;
1048     vlc_dictionary_t req_headers;
1049     vlc_dictionary_t resp_headers;
1050     int i_err = VLC_SUCCESS;
1051     char *psz_tmp;
1052     char *psz_next;
1053     char *psz_name;
1054     char *psz_value;
1055
1056     vlc_dictionary_init( &req_headers, 0 );
1057     vlc_dictionary_init( &resp_headers, 0 );
1058
1059     vlc_dictionary_insert( &req_headers, "Transport",
1060                            ((void*)"RTP/AVP/TCP;unicast;interleaved=0-1;"
1061                             "mode=record") );
1062
1063     i_err = ExecRequest( p_this, "SETUP", NULL, NULL,
1064                          &req_headers, &resp_headers );
1065     if ( i_err != VLC_SUCCESS )
1066         goto error;
1067
1068     psz_tmp = vlc_dictionary_value_for_key( &resp_headers, "Session" );
1069     if ( !psz_tmp )
1070     {
1071         msg_Err( p_this, "Missing 'Session' header during setup" );
1072         i_err = VLC_EGENERIC;
1073         goto error;
1074     }
1075
1076     free( p_sys->psz_session );
1077     p_sys->psz_session = strdup( psz_tmp );
1078
1079     /* Get server_port */
1080     psz_next = vlc_dictionary_value_for_key( &resp_headers, "Transport" );
1081     while ( SplitHeader( &psz_next, &psz_name, &psz_value ) )
1082     {
1083         if ( psz_value && strcmp( psz_name, "server_port" ) == 0 )
1084         {
1085             p_sys->i_server_port = atoi( psz_value );
1086             break;
1087         }
1088     }
1089
1090     if ( !p_sys->i_server_port )
1091     {
1092         msg_Err( p_this, "Missing 'server_port' during setup" );
1093         i_err = VLC_EGENERIC;
1094         goto error;
1095     }
1096
1097     /* Get jack type */
1098     psz_next = vlc_dictionary_value_for_key( &resp_headers,
1099                                              "Audio-Jack-Status" );
1100     while ( SplitHeader( &psz_next, &psz_name, &psz_value ) )
1101     {
1102         if ( strcmp( psz_name, "type" ) != 0 )
1103             continue;
1104
1105         if ( strcmp( psz_value, "analog" ) == 0 )
1106             p_sys->i_jack_type = JACK_TYPE_ANALOG;
1107
1108         else if ( strcmp( psz_value, "digital" ) == 0 )
1109             p_sys->i_jack_type = JACK_TYPE_DIGITAL;
1110
1111         break;
1112     }
1113
1114 error:
1115     vlc_dictionary_clear( &req_headers, NULL, NULL );
1116     vlc_dictionary_clear( &resp_headers, FreeHeader, NULL );
1117
1118     return i_err;
1119 }
1120
1121 static int SendRecord( vlc_object_t *p_this )
1122 {
1123     sout_stream_t *p_stream = (sout_stream_t*)p_this;
1124     sout_stream_sys_t *p_sys = p_stream->p_sys;
1125     vlc_dictionary_t req_headers;
1126     vlc_dictionary_t resp_headers;
1127     int i_err = VLC_SUCCESS;
1128     char *psz_value;
1129
1130     vlc_dictionary_init( &req_headers, 0 );
1131     vlc_dictionary_init( &resp_headers, 0 );
1132
1133     vlc_dictionary_insert( &req_headers, "Range", (void *)"npt=0-" );
1134     vlc_dictionary_insert( &req_headers, "RTP-Info",
1135                            (void *)"seq=0;rtptime=0" );
1136     vlc_dictionary_insert( &req_headers, "Session",
1137                            (void *)p_sys->psz_session );
1138
1139     i_err = ExecRequest( p_this, "RECORD", NULL, NULL,
1140                          &req_headers, &resp_headers );
1141     if ( i_err != VLC_SUCCESS )
1142         goto error;
1143
1144     psz_value = vlc_dictionary_value_for_key( &resp_headers, "Audio-Latency" );
1145     if ( psz_value )
1146         p_sys->i_audio_latency = atoi( psz_value );
1147     else
1148         p_sys->i_audio_latency = 0;
1149
1150 error:
1151     vlc_dictionary_clear( &req_headers, NULL, NULL );
1152     vlc_dictionary_clear( &resp_headers, FreeHeader, NULL );
1153
1154     return i_err;
1155 }
1156
1157 static int SendFlush( vlc_object_t *p_this )
1158 {
1159     VLC_UNUSED( p_this );
1160     vlc_dictionary_t resp_headers;
1161     vlc_dictionary_t req_headers;
1162     int i_err = VLC_SUCCESS;
1163
1164     vlc_dictionary_init( &req_headers, 0 );
1165     vlc_dictionary_init( &resp_headers, 0 );
1166
1167     vlc_dictionary_insert( &req_headers, "RTP-Info",
1168                            (void *)"seq=0;rtptime=0" );
1169
1170     i_err = ExecRequest( p_this, "FLUSH", NULL, NULL,
1171                          &req_headers, &resp_headers );
1172     if ( i_err != VLC_SUCCESS )
1173         goto error;
1174
1175 error:
1176     vlc_dictionary_clear( &req_headers, NULL, NULL );
1177     vlc_dictionary_clear( &resp_headers, FreeHeader, NULL );
1178
1179     return i_err;
1180 }
1181
1182 static int SendTeardown( vlc_object_t *p_this )
1183 {
1184     vlc_dictionary_t resp_headers;
1185     vlc_dictionary_t req_headers;
1186     int i_err = VLC_SUCCESS;
1187
1188     vlc_dictionary_init( &req_headers, 0 );
1189     vlc_dictionary_init( &resp_headers, 0 );
1190
1191     i_err = ExecRequest( p_this, "TEARDOWN", NULL, NULL,
1192                          &req_headers, &resp_headers );
1193     if ( i_err != VLC_SUCCESS )
1194         goto error;
1195
1196 error:
1197     vlc_dictionary_clear( &req_headers, NULL, NULL );
1198     vlc_dictionary_clear( &resp_headers, FreeHeader, NULL );
1199
1200     return i_err;
1201 }
1202
1203 static int UpdateVolume( vlc_object_t *p_this )
1204 {
1205     sout_stream_t *p_stream = (sout_stream_t*)p_this;
1206     sout_stream_sys_t *p_sys = p_stream->p_sys;
1207     vlc_dictionary_t req_headers;
1208     vlc_dictionary_t resp_headers;
1209     char *psz_parameters = NULL;
1210     double d_volume;
1211     int i_err = VLC_SUCCESS;
1212     int i_rc;
1213
1214     vlc_dictionary_init( &req_headers, 0 );
1215     vlc_dictionary_init( &resp_headers, 0 );
1216
1217     /* Our volume is 0..255, RAOP is -144..0 (-144 off, -30..0 on) */
1218
1219     /* Limit range */
1220     p_sys->i_volume = VLC_CLIP( p_sys->i_volume, 0, 255 );
1221
1222     if ( p_sys->i_volume == 0 )
1223         d_volume = -144.0;
1224     else
1225         d_volume = -30 + ( ( (double)p_sys->i_volume ) * 30.0 / 255.0 );
1226
1227     /* Format without using locales */
1228     i_rc = us_asprintf( &psz_parameters, "volume: %0.6f\r\n", d_volume );
1229     if ( i_rc < 0 )
1230     {
1231         i_err = VLC_ENOMEM;
1232         goto error;
1233     }
1234
1235     vlc_dictionary_insert( &req_headers, "Session",
1236                            (void *)p_sys->psz_session );
1237
1238     i_err = ExecRequest( p_this, "SET_PARAMETER",
1239                          "text/parameters", psz_parameters,
1240                          &req_headers, &resp_headers );
1241     if ( i_err != VLC_SUCCESS )
1242         goto error;
1243
1244 error:
1245     vlc_dictionary_clear( &req_headers, NULL, NULL );
1246     vlc_dictionary_clear( &resp_headers, FreeHeader, NULL );
1247     free( psz_parameters );
1248
1249     return i_err;
1250 }
1251
1252 static void LogInfo( vlc_object_t *p_this )
1253 {
1254     sout_stream_t *p_stream = (sout_stream_t*)p_this;
1255     sout_stream_sys_t *p_sys = p_stream->p_sys;
1256     const char *psz_jack_name;
1257
1258     msg_Info( p_this, "Audio latency: %d", p_sys->i_audio_latency );
1259
1260     switch ( p_sys->i_jack_type )
1261     {
1262         case JACK_TYPE_ANALOG:
1263             psz_jack_name = "analog";
1264             break;
1265
1266         case JACK_TYPE_DIGITAL:
1267             psz_jack_name = "digital";
1268             break;
1269
1270         case JACK_TYPE_NONE:
1271         default:
1272             psz_jack_name = "none";
1273             break;
1274     }
1275
1276     msg_Info( p_this, "Jack type: %s", psz_jack_name );
1277 }
1278
1279 static void SendAudio( sout_stream_t *p_stream, block_t *p_buffer )
1280 {
1281     sout_stream_sys_t *p_sys = p_stream->p_sys;
1282     gcry_error_t i_gcrypt_err;
1283     block_t *p_next;
1284     size_t i_len;
1285     size_t i_payload_len;
1286     size_t i_realloc_len;
1287     int rc;
1288
1289     const uint8_t header[16] = {
1290         0x24, 0x00, 0x00, 0x00,
1291         0xf0, 0xff, 0x00, 0x00,
1292         0x00, 0x00, 0x00, 0x00,
1293         0x00, 0x00, 0x00, 0x00,
1294     };
1295
1296     while ( p_buffer )
1297     {
1298         i_len = sizeof( header ) + p_buffer->i_buffer;
1299
1300         /* Buffer resize needed? */
1301         if ( i_len > p_sys->i_sendbuf_len || p_sys->p_sendbuf == NULL )
1302         {
1303             /* Grow in blocks of 4K */
1304             i_realloc_len = (1 + (i_len / 4096)) * 4096;
1305
1306             p_sys->p_sendbuf = realloc_or_free( p_sys->p_sendbuf, i_realloc_len );
1307             if ( p_sys->p_sendbuf == NULL )
1308                 goto error;
1309
1310             p_sys->i_sendbuf_len = i_realloc_len;
1311         }
1312
1313         /* Fill buffer */
1314         memcpy( p_sys->p_sendbuf, header, sizeof( header ) );
1315         memcpy( p_sys->p_sendbuf + sizeof( header ),
1316                 p_buffer->p_buffer, p_buffer->i_buffer );
1317
1318         /* Calculate payload length and update header */
1319         i_payload_len = i_len - 4;
1320         if ( i_payload_len > 0xffff )
1321         {
1322             msg_Err( p_stream, "Buffer is too long (%u bytes)",
1323                      (unsigned int)i_payload_len );
1324             goto error;
1325         }
1326
1327         p_sys->p_sendbuf[2] = ( i_payload_len >> 8 ) & 0xff;
1328         p_sys->p_sendbuf[3] = i_payload_len & 0xff;
1329
1330         /* Reset cipher */
1331         i_gcrypt_err = gcry_cipher_reset( p_sys->aes_ctx );
1332         if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
1333             goto error;
1334
1335         /* Set IV */
1336         i_gcrypt_err = gcry_cipher_setiv( p_sys->aes_ctx, p_sys->ps_aes_iv,
1337                                           sizeof( p_sys->ps_aes_iv ) );
1338         if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
1339             goto error;
1340
1341         /* Encrypt in place. Only full blocks of 16 bytes are encrypted,
1342          * the rest (0-15 bytes) is left unencrypted.
1343          */
1344         i_gcrypt_err =
1345             gcry_cipher_encrypt( p_sys->aes_ctx,
1346                                  p_sys->p_sendbuf + sizeof( header ),
1347                                  ( p_buffer->i_buffer / 16 ) * 16,
1348                                  NULL, 0 );
1349         if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
1350             goto error;
1351
1352         /* Send data */
1353         rc = net_Write( p_stream, p_sys->i_stream_fd, NULL,
1354                         p_sys->p_sendbuf, i_len );
1355         if ( rc < 0 )
1356             goto error;
1357
1358         p_next = p_buffer->p_next;
1359         block_Release( p_buffer );
1360         p_buffer = p_next;
1361     }
1362
1363 error:
1364     block_ChainRelease( p_buffer );
1365     return;
1366 }
1367
1368
1369 /*****************************************************************************
1370  * Open:
1371  *****************************************************************************/
1372 static int Open( vlc_object_t *p_this )
1373 {
1374     sout_stream_t *p_stream = (sout_stream_t*)p_this;
1375     sout_stream_sys_t *p_sys;
1376     char psz_local[NI_MAXNUMERICHOST];
1377     char *psz_pwfile = NULL;
1378     gcry_error_t i_gcrypt_err;
1379     int i_err = VLC_SUCCESS;
1380     uint32_t i_session_id;
1381     uint64_t i_client_instance;
1382
1383     vlc_gcrypt_init();
1384
1385     config_ChainParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
1386                        p_stream->p_cfg );
1387
1388     p_sys = calloc( 1, sizeof( *p_sys ) );
1389     if ( p_sys == NULL )
1390         return VLC_ENOMEM;
1391
1392     p_stream->pf_add = Add;
1393     p_stream->pf_del = Del;
1394     p_stream->pf_send = Send;
1395     p_stream->p_sys = p_sys;
1396     p_stream->pace_nocontrol = true;
1397
1398     p_sys->i_control_fd = -1;
1399     p_sys->i_stream_fd = -1;
1400     p_sys->i_volume = var_GetInteger( p_stream, SOUT_CFG_PREFIX "volume");
1401     p_sys->i_jack_type = JACK_TYPE_NONE;
1402
1403     http_auth_Init( &p_sys->auth );
1404
1405     p_sys->psz_host = var_GetNonEmptyString( p_stream,
1406                                              SOUT_CFG_PREFIX "host" );
1407     if ( p_sys->psz_host == NULL )
1408     {
1409         msg_Err( p_this, "Missing host" );
1410         i_err = VLC_EGENERIC;
1411         goto error;
1412     }
1413
1414     p_sys->psz_password = var_GetNonEmptyString( p_stream,
1415                                                  SOUT_CFG_PREFIX "password" );
1416     if ( p_sys->psz_password == NULL )
1417     {
1418         /* Try password file instead */
1419         psz_pwfile = var_GetNonEmptyString( p_stream,
1420                                             SOUT_CFG_PREFIX "password-file" );
1421         if ( psz_pwfile != NULL )
1422         {
1423             p_sys->psz_password = ReadPasswordFile( p_this, psz_pwfile );
1424             if ( p_sys->psz_password == NULL )
1425             {
1426                 i_err = VLC_EGENERIC;
1427                 goto error;
1428             }
1429         }
1430     }
1431
1432     if ( p_sys->psz_password != NULL )
1433         msg_Info( p_this, "Using password authentication" );
1434
1435     var_AddCallback( p_stream, SOUT_CFG_PREFIX "volume",
1436                      VolumeCallback, NULL );
1437     p_sys->b_volume_callback = true;
1438
1439     /* Open control connection */
1440     p_sys->i_control_fd = net_ConnectTCP( p_stream, p_sys->psz_host,
1441                                           RAOP_PORT );
1442     if ( p_sys->i_control_fd < 0 )
1443     {
1444         msg_Err( p_this, "Cannot establish control connection to %s:%d (%s)",
1445                  p_sys->psz_host, RAOP_PORT, vlc_strerror_c(errno) );
1446         i_err = VLC_EGENERIC;
1447         goto error;
1448     }
1449
1450     /* Get local IP address */
1451     if ( net_GetSockAddress( p_sys->i_control_fd, psz_local, NULL ) )
1452     {
1453         msg_Err( p_this, "cannot get local IP address" );
1454         i_err = VLC_EGENERIC;
1455         goto error;
1456     }
1457
1458     /* Random session ID */
1459     gcry_randomize( &i_session_id, sizeof( i_session_id ),
1460                     GCRY_STRONG_RANDOM );
1461
1462     /* Random client instance */
1463     gcry_randomize( &i_client_instance, sizeof( i_client_instance ),
1464                     GCRY_STRONG_RANDOM );
1465     if ( asprintf( &p_sys->psz_client_instance, "%016"PRIX64,
1466                    i_client_instance ) < 0 )
1467     {
1468         i_err = VLC_ENOMEM;
1469         goto error;
1470     }
1471
1472     /* Build session URL */
1473     if ( asprintf( &p_sys->psz_url, "rtsp://%s/%u",
1474                    psz_local, i_session_id ) < 0 )
1475     {
1476         i_err = VLC_ENOMEM;
1477         goto error;
1478     }
1479
1480     /* Generate AES key and IV */
1481     gcry_randomize( p_sys->ps_aes_key, sizeof( p_sys->ps_aes_key ),
1482                     GCRY_STRONG_RANDOM );
1483     gcry_randomize( p_sys->ps_aes_iv, sizeof( p_sys->ps_aes_iv ),
1484                     GCRY_STRONG_RANDOM );
1485
1486     /* Setup AES */
1487     i_gcrypt_err = gcry_cipher_open( &p_sys->aes_ctx, GCRY_CIPHER_AES,
1488                                      GCRY_CIPHER_MODE_CBC, 0 );
1489     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
1490     {
1491         i_err = VLC_EGENERIC;
1492         goto error;
1493     }
1494
1495     /* Set key */
1496     i_gcrypt_err = gcry_cipher_setkey( p_sys->aes_ctx, p_sys->ps_aes_key,
1497                                        sizeof( p_sys->ps_aes_key ) );
1498     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
1499     {
1500         i_err = VLC_EGENERIC;
1501         goto error;
1502     }
1503
1504     /* Protocol handshake */
1505     i_err = AnnounceSDP( p_this, psz_local, i_session_id );
1506     if ( i_err != VLC_SUCCESS )
1507         goto error;
1508
1509     i_err = SendSetup( p_this );
1510     if ( i_err != VLC_SUCCESS )
1511         goto error;
1512
1513     i_err = SendRecord( p_this );
1514     if ( i_err != VLC_SUCCESS )
1515         goto error;
1516
1517     i_err = UpdateVolume( p_this );
1518     if ( i_err != VLC_SUCCESS )
1519         goto error;
1520
1521     LogInfo( p_this );
1522
1523     /* Open stream connection */
1524     p_sys->i_stream_fd = net_ConnectTCP( p_stream, p_sys->psz_host,
1525                                          p_sys->i_server_port );
1526     if ( p_sys->i_stream_fd < 0 )
1527     {
1528         msg_Err( p_this, "Cannot establish stream connection to %s:%d (%s)",
1529                  p_sys->psz_host, p_sys->i_server_port,
1530                  vlc_strerror_c(errno)  );
1531         i_err = VLC_EGENERIC;
1532         goto error;
1533     }
1534
1535 error:
1536     free( psz_pwfile );
1537
1538     if ( i_err != VLC_SUCCESS )
1539         FreeSys( p_this, p_sys );
1540
1541     return i_err;
1542 }
1543
1544
1545 /*****************************************************************************
1546  * Close:
1547  *****************************************************************************/
1548 static void Close( vlc_object_t *p_this )
1549 {
1550     sout_stream_t *p_stream = (sout_stream_t*)p_this;
1551     sout_stream_sys_t *p_sys = p_stream->p_sys;
1552
1553     SendFlush( p_this );
1554     SendTeardown( p_this );
1555
1556     FreeSys( p_this, p_sys );
1557 }
1558
1559
1560 /*****************************************************************************
1561  * Add:
1562  *****************************************************************************/
1563 static sout_stream_id_sys_t *Add( sout_stream_t *p_stream, const es_format_t *p_fmt )
1564 {
1565     sout_stream_sys_t *p_sys = p_stream->p_sys;
1566     sout_stream_id_sys_t *id = NULL;
1567
1568     id = calloc( 1, sizeof( *id ) );
1569     if ( id == NULL )
1570         goto error;
1571
1572     es_format_Copy( &id->fmt, p_fmt );
1573
1574     switch ( id->fmt.i_cat )
1575     {
1576     case AUDIO_ES:
1577         if ( id->fmt.i_codec == VLC_CODEC_ALAC )
1578         {
1579             if ( p_sys->p_audio_stream )
1580             {
1581                 msg_Warn( p_stream, "Only the first Apple Lossless audio "
1582                                     "stream is used" );
1583             }
1584             else if ( id->fmt.audio.i_rate != 44100 ||
1585                       id->fmt.audio.i_channels != 2 )
1586             {
1587                 msg_Err( p_stream, "The Apple Lossless audio stream must be "
1588                                    "encoded with 44100 Hz and 2 channels" );
1589             }
1590             else
1591             {
1592                 /* Use this stream */
1593                 p_sys->p_audio_stream = id;
1594             }
1595         }
1596         else if ( !p_sys->b_alac_warning )
1597         {
1598             msg_Err( p_stream, "Apple Lossless is the only codec supported. "
1599                                "Use the \"transcode\" module for conversion "
1600                                "(e.g. \"transcode{acodec=alac,"
1601                                "channels=2}\")." );
1602             p_sys->b_alac_warning = true;
1603         }
1604
1605         break;
1606
1607     default:
1608         /* Leave other stream types alone */
1609         break;
1610     }
1611
1612     return id;
1613
1614 error:
1615     FreeId( id );
1616
1617     return NULL;
1618 }
1619
1620
1621 /*****************************************************************************
1622  * Del:
1623  *****************************************************************************/
1624 static void Del( sout_stream_t *p_stream, sout_stream_id_sys_t *id )
1625 {
1626     sout_stream_sys_t *p_sys = p_stream->p_sys;
1627
1628     if ( p_sys->p_audio_stream == id )
1629         p_sys->p_audio_stream = NULL;
1630
1631     FreeId( id );
1632 }
1633
1634
1635 /*****************************************************************************
1636  * Send:
1637  *****************************************************************************/
1638 static int Send( sout_stream_t *p_stream, sout_stream_id_sys_t *id,
1639                  block_t *p_buffer )
1640 {
1641     sout_stream_sys_t *p_sys = p_stream->p_sys;
1642
1643     if ( id->fmt.i_cat == AUDIO_ES && id == p_sys->p_audio_stream )
1644     {
1645         /* SendAudio takes care of releasing the buffers */
1646         SendAudio( p_stream, p_buffer );
1647     }
1648     else
1649     {
1650         block_ChainRelease( p_buffer );
1651     }
1652
1653     return VLC_SUCCESS;
1654 }
1655
1656
1657 /*****************************************************************************
1658  * VolumeCallback: called when the volume is changed on the fly.
1659  *****************************************************************************/
1660 static int VolumeCallback( vlc_object_t *p_this, char const *psz_cmd,
1661                            vlc_value_t oldval, vlc_value_t newval,
1662                            void *p_data )
1663 {
1664     VLC_UNUSED(psz_cmd);
1665     VLC_UNUSED(oldval);
1666     VLC_UNUSED(p_data);
1667     VLC_UNUSED(newval);
1668     sout_stream_t *p_stream = (sout_stream_t*)p_this;
1669     sout_stream_sys_t *p_sys = p_stream->p_sys;
1670
1671     /* TODO: Implement volume change */
1672     VLC_UNUSED(p_sys);
1673
1674     return VLC_SUCCESS;
1675 }