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