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