]> git.meshlink.io Git - catta/blob - avahi-common/domain.c
b4a6fc121b76bbdef4616808fecefef0aaede9ba
[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 #include "address.h"
40 #include "utf8.h"
41
42 /* Read the first label from string *name, unescape "\" and write it to dest */
43 char *avahi_unescape_label(const char **name, char *dest, size_t size) {
44     unsigned i = 0;
45     char *d;
46
47     assert(dest);
48     assert(size > 0);
49     assert(name);
50
51     d = dest;
52
53     for (;;) {
54         if (i >= size)
55             return NULL;
56
57         if (**name == '.') {
58             (*name)++;
59             break;
60         }
61
62         if (**name == 0)
63             break;
64
65         if (**name == '\\') {
66             /* Escaped character */
67
68             (*name) ++;
69
70             if (**name == 0)
71                 /* Ending NUL */
72                 return NULL;
73
74             else if (**name == '\\' || **name == '.') {
75                 /* Escaped backslash or dot */
76                 *(d++) = *((*name) ++);
77                 i++;
78             } else if (isdigit(**name)) {
79                 int n;
80
81                 /* Escaped literal ASCII character */
82
83                 if (!isdigit(*(*name+1)) || !isdigit(*(*name+2)))
84                     return NULL;
85
86                 n = ((uint8_t) (**name - '0') * 100) + ((uint8_t) (*(*name+1) - '0') * 10) + ((uint8_t) (*(*name +2) - '0'));
87
88                 if (n > 255 || n == 0)
89                     return NULL;
90
91                 *(d++) = (char) n;
92                 i++;
93
94                 (*name) += 3;
95             } else
96                 return NULL;
97
98         } else {
99
100             /* Normal character */
101
102             *(d++) = *((*name) ++);
103             i++;
104         }
105     }
106
107     assert(i < size);
108
109     *d = 0;
110
111     if (!avahi_utf8_valid(dest))
112         return NULL;
113
114     return dest;
115 }
116
117 /* Escape "\" and ".", append \0 */
118 char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size) {
119     char *r;
120
121     assert(src);
122     assert(ret_name);
123     assert(*ret_name);
124     assert(ret_size);
125     assert(*ret_size > 0);
126
127     r = *ret_name;
128
129     while (src_length > 0) {
130         if (*src == '.' || *src == '\\') {
131
132             /* Dot or backslash */
133
134             if (*ret_size < 3)
135                 return NULL;
136
137             *((*ret_name) ++) = '\\';
138             *((*ret_name) ++) = *src;
139             (*ret_size) -= 2;
140
141         } else if (
142             *src == '_' ||
143             *src == '-' ||
144             (*src >= '0' && *src <= '9') ||
145             (*src >= 'a' && *src <= 'z') ||
146             (*src >= 'A' && *src <= 'Z')) {
147
148             /* Proper character */
149
150             if (*ret_size < 2)
151                 return NULL;
152
153             *((*ret_name)++) = *src;
154             (*ret_size) --;
155
156         } else {
157
158             /* Everything else */
159
160             if (*ret_size < 5)
161                 return NULL;
162
163             *((*ret_name) ++) = '\\';
164             *((*ret_name) ++) = '0' + (char)  ((uint8_t) *src / 100);
165             *((*ret_name) ++) = '0' + (char) (((uint8_t) *src / 10) % 10);
166             *((*ret_name) ++) = '0' + (char)  ((uint8_t) *src % 10);
167
168             (*ret_size) -= 4;
169         }
170
171         src_length --;
172         src++;
173     }
174
175     **ret_name = 0;
176
177     return r;
178 }
179
180 char *avahi_normalize_name(const char *s, char *ret_s, size_t size) {
181     int empty = 1;
182     char *r;
183
184     assert(s);
185     assert(ret_s);
186     assert(size > 0);
187
188     r = ret_s;
189     *ret_s = 0;
190
191     while (*s) {
192         char label[AVAHI_LABEL_MAX];
193
194         if (!(avahi_unescape_label(&s, label, sizeof(label))))
195             return NULL;
196
197         if (label[0] == 0) {
198
199             if (*s == 0 && empty)
200                 return ret_s;
201
202             return NULL;
203         }
204
205         if (!empty) {
206             if (size < 1)
207                 return NULL;
208
209             *(r++) = '.';
210             size--;
211
212         } else
213             empty = 0;
214
215         avahi_escape_label(label, strlen(label), &r, &size);
216     }
217
218     return ret_s;
219 }
220
221 char *avahi_normalize_name_strdup(const char *s) {
222     char t[AVAHI_DOMAIN_NAME_MAX];
223     assert(s);
224
225     if (!(avahi_normalize_name(s, t, sizeof(t))))
226         return NULL;
227
228     return avahi_strdup(t);
229 }
230
231 int avahi_domain_equal(const char *a, const char *b) {
232     assert(a);
233     assert(b);
234
235     if (a == b)
236         return 1;
237
238     for (;;) {
239         char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *r;
240
241         r = avahi_unescape_label(&a, ca, sizeof(ca));
242         assert(r);
243         r = avahi_unescape_label(&b, cb, sizeof(cb));
244         assert(r);
245
246         if (strcasecmp(ca, cb))
247             return 0;
248
249         if (!*a && !*b)
250             return 1;
251     }
252
253     return 1;
254 }
255
256 int avahi_is_valid_service_type_generic(const char *t) {
257     assert(t);
258
259     if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
260         return 0;
261
262     do {
263         char label[AVAHI_LABEL_MAX];
264
265         if (!(avahi_unescape_label(&t, label, sizeof(label))))
266             return 0;
267
268         if (strlen(label) <= 2 || label[0] != '_')
269             return 0;
270
271     } while (*t);
272
273     return 1;
274 }
275
276 int avahi_is_valid_service_type_strict(const char *t) {
277     char label[AVAHI_LABEL_MAX];
278     assert(t);
279
280     if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
281         return 0;
282
283     /* Application name */
284
285     if (!(avahi_unescape_label(&t, label, sizeof(label))))
286         return 0;
287
288     if (strlen(label) <= 2 || label[0] != '_')
289         return 0;
290
291     if (!*t)
292         return 0;
293
294     /* _tcp or _udp boilerplate */
295
296     if (!(avahi_unescape_label(&t, label, sizeof(label))))
297         return 0;
298
299     if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp"))
300         return 0;
301
302     if (*t)
303         return 0;
304
305     return 1;
306 }
307
308 const char *avahi_get_type_from_subtype(const char *t) {
309     char label[AVAHI_LABEL_MAX];
310     const char *ret;
311     assert(t);
312
313     if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
314         return NULL;
315
316     /* Subtype name */
317
318     if (!(avahi_unescape_label(&t, label, sizeof(label))))
319         return NULL;
320
321     if (strlen(label) <= 2 || label[0] != '_')
322         return NULL;
323
324     if (!*t)
325         return NULL;
326
327     /* String "_sub" */
328
329     if (!(avahi_unescape_label(&t, label, sizeof(label))))
330         return NULL;
331
332     if (strcasecmp(label, "_sub"))
333         return NULL;
334
335     if (!*t)
336         return NULL;
337
338     ret = t;
339
340     /* Application name */
341
342     if (!(avahi_unescape_label(&t, label, sizeof(label))))
343         return NULL;
344
345     if (strlen(label) <= 2 || label[0] != '_')
346         return NULL;
347
348     if (!*t)
349         return NULL;
350
351     /* _tcp or _udp boilerplate */
352
353     if (!(avahi_unescape_label(&t, label, sizeof(label))))
354         return NULL;
355
356     if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp"))
357         return NULL;
358
359     if (*t)
360         return NULL;
361
362     return ret;
363 }
364
365 int avahi_is_valid_service_subtype(const char *t) {
366     assert(t);
367
368     return !!avahi_get_type_from_subtype(t);
369 }
370
371 int avahi_is_valid_domain_name(const char *t) {
372     int is_first = 1;
373     assert(t);
374
375     if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX)
376         return 0;
377
378     do {
379         char label[AVAHI_LABEL_MAX];
380
381         if (!(avahi_unescape_label(&t, label, sizeof(label))))
382             return 0;
383
384         /* Explicitly allow the root domain name */
385         if (is_first && label[0] == 0 && *t == 0)
386             return 1;
387
388         is_first = 0;
389
390         if (label[0] == 0)
391             return 0;
392
393     } while (*t);
394
395     return 1;
396 }
397
398 int avahi_is_valid_service_name(const char *t) {
399     assert(t);
400
401     if (strlen(t) >= AVAHI_LABEL_MAX || !*t)
402         return 0;
403
404     return 1;
405 }
406
407 int avahi_is_valid_host_name(const char *t) {
408     char label[AVAHI_LABEL_MAX];
409     assert(t);
410
411     if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
412         return 0;
413
414     if (!(avahi_unescape_label(&t, label, sizeof(label))))
415         return 0;
416
417     if (strlen(label) < 1)
418         return 0;
419
420     if (*t)
421         return 0;
422
423     return 1;
424 }
425
426 unsigned avahi_domain_hash(const char *s) {
427     unsigned hash = 0;
428
429     while (*s) {
430         char c[AVAHI_LABEL_MAX], *p, *r;
431
432         r = avahi_unescape_label(&s, c, sizeof(c));
433         assert(r);
434
435         for (p = c; *p; p++)
436             hash = 31 * hash + tolower(*p);
437     }
438
439     return hash;
440 }
441
442 int avahi_service_name_join(char *p, size_t size, const char *name, const char *type, const char *domain) {
443     char escaped_name[AVAHI_LABEL_MAX*4];
444     char normalized_type[AVAHI_DOMAIN_NAME_MAX];
445     char normalized_domain[AVAHI_DOMAIN_NAME_MAX];
446
447     assert(p);
448
449     /* Validity checks */
450
451     if ((name && !avahi_is_valid_service_name(name)))
452         return AVAHI_ERR_INVALID_SERVICE_NAME;
453
454     if (!avahi_is_valid_service_type_generic(type))
455         return AVAHI_ERR_INVALID_SERVICE_TYPE;
456
457     if (!avahi_is_valid_domain_name(domain))
458         return AVAHI_ERR_INVALID_DOMAIN_NAME;
459
460     /* Preparation */
461
462     if (name) {
463         size_t l = sizeof(escaped_name);
464         char *e = escaped_name, *r;
465         r = avahi_escape_label(name, strlen(name), &e, &l);
466         assert(r);
467     }
468
469     if (!(avahi_normalize_name(type, normalized_type, sizeof(normalized_type))))
470         return AVAHI_ERR_INVALID_SERVICE_TYPE;
471
472     if (!(avahi_normalize_name(domain, normalized_domain, sizeof(normalized_domain))))
473         return AVAHI_ERR_INVALID_DOMAIN_NAME;
474
475     /* Concatenation */
476
477     snprintf(p, size, "%s%s%s.%s", name ? escaped_name : "", name ? "." : "", normalized_type, normalized_domain);
478
479     return AVAHI_OK;
480 }
481
482 #ifndef HAVE_STRLCPY
483
484 static size_t strlcpy(char *dest, const char *src, size_t n) {
485     assert(dest);
486     assert(src);
487
488     if (n > 0) {
489         strncpy(dest, src, n-1);
490         dest[n-1] = 0;
491     }
492
493     return strlen(src);
494 }
495
496 #endif
497
498 int avahi_service_name_split(const char *p, char *name, size_t name_size, char *type, size_t type_size, char *domain, size_t domain_size) {
499     enum {
500         NAME,
501         TYPE,
502         DOMAIN
503     } state;
504     int type_empty = 1, domain_empty = 1;
505
506     assert(p);
507     assert(type);
508     assert(type_size > 0);
509     assert(domain);
510     assert(domain_size > 0);
511
512     if (name) {
513         assert(name_size > 0);
514         *name = 0;
515         state = NAME;
516     } else
517         state = TYPE;
518
519     *type = *domain = 0;
520
521     while (*p) {
522         char buf[64];
523
524         if (!(avahi_unescape_label(&p, buf, sizeof(buf))))
525             return -1;
526
527         switch (state) {
528             case NAME:
529                 strlcpy(name, buf, name_size);
530                 state = TYPE;
531                 break;
532
533             case TYPE:
534
535                 if (buf[0] == '_') {
536
537                     if (!type_empty) {
538                         if (!type_size)
539                             return AVAHI_ERR_NO_MEMORY;
540
541                         *(type++) = '.';
542                         type_size --;
543
544                     } else
545                         type_empty = 0;
546
547                     if (!(avahi_escape_label(buf, strlen(buf), &type, &type_size)))
548                         return AVAHI_ERR_NO_MEMORY;
549
550                     break;
551                 }
552
553                 state = DOMAIN;
554                 /* fall through */
555
556             case DOMAIN:
557
558                 if (!domain_empty) {
559                     if (!domain_size)
560                         return AVAHI_ERR_NO_MEMORY;
561
562                     *(domain++) = '.';
563                     domain_size --;
564                 } else
565                     domain_empty = 0;
566
567                 if (!(avahi_escape_label(buf, strlen(buf), &domain, &domain_size)))
568                     return AVAHI_ERR_NO_MEMORY;
569
570                 break;
571         }
572     }
573
574     return 0;
575 }
576
577 int avahi_is_valid_fqdn(const char *t) {
578     char label[AVAHI_LABEL_MAX];
579     char normalized[AVAHI_DOMAIN_NAME_MAX];
580     const char *k = t;
581     AvahiAddress a;
582     assert(t);
583
584     if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX)
585         return 0;
586
587     if (!avahi_is_valid_domain_name(t))
588         return 0;
589
590     /* Check if there are at least two labels*/
591     if (!(avahi_unescape_label(&k, label, sizeof(label))))
592         return 0;
593
594     if (label[0] == 0 || !k)
595         return 0;
596
597     if (!(avahi_unescape_label(&k, label, sizeof(label))))
598         return 0;
599
600     if (label[0] == 0 || !k)
601         return 0;
602
603     /* Make sure that the name is not an IP address */
604     if (!(avahi_normalize_name(t, normalized, sizeof(normalized))))
605         return 0;
606
607     if (avahi_address_parse(normalized, AVAHI_PROTO_UNSPEC, &a))
608         return 0;
609
610     return 1;
611 }