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
, i
;
437 qdcount
= _get_listlen(pkt
->query
);
439 len
= dns_getpktsize(pkt
);
440 ptr
= buf
= malloc(len
);
444 syslog(LOG_WARNING
, "WARNING: Constructed non-conform DNS-packet (size: %d)\n", len
);
446 offsets
= alloca(qdcount
* 4);
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 */
455 buf
[2] = 0x01; /* Flags: Recursion desired */
457 buf
[7] = _get_listlen(pkt
->answer
);
461 for (list
= pkt
->query
, i
=0; list
; list
= list
->next
, i
++) {
462 offsets
[i
] = ptr
-buf
;
463 memcpy(ptr
, list
->data
, list
->len
);
471 for (list
= pkt
->answer
; list
; list
= list
->next
) {
472 ptr
[0] = 0xc0 | (offsets
[list
->link
]/256);
473 ptr
[1] = offsets
[list
->link
]%256;
476 ptr
[10] = list
->len
/ 256;
477 ptr
[11] = list
->len
% 256;
479 memcpy(ptr
, list
->data
, list
->len
);
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
;
495 struct ns_answer_header
*nsh
;
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
);
515 offsets
[i
++] = ptr
- buf
;
516 rrp
= _new_listitem(&pkt
->query
);
517 rrp
->data
= decompress_label((char*)buf
, len
, (char*)ptr
);
520 syslog(LOG_ERR
, "dns_extractpkt: decompress_label choked in qd\n");
525 rrp
->len
= strlen(rrp
->data
)+1;
526 ptr
= _skip_lbl(ptr
, &remain
);
529 syslog(LOG_ERR
, "dns_extractpkt: _skip_lbl choked in qd\n");
539 rrp
= _new_listitem(&pkt
->answer
);
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
)
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
);
560 rrp
->len
= ntohs(nsh
->datalen
);
561 rrp
->data
= malloc(rrp
->len
);
562 memcpy(rrp
->data
, ptr
,rrp
->len
);
574 syslog(LOG_ERR, "dns_extractpkt: too few bytes in an\n");
579 rrp = _new_listitem(&pkt->answer);
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)
590 ptr = _skip_lbl(ptr, &remain);
591 rrp->len = ptr[9]+((ptr[8])<<8);
593 // rrp->len = ptr[10]*256+ptr[11];
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",
606 rrp->data = malloc(rrp->len);
607 memcpy(rrp->data, ptr,rrp->len);
618 dns_getquerydata(struct dnspkt
*pkt
)
621 static char *ret
= NULL
;
633 pkt
->query
= pkt
->query
->next
;
641 char *dns_getanswerdata (struct dnspkt
*pkt
, int *len
)
644 static char *ret
= NULL
;
650 pkt
->answer
= pkt
->answer
->next
;
663 dns_getfreespace(const struct dnspkt
*pkt
, int type
)
665 int raw
, ret
= 0, maxq
;
667 raw
= DNS_MAXPKT
- dns_getpktsize(pkt
);
672 if (type
== DNS_RESPONSE
) {
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)))
684 return (ret
> 0) ? ret
: 0;