/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* bos72F src/bos/usr/sbin/bootpd/bootpd.c 1.13.4.2 */ /* */ /* Licensed Materials - Property of IBM */ /* */ /* COPYRIGHT International Business Machines Corp. 1991,2016 */ /* All Rights Reserved */ /* */ /* US Government Users Restricted Rights - Use, duplication or */ /* disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ /* */ /* IBM_PROLOG_END_TAG */ static char sccsid[] = "@(#)40 1.13.4.2 src/bos/usr/sbin/bootpd/bootpd.c, cmdnet, bos72F, f2016_30B1 7/5/16 03:33:44"; /* ** COMPONENT_NAME: (CMDNET) bootpd.c ** ** FUNCTIONS: ** ** main(argc, argv) ** int chk_access(path, filesize) ** void dovend_cmu(bp, hp) ** void dovend_rfc1048(bp, hp, hp1, bootsize) ** void dump_host(fp, hp) ** dumptab() ** char *get_errmsg() ** char *haddrtoa(haddr, htype) ** boolean hwlookcmp(host1, host2) ** void insert_generic(gendata, buff, bytesleft) ** void insert_ip(tag, iplist, dest, bytesleft) ** void insert_u_long(value, dest) ** boolean iplookcmp(host1, host2) ** void list_ipaddresses(fp, ipptr) ** static nmatch(ca,cb) ** reply() ** void report(priority, fmt, p0, p1, p2, p3, p4) ** request() ** sendreply(forward) ** setarp(ia, ha, len,type) ** void usage() ** ** ORIGINS: 6,44,27 ** ** (C) COPYRIGHT International Business Machines Corp. 1991, 1996 ** All Rights Reserved ** Licensed Materials - Property of IBM ** US Government Users Restricted Rights - Use, duplication or ** disclosure restricted by GSA ADP Schedule Contract with IBM Corp. ** ** Permission to use, copy, modify, and distribute this program for any ** purpose and without fee is hereby granted, provided that this copyright, ** permission and disclaimer notice appear on all copies and supporting ** documentation; the name of IBM not be used in advertising or publicity ** pertaining to distribution of the program without specific prior ** permission; and notice be given in supporting documentation that copying ** and distribution is by permission of IBM. IBM makes no representations ** about the suitability of this software for any purpose. This program ** is provided "as is" without any express or implied warranties, including, ** without limitation, the implied warranties of merchantability and fitness ** for a particular purpose. ** ** US Government Users Restricted Rights - Use, duplication or ** disclosure restricted by GSA ADP Schedule Contract with IBM Corp. ** ** (C) COPYRIGHT Advanced Graphics Engineering 1990 ** All Rights Reserved */ /* * BOOTP (bootstrap protocol) server daemon. * * Answers BOOTP request packets from booting client machines. * See [SRI-NIC]RFC951.TXT for a description of the protocol. * See [SRI-NIC]RFC1048.TXT for vendor-information extensions. * See accompanying man page -- bootpd.8 * * Copyright 1988 by Carnegie Mellon. * * Permission to use, copy, modify, and distribute this program for any * purpose and without fee is hereby granted, provided that this copyright * and permission notice appear on all copies and supporting documentation, * the name of Carnegie Mellon not be used in advertising or publicity * pertaining to distribution of the program without specific prior * permission, and notice be given in supporting documentation that copying * and distribution is by permission of Carnegie Mellon and Stanford * University. Carnegie Mellon makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * * 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 #include #include #include #include #include #include #include #include #include #ifdef AIX32 #define _AIXVER3 #define _FIELD_SALEN #endif #ifdef AIX31 #define _AIXVER3 #include #endif #ifdef _AIXVER3 #define _FIELD_IFTYPE #define _USE_NLS #define _USE_SYSLOG #define _USE_DEBUG #endif #ifdef _INC_SOCKIO_H #include #endif #ifdef _INC_IFARP_H #include #endif #include #include #include #include /* * AIXBUILD defined when building ver 3.2 bootpd in /usr/sbin */ #ifdef AIXBUILD #include #endif #ifdef _USE_SYSLOG #include #else #define LOG_INFO 6 #define LOG_ERR 3 #define LOG_NOTICE 5 #endif #ifdef _USE_NLS #include /* included for nls support */ #endif #include #include #include #include #include #include #include "bootp.h" #include "hash.h" #include "bootpd.h" #define MAXINTERFACES 254 /* Maximum number of interfaces */ /* 582926 - We are not allocating enough space for the ifreq structures * read from the kernel. Some of them are from the AF_LINK family, which * require a total of 128 bytes. Sicne the ifreq structure is only 32 bytes * and the ifr_addr field in the ifreq structure is only 16 bytes, we quickly * run out of room, especially if several AF_LINK family structures are used. * * To take care of this, the current solution is to just allocate more buffer * space for them (128 - 16 = 112 additional bytes are needed). */ #define AF_LINK_SIZE 112 #define HASHTABLESIZE 257 /* Hash table size (prime) */ #define DEFAULT_TIMEOUT 15L /* Default timeout in minutes */ #define MAX_SECONDARY_DELAY 60 /* maximum number of secondsto delay before responding to next boot request */ /* * change definition of SIOCSARP_802_5 if not defined in ioctl.h */ #ifndef SIOCSARP_802_5 #define SIOCSARP_802_5 SIOCSARP_TOKEN #endif #ifndef CONFIG_FILE #define CONFIG_FILE "/etc/bootptab" #endif #ifndef DUMP_FILE #define DUMP_FILE "/etc/bootpd.dump" #endif #define satosin(sa) ((struct sockaddr_in *)(sa)) /* * Externals, forward declarations, and global variables */ extern char Version[]; extern char *sys_errlist[]; extern int errno, sys_nerr; void usage(); void insert_u_long(); void dump_host(); void list_ipaddresses(); #ifdef _USE_CMUVEND void dovend_cmu(); #endif void dovend_rfc1048(); boolean hwlookcmp(); boolean iplookcmp(); void insert_generic(); void insert_ip(); #ifdef _USE_DEBUG int dumptab(); #endif int chk_access(); void report(); char *get_errmsg(); byte *flip_addr(); /* * 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_in sin; struct sockaddr_in from; /* Packet source */ char ifcbuf[MAXINTERFACES * (sizeof(struct ifreq) + AF_LINK_SIZE)]; /* Holds interface configuration, Due to defect 582926 added 112 to support AF_LINK */ struct ifconf ifconf; /* Int. config ioctl block (pnts to ifreq) */ int netmask; struct arpreq arpreq; /* Arp request ioctl block */ /* * General */ int debug = 0; /* Debugging flag */ int compatible = FALSE; /* compatible with back level X-station */ int leave_giaddr_alone = FALSE; /* forces RFC 1542 compliance */ int s; /* Socket file descriptor */ byte buf[1024]; /* Receive packet buffer */ struct timezone tzp; /* Time zone offset for clients */ struct timeval tp; /* Time (extra baggage) */ long secondswest; /* Time zone offset in seconds */ struct in_addr saved_giaddr; /* save the gateway ip in request */ struct in_addr saved_ciaddr; /* save the client ip in request */ #define MAXMACBUFFER 256 typedef struct BP_IF_INFO_T { struct if_info ifi; #define IFINAME ifi.ifi_name #define IFIADDR ifi.ifi_addr.s_addr #define IFITYPE ifi.ifi_type char ifmacheader[MAXMACBUFFER]; int ifdstaddr; } bp_if_info_t; bp_if_info_t bp_if_info; /* * Globals below are associated with the bootp database file (bootptab). */ char *bootptab = NULL; #ifdef _USE_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; /* * Hardware address lengths (in bytes) 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. . . . */ unsigned maphaddrlen[MAXHTYPES + 1] = { 0, /* Type 0: Reserved (don't use this) */ 6, /* Type 1: 10Mb Ethernet (48 bits) */ 1, /* Type 2: 3Mb Ethernet (8 bits) */ 0, /* Type 3: Amateur Radio AX.25 */ 1, /* Type 4: Proteon ProNET Token Ring */ 0, /* Type 5: Chaos */ 6, /* Type 6: IEEE 802 Networks */ 0, /* Type 7: ARCNET */ 0, /* Type 8: Hyperchannel */ 0, /* Type 9: Lanstar */ 0, /* Type 10: Autonet Short Address */ 0, /* Type 11: LocalTalk */ 0, /* Type 12: LocalNet (IBM PCNet or SYSTEK LocalNET */ 0, /* Type 13: Ultra Link */ 0, /* Type 14: SMDS */ 0, /* Type 15: Frame Relay */ 0, /* Type 16: Asynchronous Transmission Mode (ATM) */ 6, /* Type 17: IEEE 802.3 Network */ 6, /* Type 18: FDDI */ 0, /* Type 19: (don't use this) */ 0, /* Type 20: (don't use this) */ 0, /* Type 21: (don't use this) */ 0, /* Type 22: (don't use this) */ 0, /* Type 23: (don't use this) */ 0, /* Type 24: (don't use this) */ 0, /* Type 25: (don't use this) */ 0, /* Type 26: (don't use this) */ 0, /* Type 27: (don't use this) */ 0, /* Type 28: (don't use this) */ 0, /* Type 29: (don't use this) */ 0, /* Type 30: (don't use this) */ 0, /* Type 31: (don't use this) */ 0, /* Type 32: (don't use this) */ 0, /* Type 33: (don't use this) */ 0, /* Type 34: (don't use this) */ 0, /* Type 35: (don't use this) */ 0, /* Type 36: (don't use this) */ 6, /* Type 37: Host Fabric INterface(HFI) */ }; /* * 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. */ extern char **environ; main(argc, argv) int argc; char **argv; { struct timeval actualtimeout, *timeout; struct bootp *bp = (struct bootp *) buf; struct servent *servp; char *stmp; int n, tolen; int nfound, readfds; int standalone; char control[MAXMACBUFFER]; struct msghdr msg; struct iovec iov; struct cmsghdr *cp; u_char *ucp; int cmsg_len; int on; #ifdef _USE_MAXDEBUG int i; /* Used by Alex's print routine */ struct bootp *b; /* Points to the buffer we just got */ #endif #ifdef _USE_NLS (void)setlocale(LC_ALL, ""); /* included for nls support */ #endif stmp = NULL; standalone = FALSE; actualtimeout.tv_usec = 0L; actualtimeout.tv_sec = 60 * DEFAULT_TIMEOUT; timeout = &actualtimeout; /* * 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': debug++; break; case 'c': /* This is the flag to make bootpd compatible with * back level X-stations, if "ga" tag is set in * /etc/bootptab, but bootpd finds the server and * is actually in same subnet, ignore gateway; * those X-station needs "ga" tag even in same * subnet as it's server. Please see defect 185064. */ compatible = TRUE; break; case 'g': leave_giaddr_alone = TRUE; break; case 's': standalone = TRUE; break; default: fprintf(stderr, "bootpd: unknown switch: -%c\n", argv[0][1]); usage(); break; } } else { if (!bootptab) { bootptab = argv[0]; #ifdef _USE_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 _USE_DEBUG if (!bootpd_dump) { bootpd_dump = DUMP_FILE; } #endif /* * 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)) { fprintf(stderr, "Unable to allocate hash tables.\n"); exit(1); } if (standalone) { /* * Go into background and disassociate from controlling terminal. */ 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 _USE_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); syslog(LOG_INFO, "%s", Version); #endif if (standalone) { /* * Get us 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. */ #ifdef _FIELD_SALEN sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(bootps_port); if (bind(s, &sin, sizeof(sin)) < 0) { perror("bind failed:"); report(LOG_ERR, "bind: %s\n", get_network_errmsg()); exit(1); } } else { /* * Assume socket was passed to us from inetd */ s = 0; tolen = sizeof(sin); bzero((char *) &sin, tolen); if (getsockname(s, &sin, &tolen) < 0) { report(LOG_ERR, "getsockname: %s\n", get_network_errmsg()); exit(1); } bootps_port = ntohs(sin.sin_port); } /* Get options from socket */ on = 1; if (setsockopt(s, IPPROTO_IP, IP_RECVIFINFO, &on, sizeof(on)) < 0) { report(LOG_ERR, "%s - %s", "setsockopt:IP_RECVIFINFO", strerror(errno)); exit(1); } if (setsockopt(s, IPPROTO_IP, IP_RECVMACHDR, &on, sizeof(on)) < 0) { report(LOG_ERR, "%s - %s", "setsockopt:IP_RECVMACHDR", strerror(errno)); exit(1); } if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)) < 0) { report(LOG_ERR, "%s - %s", "setsockopt:IP_RECVDSTADDR",strerror(errno)); 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; } /* * Read the bootptab file once immediately upon startup. */ readtab(); /* * 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 _USE_DEBUG if ((int) signal(SIGUSR1, dumptab) < 0) { report(LOG_ERR, "signal: %s\n", get_errmsg()); exit(1); } dumptab(); #endif /* * Process incoming requests. */ for (;;) { readfds = 1 << s; nfound = select(s + 1, &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); } iov.iov_base = buf; iov.iov_len = sizeof(buf); bzero(&msg, sizeof(msg)); msg.msg_control = control; msg.msg_controllen = sizeof(control); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = &from; msg.msg_namelen = sizeof(from); n = recvmsg(s, &msg, 0); if (n <= 0) { continue; } bzero(&bp_if_info, sizeof(bp_if_info)); cp = CMSG_FIRSTHDR(&msg); do { cmsg_len = cp->cmsg_len - sizeof(struct cmsghdr); if (cmsg_len > 0) { ucp = CMSG_DATA(cp); if (cp->cmsg_type == IP_RECVIFINFO) bcopy(ucp, &bp_if_info, cmsg_len); if (cp->cmsg_type == IP_RECVMACHDR) bcopy(ucp, bp_if_info.ifmacheader, cmsg_len); if (cp->cmsg_type == IP_RECVDSTADDR) bcopy(ucp,&(bp_if_info.ifdstaddr), 4); } } while ((cp=(CMSG_NXTHDR(&msg, cp))) != NULL); /* * If the client sends an empty ciaddr, then check to see if the * from address is something other than zero. */ saved_ciaddr.s_addr = bp->bp_ciaddr.s_addr; if (bp->bp_ciaddr.s_addr == 0 && bp->bp_giaddr.s_addr == 0) { bp->bp_ciaddr.s_addr = from.sin_addr.s_addr; } if (n < sizeof(struct bootp)) { report(LOG_NOTICE, "received short packet\n"); continue; } /* Start of display Alex's display routine */ #ifdef _USE_MAXDEBUG b = (struct bootp *)buf; report(LOG_INFO,"%d: unsigned char bp_op; /* packet opcode type */\n", b->bp_op); report(LOG_INFO,"%d: unsigned char bp_htype;/* hardware addr type */\n", b->bp_htype); report(LOG_INFO,"%d: unsigned char bp_hlen; /* hardware addr length */\n", b->bp_hlen); report(LOG_INFO,"%d: unsigned char bp_hops; /* gateway hops */\n", b->bp_hops); report(LOG_INFO,"%d: unsigned long bp_xid; /* transaction ID */\n", b->bp_xid); report(LOG_INFO,"%d: unsigned short bp_secs; /* seconds since boot began */\n", b->bp_secs); report(LOG_INFO,"%d: unsigned short bp_unused;\n", b->bp_unused); report(LOG_INFO,"%d: in_name bp_ciaddr; /* client IP address */\n", b->bp_ciaddr); report(LOG_INFO,"%d: in_name bp_yiaddr; /* 'your' IP address */\n", b->bp_yiaddr); report(LOG_INFO,"%d: in_name bp_siaddr; /* server IP address */\n", b->bp_siaddr); report(LOG_INFO,"%d: in_name bp_giaddr; /* gateway IP address */\n", b->bp_giaddr); report(LOG_INFO,"unsigned char bp_chaddr[16]; /* client hardware address */\n"); for(i = 0; i < 16; i++) report(LOG_INFO,"%d.",b->bp_chaddr[i]); report(LOG_INFO,"\n"); report(LOG_INFO,"unsigned char bp_sname[64]; /* server host name */\n"); for(i = 0; i < 64; i++) report(LOG_INFO,"%d.",b->bp_sname[i]); report(LOG_INFO,"\n"); report(LOG_INFO,"unsigned char bp_file[128]; /* boot file name */\n"); for(i = 0; i < 128; i++) report(LOG_INFO,"%d.",b->bp_file[i]); report(LOG_INFO,"\n"); report(LOG_INFO,"unsigned char bp_vend[64]; /* vendor-specific area */\n"); for(i = 0; i < 64; i++) report(LOG_INFO,"%d.",b->bp_vend[i]); report(LOG_INFO,"\n"); #endif readtab(); /* maybe re-read bootptab */ switch (bp->bp_op) { case BOOTREQUEST: if (debug) { report(LOG_INFO,"Received boot request. \n"); } request(); if (debug) { report(LOG_INFO,"Finished processing boot request.\n"); } break; case BOOTREPLY: reply(); break; } } } /* * Print "usage" message and exit */ void usage() { fprintf(stderr, "usage: bootpd [-d] [-c] [-s] [-t timeout] [configfile [dumpfile]]\n"); fprintf(stderr, "\t -d\tincrease debug verbosity\n"); fprintf(stderr, "\t -c\tcompatible with previous version of bootpd\n\t\tfor booting IBM X-station 120s and 130s\n"); fprintf(stderr, "\t -g\tserver does not change giaddr\n"); fprintf(stderr, "\t -s\trun standalone (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.) */ request() { register struct bootp *bp = (struct bootp *) buf; register struct host *hp; register struct host *hp1 = NULL; /*alt*/ register int n; char path[129]; struct host dummyhost; long bootsize; unsigned hlen, hashcode; int delay_seconds; int dbxbreakpt; bp->bp_op = BOOTREPLY; if (bp->bp_ciaddr.s_addr == 0) { /* * client doesnt know his IP address, * search by hardware address. */ if (bp->bp_htype > MAXHTYPES) { report(LOG_INFO, "Malformed request with unknown htype %i\n", bp->bp_htype); /* Bad htype will make us core */ return; /* So, just log it and return */ } if (debug) { report(LOG_INFO, "request from hardware address %s\n", 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) && (bp->bp_htype == HTYPE_ETHERNET)){ /*alt*/ dummyhost.htype = HTYPE_IEEE8023; /*alt*/ hp1 = (struct host *) hash_Lookup( /*alt*/ hwhashtable, hashcode, hwlookcmp, &dummyhost);/*alt*/ } /*alt*/ if((hp == NULL) && (bp->bp_htype == HTYPE_IEEE802)){ /*8023*/ bp->bp_htype = HTYPE_IEEE8023; dummyhost.htype = bp->bp_htype; /*8023*/ hp = (struct host *) hash_Lookup(hwhashtable, hashcode,/*8023*/ hwlookcmp, &dummyhost);/*8023*/ if (hp != NULL){ /*alt*/ dummyhost.htype = HTYPE_ETHERNET; /*alt*/ hp1 = (struct host *) hash_Lookup( /*alt*/ hwhashtable, hashcode, hwlookcmp, &dummyhost);/*alt*/ } /*alt*/ } /*8023*/ if (( hp == NULL) && (bp->bp_htype == HTYPE_ETHERNET )) { bp->bp_htype = HTYPE_FDDI; dummyhost.htype = bp->bp_htype; hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, &dummyhost); } if (hp == NULL) { report(LOG_NOTICE, "hardware address not found: %s\n", 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; } (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; } /* This is the partial fix for booting off */ /* the 802.3 ethernet. */ if (bp->bp_htype != hp->htype) { bp->bp_htype = hp->htype; } #if defined(FEATURE) /* * If the host is not a primary boot host, then delay before * filling the boot request. The amount of delay time is * defined in the bootptab file. */ if ( delay_seconds=chk_secondary(hp)) /* if secondary boot host */ { delay_seconds = ((delay_seconds < MAX_SECONDARY_DELAY) ? delay_seconds : MAX_SECONDARY_DELAY); if ( bp->bp_secs < delay_seconds ) return; } #endif if (debug) { report(LOG_INFO, "found %s %s\n", inet_ntoa(hp->iaddr), hp->hostname); } /* * 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; /* Save the gateway ipaddr in bootp request */ saved_giaddr = bp->bp_giaddr; if (leave_giaddr_alone != TRUE) (bp->bp_giaddr).s_addr = (hp->flags.gateway) ? hp->gateway->addr[0].s_addr : 0L; /* * 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. * * 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" * */ /* * Who knows. . . . . 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 */ bzero(path, sizeof(path)); /* * This is the code which actually implements the above truth table. */ if (bp->bp_file[0]) { /* * The client specified a file. */ if (bp->bp_file[0] == '/') { strncpy(path, bp->bp_file, sizeof(path)-1); /* Absolute pathname */ } else { if (hp->flags.homedir) { strncpy(path, hp->homedir, sizeof(path)-1); if (sizeof(path)-1-strlen(path) > 0) strncat(path, "/", sizeof(path)-1-strlen(path)); if (sizeof(path)-1-strlen(path) > 0) strncat(path, bp->bp_file, sizeof(path)-1-strlen(path)); } else { report(LOG_NOTICE, "requested file \"%s\" not found: hd unspecified\n", bp->bp_file); return; } } } else { /* * No file specified by the client. */ dbxbreakpt = 0; if (hp->flags.bootfile & (*(hp->bootfile) == '/')) { strncpy(path, hp->bootfile, sizeof(path)-1); } else if (hp->flags.homedir && hp->flags.bootfile) { strncpy(path, hp->homedir, sizeof(path)-1); if (sizeof(path)-1-strlen(path) > 0) strncat(path, "/", sizeof(path)-1-strlen(path)); if (sizeof(path)-1-strlen(path) > 0) strncat(path, hp->bootfile, sizeof(path)-1-strlen(path)); } 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); if (sizeof(path)-1-strlen(path) > 0) strncat(path, ".", sizeof(path)-1-strlen(path)); if (sizeof(path)-1-strlen(path) > 0) strncat(path, hp->hostname, sizeof(path)-1-strlen(path)); if (chk_access(path, &bootsize) < 0) { path[n] = 0; /* Try it without the suffix */ if (chk_access(path, &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; } } } bzero(bp->bp_file, sizeof(bp->bp_file)); strncpy(bp->bp_file, path, sizeof(bp->bp_file)); if (debug) report(LOG_INFO, "bootfile = %s\n", bp->bp_file); skip_file: ; /* * Zero the vendor information area, except for the magic cookie. */ bzero((bp->bp_vend) + 4, (sizeof(bp->bp_vend)) - 4); if (debug) { 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) { 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)) { /* Not an RFC1048 bootp client */ #ifdef _USE_CMUVEND dovend_cmu(bp, hp); #else dovend_rfc1048(bp, hp, hp1, bootsize); #endif } else { /* RFC1048 conformant bootp client */ dovend_rfc1048(bp, hp, hp1, bootsize); } if (hp->flags.dumb_terminal == TRUE) { sendreply(0,TRUE); } else { sendreply(0,FALSE); } } /* * 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 buf; if ((stat(path, &buf) == 0) && (buf.st_mode & (S_IREAD >> 6))) { *filesize = (long) buf.st_size; return 0; } else { return -1; } } /* * Process BOOTREPLY packet (something is using us as a gateway). */ reply() { if (debug) { report(LOG_INFO, "processing boot reply\n"); } sendreply(1,FALSE); } /* * ASSUMES global struct ifconf ifconf, global socket s * return number of identical bytes in netnumber of dst and "best" interface */ int getbestif(dst, ifrmax) struct in_addr *dst; struct ifreq **ifrmax; { char *p, *lastbyte; struct ifreq ifrq, *pifr; int netaddr, dstaddr, nbytes, maxb; maxb = 0; /* * Determine network configuration. */ ifconf.ifc_len = sizeof(ifcbuf); ifconf.ifc_buf = ifcbuf; if ((ioctl(s, SIOCGIFCONF, (caddr_t) &ifconf) < 0) || (ifconf.ifc_len <= 0)) { report(LOG_ERR, "ioctl: %s\n", get_network_errmsg()); exit(1); } *ifrmax = ifconf.ifc_req; lastbyte = ifconf.ifc_buf + ifconf.ifc_len; #ifdef _FIELD_SALEN #define max(a,b) ((a) < (b) ? (b) : (a)) #define sasize(p) max((p).sa_len, sizeof(p)) for(p = ifconf.ifc_buf; p < lastbyte; p += sizeof(pifr->ifr_name) + sasize(pifr->ifr_addr)) #else for(p = ifconf.ifc_buf; p < lastbyte; p += sizeof(*pifr)) #endif { pifr = (struct ifreq *)p; /* * if not AF_NET, skip it */ if(pifr->ifr_addr.sa_family != AF_INET) continue; ifrq = *pifr; /* * make sure interface is up and running */ if(ioctl(s, SIOCGIFFLAGS, (char *)&ifrq) < 0) { perror("ioctl(SIOCGIFFLAGS)"); ifrq.ifr_flags = 0xFFFFFFFF; } if((ifrq.ifr_flags & IFF_UP) && (ifrq.ifr_flags & IFF_RUNNING)) { netaddr = satosin(&ifrq.ifr_addr)->sin_addr.s_addr; dstaddr = dst->s_addr; if(ioctl(s, SIOCGIFNETMASK, (caddr_t)&ifrq) >= 0) { netmask = satosin(&ifrq.ifr_addr)->sin_addr.s_addr; netaddr &= netmask; dstaddr &= netmask; } else perror("ioctl(SIOCGIFNETMASK)"); if((nbytes = nmatch(&dstaddr, &netaddr)) >= maxb) { maxb = nbytes; *ifrmax = pifr; } } } return maxb; } /* * Send a reply packet to the client. 'forward' flag is set if we are * not the originator of this reply packet. */ sendreply(forward, rtinfo) int forward; int rtinfo; { register struct bootp *bp = (struct bootp *) buf; struct in_addr dst, client_s_addr; struct sockaddr_in to; struct ifreq *ifrbest; int ret, set_arp; int samesubnet = 1; struct arpreq *retarp, *setarp(); int matchbytes; set_arp = 0; ifrbest = (struct ifreq *) NULL; retarp = (struct arpreq *) NULL; to = sin; to.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 ((saved_giaddr.s_addr) && (forward == 0)) { dst = saved_giaddr; to.sin_port = htons(bootps_port); } else { dst = bp->bp_yiaddr; set_arp = 1; } /* * 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). */ matchbytes = getbestif(&dst, &ifrbest); /* Set socket option */ if (setsockopt(s, IPPROTO_IP, IP_BROADCAST_IF, ifrbest->ifr_name, IFNAMSIZ) < 0) { report(LOG_ERR, "setsockopt: IP_BROADCAST_IF\n"); } if (forward == 0) { if (bp->bp_siaddr.s_addr == 0) bp->bp_siaddr = satosin(&ifrbest->ifr_addr)->sin_addr; if (bp->bp_giaddr.s_addr == 0) { if (matchbytes == 0) { report(LOG_ERR, "No route to %s\n", inet_ntoa(dst)); return; } if (leave_giaddr_alone != TRUE) bp->bp_giaddr = satosin(&ifrbest->ifr_addr)->sin_addr; } /* To support back level eprom X-station, such as 120 and 130, * we have to find out if we should ignore the gateway or not. */ if (compatible == TRUE) { client_s_addr = bp->bp_ciaddr.s_addr ? bp->bp_ciaddr : bp->bp_yiaddr; /* gw specified but actually client and server are in * same subnet */ if( bp->bp_giaddr.s_addr && ( ( client_s_addr.s_addr & netmask) == (bp->bp_siaddr.s_addr & netmask) ) ) { /* client and server in same subnet */ samesubnet = 1; } else { /* client and server are NOT in same subnet */ samesubnet = 0; } if ( !(bp->bp_ciaddr.s_addr) && bp->bp_giaddr.s_addr && (samesubnet == 1)) { to.sin_port = htons(bootpc_port); dst = bp->bp_yiaddr; bp->bp_giaddr = bp->bp_siaddr; set_arp = 1; } } /* end of if (compatible == TRUE) */ } /* Reset the ciaddr field to the saved address */ bp->bp_ciaddr.s_addr = saved_ciaddr.s_addr; if(set_arp == 1) retarp = setarp(&dst, bp->bp_chaddr, bp->bp_hlen,bp->bp_htype, rtinfo, (ifrbest) ? ifrbest->ifr_name : (char *) NULL); to.sin_addr = dst; if (debug) { report(LOG_INFO, "The following addresses are included in the bootp reply\n"); report(LOG_INFO, "Client IP address (bp->bp_ciaddr) = %s\n", inet_ntoa(bp->bp_ciaddr.s_addr ? bp->bp_ciaddr : bp->bp_yiaddr)); report(LOG_INFO, "Server IP address (bp->bp_siaddr) = %s\n", inet_ntoa(bp->bp_siaddr)); if (bp->bp_giaddr.s_addr) report(LOG_INFO, "Gateway IP address (bp->bp_giaddr) = %s\n", inet_ntoa(bp->bp_giaddr)); } if (bp->bp_htype == HTYPE_IEEE8023) /*8023*/ bp->bp_htype = HTYPE_IEEE802; /*8023*/ else if (bp->bp_htype == HTYPE_FDDI) /*FDDI*/ bp->bp_htype = HTYPE_ETHERNET; /*FDDI*/ if ((ret = sendto(s, bp, sizeof(struct bootp), 0, &to, sizeof(to))) < 0) { report(LOG_ERR, "sendto: %s\n", get_network_errmsg()); } } #define BRADS /* * Return the number of leading bytes matching in the * internet addresses supplied. */ static nmatch(ca, cb) register char *ca, *cb; { register int n, m; #ifdef BRADS /* * Return number of bits that match. */ for (m = n = 0 ; n < 4 ; n++) { if (*ca++ != *cb++) { ca--;cb--; n = ~(*ca ^ *cb); /* first 0 bit is first different bit */ m *=8; if(n&0x80){ m++; if(n&0x40){ m++; if(n&0x20){ m++; if(n&0x10){ m++; if(n&0x08){ m++; if(n&0x04){ m++; if(n&0x02){ m++; if(n&0x01) m++;}}}}}}} return(m); } m++; } return(8*m); #else register n,m; for (m = n = 0 ; n < 4 ; n++) { if (*ca++ != *cb++) return(m); m++; } return(m); #endif } /* * Setup the arp cache so that IP address 'ia' will be temporarily * bound to hardware address 'ha' of length 'len'. * HACK HACK HACK HACK */ struct arpreq * setarp(ia, ha, len,type, rtinfo, ifname) struct in_addr *ia; byte *ha; int len; char type; int rtinfo; char *ifname; { static struct arpreq arpreq; struct arpreq *retarp; struct sockaddr_in *si; int s, flip = 0; /* Used to hold socket */ bp_if_info_t *ifinfo = &bp_if_info; if (debug) report(LOG_INFO, "Time to creat ARP entry, harware type is %d and ifname is %s\n", type, ifname); retarp = (struct arpreq *) &arpreq; bzero((caddr_t)&arpreq, sizeof(arpreq)); /* Start of MOTT hack to make this work on PS/2 */ si = (struct sockaddr_in *)&arpreq.arp_pa; si->sin_family = AF_INET; si->sin_addr = *ia; si->sin_len = sizeof(*si); /* If old style boot is requested, then create a permanent arp entry */ if (rtinfo) arpreq.arp_flags = ATF_INUSE | ATF_COM | ATF_PERM; else arpreq.arp_flags = ATF_INUSE | ATF_COM; s = socket(AF_INET,SOCK_DGRAM,0); /* Get a socket for ioctl */ if (s < 0) /* Did we get the socket we need? */ report(LOG_ERR,"No socket in setarp(%i)",s); else { arpreq.at_length = (u_short) len; arpreq.ifType = bp_if_info.IFITYPE; switch (arpreq.ifType) { case IFT_ISO88025: bcopy( ((struct ie5_mac_hdr *)(ifinfo->ifmacheader))->_First.Mac_src, (caddr_t)(arpreq.arp_ha.sa_data), 6); arpreq.arp_ha.sa_data[0] &= ~RI_PRESENT; if (has_route((struct ie5_mac_hdr *)(ifinfo->ifmacheader))) { bcopy(&(((struct ie5_mac_hdr *)(ifinfo->ifmacheader)) -> _Variable), &arpreq.ifd.token, sizeof(u_short) * 9); arpreq.ifd.token.u_rcf ^= RCF_DIRECTION; arpreq.ifd.token.u_rcf &= ~(RCF_ALL_BROADCAST|RCF_LOCAL_BROADCAST); } break; case IFT_FDDI: bcopy( ((struct fddi_mac_hdr *)(ifinfo->ifmacheader))->_First.Mac_src, (caddr_t)(arpreq.arp_ha.sa_data), 6); arpreq.arp_ha.sa_data[0] &= ~RI_PRESENT; if (has_route_f((struct fddi_mac_hdr *)(ifinfo->ifmacheader))) { bcopy( &(((struct fddi_mac_hdr *)(ifinfo->ifmacheader)) -> _Variable), &arpreq.ifd.fddi, sizeof(u_short) * 15); arpreq.ifd.fddi.u_rcf ^= RCF_DIRECTION; arpreq.ifd.fddi.u_rcf &= ~(RCF_ALL_BROADCAST|RCF_LOCAL_BROADCAST); } break; case IFT_ETHER: case IFT_ISO88023: bcopy( ((struct ie3_mac_hdr *)(ifinfo->ifmacheader))->ie3_mac_src, (caddr_t)(arpreq.arp_ha.sa_data), 6); break; default: bcopy((caddr_t)ha, (caddr_t)(arpreq.arp_ha.sa_data), 6); break; } if (ioctl(s, SIOCSARP,(caddr_t)&arpreq) < 0) { report(LOG_ERR, "ioctl(SIOCSARP): %s\n", get_network_errmsg()); } else if (debug) { report(LOG_INFO, "ioctl(SIOCSARP): Arp entry created successfully.\n"); } close(s); } return(retarp); } #ifdef _USE_DEBUG /* * Dump the internal memory database to bootpd_dump. */ 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 dumpfile - \"%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, "#\tsa -- server IP address in bootp reply packet\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); if (debug) { 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); } if (hp->flags.iaddr) { fprintf(fp, "ip=%s:", inet_ntoa(hp->iaddr)); } 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.subnet_mask) { fprintf(fp, "sm=%s:", inet_ntoa(hp->subnet_mask)); } 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.impress_server) { fprintf(fp, "im="); list_ipaddresses(fp, hp->impress_server); fprintf(fp, ":"); } 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.time_server) { fprintf(fp, "ts="); list_ipaddresses(fp, hp->time_server); fprintf(fp, ":"); } 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.homedir) { fprintf(fp, "hd=%s:", hp->homedir); } if (hp->flags.bootfile) { fprintf(fp, "bf=%s:", hp->bootfile); } if (hp->flags.bootsize) { fprintf(fp, "bs="); if (hp->flags.bootsize_auto) { fprintf(fp, "auto:"); } else { fprintf(fp, "%d:", hp->bootsize); } } if (hp->flags.name_switch && hp->flags.send_name) { fprintf(fp, "hn:"); } 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.domain) { fprintf(fp, "dn=%s:", hp->domain); } 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 _USE_CMUVEND /* * 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; /* 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); if (debug) { report(LOG_INFO, "sending CMU-style reply\n"); } } #endif /* _USE_CMUVEND */ /* * Insert the RFC1048 vendor data for the host pointed to by "hp" into the * bootp packet pointed by "bp". */ void dovend_rfc1048(bp, hp, hp1, bootsize) register struct bootp *bp; register struct host *hp; register struct host *hp1; /*alt*/ long bootsize; { int bytesleft, len; byte *vp; char *tmpstr; vp = bp->bp_vend; bytesleft = sizeof(bp->bp_vend); /* Initial vendor area size */ bcopy(vm_rfc1048, vp, 4); /* Copy in the magic cookie */ vp += 4; bytesleft -= 4; if (hp->flags.time_offset) { *vp++ = TAG_TIME_OFFSET; /* -1 byte */ *vp++ = 4; /* -1 byte */ if (hp->flags.timeoff_auto) { insert_u_long(htonl(secondswest), &vp); } else { insert_u_long(htonl(hp->time_offset), &vp); /* -4 bytes */ } bytesleft -= 6; } if (hp->flags.subnet_mask) { *vp++ = TAG_SUBNET_MASK; /* -1 byte */ *vp++ = 4; /* -1 byte */ insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ bytesleft -= 6; /* Fix real count */ if (hp->flags.gateway) { insert_ip(TAG_GATEWAY, hp->gateway, &vp, &bytesleft); } } if (hp->flags.bootsize) { bootsize = (hp->flags.bootsize_auto) ? (bootsize / 512 + 1) : (hp->bootsize); /* Round up */ *vp++ = TAG_BOOTSIZE; *vp++ = 2; *vp++ = (byte) ((bootsize >> 8) & 0xFF); *vp++ = (byte) (bootsize & 0xFF); bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ } if (hp->flags.domain_server) { insert_ip(TAG_DOMAIN_SERVER, hp->domain_server, &vp, &bytesleft); } if (hp->flags.name_server) { insert_ip(TAG_NAME_SERVER, hp->name_server, &vp, &bytesleft); } if (hp->flags.rlp_server) { insert_ip(TAG_RLP_SERVER, hp->rlp_server, &vp, &bytesleft); } if (hp->flags.time_server) { insert_ip(TAG_TIME_SERVER, hp->time_server, &vp, &bytesleft); } if (hp->flags.log_server) { insert_ip(TAG_LOG_SERVER, hp->log_server, &vp, &bytesleft); } if (hp->flags.lpr_server) { insert_ip(TAG_LPR_SERVER, hp->lpr_server, &vp, &bytesleft); } if (hp->flags.cookie_server) { insert_ip(TAG_COOKIE_SERVER, hp->cookie_server, &vp, &bytesleft); } if (hp->flags.name_switch && hp->flags.send_name) { /* * Check for room for hostname. Add 2 to account for * TAG_HOSTNAME and length. */ len = strlen(hp->hostname); if ((len + 2) > bytesleft) { /* * Not enough room for full (domain-qualified) hostname, try * stripping it down to just the first field (host). */ tmpstr = hp->hostname; len = 0; while (*tmpstr && (*tmpstr != '.')) { tmpstr++; len++; } } if ((len + 2) <= bytesleft) { *vp++ = TAG_HOSTNAME; *vp++ = (byte) (len & 0xFF); bcopy(hp->hostname, vp, len); vp += len; } } if (hp->flags.domain) { len = strlen(hp->domain); if ((len + 2) <= bytesleft) { *vp++ = TAG_DOMAIN; *vp++ = (byte) (len & 0xFF); bcopy(hp->domain, vp, len); vp += len; } } if (hp->flags.generic) { insert_generic(hp->generic, &vp, &bytesleft); } if (hp1 != NULL){ /*alt*/ *vp++ = TAG_ALT_IP; /*alt*/ *vp++ = 4; /*alt*/ insert_u_long (hp1->iaddr.s_addr, &vp); /*alt*/ bytesleft -= 6; /*alt*/ if (hp1->flags.subnet_mask){ /*alt*/ *vp++ = TAG_ALT_SM; /*alt*/ *vp++ = 4; /*alt*/ insert_u_long (hp1->subnet_mask.s_addr, &vp); /*alt*/ bytesleft -= 6; /*alt*/ if (hp1->flags.gateway){ /*alt*/ insert_ip(TAG_ALT_GW, hp1->gateway, &vp, &bytesleft); } /*alt*/ } /*alt*/ } /*alt*/ if (bytesleft >= 1) { *vp = TAG_END; } if (debug > 2){ int i; /*alt*/ report(LOG_INFO,"RFC1048 vendor data ( bp_vend[64] ) \n"); for(i = 0; i < 64; i++) /*alt*/ fprintf(stderr,"%d.",bp->bp_vend[i]); /*alt*/ } /*alt*/ if (debug) { report(LOG_INFO, "sending RFC1048-style reply\n"); } } /* * 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); } /* * Insert a tag value, a length value, and a list of IP addresses into the * memory buffer indirectly pointed to by "dest", without going past "stop". * "ipst_prt" is a pointer to an ipaddr_t (i.e. a structure containing a * pointer to a linked list of IP addresses and a linkcount specifying how * many hosts are using this list). * * This is used to fill the vendor-specific area of a bootp packet in * conformance to RFC1048. */ void insert_ip(tag, iplist, dest, bytesleft) byte tag; struct in_addr_list *iplist; byte **dest; int *bytesleft; { register struct in_addr *addrptr; register unsigned addrcount; byte *d; if (iplist && (*bytesleft >= 6)) { d = *dest; /* Save pointer for later */ **dest = tag; (*dest) += 2; (*bytesleft) -= 2; /* Account for tag and length */ addrptr = iplist->addr; addrcount = iplist->addrcount; while ((*bytesleft >= 4) && (addrcount > 0)) { insert_u_long(addrptr->s_addr, dest); addrptr++; addrcount--; (*bytesleft) -= 4; /* Four bytes per address */ } d[1] = (byte) ((*dest - d - 2) & 0xFF); } } /* * Insert generic data into a bootp packet. The data is assumed to already * be in RFC1048 format. It is inserted using a first-fit algorithm which * attempts to insert as many tags as possible. Tags and data which are * too large to fit are skipped; any remaining tags are tried until they * have all been exhausted. */ void insert_generic(gendata, buff, bytesleft) struct bindata *gendata; byte **buff; int *bytesleft; { byte *srcptr; register int length, numbytes; if (gendata) { srcptr = gendata->data; length = gendata->length; while ((length > 0) && (*bytesleft > 0)) { switch (*srcptr) { case TAG_END: length = 0; /* Force an exit on next iteration */ break; case TAG_PAD: *(*buff)++ = *srcptr++; (*bytesleft)--; length--; break; default: numbytes = srcptr[1] + 2; if (*bytesleft >= numbytes) { bcopy(srcptr, *buff, numbytes); (*buff) += numbytes; (*bytesleft) -= numbytes; } srcptr += numbytes; length -= numbytes; break; } } } } /* * 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); } /* * Insert the unsigned long "value" into memory starting at the byte * pointed to by the byte pointer (*dest). (*dest) is updated to * point to the next available byte. * * Since it is desirable to internally store network addresses in network * byte order (in struct in_addr's), this routine expects longs to be * passed in network byte order. * * However, due to the nature of the main algorithm, the long must be in * host byte order, thus necessitating the use of ntohl() first. */ void insert_u_long(value, dest) unsigned long value; byte **dest; { register byte *temp; register int n; value = ntohl(value); /* Must use host byte order here */ temp = (*dest += 4); for (n = 4; n > 0; n--) { *--temp = (byte) (value & 0xFF); value >>= 8; } /* Final result is network byte order */ } /* * 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; { /* * Print the message */ switch (priority) { case LOG_ERR : if (debug) { fprintf(stderr, "BOOTPD: "); fprintf(stderr, fmt, p0, p1, p2, p3, p4); } break; case LOG_NOTICE : if (debug > 1) { fprintf(stderr, "BOOTPD: "); fprintf(stderr, fmt, p0, p1, p2, p3, p4); } break; case LOG_INFO : if (debug > 2) { fprintf(stderr, "BOOTPD: "); fprintf(stderr, fmt, p0, p1, p2, p3, p4); } break; } #ifdef _USE_SYSLOG syslog(priority, fmt, p0, p1, p2, p3, p4); #endif } #if defined(FEATURE) /* * Check if the current host is a secondary boot host * for the terminal that is requesting the boot. * The boot host is a secondary boot host only * if the 'T175' tag value is set to non-zero value, * otherwise the boot host is considered as a primary boot host. * If the 'T175' tag is not there at all, * the host behaves like a primary boot host. * The function returns total number of seconds that * the file server should wait before it responds to next boot request. */ int chk_secondary(hp) struct host *hp; { struct bindata *gendata; register int length, numbytes; byte *srcptr; if (!( gendata = hp->generic) ) return(0); srcptr = gendata->data; length = gendata->length; while (length > 0) { switch (*srcptr) { case TAG_END: return(0); case TAG_PRIMARY: srcptr += 2; /* this arithmetic is necessary since a decimal number is read in as an hex number */ return((*srcptr/16)*10 + (*srcptr%16)); case TAG_PAD: srcptr++; length--; break; default: numbytes = srcptr[1] + 2; srcptr += numbytes; length -= numbytes; break; } /* the end of switch */ } /* the end of while */ return(0); } #endif /* FEATURE */ byte *flip_addr(ha) byte * ha; { static char buffer[12]; static char b1[2]; char ibuf[12]; char b2[2]; static byte output[6]; int i; char translate(); for (i=0;i<6;i++) sprintf(ibuf+(2*i),"%.2x",*(ha+i)); printf("input buffer is %s\n",ibuf); for(i=0;i<12;i++,i++) { buffer[i]=translate(ibuf+i+1); buffer[i+1]=translate(ibuf+i); } printf("buffer is %s\n",buffer); for (i=0;i<6;i++) bit_byte(&buffer[(2*i)],&output[i]); return(output); } char translate(cval) char *cval; { switch(*cval) { case '0': case '6': case '9': case 'F': case 'f': return(*cval); ;; case '1': return('8'); ;; case '2': return('4'); ;; case '3': return('c'); ;; case '4': return('2'); ;; case '5': return('a'); ;; case '7': return('e'); ;; case '8': return('1'); ;; case 'A': case 'a': return('5'); ;; case 'B': case 'b': return('d'); ;; case 'C': case 'c': return('3'); ;; case 'D': case 'd': return('b'); ;; case 'E': case 'e': return('7'); ;; default: printf("Unknown value:%c\n",*cval); ;; } } PRIVATE int bit_byte(src, retbyte) char src[]; byte *retbyte; { int v = 0; char xdigit; *retbyte = 0; while((isxdigit(xdigit = src[v])) && ( v < 2 )) { if ( isdigit(xdigit)) xdigit -= '0'; else if ( islower(xdigit) ) xdigit = xdigit - 'a' +10 ; else if ( isupper(xdigit)) xdigit = xdigit - 'A' + 10 ; *retbyte = 16* (*retbyte) + xdigit ; v++; } if ( !v ) return -1; *retbyte &= 0xFF; return 0; }