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