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