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