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