]> git.meshlink.io Git - catta/blob - dns.c
add client part of probing
[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
9 flxDnsPacket* flx_dns_packet_new(guint max_size) {
10     flxDnsPacket *p;
11
12     if (max_size <= 0)
13         max_size = FLX_DNS_PACKET_MAX_SIZE;
14     else if (max_size < FLX_DNS_PACKET_HEADER_SIZE)
15         max_size = FLX_DNS_PACKET_HEADER_SIZE;
16     
17     p = g_malloc(sizeof(flxDnsPacket) + max_size);
18     p->size = p->rindex = FLX_DNS_PACKET_HEADER_SIZE;
19     p->max_size = max_size;
20
21     memset(FLX_DNS_PACKET_DATA(p), 0, p->size);
22     return p;
23 }
24
25 flxDnsPacket* flx_dns_packet_new_query(guint max_size) {
26     flxDnsPacket *p;
27
28     p = flx_dns_packet_new(max_size);
29     flx_dns_packet_set_field(p, FLX_DNS_FIELD_FLAGS, FLX_DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
30     return p;
31 }
32
33 flxDnsPacket* flx_dns_packet_new_response(guint max_size) {
34     flxDnsPacket *p;
35
36     p = flx_dns_packet_new(max_size);
37     flx_dns_packet_set_field(p, FLX_DNS_FIELD_FLAGS, FLX_DNS_FLAGS(1, 0, 0, 0, 0, 0, 0, 0, 0, 0));
38     return p;
39 }
40
41 void flx_dns_packet_free(flxDnsPacket *p) {
42     g_assert(p);
43     g_free(p);
44 }
45
46 void flx_dns_packet_set_field(flxDnsPacket *p, guint index, guint16 v) {
47     g_assert(p);
48     g_assert(index < FLX_DNS_PACKET_HEADER_SIZE);
49     
50     ((guint16*) FLX_DNS_PACKET_DATA(p))[index] = g_htons(v);
51 }
52
53 guint16 flx_dns_packet_get_field(flxDnsPacket *p, guint index) {
54     g_assert(p);
55     g_assert(index < FLX_DNS_PACKET_HEADER_SIZE);
56
57     return g_ntohs(((guint16*) FLX_DNS_PACKET_DATA(p))[index]);
58 }
59
60 guint8* flx_dns_packet_append_name(flxDnsPacket *p, const gchar *name) {
61     guint8 *d, *f = NULL;
62     guint saved_size;
63     
64     g_assert(p);
65     g_assert(name);
66
67     saved_size = p->size;
68
69     for (;;) {
70         guint n = strcspn(name, ".");
71         if (!n || n > 63)
72             goto fail;
73         
74         if (!(d = flx_dns_packet_extend(p, n+1)))
75             goto fail;
76             
77         if (!f)
78             f = d;
79         d[0] = n;
80         memcpy(d+1, name, n);
81
82         name += n;
83
84         /* no trailing dot */
85         if (!*name)
86             break;
87
88         name ++;
89
90         /* trailing dot */
91         if (!*name)
92             break;
93     }
94
95     if (!(d = flx_dns_packet_extend(p, 1)))
96         goto fail;
97     
98     d[0] = 0;
99
100     return f;
101
102 fail:
103     p->size = saved_size;
104     return NULL;
105 }
106
107 guint8* flx_dns_packet_append_uint16(flxDnsPacket *p, guint16 v) {
108     guint8 *d;
109     g_assert(p);
110     
111     if (!(d = flx_dns_packet_extend(p, sizeof(guint16))))
112         return NULL;
113     
114     *((guint16*) d) = g_htons(v);
115     return d;
116 }
117
118 guint8 *flx_dns_packet_append_uint32(flxDnsPacket *p, guint32 v) {
119     guint8 *d;
120     g_assert(p);
121
122     if (!(d = flx_dns_packet_extend(p, sizeof(guint32))))
123         return NULL;
124     
125     *((guint32*) d) = g_htonl(v);
126
127     return d;
128 }
129
130 guint8 *flx_dns_packet_append_bytes(flxDnsPacket  *p, gconstpointer b, guint l) {
131     guint8* d;
132
133     g_assert(p);
134     g_assert(b);
135     g_assert(l);
136
137     if (!(d = flx_dns_packet_extend(p, l)))
138         return NULL;
139
140     memcpy(d, b, l);
141     return d;
142 }
143
144 guint8* flx_dns_packet_append_string(flxDnsPacket *p, const gchar *s) {
145     guint8* d;
146     guint k;
147     
148     g_assert(p);
149     g_assert(s);
150
151     if ((k = strlen(s)) >= 255)
152         k = 255;
153     
154     if (!(d = flx_dns_packet_extend(p, k+1)))
155         return NULL;
156
157     *d = (guint8) k;
158     memcpy(d+1, s, k);
159
160     return d;
161 }
162
163 guint8 *flx_dns_packet_extend(flxDnsPacket *p, guint l) {
164     guint8 *d;
165     
166     g_assert(p);
167
168     if (p->size+l > p->max_size)
169         return NULL;
170     
171     d = FLX_DNS_PACKET_DATA(p) + p->size;
172     p->size += l;
173     
174     return d;
175 }
176
177 guint8 *flx_dns_packet_append_name_compressed(flxDnsPacket *p, const gchar *name, guint8 *prev) {
178     guint16 *d;
179     signed long k;
180     g_assert(p);
181
182     if (!prev)
183         return flx_dns_packet_append_name(p, name);
184     
185     k = prev - FLX_DNS_PACKET_DATA(p);
186     if (k < 0 || k >= 0x4000 || (guint) k >= p->size)
187         return flx_dns_packet_append_name(p, name);
188
189     if (!(d = (guint16*) flx_dns_packet_extend(p, sizeof(guint16))))
190         return NULL;
191     
192     *d = g_htons((0xC000 | k));
193     return prev;
194 }
195
196 gint flx_dns_packet_check_valid(flxDnsPacket *p) {
197     guint16 flags;
198     g_assert(p);
199
200     if (p->size < 12)
201         return -1;
202
203     flags = flx_dns_packet_get_field(p, FLX_DNS_FIELD_FLAGS);
204
205     if (flags & FLX_DNS_FLAG_OPCODE || flags & FLX_DNS_FLAG_RCODE)
206         return -1;
207
208     return 0;
209 }
210
211 gint flx_dns_packet_is_query(flxDnsPacket *p) {
212     g_assert(p);
213     
214     return !(flx_dns_packet_get_field(p, FLX_DNS_FIELD_FLAGS) & FLX_DNS_FLAG_QR);
215 }
216
217 static gint consume_labels(flxDnsPacket *p, guint index, gchar *ret_name, guint l) {
218     gint ret = 0;
219     int compressed = 0;
220     int first_label = 1;
221     g_assert(p && ret_name && l);
222     
223     for (;;) {
224         guint8 n;
225
226         if (index+1 > p->size)
227             return -1;
228
229         n = FLX_DNS_PACKET_DATA(p)[index];
230
231         if (!n) {
232             index++;
233             if (!compressed)
234                 ret++;
235
236             if (l < 1)
237                 return -1;
238             *ret_name = 0;
239             
240             return ret;
241             
242         } else if (n <= 63) {
243             /* Uncompressed label */
244             index++;
245             if (!compressed)
246                 ret++;
247         
248             if (index + n > p->size)
249                 return -1;
250
251             if ((guint) n + 1 > l)
252                 return -1;
253
254             if (!first_label) {
255                 *(ret_name++) = '.';
256                 l--;
257             } else
258                 first_label = 0;
259
260             memcpy(ret_name, FLX_DNS_PACKET_DATA(p) + index, n);
261             index += n;
262             ret_name += n;
263             l -= n;
264             
265             if (!compressed)
266                 ret += n;
267         } else if ((n & 0xC0) == 0xC0) {
268             /* Compressed label */
269
270             if (index+2 > p->size)
271                 return -1;
272
273             index = ((guint) (FLX_DNS_PACKET_DATA(p)[index] & ~0xC0)) << 8 | FLX_DNS_PACKET_DATA(p)[index+1];
274
275             if (!compressed)
276                 ret += 2;
277             
278             compressed = 1;
279         } else
280             return -1;
281     }
282 }
283
284 gint flx_dns_packet_consume_name(flxDnsPacket *p, gchar *ret_name, guint l) {
285     gint r;
286     
287     if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0)
288         return -1;
289
290     p->rindex += r;
291     return 0;
292 }
293
294 gint flx_dns_packet_consume_uint16(flxDnsPacket *p, guint16 *ret_v) {
295     g_assert(p);
296     g_assert(ret_v);
297
298     if (p->rindex + sizeof(guint16) > p->size)
299         return -1;
300
301     *ret_v = g_ntohs(*((guint16*) (FLX_DNS_PACKET_DATA(p) + p->rindex)));
302     p->rindex += sizeof(guint16);
303
304     return 0;
305 }
306
307 gint flx_dns_packet_consume_uint32(flxDnsPacket *p, guint32 *ret_v) {
308     g_assert(p);
309     g_assert(ret_v);
310
311     if (p->rindex + sizeof(guint32) > p->size)
312         return -1;
313
314     *ret_v = g_ntohl(*((guint32*) (FLX_DNS_PACKET_DATA(p) + p->rindex)));
315     p->rindex += sizeof(guint32);
316     
317     return 0;
318 }
319
320 gint flx_dns_packet_consume_bytes(flxDnsPacket *p, gpointer ret_data, guint l) {
321     g_assert(p);
322     g_assert(ret_data);
323     g_assert(l > 0);
324     
325     if (p->rindex + l > p->size)
326         return -1;
327
328     memcpy(ret_data, FLX_DNS_PACKET_DATA(p) + p->rindex, l);
329     p->rindex += l;
330
331     return 0;
332 }
333
334 gint flx_dns_packet_consume_string(flxDnsPacket *p, gchar *ret_string, guint l) {
335     guint k;
336     
337     g_assert(p);
338     g_assert(ret_string);
339     g_assert(l > 0);
340
341     if (p->rindex >= p->size)
342         return -1;
343
344     k = FLX_DNS_PACKET_DATA(p)[p->rindex];
345
346     if (p->rindex+1+k > p->size)
347         return -1;
348
349     if (l > k+1)
350         l = k+1;
351
352     memcpy(ret_string, FLX_DNS_PACKET_DATA(p)+p->rindex+1, l-1);
353     ret_string[l-1] = 0;
354
355     
356     p->rindex += 1+k;
357
358     return 0;
359     
360 }
361
362 gconstpointer flx_dns_packet_get_rptr(flxDnsPacket *p) {
363     g_assert(p);
364     
365     if (p->rindex > p->size)
366         return NULL;
367
368     return FLX_DNS_PACKET_DATA(p) + p->rindex;
369 }
370
371 gint flx_dns_packet_skip(flxDnsPacket *p, guint length) {
372     g_assert(p);
373
374     if (p->rindex + length > p->size)
375         return -1;
376
377     p->rindex += length;
378     return 0;
379 }
380
381 flxRecord* flx_dns_packet_consume_record(flxDnsPacket *p, gboolean *ret_cache_flush) {
382     gchar name[257], buf[257];
383     guint16 type, class;
384     guint32 ttl;
385     guint16 rdlength;
386     gconstpointer data;
387     flxRecord *r = NULL;
388     gconstpointer start;
389
390     g_assert(p);
391     g_assert(ret_cache_flush);
392
393 /*     g_message("consume_record()"); */
394
395     if (flx_dns_packet_consume_name(p, name, sizeof(name)) < 0 ||
396         flx_dns_packet_consume_uint16(p, &type) < 0 ||
397         flx_dns_packet_consume_uint16(p, &class) < 0 ||
398         flx_dns_packet_consume_uint32(p, &ttl) < 0 ||
399         flx_dns_packet_consume_uint16(p, &rdlength) < 0 ||
400         p->rindex + rdlength > p->size)
401         
402         goto fail;
403
404 /*     g_message("name = %s, rdlength = %u", name, rdlength); */
405     
406     start = flx_dns_packet_get_rptr(p);
407
408     r = flx_record_new_full(name, class, type);
409     
410     switch (type) {
411         case FLX_DNS_TYPE_PTR:
412         case FLX_DNS_TYPE_CNAME:
413
414 /*             g_message("ptr"); */
415             
416             if (flx_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
417                 goto fail;
418
419             r->data.ptr.name = g_strdup(buf);
420             break;
421
422             
423         case FLX_DNS_TYPE_SRV:
424
425 /*             g_message("srv"); */
426             
427             if (flx_dns_packet_consume_uint16(p, &r->data.srv.priority) < 0 ||
428                 flx_dns_packet_consume_uint16(p, &r->data.srv.weight) < 0 ||
429                 flx_dns_packet_consume_uint16(p, &r->data.srv.port) < 0 ||
430                 flx_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
431                 goto fail;
432             
433             r->data.srv.name = g_strdup(buf);
434             break;
435
436         case FLX_DNS_TYPE_HINFO:
437             
438 /*             g_message("hinfo"); */
439
440             if (flx_dns_packet_consume_string(p, buf, sizeof(buf)) < 0)
441                 goto fail;
442
443             r->data.hinfo.cpu = g_strdup(buf);
444
445             if (flx_dns_packet_consume_string(p, buf, sizeof(buf)) < 0)
446                 goto fail;
447
448             r->data.hinfo.os = g_strdup(buf);
449             break;
450
451         case FLX_DNS_TYPE_TXT:
452
453 /*             g_message("txt"); */
454
455             if (rdlength > 0) {
456                 r->data.txt.string_list = flx_string_list_parse(flx_dns_packet_get_rptr(p), rdlength);
457                 
458                 if (flx_dns_packet_skip(p, rdlength) < 0)
459                     goto fail;
460             } else
461                 r->data.txt.string_list = NULL;
462             
463             break;
464
465         case FLX_DNS_TYPE_A:
466
467 /*             g_message("A"); */
468
469             if (flx_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(flxIPv4Address)) < 0)
470                 goto fail;
471             
472             break;
473
474         case FLX_DNS_TYPE_AAAA:
475
476 /*             g_message("aaaa"); */
477             
478             if (flx_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(flxIPv6Address)) < 0)
479                 goto fail;
480             
481             break;
482             
483         default:
484
485 /*             g_message("generic"); */
486             
487             if (rdlength > 0) {
488
489                 r->data.generic.data = g_memdup(flx_dns_packet_get_rptr(p), rdlength);
490                 
491                 if (flx_dns_packet_skip(p, rdlength) < 0)
492                     goto fail;
493             }
494
495             break;
496     }
497
498 /*     g_message("%i == %u ?", (guint8*) flx_dns_packet_get_rptr(p) - (guint8*) start, rdlength); */
499     
500     /* Check if we read enough data */
501     if ((guint8*) flx_dns_packet_get_rptr(p) - (guint8*) start != rdlength)
502         goto fail;
503     
504     *ret_cache_flush = !!(class & FLX_DNS_CACHE_FLUSH);
505     class &= ~ FLX_DNS_CACHE_FLUSH;
506
507     r->ttl = ttl;
508
509     return r;
510
511 fail:
512     if (r)
513         flx_record_unref(r);
514
515     return NULL;
516 }
517
518 flxKey* flx_dns_packet_consume_key(flxDnsPacket *p) {
519     gchar name[256];
520     guint16 type, class;
521
522     g_assert(p);
523
524     if (flx_dns_packet_consume_name(p, name, sizeof(name)) < 0 ||
525         flx_dns_packet_consume_uint16(p, &type) < 0 ||
526         flx_dns_packet_consume_uint16(p, &class) < 0)
527         return NULL;
528
529     class &= ~ FLX_DNS_CACHE_FLUSH;
530
531     return flx_key_new(name, class, type);
532 }
533
534 guint8* flx_dns_packet_append_key(flxDnsPacket *p, flxKey *k) {
535     guint8 *t;
536     guint size;
537     
538     g_assert(p);
539     g_assert(k);
540
541     size = p->size;
542     
543     if (!(t = flx_dns_packet_append_name(p, k->name)) ||
544         !flx_dns_packet_append_uint16(p, k->type) ||
545         !flx_dns_packet_append_uint16(p, k->class)) {
546         p->size = size;
547         return NULL;
548     }
549
550     return t;
551 }
552
553 guint8* flx_dns_packet_append_record(flxDnsPacket *p, flxRecord *r, gboolean cache_flush) {
554     guint8 *t, *l, *start;
555     guint size;
556
557     g_assert(p);
558     g_assert(r);
559
560     size = p->size;
561
562     if (!(t = flx_dns_packet_append_name(p, r->key->name)) ||
563         !flx_dns_packet_append_uint16(p, r->key->type) ||
564         !flx_dns_packet_append_uint16(p, cache_flush ? (r->key->class | FLX_DNS_CACHE_FLUSH) : (r->key->class &~ FLX_DNS_CACHE_FLUSH)) ||
565         !flx_dns_packet_append_uint32(p, r->ttl) ||
566         !(l = flx_dns_packet_append_uint16(p, 0)))
567         goto fail;
568
569     start = flx_dns_packet_extend(p, 0);
570
571     switch (r->key->type) {
572         
573         case FLX_DNS_TYPE_PTR:
574         case FLX_DNS_TYPE_CNAME :
575
576             if (!(flx_dns_packet_append_name(p, r->data.ptr.name)))
577                 goto fail;
578             
579             break;
580
581         case FLX_DNS_TYPE_SRV:
582
583             if (!flx_dns_packet_append_uint16(p, r->data.srv.priority) ||
584                 !flx_dns_packet_append_uint16(p, r->data.srv.weight) ||
585                 !flx_dns_packet_append_uint16(p, r->data.srv.port) ||
586                 !flx_dns_packet_append_name(p, r->data.srv.name))
587                 goto fail;
588
589             break;
590
591         case FLX_DNS_TYPE_HINFO:
592             if (!flx_dns_packet_append_string(p, r->data.hinfo.cpu) ||
593                 !flx_dns_packet_append_string(p, r->data.hinfo.os))
594                 goto fail;
595
596             break;
597
598         case FLX_DNS_TYPE_TXT: {
599
600             guint8 *data;
601             guint size;
602
603             size = flx_string_list_serialize(r->data.txt.string_list, NULL, 0);
604
605 /*             g_message("appending string: %u %p", size, r->data.txt.string_list); */
606
607             if (!(data = flx_dns_packet_extend(p, size)))
608                 goto fail;
609
610             flx_string_list_serialize(r->data.txt.string_list, data, size);
611             break;
612         }
613
614
615         case FLX_DNS_TYPE_A:
616
617             if (!flx_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address)))
618                 goto fail;
619             
620             break;
621
622         case FLX_DNS_TYPE_AAAA:
623             
624             if (!flx_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address)))
625                 goto fail;
626             
627             break;
628             
629         default:
630
631             if (r->data.generic.size &&
632                 flx_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size))
633                 goto fail;
634
635             break;
636     }
637
638
639
640     
641     size = flx_dns_packet_extend(p, 0) - start;
642     g_assert(size <= 0xFFFF);
643
644 /*     g_message("appended %u", size); */
645
646     * (guint16*) l = g_htons((guint16) size);
647     
648     return t;
649
650
651 fail:
652     p->size = size;
653     return NULL;
654 }
655
656 gboolean flx_dns_packet_is_empty(flxDnsPacket *p) {
657     g_assert(p);
658
659     return p->size <= FLX_DNS_PACKET_HEADER_SIZE;
660 }
661
662 guint flx_dns_packet_space(flxDnsPacket *p) {
663     g_assert(p);
664
665     g_assert(p->size <= p->max_size);
666     
667     return p->max_size - p->size;
668 }