Remmina - The GTK+ Remote Desktop Client  v1.4.2
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
remmina_avahi.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2009-2010 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2020 Antenore Gatta, Giovanni Panozzo
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * In addition, as a special exception, the copyright holders give
23  * permission to link the code of portions of this program with the
24  * OpenSSL library under certain conditions as described in each
25  * individual source file, and distribute linked combinations
26  * including the two.
27  * You must obey the GNU General Public License in all respects
28  * for all of the code used other than OpenSSL. * If you modify
29  * file(s) with this exception, you may extend this exception to your
30  * version of the file(s), but you are not obligated to do so. * If you
31  * do not wish to do so, delete this exception statement from your
32  * version. * If you delete this exception statement from all source
33  * files in the program, then also delete it here.
34  *
35  */
36 
37 #include <gtk/gtk.h>
38 #include "config.h"
39 #include "remmina_avahi.h"
41 
42 #ifdef HAVE_LIBAVAHI_CLIENT
43 
44 #include <avahi-client/client.h>
45 #include <avahi-client/lookup.h>
46 #include <avahi-common/simple-watch.h>
47 #include <avahi-common/malloc.h>
48 #include <avahi-common/error.h>
49 
51  AvahiSimplePoll* simple_poll;
52  AvahiClient* client;
53  AvahiServiceBrowser* sb;
55  gboolean has_event;
56 };
57 
58 static void
60  AvahiServiceResolver* r,
61  AVAHI_GCC_UNUSED AvahiIfIndex interface,
62  AVAHI_GCC_UNUSED AvahiProtocol protocol,
63  AvahiResolverEvent event,
64  const char* name,
65  const char* type,
66  const char* domain,
67  const char* host_name,
68  const AvahiAddress* address,
69  uint16_t port,
70  AvahiStringList* txt,
71  AvahiLookupResultFlags flags,
72  AVAHI_GCC_UNUSED void* userdata)
73 {
74  TRACE_CALL(__func__);
75  gchar* key;
76  gchar* value;
77  RemminaAvahi* ga = (RemminaAvahi*)userdata;
78 
79  assert(r);
80 
81  ga->priv->has_event = TRUE;
82 
83  switch (event) {
84  case AVAHI_RESOLVER_FAILURE:
85  g_print("(remmina-applet avahi-resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n",
86  name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))));
87  break;
88 
89  case AVAHI_RESOLVER_FOUND:
90  key = g_strdup_printf("%s,%s,%s", name, type, domain);
91  if (g_hash_table_lookup(ga->discovered_services, key)) {
92  g_free(key);
93  break;
94  }
95  value = g_strdup_printf("[%s]:%i", host_name, port);
96  g_hash_table_insert(ga->discovered_services, key, value);
97  /* key and value will be freed with g_free when the has table is freed */
98 
99  g_print("(remmina-applet avahi-resolver) Added service '%s'\n", value);
100 
101  break;
102  }
103 
104  avahi_service_resolver_free(r);
105 }
106 
107 static void
109  AvahiServiceBrowser* b,
110  AvahiIfIndex interface,
111  AvahiProtocol protocol,
112  AvahiBrowserEvent event,
113  const char* name,
114  const char* type,
115  const char* domain,
116  AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
117  void* userdata)
118 {
119  TRACE_CALL(__func__);
120  gchar* key;
121  RemminaAvahi* ga = (RemminaAvahi*)userdata;
122 
123  assert(b);
124 
125  ga->priv->has_event = TRUE;
126 
127  switch (event) {
128  case AVAHI_BROWSER_FAILURE:
129  g_print("(remmina-applet avahi-browser) %s\n",
130  avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
131  return;
132 
133  case AVAHI_BROWSER_NEW:
134  key = g_strdup_printf("%s,%s,%s", name, type, domain);
135  if (g_hash_table_lookup(ga->discovered_services, key)) {
136  g_free(key);
137  break;
138  }
139  g_free(key);
140 
141  g_print("(remmina-applet avahi-browser) Found service '%s' of type '%s' in domain '%s'\n", name, type, domain);
142 
143  if (!(avahi_service_resolver_new(ga->priv->client, interface, protocol, name, type, domain,
144  AVAHI_PROTO_UNSPEC, 0, remmina_avahi_resolve_callback, ga))) {
145  g_print("(remmina-applet avahi-browser) Failed to resolve service '%s': %s\n",
146  name, avahi_strerror(avahi_client_errno(ga->priv->client)));
147  }
148  break;
149 
150  case AVAHI_BROWSER_REMOVE:
151  g_print("(remmina-applet avahi-browser) Removed service '%s' of type '%s' in domain '%s'\n", name, type, domain);
152  key = g_strdup_printf("%s,%s,%s", name, type, domain);
153  g_hash_table_remove(ga->discovered_services, key);
154  g_free(key);
155  break;
156 
157  case AVAHI_BROWSER_ALL_FOR_NOW:
158  case AVAHI_BROWSER_CACHE_EXHAUSTED:
159  break;
160  }
161 }
162 
163 static void remmina_avahi_client_callback(AvahiClient* c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata)
164 {
165  TRACE_CALL(__func__);
166  RemminaAvahi* ga = (RemminaAvahi*)userdata;
167 
168  ga->priv->has_event = TRUE;
169 
170  if (state == AVAHI_CLIENT_FAILURE) {
171  g_print("(remmina-applet avahi) Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c)));
172  }
173 }
174 
176 {
177  TRACE_CALL(__func__);
178  while (TRUE) {
179  /* Call the iteration until no further events */
180  ga->priv->has_event = FALSE;
181  avahi_simple_poll_iterate(ga->priv->simple_poll, 0);
182  if (!ga->priv->has_event)
183  break;
184  }
185 
186  return TRUE;
187 }
188 
190 {
191  TRACE_CALL(__func__);
192  RemminaAvahi* ga;
193 
194  ga = g_new(RemminaAvahi, 1);
195  ga->discovered_services = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
196  ga->started = FALSE;
197  ga->priv = g_new(RemminaAvahiPriv, 1);
198  ga->priv->simple_poll = NULL;
199  ga->priv->client = NULL;
200  ga->priv->sb = NULL;
201  ga->priv->iterate_handler = 0;
202  ga->priv->has_event = FALSE;
203 
204  return ga;
205 }
206 
208 {
209  TRACE_CALL(__func__);
210  int error;
211 
212  if (ga->started)
213  return;
214 
215  ga->started = TRUE;
216 
217  ga->priv->simple_poll = avahi_simple_poll_new();
218  if (!ga->priv->simple_poll) {
219  g_print("Failed to create simple poll object.\n");
220  return;
221  }
222 
223  ga->priv->client = avahi_client_new(avahi_simple_poll_get(ga->priv->simple_poll), 0, remmina_avahi_client_callback, ga,
224  &error);
225  if (!ga->priv->client) {
226  g_print("Failed to create client: %s\n", avahi_strerror(error));
227  return;
228  }
229 
231  ga->priv->sb = avahi_service_browser_new(ga->priv->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_rfb._tcp", NULL, 0,
233  if (!ga->priv->sb) {
234  g_print("Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(ga->priv->client)));
235  return;
236  }
237 
238  ga->priv->iterate_handler = g_timeout_add(5000, (GSourceFunc)remmina_avahi_iterate, ga);
239 }
240 
242 {
243  TRACE_CALL(__func__);
244  g_hash_table_remove_all(ga->discovered_services);
245  if (ga->priv->iterate_handler) {
246  g_source_remove(ga->priv->iterate_handler);
247  ga->priv->iterate_handler = 0;
248  }
249  if (ga->priv->sb) {
250  avahi_service_browser_free(ga->priv->sb);
251  ga->priv->sb = NULL;
252  }
253  if (ga->priv->client) {
254  avahi_client_free(ga->priv->client);
255  ga->priv->client = NULL;
256  }
257  if (ga->priv->simple_poll) {
258  avahi_simple_poll_free(ga->priv->simple_poll);
259  ga->priv->simple_poll = NULL;
260  }
261  ga->started = FALSE;
262 }
263 
265 {
266  TRACE_CALL(__func__);
267  if (ga == NULL)
268  return;
269 
270  remmina_avahi_stop(ga);
271 
272  g_free(ga->priv);
273  g_hash_table_destroy(ga->discovered_services);
274  g_free(ga);
275 }
276 
277 #else
278 
280 {
281  TRACE_CALL(__func__);
282  return NULL;
283 }
284 
286 {
287  TRACE_CALL(__func__);
288 }
289 
291 {
292  TRACE_CALL(__func__);
293 }
294 
296 {
297  TRACE_CALL(__func__);
298 }
299 
300 #endif
301 
static void remmina_avahi_client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *userdata)
RemminaAvahi * remmina_avahi_new(void)
RemminaAvahiPriv * priv
Definition: remmina_avahi.h:46
typedefG_BEGIN_DECLS struct _RemminaAvahiPriv RemminaAvahiPriv
Definition: remmina_avahi.h:40
static void remmina_avahi_resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, AVAHI_GCC_UNUSED void *userdata)
Definition: remmina_avahi.c:59
static gboolean remmina_avahi_iterate(RemminaAvahi *ga)
AvahiServiceBrowser * sb
Definition: remmina_avahi.c:53
void remmina_avahi_stop(RemminaAvahi *ga)
AvahiClient * client
Definition: remmina_avahi.c:52
void remmina_avahi_free(RemminaAvahi *ga)
gboolean started
Definition: remmina_avahi.h:44
GHashTable * discovered_services
Definition: remmina_avahi.h:43
void remmina_avahi_start(RemminaAvahi *ga)
static void remmina_avahi_browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void *userdata)
AvahiSimplePoll * simple_poll
Definition: remmina_avahi.c:51