]> git.meshlink.io Git - catta/blob - avahi-core/rr.c
* correctly handle empty domain names (i.e. the root zone)
[catta] / avahi-core / rr.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 <stdio.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <arpa/inet.h>
31 #include <assert.h>
32
33 #include <avahi-common/domain.h>
34 #include <avahi-common/malloc.h>
35
36 #include "rr.h"
37 #include "log.h"
38 #include "util.h"
39 #include "hashmap.h"
40
41 AvahiKey *avahi_key_new(const char *name, uint16_t class, uint16_t type) {
42     AvahiKey *k;
43     assert(name);
44
45     if (!(k = avahi_new(AvahiKey, 1))) {
46         avahi_log_error("avahi_new() failed.");
47         return NULL;
48     }
49     
50     if (!(k->name = avahi_normalize_name_strdup(name))) {
51         avahi_log_error("avahi_normalize_name() failed.");
52         avahi_free(k);
53         return NULL;
54     }
55     
56     k->ref = 1;
57     k->clazz = class;
58     k->type = type;
59
60     return k;
61 }
62
63 AvahiKey *avahi_key_new_cname(AvahiKey *key) {
64     assert(key);
65
66     if (key->clazz != AVAHI_DNS_CLASS_IN)
67         return NULL;
68
69     if (key->type == AVAHI_DNS_TYPE_CNAME)
70         return NULL;
71
72     return avahi_key_new(key->name, key->clazz, AVAHI_DNS_TYPE_CNAME);
73 }
74
75 AvahiKey *avahi_key_ref(AvahiKey *k) {
76     assert(k);
77     assert(k->ref >= 1);
78
79     k->ref++;
80
81     return k;
82 }
83
84 void avahi_key_unref(AvahiKey *k) {
85     assert(k);
86     assert(k->ref >= 1);
87     
88     if ((--k->ref) <= 0) {
89         avahi_free(k->name);
90         avahi_free(k);
91     }
92 }
93
94 AvahiRecord *avahi_record_new(AvahiKey *k, uint32_t ttl) {
95     AvahiRecord *r;
96     
97     assert(k);
98     
99     if (!(r = avahi_new(AvahiRecord, 1))) {
100         avahi_log_error("avahi_new() failed.");
101         return NULL;
102     }
103         
104     r->ref = 1;
105     r->key = avahi_key_ref(k);
106
107     memset(&r->data, 0, sizeof(r->data));
108
109     r->ttl = ttl != (uint32_t) -1 ? ttl : AVAHI_DEFAULT_TTL;
110
111     return r;
112 }
113
114 AvahiRecord *avahi_record_new_full(const char *name, uint16_t class, uint16_t type, uint32_t ttl) {
115     AvahiRecord *r;
116     AvahiKey *k;
117
118     assert(name);
119     
120     if (!(k = avahi_key_new(name, class, type))) {
121         avahi_log_error("avahi_key_new() failed.");
122         return NULL;
123     }
124
125     r = avahi_record_new(k, ttl);
126     avahi_key_unref(k);
127
128     if (!r) {
129         avahi_log_error("avahi_record_new() failed.");
130         return NULL;
131     }
132
133     return r;
134 }
135
136 AvahiRecord *avahi_record_ref(AvahiRecord *r) {
137     assert(r);
138     assert(r->ref >= 1);
139
140     r->ref++;
141     return r;
142 }
143
144 void avahi_record_unref(AvahiRecord *r) {
145     assert(r);
146     assert(r->ref >= 1);
147
148     if ((--r->ref) <= 0) {
149         switch (r->key->type) {
150
151             case AVAHI_DNS_TYPE_SRV:
152                 avahi_free(r->data.srv.name);
153                 break;
154
155             case AVAHI_DNS_TYPE_PTR:
156             case AVAHI_DNS_TYPE_CNAME:
157             case AVAHI_DNS_TYPE_NS:
158                 avahi_free(r->data.ptr.name);
159                 break;
160
161             case AVAHI_DNS_TYPE_HINFO:
162                 avahi_free(r->data.hinfo.cpu);
163                 avahi_free(r->data.hinfo.os);
164                 break;
165
166             case AVAHI_DNS_TYPE_TXT:
167                 avahi_string_list_free(r->data.txt.string_list);
168                 break;
169
170             case AVAHI_DNS_TYPE_A:
171             case AVAHI_DNS_TYPE_AAAA:
172                 break;
173             
174             default:
175                 avahi_free(r->data.generic.data);
176         }
177         
178         avahi_key_unref(r->key);
179         avahi_free(r);
180     }
181 }
182
183 const char *avahi_dns_class_to_string(uint16_t class) {
184     if (class & AVAHI_DNS_CACHE_FLUSH) 
185         return "FLUSH";
186
187     switch (class) {
188         case AVAHI_DNS_CLASS_IN:
189             return "IN";
190         case AVAHI_DNS_CLASS_ANY:
191             return "ANY";
192         default:
193             return NULL;
194     }
195 }
196
197 const char *avahi_dns_type_to_string(uint16_t type) {
198     switch (type) {
199         case AVAHI_DNS_TYPE_CNAME:
200             return "CNAME";
201         case AVAHI_DNS_TYPE_A:
202             return "A";
203         case AVAHI_DNS_TYPE_AAAA:
204             return "AAAA";
205         case AVAHI_DNS_TYPE_PTR:
206             return "PTR";
207         case AVAHI_DNS_TYPE_HINFO:
208             return "HINFO";
209         case AVAHI_DNS_TYPE_TXT:
210             return "TXT";
211         case AVAHI_DNS_TYPE_SRV:
212             return "SRV";
213         case AVAHI_DNS_TYPE_ANY:
214             return "ANY";
215         case AVAHI_DNS_TYPE_SOA:
216             return "SOA";
217         case AVAHI_DNS_TYPE_NS:
218             return "NS";
219         default:
220             return NULL;
221     }
222 }
223
224 char *avahi_key_to_string(const AvahiKey *k) {
225     assert(k);
226     assert(k->ref >= 1);
227     
228     return avahi_strdup_printf("%s\t%s\t%s",
229                                k->name,
230                                avahi_dns_class_to_string(k->clazz),
231                                avahi_dns_type_to_string(k->type));
232 }
233
234 char *avahi_record_to_string(const AvahiRecord *r) {
235     char *p, *s;
236     char buf[257], *t = NULL, *d = NULL;
237
238     assert(r);
239     assert(r->ref >= 1);
240     
241     switch (r->key->type) {
242         case AVAHI_DNS_TYPE_A:
243             inet_ntop(AF_INET, &r->data.a.address.address, t = buf, sizeof(buf));
244             break;
245             
246         case AVAHI_DNS_TYPE_AAAA:
247             inet_ntop(AF_INET6, &r->data.aaaa.address.address, t = buf, sizeof(buf));
248             break;
249             
250         case AVAHI_DNS_TYPE_PTR:
251         case AVAHI_DNS_TYPE_CNAME:
252         case AVAHI_DNS_TYPE_NS:
253
254             t = r->data.ptr.name;
255             break;
256
257         case AVAHI_DNS_TYPE_TXT:
258             t = d = avahi_string_list_to_string(r->data.txt.string_list);
259             break;
260
261         case AVAHI_DNS_TYPE_HINFO:
262
263             snprintf(t = buf, sizeof(buf), "\"%s\" \"%s\"", r->data.hinfo.cpu, r->data.hinfo.os);
264             break;
265
266         case AVAHI_DNS_TYPE_SRV:
267
268             snprintf(t = buf, sizeof(buf), "%u %u %u %s",
269                      r->data.srv.priority,
270                      r->data.srv.weight,
271                      r->data.srv.port,
272                      r->data.srv.name);
273
274             break;
275     }
276
277     p = avahi_key_to_string(r->key);
278     s = avahi_strdup_printf("%s %s ; ttl=%u", p, t ? t : "<unparsable>", r->ttl);
279     avahi_free(p);
280     avahi_free(d);
281     
282     return s;
283 }
284
285 int avahi_key_equal(const AvahiKey *a, const AvahiKey *b) {
286     assert(a);
287     assert(b);
288
289     if (a == b)
290         return 1;
291     
292 /*     g_message("equal: %p %p", a, b); */
293     
294     return avahi_domain_equal(a->name, b->name) &&
295         a->type == b->type &&
296         a->clazz == b->clazz;
297 }
298
299 int avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) {
300     assert(pattern);
301     assert(k);
302
303 /*     g_message("equal: %p %p", a, b); */
304
305     assert(!avahi_key_is_pattern(k));
306
307     if (pattern == k)
308         return 1;
309     
310     return avahi_domain_equal(pattern->name, k->name) &&
311         (pattern->type == k->type || pattern->type == AVAHI_DNS_TYPE_ANY) &&
312         (pattern->clazz == k->clazz || pattern->clazz == AVAHI_DNS_CLASS_ANY);
313 }
314
315 int avahi_key_is_pattern(const AvahiKey *k) {
316     assert(k);
317
318     return
319         k->type == AVAHI_DNS_TYPE_ANY ||
320         k->clazz == AVAHI_DNS_CLASS_ANY;
321 }
322
323 unsigned avahi_key_hash(const AvahiKey *k) {
324     assert(k);
325
326     return
327         avahi_domain_hash(k->name) + 
328         k->type +
329         k->clazz;
330 }
331
332 static int rdata_equal(const AvahiRecord *a, const AvahiRecord *b) {
333     assert(a);
334     assert(b);
335     assert(a->key->type == b->key->type);
336
337 /*     t = avahi_record_to_string(a); */
338 /*     g_message("comparing %s", t); */
339 /*     avahi_free(t); */
340
341 /*     t = avahi_record_to_string(b); */
342 /*     g_message("and %s", t); */
343 /*     avahi_free(t); */
344
345     
346     switch (a->key->type) {
347         case AVAHI_DNS_TYPE_SRV:
348             return
349                 a->data.srv.priority == b->data.srv.priority &&
350                 a->data.srv.weight == b->data.srv.weight &&
351                 a->data.srv.port == b->data.srv.port &&
352                 avahi_domain_equal(a->data.srv.name, b->data.srv.name);
353
354         case AVAHI_DNS_TYPE_PTR:
355         case AVAHI_DNS_TYPE_CNAME:
356         case AVAHI_DNS_TYPE_NS:
357             return avahi_domain_equal(a->data.ptr.name, b->data.ptr.name);
358
359         case AVAHI_DNS_TYPE_HINFO:
360             return
361                 !strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu) &&
362                 !strcmp(a->data.hinfo.os, b->data.hinfo.os);
363
364         case AVAHI_DNS_TYPE_TXT:
365             return avahi_string_list_equal(a->data.txt.string_list, b->data.txt.string_list);
366
367         case AVAHI_DNS_TYPE_A:
368             return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)) == 0;
369
370         case AVAHI_DNS_TYPE_AAAA:
371             return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)) == 0;
372
373         default:
374             return a->data.generic.size == b->data.generic.size &&
375                 (a->data.generic.size == 0 || memcmp(a->data.generic.data, b->data.generic.data, a->data.generic.size) == 0);
376     }
377     
378 }
379
380 int avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b) {
381     assert(a);
382     assert(b);
383
384     if (a == b)
385         return 1;
386
387     return
388         avahi_key_equal(a->key, b->key) &&
389         rdata_equal(a, b);
390 }
391
392
393 AvahiRecord *avahi_record_copy(AvahiRecord *r) {
394     AvahiRecord *copy;
395
396     if (!(copy = avahi_new(AvahiRecord, 1))) {
397         avahi_log_error("avahi_new() failed.");
398         return NULL;
399     }
400     
401     copy->ref = 1;
402     copy->key = avahi_key_ref(r->key);
403     copy->ttl = r->ttl;
404
405     switch (r->key->type) {
406         case AVAHI_DNS_TYPE_PTR:
407         case AVAHI_DNS_TYPE_CNAME:
408         case AVAHI_DNS_TYPE_NS:
409             if (!(copy->data.ptr.name = avahi_strdup(r->data.ptr.name)))
410                 goto fail;
411             break;
412
413         case AVAHI_DNS_TYPE_SRV:
414             copy->data.srv.priority = r->data.srv.priority;
415             copy->data.srv.weight = r->data.srv.weight;
416             copy->data.srv.port = r->data.srv.port;
417             if (!(copy->data.srv.name = avahi_strdup(r->data.srv.name)))
418                 goto fail;
419             break;
420
421         case AVAHI_DNS_TYPE_HINFO:
422             if (!(copy->data.hinfo.os = avahi_strdup(r->data.hinfo.os)))
423                 goto fail;
424
425             if (!(copy->data.hinfo.cpu = avahi_strdup(r->data.hinfo.cpu))) {
426                 avahi_free(r->data.hinfo.os);
427                 goto fail;
428             }
429             break;
430
431         case AVAHI_DNS_TYPE_TXT:
432             copy->data.txt.string_list = avahi_string_list_copy(r->data.txt.string_list);
433             break;
434
435         case AVAHI_DNS_TYPE_A:
436             copy->data.a.address = r->data.a.address;
437             break;
438
439         case AVAHI_DNS_TYPE_AAAA:
440             copy->data.aaaa.address = r->data.aaaa.address;
441             break;
442
443         default:
444             if (!(copy->data.generic.data = avahi_memdup(r->data.generic.data, r->data.generic.size)))
445                 goto fail;
446             copy->data.generic.size = r->data.generic.size;
447             break;
448                 
449     }
450
451     return copy;
452
453 fail:
454     avahi_log_error("Failed to allocate memory");
455
456     avahi_key_unref(copy->key);
457     avahi_free(copy);
458     
459     return NULL;
460 }
461
462
463 size_t avahi_key_get_estimate_size(AvahiKey *k) {
464     assert(k);
465
466     return strlen(k->name)+1+4;
467 }
468
469 size_t avahi_record_get_estimate_size(AvahiRecord *r) {
470     size_t n;
471     assert(r);
472
473     n = avahi_key_get_estimate_size(r->key) + 4 + 2;
474
475     switch (r->key->type) {
476         case AVAHI_DNS_TYPE_PTR:
477         case AVAHI_DNS_TYPE_CNAME:
478         case AVAHI_DNS_TYPE_NS:
479             n += strlen(r->data.ptr.name) + 1;
480             break;
481
482         case AVAHI_DNS_TYPE_SRV:
483             n += 6 + strlen(r->data.srv.name) + 1;
484             break;
485
486         case AVAHI_DNS_TYPE_HINFO:
487             n += strlen(r->data.hinfo.os) + 1 + strlen(r->data.hinfo.cpu) + 1;
488             break;
489
490         case AVAHI_DNS_TYPE_TXT:
491             n += avahi_string_list_serialize(r->data.txt.string_list, NULL, 0);
492             break;
493
494         case AVAHI_DNS_TYPE_A:
495             n += sizeof(AvahiIPv4Address);
496             break;
497
498         case AVAHI_DNS_TYPE_AAAA:
499             n += sizeof(AvahiIPv6Address);
500             break;
501
502         default:
503             n += r->data.generic.size;
504     }
505
506     return n;
507 }
508
509 static int lexicographical_memcmp(const void* a, size_t al, const void* b, size_t bl) {
510     size_t c;
511     int ret;
512     
513     assert(a);
514     assert(b);
515
516     c = al < bl ? al : bl;
517     if ((ret = memcmp(a, b, c)))
518         return ret;
519
520     if (al == bl)
521         return 0;
522     else
523         return al == c ? 1 : -1;
524 }
525
526 static int uint16_cmp(uint16_t a, uint16_t b) {
527     return a == b ? 0 : (a < b ? -1 : 1);
528 }
529
530 int avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) {
531     int r;
532 /*      char *t1, *t2; */
533
534     assert(a);
535     assert(b);
536
537 /*     t1 = avahi_record_to_string(a); */
538 /*     t2 = avahi_record_to_string(b); */
539 /*     g_message("lexicocmp: %s %s", t1, t2); */
540 /*     avahi_free(t1); */
541 /*     avahi_free(t2); */
542
543     if (a == b)
544         return 0;
545
546     if ((r = uint16_cmp(a->key->clazz, b->key->clazz)) ||
547         (r = uint16_cmp(a->key->type, b->key->type)))
548         return r;
549
550     switch (a->key->type) {
551
552         case AVAHI_DNS_TYPE_PTR:
553         case AVAHI_DNS_TYPE_CNAME:
554         case AVAHI_DNS_TYPE_NS:
555             return avahi_binary_domain_cmp(a->data.ptr.name, b->data.ptr.name);
556
557         case AVAHI_DNS_TYPE_SRV: {
558             if ((r = uint16_cmp(a->data.srv.priority, b->data.srv.priority)) == 0 &&
559                 (r = uint16_cmp(a->data.srv.weight, b->data.srv.weight)) == 0 &&
560                 (r = uint16_cmp(a->data.srv.port, b->data.srv.port)) == 0)
561                 r = avahi_binary_domain_cmp(a->data.srv.name, b->data.srv.name);
562             
563             return r;
564         }
565
566         case AVAHI_DNS_TYPE_HINFO: {
567
568             if ((r = strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu)) ||
569                 (r = strcmp(a->data.hinfo.os, b->data.hinfo.os)))
570                 return r;
571
572             return 0;
573
574         }
575
576         case AVAHI_DNS_TYPE_TXT: {
577
578             uint8_t *ma = NULL, *mb = NULL;
579             size_t asize, bsize;
580
581             asize = avahi_string_list_serialize(a->data.txt.string_list, NULL, 0);
582             bsize = avahi_string_list_serialize(b->data.txt.string_list, NULL, 0);
583             
584             if (asize > 0 && !(ma = avahi_new(uint8_t, asize)))
585                 goto fail;
586             
587             if (bsize > 0 && !(mb = avahi_new(uint8_t, bsize))) {
588                 avahi_free(ma);
589                 goto fail;
590             }
591             
592             avahi_string_list_serialize(a->data.txt.string_list, ma, asize);
593             avahi_string_list_serialize(b->data.txt.string_list, mb, bsize);
594
595             if (asize && bsize)
596                 r = lexicographical_memcmp(ma, asize, mb, bsize);
597             else if (asize && !bsize)
598                 r = 1;
599             else if (!asize && bsize)
600                 r = -1;
601             else
602                 r = 0;
603             
604             avahi_free(ma);
605             avahi_free(mb);
606
607             return r;
608         }
609         
610         case AVAHI_DNS_TYPE_A:
611             return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address));
612
613         case AVAHI_DNS_TYPE_AAAA:
614             return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address));
615
616         default:
617             return lexicographical_memcmp(a->data.generic.data, a->data.generic.size,
618                                           b->data.generic.data, b->data.generic.size);
619     }
620
621     
622 fail:
623     avahi_log_error(__FILE__": Out of memory");
624     return -1; /* or whatever ... */
625 }
626
627 int avahi_record_is_goodbye(AvahiRecord *r) {
628     assert(r);
629
630     return r->ttl == 0;
631 }
632
633 int avahi_key_is_valid(AvahiKey *k) {
634     assert(k);
635
636     if (!avahi_is_valid_domain_name(k->name))
637         return 0;
638     
639     return 1;
640 }
641
642 int avahi_record_is_valid(AvahiRecord *r) {
643     assert(r);
644
645     if (!avahi_key_is_valid(r->key))
646         return 0;
647
648     switch (r->key->type) {
649
650         case AVAHI_DNS_TYPE_PTR:
651         case AVAHI_DNS_TYPE_CNAME:
652         case AVAHI_DNS_TYPE_NS:
653             return avahi_is_valid_domain_name(r->data.ptr.name);
654
655         case AVAHI_DNS_TYPE_SRV:
656             return avahi_is_valid_domain_name(r->data.srv.name);
657
658         case AVAHI_DNS_TYPE_HINFO:
659             return
660                 strlen(r->data.hinfo.os) <= 255 &&
661                 strlen(r->data.hinfo.cpu) <= 255;
662
663             
664         case AVAHI_DNS_TYPE_TXT: {
665
666             AvahiStringList *strlst;
667
668             for (strlst = r->data.txt.string_list; strlst; strlst = strlst->next)
669                 if (strlst->size > 255)
670                     return 0;
671
672             return 1;
673         }
674     }
675             
676
677     return 1;
678 }