[vlc-devel] [PATCH] Add support for AMT multicast tunneling over non-multicast enabled network

Rémi Denis-Courmont remi at remlab.net
Tue Sep 24 09:25:32 CEST 2019


Hi,

Many of those comments were already made before for the same patch set. If you don't want to get dry reviews, don't piss volunteers developers off by ignoring their review comments the first time.

That seems like pretty basic common sense to me, that people get pissed when you make them feel like you are wasting their time.

Le 24 septembre 2019 10:05:54 GMT+03:00, Thomas Guillem <thomas at gllm.fr> a écrit :
>Rémi,
>I find your review quite aggressive. I think that it can demotivate
>external developers.
>A lot of things are very obvious for both of us. We are senior
>developers, working on VLC since a very long time.
>We should focus on spreading our knowledge instead of critizing like
>that.
>
>Nathalie,
>I will help you fixing the remaining part mentioned by Rémi.
>
>On Mon, Sep 23, 2019, at 22:45, Rémi Denis-Courmont wrote:
>> Le tiistaina 17. syyskuuta 2019, 22.28.06 EEST Natalie Landsberg a
>écrit :
>> > Changes:
>> >    1. Fixed TRUE back to true. Dunno why my compiler doesn't catch
>this...
>> >    2. Fixed parantheses for if(unlikely(inet_ntop() ) == NULL) to
>> > if(unlikely(inet_ntop() == NULL) ) - "== NULL" is inside unlikely
>call
>> > correctly
>> >    3. removed GetDWBE where not used on buffer
>> > 
>> > Compiler is finally working again and I pulled latest VLC from git.
>> > Hopefully it lines up with master now. ---
>> >  modules/access/Makefile.am |    4 +
>> >  modules/access/amt.c       | 1434
>++++++++++++++++++++++++++++++++++++
>> >  2 files changed, 1438 insertions(+)
>> >  create mode 100644 modules/access/amt.c
>> > 
>> > diff --git a/modules/access/Makefile.am
>b/modules/access/Makefile.am
>> > index 1b85e8dd18..756ca5cf59 100644
>> > --- a/modules/access/Makefile.am
>> > +++ b/modules/access/Makefile.am
>> > @@ -379,6 +379,10 @@ libudp_plugin_la_SOURCES = access/udp.c
>> >  libudp_plugin_la_LIBADD = $(SOCKET_LIBS)
>> >  access_LTLIBRARIES += libudp_plugin.la
>> > 
>> > +libamt_plugin_la_SOURCES = access/amt.c
>> > +libamt_plugin_la_LIBADD = $(SOCKET_LIBS)
>> > +access_LTLIBRARIES += libamt_plugin.la
>> > +
>> >  libunc_plugin_la_SOURCES = access/unc.c access/smb_common.h
>> >  libunc_plugin_la_LIBADD = -lmpr -lnetapi32
>> >  if HAVE_WIN32
>> > diff --git a/modules/access/amt.c b/modules/access/amt.c
>> > new file mode 100644
>> > index 0000000000..66f15449e9
>> > --- /dev/null
>> > +++ b/modules/access/amt.c
>> > @@ -0,0 +1,1434 @@
>> > +/**
>> > + * @file amt.c
>> > + * @brief Automatic Multicast Tunneling Protocol (AMT) file for
>VLC media
>> > player + * Allows multicast streaming when not in a
>multicast-enabled
>> > network + * Currently IPv4 is supported, but IPv6 is not yet.
>> > + *
>> > + * Copyright (C) 2018 VLC authors and VideoLAN
>> > + * Copyright (c) Juniper Networks, Inc., 2018. All rights
>reserved.
>> > + *
>> > + * Authors: Christophe Massiot <massiot at via.ecp.fr>           -
>original
>> > UDP code + *          Tristan Leteurtre <tooney at via.ecp.fr>        
>    -
>> > original UDP code + *          Laurent Aimar <fenrir at via.ecp.fr>   
>       
>> >      - original UDP code + *          Jean-Paul Saman <jpsaman
>#_at_# m2x
>> > dot nl>       - original UDP code + *          Remi Denis-Courmont 
>       
>> >                      - original UDP code + *          Natalie
>Landsberg
>> > <natalie.landsberg97 at gmail.com> - AMT support + *          Wayne
>Brassem
>> > <wbrassem at rogers.com>               - Added FQDN support + *
>> > + * This code is licensed to you under the GNU Lesser General
>Public License
>> > + * version 2.1 or later. You may not use this code except in
>compliance
>> > with + * the GNU Lesser General Public License.
>> > + * This code is not an official Juniper product.
>> > + *
>> > + * This library 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 (at your option) any later version.
>> > + *
>> > + * This library 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 Lesser General Public License for more details.
>> > + *
>> > + * You should have received a copy of the GNU Lesser General
>Public
>> > + * License along with this library; if not, write to the Free
>Software
>> > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 
>02110-1301 
>> > USA +
>> >
>***************************************************************************
>> > */ +#ifdef HAVE_CONFIG_H
>> > +# include "config.h"
>> > +#endif
>> > +
>> > +#include <errno.h>
>> > +#include <ctype.h>
>> > +#include <assert.h>
>> > +#ifdef HAVE_ARPA_INET_H
>> > +# include <arpa/inet.h>
>> > +#endif
>> > +
>> 
>> > +#ifdef HAVE_SYS_SELECT_H
>> > +# include <sys/select.h>
>> > +#endif
>> 
>> It does not look like this can work.
>> 
>> > +
>> > +#ifdef HAVE_SYS_SOCKET_H
>> > +# include <sys/socket.h>
>> > +#endif
>> > +
>> > +#include <vlc_common.h>
>> > +#include <vlc_demux.h>
>> > +#include <vlc_plugin.h>
>> > +#include <vlc_access.h>
>> > +#include <vlc_network.h>
>> > +#include <vlc_block.h>
>> > +#include <vlc_interrupt.h>
>> > +#include <vlc_url.h>
>> > +
>> > +#ifdef HAVE_POLL
>> > + #include <poll.h>
>> > +#endif
>> > +#ifdef HAVE_SYS_UIO_H
>> > + #include <sys/uio.h>
>> > +#endif
>> > +
>> > +#define BUFFER_TEXT N_("Receive buffer")
>> > +#define BUFFER_LONGTEXT N_("AMT receive buffer size (bytes)" )
>> > +#define TIMEOUT_TEXT N_("Native multicast timeout (sec)")
>> > +#define AMT_TIMEOUT_TEXT N_("AMT timeout (sec)")
>> > +#define AMT_RELAY_ADDRESS N_("AMT relay (IP address or FQDN)")
>> > +#define AMT_RELAY_ADDR_LONG N_("AMT relay anycast address, or
>specify the
>> > relay you want by address or fully qualified domain name") +#define
>> > AMT_DEFAULT_RELAY N_("amt-relay.m2icast.net")
>> > +
>> >
>+/**************************************************************************
>> > *** + * Various Lengths of Msgs or Hdrs
>> > +
>> >
>***************************************************************************
>> > **/ +#define MAC_LEN 6                /* length of generated MAC in
>bytes */
>> > +#define NONCE_LEN 4              /* length of nonce in bytes */
>> > +
>> > +#define MSG_TYPE_LEN 1           /* length of msg type */
>> > +#define RELAY_QUERY_MSG_LEN 48   /* total length of relay query */
>> > +#define RELAY_ADV_MSG_LEN 12     /* length of relay advertisement
>message
>> > */ +#define IGMP_QUERY_LEN 24        /* length of encapsulated IGMP
>query
>> > message */ +#define IGMP_REPORT_LEN 20
>> > +#define AMT_HDR_LEN 2            /* length of AMT header on a
>packet */
>> > +#define IP_HDR_LEN 20            /* length of standard IP header
>*/
>> > +#define IP_HDR_IGMP_LEN 24       /* length of IP header with an
>IGMP report
>> > */ +#define UDP_HDR_LEN 8            /* length of standard UDP
>header */
>> > +#define AMT_REQUEST_MSG_LEN 9
>> > +#define AMT_DISCO_MSG_LEN 8
>> > +
>> >
>+/**************************************************************************
>> > *** + * Different AMT Message Types
>> > +
>> >
>***************************************************************************
>> > **/ +#define AMT_RELAY_DISCO 1       /* relay discovery */
>> > +#define AMT_RELAY_ADV 2         /* relay advertisement */
>> > +#define AMT_REQUEST 3           /* request */
>> > +#define AMT_MEM_QUERY 4         /* membership query */
>> > +#define AMT_MEM_UPD 5           /* membership update */
>> > +#define AMT_MULT_DATA 6         /* multicast data */
>> > +#define AMT_TEARDOWN 7          /* teardown (not currently
>supported) */
>> > +
>> >
>+/**************************************************************************
>> > *** + * Different IGMP Message Types
>> > +
>> >
>***************************************************************************
>> > **/ +#define AMT_IGMPV3_MEMBERSHIP_QUERY_TYPEID 0x11
>> > +#define AMT_IGMPV3_MEMBERSHIP_REPORT_TYPEID 0x22
>> > +/* IGMPv2, interoperability  */
>> > +#define AMT_IGMPV1_MEMBERSHIP_REPORT_TYPEID 0x12
>> > +#define AMT_IGMPV2_MEMBERSHIP_REPORT_TYPEID 0x16
>> > +#define AMT_IGMPV2_MEMBERSHIP_LEAVE_TYPEID 0x17
>> > +
>> > +#define AMT_IGMP_INCLUDE 0x01
>> > +#define AMT_IGMP_EXCLUDE 0x02
>> > +#define AMT_IGMP_INCLUDE_CHANGE 0x03
>> > +#define AMT_IGMP_EXCLUDE_CHANGE 0x04
>> > +#define AMT_IGMP_ALLOW 0x05
>> > +#define AMT_IGMP_BLOCK 0x06
>> > +
>> > +#define MCAST_ANYCAST  "0.0.0.0"
>> > +#define MCAST_ALLHOSTS "224.0.0.22"
>> > +#define LOCAL_LOOPBACK "127.0.0.1"
>> > +#define AMT_PORT 2268
>> > +
>> > +#define DEFAULT_MTU (1500u - (20 + 8))
>> > +#define MAX_IPV4_UDP (65535u - (20 + 8))
>> > +
>> > +/* IPv4 Header Format */
>> > +typedef struct _amt_ip {
>> > +    uint8_t  ver_ihl;
>> > +    uint8_t  tos;
>> > +    uint16_t tot_len;
>> > +    uint16_t id;
>> > +    uint16_t frag_off;
>> > +    uint8_t  ttl;
>> > +    uint8_t  protocol;
>> > +    uint16_t check;
>> > +    uint32_t srcAddr;
>> > +    uint32_t destAddr;
>> > +} amt_ip_t;
>> > +
>> > +/* IPv4 Header Format with options field */
>> > +typedef struct _amt_ip_alert {
>> > +    uint8_t  ver_ihl;
>> > +    uint8_t  tos;
>> > +    uint16_t tot_len;
>> > +    uint16_t id;
>> > +    uint16_t frag_off;
>> > +    uint8_t  ttl;
>> > +    uint8_t  protocol;
>> > +    uint16_t check;
>> > +    uint32_t srcAddr;
>> > +    uint32_t destAddr;
>> > +    uint32_t options;
>> > +} amt_ip_alert_t;
>> > +
>> > +/* IGMPv3 Group Record Format (RFC3376) */
>> > +typedef struct _amt_igmpv3_groupRecord {
>> > +    uint8_t  type;
>> > +    uint8_t  auxDatalen;
>> > +    uint16_t nSrc;
>> > +    uint32_t ssm;
>> > +    uint32_t srcIP[1];
>> > +} amt_igmpv3_groupRecord_t;
>> > +
>> > +/* IGMPv3 Membership Report Format (RFC3376) */
>> > +typedef struct _amt_igmpv3_membership_report {
>> > +    uint8_t  type;
>> > +    uint8_t  resv;
>> > +    uint16_t checksum;
>> > +    uint16_t resv2;
>> > +    uint16_t nGroupRecord;
>> > +    amt_igmpv3_groupRecord_t grp[1];
>> > +} amt_igmpv3_membership_report_t;
>> > +
>> > +/* IGMPv3 Membership Query Format (RFC3376) */
>> > +typedef struct _amt_igmpv3_membership_query {
>> > +    uint8_t  type;
>> > +    uint8_t  max_resp_code;  /* in 100ms, Max Resp Time = (mant |
>0x10) <<
>> > (exp + 3) */ +    uint32_t checksum;
>> > +    uint32_t ssmIP;
>> > +    uint8_t  s_qrv;
>> > +    uint8_t  qqic;           /* in second, query Time = (mant |
>0x10) <<
>> > (exp + 3) */ +    uint16_t nSrc;
>> > +    uint32_t srcIP[1];
>> > +} amt_igmpv3_membership_query_t;
>> > +
>> > +/* ATM Membership Update Format (RFC7450) */
>> > +typedef struct _amt_membership_update_msg {
>> > +    amt_ip_alert_t ipHead;
>> > +    amt_igmpv3_membership_report_t memReport;
>> > +} amt_membership_update_msg_t;
>> > +
>> > +/* AMT Functions */
>> > +static int amt_sockets_init( stream_t *p_access );
>> > +static void amt_send_relay_discovery_msg( stream_t *p_access, char
>> > *relay_ip ); +static void amt_send_relay_request( stream_t
>*p_access, char
>> > *relay_ip ); +static int amt_joinSSM_group( stream_t *p_access );
>> > +static int amt_joinASM_group( stream_t *p_access );
>> > +static int amt_leaveASM_group( stream_t *p_access );
>> > +static int amt_leaveSSM_group( stream_t *p_access );
>> > +static bool amt_rcv_relay_adv( stream_t *p_access );
>> > +static bool amt_rcv_relay_mem_query( stream_t *p_access );
>> > +static void amt_send_mem_update( stream_t *p_access, char
>*relay_ip, bool
>> > leave ); +static bool open_amt_tunnel( stream_t *p_access );
>> > +static void *amt_mem_upd( void *data );
>> > +
>> > +/* Struct to hold AMT state */
>> > +typedef struct _access_sys_t
>> > +{
>> > +    char *mcastGroup;
>> > +    char *mcastSrc;
>> > +    char *relay;
>> > +    char relayDisco[INET_ADDRSTRLEN];
>> > +    block_t *overflow_block;
>> > +
>> > +    vlc_thread_t updateThread;
>> > +    vlc_tick_t queryTime;
>> > +
>> > +    /* Mulicast group and source */
>> > +    struct sockaddr_in mcastGroupAddr;
>> > +    struct sockaddr_in mcastSrcAddr;
>> > +
>> > +    /* AMT relay imformation */
>> > +    struct sockaddr_in relayDiscoAddr;
>> > +    struct sockaddr_in relayAddr;
>> > +    struct sockaddr_in stLocalAddr;
>> > +    struct sockaddr_in stSvrAddr;
>> > +
>> > +    /* AMT Relay Membership Query data (RFC7450) */
>> > +    struct relay_mem_query_msg_t {
>> > +        uint32_t ulRcvedNonce;
>> > +        uint8_t  type;
>> > +        uint8_t  uchaMAC[MAC_LEN];
>> > +        uint8_t  uchaIGMP[IGMP_QUERY_LEN];
>> > +    } relay_mem_query_msg;
>> > +
>> > +    /* AMT Relay Advertisement data (RFC7450) */
>> > +    struct relay_adv_msg_t {
>> > +        uint32_t ulRcvNonce;
>> > +        uint32_t ipAddr;
>> > +        uint8_t  type;
>> > +    } relay_adv_msg;
>> > +
>> > +    amt_ip_t relay_ip_hdr;
>> > +    amt_igmpv3_membership_query_t relay_igmp_query;
>> > +    ssize_t mtu;
>> > +
>> > +    uint32_t glob_ulNonce;
>> > +    uint32_t ulRelayNonce;
>> > +
>> > +    int fd;
>> > +    int sAMT;
>> > +    int sQuery;
>> > +    int timeout;
>> > +    int amtTimeout;
>> > +
>> > +    bool tryAMT;
>> > +    bool threadReady;
>> > +} access_sys_t;
>> 
>> Remove write-only and unused members.
>> 
>> > +
>> > +/* Standard open/close functions */
>> > +static int  Open (vlc_object_t *);
>> > +static void Close (vlc_object_t *);
>> > +
>> > +/* Utility functions */
>> > +static unsigned short get_checksum( unsigned short *buffer, int
>nLen );
>> > +static void make_report( amt_igmpv3_membership_report_t *mr );
>> > +static void make_ip_header( amt_ip_alert_t *p_ipHead );
>> > +
>> > +vlc_module_begin ()
>> > +    set_shortname( N_("AMT" ) )
>> > +    set_description( N_("AMT input") )
>> > +    set_category( CAT_INPUT )
>> > +    set_subcategory( SUBCAT_INPUT_ACCESS )
>> > +
>> > +    add_integer( "amt-timeout", 5, AMT_TIMEOUT_TEXT, NULL, true )
>> > +    add_integer( "amt-native-timeout", 5, TIMEOUT_TEXT, NULL, true
>)
>> > +    add_string( "amt-relay", AMT_DEFAULT_RELAY, AMT_RELAY_ADDRESS,
>> > AMT_RELAY_ADDR_LONG, true ) +
>> > +    set_capability( "access", 0 )
>> > +    add_shortcut( "amt" )
>> > +
>> > +    set_callbacks( Open, Close )
>> > +vlc_module_end ()
>> > +
>> >
>+/**************************************************************************
>> > *** + * Local prototypes
>> > +
>> >
>***************************************************************************
>> > **/ +static block_t *BlockUDP( stream_t *, bool * );
>> > +static int Control( stream_t *, int, va_list );
>> > +
>> >
>+/**************************************************************************
>> > *** + * Open: Open a connection to the multicast feed
>> > +
>> >
>***************************************************************************
>> > **/ +static int Open( vlc_object_t *p_this )
>> > +{
>> > +    stream_t            *p_access = (stream_t*) p_this;
>> > +    access_sys_t        *sys = NULL;
>> > +    struct addrinfo      hints, *serverinfo = NULL;
>> > +    struct sockaddr_in  *server_addr;
>> > +    char                *psz_name = NULL, *saveptr, *psz_strtok_r,
>> > ip_buffer[INET_ADDRSTRLEN]; +    int                  i_bind_port =
>1234,
>> > i_server_port = 0, VLC_ret = VLC_SUCCESS, response; +    vlc_url_t 
>       
>> >   url;
>> > +
>> > +    if( p_access->b_preparsing )
>> > +        return VLC_EGENERIC;
>> > +
>> > +    /* Allocate the structure for holding AMT info and zeroize it
>*/
>> > +    sys = vlc_obj_calloc( p_this, 1, sizeof( *sys ) );
>> > +    if( unlikely( sys == NULL ) )
>> > +        return VLC_ENOMEM;
>> > +
>> > +    /* The standard MPEG-2 transport is 188 bytes.  7 packets fit
>into a
>> > standard 1500 byte Ethernet frame */ +    sys->mtu = 7 * 188;
>> > +
>> > +    /* Protective packet overflow buffer designed to accommodate
>maximum
>> > IPv4 UDP payload minus the anticapated MTU */ +   
>sys->overflow_block =
>> > block_Alloc(MAX_IPV4_UDP - sys->mtu);
>> > +    if( unlikely( sys->overflow_block == NULL ) )
>> > +        return VLC_ENOMEM;
>> > +
>> > +    p_access->p_sys = sys;
>> > +
>> > +    /* Set up p_access */
>> > +    ACCESS_SET_CALLBACKS( NULL, BlockUDP, Control, NULL );
>> > +
>> > +    if( !p_access->psz_location )
>> > +        return VLC_EGENERIC;
>> > +
>> > +    psz_name = strdup( p_access->psz_location );
>> > +    if ( unlikely( psz_name == NULL ) )
>> > +        return VLC_ENOMEM;
>> > +
>> > +    /* Parse psz_name syntax :
>> > +     * [serveraddr[:serverport]][@[bindaddr]:[bindport]] */
>> > +    if( vlc_UrlParse( &url, p_access->psz_url ) != 0 )
>> > +    {
>> > +        msg_Err( p_access, "Invalid URL: %s", p_access->psz_url );
>> > +        VLC_ret = VLC_EGENERIC;
>> > +        goto cleanup;
>> > +    }
>> > +
>> > +    /* Determining the multicast source and group depends on the
>URL
>> > provided */ +    /*                                                
>       
>> >                */ +    /* The address(es) in the URL can be in the
>form of
>> > IP address or FQDN    */ +    /* By calling vlc_getaaddrinfo() you
>get it
>> > in IP form either way         */ +    /*                           
>       
>> >                                     */ +    /* Case 1:
>> > amt://<source-ip-address>@<multicast-group-ip-address>         */ +
>   /*  
>> >                                                                    
> */ +  
>> >  /*         sys->mcastSrc = <source-ip-address>                    
>      
>> > */ +    /*         sys->mcastSrcAddr = inet_pton( sys->mcastSrc )  
>       
>> >       */ +    /*                                                   
>       
>> >             */ +    /*         sys->mcastGroup =
>> > <multicast-group-ip-address>                 */ +    /*        
>> > sys->mcastGroupAddr = inet_pton( sys->mcastGroup )             */ +
>   /*  
>> >                                                                    
> */ +  
>> >  /* Case 2: amt://<multicast-group-ip-address>                     
>      
>> > */ +    /*                                                         
>       
>> >       */ +    /*         sys->mcastSrc = MCAST_ANYCAST = "0.0.0.0" 
>       
>> >             */ +    /*         sys->mcastSrcAddr = inet_pton(
>sys->mcastSrc
>> > ) = 0             */ +    /*                                       
>       
>> >                         */ +    /*         sys->mcastGroup =
>> > <multicast-group-ip-address>                 */ +    /*        
>> > sys->mcastGroupAddr = inet_pton( sys->mcastGroup )             */ +
>   /*  
>> >                                                                    
> */ +
>> > +    /* If UDP port provided then assign port to stream */
>> > +    if( url.i_port > 0 )
>> > +        i_bind_port = url.i_port;
>> > +
>> > +    msg_Dbg( p_access, "Opening multicast: %s:%d local=%s:%d",
>> > url.psz_host, i_server_port, url.psz_path, i_bind_port ); +
>> > +    /* Initialize hints prior to call to vlc_getaddrinfo with
>either IP
>> > address or FQDN */ +    memset( &hints, 0, sizeof( hints ));
>> > +    hints.ai_family = AF_INET;  /* Setting to AF_UNSPEC accepts
>both IPv4
>> > and IPv6 */ +    hints.ai_socktype = SOCK_DGRAM;
>> > +
>> > +    /* Retrieve list of multicast addresses matching the multicast
>group
>> > identifier */ +    response = vlc_getaddrinfo( url.psz_host,
>AMT_PORT,
>> > &hints, &serverinfo ); +
>> > +    /* If an error returned print reason and exit */
>> > +    if( response != 0 )
>> > +    {
>> > +        msg_Err( p_access, "Could not find multicast group %s,
>reason: %s",
>> > url.psz_host, gai_strerror(response) ); +        VLC_ret =
>VLC_EGENERIC;
>> > +        goto cleanup;
>> > +    }
>> > +
>> > +    /* Convert binary socket address to string */
>> > +    server_addr = (struct sockaddr_in *) serverinfo->ai_addr;
>> > +    if( unlikely( inet_ntop(AF_INET, &(server_addr->sin_addr),
>ip_buffer,
>> > INET_ADDRSTRLEN) == NULL ) ) +    {
>> > +        int errConv = errno;
>> > +        msg_Err(p_access, "Could not convert binary socket address
>to
>> > string: %s", gai_strerror(errConv)); +        goto cleanup;
>> > +    }
>> > +
>> > +    /* Store the binary socket representation of multicast group
>address */
>> > +    sys->mcastGroupAddr = *server_addr;
>> > +
>> > +    /* Release the allocated memory */
>> > +    freeaddrinfo( serverinfo );
>> > +    serverinfo = NULL;
>> > +
>> > +    /* Store string representation */
>> > +    sys->mcastGroup = strdup( ip_buffer );
>> 
>> You really should not do that; there are no reasons why you need to
>keep this 
>> as a string. This is not the first time this problem is raised in
>this very 
>> patchset, nor is this the only occurence.
>> 
>> > +    if( unlikely( sys->mcastGroup == NULL ) )
>> > +    {
>> > +        VLC_ret = VLC_ENOMEM;
>> > +        goto cleanup;
>> > +    }
>> > +
>> > +    msg_Dbg( p_access, "Setting multicast group address to %s",
>> > sys->mcastGroup); +
>> > +    /* Extract the source from the URL, or the multicast group
>when no
>> > source is provided */
>> > +    if ( !( psz_strtok_r = strtok_r( psz_name, "@",
>> > &saveptr ) ) ) +    {
>> 
>> DO NOT use assignment as expression unless syntactically unavoidable.
>> 
>> > +        msg_Err( p_access, "Could not parse location %s",
>psz_name);
>> > +        VLC_ret = VLC_EGENERIC;
>> > +        goto cleanup;
>> > +    }
>> > +
>> > +    /* Store the string representation */
>> > +    sys->mcastSrc = strdup( psz_strtok_r );
>> > +    if( unlikely( sys->mcastSrc == NULL ) )
>> > +    {
>> > +        VLC_ret = VLC_ENOMEM;
>> > +        goto cleanup;
>> > +    }
>> > +
>> > +    /* If strings are equal then no multicast source has been
>specified, so
>> > try anycast */ +    if( strcmp( url.psz_host, sys->mcastSrc ) == 0
>)
>> > +    {
>> > +        free( sys->mcastSrc );
>> > +        sys->mcastSrc = strdup(MCAST_ANYCAST);
>> > +        sys->mcastSrcAddr.sin_addr.s_addr = 0;
>> > +        msg_Dbg( p_access, "No multicast source address specified,
>trying
>> > ASM..."); +    }
>> > +
>> > +    /* retrieve list of source addresses matching the multicast
>source
>> > identifier */ +    response = vlc_getaddrinfo( sys->mcastSrc,
>AMT_PORT,
>> > &hints, &serverinfo ); +
>> > +    /* If an error returned print reason and exit */
>> > +    if( response != 0 )
>> > +    {
>> > +        msg_Err( p_access, "Could not find multicast source %s,
>reason:
>> > %s", sys->mcastSrc, gai_strerror(response) ); +        VLC_ret =
>> > VLC_EGENERIC;
>> > +        goto cleanup;
>> > +    }
>> > +
>> > +    /* Convert binary socket address to string */
>> > +    server_addr = (struct sockaddr_in *) serverinfo->ai_addr;
>> > +    if( unlikely( inet_ntop(AF_INET, &(server_addr->sin_addr),
>ip_buffer,
>> > INET_ADDRSTRLEN) == NULL ) ) +    {
>> > +        int errConv = errno;
>> > +        msg_Err(p_access, "Could not binary socket address to
>string: %s",
>> > gai_strerror(errConv)); +        goto cleanup;
>> > +    }
>> > +
>> > +    /* Store the binary socket representation of multicast source
>address
>> > */ +    sys->mcastSrcAddr = *server_addr;
>> > +
>> > +    /* free the original source address buffer (IP or FQDN) and
>assign it
>> > just the IP address */ +    free( sys->mcastSrc );
>> > +
>> > +    /* Store string representation */
>> > +    sys->mcastSrc = strdup( ip_buffer );
>> > +    if( unlikely( sys->mcastSrc == NULL ) )
>> > +    {
>> > +        VLC_ret = VLC_ENOMEM;
>> > +        goto cleanup;
>> > +    }
>> > +
>> > +    msg_Dbg( p_access, "Setting multicast source address to %s",
>> > sys->mcastSrc); +
>> > +    /* Pull the AMT relay address from the settings */
>> > +    sys->relay = var_InheritString( p_access, "amt-relay" );
>> > +    if( unlikely( sys->relay == NULL ) )
>> > +    {
>> > +        msg_Err( p_access, "No relay anycast or unicast address
>specified."
>> > ); +        VLC_ret = VLC_EGENERIC;
>> > +        goto cleanup;
>> > +    }
>> > +
>> > +    msg_Dbg( p_access, "Addresses: mcastGroup: %s mcastSrc: %s
>relay: %s",
>> > \ +             sys->mcastGroup, sys->mcastSrc, sys->relay);
>> > +
>> > +    /* Native multicast file descriptor */
>> > +    sys->fd = net_OpenDgram( p_access, sys->mcastGroup,
>i_bind_port,
>> > +                             sys->mcastSrc, i_server_port,
>IPPROTO_UDP );
>> > +    if( sys->fd == -1 )
>> > +    {
>> > +        msg_Err( p_access, "cannot open socket" );
>> > +        sys->sAMT = -1;
>> > +        sys->sQuery = -1;
>> > +        VLC_ret = VLC_EGENERIC;
>> > +        goto cleanup;
>> > +    }
>> > +
>> > +    sys->timeout = var_InheritInteger( p_access,
>"amt-native-timeout");
>> > +    if( sys->timeout > 0)
>> > +        sys->timeout *= 1000;
>> > +
>> > +    sys->amtTimeout = var_InheritInteger( p_access, "amt-timeout"
>);
>> > +    if( sys->amtTimeout > 0)
>> > +        sys->amtTimeout *= 1000;
>> > +
>> > +    sys->tryAMT = false;
>> > +    sys->threadReady = false;
>> > +
>> > +cleanup: /* fall through */
>> > +
>> > +    /* release the allocated memory */
>> > +    free( psz_name );
>> > +    vlc_UrlClean( &url );
>> > +    if( serverinfo )
>> > +        freeaddrinfo( serverinfo );
>> > +
>> > +    /* if an error occurred free the memory */
>> > +    if ( VLC_ret != VLC_SUCCESS )
>> > +    {
>> > +        msg_Dbg( p_access, "Open(): Before free( sys-> sys->Addr )
>#4" );
>> > +        free( sys->mcastGroup );
>> > +        free( sys->mcastSrc );
>> > +    }
>> > +
>> > +    return VLC_ret;
>> > +}
>> > +
>> >
>+/**************************************************************************
>> > *** + * Close: Cancel thread and free data structures
>> > +
>> >
>***************************************************************************
>> > **/ +static void Close( vlc_object_t *p_this )
>> > +{
>> > +    stream_t     *p_access = (stream_t*)p_this;
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +
>> > +    msg_Dbg( p_access, "Closing AMT plugin" );
>> > +
>> > +    /* If using AMT tunneling send leave message and free the
>relay
>> > addresses */ +    if ( sys->tryAMT )
>> > +    {
>> > +        /* Prepare socket options */
>> > +        if( sys->mcastSrcAddr.sin_addr.s_addr )
>> > +            amt_leaveSSM_group( p_access );
>> > +        else
>> > +            amt_leaveASM_group( p_access );
>> > +
>> > +        /* Send IGMP leave message */
>> > +        amt_send_mem_update( p_access, sys->relayDisco, true );
>> > +
>> > +        free( sys->relay );
>> > +    }
>> > +
>> > +    /* If overflow block allocated then free the memory */
>> > +    if( sys->overflow_block )
>> > +        block_Release( sys->overflow_block );
>> > +
>> > +    /* If membership thread spawned cancel it */
>> > +    if( sys->threadReady )
>> > +    {
>> > +        msg_Dbg( p_access, "Canceling IGMP membership thread" );
>> > +        sys->threadReady = false;
>> > +        vlc_cancel( sys->updateThread );
>> > +        vlc_join( sys->updateThread, NULL );
>> > +    }
>> > +
>> > +    if( sys->mcastGroup )
>> > +        free( sys->mcastGroup );
>> > +    if( sys->mcastSrc )
>> > +        free( sys->mcastSrc );
>> > +
>> > +    net_Close( sys->fd );
>> > +    if( sys->sAMT != -1 )
>> > +        net_Close( sys->sAMT );
>> > +    if( sys->sQuery != -1 )
>> > +        net_Close( sys->sQuery );
>> > +
>> > +    msg_Dbg( p_access, "Exiting AMT plugin" );
>> > +}
>> > +
>> >
>+/**************************************************************************
>> > *** + * Control: Define stream controls
>> > +
>> >
>***************************************************************************
>> > **/ +static int Control( stream_t *p_access, int i_query, va_list
>args ) +{
>> > +    switch( i_query )
>> > +    {
>> > +        case STREAM_CAN_SEEK:
>> > +        case STREAM_CAN_FASTSEEK:
>> > +        case STREAM_CAN_PAUSE:
>> > +        case STREAM_CAN_CONTROL_PACE:
>> > +            *va_arg( args, bool * ) = false;
>> > +            break;
>> > +
>> > +        case STREAM_GET_PTS_DELAY:
>> > +            *va_arg( args, vlc_tick_t * ) =
>> > +                VLC_TICK_FROM_MS(var_InheritInteger( p_access,
>> > "network-caching" )); +            break;
>> > +
>> > +        default:
>> > +            return VLC_EGENERIC;
>> > +    }
>> > +
>> > +    return VLC_SUCCESS;
>> > +}
>> > +
>> >
>+/**************************************************************************
>> > *** + * BlockUDP: Responsible for returning the multicast payload
>> > + * BlockUDP: Sections of code are based upon BlockUDP code in
>udp.c
>> > + *
>> > + * Default MTU based on number of MPEG-2 transports carried in a
>1500 byte
>> > Ethernet frame + * however the code is able to receive maximal IPv4
>UDP
>> > frames and then adjusts the MTU +
>> >
>***************************************************************************
>> > **/ +static block_t *BlockUDP(stream_t *p_access, bool *restrict
>eof)
>> > +{
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +    ssize_t len = 0, shift = 0, tunnel = IP_HDR_LEN + UDP_HDR_LEN
>+
>> > AMT_HDR_LEN; +
>> > +    /* Allocate anticipated MTU buffer for holding the UDP packet
>suitable
>> > for native or AMT tunneled multicast */ +    block_t *pkt =
>block_Alloc(
>> > sys->mtu + tunnel );
>> > +    if ( unlikely( pkt == NULL ) )
>> > +        return NULL;
>> > +
>> > +    /* Structure initialized to hold anticipated MTU buffer along
>with
>> > protective overflow buffer */ +    struct iovec iov[] = {{
>> > +        .iov_base = pkt->p_buffer,
>> > +        .iov_len = sys->mtu + tunnel,
>> > +    },{
>> > +        .iov_base = sys->overflow_block->p_buffer,
>> > +        .iov_len = sys->overflow_block->i_buffer,
>> > +    }};
>> > +
>> > +    /* References the two element array above to be passed into
>recvmsg */
>> > +    struct msghdr msg = {
>> > +        .msg_iov = iov,
>> > +        .msg_iovlen = 2,
>> > +    };
>> > +
>> > +    struct pollfd ufd[1];
>> > +
>> > +    if( sys->tryAMT )
>> > +        ufd[0].fd = sys->sAMT; /* AMT tunneling file descriptor */
>> > +    else
>> > +        ufd[0].fd = sys->fd;   /* Native multicast file descriptor
>*/
>> > +    ufd[0].events = POLLIN;
>> > +
>> > +    switch (vlc_poll_i11e(ufd, 1, sys->timeout))
>> > +    {
>> > +        case 0:
>> > +            if( !sys->tryAMT )
>> > +            {
>> > +                msg_Err(p_access, "Native multicast receive
>time-out");
>> > +                if( !open_amt_tunnel( p_access ) )
>> > +                    goto error;
>> > +                break;
>> > +            }
>> > +            else
>> > +            {
>> > +                *eof = true;
>> > +            }
>> > +            /* fall through */
>> > +        case -1:
>> > +            goto error;
>> > +    }
>> > +
>> > +    /* If using AMT tunneling perform basic checks and point to
>beginning
>> > of the payload */
>> > +    if( sys->tryAMT )
>> > +    {
>> > +        len = recvmsg( sys->sAMT, &msg, 0 );
>> 
>> Why do you use recvmsg()?
>> 
>> > +
>> > +        /* Check for the integrity of the received AMT packet */
>> > +        if( len < 0 || *(pkt->p_buffer) != AMT_MULT_DATA )
>> > +            goto error;
>> > +
>> > +        /* Set the offet to the first byte of the payload */
>> > +        shift += tunnel;
>> > +
>> > +        /* If the length received is less than the AMT tunnel
>header then
>> > it's truncated */ +        if( len < tunnel )
>> > +        {
>> > +            msg_Err(p_access, "%zd bytes packet truncated (MTU was
>%zd)",
>> > len, sys->mtu); +            pkt->i_flags |= BLOCK_FLAG_CORRUPTED;
>> > +        }
>> > +
>> > +        /* Otherwise subtract the length of the AMT encapsulation
>from the
>> > packet received */ +        else
>> > +        {
>> > +            len -= tunnel;
>> > +        }
>> > +    }
>> > +    /* Otherwise pull native multicast */
>> > +    else
>> > +    {
>> > +        len = recvmsg(sys->fd, &msg, 0);
>> > +    }
>> > +
>> > +    /* If the payload length is greater than the MTU then the
>overflow
>> > buffer was utilized */ +    if ( unlikely( len > sys->mtu ) )
>> > +    {
>> > +        msg_Warn(p_access, "%zd bytes packet received (MTU was
>%zd),
>> > adjusting mtu", len, sys->mtu); +
>> > +        block_t *gather_block = sys->overflow_block;
>> > +
>> > +        /* Allocate a new overflow buffer based on the received
>payload
>> > length */ +        sys->overflow_block = block_Alloc(MAX_IPV4_UDP -
>len);
>> > +
>> > +        /* Set number of bytes consumed in the overflow block */
>> > +        gather_block->i_buffer = len - sys->mtu;
>> > +
>> > +        /* Chain the anticipated packet and overflow buffers, copy
>into a
>> > single buffer and free the chain */ +        pkt->p_next =
>gather_block;
>> > +        pkt = block_ChainGather( pkt );
>> > +
>> > +        /* Adjust the anticipated MTU to match the payload
>received */
>> > +        sys->mtu = len;
>> > +    }
>> > +
>> > +    /* Set the offset to payload start */
>> > +    pkt->p_buffer += shift;
>> > +    pkt->i_buffer -= shift;
>> > +
>> > +    return pkt;
>> > +
>> > +error:
>> > +    block_Release( pkt );
>> > +    return NULL;
>> > +}
>> > +
>> >
>+/**************************************************************************
>> > *** + * open_amt_tunnel: Create an AMT tunnel to the AMT relay
>> > +
>> >
>***************************************************************************
>> > **/ +static bool open_amt_tunnel( stream_t *p_access )
>> > +{
>> > +    struct addrinfo hints, *serverinfo, *server;
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +
>> > +    memset( &hints, 0, sizeof( hints ));
>> > +    hints.ai_family = AF_INET;  /* Setting to AF_UNSPEC accepts
>both IPv4
>> > and IPv6 */ +    hints.ai_socktype = SOCK_DGRAM;
>> > +
>> > +    msg_Dbg( p_access, "Attempting AMT to %s...", sys->relay);
>> > +    sys->tryAMT = true;
>> > +
>> > +    /* Retrieve list of addresses matching the AMT relay */
>> > +    int response = vlc_getaddrinfo( sys->relay, AMT_PORT, &hints,
>> > &serverinfo ); +
>> > +    /* If an error returned print reason and exit */
>> > +    if( response != 0 )
>> > +    {
>> > +        msg_Err( p_access, "Could not find relay %s, reason: %s",
>> > sys->relay, gai_strerror(response) ); +        goto error;
>> > +    }
>> > +
>> > +    /* Iterate through the list of sockets to find one that works
>*/
>> > +    for (server = serverinfo; server != NULL; server =
>server->ai_next)
>> > +    {
>> > +        struct sockaddr_in *server_addr = (struct sockaddr_in *)
>> > server->ai_addr; +        char relay_ip[INET_ADDRSTRLEN];
>> > +
>> > +        /* Convert to binary representation */
>> > +        if( unlikely( inet_ntop(AF_INET, &(server_addr->sin_addr),
>> > relay_ip, INET_ADDRSTRLEN) == NULL ) ) +        {
>> > +            int errConv = errno;
>> > +            msg_Err(p_access, "Could not convert relay ip to
>binary
>> > representation: %s", gai_strerror(errConv)); +            goto
>error;
>> > +        }
>> > +
>> > +        /* Store string representation */
>> > +        memcpy(sys->relayDisco, relay_ip, INET_ADDRSTRLEN);
>> > +        if( unlikely( sys->relayDisco == NULL ) )
>> > +        {
>> > +            goto error;
>> > +        }
>> > +
>> > +        msg_Dbg( p_access, "Trying AMT Server: %s",
>sys->relayDisco);
>> > +
>> > +        /* Store the binary representation */
>> > +        sys->relayDiscoAddr.sin_addr = server_addr->sin_addr;
>> > +
>> > +        /* If can't open socket try any others in list */
>> > +        if( amt_sockets_init( p_access ) != 0 )
>> > +            msg_Err( p_access, "Error initializing socket to %s",
>relay_ip
>> > ); +
>> > +        /* Otherwise negotiate with AMT relay and confirm you can
>pull a
>> > UDP packet  */ +        else
>> > +        {
>> > +            amt_send_relay_discovery_msg( p_access, relay_ip );
>> > +            msg_Dbg( p_access, "Sent relay AMT discovery message
>to %s",
>> > relay_ip ); +
>> > +            if( !amt_rcv_relay_adv( p_access ) )
>> > +            {
>> > +                msg_Err( p_access, "Error receiving AMT relay
>advertisement
>> > msg from %s, skipping", relay_ip ); +                goto error;
>> > +            }
>> > +            msg_Dbg( p_access, "Received AMT relay advertisement
>from %s",
>> > relay_ip ); +
>> > +            amt_send_relay_request( p_access, relay_ip );
>> > +            msg_Dbg( p_access, "Sent AMT relay request message to
>%s",
>> > relay_ip ); +
>> > +            if( !amt_rcv_relay_mem_query( p_access ) )
>> > +            {
>> > +                msg_Err( p_access, "Could not receive AMT relay
>membership
>> > query from %s, reason: %s", relay_ip, vlc_strerror(errno)); +      
>       
>> >  goto error;
>> > +            }
>> > +            msg_Dbg( p_access, "Received AMT relay membership
>query from
>> > %s", relay_ip ); +
>> > +            /* If single source multicast send SSM join */
>> > +            if( sys->mcastSrcAddr.sin_addr.s_addr )
>> > +            {
>> > +                if( amt_joinSSM_group( p_access ) != 0 )
>> > +                {
>> > +                    msg_Err( p_access, "Error joining SSM %s",
>> > vlc_strerror(errno) ); +                    goto error;
>> > +                }
>> > +                msg_Dbg( p_access, "Joined SSM src: %s group: %s",
>> > sys->mcastSrc, sys->mcastGroup ); +            }
>> > +
>> > +            /* If any source multicast send ASM join */
>> > +            else {
>> > +                if( amt_joinASM_group( p_access ) != 0 )
>> > +                {
>> > +                    msg_Err( p_access, "Error joining ASM %s",
>> > vlc_strerror(errno) ); +                    goto error;
>> > +                }
>> > +                msg_Dbg( p_access, "Joined ASM group: %s",
>sys->mcastGroup
>> > ); +            }
>> > +
>> > +            bool eof=false;
>> > +            block_t *pkt;
>> > +
>> > +            /* Confirm that you can pull a UDP packet from the
>socket */
>> > +            if ( !(pkt = BlockUDP( p_access, &eof )) )
>> > +                msg_Err( p_access, "Unable to receive UDP packet
>from AMT
>> > relay %s for multicast group %s", relay_ip, sys->mcastGroup ); +   
>       
>> > else
>> > +            {
>> > +                block_Release( pkt );
>> > +                msg_Dbg( p_access, "Got UDP packet from multicast
>group %s
>> > via AMT relay %s, continuing...", sys->mcastGroup, relay_ip ); +   
>       
>> >     break;   /* found an active server sending UDP packets, so exit
>loop */
>> > +           }
>> > +        }
>> > +    }
>> > +
>> > +    /* if server is NULL then no AMT relay is responding */
>> > +    if (server == NULL)
>> > +    {
>> > +        msg_Err( p_access, "No AMT servers responding" );
>> > +        goto error;
>> > +    }
>> > +
>> > +    sys->queryTime = vlc_tick_now() / CLOCK_FREQ;
>> > +
>> > +    /* release the allocated memory */
>> > +    freeaddrinfo( serverinfo );
>> > +    return true;
>> > +
>> > +error:
>> > +    sys->threadReady = false;
>> > +    if( serverinfo )
>> > +        freeaddrinfo( serverinfo );
>> > +    return false;
>> > +}
>> > +
>> > +/**
>> > + * Calculate checksum
>> > + * */
>> > +static unsigned short get_checksum( unsigned short *buffer, int
>nLen )
>> > +{
>> > +    int nleft = nLen;
>> > +    int sum = 0;
>> > +    unsigned short *w = buffer;
>> > +    unsigned short answer = 0;
>> > +
>> > +    while (nleft > 1)
>> > +    {
>> > +        sum += *w++;
>> > +        nleft -= 2;
>> > +    }
>> > +    if (nleft == 1)
>> > +    {
>> > +        *(unsigned char*)(&answer) = *(unsigned char*)w;
>> > +        sum += answer;
>> > +    }
>> > +    sum = (sum >> 16) + (sum & 0xffff);
>> > +    answer = ~sum;
>> > +    return (answer);
>> > +}
>> > +
>> > +/**
>> > + * Make IGMP Membership report
>> > + * */
>> > +static void make_report( amt_igmpv3_membership_report_t *mr )
>> > +{
>> > +    mr->type = AMT_IGMPV3_MEMBERSHIP_REPORT_TYPEID;
>> > +    mr->resv = 0;
>> > +    mr->checksum = 0;
>> > +    mr->resv2 = 0;
>> > +    mr->nGroupRecord = htons(1);
>> > +}
>> > +
>> > +/**
>> > + * Make IP header
>> > + * */
>> > +static void make_ip_header( amt_ip_alert_t *p_ipHead )
>> > +{
>> > +    p_ipHead->ver_ihl = 0x46;
>> > +    p_ipHead->tos = 0xc0;
>> > +    p_ipHead->tot_len = htons( IP_HDR_IGMP_LEN + IGMP_REPORT_LEN
>);
>> > +    p_ipHead->id = 0x00;
>> > +    p_ipHead->frag_off = 0x0000;
>> > +    p_ipHead->ttl = 0x01;
>> > +    p_ipHead->protocol = 0x02;
>> > +    p_ipHead->check = 0;
>> > +    p_ipHead->srcAddr = INADDR_ANY;
>> > +    p_ipHead->options = 0x0000;
>> > +}
>> > +
>> > +/** Create relay discovery socket, query socket, UDP socket and
>> > + * fills in relay anycast address for discovery
>> > + * return 0 if successful, -1 if not
>> > + */
>> > +static int amt_sockets_init( stream_t *p_access )
>> > +{
>> > +    struct sockaddr_in rcvAddr;
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +    memset( &rcvAddr, 0, sizeof(struct sockaddr_in) );
>> > +    int enable = 0, res = 0;
>> > +
>> > +    /* Relay anycast address for discovery */
>> > +    sys->relayDiscoAddr.sin_family = AF_INET;
>> > +    sys->relayDiscoAddr.sin_port = htons( AMT_PORT );
>> > +
>> > +    /* create UDP socket */
>> > +    sys->sAMT = vlc_socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP, true
>);
>> > +    if( sys->sAMT == -1 )
>> > +    {
>> > +        msg_Err( p_access, "Failed to create UDP socket" );
>> > +        goto error;
>> > +    }
>> > +
>> > +    res = setsockopt(sys->sAMT, IPPROTO_IP, IP_PKTINFO, &enable,
>> > sizeof(enable)); +    if(res < 0)
>> > +    {
>> > +        msg_Err( p_access, "Couldn't set socket options for
>IPPROTO_IP,
>> > IP_PKTINFO\n %s", vlc_strerror(errno)); +        goto error;
>> 
>> I am pretty damn sure that I already rejected that in previous
>review.
>> 
>> 
>> 
>> >
>> > +    }
>> > +
>> > +    res = setsockopt(sys->sAMT, SOL_SOCKET, SO_REUSEADDR, &enable,
>> > sizeof(enable)); +    if(res < 0)
>> > +    {
>> > +        msg_Err( p_access, "Couldn't make socket reusable");
>> > +        goto error;
>> > +    }
>> > +
>> > +    rcvAddr.sin_family      = AF_INET;
>> > +    rcvAddr.sin_port        = htons( 0 );
>> > +    rcvAddr.sin_addr.s_addr = INADDR_ANY;
>> > +
>> > +    if( bind(sys->sAMT, (struct sockaddr *)&rcvAddr,
>sizeof(rcvAddr) ) != 0
>> > ) +    {
>> > +        msg_Err( p_access, "Failed to bind UDP socket error: %s",
>> > vlc_strerror(errno) ); +        goto error;
>> > +    }
>> > +
>> > +    sys->sQuery = vlc_socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP,
>true );
>> > +    if( sys->sQuery == -1 )
>> > +    {
>> > +        msg_Err( p_access, "Failed to create query socket" );
>> > +        goto error;
>> > +    }
>> > +
>> > +    /* bind socket to local address */
>> > +    sys->stLocalAddr.sin_family      = AF_INET;
>> > +    sys->stLocalAddr.sin_port        = htons( 0 );
>> > +    sys->stLocalAddr.sin_addr.s_addr = INADDR_ANY;
>> > +
>> > +    if( bind(sys->sQuery, (struct sockaddr *)&sys->stLocalAddr,
>> > sizeof(struct sockaddr) ) != 0 ) +    {
>> > +        msg_Err( p_access, "Failed to bind query socket" );
>> > +        goto error;
>> > +    }
>> > +
>> > +    sys->stSvrAddr.sin_family        = AF_INET;
>> > +    sys->stSvrAddr.sin_port          = htons( 9124 );
>> > +    res = inet_pton( AF_INET, LOCAL_LOOPBACK,
>&sys->stSvrAddr.sin_addr );
>> > +    if( res != 1 )
>> > +    {
>> > +        msg_Err( p_access, "Could not convert loopback address" );
>> > +        goto error;
>> > +    }
>> > +
>> > +    return 0;
>> > +
>> > +error:
>> > +    if( sys->sAMT != -1 )
>> > +    {
>> > +        net_Close( sys->sAMT );
>> > +        sys->sAMT = -1;
>> > +    }
>> > +
>> > +    if( sys->sQuery != -1 )
>> > +    {
>> > +        net_Close( sys->sQuery );
>> > +        sys->sQuery = -1;
>> > +    }
>> > +    return -1;
>> > +}
>> > +
>> > +/**
>> > + * Send a relay discovery message, before 3-way handshake
>> > + * */
>> > +static void amt_send_relay_discovery_msg( stream_t *p_access, char
>> > *relay_ip ) +{
>> > +    char          chaSendBuffer[AMT_DISCO_MSG_LEN];
>> > +    unsigned int  ulNonce;
>> > +    int           nRet;
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +
>> > +    /* initialize variables */
>> > +    memset( chaSendBuffer, 0, sizeof(chaSendBuffer) );
>> > +    ulNonce = 0;
>> > +    nRet = 0;
>> > +
>> > +    /*
>> > +     * create AMT discovery message format
>> > +     * +---------------------------------------------------+
>> > +     * | Msg Type(1Byte)| Reserved (3 byte)| nonce (4byte) |
>> > +     * +---------------------------------------------------+
>> > +     */
>> > +
>> > +    chaSendBuffer[0] = AMT_RELAY_DISCO;
>> > +    chaSendBuffer[1] = 0;
>> > +    chaSendBuffer[2] = 0;
>> > +    chaSendBuffer[3] = 0;
>> > +
>> > +    /* create nonce and copy into send buffer */
>> > +    srand( (unsigned int)time(NULL) );
>> > +    ulNonce = htonl( rand() );
>> > +    memcpy( &chaSendBuffer[4], &ulNonce, sizeof(ulNonce) );
>> > +    sys->glob_ulNonce = ulNonce;
>> > +
>> > +    /* send it */
>> > +    nRet = sendto( sys->sAMT, chaSendBuffer,
>sizeof(chaSendBuffer), 0,\
>> > +            (struct sockaddr *)&sys->relayDiscoAddr, sizeof(struct
>> > sockaddr) ); +
>> > +    if( nRet < 0)
>> > +        msg_Err( p_access, "Sendto failed to %s with error %d.",
>relay_ip,
>> > errno); +}
>> > +
>> > +/**
>> > + * Send relay request message, stage 2 of handshake
>> > + * */
>> > +static void amt_send_relay_request( stream_t *p_access, char
>*relay_ip )
>> > +{
>> > +    char         chaSendBuffer[AMT_REQUEST_MSG_LEN];
>> > +    uint32_t     ulNonce;
>> > +    int          nRet;
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +
>> > +    memset( chaSendBuffer, 0, sizeof(chaSendBuffer) );
>> > +
>> > +    ulNonce = 0;
>> > +    nRet = 0;
>> > +
>> > +    /*
>> > +     * create AMT request message format
>> > +     *
>+-----------------------------------------------------------------+
>> > +     * | Msg Type(1Byte)| Reserved(1byte)|P flag(1byte)|Reserved
>(2 byte)|
>> > +     *
>+-----------------------------------------------------------------+
>> > +     * |             nonce (4byte)                                
>      |
>> > +     *
>+-----------------------------------------------------------------+
>> > +     *
>> > +     * The P flag is set to indicate which group membership
>protocol the
>> > +     * gateway wishes the relay to use in the Membership Query
>response:
>> > +
>> > +     * Value Meaning
>> > +
>> > +     *  0    The relay MUST respond with a Membership Query
>message that
>> > +     *       contains an IPv4 packet carrying an IGMPv3 General
>Query
>> > +     *       message.
>> > +     *  1    The relay MUST respond with a Membership Query
>message that
>> > +     *       contains an IPv6 packet carrying an MLDv2 General
>Query
>> > +     *       message.
>> > +     *
>> > +     */
>> > +
>> > +    chaSendBuffer[0] = AMT_REQUEST;
>> > +    chaSendBuffer[1] = 0;
>> > +    chaSendBuffer[2] = 0;
>> > +    chaSendBuffer[3] = 0;
>> > +
>> > +    ulNonce = sys->glob_ulNonce;
>> > +    memcpy( &chaSendBuffer[4], &ulNonce, sizeof(uint32_t) );
>> > +
>> > +    nRet = send( sys->sAMT, chaSendBuffer, sizeof(chaSendBuffer),
>0 );
>> > +
>> > +    if( nRet < 0 )
>> > +        msg_Err(p_access, "Error sending relay request to %s
>error: %s",
>> > relay_ip, vlc_strerror(errno) ); +}
>> > +
>> > +/*
>> > +* create AMT request message format
>> > +*
>> >
>+--------------------------------------------------------------------------
>> > --------+ +* | Msg Type(1 byte)| Reserved (1 byte)| MAC (6 byte)|
>nonce (4
>> > byte) | IGMP packet  | +*
>> >
>+--------------------------------------------------------------------------
>> > --------+ +*/
>> > +static void amt_send_mem_update( stream_t *p_access, char
>*relay_ip, bool
>> > leave) +{
>> > +    int           sendBufSize = IP_HDR_IGMP_LEN + MAC_LEN +
>NONCE_LEN +
>> > AMT_HDR_LEN; +    char          pSendBuffer[ sendBufSize +
>IGMP_REPORT_LEN
>> > ];
>> > +    uint32_t      ulNonce = 0;
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +
>> > +    memset( pSendBuffer, 0, sizeof(pSendBuffer) );
>> > +
>> > +    pSendBuffer[0] = AMT_MEM_UPD;
>> > +
>> > +    /* copy relay MAC response */
>> > +    memcpy( &pSendBuffer[2], sys->relay_mem_query_msg.uchaMAC,
>MAC_LEN );
>> > +
>> > +    /* copy nonce */
>> > +    ulNonce = ntohl(sys->glob_ulNonce);
>> > +    memcpy( &pSendBuffer[8], &ulNonce, NONCE_LEN );
>> > +
>> > +    /* make IP header for IGMP packet */
>> > +    amt_ip_alert_t p_ipHead;
>> > +    memset( &p_ipHead, 0, IP_HDR_IGMP_LEN );
>> > +    make_ip_header( &p_ipHead );
>> > +
>> > +    struct sockaddr_in temp;
>> > +    int res = inet_pton( AF_INET, MCAST_ALLHOSTS, &(temp.sin_addr)
>);
>> > +    if( res != 1 )
>> > +    {
>> > +        msg_Err(p_access, "Could not convert all hosts multicast
>address:
>> > %s", gai_strerror(errno) ); +        return;
>> > +    }
>> > +    p_ipHead.destAddr = temp.sin_addr.s_addr;
>> > +    p_ipHead.check = get_checksum( (unsigned short*)&p_ipHead,
>> > IP_HDR_IGMP_LEN ); +
>> > +    amt_igmpv3_groupRecord_t groupRcd;
>> > +    groupRcd.auxDatalen = 0;
>> > +    groupRcd.ssm = sys->mcastGroupAddr.sin_addr.s_addr;
>> > +
>> > +    if( sys->mcastSrcAddr.sin_addr.s_addr )
>> > +    {
>> > +        groupRcd.type = leave ? AMT_IGMP_BLOCK:AMT_IGMP_INCLUDE;
>> > +        groupRcd.nSrc = htons(1);
>> > +        groupRcd.srcIP[0] = sys->mcastSrcAddr.sin_addr.s_addr;
>> > +
>> > +    } else {
>> > +        groupRcd.type = leave ?
>> > AMT_IGMP_INCLUDE_CHANGE:AMT_IGMP_EXCLUDE_CHANGE; +       
>groupRcd.nSrc =
>> > htons(0);
>> > +    }
>> > +
>> > +    /* make IGMP membership report */
>> > +    amt_igmpv3_membership_report_t p_igmpMemRep;
>> > +    make_report( &p_igmpMemRep );
>> > +
>> > +    memcpy(&p_igmpMemRep.grp[0], &groupRcd, (int)sizeof(groupRcd)
>);
>> > +    p_igmpMemRep.checksum = get_checksum( (unsigned
>short*)&p_igmpMemRep,
>> > IGMP_REPORT_LEN ); +
>> > +    amt_membership_update_msg_t memUpdateMsg;
>> > +    memset(&memUpdateMsg, 0, sizeof(memUpdateMsg));
>> > +    memcpy(&memUpdateMsg.ipHead, &p_ipHead, sizeof(p_ipHead) );
>> > +    memcpy(&memUpdateMsg.memReport, &p_igmpMemRep,
>sizeof(p_igmpMemRep) );
>> > +
>> > +    memcpy( &pSendBuffer[12], &memUpdateMsg, sizeof(memUpdateMsg)
>);
>> > +
>> > +    send( sys->sAMT, pSendBuffer, sizeof(pSendBuffer), 0 );
>> > +
>> > +    msg_Dbg( p_access, "AMT relay membership report sent to %s",
>relay_ip
>> > ); +}
>> > +
>> > +/**
>> > + * Receive relay advertisement message
>> > + *
>> > + *
>> > + * 
>+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>> > + *  |  V=0  |Type=2 |                   Reserved                  
> |
>> > + * 
>+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>> > + *  |                        Discovery Nonce                      
> |
>> > + * 
>+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>> > + *  |                                                             
> |
>> > + *  ~                  Relay Address (IPv4 or IPv6)               
> ~
>> > + *  |                                                             
> |
>> > + * 
>+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>> > + * */
>> > +static bool amt_rcv_relay_adv( stream_t *p_access )
>> > +{
>> > +    char pkt[RELAY_ADV_MSG_LEN];
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +
>> > +    memset( pkt, 0, RELAY_ADV_MSG_LEN );
>> > +
>> > +    struct pollfd ufd[1];
>> > +
>> > +    ufd[0].fd = sys->sAMT;
>> > +    ufd[0].events = POLLIN;
>> > +
>> > +    switch( vlc_poll_i11e(ufd, 1, sys->timeout) )
>> > +    {
>> > +        case 0:
>> > +            msg_Err(p_access, "AMT relay advertisement receive
>time-out");
>> > +            /* fall through */
>> > +        case -1:
>> > +            return false;
>> > +    }
>> > +
>> > +    struct sockaddr temp;
>> > +    uint32_t temp_size = sizeof( struct sockaddr );
>> > +    ssize_t len = recvfrom( sys->sAMT, &pkt, RELAY_ADV_MSG_LEN, 0,
>&temp,
>> > &temp_size ); +
>> > +    if (len < 0)
>> > +    {
>> > +        msg_Err(p_access, "Received message length less than
>zero");
>> > +        return false;
>> > +    }
>> > +
>> > +    memcpy( &sys->relay_adv_msg.type, &pkt[0], MSG_TYPE_LEN );
>> > +    if( sys->relay_adv_msg.type != AMT_RELAY_ADV )
>> > +    {
>> > +        msg_Err( p_access, "Received message not an AMT relay
>> > advertisement, ignoring. "); +        return false;
>> > +    }
>> > +
>> > +    memcpy( &sys->relay_adv_msg.ulRcvNonce, &pkt[NONCE_LEN],
>NONCE_LEN );
>> > +    if( sys->glob_ulNonce != sys->relay_adv_msg.ulRcvNonce )
>> > +    {
>> > +        msg_Err( p_access, "Discovery nonces differ! currNonce:%x
>rcvd%x",
>> > sys->glob_ulNonce, ntohl(sys->relay_adv_msg.ulRcvNonce) ); +       
>return
>> > false;
>> > +    }
>> > +
>> > +    memcpy( &sys->relay_adv_msg.ipAddr, &pkt[8], 4 );
>> > +
>> > +    memset( &sys->relayAddr, 0, sizeof(sys->relayAddr) );
>> > +    sys->relayAddr.sin_family       = AF_INET;
>> > +    sys->relayAddr.sin_addr.s_addr  = sys->relay_adv_msg.ipAddr;
>> > +    sys->relayAddr.sin_port         = htons( AMT_PORT );
>> > +
>> > +    int nRet = connect( sys->sAMT, (struct sockaddr
>*)&sys->relayAddr,
>> > sizeof(sys->relayAddr) );
>> > +    if( nRet < 0 )
>> > +    {
>> > +        msg_Err( p_access, "Error connecting AMT UDP socket: %s",
>> > vlc_strerror(errno) ); +        return false;
>> > +    }
>> > +
>> > +    return true;
>> > +}
>> > +
>> > +/**
>> > + * Receive relay membership query message
>> > + *
>+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>> > +   |  V=0  |Type=4 | Reserved  |L|G|         Response MAC         
>|
>> > +   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                              
>+
>> > +   |                                                              
>|
>> > +  
>+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>> > +   |                         Request Nonce                        
>|
>> > +  
>+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>> > +   |                                                              
>|
>> > +   |               Encapsulated General Query Message             
>|
>> > +   ~                 IPv4:IGMPv3(Membership Query)                
>~
>> > +   |                  IPv6:MLDv2(Listener Query)                  
>|
>> > +   |                                                              
>|
>> > +  
>+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>> > +   |     Gateway Port Number       |                              
>|
>> > +   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                              
>+
>> > +   |                                                              
>|
>> > +   +                                                              
>+
>> > +   |                Gateway IP Address (IPv4 or IPv6)             
>|
>> > +   +                                                              
>+
>> > +   |                                                              
>|
>> > +   +                              
>+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>> > +   |                               |
>> > +   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>> > + */
>> > +static bool amt_rcv_relay_mem_query( stream_t *p_access )
>> > +{
>> > +    char pkt[RELAY_QUERY_MSG_LEN];
>> > +    memset( pkt, 0, RELAY_QUERY_MSG_LEN );
>> > +    struct pollfd ufd[1];
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +
>> > +    ufd[0].fd = sys->sAMT;
>> > +    ufd[0].events = POLLIN;
>> > +
>> > +    switch( vlc_poll_i11e(ufd, 1, sys->timeout) )
>> > +    {
>> > +        case 0:
>> > +            msg_Err(p_access, "AMT relay membership query receive
>> > time-out"); +            /* fall through */
>> > +        case -1:
>> > +            return false;
>> > +    }
>> > +
>> > +    ssize_t len = recv( sys->sAMT, &pkt, RELAY_QUERY_MSG_LEN, 0 );
>> > +
>> > +    if (len < 0)
>> > +    {
>> > +        msg_Err(p_access, "length less than zero");
>> > +        return false;
>> > +    }
>> > +
>> > +    memcpy( &sys->relay_mem_query_msg.type, &pkt[0], MSG_TYPE_LEN
>);
>> 
>> Potentially undefined read. Ditto below.
>> 
>> > +    /* pkt[1] is reserved  */
>> > +    memcpy( &sys->relay_mem_query_msg.uchaMAC[0],
>&pkt[AMT_HDR_LEN],
>> > MAC_LEN ); +    memcpy( &sys->relay_mem_query_msg.ulRcvedNonce,
>> > &pkt[AMT_HDR_LEN + MAC_LEN], NONCE_LEN )
>> > +    if(
>> > sys->relay_mem_query_msg.ulRcvedNonce != sys->glob_ulNonce )
>> > +    {
>> > +        msg_Warn( p_access, "Nonces are different rcvd: %x glob:
>%x",
>> > sys->relay_mem_query_msg.ulRcvedNonce, sys->glob_ulNonce );
>> > +        return
>> > false;
>> > +    }
>> > +
>> > +    size_t shift = AMT_HDR_LEN + MAC_LEN + NONCE_LEN;
>> > +    memcpy( &sys->relay_ip_hdr, &pkt[shift], IP_HDR_IGMP_LEN );
>> > +
>> > +    shift += IP_HDR_IGMP_LEN;
>> > +    sys->relay_igmp_query.type = pkt[shift];
>> > +    shift++; assert( shift < RELAY_QUERY_MSG_LEN);
>> > +    sys->relay_igmp_query.max_resp_code = pkt[shift];
>> > +    shift++; assert( shift < RELAY_QUERY_MSG_LEN);
>> > +    memcpy( &sys->relay_igmp_query.checksum, &pkt[shift], 2 );
>> > +    shift += 2; assert( shift < RELAY_QUERY_MSG_LEN);
>> > +    memcpy( &sys->relay_igmp_query.ssmIP, &pkt[shift], 4 );
>> > +    shift += 4; assert( shift < RELAY_QUERY_MSG_LEN);
>> > +    sys->relay_igmp_query.s_qrv = pkt[shift];
>> > +    shift++; assert( shift < RELAY_QUERY_MSG_LEN);
>> > +    if( pkt[shift] == 0 )
>> > +        sys->relay_igmp_query.qqic = 125;
>> > +    else
>> > +        sys->relay_igmp_query.qqic = pkt[shift];
>> > +
>> > +    shift++; assert( shift < RELAY_QUERY_MSG_LEN);
>> > +    memcpy( &sys->relay_igmp_query.nSrc, &pkt[shift], 2 );
>> 
>> > +    /* if a membership thread exists cancel it */
>> > +    if( sys->threadReady )
>> > +    {
>> > +        msg_Dbg( p_access, "Canceling existing AMT relay
>membership update
>> > thread"); +
>> > +        sys->threadReady = false;
>> > +        vlc_cancel( sys->updateThread );
>> > +        vlc_join( sys->updateThread, NULL );
>> > +    }
>> > +
>> > +    msg_Dbg( p_access, "Spawning AMT relay membership update
>thread");
>> > +
>> > +    if( vlc_clone( &sys->updateThread, amt_mem_upd, p_access,
>> > VLC_THREAD_PRIORITY_LOW) ) +    {
>> > +        msg_Err( p_access, "Could not create AMT relay membership
>update
>> > thread" ); +        return false;
>> > +    }
>> > +
>> > +    /* set theadReady to true, thread will send periodic
>membership updates
>> > until false */ +    sys->threadReady = true;
>> > +    return true;
>> > +}
>> > +
>> > +/**
>> > + * Join SSM group based on input addresses, or use the defaults
>> > + * */
>> > +static int amt_joinSSM_group( stream_t *p_access )
>> > +{
>> > +    struct ip_mreq_source imr;
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +
>> > +    imr.imr_multiaddr.s_addr =
>sys->mcastGroupAddr.sin_addr.s_addr;
>> > +    imr.imr_sourceaddr.s_addr = sys->mcastSrcAddr.sin_addr.s_addr;
>> > +    imr.imr_interface.s_addr = INADDR_ANY;
>> > +
>> > +    return setsockopt( sys->sAMT, IPPROTO_IP,
>IP_ADD_SOURCE_MEMBERSHIP,
>> > (char *)&imr, sizeof(imr) ); +}
>> > +
>> > +static int amt_joinASM_group( stream_t *p_access )
>> > +{
>> > +    struct ip_mreq imr;
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +
>> > +    imr.imr_multiaddr.s_addr =
>sys->mcastGroupAddr.sin_addr.s_addr;
>> > +    imr.imr_interface.s_addr = INADDR_ANY;
>> > +
>> > +    return setsockopt( sys->sAMT, IPPROTO_IP, IP_ADD_MEMBERSHIP,
>(char
>> > *)&imr, sizeof(imr) ); +}
>> > +
>> > +/**
>> > + * Leave SSM group that was joined earlier.
>> > + * */
>> > +static int amt_leaveSSM_group( stream_t *p_access )
>> > +{
>> > +    struct ip_mreq_source imr;
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +
>> > +    imr.imr_multiaddr.s_addr =
>sys->mcastGroupAddr.sin_addr.s_addr;
>> > +    imr.imr_sourceaddr.s_addr = sys->mcastSrcAddr.sin_addr.s_addr;
>> > +    imr.imr_interface.s_addr = INADDR_ANY;
>> > +
>> > +    return setsockopt( sys->sAMT, IPPROTO_IP,
>IP_DROP_SOURCE_MEMBERSHIP,
>> > (char *)&imr, sizeof(imr) ); +}
>> > +
>> > +/**
>> > + * Leave ASM group that was joined earlier.
>> > + * */
>> > +static int amt_leaveASM_group( stream_t *p_access )
>> > +{
>> > +    struct ip_mreq imr;
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +
>> > +    imr.imr_multiaddr.s_addr =
>sys->mcastGroupAddr.sin_addr.s_addr;
>> > +    imr.imr_interface.s_addr = INADDR_ANY;
>> > +
>> > +    return setsockopt( sys->sAMT, IPPROTO_IP, IP_DROP_MEMBERSHIP,
>(char
>> > *)&imr, sizeof(imr) ); +}
>> > +
>> > +/* A thread is spawned since IGMP membership updates need to
>issued
>> > periodically in order to continue to receive multicast */ +/*
>Simple timers
>> > are not suitable for this requirement since the parent is otherwise
>> > occupied in receiving UDP packet flow */
>> 
>> That looks like complete nonsense.
>> 
>> > +static void *amt_mem_upd( void
>> > *data )
>> > +{
>> > +    stream_t     *p_access = (stream_t*) data;
>> > +    access_sys_t *sys = p_access->p_sys;
>> > +
>> > +    msg_Dbg( p_access, "AMT relay membership update thread
>started" );
>> > +
>> > +    /* thread sends periodic AMT membership updates until Close()
>which
>> > terminates thread */ +    while ( true  )
>> > +    {
>> > +        amt_send_mem_update( p_access, sys->relayDisco, false );
>> > +        vlc_tick_sleep( (vlc_tick_t)sys->relay_igmp_query.qqic *
>CLOCK_FREQ
>> > ); +    }
>> > +
>> > +    return NULL;
>> > +}
>> 
>> -- 
>> Rémi Denis-Courmont
>> http://www.remlab.net/
>> 
>> 
>> 
>> _______________________________________________
>> vlc-devel mailing list
>> To unsubscribe or modify your subscription options:
>> https://mailman.videolan.org/listinfo/vlc-devel
>_______________________________________________
>vlc-devel mailing list
>To unsubscribe or modify your subscription options:
>https://mailman.videolan.org/listinfo/vlc-devel

-- 
Envoyé de mon appareil Android avec Courriel K-9 Mail. Veuillez excuser ma brièveté.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/vlc-devel/attachments/20190924/351f91bc/attachment-0001.html>


More information about the vlc-devel mailing list