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