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