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