1 /* $NetBSD: bootparamd.c,v 1.38 2001/02/19 23:22:47 cgd 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.38 2001/02/19 23:22:47 cgd 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 goto failed; 248 249 strncpy(askname, he->h_name, sizeof(askname)); 250 askname[sizeof(askname)-1] = 0; 251 err = lookup_bootparam(askname, NULL, getfile->file_id, 252 &res.server_name, &res.server_path); 253 if (err == 0) { 254 he = gethostbyname(res.server_name); 255 if (!he) 256 goto failed; 257 memmove(&res.server_address.bp_address_u.ip_addr, 258 he->h_addr, 4); 259 res.server_address.address_type = IP_ADDR_TYPE; 260 } else if (err == ENOENT && !strcmp(getfile->file_id, "dump")) { 261 /* Special for dump, answer with null strings. */ 262 res.server_name[0] = '\0'; 263 res.server_path[0] = '\0'; 264 memset(&res.server_address.bp_address_u.ip_addr, 0, 4); 265 } else { 266 failed: 267 if (debug) 268 warnx("getfile failed for %s", 269 getfile->client_name); 270 if (dolog) 271 syslog(LOG_NOTICE, 272 "getfile failed for %s", getfile->client_name); 273 return (NULL); 274 } 275 276 if (debug) 277 warnx( 278 "returning server:%s path:%s address: %d.%d.%d.%d", 279 res.server_name, res.server_path, 280 255 & res.server_address.bp_address_u.ip_addr.net, 281 255 & res.server_address.bp_address_u.ip_addr.host, 282 255 & res.server_address.bp_address_u.ip_addr.lh, 283 255 & res.server_address.bp_address_u.ip_addr.impno); 284 if (dolog) 285 syslog(LOG_NOTICE, 286 "returning server:%s path:%s address: %d.%d.%d.%d", 287 res.server_name, res.server_path, 288 255 & res.server_address.bp_address_u.ip_addr.net, 289 255 & res.server_address.bp_address_u.ip_addr.host, 290 255 & res.server_address.bp_address_u.ip_addr.lh, 291 255 & res.server_address.bp_address_u.ip_addr.impno); 292 return (&res); 293 } 294 295 296 int 297 lookup_bootparam(client, client_canonical, id, server, path) 298 char *client; 299 char *client_canonical; 300 char *id; 301 char **server; 302 char **path; 303 { 304 FILE *f = fopen(bootpfile, "r"); 305 #ifdef YP 306 static char *ypbuf = NULL; 307 static int ypbuflen = 0; 308 #endif 309 static char buf[BUFSIZ]; 310 char *canon = NULL, *bp, *word = NULL; 311 size_t idlen = id == NULL ? 0 : strlen(id); 312 int contin = 0; 313 int found = 0; 314 315 if (f == NULL) 316 return EINVAL; /* ? */ 317 318 while (fgets(buf, sizeof buf, f)) { 319 int wascontin = contin; 320 contin = buf[strlen(buf) - 2] == '\\'; 321 bp = buf + strspn(buf, " \t\n"); 322 323 switch (wascontin) { 324 case -1: 325 /* Continuation of uninteresting line */ 326 contin *= -1; 327 continue; 328 case 0: 329 /* New line */ 330 contin *= -1; 331 if (*bp == '#') 332 continue; 333 if ((word = strsep(&bp, " \t\n")) == NULL) 334 continue; 335 #ifdef YP 336 /* A + in the file means try YP now */ 337 if (!strcmp(word, "+")) { 338 char *ypdom; 339 340 if (yp_get_default_domain(&ypdom) || 341 yp_match(ypdom, "bootparams", client, 342 strlen(client), &ypbuf, &ypbuflen)) 343 continue; 344 bp = ypbuf; 345 word = client; 346 contin *= -1; 347 break; 348 } 349 #endif 350 if (debug) 351 warnx("match %s with %s", word, client); 352 353 #define HASGLOB(str) \ 354 (strchr(str, '*') != NULL || \ 355 strchr(str, '?') != NULL || \ 356 strchr(str, '[') != NULL || \ 357 strchr(str, ']') != NULL) 358 359 /* See if this line's client is the one we are 360 * looking for */ 361 if (fnmatch(word, client, FNM_CASEFOLD) == 0) { 362 /* 363 * Match. The token may be globbed, we 364 * can't just return that as the canonical 365 * name. Check to see if the token has any 366 * globbing characters in it (*, ?, [, ]). 367 * If so, just return the name we already 368 * have. Otherwise, return the token. 369 */ 370 if (HASGLOB(word)) 371 canon = client; 372 else 373 canon = word; 374 } else { 375 struct hostent *hp; 376 /* 377 * If it didn't match, try getting the 378 * canonical host name of the client 379 * on this line, if it's not a glob, 380 * and comparing it to the client we 381 * are looking up. 382 */ 383 if (HASGLOB(word)) { 384 if (debug) 385 warnx("Skipping non-match: %s", 386 word); 387 continue; 388 } 389 if ((hp = gethostbyname(word)) == NULL) { 390 if (debug) 391 warnx( 392 "Unknown bootparams host %s", 393 word); 394 if (dolog) 395 syslog(LOG_NOTICE, 396 "Unknown bootparams host %s", 397 word); 398 continue; 399 } 400 if (strcasecmp(hp->h_name, client) != 0) 401 continue; 402 canon = hp->h_name; 403 } 404 405 #undef HASGLOB 406 407 contin *= -1; 408 break; 409 case 1: 410 /* Continued line we want to parse below */ 411 break; 412 } 413 414 assert(canon != NULL); 415 if (client_canonical) 416 strncpy(client_canonical, canon, MAX_MACHINE_NAME); 417 418 /* We have found a line for CLIENT */ 419 if (id == NULL) { 420 (void) fclose(f); 421 return 0; 422 } 423 424 /* Look for a value for the parameter named by ID */ 425 while ((word = strsep(&bp, " \t\n")) != NULL) { 426 if (!strncmp(word, id, idlen) && word[idlen] == '=') { 427 /* We have found the entry we want */ 428 *server = &word[idlen + 1]; 429 *path = strchr(*server, ':'); 430 if (*path == NULL) 431 /* Malformed entry */ 432 continue; 433 *(*path)++ = '\0'; 434 (void) fclose(f); 435 return 0; 436 } 437 } 438 439 found = 1; 440 } 441 442 (void) fclose(f); 443 return found ? ENOENT : EPERM; 444 } 445 446 void 447 usage() 448 { 449 fprintf(stderr, 450 "usage: %s [-d] [-s] [-r router] [-f bootparmsfile]\n", 451 getprogname()); 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