[vlc-devel] [PATCH 5/7] Implement mDNS services advertisement submodule

Thomas Guillem thomas at gllm.fr
Mon Jul 29 13:19:34 CEST 2019



On Thu, Jul 25, 2019, at 08:04, Roland Bewick wrote:
> 
> On 24/07/2019 11:27 PM, Rémi Denis-Courmont wrote:
> > Le keskiviikkona 24. heinäkuuta 2019, 18.43.48 EEST Roland Bewick a écrit :
> >> This module will launch an mdns advertisement server and listen for
> >> discovery requests of the _vlc._tcp service type, making it possible to
> >> resolve the VLC web interface from the vlc.local domain ---
> >>   modules/services_discovery/microdns.c | 310
> >> +++++++++++++++++++++++++++++----- 1 file changed, 268 insertions(+), 42
> >> deletions(-)
> >>
> >> diff --git a/modules/services_discovery/microdns.c
> >> b/modules/services_discovery/microdns.c index e9ff0c6380..c460126b58 100644
> >> --- a/modules/services_discovery/microdns.c
> >> +++ b/modules/services_discovery/microdns.c
> >> @@ -33,9 +33,22 @@
> >>   #include <vlc_modules.h>
> >>   #include <vlc_services_discovery.h>
> >>   #include <vlc_renderer_discovery.h>
> >> +#include <vlc_services_advertisement.h>
> >>
> >>   #include <microdns/microdns.h>
> >>
> >> +
> >> +/*
> >> + * mDNS service announcement configuration
> >> + */
> >> +
> >> +/* This will point to the IP address and port of our running VLC interface
> >> */ +#define SA_DOMAIN "vlc.local"
> >> +/* respond to PTR query of this type from mDNS clients */
> >> +#define SA_SERVICE_TYPE_DOMAIN "_vlc._tcp.local"
> >> +/* link service type to our domain name (vlc.local) and instance name (VLC)
> >> */ +#define SA_SERVICE_TYPE_LINK "vlc VLC._vlc._tcp.local"
> >> +
> >>   static int OpenSD( vlc_object_t * );
> >>   static void CloseSD( vlc_object_t * );
> >>   static int OpenRD( vlc_object_t * );
> >> @@ -97,16 +110,28 @@ static const struct
> >>   };
> >>   #define NB_PROTOCOLS (sizeof(protocols) / sizeof(*protocols))
> >>
> >> -struct discovery_sys
> >> +struct microdns_sys_common
> >>   {
> >>       vlc_thread_t        thread;
> >>       atomic_bool         stop;
> >>       struct mdns_ctx *   p_microdns;
> >> +};
> >> +
> >> +struct discovery_sys
> >> +{
> >> +    struct microdns_sys_common *p_sys_common

One useless allocation here, it should be a struct microdns_sys_common, not a pointer.

Maybe, this change could be less intrusive with an union ?

> >>       const char *        ppsz_service_names[NB_PROTOCOLS];
> >>       unsigned int        i_nb_service_names;
> >>       vlc_array_t         items;
> >>   };
> >>
> >> +struct advertisement_sys
> >> +{
> >> +    struct microdns_sys_common *p_sys_common;
> >> +    int i_http_port;
> >> +    char *psz_http_host;
> >> +};
> >> +
> >>   struct item
> >>   {
> >>       char *              psz_uri;
> >> @@ -438,7 +463,7 @@ stop_sd_cb( void *p_this )
> >>       services_discovery_t *p_sd = ( services_discovery_t* )p_this;
> >>       struct discovery_sys *p_sys = p_sd->p_sys;
> >>
> >> -    if( atomic_load( &p_sys->stop ) )
> >> +    if( atomic_load( &p_sys->p_sys_common->stop ) )
> >>           return true;
> >>       else
> >>       {
> >> @@ -453,7 +478,7 @@ RunSD( void *p_this )
> >>       services_discovery_t *p_sd = ( services_discovery_t* )p_this;
> >>       struct discovery_sys *p_sys = p_sd->p_sys;
> >>
> >> -    int i_status = mdns_listen( p_sys->p_microdns,
> >> +    int i_status = mdns_listen( p_sys->p_sys_common->p_microdns,
> >>                                   p_sys->ppsz_service_names,
> >>                                   p_sys->i_nb_service_names,
> >>                                   RR_PTR, SEC_FROM_VLC_TICK(LISTEN_INTERVAL),
> >> @@ -550,7 +575,7 @@ stop_rd_cb( void *p_this )
> >>       vlc_renderer_discovery_t *p_rd = p_this;
> >>       struct discovery_sys *p_sys = p_rd->p_sys;
> >>
> >> -    if( atomic_load( &p_sys->stop ) )
> >> +    if( atomic_load( &p_sys->p_sys_common->stop ) )
> >>           return true;
> >>       else
> >>       {
> >> @@ -565,7 +590,7 @@ RunRD( void *p_this )
> >>       vlc_renderer_discovery_t *p_rd = p_this;
> >>       struct discovery_sys *p_sys = p_rd->p_sys;
> >>
> >> -    int i_status = mdns_listen( p_sys->p_microdns,
> >> +    int i_status = mdns_listen( p_sys->p_sys_common->p_microdns,
> >>                                   p_sys->ppsz_service_names,
> >>                                   p_sys->i_nb_service_names,
> >>                                   RR_PTR, SEC_FROM_VLC_TICK(LISTEN_INTERVAL),
> >> @@ -577,62 +602,205 @@ RunRD( void *p_this )
> >>       return NULL;
> >>   }
> >>
> >> -static int
> >> -OpenCommon( vlc_object_t *p_obj, struct discovery_sys *p_sys, bool
> >> b_renderer ) +static void
> >> +request_sa_callback( void *cbarg, int r, const struct rr_entry *entry )
> >>   {
> >> -    int i_ret = VLC_EGENERIC;
> >> -    atomic_init( &p_sys->stop, false );
> >> -    vlc_array_init( &p_sys->items );
> >> +    VLC_UNUSED( r );  /* result code from mdns_recv */
> >>
> >> -    /* Listen to protocols that are handled by VLC */
> >> -    for( unsigned int i = 0; i < NB_PROTOCOLS; ++i )
> >> +    services_advertisement_t *p_sa = ( services_advertisement_t* )cbarg;
> >> +    struct advertisement_sys *p_sys = p_sa->p_sys;
> >> +    struct mdns_ctx *ctx = p_sys->p_sys_common->p_microdns;
> >> +    struct mdns_hdr hdr = {0};  /* response header */
> >> +
> >> +    if ( entry->type != RR_PTR || entry->rr_class != RR_IN )
> >>       {
> >> -        if( protocols[i].b_renderer == b_renderer )
> >> -            p_sys->ppsz_service_names[p_sys->i_nb_service_names++] =
> >> -                protocols[i].psz_service_name;
> >> +        msg_Dbg( VLC_OBJECT( p_sa ),
> >> +                 "mDNS-SA: Skipped unsupported request type: %d class:
> >> %d\n", +                 entry->type,
> >> +                 entry->rr_class );
> >> +        return;
> >>       }
> >>
> >> -    if( p_sys->i_nb_service_names == 0 )
> >> +    /*
> >> +     * When we receive a PTR record (question) from an mDNS client we need
> >> to +     * answer with four record types that will allow our domain name
> >> (vlc.local) +     * to resolve the IP address and port we are hosting the
> >> VLC interface on. +     *
> >> +     * PTR: point service type (_vlc._tcp._local) to local domain
> >> +     * TXT: provide additional information (HTTP server root directory
> >> etc.) +     * SRV: provide server information (port number etc.)
> >> +     * A: link local domain to IP address
> >> +     */
> >> +
> >> +    struct rr_entry answers[4] = {0};  /* PTR, TXT, SRV, A */
> > You really should use IPv6LL first and either ignore IPv4 or use it only as
> > fallbacka these days. Apple HomeKit and most other recent frameworks in that
> > field work that way, AFAIK.
> 
> The existing mdns modules only listen on IPv4 (service discovery and 
> renderer discovery).
> 
> I'll have a look into switching services advertisement to ipv6.
> 
> Thanks
> 
> 
> 
> >
> >> +
> >> +    hdr.flags |= FLAG_QR;
> >> +    hdr.flags |= FLAG_AA;
> >> +
> >> +    hdr.num_ans_rr = sizeof( answers ) / sizeof( answers[0] );
> >> +
> >> +    for (int i = 0; i < hdr.num_ans_rr; i++)
> >>       {
> >> -        msg_Err( p_obj, "no services found" );
> >> -        goto error;
> >> +            answers[i].rr_class = RR_IN;
> >> +            answers[i].ttl      = 120;
> >> +
> >> +            if (i + 1 < hdr.num_ans_rr)
> >> +                    answers[i].next = &answers[i + 1];
> >>       }
> >> -    for( unsigned int i = 0; i < p_sys->i_nb_service_names; ++i )
> >> -        msg_Dbg( p_obj, "mDNS: listening to %s %s",
> >> p_sys->ppsz_service_names[i], -                 b_renderer ? "renderer" :
> >> "service" );
> >> +
> >> +    /* RR_PTR */
> >> +    char *ptr_name =               strdup( SA_SERVICE_TYPE_DOMAIN );
> >> +    char *ptr_domain =             strdup( SA_SERVICE_TYPE_LINK );
> >> +    answers[0].type =              RR_PTR;
> >> +    answers[0].name =              ptr_name;
> >> +    answers[0].data.PTR.domain =   ptr_domain;
> >> +
> >> +    /* RR_TXT (currently empty) */
> >> +    char *txt_name =               strdup( SA_SERVICE_TYPE_LINK );
> >> +    answers[1].type =              RR_TXT;
> >> +    answers[1].name =              txt_name;
> >> +
> >> +    /* RR_SRV */
> >> +    char *srv_name =               strdup( SA_SERVICE_TYPE_LINK );
> >> +    char *srv_target =             strdup( SA_DOMAIN );
> >> +    answers[2].type =              RR_SRV;
> >> +    answers[2].name =              srv_name;
> >> +    answers[2].data.SRV.port =     p_sys->i_http_port;
> >> +    answers[2].data.SRV.priority = 0;
> >> +    answers[2].data.SRV.weight =   0;
> >> +    answers[2].data.SRV.target =   srv_target;
> >> +
> >> +    /* RR_A */
> >> +    char *a_name = strdup( SA_DOMAIN );
> >> +    answers[3].type = RR_A;
> >> +    answers[3].name = a_name;
> >> +    sprintf( answers[3].data.A.addr_str, "%s", p_sys->psz_http_host );
> >> +    inet_pton( AF_INET, answers[3].data.A.addr_str, &answers[3].data.A.addr
> >> ); +
> >> +    mdns_entries_send( ctx, &hdr, answers );
> >> +
> >> +    free( ptr_name );
> >> +    free( ptr_domain );
> >> +    free( txt_name );
> >> +    free( srv_name );
> >> +    free( srv_target );
> >> +    free( a_name );
> >> +}
> >> +
> >> +static bool
> >> +stop_sa_cb( void *cbarg )
> >> +{
> >> +    services_advertisement_t *p_sa = ( services_advertisement_t* )cbarg;
> >> +    struct advertisement_sys *p_sys = p_sa->p_sys;
> >> +
> >> +    return atomic_load( &p_sys->p_sys_common->stop );
> >> +}
> >> +
> >> +static void *
> >> +RunSA( void *p_this )
> >> +{
> >> +    services_advertisement_t *p_sa = ( services_advertisement_t* )p_this;
> >> +    struct advertisement_sys *p_sys = p_sa->p_sys;
> >>
> >>       int i_status;
> >> -    if( ( i_status = mdns_init( &p_sys->p_microdns, MDNS_ADDR_IPV4,
> >> +
> >> +    i_status = mdns_announce( p_sys->p_sys_common->p_microdns,
> >> +                              SA_SERVICE_TYPE_DOMAIN,
> >> +                              RR_PTR,
> >> +                              request_sa_callback,
> >> +                              p_sa);
> >> +
> >> +    if( i_status < 0 )
> >> +        print_error( VLC_OBJECT( p_sa ), "announce", i_status );
> >> +
> >> +    i_status = mdns_serve(p_sys->p_sys_common->p_microdns, stop_sa_cb,
> >> p_sa); +
> >> +    return NULL;
> >> +}
> >> +
> >> +static void
> >> +CleanCommon( struct microdns_sys_common *p_sys_common )
> >> +{
> >> +    if ( p_sys_common->p_microdns )
> >> +        mdns_destroy( p_sys_common->p_microdns );
> >> +    free( p_sys_common );
> >> +}
> >> +
> >> +static void
> >> +CleanDiscoveryCommon( struct discovery_sys *p_sys )
> >> +{
> >> +    items_clear( p_sys );
> >> +    CleanCommon( p_sys->p_sys_common );
> >> +    free( p_sys );
> >> +}
> >> +
> >> +static void
> >> +CleanAdvertisementCommon( struct advertisement_sys *p_sys )
> >> +{
> >> +    free(p_sys->psz_http_host);
> >> +    CleanCommon( p_sys->p_sys_common );
> >> +    free( p_sys );
> >> +}
> >> +
> >> +static void CloseCommon( struct microdns_sys_common *p_sys_common )
> >> +{
> >> +    atomic_store( &p_sys_common->stop, true );
> >> +    vlc_join( p_sys_common->thread, NULL );
> >> +}
> >> +
> >> +static int
> >> +OpenCommon( vlc_object_t *p_obj, struct microdns_sys_common *p_sys_common,
> >> +            void *(*entry)(void *) )
> >> +{
> >> +    int i_ret = VLC_EGENERIC;
> >> +    atomic_init( &p_sys_common->stop, false );
> >> +
> >> +    int i_status;
> >> +    if( ( i_status = mdns_init( &p_sys_common->p_microdns, MDNS_ADDR_IPV4,
> >>                                   MDNS_PORT ) ) < 0 )
> >>       {
> >>           print_error( p_obj, "init", i_status );
> >>           goto error;
> >>       }
> >>
> >> -    if( vlc_clone( &p_sys->thread, b_renderer ? RunRD : RunSD, p_obj,
> >> -                   VLC_THREAD_PRIORITY_LOW) )
> >> +    if( vlc_clone( &p_sys_common->thread, entry, p_obj,
> >> VLC_THREAD_PRIORITY_LOW) ) {
> >> -        msg_Err( p_obj, "Can't run the lookup thread" );
> >> +        msg_Err( p_obj, "Can't run the mdns thread");
> >>           goto error;
> >>       }
> >>
> >>       return VLC_SUCCESS;
> >>   error:
> >> -    if( p_sys->p_microdns != NULL )
> >> -        mdns_destroy( p_sys->p_microdns );
> >> -    free( p_sys );
> >> +    CleanCommon( p_sys_common );
> >>       return i_ret;
> >>   }
> >>
> >> -static void
> >> -CleanCommon( struct discovery_sys *p_sys )
> >> +static int OpenDiscoveryCommon( vlc_object_t *p_obj, struct discovery_sys
> >> *p_sys, +                               bool b_renderer, void
> >> *(*entry)(void *) ) {
> >> -    atomic_store( &p_sys->stop, true );
> >> -    vlc_join( p_sys->thread, NULL );
> >> +    int i_ret = VLC_EGENERIC;
> >>
> >> -    items_clear( p_sys );
> >> -    mdns_destroy( p_sys->p_microdns );
> >> -    free( p_sys );
> >> +    vlc_array_init( &p_sys->items );
> >> +    /* Listen to protocols that are handled by VLC */
> >> +    for( unsigned int i = 0; i < NB_PROTOCOLS; ++i )
> >> +    {
> >> +        if( protocols[i].b_renderer == b_renderer )
> >> +            p_sys->ppsz_service_names[p_sys->i_nb_service_names++] =
> >> +                protocols[i].psz_service_name;
> >> +    }
> >> +    if( p_sys->i_nb_service_names == 0 )
> >> +    {
> >> +        msg_Err( p_obj, "no services found" );
> >> +        goto error;
> >> +    }
> >> +    for( unsigned int i = 0; i < p_sys->i_nb_service_names; ++i )
> >> +        msg_Dbg( p_obj, "mDNS: listening to %s %s",
> >> p_sys->ppsz_service_names[i], +                b_renderer ? "renderer" :
> >> "service" );
> >> +
> >> +    return OpenCommon( p_obj, p_sys->p_sys_common, entry );
> >> +error:
> >> +    CleanCommon( p_sys->p_sys_common );
> >> +    return i_ret;
> >>   }
> >>
> >>   static int
> >> @@ -643,12 +811,19 @@ OpenSD( vlc_object_t *p_obj )
> >>       struct discovery_sys *p_sys = calloc( 1, sizeof(struct discovery_sys)
> >> ); if( !p_sys )
> >>           return VLC_ENOMEM;
> >> +    p_sys->p_sys_common = calloc( 1, sizeof(struct microdns_sys_common) );
> >> +    if( !p_sys->p_sys_common )
> >> +    {
> >> +        free( p_sys );
> >> +        return VLC_ENOMEM;
> >> +    }
> >> +
> >>       p_sd->p_sys = p_sys;
> >>
> >>       p_sd->description = _("mDNS Network Discovery");
> >>       config_ChainParse( p_sd, CFG_PREFIX, ppsz_options, p_sd->p_cfg );
> >>
> >> -    return OpenCommon( p_obj, p_sys, false );
> >> +    return OpenDiscoveryCommon( p_obj, p_sys, false, RunSD );
> >>   }
> >>
> >>   static void
> >> @@ -657,7 +832,8 @@ CloseSD( vlc_object_t *p_this )
> >>       services_discovery_t *p_sd = (services_discovery_t *) p_this;
> >>       struct discovery_sys *p_sys = p_sd->p_sys;
> >>
> >> -    CleanCommon( p_sys );
> >> +    CloseCommon( p_sys->p_sys_common );
> >> +    CleanDiscoveryCommon( p_sys );
> >>   }
> >>
> >>   static int
> >> @@ -668,11 +844,18 @@ OpenRD( vlc_object_t *p_obj )
> >>       struct discovery_sys *p_sys = calloc( 1, sizeof(struct discovery_sys)
> >> ); if( !p_sys )
> >>           return VLC_ENOMEM;
> >> +    p_sys->p_sys_common = calloc( 1, sizeof(struct microdns_sys_common) );
> >> +    if( !p_sys->p_sys_common )
> >> +    {
> >> +        free( p_sys );
> >> +        return VLC_ENOMEM;
> >> +    }
> >> +
> >>       p_rd->p_sys = p_sys;
> >>
> >>       config_ChainParse( p_rd, CFG_PREFIX, ppsz_options, p_rd->p_cfg );
> >>
> >> -    return OpenCommon( p_obj, p_sys, true );
> >> +    return OpenDiscoveryCommon( p_obj, p_sys, true, RunRD );
> >>   }
> >>
> >>   static void
> >> @@ -681,18 +864,61 @@ CloseRD( vlc_object_t *p_this )
> >>       vlc_renderer_discovery_t *p_rd = (vlc_renderer_discovery_t *) p_this;
> >>       struct discovery_sys *p_sys = p_rd->p_sys;
> >>
> >> -    CleanCommon( p_sys );
> >> +    CloseCommon( p_sys->p_sys_common );
> >> +    CleanDiscoveryCommon( p_sys );
> >> +}
> >> +
> >> +static char * detect_ip_address()
> >> +{
> >> +    return strdup("127.0.0.1"); /* TODO: actually detect ip address */
> >>   }
> >>
> >>   static int
> >>   OpenSA( vlc_object_t *p_obj )
> >>   {
> >> -    msg_Dbg(p_obj, "TODO: Open service advertisement module");
> >> -    return VLC_SUCCESS;
> >> +    services_advertisement_t *p_sa = (services_advertisement_t *)p_obj;
> >> +
> >> +    struct advertisement_sys *p_sys = calloc( 1, sizeof(struct
> >> advertisement_sys) ); +    if( !p_sys )
> >> +        return VLC_ENOMEM;
> >> +    p_sys->p_sys_common = calloc( 1, sizeof(struct microdns_sys_common) );
> >> +    if( !p_sys->p_sys_common )
> >> +    {
> >> +        free( p_sys );
> >> +        return VLC_ENOMEM;
> >> +    }
> >> +
> >> +    p_sys->i_http_port = var_InheritInteger( p_obj, "http-port" ); /* 8080
> >> if unset */ +    p_sys->psz_http_host = var_InheritString( p_obj,
> >> "http-host" ); /* null if unset */ +
> >> +    if ( p_sys->psz_http_host && strlen( p_sys->psz_http_host ) >
> >> INET_ADDRSTRLEN ) +    {
> >> +        msg_Err( p_obj, "http-host is not a valid IPv4 address: %s",
> >> p_sys->psz_http_host ); +        free( p_sys->psz_http_host );
> >> +        p_sys->psz_http_host = NULL;
> >> +    }
> >> +
> >> +    if( !p_sys->psz_http_host )
> >> +    {
> >> +        msg_Dbg( p_obj, "http-host unset, detecting local IP address...");
> >> +        p_sys->psz_http_host = detect_ip_address();
> >> +        msg_Dbg( p_obj, "chose local IP address: %s",
> >> p_sys->psz_http_host); +    }
> >> +
> >> +    p_sa->p_sys = p_sys;
> >> +
> >> +    p_sa->description = _("mDNS Network Advertisement");
> >> +    config_ChainParse( p_sa, CFG_PREFIX, ppsz_options, p_sa->p_cfg );
> >> +
> >> +    return OpenCommon( p_obj, p_sys->p_sys_common, RunSA );
> >>   }
> >>
> >>   static void
> >>   CloseSA( vlc_object_t *p_this )
> >>   {
> >> -    msg_Dbg(p_this, "TODO: Close service advertisement module");
> >> +    services_advertisement_t *p_sa = (services_advertisement_t *) p_this;
> >> +    struct advertisement_sys *p_sys = p_sa->p_sys;
> >> +
> >> +    CloseCommon( p_sys->p_sys_common );
> >> +    CleanAdvertisementCommon( p_sys );
> >>   }
> >> \ No newline at end of file
> >
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel


More information about the vlc-devel mailing list