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 <stdlib.h>
27#include <string.h>
28#include <stdio.h>
29#include <assert.h>
30
31#include <sys/types.h>
32#include <netinet/in.h>
33
34#include <avahi-common/defs.h>
35#include <avahi-common/domain.h>
36#include <avahi-common/malloc.h>
37
38#include "dns.h"
39#include "log.h"
40
41AvahiDnsPacket* avahi_dns_packet_new(unsigned mtu) {
42    AvahiDnsPacket *p;
43    size_t max_size;
44
45    if (mtu <= 0)
46        max_size = AVAHI_DNS_PACKET_SIZE_MAX;
47    else if (mtu >= AVAHI_DNS_PACKET_EXTRA_SIZE)
48        max_size = mtu - AVAHI_DNS_PACKET_EXTRA_SIZE;
49    else
50        max_size = 0;
51
52    if (max_size < AVAHI_DNS_PACKET_HEADER_SIZE)
53        max_size = AVAHI_DNS_PACKET_HEADER_SIZE;
54
55    if (!(p = avahi_malloc(sizeof(AvahiDnsPacket) + max_size)))
56        return p;
57
58    p->size = p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE;
59    p->max_size = max_size;
60    p->name_table = NULL;
61    p->data = NULL;
62
63    memset(AVAHI_DNS_PACKET_DATA(p), 0, p->size);
64    return p;
65}
66
67AvahiDnsPacket* avahi_dns_packet_new_query(unsigned mtu) {
68    AvahiDnsPacket *p;
69
70    if (!(p = avahi_dns_packet_new(mtu)))
71        return NULL;
72
73    avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
74    return p;
75}
76
77AvahiDnsPacket* avahi_dns_packet_new_response(unsigned mtu, int aa) {
78    AvahiDnsPacket *p;
79
80    if (!(p = avahi_dns_packet_new(mtu)))
81        return NULL;
82
83    avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(1, 0, aa, 0, 0, 0, 0, 0, 0, 0));
84    return p;
85}
86
87AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, unsigned mtu, int copy_queries, int aa) {
88    AvahiDnsPacket *r;
89    assert(p);
90
91    if (!(r = avahi_dns_packet_new_response(mtu, aa)))
92        return NULL;
93
94    if (copy_queries) {
95        unsigned saved_rindex;
96        uint32_t n;
97
98        saved_rindex = p->rindex;
99        p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE;
100
101        for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n--) {
102            AvahiKey *k;
103            int unicast_response;
104
105            if ((k = avahi_dns_packet_consume_key(p, &unicast_response))) {
106                avahi_dns_packet_append_key(r, k, unicast_response);
107                avahi_key_unref(k);
108            }
109        }
110
111        p->rindex = saved_rindex;
112
113        avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_QDCOUNT, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT));
114    }
115
116    avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_ID, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID));
117
118    avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_FLAGS,
119                               (avahi_dns_packet_get_field(r, AVAHI_DNS_FIELD_FLAGS) & ~AVAHI_DNS_FLAG_OPCODE) |
120                               (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_OPCODE));
121
122    return r;
123}
124
125
126void avahi_dns_packet_free(AvahiDnsPacket *p) {
127    assert(p);
128
129    if (p->name_table)
130        avahi_hashmap_free(p->name_table);
131
132    avahi_free(p);
133}
134
135void avahi_dns_packet_set_field(AvahiDnsPacket *p, unsigned idx, uint16_t v) {
136    assert(p);
137    assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE);
138
139    ((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx] = htons(v);
140}
141
142uint16_t avahi_dns_packet_get_field(AvahiDnsPacket *p, unsigned idx) {
143    assert(p);
144    assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE);
145
146    return ntohs(((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx]);
147}
148
149void avahi_dns_packet_inc_field(AvahiDnsPacket *p, unsigned idx) {
150    assert(p);
151    assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE);
152
153    avahi_dns_packet_set_field(p, idx, avahi_dns_packet_get_field(p, idx) + 1);
154}
155
156
157static void name_table_cleanup(void *key, void *value, void *user_data) {
158    AvahiDnsPacket *p = user_data;
159
160    if ((uint8_t*) value >= AVAHI_DNS_PACKET_DATA(p) + p->size)
161        avahi_hashmap_remove(p->name_table, key);
162}
163
164void avahi_dns_packet_cleanup_name_table(AvahiDnsPacket *p) {
165    if (p->name_table)
166        avahi_hashmap_foreach(p->name_table, name_table_cleanup, p);
167}
168
169uint8_t* avahi_dns_packet_append_name(AvahiDnsPacket *p, const char *name) {
170    uint8_t *d, *saved_ptr = NULL;
171    size_t saved_size;
172
173    assert(p);
174    assert(name);
175
176    saved_size = p->size;
177    saved_ptr = avahi_dns_packet_extend(p, 0);
178
179    while (*name) {
180        uint8_t* prev;
181        const char *pname;
182        char label[64], *u;
183
184        /* Check whether we can compress this name. */
185
186        if (p->name_table && (prev = avahi_hashmap_lookup(p->name_table, name))) {
187            unsigned idx;
188
189            assert(prev >= AVAHI_DNS_PACKET_DATA(p));
190            idx = (unsigned) (prev - AVAHI_DNS_PACKET_DATA(p));
191
192            assert(idx < p->size);
193
194            if (idx < 0x4000) {
195                uint8_t *t;
196                if (!(t = (uint8_t*) avahi_dns_packet_extend(p, sizeof(uint16_t))))
197                    return NULL;
198
199		t[0] = (uint8_t) ((0xC000 | idx) >> 8);
200		t[1] = (uint8_t) idx;
201                return saved_ptr;
202            }
203        }
204
205        pname = name;
206
207        if (!(avahi_unescape_label(&name, label, sizeof(label))))
208            goto fail;
209
210        if (!(d = avahi_dns_packet_append_string(p, label)))
211            goto fail;
212
213        if (!p->name_table)
214            /* This works only for normalized domain names */
215            p->name_table = avahi_hashmap_new(avahi_string_hash, avahi_string_equal, avahi_free, NULL);
216
217        if (!(u = avahi_strdup(pname)))
218            avahi_log_error("avahi_strdup() failed.");
219        else
220            avahi_hashmap_insert(p->name_table, u, d);
221    }
222
223    if (!(d = avahi_dns_packet_extend(p, 1)))
224        goto fail;
225
226    *d = 0;
227
228    return saved_ptr;
229
230fail:
231    p->size = saved_size;
232    avahi_dns_packet_cleanup_name_table(p);
233
234    return NULL;
235}
236
237uint8_t* avahi_dns_packet_append_uint16(AvahiDnsPacket *p, uint16_t v) {
238    uint8_t *d;
239    assert(p);
240
241    if (!(d = avahi_dns_packet_extend(p, sizeof(uint16_t))))
242        return NULL;
243
244    d[0] = (uint8_t) (v >> 8);
245    d[1] = (uint8_t) v;
246    return d;
247}
248
249uint8_t *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, uint32_t v) {
250    uint8_t *d;
251    assert(p);
252
253    if (!(d = avahi_dns_packet_extend(p, sizeof(uint32_t))))
254        return NULL;
255
256    d[0] = (uint8_t) (v >> 24);
257    d[1] = (uint8_t) (v >> 16);
258    d[2] = (uint8_t) (v >> 8);
259    d[3] = (uint8_t) v;
260
261    return d;
262}
263
264uint8_t *avahi_dns_packet_append_bytes(AvahiDnsPacket  *p, const void *b, size_t l) {
265    uint8_t* d;
266
267    assert(p);
268    assert(b);
269    assert(l);
270
271    if (!(d = avahi_dns_packet_extend(p, l)))
272        return NULL;
273
274    memcpy(d, b, l);
275    return d;
276}
277
278uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s) {
279    uint8_t* d;
280    size_t k;
281
282    assert(p);
283    assert(s);
284
285    if ((k = strlen(s)) >= 255)
286        k = 255;
287
288    if (!(d = avahi_dns_packet_extend(p, k+1)))
289        return NULL;
290
291    *d = (uint8_t) k;
292    memcpy(d+1, s, k);
293
294    return d;
295}
296
297uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l) {
298    uint8_t *d;
299
300    assert(p);
301
302    if (p->size+l > p->max_size)
303        return NULL;
304
305    d = AVAHI_DNS_PACKET_DATA(p) + p->size;
306    p->size += l;
307
308    return d;
309}
310
311int avahi_dns_packet_check_valid(AvahiDnsPacket *p) {
312    uint16_t flags;
313    assert(p);
314
315    if (p->size < AVAHI_DNS_PACKET_HEADER_SIZE)
316        return -1;
317
318    flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS);
319
320    if (flags & AVAHI_DNS_FLAG_OPCODE)
321        return -1;
322
323    return 0;
324}
325
326int avahi_dns_packet_check_valid_multicast(AvahiDnsPacket *p) {
327    uint16_t flags;
328    assert(p);
329
330    if (avahi_dns_packet_check_valid(p) < 0)
331        return -1;
332
333    flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS);
334
335    if (flags & AVAHI_DNS_FLAG_RCODE)
336        return -1;
337
338    return 0;
339}
340
341int avahi_dns_packet_is_query(AvahiDnsPacket *p) {
342    assert(p);
343
344    return !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_QR);
345}
346
347static int consume_labels(AvahiDnsPacket *p, unsigned idx, char *ret_name, size_t l) {
348    int ret = 0;
349    int compressed = 0;
350    int first_label = 1;
351    unsigned label_ptr;
352    int i;
353    assert(p && ret_name && l);
354
355    for (i = 0; i < AVAHI_DNS_LABELS_MAX; i++) {
356        uint8_t n;
357
358        if (idx+1 > p->size)
359            return -1;
360
361        n = AVAHI_DNS_PACKET_DATA(p)[idx];
362
363        if (!n) {
364            idx++;
365            if (!compressed)
366                ret++;
367
368            if (l < 1)
369                return -1;
370            *ret_name = 0;
371
372            return ret;
373
374        } else if (n <= 63) {
375            /* Uncompressed label */
376            idx++;
377            if (!compressed)
378                ret++;
379
380            if (idx + n > p->size)
381                return -1;
382
383            if ((size_t) n + 1 > l)
384                return -1;
385
386            if (!first_label) {
387                *(ret_name++) = '.';
388                l--;
389            } else
390                first_label = 0;
391
392            if (!(avahi_escape_label((char*) AVAHI_DNS_PACKET_DATA(p) + idx, n, &ret_name, &l)))
393                return -1;
394
395            idx += n;
396
397            if (!compressed)
398                ret += n;
399        } else if ((n & 0xC0) == 0xC0) {
400            /* Compressed label */
401
402            if (idx+2 > p->size)
403                return -1;
404
405            label_ptr = ((unsigned) (AVAHI_DNS_PACKET_DATA(p)[idx] & ~0xC0)) << 8 | AVAHI_DNS_PACKET_DATA(p)[idx+1];
406
407            if ((label_ptr < AVAHI_DNS_PACKET_HEADER_SIZE) || (label_ptr >= idx))
408                return -1;
409
410            idx = label_ptr;
411
412            if (!compressed)
413                ret += 2;
414
415            compressed = 1;
416        } else
417            return -1;
418    }
419
420    return -1;
421}
422
423int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l) {
424    int r;
425
426    if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0)
427        return -1;
428
429    p->rindex += r;
430    return 0;
431}
432
433int avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, uint16_t *ret_v) {
434    uint8_t *d;
435
436    assert(p);
437    assert(ret_v);
438
439    if (p->rindex + sizeof(uint16_t) > p->size)
440        return -1;
441
442    d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex);
443    *ret_v = (d[0] << 8) | d[1];
444    p->rindex += sizeof(uint16_t);
445
446    return 0;
447}
448
449int avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, uint32_t *ret_v) {
450    uint8_t* d;
451
452    assert(p);
453    assert(ret_v);
454
455    if (p->rindex + sizeof(uint32_t) > p->size)
456        return -1;
457
458    d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex);
459    *ret_v = (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
460    p->rindex += sizeof(uint32_t);
461
462    return 0;
463}
464
465int avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, void * ret_data, size_t l) {
466    assert(p);
467    assert(ret_data);
468    assert(l > 0);
469
470    if (p->rindex + l > p->size)
471        return -1;
472
473    memcpy(ret_data, AVAHI_DNS_PACKET_DATA(p) + p->rindex, l);
474    p->rindex += l;
475
476    return 0;
477}
478
479int avahi_dns_packet_consume_string(AvahiDnsPacket *p, char *ret_string, size_t l) {
480    size_t k;
481
482    assert(p);
483    assert(ret_string);
484    assert(l > 0);
485
486    if (p->rindex >= p->size)
487        return -1;
488
489    k = AVAHI_DNS_PACKET_DATA(p)[p->rindex];
490
491    if (p->rindex+1+k > p->size)
492        return -1;
493
494    if (l > k+1)
495        l = k+1;
496
497    memcpy(ret_string, AVAHI_DNS_PACKET_DATA(p)+p->rindex+1, l-1);
498    ret_string[l-1] = 0;
499
500    p->rindex += 1+k;
501
502    return 0;
503}
504
505const void* avahi_dns_packet_get_rptr(AvahiDnsPacket *p) {
506    assert(p);
507
508    if (p->rindex > p->size)
509        return NULL;
510
511    return AVAHI_DNS_PACKET_DATA(p) + p->rindex;
512}
513
514int avahi_dns_packet_skip(AvahiDnsPacket *p, size_t length) {
515    assert(p);
516
517    if (p->rindex + length > p->size)
518        return -1;
519
520    p->rindex += length;
521    return 0;
522}
523
524static int parse_rdata(AvahiDnsPacket *p, AvahiRecord *r, uint16_t rdlength) {
525    char buf[AVAHI_DOMAIN_NAME_MAX];
526    const void* start;
527
528    assert(p);
529    assert(r);
530
531    start = avahi_dns_packet_get_rptr(p);
532
533    switch (r->key->type) {
534        case AVAHI_DNS_TYPE_PTR:
535        case AVAHI_DNS_TYPE_CNAME:
536        case AVAHI_DNS_TYPE_NS:
537
538            if (avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
539                return -1;
540
541            r->data.ptr.name = avahi_strdup(buf);
542            break;
543
544
545        case AVAHI_DNS_TYPE_SRV:
546
547            if (avahi_dns_packet_consume_uint16(p, &r->data.srv.priority) < 0 ||
548                avahi_dns_packet_consume_uint16(p, &r->data.srv.weight) < 0 ||
549                avahi_dns_packet_consume_uint16(p, &r->data.srv.port) < 0 ||
550                avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
551                return -1;
552
553            r->data.srv.name = avahi_strdup(buf);
554            break;
555
556        case AVAHI_DNS_TYPE_HINFO:
557
558            if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0)
559                return -1;
560
561            r->data.hinfo.cpu = avahi_strdup(buf);
562
563            if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0)
564                return -1;
565
566            r->data.hinfo.os = avahi_strdup(buf);
567            break;
568
569        case AVAHI_DNS_TYPE_TXT:
570
571            if (rdlength > 0) {
572                if (avahi_string_list_parse(avahi_dns_packet_get_rptr(p), rdlength, &r->data.txt.string_list) < 0)
573                    return -1;
574
575                if (avahi_dns_packet_skip(p, rdlength) < 0)
576                    return -1;
577            } else
578                r->data.txt.string_list = NULL;
579
580            break;
581
582        case AVAHI_DNS_TYPE_A:
583
584/*             avahi_log_debug("A"); */
585
586            if (avahi_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(AvahiIPv4Address)) < 0)
587                return -1;
588
589            break;
590
591        case AVAHI_DNS_TYPE_AAAA:
592
593/*             avahi_log_debug("aaaa"); */
594
595            if (avahi_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(AvahiIPv6Address)) < 0)
596                return -1;
597
598            break;
599
600        default:
601
602/*             avahi_log_debug("generic"); */
603
604            if (rdlength > 0) {
605
606                r->data.generic.data = avahi_memdup(avahi_dns_packet_get_rptr(p), rdlength);
607                r->data.generic.size = rdlength;
608
609                if (avahi_dns_packet_skip(p, rdlength) < 0)
610                    return -1;
611            }
612
613            break;
614    }
615
616    /* Check if we read enough data */
617    if ((const uint8_t*) avahi_dns_packet_get_rptr(p) - (const uint8_t*) start != rdlength)
618        return -1;
619
620    return 0;
621}
622
623AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_flush) {
624    char name[AVAHI_DOMAIN_NAME_MAX];
625    uint16_t type, class;
626    uint32_t ttl;
627    uint16_t rdlength;
628    AvahiRecord *r = NULL;
629
630    assert(p);
631
632    if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 ||
633        avahi_dns_packet_consume_uint16(p, &type) < 0 ||
634        avahi_dns_packet_consume_uint16(p, &class) < 0 ||
635        avahi_dns_packet_consume_uint32(p, &ttl) < 0 ||
636        avahi_dns_packet_consume_uint16(p, &rdlength) < 0 ||
637        p->rindex + rdlength > p->size)
638        goto fail;
639
640    if (ret_cache_flush)
641        *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH);
642    class &= ~AVAHI_DNS_CACHE_FLUSH;
643
644    if (!(r = avahi_record_new_full(name, class, type, ttl)))
645        goto fail;
646
647    if (parse_rdata(p, r, rdlength) < 0)
648        goto fail;
649
650    if (!avahi_record_is_valid(r))
651        goto fail;
652
653    return r;
654
655fail:
656    if (r)
657        avahi_record_unref(r);
658
659    return NULL;
660}
661
662AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_response) {
663    char name[256];
664    uint16_t type, class;
665    AvahiKey *k;
666
667    assert(p);
668
669    if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 ||
670        avahi_dns_packet_consume_uint16(p, &type) < 0 ||
671        avahi_dns_packet_consume_uint16(p, &class) < 0)
672        return NULL;
673
674    if (ret_unicast_response)
675        *ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE);
676
677    class &= ~AVAHI_DNS_UNICAST_RESPONSE;
678
679    if (!(k = avahi_key_new(name, class, type)))
680        return NULL;
681
682    if (!avahi_key_is_valid(k)) {
683        avahi_key_unref(k);
684        return NULL;
685    }
686
687    return k;
688}
689
690uint8_t* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, int unicast_response) {
691    uint8_t *t;
692    size_t size;
693
694    assert(p);
695    assert(k);
696
697    size = p->size;
698
699    if (!(t = avahi_dns_packet_append_name(p, k->name)) ||
700        !avahi_dns_packet_append_uint16(p, k->type) ||
701        !avahi_dns_packet_append_uint16(p, k->clazz | (unicast_response ? AVAHI_DNS_UNICAST_RESPONSE : 0))) {
702        p->size = size;
703        avahi_dns_packet_cleanup_name_table(p);
704
705        return NULL;
706    }
707
708    return t;
709}
710
711static int append_rdata(AvahiDnsPacket *p, AvahiRecord *r) {
712    assert(p);
713    assert(r);
714
715    switch (r->key->type) {
716
717        case AVAHI_DNS_TYPE_PTR:
718        case AVAHI_DNS_TYPE_CNAME:
719        case AVAHI_DNS_TYPE_NS:
720
721            if (!(avahi_dns_packet_append_name(p, r->data.ptr.name)))
722                return -1;
723
724            break;
725
726        case AVAHI_DNS_TYPE_SRV:
727
728            if (!avahi_dns_packet_append_uint16(p, r->data.srv.priority) ||
729                !avahi_dns_packet_append_uint16(p, r->data.srv.weight) ||
730                !avahi_dns_packet_append_uint16(p, r->data.srv.port) ||
731                !avahi_dns_packet_append_name(p, r->data.srv.name))
732                return -1;
733
734            break;
735
736        case AVAHI_DNS_TYPE_HINFO:
737            if (!avahi_dns_packet_append_string(p, r->data.hinfo.cpu) ||
738                !avahi_dns_packet_append_string(p, r->data.hinfo.os))
739                return -1;
740
741            break;
742
743        case AVAHI_DNS_TYPE_TXT: {
744
745            uint8_t *data;
746            size_t n;
747
748            n = avahi_string_list_serialize(r->data.txt.string_list, NULL, 0);
749
750            if (!(data = avahi_dns_packet_extend(p, n)))
751                return -1;
752
753            avahi_string_list_serialize(r->data.txt.string_list, data, n);
754            break;
755        }
756
757
758        case AVAHI_DNS_TYPE_A:
759
760            if (!avahi_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address)))
761                return -1;
762
763            break;
764
765        case AVAHI_DNS_TYPE_AAAA:
766
767            if (!avahi_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address)))
768                return -1;
769
770            break;
771
772        default:
773
774            if (r->data.generic.size)
775                if (!avahi_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size))
776                    return -1;
777
778            break;
779    }
780
781    return 0;
782}
783
784
785uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int cache_flush, unsigned max_ttl) {
786    uint8_t *t, *l, *start;
787    size_t size;
788
789    assert(p);
790    assert(r);
791
792    size = p->size;
793
794    if (!(t = avahi_dns_packet_append_name(p, r->key->name)) ||
795        !avahi_dns_packet_append_uint16(p, r->key->type) ||
796        !avahi_dns_packet_append_uint16(p, cache_flush ? (r->key->clazz | AVAHI_DNS_CACHE_FLUSH) : (r->key->clazz &~ AVAHI_DNS_CACHE_FLUSH)) ||
797        !avahi_dns_packet_append_uint32(p, (max_ttl && r->ttl > max_ttl) ? max_ttl : r->ttl) ||
798        !(l = avahi_dns_packet_append_uint16(p, 0)))
799        goto fail;
800
801    start = avahi_dns_packet_extend(p, 0);
802
803    if (append_rdata(p, r) < 0)
804        goto fail;
805
806    size = avahi_dns_packet_extend(p, 0) - start;
807    assert(size <= AVAHI_DNS_RDATA_MAX);
808
809/*     avahi_log_debug("appended %u", size); */
810
811    l[0] = (uint8_t) ((uint16_t) size >> 8);
812    l[1] = (uint8_t) ((uint16_t) size);
813
814    return t;
815
816
817fail:
818    p->size = size;
819    avahi_dns_packet_cleanup_name_table(p);
820
821    return NULL;
822}
823
824int avahi_dns_packet_is_empty(AvahiDnsPacket *p) {
825    assert(p);
826
827    return p->size <= AVAHI_DNS_PACKET_HEADER_SIZE;
828}
829
830size_t avahi_dns_packet_space(AvahiDnsPacket *p) {
831    assert(p);
832
833    assert(p->size <= p->max_size);
834
835    return p->max_size - p->size;
836}
837
838int avahi_rdata_parse(AvahiRecord *record, const void* rdata, size_t size) {
839    int ret;
840    AvahiDnsPacket p;
841
842    assert(record);
843    assert(rdata);
844
845    p.data = (void*) rdata;
846    p.max_size = p.size = size;
847    p.rindex = 0;
848    p.name_table = NULL;
849
850    ret = parse_rdata(&p, record, size);
851
852    assert(!p.name_table);
853
854    return ret;
855}
856
857size_t avahi_rdata_serialize(AvahiRecord *record, void *rdata, size_t max_size) {
858    int ret;
859    AvahiDnsPacket p;
860
861    assert(record);
862    assert(rdata);
863    assert(max_size > 0);
864
865    p.data = (void*) rdata;
866    p.max_size = max_size;
867    p.size = p.rindex = 0;
868    p.name_table = NULL;
869
870    ret = append_rdata(&p, record);
871
872    if (p.name_table)
873         avahi_hashmap_free(p.name_table);
874
875    if (ret < 0)
876        return (size_t) -1;
877
878    return p.size;
879}
880