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