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