]> git.meshlink.io Git - catta/blob - dns.c
add known answer suppresion server part
[catta] / dns.c
1 #include <stdlib.h>
2 #include <string.h>
3 #include <stdio.h>
4
5 #include "dns.h"
6
7 flxDnsPacket* flx_dns_packet_new(guint max_size) {
8     flxDnsPacket *p;
9
10     if (max_size <= 0)
11         max_size = FLX_DNS_PACKET_MAX_SIZE;
12     else if (max_size < FLX_DNS_PACKET_HEADER_SIZE)
13         max_size = FLX_DNS_PACKET_HEADER_SIZE;
14     
15     p = g_malloc(sizeof(flxDnsPacket) + max_size);
16     p->size = p->rindex = FLX_DNS_PACKET_HEADER_SIZE;
17     p->max_size = max_size;
18
19     memset(FLX_DNS_PACKET_DATA(p), 0, p->size);
20     return p;
21 }
22
23 flxDnsPacket* flx_dns_packet_new_query(guint max_size) {
24     flxDnsPacket *p;
25
26     p = flx_dns_packet_new(max_size);
27     flx_dns_packet_set_field(p, DNS_FIELD_FLAGS, DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
28     return p;
29 }
30
31 flxDnsPacket* flx_dns_packet_new_response(guint max_size) {
32     flxDnsPacket *p;
33
34     p = flx_dns_packet_new(max_size);
35     flx_dns_packet_set_field(p, DNS_FIELD_FLAGS, DNS_FLAGS(1, 0, 0, 0, 0, 0, 0, 0, 0, 0));
36     return p;
37 }
38
39 void flx_dns_packet_free(flxDnsPacket *p) {
40     g_assert(p);
41     g_free(p);
42 }
43
44 void flx_dns_packet_set_field(flxDnsPacket *p, guint index, guint16 v) {
45     g_assert(p);
46     g_assert(index < FLX_DNS_PACKET_HEADER_SIZE);
47     
48     ((guint16*) FLX_DNS_PACKET_DATA(p))[index] = g_htons(v);
49 }
50
51 guint16 flx_dns_packet_get_field(flxDnsPacket *p, guint index) {
52     g_assert(p);
53     g_assert(index < FLX_DNS_PACKET_HEADER_SIZE);
54
55     return g_ntohs(((guint16*) FLX_DNS_PACKET_DATA(p))[index]);
56 }
57
58 guint8* flx_dns_packet_append_name(flxDnsPacket *p, const gchar *name) {
59     guint8 *d, *f = NULL;
60     guint saved_size;
61     
62     g_assert(p);
63     g_assert(name);
64
65     saved_size = p->size;
66
67     for (;;) {
68         guint n = strcspn(name, ".");
69         if (!n || n > 63)
70             goto fail;
71         
72         if (!(d = flx_dns_packet_extend(p, n+1)))
73             goto fail;
74             
75         if (!f)
76             f = d;
77         d[0] = n;
78         memcpy(d+1, name, n);
79
80         name += n;
81
82         /* no trailing dot */
83         if (!*name)
84             break;
85
86         name ++;
87
88         /* trailing dot */
89         if (!*name)
90             break;
91     }
92
93     if (!(d = flx_dns_packet_extend(p, 1)))
94         goto fail;
95     
96     d[0] = 0;
97
98     return f;
99
100 fail:
101     p->size = saved_size;
102     return NULL;
103 }
104
105 guint8* flx_dns_packet_append_uint16(flxDnsPacket *p, guint16 v) {
106     guint8 *d;
107     g_assert(p);
108     
109     if (!(d = flx_dns_packet_extend(p, sizeof(guint16))))
110         return NULL;
111     
112     *((guint16*) d) = g_htons(v);
113     return d;
114 }
115
116 guint8 *flx_dns_packet_append_uint32(flxDnsPacket *p, guint32 v) {
117     guint8 *d;
118     g_assert(p);
119
120     if (!(d = flx_dns_packet_extend(p, sizeof(guint32))))
121         return NULL;
122     
123     *((guint32*) d) = g_htonl(v);
124
125     return d;
126 }
127
128 guint8 *flx_dns_packet_append_bytes(flxDnsPacket  *p, gconstpointer b, guint l) {
129     guint8* d;
130
131     g_assert(p);
132     g_assert(b);
133     g_assert(l);
134
135     if (!(d = flx_dns_packet_extend(p, l)))
136         return NULL;
137
138     memcpy(d, b, l);
139     return d;
140 }
141
142 guint8 *flx_dns_packet_extend(flxDnsPacket *p, guint l) {
143     guint8 *d;
144     
145     g_assert(p);
146
147     if (p->size+l > p->max_size)
148         return NULL;
149     
150     d = FLX_DNS_PACKET_DATA(p) + p->size;
151     p->size += l;
152     
153     return d;
154 }
155
156 guint8 *flx_dns_packet_append_name_compressed(flxDnsPacket *p, const gchar *name, guint8 *prev) {
157     guint16 *d;
158     signed long k;
159     g_assert(p);
160
161     if (!prev)
162         return flx_dns_packet_append_name(p, name);
163     
164     k = prev - FLX_DNS_PACKET_DATA(p);
165     if (k < 0 || k >= 0x4000 || (guint) k >= p->size)
166         return flx_dns_packet_append_name(p, name);
167
168     if (!(d = (guint16*) flx_dns_packet_extend(p, sizeof(guint16))))
169         return NULL;
170     
171     *d = g_htons((0xC000 | k));
172     return prev;
173 }
174
175 gint flx_dns_packet_check_valid(flxDnsPacket *p) {
176     guint16 flags;
177     g_assert(p);
178
179     if (p->size < 12)
180         return -1;
181
182     flags = flx_dns_packet_get_field(p, DNS_FIELD_FLAGS);
183
184     if (flags & DNS_FLAG_OPCODE || flags & DNS_FLAG_RCODE)
185         return -1;
186
187     return 0;
188 }
189
190 gint flx_dns_packet_is_query(flxDnsPacket *p) {
191     g_assert(p);
192     
193     return !(flx_dns_packet_get_field(p, DNS_FIELD_FLAGS) & DNS_FLAG_QR);
194 }
195
196 static gint consume_labels(flxDnsPacket *p, guint index, gchar *ret_name, guint l) {
197     gint ret = 0;
198     int compressed = 0;
199     int first_label = 1;
200     g_assert(p && ret_name && l);
201     
202     for (;;) {
203         guint8 n;
204
205         if (index+1 > p->size)
206             return -1;
207
208         n = FLX_DNS_PACKET_DATA(p)[index];
209
210         if (!n) {
211             index++;
212             if (!compressed)
213                 ret++;
214
215             if (l < 1)
216                 return -1;
217             *ret_name = 0;
218             
219             return ret;
220             
221         } else if (n <= 63) {
222             /* Uncompressed label */
223             index++;
224             if (!compressed)
225                 ret++;
226         
227             if (index + n > p->size)
228                 return -1;
229
230             if ((guint) n + 1 > l)
231                 return -1;
232
233             if (!first_label) {
234                 *(ret_name++) = '.';
235                 l--;
236             } else
237                 first_label = 0;
238
239             memcpy(ret_name, FLX_DNS_PACKET_DATA(p) + index, n);
240             index += n;
241             ret_name += n;
242             l -= n;
243             
244             if (!compressed)
245                 ret += n;
246         } else if ((n & 0xC0) == 0xC0) {
247             /* Compressed label */
248
249             if (index+2 > p->size)
250                 return -1;
251
252             index = ((guint) (FLX_DNS_PACKET_DATA(p)[index] & ~0xC0)) << 8 | FLX_DNS_PACKET_DATA(p)[index+1];
253
254             if (!compressed)
255                 ret += 2;
256             
257             compressed = 1;
258         } else
259             return -1;
260     }
261 }
262
263 gint flx_dns_packet_consume_name(flxDnsPacket *p, gchar *ret_name, guint l) {
264     gint r;
265     
266     if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0)
267         return -1;
268
269     p->rindex += r;
270     return 0;
271 }
272
273 gint flx_dns_packet_consume_uint16(flxDnsPacket *p, guint16 *ret_v) {
274     g_assert(p);
275     g_assert(ret_v);
276
277     if (p->rindex + sizeof(guint16) > p->size)
278         return -1;
279
280     *ret_v = g_ntohs(*((guint16*) (FLX_DNS_PACKET_DATA(p) + p->rindex)));
281     p->rindex += sizeof(guint16);
282
283     return 0;
284 }
285
286 gint flx_dns_packet_consume_uint32(flxDnsPacket *p, guint32 *ret_v) {
287     g_assert(p);
288     g_assert(ret_v);
289
290     if (p->rindex + sizeof(guint32) > p->size)
291         return -1;
292
293     *ret_v = g_ntohl(*((guint32*) (FLX_DNS_PACKET_DATA(p) + p->rindex)));
294     p->rindex += sizeof(guint32);
295     
296     return 0;
297 }
298
299 gint flx_dns_packet_consume_bytes(flxDnsPacket *p, gpointer ret_data, guint l) {
300     g_assert(p);
301     g_assert(ret_data);
302     g_assert(l > 0);
303     
304     if (p->rindex + l > p->size)
305         return -1;
306
307     memcpy(ret_data, FLX_DNS_PACKET_DATA(p) + p->rindex, l);
308     p->rindex += l;
309
310     return 0;
311 }
312
313 gconstpointer flx_dns_packet_get_rptr(flxDnsPacket *p) {
314     g_assert(p);
315     
316     if (p->rindex >= p->size)
317         return NULL;
318
319     return FLX_DNS_PACKET_DATA(p) + p->rindex;
320 }
321
322 gint flx_dns_packet_skip(flxDnsPacket *p, guint length) {
323     g_assert(p);
324
325     if (p->rindex + length > p->size)
326         return -1;
327
328     p->rindex += length;
329     return 0;
330 }
331
332 flxRecord* flx_dns_packet_consume_record(flxDnsPacket *p, gboolean *ret_cache_flush) {
333     gchar name[257], buf[257+6];
334     guint16 type, class;
335     guint32 ttl;
336     guint16 rdlength;
337     gconstpointer data;
338
339     g_assert(p);
340     g_assert(ret_cache_flush);
341
342     if (flx_dns_packet_consume_name(p, name, sizeof(name)) < 0 ||
343         flx_dns_packet_consume_uint16(p, &type) < 0 ||
344         flx_dns_packet_consume_uint16(p, &class) < 0 ||
345         flx_dns_packet_consume_uint32(p, &ttl) < 0 ||
346         flx_dns_packet_consume_uint16(p, &rdlength) < 0)
347         return NULL;
348
349     switch (type) {
350         case FLX_DNS_TYPE_PTR:
351         case FLX_DNS_TYPE_CNAME:
352             if (flx_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
353                 return NULL;
354             
355             data = buf;
356             rdlength = strlen(buf);
357             break;
358
359         case FLX_DNS_TYPE_SRV: {
360             const guint8 *t = flx_dns_packet_get_rptr(p);
361
362             if (flx_dns_packet_skip(p, 6) < 0)
363                 return NULL;
364             
365             memcpy(buf, t, 6);
366
367             if (flx_dns_packet_consume_name(p, buf+6, sizeof(buf)-6) < 0)
368                 return NULL;
369
370             data = buf;
371             rdlength = 6 + strlen(buf+6);
372             break;
373         }
374             
375         default:
376
377             if (rdlength > 0) {
378
379                 if (!(data = flx_dns_packet_get_rptr(p)) ||
380                     flx_dns_packet_skip(p, rdlength) < 0)
381                     return NULL;
382             } else
383                 data = NULL;
384
385             break;
386     }
387
388     *ret_cache_flush = !!(class & MDNS_CACHE_FLUSH);
389     class &= ~ MDNS_CACHE_FLUSH;
390
391     return flx_record_new_full(name, class, type, data, rdlength, ttl);
392 }
393
394 flxKey* flx_dns_packet_consume_key(flxDnsPacket *p) {
395     gchar name[256];
396     guint16 type, class;
397
398     g_assert(p);
399
400     if (flx_dns_packet_consume_name(p, name, sizeof(name)) < 0 ||
401         flx_dns_packet_consume_uint16(p, &type) < 0 ||
402         flx_dns_packet_consume_uint16(p, &class) < 0)
403         return NULL;
404
405     class &= ~ MDNS_CACHE_FLUSH;
406
407     return flx_key_new(name, class, type);
408 }
409
410 guint8* flx_dns_packet_append_key(flxDnsPacket *p, flxKey *k) {
411     guint8 *t;
412     guint size;
413     
414     g_assert(p);
415     g_assert(k);
416
417     size = p->size;
418     
419     if (!(t = flx_dns_packet_append_name(p, k->name)) ||
420         !flx_dns_packet_append_uint16(p, k->type) ||
421         !flx_dns_packet_append_uint16(p, k->class)) {
422         p->size = size;
423         return NULL;
424     }
425
426     return t;
427 }
428
429 guint8* flx_dns_packet_append_record(flxDnsPacket *p, flxRecord *r, gboolean cache_flush) {
430     guint8 *t;
431     guint size;
432
433     g_assert(p);
434     g_assert(r);
435
436     size = p->size;
437
438     if (!(t = flx_dns_packet_append_name(p, r->key->name)) ||
439         !flx_dns_packet_append_uint16(p, r->key->type) ||
440         !flx_dns_packet_append_uint16(p, cache_flush ? (r->key->class | MDNS_CACHE_FLUSH) : (r->key->class &~ MDNS_CACHE_FLUSH)) ||
441         !flx_dns_packet_append_uint32(p, r->ttl))
442         goto fail;
443
444     switch (r->key->type) {
445         
446         case FLX_DNS_TYPE_PTR:
447         case FLX_DNS_TYPE_CNAME: {
448             char ptr_name[257];
449
450             g_assert((size_t) r->size+1 <= sizeof(ptr_name));
451             memcpy(ptr_name, r->data, r->size);
452             ptr_name[r->size] = 0;
453             
454             if (!flx_dns_packet_append_uint16(p, strlen(ptr_name)+1) ||
455                 !flx_dns_packet_append_name(p, ptr_name))
456                 goto fail;
457
458             break;
459         }
460
461         case FLX_DNS_TYPE_SRV: {
462             char name[257];
463
464             g_assert(r->size >= 6 && (size_t) r->size-6+1 <= sizeof(name));
465             memcpy(name, r->data+6, r->size-6);
466             name[r->size-6] = 0;
467
468             if (!flx_dns_packet_append_uint16(p, strlen(name+6)+1+6) ||
469                 !flx_dns_packet_append_bytes(p, r->data, 6) ||
470                 !flx_dns_packet_append_name(p, name))
471                 goto fail;
472
473             break;
474         }
475
476         default:
477             if (!flx_dns_packet_append_uint16(p, r->size) ||
478                 (r->size != 0 && !flx_dns_packet_append_bytes(p, r->data, r->size)))
479                 goto fail;
480     }
481
482     return t;
483
484
485 fail:
486     p->size = size;
487     return NULL;
488 }
489
490 gboolean flx_dns_packet_is_empty(flxDnsPacket *p) {
491     g_assert(p);
492
493     return p->size <= FLX_DNS_PACKET_HEADER_SIZE;
494 }