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