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