net.c 35.2 KB
Newer Older
Laurent Aimar's avatar
   
Laurent Aimar committed
1
2
3
/*****************************************************************************
 * net.c:
 *****************************************************************************
4
 * Copyright (C) 2004-2005 the VideoLAN team
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
45
#   include <io.h>
Laurent Aimar's avatar
   
Laurent Aimar committed
46
47
48
49
#   include <winsock2.h>
#   include <ws2tcpip.h>
#else
#   include <sys/socket.h>
50
#   include <netinet/in.h>
Eric Petit's avatar
Eric Petit committed
51
52
53
#   ifdef HAVE_ARPA_INET_H
#       include <arpa/inet.h>
#   endif
54
#   include <netdb.h>
Laurent Aimar's avatar
   
Laurent Aimar committed
55
56
#endif

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

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

63
64
65
66
67
68
69
70
71
72
73
74
75
76
#ifndef INADDR_ANY
#   define INADDR_ANY  0x00000000
#endif
#ifndef INADDR_NONE
#   define INADDR_NONE 0xFFFFFFFF
#endif

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 );

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
static int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
                       int i_protocol )
{
    int fd, i_val;

    fd = socket( i_family, i_socktype, i_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
        return -1;
    }

        /* 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 ) );

#ifdef IPV6_V6ONLY
    /*
     * Accepts only IPv6 connections on IPv6 sockets
     * (and open an IPv4 socket later as well if needed).
     * Only Linux and FreeBSD can map IPv4 connections on IPv6 sockets,
     * so this allows for more uniform handling across platforms. Besides,
     * it makes sure that IPv4 addresses will be printed as w.x.y.z rather
     * than ::ffff:w.x.y.z
     */
    if( i_family == AF_INET6 )
        setsockopt( fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&i_val,
                    sizeof( i_val ) );
#endif

#if defined( WIN32 ) || defined( UNDER_CE )
# ifdef IPV6_PROTECTION_LEVEL
    if( i_family == AF_INET6 )
    {
        i_val = PROTECTION_LEVEL_UNRESTRICTED;
132
133
        setsockopt( fd, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL,
                   (const char*)&i_val, sizeof( i_val ) );
134
135
136
137
138
139
140
141
    }
# else
# warning You are using outdated headers for Winsock !
# endif
#endif
    return fd;
}

Laurent Aimar's avatar
   
Laurent Aimar committed
142
143
144
145
146
/*****************************************************************************
 * __net_OpenTCP:
 *****************************************************************************
 * Open a TCP connection and return a handle
 *****************************************************************************/
hartman's avatar
   
hartman committed
147
int __net_OpenTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
Laurent Aimar's avatar
   
Laurent Aimar committed
148
{
149
150
    struct addrinfo hints, *res, *ptr;
    const char      *psz_realhost;
151
152
    char            *psz_socks;
    int             i_realport, i_val, i_handle = -1;
Laurent Aimar's avatar
   
Laurent Aimar committed
153

154
155
    if( i_port == 0 )
        i_port = 80; /* historical VLC thing */
Laurent Aimar's avatar
   
Laurent Aimar committed
156

157
158
    memset( &hints, 0, sizeof( hints ) );
    hints.ai_socktype = SOCK_STREAM;
Laurent Aimar's avatar
   
Laurent Aimar committed
159

160
161
    psz_socks = var_CreateGetString( p_this, "socks" );
    if( *psz_socks && *psz_socks != ':' )
Laurent Aimar's avatar
   
Laurent Aimar committed
162
    {
163
        char *psz = strchr( psz_socks, ':' );
164
165
166
167

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

168
        psz_realhost = psz_socks;
169
        i_realport = ( psz != NULL ) ? atoi( psz ) : 1080;
170

171
172
        msg_Dbg( p_this, "net: connecting to '%s:%d' for '%s:%d'",
                 psz_realhost, i_realport, psz_host, i_port );
173
174
175
    }
    else
    {
176
        psz_realhost = psz_host;
177
        i_realport = i_port;
178

179
180
        msg_Dbg( p_this, "net: connecting to '%s:%d'", psz_realhost,
                 i_realport );
181
    }
182

183
    i_val = vlc_getaddrinfo( p_this, psz_realhost, i_realport, &hints, &res );
184
    if( i_val )
Laurent Aimar's avatar
   
Laurent Aimar committed
185
    {
186
187
        msg_Err( p_this, "cannot resolve '%s:%d' : %s", psz_realhost,
                 i_realport, vlc_gai_strerror( i_val ) );
188
        free( psz_socks );
Laurent Aimar's avatar
   
Laurent Aimar committed
189
190
191
        return -1;
    }

192
193
194
195
    for( ptr = res; (ptr != NULL) && (i_handle == -1); ptr = ptr->ai_next )
    {
        int fd;

196
197
        fd = net_Socket( p_this, ptr->ai_family, ptr->ai_socktype,
                         ptr->ai_protocol );
198
199
200
201
202
        if( fd == -1 )
            continue;

        if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) )
        {
203
            socklen_t i_val_size = sizeof( i_val );
204
            div_t d;
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
            struct timeval tv;
            vlc_value_t timeout;
#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 );
228
229
230
231
232
233
            if( timeout.i_int < 0 )
            {
                msg_Err( p_this, "invalid negative value for ipv4-timeout" );
                timeout.i_int = 0;
            }
            d = div( timeout.i_int, 100 );
234
235
236
237

            msg_Dbg( p_this, "connection in progress" );
            do
            {
238
239
                fd_set fds;

240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
                if( p_this->b_die )
                {
                    msg_Dbg( p_this, "connection aborted" );
                    net_Close( fd );
                    vlc_freeaddrinfo( res );
                    free( psz_socks );
                    return -1;
                }

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

                /* We'll wait 0.1 second if nothing happens */
                tv.tv_sec = 0;
255
                tv.tv_usec = (d.quot > 0) ? 100000 : (1000 * d.rem);
256
257

                i_val = select( fd + 1, NULL, &fds, NULL, &tv );
258
259
260
261
262
263
264
265
266
267

                if( d.quot <= 0 )
                {
                    msg_Dbg( p_this, "connection timed out" );
                    net_Close( fd );
                    fd = -1;
                    break;
                }

                d.quot--;
268
269
270
271
272
273
274
275
276
            }
            while( ( i_val == 0 ) || ( ( i_val < 0 ) &&
#if defined( WIN32 ) || defined( UNDER_CE )
                            ( WSAGetLastError() == WSAEWOULDBLOCK )
#else
                            ( errno == EINTR )
#endif
                     ) );

277
278
279
            if( fd == -1 )
                continue; /* timeout */

280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
            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 != ':' )
309
310
311
312
    {
        char *psz_user = var_CreateGetString( p_this, "socks-user" );
        char *psz_pwd  = var_CreateGetString( p_this, "socks-pwd" );

313
        if( SocksHandshakeTCP( p_this, i_handle, 5, psz_user, psz_pwd,
314
315
316
                               psz_host, i_port ) )
        {
            msg_Err( p_this, "failed to use the SOCKS server" );
317
318
            net_Close( i_handle );
            i_handle = -1;
319
320
321
322
323
        }

        free( psz_user );
        free( psz_pwd );
    }
324
    free( psz_socks );
325

326
    return i_handle;
Laurent Aimar's avatar
   
Laurent Aimar committed
327
328
}

329

330
331
332
/*****************************************************************************
 * __net_ListenTCP:
 *****************************************************************************
333
334
 * Open TCP passive "listening" socket(s)
 * This function returns NULL in case of error.
335
 *****************************************************************************/
336
int *__net_ListenTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
337
{
338
339
    struct addrinfo hints, *res, *ptr;
    int             i_val, *pi_handles, i_size;
340

341
342
343
    memset( &hints, 0, sizeof( hints ) );
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
344

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

347
    i_val = vlc_getaddrinfo( p_this, psz_host, i_port, &hints, &res );
348
    if( i_val )
349
    {
350
        msg_Err( p_this, "cannot resolve '%s:%d' : %s", psz_host, i_port,
351
352
                 vlc_gai_strerror( i_val ) );
        return NULL;
353
354
    }

355
356
    pi_handles = NULL;
    i_size = 1;
357

358
    for( ptr = res; ptr != NULL; ptr = ptr->ai_next )
359
    {
360
361
        int fd, *newpi;

362
363
        fd = net_Socket( p_this, ptr->ai_family, ptr->ai_socktype,
                         ptr->ai_protocol );
364
365
        if( fd == -1 )
            continue;
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
        /* 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;
            pi_handles = newpi;
        }
404
    }
405
    
Eric Petit's avatar
Eric Petit committed
406
    vlc_freeaddrinfo( res );
407

408
409
    if( pi_handles != NULL )
        pi_handles[i_size - 1] = -1;
410
    return pi_handles;
411
412
}

413
414
415
/*****************************************************************************
 * __net_Accept:
 *****************************************************************************
416
 * Accept a connection on a set of listening sockets and return it
417
 *****************************************************************************/
418
int __net_Accept( vlc_object_t *p_this, int *pi_fd, mtime_t i_wait )
419
420
421
422
423
{
    vlc_bool_t b_die = p_this->b_die, b_block = (i_wait < 0);

    while( p_this->b_die == b_die )
    {
424
425
426
427
428
429
        int i_val = -1, *pi, *pi_end;
        struct timeval timeout;
        fd_set fds_r, fds_e;

        pi = pi_fd;

430
431
432
        /* Initialize file descriptor set */
        FD_ZERO( &fds_r );
        FD_ZERO( &fds_e );
433
434
435
436
437
438
439
440
441
442
443
444

        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;
445
446
447
448

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

449
450
        i_val = select( i_val + 1, &fds_r, NULL, &fds_e, &timeout );
        if( ( ( i_val < 0 ) && ( errno == EINTR ) ) || i_val == 0 )
451
        {
452
453
454
455
            if( b_block )
                continue;
            else
                return -1;
456
        }
457
        else if( i_val < 0 )
458
        {
gbazin's avatar
gbazin committed
459
#if defined(WIN32) || defined(UNDER_CE)
460
            msg_Err( p_this, "network select error (%i)", WSAGetLastError() );
461
#else
462
            msg_Err( p_this, "network select error (%s)", strerror( errno ) );
463
464
465
466
#endif
            return -1;
        }

467
        for( pi = pi_fd; *pi != -1; pi++ )
468
        {
469
470
471
472
473
474
475
476
            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
477
#if defined(WIN32) || defined(UNDER_CE)
478
                msg_Err( p_this, "accept failed (%i)", WSAGetLastError() );
479
#else
480
                msg_Err( p_this, "accept failed (%s)", strerror( errno ) );
481
#endif
482
483
484
485
486
487
488
489
490
491
492
493
            }
            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;
            }
494
495
496
497
498
499
        }
    }

    return -1;
}

Laurent Aimar's avatar
Laurent Aimar committed
500
501
502
503
504
/*****************************************************************************
 * __net_OpenUDP:
 *****************************************************************************
 * Open a UDP connection and return a handle
 *****************************************************************************/
gbazin's avatar
gbazin committed
505
506
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
507
508
509
510
511
512
513
514
515
516
517
518
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
{
    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
544
    if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
Laurent Aimar's avatar
Laurent Aimar committed
545
546
547
548
549
550
551
552
553
554
555
    {
        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
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
/*****************************************************************************
 * __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
}

572
573
574
575
576
577
578
579
580
581
582
583
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
584
585
586
587
/*****************************************************************************
 * __net_Read:
 *****************************************************************************
 * Read from a network socket
588
 * If b_retry is true, then we repeat until we have read the right amount of
Laurent Aimar's avatar
   
Laurent Aimar committed
589
590
 * data
 *****************************************************************************/
591
592
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
593
594
{
    struct timeval  timeout;
595
    fd_set          fds_r, fds_e;
Laurent Aimar's avatar
   
Laurent Aimar committed
596
597
598
    int             i_recv;
    int             i_total = 0;
    int             i_ret;
599
    vlc_bool_t      b_die = p_this->b_die;
Laurent Aimar's avatar
   
Laurent Aimar committed
600
601
602
603
604

    while( i_data > 0 )
    {
        do
        {
605
            if( p_this->b_die != b_die )
Laurent Aimar's avatar
   
Laurent Aimar committed
606
607
608
609
610
            {
                return 0;
            }

            /* Initialize file descriptor set */
611
612
613
614
            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
615
616
617
618

            /* We'll wait 0.5 second if nothing happens */
            timeout.tv_sec = 0;
            timeout.tv_usec = 500000;
619
620
621

        } 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
622
623
624

        if( i_ret < 0 )
        {
gbazin's avatar
gbazin committed
625
#if defined(WIN32) || defined(UNDER_CE)
626
            msg_Err( p_this, "network select error (%d)", WSAGetLastError() );
gbazin's avatar
gbazin committed
627
#else
Laurent Aimar's avatar
   
Laurent Aimar committed
628
            msg_Err( p_this, "network select error (%s)", strerror(errno) );
gbazin's avatar
gbazin committed
629
#endif
Laurent Aimar's avatar
   
Laurent Aimar committed
630
631
632
            return i_total > 0 ? i_total : -1;
        }

633
634
635
        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
636
        {
gbazin's avatar
gbazin committed
637
#if defined(WIN32) || defined(UNDER_CE)
638
639
640
641
642
            if( WSAGetLastError() == WSAEWOULDBLOCK )
            {
                /* only happens with p_vs (SSL) - not really an error */
            }
            else
Laurent Aimar's avatar
Laurent Aimar committed
643
644
645
646
            /* 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
647
            if( WSAGetLastError() == WSAEMSGSIZE )
Laurent Aimar's avatar
Laurent Aimar committed
648
            {
gbazin's avatar
gbazin committed
649
                msg_Err( p_this, "recv() failed. "
Laurent Aimar's avatar
Laurent Aimar committed
650
                         "Increase the mtu size (--mtu option)" );
651
                i_total += i_data;
Laurent Aimar's avatar
Laurent Aimar committed
652
            }
653
654
            else if( WSAGetLastError() == WSAEINTR ) continue;
            else msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
655
#else
656
657
658
            /* 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) );
659
#endif
Laurent Aimar's avatar
   
Laurent Aimar committed
660
661
            return i_total > 0 ? i_total : -1;
        }
662
663
664
665
666
        else if( i_recv == 0 )
        {
            /* Connection closed */
            b_retry = VLC_FALSE;
        }
Laurent Aimar's avatar
   
Laurent Aimar committed
667
668
669
670
671
672
673
674
675
676
677
678

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

zorglub's avatar
zorglub committed
679
680
681
682
683
/*****************************************************************************
 * __net_ReadNonBlock:
 *****************************************************************************
 * Read from a network socket, non blocking mode (with timeout)
 *****************************************************************************/
684
685
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
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
{
    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
709
#if defined(WIN32) || defined(UNDER_CE)
710
        msg_Err( p_this, "network select error (%d)", WSAGetLastError() );
gbazin's avatar
gbazin committed
711
#else
zorglub's avatar
zorglub committed
712
        msg_Err( p_this, "network select error (%s)", strerror(errno) );
gbazin's avatar
gbazin committed
713
#endif
zorglub's avatar
zorglub committed
714
715
716
717
718
719
720
721
        return -1;
    }
    else if( i_ret == 0)
    {
        return 0;
    }
    else
    {
gbazin's avatar
gbazin committed
722
723
724
#if !defined(UNDER_CE)
        if( fd == 0/*STDIN_FILENO*/ ) i_recv = read( fd, p_data, i_data ); else
#endif
725
726
727
        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
728
        {
gbazin's avatar
gbazin committed
729
#if defined(WIN32) || defined(UNDER_CE)
zorglub's avatar
zorglub committed
730
731
732
733
734
735
736
737
738
            /* 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)" );
            }
739
            else msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
zorglub's avatar
zorglub committed
740
741
742
743
744
#else
            msg_Err( p_this, "recv failed (%s)", strerror(errno) );
#endif
            return -1;
        }
745
746

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

zorglub's avatar
zorglub committed
749
750
751
752
    /* We will never be here */
    return -1;
}

zorglub's avatar
zorglub committed
753
754
755
/*****************************************************************************
 * __net_Select:
 *****************************************************************************
756
757
 * Read from several sockets (with timeout). Takes data from the first socket
 * that has some.
zorglub's avatar
zorglub committed
758
 *****************************************************************************/
759
760
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
761
762
763
764
765
766
767
768
769
770
771
772
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
{
    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 ) )
            {
804
805
806
                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
807
808
809
810
811
812
813
814
815
816
817
818
                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)" );
                    }
819
820
                    else msg_Err( p_this, "recv failed (%i)",
                                  WSAGetLastError() );
zorglub's avatar
zorglub committed
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
#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
837
/* Write exact amount requested */
838
839
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
840
841
{
    struct timeval  timeout;
842
    fd_set          fds_w, fds_e;
Laurent Aimar's avatar
   
Laurent Aimar committed
843
844
845
846
    int             i_send;
    int             i_total = 0;
    int             i_ret;

847
848
    vlc_bool_t      b_die = p_this->b_die;

Laurent Aimar's avatar
   
Laurent Aimar committed
849
850
851
852
    while( i_data > 0 )
    {
        do
        {
853
            if( p_this->b_die != b_die )
Laurent Aimar's avatar
   
Laurent Aimar committed
854
855
856
857
858
            {
                return 0;
            }

            /* Initialize file descriptor set */
859
860
861
862
            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
863
864
865
866

            /* We'll wait 0.5 second if nothing happens */
            timeout.tv_sec = 0;
            timeout.tv_usec = 500000;
867
868
869

        } 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
870
871
872

        if( i_ret < 0 )
        {
gbazin's avatar
gbazin committed
873
#if defined(WIN32) || defined(UNDER_CE)
874
            msg_Err( p_this, "network select error (%d)", WSAGetLastError() );
gbazin's avatar
gbazin committed
875
#else
Laurent Aimar's avatar
   
Laurent Aimar committed
876
            msg_Err( p_this, "network select error (%s)", strerror(errno) );
gbazin's avatar
gbazin committed
877
#endif
Laurent Aimar's avatar
   
Laurent Aimar committed
878
879
880
            return i_total > 0 ? i_total : -1;
        }

881
882
883
        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
884
        {
885
886
887
            /* 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
888
889
890
891
892
893
894
895
896
897
            return i_total > 0 ? i_total : -1;
        }

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

898
char *__net_Gets( vlc_object_t *p_this, int fd, v_socket_t *p_vs )
Laurent Aimar's avatar
   
Laurent Aimar committed
899
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
900
901
    char *psz_line = NULL, *ptr = NULL;
    size_t  i_line = 0, i_max = 0;
Laurent Aimar's avatar
   
Laurent Aimar committed
902
903
904
905


    for( ;; )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
906
        if( i_line == i_max )
Laurent Aimar's avatar
   
Laurent Aimar committed
907
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
908
909
910
            i_max += 1024;
            psz_line = realloc( psz_line, i_max );
            ptr = psz_line + i_line;
Laurent Aimar's avatar
   
Laurent Aimar committed
911
        }
912

913
        if( net_Read( p_this, fd, p_vs, (uint8_t *)ptr, 1, VLC_TRUE ) != 1 )
Laurent Aimar's avatar
   
Laurent Aimar committed
914
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
915
916
917
918
919
            if( i_line == 0 )
            {
                free( psz_line );
                return NULL;
            }
Laurent Aimar's avatar
   
Laurent Aimar committed
920
921
922
            break;
        }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
923
924
        if ( *ptr == '\n' )
            break;
Laurent Aimar's avatar
   
Laurent Aimar committed
925

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
926
927
        i_line++;
        ptr++;
Laurent Aimar's avatar
   
Laurent Aimar committed
928
929
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
930
931
    *ptr-- = '\0';

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
932
    if( ( ptr >= psz_line ) && ( *ptr == '\r' ) )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
933
934
        *ptr = '\0';

Laurent Aimar's avatar
   
Laurent Aimar committed
935
936
937
    return psz_line;
}

938
939
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
940
{
941
    int i_ret;
Laurent Aimar's avatar
   
Laurent Aimar committed
942
    va_list args;
943
    va_start( args, psz_fmt );
944
    i_ret = net_vaPrintf( p_this, fd, p_vs, psz_fmt, args );
945
946
947
948
949
    va_end( args );

    return i_ret;
}

950
951
int __net_vaPrintf( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
                    const char *psz_fmt, va_list args )
952
{
Laurent Aimar's avatar
   
Laurent Aimar committed
953
    char    *psz;
954
    int     i_size, i_ret;
Laurent Aimar's avatar
   
Laurent Aimar committed
955

956
    i_size = vasprintf( &psz, psz_fmt, args );
957
958
    i_ret = __net_Write( p_this, fd, p_vs, (uint8_t *)psz, i_size ) < i_size
        ? -1 : i_size;
959
960
961
    free( psz );

    return i_ret;
Laurent Aimar's avatar
   
Laurent Aimar committed
962
}
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
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



/*****************************************************************************
 * 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
1082
        struct addrinfo hints = { 0 }, *p_res;
1083
1084

        /* v4 only support ipv4 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1085
        hints.ai_family = AF_INET;
1086
        if( vlc_getaddrinfo( p_obj, psz_host, 0, &hints, &p_res ) )
1087
1088
1089
1090
1091
            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
1092
1093
1094
1095
        memcpy( &buffer[4],             /* Address */
                &((struct sockaddr_in *)(p_res->ai_addr))->sin_addr, 4 );
        vlc_freeaddrinfo( p_res );

1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
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
        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;
}
1158

1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
int __net_GetAddress( vlc_object_t *p_this, vlc_bool_t peer, int fd,
                      char *address, int *port )
{
    struct sockaddr_storage addr;
    socklen_t addrlen = sizeof( addr );
    int val;

    val = peer ? getpeername( fd, (struct sockaddr *)&addr, &addrlen )
               : getsockname( fd, (struct sockaddr *)&addr, &addrlen );

    if (val)
    {
#if defined(WIN32) || defined (UNDER_CE)
        msg_Err( p_this, "socket address error : %d", WSAGetLastError( ) );
#else
        msg_Err( p_this, "socket address error : %s", strerror( errno ) );
#endif
        return val;
    }

    val = vlc_getnameinfo( (struct sockaddr *)&addr, addrlen,
                           address, NI_MAXNUMERICHOST, port, NI_NUMERICHOST );
    if( val )
    {
        msg_Err( p_this, "socket address error : %s",
                 vlc_gai_strerror( val ) );
    }
    return 0;
}