1 1.1 gwr /************************************************************************ 2 1.1 gwr Copyright 1988, 1991 by Carnegie Mellon University 3 1.1 gwr 4 1.1 gwr All Rights Reserved 5 1.1 gwr 6 1.1 gwr Permission to use, copy, modify, and distribute this software and its 7 1.1 gwr documentation for any purpose and without fee is hereby granted, provided 8 1.1 gwr that the above copyright notice appear in all copies and that both that 9 1.1 gwr copyright notice and this permission notice appear in supporting 10 1.1 gwr documentation, and that the name of Carnegie Mellon University not be used 11 1.1 gwr in advertising or publicity pertaining to distribution of the software 12 1.1 gwr without specific, written prior permission. 13 1.1 gwr 14 1.1 gwr CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 15 1.1 gwr SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 16 1.1 gwr IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 17 1.1 gwr DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 18 1.1 gwr PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 19 1.1 gwr ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20 1.1 gwr SOFTWARE. 21 1.1 gwr ************************************************************************/ 22 1.1 gwr 23 1.8 lukem #include <sys/cdefs.h> 24 1.1 gwr #ifndef lint 25 1.28 andvar __RCSID("$NetBSD: bootpd.c,v 1.28 2025/07/22 21:55:15 andvar Exp $"); 26 1.1 gwr #endif 27 1.1 gwr 28 1.1 gwr /* 29 1.1 gwr * BOOTP (bootstrap protocol) server daemon. 30 1.1 gwr * 31 1.1 gwr * Answers BOOTP request packets from booting client machines. 32 1.1 gwr * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol. 33 1.1 gwr * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions. 34 1.1 gwr * See RFC 1395 for option tags 14-17. 35 1.1 gwr * See accompanying man page -- bootpd.8 36 1.1 gwr * 37 1.1 gwr * HISTORY 38 1.1 gwr * See ./Changes 39 1.1 gwr * 40 1.1 gwr * BUGS 41 1.1 gwr * See ./ToDo 42 1.1 gwr */ 43 1.1 gwr 44 1.1 gwr 45 1.1 gwr 47 1.1 gwr #include <sys/types.h> 48 1.1 gwr #include <sys/param.h> 49 1.1 gwr #include <sys/socket.h> 50 1.1 gwr #include <sys/ioctl.h> 51 1.1 gwr #include <sys/file.h> 52 1.1 gwr #include <sys/time.h> 53 1.15 mycroft #include <sys/stat.h> 54 1.1 gwr #include <sys/poll.h> 55 1.1 gwr 56 1.1 gwr #include <net/if.h> 57 1.1 gwr #include <netinet/in.h> 58 1.1 gwr #include <arpa/inet.h> /* inet_ntoa */ 59 1.1 gwr 60 1.1 gwr #ifndef NO_UNISTD 61 1.1 gwr #include <unistd.h> 62 1.1 gwr #endif 63 1.1 gwr #include <stdlib.h> 64 1.1 gwr #include <signal.h> 65 1.1 gwr #include <stdio.h> 66 1.21 tls #include <string.h> 67 1.1 gwr #include <strings.h> 68 1.1 gwr #include <errno.h> 69 1.1 gwr #include <ctype.h> 70 1.1 gwr #include <netdb.h> 71 1.1 gwr #include <syslog.h> 72 1.1 gwr #include <assert.h> 73 1.1 gwr 74 1.1 gwr #ifdef NO_SETSID 75 1.1 gwr # include <fcntl.h> /* for O_RDONLY, etc */ 76 1.1 gwr #endif 77 1.1 gwr 78 1.1 gwr #ifdef SVR4 79 1.1 gwr /* Using sigset() avoids the need to re-arm each time. */ 80 1.1 gwr #define signal sigset 81 1.1 gwr #endif 82 1.1 gwr 83 1.1 gwr #include "bootp.h" 84 1.1 gwr #include "hash.h" 85 1.1 gwr #include "hwaddr.h" 86 1.1 gwr #include "bootpd.h" 87 1.1 gwr #include "dovend.h" 88 1.1 gwr #include "getif.h" 89 1.1 gwr #include "readfile.h" 90 1.1 gwr #include "report.h" 91 1.1 gwr #include "tzone.h" 92 1.1 gwr #include "patchlevel.h" 93 1.1 gwr 94 1.1 gwr #ifndef CONFIG_FILE 95 1.1 gwr #define CONFIG_FILE "/etc/bootptab" 96 1.1 gwr #endif 97 1.1 gwr #ifndef DUMPTAB_FILE 98 1.1 gwr #define DUMPTAB_FILE "/tmp/bootpd.dump" 99 1.1 gwr #endif 100 1.1 gwr 101 1.1 gwr 102 1.1 gwr 104 1.1 gwr /* 105 1.1 gwr * Externals, forward declarations, and global variables 106 1.22 xtraeme */ 107 1.1 gwr 108 1.14 wiz extern void dumptab(const char *); 109 1.14 wiz 110 1.1 gwr PRIVATE void catcher(int); 111 1.14 wiz PRIVATE int chk_access(char *, int32 *); 112 1.1 gwr #ifdef VEND_CMU 113 1.14 wiz PRIVATE void dovend_cmu(struct bootp *, struct host *); 114 1.14 wiz #endif 115 1.14 wiz PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32); 116 1.14 wiz PRIVATE void handle_reply(void); 117 1.24 joerg PRIVATE void handle_request(void); 118 1.1 gwr PRIVATE void sendreply(int forward, int32 dest_override); 119 1.1 gwr __dead PRIVATE void usage(void); 120 1.1 gwr 121 1.1 gwr /* 122 1.1 gwr * IP port numbers for client and server obtained from /etc/services 123 1.1 gwr */ 124 1.1 gwr 125 1.1 gwr u_short bootps_port, bootpc_port; 126 1.1 gwr 127 1.1 gwr 128 1.1 gwr /* 129 1.1 gwr * Internet socket and interface config structures 130 1.1 gwr */ 131 1.1 gwr 132 1.1 gwr struct sockaddr_in bind_addr; /* Listening */ 133 1.1 gwr struct sockaddr_in recv_addr; /* Packet source */ 134 1.1 gwr struct sockaddr_in send_addr; /* destination */ 135 1.1 gwr 136 1.1 gwr 137 1.1 gwr /* 138 1.1 gwr * option defaults 139 1.15 mycroft */ 140 1.1 gwr int debug = 0; /* Debugging flag (level) */ 141 1.1 gwr int actualtimeout = 15 * 60000; /* fifteen minutes */ 142 1.1 gwr 143 1.1 gwr /* 144 1.1 gwr * General 145 1.1 gwr */ 146 1.1 gwr 147 1.1 gwr int s; /* Socket file descriptor */ 148 1.22 xtraeme char *pktbuf; /* Receive packet buffer */ 149 1.1 gwr int pktlen; 150 1.9 mrg const char *progname; 151 1.1 gwr char *chdir_path; 152 1.1 gwr char hostname[MAXHOSTNAMELEN + 1]; /* System host name */ 153 1.1 gwr struct in_addr my_ip_addr; 154 1.1 gwr 155 1.1 gwr /* Flags set by signal catcher. */ 156 1.1 gwr PRIVATE int do_readtab = 0; 157 1.1 gwr PRIVATE int do_dumptab = 0; 158 1.1 gwr 159 1.1 gwr /* 160 1.1 gwr * Globals below are associated with the bootp database file (bootptab). 161 1.22 xtraeme */ 162 1.22 xtraeme 163 1.1 gwr const char *bootptab = CONFIG_FILE; 164 1.1 gwr const char *bootpd_dump = DUMPTAB_FILE; 165 1.1 gwr 166 1.1 gwr 167 1.1 gwr 169 1.1 gwr /* 170 1.1 gwr * Initialization such as command-line processing is done and then the 171 1.6 jtc * main server loop is started. 172 1.14 wiz */ 173 1.1 gwr 174 1.15 mycroft int 175 1.1 gwr main(int argc, char **argv) 176 1.1 gwr { 177 1.1 gwr int timeout; 178 1.1 gwr struct bootp *bp; 179 1.20 mrg struct servent *servp; 180 1.20 mrg struct hostent *hep; 181 1.15 mycroft char *stmp; 182 1.15 mycroft socklen_t ba_len, ra_len; 183 1.1 gwr int n; 184 1.1 gwr int nfound; 185 1.1 gwr struct pollfd set[1]; 186 1.9 mrg int standalone; 187 1.9 mrg 188 1.9 mrg progname = strrchr(argv[0], '/'); 189 1.9 mrg if (progname) 190 1.1 gwr progname++; 191 1.1 gwr else 192 1.1 gwr progname = argv[0]; 193 1.1 gwr 194 1.1 gwr /* 195 1.1 gwr * Initialize logging. 196 1.1 gwr */ 197 1.1 gwr report_init(0); /* uses progname */ 198 1.1 gwr 199 1.1 gwr /* 200 1.1 gwr * Log startup 201 1.1 gwr */ 202 1.1 gwr report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); 203 1.1 gwr 204 1.1 gwr /* Debugging for compilers with struct padding. */ 205 1.2 gwr assert(sizeof(struct bootp) == BP_MINPKTSZ); 206 1.1 gwr 207 1.1 gwr /* Get space for receiving packets and composing replies. */ 208 1.1 gwr pktbuf = malloc(MAX_MSG_SIZE); 209 1.1 gwr if (!pktbuf) { 210 1.1 gwr report(LOG_ERR, "malloc failed"); 211 1.1 gwr exit(1); 212 1.1 gwr } 213 1.1 gwr bp = (struct bootp *) pktbuf; 214 1.1 gwr 215 1.1 gwr /* 216 1.1 gwr * Check to see if a socket was passed to us from inetd. 217 1.1 gwr * 218 1.1 gwr * Use getsockname() to determine if descriptor 0 is indeed a socket 219 1.1 gwr * (and thus we are probably a child of inetd) or if it is instead 220 1.1 gwr * something else and we are running standalone. 221 1.1 gwr */ 222 1.1 gwr s = 0; 223 1.1 gwr ba_len = sizeof(bind_addr); 224 1.1 gwr bzero((char *) &bind_addr, ba_len); 225 1.1 gwr errno = 0; 226 1.1 gwr standalone = TRUE; 227 1.1 gwr if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { 228 1.1 gwr /* 229 1.1 gwr * Descriptor 0 is a socket. Assume we are a child of inetd. 230 1.1 gwr */ 231 1.1 gwr if (bind_addr.sin_family == AF_INET) { 232 1.1 gwr standalone = FALSE; 233 1.1 gwr bootps_port = ntohs(bind_addr.sin_port); 234 1.1 gwr } else { 235 1.1 gwr /* Some other type of socket? */ 236 1.1 gwr report(LOG_ERR, "getsockname: not an INET socket"); 237 1.1 gwr } 238 1.1 gwr } 239 1.1 gwr 240 1.1 gwr /* 241 1.15 mycroft * Set defaults that might be changed by option switches. 242 1.1 gwr */ 243 1.1 gwr stmp = NULL; 244 1.1 gwr timeout = actualtimeout; 245 1.1 gwr 246 1.1 gwr /* 247 1.1 gwr * Read switches. 248 1.1 gwr */ 249 1.1 gwr for (argc--, argv++; argc > 0; argc--, argv++) { 250 1.1 gwr if (argv[0][0] != '-') 251 1.1 gwr break; 252 1.1 gwr switch (argv[0][1]) { 253 1.1 gwr 254 1.1 gwr case 'c': /* chdir_path */ 255 1.1 gwr if (argv[0][2]) { 256 1.1 gwr stmp = &(argv[0][2]); 257 1.1 gwr } else { 258 1.1 gwr argc--; 259 1.1 gwr argv++; 260 1.1 gwr stmp = argv[0]; 261 1.1 gwr } 262 1.1 gwr if (!stmp || (stmp[0] != '/')) { 263 1.1 gwr fprintf(stderr, 264 1.1 gwr "bootpd: invalid chdir specification\n"); 265 1.1 gwr break; 266 1.1 gwr } 267 1.1 gwr chdir_path = stmp; 268 1.1 gwr break; 269 1.1 gwr 270 1.1 gwr case 'd': /* debug level */ 271 1.1 gwr if (argv[0][2]) { 272 1.1 gwr stmp = &(argv[0][2]); 273 1.1 gwr } else if (argv[1] && argv[1][0] == '-') { 274 1.1 gwr /* 275 1.1 gwr * Backwards-compatible behavior: 276 1.1 gwr * no parameter, so just increment the debug flag. 277 1.1 gwr */ 278 1.1 gwr debug++; 279 1.1 gwr break; 280 1.1 gwr } else { 281 1.1 gwr argc--; 282 1.1 gwr argv++; 283 1.1 gwr stmp = argv[0]; 284 1.1 gwr } 285 1.1 gwr if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 286 1.1 gwr fprintf(stderr, 287 1.1 gwr "%s: invalid debug level\n", progname); 288 1.1 gwr break; 289 1.1 gwr } 290 1.1 gwr debug = n; 291 1.1 gwr break; 292 1.1 gwr 293 1.1 gwr case 'h': /* override hostname */ 294 1.1 gwr if (argv[0][2]) { 295 1.1 gwr stmp = &(argv[0][2]); 296 1.1 gwr } else { 297 1.1 gwr argc--; 298 1.1 gwr argv++; 299 1.1 gwr stmp = argv[0]; 300 1.1 gwr } 301 1.1 gwr if (!stmp) { 302 1.1 gwr fprintf(stderr, 303 1.18 itojun "bootpd: missing hostname\n"); 304 1.1 gwr break; 305 1.1 gwr } 306 1.1 gwr strlcpy(hostname, stmp, sizeof(hostname)); 307 1.1 gwr break; 308 1.1 gwr 309 1.1 gwr case 'i': /* inetd mode */ 310 1.1 gwr standalone = FALSE; 311 1.1 gwr break; 312 1.1 gwr 313 1.1 gwr case 's': /* standalone mode */ 314 1.1 gwr standalone = TRUE; 315 1.1 gwr break; 316 1.1 gwr 317 1.1 gwr case 't': /* timeout */ 318 1.1 gwr if (argv[0][2]) { 319 1.1 gwr stmp = &(argv[0][2]); 320 1.1 gwr } else { 321 1.1 gwr argc--; 322 1.1 gwr argv++; 323 1.1 gwr stmp = argv[0]; 324 1.1 gwr } 325 1.1 gwr if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 326 1.1 gwr fprintf(stderr, 327 1.15 mycroft "%s: invalid timeout specification\n", progname); 328 1.1 gwr break; 329 1.15 mycroft } 330 1.15 mycroft actualtimeout = n * 60000; 331 1.15 mycroft /* 332 1.1 gwr * If the actual timeout is zero, pass INFTIM 333 1.15 mycroft * to poll so it blocks indefinitely, otherwise, 334 1.1 gwr * use the actual timeout value. 335 1.1 gwr */ 336 1.1 gwr timeout = (n > 0) ? actualtimeout : INFTIM; 337 1.1 gwr break; 338 1.1 gwr 339 1.1 gwr default: 340 1.1 gwr fprintf(stderr, "%s: unknown switch: -%c\n", 341 1.1 gwr progname, argv[0][1]); 342 1.1 gwr usage(); 343 1.1 gwr break; 344 1.1 gwr 345 1.1 gwr } /* switch */ 346 1.1 gwr } /* for args */ 347 1.1 gwr 348 1.1 gwr /* 349 1.1 gwr * Override default file names if specified on the command line. 350 1.1 gwr */ 351 1.1 gwr if (argc > 0) 352 1.1 gwr bootptab = argv[0]; 353 1.1 gwr 354 1.1 gwr if (argc > 1) 355 1.1 gwr bootpd_dump = argv[1]; 356 1.1 gwr 357 1.1 gwr /* 358 1.1 gwr * Get my hostname and IP address. 359 1.1 gwr */ 360 1.1 gwr if (hostname[0] == '\0') { 361 1.1 gwr if (gethostname(hostname, sizeof(hostname)) == -1) { 362 1.9 mrg fprintf(stderr, "bootpd: can't get hostname\n"); 363 1.1 gwr exit(1); 364 1.1 gwr } 365 1.1 gwr hostname[sizeof(hostname) - 1] = '\0'; 366 1.1 gwr } 367 1.1 gwr hep = gethostbyname(hostname); 368 1.1 gwr if (!hep) { 369 1.1 gwr fprintf(stderr, "Can not get my IP address\n"); 370 1.1 gwr exit(1); 371 1.1 gwr } 372 1.1 gwr bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); 373 1.1 gwr 374 1.1 gwr if (standalone) { 375 1.1 gwr /* 376 1.1 gwr * Go into background and disassociate from controlling terminal. 377 1.1 gwr */ 378 1.1 gwr if (debug < 3) { 379 1.1 gwr if (fork()) 380 1.1 gwr exit(0); 381 1.1 gwr #ifdef NO_SETSID 382 1.1 gwr setpgrp(0,0); 383 1.1 gwr #ifdef TIOCNOTTY 384 1.1 gwr n = open("/dev/tty", O_RDWR); 385 1.1 gwr if (n >= 0) { 386 1.1 gwr ioctl(n, TIOCNOTTY, (char *) 0); 387 1.1 gwr (void) close(n); 388 1.1 gwr } 389 1.1 gwr #endif /* TIOCNOTTY */ 390 1.1 gwr #else /* SETSID */ 391 1.1 gwr if (setsid() < 0) 392 1.1 gwr perror("setsid"); 393 1.1 gwr #endif /* SETSID */ 394 1.1 gwr } /* if debug < 3 */ 395 1.1 gwr 396 1.15 mycroft /* 397 1.1 gwr * Nuke any timeout value 398 1.1 gwr */ 399 1.1 gwr timeout = INFTIM; 400 1.1 gwr 401 1.1 gwr } /* if standalone (1st) */ 402 1.1 gwr 403 1.1 gwr /* Set the cwd (i.e. to /tftpboot) */ 404 1.1 gwr if (chdir_path) { 405 1.1 gwr if (chdir(chdir_path) < 0) 406 1.1 gwr report(LOG_ERR, "%s: chdir failed", chdir_path); 407 1.1 gwr } 408 1.1 gwr 409 1.1 gwr /* Get the timezone. */ 410 1.1 gwr tzone_init(); 411 1.1 gwr 412 1.1 gwr /* Allocate hash tables. */ 413 1.1 gwr rdtab_init(); 414 1.1 gwr 415 1.1 gwr /* 416 1.1 gwr * Read the bootptab file. 417 1.1 gwr */ 418 1.1 gwr readtab(1); /* force read */ 419 1.1 gwr 420 1.1 gwr if (standalone) { 421 1.1 gwr 422 1.1 gwr /* 423 1.1 gwr * Create a socket. 424 1.1 gwr */ 425 1.1 gwr if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 426 1.1 gwr report(LOG_ERR, "socket: %s", get_network_errmsg()); 427 1.1 gwr exit(1); 428 1.1 gwr } 429 1.1 gwr 430 1.1 gwr /* 431 1.1 gwr * Get server's listening port number 432 1.1 gwr */ 433 1.1 gwr servp = getservbyname("bootps", "udp"); 434 1.1 gwr if (servp) { 435 1.1 gwr bootps_port = ntohs((u_short) servp->s_port); 436 1.1 gwr } else { 437 1.1 gwr bootps_port = (u_short) IPPORT_BOOTPS; 438 1.1 gwr report(LOG_ERR, 439 1.1 gwr "udp/bootps: unknown service -- assuming port %d", 440 1.1 gwr bootps_port); 441 1.1 gwr } 442 1.1 gwr 443 1.1 gwr /* 444 1.1 gwr * Bind socket to BOOTPS port. 445 1.1 gwr */ 446 1.1 gwr bind_addr.sin_family = AF_INET; 447 1.1 gwr bind_addr.sin_addr.s_addr = INADDR_ANY; 448 1.1 gwr bind_addr.sin_port = htons(bootps_port); 449 1.1 gwr if (bind(s, (struct sockaddr *) &bind_addr, 450 1.1 gwr sizeof(bind_addr)) < 0) 451 1.1 gwr { 452 1.1 gwr report(LOG_ERR, "bind: %s", get_network_errmsg()); 453 1.1 gwr exit(1); 454 1.1 gwr } 455 1.1 gwr } /* if standalone (2nd)*/ 456 1.1 gwr 457 1.1 gwr /* 458 1.1 gwr * Get destination port number so we can reply to client 459 1.1 gwr */ 460 1.1 gwr servp = getservbyname("bootpc", "udp"); 461 1.1 gwr if (servp) { 462 1.1 gwr bootpc_port = ntohs(servp->s_port); 463 1.1 gwr } else { 464 1.1 gwr report(LOG_ERR, 465 1.1 gwr "udp/bootpc: unknown service -- assuming port %d", 466 1.1 gwr IPPORT_BOOTPC); 467 1.1 gwr bootpc_port = (u_short) IPPORT_BOOTPC; 468 1.1 gwr } 469 1.1 gwr 470 1.5 cgd /* 471 1.1 gwr * Set up signals to read or dump the table. 472 1.1 gwr */ 473 1.1 gwr if ((long) signal(SIGHUP, catcher) < 0) { 474 1.5 cgd report(LOG_ERR, "signal: %s", get_errmsg()); 475 1.1 gwr exit(1); 476 1.1 gwr } 477 1.1 gwr if ((long) signal(SIGUSR1, catcher) < 0) { 478 1.1 gwr report(LOG_ERR, "signal: %s", get_errmsg()); 479 1.1 gwr exit(1); 480 1.1 gwr } 481 1.1 gwr 482 1.15 mycroft /* 483 1.15 mycroft * Process incoming requests. 484 1.1 gwr */ 485 1.15 mycroft set[0].fd = s; 486 1.1 gwr set[0].events = POLLIN; 487 1.1 gwr for (;;) { 488 1.15 mycroft nfound = poll(set, 1, timeout); 489 1.1 gwr if (nfound < 0) { 490 1.1 gwr if (errno != EINTR) { 491 1.1 gwr report(LOG_ERR, "poll: %s", get_errmsg()); 492 1.1 gwr } 493 1.1 gwr /* 494 1.1 gwr * Call readtab() or dumptab() here to avoid the 495 1.1 gwr * dangers of doing I/O from a signal handler. 496 1.1 gwr */ 497 1.1 gwr if (do_readtab) { 498 1.1 gwr do_readtab = 0; 499 1.1 gwr readtab(1); /* force read */ 500 1.1 gwr } 501 1.1 gwr if (do_dumptab) { 502 1.1 gwr do_dumptab = 0; 503 1.1 gwr dumptab(bootpd_dump); 504 1.15 mycroft } 505 1.1 gwr continue; 506 1.16 mycroft } 507 1.16 mycroft if (nfound == 0) { 508 1.16 mycroft if (debug > 1) 509 1.1 gwr report(LOG_INFO, "exiting after %d minute%s of inactivity", 510 1.1 gwr actualtimeout / 60000, 511 1.1 gwr actualtimeout == 60000 ? "" : "s"); 512 1.2 gwr exit(0); 513 1.1 gwr } 514 1.1 gwr ra_len = sizeof(recv_addr); 515 1.1 gwr n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, 516 1.1 gwr (struct sockaddr *) &recv_addr, &ra_len); 517 1.1 gwr if (n <= 0) { 518 1.1 gwr continue; 519 1.1 gwr } 520 1.1 gwr if (debug > 1) { 521 1.23 lukem report(LOG_INFO, "recvd pkt from IP addr %s", 522 1.1 gwr inet_ntoa(recv_addr.sin_addr)); 523 1.1 gwr } 524 1.1 gwr if (n < (int)sizeof(struct bootp)) { 525 1.1 gwr if (debug) { 526 1.1 gwr report(LOG_INFO, "received short packet"); 527 1.1 gwr } 528 1.1 gwr continue; 529 1.1 gwr } 530 1.1 gwr pktlen = n; 531 1.1 gwr 532 1.1 gwr readtab(0); /* maybe re-read bootptab */ 533 1.1 gwr 534 1.1 gwr switch (bp->bp_op) { 535 1.1 gwr case BOOTREQUEST: 536 1.1 gwr handle_request(); 537 1.1 gwr break; 538 1.1 gwr case BOOTREPLY: 539 1.1 gwr handle_reply(); 540 1.1 gwr break; 541 1.1 gwr } 542 1.1 gwr } 543 1.1 gwr } 544 1.1 gwr 545 1.1 gwr 546 1.1 gwr 548 1.1 gwr 549 1.1 gwr /* 550 1.14 wiz * Print "usage" message and exit 551 1.1 gwr */ 552 1.1 gwr 553 1.1 gwr PRIVATE void 554 1.1 gwr usage(void) 555 1.1 gwr { 556 1.1 gwr fprintf(stderr, 557 1.1 gwr "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n"); 558 1.1 gwr fprintf(stderr, "\t -c n\tset current directory\n"); 559 1.1 gwr fprintf(stderr, "\t -d n\tset debug level\n"); 560 1.1 gwr fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); 561 1.1 gwr fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); 562 1.1 gwr fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); 563 1.1 gwr exit(1); 564 1.14 wiz } 565 1.1 gwr 566 1.1 gwr /* Signal catchers */ 567 1.1 gwr PRIVATE void 568 1.1 gwr catcher(int sig) 569 1.1 gwr { 570 1.1 gwr if (sig == SIGHUP) 571 1.1 gwr do_readtab = 1; 572 1.1 gwr if (sig == SIGUSR1) 573 1.1 gwr do_dumptab = 1; 574 1.1 gwr #ifdef SYSV 575 1.1 gwr /* For older "System V" derivatives with no sigset(). */ 576 1.1 gwr /* XXX - Should just do it the POSIX way (sigaction). */ 577 1.1 gwr signal(sig, catcher); 578 1.1 gwr #endif 579 1.1 gwr } 580 1.1 gwr 581 1.1 gwr 582 1.1 gwr 584 1.1 gwr /* 585 1.1 gwr * Process BOOTREQUEST packet. 586 1.1 gwr * 587 1.1 gwr * Note: This version of the bootpd.c server never forwards 588 1.1 gwr * a request to another server. That is the job of a gateway 589 1.1 gwr * program such as the "bootpgw" program included here. 590 1.1 gwr * 591 1.14 wiz * (Also this version does not interpret the hostname field of 592 1.1 gwr * the request packet; it COULD do a name->address lookup and 593 1.1 gwr * forward the request there.) 594 1.1 gwr */ 595 1.1 gwr PRIVATE void 596 1.1 gwr handle_request(void) 597 1.1 gwr { 598 1.1 gwr struct bootp *bp = (struct bootp *) pktbuf; 599 1.22 xtraeme struct host *hp = NULL; 600 1.2 gwr struct host dummyhost; 601 1.25 apb int32 bootsize = 0; 602 1.2 gwr unsigned hlen, hashcode; 603 1.1 gwr int32 dest; 604 1.1 gwr char lrealpath[1024]; 605 1.1 gwr char *clntpath; 606 1.1 gwr size_t clntpathmaxlen; 607 1.1 gwr char *homedir, *bootfile; 608 1.1 gwr int n; 609 1.1 gwr 610 1.1 gwr /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ 611 1.1 gwr 612 1.1 gwr /* 613 1.1 gwr * If the servername field is set, compare it against us. 614 1.1 gwr * If we're not being addressed, ignore this request. 615 1.1 gwr * If the server name field is null, throw in our name. 616 1.1 gwr */ 617 1.1 gwr if (strlen(bp->bp_sname)) { 618 1.1 gwr if (strcmp(bp->bp_sname, hostname)) { 619 1.1 gwr if (debug) 620 1.1 gwr report(LOG_INFO, "\ 621 1.1 gwr ignoring request for server %s from client at %s address %s", 622 1.1 gwr bp->bp_sname, netname(bp->bp_htype), 623 1.19 itojun haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 624 1.1 gwr /* XXX - Is it correct to ignore such a request? -gwr */ 625 1.1 gwr return; 626 1.10 abs } 627 1.10 abs } else { 628 1.10 abs strlcpy(bp->bp_sname, hostname, sizeof(bp->bp_sname)); 629 1.10 abs } 630 1.10 abs 631 1.10 abs /* If it uses an unknown network type, ignore the request. */ 632 1.10 abs if (bp->bp_htype >= hwinfocnt) { 633 1.10 abs if (debug) 634 1.10 abs report(LOG_INFO, 635 1.2 gwr "Request with unknown network type %u", 636 1.1 gwr bp->bp_htype); 637 1.1 gwr return; 638 1.1 gwr } 639 1.1 gwr 640 1.1 gwr /* Convert the request into a reply. */ 641 1.1 gwr bp->bp_op = BOOTREPLY; 642 1.1 gwr if (bp->bp_ciaddr.s_addr == 0) { 643 1.1 gwr /* 644 1.1 gwr * client doesnt know his IP address, 645 1.1 gwr * search by hardware address. 646 1.1 gwr */ 647 1.1 gwr if (debug > 1) { 648 1.1 gwr report(LOG_INFO, "request from %s address %s", 649 1.10 abs netname(bp->bp_htype), 650 1.1 gwr haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 651 1.1 gwr } 652 1.1 gwr hlen = haddrlength(bp->bp_htype); 653 1.1 gwr if (hlen != bp->bp_hlen) { 654 1.1 gwr report(LOG_NOTICE, "bad addr len from %s address %s", 655 1.1 gwr netname(bp->bp_htype), 656 1.1 gwr haddrtoa(bp->bp_chaddr, hlen)); 657 1.1 gwr } 658 1.1 gwr dummyhost.htype = bp->bp_htype; 659 1.1 gwr bcopy(bp->bp_chaddr, dummyhost.haddr, hlen); 660 1.1 gwr hashcode = hash_HashFunction(bp->bp_chaddr, hlen); 661 1.1 gwr hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, 662 1.1 gwr &dummyhost); 663 1.1 gwr if (hp == NULL && 664 1.1 gwr bp->bp_htype == HTYPE_IEEE802) 665 1.1 gwr { 666 1.1 gwr /* Try again with address in "canonical" form. */ 667 1.1 gwr haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); 668 1.1 gwr if (debug > 1) { 669 1.1 gwr report(LOG_INFO, "\ 670 1.1 gwr HW addr type is IEEE 802. convert to %s and check again\n", 671 1.1 gwr haddrtoa(dummyhost.haddr, bp->bp_hlen)); 672 1.1 gwr } 673 1.1 gwr hashcode = hash_HashFunction(dummyhost.haddr, hlen); 674 1.1 gwr hp = (struct host *) hash_Lookup(hwhashtable, hashcode, 675 1.1 gwr hwlookcmp, &dummyhost); 676 1.1 gwr } 677 1.1 gwr if (hp == NULL) { 678 1.1 gwr /* 679 1.1 gwr * XXX - Add dynamic IP address assignment? 680 1.1 gwr */ 681 1.1 gwr if (debug > 1) 682 1.1 gwr report(LOG_INFO, "unknown client %s address %s", 683 1.1 gwr netname(bp->bp_htype), 684 1.1 gwr haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 685 1.1 gwr return; /* not found */ 686 1.1 gwr } 687 1.1 gwr (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; 688 1.1 gwr 689 1.1 gwr } else { 690 1.1 gwr 691 1.1 gwr /* 692 1.1 gwr * search by IP address. 693 1.1 gwr */ 694 1.1 gwr if (debug > 1) { 695 1.1 gwr report(LOG_INFO, "request from IP addr %s", 696 1.1 gwr inet_ntoa(bp->bp_ciaddr)); 697 1.1 gwr } 698 1.1 gwr dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr; 699 1.1 gwr hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4); 700 1.1 gwr hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, 701 1.1 gwr &dummyhost); 702 1.1 gwr if (hp == NULL) { 703 1.1 gwr if (debug > 1) { 704 1.1 gwr report(LOG_NOTICE, "IP address not found: %s", 705 1.1 gwr inet_ntoa(bp->bp_ciaddr)); 706 1.1 gwr } 707 1.1 gwr return; 708 1.1 gwr } 709 1.1 gwr } 710 1.1 gwr 711 1.2 gwr if (debug) { 712 1.2 gwr report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr), 713 1.2 gwr hp->hostname->string); 714 1.2 gwr } 715 1.2 gwr 716 1.2 gwr /* 717 1.2 gwr * If there is a response delay threshold, ignore requests 718 1.2 gwr * with a timestamp lower than the threshold. 719 1.2 gwr */ 720 1.2 gwr if (hp->flags.min_wait) { 721 1.2 gwr u_int32 t = (u_int32) ntohs(bp->bp_secs); 722 1.2 gwr if (t < hp->min_wait) { 723 1.2 gwr if (debug > 1) 724 1.2 gwr report(LOG_INFO, 725 1.2 gwr "ignoring request due to timestamp (%d < %d)", 726 1.1 gwr t, hp->min_wait); 727 1.1 gwr return; 728 1.1 gwr } 729 1.1 gwr } 730 1.1 gwr 731 1.1 gwr #ifdef YORK_EX_OPTION 732 1.1 gwr /* 733 1.1 gwr * The need for the "ex" tag arose out of the need to empty 734 1.1 gwr * shared networked drives on diskless PCs. This solution is 735 1.1 gwr * not very clean but it does work fairly well. 736 1.1 gwr * Written by Edmund J. Sutcliffe <edmund (at) york.ac.uk> 737 1.1 gwr * 738 1.1 gwr * XXX - This could compromise security if a non-trusted user 739 1.1 gwr * managed to write an entry in the bootptab with :ex=trojan: 740 1.1 gwr * so I would leave this turned off unless you need it. -gwr 741 1.19 itojun */ 742 1.19 itojun /* Run a program, passing the client name as a parameter. */ 743 1.19 itojun if (hp->flags.exec_file) { 744 1.19 itojun char tst[100]; 745 1.1 gwr /* XXX - Check string lengths? -gwr */ 746 1.1 gwr strlcpy(tst, hp->exec_file->string, sizeof(tst)); 747 1.1 gwr strlcat(tst, " ", sizeof(tst)); 748 1.1 gwr strlcat(tst, hp->hostname->string, sizeof(tst)); 749 1.1 gwr strlcat(tst, " &", sizeof(tst)); 750 1.1 gwr if (debug) 751 1.1 gwr report(LOG_INFO, "executing %s", tst); 752 1.1 gwr system(tst); /* Hope this finishes soon... */ 753 1.1 gwr } 754 1.1 gwr #endif /* YORK_EX_OPTION */ 755 1.1 gwr 756 1.1 gwr /* 757 1.1 gwr * If a specific TFTP server address was specified in the bootptab file, 758 1.1 gwr * fill it in, otherwise zero it. 759 1.1 gwr * XXX - Rather than zero it, should it be the bootpd address? -gwr 760 1.1 gwr */ 761 1.1 gwr (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ? 762 1.1 gwr hp->bootserver.s_addr : 0L; 763 1.1 gwr 764 1.1 gwr #ifdef STANFORD_PROM_COMPAT 765 1.1 gwr /* 766 1.1 gwr * Stanford bootp PROMs (for a Sun?) have no way to leave 767 1.1 gwr * the boot file name field blank (because the boot file 768 1.1 gwr * name is automatically generated from some index). 769 1.1 gwr * As a work-around, this little hack allows those PROMs to 770 1.1 gwr * specify "sunboot14" with the same effect as a NULL name. 771 1.1 gwr * (The user specifies boot device 14 or some such magic.) 772 1.1 gwr */ 773 1.1 gwr if (strcmp(bp->bp_file, "sunboot14") == 0) 774 1.1 gwr bp->bp_file[0] = '\0'; /* treat it as unspecified */ 775 1.1 gwr #endif 776 1.1 gwr 777 1.1 gwr /* 778 1.1 gwr * Fill in the client's proper bootfile. 779 1.1 gwr * 780 1.1 gwr * If the client specifies an absolute path, try that file with a 781 1.1 gwr * ".host" suffix and then without. If the file cannot be found, no 782 1.1 gwr * reply is made at all. 783 1.1 gwr * 784 1.1 gwr * If the client specifies a null or relative file, use the following 785 1.1 gwr * table to determine the appropriate action: 786 1.1 gwr * 787 1.1 gwr * Homedir Bootfile Client's file 788 1.1 gwr * specified? specified? specification Action 789 1.1 gwr * ------------------------------------------------------------------- 790 1.1 gwr * No No Null Send null filename 791 1.1 gwr * No No Relative Discard request 792 1.1 gwr * No Yes Null Send if absolute else null 793 1.1 gwr * No Yes Relative Discard request *XXX 794 1.1 gwr * Yes No Null Send null filename 795 1.1 gwr * Yes No Relative Lookup with ".host" 796 1.1 gwr * Yes Yes Null Send home/boot or bootfile 797 1.2 gwr * Yes Yes Relative Lookup with ".host" *XXX 798 1.2 gwr * 799 1.2 gwr */ 800 1.2 gwr 801 1.2 gwr /* 802 1.2 gwr * XXX - I don't like the policy of ignoring a client when the 803 1.2 gwr * boot file is not accessible. The TFTP server might not be 804 1.1 gwr * running on the same machine as the BOOTP server, in which 805 1.1 gwr * case checking accessibility of the boot file is pointless. 806 1.2 gwr * 807 1.2 gwr * Therefore, file accessibility is now demanded ONLY if you 808 1.2 gwr * define CHECK_FILE_ACCESS in the Makefile options. -gwr 809 1.2 gwr */ 810 1.2 gwr 811 1.1 gwr /* 812 1.22 xtraeme * The "real" path is as seen by the BOOTP daemon on this 813 1.22 xtraeme * machine, while the client path is relative to the TFTP 814 1.25 apb * daemon chroot directory (i.e. /tftpboot). 815 1.1 gwr */ 816 1.22 xtraeme if (hp->flags.tftpdir) { 817 1.22 xtraeme strlcpy(lrealpath, hp->tftpdir->string, sizeof(lrealpath)); 818 1.26 joerg clntpath = &lrealpath[strlen(lrealpath)]; 819 1.1 gwr clntpathmaxlen = sizeof(lrealpath) + lrealpath - clntpath; 820 1.1 gwr } else { 821 1.2 gwr lrealpath[0] = '\0'; 822 1.2 gwr clntpath = lrealpath; 823 1.2 gwr clntpathmaxlen = sizeof(lrealpath); 824 1.2 gwr } 825 1.2 gwr 826 1.1 gwr /* 827 1.11 mrg * Determine client's requested homedir and bootfile. 828 1.11 mrg */ 829 1.2 gwr homedir = NULL; 830 1.11 mrg bootfile = NULL; 831 1.11 mrg if (bp->bp_file[0]) { 832 1.11 mrg char *t; 833 1.11 mrg 834 1.11 mrg homedir = bp->bp_file; 835 1.11 mrg 836 1.12 briggs /* make sure that the file is nul terminated */ 837 1.11 mrg for (t = homedir; t - homedir < BP_FILE_LEN; t++) 838 1.11 mrg if (*t == '\0') 839 1.11 mrg break; 840 1.2 gwr if (t - homedir < BP_FILE_LEN) { 841 1.2 gwr report(LOG_INFO, "requested path length > BP_FILE_LEN file = \"%s\", nul terminating", homedir); 842 1.2 gwr homedir[BP_FILE_LEN - 1] = '\0'; 843 1.2 gwr } 844 1.2 gwr 845 1.1 gwr bootfile = strrchr(homedir, '/'); 846 1.2 gwr if (bootfile) { 847 1.2 gwr if (homedir == bootfile) 848 1.2 gwr homedir = NULL; 849 1.1 gwr *bootfile++ = '\0'; 850 1.2 gwr } else { 851 1.2 gwr /* no "/" in the string */ 852 1.2 gwr bootfile = homedir; 853 1.2 gwr homedir = NULL; 854 1.1 gwr } 855 1.1 gwr if (debug > 2) { 856 1.1 gwr report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", 857 1.1 gwr (homedir) ? homedir : "", 858 1.2 gwr (bootfile) ? bootfile : ""); 859 1.2 gwr } 860 1.2 gwr } 861 1.2 gwr 862 1.2 gwr /* 863 1.2 gwr * Specifications in bootptab override client requested values. 864 1.2 gwr */ 865 1.2 gwr if (hp->flags.homedir) 866 1.2 gwr homedir = hp->homedir->string; 867 1.2 gwr if (hp->flags.bootfile) 868 1.2 gwr bootfile = hp->bootfile->string; 869 1.18 itojun 870 1.22 xtraeme /* 871 1.22 xtraeme * Construct bootfile path. 872 1.2 gwr */ 873 1.2 gwr if (homedir) { 874 1.2 gwr if (homedir[0] != '/') 875 1.11 mrg strlcat(lrealpath, "/", sizeof(lrealpath)); 876 1.22 xtraeme strlcat(lrealpath, homedir, sizeof(lrealpath)); 877 1.22 xtraeme homedir = NULL; 878 1.11 mrg } 879 1.22 xtraeme if (bootfile) { 880 1.22 xtraeme if (bootfile[0] != '/') { 881 1.2 gwr strlcat(lrealpath, "/", sizeof(lrealpath)); 882 1.2 gwr lrealpath[sizeof(lrealpath) - 1] = '\0'; 883 1.2 gwr } 884 1.2 gwr strlcat(lrealpath, bootfile, sizeof(lrealpath)); 885 1.1 gwr lrealpath[sizeof(lrealpath) - 1] = '\0'; 886 1.1 gwr bootfile = NULL; 887 1.2 gwr } 888 1.25 apb 889 1.25 apb /* 890 1.22 xtraeme * First try to find the file with a ".host" suffix 891 1.2 gwr */ 892 1.22 xtraeme n = strlen(clntpath); 893 1.2 gwr strlcat(clntpath, ".", clntpathmaxlen); 894 1.2 gwr strlcat(clntpath, hp->hostname->string, clntpathmaxlen); 895 1.2 gwr if (chk_access(lrealpath, &bootsize) < 0) { 896 1.1 gwr clntpath[n] = 0; /* Try it without the suffix */ 897 1.1 gwr if (chk_access(lrealpath, &bootsize) < 0) { 898 1.1 gwr /* neither "file.host" nor "file" was found */ 899 1.1 gwr #ifdef CHECK_FILE_ACCESS 900 1.1 gwr 901 1.1 gwr if (bp->bp_file[0]) { 902 1.2 gwr /* 903 1.1 gwr * Client wanted specific file 904 1.1 gwr * and we didn't have it. 905 1.2 gwr */ 906 1.2 gwr report(LOG_NOTICE, 907 1.2 gwr "requested file not found: \"%s\"", clntpath); 908 1.2 gwr return; 909 1.2 gwr } 910 1.2 gwr /* 911 1.2 gwr * Client didn't ask for a specific file and we couldn't 912 1.1 gwr * access the default file, so just zero-out the bootfile 913 1.1 gwr * field in the packet and continue processing the reply. 914 1.1 gwr */ 915 1.2 gwr bzero(bp->bp_file, sizeof(bp->bp_file)); 916 1.2 gwr goto null_file_name; 917 1.2 gwr 918 1.2 gwr #else /* CHECK_FILE_ACCESS */ 919 1.2 gwr 920 1.2 gwr /* Complain only if boot file size was needed. */ 921 1.2 gwr if (hp->flags.bootsize_auto) { 922 1.1 gwr report(LOG_ERR, "can not determine size of file \"%s\"", 923 1.1 gwr clntpath); 924 1.18 itojun } 925 1.2 gwr 926 1.2 gwr #endif /* CHECK_FILE_ACCESS */ 927 1.1 gwr } 928 1.8 lukem } 929 1.2 gwr strlcpy(bp->bp_file, clntpath, sizeof(bp->bp_file)); 930 1.8 lukem if (debug > 2) 931 1.1 gwr report(LOG_INFO, "bootfile=\"%s\"", clntpath); 932 1.1 gwr 933 1.2 gwr #ifdef CHECK_FILE_ACCESS 934 1.2 gwr null_file_name: 935 1.2 gwr #endif /* CHECK_FILE_ACCESS */ 936 1.1 gwr 937 1.1 gwr 938 1.1 gwr /* 940 1.1 gwr * Handle vendor options based on magic number. 941 1.1 gwr */ 942 1.1 gwr 943 1.1 gwr if (debug > 1) { 944 1.1 gwr report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", 945 1.1 gwr (int) ((bp->bp_vend)[0]), 946 1.1 gwr (int) ((bp->bp_vend)[1]), 947 1.1 gwr (int) ((bp->bp_vend)[2]), 948 1.1 gwr (int) ((bp->bp_vend)[3])); 949 1.1 gwr } 950 1.1 gwr /* 951 1.1 gwr * If this host isn't set for automatic vendor info then copy the 952 1.1 gwr * specific cookie into the bootp packet, thus forcing a certain 953 1.1 gwr * reply format. Only force reply format if user specified it. 954 1.1 gwr */ 955 1.1 gwr if (hp->flags.vm_cookie) { 956 1.1 gwr /* Slam in the user specified magic number. */ 957 1.1 gwr bcopy(hp->vm_cookie, bp->bp_vend, 4); 958 1.1 gwr } 959 1.1 gwr /* 960 1.1 gwr * Figure out the format for the vendor-specific info. 961 1.1 gwr * Note that bp->bp_vend may have been set above. 962 1.1 gwr */ 963 1.1 gwr if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) { 964 1.1 gwr /* RFC1048 conformant bootp client */ 965 1.1 gwr dovend_rfc1048(bp, hp, bootsize); 966 1.1 gwr if (debug > 1) { 967 1.1 gwr report(LOG_INFO, "sending reply (with RFC1048 options)"); 968 1.1 gwr } 969 1.1 gwr } 970 1.1 gwr #ifdef VEND_CMU 971 1.1 gwr else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { 972 1.1 gwr dovend_cmu(bp, hp); 973 1.1 gwr if (debug > 1) { 974 1.1 gwr report(LOG_INFO, "sending reply (with CMU options)"); 975 1.1 gwr } 976 1.1 gwr } 977 1.1 gwr #endif 978 1.1 gwr else { 979 1.1 gwr if (debug > 1) { 980 1.1 gwr report(LOG_INFO, "sending reply (with no options)"); 981 1.1 gwr } 982 1.1 gwr } 983 1.1 gwr 984 1.1 gwr dest = (hp->flags.reply_addr) ? 985 1.1 gwr hp->reply_addr.s_addr : 0L; 986 1.1 gwr 987 1.1 gwr /* not forwarded */ 988 1.1 gwr sendreply(0, dest); 989 1.1 gwr } 990 1.14 wiz 991 1.1 gwr 992 1.1 gwr /* 993 1.1 gwr * Process BOOTREPLY packet. 994 1.1 gwr */ 995 1.1 gwr PRIVATE void 996 1.1 gwr handle_reply(void) 997 1.1 gwr { 998 1.1 gwr if (debug) { 999 1.1 gwr report(LOG_INFO, "processing boot reply"); 1000 1.1 gwr } 1001 1.1 gwr /* forwarded, no destination override */ 1002 1.1 gwr sendreply(1, 0); 1003 1.1 gwr } 1004 1.1 gwr 1005 1.14 wiz 1006 1.1 gwr /* 1007 1.1 gwr * Send a reply packet to the client. 'forward' flag is set if we are 1008 1.1 gwr * not the originator of this reply packet. 1009 1.1 gwr */ 1010 1.1 gwr PRIVATE void 1011 1.1 gwr sendreply(int forward, int32 dst_override) 1012 1.1 gwr { 1013 1.1 gwr struct bootp *bp = (struct bootp *) pktbuf; 1014 1.2 gwr struct in_addr dst; 1015 1.2 gwr u_short port = bootpc_port; 1016 1.2 gwr unsigned char *ha; 1017 1.2 gwr int len; 1018 1.2 gwr 1019 1.2 gwr /* 1020 1.1 gwr * XXX - Should honor bp_flags "broadcast" bit here. 1021 1.17 wiz * Temporary workaround: use the :ra=ADDR: option to 1022 1.1 gwr * set the reply address to the broadcast address. 1023 1.1 gwr */ 1024 1.1 gwr 1025 1.1 gwr /* 1026 1.1 gwr * If the destination address was specified explicitly 1027 1.1 gwr * (i.e. the broadcast address for HP compatibility) 1028 1.1 gwr * then send the response to that address. Otherwise, 1029 1.1 gwr * act in accordance with RFC951: 1030 1.1 gwr * If the client IP address is specified, use that 1031 1.1 gwr * else if gateway IP address is specified, use that 1032 1.1 gwr * else make a temporary arp cache entry for the client's 1033 1.1 gwr * NEW IP/hardware address and use that. 1034 1.1 gwr */ 1035 1.1 gwr if (dst_override) { 1036 1.1 gwr dst.s_addr = dst_override; 1037 1.1 gwr if (debug > 1) { 1038 1.1 gwr report(LOG_INFO, "reply address override: %s", 1039 1.1 gwr inet_ntoa(dst)); 1040 1.1 gwr } 1041 1.1 gwr } else if (bp->bp_ciaddr.s_addr) { 1042 1.1 gwr dst = bp->bp_ciaddr; 1043 1.1 gwr } else if (bp->bp_giaddr.s_addr && forward == 0) { 1044 1.1 gwr dst = bp->bp_giaddr; 1045 1.1 gwr port = bootps_port; 1046 1.1 gwr if (debug > 1) { 1047 1.1 gwr report(LOG_INFO, "sending reply to gateway %s", 1048 1.1 gwr inet_ntoa(dst)); 1049 1.1 gwr } 1050 1.2 gwr } else { 1051 1.1 gwr dst = bp->bp_yiaddr; 1052 1.1 gwr ha = bp->bp_chaddr; 1053 1.1 gwr len = bp->bp_hlen; 1054 1.1 gwr if (len > MAXHADDRLEN) 1055 1.1 gwr len = MAXHADDRLEN; 1056 1.1 gwr 1057 1.1 gwr if (debug > 1) 1058 1.1 gwr report(LOG_INFO, "setarp %s - %s", 1059 1.1 gwr inet_ntoa(dst), haddrtoa(ha, len)); 1060 1.1 gwr setarp(s, &dst, ha, len); 1061 1.1 gwr } 1062 1.1 gwr 1063 1.1 gwr if ((forward == 0) && 1064 1.1 gwr (bp->bp_siaddr.s_addr == 0)) 1065 1.1 gwr { 1066 1.1 gwr struct ifreq *ifr; 1067 1.1 gwr struct in_addr siaddr; 1068 1.1 gwr /* 1069 1.1 gwr * If we are originating this reply, we 1070 1.1 gwr * need to find our own interface address to 1071 1.1 gwr * put in the bp_siaddr field of the reply. 1072 1.1 gwr * If this server is multi-homed, pick the 1073 1.1 gwr * 'best' interface (the one on the same net 1074 1.1 gwr * as the client). Of course, the client may 1075 1.1 gwr * be on the other side of a BOOTP gateway... 1076 1.1 gwr */ 1077 1.1 gwr ifr = getif(s, &dst); 1078 1.1 gwr if (ifr) { 1079 1.1 gwr struct sockaddr_in *sip; 1080 1.1 gwr sip = (struct sockaddr_in *) &(ifr->ifr_addr); 1081 1.1 gwr siaddr = sip->sin_addr; 1082 1.1 gwr } else { 1083 1.1 gwr /* Just use my "official" IP address. */ 1084 1.1 gwr siaddr = my_ip_addr; 1085 1.1 gwr } 1086 1.1 gwr 1087 1.1 gwr /* XXX - No need to set bp_giaddr here. */ 1088 1.1 gwr 1089 1.1 gwr /* Finally, set the server address field. */ 1090 1.1 gwr bp->bp_siaddr = siaddr; 1091 1.1 gwr } 1092 1.1 gwr /* Set up socket address for send. */ 1093 1.1 gwr send_addr.sin_family = AF_INET; 1094 1.1 gwr send_addr.sin_port = htons(port); 1095 1.1 gwr send_addr.sin_addr = dst; 1096 1.1 gwr 1097 1.1 gwr /* Send reply with same size packet as request used. */ 1098 1.1 gwr if (sendto(s, pktbuf, pktlen, 0, 1099 1.1 gwr (struct sockaddr *) &send_addr, 1100 1.1 gwr sizeof(send_addr)) < 0) 1101 1.1 gwr { 1102 1.1 gwr report(LOG_ERR, "sendto: %s", get_network_errmsg()); 1103 1.1 gwr } 1104 1.1 gwr } /* sendreply */ 1105 1.1 gwr 1106 1.1 gwr 1108 1.1 gwr /* nmatch() - now in getif.c */ 1109 1.1 gwr /* setarp() - now in hwaddr.c */ 1110 1.1 gwr 1111 1.1 gwr 1112 1.1 gwr /* 1113 1.1 gwr * This call checks read access to a file. It returns 0 if the file given 1114 1.1 gwr * by "path" exists and is publically readable. A value of -1 is returned if 1115 1.1 gwr * access is not permitted or an error occurs. Successful calls also 1116 1.14 wiz * return the file size in bytes using the long pointer "filesize". 1117 1.1 gwr * 1118 1.1 gwr * The read permission bit for "other" users is checked. This bit must be 1119 1.1 gwr * set for tftpd(8) to allow clients to read the file. 1120 1.1 gwr */ 1121 1.1 gwr 1122 1.1 gwr PRIVATE int 1123 1.1 gwr chk_access(char *path, int32 *filesize) 1124 1.1 gwr { 1125 1.1 gwr struct stat st; 1126 1.1 gwr 1127 1.1 gwr if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { 1128 1.1 gwr *filesize = (int32) st.st_size; 1129 1.1 gwr return 0; 1130 1.1 gwr } else { 1131 1.1 gwr return -1; 1132 1.1 gwr } 1133 1.1 gwr } 1134 1.1 gwr 1135 1.1 gwr 1137 1.1 gwr /* 1138 1.1 gwr * Now in dumptab.c : 1139 1.1 gwr * dumptab() 1140 1.1 gwr * dump_host() 1141 1.1 gwr * list_ipaddresses() 1142 1.1 gwr */ 1143 1.1 gwr 1144 1.14 wiz #ifdef VEND_CMU 1145 1.1 gwr 1146 1.1 gwr /* 1147 1.1 gwr * Insert the CMU "vendor" data for the host pointed to by "hp" into the 1148 1.1 gwr * bootp packet pointed to by "bp". 1149 1.1 gwr */ 1150 1.1 gwr 1151 1.1 gwr PRIVATE void 1152 1.1 gwr dovend_cmu(struct bootp *bp, struct host *hp) 1153 1.1 gwr { 1154 1.1 gwr struct cmu_vend *vendp; 1155 1.1 gwr struct in_addr_list *taddr; 1156 1.1 gwr 1157 1.1 gwr /* 1158 1.1 gwr * Initialize the entire vendor field to zeroes. 1159 1.19 itojun */ 1160 1.1 gwr bzero(bp->bp_vend, sizeof(bp->bp_vend)); 1161 1.1 gwr 1162 1.1 gwr /* 1163 1.1 gwr * Fill in vendor information. Subnet mask, default gateway, 1164 1.1 gwr * domain name server, ien name server, time server 1165 1.1 gwr */ 1166 1.1 gwr vendp = (struct cmu_vend *) bp->bp_vend; 1167 1.1 gwr strlcpy(vendp->v_magic, (char *)vm_cmu, sizeof(vendp->v_magic)); 1168 1.1 gwr if (hp->flags.subnet_mask) { 1169 1.1 gwr (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; 1170 1.1 gwr (vendp->v_flags) |= VF_SMASK; 1171 1.1 gwr if (hp->flags.gateway) { 1172 1.1 gwr (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; 1173 1.1 gwr } 1174 1.1 gwr } 1175 1.1 gwr if (hp->flags.domain_server) { 1176 1.1 gwr taddr = hp->domain_server; 1177 1.1 gwr if (taddr->addrcount > 0) { 1178 1.1 gwr (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; 1179 1.1 gwr if (taddr->addrcount > 1) { 1180 1.1 gwr (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; 1181 1.1 gwr } 1182 1.1 gwr } 1183 1.1 gwr } 1184 1.1 gwr if (hp->flags.name_server) { 1185 1.1 gwr taddr = hp->name_server; 1186 1.1 gwr if (taddr->addrcount > 0) { 1187 1.1 gwr (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; 1188 1.1 gwr if (taddr->addrcount > 1) { 1189 1.1 gwr (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; 1190 1.1 gwr } 1191 1.1 gwr } 1192 1.1 gwr } 1193 1.1 gwr if (hp->flags.time_server) { 1194 1.1 gwr taddr = hp->time_server; 1195 1.1 gwr if (taddr->addrcount > 0) { 1196 1.1 gwr (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; 1197 1.1 gwr if (taddr->addrcount > 1) { 1198 1.1 gwr (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; 1199 1.1 gwr } 1200 1.1 gwr } 1201 1.1 gwr } 1202 1.1 gwr /* Log message now done by caller. */ 1203 1.1 gwr } /* dovend_cmu */ 1204 1.1 gwr 1205 1.1 gwr #endif /* VEND_CMU */ 1206 1.1 gwr 1207 1.1 gwr 1209 1.1 gwr 1210 1.1 gwr /* 1211 1.1 gwr * Insert the RFC1048 vendor data for the host pointed to by "hp" into the 1212 1.14 wiz * bootp packet pointed to by "bp". 1213 1.1 gwr */ 1214 1.1 gwr #define NEED(LEN, MSG) do \ 1215 1.1 gwr if (bytesleft < (LEN)) { \ 1216 1.1 gwr report(LOG_NOTICE, noroom, \ 1217 1.13 is hp->hostname->string, MSG); \ 1218 1.1 gwr return; \ 1219 1.1 gwr } while (0) 1220 1.2 gwr PRIVATE void 1221 1.2 gwr dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize) 1222 1.2 gwr { 1223 1.2 gwr int bytesleft, len; 1224 1.2 gwr byte *vp; 1225 1.2 gwr 1226 1.2 gwr static const char noroom[] = "%s: No room for \"%s\" option"; 1227 1.2 gwr 1228 1.2 gwr vp = bp->bp_vend; 1229 1.23 lukem 1230 1.2 gwr if (hp->flags.msg_size) { 1231 1.2 gwr pktlen = hp->msg_size; 1232 1.2 gwr } else { 1233 1.2 gwr /* 1234 1.2 gwr * If the request was longer than the official length, build 1235 1.2 gwr * a response of that same length where the additional length 1236 1.2 gwr * is assumed to be part of the bp_vend (options) area. 1237 1.2 gwr */ 1238 1.2 gwr if (pktlen > (int)sizeof(*bp)) { 1239 1.2 gwr if (debug > 1) 1240 1.2 gwr report(LOG_INFO, "request message length=%d", pktlen); 1241 1.2 gwr } 1242 1.22 xtraeme /* 1243 1.2 gwr * Check whether the request contains the option: 1244 1.2 gwr * Maximum DHCP Message Size (RFC1533 sec. 9.8) 1245 1.2 gwr * and if so, override the response length with its value. 1246 1.2 gwr * This request must lie within the first BP_VEND_LEN 1247 1.2 gwr * bytes of the option space. 1248 1.2 gwr */ 1249 1.2 gwr { 1250 1.2 gwr byte *p, *ep; 1251 1.2 gwr byte tag, llen; 1252 1.2 gwr short msgsz = 0; 1253 1.2 gwr 1254 1.2 gwr p = vp + 4; 1255 1.22 xtraeme ep = p + BP_VEND_LEN - 4; 1256 1.2 gwr while (p < ep) { 1257 1.2 gwr tag = *p++; 1258 1.22 xtraeme /* Check for tags with no data first. */ 1259 1.2 gwr if (tag == TAG_PAD) 1260 1.2 gwr continue; 1261 1.2 gwr if (tag == TAG_END) 1262 1.2 gwr break; 1263 1.2 gwr /* Now scan the length byte. */ 1264 1.2 gwr llen = *p++; 1265 1.2 gwr switch (tag) { 1266 1.28 andvar case TAG_MAX_MSGSZ: 1267 1.22 xtraeme if (llen == 2) { 1268 1.2 gwr bcopy(p, (char*)&msgsz, 2); 1269 1.2 gwr msgsz = ntohs(msgsz); 1270 1.23 lukem } 1271 1.2 gwr break; 1272 1.2 gwr case TAG_SUBNET_MASK: 1273 1.2 gwr /* XXX - Should preserve this if given... */ 1274 1.2 gwr break; 1275 1.2 gwr } /* switch */ 1276 1.2 gwr p += llen; 1277 1.2 gwr } 1278 1.23 lukem 1279 1.2 gwr if (msgsz > (int)sizeof(*bp)) { 1280 1.2 gwr if (debug > 1) 1281 1.2 gwr report(LOG_INFO, "request has DHCP msglen=%d", msgsz); 1282 1.2 gwr pktlen = msgsz; 1283 1.23 lukem } 1284 1.2 gwr } 1285 1.2 gwr } 1286 1.2 gwr 1287 1.2 gwr if (pktlen < (int)sizeof(*bp)) { 1288 1.2 gwr report(LOG_ERR, "invalid response length=%d", pktlen); 1289 1.2 gwr pktlen = sizeof(*bp); 1290 1.2 gwr } 1291 1.1 gwr bytesleft = ((byte*)bp + pktlen) - vp; 1292 1.1 gwr if (pktlen > (int)sizeof(*bp)) { 1293 1.1 gwr if (debug > 1) 1294 1.1 gwr report(LOG_INFO, "extended reply, length=%d, options=%d", 1295 1.1 gwr pktlen, bytesleft); 1296 1.1 gwr } 1297 1.1 gwr 1298 1.1 gwr /* Copy in the magic cookie */ 1299 1.1 gwr bcopy(vm_rfc1048, vp, 4); 1300 1.1 gwr vp += 4; 1301 1.1 gwr bytesleft -= 4; 1302 1.1 gwr 1303 1.1 gwr if (hp->flags.subnet_mask) { 1304 1.1 gwr /* always enough room here. */ 1305 1.1 gwr *vp++ = TAG_SUBNET_MASK;/* -1 byte */ 1306 1.1 gwr *vp++ = 4; /* -1 byte */ 1307 1.1 gwr insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ 1308 1.1 gwr bytesleft -= 6; /* Fix real count */ 1309 1.23 lukem if (hp->flags.gateway) { 1310 1.1 gwr (void) insert_ip(TAG_GATEWAY, 1311 1.1 gwr hp->gateway, 1312 1.1 gwr &vp, &bytesleft); 1313 1.1 gwr } 1314 1.1 gwr } 1315 1.1 gwr if (hp->flags.bootsize) { 1316 1.1 gwr /* always enough room here */ 1317 1.1 gwr bootsize = (hp->flags.bootsize_auto) ? 1318 1.1 gwr ((bootsize + 511) / 512) : ((int32_t)hp->bootsize); /* Round up */ 1319 1.1 gwr *vp++ = TAG_BOOT_SIZE; 1320 1.1 gwr *vp++ = 2; 1321 1.1 gwr *vp++ = (byte) ((bootsize >> 8) & 0xFF); 1322 1.1 gwr *vp++ = (byte) (bootsize & 0xFF); 1323 1.1 gwr bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ 1324 1.1 gwr } 1325 1.1 gwr /* 1326 1.1 gwr * This one is special: Remaining options go in the ext file. 1327 1.1 gwr * Only the subnet_mask, bootsize, and gateway should precede. 1328 1.1 gwr */ 1329 1.1 gwr if (hp->flags.exten_file) { 1330 1.1 gwr /* 1331 1.1 gwr * Check for room for exten_file. Add 3 to account for 1332 1.1 gwr * TAG_EXTEN_FILE, length, and TAG_END. 1333 1.1 gwr */ 1334 1.1 gwr len = strlen(hp->exten_file->string); 1335 1.1 gwr NEED((len + 3), "ef"); 1336 1.1 gwr *vp++ = TAG_EXTEN_FILE; 1337 1.1 gwr *vp++ = (byte) (len & 0xFF); 1338 1.1 gwr bcopy(hp->exten_file->string, vp, len); 1339 1.1 gwr vp += len; 1340 1.1 gwr *vp++ = TAG_END; 1341 1.1 gwr bytesleft -= len + 3; 1342 1.1 gwr return; /* no more options here. */ 1343 1.1 gwr } 1344 1.1 gwr /* 1345 1.1 gwr * The remaining options are inserted by the following 1346 1.1 gwr * function (which is shared with bootpef.c). 1347 1.1 gwr * Keep back one byte for the TAG_END. 1348 1.1 gwr */ 1349 1.1 gwr len = dovend_rfc1497(hp, vp, bytesleft - 1); 1350 1.1 gwr vp += len; 1351 1.1 gwr bytesleft -= len; 1352 1.1 gwr 1353 1.1 gwr /* There should be at least one byte left. */ 1354 1.1 gwr NEED(1, "(end)"); 1355 1.1 gwr *vp++ = TAG_END; 1356 1.1 gwr bytesleft--; 1357 1.1 gwr 1358 1.1 gwr /* Log message done by caller. */ 1359 1.1 gwr if (bytesleft > 0) { 1360 1.1 gwr /* 1361 1.1 gwr * Zero out any remaining part of the vendor area. 1362 1.1 gwr */ 1363 1.1 gwr bzero(vp, bytesleft); 1364 1.1 gwr } 1365 1.1 gwr } /* dovend_rfc1048 */ 1366 1.1 gwr #undef NEED 1367 1.1 gwr 1368 1.1 gwr 1370 1.1 gwr /* 1371 1.1 gwr * Now in readfile.c: 1372 1.1 gwr * hwlookcmp() 1373 1.1 gwr * iplookcmp() 1374 1.1 gwr */ 1375 1.1 gwr 1376 1.1 gwr /* haddrtoa() - now in hwaddr.c */ 1377 1.1 gwr /* 1378 1.1 gwr * Now in dovend.c: 1379 1.1 gwr * insert_ip() 1380 1.1 gwr * insert_generic() 1381 1.1 gwr * insert_u_long() 1382 1.1 gwr */ 1383 1.1 gwr 1384 1.1 gwr /* get_errmsg() - now in report.c */ 1385 1.1 gwr 1386 1.1 gwr /* 1387 * Local Variables: 1388 * tab-width: 4 1389 * c-indent-level: 4 1390 * c-argdecl-indent: 4 1391 * c-continued-statement-offset: 4 1392 * c-continued-brace-offset: -4 1393 * c-label-offset: -4 1394 * c-brace-offset: 0 1395 * End: 1396 */ 1397