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