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