Home | History | Annotate | Line # | Download | only in rpc.bootparamd
bootparamd.c revision 1.27
      1 /*	$NetBSD: bootparamd.c,v 1.27 2000/06/06 01:40:19 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 (at) nada.kth.se>
      7  *
      8  * Various small changes by Theo de Raadt <deraadt (at) fsa.ca>
      9  * Parser rewritten (adding YP support) by Roland McGrath <roland (at) frob.com>
     10  */
     11 
     12 #include <sys/cdefs.h>
     13 #ifndef lint
     14 __RCSID("$NetBSD: bootparamd.c,v 1.27 2000/06/06 01:40:19 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 %lu, 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