]> git.meshlink.io Git - catta/blob - libavahi-core/rr.c
f4102dd45cb01a38069f44c7566a58f5346813e3
[catta] / libavahi-core / rr.c
1 #include <string.h>
2 #include <stdio.h>
3 #include <sys/types.h>
4 #include <sys/socket.h>
5 #include <arpa/inet.h>
6
7 #include "util.h"
8 #include "rr.h"
9 #include "dns.h"
10
11 AvahiKey *avahi_key_new(const gchar *name, guint16 class, guint16 type) {
12     AvahiKey *k;
13     g_assert(name);
14
15     k = g_new(AvahiKey, 1);
16     k->ref = 1;
17     k->name = avahi_normalize_name(name);    
18     k->class = class;
19     k->type = type;
20
21 /*     g_message("%p %% ref=1", k); */
22     
23     return k;
24 }
25
26 AvahiKey *avahi_key_ref(AvahiKey *k) {
27     g_assert(k);
28     g_assert(k->ref >= 1);
29
30     k->ref++;
31
32 /*     g_message("%p ++ ref=%i", k, k->ref); */
33
34     return k;
35 }
36
37 void avahi_key_unref(AvahiKey *k) {
38     g_assert(k);
39     g_assert(k->ref >= 1);
40
41 /*     g_message("%p -- ref=%i", k, k->ref-1); */
42     
43     if ((--k->ref) <= 0) {
44         g_free(k->name);
45         g_free(k);
46     }
47 }
48
49 AvahiRecord *avahi_record_new(AvahiKey *k) {
50     AvahiRecord *r;
51     
52     g_assert(k);
53     
54     r = g_new(AvahiRecord, 1);
55     r->ref = 1;
56     r->key = avahi_key_ref(k);
57
58     memset(&r->data, 0, sizeof(r->data));
59
60     r->ttl = AVAHI_DEFAULT_TTL;
61
62     return r;
63 }
64
65 AvahiRecord *avahi_record_new_full(const gchar *name, guint16 class, guint16 type) {
66     AvahiRecord *r;
67     AvahiKey *k;
68
69     g_assert(name);
70     
71     k = avahi_key_new(name, class, type);
72     r = avahi_record_new(k);
73     avahi_key_unref(k);
74
75     return r;
76 }
77
78 AvahiRecord *avahi_record_ref(AvahiRecord *r) {
79     g_assert(r);
80     g_assert(r->ref >= 1);
81
82     r->ref++;
83     return r;
84 }
85
86 void avahi_record_unref(AvahiRecord *r) {
87     g_assert(r);
88     g_assert(r->ref >= 1);
89
90     if ((--r->ref) <= 0) {
91         switch (r->key->type) {
92
93             case AVAHI_DNS_TYPE_SRV:
94                 g_free(r->data.srv.name);
95                 break;
96
97             case AVAHI_DNS_TYPE_PTR:
98             case AVAHI_DNS_TYPE_CNAME:
99                 g_free(r->data.ptr.name);
100                 break;
101
102             case AVAHI_DNS_TYPE_HINFO:
103                 g_free(r->data.hinfo.cpu);
104                 g_free(r->data.hinfo.os);
105                 break;
106
107             case AVAHI_DNS_TYPE_TXT:
108                 avahi_string_list_free(r->data.txt.string_list);
109                 break;
110
111             case AVAHI_DNS_TYPE_A:
112             case AVAHI_DNS_TYPE_AAAA:
113                 break;
114             
115             default:
116                 g_free(r->data.generic.data);
117         }
118         
119         avahi_key_unref(r->key);
120         g_free(r);
121     }
122 }
123
124 const gchar *avahi_dns_class_to_string(guint16 class) {
125     if (class & AVAHI_DNS_CACHE_FLUSH) 
126         return "FLUSH";
127     
128     if (class == AVAHI_DNS_CLASS_IN)
129         return "IN";
130
131     return NULL;
132 }
133
134 const gchar *avahi_dns_type_to_string(guint16 type) {
135     switch (type) {
136         case AVAHI_DNS_TYPE_CNAME:
137             return "CNAME";
138         case AVAHI_DNS_TYPE_A:
139             return "A";
140         case AVAHI_DNS_TYPE_AAAA:
141             return "AAAA";
142         case AVAHI_DNS_TYPE_PTR:
143             return "PTR";
144         case AVAHI_DNS_TYPE_HINFO:
145             return "HINFO";
146         case AVAHI_DNS_TYPE_TXT:
147             return "TXT";
148         case AVAHI_DNS_TYPE_SRV:
149             return "SRV";
150         case AVAHI_DNS_TYPE_ANY:
151             return "ANY";
152         default:
153             return NULL;
154     }
155 }
156
157
158 gchar *avahi_key_to_string(const AvahiKey *k) {
159     return g_strdup_printf("%s\t%s\t%s",
160                            k->name,
161                            avahi_dns_class_to_string(k->class),
162                            avahi_dns_type_to_string(k->type));
163 }
164
165 gchar *avahi_record_to_string(const AvahiRecord *r) {
166     gchar *p, *s;
167     char buf[257], *t = NULL, *d = NULL;
168
169     switch (r->key->type) {
170         case AVAHI_DNS_TYPE_A:
171             inet_ntop(AF_INET, &r->data.a.address.address, t = buf, sizeof(buf));
172             break;
173             
174         case AVAHI_DNS_TYPE_AAAA:
175             inet_ntop(AF_INET6, &r->data.aaaa.address.address, t = buf, sizeof(buf));
176             break;
177             
178         case AVAHI_DNS_TYPE_PTR:
179         case AVAHI_DNS_TYPE_CNAME :
180
181             t = r->data.ptr.name;
182             break;
183
184         case AVAHI_DNS_TYPE_TXT:
185             t = d = avahi_string_list_to_string(r->data.txt.string_list);
186             break;
187
188         case AVAHI_DNS_TYPE_HINFO:
189
190             snprintf(t = buf, sizeof(buf), "\"%s\" \"%s\"", r->data.hinfo.cpu, r->data.hinfo.os);
191             break;
192
193         case AVAHI_DNS_TYPE_SRV:
194
195             snprintf(t = buf, sizeof(buf), "%u %u %u %s",
196                      r->data.srv.priority,
197                      r->data.srv.weight,
198                      r->data.srv.port,
199                      r->data.srv.name);
200
201             break;
202     }
203
204     p = avahi_key_to_string(r->key);
205     s = g_strdup_printf("%s %s ; ttl=%u", p, t ? t : "<unparsable>", r->ttl);
206     g_free(p);
207     g_free(d);
208     
209     return s;
210 }
211
212 gboolean avahi_key_equal(const AvahiKey *a, const AvahiKey *b) {
213     g_assert(a);
214     g_assert(b);
215
216     if (a == b)
217         return TRUE;
218     
219 /*     g_message("equal: %p %p", a, b); */
220     
221     return avahi_domain_equal(a->name, b->name) &&
222         a->type == b->type &&
223         a->class == b->class;
224 }
225
226 gboolean avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) {
227     g_assert(pattern);
228     g_assert(k);
229
230 /*     g_message("equal: %p %p", a, b); */
231
232     g_assert(!avahi_key_is_pattern(k));
233
234     if (pattern == k)
235         return TRUE;
236     
237     return avahi_domain_equal(pattern->name, k->name) &&
238         (pattern->type == k->type || pattern->type == AVAHI_DNS_TYPE_ANY) &&
239         pattern->class == k->class;
240 }
241
242 gboolean avahi_key_is_pattern(const AvahiKey *k) {
243     g_assert(k);
244
245     return k->type == AVAHI_DNS_TYPE_ANY;
246 }
247
248
249 guint avahi_key_hash(const AvahiKey *k) {
250     g_assert(k);
251
252     return avahi_domain_hash(k->name) + k->type + k->class;
253 }
254
255 static gboolean rdata_equal(const AvahiRecord *a, const AvahiRecord *b) {
256     g_assert(a);
257     g_assert(b);
258     g_assert(a->key->type == b->key->type);
259
260 /*     t = avahi_record_to_string(a); */
261 /*     g_message("comparing %s", t); */
262 /*     g_free(t); */
263
264 /*     t = avahi_record_to_string(b); */
265 /*     g_message("and %s", t); */
266 /*     g_free(t); */
267
268     
269     switch (a->key->type) {
270         case AVAHI_DNS_TYPE_SRV:
271             return
272                 a->data.srv.priority == b->data.srv.priority &&
273                 a->data.srv.weight == b->data.srv.weight &&
274                 a->data.srv.port == b->data.srv.port &&
275                 avahi_domain_equal(a->data.srv.name, b->data.srv.name);
276
277         case AVAHI_DNS_TYPE_PTR:
278         case AVAHI_DNS_TYPE_CNAME:
279             return avahi_domain_equal(a->data.ptr.name, b->data.ptr.name);
280
281         case AVAHI_DNS_TYPE_HINFO:
282             return
283                 !strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu) &&
284                 !strcmp(a->data.hinfo.os, b->data.hinfo.os);
285
286         case AVAHI_DNS_TYPE_TXT:
287             return avahi_string_list_equal(a->data.txt.string_list, b->data.txt.string_list);
288
289         case AVAHI_DNS_TYPE_A:
290             return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)) == 0;
291
292         case AVAHI_DNS_TYPE_AAAA:
293             return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)) == 0;
294
295         default:
296             return a->data.generic.size == b->data.generic.size &&
297                 (a->data.generic.size == 0 || memcmp(a->data.generic.data, b->data.generic.data, a->data.generic.size) == 0);
298     }
299     
300 }
301
302 gboolean avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b) {
303     g_assert(a);
304     g_assert(b);
305
306     if (a == b)
307         return TRUE;
308
309     return
310         avahi_key_equal(a->key, b->key) &&
311         rdata_equal(a, b);
312 }
313
314
315 AvahiRecord *avahi_record_copy(AvahiRecord *r) {
316     AvahiRecord *copy;
317
318     copy = g_new(AvahiRecord, 1);
319     copy->ref = 1;
320     copy->key = avahi_key_ref(r->key);
321     copy->ttl = r->ttl;
322
323     switch (r->key->type) {
324         case AVAHI_DNS_TYPE_PTR:
325         case AVAHI_DNS_TYPE_CNAME:
326             copy->data.ptr.name = g_strdup(r->data.ptr.name);
327             break;
328
329         case AVAHI_DNS_TYPE_SRV:
330             copy->data.srv.priority = r->data.srv.priority;
331             copy->data.srv.weight = r->data.srv.weight;
332             copy->data.srv.port = r->data.srv.port;
333             copy->data.srv.name = g_strdup(r->data.srv.name);
334             break;
335
336         case AVAHI_DNS_TYPE_HINFO:
337             copy->data.hinfo.os = g_strdup(r->data.hinfo.os);
338             copy->data.hinfo.cpu = g_strdup(r->data.hinfo.cpu);
339             break;
340
341         case AVAHI_DNS_TYPE_TXT:
342             copy->data.txt.string_list = avahi_string_list_copy(r->data.txt.string_list);
343             break;
344
345         case AVAHI_DNS_TYPE_A:
346             copy->data.a.address = r->data.a.address;
347             break;
348
349         case AVAHI_DNS_TYPE_AAAA:
350             copy->data.aaaa.address = r->data.aaaa.address;
351             break;
352
353         default:
354             copy->data.generic.data = g_memdup(r->data.generic.data, r->data.generic.size);
355             copy->data.generic.size = r->data.generic.size;
356             break;
357                 
358     }
359
360     return copy;
361 }
362
363
364 guint avahi_key_get_estimate_size(AvahiKey *k) {
365     g_assert(k);
366
367     return strlen(k->name)+1+4;
368 }
369
370 guint avahi_record_get_estimate_size(AvahiRecord *r) {
371     guint n;
372     g_assert(r);
373
374     n = avahi_key_get_estimate_size(r->key) + 4 + 2;
375
376     switch (r->key->type) {
377         case AVAHI_DNS_TYPE_PTR:
378         case AVAHI_DNS_TYPE_CNAME:
379             n += strlen(r->data.ptr.name) + 1;
380             break;
381
382         case AVAHI_DNS_TYPE_SRV:
383             n += 6 + strlen(r->data.srv.name) + 1;
384             break;
385
386         case AVAHI_DNS_TYPE_HINFO:
387             n += strlen(r->data.hinfo.os) + 1 + strlen(r->data.hinfo.cpu) + 1;
388             break;
389
390         case AVAHI_DNS_TYPE_TXT:
391             n += avahi_string_list_serialize(r->data.txt.string_list, NULL, 0);
392             break;
393
394         case AVAHI_DNS_TYPE_A:
395             n += sizeof(AvahiIPv4Address);
396             break;
397
398         case AVAHI_DNS_TYPE_AAAA:
399             n += sizeof(AvahiIPv6Address);
400             break;
401
402         default:
403             n += r->data.generic.size;
404     }
405
406     return n;
407 }
408
409 static gint lexicographical_memcmp(gconstpointer a, size_t al, gconstpointer b, size_t bl) {
410     size_t c;
411     gint ret;
412     
413     g_assert(a);
414     g_assert(b);
415
416     c = al < bl ? al : bl;
417     if ((ret = memcmp(a, b, c)) != 0)
418         return ret;
419
420     if (al == bl)
421         return 0;
422     else
423         return al == c ? 1 : -1;
424 }
425
426 static gint uint16_cmp(guint16 a, guint16 b) {
427     return a == b ? 0 : (a < b ? a : b);
428 }
429
430 static gint lexicographical_domain_cmp(const gchar *a, const gchar *b) {
431     g_assert(a);
432     g_assert(b);
433     
434
435     for (;;) {
436         gchar t1[64];
437         gchar t2[64];
438         size_t al, bl;
439         gint r;
440
441         if (!a && !b)
442             return 0;
443
444         if (a && !b)
445             return 1;
446
447         if (b && !a)
448             return -1;
449         
450         avahi_unescape_label(t1, sizeof(t1), &a);
451         avahi_unescape_label(t2, sizeof(t2), &b);
452
453         al = strlen(t1);
454         bl = strlen(t2);
455         
456         if (al != bl) 
457             return al < bl ? -1 : 1;
458
459         if ((r =  strcmp(t1, t2)) != 0)
460             return r;
461     }
462 }
463
464 gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) {
465     g_assert(a);
466     g_assert(b);
467
468     if (a == b)
469         return 0;
470     
471 /*     gchar *t; */
472
473 /*     g_message("comparing [%s]", t = avahi_record_to_string(a)); */
474 /*     g_free(t); */
475
476 /*     g_message("and [%s]", t = avahi_record_to_string(b)); */
477 /*     g_free(t); */
478
479     if (a->key->class < b->key->class)
480         return -1;
481     else if (a->key->class > b->key->class)
482         return 1;
483
484     if (a->key->type < b->key->type)
485         return -1;
486     else if (a->key->type > b->key->type)
487         return 1;
488
489     switch (a->key->type) {
490
491         case AVAHI_DNS_TYPE_PTR:
492         case AVAHI_DNS_TYPE_CNAME:
493             return lexicographical_domain_cmp(a->data.ptr.name, b->data.ptr.name);
494
495         case AVAHI_DNS_TYPE_SRV: {
496             gint r;
497             if ((r = uint16_cmp(a->data.srv.priority, b->data.srv.priority)) == 0 &&
498                 (r = uint16_cmp(a->data.srv.weight, b->data.srv.weight)) == 0 &&
499                 (r = uint16_cmp(a->data.srv.port, b->data.srv.port)) == 0)
500                 r = lexicographical_domain_cmp(a->data.srv.name, b->data.srv.name);
501             
502             return r;
503         }
504
505         case AVAHI_DNS_TYPE_HINFO: {
506             size_t al = strlen(a->data.hinfo.cpu), bl = strlen(b->data.hinfo.cpu);
507             gint r;
508
509             if (al != bl)
510                 return al < bl ? -1 : 1;
511
512             if ((r = strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu)) != 0)
513                 return r;
514
515             al = strlen(a->data.hinfo.os), bl = strlen(b->data.hinfo.os);
516
517             if (al != bl)
518                 return al < bl ? -1 : 1;
519
520             if ((r = strcmp(a->data.hinfo.os, b->data.hinfo.os)) != 0)
521                 return r;
522
523             return 0;
524
525         }
526
527         case AVAHI_DNS_TYPE_TXT: {
528
529             guint8 *ma, *mb;
530             guint asize, bsize;
531             gint r;
532
533             ma = g_new(guint8, asize = avahi_string_list_serialize(a->data.txt.string_list, NULL, 0));
534             mb = g_new(guint8, bsize = avahi_string_list_serialize(b->data.txt.string_list, NULL, 0));
535             avahi_string_list_serialize(a->data.txt.string_list, ma, asize);
536             avahi_string_list_serialize(a->data.txt.string_list, mb, bsize);
537
538             r = lexicographical_memcmp(ma, asize, mb, bsize);
539             g_free(ma);
540             g_free(mb);
541
542             return r;
543         }
544         
545         case AVAHI_DNS_TYPE_A:
546             return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address));
547
548         case AVAHI_DNS_TYPE_AAAA:
549             return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address));
550
551         default:
552             return lexicographical_memcmp(a->data.generic.data, a->data.generic.size,
553                                           b->data.generic.data, b->data.generic.size);
554     }
555     
556 }