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