udp.c 19.6 KB
Newer Older
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1
2
3
/*****************************************************************************
 * udp.c:
 *****************************************************************************
4
 * Copyright (C) 2004-2006 the VideoLAN team
5
6
 * Copyright © 2006 Rémi Denis-Courmont
 *
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 * $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
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
25
26
27
28
29
30
31
32
33
 *****************************************************************************/

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

#include <errno.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
34
#include <assert.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
35
36
37
38
39

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

zorglub's avatar
zorglub committed
40
#include <vlc_network.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
41

42
43
44
45
46
47
48
#ifdef WIN32
#   if defined(UNDER_CE)
#       undef IP_MULTICAST_TTL
#       define IP_MULTICAST_TTL 3
#       undef IP_ADD_MEMBERSHIP
#       define IP_ADD_MEMBERSHIP 5
#   endif
49
#   define EAFNOSUPPORT WSAEAFNOSUPPORT
50
51
52
#   define if_nametoindex( str ) atoi( str )
#else
#   include <unistd.h>
Eric Petit's avatar
Eric Petit committed
53
54
55
#   ifdef HAVE_NET_IF_H
#       include <net/if.h>
#   endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
56
#endif
57
58
59
60
61
62
63
64
65

#ifndef SOL_IP
# define SOL_IP IPPROTO_IP
#endif
#ifndef SOL_IPV6
# define SOL_IPV6 IPPROTO_IPV6
#endif
#ifndef IPPROTO_IPV6
# define IPPROTO_IPV6 41
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
66
67
68
69
70
#endif

extern int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
                       int i_protocol );

71

72
73
74
75
76
/*
 * XXX: I am too lazy to put all these dual functions in “next generation”
 * network plugins.
 */

77
78
static int net_SetMcastHopLimit( vlc_object_t *p_this,
                                 int fd, int family, int hlim )
79
80
81
82
83
84
85
86
87
{
    int proto, cmd;

    /* There is some confusion in the world whether IP_MULTICAST_TTL 
     * takes a byte or an int as an argument.
     * BSD seems to indicate byte so we are going with that and use
     * int as a fallback to be safe */
    switch( family )
    {
88
#ifdef IP_MULTICAST_TTL
89
90
91
92
        case AF_INET:
            proto = SOL_IP;
            cmd = IP_MULTICAST_TTL;
            break;
93
#endif
94
95
96
97
98
99
100
101
102

#ifdef IPV6_MULTICAST_HOPS
        case AF_INET6:
            proto = SOL_IPV6;
            cmd = IPV6_MULTICAST_HOPS;
            break;
#endif

        default:
103
104
            msg_Warn( p_this, "%s", strerror( EAFNOSUPPORT ) );
            return VLC_EGENERIC;
105
106
107
108
109
110
111
112
    }

    if( setsockopt( fd, proto, cmd, &hlim, sizeof( hlim ) ) < 0 )
    {
        /* BSD compatibility */
        unsigned char buf;

        buf = (unsigned char)(( hlim > 255 ) ? 255 : hlim);
113
114
        if( setsockopt( fd, proto, cmd, &buf, sizeof( buf ) ) )
            return VLC_EGENERIC;
115
    }
116

117
    return VLC_SUCCESS;
118
119
120
}


121
static int net_SetMcastOutIface (int fd, int family, int scope)
122
{
123
    switch (family)
124
    {
125
126
127
128
129
130
131
#ifdef IPV6_MULTICAST_IF
        case AF_INET6:
            return setsockopt (fd, SOL_IPV6, IPV6_MULTICAST_IF,
                               &scope, sizeof (scope));
#endif

#ifdef __linux__
132
133
        case AF_INET:
        {
134
            struct ip_mreqn req = { .imr_ifindex = scope };
135

136
137
            return setsockopt (fd, SOL_IP, IP_MULTICAST_IF, &req,
                               sizeof (req));
138
        }
139
140
#endif
    }
141

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
    errno = EAFNOSUPPORT;
    return -1;
}


static inline int net_SetMcastOutIPv4 (int fd, struct in_addr ipv4)
{
#ifdef IP_MULTICAST_IF
    return setsockopt( fd, SOL_IP, IP_MULTICAST_IF, &ipv4, sizeof (ipv4));
#else
    errno = EAFNOSUPPORT;
    return -1;
#endif
}


static int net_SetMcastOut (vlc_object_t *p_this, int fd, int family,
                            const char *iface, const char *addr)
{
    if (iface != NULL)
    {
        int scope = if_nametoindex (iface);
        if (scope == 0)
165
        {
166
            msg_Err (p_this, "invalid multicast interface: %s", iface);
167
168
            return -1;
        }
169

170
171
        if (net_SetMcastOutIface (fd, family, scope) == 0)
            return 0;
172

173
174
175
176
177
178
179
180
181
        msg_Err (p_this, "%s: %s", iface, net_strerror (net_errno));
    }

    if (addr != NULL)
    {
        if (family == AF_INET)
        {
            struct in_addr ipv4;
            if (inet_pton (AF_INET, addr, &ipv4) <= 0)
182
            {
183
                msg_Err (p_this, "invalid IPv4 address for multicast: %s",
184
185
                         addr);
                return -1;
186
187
            }

188
189
190
191
192
            if (net_SetMcastOutIPv4 (fd, ipv4) == 0)
                return 0;

            msg_Err (p_this, "%s: %s", addr, net_strerror (net_errno));
        }
193
194
    }

195
    return -1;
196
197
}

198

199
200
201
202
203
204
205
/**
 * Old-style any-source multicast join.
 * In use on Windows XP/2003 and older.
 */
static int
net_IPv4Join (vlc_object_t *obj, int fd,
              const struct sockaddr_in *src, const struct sockaddr_in *grp)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
206
207
{
#ifdef IP_ADD_MEMBERSHIP
208
209
210
211
212
213
214
215
    union
    {
        struct ip_mreq gr4;
# ifdef IP_ADD_SOURCE_MEMBERSHIP
        struct ip_mreq_source gsr4;
# endif
    } opt;
    int cmd;
216
    struct in_addr id = { .s_addr = INADDR_ANY };
217
218
219
220
221
222
223
224
    socklen_t optlen;

    /* Multicast interface IPv4 address */
    char *iface = var_CreateGetString (obj, "miface-addr");
    if (iface != NULL)
    {
        if ((*iface)
         && (inet_pton (AF_INET, iface, &id) <= 0))
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
225
        {
226
227
228
229
230
            msg_Err (obj, "invalid multicast interface address %s", iface);
            free (iface);
            goto error;
        }
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
231

232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
    memset (&opt, 0, sizeof (opt));
    if (src != NULL)
    {
# ifdef IP_ADD_SOURCE_MEMBERSHIP
        cmd = IP_ADD_SOURCE_MEMBERSHIP;
        opt.gsr4.imr_multiaddr = grp->sin_addr;
        opt.gsr4.imr_sourceaddr = src->sin_addr;
        opt.gsr4.imr_interface = id;
        optlen = sizeof (opt.gsr4);
# else
        errno = ENOSYS;
        goto error;
# endif
    }
    else
    {
        cmd = IP_ADD_MEMBERSHIP;
        opt.gr4.imr_multiaddr = grp->sin_addr;
        opt.gr4.imr_interface = id;
        optlen = sizeof (opt.gr4);
    }

    msg_Dbg (obj, "IP_ADD_%sMEMBERSHIP multicast request",
             (src != NULL) ? "SOURCE_" : "");

    if (setsockopt (fd, SOL_IP, cmd, &opt, optlen) == 0)
        return 0;

error:
#endif

    msg_Err (obj, "cannot join IPv4 multicast group (%s)",
             net_strerror (net_errno));
    return -1;
}


static int
net_IPv6Join (vlc_object_t *obj, int fd, const struct sockaddr_in6 *src)
{
#ifdef IPV6_JOIN_GROUP
    struct ipv6_mreq gr6;
    memset (&gr6, 0, sizeof (gr6));
    gr6.ipv6mr_interface = src->sin6_scope_id;
    memcpy (&gr6.ipv6mr_multiaddr, &src->sin6_addr, 16);

    msg_Dbg (obj, "IPV6_JOIN_GROUP multicast request");

    if (!setsockopt (fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &gr6, sizeof (gr6)))
        return 0;
#else
    errno = ENOSYS;
#endif

    msg_Err (obj, "cannot join IPv6 any-source multicast group (%s)",
             net_strerror (net_errno));
    return -1;
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
290
291


292
#if 0
293
294
295
296
297
298
299
#if defined (WIN32) && !defined (MCAST_JOIN_SOURCE_GROUP)
/*
 * I hate manual definitions: Error-prone. Portability hell.
 * Developers shall use UP-TO-DATE compilers. Full point.
 * If you remove the warning, you remove the whole ifndef.
 */
#  warning Your C headers are out-of-date. Please update.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
300

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
301
302
303
304
305
306
307
308
309
310
311
/* No, I won't guess the layout of these two.
 * No, I don't want another socket protection level type-of obnoxious bug. */
#  error Hmmm? This needs fixing.
#  define MCAST_JOIN_GROUP XXX
struct group_req
{
    uint32_t gr_interface; FIXME
    struct sockaddr_storage gr_group; FIXME
};


312
313
314
315
316
317
318
#  define MCAST_JOIN_SOURCE_GROUP 45 /* from <ws2ipdef.h> */
struct group_source_req
{
    uint32_t gsr_interface;
    struct sockaddr_storage gsr_group;
    struct sockaddr_storage gsr_source;
};
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
319
#endif
320
#endif
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339

/**
 * IP-agnostic multicast join,
 * with fallback to old APIs, and fallback from SSM to ASM.
 */
static int
net_SourceSubscribe (vlc_object_t *obj, int fd,
                     const struct sockaddr *src, socklen_t srclen,
                     const struct sockaddr *grp, socklen_t grplen)
{
    int level, iid = 0;

    char *iface = var_CreateGetString (obj, "miface");
    if (iface != NULL)
    {
        if (*iface)
        {
            iid = if_nametoindex (iface);
            if (iid == 0)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
340
            {
341
342
                msg_Err (obj, "invalid multicast interface: %s", iface);
                free (iface);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
343
344
345
                return -1;
            }
        }
346
347
        free (iface);
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
348

349
350
    switch (grp->sa_family)
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
351
#ifdef AF_INET6
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
352
        case AF_INET6:
353
            level = SOL_IPV6;
354
355
            if (((const struct sockaddr_in6 *)grp)->sin6_scope_id)
                iid = ((const struct sockaddr_in6 *)grp)->sin6_scope_id;
356
            break;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
357
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
358

359
360
361
        case AF_INET:
            level = SOL_IP;
            break;
362

363
364
365
366
367
        default:
            errno = EAFNOSUPPORT;
            return -1;
    }

368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
    if (src != NULL)
        switch (src->sa_family)
        {
#ifdef AF_INET6
            case AF_INET6:
                if (memcmp (&((const struct sockaddr_in6 *)src)->sin6_addr,
                            &in6addr_any, sizeof (in6addr_any)) == 0)
                    src = NULL;
            break;
#endif

            case AF_INET:
                if (((const struct sockaddr_in *)src)->sin_addr.s_addr
                     == INADDR_ANY)
                    src = NULL;
                break;
        }


Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
387
388
389
390
391
392
393
394
395
396
397
398
    /* Agnostic ASM/SSM multicast join */
#ifdef MCAST_JOIN_SOURCE_GROUP
    union
    {
        struct group_req gr;
        struct group_source_req gsr;
    } opt;
    socklen_t optlen;

    memset (&opt, 0, sizeof (opt));

    if (src != NULL)
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
    {
        if ((grplen > sizeof (opt.gsr.gsr_group))
         || (srclen > sizeof (opt.gsr.gsr_source)))
            return -1;

        opt.gsr.gsr_interface = iid;
        memcpy (&opt.gsr.gsr_source, src, srclen);
        memcpy (&opt.gsr.gsr_group,  grp, grplen);
        optlen = sizeof (opt.gsr);
    }
    else
    {
        if (grplen > sizeof (opt.gr.gr_group))
            return -1;

        opt.gr.gr_interface = iid;
        memcpy (&opt.gr.gr_group, grp, grplen);
        optlen = sizeof (opt.gr);
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
419
    msg_Dbg (obj, "Multicast %sgroup join request", src ? "source " : "");
420

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
421
422
    if (setsockopt (fd, level, 
                    src ? MCAST_JOIN_SOURCE_GROUP : MCAST_JOIN_GROUP,
423
424
425
426
427
                    (void *)&opt, optlen) == 0)
        return 0;
#endif

    /* Fallback to IPv-specific APIs */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
428
    if ((src != NULL) && (src->sa_family != grp->sa_family))
429
430
431
432
433
434
        return -1;

    switch (grp->sa_family)
    {
        case AF_INET:
            if ((grplen < sizeof (struct sockaddr_in))
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
435
             || ((src != NULL) && (srclen < sizeof (struct sockaddr_in))))
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
436
437
                return -1;

438
439
440
441
442
443
444
445
            if (net_IPv4Join (obj, fd, (const struct sockaddr_in *)src,
                              (const struct sockaddr_in *)grp) == 0)
                return 0;
            break;

#ifdef AF_INET6
        case AF_INET6:
            if ((grplen < sizeof (struct sockaddr_in6))
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
446
             || ((src != NULL) && (srclen < sizeof (struct sockaddr_in6))))
447
448
                return -1;

Marian Durkovic's avatar
Marian Durkovic committed
449
450
451
            /* IPv6-specific SSM API does not exist. So if we're here
             * it means IPv6 SSM is not supported on this OS and we
             * directly fallback to ASM */
452
453
454
455

            if (net_IPv6Join (obj, fd, (const struct sockaddr_in6 *)grp) == 0)
                return 0;
            break;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
456
457
458
#endif
    }

459
460
461
    msg_Err (obj, "Multicast group join error (%s)",
             net_strerror (net_errno));

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
462
    if (src != NULL)
463
464
465
466
467
    {
        msg_Warn (obj, "Trying ASM instead of SSM...");
        return net_Subscribe (obj, fd, grp, grplen);
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
468
469
470
471
472
    msg_Err (obj, "Multicast not supported");
    return -1;
}


473
474
475
476
477
478
479
int net_Subscribe (vlc_object_t *obj, int fd,
                   const struct sockaddr *addr, socklen_t addrlen)
{
    return net_SourceSubscribe (obj, fd, NULL, 0, addr, addrlen);
}


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
507
508
509
510
511
512
int net_SetDSCP( int fd, uint8_t dscp )
{
    struct sockaddr_storage addr;
    if( getsockname( fd, (struct sockaddr *)&addr, &(socklen_t){ sizeof (addr) }) )
        return -1;

    int level, cmd;

    switch( addr.ss_family )
    {
#ifdef IPV6_TCLASS
        case AF_INET6:
            level = SOL_IPV6;
            cmd = IPV6_TCLASS;
            break;
#endif

        case AF_INET:
            level = SOL_IP;
            cmd = IP_TOS;
            break;

        default:
#ifdef ENOPROTOOPT
            errno = ENOPROTOOPT;
#endif
            return -1;
    }

    return setsockopt( fd, level, cmd, &(int){ dscp }, sizeof (int));
}


Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
513
514
515
516
517
518
519
/*****************************************************************************
 * __net_ConnectUDP:
 *****************************************************************************
 * Open a UDP socket to send data to a defined destination, with an optional
 * hop limit.
 *****************************************************************************/
int __net_ConnectUDP( vlc_object_t *p_this, const char *psz_host, int i_port,
520
                      int i_hlim )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
521
522
523
524
525
526
527
528
{
    struct addrinfo hints, *res, *ptr;
    int             i_val, i_handle = -1;
    vlc_bool_t      b_unreach = VLC_FALSE;

    if( i_port == 0 )
        i_port = 1234; /* historical VLC thing */

529
    if( i_hlim < 1 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
530
        i_hlim = var_CreateGetInteger( p_this, "ttl" );
531

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
    memset( &hints, 0, sizeof( hints ) );
    hints.ai_socktype = SOCK_DGRAM;

    msg_Dbg( p_this, "net: connecting to %s port %d", psz_host, i_port );

    i_val = vlc_getaddrinfo( p_this, psz_host, i_port, &hints, &res );
    if( i_val )
    {
        msg_Err( p_this, "cannot resolve %s port %d : %s", psz_host, i_port,
                 vlc_gai_strerror( i_val ) );
        return -1;
    }

    for( ptr = res; ptr != NULL; ptr = ptr->ai_next )
    {
547
548
549
550
        char *str;
        int fd = net_Socket (p_this, ptr->ai_family, ptr->ai_socktype,
                             ptr->ai_protocol);
        if (fd == -1)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
551
            continue;
552

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
553
#if !defined( SYS_BEOS )
554
555
556
557
558
559
560
        /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s)
        * to avoid packet loss caused by scheduling problems */
        setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &(int){ 0x80000 }, sizeof (int));
        setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &(int){ 0x80000 }, sizeof (int));

        /* Allow broadcast sending */
        setsockopt (fd, SOL_SOCKET, SO_BROADCAST, &(int){ 1 }, sizeof (int));
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
561
562
#endif

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
563
        if( i_hlim > 0 )
564
            net_SetMcastHopLimit( p_this, fd, ptr->ai_family, i_hlim );
565
566
567
568

        str = var_CreateGetString (p_this, "miface");
        if (str != NULL)
        {
569
570
            if (*str)
                net_SetMcastOut (p_this, fd, ptr->ai_family, str, NULL);
571
572
573
574
575
            free (str);
        }

        str = var_CreateGetString (p_this, "miface-addr");
        if (str != NULL)
576
        {
577
578
            if (*str)
                net_SetMcastOut (p_this, fd, ptr->ai_family, NULL, str);
579
            free (str);
580
        }
581

582
583
        net_SetDSCP (fd, var_CreateGetInteger (p_this, "dscp"));

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
        if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) == 0 )
        {
            /* success */
            i_handle = fd;
            break;
        }

#if defined( WIN32 ) || defined( UNDER_CE )
        if( WSAGetLastError () == WSAENETUNREACH )
#else
        if( errno == ENETUNREACH )
#endif
            b_unreach = VLC_TRUE;
        else
        {
            msg_Warn( p_this, "%s port %d : %s", psz_host, i_port,
                      strerror( errno ) );
            net_Close( fd );
            continue;
        }
    }

    vlc_freeaddrinfo( res );

    if( i_handle == -1 )
    {
        if( b_unreach )
            msg_Err( p_this, "Host %s port %d is unreachable", psz_host,
                     i_port );
        return -1;
    }

    return i_handle;
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
619

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
620
621
622
623
624
/*****************************************************************************
 * __net_OpenUDP:
 *****************************************************************************
 * Open a UDP connection and return a handle
 *****************************************************************************/
625
int __net_OpenUDP( vlc_object_t *obj, const char *psz_bind, int i_bind,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
626
627
                   const char *psz_server, int i_server )
{
628
629
630
    struct addrinfo hints, *loc, *rem;
    int val;

631
632
633
634
    if( !*psz_server )
        return net_ListenUDP1 (obj, psz_bind, i_bind);

    msg_Dbg( obj, "net: connecting to [%s]:%d from [%s]:%d",
635
636
637
638
639
640
             psz_server, i_server, psz_bind, i_bind );

    memset (&hints, 0, sizeof (hints));
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_protocol = IPPROTO_UDP;
    hints.ai_flags = AI_PASSIVE;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
641

642
643
    val = vlc_getaddrinfo (obj, psz_server, i_server, &hints, &rem);
    if (val)
644
    {
645
646
647
        msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind,
                 vlc_gai_strerror (val));
        return -1;
648
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
649

650
651
    val = vlc_getaddrinfo (obj, psz_bind, i_bind, &hints, &loc);
    if (val)
652
    {
653
654
655
656
        msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind,
                 vlc_gai_strerror (val));
        vlc_freeaddrinfo (rem);
        return -1;
657
658
    }

659
660
661
662
663
664
    for (struct addrinfo *ptr = loc; ptr != NULL; ptr = ptr->ai_next)
    {
        int fd = net_Socket (obj, ptr->ai_family, ptr->ai_socktype,
                             ptr->ai_protocol);
        if (fd == -1)
            continue; // usually, address family not supported
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
665

666
667
668
#ifdef SO_REUSEPORT
        setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, &(int){ 1 }, sizeof (int));
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
669

670
671
672
673
674
675
676
677
#ifdef SO_RCVBUF
        /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s)
         * to avoid packet loss caused in case of scheduling hiccups */
        setsockopt (fd, SOL_SOCKET, SO_RCVBUF,
                    (void *)&(int){ 0x80000 }, sizeof (int));
        setsockopt (fd, SOL_SOCKET, SO_SNDBUF,
                    (void *)&(int){ 0x80000 }, sizeof (int));
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
678

679
680
681
682
683
684
685
686
687
688
#if defined (WIN32) || defined (UNDER_CE)
        if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
         && (sizeof (struct sockaddr_storage) >= ptr->ai_addrlen))
        {
            // This works for IPv4 too - don't worry!
            struct sockaddr_in6 dumb =
            {
                .sin6_family = ptr->ai_addr->sa_family,
                .sin6_port =  ((struct sockaddr_in *)(ptr->ai_addr))->sin_port
            };
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
689

690
691
692
693
694
695
696
697
698
            bind (fd, (struct sockaddr *)&dumb, ptr->ai_addrlen);
        }
        else
#endif
        if (bind (fd, ptr->ai_addr, ptr->ai_addrlen))
        {
            net_Close (fd);
            continue;
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
699

700
701
702
703
704
705
706
        struct addrinfo *ptr2;
        for (ptr2 = rem; ptr2 != NULL; ptr2 = ptr2->ai_next)
        {
            if ((ptr2->ai_family != ptr->ai_family)
             || (ptr2->ai_socktype != ptr->ai_socktype)
             || (ptr2->ai_protocol != ptr->ai_protocol))
                continue;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
707

708
709
710
711
712
713
714
715
716
717
718
719
720
            if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen))
            {
                if (net_SourceSubscribe (obj, fd,
                                         ptr2->ai_addr, ptr2->ai_addrlen,
                                         ptr->ai_addr, ptr->ai_addrlen) == 0)
                    break;
            }
            else
            {
                if (connect (fd, ptr2->ai_addr, ptr2->ai_addrlen) == 0)
                    break;
            }
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
721

722
723
724
725
726
727
728
        if (ptr2 == NULL)
        {
            msg_Err (obj, "cannot connect to %s port %d: %s",
                     psz_server, i_server, net_strerror (net_errno));
            close (fd);
            continue;
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
729

730
        return fd;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
731
732
    }

733
    return -1;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
734
}