xref: /plan9/sys/src/cmd/ndb/dnserver.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include "dns.h"
5 
6 int	udpannounce(void);
7 static RR*	getipaddr(DN*);
8 static void	recurse(DNSmsg*, Request*);
9 static void	local(DNSmsg*);
10 static void	reply(int, uchar*, DNSmsg*);
11 static void	hint(RR**, RR*);
12 
13 extern char *logfile;
14 
15 /*
16  *  a process to act as a dns server for outside reqeusts
17  */
18 void
19 dnserver(void)
20 {
21 	int fd, len;
22 	char *err;
23 	RR *tp;
24 	Request req;
25 	DNSmsg reqmsg;
26 	DNSmsg repmsg;
27 	uchar buf[Udphdrsize + Maxudp];
28 	char tname[32];
29 
30 	/* fork sharing text, data, and bss with parent */
31 	switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
32 	case -1:
33 		break;
34 	case 0:
35 		break;
36 	default:
37 		return;
38 	}
39 
40 	while((fd = udpannounce()) < 0)
41 		sleep(5000);
42 	setjmp(req.mret);
43 	req.isslave = 0;
44 
45 	/* loop on requests */
46 	for(;;){
47 		memset(&repmsg, 0, sizeof(repmsg));
48 		len = read(fd, buf, sizeof(buf));
49 		err = convM2DNS(&buf[Udphdrsize], len, &reqmsg);
50 		if(err){
51 			syslog(0, logfile, "server: input error: %s from %I", err, buf);
52 			continue;
53 		}
54 		if(reqmsg.qdcount < 1){
55 			syslog(0, logfile, "server: no questions from %I", buf);
56 		} else if(reqmsg.flags & Fresp){
57 			syslog(0, logfile, "server: reply not request from %I", buf);
58 		} else if(reqmsg.qd->owner->class != Cin){
59 			syslog(0, logfile, "server: class %d from %I", reqmsg.qd->owner->class,
60 				buf);
61 			memset(&repmsg, 0, sizeof(repmsg));
62 			repmsg.id = reqmsg.id;
63 			repmsg.flags = Runimplimented | Fresp | Fcanrec | Oquery;
64 			repmsg.qd = reqmsg.qd;
65 			reply(fd, buf, &repmsg);
66 		} else if((reqmsg.flags & Omask) != Oquery){
67 			syslog(0, logfile, "server: op %d from %I", reqmsg.flags & Omask, buf);
68 		} else {
69 			/* loop through each question */
70 			while(reqmsg.qd){
71 				if(debug)
72 					syslog(0, logfile, "serve %s %s",
73 						reqmsg.qd->owner->name,
74 						rrname(reqmsg.qd->type, tname));
75 				memset(&repmsg, 0, sizeof(repmsg));
76 				repmsg.id = reqmsg.id;
77 				repmsg.flags = Fresp | Fcanrec | Oquery;
78 				repmsg.qd = reqmsg.qd;
79 				reqmsg.qd = reqmsg.qd->next;
80 				repmsg.qd->next = 0;
81 
82 				/*
83 				 *  get the answer if we can
84 				 */
85 				if(reqmsg.flags & Frecurse)
86 					recurse(&repmsg, &req);
87 				else
88 					local(&repmsg);
89 
90 				/*
91 				 *  add ip addresses as hints
92 				 */
93 				for(tp = repmsg.ns; tp; tp = tp->next)
94 					hint(&repmsg.ar, tp);
95 				for(tp = repmsg.an; tp; tp = tp->next)
96 					hint(&repmsg.ar, tp);
97 
98 				reply(fd, buf, &repmsg);
99 
100 				rrfreelist(repmsg.qd);
101 				rrfreelist(repmsg.an);
102 				rrfreelist(repmsg.ns);
103 				rrfreelist(repmsg.ar);
104 			}
105 		}
106 		rrfreelist(reqmsg.qd);
107 		rrfreelist(reqmsg.an);
108 		rrfreelist(reqmsg.ns);
109 		rrfreelist(reqmsg.ar);
110 
111 		if(req.isslave)
112 			_exits(0);
113 	}
114 }
115 
116 /*
117  *  satisfy a recursive request.  dnlookup will handle cnames.
118  */
119 static void
120 recurse(DNSmsg *mp, Request *req)
121 {
122 	int type;
123 	char *name;
124 	RR *cname;
125 
126 	name = mp->qd->owner->name;
127 	type = mp->qd->type;
128 	cname = 0;
129 	rrcat(&mp->an, dnresolve(name, Cin, type, req, &cname), type);
130 	if(cname)
131 		rrcat(&mp->an, cname, Tcname);
132 	if(mp->an && mp->an->auth)
133 		mp->flags |= Fauth;
134 }
135 
136 
137 /*
138  *  satisfy a request with local info only
139  */
140 static void
141 local(DNSmsg *mp)
142 {
143 	DN *dp, *nsdp;
144 	RR *nsrp;
145 	char *cp;
146 	int type;
147 
148 	dp = mp->qd->owner;
149 	type = mp->qd->type;
150 	mp->flags |= Fauth;
151 
152 	/* in-addr.arpa queries are special */
153 	if(type == Tptr){
154 		rrcat(&mp->an, dbinaddr(dp), type);
155 		if(mp->an)
156 			return;
157 	}
158 
159 	/*
160 	 *  Quick grab, see if it's a 'relative to my domain' request.
161 	 *  I'm not sure this is a good idea but our x-terminals want it.
162 	 */
163 	if(strchr(dp->name, '.') == 0){
164 		nsrp = dblookup(dp->name, dp->class, type, 1);
165 		if(nsrp){
166 			rrcat(&mp->an, nsrp, type);
167 			return;
168 		}
169 	}
170 
171 	/*
172  	 *  walk up the domain name looking for
173 	 *  a name server for the domain.
174 	 */
175 	for(cp = dp->name; /* mp->an==0 && */ cp; cp = walkup(cp)){
176 		/* look for ns in cache and database */
177 		nsdp = dnlookup(cp, dp->class, 0);
178 		nsrp = 0;
179 		if(nsdp)
180 			nsrp = rrlookup(nsdp, Tns);
181 		if(nsrp == 0)
182 			nsrp = dblookup(cp, dp->class, Tns, 0);
183 		if(nsrp == 0)
184 			continue;
185 
186 		if(nsrp->local){
187 			/* local domains resolved from this db */
188 			if(nsrp->db)	/* free database rr's */
189 				rrfreelist(nsrp);
190 			rrcat(&mp->an, dblookup(dp->name, Cin, type, 1), type);
191 		} else {
192 			/* just return the name of a name server to use */
193 			rrcat(&mp->ns, nsrp, Tns);
194 		}
195 
196 		break;
197 	}
198 }
199 
200 static void
201 reply(int fd, uchar *buf, DNSmsg *rep)
202 {
203 	int len;
204 	char tname[32];
205 
206 	if(debug)
207 		syslog(0, logfile, "reply (%I/%d) %s %s an %R ns %R ar %R",
208 			buf, ((buf[4])<<8)+buf[5], rep->qd->owner->name,
209 			rrname(rep->qd->type, tname), rep->an, rep->ns, rep->ar);
210 
211 	len = convDNS2M(rep, &buf[Udphdrsize], Maxudp);
212 	if(len <= 0)
213 		fatal("dnserver: converting reply");
214 	len += Udphdrsize;
215 	if(write(fd, buf, len) != len)
216 		fatal("dnserver: sending reply");
217 }
218 
219 static void
220 hint(RR **last, RR *rp)
221 {
222 	switch(rp->type){
223 	case Tns:
224 	case Tmx:
225 	case Tmb:
226 	case Tmf:
227 	case Tmd:
228 		rrcat(last, dblookup(rp->host->name, Cin, Ta, 0), Ta);
229 		break;
230 	}
231 }
232 
233 /*
234  *  get ip addresses without looking off machine
235  */
236 static RR*
237 getipaddr(DN *dp)
238 {
239 	RR *rp;
240 
241 	/* first try the cache */
242 	rp = rrlookup(dp, Ta);
243 	if(rp)
244 		return rp;
245 
246 	/* then try on disk db */
247 	return dblookup(dp->name, dp->class, Ta, 0);
248 }
249 
250 /*
251  *  announce on udp port and set message style interface
252  */
253 static char *hmsg = "headers";
254 
255 int
256 udpannounce(void)
257 {
258 	int data, ctl;
259 	char dir[64];
260 	char datafile[64+6];
261 
262 	/* get a udp port */
263 	ctl = announce("udp!*!dns", dir);
264 	if(ctl < 0){
265 		warning("can't announce on udp port");
266 		return -1;
267 	}
268 	snprint(datafile, sizeof(datafile), "%s/data", dir);
269 
270 	/* turn on header style interface */
271 	if(write(ctl, hmsg, strlen(hmsg)) , 0)
272 		fatal(hmsg);
273 	data = open(datafile, ORDWR);
274 	if(data < 0){
275 		close(ctl);
276 		warning("can't announce on udp port");
277 		return -1;
278 	}
279 
280 	close(ctl);
281 	return data;
282 }
283