]> git.meshlink.io Git - catta/blob - avahi-common/domain.c
Add avahi_service_name_snprint()
[catta] / avahi-common / domain.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5  
6   avahi is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10  
11   avahi is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14   Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with avahi; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <limits.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34 #include <assert.h>
35
36 #include "domain.h"
37 #include "malloc.h"
38 #include "error.h"
39
40 char *avahi_get_host_name(void) {
41 #ifdef HOST_NAME_MAX
42     char t[HOST_NAME_MAX];
43 #else
44     char t[256];
45 #endif
46     gethostname(t, sizeof(t));
47     t[sizeof(t)-1] = 0;
48     return avahi_normalize_name(t);
49 }
50
51 static char *unescape_uneeded(const char *src, char *ret_dest, size_t size) {
52     int escaped = 0;
53     
54     assert(src);
55     assert(ret_dest);
56     assert(size > 0);
57     
58     for (; *src; src++) {
59
60         if (!escaped && *src == '\\')
61             escaped = 1;
62         else if (escaped && (*src == '.' || *src == '\\')) {
63
64             if ((size -= 2) <= 1) break;
65             
66             *(ret_dest++) = '\\';
67             *(ret_dest++) = *src;
68             escaped = 0;
69         } else {
70             if (--size <= 1) break;
71
72             *(ret_dest++) = *src;
73             escaped = 0;
74         }
75
76     }
77
78     *ret_dest = 0;
79     
80     return ret_dest;
81 }
82
83 char *avahi_normalize_name(const char *s) {
84     char tmp[256];
85     size_t l;
86     
87     assert(s);
88
89     unescape_uneeded(s, tmp, sizeof(tmp));
90
91     l = strlen(tmp);
92
93     while (l > 0 && tmp[l-1] == '.')
94         tmp[--l] = 0;
95
96     return avahi_strdup(tmp);
97 }
98
99
100 /* Read the first label from string *name, unescape "\" and write it to dest */
101 char *avahi_unescape_label(const char **name, char *dest, size_t size) {
102     unsigned i = 0;
103     char *d;
104     
105     assert(dest);
106     assert(size > 0);
107     assert(name);
108
109     if (!**name)
110         return NULL;
111
112     d = dest;
113     
114     for (;;) {
115         if (i >= size)
116             return NULL;
117
118         if (**name == '.') {
119             (*name)++;
120             break;
121         }
122         
123         if (**name == 0)
124             break;
125         
126         if (**name == '\\') {
127             (*name) ++;
128             
129             if (**name == 0)
130                 break;
131         }
132         
133         *(d++) = *((*name) ++);
134         i++;
135     }
136
137     assert(i < size);
138
139     *d = 0;
140
141     return dest;
142 }
143
144 /* Escape "\" and ".", append \0 */
145 char *avahi_escape_label(const uint8_t* src, size_t src_length, char **ret_name, size_t *ret_size) {
146     char *r;
147
148     assert(src);
149     assert(ret_name);
150     assert(*ret_name);
151     assert(ret_size);
152     assert(*ret_size > 0);
153
154     r = *ret_name;
155
156     while (src_length > 0) {
157         if (*src == '.' || *src == '\\') {
158             if (*ret_size < 3)
159                 return NULL;
160             
161             *((*ret_name) ++) = '\\';
162             (*ret_size) --;
163         }
164
165         if (*ret_size < 2)
166             return NULL;
167         
168         *((*ret_name)++) = *src;
169         (*ret_size) --;
170
171         src_length --;
172         src++;
173     }
174
175     **ret_name = 0;
176
177     return r;
178 }
179
180 int avahi_domain_equal(const char *a, const char *b) {
181     assert(a);
182     assert(b);
183
184     if (a == b)
185         return 1;
186     
187     for (;;) {
188         char ca[65], cb[65], *pa, *pb;
189
190         pa = avahi_unescape_label(&a, ca, sizeof(ca));
191         pb = avahi_unescape_label(&b, cb, sizeof(cb));
192
193         if (!pa && !pb)
194             return 1;
195         else if ((pa && !pb) || (!pa && pb))
196             return 0;
197
198         if (strcasecmp(pa, pb))
199             return 0;
200     }
201
202     return 1;
203 }
204
205 int avahi_binary_domain_cmp(const char *a, const char *b) {
206     assert(a);
207     assert(b);
208
209     if (a == b)
210         return 0;
211
212     for (;;) {
213         char ca[65], cb[65], *pa, *pb;
214         int r;
215
216         pa = avahi_unescape_label(&a, ca, sizeof(ca));
217         pb = avahi_unescape_label(&b, cb, sizeof(cb));
218
219         if (!pa && !pb)
220             return 0;
221         else if (pa && !pb)
222             return 1;
223         else if (!pa && pb)
224             return -1;
225         
226         if ((r = strcmp(pa, pb)))
227             return r;
228     }
229 }
230
231 int avahi_is_valid_service_type(const char *t) {
232     const char *p;
233     assert(t);
234
235     if (strlen(t) < 5)
236         return 0;
237     
238     if (*t != '_')
239         return 0;
240
241     if (!(p = strchr(t, '.')))
242         return 0;
243
244     if (p - t > 63 || p - t < 2)
245         return 0;
246
247     if (*(++p) != '_')
248         return 0;
249
250     if (strchr(p, '.'))
251         return 0;
252
253     if (strlen(p) > 63 || strlen(p) < 2)
254         return 0;
255     
256     return 1;
257 }
258
259 int avahi_is_valid_domain_name(const char *t) {
260     const char *p, *dp;
261     int dot = 0;
262         
263     assert(t);
264
265     if (*t == 0)
266         return 0;
267
268     /* Domains may not start with a dot */
269     if (*t == '.')
270         return 0;
271
272     dp = t; 
273
274     for (p = t; *p; p++) {
275
276         if (*p == '.') {
277             if (dot) /* Two subsequent dots */
278                 return 0;
279
280             if (p - dp > 63)
281                 return 0;
282
283             dot = 1;
284             dp = p + 1;
285         } else
286             dot = 0;
287
288     }
289
290     if (p - dp > 63)
291         return 0;
292
293     /* A trailing dot IS allowed */
294     
295     return 1;
296 }
297
298 int avahi_is_valid_service_name(const char *t) {
299     assert(t);
300
301     if (*t == 0)
302         return 0;
303
304     if (strlen(t) > 63)
305         return 0;
306
307     return 1;
308 }
309
310 int avahi_is_valid_host_name(const char *t) {
311     assert(t);
312
313     if (*t == 0)
314         return 0;
315
316     if (strlen(t) > 63)
317         return 0;
318
319     if (strchr(t, '.'))
320         return 0;
321
322     return 1;
323 }
324
325 unsigned avahi_domain_hash(const char *s) {
326     unsigned hash = 0;
327     
328     for (;;) {
329         char c[65], *p;
330
331         if (!avahi_unescape_label(&s, c, sizeof(c)))
332             return hash;
333
334         if (!c[0])
335             continue;
336
337         for (p = c; *p; p++)
338             hash = 31 * hash + tolower(*p);
339     }
340 }
341
342 int avahi_domain_ends_with(const char *domain, const char *suffix) {
343     assert(domain);
344     assert(suffix);
345
346     assert(avahi_is_valid_domain_name(domain));
347     assert(avahi_is_valid_domain_name(suffix));
348
349     for (;;) {
350         char dummy[64];
351         
352         if (avahi_domain_equal(domain, suffix))
353             return 1;
354
355         if (!(avahi_unescape_label(&domain, dummy, sizeof(dummy))))
356             return 0;
357     } 
358 }
359
360 static void escape_service_name(char *d, size_t size, const char *s) {
361     assert(d);
362     assert(size);
363     assert(s);
364
365     while (*s && size >= 2) {
366         if (*s == '.' || *s == '\\') {
367             if (size < 3)
368                 break;
369
370             *(d++) = '\\';
371             size--;
372         }
373             
374         *(d++) = *(s++);
375         size--;
376     }
377
378     assert(size > 0);
379     *(d++) = 0;
380 }
381
382
383 int avahi_service_name_snprint(char *p, size_t size, const char *name, const char *type, const char *domain) {
384     char *t = NULL, *d = NULL;
385     char ename[64];
386     int ret;
387     
388     assert(p);
389
390     if ((name && !avahi_is_valid_service_name(name))) {
391         ret = AVAHI_ERR_INVALID_SERVICE_NAME;
392         goto fail;
393     }
394
395     if (!avahi_is_valid_service_type(type)) {
396         ret = AVAHI_ERR_INVALID_SERVICE_TYPE;
397         goto fail;
398     }
399         
400     if (!avahi_is_valid_domain_name(domain)) {
401         ret = AVAHI_ERR_INVALID_DOMAIN_NAME;
402         goto fail;
403     }
404         
405     if (name)
406         escape_service_name(ename, sizeof(ename), name);
407     
408     if (!(d = avahi_normalize_name(domain)) ||
409         !(t = avahi_normalize_name(type))) {
410         ret = AVAHI_ERR_NO_MEMORY;
411         goto fail;
412     }
413
414     snprintf(p, size, "%s%s%s.%s", name ? ename : "", name ? "." : "", t, d);
415
416     ret = AVAHI_OK;
417     
418 fail:
419
420     avahi_free(t);
421     avahi_free(d);
422
423     return ret;
424 }