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