tagging vde-2 version 2.3.2
[vde.git] / 2.3.2 / src / vde_over_ns / dns.c
blob29cb2a43b5c7c812a2b42dd8dc51ce2a4208b3f8
1 /* ----------------------------------------------------------------------------
3 VDE_OVER_NS
4 (C) 2007 Daniele Lacamera
6 Derived from:
7 NSTX -- tunneling network-packets over DNS
9 (C) 2000 by Florian Heinz and Julien Oster
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License version 2, as
13 published by the Free Software Foundation.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 -------------------------------------------------------------------------- */
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <fcntl.h>
31 #include <syslog.h>
32 #include <unistd.h>
33 #include <assert.h>
34 #include <stdio.h>
36 #include <config.h>
37 #include <vde.h>
38 #include <vdecommon.h>
39 #include "fun.h"
40 #include "dns.h"
41 #include "dns_proto.h"
42 #define min(a,b) a<b?a:b
45 /* lbl2str: Decodes a domainname to a string
46 * <len1><NAME1><len2><NAME2><len3><NAME3>'\0' ->
47 * NAME1.NAME2.NAME3
48 * A lenght-byte may not be larger than 63 bytes (the two most significant
49 * bits are reserved/used for compression */
51 /* Reverses lbl2str */
53 static const char *
54 str2lbl(const char *data)
56 const char *ptr;
57 char *ptr2, *buf;
58 unsigned int buflen, chunklen;
60 ptr = data;
61 buf = NULL;
62 buflen = 0;
64 while ((ptr2 = strchr(ptr, '.'))) {
65 chunklen = ptr2 - ptr;
66 if ((chunklen > 63) || (chunklen <= 0)) {
67 DEBUG("Too long or zero-length label");
68 if (buf)
69 free(buf);
70 return NULL;
72 buf = realloc(buf, buflen + chunklen + 1);
73 buf[buflen] = chunklen;
74 memcpy(buf+buflen+1, ptr, chunklen);
75 buflen += chunklen + 1;
76 ptr = ptr2 + 1;
78 chunklen = strlen(ptr);
79 buf = realloc(buf, buflen + chunklen + 2);
80 buf[buflen] = chunklen;
81 memcpy(buf+buflen+1, ptr, chunklen);
82 buflen += chunklen+1;
83 buf[buflen] = '\0';
84 buflen++;
86 return buf;
89 /* decompress_label decompresses the label pointed to by 'lbl' inside the
90 * DNS-packet 'msg'. */
92 static char *
93 decompress_label(const char *msg, unsigned int msglen, const char *lbl)
95 const char *ptr = lbl;
96 char *buf;
97 unsigned int chunklen, offset, buflen, followed = 0;
99 buf = NULL;
100 buflen = 0;
102 while ((chunklen = *ptr)) {
103 if (chunklen > 63) {
104 if ((ptr-msg) >= ((signed int)msglen-1)) {
105 DEBUG("Bad pointer at end of msg");
106 if (buf)
107 free(buf);
108 return NULL;
110 if (followed > 20) {
111 DEBUG("Too many pointer-loops");
112 if (buf)
113 free(buf);
114 return NULL;
116 offset = (chunklen % 64)*256 + *(ptr+1);
117 if (offset >= msglen) {
118 DEBUG("offset behind message");
119 if (buf)
120 free(buf);
121 return NULL;
123 ptr = msg + offset;
124 followed++;
126 else {
127 buf = realloc(buf, buflen + chunklen + 2);
128 if ((ptr + chunklen + 1) >= (msg + msglen)) {
129 DEBUG("Invalid chunklen");
130 if (buf)
131 free(buf);
132 return NULL;
134 memcpy(buf+buflen, ptr, chunklen + 1);
135 buflen += chunklen + 1;
136 ptr += chunklen + 1;
139 if (buf) {
140 buf[buflen] = 0;
141 buflen++;
143 return buf;
146 static const unsigned char *
147 _cstringify(const unsigned char *data, int *dlen, unsigned int clen)
149 static unsigned char *buf;
151 const unsigned char *s = data;
152 unsigned char *d;
153 unsigned int llen, len;
155 len = *dlen;
156 *dlen = 0;
158 d = buf = realloc(buf, len+len/clen+2);
160 while (len > 0) {
161 llen = (len > clen) ? clen : len;
162 *(d++) = llen;
163 memcpy(d, s, llen);
164 d += llen;
165 (*dlen) += llen + 1;
166 s += llen;
167 len -= llen;
169 *d = '\0';
170 (*dlen)++;
172 return buf;
175 static const unsigned char *
176 data2lbl (const unsigned char *data)
178 int len;
180 len = strlen((char*)data);
181 return _cstringify(data, &len, 63);
184 const unsigned char *
185 data2txt (const unsigned char *data, int *len)
187 return _cstringify(data, len, 255);
190 const unsigned char *
191 txt2data (const unsigned char *data, int *dlen)
193 static unsigned char *buf;
195 const unsigned char *s = data;
196 unsigned char *d;
197 unsigned int len, llen;
199 len = *dlen;
201 d = buf = realloc(buf, len);
204 llen = *s++;
205 if (llen > len - (s - data))
206 return NULL;
207 memcpy(d, s, llen);
208 s += llen;
209 d += llen;
210 } while (llen);
212 *d = '\0';
213 *dlen = d - buf;
214 return buf;
217 static const unsigned char *
218 lbl2data (const unsigned char *data, size_t len)
220 static signed char *buf = NULL;
221 const unsigned char *s = data;
222 signed char *d;
223 signed int llen;
225 d = buf = realloc(buf, len);
226 assert(d);
229 llen = *s++;
230 if ((llen > 63) || (llen > (signed int)(len - (s - data))))
231 break;
232 memcpy(d, s, llen);
233 s += llen;
234 d += llen;
235 } while (llen);
236 *d = '\0';
237 return (const unsigned char*)buf;
240 /* New DNS-Code */
242 static struct rr *_new_listitem (struct rr **list)
244 struct rr *rrp, *tmp;
246 rrp = malloc(sizeof(struct rr));
247 memset(rrp, 0, sizeof(struct rr));
249 if (!*list)
250 *list = rrp;
251 else {
252 for (tmp = *list; tmp->next; tmp = tmp->next)
254 tmp->next = rrp;
257 return rrp;
260 static const unsigned char *
261 _skip_lbl (const unsigned char *ptr, u_int16_t *len)
263 while (*ptr) {
264 if (*len < 2)
265 return NULL;
266 if ((*ptr & 0xc0)) {
267 ptr++;
268 (*len)--;
269 break;
271 *len -= *ptr;
272 if (*len < 1)
273 return NULL;
274 ptr += *ptr+1;
277 ptr++;
278 (*len)--;
279 return ptr;
283 static __inline__ int _get_listlen (const struct rr *list)
285 int nr = 0;
287 while (list) {
288 list = list->next;
289 nr++;
292 return nr;
295 static const char *suffix = NULL;
296 static int suffixlen = 0;
298 void dns_setsuffix (char *suf)
300 suffix = str2lbl(suf);
301 suffixlen = strlen(suf)+1;
304 struct dnspkt *dns_alloc (void)
306 void *ptr;
308 ptr = malloc(sizeof(struct dnspkt));
309 memset(ptr, 0, sizeof(struct dnspkt));
311 return ptr;
314 void dns_free (struct dnspkt *pkt)
316 struct rr *list, *next;
318 list = pkt->query;
319 while (list) {
320 if (list->data)
321 free(list->data);
322 next = list->next;
323 free(list);
324 list = next;
327 list = pkt->answer;
328 while (list) {
329 if (list->data)
330 free(list->data);
331 next = list->next;
332 free(list);
333 list = next;
336 free(pkt);
339 void dns_setid (struct dnspkt *pkt, unsigned short id)
341 pkt->id = id;
344 void dns_settype (struct dnspkt *pkt, int type)
346 pkt->type = type;
349 const char *
350 dns_data2fqdn (const char *data)
352 const char *ptr;
353 static char *fqdn;
355 ptr = (char*)data2lbl((unsigned char*)data);
356 fqdn = realloc(fqdn, strlen(ptr)+strlen(suffix)+1);
357 strcpy(fqdn, ptr);
358 strcat(fqdn, suffix);
360 return fqdn;
363 const char *
364 dns_fqdn2data (const char *fqdn)
366 static char *buf;
367 const char *off;
369 if (buf)
370 free(buf);
372 off = strstr(fqdn, suffix);
373 /* only parse if the fqdn was found, and there is more than the fqdn */
374 if (off && off != fqdn)
375 buf = strdup((char*)lbl2data((unsigned char*)fqdn, off - fqdn));
376 else
377 /* Our suffix not found... */
378 buf = NULL;
380 return buf;
384 dns_addquery (struct dnspkt *pkt, const char *data)
386 struct rr *rrp;
388 rrp = _new_listitem(&pkt->query);
389 rrp->data = strdup(data);
390 rrp->len = strlen(data)+1;
392 return _get_listlen(pkt->query) - 1;
396 dns_addanswer(struct dnspkt *pkt, const char *data, int len, int link)
398 struct rr *rrp;
399 const char *ptr;
400 char *buf;
402 ptr = (char*)data2txt((unsigned char*)data, &len);
403 buf = malloc(len);
404 memcpy(buf, ptr, len);
406 rrp = _new_listitem(&pkt->answer);
407 rrp->data = buf;
408 rrp->len = len;
409 rrp->link = link;
411 return _get_listlen(pkt->query) - 1;
415 dns_getpktsize(const struct dnspkt *pkt)
417 int size;
418 struct rr *list;
420 size = 12; /* DNS-header */
422 for (list = pkt->query; list; list = list->next)
423 size += list->len + 4;
425 for (list = pkt->answer; list; list = list->next)
426 size += list->len + 12;
428 return size;
431 unsigned char *dns_constructpacket (struct dnspkt *pkt, int *l)
433 static unsigned char *buf, *ptr;
434 int len, *offsets, qdcount, ancount, i;
435 struct rr *list;
437 qdcount = _get_listlen(pkt->query);
438 ancount = _get_listlen(pkt->answer);
440 len = dns_getpktsize(pkt);
441 ptr = buf = malloc(len);
442 memset(buf, 0, len);
444 if (len > 512)
445 syslog(LOG_WARNING, "WARNING: Constructed non-conform DNS-packet (size: %d)\n", len);
447 offsets = alloca(qdcount * 4);
449 /* Header */
450 buf[0] = pkt->id / 256;
451 buf[1] = pkt->id % 256;
452 if (pkt->type == DNS_RESPONSE) {
453 buf[2] = 0x84; /* Flags: Response, Authoritative Answer */
454 buf[3] = 0x80; /* Flag: Recursion available */
455 } else
456 buf[2] = 0x01; /* Flags: Recursion desired */
457 buf[5] = qdcount;
458 buf[7] = _get_listlen(pkt->answer);
459 ptr += 12;
461 /* Query section */
462 for (list = pkt->query, i=0; list; list = list->next, i++) {
463 offsets[i] = ptr-buf;
464 memcpy(ptr, list->data, list->len);
465 ptr += list->len;
466 ptr[1] = 16;
467 ptr[3] = 1;
468 ptr += 4;
471 /* Answer section */
472 for (list = pkt->answer; list; list = list->next) {
473 ptr[0] = 0xc0 | (offsets[list->link]/256);
474 ptr[1] = offsets[list->link]%256;
475 ptr[3] = 16;
476 ptr[5] = 1;
477 ptr[10] = list->len / 256;
478 ptr[11] = list->len % 256;
479 ptr += 12;
480 memcpy(ptr, list->data, list->len);
481 ptr += list->len;
483 *l = len;
484 dns_free (pkt);
485 return buf;
489 struct dnspkt *
490 dns_extractpkt(const unsigned char *buf, int len)
492 u_int16_t qdcount, ancount, remain, *offsets, i, j, off;
493 const unsigned char *ptr;
494 struct dnspkt *pkt;
495 struct rr *rrp;
496 struct ns_answer_header *nsh;
498 if (len < 17)
499 return NULL;
501 pkt = dns_alloc();
502 struct ns_transaction_header *nsth=
503 (struct ns_transaction_header*)buf;
504 pkt->id = ntohs(nsth->tid);
506 qdcount = ntohs(nsth->questions);
507 ancount = ntohs(nsth->answers);
508 offsets = malloc(qdcount * 4);
510 ptr = buf + sizeof(struct ns_transaction_header);
511 remain = len - sizeof(struct ns_transaction_header);
513 i = 0;
514 while (qdcount--)
516 offsets[i++] = ptr - buf;
517 rrp = _new_listitem(&pkt->query);
518 rrp->data = decompress_label((char*)buf, len, (char*)ptr);
519 if (!rrp->data)
521 syslog(LOG_ERR, "dns_extractpkt: decompress_label choked in qd\n");
522 free(offsets);
523 dns_free(pkt);
524 return NULL;
526 rrp->len = strlen(rrp->data)+1;
527 ptr = _skip_lbl(ptr, &remain);
528 if (!ptr)
530 syslog(LOG_ERR, "dns_extractpkt: _skip_lbl choked in qd\n");
531 free(offsets);
532 dns_free(pkt);
533 return NULL;
535 ptr += 4;
536 remain -= 4;
538 while (ancount--)
540 rrp = _new_listitem(&pkt->answer);
541 rrp->link = -1;
542 if ((ptr[0] & 0xc0) == 0xc0)
544 off = (ptr[0] & ~(0xc0)) * 256 + ptr[1];
545 for (j = 0; j < i; j++)
546 if (offsets[j] == off)
547 break;
548 if (j < i)
549 rrp->link = j;
551 ptr=_skip_lbl(ptr,&remain);
552 nsh=(struct ns_answer_header *)ptr;
553 ptr+=sizeof(struct ns_answer_header);
554 remain-=sizeof(struct ns_answer_header);
555 //printf("REMAIN=%u, datalen=%u, type= 0x%02x, sizeof ns_answer_header=%u\n",remain,ntohs(nsh->datalen), ntohs(nsh->type),sizeof(struct ns_answer_header));
556 if (ntohs(nsh->type) != NSTYPE_TXT){
557 ptr+=ntohs(nsh->datalen);
558 remain-=ntohs(nsh->datalen);
559 continue;
561 rrp->len = ntohs(nsh->datalen);
562 rrp->data = malloc(rrp->len);
563 memcpy(rrp->data, ptr,rrp->len);
564 remain -= rrp->len;
565 ptr += rrp->len;
567 return(pkt);
571 * old code:
573 if (remain < 12)
575 syslog(LOG_ERR, "dns_extractpkt: too few bytes in an\n");
576 free(offsets);
577 dns_free(pkt);
578 return NULL;
580 rrp = _new_listitem(&pkt->answer);
581 rrp->link = -1;
582 if ((ptr[0] & 0xc0) == 0xc0)
584 off = (ptr[0] & ~(0xc0)) * 256 + ptr[1];
585 for (j = 0; j < i; j++)
586 if (offsets[j] == off)
587 break;
588 if (j < i)
589 rrp->link = j;
591 ptr = _skip_lbl(ptr, &remain);
592 rrp->len = ptr[9]+((ptr[8])<<8);
593 type=ptr[1];
594 // rrp->len = ptr[10]*256+ptr[11];
595 ptr += 10;
596 remain -= 13;
597 //printf("Len: %u, remain: %u\n",rrp->len,remain);
598 if (remain < rrp->len)
600 syslog(LOG_ERR, "dns_extractpkt: record too long in an (%d->%d)\n",
601 remain, rrp->len);
602 return(pkt);
603 // dns_free(pkt);
604 // return NULL;
606 if(type==0x10){
607 rrp->data = malloc(rrp->len);
608 memcpy(rrp->data, ptr,rrp->len);
609 remain -= rrp->len;
611 ptr += rrp->len;
613 return pkt;
618 const char *
619 dns_getquerydata(struct dnspkt *pkt)
621 struct rr *q;
622 static char *ret = NULL;
624 if (!pkt->query)
625 return NULL;
627 if (ret)
629 free(ret);
630 ret = NULL;
633 q = pkt->query;
634 pkt->query = pkt->query->next;
636 ret = q->data;
637 free(q);
639 return ret;
642 char *dns_getanswerdata (struct dnspkt *pkt, int *len)
644 struct rr *q;
645 static char *ret = NULL;
647 if (!pkt->answer)
648 return NULL;
650 q = pkt->answer;
651 pkt->answer = pkt->answer->next;
653 if (ret)
654 free(ret);
656 ret = q->data;
657 *len = q->len;
659 free(q);
660 return ret;
664 dns_getfreespace(const struct dnspkt *pkt, int type)
666 int raw, ret = 0, maxq;
668 raw = DNS_MAXPKT - dns_getpktsize(pkt);
670 if (raw < 0)
671 return -1;
673 if (type == DNS_RESPONSE) {
674 ret = raw - 14;
675 if (ret > 253)
676 ret = 253;
677 } else if (type == DNS_QUERY)
679 // ret = ((raw-suffixlen)*189-759)/256;
680 ret = (189*(254-suffixlen))/256-6;
681 if (ret > (maxq = (183-(189*suffixlen)/256)))
682 ret = maxq;
685 return (ret > 0) ? ret : 0;