1 /***
2 This file is part of avahi.
3
4 avahi is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
8
9 avahi is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12 Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with avahi; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17 USA.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <string.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <stdio.h>
30 #include <ctype.h>
31 #include <stdlib.h>
32 #include <assert.h>
33
34 #include "domain.h"
35 #include "avahi-malloc.h"
36 #include "error.h"
37 #include "address.h"
38 #include "utf8.h"
39
40 /* Read the first label from string *name, unescape "\" and write it to dest */
avahi_unescape_label(const char ** name,char * dest,size_t size)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 if (!avahi_utf8_valid(dest))
110 return NULL;
111
112 return dest;
113 }
114
115 /* Escape "\" and ".", append \0 */
avahi_escape_label(const char * src,size_t src_length,char ** ret_name,size_t * ret_size)116 char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size) {
117 char *r;
118
119 assert(src);
120 assert(ret_name);
121 assert(*ret_name);
122 assert(ret_size);
123 assert(*ret_size > 0);
124
125 r = *ret_name;
126
127 while (src_length > 0) {
128 if (*src == '.' || *src == '\\') {
129
130 /* Dot or backslash */
131
132 if (*ret_size < 3)
133 return NULL;
134
135 *((*ret_name) ++) = '\\';
136 *((*ret_name) ++) = *src;
137 (*ret_size) -= 2;
138
139 } else if (
140 *src == '_' ||
141 *src == '-' ||
142 (*src >= '0' && *src <= '9') ||
143 (*src >= 'a' && *src <= 'z') ||
144 (*src >= 'A' && *src <= 'Z')) {
145
146 /* Proper character */
147
148 if (*ret_size < 2)
149 return NULL;
150
151 *((*ret_name)++) = *src;
152 (*ret_size) --;
153
154 } else {
155
156 /* Everything else */
157
158 if (*ret_size < 5)
159 return NULL;
160
161 *((*ret_name) ++) = '\\';
162 *((*ret_name) ++) = '0' + (char) ((uint8_t) *src / 100);
163 *((*ret_name) ++) = '0' + (char) (((uint8_t) *src / 10) % 10);
164 *((*ret_name) ++) = '0' + (char) ((uint8_t) *src % 10);
165
166 (*ret_size) -= 4;
167 }
168
169 src_length --;
170 src++;
171 }
172
173 **ret_name = 0;
174
175 return r;
176 }
177
avahi_normalize_name(const char * s,char * ret_s,size_t size)178 char *avahi_normalize_name(const char *s, char *ret_s, size_t size) {
179 int empty = 1;
180 char *r;
181
182 assert(s);
183 assert(ret_s);
184 assert(size > 0);
185
186 r = ret_s;
187 *ret_s = 0;
188
189 while (*s) {
190 char label[AVAHI_LABEL_MAX];
191
192 if (!(avahi_unescape_label(&s, label, sizeof(label))))
193 return NULL;
194
195 if (label[0] == 0) {
196
197 if (*s == 0 && empty)
198 return ret_s;
199
200 return NULL;
201 }
202
203 if (!empty) {
204 if (size < 1)
205 return NULL;
206
207 *(r++) = '.';
208 size--;
209
210 } else
211 empty = 0;
212
213 avahi_escape_label(label, strlen(label), &r, &size);
214 }
215
216 return ret_s;
217 }
218
avahi_normalize_name_strdup(const char * s)219 char *avahi_normalize_name_strdup(const char *s) {
220 char t[AVAHI_DOMAIN_NAME_MAX];
221 assert(s);
222
223 if (!(avahi_normalize_name(s, t, sizeof(t))))
224 return NULL;
225
226 return avahi_strdup(t);
227 }
228
avahi_domain_equal(const char * a,const char * b)229 int avahi_domain_equal(const char *a, const char *b) {
230 assert(a);
231 assert(b);
232
233 if (a == b)
234 return 1;
235
236 for (;;) {
237 char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *r;
238
239 r = avahi_unescape_label(&a, ca, sizeof(ca));
240 assert(r);
241 r = avahi_unescape_label(&b, cb, sizeof(cb));
242 assert(r);
243
244 if (strcasecmp(ca, cb))
245 return 0;
246
247 if (!*a && !*b)
248 return 1;
249 }
250
251 return 1;
252 }
253
avahi_is_valid_service_type_generic(const char * t)254 int avahi_is_valid_service_type_generic(const char *t) {
255 assert(t);
256
257 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
258 return 0;
259
260 do {
261 char label[AVAHI_LABEL_MAX];
262
263 if (!(avahi_unescape_label(&t, label, sizeof(label))))
264 return 0;
265
266 if (strlen(label) <= 2 || label[0] != '_')
267 return 0;
268
269 } while (*t);
270
271 return 1;
272 }
273
avahi_is_valid_service_type_strict(const char * t)274 int avahi_is_valid_service_type_strict(const char *t) {
275 char label[AVAHI_LABEL_MAX];
276 assert(t);
277
278 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
279 return 0;
280
281 /* Application name */
282
283 if (!(avahi_unescape_label(&t, label, sizeof(label))))
284 return 0;
285
286 if (strlen(label) <= 2 || label[0] != '_')
287 return 0;
288
289 if (!*t)
290 return 0;
291
292 /* _tcp or _udp boilerplate */
293
294 if (!(avahi_unescape_label(&t, label, sizeof(label))))
295 return 0;
296
297 if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp"))
298 return 0;
299
300 if (*t)
301 return 0;
302
303 return 1;
304 }
305
avahi_get_type_from_subtype(const char * t)306 const char *avahi_get_type_from_subtype(const char *t) {
307 char label[AVAHI_LABEL_MAX];
308 const char *ret;
309 assert(t);
310
311 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
312 return NULL;
313
314 /* Subtype name */
315
316 if (!(avahi_unescape_label(&t, label, sizeof(label))))
317 return NULL;
318
319 if (strlen(label) <= 2 || label[0] != '_')
320 return NULL;
321
322 if (!*t)
323 return NULL;
324
325 /* String "_sub" */
326
327 if (!(avahi_unescape_label(&t, label, sizeof(label))))
328 return NULL;
329
330 if (strcasecmp(label, "_sub"))
331 return NULL;
332
333 if (!*t)
334 return NULL;
335
336 ret = t;
337
338 /* Application name */
339
340 if (!(avahi_unescape_label(&t, label, sizeof(label))))
341 return NULL;
342
343 if (strlen(label) <= 2 || label[0] != '_')
344 return NULL;
345
346 if (!*t)
347 return NULL;
348
349 /* _tcp or _udp boilerplate */
350
351 if (!(avahi_unescape_label(&t, label, sizeof(label))))
352 return NULL;
353
354 if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp"))
355 return NULL;
356
357 if (*t)
358 return NULL;
359
360 return ret;
361 }
362
avahi_is_valid_service_subtype(const char * t)363 int avahi_is_valid_service_subtype(const char *t) {
364 assert(t);
365
366 return !!avahi_get_type_from_subtype(t);
367 }
368
avahi_is_valid_domain_name(const char * t)369 int avahi_is_valid_domain_name(const char *t) {
370 int is_first = 1;
371 assert(t);
372
373 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX)
374 return 0;
375
376 do {
377 char label[AVAHI_LABEL_MAX];
378
379 if (!(avahi_unescape_label(&t, label, sizeof(label))))
380 return 0;
381
382 /* Explicitly allow the root domain name */
383 if (is_first && label[0] == 0 && *t == 0)
384 return 1;
385
386 is_first = 0;
387
388 if (label[0] == 0)
389 return 0;
390
391 } while (*t);
392
393 return 1;
394 }
395
avahi_is_valid_service_name(const char * t)396 int avahi_is_valid_service_name(const char *t) {
397 assert(t);
398
399 if (strlen(t) >= AVAHI_LABEL_MAX || !*t)
400 return 0;
401
402 return 1;
403 }
404
avahi_is_valid_host_name(const char * t)405 int avahi_is_valid_host_name(const char *t) {
406 char label[AVAHI_LABEL_MAX];
407 assert(t);
408
409 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
410 return 0;
411
412 if (!(avahi_unescape_label(&t, label, sizeof(label))))
413 return 0;
414
415 if (strlen(label) < 1)
416 return 0;
417
418 if (*t)
419 return 0;
420
421 return 1;
422 }
423
avahi_domain_hash(const char * s)424 unsigned avahi_domain_hash(const char *s) {
425 unsigned hash = 0;
426
427 while (*s) {
428 char c[AVAHI_LABEL_MAX], *p, *r;
429
430 r = avahi_unescape_label(&s, c, sizeof(c));
431 assert(r);
432
433 for (p = c; *p; p++)
434 hash = 31 * hash + tolower(*p);
435 }
436
437 return hash;
438 }
439
avahi_service_name_join(char * p,size_t size,const char * name,const char * type,const char * domain)440 int avahi_service_name_join(char *p, size_t size, const char *name, const char *type, const char *domain) {
441 char escaped_name[AVAHI_LABEL_MAX*4];
442 char normalized_type[AVAHI_DOMAIN_NAME_MAX];
443 char normalized_domain[AVAHI_DOMAIN_NAME_MAX];
444
445 assert(p);
446
447 /* Validity checks */
448
449 if ((name && !avahi_is_valid_service_name(name)))
450 return AVAHI_ERR_INVALID_SERVICE_NAME;
451
452 if (!avahi_is_valid_service_type_generic(type))
453 return AVAHI_ERR_INVALID_SERVICE_TYPE;
454
455 if (!avahi_is_valid_domain_name(domain))
456 return AVAHI_ERR_INVALID_DOMAIN_NAME;
457
458 /* Preparation */
459
460 if (name) {
461 size_t l = sizeof(escaped_name);
462 char *e = escaped_name, *r;
463 r = avahi_escape_label(name, strlen(name), &e, &l);
464 assert(r);
465 }
466
467 if (!(avahi_normalize_name(type, normalized_type, sizeof(normalized_type))))
468 return AVAHI_ERR_INVALID_SERVICE_TYPE;
469
470 if (!(avahi_normalize_name(domain, normalized_domain, sizeof(normalized_domain))))
471 return AVAHI_ERR_INVALID_DOMAIN_NAME;
472
473 /* Concatenation */
474
475 snprintf(p, size, "%s%s%s.%s", name ? escaped_name : "", name ? "." : "", normalized_type, normalized_domain);
476
477 return AVAHI_OK;
478 }
479
480 #ifndef HAVE_STRLCPY
481
strlcpy(char * dest,const char * src,size_t n)482 static size_t strlcpy(char *dest, const char *src, size_t n) {
483 assert(dest);
484 assert(src);
485
486 if (n > 0) {
487 strncpy(dest, src, n-1);
488 dest[n-1] = 0;
489 }
490
491 return strlen(src);
492 }
493
494 #endif
495
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)496 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) {
497 enum {
498 NAME,
499 TYPE,
500 DOMAIN
501 } state;
502 int type_empty = 1, domain_empty = 1;
503
504 assert(p);
505 assert(type);
506 assert(type_size > 0);
507 assert(domain);
508 assert(domain_size > 0);
509
510 if (name) {
511 assert(name_size > 0);
512 *name = 0;
513 state = NAME;
514 } else
515 state = TYPE;
516
517 *type = *domain = 0;
518
519 while (*p) {
520 char buf[64];
521
522 if (!(avahi_unescape_label(&p, buf, sizeof(buf))))
523 return -1;
524
525 switch (state) {
526 case NAME:
527 strlcpy(name, buf, name_size);
528 state = TYPE;
529 break;
530
531 case TYPE:
532
533 if (buf[0] == '_') {
534
535 if (!type_empty) {
536 if (!type_size)
537 return AVAHI_ERR_NO_MEMORY;
538
539 *(type++) = '.';
540 type_size --;
541
542 } else
543 type_empty = 0;
544
545 if (!(avahi_escape_label(buf, strlen(buf), &type, &type_size)))
546 return AVAHI_ERR_NO_MEMORY;
547
548 break;
549 }
550
551 state = DOMAIN;
552 /* fall through */
553
554 case DOMAIN:
555
556 if (!domain_empty) {
557 if (!domain_size)
558 return AVAHI_ERR_NO_MEMORY;
559
560 *(domain++) = '.';
561 domain_size --;
562 } else
563 domain_empty = 0;
564
565 if (!(avahi_escape_label(buf, strlen(buf), &domain, &domain_size)))
566 return AVAHI_ERR_NO_MEMORY;
567
568 break;
569 }
570 }
571
572 return 0;
573 }
574
avahi_is_valid_fqdn(const char * t)575 int avahi_is_valid_fqdn(const char *t) {
576 char label[AVAHI_LABEL_MAX];
577 char normalized[AVAHI_DOMAIN_NAME_MAX];
578 const char *k = t;
579 AvahiAddress a;
580 assert(t);
581
582 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX)
583 return 0;
584
585 if (!avahi_is_valid_domain_name(t))
586 return 0;
587
588 /* Check if there are at least two labels*/
589 if (!(avahi_unescape_label(&k, label, sizeof(label))))
590 return 0;
591
592 if (label[0] == 0 || !k)
593 return 0;
594
595 if (!(avahi_unescape_label(&k, label, sizeof(label))))
596 return 0;
597
598 if (label[0] == 0 || !k)
599 return 0;
600
601 /* Make sure that the name is not an IP address */
602 if (!(avahi_normalize_name(t, normalized, sizeof(normalized))))
603 return 0;
604
605 if (avahi_address_parse(normalized, AVAHI_PROTO_UNSPEC, &a))
606 return 0;
607
608 return 1;
609 }
610