bootpd.c revision 116371
1105197Ssam/************************************************************************ 2112758Ssam Copyright 1988, 1991 by Carnegie Mellon University 3139823Simp 4112758Ssam All Rights Reserved 5112758Ssam 6112758SsamPermission to use, copy, modify, and distribute this software and its 7112758Ssamdocumentation for any purpose and without fee is hereby granted, provided 8112758Ssamthat the above copyright notice appear in all copies and that both that 9112758Ssamcopyright notice and this permission notice appear in supporting 10112758Ssamdocumentation, and that the name of Carnegie Mellon University not be used 11112758Ssamin advertising or publicity pertaining to distribution of the software 12112758Ssamwithout specific, written prior permission. 13112758Ssam 14112758SsamCARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 15112758SsamSOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 16112758SsamIN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 17112758SsamDAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 18112758SsamPROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 19112758SsamACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20112758SsamSOFTWARE. 21112758Ssam 22112758Ssam************************************************************************/ 23112758Ssam 24112758Ssam/* 25112758Ssam * BOOTP (bootstrap protocol) server daemon. 26112758Ssam * 27112758Ssam * Answers BOOTP request packets from booting client machines. 28112758Ssam * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol. 29112758Ssam * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions. 30112758Ssam * See RFC 1395 for option tags 14-17. 31112758Ssam * See accompanying man page -- bootpd.8 32112758Ssam * 33112758Ssam * HISTORY 34112758Ssam * See ./Changes 35112758Ssam * 36112758Ssam * BUGS 37112758Ssam * See ./ToDo 38105197Ssam */ 39105197Ssam 40105197Ssam#include <sys/cdefs.h> 41105197Ssam__FBSDID("$FreeBSD: head/libexec/bootpd/bootpd.c 116371 2003-06-15 03:08:37Z jmg $"); 42105197Ssam 43105197Ssam#include <sys/types.h> 44105197Ssam#include <sys/param.h> 45105197Ssam#include <sys/socket.h> 46105197Ssam#include <sys/ioctl.h> 47105197Ssam#include <sys/file.h> 48105197Ssam#include <sys/time.h> 49105197Ssam#include <sys/stat.h> 50105197Ssam#include <sys/utsname.h> 51105197Ssam 52105197Ssam#include <net/if.h> 53105197Ssam#include <netinet/in.h> 54105197Ssam#include <arpa/inet.h> /* inet_ntoa */ 55291292Sae 56105197Ssam#ifndef NO_UNISTD 57105197Ssam#include <unistd.h> 58105197Ssam#endif 59257176Sglebius 60291292Sae#include <stdlib.h> 61105197Ssam#include <signal.h> 62195699Srwatson#include <stdio.h> 63105197Ssam#include <string.h> 64105197Ssam#include <errno.h> 65105197Ssam#include <ctype.h> 66105197Ssam#include <netdb.h> 67105197Ssam#include <paths.h> 68105197Ssam#include <syslog.h> 69105197Ssam#include <assert.h> 70105197Ssam 71105197Ssam#ifdef NO_SETSID 72105197Ssam# include <fcntl.h> /* for O_RDONLY, etc */ 73105197Ssam#endif 74105197Ssam 75105197Ssam#ifndef USE_BFUNCS 76105197Ssam# include <memory.h> 77105197Ssam/* Yes, memcpy is OK here (no overlapped copies). */ 78105197Ssam# define bcopy(a,b,c) memcpy(b,a,c) 79105197Ssam# define bzero(p,l) memset(p,0,l) 80105197Ssam# define bcmp(a,b,c) memcmp(a,b,c) 81105197Ssam#endif 82105197Ssam 83105197Ssam#include "bootp.h" 84105197Ssam#include "hash.h" 85105197Ssam#include "hwaddr.h" 86105197Ssam#include "bootpd.h" 87105197Ssam#include "dovend.h" 88105197Ssam#include "getif.h" 89105197Ssam#include "readfile.h" 90105197Ssam#include "report.h" 91105197Ssam#include "tzone.h" 92105197Ssam#include "patchlevel.h" 93105197Ssam 94105197Ssam#ifndef CONFIG_FILE 95105197Ssam#define CONFIG_FILE "/etc/bootptab" 96105197Ssam#endif 97181627Svanhu#ifndef DUMPTAB_FILE 98252028Sae#define DUMPTAB_FILE "/tmp/bootpd.dump" 99252028Sae#endif 100252028Sae 101252028Sae 102252028Sae 103252028Sae/* 104252028Sae * Externals, forward declarations, and global variables 105252028Sae */ 106105197Ssam 107193947Sbzextern void dumptab(char *); 108120585Ssam 109193947SbzPRIVATE void catcher(int); 110120585SsamPRIVATE int chk_access(char *, int32 *); 111105197Ssam#ifdef VEND_CMU 112105197SsamPRIVATE void dovend_cmu(struct bootp *, struct host *); 113214351Sthomas#endif 114170793SbzPRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32); 115105197SsamPRIVATE void handle_reply(void); 116105197SsamPRIVATE void handle_request(void); 117285770SeriPRIVATE void sendreply(int forward, int32 dest_override); 118105197SsamPRIVATE void usage(void); 119105197Ssam 120281695Sae/* 121105197Ssam * IP port numbers for client and server obtained from /etc/services 122105197Ssam */ 123105197Ssam 124119643Ssamu_short bootps_port, bootpc_port; 125221129Sbz 126194062Svanhu 127194062Svanhu/* 128194062Svanhu * Internet socket and interface config structures 129221129Sbz */ 130105197Ssam 131252028Saestruct sockaddr_in bind_addr; /* Listening */ 132105197Ssamstruct sockaddr_in recv_addr; /* Packet source */ 133120585Ssamstruct sockaddr_in send_addr; /* destination */ 134105197Ssam 135170792Sbz 136170792Sbz/* 137170792Sbz * option defaults 138170792Sbz */ 139181803Sbzint debug = 0; /* Debugging flag (level) */ 140181803Sbzstruct timeval actualtimeout = 141181803Sbz{ /* fifteen minutes */ 142105197Ssam 15 * 60L, /* tv_sec */ 143252028Sae 0 /* tv_usec */ 144105197Ssam}; 145105197Ssam 146105197Ssam/* 147105197Ssam * General 148105197Ssam */ 149252028Sae 150120585Ssamint s; /* Socket file descriptor */ 151105197Ssamchar *pktbuf; /* Receive packet buffer */ 152105197Ssamint pktlen; 153105197Ssamchar *progname; 154105197Ssamchar *chdir_path; 155105197Ssamstruct in_addr my_ip_addr; 156105197Ssam 157105197Ssamstatic const char *hostname; 158105197Ssamstatic char default_hostname[MAXHOSTNAMELEN]; 159105197Ssam 160105197Ssam/* Flags set by signal catcher. */ 161105197SsamPRIVATE int do_readtab = 0; 162105197SsamPRIVATE int do_dumptab = 0; 163105197Ssam 164105197Ssam/* 165105197Ssam * Globals below are associated with the bootp database file (bootptab). 166105197Ssam */ 167105197Ssam 168105197Ssamchar *bootptab = CONFIG_FILE; 169105197Ssamchar *bootpd_dump = DUMPTAB_FILE; 170105197Ssam 171105197Ssam 172105197Ssam 173105197Ssam/* 174105197Ssam * Initialization such as command-line processing is done and then the 175105197Ssam * main server loop is started. 176105197Ssam */ 177105197Ssam 178105197Ssamint 179105197Ssammain(argc, argv) 180105197Ssam int argc; 181194062Svanhu char **argv; 182194062Svanhu{ 183194062Svanhu struct timeval *timeout; 184194062Svanhu struct bootp *bp; 185194062Svanhu struct servent *servp; 186194062Svanhu struct hostent *hep; 187105197Ssam char *stmp; 188105197Ssam int n, ba_len, ra_len; 189105197Ssam int nfound; 190105197Ssam fd_set readfds; 191105197Ssam int standalone; 192105197Ssam#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ 193105197Ssam struct sigaction sa; 194105197Ssam#endif 195281693Sae 196281693Sae progname = strrchr(argv[0], '/'); 197281693Sae if (progname) progname++; 198281693Sae else progname = argv[0]; 199281693Sae 200281693Sae /* 201281693Sae * Initialize logging. 202105197Ssam */ 203105197Ssam report_init(0); /* uses progname */ 204105197Ssam 205120585Ssam /* 206105197Ssam * Log startup 207252028Sae */ 208105197Ssam report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); 209105197Ssam 210105197Ssam /* Debugging for compilers with struct padding. */ 211105197Ssam assert(sizeof(struct bootp) == BP_MINPKTSZ); 212105197Ssam 213105197Ssam /* Get space for receiving packets and composing replies. */ 214120585Ssam pktbuf = malloc(MAX_MSG_SIZE); 215281695Sae if (!pktbuf) { 216281695Sae report(LOG_ERR, "malloc failed"); 217252028Sae exit(1); 218105197Ssam } 219105197Ssam bp = (struct bootp *) pktbuf; 220105197Ssam 221105197Ssam /* 222105197Ssam * Check to see if a socket was passed to us from inetd. 223120585Ssam * 224281695Sae * Use getsockname() to determine if descriptor 0 is indeed a socket 225281695Sae * (and thus we are probably a child of inetd) or if it is instead 226252028Sae * something else and we are running standalone. 227105197Ssam */ 228105197Ssam s = 0; 229105197Ssam ba_len = sizeof(bind_addr); 230105197Ssam bzero((char *) &bind_addr, ba_len); 231105197Ssam errno = 0; 232105197Ssam standalone = TRUE; 233105197Ssam if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { 234105197Ssam /* 235105197Ssam * Descriptor 0 is a socket. Assume we are a child of inetd. 236105197Ssam */ 237105197Ssam if (bind_addr.sin_family == AF_INET) { 238105197Ssam standalone = FALSE; 239105197Ssam bootps_port = ntohs(bind_addr.sin_port); 240105197Ssam } else { 241105197Ssam /* Some other type of socket? */ 242105197Ssam report(LOG_ERR, "getsockname: not an INET socket"); 243269699Skevlo } 244106680Ssam } 245269699Skevlo 246269699Skevlo /* 247269699Skevlo * Set defaults that might be changed by option switches. 248269699Skevlo */ 249269699Skevlo stmp = NULL; 250269699Skevlo timeout = &actualtimeout; 251269699Skevlo 252285770Seri if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) { 253285770Seri report(LOG_ERR, "bootpd: can't get hostname\n"); 254269699Skevlo exit(1); 255106680Ssam } 256120585Ssam default_hostname[sizeof(default_hostname) - 1] = '\0'; 257120585Ssam hostname = default_hostname; 258120585Ssam 259120585Ssam /* 260120585Ssam * Read switches. 261120585Ssam */ 262120585Ssam for (argc--, argv++; argc > 0; argc--, argv++) { 263106680Ssam if (argv[0][0] != '-') 264269699Skevlo break; 265269699Skevlo switch (argv[0][1]) { 266106680Ssam 267269699Skevlo case 'c': /* chdir_path */ 268269699Skevlo if (argv[0][2]) { 269269699Skevlo stmp = &(argv[0][2]); 270269699Skevlo } else { 271269699Skevlo argc--; 272269699Skevlo argv++; 273269699Skevlo stmp = argv[0]; 274285770Seri } 275285770Seri if (!stmp || (stmp[0] != '/')) { 276269699Skevlo report(LOG_ERR, 277106680Ssam "bootpd: invalid chdir specification\n"); 278269699Skevlo break; 279120585Ssam } 280120585Ssam chdir_path = stmp; 281120585Ssam break; 282120585Ssam 283120585Ssam case 'd': /* debug level */ 284120585Ssam if (argv[0][2]) { 285120585Ssam stmp = &(argv[0][2]); 286106680Ssam } else if (argv[1] && argv[1][0] == '-') { 287269699Skevlo /* 288269699Skevlo * Backwards-compatible behavior: 289106680Ssam * no parameter, so just increment the debug flag. 290269699Skevlo */ 291269699Skevlo debug++; 292269699Skevlo break; 293269699Skevlo } else { 294269699Skevlo argc--; 295269699Skevlo argv++; 296269699Skevlo stmp = argv[0]; 297285770Seri } 298285770Seri if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 299269699Skevlo report(LOG_ERR, 300106680Ssam "%s: invalid debug level\n", progname); 301106680Ssam break; 302105197Ssam } 303105197Ssam debug = n; 304105197Ssam break; 305105197Ssam 306105197Ssam case 'h': /* override hostname */ 307105197Ssam if (argv[0][2]) { 308105197Ssam stmp = &(argv[0][2]); 309275707Sae } else { 310275707Sae argc--; 311105197Ssam argv++; 312281695Sae stmp = argv[0]; 313291292Sae } 314266800Svanhu if (!stmp) { 315105197Ssam report(LOG_ERR, 316105197Ssam "bootpd: missing hostname\n"); 317105197Ssam break; 318105197Ssam } 319105197Ssam hostname = stmp; 320165222Sbz break; 321165118Sbz 322165118Sbz case 'i': /* inetd mode */ 323165118Sbz standalone = FALSE; 324165118Sbz break; 325105197Ssam 326120585Ssam case 's': /* standalone mode */ 327120585Ssam standalone = TRUE; 328120585Ssam break; 329105197Ssam 330105197Ssam case 't': /* timeout */ 331120585Ssam if (argv[0][2]) { 332105197Ssam stmp = &(argv[0][2]); 333120585Ssam } else { 334105197Ssam argc--; 335120585Ssam argv++; 336105197Ssam stmp = argv[0]; 337105197Ssam } 338105197Ssam if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 339120585Ssam report(LOG_ERR, 340252028Sae "%s: invalid timeout specification\n", progname); 341105197Ssam break; 342105197Ssam } 343105197Ssam actualtimeout.tv_sec = (int32) (60 * n); 344105197Ssam /* 345105197Ssam * If the actual timeout is zero, pass a NULL pointer 346241922Sglebius * to select so it blocks indefinitely, otherwise, 347241922Sglebius * point to the actual timeout value. 348241922Sglebius */ 349241922Sglebius timeout = (n > 0) ? &actualtimeout : NULL; 350105197Ssam break; 351120585Ssam 352281695Sae default: 353281695Sae report(LOG_ERR, "%s: unknown switch: -%c\n", 354252028Sae progname, argv[0][1]); 355105197Ssam usage(); 356105197Ssam break; 357105197Ssam 358105197Ssam } /* switch */ 359105197Ssam } /* for args */ 360105197Ssam 361105197Ssam /* 362105197Ssam * Override default file names if specified on the command line. 363105197Ssam */ 364105197Ssam if (argc > 0) 365105197Ssam bootptab = argv[0]; 366105197Ssam 367105197Ssam if (argc > 1) 368291292Sae bootpd_dump = argv[1]; 369291292Sae 370291292Sae /* 371282132Sae * Get my hostname and IP address. 372266800Svanhu */ 373105197Ssam 374272394Sae hep = gethostbyname(hostname); 375272394Sae if (!hep) { 376105197Ssam report(LOG_ERR, "Can not get my IP address\n"); 377118888Ssam exit(1); 378252028Sae } 379118888Ssam bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); 380118888Ssam 381118888Ssam if (standalone) { 382266800Svanhu /* 383266800Svanhu * Go into background and disassociate from controlling terminal. 384105197Ssam */ 385266800Svanhu if (debug < 3) { 386105197Ssam if (fork()) 387105197Ssam exit(0); 388105197Ssam#ifdef NO_SETSID 389105197Ssam setpgrp(0,0); 390105197Ssam#ifdef TIOCNOTTY 391105197Ssam n = open(_PATH_TTY, O_RDWR); 392105197Ssam if (n >= 0) { 393105197Ssam ioctl(n, TIOCNOTTY, (char *) 0); 394105197Ssam (void) close(n); 395105197Ssam } 396105197Ssam#endif /* TIOCNOTTY */ 397105197Ssam#else /* SETSID */ 398105197Ssam if (setsid() < 0) 399120585Ssam perror("setsid"); 400120585Ssam#endif /* SETSID */ 401120585Ssam } /* if debug < 3 */ 402105197Ssam 403105197Ssam /* 404105197Ssam * Nuke any timeout value 405105197Ssam */ 406105197Ssam timeout = NULL; 407252028Sae 408105197Ssam } /* if standalone (1st) */ 409105197Ssam 410105197Ssam /* Set the cwd (i.e. to /tftpboot) */ 411266800Svanhu if (chdir_path) { 412105197Ssam if (chdir(chdir_path) < 0) 413159237Spjd report(LOG_ERR, "%s: chdir failed", chdir_path); 414105197Ssam } 415274193Sae 416272394Sae /* Get the timezone. */ 417105197Ssam tzone_init(); 418118888Ssam 419252028Sae /* Allocate hash tables. */ 420118888Ssam rdtab_init(); 421118888Ssam 422118888Ssam /* 423266800Svanhu * Read the bootptab file. 424266800Svanhu */ 425266800Svanhu readtab(1); /* force read */ 426105197Ssam 427105197Ssam if (standalone) { 428105197Ssam 429105197Ssam /* 430105197Ssam * Create a socket. 431105197Ssam */ 432105197Ssam if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 433105197Ssam report(LOG_ERR, "socket: %s", get_network_errmsg()); 434105197Ssam exit(1); 435105197Ssam } 436105197Ssam 437120585Ssam /* 438120585Ssam * Get server's listening port number 439120585Ssam */ 440165118Sbz servp = getservbyname("bootps", "udp"); 441105197Ssam if (servp) { 442105197Ssam bootps_port = ntohs((u_short) servp->s_port); 443105197Ssam } else { 444105197Ssam bootps_port = (u_short) IPPORT_BOOTPS; 445252028Sae report(LOG_ERR, 446105197Ssam "bootps/udp: unknown service -- using port %d", 447105197Ssam bootps_port); 448105197Ssam } 449266800Svanhu 450105197Ssam /* 451105197Ssam * Bind socket to BOOTPS port. 452274193Sae */ 453274193Sae bind_addr.sin_family = AF_INET; 454274193Sae bind_addr.sin_addr.s_addr = INADDR_ANY; 455274193Sae bind_addr.sin_port = htons(bootps_port); 456274193Sae if (bind(s, (struct sockaddr *) &bind_addr, 457274193Sae sizeof(bind_addr)) < 0) 458274193Sae { 459274193Sae report(LOG_ERR, "bind: %s", get_network_errmsg()); 460274193Sae exit(1); 461105197Ssam } 462105197Ssam } /* if standalone (2nd)*/ 463105197Ssam 464275707Sae /* 465105197Ssam * Get destination port number so we can reply to client 466275707Sae */ 467105197Ssam servp = getservbyname("bootpc", "udp"); 468105197Ssam if (servp) { 469105197Ssam bootpc_port = ntohs(servp->s_port); 470120585Ssam } else { 471252028Sae report(LOG_ERR, 472105197Ssam "bootpc/udp: unknown service -- using port %d", 473105197Ssam IPPORT_BOOTPC); 474105197Ssam bootpc_port = (u_short) IPPORT_BOOTPC; 475105197Ssam } 476105197Ssam 477105197Ssam /* 478105197Ssam * Set up signals to read or dump the table. 479105197Ssam */ 480174054Sbz#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ 481174054Sbz sa.sa_handler = catcher; 482174054Sbz sigemptyset(&sa.sa_mask); 483105197Ssam sa.sa_flags = 0; 484105197Ssam if (sigaction(SIGHUP, &sa, NULL) < 0) { 485105197Ssam report(LOG_ERR, "sigaction: %s", get_errmsg()); 486105197Ssam exit(1); 487105197Ssam } 488105197Ssam if (sigaction(SIGUSR1, &sa, NULL) < 0) { 489272394Sae report(LOG_ERR, "sigaction: %s", get_errmsg()); 490272394Sae exit(1); 491272394Sae } 492272394Sae#else /* SA_NOCLDSTOP */ 493272394Sae /* Old-fashioned UNIX signals */ 494272394Sae if ((int) signal(SIGHUP, catcher) < 0) { 495105197Ssam report(LOG_ERR, "signal: %s", get_errmsg()); 496105197Ssam exit(1); 497105197Ssam } 498266800Svanhu if ((int) signal(SIGUSR1, catcher) < 0) { 499266800Svanhu report(LOG_ERR, "signal: %s", get_errmsg()); 500266800Svanhu exit(1); 501291292Sae } 502266800Svanhu#endif /* SA_NOCLDSTOP */ 503266800Svanhu 504266800Svanhu /* 505266800Svanhu * Process incoming requests. 506291292Sae */ 507266800Svanhu FD_ZERO(&readfds); 508266800Svanhu for (;;) { 509266800Svanhu struct timeval tv; 510266800Svanhu 511266800Svanhu FD_SET(s, &readfds); 512266800Svanhu if (timeout) 513266800Svanhu tv = *timeout; 514266800Svanhu 515266800Svanhu nfound = select(s + 1, &readfds, NULL, NULL, 516266800Svanhu (timeout) ? &tv : NULL); 517291292Sae if (nfound < 0) { 518291292Sae if (errno != EINTR) { 519291292Sae report(LOG_ERR, "select: %s", get_errmsg()); 520266800Svanhu } 521266800Svanhu /* 522252028Sae * Call readtab() or dumptab() here to avoid the 523120585Ssam * dangers of doing I/O from a signal handler. 524120585Ssam */ 525134391Sandre if (do_readtab) { 526105197Ssam do_readtab = 0; 527105197Ssam readtab(1); /* force read */ 528105197Ssam } 529105197Ssam if (do_dumptab) { 530105197Ssam do_dumptab = 0; 531105197Ssam dumptab(bootpd_dump); 532120585Ssam } 533120585Ssam continue; 534120585Ssam } 535120585Ssam if (!FD_ISSET(s, &readfds)) { 536120585Ssam if (debug > 1) 537120585Ssam report(LOG_INFO, "exiting after %ld minutes of inactivity", 538105197Ssam actualtimeout.tv_sec / 60); 539105197Ssam exit(0); 540105197Ssam } 541105197Ssam ra_len = sizeof(recv_addr); 542105197Ssam n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, 543105197Ssam (struct sockaddr *) &recv_addr, &ra_len); 544105197Ssam if (n <= 0) { 545105197Ssam continue; 546105197Ssam } 547105197Ssam if (debug > 1) { 548105197Ssam report(LOG_INFO, "recvd pkt from IP addr %s", 549105197Ssam inet_ntoa(recv_addr.sin_addr)); 550120585Ssam } 551105197Ssam if (n < sizeof(struct bootp)) { 552105197Ssam if (debug) { 553105197Ssam report(LOG_NOTICE, "received short packet"); 554105197Ssam } 555105197Ssam continue; 556105197Ssam } 557105197Ssam pktlen = n; 558105197Ssam 559105197Ssam readtab(0); /* maybe re-read bootptab */ 560105197Ssam 561105197Ssam switch (bp->bp_op) { 562105197Ssam case BOOTREQUEST: 563105197Ssam handle_request(); 564105197Ssam break; 565105197Ssam case BOOTREPLY: 566105197Ssam handle_reply(); 567120585Ssam break; 568105197Ssam } 569105197Ssam } 570105197Ssam return 0; 571105197Ssam} 572120585Ssam 573120585Ssam 574252028Sae 575105197Ssam 576105197Ssam/* 577105197Ssam * Print "usage" message and exit 578105197Ssam */ 579105197Ssam 580105197SsamPRIVATE void 581105197Ssamusage() 582105197Ssam{ 583105197Ssam fprintf(stderr, 584105197Ssam "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n"); 585105197Ssam fprintf(stderr, "\t -c n\tset current directory\n"); 586105197Ssam fprintf(stderr, "\t -d n\tset debug level\n"); 587105197Ssam fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); 588105197Ssam fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); 589105197Ssam fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); 590275707Sae exit(1); 591275707Sae} 592105197Ssam 593281695Sae/* Signal catchers */ 594291292SaePRIVATE void 595105197Ssamcatcher(sig) 596105197Ssam int sig; 597105197Ssam{ 598105197Ssam if (sig == SIGHUP) 599105197Ssam do_readtab = 1; 600281694Sae if (sig == SIGUSR1) 601105197Ssam do_dumptab = 1; 602105197Ssam#if !defined(SA_NOCLDSTOP) && defined(SYSV) 603165118Sbz /* For older "System V" derivatives with no sigaction(). */ 604165118Sbz signal(sig, catcher); 605165118Sbz#endif 606105197Ssam} 607120585Ssam 608120585Ssam 609120585Ssam 610105197Ssam/* 611105197Ssam * Process BOOTREQUEST packet. 612120585Ssam * 613105197Ssam * Note: This version of the bootpd.c server never forwards 614120585Ssam * a request to another server. That is the job of a gateway 615105197Ssam * program such as the "bootpgw" program included here. 616120585Ssam * 617105197Ssam * (Also this version does not interpret the hostname field of 618105197Ssam * the request packet; it COULD do a name->address lookup and 619105197Ssam * forward the request there.) 620120585Ssam */ 621252028SaePRIVATE void 622105197Ssamhandle_request() 623105197Ssam{ 624105197Ssam struct bootp *bp = (struct bootp *) pktbuf; 625105197Ssam struct host *hp = NULL; 626105197Ssam struct host dummyhost; 627105197Ssam int32 bootsize = 0; 628105197Ssam unsigned hlen, hashcode; 629105197Ssam int32 dest; 630120585Ssam char realpath[1024]; 631281695Sae char *clntpath; 632281695Sae char *homedir, *bootfile; 633105197Ssam int n; 634252028Sae 635105197Ssam bp->bp_file[sizeof(bp->bp_file)-1] = '\0'; 636105197Ssam 637105197Ssam /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ 638105197Ssam 639105197Ssam /* 640105197Ssam * If the servername field is set, compare it against us. 641105197Ssam * If we're not being addressed, ignore this request. 642291292Sae * If the server name field is null, throw in our name. 643291292Sae */ 644291292Sae if (strlen(bp->bp_sname)) { 645105197Ssam if (strcmp(bp->bp_sname, hostname)) { 646274466Sae if (debug) 647274466Sae report(LOG_INFO, "\ 648105197Ssamignoring request for server %s from client at %s address %s", 649274466Sae bp->bp_sname, netname(bp->bp_htype), 650274466Sae haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 651274466Sae /* XXX - Is it correct to ignore such a request? -gwr */ 652274466Sae return; 653252028Sae } 654118888Ssam } else { 655118888Ssam strcpy(bp->bp_sname, hostname); 656118888Ssam } 657274466Sae 658274466Sae /* Convert the request into a reply. */ 659266800Svanhu bp->bp_op = BOOTREPLY; 660266800Svanhu if (bp->bp_ciaddr.s_addr == 0) { 661105197Ssam /* 662105197Ssam * client doesnt know his IP address, 663105197Ssam * search by hardware address. 664105197Ssam */ 665274466Sae if (debug > 1) { 666274466Sae report(LOG_INFO, "request from %s address %s", 667274466Sae netname(bp->bp_htype), 668274466Sae haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 669274466Sae } 670105197Ssam hlen = haddrlength(bp->bp_htype); 671105197Ssam if (hlen != bp->bp_hlen) { 672120585Ssam report(LOG_NOTICE, "bad addr len from %s address %s", 673120585Ssam netname(bp->bp_htype), 674120585Ssam haddrtoa(bp->bp_chaddr, hlen)); 675274466Sae } 676105197Ssam dummyhost.htype = bp->bp_htype; 677105197Ssam bcopy(bp->bp_chaddr, dummyhost.haddr, hlen); 678105197Ssam hashcode = hash_HashFunction(bp->bp_chaddr, hlen); 679105197Ssam hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, 680252028Sae &dummyhost); 681105197Ssam if (hp == NULL && 682105197Ssam bp->bp_htype == HTYPE_IEEE802) 683105197Ssam { 684266800Svanhu /* Try again with address in "canonical" form. */ 685105197Ssam haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); 686274466Sae if (debug > 1) { 687274466Sae report(LOG_INFO, "\ 688274466SaeHW addr type is IEEE 802. convert to %s and check again\n", 689274466Sae haddrtoa(dummyhost.haddr, bp->bp_hlen)); 690274466Sae } 691252028Sae hashcode = hash_HashFunction(dummyhost.haddr, hlen); 692118888Ssam hp = (struct host *) hash_Lookup(hwhashtable, hashcode, 693118888Ssam hwlookcmp, &dummyhost); 694118888Ssam } 695274466Sae if (hp == NULL) { 696274466Sae /* 697266800Svanhu * XXX - Add dynamic IP address assignment? 698266800Svanhu */ 699105197Ssam if (debug) 700105197Ssam report(LOG_NOTICE, "unknown client %s address %s", 701105197Ssam netname(bp->bp_htype), 702105197Ssam haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 703274466Sae return; /* not found */ 704274466Sae } 705274466Sae (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; 706274466Sae 707105197Ssam } else { 708105197Ssam 709120585Ssam /* 710120585Ssam * search by IP address. 711120585Ssam */ 712274466Sae if (debug > 1) { 713105197Ssam report(LOG_INFO, "request from IP addr %s", 714105197Ssam inet_ntoa(bp->bp_ciaddr)); 715105197Ssam } 716105197Ssam dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr; 717252028Sae hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4); 718105197Ssam hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, 719105197Ssam &dummyhost); 720105197Ssam if (hp == NULL) { 721266800Svanhu if (debug) { 722159215Sgnn report(LOG_NOTICE, "IP address not found: %s", 723274466Sae inet_ntoa(bp->bp_ciaddr)); 724274466Sae } 725274466Sae return; 726274466Sae } 727105197Ssam } 728105197Ssam 729105197Ssam if (debug) { 730275707Sae report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr), 731105197Ssam hp->hostname->string); 732275707Sae } 733105197Ssam 734105197Ssam /* 735105197Ssam * If there is a response delay threshold, ignore requests 736120585Ssam * with a timestamp lower than the threshold. 737252028Sae */ 738105197Ssam if (hp->flags.min_wait) { 739105197Ssam u_int32 t = (u_int32) ntohs(bp->bp_secs); 740105197Ssam if (t < hp->min_wait) { 741105197Ssam if (debug > 1) 742105197Ssam report(LOG_INFO, 743105197Ssam "ignoring request due to timestamp (%d < %d)", 744105197Ssam t, hp->min_wait); 745105197Ssam return; 746174054Sbz } 747174054Sbz } 748174054Sbz 749105197Ssam#ifdef YORK_EX_OPTION 750105197Ssam /* 751105197Ssam * The need for the "ex" tag arose out of the need to empty 752105197Ssam * shared networked drives on diskless PCs. This solution is 753105197Ssam * not very clean but it does work fairly well. 754105197Ssam * Written by Edmund J. Sutcliffe <edmund@york.ac.uk> 755291292Sae * 756266800Svanhu * XXX - This could compromise security if a non-trusted user 757266800Svanhu * managed to write an entry in the bootptab with :ex=trojan: 758291292Sae * so I would leave this turned off unless you need it. -gwr 759291292Sae */ 760174054Sbz /* Run a program, passing the client name as a parameter. */ 761291292Sae if (hp->flags.exec_file) { 762291292Sae char tst[100]; 763291292Sae /* XXX - Check string lengths? -gwr */ 764291292Sae strcpy (tst, hp->exec_file->string); 765281694Sae strcat (tst, " "); 766281694Sae strcat (tst, hp->hostname->string); 767281694Sae strcat (tst, " &"); 768281694Sae if (debug) 769281694Sae report(LOG_INFO, "executing %s", tst); 770281694Sae system(tst); /* Hope this finishes soon... */ 771281694Sae } 772281694Sae#endif /* YORK_EX_OPTION */ 773281694Sae 774281694Sae /* 775281694Sae * If a specific TFTP server address was specified in the bootptab file, 776281694Sae * fill it in, otherwise zero it. 777281694Sae * XXX - Rather than zero it, should it be the bootpd address? -gwr 778281694Sae */ 779281694Sae (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ? 780281694Sae hp->bootserver.s_addr : 0L; 781281694Sae 782281694Sae#ifdef STANFORD_PROM_COMPAT 783281694Sae /* 784281694Sae * Stanford bootp PROMs (for a Sun?) have no way to leave 785281694Sae * the boot file name field blank (because the boot file 786281694Sae * name is automatically generated from some index). 787281694Sae * As a work-around, this little hack allows those PROMs to 788281694Sae * specify "sunboot14" with the same effect as a NULL name. 789281694Sae * (The user specifies boot device 14 or some such magic.) 790281694Sae */ 791281694Sae if (strcmp(bp->bp_file, "sunboot14") == 0) 792281694Sae bp->bp_file[0] = '\0'; /* treat it as unspecified */ 793281694Sae#endif 794105197Ssam 795105197Ssam /* 796105197Ssam * Fill in the client's proper bootfile. 797105197Ssam * 798105197Ssam * If the client specifies an absolute path, try that file with a 799105197Ssam * ".host" suffix and then without. If the file cannot be found, no 800105197Ssam * reply is made at all. 801181803Sbz * 802249294Sae * If the client specifies a null or relative file, use the following 803105197Ssam * table to determine the appropriate action: 804105197Ssam * 805105197Ssam * Homedir Bootfile Client's file 806105197Ssam * specified? specified? specification Action 807105197Ssam * ------------------------------------------------------------------- 808105197Ssam * No No Null Send null filename 809105197Ssam * No No Relative Discard request 810105197Ssam * No Yes Null Send if absolute else null 811105197Ssam * No Yes Relative Discard request *XXX 812249294Sae * Yes No Null Send null filename 813105197Ssam * Yes No Relative Lookup with ".host" 814105197Ssam * Yes Yes Null Send home/boot or bootfile 815105197Ssam * Yes Yes Relative Lookup with ".host" *XXX 816105197Ssam * 817105197Ssam */ 818105197Ssam 819105197Ssam /* 820105197Ssam * XXX - I don't like the policy of ignoring a client when the 821105197Ssam * boot file is not accessible. The TFTP server might not be 822105197Ssam * running on the same machine as the BOOTP server, in which 823105197Ssam * case checking accessibility of the boot file is pointless. 824105197Ssam * 825105197Ssam * Therefore, file accessibility is now demanded ONLY if you 826105197Ssam * define CHECK_FILE_ACCESS in the Makefile options. -gwr 827105197Ssam */ 828105197Ssam 829105197Ssam /* 830105197Ssam * The "real" path is as seen by the BOOTP daemon on this 831105197Ssam * machine, while the client path is relative to the TFTP 832105197Ssam * daemon chroot directory (i.e. /tftpboot). 833105197Ssam */ 834105197Ssam if (hp->flags.tftpdir) { 835120585Ssam snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string); 836120585Ssam clntpath = &realpath[strlen(realpath)]; 837120585Ssam } else { 838120585Ssam realpath[0] = '\0'; 839172149Sgnn clntpath = realpath; 840172149Sgnn } 841172149Sgnn 842172149Sgnn /* 843172149Sgnn * Determine client's requested homedir and bootfile. 844120585Ssam */ 845120585Ssam homedir = NULL; 846120585Ssam bootfile = NULL; 847120585Ssam if (bp->bp_file[0]) { 848120585Ssam homedir = bp->bp_file; 849120585Ssam bootfile = strrchr(homedir, '/'); 850120585Ssam if (bootfile) { 851172149Sgnn if (homedir == bootfile) 852172149Sgnn homedir = NULL; 853172149Sgnn *bootfile++ = '\0'; 854172149Sgnn } else { 855172149Sgnn /* no "/" in the string */ 856172149Sgnn bootfile = homedir; 857172149Sgnn homedir = NULL; 858172149Sgnn } 859172149Sgnn if (debug > 2) { 860172149Sgnn report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", 861120585Ssam (homedir) ? homedir : "", 862172149Sgnn (bootfile) ? bootfile : ""); 863172149Sgnn } 864120585Ssam } 865120585Ssam 866120585Ssam /* 867120585Ssam * Specifications in bootptab override client requested values. 868120585Ssam */ 869120585Ssam if (hp->flags.homedir) 870120585Ssam homedir = hp->homedir->string; 871120585Ssam if (hp->flags.bootfile) 872120585Ssam bootfile = hp->bootfile->string; 873120585Ssam 874120585Ssam /* 875120585Ssam * Construct bootfile path. 876120585Ssam */ 877120585Ssam if (homedir) { 878120585Ssam if (homedir[0] != '/') 879120585Ssam strcat(clntpath, "/"); 880120585Ssam strcat(clntpath, homedir); 881120585Ssam homedir = NULL; 882120585Ssam } 883120585Ssam if (bootfile) { 884120585Ssam if (bootfile[0] != '/') 885120585Ssam strcat(clntpath, "/"); 886120585Ssam strcat(clntpath, bootfile); 887120585Ssam bootfile = NULL; 888120585Ssam } 889120585Ssam 890120585Ssam /* 891120585Ssam * First try to find the file with a ".host" suffix 892120585Ssam */ 893120585Ssam n = strlen(clntpath); 894120585Ssam strcat(clntpath, "."); 895120585Ssam strcat(clntpath, hp->hostname->string); 896120585Ssam if (chk_access(realpath, &bootsize) < 0) { 897120585Ssam clntpath[n] = 0; /* Try it without the suffix */ 898120585Ssam if (chk_access(realpath, &bootsize) < 0) { 899120585Ssam /* neither "file.host" nor "file" was found */ 900120585Ssam#ifdef CHECK_FILE_ACCESS 901120585Ssam 902120585Ssam if (bp->bp_file[0]) { 903120585Ssam /* 904120585Ssam * Client wanted specific file 905120585Ssam * and we didn't have it. 906120585Ssam */ 907120585Ssam report(LOG_NOTICE, 908120585Ssam "requested file not found: \"%s\"", clntpath); 909120585Ssam return; 910120585Ssam } 911120585Ssam /* 912120585Ssam * Client didn't ask for a specific file and we couldn't 913120585Ssam * access the default file, so just zero-out the bootfile 914120585Ssam * field in the packet and continue processing the reply. 915120585Ssam */ 916120585Ssam bzero(bp->bp_file, sizeof(bp->bp_file)); 917120585Ssam goto null_file_name; 918120585Ssam 919120585Ssam#else /* CHECK_FILE_ACCESS */ 920120585Ssam 921120585Ssam /* Complain only if boot file size was needed. */ 922120585Ssam if (hp->flags.bootsize_auto) { 923120585Ssam report(LOG_ERR, "can not determine size of file \"%s\"", 924105197Ssam clntpath); 925 } 926 927#endif /* CHECK_FILE_ACCESS */ 928 } 929 } 930 strncpy(bp->bp_file, clntpath, BP_FILE_LEN); 931 if (debug > 2) 932 report(LOG_INFO, "bootfile=\"%s\"", clntpath); 933 934#ifdef CHECK_FILE_ACCESS 935null_file_name: 936#endif /* CHECK_FILE_ACCESS */ 937 938 939 /* 940 * Handle vendor options based on magic number. 941 */ 942 943 if (debug > 1) { 944 report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", 945 (int) ((bp->bp_vend)[0]), 946 (int) ((bp->bp_vend)[1]), 947 (int) ((bp->bp_vend)[2]), 948 (int) ((bp->bp_vend)[3])); 949 } 950 /* 951 * If this host isn't set for automatic vendor info then copy the 952 * specific cookie into the bootp packet, thus forcing a certain 953 * reply format. Only force reply format if user specified it. 954 */ 955 if (hp->flags.vm_cookie) { 956 /* Slam in the user specified magic number. */ 957 bcopy(hp->vm_cookie, bp->bp_vend, 4); 958 } 959 /* 960 * Figure out the format for the vendor-specific info. 961 * Note that bp->bp_vend may have been set above. 962 */ 963 if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) { 964 /* RFC1048 conformant bootp client */ 965 dovend_rfc1048(bp, hp, bootsize); 966 if (debug > 1) { 967 report(LOG_INFO, "sending reply (with RFC1048 options)"); 968 } 969 } 970#ifdef VEND_CMU 971 else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { 972 dovend_cmu(bp, hp); 973 if (debug > 1) { 974 report(LOG_INFO, "sending reply (with CMU options)"); 975 } 976 } 977#endif 978 else { 979 if (debug > 1) { 980 report(LOG_INFO, "sending reply (with no options)"); 981 } 982 } 983 984 dest = (hp->flags.reply_addr) ? 985 hp->reply_addr.s_addr : 0L; 986 987 /* not forwarded */ 988 sendreply(0, dest); 989} 990 991 992/* 993 * Process BOOTREPLY packet. 994 */ 995PRIVATE void 996handle_reply() 997{ 998 if (debug) { 999 report(LOG_INFO, "processing boot reply"); 1000 } 1001 /* forwarded, no destination override */ 1002 sendreply(1, 0); 1003} 1004 1005 1006/* 1007 * Send a reply packet to the client. 'forward' flag is set if we are 1008 * not the originator of this reply packet. 1009 */ 1010PRIVATE void 1011sendreply(forward, dst_override) 1012 int forward; 1013 int32 dst_override; 1014{ 1015 struct bootp *bp = (struct bootp *) pktbuf; 1016 struct in_addr dst; 1017 u_short port = bootpc_port; 1018 unsigned char *ha; 1019 int len, haf; 1020 1021 /* 1022 * XXX - Should honor bp_flags "broadcast" bit here. 1023 * Temporary workaround: use the :ra=ADDR: option to 1024 * set the reply address to the broadcast address. 1025 */ 1026 1027 /* 1028 * If the destination address was specified explicitly 1029 * (i.e. the broadcast address for HP compatibility) 1030 * then send the response to that address. Otherwise, 1031 * act in accordance with RFC951: 1032 * If the client IP address is specified, use that 1033 * else if gateway IP address is specified, use that 1034 * else make a temporary arp cache entry for the client's 1035 * NEW IP/hardware address and use that. 1036 */ 1037 if (dst_override) { 1038 dst.s_addr = dst_override; 1039 if (debug > 1) { 1040 report(LOG_INFO, "reply address override: %s", 1041 inet_ntoa(dst)); 1042 } 1043 } else if (bp->bp_ciaddr.s_addr) { 1044 dst = bp->bp_ciaddr; 1045 } else if (bp->bp_giaddr.s_addr && forward == 0) { 1046 dst = bp->bp_giaddr; 1047 port = bootps_port; 1048 if (debug > 1) { 1049 report(LOG_INFO, "sending reply to gateway %s", 1050 inet_ntoa(dst)); 1051 } 1052 } else { 1053 dst = bp->bp_yiaddr; 1054 ha = bp->bp_chaddr; 1055 len = bp->bp_hlen; 1056 if (len > MAXHADDRLEN) 1057 len = MAXHADDRLEN; 1058 haf = (int) bp->bp_htype; 1059 if (haf == 0) 1060 haf = HTYPE_ETHERNET; 1061 1062 if (debug > 1) 1063 report(LOG_INFO, "setarp %s - %s", 1064 inet_ntoa(dst), haddrtoa(ha, len)); 1065 setarp(s, &dst, haf, ha, len); 1066 } 1067 1068 if ((forward == 0) && 1069 (bp->bp_siaddr.s_addr == 0)) 1070 { 1071 struct ifreq *ifr; 1072 struct in_addr siaddr; 1073 /* 1074 * If we are originating this reply, we 1075 * need to find our own interface address to 1076 * put in the bp_siaddr field of the reply. 1077 * If this server is multi-homed, pick the 1078 * 'best' interface (the one on the same net 1079 * as the client). Of course, the client may 1080 * be on the other side of a BOOTP gateway... 1081 */ 1082 ifr = getif(s, &dst); 1083 if (ifr) { 1084 struct sockaddr_in *sip; 1085 sip = (struct sockaddr_in *) &(ifr->ifr_addr); 1086 siaddr = sip->sin_addr; 1087 } else { 1088 /* Just use my "official" IP address. */ 1089 siaddr = my_ip_addr; 1090 } 1091 1092 /* XXX - No need to set bp_giaddr here. */ 1093 1094 /* Finally, set the server address field. */ 1095 bp->bp_siaddr = siaddr; 1096 } 1097 /* Set up socket address for send. */ 1098 send_addr.sin_family = AF_INET; 1099 send_addr.sin_port = htons(port); 1100 send_addr.sin_addr = dst; 1101 1102 /* Send reply with same size packet as request used. */ 1103 if (sendto(s, pktbuf, pktlen, 0, 1104 (struct sockaddr *) &send_addr, 1105 sizeof(send_addr)) < 0) 1106 { 1107 report(LOG_ERR, "sendto: %s", get_network_errmsg()); 1108 } 1109} /* sendreply */ 1110 1111 1112/* nmatch() - now in getif.c */ 1113/* setarp() - now in hwaddr.c */ 1114 1115 1116/* 1117 * This call checks read access to a file. It returns 0 if the file given 1118 * by "path" exists and is publically readable. A value of -1 is returned if 1119 * access is not permitted or an error occurs. Successful calls also 1120 * return the file size in bytes using the long pointer "filesize". 1121 * 1122 * The read permission bit for "other" users is checked. This bit must be 1123 * set for tftpd(8) to allow clients to read the file. 1124 */ 1125 1126PRIVATE int 1127chk_access(path, filesize) 1128 char *path; 1129 int32 *filesize; 1130{ 1131 struct stat st; 1132 1133 if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { 1134 *filesize = (int32) st.st_size; 1135 return 0; 1136 } else { 1137 return -1; 1138 } 1139} 1140 1141 1142/* 1143 * Now in dumptab.c : 1144 * dumptab() 1145 * dump_host() 1146 * list_ipaddresses() 1147 */ 1148 1149#ifdef VEND_CMU 1150 1151/* 1152 * Insert the CMU "vendor" data for the host pointed to by "hp" into the 1153 * bootp packet pointed to by "bp". 1154 */ 1155 1156PRIVATE void 1157dovend_cmu(bp, hp) 1158 struct bootp *bp; 1159 struct host *hp; 1160{ 1161 struct cmu_vend *vendp; 1162 struct in_addr_list *taddr; 1163 1164 /* 1165 * Initialize the entire vendor field to zeroes. 1166 */ 1167 bzero(bp->bp_vend, sizeof(bp->bp_vend)); 1168 1169 /* 1170 * Fill in vendor information. Subnet mask, default gateway, 1171 * domain name server, ien name server, time server 1172 */ 1173 vendp = (struct cmu_vend *) bp->bp_vend; 1174 strcpy(vendp->v_magic, (char *)vm_cmu); 1175 if (hp->flags.subnet_mask) { 1176 (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; 1177 (vendp->v_flags) |= VF_SMASK; 1178 if (hp->flags.gateway) { 1179 (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; 1180 } 1181 } 1182 if (hp->flags.domain_server) { 1183 taddr = hp->domain_server; 1184 if (taddr->addrcount > 0) { 1185 (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; 1186 if (taddr->addrcount > 1) { 1187 (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; 1188 } 1189 } 1190 } 1191 if (hp->flags.name_server) { 1192 taddr = hp->name_server; 1193 if (taddr->addrcount > 0) { 1194 (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; 1195 if (taddr->addrcount > 1) { 1196 (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; 1197 } 1198 } 1199 } 1200 if (hp->flags.time_server) { 1201 taddr = hp->time_server; 1202 if (taddr->addrcount > 0) { 1203 (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; 1204 if (taddr->addrcount > 1) { 1205 (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; 1206 } 1207 } 1208 } 1209 /* Log message now done by caller. */ 1210} /* dovend_cmu */ 1211 1212#endif /* VEND_CMU */ 1213 1214 1215 1216/* 1217 * Insert the RFC1048 vendor data for the host pointed to by "hp" into the 1218 * bootp packet pointed to by "bp". 1219 */ 1220#define NEED(LEN, MSG) do \ 1221 if (bytesleft < (LEN)) { \ 1222 report(LOG_NOTICE, noroom, \ 1223 hp->hostname->string, MSG); \ 1224 return; \ 1225 } while (0) 1226PRIVATE void 1227dovend_rfc1048(bp, hp, bootsize) 1228 struct bootp *bp; 1229 struct host *hp; 1230 int32 bootsize; 1231{ 1232 int bytesleft, len; 1233 byte *vp; 1234 1235 static const char noroom[] = "%s: No room for \"%s\" option"; 1236 1237 vp = bp->bp_vend; 1238 1239 if (hp->flags.msg_size) { 1240 pktlen = hp->msg_size; 1241 } else { 1242 /* 1243 * If the request was longer than the official length, build 1244 * a response of that same length where the additional length 1245 * is assumed to be part of the bp_vend (options) area. 1246 */ 1247 if (pktlen > sizeof(*bp)) { 1248 if (debug > 1) 1249 report(LOG_INFO, "request message length=%d", pktlen); 1250 } 1251 /* 1252 * Check whether the request contains the option: 1253 * Maximum DHCP Message Size (RFC1533 sec. 9.8) 1254 * and if so, override the response length with its value. 1255 * This request must lie within the first BP_VEND_LEN 1256 * bytes of the option space. 1257 */ 1258 { 1259 byte *p, *ep; 1260 byte tag, len; 1261 short msgsz = 0; 1262 1263 p = vp + 4; 1264 ep = p + BP_VEND_LEN - 4; 1265 while (p < ep) { 1266 tag = *p++; 1267 /* Check for tags with no data first. */ 1268 if (tag == TAG_PAD) 1269 continue; 1270 if (tag == TAG_END) 1271 break; 1272 /* Now scan the length byte. */ 1273 len = *p++; 1274 switch (tag) { 1275 case TAG_MAX_MSGSZ: 1276 if (len == 2) { 1277 bcopy(p, (char*)&msgsz, 2); 1278 msgsz = ntohs(msgsz); 1279 } 1280 break; 1281 case TAG_SUBNET_MASK: 1282 /* XXX - Should preserve this if given... */ 1283 break; 1284 } /* swtich */ 1285 p += len; 1286 } 1287 1288 if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) { 1289 if (debug > 1) 1290 report(LOG_INFO, "request has DHCP msglen=%d", msgsz); 1291 pktlen = msgsz - BP_MSG_OVERHEAD; 1292 } 1293 } 1294 } 1295 1296 if (pktlen < sizeof(*bp)) { 1297 report(LOG_ERR, "invalid response length=%d", pktlen); 1298 pktlen = sizeof(*bp); 1299 } 1300 bytesleft = ((byte*)bp + pktlen) - vp; 1301 if (pktlen > sizeof(*bp)) { 1302 if (debug > 1) 1303 report(LOG_INFO, "extended reply, length=%d, options=%d", 1304 pktlen, bytesleft); 1305 } 1306 1307 /* Copy in the magic cookie */ 1308 bcopy(vm_rfc1048, vp, 4); 1309 vp += 4; 1310 bytesleft -= 4; 1311 1312 if (hp->flags.subnet_mask) { 1313 /* always enough room here. */ 1314 *vp++ = TAG_SUBNET_MASK;/* -1 byte */ 1315 *vp++ = 4; /* -1 byte */ 1316 insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ 1317 bytesleft -= 6; /* Fix real count */ 1318 if (hp->flags.gateway) { 1319 (void) insert_ip(TAG_GATEWAY, 1320 hp->gateway, 1321 &vp, &bytesleft); 1322 } 1323 } 1324 if (hp->flags.bootsize) { 1325 /* always enough room here */ 1326 bootsize = (hp->flags.bootsize_auto) ? 1327 ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ 1328 *vp++ = TAG_BOOT_SIZE; 1329 *vp++ = 2; 1330 *vp++ = (byte) ((bootsize >> 8) & 0xFF); 1331 *vp++ = (byte) (bootsize & 0xFF); 1332 bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ 1333 } 1334 /* 1335 * This one is special: Remaining options go in the ext file. 1336 * Only the subnet_mask, bootsize, and gateway should precede. 1337 */ 1338 if (hp->flags.exten_file) { 1339 /* 1340 * Check for room for exten_file. Add 3 to account for 1341 * TAG_EXTEN_FILE, length, and TAG_END. 1342 */ 1343 len = strlen(hp->exten_file->string); 1344 NEED((len + 3), "ef"); 1345 *vp++ = TAG_EXTEN_FILE; 1346 *vp++ = (byte) (len & 0xFF); 1347 bcopy(hp->exten_file->string, vp, len); 1348 vp += len; 1349 *vp++ = TAG_END; 1350 bytesleft -= len + 3; 1351 return; /* no more options here. */ 1352 } 1353 /* 1354 * The remaining options are inserted by the following 1355 * function (which is shared with bootpef.c). 1356 * Keep back one byte for the TAG_END. 1357 */ 1358 len = dovend_rfc1497(hp, vp, bytesleft - 1); 1359 vp += len; 1360 bytesleft -= len; 1361 1362 /* There should be at least one byte left. */ 1363 NEED(1, "(end)"); 1364 *vp++ = TAG_END; 1365 bytesleft--; 1366 1367 /* Log message done by caller. */ 1368 if (bytesleft > 0) { 1369 /* 1370 * Zero out any remaining part of the vendor area. 1371 */ 1372 bzero(vp, bytesleft); 1373 } 1374} /* dovend_rfc1048 */ 1375#undef NEED 1376 1377 1378/* 1379 * Now in readfile.c: 1380 * hwlookcmp() 1381 * iplookcmp() 1382 */ 1383 1384/* haddrtoa() - now in hwaddr.c */ 1385/* 1386 * Now in dovend.c: 1387 * insert_ip() 1388 * insert_generic() 1389 * insert_u_long() 1390 */ 1391 1392/* get_errmsg() - now in report.c */ 1393 1394/* 1395 * Local Variables: 1396 * tab-width: 4 1397 * c-indent-level: 4 1398 * c-argdecl-indent: 4 1399 * c-continued-statement-offset: 4 1400 * c-continued-brace-offset: -4 1401 * c-label-offset: -4 1402 * c-brace-offset: 0 1403 * End: 1404 */ 1405