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

Rémi Denis-Courmont remi at remlab.net
Wed Jul 24 18:27:31 CEST 2019


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

> +
> +    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


-- 
レミ・デニ-クールモン
http://www.remlab.net/





More information about the vlc-devel mailing list