]> git.meshlink.io Git - catta/blob - dns.c
assorted work:
[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, DNS_FIELD_FLAGS, 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, DNS_FIELD_FLAGS, 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, DNS_FIELD_FLAGS);
204
205     if (flags & DNS_FLAG_OPCODE || flags & 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, DNS_FIELD_FLAGS) & 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             }
461             
462             break;
463
464         case FLX_DNS_TYPE_A:
465
466             g_message("A");
467
468
469             g_message("%p", flx_dns_packet_get_rptr(p));
470                 
471             if (flx_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(flxIPv4Address)) < 0)
472                 goto fail;
473
474             g_message("%p", flx_dns_packet_get_rptr(p));
475             
476             break;
477
478         case FLX_DNS_TYPE_AAAA:
479
480             g_message("aaaa");
481             
482             if (flx_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(flxIPv6Address)) < 0)
483                 goto fail;
484             
485             break;
486             
487         default:
488
489             g_message("generic");
490             
491             if (rdlength > 0) {
492
493                 r->data.generic.data = g_memdup(flx_dns_packet_get_rptr(p), rdlength);
494                 
495                 if (flx_dns_packet_skip(p, rdlength) < 0)
496                     goto fail;
497             }
498
499             break;
500     }
501
502     g_message("%i == %u ?", (guint8*) flx_dns_packet_get_rptr(p) - (guint8*) start, rdlength);
503     
504     /* Check if we read enough data */
505     if ((guint8*) flx_dns_packet_get_rptr(p) - (guint8*) start != rdlength)
506         goto fail;
507     
508     *ret_cache_flush = !!(class & MDNS_CACHE_FLUSH);
509     class &= ~ MDNS_CACHE_FLUSH;
510
511     r->ttl = ttl;
512
513     return r;
514
515 fail:
516     if (r)
517         flx_record_unref(r);
518
519     return NULL;
520 }
521
522 flxKey* flx_dns_packet_consume_key(flxDnsPacket *p) {
523     gchar name[256];
524     guint16 type, class;
525
526     g_assert(p);
527
528     if (flx_dns_packet_consume_name(p, name, sizeof(name)) < 0 ||
529         flx_dns_packet_consume_uint16(p, &type) < 0 ||
530         flx_dns_packet_consume_uint16(p, &class) < 0)
531         return NULL;
532
533     class &= ~ MDNS_CACHE_FLUSH;
534
535     return flx_key_new(name, class, type);
536 }
537
538 guint8* flx_dns_packet_append_key(flxDnsPacket *p, flxKey *k) {
539     guint8 *t;
540     guint size;
541     
542     g_assert(p);
543     g_assert(k);
544
545     size = p->size;
546     
547     if (!(t = flx_dns_packet_append_name(p, k->name)) ||
548         !flx_dns_packet_append_uint16(p, k->type) ||
549         !flx_dns_packet_append_uint16(p, k->class)) {
550         p->size = size;
551         return NULL;
552     }
553
554     return t;
555 }
556
557 guint8* flx_dns_packet_append_record(flxDnsPacket *p, flxRecord *r, gboolean cache_flush) {
558     guint8 *t, *l, *start;
559     guint size;
560
561     g_assert(p);
562     g_assert(r);
563
564     size = p->size;
565
566     if (!(t = flx_dns_packet_append_name(p, r->key->name)) ||
567         !flx_dns_packet_append_uint16(p, r->key->type) ||
568         !flx_dns_packet_append_uint16(p, cache_flush ? (r->key->class | MDNS_CACHE_FLUSH) : (r->key->class &~ MDNS_CACHE_FLUSH)) ||
569         !flx_dns_packet_append_uint32(p, r->ttl) ||
570         !(l = flx_dns_packet_append_uint16(p, 0)))
571         goto fail;
572
573     start = flx_dns_packet_extend(p, 0);
574
575     switch (r->key->type) {
576         
577         case FLX_DNS_TYPE_PTR:
578         case FLX_DNS_TYPE_CNAME :
579
580             if (!(flx_dns_packet_append_name(p, r->data.ptr.name)))
581                 goto fail;
582             
583             break;
584
585         case FLX_DNS_TYPE_SRV:
586
587             if (!flx_dns_packet_append_uint16(p, r->data.srv.priority) ||
588                 !flx_dns_packet_append_uint16(p, r->data.srv.weight) ||
589                 !flx_dns_packet_append_uint16(p, r->data.srv.port) ||
590                 !flx_dns_packet_append_name(p, r->data.srv.name))
591                 goto fail;
592
593             break;
594
595         case FLX_DNS_TYPE_HINFO:
596             if (!flx_dns_packet_append_string(p, r->data.hinfo.cpu) ||
597                 !flx_dns_packet_append_string(p, r->data.hinfo.os))
598                 goto fail;
599
600             break;
601
602         case FLX_DNS_TYPE_TXT: {
603
604             guint8 *data;
605             guint size;
606
607             size = flx_string_list_serialize(r->data.txt.string_list, NULL, 0);
608
609             g_message("appending string: %u %p", size, r->data.txt.string_list);
610
611             if (!(data = flx_dns_packet_extend(p, size)))
612                 goto fail;
613
614             flx_string_list_serialize(r->data.txt.string_list, data, size);
615             break;
616         }
617
618
619         case FLX_DNS_TYPE_A:
620
621             if (!flx_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address)))
622                 goto fail;
623             
624             break;
625
626         case FLX_DNS_TYPE_AAAA:
627             
628             if (!flx_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address)))
629                 goto fail;
630             
631             break;
632             
633         default:
634
635             if (r->data.generic.size &&
636                 flx_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size))
637                 goto fail;
638
639             break;
640     }
641
642
643
644     
645     size = flx_dns_packet_extend(p, 0) - start;
646     g_assert(size <= 0xFFFF);
647
648     g_message("appended %u", size);
649
650     * (guint16*) l = g_htons((guint16) size);
651     
652     return t;
653
654
655 fail:
656     p->size = size;
657     return NULL;
658 }
659
660 gboolean flx_dns_packet_is_empty(flxDnsPacket *p) {
661     g_assert(p);
662
663     return p->size <= FLX_DNS_PACKET_HEADER_SIZE;
664 }