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