network.h 16.5 KB
Newer Older
Sam Hocevar's avatar
   
Sam Hocevar committed
1
/*****************************************************************************
Christophe Massiot's avatar
Christophe Massiot committed
2
 * network.h: interface to communicate with network plug-ins
Sam Hocevar's avatar
   
Sam Hocevar committed
3
 *****************************************************************************
4
 * Copyright (C) 2002-2005 the VideoLAN team
zorglub's avatar
zorglub committed
5
 * $Id$
Sam Hocevar's avatar
   
Sam Hocevar committed
6
 *
7
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8
 *          Laurent Aimar <fenrir@via.ecp.fr>
9
 *          Rémi Denis-Courmont <rem # videolan.org>
Sam Hocevar's avatar
   
Sam Hocevar committed
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 *
 * 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.
 *****************************************************************************/

26
27
#ifndef __VLC_NETWORK_H
# define __VLC_NETWORK_H
28

29
30
31
32
#if defined( WIN32 )
#   if defined(UNDER_CE) && defined(sockaddr_storage)
#       undef sockaddr_storage
#   endif
damienf's avatar
damienf committed
33
#   include <winsock2.h>
34
#   include <ws2tcpip.h>
35
#else
36
37
38
39
40
41
42
43
44
45
46
#   if HAVE_SYS_SOCKET_H
#      include <sys/socket.h>
#   endif
#   if HAVE_NETINET_IN_H
#      include <netinet/in.h>
#   endif
#   if HAVE_ARPA_INET_H
#      include <arpa/inet.h>
#   elif defined( SYS_BEOS )
#      include <net/netdb.h>
#   endif
47
48
49
#   include <netdb.h>
#endif

50
51
52
# ifdef __cplusplus
extern "C" {
# endif
53

Christophe Massiot's avatar
Christophe Massiot committed
54
55
56
57
/*****************************************************************************
 * network_socket_t: structure passed to a network plug-in to define the
 *                   kind of socket we want
 *****************************************************************************/
58
struct network_socket_t
Christophe Massiot's avatar
Christophe Massiot committed
59
{
60
    const char *psz_bind_addr;
Christophe Massiot's avatar
Christophe Massiot committed
61
62
    int i_bind_port;

63
    const char *psz_server_addr;
Christophe Massiot's avatar
Christophe Massiot committed
64
65
    int i_server_port;

66
67
    int i_ttl;

68
69
    int v6only;

Christophe Massiot's avatar
Christophe Massiot committed
70
71
72
    /* Return values */
    int i_handle;
    size_t i_mtu;
73
};
Christophe Massiot's avatar
Christophe Massiot committed
74

75
76
77
typedef struct
{
    char *psz_protocol;
78
79
    char *psz_username;
    char *psz_password;
80
81
82
83
84
85
    char *psz_host;
    int  i_port;

    char *psz_path;

    char *psz_option;
86
87
    
    char *psz_buffer; /* to be freed */
88
89
90
91
92
93
94
} vlc_url_t;

/*****************************************************************************
 * vlc_UrlParse:
 *****************************************************************************
 * option : if != 0 then path is split at this char
 *
95
 * format [protocol://[login[:password]@]][host[:port]]/path[OPTIONoption]
96
 *****************************************************************************/
97
98
static inline void vlc_UrlParse( vlc_url_t *url, const char *psz_url,
                                 char option )
99
{
100
101
    char *psz_dup;
    char *psz_parse;
102
103
104
    char *p;

    url->psz_protocol = NULL;
105
106
    url->psz_username = NULL;
    url->psz_password = NULL;
107
108
109
110
    url->psz_host     = NULL;
    url->i_port       = 0;
    url->psz_path     = NULL;
    url->psz_option   = NULL;
111
112
113
114
115
116
117
    
    if( psz_url == NULL )
    {
        url->psz_buffer = NULL;
        return;
    }
    url->psz_buffer = psz_parse = psz_dup = strdup( psz_url );
118

119
120
    p  = strstr( psz_parse, ":/" );
    if( p != NULL )
121
122
123
124
125
    {
        /* we have a protocol */

        /* skip :// */
        *p++ = '\0';
126
        if( p[1] == '/' )
127
            p += 2;
128
        url->psz_protocol = psz_parse;
129
        psz_parse = p;
130
131
132
133
134
135
136
    }
    p = strchr( psz_parse, '@' );
    if( p != NULL )
    {
        /* We have a login */
        url->psz_username = psz_parse;
        *p++ = '\0';
137

138
139
140
141
142
143
        psz_parse = strchr( psz_parse, ':' );
        if( psz_parse != NULL )
        {
            /* We have a password */
            *psz_parse++ = '\0';
            url->psz_password = psz_parse;
144
        }
145
146

        psz_parse = p;
147
    }
148

149
150
151
152
    p = strchr( psz_parse, '/' );
    if( !p || psz_parse < p )
    {
        char *p2;
153

154
        /* We have a host[:port] */
155
        url->psz_host = strdup( psz_parse );
156
157
158
        if( p )
        {
            url->psz_host[p - psz_parse] = '\0';
159
160
        }

161
162
163
164
165
166
167
168
169
170
171
172
173
        if( *url->psz_host == '[' )
        {
            /* Ipv6 address */
            p2 = strchr( url->psz_host, ']' );
            if( p2 )
            {
                p2 = strchr( p2, ':' );
            }
        }
        else
        {
            p2 = strchr( url->psz_host, ':' );
        }
174
175
176
177
178
179
        if( p2 )
        {
            *p2++ = '\0';
            url->i_port = atoi( p2 );
        }
    }
180
    psz_parse = p;
181
182
183
184

    /* Now parse psz_path and psz_option */
    if( psz_parse )
    {
185
        url->psz_path = psz_parse;
186
187
188
189
190
191
        if( option != '\0' )
        {
            p = strchr( url->psz_path, option );
            if( p )
            {
                *p++ = '\0';
192
                url->psz_option = p;
193
194
195
196
197
198
199
200
201
202
203
204
            }
        }
    }
}

/*****************************************************************************
 * vlc_UrlClean:
 *****************************************************************************
 *
 *****************************************************************************/
static inline void vlc_UrlClean( vlc_url_t *url )
{
205
    if( url->psz_buffer ) free( url->psz_buffer );
206
    if( url->psz_host )   free( url->psz_host );
207
208

    url->psz_protocol = NULL;
209
210
    url->psz_username = NULL;
    url->psz_password = NULL;
211
212
213
214
    url->psz_host     = NULL;
    url->i_port       = 0;
    url->psz_path     = NULL;
    url->psz_option   = NULL;
215
216

    url->psz_buffer   = NULL;
217
}
218

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
219
220
221
222
static inline int isurlsafe( int c )
{
    return ( (unsigned char)( c - 'a' ) < 26 )
        || ( (unsigned char)( c - 'A' ) < 26 )
223
        || ( (unsigned char)( c - '0' ) < 10 )
224
225
226
227
        /* Hmm, we should not encode character that are allowed in URLs
         * (even if they are not URL-safe), nor URL-safe characters.
         * We still encode some of them because of Microsoft's crap browser.
         */
228
        || ( strchr( "-_.", c ) != NULL );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
229
230
}

231
232
233
234
235
236
237
238
239
/*****************************************************************************
 * vlc_UrlEncode: 
 *****************************************************************************
 * perform URL encoding
 * (you do NOT want to do URL decoding - it is not reversible - do NOT do it)
 *****************************************************************************/
static inline char *vlc_UrlEncode( const char *psz_url )
{
    char *psz_enc, *out;
240
    const unsigned char *in;
241
242
243
244
245
246

    psz_enc = (char *)malloc( 3 * strlen( psz_url ) + 1 );
    if( psz_enc == NULL )
        return NULL;

    out = psz_enc;
247
    for( in = (const unsigned char *)psz_url; *in; in++ )
248
    {
249
        unsigned char c = *in;
250

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
251
        if( isurlsafe( c ) )
252
253
            *out++ = (char)c;
        else
254
        {
255
            *out++ = '%';
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
283
284
285
286
287
288
            *out++ = ( ( c >> 4 ) >= 0xA ) ? 'A' + ( c >> 4 ) - 0xA
                                           : '0' + ( c >> 4 );
            *out++ = ( ( c & 0xf ) >= 0xA ) ? 'A' + ( c & 0xf ) - 0xA
                                           : '0' + ( c & 0xf );
        }
    }
    *out++ = '\0';

    return (char *)realloc( psz_enc, out - psz_enc );
}

/*****************************************************************************
 * vlc_UrlIsNotEncoded:
 *****************************************************************************
 * check if given string is not a valid URL and must hence be encoded
 *****************************************************************************/
#include <ctype.h>

static inline int vlc_UrlIsNotEncoded( const char *psz_url )
{
    const char *ptr;

    for( ptr = psz_url; *ptr; ptr++ )
    {
        char c = *ptr;

        if( c == '%' )
        {
            if( !isxdigit( ptr[1] ) || !isxdigit( ptr[2] ) )
                return 1; /* not encoded */
            ptr += 2;
        }
        else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
289
        if( !isurlsafe( c ) )
290
291
292
293
            return 1;
    }
    return 0; /* looks fine - but maybe it is not encoded */
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
294

295
296
297
298
299
/*****************************************************************************
 * vlc_b64_encode:
 *****************************************************************************
 *
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
300
static inline char *vlc_b64_encode( char *src )
301
302
303
{
    static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
                                                                                
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
304
    char *dst = (char *)malloc( strlen( src ) * 4 / 3 + 12 );
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
    char *ret = dst;
    unsigned i_bits = 0;
    unsigned i_shift = 0;
                                                                                
    for( ;; )
    {
        if( *src )
        {
            i_bits = ( i_bits << 8 )|( *src++ );
            i_shift += 8;
        }
        else if( i_shift > 0 )
        {
           i_bits <<= 6 - i_shift;
           i_shift = 6;
        }
        else
        {
            *dst++ = '=';
            break;
        }
                                                                                
        while( i_shift >= 6 )
        {
            i_shift -= 6;
            *dst++ = b64[(i_bits >> i_shift)&0x3f];
        }
    }
                                                                                
    *dst++ = '\0';
                                                                                
    return ret;
}
338

339
/* Portable networking layer communication */
Laurent Aimar's avatar
   
Laurent Aimar committed
340
#define net_OpenTCP(a, b, c) __net_OpenTCP(VLC_OBJECT(a), b, c)
341
VLC_EXPORT( int, __net_OpenTCP, ( vlc_object_t *p_this, const char *psz_host, int i_port ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
342

343
#define net_ListenTCP(a, b, c) __net_ListenTCP(VLC_OBJECT(a), b, c)
344
VLC_EXPORT( int *, __net_ListenTCP, ( vlc_object_t *, const char *, int ) );
345

346
#define net_Accept(a, b, c) __net_Accept(VLC_OBJECT(a), b, c)
347
VLC_EXPORT( int, __net_Accept, ( vlc_object_t *, int *, mtime_t ) );
348

Laurent Aimar's avatar
Laurent Aimar committed
349
#define net_OpenUDP(a, b, c, d, e ) __net_OpenUDP(VLC_OBJECT(a), b, c, d, e)
350
VLC_EXPORT( int, __net_OpenUDP, ( vlc_object_t *p_this, const char *psz_bind, int i_bind, const char *psz_server, int i_server ) );
Laurent Aimar's avatar
Laurent Aimar committed
351

Laurent Aimar's avatar
   
Laurent Aimar committed
352
VLC_EXPORT( void, net_Close, ( int fd ) );
353
VLC_EXPORT( void, net_ListenClose, ( int *fd ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
354
355


356
357
358
359
360
361
362
363
364
365
/* Functions to read from or write to the networking layer */
struct virtual_socket_t
{
    void *p_sys;
    int (*pf_recv) ( void *, void *, int );
    int (*pf_send) ( void *, const void *, int );
};

#define net_Read(a,b,c,d,e,f) __net_Read(VLC_OBJECT(a),b,c,d,e,f)
VLC_EXPORT( int, __net_Read, ( vlc_object_t *p_this, int fd, v_socket_t *, uint8_t *p_data, int i_data, vlc_bool_t b_retry ) );
zorglub's avatar
zorglub committed
366

367
368
#define net_ReadNonBlock(a,b,c,d,e,f) __net_ReadNonBlock(VLC_OBJECT(a),b,c,d,e,f)
VLC_EXPORT( int, __net_ReadNonBlock, ( vlc_object_t *p_this, int fd, v_socket_t *, uint8_t *p_data, int i_data, mtime_t i_wait ) );
zorglub's avatar
zorglub committed
369

370
371
#define net_Select(a,b,c,d,e,f,g) __net_Select(VLC_OBJECT(a),b,c,d,e,f,g)
VLC_EXPORT( int, __net_Select, ( vlc_object_t *p_this, int *pi_fd, v_socket_t **, int i_fd, uint8_t *p_data, int i_data, mtime_t i_wait ) );
zorglub's avatar
zorglub committed
372

373
#define net_Write(a,b,c,d,e) __net_Write(VLC_OBJECT(a),b,c,d,e)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
374
VLC_EXPORT( int, __net_Write, ( vlc_object_t *p_this, int fd, v_socket_t *, const uint8_t *p_data, int i_data ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
375

376
377
#define net_Gets(a,b,c) __net_Gets(VLC_OBJECT(a),b,c)
VLC_EXPORT( char *, __net_Gets, ( vlc_object_t *p_this, int fd, v_socket_t * ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
378

379
VLC_EXPORT( int, net_Printf, ( vlc_object_t *p_this, int fd, v_socket_t *, const char *psz_fmt, ... ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
380

381
382
#define net_vaPrintf(a,b,c,d,e) __net_vaPrintf(VLC_OBJECT(a),b,c,d,e)
VLC_EXPORT( int, __net_vaPrintf, ( vlc_object_t *p_this, int fd, v_socket_t *, const char *psz_fmt, va_list args ) );
383

384

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
385
386
387
388
389
390
#if !HAVE_INET_PTON
/* only in core, so no need for C++ extern "C" */
int inet_pton(int af, const char *src, void *dst);
#endif


391
392
393
394
395
396
397
/*****************************************************************************
 * net_StopRecv/Send
 *****************************************************************************
 * Wrappers for shutdown()
 *****************************************************************************/
#if defined (SHUT_WR)
/* the standard way */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
398
399
# define net_StopSend( fd ) (void)shutdown( fd, SHUT_WR )
# define net_StopRecv( fd ) (void)shutdown( fd, SHUT_RD )
400
401
#elif defined (SD_SEND)
/* the Microsoft seemingly-purposedly-different-for-the-sake-of-it way */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
402
403
# define net_StopSend( fd ) (void)shutdown( fd, SD_SEND )
# define net_StopRecv( fd ) (void)shutdown( fd, SD_RECEIVE )
404
#else
Eric Petit's avatar
Eric Petit committed
405
406
407
# ifndef SYS_BEOS /* R5 just doesn't have a working shutdown() */
#  warning FIXME: implement shutdown on your platform!
# endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
408
409
# define net_StopSend( fd ) (void)0
# define net_StopRecv( fd ) (void)0
410
#endif
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
440
441
442
443
444
445
446
447
448
449
450
451
452
453
/* Portable network names/addresses resolution layer */

/* GAI error codes */
# ifndef EAI_BADFLAGS
#  define EAI_BADFLAGS -1
# endif
# ifndef EAI_NONAME
#  define EAI_NONAME -2
# endif
# ifndef EAI_AGAIN
#  define EAI_AGAIN -3
# endif
# ifndef EAI_FAIL
#  define EAI_FAIL -4
# endif
# ifndef EAI_NODATA
#  define EAI_NODATA -5
# endif
# ifndef EAI_FAMILY
#  define EAI_FAMILY -6
# endif
# ifndef EAI_SOCKTYPE
#  define EAI_SOCKTYPE -7
# endif
# ifndef EAI_SERVICE
#  define EAI_SERVICE -8
# endif
# ifndef EAI_ADDRFAMILY
#  define EAI_ADDRFAMILY -9
# endif
# ifndef EAI_MEMORY
#  define EAI_MEMORY -10
# endif
# ifndef EAI_SYSTEM
#  define EAI_SYSTEM -11
# endif


# ifndef NI_MAXHOST
#  define NI_MAXHOST 1025
#  define NI_MAXSERV 32
# endif
454
# define NI_MAXNUMERICHOST 64
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487

# ifndef NI_NUMERICHOST
#  define NI_NUMERICHOST 0x01
#  define NI_NUMERICSERV 0x02
#  define NI_NOFQDN      0x04
#  define NI_NAMEREQD    0x08
#  define NI_DGRAM       0x10
# endif

# ifndef HAVE_STRUCT_ADDRINFO
struct addrinfo
{
    int ai_flags;
    int ai_family;
    int ai_socktype;
    int ai_protocol;
    size_t ai_addrlen;
    struct sockaddr *ai_addr;
    char *ai_canonname;
    struct addrinfo *ai_next;
};
#  define AI_PASSIVE     1
#  define AI_CANONNAME   2
#  define AI_NUMERICHOST 4
# endif /* if !HAVE_STRUCT_ADDRINFO */

/*** libidn support ***/
# ifndef AI_IDN
#  define AI_IDN      0
#  define AI_CANONIDN 0
# endif

VLC_EXPORT( const char *, vlc_gai_strerror, ( int ) );
488
VLC_EXPORT( int, vlc_getnameinfo, ( const struct sockaddr *, int, char *, int, int *, int ) );
489
VLC_EXPORT( int, vlc_getaddrinfo, ( vlc_object_t *, const char *, int, const struct addrinfo *, struct addrinfo ** ) );
490
491
VLC_EXPORT( void, vlc_freeaddrinfo, ( struct addrinfo * ) );

492
493
494
495
/*****************************************************************************
 * net_AddressIsMulticast: This function returns VLC_FALSE if the psz_addr does
 * not specify a multicast address or if the address is not a valid address.
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
496
static inline vlc_bool_t net_AddressIsMulticast( vlc_object_t *p_object, const char *psz_addr )
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
{
    struct addrinfo hints, *res;
    vlc_bool_t b_multicast = VLC_FALSE;
    int i;

    memset( &hints, 0, sizeof( hints ) );
    hints.ai_socktype = SOCK_DGRAM; /* UDP */
    hints.ai_flags = AI_NUMERICHOST;

    i = vlc_getaddrinfo( p_object, psz_addr, 0,
                         &hints, &res );
    if( i )
    {
        msg_Err( p_object, "Invalid node for net_AddressIsMulticast: %s : %s",
                 psz_addr, vlc_gai_strerror( i ) );
512
        return VLC_FALSE;
513
514
515
516
517
518
    }
    
    if( res->ai_family == AF_INET )
    {
#if !defined( SYS_BEOS )
        struct sockaddr_in *v4 = (struct sockaddr_in *) res->ai_addr;
519
520
        b_multicast = ( ntohl( v4->sin_addr.s_addr ) >= 0xe0000000 )
                   && ( ntohl( v4->sin_addr.s_addr ) <= 0xefffffff );
521
522
#endif
    }
Eric Petit's avatar
Eric Petit committed
523
#if defined( WIN32 ) || defined( HAVE_GETADDRINFO )
524
525
    else if( res->ai_family == AF_INET6 )
    {
526
527
        struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)res->ai_addr;
        b_multicast = IN6_IS_ADDR_MULTICAST( &v6->sin6_addr );
528
    }
Eric Petit's avatar
Eric Petit committed
529
#endif
530
531
532
533
534
    
    vlc_freeaddrinfo( res );
    return b_multicast;
}

535
static inline int net_GetSockAddress( int fd, char *address, int *port )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
536
537
538
539
{
    struct sockaddr_storage addr;
    socklen_t addrlen = sizeof( addr );

540
541
542
543
544
    return getpeername( fd, (struct sockaddr *)&addr, &addrlen )
        || vlc_getnameinfo( (struct sockaddr *)&addr, addrlen, address,
                            NI_MAXNUMERICHOST, port, NI_NUMERICHOST )
        ? VLC_EGENERIC : 0;
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
545

546
547
548
549
static inline int net_GetPeerAddress( int fd, char *address, int *port )
{
    struct sockaddr_storage addr;
    socklen_t addrlen = sizeof( addr );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
550

551
552
553
554
    return getpeername( fd, (struct sockaddr *)&addr, &addrlen )
        || vlc_getnameinfo( (struct sockaddr *)&addr, addrlen, address,
                            NI_MAXNUMERICHOST, port, NI_NUMERICHOST )
        ? VLC_EGENERIC : 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
555
556
}

557
558
559
560
# ifdef __cplusplus
}
# endif

561
#endif