udp.c 20.5 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 42 43 44 45 46 47
#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
48
#   define EAFNOSUPPORT WSAEAFNOSUPPORT
49 50
#else
#   include <unistd.h>
Eric Petit's avatar
Eric Petit committed
51 52 53
#   ifdef HAVE_NET_IF_H
#       include <net/if.h>
#   endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
54
#endif
55

56 57 58 59 60 61 62
#ifdef HAVE_LINUX_DCCP_H
# include <linux/dccp.h>
# ifndef SOCK_DCCP /* provisional API */
#  define SOCK_DCCP 6
# endif
#endif

63 64 65 66 67 68 69
#ifndef SOL_IP
# define SOL_IP IPPROTO_IP
#endif
#ifndef SOL_IPV6
# define SOL_IPV6 IPPROTO_IPV6
#endif
#ifndef IPPROTO_IPV6
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
# 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
91 92 93 94 95
#endif

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

96
/* */
97 98
static int net_SetupDgramSocket (vlc_object_t *p_obj, int fd,
                                 const struct addrinfo *ptr)
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
{
#ifdef SO_REUSEPORT
    setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, &(int){ 1 }, sizeof (int));
#endif

#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

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

        bind (fd, (struct sockaddr *)&dumb, ptr->ai_addrlen);
    }
    else
#endif
    if (bind (fd, ptr->ai_addr, ptr->ai_addrlen))
    {
        msg_Err( p_obj, "socket bind error (%m)" );
        net_Close (fd);
        return -1;
    }
    return fd;
}

/* */
138
static int net_ListenSingle (vlc_object_t *obj, const char *host, int port,
139
                             int protocol)
140
{
141 142 143
    struct addrinfo hints = {
        .ai_socktype = SOCK_DGRAM,
        .ai_protocol = protocol,
144
        .ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_IDN,
145
    }, *res;
146 147 148 149

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

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

153
    int val = vlc_getaddrinfo (host, port, &hints, &res);
154 155 156
    if (val)
    {
        msg_Err (obj, "Cannot resolve %s port %d : %s", host, port,
157
                 gai_strerror (val));
158 159 160 161 162 163 164 165
        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,
166
                             ptr->ai_protocol);
167 168
        if (fd == -1)
        {
169
            msg_Dbg (obj, "socket error: %m");
170 171 172 173
            continue;
        }

#ifdef IPV6_V6ONLY
174
        /* Try dual-mode IPv6 if available. */
175
        if (ptr->ai_family == AF_INET6)
176
            setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &(int){ 0 }, sizeof (int));
177
#endif
178 179
        fd = net_SetupDgramSocket( obj, fd, ptr );
        if( fd == -1 )
180 181 182 183 184 185 186 187 188 189 190 191 192
            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;
    }

193
    freeaddrinfo (res);
194 195
    return val;
}
196

197

198 199
static int net_SetMcastHopLimit( vlc_object_t *p_this,
                                 int fd, int family, int hlim )
200 201 202
{
    int proto, cmd;

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

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

        default:
224 225
            errno = EAFNOSUPPORT;
            msg_Warn( p_this, "%m" );
226
            return VLC_EGENERIC;
227 228 229 230 231 232 233
    }

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

234
        msg_Dbg( p_this, "cannot set hop limit (%d): %m", hlim );
235
        buf = (unsigned char)(( hlim > 255 ) ? 255 : hlim);
236
        if( setsockopt( fd, proto, cmd, &buf, sizeof( buf ) ) )
237 238
        {
            msg_Err( p_this, "cannot set hop limit (%d): %m", hlim );
239
            return VLC_EGENERIC;
240
        }
241
    }
242

243
    return VLC_SUCCESS;
244 245 246
}


247 248
static int net_SetMcastOut (vlc_object_t *p_this, int fd, int family,
                            const char *iface)
249
{
250 251 252 253 254 255 256
    int scope = if_nametoindex (iface);
    if (scope == 0)
    {
        msg_Err (p_this, "invalid multicast interface: %s", iface);
        return -1;
    }

257
    switch (family)
258
    {
259 260
#ifdef IPV6_MULTICAST_IF
        case AF_INET6:
261
            if (setsockopt (fd, SOL_IPV6, IPV6_MULTICAST_IF,
262
                            &scope, sizeof (scope)) == 0)
263
                return 0;
264 265 266
#endif

#ifdef __linux__
267 268
        case AF_INET:
        {
269
            struct ip_mreqn req = { .imr_ifindex = scope };
270 271
            if (setsockopt (fd, SOL_IP, IP_MULTICAST_IF,
                            &req, sizeof (req)) == 0)
272 273
                return 0;
        }
274 275 276
#endif
        default:
            errno = EAFNOSUPPORT;
277
    }
278
    msg_Err (p_this, "cannot force multicast interface %s: %m", iface);
279
    return -1;
280 281
}

282

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
283
static unsigned var_GetIfIndex (vlc_object_t *obj)
284
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
285 286
    char *ifname = var_InheritString (obj, "miface");
    if (ifname == NULL)
287 288
        return 0;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
289 290 291 292 293
    unsigned ifindex = if_nametoindex (ifname);
    if (ifindex == 0)
        msg_Err (obj, "invalid multicast interface: %s", ifname);
    free (ifname);
    return ifindex;
294
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
295 296


297 298 299 300 301 302 303 304 305
/**
 * 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)
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
306 307 308 309
#ifdef MCAST_JOIN_SOURCE_GROUP
    /* Agnostic SSM multicast join */
    int level;
    struct group_source_req gsr;
310

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

314 315
    switch (grp->sa_family)
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
316
#ifdef AF_INET6
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
317
        case AF_INET6:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
318 319 320
        {
            const struct sockaddr_in6 *g6 = (const struct sockaddr_in6 *)grp;

321
            level = SOL_IPV6;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
322 323 324
            assert (grplen >= sizeof (struct sockaddr_in6));
            if (g6->sin6_scope_id != 0)
                gsr.gsr_interface = g6->sin6_scope_id;
325
            break;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
326
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
327
#endif
328 329 330 331 332 333 334 335
        case AF_INET:
            level = SOL_IP;
            break;
        default:
            errno = EAFNOSUPPORT;
            return -1;
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
336 337 338 339 340 341 342
    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;
343

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
344 345
#else
    if (src->sa_family != grp->sa_family)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
346
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
347 348 349
        errno = EAFNOSUPPORT;
        return -1;
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
350

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
351
    switch (grp->sa_family)
352
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
# 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;
372 373
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
374 375 376 377 378
#endif
    msg_Err (obj, "cannot join source multicast group: %m");
    msg_Warn (obj, "trying ASM instead of SSM...");
    return net_Subscribe (obj, fd, grp, grplen);
}
379 380


Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
381 382 383
int net_Subscribe (vlc_object_t *obj, int fd,
                   const struct sockaddr *grp, socklen_t grplen)
{
384 385 386
/* 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
387 388 389
    /* Agnostic SSM multicast join */
    int level;
    struct group_req gr;
390

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
391 392
    memset (&gr, 0, sizeof (gr));
    gr.gr_interface = var_GetIfIndex (obj);
393 394 395 396 397

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
401 402 403 404
            level = SOL_IPV6;
            assert (grplen >= sizeof (struct sockaddr_in6));
            if (g6->sin6_scope_id != 0)
                gr.gr_interface = g6->sin6_scope_id;
405
            break;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
406
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
407
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
408 409 410 411 412 413
        case AF_INET:
            level = SOL_IP;
            break;
        default:
            errno = EAFNOSUPPORT;
            return -1;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
414 415
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
416 417 418 419
    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;
420

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
421 422
#else
    switch (grp->sa_family)
423
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
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 454 455
# 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;
456 457
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
458 459
#endif
    msg_Err (obj, "cannot join multicast group: %m");
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
460 461 462 463
    return -1;
}


Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
464
static int net_SetDSCP( int fd, uint8_t dscp )
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
{
    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));
}

496
#undef net_ConnectDgram
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
497
/*****************************************************************************
498
 * net_ConnectDgram:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
499
 *****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
500 501
 * 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
502
 *****************************************************************************/
503 504
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
505
{
506 507 508
    struct addrinfo hints = {
        .ai_socktype = SOCK_DGRAM,
        .ai_protocol = proto,
509
        .ai_flags = AI_NUMERICSERV | AI_IDN,
510
    }, *res;
511
    int       i_handle = -1;
512
    bool      b_unreach = false;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
513

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

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

519 520
    int val = vlc_getaddrinfo (psz_host, i_port, &hints, &res);
    if (val)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
521
    {
522 523
        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
524 525 526
        return -1;
    }

527
    for (struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
528
    {
529 530
        char *str;
        int fd = net_Socket (p_this, ptr->ai_family, ptr->ai_socktype,
531
                             ptr->ai_protocol);
532
        if (fd == -1)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
533
            continue;
534 535 536 537 538 539 540 541

        /* 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
542

Sebastien Escudier's avatar
Sebastien Escudier committed
543
        if( i_hlim >= 0 )
544
            net_SetMcastHopLimit( p_this, fd, ptr->ai_family, i_hlim );
545

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
546
        str = var_InheritString (p_this, "miface");
547 548
        if (str != NULL)
        {
549
            net_SetMcastOut (p_this, fd, ptr->ai_family, str);
550
            free (str);
551
        }
552

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
555 556 557 558 559 560 561 562 563 564 565 566
        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
567
            b_unreach = true;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
568 569
        else
        {
570
            msg_Warn( p_this, "%s port %d : %m", psz_host, i_port);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
571 572 573 574 575
            net_Close( fd );
            continue;
        }
    }

576
    freeaddrinfo( res );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
577 578 579 580 581 582 583 584 585 586 587 588

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

589
#undef net_OpenDgram
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
590
/*****************************************************************************
591
 * net_OpenDgram:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
592
 *****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
593
 * OpenDgram a datagram socket and return a handle
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
594
 *****************************************************************************/
595
int net_OpenDgram( vlc_object_t *obj, const char *psz_bind, int i_bind,
596
                   const char *psz_server, int i_server, int protocol )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
597
{
598
    if ((psz_server == NULL) || (psz_server[0] == '\0'))
599
        return net_ListenSingle (obj, psz_bind, i_bind, protocol);
600

601 602
    msg_Dbg (obj, "net: connecting to [%s]:%d from [%s]:%d",
             psz_server, i_server, psz_bind, i_bind);
603

604 605 606
    struct addrinfo hints = {
        .ai_socktype = SOCK_DGRAM,
        .ai_protocol = protocol,
607
        .ai_flags = AI_NUMERICSERV | AI_IDN,
608
    }, *loc, *rem;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
609

610
    int val = vlc_getaddrinfo (psz_server, i_server, &hints, &rem);
611
    if (val)
612
    {
613
        msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind,
614
                 gai_strerror (val));
615
        return -1;
616
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
617

618
    hints.ai_flags |= AI_PASSIVE;
619
    val = vlc_getaddrinfo (psz_bind, i_bind, &hints, &loc);
620
    if (val)
621
    {
622
        msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind,
623
                 gai_strerror (val));
624
        freeaddrinfo (rem);
625
        return -1;
626 627
    }

628
    val = -1;
629 630 631
    for (struct addrinfo *ptr = loc; ptr != NULL; ptr = ptr->ai_next)
    {
        int fd = net_Socket (obj, ptr->ai_family, ptr->ai_socktype,
632
                             ptr->ai_protocol);
633 634
        if (fd == -1)
            continue; // usually, address family not supported
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
635

636 637
        fd = net_SetupDgramSocket( obj, fd, ptr );
        if( fd == -1 )
638
            continue;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
639

640
        for (struct addrinfo *ptr2 = rem; ptr2 != NULL; ptr2 = ptr2->ai_next)
641 642 643 644 645
        {
            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
646

647 648 649 650 651
            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))
652
            {
653 654
                msg_Err (obj, "cannot connect to %s port %d: %m",
                         psz_server, i_server);
655
                continue;
656
            }
657 658
            val = fd;
            break;
659
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
660

661 662
        if (val != -1)
            break;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
663

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
664
        net_Close (fd);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
665 666
    }

667 668
    freeaddrinfo (rem);
    freeaddrinfo (loc);
669
    return val;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
670
}
671

672 673 674 675 676 677 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

/**
 * 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
720 721
            if ((recvcov == -1) || (recvcov > 56))
                recvcov = 0;
722
            else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
723
                recvcov = (recvcov + 3) / 4;
724 725 726 727 728 729 730
            if (setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_RECV_CSCOV,
                            &recvcov, sizeof (recvcov)))
                return VLC_EGENERIC;

            return VLC_SUCCESS;
#endif
    }
731 732 733 734
#if !defined( UDPLITE_RECV_CSCOV ) && !defined( DCCP_SOCKOPT_SEND_CSCOV )
    VLC_UNUSED(sendcov);
    VLC_UNUSED(recvcov);
#endif
735 736 737

    return VLC_EGENERIC;
}