summaryrefslogtreecommitdiff
path: root/exploits/7350bindnxt/dnslib.c
blob: d1afb1363c789848ee3f682622eabb8347067c2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include "dnslib.h"
#include <netinet/in.h>
#include <arpa/inet.h>

/* make a full dns query including header. Returns length of packet.
 */
int
makequery (char *name, u_int16_t type, u_int8_t *buffer, u_int16_t id)
{
	HEADER *head;

	head = (HEADER *)buffer;

	bzero (head, DNSHDRSIZE);
	head->id = htons (id);
	head->qr = 0;
	head->opcode = 0;
	head->aa = 0;
	head->tc = 0;
	head->rd = 1;
	head->ra = 0;
	head->rcode = 0;
	head->qdcount = htons (1);
	head->ancount = 0;
	head->nscount = 0;
	head->arcount = 0;

	return (makeqbody (name, type, buffer + DNSHDRSIZE) + DNSHDRSIZE);
}

/* convert a \0-terminated string to a DNS domain name.
 * www.yahoo.com(.) => \003www\005yahoo\003\com\000
 */
int
formatname (char *in, u_int8_t *out)
{
	char *start = in, c = 0;
	int n = strlen (in);

	in += n - 1;
	out += n + 1;

	*out-- = 0;

	n = 0;
	while (in >= start) {
		c = *in--;
		if (c == '.') {
			*out-- = n;
			n = 0;
		} else {
			*out-- = c;
			n++;
		}
	}

	if (n)
		*out-- = n;

	return (strlen (out + 1) + 1);
}

/* simple function for making a, ptr and ns resource records
 * doesn't support more complicated stuph.
 */

int 
makeRR (char *name, u_int16_t type, u_int16_t class, u_int32_t ttl,
		char *rdata, char *buf)
{
	int		n;
	rrec_body	*rec;
	char		*ptr = buf;

	/* name the resource record pertains too */
	ptr += formatname (name, ptr);
	rec = (rrec_body *)ptr;
	rec->type = htons (type);
	rec->class = htons (class);
	rec->ttl = htonl (ttl);
	rec->rdlength = 0;
	ptr += 10;

	switch (type) {
	case T_A:
		*(u_int32_t *)ptr = inet_addr (rdata);
		rec->rdlength = htons (4);
		ptr += 4;
		break;
	case T_PTR:
	case T_NS:
		n = formatname (rdata, ptr);
		ptr += n;
		rec->rdlength = htons (n);
		break;
	default:
		/**/
	}
	return (ptr - buf);
}

/* make just the body of a DNS query.
 */
int
makeqbody (char *name, u_int16_t type, u_int8_t *buffer)
{
	int len;

	len = formatname (name, buffer);
	buffer += len;
	PUTSHORT (type, buffer);
	PUTSHORT (C_IN, buffer);
	return (len + 4);
}


/* uncompress compressed dns names. ugh.
 * works for normal formatted dns names too..
 * returns the length of the first part of the compressed name (i.e.
 * before redirection).
 */

int
uncompress (u_int8_t *in, char *out, u_int8_t *msg)
{
	u_int8_t *start = in, *end = NULL;
	u_int8_t len;
	u_int16_t off;

	while ((len = *in++)) {
		if (len & INDIR_MASK) {
			if (end == NULL)
				end = in + 1;
			off = (len & ~INDIR_MASK);
			off |= *in++ << 8;
			off = ntohs (off);
			in = msg + off;
			continue;
		}
		memcpy (out, in, len);
		out += len;
		in  += len;
		*out++ = '.';
	}
	if (end == NULL)
		end = in;
	*out++ = 0;
	return (end - start);
}