[vlc-devel] [PATCH 3/3] Add microdns services_advertisement submodule

Roland Bewick roland.bewick at gmail.com
Mon Sep 2 07:25:43 CEST 2019


---
 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 */
+
 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( 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 );
+}
+
+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



More information about the vlc-devel mailing list