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