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