Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <ip.h>
4
#include "dns.h"
5
 
6
enum {
7
	Logqueries = 0,
8
};
9
 
10
static int	udpannounce(char*);
11
static void	reply(int, uchar*, DNSmsg*, Request*);
12
 
13
typedef struct Inprogress Inprogress;
14
struct Inprogress
15
{
16
	int	inuse;
17
	Udphdr	uh;
18
	DN	*owner;
19
	ushort	type;
20
	int	id;
21
};
22
Inprogress inprog[Maxactive+2];
23
 
24
typedef struct Forwtarg Forwtarg;
25
struct Forwtarg {
26
	char	*host;
27
	uchar	addr[IPaddrlen];
28
	int	fd;
29
	ulong	lastdial;
30
};
31
Forwtarg forwtarg[10];
32
int currtarg;
33
 
34
static char *hmsg = "headers";
35
 
36
/*
37
 *  record client id and ignore retransmissions.
38
 *  we're still single thread at this point.
39
 */
40
static Inprogress*
41
clientrxmit(DNSmsg *req, uchar *buf)
42
{
43
	Inprogress *p, *empty;
44
	Udphdr *uh;
45
 
46
	uh = (Udphdr *)buf;
47
	empty = nil;
48
	for(p = inprog; p < &inprog[Maxactive]; p++){
49
		if(p->inuse == 0){
50
			if(empty == nil)
51
				empty = p;
52
			continue;
53
		}
54
		if(req->id == p->id && req->qd != nil &&
55
		    req->qd->owner == p->owner && req->qd->type == p->type &&
56
		    memcmp(uh, &p->uh, Udphdrsize) == 0)
57
			return nil;
58
	}
59
	if(empty == nil)
60
		return nil; /* shouldn't happen: see slave() & Maxactive def'n */
61
	if(req->qd == nil) {
62
		dnslog("clientrxmit: nil req->qd");
63
		return nil;
64
	}
65
	empty->id = req->id;
66
	empty->owner = req->qd->owner;
67
	empty->type = req->qd->type;
68
	if (empty->type != req->qd->type)
69
		dnslog("clientrxmit: bogus req->qd->type %d", req->qd->type);
70
	memmove(&empty->uh, uh, Udphdrsize);
71
	empty->inuse = 1;
72
	return empty;
73
}
74
 
75
int
76
addforwtarg(char *host)
77
{
78
	Forwtarg *tp;
79
 
80
	if (currtarg >= nelem(forwtarg)) {
81
		dnslog("too many forwarding targets");
82
		return -1;
83
	}
84
	tp = forwtarg + currtarg;
85
	if (parseip(tp->addr, host) < 0) {
86
		dnslog("can't parse ip %s", host);
87
		return -1;
88
	}
89
	tp->lastdial = time(nil);
90
	tp->fd = udpport(mntpt);
91
	if (tp->fd < 0)
92
		return -1;
93
 
94
	free(tp->host);
95
	tp->host = estrdup(host);
96
	currtarg++;
97
	return 0;
98
}
99
 
100
/*
101
 * fast forwarding of incoming queries to other dns servers.
102
 * intended primarily for debugging.
103
 */
104
static void
105
redistrib(uchar *buf, int len)
106
{
107
	Forwtarg *tp;
108
	Udphdr *uh;
109
	static uchar outpkt[1500];
110
 
111
	assert(len <= sizeof outpkt);
112
	memmove(outpkt, buf, len);
113
	uh = (Udphdr *)outpkt;
114
	for (tp = forwtarg; tp < forwtarg + currtarg; tp++)
115
		if (tp->fd > 0) {
116
			memmove(outpkt, tp->addr, sizeof tp->addr);
117
			hnputs(uh->rport, Dnsport);
118
			if (write(tp->fd, outpkt, len) != len) {
119
				close(tp->fd);
120
				tp->fd = -1;
121
			}
122
		} else if (tp->host && time(nil) - tp->lastdial > 60) {
123
			tp->lastdial = time(nil);
124
			tp->fd = udpport(mntpt);
125
		}
126
}
127
 
128
/*
129
 *  a process to act as a dns server for outside reqeusts
130
 */
131
void
132
dnudpserver(char *mntpt)
133
{
134
	volatile int fd, len, op, rcode;
135
	char *volatile err;
136
	volatile char tname[32];
137
	volatile uchar buf[Udphdrsize + Maxpayload];
138
	volatile DNSmsg reqmsg, repmsg;
139
	Inprogress *volatile p;
140
	volatile Request req;
141
	Udphdr *volatile uh;
142
 
143
	/*
144
	 * fork sharing text, data, and bss with parent.
145
	 * stay in the same note group.
146
	 */
147
	switch(rfork(RFPROC|RFMEM|RFNOWAIT)){
148
	case -1:
149
		break;
150
	case 0:
151
		break;
152
	default:
153
		return;
154
	}
155
 
156
	fd = -1;
157
restart:
158
	procsetname("udp server announcing");
159
	if(fd >= 0)
160
		close(fd);
161
	while((fd = udpannounce(mntpt)) < 0)
162
		sleep(5000);
163
 
164
//	procsetname("udp server");
165
	memset(&req, 0, sizeof req);
166
	if(setjmp(req.mret))
167
		putactivity(0);
168
	req.isslave = 0;
169
	req.id = 0;
170
	req.aborttime = 0;
171
 
172
	/* loop on requests */
173
	for(;; putactivity(0)){
174
		procsetname("served %d udp; %d alarms",
175
			stats.qrecvdudp, stats.alarms);
176
		memset(&repmsg, 0, sizeof repmsg);
177
		memset(&reqmsg, 0, sizeof reqmsg);
178
 
179
		alarm(60*1000);
180
		len = read(fd, buf, sizeof buf);
181
		alarm(0);
182
		if(len <= Udphdrsize)
183
			goto restart;
184
 
185
		redistrib(buf, len);
186
 
187
		uh = (Udphdr*)buf;
188
		len -= Udphdrsize;
189
 
190
		// dnslog("read received UDP from %I to %I",
191
		//	((Udphdr*)buf)->raddr, ((Udphdr*)buf)->laddr);
192
		getactivity(&req, 0);
193
		req.aborttime = timems() + Maxreqtm;
194
//		req.from = smprint("%I", ((Udphdr*)buf)->raddr);
195
		req.from = smprint("%I", buf);
196
		rcode = 0;
197
		stats.qrecvdudp++;
198
 
199
		err = convM2DNS(&buf[Udphdrsize], len, &reqmsg, &rcode);
200
		if(err){
201
			/* first bytes in buf are source IP addr */
202
			dnslog("server: input error: %s from %I", err, buf);
203
			free(err);
204
			goto freereq;
205
		}
206
		if (rcode == 0)
207
			if(reqmsg.qdcount < 1){
208
				dnslog("server: no questions from %I", buf);
209
				goto freereq;
210
			} else if(reqmsg.flags & Fresp){
211
				dnslog("server: reply not request from %I", buf);
212
				goto freereq;
213
			}
214
		op = reqmsg.flags & Omask;
215
		if(op != Oquery && op != Onotify){
216
			dnslog("server: op %d from %I", reqmsg.flags & Omask,
217
				buf);
218
			goto freereq;
219
		}
220
 
221
		if(debug || (trace && subsume(trace, reqmsg.qd->owner->name)))
222
			dnslog("%d: serve (%I/%d) %d %s %s",
223
				req.id, buf, uh->rport[0]<<8 | uh->rport[1],
224
				reqmsg.id, reqmsg.qd->owner->name,
225
				rrname(reqmsg.qd->type, tname, sizeof tname));
226
 
227
		p = clientrxmit(&reqmsg, buf);
228
		if(p == nil){
229
			if(debug)
230
				dnslog("%d: duplicate", req.id);
231
			goto freereq;
232
		}
233
 
234
		if (Logqueries) {
235
			RR *rr;
236
 
237
			for (rr = reqmsg.qd; rr; rr = rr->next)
238
				syslog(0, "dnsq", "id %d: (%I/%d) %d %s %s",
239
					req.id, buf, uh->rport[0]<<8 |
240
					uh->rport[1], reqmsg.id,
241
					reqmsg.qd->owner->name,
242
					rrname(reqmsg.qd->type, tname,
243
					sizeof tname));
244
		}
245
		/* loop through each question */
246
		while(reqmsg.qd){
247
			memset(&repmsg, 0, sizeof repmsg);
248
			switch(op){
249
			case Oquery:
250
				dnserver(&reqmsg, &repmsg, &req, buf, rcode);
251
				break;
252
			case Onotify:
253
				dnnotify(&reqmsg, &repmsg, &req);
254
				break;
255
			}
256
			/* send reply on fd to address in buf's udp hdr */
257
			reply(fd, buf, &repmsg, &req);
258
			freeanswers(&repmsg);
259
		}
260
 
261
		p->inuse = 0;
262
freereq:
263
		free(req.from);
264
		req.from = nil;
265
		freeanswers(&reqmsg);
266
		if(req.isslave){
267
			putactivity(0);
268
			_exits(0);
269
		}
270
	}
271
}
272
 
273
/*
274
 *  announce on well-known dns udp port and set message style interface
275
 */
276
static int
277
udpannounce(char *mntpt)
278
{
279
	int data, ctl;
280
	char dir[64], datafile[64+6];
281
	static int whined;
282
 
283
	/* get a udp port */
284
	sprint(datafile, "%s/udp!*!dns", mntpt);
285
	ctl = announce(datafile, dir);
286
	if(ctl < 0){
287
		if(!whined++)
288
			warning("can't announce on %s", datafile);
289
		return -1;
290
	}
291
 
292
	/* turn on header style interface */
293
	if(write(ctl, hmsg, strlen(hmsg)) != strlen(hmsg))
294
		abort();			/* hmsg */
295
 
296
	snprint(datafile, sizeof(datafile), "%s/data", dir);
297
	data = open(datafile, ORDWR);
298
	if(data < 0){
299
		close(ctl);
300
		if(!whined++)
301
			warning("can't open %s to announce on dns udp port",
302
				datafile);
303
		return -1;
304
	}
305
 
306
	close(ctl);
307
	return data;
308
}
309
 
310
static void
311
reply(int fd, uchar *buf, DNSmsg *rep, Request *reqp)
312
{
313
	int len;
314
	char tname[32];
315
 
316
	if(debug || (trace && subsume(trace, rep->qd->owner->name)))
317
		dnslog("%d: reply (%I/%d) %d %s %s qd %R an %R ns %R ar %R",
318
			reqp->id, buf, buf[4]<<8 | buf[5],
319
			rep->id, rep->qd->owner->name,
320
			rrname(rep->qd->type, tname, sizeof tname),
321
			rep->qd, rep->an, rep->ns, rep->ar);
322
 
323
	len = convDNS2M(rep, &buf[Udphdrsize], Maxdnspayload);
324
	len += Udphdrsize;
325
	if(write(fd, buf, len) != len)
326
		dnslog("error sending reply: %r");
327
}