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