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