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