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