xref: /netbsd-src/usr.sbin/rpc.bootparamd/bootparamd.c (revision 4472dbe5e3bd91ef2540bada7a7ca7384627ff9b)
1 /*	$NetBSD: bootparamd.c,v 1.28 2000/06/06 02:18:05 cjs Exp $	*/
2 
3 /*
4  * This code is not copyright, and is placed in the public domain.
5  * Feel free to use and modify. Please send modifications and/or
6  * suggestions + bug fixes to Klas Heggemann <klas@nada.kth.se>
7  *
8  * Various small changes by Theo de Raadt <deraadt@fsa.ca>
9  * Parser rewritten (adding YP support) by Roland McGrath <roland@frob.com>
10  */
11 
12 #include <sys/cdefs.h>
13 #ifndef lint
14 __RCSID("$NetBSD: bootparamd.c,v 1.28 2000/06/06 02:18:05 cjs Exp $");
15 #endif
16 
17 #include <sys/types.h>
18 #include <sys/ioctl.h>
19 #include <sys/stat.h>
20 #include <sys/socket.h>
21 
22 #include <ctype.h>
23 #include <errno.h>
24 #include <err.h>
25 #include <netdb.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <syslog.h>
30 #include <unistd.h>
31 #include <util.h>
32 #include <ifaddrs.h>
33 
34 #include <net/if.h>
35 
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 
39 #include <rpc/rpc.h>
40 #include <rpc/pmap_clnt.h>
41 #include <rpcsvc/bootparam_prot.h>
42 #include <rpcsvc/ypclnt.h>
43 
44 #include "pathnames.h"
45 
46 #define MAXLEN 800
47 
48 static char hostname[MAX_MACHINE_NAME];
49 static char askname[MAX_MACHINE_NAME];
50 static char domain_name[MAX_MACHINE_NAME];
51 
52 extern void bootparamprog_1 __P((struct svc_req *, SVCXPRT *));
53 
54 int	_rpcsvcdirty = 0;
55 int	_rpcpmstart = 0;
56 int     debug = 0;
57 int     dolog = 0;
58 struct in_addr route_addr;
59 struct sockaddr_in my_addr;
60 extern char *__progname;
61 char   *bootpfile = _PATH_BOOTPARAMS;
62 
63 int	main __P((int, char *[]));
64 int	lookup_bootparam __P((char *, char *, char *, char **, char **));
65 void	usage __P((void));
66 static int get_localaddr __P((const char *, struct sockaddr_in *));
67 
68 
69 /*
70  * ever familiar
71  */
72 int
73 main(argc, argv)
74 	int     argc;
75 	char  *argv[];
76 {
77 	SVCXPRT *transp;
78 	struct hostent *he;
79 	struct stat buf;
80 	int    c;
81 
82 	while ((c = getopt(argc, argv, "dsr:f:")) != -1)
83 		switch (c) {
84 		case 'd':
85 			debug = 1;
86 			break;
87 		case 'r':
88 			if (isdigit(*optarg)) {
89 				if (inet_aton(optarg, &route_addr) != 0)
90 					break;
91 			}
92 			he = gethostbyname(optarg);
93 			if (he == 0) {
94 				warnx("no such host: %s", optarg);
95 				usage();
96 			}
97 			memmove(&route_addr.s_addr, he->h_addr, he->h_length);
98 			break;
99 		case 'f':
100 			bootpfile = optarg;
101 			break;
102 		case 's':
103 			dolog = 1;
104 #ifndef LOG_DAEMON
105 			openlog(__progname, 0, 0);
106 #else
107 			openlog(__progname, 0, LOG_DAEMON);
108 			setlogmask(LOG_UPTO(LOG_NOTICE));
109 #endif
110 			break;
111 		default:
112 			usage();
113 		}
114 
115 	if (stat(bootpfile, &buf))
116 		err(1, "%s", bootpfile);
117 
118 	if (route_addr.s_addr == 0) {
119 		if (get_localaddr(NULL, &my_addr) != 0)
120 			errx(1, "router address not found");
121 		route_addr.s_addr = my_addr.sin_addr.s_addr;
122 	}
123 	if (!debug) {
124 		if (daemon(0, 0))
125 			err(1, "can't detach from terminal");
126 	}
127 	pidfile(NULL);
128 
129 	(void) pmap_unset(BOOTPARAMPROG, BOOTPARAMVERS);
130 
131 	transp = svcudp_create(RPC_ANYSOCK);
132 	if (transp == NULL)
133 		errx(1, "can't create udp service");
134 
135 	if (!svc_register(transp, BOOTPARAMPROG, BOOTPARAMVERS, bootparamprog_1,
136 	    IPPROTO_UDP))
137 		errx(1, "unable to register BOOTPARAMPROG version %u, udp",
138 		    BOOTPARAMVERS);
139 
140 	svc_run();
141 	errx(1, "svc_run returned");
142 }
143 
144 bp_whoami_res *
145 bootparamproc_whoami_1_svc(whoami, rqstp)
146 	bp_whoami_arg *whoami;
147 	struct svc_req *rqstp;
148 {
149 	static bp_whoami_res res;
150 	struct hostent *he;
151 	struct in_addr haddr;
152 	int e;
153 
154 	if (debug)
155 		warnx("whoami got question for %d.%d.%d.%d",
156 		    255 & whoami->client_address.bp_address_u.ip_addr.net,
157 		    255 & whoami->client_address.bp_address_u.ip_addr.host,
158 		    255 & whoami->client_address.bp_address_u.ip_addr.lh,
159 		    255 & whoami->client_address.bp_address_u.ip_addr.impno);
160 	if (dolog)
161 		syslog(LOG_NOTICE, "whoami got question for %d.%d.%d.%d",
162 		    255 & whoami->client_address.bp_address_u.ip_addr.net,
163 		    255 & whoami->client_address.bp_address_u.ip_addr.host,
164 		    255 & whoami->client_address.bp_address_u.ip_addr.lh,
165 		    255 & whoami->client_address.bp_address_u.ip_addr.impno);
166 
167 	memmove((char *) &haddr,
168 	    (char *) &whoami->client_address.bp_address_u.ip_addr,
169 	    sizeof(haddr));
170 	he = gethostbyaddr((char *) &haddr, sizeof(haddr), AF_INET);
171 	if (he) {
172 		strncpy(askname, he->h_name, sizeof(askname));
173 		askname[sizeof(askname)-1] = 0;
174 	} else {
175 		strncpy(askname, inet_ntoa(haddr), sizeof(askname));
176 		askname[sizeof(askname)-1] = 0;
177 	}
178 
179 	if (debug)
180 		warnx("This is host %s", askname);
181 	if (dolog)
182 		syslog(LOG_NOTICE, "This is host %s", askname);
183 
184 	if ((e = lookup_bootparam(askname, hostname, NULL, NULL, NULL)) == 0) {
185 		res.client_name = hostname;
186 		getdomainname(domain_name, MAX_MACHINE_NAME);
187 		res.domain_name = domain_name;
188 
189 		if (res.router_address.address_type != IP_ADDR_TYPE) {
190 			res.router_address.address_type = IP_ADDR_TYPE;
191 			memmove(&res.router_address.bp_address_u.ip_addr,
192 			    &route_addr.s_addr,4);
193 		}
194 		if (debug)
195 			warnx("Returning %s   %s    %d.%d.%d.%d",
196 			    res.client_name, res.domain_name,
197 			    255 & res.router_address.bp_address_u.ip_addr.net,
198 			    255 & res.router_address.bp_address_u.ip_addr.host,
199 			    255 & res.router_address.bp_address_u.ip_addr.lh,
200 			    255 &res.router_address.bp_address_u.ip_addr.impno);
201 		if (dolog)
202 			syslog(LOG_NOTICE, "Returning %s   %s    %d.%d.%d.%d",
203 			    res.client_name, res.domain_name,
204 			    255 & res.router_address.bp_address_u.ip_addr.net,
205 			    255 & res.router_address.bp_address_u.ip_addr.host,
206 			    255 & res.router_address.bp_address_u.ip_addr.lh,
207 			    255 &res.router_address.bp_address_u.ip_addr.impno);
208 
209 		return (&res);
210 	}
211 	errno = e;
212 	if (debug)
213 		warn("whoami failed");
214 	if (dolog)
215 		syslog(LOG_NOTICE, "whoami failed %m");
216 	return (NULL);
217 }
218 
219 
220 bp_getfile_res *
221 bootparamproc_getfile_1_svc(getfile, rqstp)
222 	bp_getfile_arg *getfile;
223 	struct svc_req *rqstp;
224 {
225 	static bp_getfile_res res;
226 	struct hostent *he;
227 	int     err;
228 
229 	if (debug)
230 		warnx("getfile got question for \"%s\" and file \"%s\"",
231 		    getfile->client_name, getfile->file_id);
232 
233 	if (dolog)
234 		syslog(LOG_NOTICE,
235 		    "getfile got question for \"%s\" and file \"%s\"",
236 		    getfile->client_name, getfile->file_id);
237 
238 	he = NULL;
239 	he = gethostbyname(getfile->client_name);
240 	if (!he)
241 		goto failed;
242 
243 	strncpy(askname, he->h_name, sizeof(askname));
244 	askname[sizeof(askname)-1] = 0;
245 	err = lookup_bootparam(askname, NULL, getfile->file_id,
246 	    &res.server_name, &res.server_path);
247 	if (err == 0) {
248 		he = gethostbyname(res.server_name);
249 		if (!he)
250 			goto failed;
251 		memmove(&res.server_address.bp_address_u.ip_addr,
252 		    he->h_addr, 4);
253 		res.server_address.address_type = IP_ADDR_TYPE;
254 	} else if (err == ENOENT && !strcmp(getfile->file_id, "dump")) {
255 		/* Special for dump, answer with null strings. */
256 		res.server_name[0] = '\0';
257 		res.server_path[0] = '\0';
258 		memset(&res.server_address.bp_address_u.ip_addr, 0, 4);
259 	} else {
260 failed:
261 		if (debug)
262 			warnx("getfile failed for %s",
263 			    getfile->client_name);
264 		if (dolog)
265 			syslog(LOG_NOTICE,
266 			    "getfile failed for %s", getfile->client_name);
267 		return (NULL);
268 	}
269 
270 	if (debug)
271 		warnx(
272 		    "returning server:%s path:%s address: %d.%d.%d.%d",
273 		    res.server_name, res.server_path,
274 		    255 & res.server_address.bp_address_u.ip_addr.net,
275 		    255 & res.server_address.bp_address_u.ip_addr.host,
276 		    255 & res.server_address.bp_address_u.ip_addr.lh,
277 		    255 & res.server_address.bp_address_u.ip_addr.impno);
278 	if (dolog)
279 		syslog(LOG_NOTICE,
280 		    "returning server:%s path:%s address: %d.%d.%d.%d",
281 		    res.server_name, res.server_path,
282 		    255 & res.server_address.bp_address_u.ip_addr.net,
283 		    255 & res.server_address.bp_address_u.ip_addr.host,
284 		    255 & res.server_address.bp_address_u.ip_addr.lh,
285 		    255 & res.server_address.bp_address_u.ip_addr.impno);
286 	return (&res);
287 }
288 
289 
290 int
291 lookup_bootparam(client, client_canonical, id, server, path)
292 	char	*client;
293 	char	*client_canonical;
294 	char	*id;
295 	char	**server;
296 	char	**path;
297 {
298 	FILE   *f = fopen(bootpfile, "r");
299 #ifdef YP
300 	static char *ypbuf = NULL;
301 	static int ypbuflen = 0;
302 #endif
303 	static char buf[BUFSIZ];
304 	char   *bp, *word = NULL;
305 	size_t  idlen = id == NULL ? 0 : strlen(id);
306 	int     contin = 0;
307 	int     found = 0;
308 
309 	if (f == NULL)
310 		return EINVAL;	/* ? */
311 
312 	while (fgets(buf, sizeof buf, f)) {
313 		int     wascontin = contin;
314 		contin = buf[strlen(buf) - 2] == '\\';
315 		bp = buf + strspn(buf, " \t\n");
316 
317 		switch (wascontin) {
318 		case -1:
319 			/* Continuation of uninteresting line */
320 			contin *= -1;
321 			continue;
322 		case 0:
323 			/* New line */
324 			contin *= -1;
325 			if (*bp == '#')
326 				continue;
327 			if ((word = strsep(&bp, " \t\n")) == NULL)
328 				continue;
329 #ifdef YP
330 			/* A + in the file means try YP now */
331 			if (!strcmp(word, "+")) {
332 				char   *ypdom;
333 
334 				if (yp_get_default_domain(&ypdom) ||
335 				    yp_match(ypdom, "bootparams", client,
336 					strlen(client), &ypbuf, &ypbuflen))
337 					continue;
338 				bp = ypbuf;
339 				word = client;
340 				contin *= -1;
341 				break;
342 			}
343 #endif
344 			if (debug)
345 				warnx("match %s with %s", word, client);
346 			/* See if this line's client is the one we are
347 			 * looking for */
348 			if (strcasecmp(word, client) != 0) {
349 				/*
350 				 * If it didn't match, try getting the
351 				 * canonical host name of the client
352 				 * on this line and comparing that to
353 				 * the client we are looking for
354 				 */
355 				struct hostent *hp = gethostbyname(word);
356 				if (hp == NULL ) {
357 					if (debug)
358 						warnx(
359 					    "Unknown bootparams host %s", word);
360 					if (dolog)
361 						syslog(LOG_NOTICE,
362 					    "Unknown bootparams host %s", word);
363 					continue;
364 				}
365 				if (strcasecmp(hp->h_name, client))
366 					continue;
367 			}
368 			contin *= -1;
369 			break;
370 		case 1:
371 			/* Continued line we want to parse below */
372 			break;
373 		}
374 
375 		if (client_canonical)
376 			strncpy(client_canonical, word, MAX_MACHINE_NAME);
377 
378 		/* We have found a line for CLIENT */
379 		if (id == NULL) {
380 			(void) fclose(f);
381 			return 0;
382 		}
383 
384 		/* Look for a value for the parameter named by ID */
385 		while ((word = strsep(&bp, " \t\n")) != NULL) {
386 			if (!strncmp(word, id, idlen) && word[idlen] == '=') {
387 				/* We have found the entry we want */
388 				*server = &word[idlen + 1];
389 				*path = strchr(*server, ':');
390 				if (*path == NULL)
391 					/* Malformed entry */
392 					continue;
393 				*(*path)++ = '\0';
394 				(void) fclose(f);
395 				return 0;
396 			}
397 		}
398 
399 		found = 1;
400 	}
401 
402 	(void) fclose(f);
403 	return found ? ENOENT : EPERM;
404 }
405 
406 void
407 usage()
408 {
409 	fprintf(stderr,
410 	    "usage: %s [-d] [-s] [-r router] [-f bootparmsfile]\n", __progname);
411 	exit(1);
412 }
413 
414 static int
415 get_localaddr(ifname, sin)
416 	const char *ifname;
417 	struct sockaddr_in *sin;
418 {
419 	struct ifaddrs *ifap, *ifa;
420 
421 	if (getifaddrs(&ifap) != 0)
422 		return -1;
423 
424 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
425 		if (ifname && strcmp(ifname, ifa->ifa_name) != 0)
426 			continue;
427 		if (ifa->ifa_addr->sa_family != AF_INET)
428 			continue;
429 		if (ifa->ifa_addr->sa_len != sizeof(*sin))
430 			continue;
431 
432 		/* no loopback please */
433 #ifdef IFF_LOOPBACK
434 		if (ifa->ifa_flags & IFF_LOOPBACK)
435 			continue;
436 #else
437 		if (strncmp(ifa->ifa_name, "lo", 2) == 0 &&
438 		    (isdigit(ifa->ifa_name[2]) || ifa->ifa_name[2] == '\0'))
439 			continue;
440 #endif
441 
442 		/* XXX more sanity checks? */
443 
444 		/* candidate found */
445 		memcpy(sin, ifa->ifa_addr, ifa->ifa_addr->sa_len);
446 		freeifaddrs(ifap);
447 		return 0;
448 	}
449 
450 	/* no candidate */
451 	freeifaddrs(ifap);
452 	return -1;
453 }
454