network.h 15.9 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

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

60
    const char *psz_server_addr;
Christophe Massiot's avatar
Christophe Massiot committed
61
62
    int i_server_port;

63
64
    int i_ttl;

Christophe Massiot's avatar
Christophe Massiot committed
65
66
67
    /* Return values */
    int i_handle;
    size_t i_mtu;
68
};
Christophe Massiot's avatar
Christophe Massiot committed
69

70
71
72
typedef struct
{
    char *psz_protocol;
73
74
    char *psz_username;
    char *psz_password;
75
76
77
78
79
80
    char *psz_host;
    int  i_port;

    char *psz_path;

    char *psz_option;
81
82
    
    char *psz_buffer; /* to be freed */
83
84
85
86
87
88
89
} vlc_url_t;

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

    url->psz_protocol = NULL;
100
101
    url->psz_username = NULL;
    url->psz_password = NULL;
102
103
104
105
    url->psz_host     = NULL;
    url->i_port       = 0;
    url->psz_path     = NULL;
    url->psz_option   = NULL;
106
107
108
109
110
111
112
    
    if( psz_url == NULL )
    {
        url->psz_buffer = NULL;
        return;
    }
    url->psz_buffer = psz_parse = psz_dup = strdup( psz_url );
113

114
115
    p  = strstr( psz_parse, ":/" );
    if( p != NULL )
116
117
118
119
120
    {
        /* we have a protocol */

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

133
134
135
136
137
138
        psz_parse = strchr( psz_parse, ':' );
        if( psz_parse != NULL )
        {
            /* We have a password */
            *psz_parse++ = '\0';
            url->psz_password = psz_parse;
139
        }
140
141

        psz_parse = p;
142
    }
143

144
145
146
147
    p = strchr( psz_parse, '/' );
    if( !p || psz_parse < p )
    {
        char *p2;
148

149
        /* We have a host[:port] */
150
        url->psz_host = strdup( psz_parse );
151
152
153
        if( p )
        {
            url->psz_host[p - psz_parse] = '\0';
154
155
        }

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

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

/*****************************************************************************
 * vlc_UrlClean:
 *****************************************************************************
 *
 *****************************************************************************/
static inline void vlc_UrlClean( vlc_url_t *url )
{
200
    if( url->psz_buffer ) free( url->psz_buffer );
201
    if( url->psz_host )   free( url->psz_host );
202
203

    url->psz_protocol = NULL;
204
205
    url->psz_username = NULL;
    url->psz_password = NULL;
206
207
208
209
    url->psz_host     = NULL;
    url->i_port       = 0;
    url->psz_path     = NULL;
    url->psz_option   = NULL;
210
211

    url->psz_buffer   = NULL;
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

/*****************************************************************************
 * 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;
    const char *in;

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

    out = psz_enc;
    for( in = psz_url; *in; in++ )
    {
        char c = *in;

        if( ( c <= 32 ) || ( c == '%' ) || ( c == '?' ) || ( c == '&' )
         || ( c == '+' ) )
        {
            *out++ = '%';   
            *out++ = ( ( c >> 4 ) >= 0xA ) ? 'A' + ( c >> 4 ) - 0xA
                                           : '0' + ( c >> 4 );
            *out++ = ( ( c & 0xf ) >= 0xA ) ? 'A' + ( c & 0xf ) - 0xA
                                           : '0' + ( c & 0xf );
        }
        else
            *out++ = c;
    }
    *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
        if( c == ' ' )
            return 1;
    }
    return 0; /* looks fine - but maybe it is not encoded */
}
278
279
280
281
282
283
                    
/*****************************************************************************
 * vlc_b64_encode:
 *****************************************************************************
 *
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
284
static inline char *vlc_b64_encode( char *src )
285
286
287
{
    static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
                                                                                
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
288
    char *dst = (char *)malloc( strlen( src ) * 4 / 3 + 12 );
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
    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;
}
322

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

327
#define net_ListenTCP(a, b, c) __net_ListenTCP(VLC_OBJECT(a), b, c)
328
VLC_EXPORT( int *, __net_ListenTCP, ( vlc_object_t *, const char *, int ) );
329

330
#define net_Accept(a, b, c) __net_Accept(VLC_OBJECT(a), b, c)
331
VLC_EXPORT( int, __net_Accept, ( vlc_object_t *, int *, mtime_t ) );
332

Laurent Aimar's avatar
Laurent Aimar committed
333
#define net_OpenUDP(a, b, c, d, e ) __net_OpenUDP(VLC_OBJECT(a), b, c, d, e)
334
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
335

Laurent Aimar's avatar
   
Laurent Aimar committed
336
VLC_EXPORT( void, net_Close, ( int fd ) );
337
VLC_EXPORT( void, net_ListenClose, ( int *fd ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
338
339


340
341
342
343
344
345
346
347
348
349
/* 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
350

351
352
#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
353

354
355
#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
356

357
358
#define net_Write(a,b,c,d,e) __net_Write(VLC_OBJECT(a),b,c,d,e)
VLC_EXPORT( int, __net_Write, ( vlc_object_t *p_this, int fd, v_socket_t *, uint8_t *p_data, int i_data ) );
Laurent Aimar's avatar
   
Laurent Aimar committed
359

360
361
#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
362

363
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
364

365
366
#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 ) );
367

368

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
369
370
371
372
373
374
#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


375
376
377
378
379
380
381
/*****************************************************************************
 * net_StopRecv/Send
 *****************************************************************************
 * Wrappers for shutdown()
 *****************************************************************************/
#if defined (SHUT_WR)
/* the standard way */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
382
383
# define net_StopSend( fd ) (void)shutdown( fd, SHUT_WR )
# define net_StopRecv( fd ) (void)shutdown( fd, SHUT_RD )
384
385
#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
386
387
# define net_StopSend( fd ) (void)shutdown( fd, SD_SEND )
# define net_StopRecv( fd ) (void)shutdown( fd, SD_RECEIVE )
388
389
#else
# warning FIXME: implement shutdown on your platform!
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
390
391
# define net_StopSend( fd ) (void)0
# define net_StopRecv( fd ) (void)0
392
#endif
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
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
/* 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
436
# define NI_MAXNUMERICHOST 64
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469

# 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 ) );
470
VLC_EXPORT( int, vlc_getnameinfo, ( const struct sockaddr *, int, char *, int, int *, int ) );
471
VLC_EXPORT( int, vlc_getaddrinfo, ( vlc_object_t *, const char *, int, const struct addrinfo *, struct addrinfo ** ) );
472
473
VLC_EXPORT( void, vlc_freeaddrinfo, ( struct addrinfo * ) );

474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
/*****************************************************************************
 * 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.
 *****************************************************************************/
static inline vlc_bool_t net_AddressIsMulticast( vlc_object_t *p_object, char *psz_addr )
{
    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 ) );
494
        return VLC_FALSE;
495
496
497
498
499
500
    }
    
    if( res->ai_family == AF_INET )
    {
#if !defined( SYS_BEOS )
        struct sockaddr_in *v4 = (struct sockaddr_in *) res->ai_addr;
501
502
        b_multicast = ( ntohl( v4->sin_addr.s_addr ) >= 0xe0000000 )
                   && ( ntohl( v4->sin_addr.s_addr ) <= 0xefffffff );
503
504
505
506
#endif
    }
    else if( res->ai_family == AF_INET6 )
    {
507
508
509
#if defined( WIN32 ) || defined( HAVE_GETADDRINFO )
        struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)res->ai_addr;
        b_multicast = IN6_IS_ADDR_MULTICAST( &v6->sin6_addr );
510
511
512
513
514
515
516
#endif
    }
    
    vlc_freeaddrinfo( res );
    return b_multicast;
}

517
static inline int net_GetSockAddress( int fd, char *address, int *port )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
518
519
520
521
{
    struct sockaddr_storage addr;
    socklen_t addrlen = sizeof( addr );

522
523
524
525
526
    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
527

528
529
530
531
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
532

533
534
535
536
    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
537
538
}

539
#endif