[vlc-devel] [PATCH 3/3] Add microdns services_advertisement submodule
Thomas Guillem
thomas at gllm.fr
Mon Sep 2 14:14:11 CEST 2019
On Mon, Sep 2, 2019, at 07:25, Roland Bewick wrote:
> ---
> modules/services_discovery/microdns.c | 318 +++++++++++++++++++++-----
> 1 file changed, 261 insertions(+), 57 deletions(-)
>
> diff --git a/modules/services_discovery/microdns.c
> b/modules/services_discovery/microdns.c
> index ad0d2691f7..d5dc5bda94 100644
> --- a/modules/services_discovery/microdns.c
> +++ b/modules/services_discovery/microdns.c
> @@ -33,6 +33,7 @@
> #include <vlc_modules.h>
> #include <vlc_services_discovery.h>
> #include <vlc_renderer_discovery.h>
> +#include <vlc_services_advertisement.h>
>
> #include <microdns/microdns.h>
>
> @@ -40,6 +41,8 @@ static int OpenSD( vlc_object_t * );
> static void CloseSD( vlc_object_t * );
> static int OpenRD( vlc_object_t * );
> static void CloseRD( vlc_object_t * );
> +static int OpenSA( vlc_object_t * );
> +static void CloseSA( vlc_object_t * );
>
> VLC_SD_PROBE_HELPER( "microdns", N_("mDNS Network Discovery"), SD_CAT_LAN )
> VLC_RD_PROBE_HELPER( "microdns_renderer", "mDNS renderer Discovery" )
> @@ -69,6 +72,14 @@ vlc_module_begin()
> set_callbacks( OpenRD, CloseRD )
> add_shortcut( "mdns_renderer", "microdns_renderer" )
> VLC_RD_PROBE_SUBMODULE
> + add_submodule() \
> + set_description( N_( "mDNS Services Advertisement" ) )
> + set_category( CAT_ADVANCED )
> + set_subcategory( SUBCAT_ADVANCED_NETWORK )
> + set_capability( "services_advertisement", 0 )
> + set_callbacks( OpenSA, CloseSA )
> + add_shortcut( "mdns", "microdns" )
> + VLC_RD_PROBE_SUBMODULE
> vlc_module_end ()
>
> static const struct
> @@ -87,16 +98,37 @@ static const struct
> };
> #define NB_PROTOCOLS (sizeof(protocols) / sizeof(*protocols))
>
> +/*
> + * Services advertisement configuration
> + */
> +char sa_domain_name[] = "vlc.local"; /* Announce our service on this
> domain name*/
> +char sa_service_type[] = "_vlc._tcp.local"; /* respond to PTR query of
> this type */
> +char sa_service_type_link[] = "vlc VLC._vlc._tcp.local"; /* link
> service type to domain */
These 3 char * should be const and static.
> +
> struct discovery_sys
> {
> - vlc_thread_t thread;
> - atomic_bool stop;
> - struct mdns_ctx * p_microdns;
> const char * ppsz_service_names[NB_PROTOCOLS];
> unsigned int i_nb_service_names;
> vlc_array_t items;
> };
>
> +struct advertisement_sys
> +{
> + int i_http_port;
> +};
> +
> +struct microdns_sys
> +{
> + vlc_thread_t thread;
> + atomic_bool stop;
> + struct mdns_ctx * p_microdns;
> + union
> + {
> + struct discovery_sys discovery_sys;
> + struct advertisement_sys advertisement_sys;
> + };
> +};
> +
> struct item
> {
> char * psz_uri;
> @@ -383,7 +415,7 @@ static void
> new_entries_sd_cb( void *p_this, int i_status, const struct rr_entry
> *p_entries )
> {
> services_discovery_t *p_sd = (services_discovery_t *)p_this;
> - struct discovery_sys *p_sys = p_sd->p_sys;
> + struct microdns_sys *p_sys = p_sd->p_sys;
If you do
struct microdns_sys *p_mdns_sys = p_sd->p_sys;
struct discovery_sys *p_sys = p_mdns_sys->p_sys;
You won't have to modify every access of discovery_sys. The diff will be smaller and easier to read.
> if( i_status < 0 )
> {
> print_error( VLC_OBJECT( p_sd ), "entry callback", i_status );
> @@ -408,12 +440,12 @@ new_entries_sd_cb( void *p_this, int i_status,
> const struct rr_entry *p_entries
> if( psz_uri == NULL )
> break;
>
> - if( items_exists( p_sys, psz_uri ) )
> + if( items_exists( &p_sys->discovery_sys, psz_uri ) )
> {
> free( psz_uri );
> continue;
> }
> - items_add_input( p_sys, p_sd, psz_uri, p_srv->psz_device_name
> );
> + items_add_input( &p_sys->discovery_sys, p_sd, psz_uri,
> p_srv->psz_device_name );
> }
>
> for( unsigned int i = 0; i < i_nb_srv; ++i )
> @@ -426,13 +458,13 @@ static bool
> 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;
> + struct microdns_sys *p_sys = p_sd->p_sys;
>
> if( atomic_load( &p_sys->stop ) )
> return true;
> else
> {
> - items_timeout( p_sys, p_sd, NULL );
> + items_timeout( &p_sys->discovery_sys, p_sd, NULL );
> return false;
> }
> }
> @@ -441,11 +473,11 @@ static void *
> RunSD( void *p_this )
> {
> services_discovery_t *p_sd = ( services_discovery_t* )p_this;
> - struct discovery_sys *p_sys = p_sd->p_sys;
> + struct microdns_sys *p_sys = p_sd->p_sys;
>
> int i_status = mdns_listen( p_sys->p_microdns,
> - p_sys->ppsz_service_names,
> - p_sys->i_nb_service_names,
> + p_sys->discovery_sys.ppsz_service_names,
> + p_sys->discovery_sys.i_nb_service_names,
> RR_PTR, SEC_FROM_VLC_TICK(LISTEN_INTERVAL),
> stop_sd_cb, new_entries_sd_cb, p_sd );
>
> @@ -459,7 +491,7 @@ static void
> new_entries_rd_cb( void *p_this, int i_status, const struct rr_entry
> *p_entries )
> {
> vlc_renderer_discovery_t *p_rd = (vlc_renderer_discovery_t
> *)p_this;
> - struct discovery_sys *p_sys = p_rd->p_sys;
> + struct microdns_sys *p_sys = p_rd->p_sys;
> if( i_status < 0 )
> {
> print_error( VLC_OBJECT( p_rd ), "entry callback", i_status );
> @@ -506,7 +538,7 @@ new_entries_rd_cb( void *p_this, int i_status,
> const struct rr_entry *p_entries
> if( psz_uri == NULL )
> break;
>
> - if( items_exists( p_sys, psz_uri ) )
> + if( items_exists( &p_sys->discovery_sys, psz_uri ) )
> {
> free( psz_uri );
> continue;
> @@ -523,8 +555,8 @@ new_entries_rd_cb( void *p_this, int i_status,
> const struct rr_entry *p_entries
> if( strcmp( p_srv->psz_protocol, "chromecast" ) == 0)
> psz_demux_filter = "cc_demux";
>
> - items_add_renderer( p_sys, p_rd, p_srv->psz_device_name,
> psz_uri,
> - psz_demux_filter, psz_icon_uri,
> + items_add_renderer( &p_sys->discovery_sys, p_rd,
> p_srv->psz_device_name,
> + psz_uri, psz_demux_filter, psz_icon_uri,
> p_srv->i_renderer_flags );
> free(psz_icon_uri);
> }
> @@ -538,13 +570,13 @@ static bool
> stop_rd_cb( void *p_this )
> {
> vlc_renderer_discovery_t *p_rd = p_this;
> - struct discovery_sys *p_sys = p_rd->p_sys;
> + struct microdns_sys *p_sys = p_rd->p_sys;
>
> if( atomic_load( &p_sys->stop ) )
> return true;
> else
> {
> - items_timeout( p_sys, NULL, p_rd );
> + items_timeout( &p_sys->discovery_sys, NULL, p_rd );
> return false;
> }
> }
> @@ -553,11 +585,11 @@ static void *
> RunRD( void *p_this )
> {
> vlc_renderer_discovery_t *p_rd = p_this;
> - struct discovery_sys *p_sys = p_rd->p_sys;
> + struct microdns_sys *p_sys = p_rd->p_sys;
>
> int i_status = mdns_listen( p_sys->p_microdns,
> - p_sys->ppsz_service_names,
> - p_sys->i_nb_service_names,
> + p_sys->discovery_sys.ppsz_service_names,
> + p_sys->discovery_sys.i_nb_service_names,
> RR_PTR, SEC_FROM_VLC_TICK(LISTEN_INTERVAL),
> stop_rd_cb, new_entries_rd_cb, p_rd );
>
> @@ -567,62 +599,195 @@ 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 mdns_ip *mdns_ip,
> + 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 microdns_sys *p_sys = p_sa->p_sys;
> + struct mdns_ctx *ctx = p_sys->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;
> + }
> +
> + /*
> + * 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 */
> +
> + 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++ )
> + {
> + answers[i].rr_class = RR_IN;
> + answers[i].ttl = 120;
> +
> + if (i + 1 < hdr.num_ans_rr)
> + answers[i].next = &answers[i + 1];
> }
>
> - if( p_sys->i_nb_service_names == 0 )
> + // RR_PTR: point service type to local domain and instance name
> (VLC)
> + answers[0].type = RR_PTR;
> + answers[0].name = sa_service_type;
> + answers[0].data.PTR.domain = sa_service_type_link;
> +
> + // RR_TXT: provide additional information (HTTP server root
> directory etc.)
> + answers[1].type = RR_TXT;
> + answers[1].name = sa_service_type_link;
> +
> + // RR_SRV: provide info about the service we're announcing (port
> no, etc.)
> + answers[2].type = RR_SRV;
> + answers[2].name = sa_service_type_link;
> + answers[2].data.SRV.port = p_sys->advertisement_sys.i_http_port;
> + answers[2].data.SRV.priority = 0;
> + answers[2].data.SRV.weight = 0;
> + answers[2].data.SRV.target = sa_domain_name;
> +
> + // RR_A/AAAA: link .local domain to IP address
> + answers[3].name = sa_domain_name;
> + if (mdns_ip->family == AF_INET)
> {
> - msg_Err( p_obj, "no services found" );
> - goto error;
> + answers[3].type = RR_A;
> + memcpy(&answers[3].data.A.addr, &mdns_ip->ipv4,
> + sizeof(answers[3].data.A.addr));
> }
> - 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" );
> + else
> + {
> + answers[3].type = RR_AAAA;
> + memcpy(&answers[3].data.AAAA.addr, &mdns_ip->ipv6,
> + sizeof(answers[3].data.AAAA.addr));
> + }
> +
> + mdns_entries_send(ctx, &hdr, answers);
> +}
> +
> +static bool
> +stop_sa_cb( void *cbarg )
> +{
> + services_advertisement_t *p_sa = ( services_advertisement_t*
> )cbarg;
> + struct microdns_sys *p_sys = p_sa->p_sys;
> +
> + return atomic_load( &p_sys->stop );
> +}
> +
> +static void *
> +RunSA( void *p_this )
> +{
> + services_advertisement_t *p_sa = ( services_advertisement_t*
> )p_this;
> + struct microdns_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_microdns,
> + sa_service_type,
> + 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_microdns, stop_sa_cb, p_sa);
> +
> + return NULL;
> +}
> +
> +static void
> +CleanCommon( struct microdns_sys *p_sys )
> +{
> + if ( p_sys->p_microdns )
> + mdns_destroy( p_sys->p_microdns );
> + free( p_sys );
> +}
> +
> +static void
> +CleanDiscoveryCommon( struct microdns_sys *p_sys )
> +{
> + items_clear( &p_sys->discovery_sys );
> + CleanCommon( p_sys );
> +}
> +
> +
> +static void CloseCommon( struct microdns_sys *p_sys )
> +{
> + atomic_store( &p_sys->stop, true );
> + vlc_join( p_sys->thread, NULL );
> +}
> +
> +static int
> +OpenCommon( vlc_object_t *p_obj, struct microdns_sys *p_sys,
> + const char *mdns_addr, void *(*entry)(void *) )
> +{
> + int i_ret = VLC_EGENERIC;
> + atomic_init( &p_sys->stop, false );
> +
> + int i_status;
> + if( ( i_status = mdns_init( &p_sys->p_microdns, mdns_addr,
> 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->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 );
> return i_ret;
> }
>
> -static void
> -CleanCommon( struct discovery_sys *p_sys )
> +static int OpenDiscoveryCommon( vlc_object_t *p_obj, struct
> microdns_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;
> + struct discovery_sys *discovery_sys = &p_sys->discovery_sys;
>
> - items_clear( p_sys );
> - mdns_destroy( p_sys->p_microdns );
> - free( p_sys );
> + vlc_array_init( &discovery_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 )
> +
> discovery_sys->ppsz_service_names[discovery_sys->i_nb_service_names++] =
> + protocols[i].psz_service_name;
> + }
> + if( discovery_sys->i_nb_service_names == 0 )
> + {
> + msg_Err( p_obj, "no services found" );
> + goto error;
> + }
> + for( unsigned int i = 0; i < discovery_sys->i_nb_service_names;
> ++i )
> + msg_Dbg( p_obj, "mDNS: listening to %s %s",
> discovery_sys->ppsz_service_names[i],
> + b_renderer ? "renderer" : "service" );
> +
> + return OpenCommon( p_obj, p_sys, MDNS_ADDR_IPV4, entry );
> +error:
> + CleanCommon( p_sys );
> + return i_ret;
> }
>
> static int
> @@ -630,7 +795,7 @@ OpenSD( vlc_object_t *p_obj )
> {
> services_discovery_t *p_sd = (services_discovery_t *)p_obj;
>
> - struct discovery_sys *p_sys = calloc( 1, sizeof(struct discovery_sys) );
> + struct microdns_sys *p_sys = calloc( 1, sizeof(struct microdns_sys) );
> if( !p_sys )
> return VLC_ENOMEM;
> p_sd->p_sys = p_sys;
> @@ -638,16 +803,17 @@ OpenSD( vlc_object_t *p_obj )
> 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
> 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;
> + struct microdns_sys *p_sys = p_sd->p_sys;
>
> - CleanCommon( p_sys );
> + CloseCommon( p_sys );
> + CleanDiscoveryCommon( p_sys );
> }
>
> static int
> @@ -655,21 +821,59 @@ OpenRD( vlc_object_t *p_obj )
> {
> vlc_renderer_discovery_t *p_rd = (vlc_renderer_discovery_t *)p_obj;
>
> - struct discovery_sys *p_sys = calloc( 1, sizeof(struct discovery_sys) );
> + struct microdns_sys *p_sys = calloc( 1, sizeof(struct microdns_sys) );
> if( !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
> 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;
> + struct microdns_sys *p_sys = p_rd->p_sys;
> +
> + CloseCommon( p_sys );
> + CleanDiscoveryCommon( p_sys );
> +}
> +
> +static int
> +OpenSA( vlc_object_t *p_obj )
> +{
> + services_advertisement_t *p_sa = (services_advertisement_t *)p_obj;
> +
> + struct microdns_sys *p_sys = calloc( 1, sizeof(struct microdns_sys) );
> + if( !p_sys )
> + return VLC_ENOMEM;
> +
> + p_sa->p_sys = p_sys;
> + config_ChainParse( p_sa, CFG_PREFIX, ppsz_options, p_sa->p_cfg );
> +
> + p_sys->advertisement_sys.i_http_port = var_InheritInteger(
> + p_obj, "http-port" ); /* 8080 if unset */
> + char *psz_address_family = var_InheritString(
> + p_obj, "services-advertisement-address-family" ); /* v6 if unset */
> +
> +
> + p_sa->description = _("mDNS Network Advertisement");
> +
> + const char *mdns_addr = MDNS_ADDR_IPV6;
> + if( strcmp( psz_address_family, "v4" ) == 0 )
> + mdns_addr = MDNS_ADDR_IPV4;
> +
> + return OpenCommon( p_obj, p_sys, mdns_addr, RunSA );
YOu should free (psz_address_family).
> +}
> +
> +static void
> +CloseSA( vlc_object_t *p_this )
> +{
> + services_advertisement_t *p_sa = (services_advertisement_t *) p_this;
> + struct microdns_sys *p_sys = p_sa->p_sys;
>
> + CloseCommon( p_sys );
> CleanCommon( p_sys );
> }
> --
> 2.17.1
>
> _______________________________________________
> 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