]> git.meshlink.io Git - catta/blob - dns.c
* Complete conflict detection stuff (including probing et al)
[catta] / 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 flxDnsPacket* flx_dns_packet_new(guint max_size) {
11     flxDnsPacket *p;
12
13     if (max_size <= 0)
14         max_size = FLX_DNS_PACKET_MAX_SIZE;
15     else if (max_size < FLX_DNS_PACKET_HEADER_SIZE)
16         max_size = FLX_DNS_PACKET_HEADER_SIZE;
17     
18     p = g_malloc(sizeof(flxDnsPacket) + max_size);
19     p->size = p->rindex = FLX_DNS_PACKET_HEADER_SIZE;
20     p->max_size = max_size;
21     p->name_table = NULL;
22
23     memset(FLX_DNS_PACKET_DATA(p), 0, p->size);
24     return p;
25 }
26
27 flxDnsPacket* flx_dns_packet_new_query(guint max_size) {
28     flxDnsPacket *p;
29
30     p = flx_dns_packet_new(max_size);
31     flx_dns_packet_set_field(p, FLX_DNS_FIELD_FLAGS, FLX_DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
32     return p;
33 }
34
35 flxDnsPacket* flx_dns_packet_new_response(guint max_size) {
36     flxDnsPacket *p;
37
38     p = flx_dns_packet_new(max_size);
39     flx_dns_packet_set_field(p, FLX_DNS_FIELD_FLAGS, FLX_DNS_FLAGS(1, 0, 0, 0, 0, 0, 0, 0, 0, 0));
40     return p;
41 }
42
43 void flx_dns_packet_free(flxDnsPacket *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 flx_dns_packet_set_field(flxDnsPacket *p, guint index, guint16 v) {
53     g_assert(p);
54     g_assert(index < FLX_DNS_PACKET_HEADER_SIZE);
55     
56     ((guint16*) FLX_DNS_PACKET_DATA(p))[index] = g_htons(v);
57 }
58
59 guint16 flx_dns_packet_get_field(flxDnsPacket *p, guint index) {
60     g_assert(p);
61     g_assert(index < FLX_DNS_PACKET_HEADER_SIZE);
62
63     return g_ntohs(((guint16*) FLX_DNS_PACKET_DATA(p))[index]);
64 }
65
66 /* Read the first label from string *name, unescape "\" and write it to dest */
67 gchar *flx_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* flx_dns_packet_append_name(flxDnsPacket *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 = flx_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 >= FLX_DNS_PACKET_DATA(p));
130             index = (guint) (prev - FLX_DNS_PACKET_DATA(p));
131
132             g_assert(index < p->size);
133
134             if (index < 0x4000) {
135                 guint16 *t;
136                 if (!(t = (guint16*) flx_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 (!(flx_unescape_label(label, sizeof(label), &name)))
147             goto fail;
148
149         if (!(d = flx_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) flx_domain_hash, (GEqualFunc) flx_domain_equal, g_free, NULL);
154
155         g_hash_table_insert(p->name_table, g_strdup(pname), d);
156     }
157
158     if (!(d = flx_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* flx_dns_packet_append_uint16(flxDnsPacket *p, guint16 v) {
171     guint8 *d;
172     g_assert(p);
173     
174     if (!(d = flx_dns_packet_extend(p, sizeof(guint16))))
175         return NULL;
176     
177     *((guint16*) d) = g_htons(v);
178     return d;
179 }
180
181 guint8 *flx_dns_packet_append_uint32(flxDnsPacket *p, guint32 v) {
182     guint8 *d;
183     g_assert(p);
184
185     if (!(d = flx_dns_packet_extend(p, sizeof(guint32))))
186         return NULL;
187     
188     *((guint32*) d) = g_htonl(v);
189
190     return d;
191 }
192
193 guint8 *flx_dns_packet_append_bytes(flxDnsPacket  *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 = flx_dns_packet_extend(p, l)))
201         return NULL;
202
203     memcpy(d, b, l);
204     return d;
205 }
206
207 guint8* flx_dns_packet_append_string(flxDnsPacket *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 = flx_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 *flx_dns_packet_extend(flxDnsPacket *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 = FLX_DNS_PACKET_DATA(p) + p->size;
235     p->size += l;
236     
237     return d;
238 }
239
240 gint flx_dns_packet_check_valid(flxDnsPacket *p) {
241     guint16 flags;
242     g_assert(p);
243
244     if (p->size < 12)
245         return -1;
246
247     flags = flx_dns_packet_get_field(p, FLX_DNS_FIELD_FLAGS);
248
249     if (flags & FLX_DNS_FLAG_OPCODE || flags & FLX_DNS_FLAG_RCODE)
250         return -1;
251
252     return 0;
253 }
254
255 gint flx_dns_packet_is_query(flxDnsPacket *p) {
256     g_assert(p);
257     
258     return !(flx_dns_packet_get_field(p, FLX_DNS_FIELD_FLAGS) & FLX_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(flxDnsPacket *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 = FLX_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(FLX_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) (FLX_DNS_PACKET_DATA(p)[index] & ~0xC0)) << 8 | FLX_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 flx_dns_packet_consume_name(flxDnsPacket *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 flx_dns_packet_consume_uint16(flxDnsPacket *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*) (FLX_DNS_PACKET_DATA(p) + p->rindex)));
382     p->rindex += sizeof(guint16);
383
384     return 0;
385 }
386
387 gint flx_dns_packet_consume_uint32(flxDnsPacket *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*) (FLX_DNS_PACKET_DATA(p) + p->rindex)));
395     p->rindex += sizeof(guint32);
396     
397     return 0;
398 }
399
400 gint flx_dns_packet_consume_bytes(flxDnsPacket *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, FLX_DNS_PACKET_DATA(p) + p->rindex, l);
409     p->rindex += l;
410
411     return 0;
412 }
413
414 gint flx_dns_packet_consume_string(flxDnsPacket *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 = FLX_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, FLX_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 flx_dns_packet_get_rptr(flxDnsPacket *p) {
443     g_assert(p);
444     
445     if (p->rindex > p->size)
446         return NULL;
447
448     return FLX_DNS_PACKET_DATA(p) + p->rindex;
449 }
450
451 gint flx_dns_packet_skip(flxDnsPacket *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 flxRecord* flx_dns_packet_consume_record(flxDnsPacket *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     flxRecord *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 (flx_dns_packet_consume_name(p, name, sizeof(name)) < 0 ||
476         flx_dns_packet_consume_uint16(p, &type) < 0 ||
477         flx_dns_packet_consume_uint16(p, &class) < 0 ||
478         flx_dns_packet_consume_uint32(p, &ttl) < 0 ||
479         flx_dns_packet_consume_uint16(p, &rdlength) < 0 ||
480         p->rindex + rdlength > p->size)
481         
482         goto fail;
483
484 /*     g_message("name = %s, rdlength = %u", name, rdlength); */
485     
486     start = flx_dns_packet_get_rptr(p);
487
488     r = flx_record_new_full(name, class, type);
489     
490     switch (type) {
491         case FLX_DNS_TYPE_PTR:
492         case FLX_DNS_TYPE_CNAME:
493
494 /*             g_message("ptr"); */
495             
496             if (flx_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
497                 goto fail;
498
499             r->data.ptr.name = g_strdup(buf);
500             break;
501
502             
503         case FLX_DNS_TYPE_SRV:
504
505 /*             g_message("srv"); */
506             
507             if (flx_dns_packet_consume_uint16(p, &r->data.srv.priority) < 0 ||
508                 flx_dns_packet_consume_uint16(p, &r->data.srv.weight) < 0 ||
509                 flx_dns_packet_consume_uint16(p, &r->data.srv.port) < 0 ||
510                 flx_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
511                 goto fail;
512             
513             r->data.srv.name = g_strdup(buf);
514             break;
515
516         case FLX_DNS_TYPE_HINFO:
517             
518 /*             g_message("hinfo"); */
519
520             if (flx_dns_packet_consume_string(p, buf, sizeof(buf)) < 0)
521                 goto fail;
522
523             r->data.hinfo.cpu = g_strdup(buf);
524
525             if (flx_dns_packet_consume_string(p, buf, sizeof(buf)) < 0)
526                 goto fail;
527
528             r->data.hinfo.os = g_strdup(buf);
529             break;
530
531         case FLX_DNS_TYPE_TXT:
532
533 /*             g_message("txt"); */
534
535             if (rdlength > 0) {
536                 r->data.txt.string_list = flx_string_list_parse(flx_dns_packet_get_rptr(p), rdlength);
537                 
538                 if (flx_dns_packet_skip(p, rdlength) < 0)
539                     goto fail;
540             } else
541                 r->data.txt.string_list = NULL;
542             
543             break;
544
545         case FLX_DNS_TYPE_A:
546
547 /*             g_message("A"); */
548
549             if (flx_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(flxIPv4Address)) < 0)
550                 goto fail;
551             
552             break;
553
554         case FLX_DNS_TYPE_AAAA:
555
556 /*             g_message("aaaa"); */
557             
558             if (flx_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(flxIPv6Address)) < 0)
559                 goto fail;
560             
561             break;
562             
563         default:
564
565 /*             g_message("generic"); */
566             
567             if (rdlength > 0) {
568
569                 r->data.generic.data = g_memdup(flx_dns_packet_get_rptr(p), rdlength);
570                 
571                 if (flx_dns_packet_skip(p, rdlength) < 0)
572                     goto fail;
573             }
574
575             break;
576     }
577
578 /*     g_message("%i == %u ?", (guint8*) flx_dns_packet_get_rptr(p) - (guint8*) start, rdlength); */
579     
580     /* Check if we read enough data */
581     if ((guint8*) flx_dns_packet_get_rptr(p) - (guint8*) start != rdlength)
582         goto fail;
583     
584     *ret_cache_flush = !!(class & FLX_DNS_CACHE_FLUSH);
585     class &= ~ FLX_DNS_CACHE_FLUSH;
586
587     r->ttl = ttl;
588
589     return r;
590
591 fail:
592     if (r)
593         flx_record_unref(r);
594
595     return NULL;
596 }
597
598 flxKey* flx_dns_packet_consume_key(flxDnsPacket *p) {
599     gchar name[256];
600     guint16 type, class;
601
602     g_assert(p);
603
604     if (flx_dns_packet_consume_name(p, name, sizeof(name)) < 0 ||
605         flx_dns_packet_consume_uint16(p, &type) < 0 ||
606         flx_dns_packet_consume_uint16(p, &class) < 0)
607         return NULL;
608
609     class &= ~ FLX_DNS_CACHE_FLUSH;
610
611     return flx_key_new(name, class, type);
612 }
613
614 guint8* flx_dns_packet_append_key(flxDnsPacket *p, flxKey *k) {
615     guint8 *t;
616     guint size;
617     
618     g_assert(p);
619     g_assert(k);
620
621     size = p->size;
622     
623     if (!(t = flx_dns_packet_append_name(p, k->name)) ||
624         !flx_dns_packet_append_uint16(p, k->type) ||
625         !flx_dns_packet_append_uint16(p, k->class)) {
626         p->size = size;
627         return NULL;
628     }
629
630     return t;
631 }
632
633 guint8* flx_dns_packet_append_record(flxDnsPacket *p, flxRecord *r, gboolean cache_flush) {
634     guint8 *t, *l, *start;
635     guint size;
636
637     g_assert(p);
638     g_assert(r);
639
640     size = p->size;
641
642     if (!(t = flx_dns_packet_append_name(p, r->key->name)) ||
643         !flx_dns_packet_append_uint16(p, r->key->type) ||
644         !flx_dns_packet_append_uint16(p, cache_flush ? (r->key->class | FLX_DNS_CACHE_FLUSH) : (r->key->class &~ FLX_DNS_CACHE_FLUSH)) ||
645         !flx_dns_packet_append_uint32(p, r->ttl) ||
646         !(l = flx_dns_packet_append_uint16(p, 0)))
647         goto fail;
648
649     start = flx_dns_packet_extend(p, 0);
650
651     switch (r->key->type) {
652         
653         case FLX_DNS_TYPE_PTR:
654         case FLX_DNS_TYPE_CNAME :
655
656             if (!(flx_dns_packet_append_name(p, r->data.ptr.name)))
657                 goto fail;
658             
659             break;
660
661         case FLX_DNS_TYPE_SRV:
662
663             if (!flx_dns_packet_append_uint16(p, r->data.srv.priority) ||
664                 !flx_dns_packet_append_uint16(p, r->data.srv.weight) ||
665                 !flx_dns_packet_append_uint16(p, r->data.srv.port) ||
666                 !flx_dns_packet_append_name(p, r->data.srv.name))
667                 goto fail;
668
669             break;
670
671         case FLX_DNS_TYPE_HINFO:
672             if (!flx_dns_packet_append_string(p, r->data.hinfo.cpu) ||
673                 !flx_dns_packet_append_string(p, r->data.hinfo.os))
674                 goto fail;
675
676             break;
677
678         case FLX_DNS_TYPE_TXT: {
679
680             guint8 *data;
681             guint size;
682
683             size = flx_string_list_serialize(r->data.txt.string_list, NULL, 0);
684
685 /*             g_message("appending string: %u %p", size, r->data.txt.string_list); */
686
687             if (!(data = flx_dns_packet_extend(p, size)))
688                 goto fail;
689
690             flx_string_list_serialize(r->data.txt.string_list, data, size);
691             break;
692         }
693
694
695         case FLX_DNS_TYPE_A:
696
697             if (!flx_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address)))
698                 goto fail;
699             
700             break;
701
702         case FLX_DNS_TYPE_AAAA:
703             
704             if (!flx_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address)))
705                 goto fail;
706             
707             break;
708             
709         default:
710
711             if (r->data.generic.size &&
712                 flx_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size))
713                 goto fail;
714
715             break;
716     }
717
718
719
720     
721     size = flx_dns_packet_extend(p, 0) - start;
722     g_assert(size <= 0xFFFF);
723
724 /*     g_message("appended %u", size); */
725
726     * (guint16*) l = g_htons((guint16) size);
727     
728     return t;
729
730
731 fail:
732     p->size = size;
733     return NULL;
734 }
735
736 gboolean flx_dns_packet_is_empty(flxDnsPacket *p) {
737     g_assert(p);
738
739     return p->size <= FLX_DNS_PACKET_HEADER_SIZE;
740 }
741
742 guint flx_dns_packet_space(flxDnsPacket *p) {
743     g_assert(p);
744
745     g_assert(p->size <= p->max_size);
746     
747     return p->max_size - p->size;
748 }