net.c 34.8 KB
Newer Older
Laurent Aimar's avatar
   
Laurent Aimar committed
1
2
3
/*****************************************************************************
 * net.c:
 *****************************************************************************
4
 * Copyright (C) 2004-2005 VideoLAN
5
 * $Id$
Laurent Aimar's avatar
   
Laurent Aimar committed
6
7
 *
 * Authors: Laurent Aimar <fenrir@videolan.org>
8
 *          Rémi Denis-Courmont <rem # videolan.org>
Laurent Aimar's avatar
   
Laurent Aimar committed
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>
#include <vlc/vlc.h>

31
#include <errno.h>
Laurent Aimar's avatar
   
Laurent Aimar committed
32
33
34
35
36
37
38
39
40

#ifdef HAVE_FCNTL_H
#   include <fcntl.h>
#endif

#ifdef HAVE_SYS_TIME_H
#    include <sys/time.h>
#endif

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
41
#if defined( WIN32 ) || defined( UNDER_CE )
42
43
44
#   if defined(UNDER_CE) && defined(sockaddr_storage)
#       undef sockaddr_storage
#   endif
Laurent Aimar's avatar
   
Laurent Aimar committed
45
46
47
48
#   include <winsock2.h>
#   include <ws2tcpip.h>
#else
#   include <sys/socket.h>
49
#   include <netinet/in.h>
Eric Petit's avatar
Eric Petit committed
50
51
52
#   ifdef HAVE_ARPA_INET_H
#       include <arpa/inet.h>
#   endif
53
#   include <netdb.h>
Laurent Aimar's avatar
   
Laurent Aimar committed
54
55
#endif

gbazin's avatar
gbazin committed
56
57
58
59
#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#endif

Laurent Aimar's avatar
   
Laurent Aimar committed
60
61
#include "network.h"

62
63
64
65
66
67
#ifndef INADDR_ANY
#   define INADDR_ANY  0x00000000
#endif
#ifndef INADDR_NONE
#   define INADDR_NONE 0xFFFFFFFF
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
68
69
70
#ifndef PF_INET
#   define PF_INET AF_INET
#endif
71
72
73
74
75
76
77
78

static int SocksNegociate( vlc_object_t *, int fd, int i_socks_version,
                           char *psz_socks_user, char *psz_socks_passwd );
static int SocksHandshakeTCP( vlc_object_t *,
                              int fd, int i_socks_version,
                              char *psz_socks_user, char *psz_socks_passwd,
                              const char *psz_host, int i_port );

Laurent Aimar's avatar
   
Laurent Aimar committed
79
80
81
82
83
/*****************************************************************************
 * __net_OpenTCP:
 *****************************************************************************
 * Open a TCP connection and return a handle
 *****************************************************************************/
hartman's avatar
   
hartman committed
84
int __net_OpenTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
Laurent Aimar's avatar
   
Laurent Aimar committed
85
{
86
87
88
89
    struct addrinfo hints, *res, *ptr;
    const char      *psz_realhost;
    char            *psz_realport, *psz_socks;
    int             i_val, i_handle = -1;
Laurent Aimar's avatar
   
Laurent Aimar committed
90

91
92
93
94
    if( ( i_port < 0 ) || ( i_port > 65535 ) )
        return -1; /* I don't expect the next TCP version shortly */
    if( i_port == 0 )
        i_port = 80; /* historical VLC thing */
Laurent Aimar's avatar
   
Laurent Aimar committed
95

96
97
    memset( &hints, 0, sizeof( hints ) );
    hints.ai_socktype = SOCK_STREAM;
Laurent Aimar's avatar
   
Laurent Aimar committed
98

99
100
    psz_socks = var_CreateGetString( p_this, "socks" );
    if( *psz_socks && *psz_socks != ':' )
Laurent Aimar's avatar
   
Laurent Aimar committed
101
    {
102
        char *psz = strchr( psz_socks, ':' );
103
104
105
106

        if( psz )
            *psz++ = '\0';

107
108
        psz_realhost = psz_socks;
        psz_realport = strdup( ( psz != NULL ) ? psz : "1080" );
109

110
111
        msg_Dbg( p_this, "net: connecting to '%s:%s' for '%s:%d'",
                 psz_realhost, psz_realport, psz_host, i_port );
112
113
114
    }
    else
    {
115
116
117
118
119
120
121
        psz_realhost = psz_host;
        psz_realport = malloc( 6 );
        if( psz_realport == NULL )
        {
            free( psz_socks );
            return -1;
        }
122

123
124
125
126
        sprintf( psz_realport, "%d", i_port );
        msg_Dbg( p_this, "net: connecting to '%s:%s'", psz_realhost,
                 psz_realport );
    }
127

128
129
130
131
    i_val = vlc_getaddrinfo( p_this, psz_realhost, psz_realport, &hints,
                             &res );
    free( psz_realport );
    if( i_val )
Laurent Aimar's avatar
   
Laurent Aimar committed
132
    {
133
134
135
        msg_Err( p_this, "cannot resolve '%s' : %s", psz_realhost,
                 vlc_gai_strerror( i_val ) );
        free( psz_socks );
Laurent Aimar's avatar
   
Laurent Aimar committed
136
137
138
        return -1;
    }

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
    for( ptr = res; (ptr != NULL) && (i_handle == -1); ptr = ptr->ai_next )
    {
        int fd;

        fd = socket( ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol );
        if( fd == -1 )
        {
#if defined(WIN32) || defined(UNDER_CE)
            msg_Warn( p_this, "cannot create socket (%i)",
                      WSAGetLastError() );
#else
            msg_Warn( p_this, "cannot create socket (%s)",
                      strerror( errno ) );
#endif
            continue;
        }


        /* Set to non-blocking */
#if defined( WIN32 ) || defined( UNDER_CE )
        {
            unsigned long i_dummy = 1;
            if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 )
                msg_Err( p_this, "cannot set socket to non-blocking mode" );
        }

# ifdef IPV6_PROTECTION_LEVEL
        if( ptr->ai_family == PF_INET6 )
        {
            i_val = PROTECTION_LEVEL_UNRESTRICTED;
            setsockopt( fd, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, &i_val,
                        sizeof( i_val ) );
        }
# else
#  warning You are using outdated headers for Winsock !
# endif
#else
        if( ( ( i_val = fcntl( fd, F_GETFL, 0 ) ) < 0 ) ||
            ( fcntl( fd, F_SETFL, i_val | O_NONBLOCK ) < 0 ) )
            msg_Err( p_this, "cannot set socket to non-blocking mode (%s)",
                     strerror( errno ) );
#endif

        i_val = 1;
        setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (void *)&i_val,
                    sizeof( i_val ) );

        if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) )
        {
            int i_val_size = sizeof( i_val ), i_max_count;
            struct timeval tv;
            vlc_value_t timeout;
            fd_set fds;

#if defined( WIN32 ) || defined( UNDER_CE )
            if( WSAGetLastError() != WSAEWOULDBLOCK )
            {
                msg_Warn( p_this, "connection to %s:%d failed (%d)", psz_host,
                          i_port, WSAGetLastError( ) );
                net_Close( fd );
                continue;
            }
#else
            if( errno != EINPROGRESS )
            {
                msg_Warn( p_this, "connection to %s:%d : %s", psz_host,
                          i_port, strerror( errno ) );
                net_Close( fd );
                continue;
            }
#endif

            var_Create( p_this, "ipv4-timeout",
                        VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
            var_Get( p_this, "ipv4-timeout", &timeout );
            i_max_count = timeout.i_int / 100;

            msg_Dbg( p_this, "connection in progress" );
            do
            {
                if( p_this->b_die )
                {
                    msg_Dbg( p_this, "connection aborted" );
                    net_Close( fd );
                    vlc_freeaddrinfo( res );
                    free( psz_socks );
                    return -1;
                }
                if( i_max_count <= 0 )
                {
                    msg_Dbg( p_this, "connection timed out" );
                    net_Close( fd );
                    continue;
                }

                i_max_count--;

                /* Initialize file descriptor set */
                FD_ZERO( &fds );
                FD_SET( fd, &fds );

                /* We'll wait 0.1 second if nothing happens */
                tv.tv_sec = 0;
                tv.tv_usec = 100000;

                i_val = select( fd + 1, NULL, &fds, NULL, &tv );
            }
            while( ( i_val == 0 ) || ( ( i_val < 0 ) &&
#if defined( WIN32 ) || defined( UNDER_CE )
                            ( WSAGetLastError() == WSAEWOULDBLOCK )
#else
                            ( errno == EINTR )
#endif
                     ) );

            if( i_val < 0 )
            {
                msg_Warn( p_this, "connection aborted (select failed)" );
                net_Close( fd );
                continue;
            }

#if !defined( SYS_BEOS ) && !defined( UNDER_CE )
            if( getsockopt( fd, SOL_SOCKET, SO_ERROR, (void*)&i_val,
                            &i_val_size ) == -1 || i_val != 0 )
            {
#ifdef WIN32
                msg_Warn( p_this, "connection to %s:%d failed (%d)", psz_host,
                          i_port, WSAGetLastError( ) );
#else
                msg_Warn( p_this, "connection to %s:%d : %s", psz_host,
                          i_port, strerror( i_val ) );
#endif
                net_Close( fd );
                continue;
            }
#endif
        }
        i_handle = fd; /* success! */
    }
    
    vlc_freeaddrinfo( res );

    if( *psz_socks && *psz_socks != ':' )
283
284
285
286
    {
        char *psz_user = var_CreateGetString( p_this, "socks-user" );
        char *psz_pwd  = var_CreateGetString( p_this, "socks-pwd" );

287
        if( SocksHandshakeTCP( p_this, i_handle, 5, psz_user, psz_pwd,
288
289
290
                               psz_host, i_port ) )
        {
            msg_Err( p_this, "failed to use the SOCKS server" );
291
292
            net_Close( i_handle );
            i_handle = -1;
293
294
295
296
297
        }

        free( psz_user );
        free( psz_pwd );
    }
298
    free( psz_socks );
299

300
    return i_handle;
Laurent Aimar's avatar
   
Laurent Aimar committed
301
302
}

303

304
305
306
/*****************************************************************************
 * __net_ListenTCP:
 *****************************************************************************
307
308
 * Open TCP passive "listening" socket(s)
 * This function returns NULL in case of error.
309
 *****************************************************************************/
310
int *__net_ListenTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
311
{
312
313
314
    struct addrinfo hints, *res, *ptr;
    int             i_val, *pi_handles, i_size;
    char            *psz_port;
315

316
317
318
319
    if( ( i_port < 0 ) || ( i_port > 65535 ) )
        return NULL; /* I don't expect the next TCP version shortly */
    if( i_port == 0 )
        i_port = 80; /* historical VLC thing */
320

321
322
323
    memset( &hints, 0, sizeof( hints ) );
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
324

325
326
327
328
329
330
331
332
333
334
    psz_port = malloc( 6 );
    if( psz_port == NULL )
        return NULL;

    sprintf( psz_port, "%d", i_port );
    msg_Dbg( p_this, "net: listening to '%s:%s'", psz_host, psz_port );

    i_val = vlc_getaddrinfo( p_this, psz_host, psz_port, &hints, &res );
    free( psz_port );
    if( i_val )
335
    {
336
337
338
        msg_Err( p_this, "cannot resolve '%s' : %s", psz_host,
                 vlc_gai_strerror( i_val ) );
        return NULL;
339
340
    }

341
342
    pi_handles = NULL;
    i_size = 1;
343

344
    for( ptr = res; ptr != NULL; ptr = ptr->ai_next )
345
    {
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
        int fd, *newpi;

        fd = socket( ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol );
        if( fd == -1 )
        {
#if defined(WIN32) || defined(UNDER_CE)
            msg_Warn( p_this, "cannot create socket (%i)",
                      WSAGetLastError() );
#else
            msg_Warn( p_this, "cannot create socket (%s)",
                      strerror( errno ) );
#endif
            continue;
        }

        /* Set to non-blocking */
#if defined( WIN32 ) || defined( UNDER_CE )
        {
            unsigned long i_dummy = 1;
            if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 )
                msg_Err( p_this, "cannot set socket to non-blocking mode" );
        }
#else
        if( ( ( i_val = fcntl( fd, F_GETFL, 0 ) ) < 0 ) ||
            ( fcntl( fd, F_SETFL, i_val | O_NONBLOCK ) < 0 ) )
            msg_Err( p_this, "cannot set socket to non-blocking mode (%s)",
                     strerror( errno ) );
#endif

        i_val = 1;
        setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (void *)&i_val,
                    sizeof( i_val ) );

        /* Bind the socket */
        if( bind( fd, ptr->ai_addr, ptr->ai_addrlen ) )
        {
#if defined(WIN32) || defined(UNDER_CE)
            msg_Warn( p_this, "cannot bind socket (%i)", WSAGetLastError( ) );
#else
            msg_Warn( p_this, "cannot bind socket (%s)", strerror( errno ) );
#endif
            net_Close( fd );
            continue;
        }
 
        /* Listen */
        if( listen( fd, 100 ) == -1 )
        {
#if defined(WIN32) || defined(UNDER_CE)
            msg_Err( p_this, "cannot bring socket in listening mode (%i)",
                     WSAGetLastError());
#else
            msg_Err( p_this, "cannot bring the socket in listening mode (%s)",
                     strerror( errno ) );
#endif
            net_Close( fd );
            continue;
        }

        newpi = (int *)realloc( pi_handles, (++i_size) * sizeof( int ) );
        if( newpi == NULL )
        {
            net_Close( fd );
            break;
        }
        else
        {
            newpi[i_size - 2] = fd;
            if( pi_handles == NULL )
                newpi[i_size - 1] = -1;
            pi_handles = newpi;
        }
418
    }
419
    
Eric Petit's avatar
Eric Petit committed
420
    vlc_freeaddrinfo( res );
421

422
    return pi_handles;
423
424
}

425
426
427
/*****************************************************************************
 * __net_Accept:
 *****************************************************************************
428
 * Accept a connection on a set of listening sockets and return it
429
 *****************************************************************************/
430
int __net_Accept( vlc_object_t *p_this, int *pi_fd, mtime_t i_wait )
431
432
433
434
435
{
    vlc_bool_t b_die = p_this->b_die, b_block = (i_wait < 0);

    while( p_this->b_die == b_die )
    {
436
437
438
439
440
441
        int i_val = -1, *pi, *pi_end;
        struct timeval timeout;
        fd_set fds_r, fds_e;

        pi = pi_fd;

442
443
444
        /* Initialize file descriptor set */
        FD_ZERO( &fds_r );
        FD_ZERO( &fds_e );
445
446
447
448
449
450
451
452
453
454
455
456

        for( pi = pi_fd; *pi != -1; pi++ )
        {
            int i_fd = *pi;

            if( i_fd > i_val )
                i_val = i_fd;

            FD_SET( i_fd, &fds_r );
            FD_SET( i_fd, &fds_e );
        }
        pi_end = pi;
457
458
459
460

        timeout.tv_sec = 0;
        timeout.tv_usec = b_block ? 500000 : i_wait;

461
462
        i_val = select( i_val + 1, &fds_r, NULL, &fds_e, &timeout );
        if( ( ( i_val < 0 ) && ( errno == EINTR ) ) || i_val == 0 )
463
        {
464
465
466
467
            if( b_block )
                continue;
            else
                return -1;
468
        }
469
        else if( i_val < 0 )
470
        {
gbazin's avatar
gbazin committed
471
#if defined(WIN32) || defined(UNDER_CE)
472
            msg_Err( p_this, "network select error (%i)", WSAGetLastError() );
473
#else
474
            msg_Err( p_this, "network select error (%s)", strerror( errno ) );
475
476
477
478
#endif
            return -1;
        }

479
        for( pi = pi_fd; *pi != -1; pi++ )
480
        {
481
482
483
484
485
486
487
488
            int i_fd = *pi;

            if( !FD_ISSET( i_fd, &fds_r ) && !FD_ISSET( i_fd, &fds_e ) )
                continue;

            i_val = accept( i_fd, NULL, 0 );
            if( i_val < 0 )
            {
gbazin's avatar
gbazin committed
489
#if defined(WIN32) || defined(UNDER_CE)
490
                msg_Err( p_this, "accept failed (%i)", WSAGetLastError() );
491
#else
492
                msg_Err( p_this, "accept failed (%s)", strerror( errno ) );
493
#endif
494
495
496
497
498
499
500
501
502
503
504
505
            }
            else
            {
                /*
                 * This round-robin trick ensures that the first sockets in
                 * pi_fd won't prevent the last ones from getting accept'ed.
                 */
                --pi_end;
                memmove( pi, pi + 1, pi_end - pi );
                *pi_end = i_fd;
                return i_val;
            }
506
507
508
509
510
511
        }
    }

    return -1;
}

Laurent Aimar's avatar
Laurent Aimar committed
512
513
514
515
516
/*****************************************************************************
 * __net_OpenUDP:
 *****************************************************************************
 * Open a UDP connection and return a handle
 *****************************************************************************/
gbazin's avatar
gbazin committed
517
518
int __net_OpenUDP( vlc_object_t *p_this, char *psz_bind, int i_bind,
                   char *psz_server, int i_server )
Laurent Aimar's avatar
Laurent Aimar committed
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
{
    vlc_value_t      val;
    void            *private;

    char            *psz_network = "";
    network_socket_t sock;
    module_t         *p_network;


    /* Check if we have force ipv4 or ipv6 */
    var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    var_Get( p_this, "ipv4", &val );
    if( val.b_bool )
    {
        psz_network = "ipv4";
    }

    var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    var_Get( p_this, "ipv6", &val );
    if( val.b_bool )
    {
        psz_network = "ipv6";
    }
    if( psz_server == NULL ) psz_server = "";
    if( psz_bind   == NULL ) psz_bind   = "";

    /* Prepare the network_socket_t structure */
    sock.psz_bind_addr   = psz_bind;
    sock.i_bind_port     = i_bind;
    sock.psz_server_addr = psz_server;
    sock.i_server_port   = i_server;
    sock.i_ttl           = 0;

    msg_Dbg( p_this, "net: connecting to '%s:%d@%s:%d'",
             psz_server, i_server, psz_bind, i_bind );
    private = p_this->p_private;
    p_this->p_private = (void*)&sock;
gbazin's avatar
   
gbazin committed
556
    if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
Laurent Aimar's avatar
Laurent Aimar committed
557
558
559
560
561
562
563
564
565
566
567
    {
        msg_Dbg( p_this, "net: connection to '%s:%d@%s:%d' failed",
                 psz_server, i_server, psz_bind, i_bind );
        return -1;
    }
    module_Unneed( p_this, p_network );
    p_this->p_private = private;

    return sock.i_handle;
}

Laurent Aimar's avatar
   
Laurent Aimar committed
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
/*****************************************************************************
 * __net_Close:
 *****************************************************************************
 * Close a network handle
 *****************************************************************************/
void net_Close( int fd )
{
#ifdef UNDER_CE
    CloseHandle( (HANDLE)fd );
#elif defined( WIN32 )
    closesocket( fd );
#else
    close( fd );
#endif
}

584
585
586
587
588
589
590
591
592
593
594
595
void net_ListenClose( int *pi_fd )
{
    if( pi_fd != NULL )
    {
        int *pi;

        for( pi = pi_fd; *pi != -1; pi++ )
            net_Close( *pi );
        free( pi_fd );
    }
}

Laurent Aimar's avatar
   
Laurent Aimar committed
596
597
598
599
600
601
602
/*****************************************************************************
 * __net_Read:
 *****************************************************************************
 * Read from a network socket
 * If b_rety is true, then we repeat until we have read the right amount of
 * data
 *****************************************************************************/
603
604
int __net_Read( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
                uint8_t *p_data, int i_data, vlc_bool_t b_retry )
Laurent Aimar's avatar
   
Laurent Aimar committed
605
606
{
    struct timeval  timeout;
607
    fd_set          fds_r, fds_e;
Laurent Aimar's avatar
   
Laurent Aimar committed
608
609
610
    int             i_recv;
    int             i_total = 0;
    int             i_ret;
611
    vlc_bool_t      b_die = p_this->b_die;
Laurent Aimar's avatar
   
Laurent Aimar committed
612
613
614
615
616

    while( i_data > 0 )
    {
        do
        {
617
            if( p_this->b_die != b_die )
Laurent Aimar's avatar
   
Laurent Aimar committed
618
619
620
621
622
            {
                return 0;
            }

            /* Initialize file descriptor set */
623
624
625
626
            FD_ZERO( &fds_r );
            FD_SET( fd, &fds_r );
            FD_ZERO( &fds_e );
            FD_SET( fd, &fds_e );
Laurent Aimar's avatar
   
Laurent Aimar committed
627
628
629
630

            /* We'll wait 0.5 second if nothing happens */
            timeout.tv_sec = 0;
            timeout.tv_usec = 500000;
631
632
633

        } while( (i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout)) == 0
                 || ( i_ret < 0 && errno == EINTR ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
634
635
636

        if( i_ret < 0 )
        {
gbazin's avatar
gbazin committed
637
638
639
#if defined(WIN32) || defined(UNDER_CE)
            msg_Err( p_this, "network select error" );
#else
Laurent Aimar's avatar
   
Laurent Aimar committed
640
            msg_Err( p_this, "network select error (%s)", strerror(errno) );
gbazin's avatar
gbazin committed
641
#endif
Laurent Aimar's avatar
   
Laurent Aimar committed
642
643
644
            return i_total > 0 ? i_total : -1;
        }

645
646
647
        if( ( i_recv = (p_vs != NULL)
              ? p_vs->pf_recv( p_vs->p_sys, p_data, i_data )
              : recv( fd, p_data, i_data, 0 ) ) < 0 )
Laurent Aimar's avatar
   
Laurent Aimar committed
648
        {
gbazin's avatar
gbazin committed
649
#if defined(WIN32) || defined(UNDER_CE)
650
651
652
653
654
            if( WSAGetLastError() == WSAEWOULDBLOCK )
            {
                /* only happens with p_vs (SSL) - not really an error */
            }
            else
Laurent Aimar's avatar
Laurent Aimar committed
655
656
657
658
            /* For udp only */
            /* On win32 recv() will fail if the datagram doesn't fit inside
             * the passed buffer, even though the buffer will be filled with
             * the first part of the datagram. */
zorglub's avatar
zorglub committed
659
            if( WSAGetLastError() == WSAEMSGSIZE )
Laurent Aimar's avatar
Laurent Aimar committed
660
            {
gbazin's avatar
gbazin committed
661
                msg_Err( p_this, "recv() failed. "
Laurent Aimar's avatar
Laurent Aimar committed
662
                         "Increase the mtu size (--mtu option)" );
663
                i_total += i_data;
Laurent Aimar's avatar
Laurent Aimar committed
664
            }
665
666
            else if( WSAGetLastError() == WSAEINTR ) continue;
            else msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
667
#else
668
669
670
            /* EAGAIN only happens with p_vs (SSL) and it's not an error */
            if( errno != EAGAIN )
                msg_Err( p_this, "recv failed (%s)", strerror(errno) );
671
#endif
Laurent Aimar's avatar
   
Laurent Aimar committed
672
673
            return i_total > 0 ? i_total : -1;
        }
674
675
676
677
678
        else if( i_recv == 0 )
        {
            /* Connection closed */
            b_retry = VLC_FALSE;
        }
Laurent Aimar's avatar
   
Laurent Aimar committed
679
680
681
682
683
684
685
686
687
688
689
690

        p_data += i_recv;
        i_data -= i_recv;
        i_total+= i_recv;
        if( !b_retry )
        {
            break;
        }
    }
    return i_total;
}

zorglub's avatar
zorglub committed
691
692
693
694
695
/*****************************************************************************
 * __net_ReadNonBlock:
 *****************************************************************************
 * Read from a network socket, non blocking mode (with timeout)
 *****************************************************************************/
696
697
int __net_ReadNonBlock( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
                        uint8_t *p_data, int i_data, mtime_t i_wait)
zorglub's avatar
zorglub committed
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
{
    struct timeval  timeout;
    fd_set          fds_r, fds_e;
    int             i_recv;
    int             i_ret;

    /* Initialize file descriptor set */
    FD_ZERO( &fds_r );
    FD_SET( fd, &fds_r );
    FD_ZERO( &fds_e );
    FD_SET( fd, &fds_e );

    timeout.tv_sec = 0;
    timeout.tv_usec = i_wait;

    i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout);

    if( i_ret < 0 && errno == EINTR )
    {
        return 0;
    }
    else if( i_ret < 0 )
    {
gbazin's avatar
gbazin committed
721
722
723
#if defined(WIN32) || defined(UNDER_CE)
        msg_Err( p_this, "network select error" );
#else
zorglub's avatar
zorglub committed
724
        msg_Err( p_this, "network select error (%s)", strerror(errno) );
gbazin's avatar
gbazin committed
725
#endif
zorglub's avatar
zorglub committed
726
727
728
729
730
731
732
733
        return -1;
    }
    else if( i_ret == 0)
    {
        return 0;
    }
    else
    {
gbazin's avatar
gbazin committed
734
735
736
#if !defined(UNDER_CE)
        if( fd == 0/*STDIN_FILENO*/ ) i_recv = read( fd, p_data, i_data ); else
#endif
737
738
739
        if( ( i_recv = (p_vs != NULL)
              ? p_vs->pf_recv( p_vs->p_sys, p_data, i_data )
              : recv( fd, p_data, i_data, 0 ) ) <= 0 )
zorglub's avatar
zorglub committed
740
        {
gbazin's avatar
gbazin committed
741
#if defined(WIN32) || defined(UNDER_CE)
zorglub's avatar
zorglub committed
742
743
744
745
746
747
748
749
750
            /* For udp only */
            /* On win32 recv() will fail if the datagram doesn't fit inside
             * the passed buffer, even though the buffer will be filled with
             * the first part of the datagram. */
            if( WSAGetLastError() == WSAEMSGSIZE )
            {
                msg_Err( p_this, "recv() failed. "
                         "Increase the mtu size (--mtu option)" );
            }
751
            else msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
zorglub's avatar
zorglub committed
752
753
754
755
756
#else
            msg_Err( p_this, "recv failed (%s)", strerror(errno) );
#endif
            return -1;
        }
757
758

        return i_recv ? i_recv : -1;  /* !i_recv -> connection closed if tcp */
zorglub's avatar
zorglub committed
759
    }
760

zorglub's avatar
zorglub committed
761
762
763
764
    /* We will never be here */
    return -1;
}

zorglub's avatar
zorglub committed
765
766
767
/*****************************************************************************
 * __net_Select:
 *****************************************************************************
768
769
 * Read from several sockets (with timeout). Takes data from the first socket
 * that has some.
zorglub's avatar
zorglub committed
770
 *****************************************************************************/
771
772
int __net_Select( vlc_object_t *p_this, int *pi_fd, v_socket_t **pp_vs,
                  int i_fd, uint8_t *p_data, int i_data, mtime_t i_wait )
zorglub's avatar
zorglub committed
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
{
    struct timeval  timeout;
    fd_set          fds_r, fds_e;
    int             i_recv;
    int             i_ret;
    int             i;
    int             i_max_fd = 0;

    /* Initialize file descriptor set */
    FD_ZERO( &fds_r );
    FD_ZERO( &fds_e );

    for( i = 0 ; i < i_fd ; i++)
    {
        if( pi_fd[i] > i_max_fd ) i_max_fd = pi_fd[i];
        FD_SET( pi_fd[i], &fds_r );
        FD_SET( pi_fd[i], &fds_e );
    }

    timeout.tv_sec = 0;
    timeout.tv_usec = i_wait;

    i_ret = select( i_max_fd + 1, &fds_r, NULL, &fds_e, &timeout );

    if( i_ret < 0 && errno == EINTR )
    {
        return 0;
    }
    else if( i_ret < 0 )
    {
        msg_Err( p_this, "network select error (%s)", strerror(errno) );
        return -1;
    }
    else if( i_ret == 0 )
    {
        return 0;
    }
    else
    {
        for( i = 0 ; i < i_fd ; i++)
        {
            if( FD_ISSET( pi_fd[i], &fds_r ) )
            {
816
817
818
                i_recv = ((pp_vs != NULL) && (pp_vs[i] != NULL))
                         ? pp_vs[i]->pf_recv( pp_vs[i]->p_sys, p_data, i_data )
                         : recv( pi_fd[i], p_data, i_data, 0 );
zorglub's avatar
zorglub committed
819
820
821
822
823
824
825
826
827
828
829
830
                if( i_recv <= 0 )
                {
#ifdef WIN32
                    /* For udp only */
                    /* On win32 recv() will fail if the datagram doesn't
                     * fit inside the passed buffer, even though the buffer
                     *  will be filled with the first part of the datagram. */
                    if( WSAGetLastError() == WSAEMSGSIZE )
                    {
                        msg_Err( p_this, "recv() failed. "
                             "Increase the mtu size (--mtu option)" );
                    }
831
832
                    else msg_Err( p_this, "recv failed (%i)",
                                  WSAGetLastError() );
zorglub's avatar
zorglub committed
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
#else
                     msg_Err( p_this, "recv failed (%s)", strerror(errno) );
#endif
                    return VLC_EGENERIC;
                }

                return i_recv;
            }
        }
    }

    /* We will never be here */
    return -1;
}


Laurent Aimar's avatar
   
Laurent Aimar committed
849
/* Write exact amount requested */
850
851
int __net_Write( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
                 uint8_t *p_data, int i_data )
Laurent Aimar's avatar
   
Laurent Aimar committed
852
853
{
    struct timeval  timeout;
854
    fd_set          fds_w, fds_e;
Laurent Aimar's avatar
   
Laurent Aimar committed
855
856
857
858
    int             i_send;
    int             i_total = 0;
    int             i_ret;

859
860
    vlc_bool_t      b_die = p_this->b_die;

Laurent Aimar's avatar
   
Laurent Aimar committed
861
862
863
864
    while( i_data > 0 )
    {
        do
        {
865
            if( p_this->b_die != b_die )
Laurent Aimar's avatar
   
Laurent Aimar committed
866
867
868
869
870
            {
                return 0;
            }

            /* Initialize file descriptor set */
871
872
873
874
            FD_ZERO( &fds_w );
            FD_SET( fd, &fds_w );
            FD_ZERO( &fds_e );
            FD_SET( fd, &fds_e );
Laurent Aimar's avatar
   
Laurent Aimar committed
875
876
877
878

            /* We'll wait 0.5 second if nothing happens */
            timeout.tv_sec = 0;
            timeout.tv_usec = 500000;
879
880
881

        } while( (i_ret = select(fd + 1, NULL, &fds_w, &fds_e, &timeout)) == 0
                 || ( i_ret < 0 && errno == EINTR ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
882
883
884

        if( i_ret < 0 )
        {
gbazin's avatar
gbazin committed
885
886
887
#if defined(WIN32) || defined(UNDER_CE)
            msg_Err( p_this, "network select error" );
#else
Laurent Aimar's avatar
   
Laurent Aimar committed
888
            msg_Err( p_this, "network select error (%s)", strerror(errno) );
gbazin's avatar
gbazin committed
889
#endif
Laurent Aimar's avatar
   
Laurent Aimar committed
890
891
892
            return i_total > 0 ? i_total : -1;
        }

893
894
895
        if( ( i_send = (p_vs != NULL)
                       ? p_vs->pf_send( p_vs->p_sys, p_data, i_data )
                       : send( fd, p_data, i_data, 0 ) ) < 0 )
Laurent Aimar's avatar
   
Laurent Aimar committed
896
        {
897
898
899
            /* XXX With udp for example, it will issue a message if the host
             * isn't listening */
            /* msg_Err( p_this, "send failed (%s)", strerror(errno) ); */
Laurent Aimar's avatar
   
Laurent Aimar committed
900
901
902
903
904
905
906
907
908
909
            return i_total > 0 ? i_total : -1;
        }

        p_data += i_send;
        i_data -= i_send;
        i_total+= i_send;
    }
    return i_total;
}

910
char *__net_Gets( vlc_object_t *p_this, int fd, v_socket_t *p_vs )
Laurent Aimar's avatar
   
Laurent Aimar committed
911
912
913
914
915
916
917
918
{
    char *psz_line = malloc( 1024 );
    int  i_line = 0;
    int  i_max = 1024;


    for( ;; )
    {
919
        if( net_Read( p_this, fd, p_vs, &psz_line[i_line], 1, VLC_TRUE ) != 1 )
Laurent Aimar's avatar
   
Laurent Aimar committed
920
        {
921
            psz_line[i_line] = '\0';
Laurent Aimar's avatar
   
Laurent Aimar committed
922
923
            break;
        }
924
925
926
        i_line++;

        if( psz_line[i_line-1] == '\n' )
Laurent Aimar's avatar
   
Laurent Aimar committed
927
        {
928
            psz_line[i_line] = '\0';
Laurent Aimar's avatar
   
Laurent Aimar committed
929
930
931
            break;
        }

932
        if( i_line >= i_max - 1 )
Laurent Aimar's avatar
   
Laurent Aimar committed
933
934
935
936
937
938
939
940
941
942
943
944
        {
            i_max += 1024;
            psz_line = realloc( psz_line, i_max );
        }
    }

    if( i_line <= 0 )
    {
        free( psz_line );
        return NULL;
    }

945
946
    while( i_line >= 1 &&
           ( psz_line[i_line-1] == '\n' || psz_line[i_line-1] == '\r' ) )
Laurent Aimar's avatar
   
Laurent Aimar committed
947
948
    {
        i_line--;
949
        psz_line[i_line] = '\0';
Laurent Aimar's avatar
   
Laurent Aimar committed
950
951
952
953
    }
    return psz_line;
}

954
955
int net_Printf( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
                const char *psz_fmt, ... )
Laurent Aimar's avatar
   
Laurent Aimar committed
956
{
957
    int i_ret;
Laurent Aimar's avatar
   
Laurent Aimar committed
958
    va_list args;
959
    va_start( args, psz_fmt );
960
    i_ret = net_vaPrintf( p_this, fd, p_vs, psz_fmt, args );
961
962
963
964
965
    va_end( args );

    return i_ret;
}

966
967
int __net_vaPrintf( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
                    const char *psz_fmt, va_list args )
968
{
Laurent Aimar's avatar
   
Laurent Aimar committed
969
    char    *psz;
970
    int     i_size, i_ret;
Laurent Aimar's avatar
   
Laurent Aimar committed
971

972
    vasprintf( &psz, psz_fmt, args );
Laurent Aimar's avatar
   
Laurent Aimar committed
973
    i_size = strlen( psz );
974
    i_ret = __net_Write( p_this, fd, p_vs, psz, i_size ) < i_size ? -1 : i_size;
975
976
977
    free( psz );

    return i_ret;
Laurent Aimar's avatar
   
Laurent Aimar committed
978
}
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097



/*****************************************************************************
 * SocksNegociate:
 *****************************************************************************
 * Negociate authentication with a SOCKS server.
 *****************************************************************************/
static int SocksNegociate( vlc_object_t *p_obj,
                           int fd, int i_socks_version,
                           char *psz_socks_user,
                           char *psz_socks_passwd )
{
    uint8_t buffer[128+2*256];
    int i_len;
    vlc_bool_t b_auth = VLC_FALSE;

    if( i_socks_version != 5 )
        return VLC_SUCCESS;

    /* We negociate authentication */

    if( psz_socks_user && psz_socks_passwd &&
        *psz_socks_user && *psz_socks_passwd )
        b_auth = VLC_TRUE;

    buffer[0] = i_socks_version;    /* SOCKS version */
    if( b_auth )
    {
        buffer[1] = 2;                  /* Number of methods */
        buffer[2] = 0x00;               /* - No auth required */
        buffer[3] = 0x02;               /* - USer/Password */
        i_len = 4;
    }
    else
    {
        buffer[1] = 1;                  /* Number of methods */
        buffer[2] = 0x00;               /* - No auth required */
        i_len = 3;
    }
    
    if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
        return VLC_EGENERIC;
    if( net_Read( p_obj, fd, NULL, buffer, 2, VLC_TRUE ) != 2 )
        return VLC_EGENERIC;

    msg_Dbg( p_obj, "socks: v=%d method=%x", buffer[0], buffer[1] );

    if( buffer[1] == 0x00 )
    {
        msg_Dbg( p_obj, "socks: no authentication required" );
    }
    else if( buffer[1] == 0x02 )
    {
        int i_len1 = __MIN( strlen(psz_socks_user), 255 );
        int i_len2 = __MIN( strlen(psz_socks_passwd), 255 );
        msg_Dbg( p_obj, "socks: username/password authentication" );

        /* XXX: we don't support user/pwd > 255 (truncated)*/
        buffer[0] = i_socks_version;        /* Version */
        buffer[1] = i_len1;                 /* User length */
        memcpy( &buffer[2], psz_socks_user, i_len1 );
        buffer[2+i_len1] = i_len2;          /* Password length */
        memcpy( &buffer[2+i_len1+1], psz_socks_passwd, i_len2 );

        i_len = 3 + i_len1 + i_len2;

        if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
            return VLC_EGENERIC;

        if( net_Read( p_obj, fd, NULL, buffer, 2, VLC_TRUE ) != 2 )
            return VLC_EGENERIC;

        msg_Dbg( p_obj, "socks: v=%d status=%x", buffer[0], buffer[1] );
        if( buffer[1] != 0x00 )
        {
            msg_Err( p_obj, "socks: authentication rejected" );
            return VLC_EGENERIC;
        }
    }
    else
    {
        if( b_auth )
            msg_Err( p_obj, "socks: unsupported authentication method %x",
                     buffer[0] );
        else
            msg_Err( p_obj, "socks: authentification needed" );
        return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}

/*****************************************************************************
 * SocksHandshakeTCP:
 *****************************************************************************
 * Open a TCP connection using a SOCKS server and return a handle (RFC 1928)
 *****************************************************************************/
static int SocksHandshakeTCP( vlc_object_t *p_obj,
                              int fd,
                              int i_socks_version,
                              char *psz_socks_user, char *psz_socks_passwd,
                              const char *psz_host, int i_port )
{
    uint8_t buffer[128+2*256];

    if( i_socks_version != 4 && i_socks_version != 5 )
    {
        msg_Warn( p_obj, "invalid socks protocol version %d", i_socks_version );
        i_socks_version = 5;
    }

    if( i_socks_version == 5 && 
        SocksNegociate( p_obj, fd, i_socks_version,
                        psz_socks_user, psz_socks_passwd ) )
        return VLC_EGENERIC;

    if( i_socks_version == 4 )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1098
        struct addrinfo hints = { 0 }, *p_res;
1099
1100

        /* v4 only support ipv4 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1101
1102
        hints.ai_family = PF_INET;
        if( vlc_getaddrinfo( p_obj, psz_host, NULL, &hints, &p_res ) )
1103
1104
1105
1106
1107
            return VLC_EGENERIC;

        buffer[0] = i_socks_version;
        buffer[1] = 0x01;               /* CONNECT */
        SetWBE( &buffer[2], i_port );   /* Port */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1108
1109
1110
1111
        memcpy( &buffer[4],             /* Address */
                &((struct sockaddr_in *)(p_res->ai_addr))->sin_addr, 4 );
        vlc_freeaddrinfo( p_res );

1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
        buffer[8] = 0;                  /* Empty user id */

        if( net_Write( p_obj, fd, NULL, buffer, 9 ) != 9 )
            return VLC_EGENERIC;
        if( net_Read( p_obj, fd, NULL, buffer, 8, VLC_TRUE ) != 8 )
            return VLC_EGENERIC;

        msg_Dbg( p_obj, "socks: v=%d cd=%d",
                 buffer[0], buffer[1] );

        if( buffer[1] != 90 )
            return VLC_EGENERIC;
    }
    else if( i_socks_version == 5 )
    {
        int i_hlen = __MIN(strlen( psz_host ), 255);
        int i_len;

        buffer[0] = i_socks_version;    /* Version */
        buffer[1] = 0x01;               /* Cmd: connect */
        buffer[2] = 0x00;               /* Reserved */
        buffer[3] = 3;                  /* ATYP: for now domainname */

        buffer[4] = i_hlen;
        memcpy( &buffer[5], psz_host, i_hlen );
        SetWBE( &buffer[5+i_hlen], i_port );

        i_len = 5 + i_hlen + 2;


        if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
            return VLC_EGENERIC;

        /* Read the header */
        if( net_Read( p_obj, fd, NULL, buffer, 5, VLC_TRUE ) != 5 )
            return VLC_EGENERIC;

        msg_Dbg( p_obj, "socks: v=%d rep=%d atyp=%d",
                 buffer[0], buffer[1], buffer[3] );

        if( buffer[1] != 0x00 )
        {
            msg_Err( p_obj, "socks: CONNECT request failed\n" );
            return VLC_EGENERIC;
        }

        /* Read the remaining bytes */
        if( buffer[3] == 0x01 )
            i_len = 4-1 + 2;
        else if( buffer[3] == 0x03 )
            i_len = buffer[4] + 2;
        else if( buffer[3] == 0x04 )
            i_len = 16-1+2;
        else 
            return VLC_EGENERIC;

        if( net_Read( p_obj, fd, NULL, buffer, i_len, VLC_TRUE ) != i_len )
            return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}