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