1 /*****************************************************************************
2 * avahi.c: Bonjour services discovery module
3 *****************************************************************************
4 * Copyright (C) 2005-2009, 2016 VideoLAN and VLC authors
6 * Authors: Jon Lech Johansen <jon@nanocrew.net>
7 * Jean-Baptiste Kempf <jb@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_services_discovery.h>
37 #include <avahi-client/client.h>
38 #include <avahi-client/publish.h>
39 #include <avahi-client/lookup.h>
40 #include <avahi-common/thread-watch.h>
41 #include <avahi-common/malloc.h>
42 #include <avahi-common/error.h>
44 /*****************************************************************************
46 *****************************************************************************/
49 static int Open ( vlc_object_t
* );
50 static void Close( vlc_object_t
* );
52 VLC_SD_PROBE_HELPER("avahi", N_("Zeroconf network services"), SD_CAT_LAN
)
55 set_shortname( "Avahi" )
56 set_description( N_("Zeroconf services") )
57 set_category( CAT_PLAYLIST
)
58 set_subcategory( SUBCAT_PLAYLIST_SD
)
59 set_capability( "services_discovery", 0 )
60 set_callbacks( Open
, Close
)
61 add_shortcut( "mdns", "avahi" )
63 VLC_SD_PROBE_SUBMODULE
66 /*****************************************************************************
68 *****************************************************************************/
72 AvahiThreadedPoll
*poll
;
74 vlc_dictionary_t services_name_to_input_item
;
75 } services_discovery_sys_t
;
79 const char *psz_protocol
;
80 const char *psz_service_name
;
82 { "ftp", "_ftp._tcp" },
83 { "smb", "_smb._tcp" },
84 { "nfs", "_nfs._tcp" },
85 { "sftp", "_sftp-ssh._tcp" },
86 { "rtsp", "_rtsp._tcp" },
88 #define NB_PROTOCOLS (sizeof(protocols) / sizeof(*protocols))
90 /*****************************************************************************
92 *****************************************************************************/
93 static void client_callback( AvahiClient
*c
, AvahiClientState state
,
96 services_discovery_t
*p_sd
= ( services_discovery_t
* )userdata
;
97 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
99 if( state
== AVAHI_CLIENT_FAILURE
&&
100 (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
) )
102 msg_Err( p_sd
, "avahi client disconnected" );
103 avahi_threaded_poll_quit( p_sys
->poll
);
107 /*****************************************************************************
109 *****************************************************************************/
110 static void resolve_callback(
111 AvahiServiceResolver
*r
,
112 AvahiIfIndex interface
,
113 AvahiProtocol protocol
,
114 AvahiResolverEvent event
,
118 const char *host_name
,
119 const AvahiAddress
*address
,
121 AvahiStringList
*txt
,
122 AvahiLookupResultFlags flags
,
125 services_discovery_t
*p_sd
= ( services_discovery_t
* )userdata
;
126 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
128 VLC_UNUSED(interface
); VLC_UNUSED(host_name
);
131 if( event
== AVAHI_RESOLVER_FAILURE
)
134 "failed to resolve service '%s' of type '%s' in domain '%s'",
135 name
, type
, domain
);
137 else if( event
== AVAHI_RESOLVER_FOUND
)
140 char *psz_uri
= NULL
;
141 char *psz_addr
= NULL
;
142 AvahiStringList
*asl
= NULL
;
143 input_item_t
*p_input
= NULL
;
145 msg_Info( p_sd
, "service '%s' of type '%s' in domain '%s' port %i",
146 name
, type
, domain
, port
);
148 avahi_address_snprint(a
, (sizeof(a
)/sizeof(a
[0]))-1, address
);
149 if( protocol
== AVAHI_PROTO_INET6
)
150 if( asprintf( &psz_addr
, "[%s]", a
) == -1 )
153 const char *psz_protocol
= NULL
;
154 for( unsigned int i
= 0; i
< NB_PROTOCOLS
; i
++ )
156 if( !strcmp(type
, protocols
[i
].psz_service_name
) )
157 psz_protocol
= protocols
[i
].psz_protocol
;
159 if( psz_protocol
== NULL
)
163 asl
= avahi_string_list_find( txt
, "path" );
169 if( avahi_string_list_get_pair( asl
, &key
, &value
, &size
) == 0 &&
172 if( asprintf( &psz_uri
, "%s://%s:%d%s",
174 psz_addr
!= NULL
? psz_addr
: a
,
175 port
, value
) == -1 )
182 avahi_free( (void *)key
);
184 avahi_free( (void *)value
);
188 if( asprintf( &psz_uri
, "%s://%s:%d",
190 psz_addr
!= NULL
? psz_addr
: a
, port
) == -1 )
199 if( psz_uri
!= NULL
)
201 p_input
= input_item_New( psz_uri
, name
);
204 if( p_input
!= NULL
)
206 vlc_dictionary_insert( &p_sys
->services_name_to_input_item
,
208 services_discovery_AddItem( p_sd
, p_input
);
209 input_item_Release( p_input
);
213 avahi_service_resolver_free( r
);
216 /*****************************************************************************
218 *****************************************************************************/
219 static void browse_callback(
220 AvahiServiceBrowser
*b
,
221 AvahiIfIndex interface
,
222 AvahiProtocol protocol
,
223 AvahiBrowserEvent event
,
227 AvahiLookupResultFlags flags
,
232 services_discovery_t
*p_sd
= ( services_discovery_t
* )userdata
;
233 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
234 if( event
== AVAHI_BROWSER_NEW
)
236 if( avahi_service_resolver_new( p_sys
->client
, interface
, protocol
,
237 name
, type
, domain
, AVAHI_PROTO_UNSPEC
,
239 resolve_callback
, userdata
) == NULL
)
241 msg_Err( p_sd
, "failed to resolve service '%s': %s", name
,
242 avahi_strerror( avahi_client_errno( p_sys
->client
) ) );
245 else if( event
== AVAHI_BROWSER_REMOVE
&& name
)
247 /** \todo Store the input id and search it, rather than searching the items */
248 input_item_t
*p_item
;
249 p_item
= vlc_dictionary_value_for_key(
250 &p_sys
->services_name_to_input_item
,
253 msg_Err( p_sd
, "failed to find service '%s' in playlist", name
);
256 services_discovery_RemoveItem( p_sd
, p_item
);
257 vlc_dictionary_remove_value_for_key(
258 &p_sys
->services_name_to_input_item
,
264 /*****************************************************************************
265 * Open: initialize and create stuff
266 *****************************************************************************/
267 static int Open( vlc_object_t
*p_this
)
269 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
270 services_discovery_sys_t
*p_sys
;
273 p_sd
->p_sys
= p_sys
= calloc( 1, sizeof( services_discovery_sys_t
) );
277 p_sd
->description
= _("Zeroconf network services");
279 vlc_dictionary_init( &p_sys
->services_name_to_input_item
, 1 );
281 p_sys
->poll
= avahi_threaded_poll_new();
282 if( p_sys
->poll
== NULL
)
284 msg_Err( p_sd
, "failed to create Avahi threaded poll" );
288 p_sys
->client
= avahi_client_new( avahi_threaded_poll_get(p_sys
->poll
),
289 0, client_callback
, p_sd
, &err
);
290 if( p_sys
->client
== NULL
)
292 msg_Err( p_sd
, "failed to create avahi client: %s",
293 avahi_strerror( err
) );
297 for( unsigned i
= 0; i
< NB_PROTOCOLS
; i
++ )
299 AvahiServiceBrowser
*sb
;
300 sb
= avahi_service_browser_new( p_sys
->client
, AVAHI_IF_UNSPEC
,
302 protocols
[i
].psz_service_name
, NULL
,
303 0, browse_callback
, p_sd
);
306 msg_Err( p_sd
, "failed to create avahi service browser %s", avahi_strerror( avahi_client_errno(p_sys
->client
) ) );
311 avahi_threaded_poll_start( p_sys
->poll
);
316 if( p_sys
->client
!= NULL
)
317 avahi_client_free( p_sys
->client
);
318 if( p_sys
->poll
!= NULL
)
319 avahi_threaded_poll_free( p_sys
->poll
);
321 vlc_dictionary_clear( &p_sys
->services_name_to_input_item
, NULL
, NULL
);
327 /*****************************************************************************
329 *****************************************************************************/
330 static void Close( vlc_object_t
*p_this
)
332 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
333 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
334 avahi_threaded_poll_stop( p_sys
->poll
);
336 avahi_client_free( p_sys
->client
);
337 avahi_threaded_poll_free( p_sys
->poll
);
339 vlc_dictionary_clear( &p_sys
->services_name_to_input_item
, NULL
, NULL
);