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