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