tcp.c 16.3 KB
Newer Older
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1
2
3
4
/*****************************************************************************
 * tcp.c:
 *****************************************************************************
 * Copyright (C) 2004-2005 the VideoLAN team
5
 * Copyright (C) 2005-2006 Rémi Denis-Courmont
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 * $Id$
 *
 * Authors: Laurent Aimar <fenrir@videolan.org>
 *          Rémi Denis-Courmont <rem # videolan.org>
 *
 * 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
dionoea's avatar
dionoea committed
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
24
25
26
27
28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
29
30
31
32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33
#include <vlc_common.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
34
35

#include <errno.h>
36
#include <assert.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
37
38
39
40
41
42
43
44
45
46

#ifdef HAVE_FCNTL_H
#   include <fcntl.h>
#endif
#ifdef HAVE_SYS_TIME_H
#    include <sys/time.h>
#endif
#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#endif
47
48
49
#ifdef HAVE_POLL
# include <poll.h>
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
50

zorglub's avatar
zorglub committed
51
#include <vlc_network.h>
52
53
54
55
56
57
58
59
#if defined (WIN32) || defined (UNDER_CE)
#   undef EINPROGRESS
#   define EINPROGRESS WSAEWOULDBLOCK
#   undef EINTR
#   define EINTR WSAEINTR
#   undef ETIMEDOUT
#   define ETIMEDOUT WSAETIMEDOUT
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
60

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
61
static int SocksNegotiate( vlc_object_t *, int fd, int i_socks_version,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
62
                           const char *psz_user, const char *psz_passwd );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
63
64
static int SocksHandshakeTCP( vlc_object_t *,
                              int fd, int i_socks_version,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
65
                              const char *psz_user, const char *psz_passwd,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
66
67
68
69
70
                              const char *psz_host, int i_port );
extern int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
                       int i_protocol );

/*****************************************************************************
71
 * __net_Connect:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
72
 *****************************************************************************
73
74
 * Open a network connection.
 * @return socket handler or -1 on error.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
75
 *****************************************************************************/
76
77
int __net_Connect( vlc_object_t *p_this, const char *psz_host, int i_port,
                   int type, int proto )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
78
79
80
81
{
    struct addrinfo hints, *res, *ptr;
    const char      *psz_realhost;
    char            *psz_socks;
82
    int             i_realport, i_val, i_handle = -1;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
83

84
85
86
87
    int evfd = vlc_object_waitpipe (p_this);
    if (evfd == -1)
        return -1;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
88
89
90
    memset( &hints, 0, sizeof( hints ) );
    hints.ai_socktype = SOCK_STREAM;

91
92
    psz_socks = var_CreateGetNonEmptyString( p_this, "socks" );
    if( psz_socks != NULL )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
93
94
95
96
97
98
99
100
    {
        char *psz = strchr( psz_socks, ':' );

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

        psz_realhost = psz_socks;
        i_realport = ( psz != NULL ) ? atoi( psz ) : 1080;
101
        hints.ai_flags &= ~AI_NUMERICHOST;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
102

103
104
105
        msg_Dbg( p_this, "net: connecting to %s port %d (SOCKS) "
                 "for %s port %d", psz_realhost, i_realport,
                 psz_host, i_port );
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

        /* We only implement TCP with SOCKS */
        switch( type )
        {
            case 0:
                type = SOCK_STREAM;
            case SOCK_STREAM:
                break;
            default:
                msg_Err( p_this, "Socket type not supported through SOCKS" );
                free( psz_socks );
                return -1;
        }
        switch( proto )
        {
            case 0:
                proto = IPPROTO_TCP;
            case IPPROTO_TCP:
                break;
            default:
                msg_Err( p_this, "Transport not supported through SOCKS" );
                free( psz_socks );
                return -1;
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
130
131
132
133
134
135
136
137
138
139
140
    }
    else
    {
        psz_realhost = psz_host;
        i_realport = i_port;

        msg_Dbg( p_this, "net: connecting to %s port %d", psz_realhost,
                 i_realport );
    }

    i_val = vlc_getaddrinfo( p_this, psz_realhost, i_realport, &hints, &res );
141
142
    free( psz_socks );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
143
144
145
146
147
148
149
    if( i_val )
    {
        msg_Err( p_this, "cannot resolve %s port %d : %s", psz_realhost,
                 i_realport, vlc_gai_strerror( i_val ) );
        return -1;
    }

150
    for( ptr = res; ptr != NULL; ptr = ptr->ai_next )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
151
    {
152
153
        int fd = net_Socket( p_this, ptr->ai_family, type ?: ptr->ai_socktype,
                             proto ?: ptr->ai_protocol );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
154
        if( fd == -1 )
155
        {
156
            msg_Dbg( p_this, "socket error: %m" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
157
            continue;
158
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
159
160
161

        if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) )
        {
162
            int timeout, val;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
163

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
164
            if( net_errno != EINPROGRESS && net_errno != EINTR )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
165
            {
166
                msg_Err( p_this, "connection failed: %m" );
167
                goto next_ai;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
168
            }
169
            msg_Dbg( p_this, "connection: %m" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
170

171
172
            timeout = var_CreateGetInteger (p_this, "ipv4-timeout");
            if (timeout < 0)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
173
174
            {
                msg_Err( p_this, "invalid negative value for ipv4-timeout" );
175
                timeout = 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
176
177
            }

178
179
180
181
182
183
184
185
186
187
188
            struct pollfd ufd[2] = {
                { .fd = fd,   .events = POLLOUT },
                { .fd = evfd, .events = POLLIN },
            };

            do
                /* NOTE: timeout screwed up if we catch a signal (EINTR) */
                val = poll (ufd, sizeof (ufd) / sizeof (ufd[0]), timeout);
            while ((val == -1) && (net_errno == EINTR));

            switch (val)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
189
            {
190
191
192
193
194
195
196
197
198
199
200
                 case -1: /* error */
                     msg_Err (p_this, "connection polling error: %m");
                     goto next_ai;

                 case 0: /* timeout */
                     msg_Warn (p_this, "connection timed out");
                     goto next_ai;

                 default: /* something happended */
                     if (ufd[1].revents)
                         goto next_ai; /* LibVLC object killed */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
201
202
            }

203
204
205
206
            /* There is NO WAY around checking SO_ERROR.
             * Don't ifdef it out!!! */
            if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &val,
                            &(socklen_t){ sizeof (val) }) || val)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
207
            {
208
209
                errno = val;
                msg_Err (p_this, "connection failed: %m");
210
                goto next_ai;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
211
212
            }
        }
213

214
        msg_Dbg( p_this, "connection succeeded (socket = %d)", fd );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
215
        i_handle = fd; /* success! */
216
217
218
219
220
        break;

next_ai: /* failure */
        net_Close( fd );
        continue;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
221
222
223
224
225
226
227
    }

    vlc_freeaddrinfo( res );

    if( i_handle == -1 )
        return -1;

228
    if( psz_socks != NULL )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
229
    {
230
        /* NOTE: psz_socks already free'd! */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
231
232
        char *psz_user = var_CreateGetNonEmptyString( p_this, "socks-user" );
        char *psz_pwd  = var_CreateGetNonEmptyString( p_this, "socks-pwd" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
233
234
235
236

        if( SocksHandshakeTCP( p_this, i_handle, 5, psz_user, psz_pwd,
                               psz_host, i_port ) )
        {
237
            msg_Err( p_this, "SOCKS handshake failed" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
238
239
240
241
242
243
244
245
246
247
248
249
            net_Close( i_handle );
            i_handle = -1;
        }

        free( psz_user );
        free( psz_pwd );
    }

    return i_handle;
}


250
251
int net_AcceptSingle (vlc_object_t *obj, int lfd)
{
252
253
254
255
256
    int fd;
    do
        fd = accept (lfd, NULL, NULL);
    while (fd == -1 && errno == EINTR);

257
258
259
260
261
262
263
264
265
    if (fd == -1)
    {
        if (net_errno != EAGAIN)
            msg_Err (obj, "accept failed (from socket %d): %m", lfd);
        return -1;
    }

    msg_Dbg (obj, "accepted socket %d (from socket %d)", fd, lfd);
    net_SetupSocket (fd);
266
    return fd;
267
268
269
}


Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
270
271
272
273
274
/*****************************************************************************
 * __net_Accept:
 *****************************************************************************
 * Accept a connection on a set of listening sockets and return it
 *****************************************************************************/
275
int __net_Accept( vlc_object_t *p_this, int *pi_fd, mtime_t i_wait )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
276
{
277
    int timeout = (i_wait < 0) ? -1 : i_wait / 1000;
278
    int evfd = vlc_object_waitpipe (p_this);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
279

280
281
    if (evfd == -1)
        return -1;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
282

283
    assert( pi_fd != NULL );
284

285
    for (;;)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
286
    {
287
288
289
        unsigned n = 0;
        while (pi_fd[n] != -1)
            n++;
290
        struct pollfd ufd[n + 1];
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
291
292

        /* Initialize file descriptor set */
293
        for (unsigned i = 0; i <= n; i++)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
294
        {
295
            ufd[i].fd = (i < n) ? pi_fd[i] : evfd;
296
297
            ufd[i].events = POLLIN;
            ufd[i].revents = 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
298
        }
299
300
        if (evfd == -1)
            n--; /* avoid EBADF */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
301

302
        switch (poll (ufd, n, timeout))
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
303
        {
304
            case -1:
305
306
307
308
                if (net_errno == EINTR)
                    continue;
                msg_Err (p_this, "poll error: %m");
                return -1;
309
            case 0:
310
311
                errno = ETIMEDOUT;
                return -1;
312
313
314
315
316
317
        }

        if (ufd[n].revents)
        {
            errno = EINTR;
            break;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
318
319
        }

320
        for (unsigned i = 0; i < n; i++)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
321
        {
322
            if (ufd[i].revents == 0)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
323
324
                continue;

325
            int sfd = ufd[i].fd;
326
            int fd = net_AcceptSingle (p_this, sfd);
327
            if (fd == -1)
328
329
330
                continue;

            /*
331
332
             * Move listening socket to the end to let the others in the
             * set a chance next time.
333
             */
334
335
            memmove (pi_fd + i, pi_fd + i + 1, n - (i + 1));
            pi_fd[n - 1] = sfd;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
336
            return fd;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
337
338
339
340
341
342
343
        }
    }
    return -1;
}


/*****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
344
 * SocksNegotiate:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
345
 *****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
346
 * Negotiate authentication with a SOCKS server.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
347
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
348
static int SocksNegotiate( vlc_object_t *p_obj,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
349
                           int fd, int i_socks_version,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
350
351
                           const char *psz_socks_user,
                           const char *psz_socks_passwd )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
352
353
354
{
    uint8_t buffer[128+2*256];
    int i_len;
355
    bool b_auth = false;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
356
357
358
359

    if( i_socks_version != 5 )
        return VLC_SUCCESS;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
360
    /* We negotiate authentication */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
361

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
362
    if( ( psz_socks_user == NULL ) && ( psz_socks_passwd == NULL ) )
363
        b_auth = true;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381

    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;
382
    if( net_Read( p_obj, fd, NULL, buffer, 2, true ) != 2 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
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
        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;

409
        if( net_Read( p_obj, fd, NULL, buffer, 2, true ) != 2 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
            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,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
440
                              const char *psz_user, const char *psz_passwd,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
441
442
443
444
445
446
447
448
449
450
                              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;
    }

451
    if( i_socks_version == 5 &&
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
452
        SocksNegotiate( p_obj, fd, i_socks_version,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
453
                        psz_user, psz_passwd ) )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
454
455
456
457
        return VLC_EGENERIC;

    if( i_socks_version == 4 )
    {
458
        struct addrinfo hints, *p_res;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
459
460

        /* v4 only support ipv4 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
461
        memset (&hints, 0, sizeof (hints));
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
        hints.ai_family = AF_INET;
        if( vlc_getaddrinfo( p_obj, psz_host, 0, &hints, &p_res ) )
            return VLC_EGENERIC;

        buffer[0] = i_socks_version;
        buffer[1] = 0x01;               /* CONNECT */
        SetWBE( &buffer[2], i_port );   /* Port */
        memcpy( &buffer[4],             /* Address */
                &((struct sockaddr_in *)(p_res->ai_addr))->sin_addr, 4 );
        vlc_freeaddrinfo( p_res );

        buffer[8] = 0;                  /* Empty user id */

        if( net_Write( p_obj, fd, NULL, buffer, 9 ) != 9 )
            return VLC_EGENERIC;
477
        if( net_Read( p_obj, fd, NULL, buffer, 8, true ) != 8 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
            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 */
507
        if( net_Read( p_obj, fd, NULL, buffer, 5, true ) != 5 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
            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;
526
        else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
527
528
            return VLC_EGENERIC;

529
        if( net_Read( p_obj, fd, NULL, buffer, i_len, true ) != i_len )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
            return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}

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