udp.c 20.9 KB
Newer Older
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1 2 3
/*****************************************************************************
 * udp.c:
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2004-2006 VLC authors and VideoLAN
5
 * Copyright © 2006-2007 Rémi Denis-Courmont
6
 *
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
7 8 9 10 11
 * $Id$
 *
 * Authors: Laurent Aimar <fenrir@videolan.org>
 *          Rémi Denis-Courmont <rem # videolan.org>
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
12 13 14
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
15 16 17 18
 * (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
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
19 20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
21
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
22 23 24
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software 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
 *****************************************************************************/

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

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

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

Clément Stenac's avatar
Clément Stenac committed
39
#include <vlc_network.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
40

41
#ifdef _WIN32
42
#   undef EAFNOSUPPORT
43
#   define EAFNOSUPPORT WSAEAFNOSUPPORT
44
#   include <iphlpapi.h>
45 46
#else
#   include <unistd.h>
Eric Petit's avatar
Eric Petit committed
47 48 49
#   ifdef HAVE_NET_IF_H
#       include <net/if.h>
#   endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
50
#endif
51

52 53 54 55 56 57 58
#ifdef HAVE_LINUX_DCCP_H
# include <linux/dccp.h>
# ifndef SOCK_DCCP /* provisional API */
#  define SOCK_DCCP 6
# endif
#endif

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
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
# define IPPROTO_IPV6 41 /* IANA */
#endif
#ifndef SOL_DCCP
# define SOL_DCCP IPPROTO_DCCP
#endif
#ifndef IPPROTO_DCCP
# define IPPROTO_DCCP 33 /* IANA */
#endif
#ifndef SOL_UDPLITE
# define SOL_UDPLITE IPPROTO_UDPLITE
#endif
#ifndef IPPROTO_UDPLITE
# define IPPROTO_UDPLITE 136 /* IANA */
#endif

#if defined (HAVE_NETINET_UDPLITE_H)
# include <netinet/udplite.h>
#elif defined (__linux__)
/* still missing from glibc 2.6 */
# define UDPLITE_SEND_CSCOV     10
# define UDPLITE_RECV_CSCOV     11
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
87 88 89 90 91
#endif

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

92
/* */
93 94
static int net_SetupDgramSocket (vlc_object_t *p_obj, int fd,
                                 const struct addrinfo *ptr)
95 96 97 98 99
{
#ifdef SO_REUSEPORT
    setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, &(int){ 1 }, sizeof (int));
#endif

100
#if defined (_WIN32)
101 102 103 104 105 106 107

    /* Check windows version so we know if we need to increase receive buffers
     * for Windows 7 and earlier

     * SetSocketMediaStreamingMode is present in win 8 and later, so we set
     * receive buffer if that isn't present
     */
108 109
#if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
    HINSTANCE h_Network = LoadLibrary(TEXT("Windows.Networking.dll"));
110 111 112 113 114 115 116 117
    if( (h_Network == NULL) ||
        (GetProcAddress( h_Network, "SetSocketMediaStreamingMode" ) == NULL ) )
    {
        setsockopt (fd, SOL_SOCKET, SO_RCVBUF,
                         (void *)&(int){ 0x80000 }, sizeof (int));
    }
    if( h_Network )
        FreeLibrary( h_Network );
118
#endif
119

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
    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
        };

        bind (fd, (struct sockaddr *)&dumb, ptr->ai_addrlen);
    }
    else
#endif
    if (bind (fd, ptr->ai_addr, ptr->ai_addrlen))
    {
136
        msg_Err( p_obj, "socket bind error: %s", vlc_strerror_c(net_errno) );
137 138 139 140 141 142 143
        net_Close (fd);
        return -1;
    }
    return fd;
}

/* */
144
static int net_ListenSingle (vlc_object_t *obj, const char *host, int port,
145
                             int protocol)
146
{
147 148 149
    struct addrinfo hints = {
        .ai_socktype = SOCK_DGRAM,
        .ai_protocol = protocol,
150
        .ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_IDN,
151
    }, *res;
152 153 154 155

    if (host && !*host)
        host = NULL;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
156 157
    msg_Dbg (obj, "net: opening %s datagram port %d",
             host ? host : "any", port);
158

159
    int val = vlc_getaddrinfo (host, port, &hints, &res);
160 161 162
    if (val)
    {
        msg_Err (obj, "Cannot resolve %s port %d : %s", host, port,
163
                 gai_strerror (val));
164 165 166 167 168 169 170 171
        return -1;
    }

    val = -1;

    for (const struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
    {
        int fd = net_Socket (obj, ptr->ai_family, ptr->ai_socktype,
172
                             ptr->ai_protocol);
173 174
        if (fd == -1)
        {
175
            msg_Dbg (obj, "socket error: %s", vlc_strerror_c(net_errno));
176 177 178 179
            continue;
        }

#ifdef IPV6_V6ONLY
180
        /* Try dual-mode IPv6 if available. */
181
        if (ptr->ai_family == AF_INET6)
182
            setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &(int){ 0 }, sizeof (int));
183
#endif
184 185
        fd = net_SetupDgramSocket( obj, fd, ptr );
        if( fd == -1 )
186 187 188 189 190 191 192 193 194 195 196 197 198
            continue;

        if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
         && net_Subscribe (obj, fd, ptr->ai_addr, ptr->ai_addrlen))
        {
            net_Close (fd);
            continue;
        }

        val = fd;
        break;
    }

199
    freeaddrinfo (res);
200 201
    return val;
}
202

203

204 205
static int net_SetMcastHopLimit( vlc_object_t *p_this,
                                 int fd, int family, int hlim )
206 207 208
{
    int proto, cmd;

209
    /* There is some confusion in the world whether IP_MULTICAST_TTL
210 211 212 213 214
     * 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 )
    {
215
#ifdef IP_MULTICAST_TTL
216 217 218 219
        case AF_INET:
            proto = SOL_IP;
            cmd = IP_MULTICAST_TTL;
            break;
220
#endif
221 222 223 224 225 226 227 228 229

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

        default:
230
            errno = EAFNOSUPPORT;
231
            msg_Warn( p_this, "%s", vlc_strerror_c(EAFNOSUPPORT) );
232
            return VLC_EGENERIC;
233 234 235 236 237 238 239
    }

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

240 241
        msg_Dbg( p_this, "cannot set hop limit (%d): %s", hlim,
                 vlc_strerror_c(net_errno) );
242
        buf = (unsigned char)(( hlim > 255 ) ? 255 : hlim);
243
        if( setsockopt( fd, proto, cmd, &buf, sizeof( buf ) ) )
244
        {
245 246
            msg_Err( p_this, "cannot set hop limit (%d): %s", hlim,
                     vlc_strerror_c(net_errno) );
247
            return VLC_EGENERIC;
248
        }
249
    }
250

251
    return VLC_SUCCESS;
252 253 254
}


255 256
static int net_SetMcastOut (vlc_object_t *p_this, int fd, int family,
                            const char *iface)
257
{
258 259 260 261 262 263 264
    int scope = if_nametoindex (iface);
    if (scope == 0)
    {
        msg_Err (p_this, "invalid multicast interface: %s", iface);
        return -1;
    }

265
    switch (family)
266
    {
267 268
#ifdef IPV6_MULTICAST_IF
        case AF_INET6:
269
            if (setsockopt (fd, SOL_IPV6, IPV6_MULTICAST_IF,
270
                            &scope, sizeof (scope)) == 0)
271
                return 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
272
            break;
273 274 275
#endif

#ifdef __linux__
276 277
        case AF_INET:
        {
278
            struct ip_mreqn req = { .imr_ifindex = scope };
279 280
            if (setsockopt (fd, SOL_IP, IP_MULTICAST_IF,
                            &req, sizeof (req)) == 0)
281
                return 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
282
            break;
283
        }
284 285 286
#endif
        default:
            errno = EAFNOSUPPORT;
287
    }
288 289
    msg_Err (p_this, "cannot force multicast interface %s: %s", iface,
             vlc_strerror_c(errno));
290
    return -1;
291 292
}

293

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
294
static unsigned var_GetIfIndex (vlc_object_t *obj)
295
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
296 297
    char *ifname = var_InheritString (obj, "miface");
    if (ifname == NULL)
298 299
        return 0;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
300 301 302 303 304
    unsigned ifindex = if_nametoindex (ifname);
    if (ifindex == 0)
        msg_Err (obj, "invalid multicast interface: %s", ifname);
    free (ifname);
    return ifindex;
305
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
306 307


308 309 310 311 312 313 314 315 316
/**
 * 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)
{
317 318 319
/* MCAST_JOIN_SOURCE_GROUP was introduced to OS X in v10.7, but it doesn't work,
 * so ignore it to use the same code path as on 10.5 or 10.6 */
#if defined (MCAST_JOIN_SOURCE_GROUP) && !defined (__APPLE__)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
320
    /* Family-agnostic Source-Specific Multicast join */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
321 322
    int level;
    struct group_source_req gsr;
323

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
324 325
    memset (&gsr, 0, sizeof (gsr));
    gsr.gsr_interface = var_GetIfIndex (obj);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
326

327 328
    switch (grp->sa_family)
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
329
#ifdef AF_INET6
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
330
        case AF_INET6:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
331 332 333
        {
            const struct sockaddr_in6 *g6 = (const struct sockaddr_in6 *)grp;

334
            level = SOL_IPV6;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
335 336 337
            assert (grplen >= sizeof (struct sockaddr_in6));
            if (g6->sin6_scope_id != 0)
                gsr.gsr_interface = g6->sin6_scope_id;
338
            break;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
339
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
340
#endif
341 342 343 344 345 346 347 348
        case AF_INET:
            level = SOL_IP;
            break;
        default:
            errno = EAFNOSUPPORT;
            return -1;
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
349 350 351 352 353 354 355
    assert (grplen <= sizeof (gsr.gsr_group));
    memcpy (&gsr.gsr_source, src, srclen);
    assert (srclen <= sizeof (gsr.gsr_source));
    memcpy (&gsr.gsr_group,  grp, grplen);
    if (setsockopt (fd, level, MCAST_JOIN_SOURCE_GROUP,
                    &gsr, sizeof (gsr)) == 0)
        return 0;
356

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
357 358
#else
    if (src->sa_family != grp->sa_family)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
359
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
360 361 362
        errno = EAFNOSUPPORT;
        return -1;
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
363

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
364
    switch (grp->sa_family)
365
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
# ifdef IP_ADD_SOURCE_MEMBERSHIP
        /* IPv4-specific API */
        case AF_INET:
        {
            struct ip_mreq_source imr;

            memset (&imr, 0, sizeof (imr));
            assert (grplen >= sizeof (struct sockaddr_in));
            imr.imr_multiaddr = ((const struct sockaddr_in *)grp)->sin_addr;
            assert (srclen >= sizeof (struct sockaddr_in));
            imr.imr_sourceaddr = ((const struct sockaddr_in *)src)->sin_addr;
            if (setsockopt (fd, SOL_IP, IP_ADD_SOURCE_MEMBERSHIP,
                            &imr, sizeof (imr)) == 0)
                return 0;
            break;
        }
# endif
        default:
            errno = EAFNOSUPPORT;
385 386
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
387
#endif
388 389
    msg_Err (obj, "cannot join source multicast group: %s",
             vlc_strerror_c(net_errno));
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
390 391 392
    msg_Warn (obj, "trying ASM instead of SSM...");
    return net_Subscribe (obj, fd, grp, grplen);
}
393 394


Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
395 396 397
int net_Subscribe (vlc_object_t *obj, int fd,
                   const struct sockaddr *grp, socklen_t grplen)
{
398 399 400
/* MCAST_JOIN_GROUP was introduced to OS X in v10.7, but it doesn't work,
 * so ignore it to use the same code as on 10.5 or 10.6 */
#if defined (MCAST_JOIN_GROUP) && !defined (__APPLE__)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
401
    /* Family-agnostic Any-Source Multicast join */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
402 403
    int level;
    struct group_req gr;
404

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
405 406
    memset (&gr, 0, sizeof (gr));
    gr.gr_interface = var_GetIfIndex (obj);
407 408 409 410 411

    switch (grp->sa_family)
    {
#ifdef AF_INET6
        case AF_INET6:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
412 413
        {
            const struct sockaddr_in6 *g6 = (const struct sockaddr_in6 *)grp;
414

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
415 416 417 418
            level = SOL_IPV6;
            assert (grplen >= sizeof (struct sockaddr_in6));
            if (g6->sin6_scope_id != 0)
                gr.gr_interface = g6->sin6_scope_id;
419
            break;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
420
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
421
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
422 423 424 425 426 427
        case AF_INET:
            level = SOL_IP;
            break;
        default:
            errno = EAFNOSUPPORT;
            return -1;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
428 429
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
430 431 432 433
    assert (grplen <= sizeof (gr.gr_group));
    memcpy (&gr.gr_group, grp, grplen);
    if (setsockopt (fd, level, MCAST_JOIN_GROUP, &gr, sizeof (gr)) == 0)
        return 0;
434

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
435 436
#else
    switch (grp->sa_family)
437
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
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
# ifdef IPV6_JOIN_GROUP
        case AF_INET6:
        {
            struct ipv6_mreq ipv6mr;
            const struct sockaddr_in6 *g6 = (const struct sockaddr_in6 *)grp;

            memset (&ipv6mr, 0, sizeof (ipv6mr));
            assert (grplen >= sizeof (struct sockaddr_in6));
            ipv6mr.ipv6mr_multiaddr = g6->sin6_addr;
            ipv6mr.ipv6mr_interface = g6->sin6_scope_id;
            if (!setsockopt (fd, SOL_IPV6, IPV6_JOIN_GROUP,
                             &ipv6mr, sizeof (ipv6mr)))
                return 0;
            break;
        }
# endif
# ifdef IP_ADD_MEMBERSHIP
        case AF_INET:
        {
            struct ip_mreq imr;

            memset (&imr, 0, sizeof (imr));
            assert (grplen >= sizeof (struct sockaddr_in));
            imr.imr_multiaddr = ((const struct sockaddr_in *)grp)->sin_addr;
            if (setsockopt (fd, SOL_IP, IP_ADD_MEMBERSHIP,
                            &imr, sizeof (imr)) == 0)
                return 0;
            break;
        }
# endif
        default:
            errno = EAFNOSUPPORT;
470 471
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
472
#endif
473 474
    msg_Err (obj, "cannot join multicast group: %s",
             vlc_strerror_c(net_errno));
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
475 476 477 478
    return -1;
}


Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
479
static int net_SetDSCP( int fd, uint8_t dscp )
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
{
    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));
}

511
#undef net_ConnectDgram
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
512
/*****************************************************************************
513
 * net_ConnectDgram:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
514
 *****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
515 516
 * Open a datagram socket to send data to a defined destination, with an
 * optional hop limit.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
517
 *****************************************************************************/
518 519
int net_ConnectDgram( vlc_object_t *p_this, const char *psz_host, int i_port,
                      int i_hlim, int proto )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
520
{
521 522 523
    struct addrinfo hints = {
        .ai_socktype = SOCK_DGRAM,
        .ai_protocol = proto,
524
        .ai_flags = AI_NUMERICSERV | AI_IDN,
525
    }, *res;
526
    int       i_handle = -1;
527
    bool      b_unreach = false;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
528

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

532
    msg_Dbg( p_this, "net: connecting to [%s]:%d", psz_host, i_port );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
533

534 535
    int val = vlc_getaddrinfo (psz_host, i_port, &hints, &res);
    if (val)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
536
    {
537 538
        msg_Err (p_this, "cannot resolve [%s]:%d : %s", psz_host, i_port,
                 gai_strerror (val));
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
539 540 541
        return -1;
    }

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

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

Sebastien Escudier's avatar
Sebastien Escudier committed
553
        if( i_hlim >= 0 )
554
            net_SetMcastHopLimit( p_this, fd, ptr->ai_family, i_hlim );
555

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
556
        str = var_InheritString (p_this, "miface");
557 558
        if (str != NULL)
        {
559
            net_SetMcastOut (p_this, fd, ptr->ai_family, str);
560
            free (str);
561
        }
562

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
563
        net_SetDSCP (fd, var_InheritInteger (p_this, "dscp"));
564

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
565 566 567 568 569 570 571
        if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) == 0 )
        {
            /* success */
            i_handle = fd;
            break;
        }

572
#if defined( _WIN32 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
573 574 575 576
        if( WSAGetLastError () == WSAENETUNREACH )
#else
        if( errno == ENETUNREACH )
#endif
577
            b_unreach = true;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
578
        else
579 580
            msg_Warn( p_this, "%s port %d : %s", psz_host, i_port,
                      vlc_strerror_c(errno) );
581
        net_Close( fd );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
582 583
    }

584
    freeaddrinfo( res );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
585 586 587 588 589 590 591 592 593 594 595 596

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

597
#undef net_OpenDgram
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
598
/*****************************************************************************
599
 * net_OpenDgram:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
600
 *****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
601
 * OpenDgram a datagram socket and return a handle
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
602
 *****************************************************************************/
603
int net_OpenDgram( vlc_object_t *obj, const char *psz_bind, int i_bind,
604
                   const char *psz_server, int i_server, int protocol )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
605
{
606
    if ((psz_server == NULL) || (psz_server[0] == '\0'))
607
        return net_ListenSingle (obj, psz_bind, i_bind, protocol);
608

609 610
    msg_Dbg (obj, "net: connecting to [%s]:%d from [%s]:%d",
             psz_server, i_server, psz_bind, i_bind);
611

612 613 614
    struct addrinfo hints = {
        .ai_socktype = SOCK_DGRAM,
        .ai_protocol = protocol,
615
        .ai_flags = AI_NUMERICSERV | AI_IDN,
616
    }, *loc, *rem;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
617

618
    int val = vlc_getaddrinfo (psz_server, i_server, &hints, &rem);
619
    if (val)
620
    {
621
        msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind,
622
                 gai_strerror (val));
623
        return -1;
624
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
625

626
    hints.ai_flags |= AI_PASSIVE;
627
    val = vlc_getaddrinfo (psz_bind, i_bind, &hints, &loc);
628
    if (val)
629
    {
630
        msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind,
631
                 gai_strerror (val));
632
        freeaddrinfo (rem);
633
        return -1;
634 635
    }

636
    val = -1;
637 638 639
    for (struct addrinfo *ptr = loc; ptr != NULL; ptr = ptr->ai_next)
    {
        int fd = net_Socket (obj, ptr->ai_family, ptr->ai_socktype,
640
                             ptr->ai_protocol);
641 642
        if (fd == -1)
            continue; // usually, address family not supported
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
643

644 645
        fd = net_SetupDgramSocket( obj, fd, ptr );
        if( fd == -1 )
646
            continue;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
647

648
        for (struct addrinfo *ptr2 = rem; ptr2 != NULL; ptr2 = ptr2->ai_next)
649 650 651 652 653
        {
            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
654

655 656 657 658 659
            if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
              ? net_SourceSubscribe (obj, fd,
                                     ptr2->ai_addr, ptr2->ai_addrlen,
                                     ptr->ai_addr, ptr->ai_addrlen)
              : connect (fd, ptr2->ai_addr, ptr2->ai_addrlen))
660
            {
661 662
                msg_Err (obj, "cannot connect to %s port %d: %s",
                         psz_server, i_server, vlc_strerror_c(net_errno));
663
                continue;
664
            }
665 666
            val = fd;
            break;
667
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
668

669 670
        if (val != -1)
            break;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
671

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
672
        net_Close (fd);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
673 674
    }

675 676
    freeaddrinfo (rem);
    freeaddrinfo (loc);
677
    return val;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
678
}
679

680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727

/**
 * net_SetCSCov:
 * Sets the send and receive checksum coverage of a socket:
 * @param fd socket
 * @param sendcov payload coverage of sent packets (bytes), -1 for full
 * @param recvcov minimum payload coverage of received packets, -1 for full
 */
int net_SetCSCov (int fd, int sendcov, int recvcov)
{
    int type;

    if (getsockopt (fd, SOL_SOCKET, SO_TYPE,
                    &type, &(socklen_t){ sizeof (type) }))
        return VLC_EGENERIC;

    switch (type)
    {
#ifdef UDPLITE_RECV_CSCOV
        case SOCK_DGRAM: /* UDP-Lite */
            if (sendcov == -1)
                sendcov = 0;
            else
                sendcov += 8; /* partial */
            if (setsockopt (fd, SOL_UDPLITE, UDPLITE_SEND_CSCOV, &sendcov,
                            sizeof (sendcov)))
                return VLC_EGENERIC;

            if (recvcov == -1)
                recvcov = 0;
            else
                recvcov += 8;
            if (setsockopt (fd, SOL_UDPLITE, UDPLITE_RECV_CSCOV,
                            &recvcov, sizeof (recvcov)))
                return VLC_EGENERIC;

            return VLC_SUCCESS;
#endif
#ifdef DCCP_SOCKOPT_SEND_CSCOV
        case SOCK_DCCP: /* DCCP and its ill-named socket type */
            if ((sendcov == -1) || (sendcov > 56))
                sendcov = 0;
            else
                sendcov = (sendcov + 3) / 4;
            if (setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_SEND_CSCOV,
                            &sendcov, sizeof (sendcov)))
                return VLC_EGENERIC;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
728 729
            if ((recvcov == -1) || (recvcov > 56))
                recvcov = 0;
730
            else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
731
                recvcov = (recvcov + 3) / 4;
732 733 734 735 736 737 738
            if (setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_RECV_CSCOV,
                            &recvcov, sizeof (recvcov)))
                return VLC_EGENERIC;

            return VLC_SUCCESS;
#endif
    }
739 740 741 742
#if !defined( UDPLITE_RECV_CSCOV ) && !defined( DCCP_SOCKOPT_SEND_CSCOV )
    VLC_UNUSED(sendcov);
    VLC_UNUSED(recvcov);
#endif
743 744 745

    return VLC_EGENERIC;
}