xref: /plan9/sys/src/cmd/ndb/dnresolve.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include <u.h>
2 #include <libc.h>
3 #include <lock.h>
4 #include "dns.h"
5 #include "ip.h"
6 
7 enum
8 {
9 	Maxdest=	32,	/* maximum destinations for a request message */
10 	Maxtrans=	3,	/* maximum transmissions to a server */
11 };
12 
13 typedef struct Dest	Dest;
14 struct Dest
15 {
16 	uchar	a[4];	/* ip address */
17 	DN	*s;	/* name server */
18 	int	nx;	/* number of transmissions */
19 };
20 
21 static ulong reqno;	/* request id */
22 
23 static int	netquery(DN*, int, RR*, Request*);
24 static RR*	dnresolve1(char*, int, int, Request*);
25 
26 /*
27  *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
28  *  looking it up as a canonical name.
29  */
30 RR*
31 dnresolve(char *name, int class, int type, Request *req, RR **cn)
32 {
33 	RR *rp;
34 	DN *dp;
35 
36 	/* try the name directly */
37 	rp = dnresolve1(name, class, type, req);
38 	if(rp)
39 		return rp;
40 
41 	/* try it as a canonical name */
42 	rp = dnresolve1(name, class, Tcname, req);
43 	if(rp == 0)
44 		return 0;
45 	if(rp && cn)
46 		*cn = rp;
47 	dp = rp->host;
48 	return dnresolve1(dp->name, class, type, req);
49 }
50 
51 static RR*
52 dnresolve1(char *name, int class, int type, Request *req)
53 {
54 	DN *dp, *nsdp;
55 	RR *rp, *nsrp;
56 	char *cp;
57 
58 	/* only class Cin implemented so far */
59 	if(class != Cin)
60 		return 0;
61 
62 	dp = dnlookup(name, class, 1);
63 
64 	/* first try the cache */
65 	rp = rrlookup(dp, type);
66 	if(rp)
67 		return rp;
68 
69 	/* in-addr.arpa queries (and all) are special */
70 	if(tsame(type, Tptr)){
71 		rp = dbinaddr(dp);
72 		if(rp)
73 			return rp;
74 	}
75 
76 	/*
77 	 *  Quick grab, see if it's a 'relative to my domain' request.
78 	 *  I'm not sure this is a good idea but our x-terminals want it.
79 	 */
80 	if(strchr(name, '.') == 0){
81 		rp = dblookup(name, class, type, 1);
82 		if(rp)
83 			return rp;
84 	}
85 
86 	/*
87  	 *  walk up the domain name looking for
88 	 *  a name server for the domain.
89 	 */
90 	for(cp = name; cp; cp = walkup(cp)){
91 		/* look for ns in cache and database */
92 		nsdp = dnlookup(cp, class, 0);
93 		nsrp = 0;
94 		if(nsdp)
95 			nsrp = rrlookup(nsdp, Tns);
96 		if(nsrp == 0)
97 			nsrp = dblookup(cp, class, Tns, 0);
98 
99 		if(nsrp){
100 			/* local domains resolved from this db */
101 			if(nsrp->local){
102 				if(nsrp->db)	/* free database rr's */
103 					rrfreelist(nsrp);
104 				return dblookup(name, class, type, 1);
105 			}
106 
107 			/* try the name servers */
108 			if(netquery(dp, type, nsrp, req)){
109 				/* we got an answer */
110 				if(nsrp->db)	/* free database rr's */
111 					rrfreelist(nsrp);
112 				return rrlookup(dp, type);
113 			}
114 		}
115 	}
116 
117 	/* noone answered */
118 	return 0;
119 }
120 
121 /*
122  *  walk a domain name one element to the right.  return a pointer to that element.
123  *  in other words, return a pointer to the parent domain name.
124  */
125 char*
126 walkup(char *name)
127 {
128 	char *cp;
129 
130 	cp = strchr(name, '.');
131 	if(cp)
132 		return cp+1;
133 	else if(*name)
134 		return "";
135 	else
136 		return 0;
137 }
138 
139 /*
140  *  Get a udpport for requests and replies.  Put the port
141  *  into "headers" mode.
142  */
143 static char *hmsg = "headers";
144 
145 static int
146 udpport(void)
147 {
148 	int fd, ctl;
149 
150 	/* get a udp port */
151 	fd = dial("udp!0.0.0.0!0", 0, 0, &ctl);
152 	if(fd < 0){
153 		warning("can't get udp port");
154 		return -1;
155 	}
156 
157 	/* turn on header style interface */
158 	if(write(ctl, hmsg, strlen(hmsg)) , 0){
159 		warning(hmsg);
160 		return -1;
161 	}
162 
163 	close(ctl);
164 	return fd;
165 }
166 
167 static int
168 mkreq(DN *dp, int type, uchar *buf, int reqno)
169 {
170 	DNSmsg m;
171 	int len;
172 
173 	/* stuff port number into output buffer */
174 	buf[4] = 0;
175 	buf[5] = 53;
176 
177 	/* make request and convert it to output format */
178 	memset(&m, 0, sizeof(m));
179 	m.flags = Frecurse;
180 	m.id = reqno;
181 	m.qd = rralloc(type);
182 	m.qd->owner = dp;
183 	m.qd->type = type;
184 	len = convDNS2M(&m, &buf[Udphdrsize], Maxudp);
185 	if(len < 0)
186 		fatal("can't convert");
187 	return len;
188 }
189 
190 /*
191  *  read replies to a request.  ignore any of the wrong type.
192  */
193 static int
194 readreq(int fd, DN *dp, int type, int req, uchar *ibuf, DNSmsg *mp)
195 {
196 	char *err;
197 	int len;
198 
199 	for(;;){
200 		len = read(fd, ibuf, Udphdrsize+Maxudp);
201 		len -= Udphdrsize;
202 		if(len < 0)
203 			return -1;	/* timed out */
204 
205 		/* convert into internal format  */
206 		err = convM2DNS(&ibuf[Udphdrsize], len, mp);
207 		if(err){
208 			syslog(0, "dns", "input err %s", err);
209 			continue;
210 		}
211 		if(debug){
212 			if(mp->qd)
213 				syslog(0, "dns", "rcvd %I qd %s", ibuf, mp->qd->owner->name);
214 			if(mp->an)
215 				syslog(0, "dns", "rcvd %I an %R", ibuf, mp->an);
216 			if(mp->ns)
217 				syslog(0, "dns", "rcvd %I ns %R", ibuf, mp->ns);
218 			if(mp->ar)
219 				syslog(0, "dns", "rcvd %I ar %R", ibuf, mp->ar);
220 		}
221 
222 		/* answering the right question? */
223 		if(mp->id != req){
224 			syslog(0, "dns", "id %d instead of %d", mp->id, req);
225 			continue;
226 		}
227 		if(mp->qd == 0){
228 			syslog(0, "dns", "no question RR");
229 			continue;
230 		}
231 		if(mp->qd->owner != dp){
232 			syslog(0, "dns", "owner %s instead of %s", mp->qd->owner->name,
233 				dp->name);
234 			continue;
235 		}
236 		if(mp->qd->type != type){
237 			syslog(0, "dns", "type %d instead of %d", mp->qd->type, type);
238 			continue;
239 		}
240 		return 0;
241 	}
242 
243 	return 0;	/* never reached */
244 }
245 
246 /*
247  *  query name servers.  If the name server returns a pointer to another
248  *  name server, recurse.
249  */
250 static void
251 ding(void *x, char *msg)
252 {
253 	USED(x);
254 	if(strcmp(msg, "alarm") == 0)
255 		noted(NCONT);
256 	else
257 		noted(NDFLT);
258 }
259 static int
260 netquery(DN *dp, int type, RR *nsrp, Request *reqp)
261 {
262 	int fd, i, j, len;
263 	ulong req;
264 	RR *rp;
265 	Dest *p, *l;
266 	DN *ndp;
267 	Dest dest[Maxdest];
268 	DNSmsg m;
269 	uchar obuf[Maxudp+Udphdrsize];
270 	uchar ibuf[Maxudp+Udphdrsize];
271 
272 	slave(reqp);
273 
274 	/* get the addresses */
275 	l = dest;
276 	for(; nsrp && nsrp->type == Tns; nsrp = nsrp->next){
277 		rp = rrlookup(nsrp->host, Ta);
278 		if(rp == 0)
279 			rp = dblookup(nsrp->host->name, Cin, Ta, 0);
280 		for(; rp && rp->type == Ta; rp = rp->next){
281 			if(l >= &dest[Maxdest])
282 				break;
283 			parseip(l->a, rp->ip->name);
284 			l->nx = 0;
285 			l->s = nsrp->host;
286 			l++;
287 		}
288 	}
289 
290 	/* pack request into a message */
291 	req = reqno++;
292 	len = mkreq(dp, type, obuf, req);
293 
294 	/*
295 	 *  transmit requests and wait for answers.
296 	 *  at most Maxtrans attempts to each address.
297 	 *  each cycle send one more message than the previous.
298 	 */
299 	fd = udpport();
300 	if(fd < 0)
301 		return 0;
302 	notify(ding);
303 	for(i = 1;; i++){
304 		/* send to i destinations */
305 		p = dest;
306 		for(j = 0; j < i; j++){
307 			/* skip destinations we've finished with */
308 			for(; p < l; p++)
309 				if(p->nx < Maxtrans)
310 					break;
311 			if(p >= l)
312 				break;
313 
314 			p->nx++;
315 			memmove(obuf, p->a, sizeof(p->a));
316 			if(debug)
317 				syslog(0, "dns", "sending to %I", obuf);
318 			if(write(fd, obuf, len + Udphdrsize) < 0)
319 				warning("sending udp msg %r");
320 			p++;
321 		}
322 		if(j == 0)
323 			break;		/* no destinations left */
324 
325 		/* wait a fixed time for replies */
326 		alarm(1000);
327 		for(;;){
328 			if(readreq(fd, dp, type, req, ibuf, &m) < 0)
329 				break;		/* timed out */
330 
331 			/* remove all addrs of responding server from list */
332 			for(p = dest; p < l; p++)
333 				if(memcmp(p->a, ibuf, sizeof(p->a)) == 0){
334 					ndp = p->s;
335 					for(p = dest; p < l; p++)
336 						if(p->s == ndp)
337 							p->nx = Maxtrans;
338 					break;
339 				}
340 
341 			/* incorporate answers */
342 			if(m.an)
343 				rrattach(m.an, m.flags & Fauth);
344 			if(m.ar)
345 				rrattach(m.ar, 0);
346 
347 			/*
348 			 *  Any reply from an authoritative server,
349 			 *  or a positive reply terminates the search
350 			 */
351 			if(m.an || (m.flags & Fauth)){
352 				alarm(0);
353 				close(fd);
354 				return 1;
355 			}
356 
357 			/*
358 			 *  if we've been given better name servers
359 			 *  recurse
360 			 */
361 			if(m.ns){
362 				alarm(0);
363 				close(fd);
364 				ndp = m.ns->owner;
365 				rrattach(m.ns, 0);
366 				return netquery(dp, type, rrlookup(ndp, Tns), reqp);
367 			}
368 		}
369 		alarm(0);
370 	}
371 	alarm(0);
372 	close(fd);
373 	return 0;
374 }
375