1 /* $NetBSD: bootparamd.c,v 1.44 2004/10/30 15:23:30 dsl 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.44 2004/10/30 15:23:30 dsl 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((unsigned char)*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 (void)strlcpy(askname, he->h_name, sizeof(askname)); 185 } else { 186 (void)strlcpy(askname, inet_ntoa(haddr), sizeof(askname)); 187 } 188 189 if (debug) 190 warnx("This is host %s", askname); 191 if (dolog) 192 syslog(LOG_NOTICE, "This is host %s", askname); 193 194 if ((e = lookup_bootparam(askname, hostname, NULL, NULL, NULL)) == 0) { 195 res.client_name = hostname; 196 getdomainname(domain_name, MAX_MACHINE_NAME); 197 res.domain_name = domain_name; 198 199 if (res.router_address.address_type != IP_ADDR_TYPE) { 200 res.router_address.address_type = IP_ADDR_TYPE; 201 memmove(&res.router_address.bp_address_u.ip_addr, 202 &route_addr.s_addr,4); 203 } 204 if (debug) 205 warnx("Returning %s %s %d.%d.%d.%d", 206 res.client_name, res.domain_name, 207 255 & res.router_address.bp_address_u.ip_addr.net, 208 255 & res.router_address.bp_address_u.ip_addr.host, 209 255 & res.router_address.bp_address_u.ip_addr.lh, 210 255 &res.router_address.bp_address_u.ip_addr.impno); 211 if (dolog) 212 syslog(LOG_NOTICE, "Returning %s %s %d.%d.%d.%d", 213 res.client_name, res.domain_name, 214 255 & res.router_address.bp_address_u.ip_addr.net, 215 255 & res.router_address.bp_address_u.ip_addr.host, 216 255 & res.router_address.bp_address_u.ip_addr.lh, 217 255 &res.router_address.bp_address_u.ip_addr.impno); 218 219 return (&res); 220 } 221 errno = e; 222 if (debug) 223 warn("whoami failed"); 224 if (dolog) 225 syslog(LOG_NOTICE, "whoami failed %m"); 226 return (NULL); 227 } 228 229 230 bp_getfile_res * 231 bootparamproc_getfile_1_svc(getfile, rqstp) 232 bp_getfile_arg *getfile; 233 struct svc_req *rqstp; 234 { 235 static bp_getfile_res res; 236 struct hostent *he; 237 int err; 238 239 if (debug) 240 warnx("getfile got question for \"%s\" and file \"%s\"", 241 getfile->client_name, getfile->file_id); 242 243 if (dolog) 244 syslog(LOG_NOTICE, 245 "getfile got question for \"%s\" and file \"%s\"", 246 getfile->client_name, getfile->file_id); 247 248 he = NULL; 249 he = gethostbyname(getfile->client_name); 250 if (!he) { 251 if (debug) 252 warnx("getfile can't resolve client %s", 253 getfile->client_name); 254 if (dolog) 255 syslog(LOG_NOTICE, "getfile can't resolve client %s", 256 getfile->client_name); 257 return (NULL); 258 } 259 260 (void)strlcpy(askname, he->h_name, sizeof(askname)); 261 err = lookup_bootparam(askname, NULL, getfile->file_id, 262 &res.server_name, &res.server_path); 263 if (err == 0) { 264 he = gethostbyname(res.server_name); 265 if (!he) { 266 if (debug) 267 warnx("getfile can't resolve server %s for %s", 268 res.server_name, getfile->client_name); 269 if (dolog) 270 syslog(LOG_NOTICE, 271 "getfile can't resolve server %s for %s", 272 res.server_name, getfile->client_name); 273 return (NULL); 274 275 } 276 memmove(&res.server_address.bp_address_u.ip_addr, 277 he->h_addr, 4); 278 res.server_address.address_type = IP_ADDR_TYPE; 279 } else if (err == ENOENT && !strcmp(getfile->file_id, "dump")) { 280 /* Special for dump, answer with null strings. */ 281 res.server_name[0] = '\0'; 282 res.server_path[0] = '\0'; 283 memset(&res.server_address.bp_address_u.ip_addr, 0, 4); 284 } else { 285 if (debug) 286 warnx("getfile lookup failed for %s", 287 getfile->client_name); 288 if (dolog) 289 syslog(LOG_NOTICE, 290 "getfile lookup failed for %s", 291 getfile->client_name); 292 return (NULL); 293 } 294 295 if (debug) 296 warnx( 297 "returning server:%s path:%s address: %d.%d.%d.%d", 298 res.server_name, res.server_path, 299 255 & res.server_address.bp_address_u.ip_addr.net, 300 255 & res.server_address.bp_address_u.ip_addr.host, 301 255 & res.server_address.bp_address_u.ip_addr.lh, 302 255 & res.server_address.bp_address_u.ip_addr.impno); 303 if (dolog) 304 syslog(LOG_NOTICE, 305 "returning server:%s path:%s address: %d.%d.%d.%d", 306 res.server_name, res.server_path, 307 255 & res.server_address.bp_address_u.ip_addr.net, 308 255 & res.server_address.bp_address_u.ip_addr.host, 309 255 & res.server_address.bp_address_u.ip_addr.lh, 310 255 & res.server_address.bp_address_u.ip_addr.impno); 311 return (&res); 312 } 313 314 315 int 316 lookup_bootparam(client, client_canonical, id, server, path) 317 char *client; 318 char *client_canonical; 319 char *id; 320 char **server; 321 char **path; 322 { 323 FILE *f = fopen(bootpfile, "r"); 324 #ifdef YP 325 static char *ypbuf = NULL; 326 static int ypbuflen = 0; 327 #endif 328 static char buf[BUFSIZ]; 329 char *canon = NULL, *bp, *word = NULL; 330 size_t idlen = id == NULL ? 0 : strlen(id); 331 int contin = 0; 332 int found = 0; 333 334 if (f == NULL) 335 return EINVAL; /* ? */ 336 337 while (fgets(buf, sizeof buf, f)) { 338 int wascontin = contin; 339 contin = buf[strlen(buf) - 2] == '\\'; 340 bp = buf + strspn(buf, " \t\n"); 341 342 switch (wascontin) { 343 case -1: 344 /* Continuation of uninteresting line */ 345 contin *= -1; 346 continue; 347 case 0: 348 /* New line */ 349 contin *= -1; 350 if (*bp == '#') 351 continue; 352 if ((word = strsep(&bp, " \t\n")) == NULL) 353 continue; 354 #ifdef YP 355 /* A + in the file means try YP now */ 356 if (!strcmp(word, "+")) { 357 char *ypdom; 358 359 if (yp_get_default_domain(&ypdom) || 360 yp_match(ypdom, "bootparams", client, 361 strlen(client), &ypbuf, &ypbuflen)) 362 continue; 363 bp = ypbuf; 364 word = client; 365 contin *= -1; 366 break; 367 } 368 #endif 369 if (debug) 370 warnx("match %s with %s", word, client); 371 372 #define HASGLOB(str) \ 373 (strchr(str, '*') != NULL || \ 374 strchr(str, '?') != NULL || \ 375 strchr(str, '[') != NULL || \ 376 strchr(str, ']') != NULL) 377 378 /* See if this line's client is the one we are 379 * looking for */ 380 if (fnmatch(word, client, FNM_CASEFOLD) == 0) { 381 /* 382 * Match. The token may be globbed, we 383 * can't just return that as the canonical 384 * name. Check to see if the token has any 385 * globbing characters in it (*, ?, [, ]). 386 * If so, just return the name we already 387 * have. Otherwise, return the token. 388 */ 389 if (HASGLOB(word)) 390 canon = client; 391 else 392 canon = word; 393 } else { 394 struct hostent *hp; 395 /* 396 * If it didn't match, try getting the 397 * canonical host name of the client 398 * on this line, if it's not a glob, 399 * and comparing it to the client we 400 * are looking up. 401 */ 402 if (HASGLOB(word)) { 403 if (debug) 404 warnx("Skipping non-match: %s", 405 word); 406 continue; 407 } 408 if ((hp = gethostbyname(word)) == NULL) { 409 if (debug) 410 warnx( 411 "Unknown bootparams host %s", 412 word); 413 if (dolog) 414 syslog(LOG_NOTICE, 415 "Unknown bootparams host %s", 416 word); 417 continue; 418 } 419 if (strcasecmp(hp->h_name, client) != 0) 420 continue; 421 canon = hp->h_name; 422 } 423 424 #undef HASGLOB 425 426 contin *= -1; 427 break; 428 case 1: 429 /* Continued line we want to parse below */ 430 break; 431 } 432 433 assert(canon != NULL); 434 if (client_canonical) 435 strncpy(client_canonical, canon, MAX_MACHINE_NAME); 436 437 /* We have found a line for CLIENT */ 438 if (id == NULL) { 439 (void) fclose(f); 440 return 0; 441 } 442 443 /* Look for a value for the parameter named by ID */ 444 while ((word = strsep(&bp, " \t\n")) != NULL) { 445 if (!strncmp(word, id, idlen) && word[idlen] == '=') { 446 /* We have found the entry we want */ 447 *server = &word[idlen + 1]; 448 *path = strchr(*server, ':'); 449 if (*path == NULL) 450 /* Malformed entry */ 451 continue; 452 *(*path)++ = '\0'; 453 (void) fclose(f); 454 return 0; 455 } 456 } 457 458 found = 1; 459 } 460 461 (void) fclose(f); 462 return found ? ENOENT : EPERM; 463 } 464 465 void 466 usage() 467 { 468 fprintf(stderr, 469 "usage: %s [-ds] [-i interface] [-r router] [-f bootparamsfile]\n", 470 getprogname()); 471 exit(1); 472 } 473 474 static int 475 get_localaddr(ifname, sin) 476 const char *ifname; 477 struct sockaddr_in *sin; 478 { 479 struct ifaddrs *ifap, *ifa; 480 481 if (getifaddrs(&ifap) != 0) 482 return -1; 483 484 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 485 if (ifname && strcmp(ifname, ifa->ifa_name) != 0) 486 continue; 487 if (ifa->ifa_addr->sa_family != AF_INET) 488 continue; 489 if (ifa->ifa_addr->sa_len != sizeof(*sin)) 490 continue; 491 492 /* no loopback please */ 493 #ifdef IFF_LOOPBACK 494 if (ifa->ifa_flags & IFF_LOOPBACK) 495 continue; 496 #else 497 if (strncmp(ifa->ifa_name, "lo", 2) == 0 && 498 (isdigit(ifa->ifa_name[2]) || ifa->ifa_name[2] == '\0')) 499 continue; 500 #endif 501 502 if (!iface || strcmp(ifa->ifa_name, iface) == 0) 503 ; 504 else 505 continue; 506 507 /* candidate found */ 508 memcpy(sin, ifa->ifa_addr, ifa->ifa_addr->sa_len); 509 freeifaddrs(ifap); 510 return 0; 511 } 512 513 /* no candidate */ 514 freeifaddrs(ifap); 515 return -1; 516 } 517