[vlc-devel] [PATCH 5/7] Implement mDNS services advertisement submodule
Roland Bewick
roland.bewick at gmail.com
Tue Jul 30 05:11:27 CEST 2019
On 29/07/2019 6:19 PM, Thomas Guillem wrote:
>
> 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 ?
Thanks - Will do.
Roland
>
>>>> 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
> _______________________________________________
> 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