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