home *** CD-ROM | disk | FTP | other *** search
- #ifndef _BLURB_
- #define _BLURB_
- /************************************************************************
- Copyright 1988, 1991 by Carnegie Mellon University
-
- All Rights Reserved
-
- Permission to use, copy, modify, and distribute this software and its
- documentation for any purpose and without fee is hereby granted, provided
- that the above copyright notice appear in all copies and that both that
- copyright notice and this permission notice appear in supporting
- documentation, and that the name of Carnegie Mellon University not be used
- in advertising or publicity pertaining to distribution of the software
- without specific, written prior permission.
-
- CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
- SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
- IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
- DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- SOFTWARE.
- ************************************************************************/
- #endif /* _BLURB_ */
-
-
- #ifndef lint
- static char sccsid[] = "@(#)bootp.c 1.1 (Stanford) 1/22/86";
- static char rcsid[] = "$Header: /afs/andrew.cmu.edu/netdev/src/cmu/bootp-public/RCS/bootpd.c,v 1.3 1991/11/01 10:02:29 ww0n Exp ww0n $";
- #endif
-
-
- /*
- * BOOTP (bootstrap protocol) server daemon.
- *
- * Answers BOOTP request packets from booting client machines.
- * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
- * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
- * See accompanying man page -- bootpd.8
- *
- *
- * HISTORY
- *
- * 01/22/86 Bill Croft at Stanford University
- * Created.
- *
- * 07/30/86 David Kovar at Carnegie Mellon University
- * Modified to work at CMU.
- *
- * 07/24/87 Drew D. Perkins at Carnegie Mellon University
- * Modified to use syslog instead of Kovar's
- * routines. Add debugging dumps. Many other fixups.
- *
- * 07/15/88 Walter L. Wimer at Carnegie Mellon University
- * Added vendor information to conform to RFC1048.
- * Adopted termcap-like file format to support above.
- * Added hash table lookup instead of linear search.
- * Other cleanups.
- *
- *
- * BUGS
- *
- * Currently mallocs memory in a very haphazard manner. As such, most of
- * the program ends up core-resident all the time just to follow all the
- * stupid pointers around. . . .
- *
- */
-
-
-
-
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/ioctl.h>
- #include <sys/file.h>
- #include <sys/time.h>
- #include <sys/stat.h>
- #include <net/if.h>
- #if defined(SUNOS4) || defined(SVR4)
- #include <sys/sockio.h>
- #include <net/if_arp.h>
- #endif
- #include <netinet/in.h>
- #include <stdlib.h>
- #include <signal.h>
- #include <stdio.h>
- #ifdef SVR4
- #include <sys/termios.h> /* Why? */
- #include <string.h>
- #include <sys/fcntl.h> /* Why? */
- #else
- #include <strings.h>
- #endif
- #include <errno.h>
- #include <ctype.h>
- #include <netdb.h>
- #ifdef SYSLOG
- #include <syslog.h>
- #endif
- #include "bootp.h"
- #include "hash.h"
- #include "bootpd.h"
-
- #define HASHTABLESIZE 257 /* Hash table size (prime) */
- #define DEFAULT_TIMEOUT 15L /* Default timeout in minutes */
- #define MAXPKT (3*512) /* Maximum packet size */
-
- #ifdef DEBUG
- #define CONFIG_FILE "bootptab.debug"
- #endif /* XXX */
-
- #ifndef CONFIG_FILE
- #define CONFIG_FILE "/etc/bootptab"
- #endif
- #ifndef DUMP_FILE
- #define DUMP_FILE "/etc/bootpd.dump"
- #endif
-
- #ifdef SVR4
- #define bcopy(a,b,c) memcpy(b,a,c)
- #define bzero(p,l) memset(p,0,l)
- #define bcmp(a,b,c) memcmp(a,b,c)
- #define index(a,b) strchr(a,b)
- #endif
-
-
-
- /*
- * Externals, forward declarations, and global variables
- */
-
- extern char Version[];
- extern char *sys_errlist[];
- extern int errno, sys_nerr;
-
- void usage();
- void dump_host();
- void list_ipaddresses();
- #ifdef VEND_CMU
- void dovend_cmu();
- #endif
- void dovend_rfc1048();
- boolean hwlookcmp();
- boolean iplookcmp();
- void insert_generic();
- void insert_ip();
- void dumptab();
- int chk_access();
- void report();
- char *get_errmsg();
- void answer_request();
- void forward_reply();
-
- /*
- * IP port numbers for client and server obtained from /etc/services
- */
-
- u_short bootps_port, bootpc_port;
-
-
- /*
- * Internet socket and interface config structures
- */
-
- struct sockaddr server_addr;
- struct sockaddr client_addr;
- struct ifreq ifreq[10]; /* Holds interface configuration */
- struct ifconf ifconf; /* Int. config ioctl block (pnts to ifreq) */
- struct arpreq arpreq; /* Arp request ioctl block */
-
-
- /*
- * General
- */
-
- int debug = 0; /* Debugging flag (level) */
- int s; /* Socket file descriptor */
- char *pktbuf;
- int pktlen;
- struct timezone tzp; /* Time zone offset for clients */
- struct timeval tp; /* Time (extra baggage) */
- long secondswest; /* Time zone offset in seconds */
-
- /*
- * Globals below are associated with the bootp database file (bootptab).
- */
-
- char *bootptab = NULL;
- #ifdef DEBUG
- char *bootpd_dump = NULL;
- #endif
-
-
-
- /*
- * Vendor magic cookies for CMU and RFC1048
- */
-
- unsigned char vm_cmu[4] = VM_CMU;
- unsigned char vm_rfc1048[4] = VM_RFC1048;
- unsigned char vm_zero[4] = { 0, 0, 0, 0 };
-
-
- /*
- * Hardware address lengths (in bytes) and network name based on hardware
- * type code. List in order specified by Assigned Numbers RFC; Array index
- * is hardware type code. Entries marked as zero are unknown to the author
- * at this time. . . .
- */
-
- struct hwinfo hwinfolist[] = {
- { 0, "Reserved" }, /* Type 0: Reserved (don't use this) */
- { 6, "Ethernet" }, /* Type 1: 10Mb Ethernet (48 bits) */
- { 1, "3Mb Ethernet" }, /* Type 2: 3Mb Ethernet (8 bits) */
- { 0, "AX.25" }, /* Type 3: Amateur Radio AX.25 */
- { 1, "ProNET" }, /* Type 4: Proteon ProNET Token Ring */
- { 0, "Chaos" }, /* Type 5: Chaos */
- { 6, "IEEE 802" }, /* Type 6: IEEE 802 Networks */
- { 0, "ARCNET" } /* Type 7: ARCNET */
- };
- int hwinfocnt = sizeof(hwinfolist) / sizeof(hwinfolist[0]);
-
- /*
- * Main hash tables
- */
-
- hash_tbl *hwhashtable;
- hash_tbl *iphashtable;
- hash_tbl *nmhashtable;
-
-
-
-
- /*
- * Initialization such as command-line processing is done and then the main
- * server loop is started.
- */
-
- main(argc, argv)
- int argc;
- char **argv;
- {
- struct timeval actualtimeout, *timeout;
- struct bootp *bp;
- struct servent *servp;
- struct sockaddr_in *s_sin;
- char *stmp;
- int n, sa_len, ca_len;
- int nfound, readfds;
- int standalone;
-
- stmp = NULL;
- actualtimeout.tv_usec = 0L;
- actualtimeout.tv_sec = 60 * DEFAULT_TIMEOUT;
- timeout = &actualtimeout;
- s_sin = (struct sockaddr_in *) &server_addr;
-
- /* Get space for receiving packets and composing replies. */
- pktbuf = malloc(MAXPKT);
- bp = (struct bootp *) pktbuf;
-
- /*
- * Check to see if a socket was passed to us from inetd.
- *
- * Use getsockname() to determine if descriptor 0 is indeed a socket
- * (and thus we are probably a child of inetd) or if it is instead
- * something else and we are running standalone.
- */
- s = 0;
- sa_len = sizeof(server_addr);
- bzero((char *) &server_addr, sa_len);
- errno = 0;
- standalone = TRUE;
- if (getsockname(s, &server_addr, &sa_len) == 0) {
- /*
- * Descriptor 0 is a socket. Assume we are a child of inetd.
- */
- if (s_sin->sin_family == AF_INET) {
- standalone = FALSE;
- bootps_port = ntohs(s_sin->sin_port);
- } else {
- report(LOG_INFO, "getsockname: not an INET socket\n");
- }
- }
-
- /*
- * Read switches.
- */
- for (argc--, argv++; argc > 0; argc--, argv++) {
- if (argv[0][0] == '-') {
- switch (argv[0][1]) {
- case 't':
- if (argv[0][2]) {
- stmp = &(argv[0][2]);
- } else {
- argc--;
- argv++;
- stmp = argv[0];
- }
- if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
- fprintf(stderr,
- "bootpd: invalid timeout specification\n");
- break;
- }
- actualtimeout.tv_sec = (long) (60 * n);
- /*
- * If the actual timeout is zero, pass a NULL pointer
- * to select so it blocks indefinitely, otherwise,
- * point to the actual timeout value.
- */
- timeout = (n > 0) ? &actualtimeout : NULL;
- break;
- case 'd':
- if (argv[0][2]) {
- stmp = &(argv[0][2]);
- } else if (argv[1][0] == '-') {
- /*
- * Backwards-compatible behavior:
- * no parameter, so just increment the debug flag.
- */
- debug++;
- break;
- } else {
- argc--;
- argv++;
- stmp = argv[0];
- }
- if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
- fprintf(stderr,
- "bootpd: invalid debug level\n");
- break;
- }
- debug = n;
- break;
- case 's':
- standalone = TRUE;
- break;
- case 'i':
- standalone = FALSE;
- break;
- default:
- fprintf(stderr, "bootpd: unknown switch: -%c\n",
- argv[0][1]);
- usage();
- break;
- }
- } else {
- if (!bootptab) {
- bootptab = argv[0];
- #ifdef DEBUG
- } else if (!bootpd_dump) {
- bootpd_dump = argv[0];
- #endif
- } else {
- fprintf(stderr, "bootpd: unknown argument: %s\n", argv[0]);
- usage();
- }
- }
- }
-
-
- /*
- * Set default file names if not specified on command line
- */
- if (!bootptab) {
- bootptab = CONFIG_FILE;
- }
- #ifdef DEBUG
- if (!bootpd_dump) {
- bootpd_dump = DUMP_FILE;
- }
- #endif
-
-
- if (standalone) {
- /*
- * Go into background and disassociate from controlling terminal.
- * XXX - This is not the POSIX way... -gwr
- */
- if (debug < 3) {
- if (fork())
- exit(0);
- for (n = 0; n < 10; n++)
- (void) close(n);
- (void) open("/", O_RDONLY);
- (void) dup2(0, 1);
- (void) dup2(0, 2);
- n = open("/dev/tty", O_RDWR);
- if (n >= 0) {
- ioctl(n, TIOCNOTTY, (char *) 0);
- (void) close(n);
- }
- }
- /*
- * Nuke any timeout value
- */
- timeout = NULL;
- }
-
-
- #ifdef SYSLOG
- /*
- * Initialize logging.
- */
- #ifndef LOG_CONS
- #define LOG_CONS 0 /* Don't bother if not defined... */
- #endif
- #ifndef LOG_DAEMON
- #define LOG_DAEMON 0
- #endif
- openlog("bootpd", LOG_PID | LOG_CONS, LOG_DAEMON);
- #endif
-
- /*
- * Log startup
- */
- report(LOG_INFO, "%s", Version);
-
- /*
- * Get our timezone offset so we can give it to clients if the
- * configuration file doesn't specify one.
- */
- if (gettimeofday(&tp, &tzp) < 0) {
- secondswest = 0L; /* Assume GMT for lack of anything better */
- report(LOG_ERR, "gettimeofday: %s\n", get_errmsg());
- } else {
- secondswest = 60L * tzp.tz_minuteswest; /* Convert to seconds */
- }
-
- /*
- * Allocate hash tables for hardware address, ip address, and hostname
- */
- hwhashtable = hash_Init(HASHTABLESIZE);
- iphashtable = hash_Init(HASHTABLESIZE);
- nmhashtable = hash_Init(HASHTABLESIZE);
- if (!(hwhashtable && iphashtable && nmhashtable)) {
- report(LOG_ERR, "Unable to allocate hash tables.\n");
- exit(1);
- }
-
-
- /*
- * Read the bootptab file once immediately upon startup.
- */
- readtab();
-
-
- if (standalone) {
- /*
- * Create a socket.
- */
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
- report(LOG_ERR, "socket: %s\n", get_network_errmsg());
- exit(1);
- }
-
- /*
- * Get server's listening port number
- */
- servp = getservbyname("bootps", "udp");
- if (servp) {
- bootps_port = ntohs((u_short) servp->s_port);
- } else {
- report(LOG_ERR,
- "udp/bootps: unknown service -- assuming port %d\n",
- IPPORT_BOOTPS);
- bootps_port = (u_short) IPPORT_BOOTPS;
- }
-
- /*
- * Bind socket to BOOTPS port.
- */
- s_sin->sin_family = AF_INET;
- s_sin->sin_addr.s_addr = INADDR_ANY;
- s_sin->sin_port = htons(bootps_port);
- if (bind(s, &server_addr, sizeof(*s_sin)) < 0) {
- report(LOG_ERR, "bind: %s\n", get_network_errmsg());
- exit(1);
- }
- }
-
-
- /*
- * Get destination port number so we can reply to client
- */
- servp = getservbyname("bootpc", "udp");
- if (servp) {
- bootpc_port = ntohs(servp->s_port);
- } else {
- report(LOG_ERR,
- "udp/bootpc: unknown service -- assuming port %d\n",
- IPPORT_BOOTPC);
- bootpc_port = (u_short) IPPORT_BOOTPC;
- }
-
-
- /*
- * Determine network configuration.
- */
- ifconf.ifc_len = sizeof(ifreq);
- ifconf.ifc_req = ifreq;
- if ((ioctl(s, SIOCGIFCONF, (caddr_t) &ifconf) < 0) ||
- (ifconf.ifc_len <= 0)) {
- report(LOG_ERR, "ioctl: %s\n", get_network_errmsg());
- exit(1);
- }
-
-
- /*
- * Set up signals to read or dump the table.
- */
- if ((int) signal(SIGHUP, readtab) < 0) {
- report(LOG_ERR, "signal: %s\n", get_errmsg());
- exit(1);
- }
- #ifdef DEBUG
- if ((int) signal(SIGUSR1, dumptab) < 0) {
- report(LOG_ERR, "signal: %s\n", get_errmsg());
- exit(1);
- }
- #endif
-
- /*
- * Process incoming requests.
- */
- for (;;) {
- readfds = 1 << s;
- nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, timeout);
- if (nfound < 0) {
- if (errno != EINTR) {
- report(LOG_ERR, "select: %s\n", get_errmsg());
- }
- continue;
- }
- if (!(readfds & (1 << s))) {
- report(LOG_INFO, "exiting after %ld minutes of inactivity\n",
- actualtimeout.tv_sec / 60);
- exit(0);
- }
- ca_len = sizeof(client_addr);
- n = recvfrom(s, pktbuf, MAXPKT, 0, &client_addr, &ca_len);
- if (n <= 0) {
- continue;
- }
-
- if (n < sizeof(struct bootp)) {
- if (debug) {
- report(LOG_INFO, "received short packet\n");
- }
- continue;
- }
- pktlen = n;
-
- readtab(); /* maybe re-read bootptab */
- switch (bp->bp_op) {
- case BOOTREQUEST:
- /* Handle a request by answering with a reply. */
- answer_request();
- break;
-
- case BOOTREPLY:
- /* Handle a reply by forwarding it to the client. */
- forward_reply();
- break;
- }
- }
- }
-
-
-
-
- /*
- * Print "usage" message and exit
- */
-
- void usage()
- {
- fprintf(stderr,
- "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
- fprintf(stderr, "\t -d n\tset debug level\n");
- fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
- fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
- fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
- exit(1);
- }
-
-
-
- /*
- * Process BOOTREQUEST packet.
- *
- * (Note, this version of the bootpd.c server never forwards
- * the request to another server. In our environment the
- * stand-alone gateways perform that function.)
- *
- * (Also this version does not interpret the hostname field of
- * the request packet; it COULD do a name->address lookup and
- * forward the request there.)
- */
- void
- answer_request()
- {
- register struct bootp *bp = (struct bootp *) pktbuf;
- register struct host *hp;
- register int n;
- char *path;
- struct host dummyhost;
- long bootsize;
- unsigned hlen, hashcode;
- char realpath[1024];
-
- bp->bp_op = BOOTREPLY;
- if (bp->bp_ciaddr.s_addr == 0) {
- /*
- * client doesnt know his IP address,
- * search by hardware address.
- */
- if (debug) {
- report(LOG_INFO, "request from %s address %s\n",
- netname(bp->bp_htype),
- haddrtoa(bp->bp_chaddr, bp->bp_htype));
- }
-
- dummyhost.htype = bp->bp_htype;
- hlen = haddrlength(bp->bp_htype);
- bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
- hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
- hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
- &dummyhost);
- if (hp == NULL) {
- /* XXX - Do we need more control over this noise? -gwr */
- if (debug)
- report(LOG_INFO, "unknown client %s address %s\n",
- netname(bp->bp_htype),
- haddrtoa(bp->bp_chaddr, bp->bp_htype));
- return; /* not found */
- }
- (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
-
- } else {
-
- /*
- * search by IP address.
- */
- if (debug) {
- report(LOG_INFO, "request from IP addr %s\n",
- inet_ntoa(bp->bp_ciaddr));
- }
- dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
- hashcode = hash_HashFunction((char *) &(bp->bp_ciaddr.s_addr), 4);
- hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
- &dummyhost);
- if (hp == NULL) {
- report(LOG_NOTICE,
- "IP address not found: %s\n", inet_ntoa(bp->bp_ciaddr));
- return;
- }
- }
-
- if (debug) {
- report(LOG_INFO, "found %s %s\n", inet_ntoa(hp->iaddr),
- hp->hostname->string);
- }
-
- /*
- * If a specific TFTP server address was specified in the bootptab file,
- * fill it in, otherwise zero it.
- */
- (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
- hp->bootserver.s_addr : 0L;
-
- /*
- * This next line is a bit of a mystery. It seems to be vestigial
- * code (from Stanford???) which should probably be axed.
- */
- if (strcmp(bp->bp_file, "sunboot14") == 0)
- bp->bp_file[0] = 0; /* pretend it's null */
-
-
- /*
- * Fill in the client's proper bootfile.
- *
- * If the client specifies an absolute path, try that file with a
- * ".host" suffix and then without. If the file cannot be found, no
- * reply is made at all.
- *
- * [ I think this policy is too complicated. Why not just send the
- * reply if we know how, whether or not the file exists? -gwr ]
- *
- * If the client specifies a null or relative file, use the following
- * table to determine the appropriate action:
- *
- * Homedir Bootfile Client's file
- * specified? specified? specification Action
- * -------------------------------------------------------------------
- * No No Null Send null filename
- * No No Relative Discard request
- * No Yes Null Send if absolute else null
- * No Yes Relative Discard request
- * Yes No Null Send null filename
- * Yes No Relative Lookup with ".host"
- * Yes Yes Null Send home/boot or bootfile
- * Yes Yes Relative Lookup with ".host"
- *
- */
-
- if (hp->flags.tftpdir) {
- strcpy(realpath, hp->tftpdir->string);
- path = &realpath[strlen(realpath)];
- } else {
- path = realpath;
- }
-
- if (bp->bp_file[0]) {
- /*
- * The client specified a file.
- */
- if (bp->bp_file[0] == '/') {
- strcpy(path, bp->bp_file); /* Absolute pathname */
- } else {
- if (hp->flags.homedir) {
- strcpy(path, hp->homedir->string);
- strcat(path, "/");
- strcat(path, bp->bp_file);
- } else {
- report(LOG_NOTICE,
- "requested file \"%s\" not found: hd unspecified\n",
- bp->bp_file);
- return;
- }
- }
- } else {
- /*
- * No file specified by the client.
- */
- if (hp->flags.bootfile && ((hp->bootfile->string)[0] == '/')) {
- strcpy(path, hp->bootfile->string);
- } else if (hp->flags.homedir && hp->flags.bootfile) {
- strcpy(path, hp->homedir->string);
- strcat(path, "/");
- strcat(path, hp->bootfile->string);
- } else {
- bzero(bp->bp_file, sizeof(bp->bp_file));
- goto skip_file; /* Don't bother trying to access the file */
- }
- }
-
- /*
- * First try to find the file with a ".host" suffix
- */
- n = strlen(path);
- strcat(path, ".");
- strcat(path, hp->hostname->string);
- if (chk_access(realpath, &bootsize) < 0) {
- path[n] = 0; /* Try it without the suffix */
- if (chk_access(realpath, &bootsize) < 0) {
- if (bp->bp_file[0]) {
- /*
- * Client wanted specific file
- * and we didn't have it.
- */
- report(LOG_NOTICE,
- "requested file not found: \"%s\"\n", path);
- return;
- } else {
- /*
- * Client didn't ask for a specific file and we couldn't
- * access the default file, so just zero-out the bootfile
- * field in the packet and continue processing the reply.
- */
- bzero(bp->bp_file, sizeof(bp->bp_file));
- goto skip_file;
- }
- }
- }
- strcpy(bp->bp_file, path);
-
- skip_file: ;
-
-
-
- if (debug > 1) {
- report(LOG_INFO, "vendor magic field is %d.%d.%d.%d\n",
- (int) ((bp->bp_vend)[0]),
- (int) ((bp->bp_vend)[1]),
- (int) ((bp->bp_vend)[2]),
- (int) ((bp->bp_vend)[3]));
- }
-
- /*
- * If this host isn't set for automatic vendor info then copy the
- * specific cookie into the bootp packet, thus forcing a certain
- * reply format.
- */
- if (!hp->flags.vm_auto) {
- /*
- * If the client supplies a vendor magic number,
- * bootp probably should not change it...
- */
- if (debug > 1 && bcmp(bp->bp_vend, vm_zero, 4)) {
- report(LOG_INFO, "vendor magic field forced\n");
- }
- bcopy(hp->vm_cookie, bp->bp_vend, 4);
- }
-
- /*
- * Figure out the format for the vendor-specific info.
- */
- if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
- /* RFC1048 conformant bootp client */
- dovend_rfc1048(bp, hp, bootsize);
- if (debug > 1) {
- report(LOG_INFO, "sending reply (with RFC1048 options)\n");
- }
- }
- #ifdef VEND_CMU
- else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
- dovend_cmu(bp, hp);
- if (debug > 1) {
- report(LOG_INFO, "sending reply (with CMU options)\n");
- }
- }
- #endif
- else if (!bcmp(bp->bp_vend, vm_zero, 4)) {
- if (debug > 1) {
- report(LOG_INFO, "sending reply (no options)\n");
- }
- }
- else {
- if (debug > 1) {
- report(LOG_INFO, "sending reply (unknown options)\n");
- }
- }
-
- sendreply(0);
- }
-
-
-
- /*
- * Process BOOTREPLY packet (something is using us as a gateway).
- */
-
- void
- forward_reply()
- {
- if (debug) {
- report(LOG_INFO, "processing boot reply\n");
- }
- sendreply(1);
- }
-
-
-
- /*
- * Send a reply packet to the client. 'forward' flag is set if we are
- * not the originator of this reply packet.
- */
- sendreply(forward)
- int forward;
- {
- register struct bootp *bp = (struct bootp *) pktbuf;
- struct in_addr dst; /* XXX */
- struct sockaddr_in *c_sin;
-
- c_sin = (struct sockaddr_in *) &client_addr;
-
- c_sin->sin_port = htons(bootpc_port);
- /*
- * If the client IP address is specified, use that
- * else if gateway IP address is specified, use that
- * else make a temporary arp cache entry for the client's NEW
- * IP/hardware address and use that.
- */
- if (bp->bp_ciaddr.s_addr) {
- dst = bp->bp_ciaddr;
- } else if (bp->bp_giaddr.s_addr && forward == 0) {
- dst = bp->bp_giaddr;
- c_sin->sin_port = htons(bootps_port);
- } else {
- dst = bp->bp_yiaddr;
- setarp(&dst, bp->bp_chaddr, bp->bp_hlen);
- }
-
- if (forward == 0) {
- /*
- * If we are originating this reply, we
- * need to find our own interface address to
- * put in the bp_siaddr field of the reply.
- * If this server is multi-homed, pick the
- * 'best' interface (the one on the same net
- * as the client).
- */
- int maxmatch = 0;
- int len, m;
- register struct ifreq *ifrq, *ifrmax;
-
- ifrmax = ifrq = &ifreq[0];
- len = ifconf.ifc_len;
- for (; len > 0; len -= sizeof(ifreq[0]), ifrq++) {
- m = nmatch(&dst, &((struct sockaddr_in *)
- (&ifrq->ifr_addr))->sin_addr);
- if (m > maxmatch) {
- maxmatch = m;
- ifrmax = ifrq;
- }
- }
-
- /* XXX - Should put anything here? :gw=: ? */
- if (bp->bp_giaddr.s_addr == 0) {
- if (maxmatch == 0) {
- return;
- }
- bp->bp_giaddr = ((struct sockaddr_in *)
- (&ifrmax->ifr_addr))->sin_addr;
- }
-
- /*
- * If a specific TFTP server address wasn't specified
- * in the bootptab file, fill in our own address.
- */
- if (bp->bp_siaddr.s_addr == 0) {
- bp->bp_siaddr = ((struct sockaddr_in *)
- (&ifrmax->ifr_addr))->sin_addr;
- }
- }
-
- c_sin->sin_addr = dst;
- /* Send reply with same size packet as request used. */
- if (sendto(s, pktbuf, pktlen, 0,
- &client_addr, sizeof(*c_sin)) < 0) {
- report(LOG_ERR, "sendto: %s\n", get_network_errmsg());
- }
- } /* sendreply */
-
-
-
- /*
- * Return the number of leading bytes matching in the
- * internet addresses supplied.
- */
- nmatch(ca,cb)
- register char *ca, *cb;
- {
- register n,m;
-
- for (m = n = 0 ; n < 4 ; n++) {
- if (*ca++ != *cb++)
- return(m);
- m++;
- }
- return(m);
- }
-
-
-
- /*
- * Setup the arp cache so that IP address 'ia' will be temporarily
- * bound to hardware address 'ha' of length 'len'.
- */
- setarp(ia, ha, len)
- struct in_addr *ia;
- byte *ha;
- int len;
- {
- struct sockaddr_in *si;
-
- bzero((caddr_t)&arpreq, sizeof(arpreq));
-
- arpreq.arp_pa.sa_family = AF_INET;
- si = (struct sockaddr_in *) &arpreq.arp_pa;
- si->sin_addr = *ia;
-
- arpreq.arp_flags = ATF_INUSE | ATF_COM;
-
- bcopy(ha, arpreq.arp_ha.sa_data, len);
-
- if (ioctl(s, SIOCSARP, (caddr_t)&arpreq) < 0) {
- report(LOG_ERR, "ioctl(SIOCSARP): %s\n", get_network_errmsg());
- }
- }
-
-
-
- /*
- * This call checks read access to a file. It returns 0 if the file given
- * by "path" exists and is publically readable. A value of -1 is returned if
- * access is not permitted or an error occurs. Successful calls also
- * return the file size in bytes using the long pointer "filesize".
- *
- * The read permission bit for "other" users is checked. This bit must be
- * set for tftpd(8) to allow clients to read the file.
- */
-
- int chk_access(path, filesize)
- char *path;
- long *filesize;
- {
- struct stat st;
-
- if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
- *filesize = (long) st.st_size;
- return 0;
- } else {
- return -1;
- }
- }
-
-
-
- #ifdef DEBUG
-
- /*
- * Dump the internal memory database to bootpd_dump.
- */
-
- void dumptab()
- {
- register int n;
- register struct host *hp;
- register FILE *fp;
- long t;
-
- /*
- * Open bootpd.dump file.
- */
- if ((fp = fopen(bootpd_dump, "w")) == NULL) {
- report(LOG_ERR, "error opening \"%s\": %s\n", bootpd_dump,
- get_errmsg());
- exit(1);
- }
-
- t = time(NULL);
- fprintf(fp, "\n# %s\n", Version);
- fprintf(fp, "# %s: dump of bootp server database.\n", bootpd_dump);
- fprintf(fp, "#\n# Dump taken %s", ctime(&t));
- fprintf(fp, "#\n#\n# Legend:\n");
- fprintf(fp, "#\thd -- home directory\n");
- fprintf(fp, "#\tbf -- bootfile\n");
- fprintf(fp, "#\tbs -- bootfile size in 512-octet blocks\n");
- fprintf(fp, "#\tcs -- cookie servers\n");
- fprintf(fp, "#\tds -- domain name servers\n");
- fprintf(fp, "#\tgw -- gateways\n");
- fprintf(fp, "#\tha -- hardware address\n");
- fprintf(fp, "#\thd -- home directory for bootfiles\n");
- fprintf(fp, "#\tht -- hardware type\n");
- fprintf(fp, "#\tim -- impress servers\n");
- fprintf(fp, "#\tip -- host IP address\n");
- fprintf(fp, "#\tlg -- log servers\n");
- fprintf(fp, "#\tlp -- LPR servers\n");
- fprintf(fp, "#\tns -- IEN-116 name servers\n");
- fprintf(fp, "#\trl -- resource location protocol servers\n");
- fprintf(fp, "#\tsm -- subnet mask\n");
- fprintf(fp, "#\tto -- time offset (seconds)\n");
- fprintf(fp, "#\tts -- time servers\n\n\n");
-
- n = 0;
- for (hp = (struct host *) hash_FirstEntry(nmhashtable); hp != NULL;
- hp = (struct host *) hash_NextEntry(nmhashtable)) {
- dump_host(fp, hp);
- fprintf(fp, "\n");
- n++;
- }
- fclose(fp);
-
- report(LOG_INFO, "dumped %d entries to \"%s\".\n", n, bootpd_dump);
- }
-
-
-
- /*
- * Dump all the available information on the host pointed to by "hp".
- * The output is sent to the file pointed to by "fp".
- */
-
- void dump_host(fp, hp)
- FILE *fp;
- struct host *hp;
- {
- register int i;
- register byte *dataptr;
-
- if (hp) {
- if (hp->hostname) {
- fprintf(fp, "%s:", hp->hostname->string);
- }
- if (hp->flags.bootfile) {
- fprintf(fp, "bf=%s:", hp->bootfile->string);
- }
- if (hp->flags.bootsize) {
- fprintf(fp, "bs=");
- if (hp->flags.bootsize_auto) {
- fprintf(fp, "auto:");
- } else {
- fprintf(fp, "%d:", hp->bootsize);
- }
- }
- if (hp->flags.cookie_server) {
- fprintf(fp, "cs=");
- list_ipaddresses(fp, hp->cookie_server);
- fprintf(fp, ":");
- }
- if (hp->flags.domain_server) {
- fprintf(fp, "ds=");
- list_ipaddresses(fp, hp->domain_server);
- fprintf(fp, ":");
- }
- if (hp->flags.gateway) {
- fprintf(fp, "gw=");
- list_ipaddresses(fp, hp->gateway);
- fprintf(fp, ":");
- }
- if (hp->flags.homedir) {
- fprintf(fp, "hd=%s:", hp->homedir->string);
- }
- if (hp->flags.name_switch && hp->flags.send_name) {
- fprintf(fp, "hn:");
- }
- if (hp->flags.htype) {
- fprintf(fp, "ht=%u:", (unsigned) hp->htype);
- if (hp->flags.haddr) {
- fprintf(fp, "ha=%s:", haddrtoa(hp->haddr, hp->htype));
- }
- }
- if (hp->flags.impress_server) {
- fprintf(fp, "im=");
- list_ipaddresses(fp, hp->impress_server);
- fprintf(fp, ":");
- }
- if (hp->flags.iaddr) {
- fprintf(fp, "ip=%s:", inet_ntoa(hp->iaddr));
- }
- if (hp->flags.log_server) {
- fprintf(fp, "lg=");
- list_ipaddresses(fp, hp->log_server);
- fprintf(fp, ":");
- }
- if (hp->flags.lpr_server) {
- fprintf(fp, "lp=");
- list_ipaddresses(fp, hp->lpr_server);
- fprintf(fp, ":");
- }
- if (hp->flags.name_server) {
- fprintf(fp, "ns=");
- list_ipaddresses(fp, hp->name_server);
- fprintf(fp, ":");
- }
- if (hp->flags.rlp_server) {
- fprintf(fp, "rl=");
- list_ipaddresses(fp, hp->rlp_server);
- fprintf(fp, ":");
- }
- if (hp->flags.bootserver) {
- fprintf(fp, "sa=%s:", inet_ntoa(hp->bootserver));
- }
- if (hp->flags.subnet_mask) {
- fprintf(fp, "sm=%s:", inet_ntoa(hp->subnet_mask));
- }
- if (hp->flags.tftpdir) {
- fprintf(fp, "td=%s:", hp->tftpdir->string);
- }
- if (hp->flags.time_offset) {
- if (hp->flags.timeoff_auto) {
- fprintf(fp, "to=auto:");
- } else {
- fprintf(fp, "to=%ld:", hp->time_offset);
- }
- }
- if (hp->flags.time_server) {
- fprintf(fp, "ts=");
- list_ipaddresses(fp, hp->time_server);
- fprintf(fp, ":");
- }
- if (hp->flags.vendor_magic) {
- fprintf(fp, "vm=");
- if (hp->flags.vm_auto) {
- fprintf(fp, "auto:");
- } else if (!bcmp(hp->vm_cookie, vm_cmu, 4)) {
- fprintf(fp, "cmu:");
- } else if (!bcmp(hp->vm_cookie, vm_rfc1048, 4)) {
- fprintf(fp, "rfc1048");
- } else {
- fprintf(fp, "%d.%d.%d.%d:",
- (int) ((hp->vm_cookie)[0]),
- (int) ((hp->vm_cookie)[1]),
- (int) ((hp->vm_cookie)[2]),
- (int) ((hp->vm_cookie)[3]));
- }
- }
- if (hp->flags.generic) {
- fprintf(fp, "generic=");
- dataptr = hp->generic->data;
- for (i = hp->generic->length; i > 0; i--) {
- fprintf(fp, "%02X", (int) *dataptr++);
- }
- fprintf(fp, ":");
- }
- }
- }
-
-
-
- /*
- * Dump an entire struct in_addr_list of IP addresses to the indicated file.
- *
- * The addresses are printed in standard ASCII "dot" notation and separated
- * from one another by a single space. A single leading space is also
- * printed before the first adddress.
- *
- * Null lists produce no output (and no error).
- */
-
- void list_ipaddresses(fp, ipptr)
- FILE *fp;
- struct in_addr_list *ipptr;
- {
- register unsigned count;
- register struct in_addr *addrptr;
-
- if (ipptr) {
- count = ipptr->addrcount;
- addrptr = ipptr->addr;
- if (count-- > 0) {
- fprintf(fp, "%s", inet_ntoa(*addrptr++));
- while (count-- > 0) {
- fprintf(fp, " %s", inet_ntoa(*addrptr++));
- }
- }
- }
- }
- #endif /* DEBUG */
-
-
-
- #ifdef VEND_CMU
-
- /*
- * Insert the CMU "vendor" data for the host pointed to by "hp" into the
- * bootp packet pointed to by "bp".
- */
-
- void dovend_cmu(bp, hp)
- register struct bootp *bp;
- register struct host *hp;
- {
- struct cmu_vend *vendp;
- register struct in_addr_list *taddr;
-
- /*
- * Initialize the entire vendor field to zeroes.
- */
- bzero(bp->bp_vend, sizeof(bp->bp_vend));
-
- /*
- * Fill in vendor information. Subnet mask, default gateway,
- * domain name server, ien name server, time server
- */
- vendp = (struct cmu_vend *) bp->bp_vend;
- if (hp->flags.subnet_mask) {
- (vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
- (vendp->v_flags) |= VF_SMASK;
- if (hp->flags.gateway) {
- (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
- }
- }
- if (hp->flags.domain_server) {
- taddr = hp->domain_server;
- if (taddr->addrcount > 0) {
- (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
- if (taddr->addrcount > 1) {
- (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
- }
- }
- }
- if (hp->flags.name_server) {
- taddr = hp->name_server;
- if (taddr->addrcount > 0) {
- (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
- if (taddr->addrcount > 1) {
- (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
- }
- }
- }
- if (hp->flags.time_server) {
- taddr = hp->time_server;
- if (taddr->addrcount > 0) {
- (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
- if (taddr->addrcount > 1) {
- (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
- }
- }
- }
- strcpy(vendp->v_magic, vm_cmu);
-
- } /* dovend_cmu */
-
- #endif /* VEND_CMU */
-
-
-
- /*
- * Fill in the RFC1048 options. All tag and length bytes provided
- * by the client must be left where they are. Data associated with
- * each option are updated if we have a value for the option.
- *
- * This implementation does not change any tag or length bytes.
- *
- * One could append additional options when there is room after the
- * end record but this implementation does not do so. One would need
- * a way to know which options should be "forced" into the list.
- */
- void dovend_rfc1048(bp, hp, bootsize)
- register struct bootp *bp;
- register struct host *hp;
- long bootsize; /* "auto" value, from stat() */
- {
- int i, n, len;
- byte *vp, *ep;
- byte tag;
- char *tmpstr;
- unsigned long ul;
- unsigned short us;
-
- /*
- * Compute pointers to beginning and end of option area.
- * Note that options (formerly "vendor data") are now
- * allowed to extend to the end of the packet, and are
- * not limited to a maximum of BP_VEND_LEN bytes.
- * That value is the MINIMUM size of the option area.
- * (See the IETF draft: Clarifications and Extensions
- * for the Bootstrap Protocol, W. Wimer, Sept. 1992.)
- */
- vp = bp->bp_vend;
- ep = ((char *)bp) + pktlen;
-
- /* Step over the magic cookie (checked by caller). */
- vp += 4;
-
- while (vp < ep) {
- tag = *vp++;
-
- /* Check for tags with no data first. */
- if (tag == TAG_PAD)
- continue;
- if (tag == TAG_END)
- return;
-
- /* Now scan the length byte. */
- len = *vp++;
-
- if (vp + len >= ep) {
- report(LOG_ERR, "truncated field in vendor data\n");
- /* do the best we can... */
- len = ep - vp;
- }
-
- switch (tag) {
-
- case TAG_SUBNET_MASK: /* may demand len==4 */
- if (len >= 4) {
- ul = hp->subnet_mask.s_addr;
- bcopy((char*)&ul, vp, 4);
- }
- break;
-
- case TAG_TIME_OFFSET: /* may demand len==4 */
- if (len >= 4) {
- ul = (hp->flags.timeoff_auto) ?
- secondswest : hp->time_offset;
- ul = htonl(ul);
- bcopy((char*)&ul, vp, 4);
- }
- break;
-
- case TAG_GATEWAY:
- if (hp->flags.gateway)
- insert_ip(hp->gateway, vp, len);
- break;
-
- case TAG_TIME_SERVER:
- if (hp->flags.time_server)
- insert_ip(hp->time_server, vp, len);
- break;
-
- case TAG_NAME_SERVER:
- if (hp->flags.name_server)
- insert_ip(hp->name_server, vp, len);
- break;
-
- case TAG_DOMAIN_SERVER:
- if (hp->flags.domain_server)
- insert_ip(hp->domain_server, vp, len);
- break;
-
- case TAG_LOG_SERVER:
- if (hp->flags.log_server)
- insert_ip(hp->log_server, vp, len);
- break;
-
- case TAG_COOKIE_SERVER:
- if (hp->flags.cookie_server)
- insert_ip(hp->cookie_server, vp, len);
- break;
-
- case TAG_LPR_SERVER:
- if (hp->flags.lpr_server)
- insert_ip(hp->lpr_server, vp, len);
- break;
-
- case TAG_IMPRESS_SERVER:
- if (hp->flags.impress_server)
- insert_ip(hp->impress_server, vp, len);
- break;
-
- case TAG_RLP_SERVER:
- if (hp->flags.rlp_server)
- insert_ip(hp->rlp_server, vp, len);
- break;
-
- case TAG_HOSTNAME: /* of client */
- if (hp->flags.name_switch &&
- hp->flags.send_name) {
-
- /* Room for the whole name? */
- n = strlen(hp->hostname->string);
- if (n < len) {
- bcopy(hp->hostname->string, vp, n);
- } else {
- /*
- * Not enough room for fully qualified hostname,
- * try stripping off the domain parts.
- */
- if (debug > 1)
- report(LOG_INFO, "shortening hostname to fit\n");
- tmpstr = index(hp->hostname->string, '.');
- if (tmpstr) {
- n = tmpstr - hp->hostname->string;
- bcopy(hp->hostname->string, vp, n);
- }
- }
- }
- break;
-
- case TAG_BOOTSIZE:
- if (len >= 2) {
- if (hp->flags.bootsize_auto)
- us = (bootsize + 511) / 512; /* Round up */
- else us = hp->bootsize;
- bcopy((char*)&us, vp, 2);
- }
- break;
-
- default:
- if (hp->flags.generic)
- insert_generic(tag, hp->generic, vp, len);
- break;
-
- } /* switch (tag) */
- vp += len;
- } /* while */
- return;
- } /* dovend_rfc1048 */
-
-
-
- /*
- * Compare function to determine whether two hardware addresses are
- * equivalent. Returns TRUE if "host1" and "host2" are equivalent, FALSE
- * otherwise.
- *
- * This function is used when retrieving elements from the hardware address
- * hash table.
- */
-
- boolean hwlookcmp(host1, host2)
- struct host *host1, *host2;
- {
- if (host1->htype != host2->htype) {
- return FALSE;
- }
- if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) {
- return FALSE;
- }
- return TRUE;
- }
-
-
-
-
- /*
- * Compare function for doing IP address hash table lookup.
- */
-
- boolean iplookcmp(host1, host2)
- struct host *host1, *host2;
- {
- return (host1->iaddr.s_addr == host2->iaddr.s_addr);
- }
-
-
-
- /*
- * Copy a list of IP addresses to the memory buffer "dest".
- * The "iplist" is a pointer to a list of IP addresses
- * (struct in_addr_list), and "len" points to an integer which
- * indicates the size of the "dest" buffer.
- *
- * This is used to fill the vendor-specific area of a bootp packet in
- * conformance to RFC1048.
- */
-
- void insert_ip(iplist, dest, len)
- struct in_addr_list *iplist;
- byte *dest;
- int len; /* space for this list */
- {
- register struct in_addr *addrptr;
- register unsigned addrcount;
-
- if (!iplist) return;
-
- addrptr = iplist->addr;
- addrcount = iplist->addrcount;
- while ((len >= 4) && (addrcount > 0)) {
- bcopy(addrptr, dest, 4);
- addrptr++;
- addrcount--;
- dest += 4;
- len -= 4;
- }
- }
-
-
-
- /*
- * Find an RFC1048 record of type "tag" and copy it to "dest".
- * The data in "gendata" is assumed to already in RFC1048 format.
- */
-
- void insert_generic(tag, gendata, dest, destlen)
- int tag;
- struct shared_bindata *gendata;
- byte *dest;
- int destlen;
- {
- byte *srcptr, *ep;
- register int t, n;
-
- if (!gendata) return;
-
- srcptr = gendata->data;
- ep = srcptr + gendata->length;
-
- /* Search the generic records for a tag of the desired type. */
- while (srcptr < ep) {
- t = *srcptr++;
- n = *srcptr++;
-
- if (t == tag) {
- /* Copy whatever fits. */
- if (n > destlen)
- n = destlen;
- bcopy(srcptr, dest, n);
- return;
- }
- srcptr += n;
- }
- }
-
-
-
-
- /*
- * Convert a hardware address to an ASCII string.
- */
-
- char *haddrtoa(haddr, htype)
- register byte *haddr;
- byte htype;
- {
- static char haddrbuf[2 * MAXHADDRLEN + 1];
- register char *bufptr;
- register unsigned count;
-
- bufptr = haddrbuf;
- for (count = haddrlength(htype); count > 0; count--) {
- sprintf(bufptr, "%02X", (unsigned) (*haddr++ & 0xFF));
- bufptr += 2;
- }
- return (haddrbuf);
- }
-
-
-
- /*
- * Return pointer to static string which gives full filesystem error message.
- */
-
- char *get_errmsg()
- {
- static char errmsg[80];
-
- if (errno < sys_nerr) {
- return sys_errlist[errno];
- } else {
- sprintf(errmsg, "Error %d", errno);
- return errmsg;
- }
- }
-
-
-
- /*
- * This routine reports errors and such via stderr and syslog() if
- * appopriate. It just helps avoid a lot of "#ifdef SYSLOG" constructs
- * from being scattered throughout the code.
- *
- * The syntax is identical to syslog(3), but %m is not considered special
- * for output to stderr (i.e. you'll see "%m" in the output. . .). Also,
- * control strings should normally end with \n since newlines aren't
- * automatically generated for stderr output (whereas syslog strips out all
- * newlines and adds its own at the end).
- */
-
- /*VARARGS2*/
- void report(priority, fmt, p0, p1, p2, p3, p4)
- int priority;
- char *fmt;
- {
- #ifdef LOG_SALERT
- static char *levelnames[] = {
- "unknown level: ",
- "alert(1): ",
- "subalert(2): ",
- "emergency(3): ",
- "error(4): ",
- "critical(5): ",
- "warning(6): ",
- "notice(7): ",
- "information(8):",
- "debug(9): ",
- "unknown level: "
- };
- #else
- static char *levelnames[] = {
- "emergency(0): ",
- "alert(1): ",
- "critical(2): ",
- "error(3): ",
- "warning(4): ",
- "notice(5): ",
- "information(6):",
- "debug(7): ",
- "unknown level: "
- };
- #endif
-
- if ((priority < 0) || (priority >= sizeof(levelnames)/sizeof(char *))) {
- priority = sizeof(levelnames) / sizeof(char *) - 1;
- }
-
- /*
- * Print the message
- */
- if (debug > 2) {
- fprintf(stderr, "bootpd: %s ", levelnames[priority]);
- fprintf(stderr, fmt, p0, p1, p2, p3, p4);
- }
- #ifdef SYSLOG
- syslog(priority, fmt, p0, p1, p2, p3, p4);
- #endif
- }
-