1 /* ----------------------------------------------------------------------------
4 (C) 2007 Daniele Lacamera
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 -------------------------------------------------------------------------- */
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
38 #include <vdecommon.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' ->
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 */
54 str2lbl(const char *data
)
58 unsigned int buflen
, chunklen
;
64 while ((ptr2
= strchr(ptr
, '.'))) {
65 chunklen
= ptr2
- ptr
;
66 if ((chunklen
> 63) || (chunklen
<= 0)) {
67 DEBUG("Too long or zero-length label");
72 buf
= realloc(buf
, buflen
+ chunklen
+ 1);
73 buf
[buflen
] = chunklen
;
74 memcpy(buf
+buflen
+1, ptr
, chunklen
);
75 buflen
+= chunklen
+ 1;
78 chunklen
= strlen(ptr
);
79 buf
= realloc(buf
, buflen
+ chunklen
+ 2);
80 buf
[buflen
] = chunklen
;
81 memcpy(buf
+buflen
+1, ptr
, chunklen
);
89 /* decompress_label decompresses the label pointed to by 'lbl' inside the
90 * DNS-packet 'msg'. */
93 decompress_label(const char *msg
, unsigned int msglen
, const char *lbl
)
95 const char *ptr
= lbl
;
97 unsigned int chunklen
, offset
, buflen
, followed
= 0;
102 while ((chunklen
= *ptr
)) {
104 if ((ptr
-msg
) >= ((signed int)msglen
-1)) {
105 DEBUG("Bad pointer at end of msg");
111 DEBUG("Too many pointer-loops");
116 offset
= (chunklen
% 64)*256 + *(ptr
+1);
117 if (offset
>= msglen
) {
118 DEBUG("offset behind message");
127 buf
= realloc(buf
, buflen
+ chunklen
+ 2);
128 if ((ptr
+ chunklen
+ 1) >= (msg
+ msglen
)) {
129 DEBUG("Invalid chunklen");
134 memcpy(buf
+buflen
, ptr
, chunklen
+ 1);
135 buflen
+= chunklen
+ 1;
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
;
153 unsigned int llen
, len
;
158 d
= buf
= realloc(buf
, len
+len
/clen
+2);
161 llen
= (len
> clen
) ? clen
: len
;
175 static const unsigned char *
176 data2lbl (const unsigned char *data
)
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
;
197 unsigned int len
, llen
;
201 d
= buf
= realloc(buf
, len
);
205 if (llen
> len
- (s
- data
))
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
;
225 d
= buf
= realloc(buf
, len
);
230 if ((llen
> 63) || (llen
> (signed int)(len
- (s
- data
))))
237 return (const unsigned char*)buf
;
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
));
252 for (tmp
= *list
; tmp
->next
; tmp
= tmp
->next
)
260 static const unsigned char *
261 _skip_lbl (const unsigned char *ptr
, u_int16_t
*len
)
283 static __inline__
int _get_listlen (const struct rr
*list
)
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)
308 ptr
= malloc(sizeof(struct dnspkt
));
309 memset(ptr
, 0, sizeof(struct dnspkt
));
314 void dns_free (struct dnspkt
*pkt
)
316 struct rr
*list
, *next
;
339 void dns_setid (struct dnspkt
*pkt
, unsigned short id
)
344 void dns_settype (struct dnspkt
*pkt
, int type
)
350 dns_data2fqdn (const char *data
)
355 ptr
= (char*)data2lbl((unsigned char*)data
);
356 fqdn
= realloc(fqdn
, strlen(ptr
)+strlen(suffix
)+1);
358 strcat(fqdn
, suffix
);
364 dns_fqdn2data (const char *fqdn
)
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
));
377 /* Our suffix not found... */
384 dns_addquery (struct dnspkt
*pkt
, const char *data
)
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
)
402 ptr
= (char*)data2txt((unsigned char*)data
, &len
);
404 memcpy(buf
, ptr
, len
);
406 rrp
= _new_listitem(&pkt
->answer
);
411 return _get_listlen(pkt
->query
) - 1;
415 dns_getpktsize(const struct dnspkt
*pkt
)
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;
431 unsigned char *dns_constructpacket (struct dnspkt
*pkt
, int *l
)
433 static unsigned char *buf
, *ptr
;
434 int len
, *offsets
, qdcount
, ancount
, i
;
437 qdcount
= _get_listlen(pkt
->query
);
438 ancount
= _get_listlen(pkt
->answer
);
440 len
= dns_getpktsize(pkt
);
441 ptr
= buf
= malloc(len
);
445 syslog(LOG_WARNING
, "WARNING: Constructed non-conform DNS-packet (size: %d)\n", len
);
447 offsets
= alloca(qdcount
* 4);
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 */
456 buf
[2] = 0x01; /* Flags: Recursion desired */
458 buf
[7] = _get_listlen(pkt
->answer
);
462 for (list
= pkt
->query
, i
=0; list
; list
= list
->next
, i
++) {
463 offsets
[i
] = ptr
-buf
;
464 memcpy(ptr
, list
->data
, list
->len
);
472 for (list
= pkt
->answer
; list
; list
= list
->next
) {
473 ptr
[0] = 0xc0 | (offsets
[list
->link
]/256);
474 ptr
[1] = offsets
[list
->link
]%256;
477 ptr
[10] = list
->len
/ 256;
478 ptr
[11] = list
->len
% 256;
480 memcpy(ptr
, list
->data
, list
->len
);
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
;
496 struct ns_answer_header
*nsh
;
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
);
516 offsets
[i
++] = ptr
- buf
;
517 rrp
= _new_listitem(&pkt
->query
);
518 rrp
->data
= decompress_label((char*)buf
, len
, (char*)ptr
);
521 syslog(LOG_ERR
, "dns_extractpkt: decompress_label choked in qd\n");
526 rrp
->len
= strlen(rrp
->data
)+1;
527 ptr
= _skip_lbl(ptr
, &remain
);
530 syslog(LOG_ERR
, "dns_extractpkt: _skip_lbl choked in qd\n");
540 rrp
= _new_listitem(&pkt
->answer
);
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
)
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
);
561 rrp
->len
= ntohs(nsh
->datalen
);
562 rrp
->data
= malloc(rrp
->len
);
563 memcpy(rrp
->data
, ptr
,rrp
->len
);
575 syslog(LOG_ERR, "dns_extractpkt: too few bytes in an\n");
580 rrp = _new_listitem(&pkt->answer);
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)
591 ptr = _skip_lbl(ptr, &remain);
592 rrp->len = ptr[9]+((ptr[8])<<8);
594 // rrp->len = ptr[10]*256+ptr[11];
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",
607 rrp->data = malloc(rrp->len);
608 memcpy(rrp->data, ptr,rrp->len);
619 dns_getquerydata(struct dnspkt
*pkt
)
622 static char *ret
= NULL
;
634 pkt
->query
= pkt
->query
->next
;
642 char *dns_getanswerdata (struct dnspkt
*pkt
, int *len
)
645 static char *ret
= NULL
;
651 pkt
->answer
= pkt
->answer
->next
;
664 dns_getfreespace(const struct dnspkt
*pkt
, int type
)
666 int raw
, ret
= 0, maxq
;
668 raw
= DNS_MAXPKT
- dns_getpktsize(pkt
);
673 if (type
== DNS_RESPONSE
) {
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)))
685 return (ret
> 0) ? ret
: 0;