1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2008 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22 
23 #include <config.h>
24 #include <string.h>
25 #include <glib/gi18n-lib.h>
26 
27 #include <avahi-client/client.h>
28 #include <avahi-client/lookup.h>
29 #include <avahi-common/error.h>
30 #include <avahi-common/timeval.h>
31 #include <avahi-glib/glib-watch.h>
32 #include <avahi-glib/glib-malloc.h>
33 
34 #include "gvfsdnssdutils.h"
35 
36 static gchar *
escape_service_name(const gchar * service_name)37 escape_service_name (const gchar *service_name)
38 {
39   GString *s;
40   char *res;
41   const gchar *p;
42 
43   g_return_val_if_fail (service_name != NULL, NULL);
44 
45   s = g_string_new (NULL);
46 
47   p = service_name;
48   while (*p != '\0')
49     {
50       if (*p == '\\')
51         g_string_append (s, "\\\\");
52       else if (*p == '.')
53         g_string_append (s, "\\.");
54       else if (*p == '/')
55         g_string_append (s, "\\s");
56       else
57         g_string_append_c (s, *p);
58       p++;
59     }
60 
61   res = g_uri_escape_string (s->str, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
62   g_string_free (s, TRUE);
63   return res;
64 }
65 
66 static gchar *
escape_service_name2(const gchar * service_name)67 escape_service_name2 (const gchar *service_name)
68 {
69   GString *s;
70   const gchar *p;
71 
72   g_return_val_if_fail (service_name != NULL, NULL);
73 
74   s = g_string_new (NULL);
75 
76   p = service_name;
77   while (*p != '\0')
78     {
79       if (*p == '.')
80         g_string_append (s, "%2e");
81       else
82         g_string_append_c (s, *p);
83       p++;
84     }
85 
86   return g_string_free (s, FALSE);
87 }
88 
89 /**
90  * g_vfs_get_dns_sd_uri_for_triple:
91  * @service_name: DNS-SD service name.
92  * @service_type: DNS-SD service type.
93  * @domain: DNS-SD domain.
94  *
95  * Creates an URI for a file on the GVfs <literal>dns-sd</literal>
96  * virtual file system that provides live data for resolving the given
97  * DNS-SD service.
98  *
99  * The URI is of the form
100  * <literal>dns-sd://domain/service_name.service_type<literal> with
101  * suitable encoding added.
102  *
103  * Note that there may not exist a file at the returned URI, the
104  * resource providing the DNS-SD service will have to be available for
105  * the file to exist.
106  *
107  * Returns: An URI. Free with g_free().
108  **/
109 gchar *
g_vfs_get_dns_sd_uri_for_triple(const gchar * service_name,const gchar * service_type,const gchar * domain)110 g_vfs_get_dns_sd_uri_for_triple (const gchar *service_name,
111                                  const gchar *service_type,
112                                  const gchar *domain)
113 {
114   gchar *escaped_service_name;
115   gchar *ret;
116 
117   g_return_val_if_fail (service_name != NULL, NULL);
118   g_return_val_if_fail (service_type != NULL, NULL);
119   g_return_val_if_fail (domain != NULL, NULL);
120 
121   escaped_service_name = escape_service_name (service_name);
122 
123   ret = g_strdup_printf ("dns-sd://%s/%s.%s",
124                          domain,
125                          escaped_service_name,
126                          service_type);
127   g_free (escaped_service_name);
128 
129   return ret;
130 }
131 
132 /**
133  * g_vfs_encode_dns_sd_triple:
134  * @service_name: DNS-SD service name.
135  * @service_type: DNS-SD service type.
136  * @domain: DNS-SD domain.
137  *
138  * Creates an encoded triple representing a DNS-SD service. The triple
139  * will be of the form
140  * <literal>service_name.service_type.domain</literal> with suitable
141  * encoding.
142  *
143  * Use g_vfs_decode_dns_sd_triple() to decode the returned string.
144  *
145  * Returns: A string representing the triple, free with g_free().
146  **/
147 gchar *
g_vfs_encode_dns_sd_triple(const gchar * service_name,const gchar * service_type,const gchar * domain)148 g_vfs_encode_dns_sd_triple (const gchar *service_name,
149                             const gchar *service_type,
150                             const gchar *domain)
151 {
152   char *dot_escaped_service_name;
153   char *escaped_service_name;
154   char *escaped_service_type;
155   char *escaped_domain;
156   char *s;
157 
158   escaped_service_name = g_uri_escape_string (service_name, NULL, FALSE);
159   dot_escaped_service_name = escape_service_name2 (escaped_service_name);
160   escaped_service_type = g_uri_escape_string (service_type, NULL, FALSE);
161   escaped_domain = g_uri_escape_string (domain, NULL, FALSE);
162   s = g_strdup_printf ("%s.%s.%s",
163                        dot_escaped_service_name,
164                        escaped_service_type,
165                        escaped_domain);
166   g_free (dot_escaped_service_name);
167   g_free (escaped_service_name);
168   g_free (escaped_service_type);
169   g_free (escaped_domain);
170   return s;
171 }
172 
173 /**
174  * g_vfs_decode_dns_sd_triple:
175  * @encoded_triple: A string obtained from g_vfs_encode_dns_sd_triple().
176  * @out_service_name: %NULL or return location for the service name.
177  * @out_service_type: %NULL or return location for the service type.
178  * @out_domain: %NULL or return location for the domain.
179  * @error: Return location for error or %NULL.
180  *
181  * Constructs a DNS-SD triple by decoding a string generated from
182  * g_vfs_encode_dns_sd_triple(). This can fail if @encoded_triple is
183  * malformed.
184  *
185  * Returns: %TRUE unless @error is set.
186  **/
187 gboolean
g_vfs_decode_dns_sd_triple(const gchar * encoded_triple,gchar ** out_service_name,gchar ** out_service_type,gchar ** out_domain,GError ** error)188 g_vfs_decode_dns_sd_triple (const gchar *encoded_triple,
189                             gchar      **out_service_name,
190                             gchar      **out_service_type,
191                             gchar      **out_domain,
192                             GError     **error)
193 {
194   gboolean ret;
195   int n;
196   int m;
197   int service_type_pos;
198   char *escaped_service_name;
199   char *escaped_service_type;
200   char *escaped_domain;
201 
202   g_return_val_if_fail (encoded_triple != NULL, FALSE);
203 
204 
205   escaped_service_name = NULL;
206   escaped_service_type = NULL;
207   escaped_domain = NULL;
208   ret = FALSE;
209 
210   if (out_service_name != NULL)
211     *out_service_name = NULL;
212 
213   if (out_service_type != NULL)
214     *out_service_type = NULL;
215 
216   if (out_domain != NULL)
217     *out_domain = NULL;
218 
219   /* Find first '.' followed by an underscore. */
220   for (n = 0; encoded_triple[n] != '\0'; n++)
221     {
222       if (encoded_triple[n] == '.')
223         {
224           if (encoded_triple[n + 1] == '_')
225             break;
226         }
227     }
228   if (encoded_triple[n] == '\0')
229     {
230       g_set_error (error,
231                    G_IO_ERROR,
232                    G_IO_ERROR_INVALID_ARGUMENT,
233                    _("Malformed DNS-SD encoded_triple “%s”"),
234                    encoded_triple);
235       goto out;
236     }
237 
238   escaped_service_name = g_strndup (encoded_triple, n);
239   if (escaped_service_name == NULL)
240     goto out;
241 
242   if (out_service_name != NULL)
243     *out_service_name = g_uri_unescape_string (escaped_service_name, NULL);
244 
245   /* skip dot between service name and service type */
246   n += 1;
247 
248   service_type_pos = n;
249 
250   /* skip next two dots */
251   for (m = 0; m < 2; m++)
252     {
253       for (; encoded_triple[n] != '\0'; n++)
254         {
255           if (encoded_triple[n] == '.')
256             break;
257         }
258       if (encoded_triple[n] == '\0')
259         {
260           g_set_error (error,
261                        G_IO_ERROR,
262                        G_IO_ERROR_INVALID_ARGUMENT,
263                        _("Malformed DNS-SD encoded_triple “%s”"),
264                        encoded_triple);
265           goto out;
266         }
267       n++;
268     }
269 
270   escaped_service_type = g_strndup (encoded_triple + service_type_pos, n - service_type_pos - 1);
271   if (out_service_type != NULL)
272     *out_service_type = g_uri_unescape_string (escaped_service_type, NULL);
273 
274   /* the domain is the rest */
275   if (encoded_triple[n] == '\0')
276     {
277       g_set_error (error,
278                    G_IO_ERROR,
279                    G_IO_ERROR_INVALID_ARGUMENT,
280                    _("Malformed DNS-SD encoded_triple “%s”"),
281                    encoded_triple);
282       goto out;
283     }
284 
285   escaped_domain = g_strdup (encoded_triple + n);
286   if (out_domain != NULL)
287     *out_domain = g_uri_unescape_string (escaped_domain, NULL);
288 
289   ret = TRUE;
290 
291  out:
292   g_free (escaped_service_name);
293   g_free (escaped_service_type);
294   g_free (escaped_domain);
295   return ret;
296 }
297 
298 gchar *
g_vfs_normalize_encoded_dns_sd_triple(const gchar * encoded_triple)299 g_vfs_normalize_encoded_dns_sd_triple (const gchar *encoded_triple)
300 {
301   char *service_name;
302   char *service_type;
303   char *domain;
304   char *ret;
305 
306   ret = NULL;
307 
308   if (!g_vfs_decode_dns_sd_triple (encoded_triple,
309                                    &service_name,
310                                    &service_type,
311                                    &domain,
312                                    NULL))
313     goto out;
314 
315   ret = g_vfs_encode_dns_sd_triple (service_name, service_type, domain);
316   g_free (service_name);
317   g_free (service_type);
318   g_free (domain);
319 
320  out:
321   return ret;
322 }
323 
324