Enable vde_over_ns when header is present.
[vde.git] / vde-2 / src / vde_over_ns / dns.c
blobf07dcd480a9af9665f6ef92cd4e987578350e02e
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, i;
435 struct rr *list;
437 qdcount = _get_listlen(pkt->query);
439 len = dns_getpktsize(pkt);
440 ptr = buf = malloc(len);
441 memset(buf, 0, len);
443 if (len > 512)
444 syslog(LOG_WARNING, "WARNING: Constructed non-conform DNS-packet (size: %d)\n", len);
446 offsets = alloca(qdcount * 4);
448 /* Header */
449 buf[0] = pkt->id / 256;
450 buf[1] = pkt->id % 256;
451 if (pkt->type == DNS_RESPONSE) {
452 buf[2] = 0x84; /* Flags: Response, Authoritative Answer */
453 buf[3] = 0x80; /* Flag: Recursion available */
454 } else
455 buf[2] = 0x01; /* Flags: Recursion desired */
456 buf[5] = qdcount;
457 buf[7] = _get_listlen(pkt->answer);
458 ptr += 12;
460 /* Query section */
461 for (list = pkt->query, i=0; list; list = list->next, i++) {
462 offsets[i] = ptr-buf;
463 memcpy(ptr, list->data, list->len);
464 ptr += list->len;
465 ptr[1] = 16;
466 ptr[3] = 1;
467 ptr += 4;
470 /* Answer section */
471 for (list = pkt->answer; list; list = list->next) {
472 ptr[0] = 0xc0 | (offsets[list->link]/256);
473 ptr[1] = offsets[list->link]%256;
474 ptr[3] = 16;
475 ptr[5] = 1;
476 ptr[10] = list->len / 256;
477 ptr[11] = list->len % 256;
478 ptr += 12;
479 memcpy(ptr, list->data, list->len);
480 ptr += list->len;
482 *l = len;
483 dns_free (pkt);
484 return buf;
488 struct dnspkt *
489 dns_extractpkt(const unsigned char *buf, int len)
491 u_int16_t qdcount, ancount, remain, *offsets, i, j, off;
492 const unsigned char *ptr;
493 struct dnspkt *pkt;
494 struct rr *rrp;
495 struct ns_answer_header *nsh;
497 if (len < 17)
498 return NULL;
500 pkt = dns_alloc();
501 struct ns_transaction_header *nsth=
502 (struct ns_transaction_header*)buf;
503 pkt->id = ntohs(nsth->tid);
505 qdcount = ntohs(nsth->questions);
506 ancount = ntohs(nsth->answers);
507 offsets = malloc(qdcount * 4);
509 ptr = buf + sizeof(struct ns_transaction_header);
510 remain = len - sizeof(struct ns_transaction_header);
512 i = 0;
513 while (qdcount--)
515 offsets[i++] = ptr - buf;
516 rrp = _new_listitem(&pkt->query);
517 rrp->data = decompress_label((char*)buf, len, (char*)ptr);
518 if (!rrp->data)
520 syslog(LOG_ERR, "dns_extractpkt: decompress_label choked in qd\n");
521 free(offsets);
522 dns_free(pkt);
523 return NULL;
525 rrp->len = strlen(rrp->data)+1;
526 ptr = _skip_lbl(ptr, &remain);
527 if (!ptr)
529 syslog(LOG_ERR, "dns_extractpkt: _skip_lbl choked in qd\n");
530 free(offsets);
531 dns_free(pkt);
532 return NULL;
534 ptr += 4;
535 remain -= 4;
537 while (ancount--)
539 rrp = _new_listitem(&pkt->answer);
540 rrp->link = -1;
541 if ((ptr[0] & 0xc0) == 0xc0)
543 off = (ptr[0] & ~(0xc0)) * 256 + ptr[1];
544 for (j = 0; j < i; j++)
545 if (offsets[j] == off)
546 break;
547 if (j < i)
548 rrp->link = j;
550 ptr=_skip_lbl(ptr,&remain);
551 nsh=(struct ns_answer_header *)ptr;
552 ptr+=sizeof(struct ns_answer_header);
553 remain-=sizeof(struct ns_answer_header);
554 //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));
555 if (ntohs(nsh->type) != NSTYPE_TXT){
556 ptr+=ntohs(nsh->datalen);
557 remain-=ntohs(nsh->datalen);
558 continue;
560 rrp->len = ntohs(nsh->datalen);
561 rrp->data = malloc(rrp->len);
562 memcpy(rrp->data, ptr,rrp->len);
563 remain -= rrp->len;
564 ptr += rrp->len;
566 return(pkt);
570 * old code:
572 if (remain < 12)
574 syslog(LOG_ERR, "dns_extractpkt: too few bytes in an\n");
575 free(offsets);
576 dns_free(pkt);
577 return NULL;
579 rrp = _new_listitem(&pkt->answer);
580 rrp->link = -1;
581 if ((ptr[0] & 0xc0) == 0xc0)
583 off = (ptr[0] & ~(0xc0)) * 256 + ptr[1];
584 for (j = 0; j < i; j++)
585 if (offsets[j] == off)
586 break;
587 if (j < i)
588 rrp->link = j;
590 ptr = _skip_lbl(ptr, &remain);
591 rrp->len = ptr[9]+((ptr[8])<<8);
592 type=ptr[1];
593 // rrp->len = ptr[10]*256+ptr[11];
594 ptr += 10;
595 remain -= 13;
596 //printf("Len: %u, remain: %u\n",rrp->len,remain);
597 if (remain < rrp->len)
599 syslog(LOG_ERR, "dns_extractpkt: record too long in an (%d->%d)\n",
600 remain, rrp->len);
601 return(pkt);
602 // dns_free(pkt);
603 // return NULL;
605 if(type==0x10){
606 rrp->data = malloc(rrp->len);
607 memcpy(rrp->data, ptr,rrp->len);
608 remain -= rrp->len;
610 ptr += rrp->len;
612 return pkt;
617 const char *
618 dns_getquerydata(struct dnspkt *pkt)
620 struct rr *q;
621 static char *ret = NULL;
623 if (!pkt->query)
624 return NULL;
626 if (ret)
628 free(ret);
629 ret = NULL;
632 q = pkt->query;
633 pkt->query = pkt->query->next;
635 ret = q->data;
636 free(q);
638 return ret;
641 char *dns_getanswerdata (struct dnspkt *pkt, int *len)
643 struct rr *q;
644 static char *ret = NULL;
646 if (!pkt->answer)
647 return NULL;
649 q = pkt->answer;
650 pkt->answer = pkt->answer->next;
652 if (ret)
653 free(ret);
655 ret = q->data;
656 *len = q->len;
658 free(q);
659 return ret;
663 dns_getfreespace(const struct dnspkt *pkt, int type)
665 int raw, ret = 0, maxq;
667 raw = DNS_MAXPKT - dns_getpktsize(pkt);
669 if (raw < 0)
670 return -1;
672 if (type == DNS_RESPONSE) {
673 ret = raw - 14;
674 if (ret > 253)
675 ret = 253;
676 } else if (type == DNS_QUERY)
678 // ret = ((raw-suffixlen)*189-759)/256;
679 ret = (189*(254-suffixlen))/256-6;
680 if (ret > (maxq = (183-(189*suffixlen)/256)))
681 ret = maxq;
684 return (ret > 0) ? ret : 0;