144099b7bSPaul Traina /************************************************************************ 244099b7bSPaul Traina Copyright 1988, 1991 by Carnegie Mellon University 344099b7bSPaul Traina 444099b7bSPaul Traina All Rights Reserved 544099b7bSPaul Traina 644099b7bSPaul Traina Permission to use, copy, modify, and distribute this software and its 744099b7bSPaul Traina documentation for any purpose and without fee is hereby granted, provided 844099b7bSPaul Traina that the above copyright notice appear in all copies and that both that 944099b7bSPaul Traina copyright notice and this permission notice appear in supporting 1044099b7bSPaul Traina documentation, and that the name of Carnegie Mellon University not be used 1144099b7bSPaul Traina in advertising or publicity pertaining to distribution of the software 1244099b7bSPaul Traina without specific, written prior permission. 1344099b7bSPaul Traina 1444099b7bSPaul Traina CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 1544099b7bSPaul Traina SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 1644099b7bSPaul Traina IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 1744099b7bSPaul Traina DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 1844099b7bSPaul Traina PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 1944099b7bSPaul Traina ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 2044099b7bSPaul Traina SOFTWARE. 21148531efSWolfram Schneider 2244099b7bSPaul Traina ************************************************************************/ 2344099b7bSPaul Traina 2444099b7bSPaul Traina /* 2544099b7bSPaul Traina * BOOTP (bootstrap protocol) server daemon. 2644099b7bSPaul Traina * 2744099b7bSPaul Traina * Answers BOOTP request packets from booting client machines. 2844099b7bSPaul Traina * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol. 2944099b7bSPaul Traina * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions. 3044099b7bSPaul Traina * See RFC 1395 for option tags 14-17. 3144099b7bSPaul Traina * See accompanying man page -- bootpd.8 3244099b7bSPaul Traina * 3344099b7bSPaul Traina * HISTORY 3444099b7bSPaul Traina * See ./Changes 3544099b7bSPaul Traina * 3644099b7bSPaul Traina * BUGS 3744099b7bSPaul Traina * See ./ToDo 3844099b7bSPaul Traina */ 3944099b7bSPaul Traina 40538015aaSPhilippe Charnier #include <sys/cdefs.h> 41538015aaSPhilippe Charnier __FBSDID("$FreeBSD$"); 4244099b7bSPaul Traina 4344099b7bSPaul Traina #include <sys/types.h> 4444099b7bSPaul Traina #include <sys/param.h> 4544099b7bSPaul Traina #include <sys/socket.h> 4644099b7bSPaul Traina #include <sys/ioctl.h> 4744099b7bSPaul Traina #include <sys/file.h> 4844099b7bSPaul Traina #include <sys/time.h> 4944099b7bSPaul Traina #include <sys/stat.h> 50e08ac58bSPaul Traina #include <sys/utsname.h> 5144099b7bSPaul Traina 5244099b7bSPaul Traina #include <net/if.h> 5344099b7bSPaul Traina #include <netinet/in.h> 5444099b7bSPaul Traina #include <arpa/inet.h> /* inet_ntoa */ 5544099b7bSPaul Traina 5644099b7bSPaul Traina #ifndef NO_UNISTD 5744099b7bSPaul Traina #include <unistd.h> 5844099b7bSPaul Traina #endif 59e08ac58bSPaul Traina 6044099b7bSPaul Traina #include <stdlib.h> 6144099b7bSPaul Traina #include <signal.h> 6244099b7bSPaul Traina #include <stdio.h> 6344099b7bSPaul Traina #include <string.h> 6444099b7bSPaul Traina #include <errno.h> 6544099b7bSPaul Traina #include <ctype.h> 6644099b7bSPaul Traina #include <netdb.h> 671a37aa56SDavid E. O'Brien #include <paths.h> 6844099b7bSPaul Traina #include <syslog.h> 6944099b7bSPaul Traina #include <assert.h> 7009c00166STom Rhodes #include <inttypes.h> 7144099b7bSPaul Traina 7244099b7bSPaul Traina #ifdef NO_SETSID 7344099b7bSPaul Traina # include <fcntl.h> /* for O_RDONLY, etc */ 7444099b7bSPaul Traina #endif 7544099b7bSPaul Traina 7644099b7bSPaul Traina #ifndef USE_BFUNCS 7744099b7bSPaul Traina # include <memory.h> 7844099b7bSPaul Traina /* Yes, memcpy is OK here (no overlapped copies). */ 7944099b7bSPaul Traina # define bcopy(a,b,c) memcpy(b,a,c) 8044099b7bSPaul Traina # define bzero(p,l) memset(p,0,l) 8144099b7bSPaul Traina # define bcmp(a,b,c) memcmp(a,b,c) 8244099b7bSPaul Traina #endif 8344099b7bSPaul Traina 8444099b7bSPaul Traina #include "bootp.h" 8544099b7bSPaul Traina #include "hash.h" 8644099b7bSPaul Traina #include "hwaddr.h" 8744099b7bSPaul Traina #include "bootpd.h" 8844099b7bSPaul Traina #include "dovend.h" 8944099b7bSPaul Traina #include "getif.h" 9044099b7bSPaul Traina #include "readfile.h" 9144099b7bSPaul Traina #include "report.h" 9244099b7bSPaul Traina #include "tzone.h" 9344099b7bSPaul Traina #include "patchlevel.h" 9444099b7bSPaul Traina 9544099b7bSPaul Traina #ifndef CONFIG_FILE 9644099b7bSPaul Traina #define CONFIG_FILE "/etc/bootptab" 9744099b7bSPaul Traina #endif 9844099b7bSPaul Traina #ifndef DUMPTAB_FILE 9944099b7bSPaul Traina #define DUMPTAB_FILE "/tmp/bootpd.dump" 10044099b7bSPaul Traina #endif 10144099b7bSPaul Traina 10244099b7bSPaul Traina 10344099b7bSPaul Traina 10444099b7bSPaul Traina /* 10544099b7bSPaul Traina * Externals, forward declarations, and global variables 10644099b7bSPaul Traina */ 10744099b7bSPaul Traina 108f19d047aSAlfred Perlstein extern void dumptab(char *); 10944099b7bSPaul Traina 110f19d047aSAlfred Perlstein PRIVATE void catcher(int); 111f19d047aSAlfred Perlstein PRIVATE int chk_access(char *, int32 *); 11244099b7bSPaul Traina #ifdef VEND_CMU 113f19d047aSAlfred Perlstein PRIVATE void dovend_cmu(struct bootp *, struct host *); 11444099b7bSPaul Traina #endif 115f19d047aSAlfred Perlstein PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32); 116f19d047aSAlfred Perlstein PRIVATE void handle_reply(void); 117f19d047aSAlfred Perlstein PRIVATE void handle_request(void); 118f19d047aSAlfred Perlstein PRIVATE void sendreply(int forward, int32 dest_override); 119f19d047aSAlfred Perlstein PRIVATE void usage(void); 12044099b7bSPaul Traina 12144099b7bSPaul Traina /* 12244099b7bSPaul Traina * IP port numbers for client and server obtained from /etc/services 12344099b7bSPaul Traina */ 12444099b7bSPaul Traina 12544099b7bSPaul Traina u_short bootps_port, bootpc_port; 12644099b7bSPaul Traina 12744099b7bSPaul Traina 12844099b7bSPaul Traina /* 12944099b7bSPaul Traina * Internet socket and interface config structures 13044099b7bSPaul Traina */ 13144099b7bSPaul Traina 13244099b7bSPaul Traina struct sockaddr_in bind_addr; /* Listening */ 13344099b7bSPaul Traina struct sockaddr_in recv_addr; /* Packet source */ 13444099b7bSPaul Traina struct sockaddr_in send_addr; /* destination */ 13544099b7bSPaul Traina 13644099b7bSPaul Traina 13744099b7bSPaul Traina /* 13844099b7bSPaul Traina * option defaults 13944099b7bSPaul Traina */ 14044099b7bSPaul Traina int debug = 0; /* Debugging flag (level) */ 14144099b7bSPaul Traina struct timeval actualtimeout = 14244099b7bSPaul Traina { /* fifteen minutes */ 14344099b7bSPaul Traina 15 * 60L, /* tv_sec */ 14444099b7bSPaul Traina 0 /* tv_usec */ 14544099b7bSPaul Traina }; 14635131b46SMark Johnston int arpmod = TRUE; /* modify the ARP table */ 14744099b7bSPaul Traina 14844099b7bSPaul Traina /* 14944099b7bSPaul Traina * General 15044099b7bSPaul Traina */ 15144099b7bSPaul Traina 15244099b7bSPaul Traina int s; /* Socket file descriptor */ 15344099b7bSPaul Traina char *pktbuf; /* Receive packet buffer */ 15444099b7bSPaul Traina int pktlen; 15544099b7bSPaul Traina char *progname; 15644099b7bSPaul Traina char *chdir_path; 15744099b7bSPaul Traina struct in_addr my_ip_addr; 15844099b7bSPaul Traina 1599e9a43bdSBrian Somers static const char *hostname; 1609e9a43bdSBrian Somers static char default_hostname[MAXHOSTNAMELEN]; 161e08ac58bSPaul Traina 16244099b7bSPaul Traina /* Flags set by signal catcher. */ 16344099b7bSPaul Traina PRIVATE int do_readtab = 0; 16444099b7bSPaul Traina PRIVATE int do_dumptab = 0; 16544099b7bSPaul Traina 16644099b7bSPaul Traina /* 16744099b7bSPaul Traina * Globals below are associated with the bootp database file (bootptab). 16844099b7bSPaul Traina */ 16944099b7bSPaul Traina 17044099b7bSPaul Traina char *bootptab = CONFIG_FILE; 17144099b7bSPaul Traina char *bootpd_dump = DUMPTAB_FILE; 17244099b7bSPaul Traina 17344099b7bSPaul Traina 17444099b7bSPaul Traina 17544099b7bSPaul Traina /* 17644099b7bSPaul Traina * Initialization such as command-line processing is done and then the 17744099b7bSPaul Traina * main server loop is started. 17844099b7bSPaul Traina */ 17944099b7bSPaul Traina 18085966371SWarner Losh int 18144099b7bSPaul Traina main(argc, argv) 18244099b7bSPaul Traina int argc; 18344099b7bSPaul Traina char **argv; 18444099b7bSPaul Traina { 18544099b7bSPaul Traina struct timeval *timeout; 18644099b7bSPaul Traina struct bootp *bp; 18744099b7bSPaul Traina struct servent *servp; 18844099b7bSPaul Traina struct hostent *hep; 18944099b7bSPaul Traina char *stmp; 19078e3eed0SStefan Farfeleder socklen_t ba_len, ra_len; 19178e3eed0SStefan Farfeleder int n; 192b6a0d472SJohn-Mark Gurney int nfound; 193b6a0d472SJohn-Mark Gurney fd_set readfds; 19444099b7bSPaul Traina int standalone; 195e08ac58bSPaul Traina #ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ 196e08ac58bSPaul Traina struct sigaction sa; 197e08ac58bSPaul Traina #endif 19844099b7bSPaul Traina 19944099b7bSPaul Traina progname = strrchr(argv[0], '/'); 20044099b7bSPaul Traina if (progname) progname++; 20144099b7bSPaul Traina else progname = argv[0]; 20244099b7bSPaul Traina 20344099b7bSPaul Traina /* 20444099b7bSPaul Traina * Initialize logging. 20544099b7bSPaul Traina */ 20644099b7bSPaul Traina report_init(0); /* uses progname */ 20744099b7bSPaul Traina 20844099b7bSPaul Traina /* 20944099b7bSPaul Traina * Log startup 21044099b7bSPaul Traina */ 21144099b7bSPaul Traina report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); 21244099b7bSPaul Traina 21344099b7bSPaul Traina /* Debugging for compilers with struct padding. */ 21444099b7bSPaul Traina assert(sizeof(struct bootp) == BP_MINPKTSZ); 21544099b7bSPaul Traina 21644099b7bSPaul Traina /* Get space for receiving packets and composing replies. */ 21744099b7bSPaul Traina pktbuf = malloc(MAX_MSG_SIZE); 21844099b7bSPaul Traina if (!pktbuf) { 21944099b7bSPaul Traina report(LOG_ERR, "malloc failed"); 22044099b7bSPaul Traina exit(1); 22144099b7bSPaul Traina } 22244099b7bSPaul Traina bp = (struct bootp *) pktbuf; 22344099b7bSPaul Traina 22444099b7bSPaul Traina /* 22544099b7bSPaul Traina * Check to see if a socket was passed to us from inetd. 22644099b7bSPaul Traina * 22744099b7bSPaul Traina * Use getsockname() to determine if descriptor 0 is indeed a socket 22844099b7bSPaul Traina * (and thus we are probably a child of inetd) or if it is instead 22944099b7bSPaul Traina * something else and we are running standalone. 23044099b7bSPaul Traina */ 23144099b7bSPaul Traina s = 0; 23244099b7bSPaul Traina ba_len = sizeof(bind_addr); 23344099b7bSPaul Traina bzero((char *) &bind_addr, ba_len); 23444099b7bSPaul Traina errno = 0; 23544099b7bSPaul Traina standalone = TRUE; 23644099b7bSPaul Traina if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { 23744099b7bSPaul Traina /* 23844099b7bSPaul Traina * Descriptor 0 is a socket. Assume we are a child of inetd. 23944099b7bSPaul Traina */ 24044099b7bSPaul Traina if (bind_addr.sin_family == AF_INET) { 24144099b7bSPaul Traina standalone = FALSE; 24244099b7bSPaul Traina bootps_port = ntohs(bind_addr.sin_port); 24344099b7bSPaul Traina } else { 24444099b7bSPaul Traina /* Some other type of socket? */ 24544099b7bSPaul Traina report(LOG_ERR, "getsockname: not an INET socket"); 24644099b7bSPaul Traina } 24744099b7bSPaul Traina } 24844099b7bSPaul Traina 24944099b7bSPaul Traina /* 25044099b7bSPaul Traina * Set defaults that might be changed by option switches. 25144099b7bSPaul Traina */ 25244099b7bSPaul Traina stmp = NULL; 25344099b7bSPaul Traina timeout = &actualtimeout; 25444099b7bSPaul Traina 2559e9a43bdSBrian Somers if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) { 256798c69cdSPoul-Henning Kamp report(LOG_ERR, "bootpd: can't get hostname\n"); 257e08ac58bSPaul Traina exit(1); 258e08ac58bSPaul Traina } 2599e9a43bdSBrian Somers default_hostname[sizeof(default_hostname) - 1] = '\0'; 26009caeadeSJoerg Wunsch hostname = default_hostname; 261e08ac58bSPaul Traina 26244099b7bSPaul Traina /* 26344099b7bSPaul Traina * Read switches. 26444099b7bSPaul Traina */ 26544099b7bSPaul Traina for (argc--, argv++; argc > 0; argc--, argv++) { 26644099b7bSPaul Traina if (argv[0][0] != '-') 26744099b7bSPaul Traina break; 26844099b7bSPaul Traina switch (argv[0][1]) { 26944099b7bSPaul Traina 27035131b46SMark Johnston case 'a': /* don't modify the ARP table */ 27135131b46SMark Johnston arpmod = FALSE; 27235131b46SMark Johnston break; 27344099b7bSPaul Traina case 'c': /* chdir_path */ 27444099b7bSPaul Traina if (argv[0][2]) { 27544099b7bSPaul Traina stmp = &(argv[0][2]); 27644099b7bSPaul Traina } else { 27744099b7bSPaul Traina argc--; 27844099b7bSPaul Traina argv++; 27944099b7bSPaul Traina stmp = argv[0]; 28044099b7bSPaul Traina } 28144099b7bSPaul Traina if (!stmp || (stmp[0] != '/')) { 282798c69cdSPoul-Henning Kamp report(LOG_ERR, 28344099b7bSPaul Traina "bootpd: invalid chdir specification\n"); 28444099b7bSPaul Traina break; 28544099b7bSPaul Traina } 28644099b7bSPaul Traina chdir_path = stmp; 28744099b7bSPaul Traina break; 28844099b7bSPaul Traina 28944099b7bSPaul Traina case 'd': /* debug level */ 29044099b7bSPaul Traina if (argv[0][2]) { 29144099b7bSPaul Traina stmp = &(argv[0][2]); 29244099b7bSPaul Traina } else if (argv[1] && argv[1][0] == '-') { 29344099b7bSPaul Traina /* 29444099b7bSPaul Traina * Backwards-compatible behavior: 29544099b7bSPaul Traina * no parameter, so just increment the debug flag. 29644099b7bSPaul Traina */ 29744099b7bSPaul Traina debug++; 29844099b7bSPaul Traina break; 29944099b7bSPaul Traina } else { 30044099b7bSPaul Traina argc--; 30144099b7bSPaul Traina argv++; 30244099b7bSPaul Traina stmp = argv[0]; 30344099b7bSPaul Traina } 30444099b7bSPaul Traina if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 305798c69cdSPoul-Henning Kamp report(LOG_ERR, 30644099b7bSPaul Traina "%s: invalid debug level\n", progname); 30744099b7bSPaul Traina break; 30844099b7bSPaul Traina } 30944099b7bSPaul Traina debug = n; 31044099b7bSPaul Traina break; 31144099b7bSPaul Traina 31244099b7bSPaul Traina case 'h': /* override hostname */ 31344099b7bSPaul Traina if (argv[0][2]) { 31444099b7bSPaul Traina stmp = &(argv[0][2]); 31544099b7bSPaul Traina } else { 31644099b7bSPaul Traina argc--; 31744099b7bSPaul Traina argv++; 31844099b7bSPaul Traina stmp = argv[0]; 31944099b7bSPaul Traina } 32044099b7bSPaul Traina if (!stmp) { 321798c69cdSPoul-Henning Kamp report(LOG_ERR, 32244099b7bSPaul Traina "bootpd: missing hostname\n"); 32344099b7bSPaul Traina break; 32444099b7bSPaul Traina } 325e08ac58bSPaul Traina hostname = stmp; 32644099b7bSPaul Traina break; 32744099b7bSPaul Traina 32844099b7bSPaul Traina case 'i': /* inetd mode */ 32944099b7bSPaul Traina standalone = FALSE; 33044099b7bSPaul Traina break; 33144099b7bSPaul Traina 33244099b7bSPaul Traina case 's': /* standalone mode */ 33344099b7bSPaul Traina standalone = TRUE; 33444099b7bSPaul Traina break; 33544099b7bSPaul Traina 33644099b7bSPaul Traina case 't': /* timeout */ 33744099b7bSPaul Traina if (argv[0][2]) { 33844099b7bSPaul Traina stmp = &(argv[0][2]); 33944099b7bSPaul Traina } else { 34044099b7bSPaul Traina argc--; 34144099b7bSPaul Traina argv++; 34244099b7bSPaul Traina stmp = argv[0]; 34344099b7bSPaul Traina } 34444099b7bSPaul Traina if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 345798c69cdSPoul-Henning Kamp report(LOG_ERR, 34644099b7bSPaul Traina "%s: invalid timeout specification\n", progname); 34744099b7bSPaul Traina break; 34844099b7bSPaul Traina } 34944099b7bSPaul Traina actualtimeout.tv_sec = (int32) (60 * n); 35044099b7bSPaul Traina /* 35144099b7bSPaul Traina * If the actual timeout is zero, pass a NULL pointer 35244099b7bSPaul Traina * to select so it blocks indefinitely, otherwise, 35344099b7bSPaul Traina * point to the actual timeout value. 35444099b7bSPaul Traina */ 35544099b7bSPaul Traina timeout = (n > 0) ? &actualtimeout : NULL; 35644099b7bSPaul Traina break; 35744099b7bSPaul Traina 35844099b7bSPaul Traina default: 359798c69cdSPoul-Henning Kamp report(LOG_ERR, "%s: unknown switch: -%c\n", 36044099b7bSPaul Traina progname, argv[0][1]); 36144099b7bSPaul Traina usage(); 36244099b7bSPaul Traina break; 36344099b7bSPaul Traina 36444099b7bSPaul Traina } /* switch */ 36544099b7bSPaul Traina } /* for args */ 36644099b7bSPaul Traina 36744099b7bSPaul Traina /* 36844099b7bSPaul Traina * Override default file names if specified on the command line. 36944099b7bSPaul Traina */ 37044099b7bSPaul Traina if (argc > 0) 37144099b7bSPaul Traina bootptab = argv[0]; 37244099b7bSPaul Traina 37344099b7bSPaul Traina if (argc > 1) 37444099b7bSPaul Traina bootpd_dump = argv[1]; 37544099b7bSPaul Traina 37644099b7bSPaul Traina /* 37744099b7bSPaul Traina * Get my hostname and IP address. 37844099b7bSPaul Traina */ 379e08ac58bSPaul Traina 38044099b7bSPaul Traina hep = gethostbyname(hostname); 38144099b7bSPaul Traina if (!hep) { 382798c69cdSPoul-Henning Kamp report(LOG_ERR, "Can not get my IP address\n"); 38344099b7bSPaul Traina exit(1); 38444099b7bSPaul Traina } 38544099b7bSPaul Traina bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); 38644099b7bSPaul Traina 38744099b7bSPaul Traina if (standalone) { 38844099b7bSPaul Traina /* 38944099b7bSPaul Traina * Go into background and disassociate from controlling terminal. 39044099b7bSPaul Traina */ 39144099b7bSPaul Traina if (debug < 3) { 39244099b7bSPaul Traina if (fork()) 39344099b7bSPaul Traina exit(0); 39444099b7bSPaul Traina #ifdef NO_SETSID 39544099b7bSPaul Traina setpgrp(0,0); 39644099b7bSPaul Traina #ifdef TIOCNOTTY 3971a37aa56SDavid E. O'Brien n = open(_PATH_TTY, O_RDWR); 39844099b7bSPaul Traina if (n >= 0) { 39944099b7bSPaul Traina ioctl(n, TIOCNOTTY, (char *) 0); 40044099b7bSPaul Traina (void) close(n); 40144099b7bSPaul Traina } 40244099b7bSPaul Traina #endif /* TIOCNOTTY */ 40344099b7bSPaul Traina #else /* SETSID */ 40444099b7bSPaul Traina if (setsid() < 0) 40544099b7bSPaul Traina perror("setsid"); 40644099b7bSPaul Traina #endif /* SETSID */ 40744099b7bSPaul Traina } /* if debug < 3 */ 40844099b7bSPaul Traina 40944099b7bSPaul Traina /* 41044099b7bSPaul Traina * Nuke any timeout value 41144099b7bSPaul Traina */ 41244099b7bSPaul Traina timeout = NULL; 41344099b7bSPaul Traina 41444099b7bSPaul Traina } /* if standalone (1st) */ 41544099b7bSPaul Traina 41644099b7bSPaul Traina /* Set the cwd (i.e. to /tftpboot) */ 41744099b7bSPaul Traina if (chdir_path) { 41844099b7bSPaul Traina if (chdir(chdir_path) < 0) 41944099b7bSPaul Traina report(LOG_ERR, "%s: chdir failed", chdir_path); 42044099b7bSPaul Traina } 42144099b7bSPaul Traina 42244099b7bSPaul Traina /* Get the timezone. */ 42344099b7bSPaul Traina tzone_init(); 42444099b7bSPaul Traina 42544099b7bSPaul Traina /* Allocate hash tables. */ 42644099b7bSPaul Traina rdtab_init(); 42744099b7bSPaul Traina 42844099b7bSPaul Traina /* 42944099b7bSPaul Traina * Read the bootptab file. 43044099b7bSPaul Traina */ 43144099b7bSPaul Traina readtab(1); /* force read */ 43244099b7bSPaul Traina 43344099b7bSPaul Traina if (standalone) { 43444099b7bSPaul Traina 43544099b7bSPaul Traina /* 43644099b7bSPaul Traina * Create a socket. 43744099b7bSPaul Traina */ 43844099b7bSPaul Traina if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 43944099b7bSPaul Traina report(LOG_ERR, "socket: %s", get_network_errmsg()); 44044099b7bSPaul Traina exit(1); 44144099b7bSPaul Traina } 44244099b7bSPaul Traina 44344099b7bSPaul Traina /* 44444099b7bSPaul Traina * Get server's listening port number 44544099b7bSPaul Traina */ 44644099b7bSPaul Traina servp = getservbyname("bootps", "udp"); 44744099b7bSPaul Traina if (servp) { 44844099b7bSPaul Traina bootps_port = ntohs((u_short) servp->s_port); 44944099b7bSPaul Traina } else { 45044099b7bSPaul Traina bootps_port = (u_short) IPPORT_BOOTPS; 45144099b7bSPaul Traina report(LOG_ERR, 452538015aaSPhilippe Charnier "bootps/udp: unknown service -- using port %d", 45344099b7bSPaul Traina bootps_port); 45444099b7bSPaul Traina } 45544099b7bSPaul Traina 45644099b7bSPaul Traina /* 45744099b7bSPaul Traina * Bind socket to BOOTPS port. 45844099b7bSPaul Traina */ 45944099b7bSPaul Traina bind_addr.sin_family = AF_INET; 46044099b7bSPaul Traina bind_addr.sin_addr.s_addr = INADDR_ANY; 46144099b7bSPaul Traina bind_addr.sin_port = htons(bootps_port); 46244099b7bSPaul Traina if (bind(s, (struct sockaddr *) &bind_addr, 46344099b7bSPaul Traina sizeof(bind_addr)) < 0) 46444099b7bSPaul Traina { 46544099b7bSPaul Traina report(LOG_ERR, "bind: %s", get_network_errmsg()); 46644099b7bSPaul Traina exit(1); 46744099b7bSPaul Traina } 46844099b7bSPaul Traina } /* if standalone (2nd)*/ 46944099b7bSPaul Traina 47044099b7bSPaul Traina /* 47144099b7bSPaul Traina * Get destination port number so we can reply to client 47244099b7bSPaul Traina */ 47344099b7bSPaul Traina servp = getservbyname("bootpc", "udp"); 47444099b7bSPaul Traina if (servp) { 47544099b7bSPaul Traina bootpc_port = ntohs(servp->s_port); 47644099b7bSPaul Traina } else { 47744099b7bSPaul Traina report(LOG_ERR, 478538015aaSPhilippe Charnier "bootpc/udp: unknown service -- using port %d", 47944099b7bSPaul Traina IPPORT_BOOTPC); 48044099b7bSPaul Traina bootpc_port = (u_short) IPPORT_BOOTPC; 48144099b7bSPaul Traina } 48244099b7bSPaul Traina 48344099b7bSPaul Traina /* 48444099b7bSPaul Traina * Set up signals to read or dump the table. 48544099b7bSPaul Traina */ 486e08ac58bSPaul Traina #ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ 487e08ac58bSPaul Traina sa.sa_handler = catcher; 488e08ac58bSPaul Traina sigemptyset(&sa.sa_mask); 489e08ac58bSPaul Traina sa.sa_flags = 0; 490e08ac58bSPaul Traina if (sigaction(SIGHUP, &sa, NULL) < 0) { 491e08ac58bSPaul Traina report(LOG_ERR, "sigaction: %s", get_errmsg()); 492e08ac58bSPaul Traina exit(1); 493e08ac58bSPaul Traina } 494e08ac58bSPaul Traina if (sigaction(SIGUSR1, &sa, NULL) < 0) { 495e08ac58bSPaul Traina report(LOG_ERR, "sigaction: %s", get_errmsg()); 496e08ac58bSPaul Traina exit(1); 497e08ac58bSPaul Traina } 498e08ac58bSPaul Traina #else /* SA_NOCLDSTOP */ 499e08ac58bSPaul Traina /* Old-fashioned UNIX signals */ 50044099b7bSPaul Traina if ((int) signal(SIGHUP, catcher) < 0) { 50144099b7bSPaul Traina report(LOG_ERR, "signal: %s", get_errmsg()); 50244099b7bSPaul Traina exit(1); 50344099b7bSPaul Traina } 50444099b7bSPaul Traina if ((int) signal(SIGUSR1, catcher) < 0) { 50544099b7bSPaul Traina report(LOG_ERR, "signal: %s", get_errmsg()); 50644099b7bSPaul Traina exit(1); 50744099b7bSPaul Traina } 508e08ac58bSPaul Traina #endif /* SA_NOCLDSTOP */ 50944099b7bSPaul Traina 51044099b7bSPaul Traina /* 51144099b7bSPaul Traina * Process incoming requests. 51244099b7bSPaul Traina */ 513b6a0d472SJohn-Mark Gurney FD_ZERO(&readfds); 51444099b7bSPaul Traina for (;;) { 515e08ac58bSPaul Traina struct timeval tv; 516e08ac58bSPaul Traina 517b6a0d472SJohn-Mark Gurney FD_SET(s, &readfds); 518e08ac58bSPaul Traina if (timeout) 519e08ac58bSPaul Traina tv = *timeout; 520e08ac58bSPaul Traina 521b6a0d472SJohn-Mark Gurney nfound = select(s + 1, &readfds, NULL, NULL, 522e08ac58bSPaul Traina (timeout) ? &tv : NULL); 52344099b7bSPaul Traina if (nfound < 0) { 52444099b7bSPaul Traina if (errno != EINTR) { 52544099b7bSPaul Traina report(LOG_ERR, "select: %s", get_errmsg()); 52644099b7bSPaul Traina } 52744099b7bSPaul Traina /* 52844099b7bSPaul Traina * Call readtab() or dumptab() here to avoid the 52944099b7bSPaul Traina * dangers of doing I/O from a signal handler. 53044099b7bSPaul Traina */ 53144099b7bSPaul Traina if (do_readtab) { 53244099b7bSPaul Traina do_readtab = 0; 53344099b7bSPaul Traina readtab(1); /* force read */ 53444099b7bSPaul Traina } 53544099b7bSPaul Traina if (do_dumptab) { 53644099b7bSPaul Traina do_dumptab = 0; 53744099b7bSPaul Traina dumptab(bootpd_dump); 53844099b7bSPaul Traina } 53944099b7bSPaul Traina continue; 54044099b7bSPaul Traina } 541b6a0d472SJohn-Mark Gurney if (!FD_ISSET(s, &readfds)) { 54244099b7bSPaul Traina if (debug > 1) 54309c00166STom Rhodes report(LOG_INFO, "exiting after %jd minutes of inactivity", 54409c00166STom Rhodes (intmax_t)actualtimeout.tv_sec / 60); 54544099b7bSPaul Traina exit(0); 54644099b7bSPaul Traina } 54744099b7bSPaul Traina ra_len = sizeof(recv_addr); 54844099b7bSPaul Traina n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, 54944099b7bSPaul Traina (struct sockaddr *) &recv_addr, &ra_len); 55044099b7bSPaul Traina if (n <= 0) { 55144099b7bSPaul Traina continue; 55244099b7bSPaul Traina } 55344099b7bSPaul Traina if (debug > 1) { 55444099b7bSPaul Traina report(LOG_INFO, "recvd pkt from IP addr %s", 55544099b7bSPaul Traina inet_ntoa(recv_addr.sin_addr)); 55644099b7bSPaul Traina } 55744099b7bSPaul Traina if (n < sizeof(struct bootp)) { 55844099b7bSPaul Traina if (debug) { 559e08ac58bSPaul Traina report(LOG_NOTICE, "received short packet"); 56044099b7bSPaul Traina } 56144099b7bSPaul Traina continue; 56244099b7bSPaul Traina } 56344099b7bSPaul Traina pktlen = n; 56444099b7bSPaul Traina 56544099b7bSPaul Traina readtab(0); /* maybe re-read bootptab */ 56644099b7bSPaul Traina 56744099b7bSPaul Traina switch (bp->bp_op) { 56844099b7bSPaul Traina case BOOTREQUEST: 56944099b7bSPaul Traina handle_request(); 57044099b7bSPaul Traina break; 57144099b7bSPaul Traina case BOOTREPLY: 57244099b7bSPaul Traina handle_reply(); 57344099b7bSPaul Traina break; 57444099b7bSPaul Traina } 57544099b7bSPaul Traina } 57685966371SWarner Losh return 0; 57744099b7bSPaul Traina } 57844099b7bSPaul Traina 57944099b7bSPaul Traina 58044099b7bSPaul Traina 58144099b7bSPaul Traina 58244099b7bSPaul Traina /* 58344099b7bSPaul Traina * Print "usage" message and exit 58444099b7bSPaul Traina */ 58544099b7bSPaul Traina 58644099b7bSPaul Traina PRIVATE void 58744099b7bSPaul Traina usage() 58844099b7bSPaul Traina { 58944099b7bSPaul Traina fprintf(stderr, 590*d3f77d0aSMark Johnston "usage: bootpd [-a] [-i | -s] [-c chdir-path] [-d level] [-h hostname]\n" 591*d3f77d0aSMark Johnston " [-t timeout] [bootptab [dumpfile]]\n"); 59235131b46SMark Johnston fprintf(stderr, "\t -a\tdon't modify ARP table\n"); 59344099b7bSPaul Traina fprintf(stderr, "\t -c n\tset current directory\n"); 59444099b7bSPaul Traina fprintf(stderr, "\t -d n\tset debug level\n"); 595dc546e1aSPeter Pentchev fprintf(stderr, "\t -h n\tset the hostname to listen on\n"); 59644099b7bSPaul Traina fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); 59744099b7bSPaul Traina fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); 59844099b7bSPaul Traina fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); 59944099b7bSPaul Traina exit(1); 60044099b7bSPaul Traina } 60144099b7bSPaul Traina 60244099b7bSPaul Traina /* Signal catchers */ 60344099b7bSPaul Traina PRIVATE void 60444099b7bSPaul Traina catcher(sig) 60544099b7bSPaul Traina int sig; 60644099b7bSPaul Traina { 60744099b7bSPaul Traina if (sig == SIGHUP) 60844099b7bSPaul Traina do_readtab = 1; 60944099b7bSPaul Traina if (sig == SIGUSR1) 61044099b7bSPaul Traina do_dumptab = 1; 611e08ac58bSPaul Traina #if !defined(SA_NOCLDSTOP) && defined(SYSV) 612e08ac58bSPaul Traina /* For older "System V" derivatives with no sigaction(). */ 61344099b7bSPaul Traina signal(sig, catcher); 61444099b7bSPaul Traina #endif 61544099b7bSPaul Traina } 61644099b7bSPaul Traina 61744099b7bSPaul Traina 61844099b7bSPaul Traina 61944099b7bSPaul Traina /* 62044099b7bSPaul Traina * Process BOOTREQUEST packet. 62144099b7bSPaul Traina * 62244099b7bSPaul Traina * Note: This version of the bootpd.c server never forwards 62344099b7bSPaul Traina * a request to another server. That is the job of a gateway 62444099b7bSPaul Traina * program such as the "bootpgw" program included here. 62544099b7bSPaul Traina * 62644099b7bSPaul Traina * (Also this version does not interpret the hostname field of 62744099b7bSPaul Traina * the request packet; it COULD do a name->address lookup and 62844099b7bSPaul Traina * forward the request there.) 62944099b7bSPaul Traina */ 63044099b7bSPaul Traina PRIVATE void 63144099b7bSPaul Traina handle_request() 63244099b7bSPaul Traina { 63344099b7bSPaul Traina struct bootp *bp = (struct bootp *) pktbuf; 63444099b7bSPaul Traina struct host *hp = NULL; 63544099b7bSPaul Traina struct host dummyhost; 63644099b7bSPaul Traina int32 bootsize = 0; 63744099b7bSPaul Traina unsigned hlen, hashcode; 63844099b7bSPaul Traina int32 dest; 63944099b7bSPaul Traina char realpath[1024]; 64044099b7bSPaul Traina char *clntpath; 64144099b7bSPaul Traina char *homedir, *bootfile; 64244099b7bSPaul Traina int n; 64344099b7bSPaul Traina 6441e13299fSEd Maste if (bp->bp_htype >= hwinfocnt) { 6451e13299fSEd Maste report(LOG_NOTICE, "bad hw addr type %u", bp->bp_htype); 6461e13299fSEd Maste return; 6471e13299fSEd Maste } 648d284feaaSEivind Eklund bp->bp_file[sizeof(bp->bp_file)-1] = '\0'; 649d284feaaSEivind Eklund 65044099b7bSPaul Traina /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ 65144099b7bSPaul Traina 65244099b7bSPaul Traina /* 65344099b7bSPaul Traina * If the servername field is set, compare it against us. 65444099b7bSPaul Traina * If we're not being addressed, ignore this request. 65544099b7bSPaul Traina * If the server name field is null, throw in our name. 65644099b7bSPaul Traina */ 65744099b7bSPaul Traina if (strlen(bp->bp_sname)) { 65844099b7bSPaul Traina if (strcmp(bp->bp_sname, hostname)) { 65944099b7bSPaul Traina if (debug) 66044099b7bSPaul Traina report(LOG_INFO, "\ 66144099b7bSPaul Traina ignoring request for server %s from client at %s address %s", 66244099b7bSPaul Traina bp->bp_sname, netname(bp->bp_htype), 66344099b7bSPaul Traina haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 66444099b7bSPaul Traina /* XXX - Is it correct to ignore such a request? -gwr */ 66544099b7bSPaul Traina return; 66644099b7bSPaul Traina } 66744099b7bSPaul Traina } else { 66844099b7bSPaul Traina strcpy(bp->bp_sname, hostname); 66944099b7bSPaul Traina } 67044099b7bSPaul Traina 67144099b7bSPaul Traina /* Convert the request into a reply. */ 67244099b7bSPaul Traina bp->bp_op = BOOTREPLY; 67344099b7bSPaul Traina if (bp->bp_ciaddr.s_addr == 0) { 67444099b7bSPaul Traina /* 6751acf0dbaSUlrich Spörlein * client doesn't know his IP address, 67644099b7bSPaul Traina * search by hardware address. 67744099b7bSPaul Traina */ 67844099b7bSPaul Traina if (debug > 1) { 67944099b7bSPaul Traina report(LOG_INFO, "request from %s address %s", 68044099b7bSPaul Traina netname(bp->bp_htype), 68144099b7bSPaul Traina haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 68244099b7bSPaul Traina } 68344099b7bSPaul Traina hlen = haddrlength(bp->bp_htype); 68444099b7bSPaul Traina if (hlen != bp->bp_hlen) { 6859bf69bf2SJohn-Mark Gurney report(LOG_NOTICE, "bad addr len from %s address %s", 68644099b7bSPaul Traina netname(bp->bp_htype), 68744099b7bSPaul Traina haddrtoa(bp->bp_chaddr, hlen)); 68844099b7bSPaul Traina } 68944099b7bSPaul Traina dummyhost.htype = bp->bp_htype; 69044099b7bSPaul Traina bcopy(bp->bp_chaddr, dummyhost.haddr, hlen); 69144099b7bSPaul Traina hashcode = hash_HashFunction(bp->bp_chaddr, hlen); 69244099b7bSPaul Traina hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, 69344099b7bSPaul Traina &dummyhost); 69444099b7bSPaul Traina if (hp == NULL && 69544099b7bSPaul Traina bp->bp_htype == HTYPE_IEEE802) 69644099b7bSPaul Traina { 69744099b7bSPaul Traina /* Try again with address in "canonical" form. */ 69844099b7bSPaul Traina haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); 69944099b7bSPaul Traina if (debug > 1) { 70044099b7bSPaul Traina report(LOG_INFO, "\ 70144099b7bSPaul Traina HW addr type is IEEE 802. convert to %s and check again\n", 70244099b7bSPaul Traina haddrtoa(dummyhost.haddr, bp->bp_hlen)); 70344099b7bSPaul Traina } 70444099b7bSPaul Traina hashcode = hash_HashFunction(dummyhost.haddr, hlen); 70544099b7bSPaul Traina hp = (struct host *) hash_Lookup(hwhashtable, hashcode, 70644099b7bSPaul Traina hwlookcmp, &dummyhost); 70744099b7bSPaul Traina } 70844099b7bSPaul Traina if (hp == NULL) { 70944099b7bSPaul Traina /* 71044099b7bSPaul Traina * XXX - Add dynamic IP address assignment? 71144099b7bSPaul Traina */ 712e08ac58bSPaul Traina if (debug) 713e08ac58bSPaul Traina report(LOG_NOTICE, "unknown client %s address %s", 71444099b7bSPaul Traina netname(bp->bp_htype), 71544099b7bSPaul Traina haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 71644099b7bSPaul Traina return; /* not found */ 71744099b7bSPaul Traina } 71844099b7bSPaul Traina (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; 71944099b7bSPaul Traina 72044099b7bSPaul Traina } else { 72144099b7bSPaul Traina 72244099b7bSPaul Traina /* 72344099b7bSPaul Traina * search by IP address. 72444099b7bSPaul Traina */ 72544099b7bSPaul Traina if (debug > 1) { 72644099b7bSPaul Traina report(LOG_INFO, "request from IP addr %s", 72744099b7bSPaul Traina inet_ntoa(bp->bp_ciaddr)); 72844099b7bSPaul Traina } 72944099b7bSPaul Traina dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr; 73044099b7bSPaul Traina hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4); 73144099b7bSPaul Traina hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, 73244099b7bSPaul Traina &dummyhost); 73344099b7bSPaul Traina if (hp == NULL) { 734e08ac58bSPaul Traina if (debug) { 73544099b7bSPaul Traina report(LOG_NOTICE, "IP address not found: %s", 73644099b7bSPaul Traina inet_ntoa(bp->bp_ciaddr)); 73744099b7bSPaul Traina } 73844099b7bSPaul Traina return; 73944099b7bSPaul Traina } 74044099b7bSPaul Traina } 74144099b7bSPaul Traina 74244099b7bSPaul Traina if (debug) { 74344099b7bSPaul Traina report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr), 74444099b7bSPaul Traina hp->hostname->string); 74544099b7bSPaul Traina } 74644099b7bSPaul Traina 74744099b7bSPaul Traina /* 74844099b7bSPaul Traina * If there is a response delay threshold, ignore requests 74944099b7bSPaul Traina * with a timestamp lower than the threshold. 75044099b7bSPaul Traina */ 75144099b7bSPaul Traina if (hp->flags.min_wait) { 75244099b7bSPaul Traina u_int32 t = (u_int32) ntohs(bp->bp_secs); 75344099b7bSPaul Traina if (t < hp->min_wait) { 75444099b7bSPaul Traina if (debug > 1) 75544099b7bSPaul Traina report(LOG_INFO, 75644099b7bSPaul Traina "ignoring request due to timestamp (%d < %d)", 75744099b7bSPaul Traina t, hp->min_wait); 75844099b7bSPaul Traina return; 75944099b7bSPaul Traina } 76044099b7bSPaul Traina } 76144099b7bSPaul Traina 76244099b7bSPaul Traina #ifdef YORK_EX_OPTION 76344099b7bSPaul Traina /* 76444099b7bSPaul Traina * The need for the "ex" tag arose out of the need to empty 76544099b7bSPaul Traina * shared networked drives on diskless PCs. This solution is 76644099b7bSPaul Traina * not very clean but it does work fairly well. 76744099b7bSPaul Traina * Written by Edmund J. Sutcliffe <edmund@york.ac.uk> 76844099b7bSPaul Traina * 76944099b7bSPaul Traina * XXX - This could compromise security if a non-trusted user 77044099b7bSPaul Traina * managed to write an entry in the bootptab with :ex=trojan: 77144099b7bSPaul Traina * so I would leave this turned off unless you need it. -gwr 77244099b7bSPaul Traina */ 77344099b7bSPaul Traina /* Run a program, passing the client name as a parameter. */ 77444099b7bSPaul Traina if (hp->flags.exec_file) { 77544099b7bSPaul Traina char tst[100]; 77644099b7bSPaul Traina /* XXX - Check string lengths? -gwr */ 77744099b7bSPaul Traina strcpy (tst, hp->exec_file->string); 77844099b7bSPaul Traina strcat (tst, " "); 77944099b7bSPaul Traina strcat (tst, hp->hostname->string); 78044099b7bSPaul Traina strcat (tst, " &"); 78144099b7bSPaul Traina if (debug) 78244099b7bSPaul Traina report(LOG_INFO, "executing %s", tst); 78344099b7bSPaul Traina system(tst); /* Hope this finishes soon... */ 78444099b7bSPaul Traina } 78544099b7bSPaul Traina #endif /* YORK_EX_OPTION */ 78644099b7bSPaul Traina 78744099b7bSPaul Traina /* 78844099b7bSPaul Traina * If a specific TFTP server address was specified in the bootptab file, 78944099b7bSPaul Traina * fill it in, otherwise zero it. 79044099b7bSPaul Traina * XXX - Rather than zero it, should it be the bootpd address? -gwr 79144099b7bSPaul Traina */ 79244099b7bSPaul Traina (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ? 79344099b7bSPaul Traina hp->bootserver.s_addr : 0L; 79444099b7bSPaul Traina 79544099b7bSPaul Traina #ifdef STANFORD_PROM_COMPAT 79644099b7bSPaul Traina /* 79744099b7bSPaul Traina * Stanford bootp PROMs (for a Sun?) have no way to leave 79844099b7bSPaul Traina * the boot file name field blank (because the boot file 79944099b7bSPaul Traina * name is automatically generated from some index). 80044099b7bSPaul Traina * As a work-around, this little hack allows those PROMs to 80144099b7bSPaul Traina * specify "sunboot14" with the same effect as a NULL name. 80244099b7bSPaul Traina * (The user specifies boot device 14 or some such magic.) 80344099b7bSPaul Traina */ 80444099b7bSPaul Traina if (strcmp(bp->bp_file, "sunboot14") == 0) 80544099b7bSPaul Traina bp->bp_file[0] = '\0'; /* treat it as unspecified */ 80644099b7bSPaul Traina #endif 80744099b7bSPaul Traina 80844099b7bSPaul Traina /* 80944099b7bSPaul Traina * Fill in the client's proper bootfile. 81044099b7bSPaul Traina * 81144099b7bSPaul Traina * If the client specifies an absolute path, try that file with a 81244099b7bSPaul Traina * ".host" suffix and then without. If the file cannot be found, no 81344099b7bSPaul Traina * reply is made at all. 81444099b7bSPaul Traina * 81544099b7bSPaul Traina * If the client specifies a null or relative file, use the following 81644099b7bSPaul Traina * table to determine the appropriate action: 81744099b7bSPaul Traina * 81844099b7bSPaul Traina * Homedir Bootfile Client's file 81944099b7bSPaul Traina * specified? specified? specification Action 82044099b7bSPaul Traina * ------------------------------------------------------------------- 82144099b7bSPaul Traina * No No Null Send null filename 82244099b7bSPaul Traina * No No Relative Discard request 82344099b7bSPaul Traina * No Yes Null Send if absolute else null 82444099b7bSPaul Traina * No Yes Relative Discard request *XXX 82544099b7bSPaul Traina * Yes No Null Send null filename 82644099b7bSPaul Traina * Yes No Relative Lookup with ".host" 82744099b7bSPaul Traina * Yes Yes Null Send home/boot or bootfile 82844099b7bSPaul Traina * Yes Yes Relative Lookup with ".host" *XXX 82944099b7bSPaul Traina * 83044099b7bSPaul Traina */ 83144099b7bSPaul Traina 83244099b7bSPaul Traina /* 83344099b7bSPaul Traina * XXX - I don't like the policy of ignoring a client when the 83444099b7bSPaul Traina * boot file is not accessible. The TFTP server might not be 83544099b7bSPaul Traina * running on the same machine as the BOOTP server, in which 83644099b7bSPaul Traina * case checking accessibility of the boot file is pointless. 83744099b7bSPaul Traina * 83844099b7bSPaul Traina * Therefore, file accessibility is now demanded ONLY if you 83944099b7bSPaul Traina * define CHECK_FILE_ACCESS in the Makefile options. -gwr 84044099b7bSPaul Traina */ 84144099b7bSPaul Traina 84244099b7bSPaul Traina /* 84344099b7bSPaul Traina * The "real" path is as seen by the BOOTP daemon on this 84444099b7bSPaul Traina * machine, while the client path is relative to the TFTP 84544099b7bSPaul Traina * daemon chroot directory (i.e. /tftpboot). 84644099b7bSPaul Traina */ 84744099b7bSPaul Traina if (hp->flags.tftpdir) { 8483a3c0cf4SMatthew Dillon snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string); 84944099b7bSPaul Traina clntpath = &realpath[strlen(realpath)]; 85044099b7bSPaul Traina } else { 85144099b7bSPaul Traina realpath[0] = '\0'; 85244099b7bSPaul Traina clntpath = realpath; 85344099b7bSPaul Traina } 85444099b7bSPaul Traina 85544099b7bSPaul Traina /* 85644099b7bSPaul Traina * Determine client's requested homedir and bootfile. 85744099b7bSPaul Traina */ 85844099b7bSPaul Traina homedir = NULL; 85944099b7bSPaul Traina bootfile = NULL; 86044099b7bSPaul Traina if (bp->bp_file[0]) { 86144099b7bSPaul Traina homedir = bp->bp_file; 86244099b7bSPaul Traina bootfile = strrchr(homedir, '/'); 86344099b7bSPaul Traina if (bootfile) { 86444099b7bSPaul Traina if (homedir == bootfile) 86544099b7bSPaul Traina homedir = NULL; 86644099b7bSPaul Traina *bootfile++ = '\0'; 86744099b7bSPaul Traina } else { 86844099b7bSPaul Traina /* no "/" in the string */ 86944099b7bSPaul Traina bootfile = homedir; 87044099b7bSPaul Traina homedir = NULL; 87144099b7bSPaul Traina } 87244099b7bSPaul Traina if (debug > 2) { 87344099b7bSPaul Traina report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", 87444099b7bSPaul Traina (homedir) ? homedir : "", 87544099b7bSPaul Traina (bootfile) ? bootfile : ""); 87644099b7bSPaul Traina } 87744099b7bSPaul Traina } 87844099b7bSPaul Traina 87944099b7bSPaul Traina /* 88044099b7bSPaul Traina * Specifications in bootptab override client requested values. 88144099b7bSPaul Traina */ 88244099b7bSPaul Traina if (hp->flags.homedir) 88344099b7bSPaul Traina homedir = hp->homedir->string; 88444099b7bSPaul Traina if (hp->flags.bootfile) 88544099b7bSPaul Traina bootfile = hp->bootfile->string; 88644099b7bSPaul Traina 88744099b7bSPaul Traina /* 88844099b7bSPaul Traina * Construct bootfile path. 88944099b7bSPaul Traina */ 89044099b7bSPaul Traina if (homedir) { 89144099b7bSPaul Traina if (homedir[0] != '/') 89244099b7bSPaul Traina strcat(clntpath, "/"); 89344099b7bSPaul Traina strcat(clntpath, homedir); 89444099b7bSPaul Traina homedir = NULL; 89544099b7bSPaul Traina } 89644099b7bSPaul Traina if (bootfile) { 89744099b7bSPaul Traina if (bootfile[0] != '/') 89844099b7bSPaul Traina strcat(clntpath, "/"); 89944099b7bSPaul Traina strcat(clntpath, bootfile); 90044099b7bSPaul Traina bootfile = NULL; 90144099b7bSPaul Traina } 90244099b7bSPaul Traina 90344099b7bSPaul Traina /* 90444099b7bSPaul Traina * First try to find the file with a ".host" suffix 90544099b7bSPaul Traina */ 90644099b7bSPaul Traina n = strlen(clntpath); 90744099b7bSPaul Traina strcat(clntpath, "."); 90844099b7bSPaul Traina strcat(clntpath, hp->hostname->string); 90944099b7bSPaul Traina if (chk_access(realpath, &bootsize) < 0) { 91044099b7bSPaul Traina clntpath[n] = 0; /* Try it without the suffix */ 91144099b7bSPaul Traina if (chk_access(realpath, &bootsize) < 0) { 91244099b7bSPaul Traina /* neither "file.host" nor "file" was found */ 91344099b7bSPaul Traina #ifdef CHECK_FILE_ACCESS 91444099b7bSPaul Traina 91544099b7bSPaul Traina if (bp->bp_file[0]) { 91644099b7bSPaul Traina /* 91744099b7bSPaul Traina * Client wanted specific file 91844099b7bSPaul Traina * and we didn't have it. 91944099b7bSPaul Traina */ 92044099b7bSPaul Traina report(LOG_NOTICE, 92144099b7bSPaul Traina "requested file not found: \"%s\"", clntpath); 92244099b7bSPaul Traina return; 92344099b7bSPaul Traina } 92444099b7bSPaul Traina /* 92544099b7bSPaul Traina * Client didn't ask for a specific file and we couldn't 92644099b7bSPaul Traina * access the default file, so just zero-out the bootfile 92744099b7bSPaul Traina * field in the packet and continue processing the reply. 92844099b7bSPaul Traina */ 92944099b7bSPaul Traina bzero(bp->bp_file, sizeof(bp->bp_file)); 93044099b7bSPaul Traina goto null_file_name; 93144099b7bSPaul Traina 93244099b7bSPaul Traina #else /* CHECK_FILE_ACCESS */ 93344099b7bSPaul Traina 93444099b7bSPaul Traina /* Complain only if boot file size was needed. */ 93544099b7bSPaul Traina if (hp->flags.bootsize_auto) { 93644099b7bSPaul Traina report(LOG_ERR, "can not determine size of file \"%s\"", 93744099b7bSPaul Traina clntpath); 93844099b7bSPaul Traina } 93944099b7bSPaul Traina 94044099b7bSPaul Traina #endif /* CHECK_FILE_ACCESS */ 94144099b7bSPaul Traina } 94244099b7bSPaul Traina } 94344099b7bSPaul Traina strncpy(bp->bp_file, clntpath, BP_FILE_LEN); 94444099b7bSPaul Traina if (debug > 2) 94544099b7bSPaul Traina report(LOG_INFO, "bootfile=\"%s\"", clntpath); 94644099b7bSPaul Traina 947e08ac58bSPaul Traina #ifdef CHECK_FILE_ACCESS 94844099b7bSPaul Traina null_file_name: 949e08ac58bSPaul Traina #endif /* CHECK_FILE_ACCESS */ 95044099b7bSPaul Traina 95144099b7bSPaul Traina 95244099b7bSPaul Traina /* 95344099b7bSPaul Traina * Handle vendor options based on magic number. 95444099b7bSPaul Traina */ 95544099b7bSPaul Traina 95644099b7bSPaul Traina if (debug > 1) { 95744099b7bSPaul Traina report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", 95844099b7bSPaul Traina (int) ((bp->bp_vend)[0]), 95944099b7bSPaul Traina (int) ((bp->bp_vend)[1]), 96044099b7bSPaul Traina (int) ((bp->bp_vend)[2]), 96144099b7bSPaul Traina (int) ((bp->bp_vend)[3])); 96244099b7bSPaul Traina } 96344099b7bSPaul Traina /* 96444099b7bSPaul Traina * If this host isn't set for automatic vendor info then copy the 96544099b7bSPaul Traina * specific cookie into the bootp packet, thus forcing a certain 96644099b7bSPaul Traina * reply format. Only force reply format if user specified it. 96744099b7bSPaul Traina */ 96844099b7bSPaul Traina if (hp->flags.vm_cookie) { 96944099b7bSPaul Traina /* Slam in the user specified magic number. */ 97044099b7bSPaul Traina bcopy(hp->vm_cookie, bp->bp_vend, 4); 97144099b7bSPaul Traina } 97244099b7bSPaul Traina /* 97344099b7bSPaul Traina * Figure out the format for the vendor-specific info. 97444099b7bSPaul Traina * Note that bp->bp_vend may have been set above. 97544099b7bSPaul Traina */ 97644099b7bSPaul Traina if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) { 97744099b7bSPaul Traina /* RFC1048 conformant bootp client */ 97844099b7bSPaul Traina dovend_rfc1048(bp, hp, bootsize); 97944099b7bSPaul Traina if (debug > 1) { 98044099b7bSPaul Traina report(LOG_INFO, "sending reply (with RFC1048 options)"); 98144099b7bSPaul Traina } 98244099b7bSPaul Traina } 98344099b7bSPaul Traina #ifdef VEND_CMU 98444099b7bSPaul Traina else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { 98544099b7bSPaul Traina dovend_cmu(bp, hp); 98644099b7bSPaul Traina if (debug > 1) { 98744099b7bSPaul Traina report(LOG_INFO, "sending reply (with CMU options)"); 98844099b7bSPaul Traina } 98944099b7bSPaul Traina } 99044099b7bSPaul Traina #endif 99144099b7bSPaul Traina else { 99244099b7bSPaul Traina if (debug > 1) { 99344099b7bSPaul Traina report(LOG_INFO, "sending reply (with no options)"); 99444099b7bSPaul Traina } 99544099b7bSPaul Traina } 99644099b7bSPaul Traina 99744099b7bSPaul Traina dest = (hp->flags.reply_addr) ? 99844099b7bSPaul Traina hp->reply_addr.s_addr : 0L; 99944099b7bSPaul Traina 100044099b7bSPaul Traina /* not forwarded */ 100144099b7bSPaul Traina sendreply(0, dest); 100244099b7bSPaul Traina } 100344099b7bSPaul Traina 100444099b7bSPaul Traina 100544099b7bSPaul Traina /* 100644099b7bSPaul Traina * Process BOOTREPLY packet. 100744099b7bSPaul Traina */ 100844099b7bSPaul Traina PRIVATE void 100944099b7bSPaul Traina handle_reply() 101044099b7bSPaul Traina { 101144099b7bSPaul Traina if (debug) { 101244099b7bSPaul Traina report(LOG_INFO, "processing boot reply"); 101344099b7bSPaul Traina } 101444099b7bSPaul Traina /* forwarded, no destination override */ 101544099b7bSPaul Traina sendreply(1, 0); 101644099b7bSPaul Traina } 101744099b7bSPaul Traina 101844099b7bSPaul Traina 101944099b7bSPaul Traina /* 102044099b7bSPaul Traina * Send a reply packet to the client. 'forward' flag is set if we are 102144099b7bSPaul Traina * not the originator of this reply packet. 102244099b7bSPaul Traina */ 102344099b7bSPaul Traina PRIVATE void 102444099b7bSPaul Traina sendreply(forward, dst_override) 102544099b7bSPaul Traina int forward; 102644099b7bSPaul Traina int32 dst_override; 102744099b7bSPaul Traina { 102844099b7bSPaul Traina struct bootp *bp = (struct bootp *) pktbuf; 102944099b7bSPaul Traina struct in_addr dst; 103044099b7bSPaul Traina u_short port = bootpc_port; 103144099b7bSPaul Traina unsigned char *ha; 1032e08ac58bSPaul Traina int len, haf; 103344099b7bSPaul Traina 103444099b7bSPaul Traina /* 103544099b7bSPaul Traina * XXX - Should honor bp_flags "broadcast" bit here. 103644099b7bSPaul Traina * Temporary workaround: use the :ra=ADDR: option to 103744099b7bSPaul Traina * set the reply address to the broadcast address. 103844099b7bSPaul Traina */ 103944099b7bSPaul Traina 104044099b7bSPaul Traina /* 104144099b7bSPaul Traina * If the destination address was specified explicitly 10429bf69bf2SJohn-Mark Gurney * (i.e. the broadcast address for HP compatibility) 104344099b7bSPaul Traina * then send the response to that address. Otherwise, 104444099b7bSPaul Traina * act in accordance with RFC951: 104544099b7bSPaul Traina * If the client IP address is specified, use that 104644099b7bSPaul Traina * else if gateway IP address is specified, use that 104744099b7bSPaul Traina * else make a temporary arp cache entry for the client's 104844099b7bSPaul Traina * NEW IP/hardware address and use that. 104944099b7bSPaul Traina */ 105044099b7bSPaul Traina if (dst_override) { 105144099b7bSPaul Traina dst.s_addr = dst_override; 105244099b7bSPaul Traina if (debug > 1) { 105344099b7bSPaul Traina report(LOG_INFO, "reply address override: %s", 105444099b7bSPaul Traina inet_ntoa(dst)); 105544099b7bSPaul Traina } 105644099b7bSPaul Traina } else if (bp->bp_ciaddr.s_addr) { 105744099b7bSPaul Traina dst = bp->bp_ciaddr; 105844099b7bSPaul Traina } else if (bp->bp_giaddr.s_addr && forward == 0) { 105944099b7bSPaul Traina dst = bp->bp_giaddr; 106044099b7bSPaul Traina port = bootps_port; 106144099b7bSPaul Traina if (debug > 1) { 106244099b7bSPaul Traina report(LOG_INFO, "sending reply to gateway %s", 106344099b7bSPaul Traina inet_ntoa(dst)); 106444099b7bSPaul Traina } 106544099b7bSPaul Traina } else { 106644099b7bSPaul Traina dst = bp->bp_yiaddr; 106744099b7bSPaul Traina ha = bp->bp_chaddr; 106844099b7bSPaul Traina len = bp->bp_hlen; 106944099b7bSPaul Traina if (len > MAXHADDRLEN) 107044099b7bSPaul Traina len = MAXHADDRLEN; 1071e08ac58bSPaul Traina haf = (int) bp->bp_htype; 1072e08ac58bSPaul Traina if (haf == 0) 1073e08ac58bSPaul Traina haf = HTYPE_ETHERNET; 107444099b7bSPaul Traina 107535131b46SMark Johnston if (arpmod) { 107644099b7bSPaul Traina if (debug > 1) 107744099b7bSPaul Traina report(LOG_INFO, "setarp %s - %s", 107844099b7bSPaul Traina inet_ntoa(dst), haddrtoa(ha, len)); 1079e08ac58bSPaul Traina setarp(s, &dst, haf, ha, len); 108044099b7bSPaul Traina } 108135131b46SMark Johnston } 108244099b7bSPaul Traina 108344099b7bSPaul Traina if ((forward == 0) && 108444099b7bSPaul Traina (bp->bp_siaddr.s_addr == 0)) 108544099b7bSPaul Traina { 108644099b7bSPaul Traina struct ifreq *ifr; 108744099b7bSPaul Traina struct in_addr siaddr; 108844099b7bSPaul Traina /* 108944099b7bSPaul Traina * If we are originating this reply, we 109044099b7bSPaul Traina * need to find our own interface address to 109144099b7bSPaul Traina * put in the bp_siaddr field of the reply. 109244099b7bSPaul Traina * If this server is multi-homed, pick the 109344099b7bSPaul Traina * 'best' interface (the one on the same net 109444099b7bSPaul Traina * as the client). Of course, the client may 109544099b7bSPaul Traina * be on the other side of a BOOTP gateway... 109644099b7bSPaul Traina */ 109744099b7bSPaul Traina ifr = getif(s, &dst); 109844099b7bSPaul Traina if (ifr) { 109944099b7bSPaul Traina struct sockaddr_in *sip; 110044099b7bSPaul Traina sip = (struct sockaddr_in *) &(ifr->ifr_addr); 110144099b7bSPaul Traina siaddr = sip->sin_addr; 110244099b7bSPaul Traina } else { 110344099b7bSPaul Traina /* Just use my "official" IP address. */ 110444099b7bSPaul Traina siaddr = my_ip_addr; 110544099b7bSPaul Traina } 110644099b7bSPaul Traina 110744099b7bSPaul Traina /* XXX - No need to set bp_giaddr here. */ 110844099b7bSPaul Traina 110944099b7bSPaul Traina /* Finally, set the server address field. */ 111044099b7bSPaul Traina bp->bp_siaddr = siaddr; 111144099b7bSPaul Traina } 111244099b7bSPaul Traina /* Set up socket address for send. */ 111344099b7bSPaul Traina send_addr.sin_family = AF_INET; 111444099b7bSPaul Traina send_addr.sin_port = htons(port); 111544099b7bSPaul Traina send_addr.sin_addr = dst; 111644099b7bSPaul Traina 111744099b7bSPaul Traina /* Send reply with same size packet as request used. */ 111844099b7bSPaul Traina if (sendto(s, pktbuf, pktlen, 0, 111944099b7bSPaul Traina (struct sockaddr *) &send_addr, 112044099b7bSPaul Traina sizeof(send_addr)) < 0) 112144099b7bSPaul Traina { 112244099b7bSPaul Traina report(LOG_ERR, "sendto: %s", get_network_errmsg()); 112344099b7bSPaul Traina } 112444099b7bSPaul Traina } /* sendreply */ 112544099b7bSPaul Traina 112644099b7bSPaul Traina 112744099b7bSPaul Traina /* nmatch() - now in getif.c */ 112844099b7bSPaul Traina /* setarp() - now in hwaddr.c */ 112944099b7bSPaul Traina 113044099b7bSPaul Traina 113144099b7bSPaul Traina /* 113244099b7bSPaul Traina * This call checks read access to a file. It returns 0 if the file given 11331acf0dbaSUlrich Spörlein * by "path" exists and is publicly readable. A value of -1 is returned if 113444099b7bSPaul Traina * access is not permitted or an error occurs. Successful calls also 113544099b7bSPaul Traina * return the file size in bytes using the long pointer "filesize". 113644099b7bSPaul Traina * 113744099b7bSPaul Traina * The read permission bit for "other" users is checked. This bit must be 113844099b7bSPaul Traina * set for tftpd(8) to allow clients to read the file. 113944099b7bSPaul Traina */ 114044099b7bSPaul Traina 114144099b7bSPaul Traina PRIVATE int 114244099b7bSPaul Traina chk_access(path, filesize) 114344099b7bSPaul Traina char *path; 114444099b7bSPaul Traina int32 *filesize; 114544099b7bSPaul Traina { 114644099b7bSPaul Traina struct stat st; 114744099b7bSPaul Traina 114844099b7bSPaul Traina if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { 114944099b7bSPaul Traina *filesize = (int32) st.st_size; 115044099b7bSPaul Traina return 0; 115144099b7bSPaul Traina } else { 115244099b7bSPaul Traina return -1; 115344099b7bSPaul Traina } 115444099b7bSPaul Traina } 115544099b7bSPaul Traina 115644099b7bSPaul Traina 115744099b7bSPaul Traina /* 115844099b7bSPaul Traina * Now in dumptab.c : 115944099b7bSPaul Traina * dumptab() 116044099b7bSPaul Traina * dump_host() 116144099b7bSPaul Traina * list_ipaddresses() 116244099b7bSPaul Traina */ 116344099b7bSPaul Traina 116444099b7bSPaul Traina #ifdef VEND_CMU 116544099b7bSPaul Traina 116644099b7bSPaul Traina /* 116744099b7bSPaul Traina * Insert the CMU "vendor" data for the host pointed to by "hp" into the 116844099b7bSPaul Traina * bootp packet pointed to by "bp". 116944099b7bSPaul Traina */ 117044099b7bSPaul Traina 117144099b7bSPaul Traina PRIVATE void 117244099b7bSPaul Traina dovend_cmu(bp, hp) 117344099b7bSPaul Traina struct bootp *bp; 117444099b7bSPaul Traina struct host *hp; 117544099b7bSPaul Traina { 117644099b7bSPaul Traina struct cmu_vend *vendp; 117744099b7bSPaul Traina struct in_addr_list *taddr; 117844099b7bSPaul Traina 117944099b7bSPaul Traina /* 118044099b7bSPaul Traina * Initialize the entire vendor field to zeroes. 118144099b7bSPaul Traina */ 118244099b7bSPaul Traina bzero(bp->bp_vend, sizeof(bp->bp_vend)); 118344099b7bSPaul Traina 118444099b7bSPaul Traina /* 118544099b7bSPaul Traina * Fill in vendor information. Subnet mask, default gateway, 118644099b7bSPaul Traina * domain name server, ien name server, time server 118744099b7bSPaul Traina */ 118844099b7bSPaul Traina vendp = (struct cmu_vend *) bp->bp_vend; 118944099b7bSPaul Traina strcpy(vendp->v_magic, (char *)vm_cmu); 119044099b7bSPaul Traina if (hp->flags.subnet_mask) { 119144099b7bSPaul Traina (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; 119244099b7bSPaul Traina (vendp->v_flags) |= VF_SMASK; 119344099b7bSPaul Traina if (hp->flags.gateway) { 119444099b7bSPaul Traina (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; 119544099b7bSPaul Traina } 119644099b7bSPaul Traina } 119744099b7bSPaul Traina if (hp->flags.domain_server) { 119844099b7bSPaul Traina taddr = hp->domain_server; 119944099b7bSPaul Traina if (taddr->addrcount > 0) { 120044099b7bSPaul Traina (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; 120144099b7bSPaul Traina if (taddr->addrcount > 1) { 120244099b7bSPaul Traina (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; 120344099b7bSPaul Traina } 120444099b7bSPaul Traina } 120544099b7bSPaul Traina } 120644099b7bSPaul Traina if (hp->flags.name_server) { 120744099b7bSPaul Traina taddr = hp->name_server; 120844099b7bSPaul Traina if (taddr->addrcount > 0) { 120944099b7bSPaul Traina (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; 121044099b7bSPaul Traina if (taddr->addrcount > 1) { 121144099b7bSPaul Traina (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; 121244099b7bSPaul Traina } 121344099b7bSPaul Traina } 121444099b7bSPaul Traina } 121544099b7bSPaul Traina if (hp->flags.time_server) { 121644099b7bSPaul Traina taddr = hp->time_server; 121744099b7bSPaul Traina if (taddr->addrcount > 0) { 121844099b7bSPaul Traina (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; 121944099b7bSPaul Traina if (taddr->addrcount > 1) { 122044099b7bSPaul Traina (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; 122144099b7bSPaul Traina } 122244099b7bSPaul Traina } 122344099b7bSPaul Traina } 122444099b7bSPaul Traina /* Log message now done by caller. */ 122544099b7bSPaul Traina } /* dovend_cmu */ 122644099b7bSPaul Traina 122744099b7bSPaul Traina #endif /* VEND_CMU */ 122844099b7bSPaul Traina 122944099b7bSPaul Traina 123044099b7bSPaul Traina 123144099b7bSPaul Traina /* 123244099b7bSPaul Traina * Insert the RFC1048 vendor data for the host pointed to by "hp" into the 123344099b7bSPaul Traina * bootp packet pointed to by "bp". 123444099b7bSPaul Traina */ 123544099b7bSPaul Traina #define NEED(LEN, MSG) do \ 123644099b7bSPaul Traina if (bytesleft < (LEN)) { \ 123744099b7bSPaul Traina report(LOG_NOTICE, noroom, \ 123844099b7bSPaul Traina hp->hostname->string, MSG); \ 123944099b7bSPaul Traina return; \ 124044099b7bSPaul Traina } while (0) 124144099b7bSPaul Traina PRIVATE void 124244099b7bSPaul Traina dovend_rfc1048(bp, hp, bootsize) 124344099b7bSPaul Traina struct bootp *bp; 124444099b7bSPaul Traina struct host *hp; 124544099b7bSPaul Traina int32 bootsize; 124644099b7bSPaul Traina { 124744099b7bSPaul Traina int bytesleft, len; 124844099b7bSPaul Traina byte *vp; 124944099b7bSPaul Traina 1250be9efd56SKris Kennaway static const char noroom[] = "%s: No room for \"%s\" option"; 125144099b7bSPaul Traina 125244099b7bSPaul Traina vp = bp->bp_vend; 125344099b7bSPaul Traina 125444099b7bSPaul Traina if (hp->flags.msg_size) { 125544099b7bSPaul Traina pktlen = hp->msg_size; 125644099b7bSPaul Traina } else { 125744099b7bSPaul Traina /* 125844099b7bSPaul Traina * If the request was longer than the official length, build 125944099b7bSPaul Traina * a response of that same length where the additional length 126044099b7bSPaul Traina * is assumed to be part of the bp_vend (options) area. 126144099b7bSPaul Traina */ 126244099b7bSPaul Traina if (pktlen > sizeof(*bp)) { 126344099b7bSPaul Traina if (debug > 1) 126444099b7bSPaul Traina report(LOG_INFO, "request message length=%d", pktlen); 126544099b7bSPaul Traina } 126644099b7bSPaul Traina /* 126744099b7bSPaul Traina * Check whether the request contains the option: 126844099b7bSPaul Traina * Maximum DHCP Message Size (RFC1533 sec. 9.8) 126944099b7bSPaul Traina * and if so, override the response length with its value. 127044099b7bSPaul Traina * This request must lie within the first BP_VEND_LEN 127144099b7bSPaul Traina * bytes of the option space. 127244099b7bSPaul Traina */ 127344099b7bSPaul Traina { 127444099b7bSPaul Traina byte *p, *ep; 127544099b7bSPaul Traina byte tag, len; 127644099b7bSPaul Traina short msgsz = 0; 127744099b7bSPaul Traina 127844099b7bSPaul Traina p = vp + 4; 127944099b7bSPaul Traina ep = p + BP_VEND_LEN - 4; 128044099b7bSPaul Traina while (p < ep) { 128144099b7bSPaul Traina tag = *p++; 128244099b7bSPaul Traina /* Check for tags with no data first. */ 128344099b7bSPaul Traina if (tag == TAG_PAD) 128444099b7bSPaul Traina continue; 128544099b7bSPaul Traina if (tag == TAG_END) 128644099b7bSPaul Traina break; 128744099b7bSPaul Traina /* Now scan the length byte. */ 128844099b7bSPaul Traina len = *p++; 128944099b7bSPaul Traina switch (tag) { 129044099b7bSPaul Traina case TAG_MAX_MSGSZ: 129144099b7bSPaul Traina if (len == 2) { 129244099b7bSPaul Traina bcopy(p, (char*)&msgsz, 2); 129344099b7bSPaul Traina msgsz = ntohs(msgsz); 129444099b7bSPaul Traina } 129544099b7bSPaul Traina break; 129644099b7bSPaul Traina case TAG_SUBNET_MASK: 129744099b7bSPaul Traina /* XXX - Should preserve this if given... */ 129844099b7bSPaul Traina break; 129944099b7bSPaul Traina } /* swtich */ 130044099b7bSPaul Traina p += len; 130144099b7bSPaul Traina } 130244099b7bSPaul Traina 13033f48bf18SIan Dowse if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) { 130444099b7bSPaul Traina if (debug > 1) 130544099b7bSPaul Traina report(LOG_INFO, "request has DHCP msglen=%d", msgsz); 13063f48bf18SIan Dowse pktlen = msgsz - BP_MSG_OVERHEAD; 130744099b7bSPaul Traina } 130844099b7bSPaul Traina } 130944099b7bSPaul Traina } 131044099b7bSPaul Traina 131144099b7bSPaul Traina if (pktlen < sizeof(*bp)) { 131244099b7bSPaul Traina report(LOG_ERR, "invalid response length=%d", pktlen); 131344099b7bSPaul Traina pktlen = sizeof(*bp); 131444099b7bSPaul Traina } 131544099b7bSPaul Traina bytesleft = ((byte*)bp + pktlen) - vp; 131644099b7bSPaul Traina if (pktlen > sizeof(*bp)) { 131744099b7bSPaul Traina if (debug > 1) 131844099b7bSPaul Traina report(LOG_INFO, "extended reply, length=%d, options=%d", 131944099b7bSPaul Traina pktlen, bytesleft); 132044099b7bSPaul Traina } 132144099b7bSPaul Traina 132244099b7bSPaul Traina /* Copy in the magic cookie */ 132344099b7bSPaul Traina bcopy(vm_rfc1048, vp, 4); 132444099b7bSPaul Traina vp += 4; 132544099b7bSPaul Traina bytesleft -= 4; 132644099b7bSPaul Traina 132744099b7bSPaul Traina if (hp->flags.subnet_mask) { 132844099b7bSPaul Traina /* always enough room here. */ 132944099b7bSPaul Traina *vp++ = TAG_SUBNET_MASK;/* -1 byte */ 133044099b7bSPaul Traina *vp++ = 4; /* -1 byte */ 133144099b7bSPaul Traina insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ 133244099b7bSPaul Traina bytesleft -= 6; /* Fix real count */ 133344099b7bSPaul Traina if (hp->flags.gateway) { 133444099b7bSPaul Traina (void) insert_ip(TAG_GATEWAY, 133544099b7bSPaul Traina hp->gateway, 133644099b7bSPaul Traina &vp, &bytesleft); 133744099b7bSPaul Traina } 133844099b7bSPaul Traina } 133944099b7bSPaul Traina if (hp->flags.bootsize) { 134044099b7bSPaul Traina /* always enough room here */ 134144099b7bSPaul Traina bootsize = (hp->flags.bootsize_auto) ? 134244099b7bSPaul Traina ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ 134344099b7bSPaul Traina *vp++ = TAG_BOOT_SIZE; 134444099b7bSPaul Traina *vp++ = 2; 134544099b7bSPaul Traina *vp++ = (byte) ((bootsize >> 8) & 0xFF); 134644099b7bSPaul Traina *vp++ = (byte) (bootsize & 0xFF); 134744099b7bSPaul Traina bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ 134844099b7bSPaul Traina } 134944099b7bSPaul Traina /* 135044099b7bSPaul Traina * This one is special: Remaining options go in the ext file. 135144099b7bSPaul Traina * Only the subnet_mask, bootsize, and gateway should precede. 135244099b7bSPaul Traina */ 135344099b7bSPaul Traina if (hp->flags.exten_file) { 135444099b7bSPaul Traina /* 135544099b7bSPaul Traina * Check for room for exten_file. Add 3 to account for 135644099b7bSPaul Traina * TAG_EXTEN_FILE, length, and TAG_END. 135744099b7bSPaul Traina */ 135844099b7bSPaul Traina len = strlen(hp->exten_file->string); 135944099b7bSPaul Traina NEED((len + 3), "ef"); 136044099b7bSPaul Traina *vp++ = TAG_EXTEN_FILE; 136144099b7bSPaul Traina *vp++ = (byte) (len & 0xFF); 136244099b7bSPaul Traina bcopy(hp->exten_file->string, vp, len); 136344099b7bSPaul Traina vp += len; 136444099b7bSPaul Traina *vp++ = TAG_END; 136544099b7bSPaul Traina bytesleft -= len + 3; 136644099b7bSPaul Traina return; /* no more options here. */ 136744099b7bSPaul Traina } 136844099b7bSPaul Traina /* 136944099b7bSPaul Traina * The remaining options are inserted by the following 137044099b7bSPaul Traina * function (which is shared with bootpef.c). 137144099b7bSPaul Traina * Keep back one byte for the TAG_END. 137244099b7bSPaul Traina */ 137344099b7bSPaul Traina len = dovend_rfc1497(hp, vp, bytesleft - 1); 137444099b7bSPaul Traina vp += len; 137544099b7bSPaul Traina bytesleft -= len; 137644099b7bSPaul Traina 137744099b7bSPaul Traina /* There should be at least one byte left. */ 137844099b7bSPaul Traina NEED(1, "(end)"); 137944099b7bSPaul Traina *vp++ = TAG_END; 138044099b7bSPaul Traina bytesleft--; 138144099b7bSPaul Traina 138244099b7bSPaul Traina /* Log message done by caller. */ 138344099b7bSPaul Traina if (bytesleft > 0) { 138444099b7bSPaul Traina /* 138544099b7bSPaul Traina * Zero out any remaining part of the vendor area. 138644099b7bSPaul Traina */ 138744099b7bSPaul Traina bzero(vp, bytesleft); 138844099b7bSPaul Traina } 138944099b7bSPaul Traina } /* dovend_rfc1048 */ 139044099b7bSPaul Traina #undef NEED 139144099b7bSPaul Traina 139244099b7bSPaul Traina 139344099b7bSPaul Traina /* 139444099b7bSPaul Traina * Now in readfile.c: 139544099b7bSPaul Traina * hwlookcmp() 139644099b7bSPaul Traina * iplookcmp() 139744099b7bSPaul Traina */ 139844099b7bSPaul Traina 139944099b7bSPaul Traina /* haddrtoa() - now in hwaddr.c */ 140044099b7bSPaul Traina /* 140144099b7bSPaul Traina * Now in dovend.c: 140244099b7bSPaul Traina * insert_ip() 140344099b7bSPaul Traina * insert_generic() 140444099b7bSPaul Traina * insert_u_long() 140544099b7bSPaul Traina */ 140644099b7bSPaul Traina 140744099b7bSPaul Traina /* get_errmsg() - now in report.c */ 140844099b7bSPaul Traina 140944099b7bSPaul Traina /* 141044099b7bSPaul Traina * Local Variables: 141144099b7bSPaul Traina * tab-width: 4 141244099b7bSPaul Traina * c-indent-level: 4 141344099b7bSPaul Traina * c-argdecl-indent: 4 141444099b7bSPaul Traina * c-continued-statement-offset: 4 141544099b7bSPaul Traina * c-continued-brace-offset: -4 141644099b7bSPaul Traina * c-label-offset: -4 141744099b7bSPaul Traina * c-brace-offset: 0 141844099b7bSPaul Traina * End: 141944099b7bSPaul Traina */ 1420