diff options
| author | gwr <gwr@NetBSD.org> | 1994-05-24 15:19:56 +0000 |
|---|---|---|
| committer | gwr <gwr@NetBSD.org> | 1994-05-24 15:19:56 +0000 |
| commit | 438d67fe4e073750dfca3245e8a2bac11187b223 (patch) | |
| tree | 5f8eeb02ac62cac4202c3e627246b061b51ed1b4 | |
| parent | 0645f8963f663d296322b025a9e0c1dff377718f (diff) | |
Wholesale update to bootp-2.3.7 release.
47 files changed, 8239 insertions, 3472 deletions
diff --git a/libexec/bootpd/Announce-2.1 b/libexec/bootpd/Announce-2.1 new file mode 100644 index 00000000000..28ddca6a63e --- /dev/null +++ b/libexec/bootpd/Announce-2.1 @@ -0,0 +1,37 @@ +Subject: Updated RFC1048 BOOTP server now available + +Well, no surprise, bootpd 2.0 had a few bugs. A new improved version, +bootpd 2.1, is now available for anonymous FTP from lancaster.andrew.cmu.edu +(128.2.13.21). The new server can be found in pub/bootp.2.1.tar. + +Bug fixes and improvements in version 2.1 include: + +o The definition of "access to the bootfile" has been changed to require the + public read access bit to be set. This is required by tftpd(8), so the + server will not reply with a file which a client cannot obtain via TFTP. +o The RFC1084 bootfile size tag has been implemented. It allows either + automatic or manual specification of the bootfile size in 512-octet blocks. +o Generic tags now work as advertised. +o A subtle bug which caused strange parsing behavior under certain conditions + has been fixed. +o The RFC1048 vendor information now has the correct byte order on + little-endien machines such as the VAX. +o Failure to specify the bootfile home directory and/or default bootfile in + the configuration file no longer causes server crashes. The server now + makes a reasonably intelligent choice if this configuration information is + missing. This is documented in the man page. +o BOOTP requests from clients which already know their IP addresses no longer + cause server crashes. + + + +Please direct questions, comments, and bug reports to +Walt Wimer <ww0n@andrew.cmu.edu> or Drew Perkins <ddp@andrew.cmu.edu>. + + + +Good luck, + +Walt Wimer +Network Development +Carnegie Mellon University diff --git a/libexec/bootpd/Announce-2.2 b/libexec/bootpd/Announce-2.2 new file mode 100644 index 00000000000..34f2e3982a5 --- /dev/null +++ b/libexec/bootpd/Announce-2.2 @@ -0,0 +1,51 @@ + +New features in version 2.2 include: + +o A new "td" tag allows the specification of a "TFTP directory" for use + with so-called "secure" implementations of tftpd which chroot(2) to a + particular directory. + +o A new "sa" tag allows the explicit specification of the TFTP + "server address." Formerly, the 'siaddr' field of the BOOTREPLY was + always filled with the IP address of the BOOTP server. The "sa" tag + now allows the BOOTP server and the TFTP server to be two different + machines, if desired. + +o The server now automatically determines whether it is running as a + standalone program (e.g. invoked by hand from a shell) or as a child of + /etc/inetd. The -s option and a new -i option are provided to force + standalone or inetd mode if necessary. + +o When the vendor magic cookie is zero, BOOTP replies now default to the + RFC 1084 vendor format, rather than the old CMU format. This helps + interoperability with uncooperative BOOTP clients which want RFC 1084 + format but don't bother filling in the magic cookie properly to tell + the server... *sigh* (This makes the ":vm=rfc1048:" tag unnecessary + in most cases now. Oh, the "vm" tag now accepts "rfc1084" as well as + "rfc1048" -- they mean the same thing.) + +o Log messages now include the specific network type. For example, rather + than saying "request from hardware address ABCDEF012345", the message is + now "request from Ethernet address ABCEDF012345", or "request from + IEEE802 address 4000A1B2C3D4". + + +Bug fixes in this version include: + +o The automatic bootfile-size calculation now works correctly when the + file size is an exact multiple of 512 octets. It used to return a number + which was one 512-octet unit greater than necessary. + +o A bug in comparing subnet masks has been fixed. + +o A bug in calculating the size of the vendor information area when + inserting the hostname has been fixed. + + +Other changes: + +o The man page has been split into two man pages. One covers the server + itself (bootpd.8) and the other covers the configuration file format + (bootptab.5). + + diff --git a/libexec/bootpd/Announce-2.3 b/libexec/bootpd/Announce-2.3 new file mode 100644 index 00000000000..0eef6547752 --- /dev/null +++ b/libexec/bootpd/Announce-2.3 @@ -0,0 +1,58 @@ + +This is an enhanced version of the CMU BOOTP server which was derived +from the original BOOTP server created by Bill Croft at Stanford. +This version merges most of the enhancements and bug-fixes from the +NetBSD, Columbia, and other versions. + +New features: + + Added a simple BOOTP gateway program: bootpgw + Allow host name anywhere IP address is expected. + Automatically lookup the IP address when the name of a + bootptab entry is a valid hostname. + (Dummy entries names should start with '.') + Merged changes from NetBSD and Columbia versions. + Merged changes for Solaris-2.X and SVR4 systems. + Combined bootptest into the bootp release. + Merged tag 18 support (:ef=...:) from Jason Zions. + Use :ef=extension_file_name: and make the + extension files for all clients using bootpef. + Merged HP compatibility (:ra=...:) from David R Linn. + Allows you to override the reply address. + (i.e. send the reply to a broadcast address) + Add /etc/ethers support for NetBSD. + More systems support getether (Ultrix, OSF, NetBSD) + Added RFC 1533 tags 40,41,42 + :yd=<NIS domain>:ys=<NIS server>:nt=<NTP server>: + ConvOldTab.sh to convert old (1.1) bootptab to new format. + +Fixes included: + Fixed references to free host structures. + (used to cause core dump on Solaris) + Remove change that added null terminator to string options. + (this annoyed some clients...) + Add missing symbols to dump routine, fix order. + Works (again) with no -DSYSLOGD defined. + Fixed several more NULL references in readfile. + Added proper length checks to option insertions. + Fixed bootptest IP address printing. + Cleaned-up signed/unsigned and byteorder bugs. + Added SVR4/Streams support to getif and getether + Removed extra newlines in syslog messages. + Specify facility code when calling syslog(3) + When lookup_hwa fails, assume numeric HW address. + +Systems on which I have seen this code work: + SunOS 4.X (Solaris 1.X) + SunOS 5.X (Solaris 2.X) + System V/386 Rel. 4.0 + +Systems on which others say this code works: + CDC EP/IX (1.4.3, 2.1.1) + DEC Ultrix (4.2, 4.3) + NetBSD (Current-1/94) + OSF/1 (DEC Alpha CPU) + +Gordon W. Ross Mercury Computer Systems +gwr@mc.com 199 Riverneck Road +508-256-1300 Chelmsford, MA 01824-2820 diff --git a/libexec/bootpd/ConvOldTab.sh b/libexec/bootpd/ConvOldTab.sh new file mode 100644 index 00000000000..00683f0c049 --- /dev/null +++ b/libexec/bootpd/ConvOldTab.sh @@ -0,0 +1,141 @@ +#!/bin/sh +# convert_bootptab Jeroen.Scheerder@let.ruu.nl 02/25/94 +# This script can be used to convert bootptab files in old format +# to new (termcap-like) bootptab files +# +# The old format - real entries are commented out by '###' +# +# Old-style bootp files consist of two sections. +# The first section has two entries: +# First, a line that specifies the home directory +# (where boot file paths are relative to) + +###/tftpboot + +# The next non-empty non-comment line specifies the default bootfile + +###no-file + +# End of first section - indicated by '%%' at the start of the line + +###%% + +# The remainder of this file contains one line per client +# interface with the information shown by the table headings +# below. The host name is also tried as a suffix for the +# bootfile when searching the home directory (that is, +# bootfile.host) +# +# Note that htype is always 1, indicating the hardware type Ethernet. +# Conversion therefore always yields ':ha=ether:'. +# +# host htype haddr iaddr bootfile +# + +###somehost 1 00:0b:ad:01:de:ad 128.128.128.128 dummy + +# That's all for the description of the old format. +# For the new-and-improved format, see bootptab(5). + +set -u$DX + +case $# +in 2 ) OLDTAB=$1 ; NEWTAB=$2 ;; + * ) echo "Usage: `basename $0` <Input> <Output>" + exit 1 +esac + +if [ ! -r $OLDTAB ] +then + echo "`basename $0`: $OLDTAB does not exist or is unreadable." + exit 1 +fi + +if touch $NEWTAB 2> /dev/null +then + : +else + echo "`basename $0`: cannot write to $NEWTAB." + exit 1 +fi + + +cat << END_OF_HEADER >> $NEWTAB +# /etc/bootptab: database for bootp server (/etc/bootpd) +# This file was generated automagically + +# Blank lines and lines beginning with '#' are ignored. +# +# Legend: (see bootptab.5) +# first field -- hostname (not indented) +# bf -- bootfile +# bs -- bootfile size in 512-octet blocks +# cs -- cookie servers +# df -- dump file name +# dn -- domain name +# ds -- domain name servers +# ef -- extension file +# gw -- gateways +# ha -- hardware address +# hd -- home directory for bootfiles +# hn -- host name set for client +# ht -- hardware type +# im -- impress servers +# ip -- host IP address +# lg -- log servers +# lp -- LPR servers +# ns -- IEN-116 name servers +# ra -- reply address +# rl -- resource location protocol servers +# rp -- root path +# sa -- boot server address +# sm -- subnet mask +# sw -- swap server +# tc -- template host (points to similar host entry) +# td -- TFTP directory +# to -- time offset (seconds) +# ts -- time servers +# vm -- vendor magic number +# Tn -- generic option tag n +# +# Be careful about including backslashes where they're needed. Weird (bad) +# things can happen when a backslash is omitted where one is intended. +# Also, note that generic option data must be either a string or a +# sequence of bytes where each byte is a two-digit hex value. + +# First, we define a global entry which specifies the stuff every host uses. +# (Host name lookups are relative to the domain: your.domain.name) + +END_OF_HEADER + +# Fix up HW addresses in aa:bb:cc:dd:ee:ff and aa-bb-cc-dd-ee-ff style first +# Then awk our stuff together +sed -e 's/[:-]//g' < $OLDTAB | \ +nawk 'BEGIN { PART = 0 ; FIELD=0 ; BOOTPATH="unset" ; BOOTFILE="unset" } + /^%%/ { + PART = 1 + printf ".default:\\\n\t:ht=ether:\\\n\t:hn:\\\n\t:dn=your.domain.name:\\\n\t:ds=your,dns,servers:\\\n\t:sm=255.255.0.0:\\\n\t:hd=%s:\\\n\t:rp=%s:\\\n\t:td=%s:\\\n\t:bf=%s:\\\n\t:to=auto:\n\n", BOOTPATH, BOOTPATH, BOOTPATH, BOOTFILE + next + } + /^$/ { next } + /^#/ { next } + { + if ( PART == 0 && FIELD < 2 ) + { + if ( FIELD == 0 ) BOOTPATH=$1 + if ( FIELD == 1 ) BOOTFILE=$1 + FIELD++ + } + } + { + if ( PART == 1 ) + { + HOST=$1 + HA=$3 + IP=$4 + BF=$5 + printf "%s:\\\n\t:tc=.default:\\\n\t:ha=0x%s:\\\n\t:ip=%s:\\\n\t:bf=%s:\n", HOST, HA, IP, BF + } + }' >> $NEWTAB + +exit 0 diff --git a/libexec/bootpd/Installation b/libexec/bootpd/Installation new file mode 100644 index 00000000000..466cabce0cd --- /dev/null +++ b/libexec/bootpd/Installation @@ -0,0 +1,29 @@ + +Installation instructions for SunOS + +Compile the executable: +For SunOS 4.X: + make sunos4 +For SunOS 5.X: (Solaris) + make sunos5 + +Install the executables: + + make install + +Edit (or create) the bootptab: +(See bootptab.sample and bootptab.5 manual entry) + edit /etc/bootptab + +Edit /etc/services to add these two lines: +bootps 67/udp bootp # BOOTP Server +bootpc 68/udp # BOOTP Client + +Edit /etc/inetd.conf to add the line: +bootp dgram udp wait root /usr/etc/bootpd bootpd -i + +If you compiled report.c with LOG_LOCAL2 (defined in the Makefile) +then you may want to capture syslog messages from BOOTP by changing +your syslog.conf file. (See the sample syslog.conf file here). +Test the change with: logger -t test -p local2.info "message" + diff --git a/libexec/bootpd/Makefile b/libexec/bootpd/Makefile index de5eeaa903c..00f228b7fca 100644 --- a/libexec/bootpd/Makefile +++ b/libexec/bootpd/Makefile @@ -1,10 +1,50 @@ +# The next few lines may be uncommented and changed to alter the default +# filenames bootpd uses for its configuration and dump files. +#CONFFILE=-DCONFIG_FILE=\"/usr/etc/bootptab\" +DUMPFILE=-DDUMPTAB_FILE=\"/var/run/bootpd.dump\" +FILESPECS=${CONFFILE} ${DUMPFILE} + # Remove the -DVEND_CMU if you don't wish to support the "CMU vendor format" # in addition to the RFC1048 format. PROG= bootpd -CFLAGS+=-DSYSLOG -DDEBUG -DVEND_CMU -SRCS= bootpd.c readfile.c hash.c Version.c +# Note: PRIVATE=static is defined by default in bootpd.h +CFLAGS+=-DETC_ETHERS -DSYSLOG -DDEBUG -DVEND_CMU ${FILESPECS} +SRCS= bootpd.c dovend.c readfile.c hash.c dumptab.c \ + lookup.c getif.c hwaddr.c report.c tzone.c MAN5= bootptab.0 MAN8= bootpd.0 +MLINKS= bootpd.8 bootpgw.8 + +BINDIR=/usr/sbin + .include <bsd.prog.mk> + +# Additional targets (bootpd is done automatically) +all: bootpef bootpef.0 bootpgw bootptest bootptest.0 +CLEANFILES+= bootpef bootpgw bootptest + +bootpef: bootpef.o dovend.o readfile.o hash.o dumptab.o \ + lookup.o hwaddr.o report.o tzone.o + ${CC} -o ${.TARGET} ${CFLAGS} ${LDFLAGS} ${.ALLSRC} ${LIBS} + +bootpgw: bootpgw.o getif.o hwaddr.o report.o + ${CC} -o ${.TARGET} ${CFLAGS} ${LDFLAGS} ${.ALLSRC} ${LIBS} + +print-bootp.o : print-bootp.c + ${CC} ${CFLAGS} -DBOOTPTEST -c ${.IMPSRC} + +bootptest: bootptest.o print-bootp.o getif.o getether.o report.o + ${CC} -o ${.TARGET} ${CFLAGS} ${LDFLAGS} ${.ALLSRC} ${LIBS} + +bootpef.0: bootpef.8 +bootptest.0: bootptest.8 + +install: + install ${COPY} ${STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + bootpef ${DESTDIR}${BINDIR} + install ${COPY} ${STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + bootpgw ${DESTDIR}${BINDIR} + ${MINSTALL} bootpef.0 ${DESTDIR}${MANDIR}8${MANSUBDIR} + diff --git a/libexec/bootpd/Problems b/libexec/bootpd/Problems new file mode 100644 index 00000000000..9478676eca4 --- /dev/null +++ b/libexec/bootpd/Problems @@ -0,0 +1,47 @@ + +Common problems and ways to work around them: + +Bootpd complains that it "can not get IP addr for HOSTNAME" + + If the entry is a "dummy" (not a real host) used only for + reference by other entries, put '.' in front of the name. + + If the entry is for a real client and the IP address for + the client can not be found using gethostbyname(), specify + the IP address for the client using numeric form. + +Bootpd takes a long time to finish parsing the bootptab file: + + Excessive startup time is usually caused by waiting for + timeouts on failed DNS lookup operations. If this is the + problem, find the client names for which DNS lookup fails + and change the bootptab to specify the IP addresses for + those clients using numeric form. + + When bootptab entries do not specify an ip address, bootpd + attempts to lookup the tagname as a host name to find the + IP address. To suppress this default action, either make + the entry a "dummy" or specify its IP numeric address. + + If your DNS lookups work but are just slow, consider either + running bootpd on the same machine as the DNS server or + running a caching DNS server on the host running bootpd. + +My huge bootptab file causes startup time to be so long that clients +give up waiting for a reply. + + Truly huge bootptab files make "inetd" mode impractical. + Start bootpd in "standalone" mode when the server boots. + + Another possibility is to run one bootpd on each network + segment so each one can have a smaller bootptab. Only one + instance of bootpd may run on one server, so you would need + to use a different server for each network segment. + +My bootp clients are given responses with a boot file name that is +not a fully specified path. + + Make sure the TFTP directory or home directory tags are set: + :td=/tftpboot: (or) + :hd=/usr/boot: (for example) + diff --git a/libexec/bootpd/README b/libexec/bootpd/README index f99f2efc341..88dd6f8fc48 100644 --- a/libexec/bootpd/README +++ b/libexec/bootpd/README @@ -1,4 +1,19 @@ -This is a port of bootpd (CMU version 2.2a) to NetBSD-current. + +This is an enhanced version of the CMU BOOTP server which was derived +from the original BOOTP server created by Bill Croft at Stanford. +This version merges all the enhancements and bug-fixes from the +NetBSD, Columbia, and other versions. + +If you have problems or questions about this version, send email to: + gwr@mc.com (Gordon W. Ross) + +For general questions about BOOTP, send email to the list: + bootp@andrew.cmu.edu +You can subscribe to this mailing list by sending mail to: + bootp-request@andrew.cmu.edu +(The body of the message should contain: "Add <your-address>") + +[ From the NetBSD README file: ] BOOTPD is a useful adjunct to the nfs diskless boot EPROM code. @@ -7,39 +22,7 @@ are to use RARP protocol, or BOOTP protocol. BOOTP is more flexible; it allows additional items of information to be returned to the booting client; it also supports booting across gateways. -There are two parts to this package: - -- The bootpd sources to be compiled and installed. - -- A patch to etc/services and etc/inetd.conf to define the bootpd -network service names and to define the bootpd task to inetd. These -items are in the PATCH file. - -If you have problems or questions about this port, send me email at -mckim@lerc.nasa.gov - -- Jim McKim - -enclosed below is the original README: - - -This directory contains the source for the CMU BOOTP server. The files -include: - -README This file -bootpd.8 The manual page -bootp.h The protocol header file -bootpd.h The server header file -bootpd.c The main server module -readfile.c The configuration file-reading routines -hash.h The hash table header file -hash.c The hash table module -Version.c The version module -newvers.sh A shell script to generate new version numbers -Makefile The makefile -bootptab An example database file for the server -gmods Text describing optional gateway modifications - +[ From the CMU README file: ] Notes: 1) BOOTP was originally designed and implemented by Bill Croft at Stanford. @@ -53,13 +36,16 @@ Notes: should compile without the -DSYSLOG switch. 3) You must update your /etc/services file to contain the following two lines: - bootps 67/udp # bootp server - bootpc 68/udp # bootp client + bootps 67/udp bootp # BOOTP Server + bootpc 68/udp # BOOTP Client -4) The code and the bootptab should be reasonably well commented. If you - have any questions, just let us know. +4) Edit the bootptab. It has some explanitory comments, and there + is a manual entry describing its format (bootptab.5) + If you have any questions, just let us know. Construction: + [ See the file Installation which is more up-to-date. -gwr ] + Make sure all of the files exist first. If anything is missing, please contact either Walt Wimer or Drew Perkins by E-mail or phone. Addresses and phone numbers are listed below. @@ -74,7 +60,9 @@ Construction: Type 'make install'. This should put all of the files in the right place. Edit your /etc/rc.local or /etc/inetd.conf file to start up bootpd upon - reboot. + reboot. The following is a sample /etc/inetd.conf entry: + # BOOTP server + bootps dgram udp wait root /usr/etc/bootpd bootpd -i Care and feeding: If you change the interface cards on your host or add new hosts you will @@ -100,3 +88,46 @@ ww0n@andrew.cmu.edu ddp@andrew.cmu.edu 4910 Forbes Ave Pittsburgh, PA 15213 +[ Contents description by file: ] + +Announce* Text of release announcements +Installation Instructions for building and installing +Makefile* for "make" +README This file +bootp.h The protocol header file +bootpd.8 Manual page for bootpd, boopgw +bootpd.c BOOTP server main module +bootpd.h header for above (and others) +bootpef.8 Manual page for bootpef +bootpef.c BOOTP extension file compiler +bootpgw.c BOOTP gateway main module +bootptab.5 A manual describing the bootptab format +bootptab.cmu A sample database file for the server +bootptab.mcs Another sample from <gwr@mc.com> +bootptest.8 Manual page for bootptest +bootptest.c BOOTP test program (fake client) +bootptest.h header for above +dovend.c Vendor Option builder (for bootpd, bootpef) +dovend.h header for above +dumptab.c Implements debugging dump for bootpd +getether.c For bootptest (not used yet) +getif.c Get network interface info. +getif.h header for above +hash.c The hash table module +hash.h header for above +hwaddr.c Hardware address support +hwaddr.h header for above +lookup.c Internet Protocol address lookup +lookup.h header for above +patchlevel.h Holds version numbers +print-bootp.c Prints BOOTP packets (taken from BSD tcpdump) +readfile.c The configuration file-reading routines +readfile.h header for above +report.c Does syslog-style messages +report.h header for above +strerror.c Library errno-to-string (for systems lacking it) +syslog.conf Sample config file for syslogd(8) +syslog.h For systems that lack syslog(3) +try*.c Test programs (for debugging) +tzone.c Get timezone offset +tzone.h header for above diff --git a/libexec/bootpd/bootp.h b/libexec/bootpd/bootp.h index fa09299d2c7..912d1f6c02f 100644 --- a/libexec/bootpd/bootp.h +++ b/libexec/bootpd/bootp.h @@ -1,5 +1,3 @@ -#ifndef _BLURB_ -#define _BLURB_ /************************************************************************ Copyright 1988, 1991 by Carnegie Mellon University @@ -21,12 +19,11 @@ 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_ */ /* * Bootstrap Protocol (BOOTP). RFC951 and RFC1395. * - * $Header: /cvsroot/src/libexec/bootpd/Attic/bootp.h,v 1.1 1994/04/18 05:15:52 glass Exp $ + * $Header: /cvsroot/src/libexec/bootpd/Attic/bootp.h,v 1.2 1994/05/24 15:20:08 gwr Exp $ * * * This file specifies the "implementation-independent" BOOTP protocol @@ -34,17 +31,20 @@ SOFTWARE. * */ +#include "bptypes.h" /* for int32, u_int32 */ + #define BP_CHADDR_LEN 16 #define BP_SNAME_LEN 64 #define BP_FILE_LEN 128 #define BP_VEND_LEN 64 +#define BP_MINPKTSZ 300 /* to check sizeof(struct bootp) */ struct bootp { unsigned char bp_op; /* packet opcode type */ unsigned char bp_htype; /* hardware addr type */ unsigned char bp_hlen; /* hardware addr length */ unsigned char bp_hops; /* gateway hops */ - unsigned long bp_xid; /* transaction ID */ + unsigned int32 bp_xid; /* transaction ID */ unsigned short bp_secs; /* seconds since boot began */ unsigned short bp_unused; struct in_addr bp_ciaddr; /* client IP address */ @@ -52,9 +52,10 @@ struct bootp { struct in_addr bp_siaddr; /* server IP address */ struct in_addr bp_giaddr; /* gateway IP address */ unsigned char bp_chaddr[BP_CHADDR_LEN]; /* client hardware address */ - unsigned char bp_sname[BP_SNAME_LEN]; /* server host name */ - unsigned char bp_file[BP_FILE_LEN]; /* boot file name */ + char bp_sname[BP_SNAME_LEN]; /* server host name */ + char bp_file[BP_FILE_LEN]; /* boot file name */ unsigned char bp_vend[BP_VEND_LEN]; /* vendor-specific area */ + /* note that bp_vend can be longer, extending to end of packet. */ }; /* @@ -69,7 +70,6 @@ struct bootp { /* * Hardware types from Assigned Numbers RFC. */ -#define HTYPE_DIRECT 0 /* non-standard */ #define HTYPE_ETHERNET 1 #define HTYPE_EXP_ETHERNET 2 #define HTYPE_AX25 3 @@ -91,10 +91,11 @@ struct bootp { /* - * RFC1395 tag values used to specify what information is being supplied in - * the vendor field of the packet. + * Tag values used to specify what information is being supplied in + * the vendor (options) data area of the packet. */ - +/* RFC 1048 */ +#define TAG_END ((unsigned char) 255) #define TAG_PAD ((unsigned char) 0) #define TAG_SUBNET_MASK ((unsigned char) 1) #define TAG_TIME_OFFSET ((unsigned char) 2) @@ -107,14 +108,20 @@ struct bootp { #define TAG_LPR_SERVER ((unsigned char) 9) #define TAG_IMPRESS_SERVER ((unsigned char) 10) #define TAG_RLP_SERVER ((unsigned char) 11) -#define TAG_HOSTNAME ((unsigned char) 12) -#define TAG_BOOTSIZE ((unsigned char) 13) -#define TAG_DUMPFILE ((unsigned char) 14) -#define TAG_DOMAINNAME ((unsigned char) 15) -#define TAG_SWAPSERVER ((unsigned char) 16) -#define TAG_ROOTPATH ((unsigned char) 17) -#define TAG_END ((unsigned char) 255) - +#define TAG_HOST_NAME ((unsigned char) 12) +#define TAG_BOOT_SIZE ((unsigned char) 13) +/* RFC 1395 */ +#define TAG_DUMP_FILE ((unsigned char) 14) +#define TAG_DOMAIN_NAME ((unsigned char) 15) +#define TAG_SWAP_SERVER ((unsigned char) 16) +#define TAG_ROOT_PATH ((unsigned char) 17) +/* RFC 1497 */ +#define TAG_EXTEN_FILE ((unsigned char) 18) +/* RFC 1533 */ +#define TAG_NIS_DOMAIN ((unsigned char) 40) +#define TAG_NIS_SERVER ((unsigned char) 41) +#define TAG_NTP_SERVER ((unsigned char) 42) +/* XXX - Add new tags here */ /* @@ -122,14 +129,14 @@ struct bootp { */ struct cmu_vend { - unsigned char v_magic[4]; /* magic number */ - unsigned long v_flags; /* flags/opcodes, etc. */ + char v_magic[4]; /* magic number */ + unsigned int32 v_flags; /* flags/opcodes, etc. */ struct in_addr v_smask; /* Subnet mask */ struct in_addr v_dgate; /* Default gateway */ struct in_addr v_dns1, v_dns2; /* Domain name servers */ struct in_addr v_ins1, v_ins2; /* IEN-116 name servers */ struct in_addr v_ts1, v_ts2; /* Time servers */ - unsigned char v_unused[25]; /* currently unused */ + int32 v_unused[6]; /* currently unused */ }; diff --git a/libexec/bootpd/bootpd.8 b/libexec/bootpd/bootpd.8 index aeaa3f2a4d0..cca20b94570 100644 --- a/libexec/bootpd/bootpd.8 +++ b/libexec/bootpd/bootpd.8 @@ -1,14 +1,12 @@ .\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University .\" -.\" $Header: /cvsroot/src/libexec/bootpd/Attic/bootpd.8,v 1.1 1994/04/18 05:15:53 glass Exp $ +.\" $Header: /cvsroot/src/libexec/bootpd/Attic/bootpd.8,v 1.2 1994/05/24 15:20:09 gwr Exp $ .\" -.TH BOOTPD 8 "November 11, 1991" "Carnegie Mellon University" -.UC 6 - +.TH BOOTPD 8 "November 06, 1993" "Carnegie Mellon University" .SH NAME -bootpd \- Internet Boot Protocol server +bootpd, bootpgw \- Internet Boot Protocol server/gateway .SH SYNOPSIS -.B /usr/libexec/bootpd +.B bootpd [ .B \-i .B \-s @@ -16,149 +14,292 @@ bootpd \- Internet Boot Protocol server timeout .B \-d level +.B \-c +chdir\-path ] [ -.I configfile +.I bootptab [ .I dumpfile ] ] +.br +.B bootpgw +[ +.B \-i +.B \-s +.B \-t +timeout +.B \-d +level +] server .SH DESCRIPTION .I Bootpd -implements an Internet Bootstrap Protocol server as defined in RFC951, -RFC1048, and RFC1084. It is normally run by -.I /usr/sbin/inetd -by including the following line in the file -.IR /etc/inetd.conf : +implements an Internet Bootstrap Protocol (BOOTP) server as defined in +RFC951, RFC1532, and RFC1533. +.I Bootpgw +implements a simple BOOTP gateway which can be used to forward +requests and responses between clients on one subnet and a +BOOTP server (i.e. +.IR bootpd ) +on another subnet. While either +.I bootpd +or +.I bootpgw +will forward BOOTREPLY packets, only +.I bootpgw +will forward BOOTREQUEST packets. .PP +One host on each network segment is normally configured to run either +.I bootpd +or +.I bootpgw +from +.I inetd +by including one of the following lines in the file +.IR /etc/inetd.conf : +.IP +bootps dgram udp wait root /etc/bootpd bootpd bootptab .br - bootps dgram udp wait root /usr/libexec/bootpd bootpd +bootps dgram udp wait root /etc/bootpgw bootpgw server .PP This mode of operation is referred to as "inetd mode" and causes .I bootpd -to be started only when a boot request arrives. If -.I bootpd -does not receive another boot request within fifteen minutes of the last one +(or +.IR bootpgw ) +to be started only when a boot request arrives. If it does not +receive another packet within fifteen minutes of the last one it received, it will exit to conserve system resources. The .B \-t -switch may be used to specify a different timeout value in minutes (e.g. --t 20). A timeout value of zero means forever. +option controls this timeout (see OPTIONS). .PP It is also possible to run .I bootpd +(or +.IR bootpgw ) in "standalone mode" (without .IR inetd ) by simply invoking it from a shell like any other regular command. -Standalone mode is probably the desired mode of operation for large network -installations with many BOOTP clients. (The greater the number of clients -listed in the configuration database, -.IR /etc/bootptab , -the longer it takes -.I bootpd -to start up. To ensure quick response to clients in a large network, -it is better to start +Standalone mode is particularly useful when .I bootpd -once during the server machine's bootup sequence. This can be done by invoking +is used with a large configuration database, where the start up +delay might otherwise prevent timely response to client requests. +(Automatic start up in standalone mode can be done by invoking .I bootpd from within .IR /etc/rc.local , for example.) -. -In standalone mode, the -.B \-t -switch has no effect since -.I bootpd -will never exit. +Standalone mode is less useful for +.I bootgw +which +has very little start up delay because +it does not read a configuration file. .PP -The server automatically detects whether it was invoked from inetd or from a -shell and automatically selects the appropriate mode. For compatibility with -older versions of -.IR bootpd , -the +Either program automatically detects whether it was invoked from inetd +or from a shell and automatically selects the appropriate mode. +The .B \-s -switch may be used to force standalone operation. Similarly, the +or .B \-i -switch may be used to force the inetd mode of operation. Normally, though, -it should be unnecessary to use these switches. -.PP -The -.B \-d -switch takes a numeric parameter which sets the level of debugging output. +option may be used to force standalone or inetd mode respectively +(see OPTIONS). +.SH OPTIONS +.TP +.BI \-t \ timeout +Specifies the +.I timeout +value (in minutes) that a +.I bootpd +or +.I bootpgw +process will wait for a BOOTP packet before exiting. +If no packets are recieved for +.I timeout +seconds, then the program will exit. +A timeout value of zero means "run forever". +In standalone mode, this option is forced to zero. +.TP +.BI \-d \ debug\-level +Sets the +.I debug\-level +variable that controls the amount of debugging messages generated. For example, -d4 or -d 4 will set the debugging level to 4. For compatibility with older versions of .IR bootpd , omitting the numeric parameter (i.e. just -d) will simply increment the debug level by one. +.TP +.BI \-c \ chdir\-path +Sets the current directory used by +.I bootpd +while checking the existence and size of client boot files. This is +useful when client boot files are specified as relative pathnames, and +.I bootpd +needs to use the same current directory as the TFTP server +(typically /tftpboot). This option is not recoginzed by +.IR bootpgw . +.TP +.B \-i +Force inetd mode. This option is obsolete, but remains for +compatibility with older versions of +.IR bootpd . +.TP +.B \-s +Force standalone mode. This option is obsolete, but remains for +compatibility with older versions of +.IR bootpd . +.TP +.I bootptab +Specifies the name of the configuration file from which +.I bootpd +loads its database of known clients and client options +.RI ( bootpd +only). +.TP +.I dumpfile +Specifies the name of the file that +.I bootpd +will dump its internal database into when it receives a +SIGUSR1 signal +.RI ( bootpd +only). This option is only recognized if +.I bootpd +was compiled with the -DDEBUG flag. +.TP +.I server +Specifies the name of a BOOTP server to which +.I bootpgw +will forward all BOOTREQUEST packets it receives +.RI ( bootpgw +only). +.SH OPERATION .PP -Upon startup, +Both .I bootpd -first reads its configuration file, -.IR /etc/bootptab , -and then begins listening for BOOTREQUEST packets. +and +.I bootpgw +operate similarly in that both listen for any packets sent to the +.I bootps +port, and both simply forward any BOOTREPLY packets. +They differ in their handling of BOOTREQUEST packets. .PP -.I Bootpd -looks in -.I /etc/services -to find the UDP port numbers it should use. Two entries are extracted: -.BR bootps , -the bootp server listening port, and -.BR bootpc , -the destination port used to reply to clients. If the port numbers cannot -be determined this way, -.I bootpd -defaults to using 67 for the server and 68 for the client. +When +.I bootpgw +is started, it determines the address of a BOOTP server +whose name is provided as a command line parameter. When +.I bootpgw +receives a BOOTREQUEST packet, it sets the "gateway address" +and "hop count" fields in the packet and forwards the packet +to the BOOTP server at the address determined earlier. +Requests are forwarded only if they indicate that +the client has been waiting for at least three seconds. .PP -.I Bootpd -completely reloads its configuration file when it receives a hangup signal, -SIGHUP, or when it receives a BOOTREQUEST packet and detects that the file -has been updated. If +When +.I bootpd +is started it reads a configuration file, (normally +.IR /etc/bootptab ) +that initializes the internal database of known clients and client +options. This internal database is reloaded +from the configuration file when +.I bootpd +receives a hangup signal (SIGHUP) or when it discovers that the +configuration file has changed. +.PP +When +.I bootpd +receives a BOOTREQUEST packet, it +.\" checks the modification time of the +.\" configuration file and reloads the database if necessary. Then it +looks for a database entry matching the client request. +If the client is known, .I bootpd -is compiled with the -DDEBUG option, receipt of a SIGUSR1 signal causes it -to dump its memory-resident database to -the file +composes a BOOTREPLY packet using the database entry found above, +and sends the reply to the client (possibly using a gateway). +If the client is unknown, the request is discarded +(with a notice if debug > 0). +.PP +If +.I bootpd +is compiled with the -DDEBUG option, receipt of a SIGUSR1 signal causes +it to dump its internal database to the file .I /etc/bootpd.dump -or the command-line-specified dumpfile. - +or the dumpfile specified as a command line parameter. +.PP +During initialization, both programs +determine the UDP port numbers to be used by calling +.I getservbyname +(which nomally uses +.IR /etc/services). +Two service names (and port numbers) are used: +.IP +bootps \- BOOTP Server listening port +.br +bootpc \- BOOTP Client destination port +.LP +If the port numbers cannot +be determined using +.I getservbyname +then the values default to boopts=67 and bootpc=68. .SH FILES +.TP 20 /etc/bootptab -.br +Database file read by +.IR bootpd . +.TP /etc/bootpd.dump -.br +Debugging dump file created by +.IR bootpd . +.TP /etc/services +Internet service numbers. +.TP +/tftpboot +Current directory typically used by the TFTP server and +.IR bootpd . .SH BUGS Individual host entries must not exceed 1024 characters. -.SH HISTORY +.SH CREDITS +.PP +This distribution is currently maintained by +Walter L. Wimer <walt+@cmu.edu>. +.PP +The original BOOTP server was created by +Bill Croft at Stanford University in January 1986. +.PP +The current version of +.I bootpd +is primarily the work of David Kovar, +Drew D. Perkins, and Walter L. Wimer, +at Carnegie Mellon University. .TP -22-Jan-86 Bill Croft at Stanford University +Enhancements and bug\-fixes have been contributed by: +(in alphabetical order) .br -Created. - -.TP -30-Jul-86 David Kovar at Carnegie Mellon University +Danny Backx <db@sunbim.be> .br -Modified to CMU specifications. - -.TP -24-Jul-87 Drew D. Perkins at Carnegie Mellon University +John Brezak <brezak@ch.hp.com> .br -Modified to use syslog. Added debugging dumps. Other bug fixes. - -.TP -17-Jul-88 Walter L. Wimer at Carnegie Mellon University +Frank da Cruz <fdc@cc.columbia.edu> .br -Added vendor information to conform to RFC1048. -Adopted termcap-like file format to allow variable data. - -.TP -11-Nov-91 Walter L. Wimer at Carnegie Mellon University +David R. Linn <drl@vuse.vanderbilt.edu> .br -Added TFTP directory- and server-specification features. Added automatic -detection of inetd/standalone mode, making -s switch no longer necessary. -Other minor improvements and bug fixes. - -.SH "SEE ALSO" +Jim McKim <mckim@lerc.nasa.gov> .br -bootptab(5), inetd(8), tftpd(8), +Gordon W. Ross <gwr@mc.com> .br -DARPA Internet Request For Comments RFC951, RFC1048, RFC1084, Assigned Numbers +Jason Zions <jazz@hal.com> +.SH "SEE ALSO" +.LP +bootptab(5), inetd(8), tftpd(8) +.LP +DARPA Internet Request For Comments: +.TP 10 +RFC951 +Bootstrap Protocol +.TP 10 +RFC1532 +Clarifications and Extensions for the Bootstrap Protocol +.TP 10 +RFC1533 +DHCP Options and BOOTP Vendor Extensions diff --git a/libexec/bootpd/bootpd.c b/libexec/bootpd/bootpd.c index a264c284cd2..35f9c700251 100644 --- a/libexec/bootpd/bootpd.c +++ b/libexec/bootpd/bootpd.c @@ -1,5 +1,3 @@ -#ifndef _BLURB_ -#define _BLURB_ /************************************************************************ Copyright 1988, 1991 by Carnegie Mellon University @@ -21,54 +19,29 @@ 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: /cvsroot/src/libexec/bootpd/Attic/bootpd.c,v 1.1 1994/04/18 05:15:54 glass Exp $"; +static char rcsid[] = "$Id: bootpd.c,v 1.2 1994/05/24 15:20:10 gwr Exp $"; #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 RFC 1395 for option tags 14-17. * 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. - * + * See ./Changes * * 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. . . . - * + * See ./ToDo */ - #include <sys/types.h> #include <sys/param.h> #include <sys/socket.h> @@ -76,32 +49,60 @@ static char rcsid[] = "$Header: /cvsroot/src/libexec/bootpd/Attic/bootpd.c,v 1.1 #include <sys/file.h> #include <sys/time.h> #include <sys/stat.h> + #include <net/if.h> -#ifdef SUNOS40 -#include <sys/sockio.h> -#include <net/if_arp.h> -#endif #include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <stdlib.h> #include <signal.h> #include <stdio.h> -#include <strings.h> +#include <string.h> #include <errno.h> #include <ctype.h> #include <netdb.h> #include <syslog.h> +#include <assert.h> + +#ifdef NO_SETSID +# include <fcntl.h> /* for O_RDONLY, etc */ +#endif + +#ifdef SVR4 +/* Using sigset() avoids the need to re-arm each time. */ +#define signal sigset +#endif + +#ifndef USE_BFUNCS +# include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +# 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) +#endif #include "bootp.h" #include "hash.h" +#include "hwaddr.h" #include "bootpd.h" +#include "dovend.h" +#include "getif.h" +#include "readfile.h" +#include "report.h" +#include "tzone.h" +#include "patchlevel.h" -#define HASHTABLESIZE 257 /* Hash table size (prime) */ -#define DEFAULT_TIMEOUT 15L /* Default timeout in minutes */ +/* Local definitions: */ +#define MAXPKT (3*512) /* Maximum packet size */ #ifndef CONFIG_FILE #define CONFIG_FILE "/etc/bootptab" #endif -#ifndef DUMP_FILE -#define DUMP_FILE "/etc/bootpd.dump" +#ifndef DUMPTAB_FILE +#define DUMPTAB_FILE "/tmp/bootpd.dump" #endif @@ -110,28 +111,26 @@ static char rcsid[] = "$Header: /cvsroot/src/libexec/bootpd/Attic/bootpd.c,v 1.1 * Externals, forward declarations, and global variables */ -extern char Version[]; -#ifndef __NetBSD__ -extern char *sys_errlist[]; +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () #endif -extern int errno, sys_nerr; -void usage(); -void insert_u_long(); -void dump_host(); -void list_ipaddresses(); +extern void dumptab P((char *)); + +PRIVATE void catcher P((int)); +PRIVATE int chk_access P((char *, int32 *)); #ifdef VEND_CMU -void dovend_cmu(); +PRIVATE void dovend_cmu P((struct bootp *, struct host *)); #endif -void dovend_rfc1048(); -boolean hwlookcmp(); -boolean iplookcmp(); -void insert_generic(); -void insert_ip(); -int dumptab(); -int chk_access(); -void report(); -char *get_errmsg(); +PRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32)); +PRIVATE void handle_reply P((void)); +PRIVATE void handle_request P((void)); +PRIVATE void sendreply P((int forward, int32 dest_override)); +PRIVATE void usage P((void)); + +#undef P /* * IP port numbers for client and server obtained from /etc/services @@ -144,417 +143,411 @@ u_short bootps_port, bootpc_port; * Internet socket and interface config structures */ -struct sockaddr_in s_in; -struct sockaddr_in from; /* Packet source */ -struct ifreq ifreq[10]; /* Holds interface configuration */ -struct ifconf ifconf; /* Int. config ioctl block (pnts to ifreq) */ -struct arpreq arpreq; /* Arp request ioctl block */ +struct sockaddr_in bind_addr; /* Listening */ +struct sockaddr_in recv_addr; /* Packet source */ +struct sockaddr_in send_addr; /* destination */ /* - * General + * option defaults */ - -int debug = 0; /* Debugging flag (level) */ -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 */ -char hostname[MAXHOSTNAMELEN]; /* System host name */ +int debug = 0; /* Debugging flag (level) */ +struct timeval actualtimeout = +{ /* fifteen minutes */ + 15 * 60L, /* tv_sec */ + 0 /* tv_usec */ +}; /* - * Globals below are associated with the bootp database file (bootptab). + * General */ -char *bootptab = NULL; -#ifdef DEBUG -char *bootpd_dump = NULL; +int s; /* Socket file descriptor */ +char *pktbuf; /* Receive packet buffer */ +int pktlen; +char *progname; +char *chdir_path; +char hostname[MAXHOSTNAMELEN]; /* System host name */ +struct in_addr my_ip_addr; +#ifdef YORK_EX_OPTION +char *exec_file_name; #endif - +/* Flags set by signal catcher. */ +PRIVATE int do_readtab = 0; +PRIVATE int do_dumptab = 0; /* - * Vendor magic cookies for CMU and RFC1048 + * Globals below are associated with the bootp database file (bootptab). */ -unsigned char vm_cmu[4] = VM_CMU; -unsigned char vm_rfc1048[4] = VM_RFC1048; +char *bootptab = CONFIG_FILE; +char *bootpd_dump = DUMPTAB_FILE; + /* - * 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. . . . + * Initialization such as command-line processing is done and then the + * main server loop is started. */ -struct hwinfo hwinfolist[MAXHTYPES + 1] = { - { 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 */ -}; +void +main(argc, argv) + int argc; + char **argv; +{ + struct timeval *timeout; + struct bootp *bp; + struct servent *servp; + struct hostent *hep; + char *stmp; + int n, ba_len, ra_len; + int nfound, readfds; + int standalone; + + progname = strrchr(argv[0], '/'); + if (progname) progname++; + else progname = argv[0]; + /* + * Initialize logging. + */ + report_init(0); /* uses progname */ -/* - * Main hash tables - */ + /* + * Log startup + */ + report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); -hash_tbl *hwhashtable; -hash_tbl *iphashtable; -hash_tbl *nmhashtable; + /* Debugging for compilers with struct padding. */ + assert(sizeof(struct bootp) == BP_MINPKTSZ); + /* Get space for receiving packets and composing replies. */ + pktbuf = malloc(MAXPKT); + if (!pktbuf) { + report(LOG_ERR, "malloc failed"); + exit(1); + } + 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; + ba_len = sizeof(bind_addr); + bzero((char *) &bind_addr, ba_len); + errno = 0; + standalone = TRUE; + if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { + /* + * Descriptor 0 is a socket. Assume we are a child of inetd. + */ + if (bind_addr.sin_family == AF_INET) { + standalone = FALSE; + bootps_port = ntohs(bind_addr.sin_port); + } else { + /* Some other type of socket? */ + report(LOG_ERR, "getsockname: not an INET socket"); + } + } -/* - * Initialization such as command-line processing is done and then the main - * server loop is started. - */ + /* + * Set defaults that might be changed by option switches. + */ + stmp = NULL; + timeout = &actualtimeout; -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, fromlen; - int nfound, readfds; - int standalone; - - stmp = NULL; - standalone = FALSE; - actualtimeout.tv_usec = 0L; - actualtimeout.tv_sec = 60 * DEFAULT_TIMEOUT; - timeout = &actualtimeout; - - - /* - * Assume 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; - tolen = sizeof(s_in); - bzero((char *) &s_in, tolen); - errno = 0; - if (getsockname(s, (struct sockaddr *) &s_in, &tolen) == 0) { /* - * Descriptor 0 is a socket. Assume we're running as a child of inetd. + * Read switches. */ - bootps_port = ntohs(s_in.sin_port); - standalone = FALSE; - } else { - if (errno == ENOTSOCK) { - /* - * Descriptor 0 is NOT a socket. Run in standalone mode. - */ - standalone = TRUE; - } else { - /* - * Something else went wrong. Punt. - */ - fprintf(stderr, "bootpd: getsockname: %s\n", - get_network_errmsg()); - exit(1); - } - } - - - /* - * 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"); + for (argc--, argv++; argc > 0; argc--, argv++) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'c': /* chdir_path */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (stmp[0] != '/')) { + fprintf(stderr, + "bootpd: invalid chdir specification\n"); + break; + } + chdir_path = stmp; + break; + + case 'd': /* debug level */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else if (argv[1] && 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, + "%s: invalid debug level\n", progname); + break; + } + debug = n; + break; + + case 'i': /* inetd mode */ + standalone = FALSE; 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] && argv[1][0] == '-') { + + case 's': /* standalone mode */ + standalone = TRUE; + break; + + case 't': /* timeout */ + 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, + "%s: invalid timeout specification\n", progname); + break; + } + actualtimeout.tv_sec = (int32) (60 * n); /* - * Backwards-compatible behavior: - * no parameter, so just increment the debug flag. + * If the actual timeout is zero, pass a NULL pointer + * to select so it blocks indefinitely, otherwise, + * point to the actual timeout value. */ - debug++; + timeout = (n > 0) ? &actualtimeout : NULL; break; - } else { - argc--; - argv++; - stmp = argv[0]; - } - if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { - fprintf(stderr, - "bootpd: invalid debug level\n"); + +#ifdef YORK_EX_OPTION + case 'x': + if (argc < 2) { + fprintf(stderr, "missing exec_file_name\n"); + break; + } + argc--; argv++; + exec_file_name = argv[1]; 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(); - } - } - } +#endif /* YORK_EX_OPTION */ - /* - * Get hostname. - */ - if (gethostname(hostname, sizeof(hostname)) == -1) { - fprintf(stderr, "bootpd: can't get hostname\n"); - exit(1); - } - - /* - * 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 + default: + fprintf(stderr, "%s: unknown switch: -%c\n", + progname, argv[0][1]); + usage(); + break; + } /* switch */ + } /* for args */ - if (standalone) { /* - * Go into background and disassociate from controlling terminal. + * Override default file names if specified on the command line. */ - 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); - } - } + if (argc > 0) + bootptab = argv[0]; + + if (argc > 1) + bootpd_dump = argv[1]; + /* - * Nuke any timeout value + * Get my hostname and IP address. */ - timeout = NULL; - } + if (gethostname(hostname, sizeof(hostname)) == -1) { + fprintf(stderr, "bootpd: can't get hostname\n"); + exit(1); + } + hep = gethostbyname(hostname); + if (!hep) { + fprintf(stderr, "Can not get my IP address\n"); + exit(1); + } + bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); + if (standalone) { + /* + * Go into background and disassociate from controlling terminal. + */ + if (debug < 3) { + if (fork()) + exit(0); +#ifdef NO_SETSID + setpgrp(0,0); +#ifdef TIOCNOTTY + n = open("/dev/tty", O_RDWR); + if (n >= 0) { + ioctl(n, TIOCNOTTY, (char *) 0); + (void) close(n); + } +#endif /* TIOCNOTTY */ +#else /* SETSID */ + if (setsid() < 0) + perror("setsid"); +#endif /* SETSID */ + } /* if debug < 3 */ -#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 + /* + * Nuke any timeout value + */ + timeout = NULL; - /* - * 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); - } + } /* if standalone (1st) */ + /* Set the cwd (i.e. to /tftpboot) */ + if (chdir_path) { + if (chdir(chdir_path) < 0) + report(LOG_ERR, "%s: chdir failed", chdir_path); + } - /* - * Read the bootptab file once immediately upon startup. - */ - readtab(); + /* Get the timezone. */ + tzone_init(); + /* Allocate hash tables. */ + rdtab_init(); - if (standalone) { /* - * Create a socket. + * Read the bootptab file. */ - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - report(LOG_ERR, "socket: %s\n", get_network_errmsg()); - exit(1); - } + readtab(1); /* force read */ + + if (standalone) { + + /* + * Create a socket. + */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "socket: %s", 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 { + bootps_port = (u_short) IPPORT_BOOTPS; + report(LOG_ERR, + "udp/bootps: unknown service -- assuming port %d", + bootps_port); + } + + /* + * Bind socket to BOOTPS port. + */ + bind_addr.sin_family = AF_INET; + bind_addr.sin_addr.s_addr = INADDR_ANY; + bind_addr.sin_port = htons(bootps_port); + if (bind(s, (struct sockaddr *) &bind_addr, + sizeof(bind_addr)) < 0) + { + report(LOG_ERR, "bind: %s", get_network_errmsg()); + exit(1); + } + } /* if standalone (2nd)*/ /* - * Get server's listening port number + * Get destination port number so we can reply to client */ - servp = getservbyname("bootps", "udp"); + servp = getservbyname("bootpc", "udp"); if (servp) { - bootps_port = ntohs((u_short) servp->s_port); + bootpc_port = ntohs(servp->s_port); } else { - report(LOG_ERR, - "udp/bootps: unknown service -- assuming port %d\n", - IPPORT_BOOTPS); - bootps_port = (u_short) IPPORT_BOOTPS; + report(LOG_ERR, + "udp/bootpc: unknown service -- assuming port %d", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; } /* - * Bind socket to BOOTPS port. + * Set up signals to read or dump the table. */ - s_in.sin_family = AF_INET; - s_in.sin_addr.s_addr = INADDR_ANY; - s_in.sin_port = htons(bootps_port); - if (bind(s, (struct sockaddr *) &s_in, sizeof(s_in)) < 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, &readfds, NULL, NULL, timeout); - if (nfound < 0) { - if (errno != EINTR) { - report(LOG_ERR, "select: %s\n", get_errmsg()); - } - continue; + if ((int) signal(SIGHUP, catcher) < 0) { + report(LOG_ERR, "signal: %s", get_errmsg()); + exit(1); } - if (!(readfds & (1 << s))) { - report(LOG_INFO, "exiting after %ld minutes of inactivity\n", - actualtimeout.tv_sec / 60); - exit(0); - } - fromlen = sizeof(from); - n = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from, - &fromlen); - if (n <= 0) { - continue; + if ((int) signal(SIGUSR1, catcher) < 0) { + report(LOG_ERR, "signal: %s", get_errmsg()); + exit(1); } - if (n < sizeof(struct bootp)) { - if (debug) { - report(LOG_INFO, "received short packet\n"); - } - continue; - } + /* + * 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", get_errmsg()); + } + /* + * Call readtab() or dumptab() here to avoid the + * dangers of doing I/O from a signal handler. + */ + if (do_readtab) { + do_readtab = 0; + readtab(1); /* force read */ + } + if (do_dumptab) { + do_dumptab = 0; + dumptab(bootpd_dump); + } + continue; + } + if (!(readfds & (1 << s))) { + if (debug > 1) + report(LOG_INFO, "exiting after %ld minutes of inactivity", + actualtimeout.tv_sec / 60); + exit(0); + } + ra_len = sizeof(recv_addr); + n = recvfrom(s, pktbuf, MAXPKT, 0, + (struct sockaddr *) &recv_addr, &ra_len); + if (n <= 0) { + continue; + } + if (debug > 1) { + report(LOG_INFO, "recvd pkt from IP addr %s", + inet_ntoa(recv_addr.sin_addr)); + } + if (n < sizeof(struct bootp)) { + if (debug) { + report(LOG_INFO, "received short packet"); + } + continue; + } + pktlen = n; - readtab(); /* maybe re-read bootptab */ - switch (bp->bp_op) { - case BOOTREQUEST: - request(); - break; + readtab(0); /* maybe re-read bootptab */ - case BOOTREPLY: - reply(); - break; + switch (bp->bp_op) { + case BOOTREQUEST: + handle_request(); + break; + case BOOTREPLY: + handle_reply(); + break; + } } - } } @@ -564,15 +557,33 @@ main(argc, argv) * Print "usage" message and exit */ -void usage() +PRIVATE 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); + fprintf(stderr, + "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n"); + fprintf(stderr, "\t -c n\tset current directory\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); +} + +/* Signal catchers */ +PRIVATE void +catcher(sig) + int sig; +{ + if (sig == SIGHUP) + do_readtab = 1; + if (sig == SIGUSR1) + do_dumptab = 1; +#ifdef SYSV + /* For older "System V" derivatives with no sigset(). */ + /* XXX - Should just do it the POSIX way (sigaction). */ + signal(sig, catcher); +#endif } @@ -580,402 +591,527 @@ void usage() /* * 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.) + * Note: This version of the bootpd.c server never forwards + * a request to another server. That is the job of a gateway + * program such as the "bootpgw" program included here. * * (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() +PRIVATE void +handle_request() { - struct bootp *bp = (struct bootp *) buf; - struct host *hp; - int n; - char *path; - struct host dummyhost; - long bootsize; - unsigned hlen, hashcode; - char realpath[1024]; - - /* - * If the servername field is set, compare it against us. - * If we're not being addressed, ignore this request. - * If the server name field is null, throw in our name. - */ - if (strlen(bp->bp_sname)) { - if (strcmp((char *)bp->bp_sname, hostname)) { - return; - } - } else { - strcpy((char *)bp->bp_sname, hostname); - } - bp->bp_op = BOOTREPLY; - if (bp->bp_ciaddr.s_addr == 0) { + struct bootp *bp = (struct bootp *) pktbuf; + struct host *hp = NULL; + struct host dummyhost; + int32 bootsize = 0; + unsigned hlen, hashcode; + int32 dest; +#ifdef CHECK_FILE_ACCESS + char realpath[1024]; + char *path; + int n; +#endif + + /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ + /* - * client doesnt know his IP address, - * search by hardware address. + * If the servername field is set, compare it against us. + * If we're not being addressed, ignore this request. + * If the server name field is null, throw in our name. */ - if (debug) { - report(LOG_INFO, "request from %s address %s\n", - netname(bp->bp_htype), - haddrtoa(bp->bp_chaddr, bp->bp_htype)); + if (strlen(bp->bp_sname)) { + if (strcmp(bp->bp_sname, hostname)) { + if (debug) + report(LOG_INFO, "\ +ignoring request for server %s from client at %s address %s", + bp->bp_sname, netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, bp->bp_hlen)); + /* XXX - Is it correct to ignore such a request? -gwr */ + return; + } + } else { + strcpy(bp->bp_sname, hostname); } - 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) { - report(LOG_NOTICE, "%s address not found: %s\n", - netname(bp->bp_htype), - haddrtoa(bp->bp_chaddr, bp->bp_htype)); - return; /* not found */ + bp->bp_op = BOOTREPLY; + if (bp->bp_ciaddr.s_addr == 0) { + /* + * client doesnt know his IP address, + * search by hardware address. + */ + if (debug > 1) { + report(LOG_INFO, "request from %s address %s", + netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, bp->bp_hlen)); + } + hlen = haddrlength(bp->bp_htype); + if (hlen != bp->bp_hlen) { + report(LOG_NOTICE, "bad addr len from from %s address %s", + netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, hlen)); + } + dummyhost.htype = 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_IEEE802) + { + /* Try again with address in "canonical" form. */ + haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); + if (debug > 1) { + report(LOG_INFO, "\ +HW addr type is IEEE 802. convert to %s and check again\n", + haddrtoa(dummyhost.haddr, bp->bp_hlen)); + } + hashcode = hash_HashFunction(dummyhost.haddr, hlen); + hp = (struct host *) hash_Lookup(hwhashtable, hashcode, + hwlookcmp, &dummyhost); + } + if (hp == NULL) { + /* + * XXX - Add dynamic IP address assignment? + */ + if (debug > 1) + report(LOG_INFO, "unknown client %s address %s", + netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, bp->bp_hlen)); + return; /* not found */ + } + (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; + + } else { + + /* + * search by IP address. + */ + if (debug > 1) { + report(LOG_INFO, "request from IP addr %s", + inet_ntoa(bp->bp_ciaddr)); + } + dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr; + hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4); + hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, + &dummyhost); + if (hp == NULL) { + if (debug > 1) { + report(LOG_NOTICE, "IP address not found: %s", + inet_ntoa(bp->bp_ciaddr)); + } + return; + } } - (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)); + report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr), + hp->hostname->string); } - 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. - * - * 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]) { + +#ifdef YORK_EX_OPTION /* - * The client specified a file. + * The need for the "ex" tag arose out of the need to empty + * shared networked drives on diskless PCs. This solution is + * not very clean but it does work fairly well. + * Written by Edmund J. Sutcliffe <edmund@york.ac.uk> */ - 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 { + /* Run a program, passing the client name as a parameter. */ + if (exec_file_name) { + char tst[100]; + strcpy (tst, exec_file_name); + strcat (tst, " "); + strcat (tst, hp->hostname->string); + strcat (tst, " &"); + if (debug) + report(LOG_INFO, "executing %s", tst); + system(tst); /* Hope this finishes soon... */ + } +#endif /* YORK_EX_OPTION */ + + /* + * If a specific TFTP server address was specified in the bootptab file, + * fill it in, otherwise zero it. + * XXX - Rather than zero it, should it be the bootpd address? -gwr + */ + (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ? + hp->bootserver.s_addr : 0L; + +#ifdef STANFORD_PROM_COMPAT + /* + * Stanford bootp PROMs (for a Sun?) have no way to leave + * the boot file name field blank (because the boot file + * name is automatically generated from some index). + * As a work-around, this little hack allows those PROMs to + * specify "sunboot14" with the same effect as a NULL name. + * (The user specifies boot device 14 or some such magic.) + */ + if (strcmp(bp->bp_file, "sunboot14") == 0) + bp->bp_file[0] = '\0'; /* treat it as unspecified */ +#endif + /* - * No file specified by the client. + * 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 *XXX + * 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" *XXX + * */ - 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); + + /* + * XXX - I think the above policy is too complicated. When the + * boot file is missing, it is not obvious why bootpd will not + * respond to client requests. Define CHECK_FILE_ACCESS if you + * want the original complicated policy, otherwise bootpd will + * no longer check for existence of the boot file. -gwr + */ + +#ifdef CHECK_FILE_ACCESS + + if (hp->flags.tftpdir) { + strcpy(realpath, hp->tftpdir->string); + path = &realpath[strlen(realpath)]; } else { - bzero(bp->bp_file, sizeof(bp->bp_file)); - goto skip_file; /* Don't bother trying to access the file */ + path = realpath; } - } - - /* - * 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]) { + + if (bp->bp_file[0]) { /* - * Client wanted specific file - * and we didn't have it. + * The client specified a file. */ - report(LOG_NOTICE, - "requested file not found: \"%s\"\n", path); - return; - } else { + 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", + bp->bp_file); + 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. + * No file specified by the client. */ - bzero(bp->bp_file, sizeof(bp->bp_file)); - goto skip_file; - } + 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 */ + } } - } - strcpy(bp->bp_file, path); -skip_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\"", 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: + ; +#else /* CHECK_FILE_ACCESS */ - 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) { - bcopy(hp->vm_cookie, bp->bp_vend, 4); - } - - /* - * Figure out the format for the vendor-specific info. - */ - if (!bcmp(bp->bp_vend, vm_cmu, 4)) { - /* Not an RFC1048 bootp client */ + /* + * This implements a simple response policy, where bootpd + * will fail to respond only if it knows nothing about + * the client that sent the request. This plugs in the + * boot file name but does not demand that it exist. + * + * If either the client or the server specifies a boot file, + * build the path name for it. Server boot file preferred. + */ + if (bp->bp_file[0] || hp->flags.bootfile) { + char requested_file[BP_FILE_LEN]; + char *given_file; + char *p = bp->bp_file; + int space = BP_FILE_LEN; + int n; + + /* Save client's requested file name. */ + strncpy(requested_file, bp->bp_file, BP_FILE_LEN); + + /* If tftpdir is set, insert it. */ + if (hp->flags.tftpdir) { + n = strlen(hp->tftpdir->string); + if ((n+1) >= space) + goto nospc; + strcpy(p, hp->tftpdir->string); + p += n; + space -= n; + } + + /* If homedir is set, insert it. */ + if (hp->flags.homedir) { + n = strlen(hp->homedir->string); + if ((n+1) >= space) + goto nospc; + strcpy(p, hp->homedir->string); + p += n; + space -= n; + } + + /* Finally, append the boot file name. */ + if (hp->flags.bootfile) + given_file = hp->bootfile->string; + else + given_file = requested_file; + assert(given_file); + n = strlen(given_file); + if ((n+1) >= space) + goto nospc; + strcpy(p, given_file); + p += n; + space -= n; + *p = '\0'; + + if (space <= 0) { + nospc: + report(LOG_ERR, "boot file path too long (%s)", + hp->hostname->string); + } + } + + /* Determine boot file size if requested. */ + if (hp->flags.bootsize_auto) { + if (bp->bp_file[0] == '\0' || + chk_access(bp->bp_file, &bootsize) < 0) + { + report(LOG_ERR, "can not determine boot file size for %s", + hp->hostname->string); + } + } + +#endif /* CHECK_FILE_ACCESS */ + + + if (debug > 1) { + report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", + (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. Only force reply format if user specified it. + */ + if (hp->flags.vm_cookie) { + /* Slam in the user specified magic number. */ + bcopy(hp->vm_cookie, bp->bp_vend, 4); + } + /* + * Figure out the format for the vendor-specific info. + * Note that bp->bp_vend may have been set above. + */ + 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)"); + } + } #ifdef VEND_CMU - dovend_cmu(bp, hp); -#else - dovend_rfc1048(bp, hp, bootsize); + else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { + dovend_cmu(bp, hp); + if (debug > 1) { + report(LOG_INFO, "sending reply (with CMU options)"); + } + } #endif - } else { - /* RFC1048 conformant bootp client */ - dovend_rfc1048(bp, hp, bootsize); - } - sendreply(0); + else { + if (debug > 1) { + report(LOG_INFO, "sending reply (with no options)"); + } + } + + dest = (hp->flags.reply_addr) ? + hp->reply_addr.s_addr : 0L; + + /* not forwarded */ + sendreply(0, dest); } - /* - * Process BOOTREPLY packet (something is using us as a gateway). + * Process BOOTREPLY packet. */ - -reply() +PRIVATE void +handle_reply() { if (debug) { - report(LOG_INFO, "processing boot reply\n"); + report(LOG_INFO, "processing boot reply"); } - sendreply(1); + /* forwarded, no destination override */ + sendreply(1, 0); } - /* * 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; +PRIVATE void +sendreply(forward, dst_override) + int forward; + int32 dst_override; { - struct bootp *bp = (struct bootp *) buf; + struct bootp *bp = (struct bootp *) pktbuf; struct in_addr dst; - struct sockaddr_in to; - - to = s_in; + u_short port = bootpc_port; +#if 0 + u_char canon_haddr[MAXHADDRLEN]; +#endif + unsigned char *ha; + int len; - to.sin_port = htons(bootpc_port); /* - * If the client IP address is specified, use that + * If the destination address was specified explicitly + * (i.e. the broadcast address for HP compatiblity) + * then send the response to that address. Otherwise, + * act in accordance with RFC951: + * 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. + * else make a temporary arp cache entry for the client's + * NEW IP/hardware address and use that. */ - if (bp->bp_ciaddr.s_addr) { + if (dst_override) { + dst.s_addr = dst_override; + if (debug > 1) { + report(LOG_INFO, "reply address override: %s", + inet_ntoa(dst)); + } + } else if (bp->bp_ciaddr.s_addr) { dst = bp->bp_ciaddr; } else if (bp->bp_giaddr.s_addr && forward == 0) { dst = bp->bp_giaddr; - to.sin_port = htons(bootps_port); + port = bootps_port; + if (debug > 1) { + report(LOG_INFO, "sending reply to gateway %s", + inet_ntoa(dst)); + } } else { dst = bp->bp_yiaddr; - setarp(&dst, bp->bp_chaddr, bp->bp_hlen); + ha = bp->bp_chaddr; + len = bp->bp_hlen; + if (len > MAXHADDRLEN) + len = MAXHADDRLEN; +#if 0 + /* + * XXX - Is this necessary, given that the HW address + * in bp_chaddr was left as the client provided it? + * Does some DEC version of TCP/IP need this? -gwr + */ + if (bp->bp_htype == HTYPE_IEEE802) { + haddr_conv802(ha, canon_haddr, len); + ha = canon_haddr; + } +#endif + if (debug > 1) + report(LOG_INFO, "setarp %s - %s", + inet_ntoa(dst), haddrtoa(ha, len)); + setarp(s, &dst, ha, len); } - if (forward == 0) { + if ((forward == 0) && + (bp->bp_siaddr.s_addr == 0)) + { + struct ifreq *ifr; + struct in_addr siaddr; /* * 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). + * as the client). Of course, the client may + * be on the other side of a BOOTP gateway... */ -#if 0 - int maxmatch = 0; - int len, m; - 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; - } - } -#endif - int maxmatch = 0; - int len, m; - struct ifreq *ifrq, *ifrmax; - char *p; - - ifrmax = &ifreq[0]; - p = (char *)&ifreq[0]; - len = ifconf.ifc_len; - for (; len > 0; ) { - ifrq = (struct ifreq *)p; - m = nmatch(&dst, &((struct sockaddr_in *) - (&ifrq->ifr_addr))->sin_addr); - if (m > maxmatch) { - maxmatch = m; - ifrmax = ifrq; - } - p += ifrq->ifr_addr.sa_len + IFNAMSIZ; - len -= ifrq->ifr_addr.sa_len + IFNAMSIZ; + ifr = getif(s, &dst); + if (ifr) { + struct sockaddr_in *sip; + sip = (struct sockaddr_in *) &(ifr->ifr_addr); + siaddr = sip->sin_addr; + } else { + /* Just use my "official" IP address. */ + siaddr = my_ip_addr; } - if (bp->bp_giaddr.s_addr == 0) { - if (maxmatch == 0) { - return; - } - bp->bp_giaddr = ((struct sockaddr_in *) - (&ifrmax->ifr_addr))->sin_addr; - } + /* XXX - No need to set bp_giaddr here. */ - /* - * 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; - } + /* Finally, set the server address field. */ + bp->bp_siaddr = siaddr; } + /* Set up socket address for send. */ + send_addr.sin_family = AF_INET; + send_addr.sin_port = htons(port); + send_addr.sin_addr = dst; - to.sin_addr = dst; - if (sendto(s, bp, sizeof(struct bootp), 0, (struct sockaddr *) &to, - sizeof(to)) < 0) { - report(LOG_ERR, "sendto: %s\n", get_network_errmsg()); + /* Send reply with same size packet as request used. */ + if (sendto(s, pktbuf, pktlen, 0, + (struct sockaddr *) &send_addr, + sizeof(send_addr)) < 0) + { + report(LOG_ERR, "sendto: %s", get_network_errmsg()); } -} - +} /* sendreply */ -/* - * Return the number of leading bytes matching in the - * internet addresses supplied. - */ -nmatch(ca,cb) - char *ca, *cb; -{ - int n,m; - - for (m = n = 0 ; n < 4 ; n++) { - if (*ca++ != *cb++) - return(m); - m++; - } - return(m); -} - +/* nmatch() - now in getif.c */ +/* setarp() - now in hwaddr.c */ /* - * 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 @@ -985,749 +1121,214 @@ setarp(ia, ha, len) * set for tftpd(8) to allow clients to read the file. */ -int chk_access(path, filesize) -char *path; -long *filesize; +PRIVATE int +chk_access(path, filesize) + char *path; + int32 *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; - } -} + struct stat st; + if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { + *filesize = (int32) st.st_size; + return 0; + } else { + return -1; + } +} -#ifdef DEBUG - /* - * Dump the internal memory database to bootpd_dump. + * Now in dumptab.c : + * dumptab() + * dump_host() + * list_ipaddresses() */ -dumptab() -{ - int n; - struct host *hp; - 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, "#\tbf -- bootfile\n"); - fprintf(fp, "#\tbs -- bootfile size in 512-octet blocks\n"); - fprintf(fp, "#\tcs -- cookie servers\n"); - fprintf(fp, "#\tdf -- dump file name\n"); - fprintf(fp, "#\tdn -- domain name\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, "#\trp -- root path\n"); - fprintf(fp, "#\tsm -- subnet mask\n"); - fprintf(fp, "#\tsw -- swap server\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); -} - - +#ifdef VEND_CMU /* - * Dump all the available information on the host pointed to by "hp". - * The output is sent to the file pointed to by "fp". + * Insert the CMU "vendor" data for the host pointed to by "hp" into the + * bootp packet pointed to by "bp". */ -void dump_host(fp, hp) -FILE *fp; -struct host *hp; +PRIVATE void +dovend_cmu(bp, hp) + struct bootp *bp; + struct host *hp; { - int i; - byte *dataptr; + struct cmu_vend *vendp; + struct in_addr_list *taddr; - 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, ":"); + /* + * 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; + strcpy(vendp->v_magic, (char *)vm_cmu); + 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) { - 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.swap_server) { - fprintf(fp, "sw=%s:", inet_ntoa(hp->subnet_mask)); - } - 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, ":"); + 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) { - 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.rootpath) { - fprintf(fp, "rp=%s:", hp->rootpath->string); - } - if (hp->flags.domainname) { - fprintf(fp, "dn=%s:", hp->domainname->string); - } - if (hp->flags.dumpfile) { - fprintf(fp, "df=%s:", hp->dumpfile->string); - } - if (hp->flags.time_offset) { - if (hp->flags.timeoff_auto) { - fprintf(fp, "to=auto:"); - } else { - fprintf(fp, "to=%ld:", hp->time_offset); - } + 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) { - 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; -{ - unsigned count; - 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++)); - } + 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; + } + } } - } -} -#endif /* DEBUG */ + /* Log message now done by caller. */ +} /* dovend_cmu */ +#endif /* VEND_CMU */ -#ifdef VEND_CMU /* - * Insert the CMU "vendor" data for the host pointed to by "hp" into the + * Insert the RFC1048 vendor data for the host pointed to by "hp" into the * bootp packet pointed to by "bp". */ - -void dovend_cmu(bp, hp) - struct bootp *bp; - struct host *hp; +#define NEED(LEN, MSG) do \ + if (bytesleft < (LEN)) { \ + report(LOG_NOTICE, noroom, \ + hp->hostname->string, MSG); \ + return; \ + } while (0) +PRIVATE void +dovend_rfc1048(bp, hp, bootsize) + struct bootp *bp; + struct host *hp; + int32 bootsize; { - struct cmu_vend *vendp; - 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); - - if (debug > 1) { - report(LOG_INFO, "sending CMU-style reply\n"); - } -} + int bytesleft, len; + byte *vp; + char *tmpstr; -#endif /* VEND_CMU */ - - + static char noroom[] = "%s: No room for \"%s\" option"; -/* - * Insert the RFC1048 vendor data for the host pointed to by "hp" into the - * bootp packet pointed to by "bp". - */ + 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; -void dovend_rfc1048(bp, hp, bootsize) - struct bootp *bp; - struct host *hp; - 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 + 511) / 512) : (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.swap_server) { - *vp++ = TAG_SWAPSERVER; /* -1 byte */ - *vp++ = 4; /* -1 byte */ - insert_u_long(hp->swapserver.s_addr, &vp); /* -4 bytes */ - bytesleft -= 6; /* Fix real count */ - } - if (hp->flags.rootpath) { - /* - * Check for room for rootpath. Add 2 to account for - * TAG_ROOTPATH and length. - */ - len = strlen(hp->rootpath->string); - if ((len + 2) <= bytesleft) { - *vp++ = TAG_ROOTPATH; - *vp++ = (byte) (len & 0xFF); - bcopy(hp->rootpath->string, vp, len); - vp += len; - bytesleft -= len + 2; - } - } - if (hp->flags.dumpfile) { - /* - * Check for room for dumpfile. Add 2 to account for - * TAG_DUMPFILE and length. - */ - len = strlen(hp->dumpfile->string); - if ((len + 2) <= bytesleft) { - *vp++ = TAG_DUMPFILE; - *vp++ = (byte) (len & 0xFF); - bcopy(hp->dumpfile->string, vp, len); - vp += len; - bytesleft -= len + 2; + if (hp->flags.subnet_mask) { + /* always enough room here. */ + *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) { + (void) insert_ip(TAG_GATEWAY, + hp->gateway, + &vp, &bytesleft); + } } - } - if (hp->flags.domain_server) { - insert_ip(TAG_DOMAIN_SERVER, hp->domain_server, &vp, &bytesleft); - } - if (hp->flags.domainname) { - /* - * Check for room for domainname. Add 2 to account for - * TAG_DOMAINNAME and length. - */ - len = strlen(hp->domainname->string); - if ((len + 2) <= bytesleft) { - *vp++ = TAG_DOMAINNAME; - *vp++ = (byte) (len & 0xFF); - bcopy(hp->domainname->string, vp, len); - vp += len; - bytesleft -= len + 2; + if (hp->flags.bootsize) { + /* always enough room here */ + bootsize = (hp->flags.bootsize_auto) ? + ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ + *vp++ = TAG_BOOT_SIZE; + *vp++ = 2; + *vp++ = (byte) ((bootsize >> 8) & 0xFF); + *vp++ = (byte) (bootsize & 0xFF); + bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ } - } - 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.lpr_server) { - insert_ip(TAG_LPR_SERVER, hp->lpr_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. + * This one is special: Remaining options go in the ext file. + * Only the subnet_mask, bootsize, and gateway should precede. */ - len = strlen(hp->hostname->string); - 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->string; - len = 0; - while (*tmpstr && (*tmpstr != '.')) { - tmpstr++; - len++; - } - } - if ((len + 2) <= bytesleft) { - *vp++ = TAG_HOSTNAME; - *vp++ = (byte) (len & 0xFF); - bcopy(hp->hostname->string, vp, len); - vp += len; - bytesleft -= len + 2; + if (hp->flags.exten_file) { + /* + * Check for room for exten_file. Add 3 to account for + * TAG_EXTEN_FILE, length, and TAG_END. + */ + len = strlen(hp->exten_file->string); + NEED((len + 3), "ef"); + *vp++ = TAG_EXTEN_FILE; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->exten_file->string, vp, len); + vp += len; + *vp++ = TAG_END; + bytesleft -= len + 3; + return; /* no more options here. */ } - } - if (hp->flags.cookie_server) { - insert_ip(TAG_COOKIE_SERVER, hp->cookie_server, &vp, &bytesleft); - } - if (hp->flags.log_server) { - insert_ip(TAG_LOG_SERVER, hp->log_server, &vp, &bytesleft); - } - - if (hp->flags.generic) { - insert_generic(hp->generic, &vp, &bytesleft); - } - - if (bytesleft >= 1) { - *vp++ = TAG_END; - bytesleft--; - } - - if (debug > 1) { - report(LOG_INFO, "sending RFC1048-style reply\n"); - } - - if (bytesleft > 0) { /* - * Zero out any remaining part of the vendor area. + * The remaining options are inserted by the following + * function (which is shared with bootpef.c). + * Keep back one byte for the TAG_END. */ - bzero(vp, bytesleft); - } -} + len = dovend_rfc1497(hp, vp, bytesleft - 1); + vp += len; + bytesleft -= len; - - -/* - * 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". "tag" is the RFC1048 tag - * number to use, "iplist" is a pointer to a list of IP addresses - * (struct in_addr_list), and "bytesleft" points to an integer which - * indicates the size of the "dest" buffer. The number of IP addresses - * actually inserted - * - * 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; -{ - struct in_addr *addrptr; - 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. - */ + /* There should be at least one byte left. */ + NEED(1, "(end)"); + *vp++ = TAG_END; + bytesleft--; -void insert_generic(gendata, buff, bytesleft) - struct shared_bindata *gendata; - byte **buff; - int *bytesleft; -{ - byte *srcptr; - 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; - } + /* Log message done by caller. */ + if (bytesleft > 0) { + /* + * Zero out any remaining part of the vendor area. + */ + bzero(vp, bytesleft); } - } -} - - +} /* dovend_rfc1048 */ +#undef NEED /* - * Convert a hardware address to an ASCII string. + * Now in readfile.c: + * hwlookcmp() + * iplookcmp() */ -char *haddrtoa(haddr, htype) - byte *haddr; - byte htype; -{ - static char haddrbuf[2 * MAXHADDRLEN + 1]; - char *bufptr; - unsigned count; - - bufptr = haddrbuf; - for (count = haddrlength(htype); count > 0; count--) { - sprintf(bufptr, "%02X", (unsigned) (*haddr++ & 0xFF)); - bufptr += 2; - } - return (haddrbuf); -} - - - +/* haddrtoa() - now in hwaddr.c */ /* - * 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. + * Now in dovend.c: + * insert_ip() + * insert_generic() + * insert_u_long() */ -void insert_u_long(value, dest) - unsigned long value; - byte **dest; -{ - byte *temp; - 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 */ -} - - +/* get_errmsg() - now in report.c */ /* - * Return pointer to static string which gives full filesystem error message. + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: */ - -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 -} diff --git a/libexec/bootpd/bootpd.h b/libexec/bootpd/bootpd.h index 94fb86e855f..716204a10d4 100644 --- a/libexec/bootpd/bootpd.h +++ b/libexec/bootpd/bootpd.h @@ -1,5 +1,3 @@ -#ifndef _BLURB_ -#define _BLURB_ /************************************************************************ Copyright 1988, 1991 by Carnegie Mellon University @@ -21,13 +19,15 @@ 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_ */ /* * bootpd.h -- common header file for all the modules of the bootpd program. */ +#include "bptypes.h" +#include "hash.h" +#include "hwaddr.h" #ifndef TRUE #define TRUE 1 @@ -44,17 +44,8 @@ SOFTWARE. #define SIGUSR1 30 /* From 4.3 <signal.h> */ #endif -#define MAXHTYPES 7 /* Number of htypes defined */ -#define MAXHADDRLEN 6 /* Max hw address length in bytes */ #define MAXSTRINGLEN 80 /* Max string length */ -/* - * Return the length in bytes of a hardware address of the given type. - * Return the canonical name of the network of the given type. - */ -#define haddrlength(type) ((hwinfolist[(int) (type)]).hlen) -#define netname(type) ((hwinfolist[(int) (type)]).name) - /* * Return pointer to static string which gives full network error message. @@ -63,59 +54,13 @@ SOFTWARE. /* - * Variables shared among modules. - */ - -extern int debug; -extern char *bootptab; - -extern struct hwinfo hwinfolist[]; - -extern hash_tbl *hwhashtable; -extern hash_tbl *iphashtable; -extern hash_tbl *nmhashtable; -extern unsigned char vm_cmu[4]; -extern unsigned char vm_rfc1048[4]; - - -/* - * Functions shared among modules - */ - -extern void report(); -extern char *get_errmsg(); -extern char *haddrtoa(); -extern int readtab(); - - - -/* - * Nice typedefs. . . - */ - -typedef int boolean; -typedef unsigned char byte; - - -/* - * This structure holds information about a specific network type. The - * length of the network hardware address is stored in "hlen". - * The string pointed to by "name" is the cononical name of the network. - */ -struct hwinfo { - unsigned hlen; - char *name; -}; - - -/* * Data structure used to hold an arbitrary-lengthed list of IP addresses. * The list may be shared among multiple hosts by setting the linkcount * appropriately. */ struct in_addr_list { - unsigned linkcount, addrcount; + unsigned int linkcount, addrcount; struct in_addr addr[1]; /* Dynamically extended */ }; @@ -126,12 +71,12 @@ struct in_addr_list { */ struct shared_string { - unsigned linkcount; + unsigned int linkcount; char string[1]; /* Dynamically extended */ }; struct shared_bindata { - unsigned linkcount, length; + unsigned int linkcount, length; byte data[1]; /* Dynamically extended */ }; @@ -165,14 +110,18 @@ struct flag { subnet_mask :1, tftpdir :1, time_offset :1, - timeoff_auto :1, time_server :1, - vendor_magic :1, - dumpfile :1, - domainname :1, + dump_file :1, + domain_name :1, swap_server :1, - rootpath :1, - vm_auto :1; + root_path :1, + exten_file :1, + reply_addr :1, + nis_domain :1, + nis_server :1, + ntp_server :1, + /* XXX - Add new tags here */ + vm_cookie :1; }; @@ -197,6 +146,7 @@ struct flag { */ struct host { + unsigned linkcount; /* hash list inserts */ struct flag flags; /* ALL valid fields */ struct in_addr_list *cookie_server, *domain_server, @@ -206,23 +156,47 @@ struct host { *lpr_server, *name_server, *rlp_server, - *time_server; + *time_server, + *nis_server, + *ntp_server; struct shared_string *bootfile, *hostname, - *domainname, + *domain_name, *homedir, *tftpdir, - *dumpfile, - *rootpath; + *dump_file, + *exten_file, + *root_path, + *nis_domain; struct shared_bindata *generic; byte vm_cookie[4], htype, /* RFC826 says this should be 16-bits but RFC951 only allocates 1 byte. . . */ haddr[MAXHADDRLEN]; - long time_offset; + int32 time_offset; unsigned int bootsize; struct in_addr bootserver, iaddr, - swapserver, + swap_server, + reply_addr, subnet_mask; + /* XXX - Add new tags here (or above as appropriate) */ }; + + + +/* + * Variables shared among modules. + */ + +extern int debug; +extern char *bootptab; +extern char *progname; + +extern u_char vm_cmu[4]; +extern u_char vm_rfc1048[4]; + +extern hash_tbl *hwhashtable; +extern hash_tbl *iphashtable; +extern hash_tbl *nmhashtable; + diff --git a/libexec/bootpd/bootpef.8 b/libexec/bootpd/bootpef.8 new file mode 100644 index 00000000000..0f0b1fc7b43 --- /dev/null +++ b/libexec/bootpd/bootpef.8 @@ -0,0 +1,52 @@ +.\" bootpef.8 +.TH BOOTPEF 8 "4 Dec 1993" "MAINTENANCE COMMANDS" +.SH NAME +bootpef \- BOOTP Extension File compiler +.SH SYNOPSIS +.LP +.B bootpef +.RI [ "-c chdir" ] +.RI [ "-d debug-level" ] +.RI [ "-f config-file" ] +.RI [ client-name " [...]]" +.SH DESCRIPTION +.B bootpef +builds the +.I Extension Path +files described by RFC 1497 (tag 18). +If any +.I client-name +arguments are specified, then +.I bootpef +compiles the extension files for only those clients. +.SH OPTIONS +.TP +.BI \-c \ chdir\-path +Sets the current directory used by +.I bootpef +while creating extension files. This is useful when the +extension file names are specified as relative pathnames, and +.I bootpef +needs to use the same current directory as the TFTP server +(typically /tftpboot). +.TP +.BI \-d \ debug\-level +Sets the +.I debug\-level +variable that controls the amount of debugging messages generated. +For example, -d4 or -d 4 will set the debugging level to 4. +.TP +.BI \-f \ config\-file +Set the name of the config file that specifies the option +data to be sent to each client. +.SH "SEE ALSO" +bootpd(8), tftpd(8) +.SH REFERENCES +.TP +RFC951 +BOOTSTRAP PROTOCOL (BOOTP) +.TP +RFC1497 +BOOTP Vendor Information Extensions + + diff --git a/libexec/bootpd/bootpef.c b/libexec/bootpd/bootpef.c new file mode 100644 index 00000000000..e035ef24231 --- /dev/null +++ b/libexec/bootpd/bootpef.c @@ -0,0 +1,347 @@ +/************************************************************************ + 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. +************************************************************************/ + +#ifndef lint +static char rcsid[] = "$Header: /cvsroot/src/libexec/bootpd/Attic/bootpef.c,v 1.1 1994/05/24 15:20:16 gwr Exp $"; +#endif + + +/* + * bootpef - BOOTP Extension File generator + * Makes an "Extension File" for each host entry that + * defines an and Extension File. (See RFC1497, tag 18.) + * + * HISTORY + * See ./Changes + * + * BUGS + * See ./ToDo + */ + + + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include <sys/types.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#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) +#endif + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "bootpd.h" +#include "dovend.h" +#include "readfile.h" +#include "report.h" +#include "tzone.h" +#include "patchlevel.h" + +#define BUFFERSIZE 0x4000 + +#ifndef CONFIG_FILE +#define CONFIG_FILE "/etc/bootptab" +#endif + + + +/* + * Externals, forward declarations, and global variables + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +static void dovend_rfc1048 P((struct bootp *, struct host *, int32)); +static void mktagfile P((struct host *)); +static void usage P((void)); + +#undef P + + +/* + * General + */ + +char *progname; +char *chdir_path; +int debug = 0; /* Debugging flag (level) */ +byte *buffer; + +/* + * Globals below are associated with the bootp database file (bootptab). + */ + +char *bootptab = CONFIG_FILE; + + +/* + * Print "usage" message and exit + */ +static void +usage() +{ + fprintf(stderr, + "usage: $s [ -c chdir ] [-d level] [-f configfile] [host...]\n"); + fprintf(stderr, "\t -c n\tset current directory\n"); + fprintf(stderr, "\t -d n\tset debug level\n"); + fprintf(stderr, "\t -f n\tconfig file name\n"); + exit(1); +} + + +/* + * Initialization such as command-line processing is done and then the + * main server loop is started. + */ +void +main(argc, argv) + int argc; + char **argv; +{ + struct host *hp; + char *stmp; + int n; + + progname = strrchr(argv[0], '/'); + if (progname) progname++; + else progname = argv[0]; + + /* Get work space for making tag 18 files. */ + buffer = (byte *) malloc(BUFFERSIZE); + if (!buffer) { + report(LOG_ERR, "malloc failed"); + exit(1); + } + /* + * Set defaults that might be changed by option switches. + */ + stmp = NULL; + + /* + * Read switches. + */ + for (argc--, argv++; argc > 0; argc--, argv++) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'c': /* chdir_path */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (stmp[0] != '/')) { + fprintf(stderr, + "bootpd: invalid chdir specification\n"); + break; + } + chdir_path = stmp; + break; + + case 'd': /* debug */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else if (argv[1] && 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 'f': /* config file */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + bootptab = stmp; + break; + + default: + fprintf(stderr, "bootpd: unknown switch: -%c\n", + argv[0][1]); + usage(); + break; + } + } + + /* Get the timezone. */ + tzone_init(); + + /* Allocate hash tables. */ + rdtab_init(); + + /* + * Read the bootptab file. + */ + readtab(1); /* force read */ + + /* Set the cwd (i.e. to /tftpboot) */ + if (chdir_path) { + if (chdir(chdir_path) < 0) + report(LOG_ERR, "%s: chdir failed", chdir_path); + } + /* If there are host names on the command line, do only those. */ + if (argc > 0) { + unsigned int tlen, hashcode; + + while (argc) { + tlen = strlen(argv[0]); + hashcode = hash_HashFunction((u_char *)argv[0], tlen); + hp = (struct host *) hash_Lookup(nmhashtable, + hashcode, + nmcmp, argv[0]); + if (!hp) { + printf("%s: no matching entry\n", argv[0]); + exit(1); + } + if (!hp->flags.exten_file) { + printf("%s: no extension file\n", argv[0]); + exit(1); + } + mktagfile(hp); + argv++; + argc--; + } + exit(0); + } + /* No host names specified. Do them all. */ + hp = (struct host *) hash_FirstEntry(nmhashtable); + while (hp != NULL) { + mktagfile(hp); + hp = (struct host *) hash_NextEntry(nmhashtable); + } +} + + + +/* + * Make a "TAG 18" file for this host. + * (Insert the RFC1497 options.) + */ + +static void +mktagfile(hp) + struct host *hp; +{ + FILE *fp; + int bytesleft, len; + byte *vp; + char *tmpstr; + + if (!hp->flags.exten_file) + return; + + vp = buffer; + bytesleft = BUFFERSIZE; + bcopy(vm_rfc1048, vp, 4); /* Copy in the magic cookie */ + vp += 4; + bytesleft -= 4; + + /* + * The "extension file" options are appended by the following + * function (which is shared with bootpd.c). + */ + len = dovend_rfc1497(hp, vp, bytesleft); + vp += len; + bytesleft -= len; + + if (bytesleft < 1) { + report(LOG_ERR, "%s: too much option data", + hp->exten_file->string); + return; + } + *vp++ = TAG_END; + bytesleft--; + + /* Write the buffer to the extension file. */ + printf("Updating \"%s\"\n", hp->exten_file->string); + if ((fp = fopen(hp->exten_file->string, "w")) == NULL) { + report(LOG_ERR, "error opening \"%s\": %s", + hp->exten_file->string, get_errmsg()); + return; + } + len = vp - buffer; + if (len != fwrite(buffer, 1, len, fp)) { + report(LOG_ERR, "write failed on \"%s\" : %s", + hp->exten_file->string, get_errmsg()); + } + fclose(fp); + +} /* dovend_rfc1048 */ + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/bootpgw.c b/libexec/bootpd/bootpgw.c new file mode 100644 index 00000000000..cdf11244dae --- /dev/null +++ b/libexec/bootpd/bootpgw.c @@ -0,0 +1,675 @@ +/* + * bootpgw.c - BOOTP GateWay + * This program forwards BOOTP Request packets to a BOOTP server. + */ + +/************************************************************************ + 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. +************************************************************************/ + +#ifndef lint +static char rcsid[] = "$Header: /cvsroot/src/libexec/bootpd/Attic/bootpgw.c,v 1.1 1994/05/24 15:20:18 gwr Exp $"; +#endif + +/* + * BOOTPGW is typically used to forward BOOTP client requests from + * one subnet to a BOOTP server on a different subnet. + */ + +#include <sys/types.h> +#include <sys/param.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> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <stdlib.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <netdb.h> +#include <syslog.h> +#include <assert.h> + +#ifdef NO_SETSID +# include <fcntl.h> /* for O_RDONLY, etc */ +#endif + +#ifndef USE_BFUNCS +# include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +# 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) +#endif + +#include "bootp.h" +#include "getif.h" +#include "hwaddr.h" +#include "report.h" +#include "patchlevel.h" + +/* Local definitions: */ +#define MAXPKT (3*512) /* Maximum packet size */ +#define TRUE 1 +#define FALSE 0 +#define get_network_errmsg get_errmsg + + + +/* + * Externals, forward declarations, and global variables + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +static void usage P((void)); +static void handle_reply P((void)); +static void handle_request P((void)); + +#undef P + +/* + * 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 bind_addr; /* Listening */ +struct sockaddr_in recv_addr; /* Packet source */ +struct sockaddr_in send_addr; /* destination */ + + +/* + * option defaults + */ +int debug = 0; /* Debugging flag (level) */ +struct timeval actualtimeout = +{ /* fifteen minutes */ + 15 * 60L, /* tv_sec */ + 0 /* tv_usec */ +}; +u_int maxhops = 4; /* Number of hops allowed for requests. */ +u_int minwait = 3; /* Number of seconds client must wait before + its bootrequest packets are forwarded. */ + +/* + * General + */ + +int s; /* Socket file descriptor */ +char *pktbuf; /* Receive packet buffer */ +int pktlen; +char *progname; +char *servername; +int32 server_ipa; /* Real server IP address, network order. */ + +char myhostname[64]; +struct in_addr my_ip_addr; + + + + +/* + * Initialization such as command-line processing is done and then the + * main server loop is started. + */ + +void +main(argc, argv) + int argc; + char **argv; +{ + struct timeval *timeout; + struct bootp *bp; + struct servent *servp; + struct hostent *hep; + char *stmp; + int n, ba_len, ra_len; + int nfound, readfds; + int standalone; + + progname = strrchr(argv[0], '/'); + if (progname) progname++; + else progname = argv[0]; + + /* + * Initialize logging. + */ + report_init(0); /* uses progname */ + + /* + * Log startup + */ + report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); + + /* Debugging for compilers with struct padding. */ + assert(sizeof(struct bootp) == BP_MINPKTSZ); + + /* Get space for receiving packets and composing replies. */ + pktbuf = malloc(MAXPKT); + if (!pktbuf) { + report(LOG_ERR, "malloc failed"); + exit(1); + } + 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; + ba_len = sizeof(bind_addr); + bzero((char *) &bind_addr, ba_len); + errno = 0; + standalone = TRUE; + if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { + /* + * Descriptor 0 is a socket. Assume we are a child of inetd. + */ + if (bind_addr.sin_family == AF_INET) { + standalone = FALSE; + bootps_port = ntohs(bind_addr.sin_port); + } else { + /* Some other type of socket? */ + report(LOG_INFO, "getsockname: not an INET socket"); + } + } + /* + * Set defaults that might be changed by option switches. + */ + stmp = NULL; + timeout = &actualtimeout; + gethostname(myhostname, sizeof(myhostname)); + hep = gethostbyname(myhostname); + if (!hep) { + printf("Can not get my IP address\n"); + exit(1); + } + bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); + + /* + * Read switches. + */ + for (argc--, argv++; argc > 0; argc--, argv++) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'd': /* debug level */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else if (argv[1] && 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, + "%s: invalid debug level\n", progname); + break; + } + debug = n; + break; + + case 'h': /* hop count limit */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || + (n < 0) || (n > 16)) + { + fprintf(stderr, + "bootpgw: invalid hop count limit\n"); + break; + } + maxhops = (u_int)n; + break; + + case 'i': /* inetd mode */ + standalone = FALSE; + break; + + case 's': /* standalone mode */ + standalone = TRUE; + break; + + case 't': /* timeout */ + 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, + "%s: invalid timeout specification\n", progname); + break; + } + actualtimeout.tv_sec = (int32) (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 'w': /* wait time */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || + (n < 0) || (n > 60)) + { + fprintf(stderr, + "bootpgw: invalid wait time\n"); + break; + } + minwait = (u_int)n; + break; + + default: + fprintf(stderr, "%s: unknown switch: -%c\n", + progname, argv[0][1]); + usage(); + break; + + } /* switch */ + } /* for args */ + + /* Make sure server name argument is suplied. */ + servername = argv[0]; + if (!servername) { + fprintf(stderr, "bootpgw: missing server name\n"); + usage(); + } + /* + * Get address of real bootp server. + */ + if (isdigit(servername[0])) + server_ipa = inet_addr(servername); + else { + hep = gethostbyname(servername); + if (!hep) { + fprintf(stderr, "bootpgw: can't get addr for %s\n", servername); + exit(1); + } + bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa)); + } + + if (standalone) { + /* + * Go into background and disassociate from controlling terminal. + * XXX - This is not the POSIX way (Should use setsid). -gwr + */ + if (debug < 3) { + if (fork()) + exit(0); +#ifdef NO_SETSID + setpgrp(0,0); +#ifdef TIOCNOTTY + n = open("/dev/tty", O_RDWR); + if (n >= 0) { + ioctl(n, TIOCNOTTY, (char *) 0); + (void) close(n); + } +#endif /* TIOCNOTTY */ +#else /* SETSID */ + if (setsid() < 0) + perror("setsid"); +#endif /* SETSID */ + } /* if debug < 3 */ + /* + * Nuke any timeout value + */ + timeout = NULL; + + /* + * Here, bootpd would do: + * chdir + * tzone_init + * rdtab_init + * readtab + */ + + /* + * Create a socket. + */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "socket: %s", 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 { + bootps_port = (u_short) IPPORT_BOOTPS; + report(LOG_ERR, + "udp/bootps: unknown service -- assuming port %d", + bootps_port); + } + + /* + * Bind socket to BOOTPS port. + */ + bind_addr.sin_family = AF_INET; + bind_addr.sin_port = htons(bootps_port); + bind_addr.sin_addr.s_addr = INADDR_ANY; + if (bind(s, (struct sockaddr *) &bind_addr, + sizeof(bind_addr)) < 0) + { + report(LOG_ERR, "bind: %s", get_network_errmsg()); + exit(1); + } + } /* if standalone */ + /* + * 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", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; + } + + /* no signal catchers */ + + /* + * 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", get_errmsg()); + } + continue; + } + if (!(readfds & (1 << s))) { + report(LOG_INFO, "exiting after %ld minutes of inactivity", + actualtimeout.tv_sec / 60); + exit(0); + } + ra_len = sizeof(recv_addr); + n = recvfrom(s, pktbuf, MAXPKT, 0, + (struct sockaddr *) &recv_addr, &ra_len); + if (n <= 0) { + continue; + } + if (debug > 3) { + report(LOG_INFO, "recvd pkt from IP addr %s", + inet_ntoa(recv_addr.sin_addr)); + } + if (n < sizeof(struct bootp)) { + if (debug) { + report(LOG_INFO, "received short packet"); + } + continue; + } + pktlen = n; + + switch (bp->bp_op) { + case BOOTREQUEST: + handle_request(); + break; + case BOOTREPLY: + handle_reply(); + break; + } + } +} + + + + +/* + * Print "usage" message and exit + */ + +static void +usage() +{ + fprintf(stderr, + "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n"); + fprintf(stderr, "\t -d n\tset debug level\n"); + fprintf(stderr, "\t -h n\tset max hop count\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"); + fprintf(stderr, "\t -w n\tset min wait time (secs)\n"); + exit(1); +} + + + +/* + * Process BOOTREQUEST packet. + * + * Note, this just forwards the request to a real server. + */ +static void +handle_request() +{ + struct bootp *bp = (struct bootp *) pktbuf; + struct ifreq *ifr; + u_short secs, hops; + + /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ + + if (debug) { + report(LOG_INFO, "request from %s", + inet_ntoa(recv_addr.sin_addr)); + } + /* Has the client been waiting long enough? */ + secs = ntohs(bp->bp_secs); + if (secs < minwait) + return; + + /* Has this packet hopped too many times? */ + hops = ntohs(bp->bp_hops); + if (++hops > maxhops) { + report(LOG_NOTICE, "reqest from %s reached hop limit", + inet_ntoa(recv_addr.sin_addr)); + return; + } + bp->bp_hops = htons(hops); + + /* + * Here one might discard a request from the same subnet as the + * real server, but we can assume that the real server will send + * a reply to the client before it waits for minwait seconds. + */ + + /* If gateway address is not set, put in local interface addr. */ + if (bp->bp_giaddr.s_addr == 0) { +#if 0 /* BUG */ + struct sockaddr_in *sip; + /* + * XXX - This picks the wrong interface when the receive addr + * is the broadcast address. There is no portable way to + * find out which interface a broadcast was received on. -gwr + * (Thanks to <walker@zk3.dec.com> for finding this bug!) + */ + ifr = getif(s, &recv_addr.sin_addr); + if (!ifr) { + report(LOG_NOTICE, "no interface for request from %s", + inet_ntoa(recv_addr.sin_addr)); + return; + } + sip = (struct sockaddr_in *) &(ifr->ifr_addr); + bp->bp_giaddr = sip->sin_addr; +#else /* BUG */ + /* + * XXX - Just set "giaddr" to our "official" IP address. + * RFC 1532 says giaddr MUST be set to the address of the + * interface on which the request was received. Setting + * it to our "default" IP address is not strictly correct, + * but is good enough to allow the real BOOTP server to + * get the reply back here. Then, before we forward the + * reply to the client, the giaddr field is corrected. + * (In case the client uses giaddr, which it should not.) + * See handle_reply() + */ + bp->bp_giaddr = my_ip_addr; +#endif /* BUG */ + + /* + * XXX - DHCP says to insert a subnet mask option into the + * options area of the request (if vendor magic == std). + */ + } + /* Set up socket address for send. */ + send_addr.sin_family = AF_INET; + send_addr.sin_port = htons(bootps_port); + send_addr.sin_addr.s_addr = server_ipa; + + /* Send reply with same size packet as request used. */ + if (sendto(s, pktbuf, pktlen, 0, + (struct sockaddr *) &send_addr, + sizeof(send_addr)) < 0) + { + report(LOG_ERR, "sendto: %s", get_network_errmsg()); + } +} + + + +/* + * Process BOOTREPLY packet. + */ +static void +handle_reply() +{ + struct bootp *bp = (struct bootp *) pktbuf; + struct ifreq *ifr; + struct sockaddr_in *sip; + u_char canon_haddr[MAXHADDRLEN]; + unsigned char *ha; + int len; + + if (debug) { + report(LOG_INFO, " reply for %s", + inet_ntoa(bp->bp_yiaddr)); + } + /* Make sure client is directly accessible. */ + ifr = getif(s, &(bp->bp_yiaddr)); + if (!ifr) { + report(LOG_NOTICE, "no interface for reply to %s", + inet_ntoa(bp->bp_yiaddr)); + return; + } +#if 1 /* Experimental (see BUG above) */ +/* #ifdef CATER_TO_OLD_CLIENTS ? */ + /* + * The giaddr field has been set to our "default" IP address + * which might not be on the same interface as the client. + * In case the client looks at giaddr, (which it should not) + * giaddr is now set to the address of the correct interface. + */ + sip = (struct sockaddr_in *) &(ifr->ifr_addr); + bp->bp_giaddr = sip->sin_addr; +#endif + + /* Set up socket address for send to client. */ + send_addr.sin_family = AF_INET; + send_addr.sin_addr = bp->bp_yiaddr; + send_addr.sin_port = htons(bootpc_port); + + /* Create an ARP cache entry for the client. */ + ha = bp->bp_chaddr; + len = bp->bp_hlen; + if (len > MAXHADDRLEN) + len = MAXHADDRLEN; + if (bp->bp_htype == HTYPE_IEEE802) { + haddr_conv802(ha, canon_haddr, len); + ha = canon_haddr; + } + if (debug > 1) + report(LOG_INFO, "setarp %s - %s", + inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len)); + setarp(s, &bp->bp_yiaddr, ha, len); + + /* Send reply with same size packet as request used. */ + if (sendto(s, pktbuf, pktlen, 0, + (struct sockaddr *) &send_addr, + sizeof(send_addr)) < 0) + { + report(LOG_ERR, "sendto: %s", get_network_errmsg()); + } +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/bootptab.5 b/libexec/bootpd/bootptab.5 index 39f51216578..9ccd6cbf6c9 100644 --- a/libexec/bootpd/bootptab.5 +++ b/libexec/bootpd/bootptab.5 @@ -1,6 +1,6 @@ .\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University .\" -.\" $Header: /cvsroot/src/libexec/bootpd/Attic/bootptab.5,v 1.1 1994/04/18 05:15:58 glass Exp $ +.\" $Header: /cvsroot/src/libexec/bootpd/Attic/bootptab.5,v 1.2 1994/05/24 15:20:19 gwr Exp $ .\" .TH BOOTPTAB 5 "October 31, 1991" "Carnegie Mellon University" .UC 6 @@ -23,9 +23,13 @@ colons (:), with a general format of: .PP where .I hostname -is the actual name of a bootp client and +is the actual name of a bootp client (or a "dummy entry"), and .I tg -is a two-character tag symbol. Most tags must be followed by an equals-sign +is a two-character tag symbol. Dummy entries have an invalid hostname +(one with a "." as the first character) and are used to provide +default values used by other entries via the +.B tc=.dummy-entry +mechanism. Most tags must be followed by an equals-sign and a value as above. Some may also appear in a boolean form with no value (i.e. .RI : tg :). @@ -44,6 +48,8 @@ The currently recognized tags are: .br ds Domain name server address list .br + ef Extension file +.br gw Gateway address list .br ha Host hardware address @@ -64,6 +70,10 @@ The currently recognized tags are: .br ns IEN-116 name server address list .br + nt NTP (time) Server (RFC 1129) +.br + ra Reply address override +.br rl Resource location protocol server address list .br rp Root path to mount as root @@ -83,6 +93,10 @@ The currently recognized tags are: ts Time server address list .br vm Vendor magic cookie selector +.br + yd YP (NIS) domain name +.br + ys YP (NIS) server address .PP There is also a generic tag, @@ -105,6 +119,8 @@ The following tags take a whitespace-separated list of IP addresses: .BR lg , .BR lp , .BR ns , +.BR nt , +.BR ra , .BR rl , and .BR ts . @@ -112,12 +128,22 @@ The .BR ip , .BR sa , .BR sw , +.BR sm , and -.B sm +.B ys tags each take a single IP address. -All IP addresses are specified in standard Internet "dot" notation and may use -decimal, octal, or hexadecimal numbers (octal numbers begin with 0, -hexadecimal numbers begin with '0x' or '0X'). +All IP addresses are specified in standard Internet "dot" notation +and may use decimal, octal, or hexadecimal numbers +(octal numbers begin with 0, hexadecimal numbers begin with '0x' or '0X'). +Any IP addresses may alternatively be specified as a hostname, causing +.I bootpd +to lookup the IP address for that host name using gethostbyname(3). +If the +.B ip +tag is not specified, +.I bootpd +will determine the IP address using the entry name as the host name. +(Dummy entries use an invalid host name to avoid automatic IP lookup.) .PP The .B ht @@ -145,7 +171,8 @@ or for Chaos, ARCNET, and AX.25 Amateur Radio networks, respectively. The .B ha -tag takes a hardware address which +tag takes a hardware address which may be specified as a host name +or in numeric form. Note that the numeric form .I must be specified in hexadecimal; optional periods and/or a leading '0x' may be included for readability. The @@ -155,6 +182,10 @@ tag must be preceded by the tag (either explicitly or implicitly; see .B tc below). +If the hardware address is not specified and the type is specified +as either "ethernet" or "ieee802", then +.I bootpd +will try to determine the hardware address using ether_hton(3). .PP The hostname, home directory, and bootfile are ASCII strings which may be optionally surrounded by double quotes ("). The client's request and the @@ -165,42 +196,18 @@ and symbols determine how the server fills in the bootfile field of the bootp reply packet. .PP -If the client specifies an absolute pathname and that file exists on the -server machine, that pathname is returned in the reply packet. If the file -cannot be found, the request is discarded; no reply is sent. If the client -specifies a relative pathname, a full pathname is formed by prepending the -value of the -.B hd -tag and testing for existence of the file. If the -.B hd -tag is not supplied in the configuration file or if the resulting boot file -cannot be found, then the request is discarded. -.PP -Clients which specify null boot files will always elicit a reply from the -server. The exact reply will again depend upon the -.B hd -and +If the client provides a file name it is left as is. +Otherwise, if the .B bf -tags. If the -.B bf -tag gives an absolute pathname and the file exists, that pathname is returned -in the reply packet. Otherwise, if the +option is specified its value is copied into the reply packet. +If the .B hd -and -.B bf -tags together specify an accessible file, that filename is returned in the -reply. If a complete filename cannot be determined or the file does not -exist, the reply will contain a zeroed-out bootfile field. -.PP -In all these cases, existence of the file means that, in addition to actually -being present, the file must have its public read access bit set, since this -is required by -.IR tftpd (8) -to permit the file transfer. Also, all filenames are first tried as -.I filename.hostname -and then simply as -.IR filename , -thus providing for individual per-host bootfiles. +option is specified as well, its value is prepended to the +boot file copied into the reply packet. +The existence of the boot file is checked only if the +.BR bs =auto +option is used (to determine the boot file size). +A reply may be sent whether or not the boot file exists. .PP Some newer versions of .I tftpd @@ -214,6 +221,9 @@ tag may be used to inform .I bootpd of this special root directory used by .IR tftpd . +(One may alternatively use the +.I bootpd +"-c chdir" option.) The .B hd tag is actually relative to the root directory specified by the @@ -343,57 +353,38 @@ An example .I /etc/bootptab file follows: .PP +.nf + # Sample bootptab file (domain=andrew.cmu.edu) -.br - # Sample bootptab file - -.br - default1:\\ -.br + .default:\\ :hd=/usr/boot:bf=null:\\ -.br - :ds=128.2.35.50 128.2.13.21:\\ -.br - :ns=0x80020b4d 0x80020ffd:\\ -.br - :ts=0x80020b4d 0x80020ffd:\\ -.br - :sm=255.255.0.0:gw=0x8002fe24:\\ -.br - :hn:vm=auto:to=-18000:\\ -.br - :T37=0x12345927AD3BCF:T99="Special ASCII string": + :ds=netserver, lancaster:\\ + :ns=pcs2, pcs1:\\ + :ts=pcs2, pcs1:\\ + :sm=255.255.255.0:\\ + :gw=gw.cs.cmu.edu:\\ + :hn:to=-18000: -.br - carnegie:ht=6:ha=7FF8100000AF:ip=128.2.11.1:tc=default1: -.br - baldwin:ht=1:ha=0800200159C3:ip=128.2.11.10:tc=default1: -.br - wylie:ht=1:ha=00DD00CADF00:ip=128.2.11.100:tc=default1: -.br - arnold:ht=1:ha=0800200102AD:ip=128.2.11.102:tc=default1: -.br - bairdford:ht=1:ha=08002B02A2F9:ip=128.2.11.103:tc=default1: -.br - bakerstown:ht=1:ha=08002B0287C8:ip=128.2.11.104:tc=default1: - -.br - # Special domain name server for next host -.br - butlerjct:ht=1:ha=08002001560D:ip=128.2.11.108:ds=128.2.13.42:tc=default1: + carnegie:ht=6:ha=7FF8100000AF:tc=.default: + baldwin:ht=1:ha=0800200159C3:tc=.default: + wylie:ht=1:ha=00DD00CADF00:tc=.default: + arnold:ht=1:ha=0800200102AD:tc=.default: + bairdford:ht=1:ha=08002B02A2F9:tc=.default: + bakerstown:ht=1:ha=08002B0287C8:tc=.default: -.br - gastonville:ht=6:ha=7FFF81000A47:ip=128.2.11.115:tc=default1: -.br - hahntown:ht=6:ha=7FFF81000434:ip=128.2.11.117:tc=default1: -.br - hickman:ht=6:ha=7FFF810001BA:ip=128.2.11.118:tc=default1: -.br - lowber:ht=1:ha=00DD00CAF000:ip=128.2.11.121:tc=default1: -.br - mtoliver:ht=1:ha=00DD00FE1600:ip=128.2.11.122:tc=default1: + # Special domain name server and option tags for next host + butlerjct:ha=08002001560D:ds=128.2.13.42:\\ + :T37=0x12345927AD3BCF:\\ + :T99="Special ASCII string":\\ + :tc=.default: + gastonville:ht=6:ha=7FFF81000A47:tc=.default: + hahntown:ht=6:ha=7FFF81000434:tc=.default: + hickman:ht=6:ha=7FFF810001BA:tc=.default: + lowber:ht=1:ha=00DD00CAF000:tc=.default: + mtoliver:ht=1:ha=00DD00FE1600:tc=.default: +.fi .SH FILES /etc/bootptab diff --git a/libexec/bootpd/bootptab.cmu b/libexec/bootpd/bootptab.cmu new file mode 100644 index 00000000000..66212d421a2 --- /dev/null +++ b/libexec/bootpd/bootptab.cmu @@ -0,0 +1,124 @@ +# /etc/bootptab: database for bootp server (/etc/bootpd) +# (I've hacked on this but can't test it... -gwr) + +# Blank lines and lines beginning with '#' are ignored. +# +# Legend: (see bootptab.5) +# first field -- hostname (not indented) +# bf -- bootfile +# bs -- bootfile size in 512-octet blocks +# cs -- cookie servers +# df -- dump file name +# dn -- domain name +# ds -- domain name servers +# ef -- extension file +# gw -- gateways +# ha -- hardware address +# hd -- home directory for bootfiles +# hn -- host name set for client +# ht -- hardware type +# im -- impress servers +# ip -- host IP address +# lg -- log servers +# lp -- LPR servers +# ns -- IEN-116 name servers +# ra -- reply address +# rl -- resource location protocol servers +# rp -- root path +# sa -- boot server address +# sm -- subnet mask +# sw -- swap server +# tc -- template host (points to similar host entry) +# td -- TFTP directory +# to -- time offset (seconds) +# ts -- time servers +# vm -- vendor magic number +# Tn -- generic option tag n +# +# Be careful about including backslashes where they're needed. Weird (bad) +# things can happen when a backslash is omitted where one is intended. +# Also, note that generic option data must be either a string or a +# sequence of bytes where each byte is a two-digit hex value. + +# First, we define a global entry which specifies the stuff every host uses. +# (Host name lookups are relative to the domain: andrew.cmu.edu) +.default:\ + :hn:dn=cmu.edu:\ + :hd=/usr/boot:\ + :ds=netserver, lancaster:\ + :ns=pcs2, pcs1:\ + :ts=pcs2, pcs1:\ + :sm=255.255.0.0:\ + :gw=gw.cs.cmu.edu:\ + to=auto: + + +# Next, we can define different master entries for each subnet. . . +.subnet13 :sm=255.255.255.0:gw=128.2.13.1 :tc=.default: +.subnet19 :sm=255.255.255.0:gw=128.2.19.1 :tc=.default: +.subnet232 :sm=255.255.255.0:gw=128.2.232.1 :tc=.default: + +# +# We should be able to use as many levels of indirection as desired. Use +# your imagination. . . +# + + +# Individual entries (could also have different servers for some/all of these +# hosts, but we don't really use this feature at CMU): + +carnegie:tc=.subnet13:ht=ieee802:ha=7FF8100000AF: +baldwin:tc=.subnet19:ha=0800200159C3: +wylie:tc=.subnet232:ha=00DD00CADF00: +arnold:tc=.subnet19:ha=0800200102AD: +bairdford:tc=.subnet19:ha=08002B02A2F9: +bakerstown:tc=.subnet19:ha=08002B0287C8: +butlerjct:tc=.subnet232:ha=08002001560D: +gastonville:tc=.subnet232:ht=ieee802:ha=7FFF81000A47: +hahntown:tc=.subnet13:ht=ieee802:ha=7FFF81000434: +hickman:tc=.subnet19:ht=ieee802:ha=7FFF810001BA: +lowber:tc=.subnet13:ha=00DD00CAF000: +mtoliver:tc=.subnet19:ha=00DD00FE1600: +osborne:tc=.subnet232:ha=00DD00CAD600: +russelton:tc=.subnet232:ha=080020017FC3: +thornburg:tc=.subnet13:ha=080020012A33: + + +# Hmmm. . . Let's throw in some whitespace for readability. . . . + +andrew: tc=.subnet19:ha=00DD00C88900: +birdville: tc=.subnet19:ha=00DD00FE2D00: +coudersport: tc=.subnet13:ha=00DD00CB1E00: +bridgeville: tc=.subnet232:ha=080020011394: +franklin: tc=.subnet19:ha=08002B02A5D5: +hollidaysburg: tc=.subnet19:ht=ieee802:ha=7FFF810002C8: +honesdale: tc=.subnet19:ha=08002B02F83F: +huntingdon: tc=.subnet19:ha=08002B02E410: +indiana: tc=.subnet13:ha=08002B029BEC: +jimthorpe: tc=.subnet232:ha=08002B02FBBA: +kittanning: tc=.subnet232:ha=08002B0273FC: +lebanon: tc=.subnet232:ha=08002B037F67: +lewisburg: tc=.subnet19:ha=50005A1A0DE4: +middleburg: tc=.subnet232:ha=00DD00FE1200: +aspinwall: tc=.subnet13:ha=08002B03C163: +berlin: tc=.subnet13:ha=00DD000A4400: +norristown: tc=.subnet13:ha=08002001455B: +pottsville: tc=.subnet13:ha=00DD000A3700: +ridgway: tc=.subnet19:ha=08002B029425: +scranton: tc=.subnet232:ha=0800200113A1: +chalfont: tc=.subnet13:ha=08002001124B: +washington: tc=.subnet19:ha=00DD00656E00: +wellsboro: tc=.subnet13:ha=00DD00CB1C00: +bb1: tc=.subnet19:ha=00DD000A1F00: +adamstown: tc=.subnet13:ha=08002B02D0E6: +beta: tc=.subnet19:ha=02070100B197: +carbondale: tc=.subnet232:ha=08002B022A73: +clairton: tc=.subnet19:ha=080020010FD1: +egypt: tc=.subnet13:ha=00DD00847B00: +fairchance: tc=.subnet232:ha=00DD000AB100: +fairhope: tc=.subnet232:ha=00DD00CB0800: +galeton: tc=.subnet232:ha=08002001138C: +imperial: tc=.subnet232:ha=08002001130C: +kingston: tc=.subnet232:ha=080020011382: +knox: tc=.subnet232:ha=50005A1A0D2A: +lakecity: tc=.subnet13:ha=080020011380: diff --git a/libexec/bootpd/bootptab.mcs b/libexec/bootpd/bootptab.mcs new file mode 100644 index 00000000000..c12ef71d6fd --- /dev/null +++ b/libexec/bootpd/bootptab.mcs @@ -0,0 +1,93 @@ +# /etc/bootptab: database for bootp server (/etc/bootpd) +# Last update: gwr, Sun Dec 12 19:00:00 EDT 1993 +# Blank lines and lines beginning with '#' are ignored. +# +# Legend: (see bootptab.5) +# first field -- hostname (not indented) +# bf -- bootfile +# bs -- bootfile size in 512-octet blocks +# cs -- cookie servers +# df -- dump file name +# dn -- domain name +# ds -- domain name servers +# ef -- extension file +# gw -- gateways +# ha -- hardware address +# hd -- home directory for bootfiles +# hn -- host name set for client +# ht -- hardware type +# im -- impress servers +# ip -- host IP address +# lg -- log servers +# lp -- LPR servers +# ns -- IEN-116 name servers +# ra -- reply address +# rl -- resource location protocol servers +# rp -- root path +# sa -- boot server address +# sm -- subnet mask +# sw -- swap server +# tc -- template host (points to similar host entry) +# td -- TFTP directory +# to -- time offset (seconds) +# ts -- time servers +# vm -- vendor magic number +# Tn -- generic option tag n +# +# Be careful about including backslashes where they're needed. Weird (bad) +# things can happen when a backslash is omitted where one is intended. +# Also, note that generic option data must be either a string or a +# sequence of bytes where each byte is a two-digit hex value. + +# First, we define a global entry which specifies the stuff every host uses. + +# If you leave "td" and "hd" unset, run bootpd with the "-c /tftpboot" +# switch so path names (boot files) will be interpreted relative to +# the same directory as tftpd will use as its CWD. +.default:\ + :hn:dn="mc.com":\ + :td=/tftpboot:\ + :ds=merlin, jericho:\ + :to=auto: + +# Next, we can define different master entries for each subnet. . . + +.subnet16:\ + :tc=.default:\ + :sm=255.255.255.0:\ + :gw=merlin:\ + :sa=merlin: + +.subnet17:\ + :tc=.default:\ + :sm=255.255.255.0:\ + :gw=merlin-gw:\ + :sa=merlin-gw: + +# +# We should be able to use as many levels of indirection as desired. Use +# your imagination. . . +# + +# Individual entries (could also have different servers for some/all of these +# hosts, but we don't really use this feature at CMU): + +# Emulex terminal server +emulex: tc=.subnet16:ha=00.00.C9.00.42.E0:bf=P4KTL0E: + +# Lantronix eps1 +eps1: tc=.subnet16:ha=00.80.A3.04.1D.78: + +# Tadpole 885 board. +tp885: tc=.subnet17:ha=08.00.4C.00.2F.74:bf=tp885sys2.cfe: + +# MVME147 VxWorks board. +mvme147:tc=.subnet17:ha=08.00.3e.20.da.47:bf=mv147vxw.st: + +# These are just for testing + +walnut:tc=.subnet16:ha=walnut:sa=merlin-gw:\ + :td@:hd=/export/walnut/: + +banana:tc=.subnet17:ha=banana:bf=boot.sun4c: +thor:tc=.subnet17:ha=thor:bf=boot.sun4e: diff --git a/libexec/bootpd/bootptest.8 b/libexec/bootpd/bootptest.8 new file mode 100644 index 00000000000..a564bcf0a33 --- /dev/null +++ b/libexec/bootpd/bootptest.8 @@ -0,0 +1,56 @@ +.\" bootptest.8 +.TH BOOTPTEST 8 "10 June 1993" "MAINTENANCE COMMANDS" +.SH NAME +bootptest \- send BOOTP queries and print responses +.SH SYNOPSIS +.LP +.B bootptest +[ +.B \-h +] +.I server\-name +.RI [ template-file ] +.SH DESCRIPTION +.B bootptest +sends BOOTP requests to the host specified as +.I server\-name +at one\-second intervals until either a response is received, +or until ten requests have gone unanswered. +After a response is received, +.B bootptest +will wait one more second listening for additional responses. +.SH OPTIONS +.TP +.B \-h +Use the hardware (Ethernet) address to identify the client. +By default, the IP address is copied into the request +indicating that this client already knows its IP address. +.LP +A +.I template-file +may be specified, in which case +.B bootptest +uses the (binary) contents of this file to initialize the +.I options +area of the request packet. +.SH CREDITS +.LP +The bootptest program is a combination of original and derived works. +The main program module (bootptest.c) is original work by +Gordon W. Ross <gwr@mc.com>. +The packet printing module (print-bootp.c) is a slightly modified +version of a file from the BSD tcpdump program. +.LP +This program includes software developed by the University of +California, Lawrence Berkeley Laboratory and its contributors. +(See the copyright notice in print-bootp.c) +.SH "SEE ALSO" +.LP +bootpd(8) +.SH REFERENCES +.TP +RFC951 +BOOTSTRAP PROTOCOL (BOOTP) +.TP +RFC1048 +BOOTP Vendor Information Extensions diff --git a/libexec/bootpd/bootptest.c b/libexec/bootpd/bootptest.c new file mode 100644 index 00000000000..b288641232e --- /dev/null +++ b/libexec/bootpd/bootptest.c @@ -0,0 +1,484 @@ +/* + * bootptest.c - Test out a bootp server. + * + * This simple program was put together from pieces taken from + * various places, including the CMU BOOTP client and server. + * The packet printing routine is from the Berkeley "tcpdump" + * program with some enhancements I added. The print-bootp.c + * file is shared with my copy of "tcpdump" and therefore uses + * some unusual utility routines that would normally be provided + * by various parts of the tcpdump program. + * + * Boilerplate: + * + * This program includes software developed by the University of + * California, Lawrence Berkeley Laboratory and its contributors. + * (See the copyright notice in print-bootp.c) + * + * The remainder of this program is public domain. You may do + * whatever you like with it except claim that you wrote it. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * HISTORY: + * + * 12/02/93 Released version 1.4 (with bootp-2.3.2) + * 11/05/93 Released version 1.3 + * 10/14/93 Released version 1.2 + * 10/11/93 Released version 1.1 + * 09/28/93 Released version 1.0 + * 09/93 Original developed by Gordon W. Ross <gwr@mc.com> + */ + +char *usage = "bootptest [-h] server-name [vendor-data-template-file]"; + +#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> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <stdlib.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <netdb.h> +#include <assert.h> + +#include "bootp.h" +#include "bootptest.h" +#include "getif.h" +#include "patchlevel.h" + +#define LOG_ERR 1 +#define BUFLEN 1024 +#define WAITSECS 1 +#define MAXWAIT 10 + +int vflag = 1; +int tflag = 0; +int thiszone; +char *progname; +unsigned char *packetp; +unsigned char *snapend; +int snaplen; + + +/* + * 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_server; /* where to send requests */ +struct sockaddr_in sin_client; /* for bind and listen */ +struct sockaddr_in sin_from; /* Packet source */ +u_char eaddr[16]; /* Ethernet address */ + +/* + * General + */ + +int debug = 1; /* Debugging flag (level) */ +char hostname[64]; +char *sndbuf; /* Send packet buffer */ +char *rcvbuf; /* Receive packet buffer */ + +/* + * Vendor magic cookies for CMU and RFC1048 + */ + +unsigned char vm_cmu[4] = VM_CMU; +unsigned char vm_rfc1048[4] = VM_RFC1048; +short secs; /* How long client has waited */ + +char *get_errmsg(); +extern void bootp_print(); + +/* + * Initialization such as command-line processing is done, then + * the receiver loop is started. Die when interrupted. + */ + +main(argc, argv) + int argc; + char **argv; +{ + struct bootp *bp; + struct servent *sep; + struct hostent *hep; + + char *servername = NULL; + char *vendor_file = NULL; + char *bp_file = NULL; + int32 server_addr; /* inet addr, network order */ + int s; /* Socket file descriptor */ + int n, tolen, fromlen, recvcnt; + int use_hwa = 0; + int32 xid; + + progname = strrchr(argv[0], '/'); + if (progname) + progname++; + else + progname = argv[0]; + argc--; + argv++; + + if (debug) + printf("%s: version %s.%d\n", progname, VERSION, PATCHLEVEL); + + /* Debugging for compilers with struct padding. */ + assert(sizeof(struct bootp) == BP_MINPKTSZ); + + sndbuf = malloc(BUFLEN); + rcvbuf = malloc(BUFLEN); + if (!sndbuf || !rcvbuf) { + printf("malloc failed\n"); + exit(1); + } + /* Handle option switches. */ + while (argc > 0) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'f': /* File name to reqest. */ + if (argc < 2) + goto error; + argc--; argv++; + bp_file = *argv; + break; + + case 'h': /* Use hardware address. */ + use_hwa = 1; + break; + + error: + default: + puts(usage); + exit(1); + + } + argc--; + argv++; + } + + /* Get server name (or address) for query. */ + if (argc > 0) { + servername = *argv; + argc--; + argv++; + } + /* Get optional vendor-data-template-file. */ + if (argc > 0) { + vendor_file = *argv; + argc--; + argv++; + } + if (!servername) { + printf("missing server name.\n"); + puts(usage); + exit(1); + } + /* + * Create a socket. + */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + exit(1); + } + /* + * Get server's listening port number + */ + sep = getservbyname("bootps", "udp"); + if (sep) { + bootps_port = ntohs((u_short) sep->s_port); + } else { + fprintf(stderr, "udp/bootps: unknown service -- using port %d\n", + IPPORT_BOOTPS); + bootps_port = (u_short) IPPORT_BOOTPS; + } + + /* + * Set up server socket address (for send) + */ + if (servername) { + if (isdigit(servername[0])) + server_addr = inet_addr(servername); + else { + hep = gethostbyname(servername); + if (!hep) { + fprintf(stderr, "%s: unknown host\n", servername); + exit(1); + } + bcopy(hep->h_addr, &server_addr, sizeof(server_addr)); + } + } else { + /* Get broadcast address */ + /* XXX - not yet */ + server_addr = INADDR_ANY; + } + sin_server.sin_family = AF_INET; + sin_server.sin_port = htons(bootps_port); + sin_server.sin_addr.s_addr = server_addr; + + /* + * Get client's listening port number + */ + sep = getservbyname("bootpc", "udp"); + if (sep) { + bootpc_port = ntohs(sep->s_port); + } else { + fprintf(stderr, "udp/bootpc: unknown service -- using port %d\n", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; + } + + /* + * Set up client socket address (for listen) + */ + sin_client.sin_family = AF_INET; + sin_client.sin_port = htons(bootpc_port); + sin_client.sin_addr.s_addr = INADDR_ANY; + + /* + * Bind client socket to BOOTPC port. + */ + if (bind(s, (struct sockaddr *) &sin_client, sizeof(sin_client)) < 0) { + perror("bind BOOTPC port"); + if (errno == EACCES) + fprintf(stderr, "You need to run this as root\n"); + exit(1); + } + /* + * Build a request. + */ + bp = (struct bootp *) sndbuf; + bzero(bp, sizeof(*bp)); + bp->bp_op = BOOTREQUEST; + xid = (int32) getpid(); + bp->bp_xid = (u_int32) htonl(xid); + if (bp_file) + strncpy(bp->bp_file, bp_file, BP_FILE_LEN); + + /* + * Fill in the hardware address (or client IP address) + */ + if (use_hwa) { + struct ifreq *ifr; + + ifr = getif(s, &sin_server.sin_addr); + if (!ifr) { + printf("No interface for %s\n", servername); + exit(1); + } + if (getether(ifr->ifr_name, eaddr)) { + printf("Can not get ether addr for %s\n", ifr->ifr_name); + exit(1); + } + /* Copy Ethernet address into request packet. */ + bp->bp_htype = 1; + bp->bp_hlen = 6; + bcopy(eaddr, bp->bp_chaddr, bp->bp_hlen); + } else { + /* Fill in the client IP address. */ + gethostname(hostname, sizeof(hostname)); + hep = gethostbyname(hostname); + if (!hep) { + printf("Can not get my IP address\n"); + exit(1); + } + bcopy(hep->h_addr, &bp->bp_ciaddr, hep->h_length); + } + + /* + * Copy in the default vendor data. + */ + bcopy(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)); + bp->bp_vend[4] = TAG_END; + + /* + * Read in the "options" part of the request. + * This also determines the size of the packet. + */ + snaplen = sizeof(*bp); + if (vendor_file) { + int fd = open(vendor_file, 0); + if (fd < 0) { + perror(vendor_file); + exit(1); + } + /* Compute actual space for options. */ + n = BUFLEN - sizeof(*bp) + BP_VEND_LEN; + n = read(fd, bp->bp_vend, n); + close(fd); + if (n < 0) { + perror(vendor_file); + exit(1); + } + printf("read %d bytes of vendor template\n", n); + if (n > BP_VEND_LEN) { + printf("warning: extended options in use (len > %d)\n", + BP_VEND_LEN); + snaplen += (n - BP_VEND_LEN); + } + } + /* + * Set globals needed by print_bootp + * (called by send_request) + */ + packetp = (unsigned char *) eaddr; + snapend = (unsigned char *) sndbuf + snaplen; + + /* Send a request once per second while waiting for replies. */ + recvcnt = 0; + bp->bp_secs = secs = 0; + send_request(s); + while (1) { + struct timeval tv; + int readfds; + + tv.tv_sec = WAITSECS; + tv.tv_usec = 0L; + readfds = (1 << s); + n = select(s + 1, (fd_set *) & readfds, NULL, NULL, &tv); + if (n < 0) { + perror("select"); + break; + } + if (n == 0) { + /* + * We have not received a response since the last send. + * If we have ever received any responses, exit now. + * Otherwise, bump the "wait time" field and re-send. + */ + if (recvcnt > 0) + exit(0); + secs += WAITSECS; + if (secs > MAXWAIT) + break; + bp->bp_secs = htons(secs); + send_request(s); + continue; + } + fromlen = sizeof(sin_from); + n = recvfrom(s, rcvbuf, BUFLEN, 0, + (struct sockaddr *) &sin_from, &fromlen); + if (n <= 0) { + continue; + } + if (n < sizeof(struct bootp)) { + printf("received short packet\n"); + continue; + } + recvcnt++; + + /* Print the received packet. */ + printf("Recvd from %s", inet_ntoa(sin_from.sin_addr)); + /* set globals needed by bootp_print() */ + snaplen = n; + snapend = (unsigned char *) rcvbuf + snaplen; + bootp_print(rcvbuf, n, sin_from.sin_port, 0); + putchar('\n'); + /* + * This no longer exits immediately after receiving + * one response because it is useful to know if the + * client might get multiple responses. This code + * will now listen for one second after a response. + */ + } + fprintf(stderr, "no response from %s\n", servername); + exit(1); +} + +send_request(s) + int s; +{ + /* Print the request packet. */ + printf("Sending to %s", inet_ntoa(sin_server.sin_addr)); + bootp_print(sndbuf, snaplen, sin_from.sin_port, 0); + putchar('\n'); + + /* Send the request packet. */ + if (sendto(s, sndbuf, snaplen, 0, + (struct sockaddr *) &sin_server, + sizeof(sin_server)) < 0) + { + perror("sendto server"); + exit(1); + } +} + +/* + * Print out a filename (or other ascii string). + * Return true if truncated. + */ +int +printfn(s, ep) + register u_char *s, *ep; +{ + register u_char c; + + putchar('"'); + while (c = *s++) { + if (s > ep) { + putchar('"'); + return (1); + } + if (!isascii(c)) { + c = toascii(c); + putchar('M'); + putchar('-'); + } + if (!isprint(c)) { + c ^= 0x40; /* DEL to ?, others to alpha */ + putchar('^'); + } + putchar(c); + } + putchar('"'); + return (0); +} + +/* + * Convert an IP addr to a string. + * (like inet_ntoa, but ina is a pointer) + */ +char * +ipaddr_string(ina) + struct in_addr *ina; +{ + static char b[24]; + u_char *p; + + p = (u_char *) ina; + sprintf(b, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + return (b); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/bootptest.h b/libexec/bootpd/bootptest.h new file mode 100644 index 00000000000..27f78ba963b --- /dev/null +++ b/libexec/bootpd/bootptest.h @@ -0,0 +1,30 @@ +/* bootptest.h */ +/* + * Hacks for sharing print-bootp.c between tcpdump and bootptest. + */ +#define ESRC(p) (p) +#define EDST(p) (p) + +#ifndef USE_BFUNCS +/* Use mem/str functions */ +/* There are no overlapped copies, so memcpy is OK. */ +#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) +#endif + +extern int vflag; /* verbose flag */ + +/* global pointers to beginning and end of current packet (during printing) */ +extern unsigned char *packetp; +extern unsigned char *snapend; + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern char *ipaddr_string P((struct in_addr *)); + +#undef P diff --git a/libexec/bootpd/bptypes.h b/libexec/bootpd/bptypes.h new file mode 100644 index 00000000000..537da4e5e57 --- /dev/null +++ b/libexec/bootpd/bptypes.h @@ -0,0 +1,23 @@ +/* bptypes.h */ + +#ifndef BPTYPES_H +#define BPTYPES_H + +/* + * 32 bit integers are different types on various architectures + */ + +#ifndef int32 +#define int32 long +#endif +typedef unsigned int32 u_int32; + +/* + * Nice typedefs. . . + */ + +typedef int boolean; +typedef unsigned char byte; + + +#endif /* BPTYPES_H */ diff --git a/libexec/bootpd/dovend.c b/libexec/bootpd/dovend.c new file mode 100644 index 00000000000..ba6ab288b8b --- /dev/null +++ b/libexec/bootpd/dovend.c @@ -0,0 +1,413 @@ +/* + * dovend.c : Inserts all but the first few vendor options. + */ + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +# include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +# 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 strchr +#endif + +#include "bootp.h" +#include "bootpd.h" +#include "report.h" +#include "dovend.h" + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +PRIVATE int insert_generic P((struct shared_bindata *, byte **, int *)); + +/* + * Insert the 2nd part of the options into an option buffer. + * Return amount of space used. + * + * This inserts everything EXCEPT: + * magic cookie, subnet mask, gateway, bootsize, extension file + * Those are handled separately (in bootpd.c) to allow this function + * to be shared between bootpd and bootpef. + * + * When an "extension file" is in use, the options inserted by + * this function go into the exten_file, not the bootp response. + */ + +int +dovend_rfc1497(hp, buf, len) + struct host *hp; + byte *buf; + int len; +{ + int bytesleft = len; + byte *vp = buf; + char *tmpstr; + + static char noroom[] = "%s: No room for \"%s\" option"; +#define NEED(LEN, MSG) do \ + if (bytesleft < (LEN)) { \ + report(LOG_NOTICE, noroom, \ + hp->hostname->string, MSG); \ + return (vp - buf); \ + } while (0) + + /* + * Note that the following have already been inserted: + * magic_cookie, subnet_mask, gateway, bootsize + * + * The remaining options are inserted in order of importance. + * (Of course the importance of each is a matter of opinion.) + * The option insertion order should probably be configurable. + * + * This is the order used in the NetBSD version. Can anyone + * explain why the time_offset and swap_server are first? + * Also, why is the hostname so far down the list? -gwr + */ + + if (hp->flags.time_offset) { + NEED(6, "to"); + *vp++ = TAG_TIME_OFFSET;/* -1 byte */ + *vp++ = 4; /* -1 byte */ + insert_u_long(htonl(hp->time_offset), &vp); /* -4 bytes */ + bytesleft -= 6; + } + /* + * swap server, root path, dump path + */ + if (hp->flags.swap_server) { + NEED(6, "sw"); + /* There is just one SWAP_SERVER, so it is not an iplist. */ + *vp++ = TAG_SWAP_SERVER;/* -1 byte */ + *vp++ = 4; /* -1 byte */ + insert_u_long(hp->swap_server.s_addr, &vp); /* -4 bytes */ + bytesleft -= 6; /* Fix real count */ + } + if (hp->flags.root_path) { + /* + * Check for room for root_path. Add 2 to account for + * TAG_ROOT_PATH and length. + */ + len = strlen(hp->root_path->string); + NEED((len + 2), "rp"); + *vp++ = TAG_ROOT_PATH; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->root_path->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + if (hp->flags.dump_file) { + /* + * Check for room for dump_file. Add 2 to account for + * TAG_DUMP_FILE and length. + */ + len = strlen(hp->dump_file->string); + NEED((len + 2), "df"); + *vp++ = TAG_DUMP_FILE; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->dump_file->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* + * DNS server and domain + */ + if (hp->flags.domain_server) { + if (insert_ip(TAG_DOMAIN_SERVER, + hp->domain_server, + &vp, &bytesleft)) + NEED(8, "ds"); + } + if (hp->flags.domain_name) { + /* + * Check for room for domain_name. Add 2 to account for + * TAG_DOMAIN_NAME and length. + */ + len = strlen(hp->domain_name->string); + NEED((len + 2), "dn"); + *vp++ = TAG_DOMAIN_NAME; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->domain_name->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* + * NIS (YP) server and domain + */ + if (hp->flags.nis_server) { + if (insert_ip(TAG_NIS_SERVER, + hp->nis_server, + &vp, &bytesleft)) + NEED(8, "ds"); + } + if (hp->flags.nis_domain) { + /* + * Check for room for nis_domain. Add 2 to account for + * TAG_NIS_DOMAIN and length. + */ + len = strlen(hp->nis_domain->string); + NEED((len + 2), "dn"); + *vp++ = TAG_NIS_DOMAIN; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->nis_domain->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* IEN 116 name server */ + if (hp->flags.name_server) { + if (insert_ip(TAG_NAME_SERVER, + hp->name_server, + &vp, &bytesleft)) + NEED(8, "ns"); + } + if (hp->flags.rlp_server) { + if (insert_ip(TAG_RLP_SERVER, + hp->rlp_server, + &vp, &bytesleft)) + NEED(8, "rl"); + } + /* Time server (RFC 868) */ + if (hp->flags.time_server) { + if (insert_ip(TAG_TIME_SERVER, + hp->time_server, + &vp, &bytesleft)) + NEED(8, "ts"); + } + /* NTP (time) Server (RFC 1129) */ + if (hp->flags.ntp_server) { + if (insert_ip(TAG_NTP_SERVER, + hp->ntp_server, + &vp, &bytesleft)) + NEED(8, "ts"); + } + /* + * I wonder: If the hostname were "promoted" into the BOOTP + * response part, might these "extension" files possibly be + * shared between several clients? + * + * Also, why not just use longer BOOTP packets with all the + * additional length used as option data. This bootpd version + * already supports that feature by replying with the same + * packet length as the client request packet. -gwr + */ + if (hp->flags.name_switch && hp->flags.send_name) { + /* + * Check for room for hostname. Add 2 to account for + * TAG_HOST_NAME and length. + */ + len = strlen(hp->hostname->string); +#if 0 + /* + * XXX - Too much magic. The user can always set the hostname + * to the short version in the bootptab file. -gwr + */ + 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->string; + len = 0; + while (*tmpstr && (*tmpstr != '.')) { + tmpstr++; + len++; + } + } +#endif + NEED((len + 2), "hn"); + *vp++ = TAG_HOST_NAME; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->hostname->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* + * The rest of these are less important, so they go last. + */ + if (hp->flags.lpr_server) { + if (insert_ip(TAG_LPR_SERVER, + hp->lpr_server, + &vp, &bytesleft)) + NEED(8, "lp"); + } + if (hp->flags.cookie_server) { + if (insert_ip(TAG_COOKIE_SERVER, + hp->cookie_server, + &vp, &bytesleft)) + NEED(8, "cs"); + } + if (hp->flags.log_server) { + if (insert_ip(TAG_LOG_SERVER, + hp->log_server, + &vp, &bytesleft)) + NEED(8, "lg"); + } + /* + * XXX - Add new tags here (to insert options) + */ + if (hp->flags.generic) { + if (insert_generic(hp->generic, &vp, &bytesleft)) + NEED(64, "(generic)"); + } + /* + * The end marker is inserted by the caller. + */ + return (vp - buf); +#undef NEED +} /* dovend_rfc1497 */ + + + +/* + * Insert a tag value, a length value, and a list of IP addresses into the + * memory buffer indirectly pointed to by "dest". "tag" is the RFC1048 tag + * number to use, "iplist" is a pointer to a list of IP addresses + * (struct in_addr_list), and "bytesleft" points to an integer which + * indicates the size of the "dest" buffer. + * + * Return zero if everything fits. + * + * This is used to fill the vendor-specific area of a bootp packet in + * conformance to RFC1048. + */ + +int +insert_ip(tag, iplist, dest, bytesleft) + byte tag; + struct in_addr_list *iplist; + byte **dest; + int *bytesleft; +{ + struct in_addr *addrptr; + unsigned addrcount = 1; + byte *d; + + if (iplist == NULL) + return (0); + + if (*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); + } + return (addrcount); +} + + + +/* + * 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. + * Return zero if everything fits. + */ + +static int +insert_generic(gendata, buff, bytesleft) + struct shared_bindata *gendata; + byte **buff; + int *bytesleft; +{ + byte *srcptr; + int length, numbytes; + int skipped = 0; + + if (gendata == NULL) + return (0); + + 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) + skipped += numbytes; + else { + bcopy(srcptr, *buff, numbytes); + (*buff) += numbytes; + (*bytesleft) -= numbytes; + } + srcptr += numbytes; + length -= numbytes; + break; + } + } /* while */ + return (skipped); +} + +/* + * 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) + u_int32 value; + byte **dest; +{ + byte *temp; + 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 */ +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/dovend.h b/libexec/bootpd/dovend.h new file mode 100644 index 00000000000..b30c982d8e2 --- /dev/null +++ b/libexec/bootpd/dovend.h @@ -0,0 +1,13 @@ +/* dovend.h */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern int dovend_rfc1497 P((struct host *hp, u_char *buf, int len)); +extern int insert_ip P((int, struct in_addr_list *, u_char **, int *)); +extern void insert_u_long P((u_int32, u_char **)); + +#undef P diff --git a/libexec/bootpd/dumptab.c b/libexec/bootpd/dumptab.c new file mode 100644 index 00000000000..e22c2715d31 --- /dev/null +++ b/libexec/bootpd/dumptab.c @@ -0,0 +1,370 @@ +/* + * dumptab.c - handles dumping the database + */ + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <stdio.h> +#include <stdlib.h> +#include <syslog.h> +#include <time.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#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) +#endif + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "report.h" +#include "patchlevel.h" +#include "bootpd.h" + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +static void dump_generic P((FILE *, struct shared_bindata *)); +static void dump_host P((FILE *, struct host *)); +static void list_ipaddresses P((FILE *, struct in_addr_list *)); + +#undef P + +#ifndef DEBUG +void +dumptab(filename) + char *filename; +{ + report(LOG_INFO, "No dumptab support!"); +} + +#else /* DEBUG */ + +/* + * Dump the internal memory database to bootpd_dump. + */ + +void +dumptab(filename) + char *filename; +{ + int n; + struct host *hp; + FILE *fp; + long t; + /* Print symbols in alphabetical order for reader's convenience. */ + static char legend[] = "#\n# Legend:\t(see bootptab.5)\n\ +#\tfirst field -- hostname (not indented)\n\ +#\tbf -- bootfile\n\ +#\tbs -- bootfile size in 512-octet blocks\n\ +#\tcs -- cookie servers\n\ +#\tdf -- dump file name\n\ +#\tdn -- domain name\n\ +#\tds -- domain name servers\n\ +#\tef -- extension file\n\ +#\tgw -- gateways\n\ +#\tha -- hardware address\n\ +#\thd -- home directory for bootfiles\n\ +#\thn -- host name set for client\n\ +#\tht -- hardware type\n\ +#\tim -- impress servers\n\ +#\tip -- host IP address\n\ +#\tlg -- log servers\n\ +#\tlp -- LPR servers\n\ +#\tns -- IEN-116 name servers\n\ +#\tnt -- NTP servers (RFC 1129)\n\ +#\tra -- reply address override\n\ +#\trl -- resource location protocol servers\n\ +#\trp -- root path\n\ +#\tsa -- boot server address\n\ +#\tsm -- subnet mask\n\ +#\tsw -- swap server\n\ +#\ttc -- template host (points to similar host entry)\n\ +#\ttd -- TFTP directory\n\ +#\tto -- time offset (seconds)\n\ +#\tts -- time servers\n\ +#\tvm -- vendor magic number\n\ +#\tyd -- YP (NIS) domain\n\ +#\tys -- YP (NIS) servers\n\ +#\tTn -- generic option tag n\n\ +\n"; + + /* + * Open bootpd.dump file. + */ + if ((fp = fopen(filename, "w")) == NULL) { + report(LOG_ERR, "error opening \"%s\": %s", + filename, get_errmsg()); + exit(1); + } + t = time(NULL); + fprintf(fp, "\n# %s %s.%d\n", progname, VERSION, PATCHLEVEL); + fprintf(fp, "# %s: dump of bootp server database.\n", filename); + fprintf(fp, "# Dump taken %s", ctime(&t)); + fwrite(legend, 1, sizeof(legend) - 1, fp); + + 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, filename); +} + + + +/* + * Dump all the available information on the host pointed to by "hp". + * The output is sent to the file pointed to by "fp". + */ + +static void +dump_host(fp, hp) + FILE *fp; + struct host *hp; +{ + /* Print symbols in alphabetical order for reader's convenience. */ + if (hp) { + fprintf(fp, "%s:", (hp->hostname ? + hp->hostname->string : "?")); + if (hp->flags.bootfile) { + fprintf(fp, "\\\n\t:bf=%s:", hp->bootfile->string); + } + if (hp->flags.bootsize) { + fprintf(fp, "\\\n\t:bs="); + if (hp->flags.bootsize_auto) { + fprintf(fp, "auto:"); + } else { + fprintf(fp, "%d:", hp->bootsize); + } + } + if (hp->flags.cookie_server) { + fprintf(fp, "\\\n\t:cs="); + list_ipaddresses(fp, hp->cookie_server); + fprintf(fp, ":"); + } + if (hp->flags.dump_file) { + fprintf(fp, "\\\n\t:df=%s:", hp->dump_file->string); + } + if (hp->flags.domain_name) { + fprintf(fp, "\\\n\t:dn=%s:", hp->domain_name->string); + } + if (hp->flags.domain_server) { + fprintf(fp, "\\\n\t:ds="); + list_ipaddresses(fp, hp->domain_server); + fprintf(fp, ":"); + } + if (hp->flags.exten_file) { + fprintf(fp, "\\\n\t:ef=%s:", hp->exten_file->string); + } + if (hp->flags.gateway) { + fprintf(fp, "\\\n\t:gw="); + list_ipaddresses(fp, hp->gateway); + fprintf(fp, ":"); + } + /* FdC: swap_server (see below) */ + if (hp->flags.homedir) { + fprintf(fp, "\\\n\t:hd=%s:", hp->homedir->string); + } + /* FdC: dump_file (see above) */ + /* FdC: domain_name (see above) */ + /* FdC: root_path (see below) */ + if (hp->flags.name_switch && hp->flags.send_name) { + fprintf(fp, "\\\n\t:hn:"); + } + if (hp->flags.htype) { + int hlen = haddrlength(hp->htype); + fprintf(fp, "\\\n\t:ht=%u:", (unsigned) hp->htype); + if (hp->flags.haddr) { + fprintf(fp, "ha=%s:", + haddrtoa(hp->haddr, hlen)); + } + } + if (hp->flags.impress_server) { + fprintf(fp, "\\\n\t:im="); + list_ipaddresses(fp, hp->impress_server); + fprintf(fp, ":"); + } + /* NetBSD: swap_server (see below) */ + if (hp->flags.iaddr) { + fprintf(fp, "\\\n\t:ip=%s:", inet_ntoa(hp->iaddr)); + } + if (hp->flags.log_server) { + fprintf(fp, "\\\n\t:lg="); + list_ipaddresses(fp, hp->log_server); + fprintf(fp, ":"); + } + if (hp->flags.lpr_server) { + fprintf(fp, "\\\n\t:lp="); + list_ipaddresses(fp, hp->lpr_server); + fprintf(fp, ":"); + } + if (hp->flags.name_server) { + fprintf(fp, "\\\n\t:ns="); + list_ipaddresses(fp, hp->name_server); + fprintf(fp, ":"); + } + if (hp->flags.ntp_server) { + fprintf(fp, "\\\n\t:nt="); + list_ipaddresses(fp, hp->ntp_server); + fprintf(fp, ":"); + } + if (hp->flags.reply_addr) { + fprintf(fp, "\\\n\t:ra=%s:", inet_ntoa(hp->reply_addr)); + } + if (hp->flags.rlp_server) { + fprintf(fp, "\\\n\t:rl="); + list_ipaddresses(fp, hp->rlp_server); + fprintf(fp, ":"); + } + if (hp->flags.root_path) { + fprintf(fp, "\\\n\t:rp=%s:", hp->root_path->string); + } + if (hp->flags.bootserver) { + fprintf(fp, "\\\n\t:sa=%s:", inet_ntoa(hp->bootserver)); + } + if (hp->flags.subnet_mask) { + fprintf(fp, "\\\n\t:sm=%s:", inet_ntoa(hp->subnet_mask)); + } + if (hp->flags.swap_server) { + fprintf(fp, "\\\n\t:sw=%s:", inet_ntoa(hp->subnet_mask)); + } + if (hp->flags.tftpdir) { + fprintf(fp, "\\\n\t:td=%s:", hp->tftpdir->string); + } + /* NetBSD: rootpath (see above) */ + /* NetBSD: domainname (see above) */ + /* NetBSD: dumpfile (see above) */ + if (hp->flags.time_offset) { + fprintf(fp, "\\\n\t:to=%ld:", hp->time_offset); + } + if (hp->flags.time_server) { + fprintf(fp, "\\\n\t:ts="); + list_ipaddresses(fp, hp->time_server); + fprintf(fp, ":"); + } + if (hp->flags.vm_cookie) { + fprintf(fp, "\\\n\t:vm="); + if (!bcmp(hp->vm_cookie, vm_rfc1048, 4)) { + fprintf(fp, "rfc1048:"); + } else if (!bcmp(hp->vm_cookie, vm_cmu, 4)) { + fprintf(fp, "cmu:"); + } 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.nis_domain) { + fprintf(fp, "\\\n\t:yd=%s:", + hp->nis_domain->string); + } + if (hp->flags.nis_server) { + fprintf(fp, "\\\n\t:ys="); + list_ipaddresses(fp, hp->nis_server); + fprintf(fp, ":"); + } + /* + * XXX - Add new tags here (or above, + * so they print in alphabetical order). + */ + + if (hp->flags.generic) { + dump_generic(fp, hp->generic); + } + } +} + + +static void +dump_generic(fp, generic) + FILE *fp; + struct shared_bindata *generic; +{ + u_char *bp = generic->data; + u_char *ep = bp + generic->length; + u_char tag; + int len; + + while (bp < ep) { + tag = *bp++; + if (tag == TAG_PAD) + continue; + if (tag == TAG_END) + return; + len = *bp++; + if (bp + len > ep) { + fprintf(fp, " #junk in generic! :"); + return; + } + fprintf(fp, "\\\n\t:T%d=", tag); + while (len) { + fprintf(fp, "%02X", *bp); + bp++; + len--; + if (len) + fprintf(fp, "."); + } + 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). + */ + +static void +list_ipaddresses(fp, ipptr) + FILE *fp; + struct in_addr_list *ipptr; +{ + unsigned count; + struct in_addr *addrptr; + + if (ipptr) { + count = ipptr->addrcount; + addrptr = ipptr->addr; + while (count > 0) { + fprintf(fp, "%s", inet_ntoa(*addrptr++)); + count--; + if (count) + fprintf(fp, ", "); + } + } +} + +#endif /* DEBUG */ + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/getether.c b/libexec/bootpd/getether.c new file mode 100644 index 00000000000..d131b50f7f8 --- /dev/null +++ b/libexec/bootpd/getether.c @@ -0,0 +1,374 @@ +/* + * getether.c : get the ethernet address of an interface + * + * All of this code is quite system-specific. As you may well + * guess, it took a good bit of detective work to figure out! + * + * If you figure out how to do this on another system, + * please let me know. <gwr@mc.com> + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <ctype.h> +#include <syslog.h> + +#include "report.h" +#define EALEN 6 + +#if defined(ultrix) || (defined(__osf__) && defined(__alpha)) +/* + * This is really easy on Ultrix! Thanks to + * Harald Lundberg <hl@tekla.fi> for this code. + * + * The code here is not specific to the Alpha, but that was the + * only symbol we could find to identify DEC's version of OSF. + * (Perhaps we should just define DEC in the Makefile... -gwr) + */ + +#include <sys/ioctl.h> +#include <net/if.h> /* struct ifdevea */ + +getether(ifname, eap) + char *ifname, *eap; +{ + int rc = -1; + int fd; + struct ifdevea phys; + bzero(&phys, sizeof(phys)); + strcpy(phys.ifr_name, ifname); + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "getether: socket(INET,DGRAM) failed"); + return -1; + } + if (ioctl(fd, SIOCRPHYSADDR, &phys) < 0) { + report(LOG_ERR, "getether: ioctl SIOCRPHYSADDR failed"); + } else { + bcopy(&phys.current_pa[0], eap, EALEN); + rc = 0; + } + close(fd); + return rc; +} + +#define GETETHER +#endif /* ultrix|osf1 */ + + +#ifdef SUNOS + +#include <sys/sockio.h> +#include <sys/time.h> /* needed by net_if.h */ +#include <net/nit_if.h> /* for NIOCBIND */ +#include <net/if.h> /* for struct ifreq */ + +getether(ifname, eap) + char *ifname; /* interface name from ifconfig structure */ + char *eap; /* Ether address (output) */ +{ + int rc = -1; + + struct ifreq ifrnit; + int nit; + + bzero((char *) &ifrnit, sizeof(ifrnit)); + strncpy(&ifrnit.ifr_name[0], ifname, IFNAMSIZ); + + nit = open("/dev/nit", 0); + if (nit < 0) { + report(LOG_ERR, "getether: open /dev/nit: %s", + get_errmsg()); + return rc; + } + do { + if (ioctl(nit, NIOCBIND, &ifrnit) < 0) { + report(LOG_ERR, "getether: NIOCBIND on nit"); + break; + } + if (ioctl(nit, SIOCGIFADDR, &ifrnit) < 0) { + report(LOG_ERR, "getether: SIOCGIFADDR on nit"); + break; + } + bcopy(&ifrnit.ifr_addr.sa_data[0], eap, EALEN); + rc = 0; + } while (0); + close(nit); + return rc; +} + +#define GETETHER +#endif /* SUNOS */ + + +#if defined(__386BSD__) || defined(__NetBSD__) +/* Thanks to John Brezak <brezak@ch.hp.com> for this code. */ +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> + +getether(ifname, eap) + char *ifname; /* interface name from ifconfig structure */ + char *eap; /* Ether address (output) */ +{ + int fd, rc = -1; + register int n; + struct ifreq ibuf[16], ifr; + struct ifconf ifc; + register struct ifreq *ifrp, *ifend; + + /* Fetch the interface configuration */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + report(LOG_ERR, "getether: socket %s: %s", ifname, get_errmsg()); + return (fd); + } + ifc.ifc_len = sizeof(ibuf); + ifc.ifc_buf = (caddr_t) ibuf; + if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0 || + ifc.ifc_len < sizeof(struct ifreq)) { + report(LOG_ERR, "getether: SIOCGIFCONF: %s", get_errmsg); + goto out; + } + /* Search interface configuration list for link layer address. */ + ifrp = ibuf; + ifend = (struct ifreq *) ((char *) ibuf + ifc.ifc_len); + while (ifrp < ifend) { + /* Look for interface */ + if (strcmp(ifname, ifrp->ifr_name) == 0 && + ifrp->ifr_addr.sa_family == AF_LINK && + ((struct sockaddr_dl *) &ifrp->ifr_addr)->sdl_type == IFT_ETHER) { + bcopy(LLADDR((struct sockaddr_dl *) &ifrp->ifr_addr), eap, EALEN); + rc = 0; + break; + } + /* Bump interface config pointer */ + n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); + if (n < sizeof(*ifrp)) + n = sizeof(*ifrp); + ifrp = (struct ifreq *) ((char *) ifrp + n); + } + + out: + close(fd); + return (rc); +} + +#define GETETHER +#endif /* __NetBSD__ */ + + +#ifdef SVR4 +/* + * This is for "Streams TCP/IP" by Lachman Associates. + * They sure made this cumbersome! -gwr + */ + +#include <sys/sockio.h> +#include <sys/dlpi.h> +#include <stropts.h> +#ifndef NULL +#define NULL 0 +#endif + +getether(ifname, eap) + char *ifname; /* interface name from ifconfig structure */ + char *eap; /* Ether address (output) */ +{ + int rc = -1; + char devname[32]; + char tmpbuf[sizeof(union DL_primitives) + 16]; + struct strbuf cbuf; + int fd, flags; + union DL_primitives *dlp; + char *enaddr; + int unit = -1; /* which unit to attach */ + + sprintf(devname, "/dev/%s", ifname); + fd = open(devname, 2); + if (fd < 0) { + /* Try without the trailing digit. */ + char *p = devname + 5; + while (isalpha(*p)) + p++; + if (isdigit(*p)) { + unit = *p - '0'; + *p = '\0'; + } + fd = open(devname, 2); + if (fd < 0) { + report(LOG_ERR, "getether: open %s: %s", + devname, get_errmsg()); + return rc; + } + } +#ifdef DL_ATTACH_REQ + /* + * If this is a "Style 2" DLPI, then we must "attach" first + * to tell the driver which unit (board, port) we want. + * For now, decide this based on the device name. + * (Should do "info_req" and check dl_provider_style ...) + */ + if (unit >= 0) { + memset(tmpbuf, 0, sizeof(tmpbuf)); + dlp = (union DL_primitives *) tmpbuf; + dlp->dl_primitive = DL_ATTACH_REQ; + dlp->attach_req.dl_ppa = unit; + cbuf.buf = tmpbuf; + cbuf.len = DL_ATTACH_REQ_SIZE; + if (putmsg(fd, &cbuf, NULL, 0) < 0) { + report(LOG_ERR, "getether: attach: putmsg: %s", get_errmsg()); + goto out; + } + /* Recv the ack. */ + cbuf.buf = tmpbuf; + cbuf.maxlen = sizeof(tmpbuf); + flags = 0; + if (getmsg(fd, &cbuf, NULL, &flags) < 0) { + report(LOG_ERR, "getether: attach: getmsg: %s", get_errmsg()); + goto out; + } + /* + * Check the type, etc. + */ + if (dlp->dl_primitive == DL_ERROR_ACK) { + report(LOG_ERR, "getether: attach: dlpi_errno=%d, unix_errno=%d", + dlp->error_ack.dl_errno, + dlp->error_ack.dl_unix_errno); + goto out; + } + if (dlp->dl_primitive != DL_OK_ACK) { + report(LOG_ERR, "getether: attach: not OK or ERROR"); + goto out; + } + } /* unit >= 0 */ +#endif /* DL_ATTACH_REQ */ + + /* + * Get the Ethernet address the same way the ARP module + * does when it is pushed onto a new stream (bind). + * One should instead be able just do an dl_info_req + * but many drivers do not supply the hardware address + * in the response to dl_info_req (they MUST supply it + * for dl_bind_ack because the ARP module requires it). + */ + memset(tmpbuf, 0, sizeof(tmpbuf)); + dlp = (union DL_primitives *) tmpbuf; + dlp->dl_primitive = DL_BIND_REQ; + dlp->bind_req.dl_sap = 0x8FF; /* XXX - Unused SAP */ + cbuf.buf = tmpbuf; + cbuf.len = DL_BIND_REQ_SIZE; + if (putmsg(fd, &cbuf, NULL, 0) < 0) { + report(LOG_ERR, "getether: bind: putmsg: %s", get_errmsg()); + goto out; + } + /* Recv the ack. */ + cbuf.buf = tmpbuf; + cbuf.maxlen = sizeof(tmpbuf); + flags = 0; + if (getmsg(fd, &cbuf, NULL, &flags) < 0) { + report(LOG_ERR, "getether: bind: getmsg: %s", get_errmsg()); + goto out; + } + /* + * Check the type, etc. + */ + if (dlp->dl_primitive == DL_ERROR_ACK) { + report(LOG_ERR, "getether: bind: dlpi_errno=%d, unix_errno=%d", + dlp->error_ack.dl_errno, + dlp->error_ack.dl_unix_errno); + goto out; + } + if (dlp->dl_primitive != DL_BIND_ACK) { + report(LOG_ERR, "getether: bind: not OK or ERROR"); + goto out; + } + if (dlp->bind_ack.dl_addr_offset == 0) { + report(LOG_ERR, "getether: bind: ack has no address"); + goto out; + } + if (dlp->bind_ack.dl_addr_length < EALEN) { + report(LOG_ERR, "getether: bind: ack address truncated"); + goto out; + } + /* + * Copy the Ethernet address out of the message. + */ + enaddr = tmpbuf + dlp->bind_ack.dl_addr_offset; + memcpy(eap, enaddr, EALEN); + rc = 0; + + out: + close(fd); + return rc; +} + +#define GETETHER +#endif /* SVR4 */ + + +#ifdef linux +/* + * This is really easy on Linux! This version (for linux) + * written by Nigel Metheringham <nigelm@ohm.york.ac.uk> + * + * The code is almost identical to the Ultrix code - however + * the names are different to confuse the innocent :-) + * Most of this code was stolen from the Ultrix bit above. + */ + +#include <sys/ioctl.h> +#include <net/if.h> /* struct ifreq */ + +/* In a properly configured system this should be either sys/socketio.h + or sys/sockios.h, but on my distribution these don't line up correctly */ +#include <linux/sockios.h> /* Needed for IOCTL defs */ + +getether(ifname, eap) + char *ifname, *eap; +{ + int rc = -1; + int fd; + struct ifreq phys; + bzero(&phys, sizeof(phys)); + strcpy(phys.ifr_name, ifname); + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "getether: socket(INET,DGRAM) failed"); + return -1; + } + if (ioctl(fd, SIOCGIFHWADDR, &phys) < 0) { + report(LOG_ERR, "getether: ioctl SIOCGIFHWADDR failed"); + } else { + bcopy(phys.ifr_hwaddr, eap, EALEN); + rc = 0; + } + close(fd); + return rc; +} + +#define GETETHER +#endif /* linux */ + + +/* If we don't know how on this system, just return an error. */ +#ifndef GETETHER +getether(ifname, eap) + char *ifname, *eap; +{ + return -1; +} + +#endif /* !GETETHER */ + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/getif.c b/libexec/bootpd/getif.c new file mode 100644 index 00000000000..6cc16496ae4 --- /dev/null +++ b/libexec/bootpd/getif.c @@ -0,0 +1,145 @@ +/* + * getif.c : get an interface structure + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#if defined(SUNOS) || defined(SVR4) +#include <sys/sockio.h> +#endif +#ifdef SVR4 +#include <sys/stropts.h> +#endif + +#include <net/if.h> /* for struct ifreq */ +#include <netinet/in.h> + +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <syslog.h> +#include <errno.h> +#include <assert.h> + +#include "getif.h" +#include "report.h" + +#ifdef __bsdi__ +#define BSD 43 +#endif + +static struct ifreq ifreq[10]; /* Holds interface configuration */ +static struct ifconf ifconf; /* points to ifreq */ + +static int nmatch(); + +/* Return a pointer to the interface struct for the passed address. */ +struct ifreq * +getif(s, addrp) + int s; /* socket file descriptor */ + struct in_addr *addrp; /* destination address on interface */ +{ + int maxmatch; + int len, m, incr; + struct ifreq *ifrq, *ifrmax; + struct sockaddr_in *sip; + char *p; + + /* If no address was supplied, just return NULL. */ + if (!addrp) + return (struct ifreq *) 0; + + /* Get the interface config if not done already. */ + if (ifconf.ifc_len == 0) { +#ifdef SVR4 + /* + * SysVr4 returns garbage if you do this the obvious way! + * This one took a while to figure out... -gwr + */ + struct strioctl ioc; + ioc.ic_cmd = SIOCGIFCONF; + ioc.ic_timout = 0; + ioc.ic_len = sizeof(ifreq); + ioc.ic_dp = (char *) ifreq; + m = ioctl(s, I_STR, (char *) &ioc); + ifconf.ifc_len = ioc.ic_len; + ifconf.ifc_req = ifreq; +#else /* SVR4 */ + ifconf.ifc_len = sizeof(ifreq); + ifconf.ifc_req = ifreq; + m = ioctl(s, SIOCGIFCONF, (caddr_t) & ifconf); +#endif /* SVR4 */ + if ((m < 0) || (ifconf.ifc_len <= 0)) { + report(LOG_ERR, "ioctl SIOCGIFCONF"); + return (struct ifreq *) 0; + } + } + maxmatch = 7; /* this many bits or less... */ + ifrmax = (struct ifreq *) 0;/* ... is not a valid match */ + p = (char *) ifreq; + len = ifconf.ifc_len; + while (len > 0) { + ifrq = (struct ifreq *) p; + sip = (struct sockaddr_in *) &ifrq->ifr_addr; + m = nmatch(addrp, &(sip->sin_addr)); + if (m > maxmatch) { + maxmatch = m; + ifrmax = ifrq; + } + /* XXX - Could this be just #ifndef IFNAMSIZ instead? -gwr */ +#if (BSD - 0) < 43 + /* BSD not defined or earlier than 4.3 */ + incr = sizeof(*ifrq); +#else /* NetBSD */ + incr = ifrq->ifr_addr.sa_len + IFNAMSIZ; +#endif /* NetBSD */ + + p += incr; + len -= incr; + } + + return ifrmax; +} + +/* + * Return the number of leading bits matching in the + * internet addresses supplied. + */ +static int +nmatch(ca, cb) + u_char *ca, *cb; /* ptrs to IP address, network order */ +{ + u_int m = 0; /* count of matching bits */ + u_int n = 4; /* bytes left, then bitmask */ + + /* Count matching bytes. */ + while (n && (*ca == *cb)) { + ca++; + cb++; + m += 8; + n--; + } + /* Now count matching bits. */ + if (n) { + n = 0x80; + while (n && ((*ca & n) == (*cb & n))) { + m++; + n >>= 1; + } + } + return (m); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/getif.h b/libexec/bootpd/getif.h new file mode 100644 index 00000000000..c51dafd2095 --- /dev/null +++ b/libexec/bootpd/getif.h @@ -0,0 +1,7 @@ +/* getif.h */ + +#ifdef __STDC__ +extern struct ifreq *getif(int, struct in_addr *); +#else +extern struct ifreq *getif(); +#endif diff --git a/libexec/bootpd/hash.c b/libexec/bootpd/hash.c index a0a3d980683..e78001b732a 100644 --- a/libexec/bootpd/hash.c +++ b/libexec/bootpd/hash.c @@ -1,5 +1,3 @@ -#ifndef _BLURB_ -#define _BLURB_ /************************************************************************ Copyright 1988, 1991 by Carnegie Mellon University @@ -21,11 +19,9 @@ 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 rcsid[] = "$Header: /cvsroot/src/libexec/bootpd/Attic/hash.c,v 1.1 1994/04/18 05:16:01 glass Exp $"; +static char rcsid[] = "$Header: /cvsroot/src/libexec/bootpd/Attic/hash.c,v 1.2 1994/05/24 15:20:37 gwr Exp $"; #endif @@ -41,19 +37,44 @@ static char rcsid[] = "$Header: /cvsroot/src/libexec/bootpd/Attic/hash.c,v 1.1 1 */ +#include <sys/types.h> +#include <stdlib.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#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) +#endif + #include "hash.h" #define TRUE 1 #define FALSE 0 +#ifndef NULL #define NULL 0 +#endif /* * This can be changed to make internal routines visible to debuggers, etc. */ +#ifndef PRIVATE #define PRIVATE static +#endif + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +PRIVATE void hashi_FreeMembers P((hash_member *, hash_freefp)); +#undef P + /* * Hash table initialization routine. * @@ -63,47 +84,48 @@ static char rcsid[] = "$Header: /cvsroot/src/libexec/bootpd/Attic/hash.c,v 1.1 1 * calls return NULL. */ -hash_tbl *hash_Init(tablesize) -unsigned tablesize; +hash_tbl * +hash_Init(tablesize) + unsigned tablesize; { - register hash_tbl *hashtblptr; - register unsigned totalsize; + register hash_tbl *hashtblptr; + register unsigned totalsize; - if (tablesize > 0) { - totalsize = sizeof(hash_tbl) + if (tablesize > 0) { + totalsize = sizeof(hash_tbl) + sizeof(hash_member *) * (tablesize - 1); - hashtblptr = (hash_tbl *) malloc(totalsize); - if (hashtblptr) { - bzero((char *) hashtblptr, totalsize); - hashtblptr->size = tablesize; /* Success! */ - hashtblptr->bucketnum = 0; - hashtblptr->member = (hashtblptr->table)[0]; + hashtblptr = (hash_tbl *) malloc(totalsize); + if (hashtblptr) { + bzero((char *) hashtblptr, totalsize); + hashtblptr->size = tablesize; /* Success! */ + hashtblptr->bucketnum = 0; + hashtblptr->member = (hashtblptr->table)[0]; + } + } else { + hashtblptr = NULL; /* Disallow zero-length tables */ } - } else { - hashtblptr = NULL; /* Disallow zero-length tables */ - } - return hashtblptr; /* NULL if failure */ + return hashtblptr; /* NULL if failure */ } - + /* - * Recursively frees an entire linked list of bucket members (used in the open + * Frees an entire linked list of bucket members (used in the open * hashing scheme). Does nothing if the passed pointer is NULL. */ -PRIVATE void hashi_FreeMember(bucketptr, free_data) -hash_member *bucketptr; -void (*free_data)(); +PRIVATE void +hashi_FreeMembers(bucketptr, free_data) + hash_member *bucketptr; + hash_freefp free_data; { - if (bucketptr) { - /* - * Free next member if necessary - */ - hashi_FreeMember(bucketptr->next, free_data); - (*free_data)(bucketptr->data); - free((char *) bucketptr); - } + hash_member *nextbucket; + while (bucketptr) { + nextbucket = bucketptr->next; + (*free_data) (bucketptr->data); + free((char *) bucketptr); + bucketptr = nextbucket; + } } @@ -114,24 +136,25 @@ void (*free_data)(); * memory and resets all bucket pointers to NULL. */ -void hash_Reset(hashtable, free_data) -hash_tbl *hashtable; -void (*free_data)(); +void +hash_Reset(hashtable, free_data) + hash_tbl *hashtable; + hash_freefp free_data; { - hash_member **bucketptr; - unsigned i; - - bucketptr = hashtable->table; - for (i = 0; i < hashtable->size; i++) { - hashi_FreeMember(*bucketptr, free_data); - *bucketptr++ = NULL; - } - hashtable->bucketnum = 0; - hashtable->member = (hashtable->table)[0]; -} + hash_member **bucketptr; + unsigned i; + bucketptr = hashtable->table; + for (i = 0; i < hashtable->size; i++) { + hashi_FreeMembers(*bucketptr, free_data); + *bucketptr++ = NULL; + } + hashtable->bucketnum = 0; + hashtable->member = (hashtable->table)[0]; +} + /* * Generic hash function to calculate a hash code from the given string. * @@ -152,47 +175,49 @@ void (*free_data)(); * features of this package. */ -unsigned hash_HashFunction(string, len) -unsigned char *string; -register unsigned len; +unsigned +hash_HashFunction(string, len) + unsigned char *string; + register unsigned len; { - register unsigned accum; - - accum = 0; - for (; len > 0; len--) { - accum <<= 1; - accum += (unsigned) (*string++ & 0xFF); - } - return accum; -} + register unsigned accum; + accum = 0; + for (; len > 0; len--) { + accum <<= 1; + accum += (unsigned) (*string++ & 0xFF); + } + return accum; +} + /* * Returns TRUE if at least one entry for the given key exists; FALSE * otherwise. */ -int hash_Exists(hashtable, hashcode, compare, key) -hash_tbl *hashtable; -unsigned hashcode; -int (*compare)(); -hash_datum *key; +int +hash_Exists(hashtable, hashcode, compare, key) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key; { - register hash_member *memberptr; - - memberptr = (hashtable->table)[hashcode % (hashtable->size)]; - while (memberptr) { - if ((*compare)(key, memberptr->data)) { - return TRUE; /* Entry does exist */ + register hash_member *memberptr; + + memberptr = (hashtable->table)[hashcode % (hashtable->size)]; + while (memberptr) { + if ((*compare) (key, memberptr->data)) { + return TRUE; /* Entry does exist */ + } + memberptr = memberptr->next; } - memberptr = memberptr->next; - } - return FALSE; /* Entry does not exist */ + return FALSE; /* Entry does not exist */ } - + /* * Insert the data item "element" into the hash table using "hashcode" * to determine the bucket number, and "compare" and "key" to determine @@ -203,95 +228,96 @@ hash_datum *key; * occurs, -1 is returned and the insertion is not done. */ -int hash_Insert(hashtable, hashcode, compare, key, element) -hash_tbl *hashtable; -unsigned hashcode; -int (*compare)(); -hash_datum *key, *element; +int +hash_Insert(hashtable, hashcode, compare, key, element) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key, *element; { - hash_member *memberptr, *temp; - - hashcode %= hashtable->size; - if (hash_Exists(hashtable, hashcode, compare, key)) { - return -1; /* At least one entry already exists */ - } - memberptr = (hashtable->table)[hashcode]; - temp = (hash_member *) malloc(sizeof(hash_member)); - if (temp) { - (hashtable->table)[hashcode] = temp; + hash_member *temp; + + hashcode %= hashtable->size; + if (hash_Exists(hashtable, hashcode, compare, key)) { + return -1; /* At least one entry already exists */ + } + temp = (hash_member *) malloc(sizeof(hash_member)); + if (!temp) + return -1; /* malloc failed! */ + temp->data = element; - temp->next = memberptr; - return 0; /* Success */ - } else { - return -1; /* malloc failed! */ - } + temp->next = (hashtable->table)[hashcode]; + (hashtable->table)[hashcode] = temp; + return 0; /* Success */ } - + /* * Delete all data elements which match the given key. If at least one * element is found and the deletion is successful, 0 is returned. * If no matching elements can be found in the hash table, -1 is returned. */ -int hash_Delete(hashtable, hashcode, compare, key, free_data) -hash_tbl *hashtable; -unsigned hashcode; -int (*compare)(); -hash_datum *key; -void (*free_data)(); +int +hash_Delete(hashtable, hashcode, compare, key, free_data) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key; + hash_freefp free_data; { - hash_member *memberptr, *previous, *tempptr; - int retval; - - retval = -1; - hashcode %= hashtable->size; - - /* - * Delete the first member of the list if it matches. Since this moves - * the second member into the first position we have to keep doing this - * over and over until it no longer matches. - */ - memberptr = (hashtable->table)[hashcode]; - while (memberptr && (*compare)(key, memberptr->data)) { - (hashtable->table)[hashcode] = memberptr->next; + hash_member *memberptr, *tempptr; + hash_member *previous = NULL; + int retval; + + retval = -1; + hashcode %= hashtable->size; + /* - * Stop hashi_FreeMember() from recursively deleting the whole list! + * Delete the first member of the list if it matches. Since this moves + * the second member into the first position we have to keep doing this + * over and over until it no longer matches. */ - memberptr->next = NULL; - hashi_FreeMember(memberptr, free_data); memberptr = (hashtable->table)[hashcode]; - retval = 0; - } - - /* - * Now traverse the rest of the list - */ - if (memberptr) { - previous = memberptr; - memberptr = memberptr->next; - } - while (memberptr) { - if ((*compare)(key, memberptr->data)) { - tempptr = memberptr; - previous->next = memberptr = memberptr->next; - /* - * Put the brakes on hashi_FreeMember(). . . . - */ - tempptr->next = NULL; - hashi_FreeMember(tempptr, free_data); - retval = 0; - } else { - previous = memberptr; - memberptr = memberptr->next; + while (memberptr && (*compare) (key, memberptr->data)) { + (hashtable->table)[hashcode] = memberptr->next; + /* + * Stop hashi_FreeMembers() from deleting the whole list! + */ + memberptr->next = NULL; + hashi_FreeMembers(memberptr, free_data); + memberptr = (hashtable->table)[hashcode]; + retval = 0; } - } - return retval; -} + /* + * Now traverse the rest of the list + */ + if (memberptr) { + previous = memberptr; + memberptr = memberptr->next; + } + while (memberptr) { + if ((*compare) (key, memberptr->data)) { + tempptr = memberptr; + previous->next = memberptr = memberptr->next; + /* + * Put the brakes on hashi_FreeMembers(). . . . + */ + tempptr->next = NULL; + hashi_FreeMembers(tempptr, free_data); + retval = 0; + } else { + previous = memberptr; + memberptr = memberptr->next; + } + } + return retval; +} + /* * Locate and return the data entry associated with the given key. * @@ -299,88 +325,101 @@ void (*free_data)(); * NULL is returned. */ -hash_datum *hash_Lookup(hashtable, hashcode, compare, key) -hash_tbl *hashtable; -unsigned hashcode; -int (*compare)(); -hash_datum *key; +hash_datum * +hash_Lookup(hashtable, hashcode, compare, key) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key; { - hash_member *memberptr; - - memberptr = (hashtable->table)[hashcode % (hashtable->size)]; - while (memberptr) { - if ((*compare)(key, memberptr->data)) { - return (memberptr->data); + hash_member *memberptr; + + memberptr = (hashtable->table)[hashcode % (hashtable->size)]; + while (memberptr) { + if ((*compare) (key, memberptr->data)) { + return (memberptr->data); + } + memberptr = memberptr->next; } - memberptr = memberptr->next; - } - return NULL; + return NULL; } - + /* * Return the next available entry in the hashtable for a linear search */ -hash_datum *hash_NextEntry(hashtable) -hash_tbl *hashtable; +hash_datum * +hash_NextEntry(hashtable) + hash_tbl *hashtable; { - register unsigned bucket; - register hash_member *memberptr; - - /* - * First try to pick up where we left off. - */ - memberptr = hashtable->member; - if (memberptr) { - hashtable->member = memberptr->next; /* Set up for next call */ - return memberptr->data; /* Return the data */ - } - - /* - * We hit the end of a chain, so look through the array of buckets - * until we find a new chain (non-empty bucket) or run out of buckets. - */ - bucket = hashtable->bucketnum + 1; - while ((bucket < hashtable->size) && - !(memberptr = (hashtable->table)[bucket])) { - bucket++; - } - - /* - * Check to see if we ran out of buckets. - */ - if (bucket >= hashtable->size) { + register unsigned bucket; + register hash_member *memberptr; + /* - * Reset to top of table for next call. + * First try to pick up where we left off. */ - hashtable->bucketnum = 0; - hashtable->member = (hashtable->table)[0]; + memberptr = hashtable->member; + if (memberptr) { + hashtable->member = memberptr->next; /* Set up for next call */ + return memberptr->data; /* Return the data */ + } /* - * But return end-of-table indication to the caller this time. + * We hit the end of a chain, so look through the array of buckets + * until we find a new chain (non-empty bucket) or run out of buckets. */ - return NULL; - } - - /* - * Must have found a non-empty bucket. - */ - hashtable->bucketnum = bucket; - hashtable->member = memberptr->next; /* Set up for next call */ - return memberptr->data; /* Return the data */ -} + bucket = hashtable->bucketnum + 1; + while ((bucket < hashtable->size) && + !(memberptr = (hashtable->table)[bucket])) { + bucket++; + } + /* + * Check to see if we ran out of buckets. + */ + if (bucket >= hashtable->size) { + /* + * Reset to top of table for next call. + */ + hashtable->bucketnum = 0; + hashtable->member = (hashtable->table)[0]; + /* + * But return end-of-table indication to the caller this time. + */ + return NULL; + } + /* + * Must have found a non-empty bucket. + */ + hashtable->bucketnum = bucket; + hashtable->member = memberptr->next; /* Set up for next call */ + return memberptr->data; /* Return the data */ +} + /* * Return the first entry in a hash table for a linear search */ -hash_datum *hash_FirstEntry(hashtable) -hash_tbl *hashtable; +hash_datum * +hash_FirstEntry(hashtable) + hash_tbl *hashtable; { - hashtable->bucketnum = 0; - hashtable->member = (hashtable->table)[0]; - return hash_NextEntry(hashtable); + hashtable->bucketnum = 0; + hashtable->member = (hashtable->table)[0]; + return hash_NextEntry(hashtable); } + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/hash.h b/libexec/bootpd/hash.h index 2a1d8b8e5a3..51d0a5ebd33 100644 --- a/libexec/bootpd/hash.h +++ b/libexec/bootpd/hash.h @@ -1,5 +1,6 @@ -#ifndef _BLURB_ -#define _BLURB_ +#ifndef HASH_H +#define HASH_H +/* hash.h */ /************************************************************************ Copyright 1988, 1991 by Carnegie Mellon University @@ -21,8 +22,6 @@ 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_ */ - /* * Generalized hash table ADT @@ -91,10 +90,6 @@ SOFTWARE. */ -#ifndef __HASHXYZ973__ - -#define __HASHXYZ973__ - /* * Define "hash_datum" as a universal data type */ @@ -124,14 +119,40 @@ struct hash_tblstruct { hash_member *table[1]; /* Dynamically extended */ }; -extern hash_tbl *hash_Init(); -extern void hash_Reset(); -extern unsigned hash_HashFunction(); -extern int hash_Exists(); -extern int hash_Insert(); -extern int hash_Delete(); -extern hash_datum *hash_Lookup(); -extern hash_datum *hash_FirstEntry(); -extern hash_datum *hash_NextEntry(); - +/* ANSI function prototypes or empty arg list? */ +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () #endif + +typedef int (*hash_cmpfp) P((hash_datum *, hash_datum *)); +typedef void (*hash_freefp) P((hash_datum *)); + +extern hash_tbl *hash_Init P((u_int tablesize)); + +extern void hash_Reset P((hash_tbl *tbl, hash_freefp)); + +extern unsigned hash_HashFunction P((u_char *str, u_int len)); + +extern int hash_Exists P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key)); + +extern int hash_Insert P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key, + hash_datum *element)); + +extern int hash_Delete P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key, + hash_freefp)); + +extern hash_datum *hash_Lookup P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key)); + +extern hash_datum *hash_FirstEntry P((hash_tbl *)); + +extern hash_datum *hash_NextEntry P((hash_tbl *)); + +#undef P + +#endif /* HASH_H */ diff --git a/libexec/bootpd/hwaddr.c b/libexec/bootpd/hwaddr.c new file mode 100644 index 00000000000..95418c53aac --- /dev/null +++ b/libexec/bootpd/hwaddr.c @@ -0,0 +1,265 @@ +/* + * hwaddr.c - routines that deal with hardware addresses. + * (i.e. Ethernet) + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#if defined(SUNOS) || defined(SVR4) +#include <sys/sockio.h> +#endif +#ifdef SVR4 +#include <sys/stream.h> +#include <stropts.h> +#include <fcntl.h> +#endif + +#include <net/if_arp.h> +#include <netinet/in.h> +#include <stdio.h> +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <syslog.h> + +#ifndef USE_BFUNCS +/* Yes, memcpy is OK here (no overlapped copies). */ +#include <memory.h> +#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) +#endif + +#include "bptypes.h" +#include "hwaddr.h" +#include "report.h" + +extern int debug; + +/* + * 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]); + + +/* + * Setup the arp cache so that IP address 'ia' will be temporarily + * bound to hardware address 'ha' of length 'len'. + */ +void +setarp(s, ia, ha, len) + int s; /* socket fd */ + struct in_addr *ia; + u_char *ha; + int len; +{ + struct arpreq arpreq; /* Arp request ioctl block */ + struct sockaddr_in *si; +#ifdef SVR4 + int fd; + struct strioctl iocb; +#endif /* SVR4 */ + + bzero((caddr_t) & arpreq, sizeof(arpreq)); + arpreq.arp_flags = ATF_INUSE | ATF_COM; + + /* Set up the protocol address. */ + arpreq.arp_pa.sa_family = AF_INET; + si = (struct sockaddr_in *) &arpreq.arp_pa; + si->sin_addr = *ia; + + /* Set up the hardware address. */ + bcopy(ha, arpreq.arp_ha.sa_data, len); + +#ifdef SVR4 + /* + * And now the stuff for System V Rel 4.x which does not + * appear to allow SIOCxxx ioctls on a socket descriptor. + * Thanks to several people: (all sent the same fix) + * Barney Wolff <barney@databus.com>, + * bear@upsys.se (Bj|rn Sj|holm), + * Michael Kuschke <Michael.Kuschke@Materna.DE>, + */ + if ((fd=open("/dev/arp", O_RDWR)) < 0) { + report(LOG_ERR, "open /dev/arp: %s\n", get_errmsg()); + } + iocb.ic_cmd = SIOCSARP; + iocb.ic_timout = 0; + iocb.ic_dp = (char *)&arpreq; + iocb.ic_len = sizeof(arpreq); + if (ioctl(fd, I_STR, (caddr_t)&iocb) < 0) { + report(LOG_ERR, "ioctl I_STR: %s\n", get_errmsg()); + } + close (fd); + +#else /* SVR4 */ + /* + * On SunOS, the ioctl sometimes returns ENXIO, and it + * appears to happen when the ARP cache entry you tried + * to add is already in the cache. (Sigh...) + * XXX - Should this error simply be ignored? -gwr + */ + if (ioctl(s, SIOCSARP, (caddr_t) & arpreq) < 0) { + report(LOG_ERR, "ioctl SIOCSARP: %s", get_errmsg()); + } +#endif /* SVR4 */ +} + + +/* + * Convert a hardware address to an ASCII string. + */ +char * +haddrtoa(haddr, hlen) + u_char *haddr; + int hlen; +{ + static char haddrbuf[3 * MAXHADDRLEN + 1]; + char *bufptr; + + if (hlen > MAXHADDRLEN) + hlen = MAXHADDRLEN; + + bufptr = haddrbuf; + while (hlen > 0) { + sprintf(bufptr, "%02X.", (unsigned) (*haddr++ & 0xFF)); + bufptr += 3; + hlen--; + } + bufptr[-1] = 0; + return (haddrbuf); +} + + +/* + * haddr_conv802() + * -------------- + * + * Converts a backwards address to a canonical address and a canonical address + * to a backwards address. + * + * INPUTS: + * adr_in - pointer to six byte string to convert (unsigned char *) + * addr_len - how many bytes to convert + * + * OUTPUTS: + * addr_out - The string is updated to contain the converted address. + * + * CALLER: + * many + * + * DATA: + * Uses conv802table to bit-reverse the address bytes. + */ + +static u_char conv802table[256] = +{ + /* 0x00 */ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + /* 0x08 */ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + /* 0x10 */ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + /* 0x18 */ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + /* 0x20 */ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + /* 0x28 */ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + /* 0x30 */ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + /* 0x38 */ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + /* 0x40 */ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + /* 0x48 */ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + /* 0x50 */ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + /* 0x58 */ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + /* 0x60 */ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + /* 0x68 */ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + /* 0x70 */ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + /* 0x78 */ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + /* 0x80 */ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + /* 0x88 */ 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + /* 0x90 */ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + /* 0x98 */ 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + /* 0xA0 */ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + /* 0xA8 */ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + /* 0xB0 */ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + /* 0xB8 */ 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + /* 0xC0 */ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + /* 0xC8 */ 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + /* 0xD0 */ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + /* 0xD8 */ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + /* 0xE0 */ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + /* 0xE8 */ 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + /* 0xF0 */ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + /* 0xF8 */ 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, +}; + +void +haddr_conv802(addr_in, addr_out, len) + register u_char *addr_in, *addr_out; + int len; +{ + u_char *lim; + + lim = addr_out + len; + while (addr_out < lim) + *addr_out++ = conv802table[*addr_in++]; +} + +#if 0 +/* + * For the record, here is a program to generate the + * bit-reverse table above. + */ +static int +bitrev(n) + int n; +{ + int i, r; + + r = 0; + for (i = 0; i < 8; i++) { + r <<= 1; + r |= (n & 1); + n >>= 1; + } + return r; +} + +main() +{ + int i; + for (i = 0; i <= 0xFF; i++) { + if ((i & 7) == 0) + printf("/* 0x%02X */", i); + printf(" 0x%02X,", bitrev(i)); + if ((i & 7) == 7) + printf("\n"); + } +} + +#endif + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/hwaddr.h b/libexec/bootpd/hwaddr.h new file mode 100644 index 00000000000..dea7158be8c --- /dev/null +++ b/libexec/bootpd/hwaddr.h @@ -0,0 +1,39 @@ +/* hwaddr.h */ +#ifndef HWADDR_H +#define HWADDR_H + +#define MAXHADDRLEN 8 /* Max hw address length in bytes */ + +/* + * This structure holds information about a specific network type. The + * length of the network hardware address is stored in "hlen". + * The string pointed to by "name" is the cononical name of the network. + */ +struct hwinfo { + unsigned int hlen; + char *name; +}; + +extern struct hwinfo hwinfolist[]; +extern int hwinfocnt; + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern void setarp P((int, struct in_addr *, u_char *, int)); +extern char *haddrtoa P((u_char *, int)); +extern void haddr_conv802 P((u_char *, u_char *, int)); + +#undef P + +/* + * Return the length in bytes of a hardware address of the given type. + * Return the canonical name of the network of the given type. + */ +#define haddrlength(type) ((hwinfolist[(int) (type)]).hlen) +#define netname(type) ((hwinfolist[(int) (type)]).name) + +#endif /* HWADDR_H */ diff --git a/libexec/bootpd/lookup.c b/libexec/bootpd/lookup.c new file mode 100644 index 00000000000..2a30a59b2c3 --- /dev/null +++ b/libexec/bootpd/lookup.c @@ -0,0 +1,126 @@ +/* + * lookup.c - Lookup IP address, HW address, netmask + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <netinet/in.h> + +#ifdef ETC_ETHERS +#include <netinet/if_ether.h> +extern int ether_hostton(); +#endif + +#include <netdb.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#endif + +#include "bootp.h" +#include "lookup.h" +#include "report.h" + +/* + * Lookup an Ethernet address and return it. + * Return NULL if addr not found. + */ +u_char * +lookup_hwa(hostname, htype) + char *hostname; + int htype; +{ + switch (htype) { + + /* XXX - How is this done on other systems? -gwr */ +#ifdef ETC_ETHERS + case HTYPE_ETHERNET: + case HTYPE_IEEE802: + { + static struct ether_addr ea; + /* This does a lookup in /etc/ethers */ + if (ether_hostton(hostname, &ea)) { + report(LOG_ERR, "no HW addr for host \"%s\"", + hostname); + return (u_char *) 0; + } + return (u_char *) & ea; + } +#endif /* ETC_ETHERS */ + + default: + report(LOG_ERR, "no lookup for HW addr type %d", htype); + } /* switch */ + + /* If the system can't do it, just return an error. */ + return (u_char *) 0; +} + + +/* + * Lookup an IP address. + * Return non-zero on failure. + */ +int +lookup_ipa(hostname, result) + char *hostname; + u_int32 *result; +{ + struct hostent *hp; + hp = gethostbyname(hostname); + if (!hp) + return -1; + bcopy(hp->h_addr, result, sizeof(*result)); + return 0; +} + + +/* + * Lookup a netmask + * Return non-zero on failure. + * + * XXX - This is OK as a default, but to really make this automatic, + * we would need to get the subnet mask from the ether interface. + * If this is wrong, specify the correct value in the bootptab. + */ +int +lookup_netmask(addr, result) + u_int32 addr; /* both in network order */ + u_int32 *result; +{ + int32 m, a; + + a = ntohl(addr); + m = 0; + + if (IN_CLASSA(a)) + m = IN_CLASSA_NET; + + if (IN_CLASSB(a)) + m = IN_CLASSB_NET; + + if (IN_CLASSC(a)) + m = IN_CLASSC_NET; + + if (!m) + return -1; + *result = htonl(m); + return 0; +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/lookup.h b/libexec/bootpd/lookup.h new file mode 100644 index 00000000000..04805d8915c --- /dev/null +++ b/libexec/bootpd/lookup.h @@ -0,0 +1,15 @@ +/* lookup.h */ + +#include "bptypes.h" /* for int32, u_int32 */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern u_char *lookup_hwa P((char *hostname, int htype)); +extern int lookup_ipa P((char *hostname, u_int32 *addr)); +extern int lookup_netmask P((u_int32 addr, u_int32 *mask)); + +#undef P diff --git a/libexec/bootpd/patchlevel.h b/libexec/bootpd/patchlevel.h new file mode 100644 index 00000000000..d48f9d2ec9e --- /dev/null +++ b/libexec/bootpd/patchlevel.h @@ -0,0 +1,3 @@ +/* patchlevel.h */ +#define VERSION "2.3" +#define PATCHLEVEL 7 diff --git a/libexec/bootpd/print-bootp.c b/libexec/bootpd/print-bootp.c new file mode 100644 index 00000000000..b6819ddae20 --- /dev/null +++ b/libexec/bootpd/print-bootp.c @@ -0,0 +1,500 @@ +/* + * Copyright (c) 1988-1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Format and print bootp packets. + * + * This file was copied from tcpdump-2.1.1 and modified. + * There is an e-mail list for tcpdump: <tcpdump@ee.lbl.gov> + */ +#ifndef lint +static char rcsid[] = +"@(#) $Header: /cvsroot/src/libexec/bootpd/Attic/print-bootp.c,v 1.1 1994/05/24 15:20:45 gwr Exp $"; +/* 93/10/10 <gwr@mc.com> New data-driven option print routine. */ +#endif + +#include <stdio.h> + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <string.h> +#include <ctype.h> + +#include "bootp.h" + +#ifdef BOOTPTEST +#include "bootptest.h" +#else /* BOOTPTEST */ +#include "interface.h" +#include "addrtoname.h" +#endif /* BOOTPTEST */ + +/* These decode the vendor data. */ +static void rfc1048_print(); +static void cmu_print(); +static void other_print(); +static void dump_hex(); + +/* + * Print bootp requests + */ +void +bootp_print(bp, length, sport, dport) + struct bootp *bp; + int length; + u_short sport, dport; +{ + static char tstr[] = " [|bootp]"; + static unsigned char vm_cmu[4] = VM_CMU; + static unsigned char vm_rfc1048[4] = VM_RFC1048; + u_char *ep; + int vdlen; + +#define TCHECK(var, l) if ((u_char *)&(var) > ep - l) goto trunc + + /* Note funny sized packets */ + if (length != sizeof(struct bootp)) + (void) printf(" [len=%d]", length); + + /* 'ep' points to the end of avaible data. */ + ep = (u_char *) snapend; + + switch (bp->bp_op) { + + case BOOTREQUEST: + /* Usually, a request goes from a client to a server */ + if (sport != IPPORT_BOOTPC || dport != IPPORT_BOOTPS) + printf(" (request)"); + break; + + case BOOTREPLY: + /* Usually, a reply goes from a server to a client */ + if (sport != IPPORT_BOOTPS || dport != IPPORT_BOOTPC) + printf(" (reply)"); + break; + + default: + printf(" bootp-#%d", bp->bp_op); + } + + /* The usual hardware address type is 1 (10Mb Ethernet) */ + if (bp->bp_htype != 1) + printf(" htype:%d", bp->bp_htype); + + /* The usual length for 10Mb Ethernet address is 6 bytes */ + if (bp->bp_hlen != 6) + printf(" hlen:%d", bp->bp_hlen); + + /* Client's Hardware address */ + if (bp->bp_hlen) { + register struct ether_header *eh; + register char *e; + + TCHECK(bp->bp_chaddr[0], 6); + eh = (struct ether_header *) packetp; + if (bp->bp_op == BOOTREQUEST) + e = (char *) ESRC(eh); + else if (bp->bp_op == BOOTREPLY) + e = (char *) EDST(eh); + else + e = 0; + if (e == 0 || bcmp((char *) bp->bp_chaddr, e, 6)) + dump_hex(bp->bp_chaddr, bp->bp_hlen); + } + /* Only print interesting fields */ + if (bp->bp_hops) + printf(" hops:%d", bp->bp_hops); + + if (bp->bp_xid) + printf(" xid:%d", ntohl(bp->bp_xid)); + + if (bp->bp_secs) + printf(" secs:%d", ntohs(bp->bp_secs)); + + /* Client's ip address */ + TCHECK(bp->bp_ciaddr, sizeof(bp->bp_ciaddr)); + if (bp->bp_ciaddr.s_addr) + printf(" C:%s", ipaddr_string(&bp->bp_ciaddr)); + + /* 'your' ip address (bootp client) */ + TCHECK(bp->bp_yiaddr, sizeof(bp->bp_yiaddr)); + if (bp->bp_yiaddr.s_addr) + printf(" Y:%s", ipaddr_string(&bp->bp_yiaddr)); + + /* Server's ip address */ + TCHECK(bp->bp_siaddr, sizeof(bp->bp_siaddr)); + if (bp->bp_siaddr.s_addr) + printf(" S:%s", ipaddr_string(&bp->bp_siaddr)); + + /* Gateway's ip address */ + TCHECK(bp->bp_giaddr, sizeof(bp->bp_giaddr)); + if (bp->bp_giaddr.s_addr) + printf(" G:%s", ipaddr_string(&bp->bp_giaddr)); + + TCHECK(bp->bp_sname[0], sizeof(bp->bp_sname)); + if (*bp->bp_sname) { + printf(" sname:"); + if (printfn(bp->bp_sname, ep)) { + fputs(tstr + 1, stdout); + return; + } + } + TCHECK(bp->bp_file[0], sizeof(bp->bp_file)); + if (*bp->bp_file) { + printf(" file:"); + if (printfn(bp->bp_file, ep)) { + fputs(tstr + 1, stdout); + return; + } + } + /* Don't try to decode the vendor buffer unless we're verbose */ + if (vflag <= 0) + return; + + vdlen = sizeof(bp->bp_vend); + /* Vendor data can extend to the end of the packet. */ + if (vdlen < (ep - bp->bp_vend)) + vdlen = (ep - bp->bp_vend); + + TCHECK(bp->bp_vend[0], vdlen); + printf(" vend"); + if (!bcmp(bp->bp_vend, vm_rfc1048, sizeof(u_int32))) + rfc1048_print(bp->bp_vend, vdlen); + else if (!bcmp(bp->bp_vend, vm_cmu, sizeof(u_int32))) + cmu_print(bp->bp_vend, vdlen); + else + other_print(bp->bp_vend, vdlen); + + return; + trunc: + fputs(tstr, stdout); +#undef TCHECK +} + +/* + * Option description data follows. + * These are decribed in: RFC-1048, RFC-1395, RFC-1497, RFC-1533 + * + * The first char of each option string encodes the data format: + * ?: unknown + * a: ASCII + * b: byte (8-bit) + * i: inet address + * l: int32 + * s: short (16-bit) + */ +char * +rfc1048_opts[] = { + /* Originally from RFC-1048: */ + "?PAD", /* 0: Padding - special, no data. */ + "iSM", /* 1: subnet mask (RFC950)*/ + "lTZ", /* 2: time offset, seconds from UTC */ + "iGW", /* 3: gateways (or routers) */ + "iTS", /* 4: time servers (RFC868) */ + "iINS", /* 5: IEN name servers (IEN116) */ + "iDNS", /* 6: domain name servers (RFC1035)(1034?) */ + "iLOG", /* 7: MIT log servers */ + "iCS", /* 8: cookie servers (RFC865) */ + "iLPR", /* 9: lpr server (RFC1179) */ + "iIPS", /* 10: impress servers (Imagen) */ + "iRLP", /* 11: resource location servers (RFC887) */ + "aHN", /* 12: host name (ASCII) */ + "sBFS", /* 13: boot file size (in 512 byte blocks) */ + + /* Added by RFC-1395: */ + "aDUMP", /* 14: Merit Dump File */ + "aDNAM", /* 15: Domain Name (for DNS) */ + "iSWAP", /* 16: Swap Server */ + "aROOT", /* 17: Root Path */ + + /* Added by RFC-1497: */ + "aEXTF", /* 18: Extensions Path (more options) */ + + /* Added by RFC-1533: (many, many options...) */ +#if 1 /* These might not be worth recognizing by name. */ + + /* IP Layer Parameters, per-host (RFC-1533, sect. 4) */ + "bIP-forward", /* 19: IP Forwarding flag */ + "bIP-srcroute", /* 20: IP Source Routing Enable flag */ + "iIP-filters", /* 21: IP Policy Filter (addr pairs) */ + "sIP-maxudp", /* 22: IP Max-UDP reassembly size */ + "bIP-ttlive", /* 23: IP Time to Live */ + "lIP-pmtuage", /* 24: IP Path MTU aging timeout */ + "sIP-pmtutab", /* 25: IP Path MTU plateau table */ + + /* IP parameters, per-interface (RFC-1533, sect. 5) */ + "sIP-mtu-sz", /* 26: IP MTU size */ + "bIP-mtu-sl", /* 27: IP MTU all subnets local */ + "bIP-bcast1", /* 28: IP Broadcast Addr ones flag */ + "bIP-mask-d", /* 29: IP do mask discovery */ + "bIP-mask-s", /* 30: IP do mask supplier */ + "bIP-rt-dsc", /* 31: IP do router discovery */ + "iIP-rt-sa", /* 32: IP router solicitation addr */ + "iIP-routes", /* 33: IP static routes (dst,router) */ + + /* Link Layer parameters, per-interface (RFC-1533, sect. 6) */ + "bLL-trailer", /* 34: do tralier encapsulation */ + "lLL-arp-tmo", /* 35: ARP cache timeout */ + "bLL-ether2", /* 36: Ethernet version 2 (IEEE 802.3) */ + + /* TCP parameters (RFC-1533, sect. 7) */ + "bTCP-def-ttl", /* 37: default time to live */ + "lTCP-KA-tmo", /* 38: keepalive time interval */ + "bTCP-KA-junk", /* 39: keepalive sends extra junk */ + + /* Application and Service Parameters (RFC-1533, sect. 8) */ + "aNISDOM", /* 40: NIS Domain (Sun YP) */ + "iNISSRV", /* 41: NIS Servers */ + "iNTPSRV", /* 42: NTP (time) Servers (RFC 1129) */ + "?VSINFO", /* 43: Vendor Specific Info (encapsulated) */ + "iNBiosNS", /* 44: NetBIOS Name Server (RFC-1001,1..2) */ + "iNBiosDD", /* 45: NetBIOS Datagram Dist. Server. */ + "bNBiosNT", /* 46: NetBIOS Note Type */ + "?NBiosS", /* 47: NetBIOS Scope */ + "iXW-FS", /* 48: X Window System Font Servers */ + "iXW-DM", /* 49: X Window System Display Managers */ + + /* DHCP extensions (RFC-1533, sect. 9) */ +#endif +}; +#define KNOWN_OPTIONS (sizeof(rfc1048_opts) / sizeof(rfc1048_opts[0])) + +static void print_string(); + +static void +rfc1048_print(bp, length) + register u_char *bp; + int length; +{ + u_char tag; + u_char *ep; + register int len, j; + u_int32 ul; + u_short us; + struct in_addr ia; + char *optstr; + + printf("-rfc1395"); + + /* Step over magic cookie */ + bp += sizeof(int32); + /* Setup end pointer */ + ep = bp + length; + while (bp < ep) { + tag = *bp++; + /* Check for tags with no data first. */ + if (tag == TAG_PAD) + continue; + if (tag == TAG_END) + return; + if (tag < KNOWN_OPTIONS) { + optstr = rfc1048_opts[tag]; + printf(" %s:", optstr + 1); + } else { + printf(" T%d:", tag); + optstr = "?"; + } + /* Now scan the length byte. */ + len = *bp++; + if (bp + len > ep) { + /* truncated option */ + printf(" |(%d>%d)", len, ep - bp); + return; + } + /* Print the option value(s). */ + switch (optstr[0]) { + + case 'a': /* ASCII string */ + printfn(bp, bp + len); + bp += len; + len = 0; + break; + + case 's': /* Word formats */ + while (len >= 2) { + bcopy((char *) bp, (char *) &us, 2); + printf("%d", ntohs(us)); + bp += 2; + len -= 2; + if (len) printf(","); + } + if (len) printf("(junk=%d)", len); + break; + + case 'l': /* Long words */ + while (len >= 4) { + bcopy((char *) bp, (char *) &ul, 4); + printf("%d", ntohl(ul)); + bp += 4; + len -= 4; + if (len) printf(","); + } + if (len) printf("(junk=%d)", len); + break; + + case 'i': /* INET addresses */ + while (len >= 4) { + bcopy((char *) bp, (char *) &ia, 4); + printf("%s", ipaddr_string(&ia)); + bp += 4; + len -= 4; + if (len) printf(","); + } + if (len) printf("(junk=%d)", len); + break; + + case 'b': + default: + break; + + } /* switch */ + + /* Print as characters, if appropriate. */ + if (len) { + dump_hex(bp, len); + if (isascii(*bp) && isprint(*bp)) { + printf("("); + printfn(bp, bp + len); + printf(")"); + } + bp += len; + len = 0; + } + } /* while bp < ep */ +} + +static void +cmu_print(bp, length) + register u_char *bp; + int length; +{ + struct cmu_vend *v; + u_char *ep; + + printf("-cmu"); + + v = (struct cmu_vend *) bp; + if (length < sizeof(*v)) { + printf(" |L=%d", length); + return; + } + /* Setup end pointer */ + ep = bp + length; + + /* Subnet mask */ + if (v->v_flags & VF_SMASK) { + printf(" SM:%s", ipaddr_string(&v->v_smask)); + } + /* Default gateway */ + if (v->v_dgate.s_addr) + printf(" GW:%s", ipaddr_string(&v->v_dgate)); + + /* Domain name servers */ + if (v->v_dns1.s_addr) + printf(" DNS1:%s", ipaddr_string(&v->v_dns1)); + if (v->v_dns2.s_addr) + printf(" DNS2:%s", ipaddr_string(&v->v_dns2)); + + /* IEN-116 name servers */ + if (v->v_ins1.s_addr) + printf(" INS1:%s", ipaddr_string(&v->v_ins1)); + if (v->v_ins2.s_addr) + printf(" INS2:%s", ipaddr_string(&v->v_ins2)); + + /* Time servers */ + if (v->v_ts1.s_addr) + printf(" TS1:%s", ipaddr_string(&v->v_ts1)); + if (v->v_ts2.s_addr) + printf(" TS2:%s", ipaddr_string(&v->v_ts2)); + +} + + +/* + * Print out arbitrary, unknown vendor data. + */ + +static void +other_print(bp, length) + register u_char *bp; + int length; +{ + u_char *ep; /* end pointer */ + u_char *zp; /* points one past last non-zero byte */ + register int i, j; + + /* Setup end pointer */ + ep = bp + length; + + /* Find the last non-zero byte. */ + for (zp = ep; zp > bp; zp--) { + if (zp[-1] != 0) + break; + } + + /* Print the all-zero case in a compact representation. */ + if (zp == bp) { + printf("-all-zero"); + return; + } + printf("-unknown"); + + /* Are there enough trailing zeros to make "00..." worthwhile? */ + if (zp + 2 > ep) + zp = ep; /* print them all normally */ + + /* Now just print all the non-zero data. */ + while (bp < zp) { + printf(".%02X", *bp); + bp++; + } + + if (zp < ep) + printf(".00..."); + + return; +} + +static void +dump_hex(bp, len) + u_char *bp; + int len; +{ + while (len > 0) { + printf("%02X", *bp); + bp++; + len--; + if (len) printf("."); + } +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/readfile.c b/libexec/bootpd/readfile.c index 6bebb3ff6fe..f3950268948 100644 --- a/libexec/bootpd/readfile.c +++ b/libexec/bootpd/readfile.c @@ -1,5 +1,3 @@ -#ifndef _BLURB_ -#define _BLURB_ /************************************************************************ Copyright 1988, 1991 by Carnegie Mellon University @@ -21,11 +19,9 @@ 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 rcsid[] = "$Header: /cvsroot/src/libexec/bootpd/Attic/readfile.c,v 1.1 1994/04/18 05:16:03 glass Exp $"; +static char rcsid[] = "$Header: /cvsroot/src/libexec/bootpd/Attic/readfile.c,v 1.2 1994/05/24 15:20:47 gwr Exp $"; #endif @@ -38,45 +34,60 @@ static char rcsid[] = "$Header: /cvsroot/src/libexec/bootpd/Attic/readfile.c,v 1 */ -#include <stdio.h> -#include <strings.h> -#include <ctype.h> #include <sys/errno.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/file.h> #include <sys/time.h> #include <netinet/in.h> -#ifdef SYSLOG + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> #include <syslog.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#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) #endif + #include "bootp.h" #include "hash.h" +#include "hwaddr.h" +#include "lookup.h" +#include "readfile.h" +#include "report.h" +#include "tzone.h" #include "bootpd.h" +#define HASHTABLESIZE 257 /* Hash table size (prime) */ +/* Non-standard hardware address type (see bootp.h) */ +#define HTYPE_DIRECT 0 + +/* Error codes returned by eval_symbol: */ #define SUCCESS 0 #define E_END_OF_ENTRY (-1) #define E_SYNTAX_ERROR (-2) #define E_UNKNOWN_SYMBOL (-3) #define E_BAD_IPADDR (-4) -#define E_BAD_HADDR (-5) -#define E_BAD_SMASK (-6) -#define E_BAD_TIMEOFF (-7) -#define E_BAD_VM_COOKIE (-8) -#define E_BAD_HTYPE (-9) -#define E_BAD_BOOTSIZE (-10) -#define E_BAD_BOOT_SERVER (-11) -#define E_BAD_HOMEDIR (-12) -#define E_BAD_TFTPDIR (-13) -#define E_BAD_SWAP_SERVER (-14) +#define E_BAD_HWADDR (-5) +#define E_BAD_LONGWORD (-6) +#define E_BAD_HWATYPE (-7) +#define E_BAD_PATHNAME (-8) +/* Tag idendities. */ #define SYM_NULL 0 #define SYM_BOOTFILE 1 #define SYM_COOKIE_SERVER 2 #define SYM_DOMAIN_SERVER 3 #define SYM_GATEWAY 4 -#define SYM_HADDR 5 +#define SYM_HWADDR 5 #define SYM_HOMEDIR 6 #define SYM_HTYPE 7 #define SYM_IMPRESS_SERVER 8 @@ -94,42 +105,49 @@ static char rcsid[] = "$Header: /cvsroot/src/libexec/bootpd/Attic/readfile.c,v 1 #define SYM_BOOTSIZE 20 #define SYM_BOOT_SERVER 22 #define SYM_TFTPDIR 23 -#define SYM_DUMPFILE 24 +#define SYM_DUMP_FILE 24 #define SYM_DOMAIN_NAME 25 #define SYM_SWAP_SERVER 26 -#define SYM_ROOTPATH 27 - -#define OP_ADDITION 1 /* Operations on tags */ +#define SYM_ROOT_PATH 27 +#define SYM_EXTEN_FILE 28 +#define SYM_REPLY_ADDR 29 +#define SYM_NIS_DOMAIN 30 /* RFC 1533 */ +#define SYM_NIS_SERVER 31 /* RFC 1533 */ +#define SYM_NTP_SERVER 32 /* RFC 1533 */ +/* XXX - Add new tags here */ + +#define OP_ADDITION 1 /* Operations on tags */ #define OP_DELETION 2 #define OP_BOOLEAN 3 -#define MAXINADDRS 16 /* Max size of an IP address list */ -#define MAXBUFLEN 64 /* Max temp buffer space */ +#define MAXINADDRS 16 /* Max size of an IP address list */ +#define MAXBUFLEN 64 /* Max temp buffer space */ #define MAXENTRYLEN 2048 /* Max size of an entire entry */ - + /* * Structure used to map a configuration-file symbol (such as "ds") to a * unique integer. */ struct symbolmap { - char *symbol; - int symbolcode; + char *symbol; + int symbolcode; }; struct htypename { - char *name; - byte htype; + char *name; + byte htype; }; -PRIVATE int nhosts; /* Number of hosts (/w hw or IP address) */ -PRIVATE int nentries; /* Total number of entries */ -PRIVATE long modtime = 0; /* Last modification time of bootptab */ - +PRIVATE int nhosts; /* Number of hosts (/w hw or IP address) */ +PRIVATE int nentries; /* Total number of entries */ +PRIVATE int32 modtime = 0; /* Last modification time of bootptab */ +PRIVATE char *current_hostname; /* Name of the current entry. */ +PRIVATE char current_tagname[8]; /* * List of symbolic names used in the bootptab file. The order and actual @@ -138,32 +156,38 @@ PRIVATE long modtime = 0; /* Last modification time of bootptab */ */ PRIVATE struct symbolmap symbol_list[] = { - { "bf", SYM_BOOTFILE }, - { "bs", SYM_BOOTSIZE }, - { "cs", SYM_COOKIE_SERVER }, - { "df", SYM_DUMPFILE }, - { "dn", SYM_DOMAIN_NAME }, - { "ds", SYM_DOMAIN_SERVER }, - { "gw", SYM_GATEWAY }, - { "ha", SYM_HADDR }, - { "hd", SYM_HOMEDIR }, - { "hn", SYM_NAME_SWITCH }, - { "ht", SYM_HTYPE }, - { "im", SYM_IMPRESS_SERVER }, - { "ip", SYM_IPADDR }, - { "lg", SYM_LOG_SERVER }, - { "lp", SYM_LPR_SERVER }, - { "ns", SYM_NAME_SERVER }, - { "rl", SYM_RLP_SERVER }, - { "rp", SYM_ROOTPATH }, - { "sa", SYM_BOOT_SERVER }, - { "sm", SYM_SUBNET_MASK }, - { "sw", SYM_SWAP_SERVER }, - { "tc", SYM_SIMILAR_ENTRY }, - { "td", SYM_TFTPDIR }, - { "to", SYM_TIME_OFFSET }, - { "ts", SYM_TIME_SERVER }, - { "vm", SYM_VENDOR_MAGIC }, + {"bf", SYM_BOOTFILE}, + {"bs", SYM_BOOTSIZE}, + {"cs", SYM_COOKIE_SERVER}, + {"df", SYM_DUMP_FILE}, + {"dn", SYM_DOMAIN_NAME}, + {"ds", SYM_DOMAIN_SERVER}, + {"ef", SYM_EXTEN_FILE}, + {"gw", SYM_GATEWAY}, + {"ha", SYM_HWADDR}, + {"hd", SYM_HOMEDIR}, + {"hn", SYM_NAME_SWITCH}, + {"ht", SYM_HTYPE}, + {"im", SYM_IMPRESS_SERVER}, + {"ip", SYM_IPADDR}, + {"lg", SYM_LOG_SERVER}, + {"lp", SYM_LPR_SERVER}, + {"ns", SYM_NAME_SERVER}, + {"nt", SYM_NTP_SERVER}, + {"ra", SYM_REPLY_ADDR}, + {"rl", SYM_RLP_SERVER}, + {"rp", SYM_ROOT_PATH}, + {"sa", SYM_BOOT_SERVER}, + {"sm", SYM_SUBNET_MASK}, + {"sw", SYM_SWAP_SERVER}, + {"tc", SYM_SIMILAR_ENTRY}, + {"td", SYM_TFTPDIR}, + {"to", SYM_TIME_OFFSET}, + {"ts", SYM_TIME_SERVER}, + {"vm", SYM_VENDOR_MAGIC}, + {"yd", SYM_NIS_DOMAIN}, + {"ys", SYM_NIS_SERVER}, + /* XXX - Add new tags here */ }; @@ -175,21 +199,21 @@ PRIVATE struct symbolmap symbol_list[] = { */ PRIVATE struct htypename htnamemap[] = { - { "ethernet", HTYPE_ETHERNET }, - { "ethernet3", HTYPE_EXP_ETHERNET }, - { "ether", HTYPE_ETHERNET }, - { "ether3", HTYPE_EXP_ETHERNET }, - { "ieee802", HTYPE_IEEE802 }, - { "tr", HTYPE_IEEE802 }, - { "token-ring", HTYPE_IEEE802 }, - { "pronet", HTYPE_PRONET }, - { "chaos", HTYPE_CHAOS }, - { "arcnet", HTYPE_ARCNET }, - { "ax.25", HTYPE_AX25 }, - { "direct", HTYPE_DIRECT }, - { "serial", HTYPE_DIRECT }, - { "slip", HTYPE_DIRECT }, - { "ppp", HTYPE_DIRECT } + {"ethernet", HTYPE_ETHERNET}, + {"ethernet3", HTYPE_EXP_ETHERNET}, + {"ether", HTYPE_ETHERNET}, + {"ether3", HTYPE_EXP_ETHERNET}, + {"ieee802", HTYPE_IEEE802}, + {"tr", HTYPE_IEEE802}, + {"token-ring", HTYPE_IEEE802}, + {"pronet", HTYPE_PRONET}, + {"chaos", HTYPE_CHAOS}, + {"arcnet", HTYPE_ARCNET}, + {"ax.25", HTYPE_AX25}, + {"direct", HTYPE_DIRECT}, + {"serial", HTYPE_DIRECT}, + {"slip", HTYPE_DIRECT}, + {"ppp", HTYPE_DIRECT} }; @@ -198,32 +222,93 @@ PRIVATE struct htypename htnamemap[] = { * Externals and forward declarations. */ -extern char *malloc(); +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + extern boolean iplookcmp(); -extern int errno; - -PRIVATE char *smalloc(); -PRIVATE void fill_defaults(); -PRIVATE void del_string(); -PRIVATE void del_bindata(); -PRIVATE void del_iplist(); -PRIVATE void free_host(); -PRIVATE u_long get_u_long(); -PRIVATE void process_generic(); -PRIVATE char *get_string(); -PRIVATE struct shared_string *get_shared_string(); -PRIVATE void read_entry(); -PRIVATE boolean nullcmp(); -PRIVATE boolean nmcmp(); -PRIVATE boolean hwinscmp(); -PRIVATE void adjust(); -PRIVATE void eat_whitespace(); -PRIVATE void makelower(); -PRIVATE struct in_addr_list *get_addresses(); -PRIVATE byte *prs_haddr(); +boolean nmcmp P((hash_datum *, hash_datum *)); + +PRIVATE void + adjust P((char **)); +PRIVATE void + del_string P((struct shared_string *)); +PRIVATE void + del_bindata P((struct shared_bindata *)); +PRIVATE void + del_iplist P((struct in_addr_list *)); +PRIVATE void + eat_whitespace P((char **)); +PRIVATE int + eval_symbol P((char **, struct host *)); +PRIVATE void + fill_defaults P((struct host *, char **)); +PRIVATE void + free_host P((hash_datum *)); +PRIVATE struct in_addr_list * + get_addresses P((char **)); +PRIVATE struct shared_string * + get_shared_string P((char **)); +PRIVATE char * + get_string P((char **, char *, u_int *)); +PRIVATE u_int32 + get_u_long P((char **)); +PRIVATE boolean + goodname P((char *)); +PRIVATE boolean + hwinscmp P((hash_datum *, hash_datum *)); +PRIVATE int + interp_byte P((char **, byte *)); +PRIVATE void + makelower P((char *)); +PRIVATE boolean + nullcmp P((hash_datum *, hash_datum *)); +PRIVATE int + process_entry P((struct host *, char *)); +PRIVATE int + process_generic P((char **, struct shared_bindata **, u_int)); +PRIVATE byte * + prs_haddr P((char **, u_int)); +PRIVATE int + prs_inetaddr P((char **, u_int32 *)); +PRIVATE void + read_entry P((FILE *, char *, u_int *)); +PRIVATE char * + smalloc P((u_int)); + +#undef P + +/* + * Vendor magic cookies for CMU and RFC1048 + */ +u_char vm_cmu[4] = VM_CMU; +u_char vm_rfc1048[4] = VM_RFC1048; +/* + * Main hash tables + */ +hash_tbl *hwhashtable; +hash_tbl *iphashtable; +hash_tbl *nmhashtable; +/* + * Allocate hash tables for hardware address, ip address, and hostname + * (shared by bootpd and bootpef) + */ +void +rdtab_init() +{ + hwhashtable = hash_Init(HASHTABLESIZE); + iphashtable = hash_Init(HASHTABLESIZE); + nmhashtable = hash_Init(HASHTABLESIZE); + if (!(hwhashtable && iphashtable && nmhashtable)) { + report(LOG_ERR, "Unable to allocate hash tables."); + exit(1); + } +} /* @@ -231,126 +316,170 @@ PRIVATE byte *prs_haddr(); * write date hasn't changed since the last time we read it. */ -int readtab() +void +readtab(force) + int force; { - struct host *hp; - FILE *fp; - struct stat st; - unsigned hashcode, buflen; - static char buffer[MAXENTRYLEN]; -#ifdef DEBUG - char timestr[26]; -#endif + struct host *hp; + FILE *fp; + struct stat st; + unsigned hashcode, buflen; + static char buffer[MAXENTRYLEN]; - /* - * Check the last modification time. - */ - if (stat(bootptab, &st) < 0) { - report(LOG_ERR, "stat on \"%s\": %s\n", bootptab, get_errmsg()); - return -1; - } + /* + * Check the last modification time. + */ + if (stat(bootptab, &st) < 0) { + report(LOG_ERR, "stat on \"%s\": %s", + bootptab, get_errmsg()); + return; + } #ifdef DEBUG - if (debug > 3) { - strcpy(timestr, ctime(&(st.st_mtime))); - report(LOG_INFO, "bootptab mtime is %s", - timestr); - } + if (debug > 3) { + char timestr[28]; + strcpy(timestr, ctime(&(st.st_mtime))); + /* zap the newline */ + timestr[24] = '\0'; + report(LOG_INFO, "bootptab mtime: %s", + timestr); + } #endif - if (st.st_mtime == modtime && st.st_nlink) { + if ((force == 0) && + (st.st_mtime == modtime) && + st.st_nlink) { + /* + * hasn't been modified or deleted yet. + */ + return; + } + if (debug) + report(LOG_INFO, "reading %s\"%s\"", + (modtime != 0L) ? "new " : "", + bootptab); + /* - * hasn't been modified or deleted yet. + * Open bootptab file. */ - return 0; - } - report(LOG_INFO, "reading %s\"%s\"\n", - (modtime != 0L) ? "new " : "", - bootptab); - - /* - * Open bootptab file. - */ - if ((fp = fopen(bootptab, "r")) == NULL) { - report(LOG_ERR, "error opening \"%s\": %s\n", bootptab, get_errmsg()); - return -1; - } - - /* - * Record file modification time. - */ - if (fstat(fileno(fp), &st) < 0) { - report(LOG_ERR, "fstat: %s\n", get_errmsg()); - fclose(fp); - return -1; - } - modtime = st.st_mtime; - - /* - * Entirely erase all hash tables. - */ - hash_Reset(hwhashtable, free_host); - hash_Reset(iphashtable, free_host); - hash_Reset(nmhashtable, free_host); - - nhosts = 0; - nentries = 0; - while (TRUE) { - buflen = sizeof(buffer); - read_entry(fp, buffer, &buflen); - if (buflen == 0) { /* More entries? */ - break; + if ((fp = fopen(bootptab, "r")) == NULL) { + report(LOG_ERR, "error opening \"%s\": %s", bootptab, get_errmsg()); + return; } - hp = (struct host *) smalloc(sizeof(struct host)); - /* - * Get individual info + * Record file modification time. */ - hp->flags.vm_auto = TRUE; - bcopy(vm_rfc1048, hp->vm_cookie, 4); - if (process_entry(hp, buffer) < 0) { - free_host(hp); - continue; + if (fstat(fileno(fp), &st) < 0) { + report(LOG_ERR, "fstat: %s", get_errmsg()); + fclose(fp); + return; } + modtime = st.st_mtime; - if ((hp->flags.htype && hp->flags.haddr) || hp->flags.iaddr) { - nhosts++; - } - if (hp->flags.htype && hp->flags.haddr) { - hashcode = hash_HashFunction(hp->haddr, haddrlength(hp->htype)); - if (hash_Insert(hwhashtable, hashcode, hwinscmp, hp, hp) < 0) { - report(LOG_WARNING, "duplicate %s address: %s\n", - netname(hp->htype), - haddrtoa(hp->haddr, hp->htype)); - free_host(hp); - continue; - } - } - if (hp->flags.iaddr) { - hashcode = hash_HashFunction(&(hp->iaddr.s_addr), 4); - if (hash_Insert(iphashtable, hashcode, nullcmp, hp, hp) < 0) { - report(LOG_ERR, - "hash_Insert() failed on IP address insertion\n"); - } - } + /* + * Entirely erase all hash tables. + */ + hash_Reset(hwhashtable, free_host); + hash_Reset(iphashtable, free_host); + hash_Reset(nmhashtable, free_host); + + nhosts = 0; + nentries = 0; + while (TRUE) { + buflen = sizeof(buffer); + read_entry(fp, buffer, &buflen); + if (buflen == 0) { /* More entries? */ + break; + } + hp = (struct host *) smalloc(sizeof(struct host)); + bzero((char *) hp, sizeof(*hp)); + /* the link count it zero */ + + /* + * Get individual info + */ + if (process_entry(hp, buffer) < 0) { + hp->linkcount = 1; + free_host((hash_datum *) hp); + continue; + } + /* + * If this is not a dummy entry, and the IP or HW + * address is not yet set, try to get them here. + * Dummy entries have . as first char of name. + */ + if (goodname(hp->hostname->string)) { + char *hn = hp->hostname->string; + u_int32 value; + if (hp->flags.iaddr == 0) { + if (lookup_ipa(hn, &value)) { + report(LOG_ERR, "can not get IP addr for %s", hn); + report(LOG_ERR, "(dummy names should start with '.')"); + } else { + hp->iaddr.s_addr = value; + hp->flags.iaddr = TRUE; + } + } + /* Set default subnet mask. */ + if (hp->flags.subnet_mask == 0) { + if (lookup_netmask(hp->iaddr.s_addr, &value)) { + report(LOG_ERR, "can not get netmask for %s", hn); + } else { + hp->subnet_mask.s_addr = value; + hp->flags.subnet_mask = TRUE; + } + } + } + if (hp->flags.iaddr) { + nhosts++; + } + /* Register by HW addr if known. */ + if (hp->flags.htype && hp->flags.haddr) { + /* We will either insert it or free it. */ + hp->linkcount++; + hashcode = hash_HashFunction(hp->haddr, haddrlength(hp->htype)); + if (hash_Insert(hwhashtable, hashcode, hwinscmp, hp, hp) < 0) { + report(LOG_NOTICE, "duplicate %s address: %s", + netname(hp->htype), + haddrtoa(hp->haddr, hp->htype)); + free_host((hash_datum *) hp); + continue; + } + } + /* Register by IP addr if known. */ + if (hp->flags.iaddr) { + hashcode = hash_HashFunction((u_char *) & (hp->iaddr.s_addr), 4); + if (hash_Insert(iphashtable, hashcode, nullcmp, hp, hp) < 0) { + report(LOG_ERR, + "hash_Insert() failed on IP address insertion"); + } else { + /* Just inserted the host struct in a new hash list. */ + hp->linkcount++; + } + } + /* Register by Name (always known) */ + hashcode = hash_HashFunction((u_char *) hp->hostname->string, + strlen(hp->hostname->string)); + if (hash_Insert(nmhashtable, hashcode, nullcmp, + hp->hostname->string, hp) < 0) { + report(LOG_ERR, + "hash_Insert() failed on insertion of hostname: \"%s\"", + hp->hostname->string); + } else { + /* Just inserted the host struct in a new hash list. */ + hp->linkcount++; + } - hashcode = hash_HashFunction(hp->hostname->string, - strlen(hp->hostname->string)); - if (hash_Insert(nmhashtable, hashcode, nullcmp, hp->hostname->string, hp) < 0) { - report(LOG_ERR, - "hash_Insert() failed on insertion of hostname: \"%s\"\n", - hp->hostname->string); + nentries++; } - nentries++; - } - -done: - fclose(fp); - report(LOG_INFO, "read %d entries (%d hosts) from \"%s\"\n", - nentries, nhosts, bootptab); - return 0; -} + fclose(fp); + if (debug) + report(LOG_INFO, "read %d entries (%d hosts) from \"%s\"", + nentries, nhosts, bootptab); + return; +} + /* * Read an entire host entry from the file pointed to by "fp" and insert it * into the memory pointed to by "buffer". Leading whitespace and comments @@ -370,135 +499,135 @@ done: * the easiest way to implement it. Maybe later I'll clean it up. */ -PRIVATE void read_entry(fp, buffer, bufsiz) -FILE *fp; -char *buffer; -unsigned *bufsiz; +PRIVATE void +read_entry(fp, buffer, bufsiz) + FILE *fp; + char *buffer; + unsigned *bufsiz; { - int c, length; - - length = 0; - - /* - * Eat whitespace, blank lines, and comment lines. - */ -top: - c = fgetc(fp); - if (c < 0) { - goto done; /* Exit if end-of-file */ - } - if (isspace(c)) { - goto top; /* Skip over whitespace */ - } - if (c == '#') { - while (TRUE) { /* Eat comments after # */ - c = fgetc(fp); - if (c < 0) { - goto done; /* Exit if end-of-file */ - } - if (c == '\n') { - goto top; /* Try to read the next line */ - } + int c, length; + + length = 0; + + /* + * Eat whitespace, blank lines, and comment lines. + */ + top: + c = fgetc(fp); + if (c < 0) { + goto done; /* Exit if end-of-file */ + } + if (isspace(c)) { + goto top; /* Skip over whitespace */ } - } - ungetc(c, fp); /* Other character, push it back to reprocess it */ + if (c == '#') { + while (TRUE) { /* Eat comments after # */ + c = fgetc(fp); + if (c < 0) { + goto done; /* Exit if end-of-file */ + } + if (c == '\n') { + goto top; /* Try to read the next line */ + } + } + } + ungetc(c, fp); /* Other character, push it back to reprocess it */ - /* - * Now we're actually reading a data entry. Get each character and - * assemble it into the data buffer, processing special characters like - * double quotes (") and backslashes (\). - */ + /* + * Now we're actually reading a data entry. Get each character and + * assemble it into the data buffer, processing special characters like + * double quotes (") and backslashes (\). + */ -mainloop: - c = fgetc(fp); - switch (c) { - case EOF: + mainloop: + c = fgetc(fp); + switch (c) { + case EOF: case '\n': - goto done; /* Exit on EOF or newline */ + goto done; /* Exit on EOF or newline */ case '\\': - c = fgetc(fp); /* Backslash, read a new character */ - if (c < 0) { - goto done; /* Exit on EOF */ - } - *buffer++ = c; /* Store the literal character */ - length++; - if (length < *bufsiz - 1) { - goto mainloop; - } else { - goto done; - } + c = fgetc(fp); /* Backslash, read a new character */ + if (c < 0) { + goto done; /* Exit on EOF */ + } + *buffer++ = c; /* Store the literal character */ + length++; + if (length < *bufsiz - 1) { + goto mainloop; + } else { + goto done; + } case '"': - *buffer++ = '"'; /* Store double-quote */ - length++; - if (length >= *bufsiz - 1) { - goto done; - } - while (TRUE) { /* Special quote processing loop */ - c = fgetc(fp); - switch (c) { - case EOF: - goto done; /* Exit on EOF . . . */ - case '"': - *buffer++ = '"'; /* Store matching quote */ - length++; - if (length < *bufsiz - 1) { - goto mainloop; /* And continue main loop */ - } else { - goto done; - } - case '\\': - if ((c = fgetc(fp)) < 0) { /* Backslash */ - goto done; /* EOF. . . .*/ - } /* else fall through */ - default: - *buffer++ = c; /* Other character, store it */ - length++; - if (length >= *bufsiz - 1) { - goto done; + *buffer++ = '"'; /* Store double-quote */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + while (TRUE) { /* Special quote processing loop */ + c = fgetc(fp); + switch (c) { + case EOF: + goto done; /* Exit on EOF . . . */ + case '"': + *buffer++ = '"';/* Store matching quote */ + length++; + if (length < *bufsiz - 1) { + goto mainloop; /* And continue main loop */ + } else { + goto done; + } + case '\\': + if ((c = fgetc(fp)) < 0) { /* Backslash */ + goto done; /* EOF. . . .*/ + } /* else fall through */ + default: + *buffer++ = c; /* Other character, store it */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } } } - } case ':': - *buffer++ = c; /* Store colons */ - length++; - if (length >= *bufsiz - 1) { - goto done; - } - - do { /* But remove whitespace after them */ - c = fgetc(fp); - if ((c < 0) || (c == '\n')) { - goto done; + *buffer++ = c; /* Store colons */ + length++; + if (length >= *bufsiz - 1) { + goto done; } - } while (isspace(c)); /* Skip whitespace */ + do { /* But remove whitespace after them */ + c = fgetc(fp); + if ((c < 0) || (c == '\n')) { + goto done; + } + } while (isspace(c)); /* Skip whitespace */ - if (c == '\\') { /* Backslash quotes next character */ - c = fgetc(fp); - if (c < 0) { - goto done; - } - if (c == '\n') { - goto top; /* Backslash-newline continuation */ + if (c == '\\') { /* Backslash quotes next character */ + c = fgetc(fp); + if (c < 0) { + goto done; + } + if (c == '\n') { + goto top; /* Backslash-newline continuation */ + } } - } - /* fall through if "other" character */ + /* fall through if "other" character */ default: - *buffer++ = c; /* Store other characters */ - length++; - if (length >= *bufsiz - 1) { - goto done; - } - } - goto mainloop; /* Keep going */ - -done: - *buffer = '\0'; /* Terminate string */ - *bufsiz = length; /* Tell the caller its length */ -} + *buffer++ = c; /* Store other characters */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + } + goto mainloop; /* Keep going */ + done: + *buffer = '\0'; /* Terminate string */ + *bufsiz = length; /* Tell the caller its length */ +} + /* * Parse out all the various tags and parameters in the host entry pointed * to by "src". Stuff all the data into the appropriate fields of the @@ -509,112 +638,128 @@ done: * (Some errors probably shouldn't be so completely fatal. . . .) */ -PRIVATE int process_entry(host, src) -struct host *host; -char *src; +PRIVATE int +process_entry(host, src) + struct host *host; + char *src; { - int retval; - - if (!host || *src == '\0') { - return -1; - } - host->hostname = get_shared_string(&src); - if (!goodname(host->hostname->string)) { - report(LOG_ERR, "bad hostname: \"%s\"\n", host->hostname->string); - del_string(host->hostname); - return -1; - } - adjust(&src); - while (TRUE) { - retval = eval_symbol(&src, host); - switch (retval) { - case SUCCESS: - break; - case E_SYNTAX_ERROR: - report(LOG_ERR, "syntax error in entry for host \"%s\"\n", - host->hostname->string); - return -1; - case E_UNKNOWN_SYMBOL: - report(LOG_ERR, "unknown symbol in entry for host \"%s\"\n", - host->hostname->string); - return -1; - case E_BAD_IPADDR: - report(LOG_ERR, "bad IP address for host \"%s\"\n", - host->hostname->string); - return -1; - case E_BAD_HADDR: - report(LOG_ERR, "bad hardware address for host \"%s\"\n", - host->hostname->string); - return -1; - case E_BAD_SMASK: - report(LOG_ERR, "bad subnet mask for host \"%s\"\n", - host->hostname->string); - return -1; - case E_BAD_TIMEOFF: - report(LOG_ERR, "bad time offset for host \"%s\"\n", - host->hostname->string); - return -1; - case E_BAD_VM_COOKIE: - report(LOG_ERR, "bad vendor magic cookie for host \"%s\"\n", - host->hostname->string); - return -1; - case E_BAD_HOMEDIR: - report(LOG_ERR, "bad home directory for host \"%s\"\n", - host->hostname->string); - return -1; - case E_BAD_TFTPDIR: - report(LOG_ERR, "bad TFTP directory for host \"%s\"\n", - host->hostname->string); - return -1; - case E_BAD_BOOT_SERVER: - report(LOG_ERR, "bad boot server IP address for host \"%s\"\n", - host->hostname->string); - return -1; - case E_BAD_SWAP_SERVER: - report(LOG_ERR, "bad swap server IP address for host \"%s\"\n", - host->hostname->string); + int retval; + char *msg; + + if (!host || *src == '\0') { return -1; - case E_END_OF_ENTRY: - default: + } + host->hostname = get_shared_string(&src); #if 0 - /* - * For now, don't try to make-up a subnet mask if one - * wasn't specified. - * - * This algorithm is also not entirely correct. - */ - if (!(hp->flags.subnet_mask)) { - /* - * Try to deduce the subnet mask from the network class - */ - value = (ntohl(value) >> 30) & 0x03; - switch (value) { - case 0: - case 1: - hp->subnet_mask.s_addr = htonl(0xFF000000L); - break; - case 2: - hp->subnet_mask.s_addr = htonl(0xFFFF0000L); - break; - case 3: - hp->subnet_mask.s_addr = htonl(0xFFFFFF00L); - break; - } - hp->flags.subnet_mask = TRUE; - } -#endif - /* - * And now we're done with this entry - */ - return 0; + /* Be more liberal for the benefit of dummy tag names. */ + if (!goodname(host->hostname->string)) { + report(LOG_ERR, "bad hostname: \"%s\"", host->hostname->string); + del_string(host->hostname); + return -1; } +#endif + current_hostname = host->hostname->string; adjust(&src); - } + while (TRUE) { + retval = eval_symbol(&src, host); + if (retval == SUCCESS) { + adjust(&src); + continue; + } + if (retval == E_END_OF_ENTRY) { + /* The default subnet mask is set in readtab() */ + return 0; + } + /* Some kind of error. */ + switch (retval) { + case E_SYNTAX_ERROR: + msg = "bad syntax"; + break; + case E_UNKNOWN_SYMBOL: + msg = "unknown symbol"; + break; + case E_BAD_IPADDR: + msg = "bad INET address"; + break; + case E_BAD_HWADDR: + msg = "bad hardware address"; + break; + case E_BAD_LONGWORD: + msg = "bad longword value"; + break; + case E_BAD_HWATYPE: + msg = "bad HW address type"; + break; + case E_BAD_PATHNAME: + msg = "bad pathname (need leading '/')"; + default: + msg = "unkown error"; + break; + } /* switch */ + report(LOG_ERR, "in entry named \"%s\", symbol \"%s\": %s", + current_hostname, current_tagname, msg); + return -1; + } } - /* + * Macros for use in the function below: + */ + +/* Parse one INET address stored directly in MEMBER. */ +#define PARSE_IA1(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = FALSE; \ + if (optype == OP_ADDITION) { \ + if (prs_inetaddr(symbol, &value) < 0) \ + return E_BAD_IPADDR; \ + hp->MEMBER.s_addr = value; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* Parse a list of INET addresses pointed to by MEMBER */ +#define PARSE_IAL(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + if (hp->flags.MEMBER) { \ + hp->flags.MEMBER = FALSE; \ + assert(hp->MEMBER); \ + del_iplist(hp->MEMBER); \ + hp->MEMBER = NULL; \ + } \ + if (optype == OP_ADDITION) { \ + hp->MEMBER = get_addresses(symbol); \ + if (hp->MEMBER == NULL) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* Parse a shared string pointed to by MEMBER */ +#define PARSE_STR(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + if (hp->flags.MEMBER) { \ + hp->flags.MEMBER = FALSE; \ + assert(hp->MEMBER); \ + del_string(hp->MEMBER); \ + hp->MEMBER = NULL; \ + } \ + if (optype == OP_ADDITION) { \ + hp->MEMBER = get_shared_string(symbol); \ + if (hp->MEMBER == NULL) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* * Evaluate the two-character tag symbol pointed to by "symbol" and place * the data in the structure pointed to by "hp". The pointer pointed to * by "symbol" is updated to point past the source string (but may not @@ -622,664 +767,350 @@ char *src; * * Obviously, this need a few more comments. . . . */ - -PRIVATE eval_symbol(symbol, hp) -char **symbol; -struct host *hp; +PRIVATE int +eval_symbol(symbol, hp) + char **symbol; + struct host *hp; { - char tmpstr[MAXSTRINGLEN]; - byte *tmphaddr, *ustr; - struct shared_string *ss; - struct symbolmap *symbolptr; - u_long value; - long timeoff; - int i, numsymbols; - unsigned len; - int optype; /* Indicates boolean, addition, or deletion */ - - if ((*symbol)[0] == '\0') { - return E_END_OF_ENTRY; - } - if ((*symbol)[0] == ':') { - return SUCCESS; - } - if ((*symbol)[0] == 'T') { /* generic symbol */ - (*symbol)++; - value = get_u_long(symbol); + char tmpstr[MAXSTRINGLEN]; + byte *tmphaddr; + struct shared_string *ss; + struct symbolmap *symbolptr; + u_int32 value; + int32 timeoff; + int i, numsymbols; + unsigned len; + int optype; /* Indicates boolean, addition, or deletion */ + eat_whitespace(symbol); - if ((*symbol)[0] != '=') { - return E_SYNTAX_ERROR; - } - (*symbol)++; - if (!(hp->generic)) { - hp->generic = (struct shared_bindata *) - smalloc(sizeof(struct shared_bindata)); - } - process_generic(symbol, &(hp->generic), (byte) (value & 0xFF)); - hp->flags.generic = TRUE; - return SUCCESS; - } - eat_whitespace(symbol); + /* Make sure this is set before returning. */ + current_tagname[0] = (*symbol)[0]; + current_tagname[1] = (*symbol)[1]; + current_tagname[2] = 0; - /* - * Determine the type of operation to be done on this symbol - */ - switch ((*symbol)[2]) { + if ((*symbol)[0] == '\0') { + return E_END_OF_ENTRY; + } + if ((*symbol)[0] == ':') { + return SUCCESS; + } + if ((*symbol)[0] == 'T') { /* generic symbol */ + (*symbol)++; + value = get_u_long(symbol); + sprintf(current_tagname, "T%d", value); + eat_whitespace(symbol); + if ((*symbol)[0] != '=') { + return E_SYNTAX_ERROR; + } + (*symbol)++; + if (!(hp->generic)) { + hp->generic = (struct shared_bindata *) + smalloc(sizeof(struct shared_bindata)); + } + if (process_generic(symbol, &(hp->generic), (byte) (value & 0xFF))) + return E_SYNTAX_ERROR; + hp->flags.generic = TRUE; + return SUCCESS; + } + /* + * Determine the type of operation to be done on this symbol + */ + switch ((*symbol)[2]) { case '=': - optype = OP_ADDITION; - break; + optype = OP_ADDITION; + break; case '@': - optype = OP_DELETION; - break; + optype = OP_DELETION; + break; case ':': case '\0': - optype = OP_BOOLEAN; - break; + optype = OP_BOOLEAN; + break; default: - return E_SYNTAX_ERROR; - } - - symbolptr = symbol_list; - numsymbols = sizeof(symbol_list) / sizeof(struct symbolmap); - for (i = 0; i < numsymbols; i++) { - if (((symbolptr->symbol)[0] == (*symbol)[0]) && - ((symbolptr->symbol)[1] == (*symbol)[1])) { - break; + return E_SYNTAX_ERROR; } - symbolptr++; - } - if (i >= numsymbols) { - return E_UNKNOWN_SYMBOL; - } - - /* - * Skip past the = or @ character (to point to the data) if this - * isn't a boolean operation. For boolean operations, just skip - * over the two-character tag symbol (and nothing else. . . .). - */ - (*symbol) += (optype == OP_BOOLEAN) ? 2 : 3; - - switch (symbolptr->symbolcode) { + + symbolptr = symbol_list; + numsymbols = sizeof(symbol_list) / sizeof(struct symbolmap); + for (i = 0; i < numsymbols; i++) { + if (((symbolptr->symbol)[0] == (*symbol)[0]) && + ((symbolptr->symbol)[1] == (*symbol)[1])) { + break; + } + symbolptr++; + } + if (i >= numsymbols) { + return E_UNKNOWN_SYMBOL; + } + /* + * Skip past the = or @ character (to point to the data) if this + * isn't a boolean operation. For boolean operations, just skip + * over the two-character tag symbol (and nothing else. . . .). + */ + (*symbol) += (optype == OP_BOOLEAN) ? 2 : 3; + + eat_whitespace(symbol); + + /* The cases below are in order by symbolcode value. */ + switch (symbolptr->symbolcode) { + case SYM_BOOTFILE: - switch (optype) { - case OP_ADDITION: - if (ss = get_shared_string(symbol)) { - if (hp->bootfile) { - del_string(hp->bootfile); - } - hp->bootfile = ss; - hp->flags.bootfile = TRUE; - } - break; - case OP_DELETION: - if (hp->bootfile) { - del_string(hp->bootfile); - } - hp->bootfile = NULL; - hp->flags.bootfile = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_STR(bootfile); + break; case SYM_COOKIE_SERVER: - switch (optype) { - case OP_ADDITION: - if (hp->flags.cookie_server && hp->cookie_server) { - del_iplist(hp->cookie_server); - } - hp->cookie_server = get_addresses(symbol); - hp->flags.cookie_server = TRUE; - break; - case OP_DELETION: - if (hp->flags.cookie_server && hp->cookie_server) { - del_iplist(hp->cookie_server); - } - hp->cookie_server = NULL; - hp->flags.cookie_server = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_IAL(cookie_server); + break; case SYM_DOMAIN_SERVER: - switch (optype) { - case OP_ADDITION: - if (hp->flags.domain_server && hp->domain_server) { - del_iplist(hp->domain_server); - } - hp->domain_server = get_addresses(symbol); - hp->flags.domain_server = TRUE; - break; - case OP_DELETION: - if (hp->flags.domain_server && hp->domain_server) { - del_iplist(hp->domain_server); - } - hp->domain_server = NULL; - hp->flags.domain_server = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_IAL(domain_server); + break; case SYM_GATEWAY: - switch (optype) { - case OP_ADDITION: - if (hp->flags.gateway && hp->gateway) { - del_iplist(hp->gateway); - } - hp->gateway = get_addresses(symbol); - hp->flags.gateway = TRUE; - break; - case OP_DELETION: - if (hp->flags.gateway && hp->gateway) { - del_iplist(hp->gateway); - } - hp->gateway = NULL; - hp->flags.gateway = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_IAL(gateway); + break; - case SYM_HADDR: - switch (optype) { - case OP_ADDITION: - if (hp->flags.htype && hp->htype && - (tmphaddr = prs_haddr(symbol, hp->htype))) { + case SYM_HWADDR: + if (optype == OP_BOOLEAN) + return E_SYNTAX_ERROR; + hp->flags.haddr = FALSE; + if (optype == OP_ADDITION) { + /* Default the HW type to Ethernet */ + if (hp->flags.htype == 0) { + hp->flags.htype = TRUE; + hp->htype = HTYPE_ETHERNET; + } + tmphaddr = prs_haddr(symbol, hp->htype); + if (!tmphaddr) + return E_BAD_HWADDR; bcopy(tmphaddr, hp->haddr, haddrlength(hp->htype)); hp->flags.haddr = TRUE; - } else { - return E_BAD_HADDR; - } - break; - case OP_DELETION: - hp->flags.haddr = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + } + break; case SYM_HOMEDIR: - switch (optype) { - case OP_ADDITION: - if (ss = get_shared_string(symbol)) { - if ((ss->string)[0] == '/') { - if (hp->homedir) { - del_string(hp->homedir); - } - hp->homedir = ss; - hp->flags.homedir = TRUE; - } else { - return E_BAD_HOMEDIR; - } - } else { - return E_BAD_HOMEDIR; - } - break; - case OP_DELETION: - if (hp->homedir) { - del_string(hp->homedir); - } - hp->homedir = NULL; - hp->flags.homedir = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_STR(homedir); + if ((hp->homedir != NULL) && + (hp->homedir->string[0] != '/')) + return E_BAD_PATHNAME; + break; case SYM_HTYPE: - switch (optype) { - case OP_ADDITION: - value = 0L; /* Assume an illegal value */ - eat_whitespace(symbol); - if (isdigit(**symbol)) { - value = get_u_long(symbol); - } else { - len = sizeof(tmpstr); - (void) get_string(symbol, tmpstr, &len); - makelower(tmpstr); - numsymbols = sizeof(htnamemap) / - sizeof(struct htypename); - for (i = 0; i < numsymbols; i++) { - if (!strcmp(htnamemap[i].name, tmpstr)) { - break; - } + if (optype == OP_BOOLEAN) + return E_SYNTAX_ERROR; + hp->flags.htype = FALSE; + if (optype == OP_ADDITION) { + value = 0L; /* Assume an illegal value */ + eat_whitespace(symbol); + if (isdigit(**symbol)) { + value = get_u_long(symbol); + } else { + len = sizeof(tmpstr); + (void) get_string(symbol, tmpstr, &len); + makelower(tmpstr); + numsymbols = sizeof(htnamemap) / + sizeof(struct htypename); + for (i = 0; i < numsymbols; i++) { + if (!strcmp(htnamemap[i].name, tmpstr)) { + break; + } + } + if (i < numsymbols) { + value = htnamemap[i].htype; + } } - if (i < numsymbols) { - value = htnamemap[i].htype; + if (value >= hwinfocnt) { + return E_BAD_HWATYPE; } - } - if ((value < 0) || (value > MAXHTYPES)) { - return E_BAD_HTYPE; - } - hp->htype = (byte) (value & 0xFF); - hp->flags.htype = TRUE; - break; - case OP_DELETION: - hp->flags.htype = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + hp->htype = (byte) (value & 0xFF); + hp->flags.htype = TRUE; + } + break; case SYM_IMPRESS_SERVER: - switch (optype) { - case OP_ADDITION: - if (hp->flags.impress_server && hp->impress_server) { - del_iplist(hp->impress_server); - } - hp->impress_server = get_addresses(symbol); - hp->flags.impress_server = TRUE; - break; - case OP_DELETION: - if (hp->flags.impress_server && hp->impress_server) { - del_iplist(hp->impress_server); - } - hp->impress_server = NULL; - hp->flags.impress_server = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_IAL(impress_server); + break; case SYM_IPADDR: - switch (optype) { - case OP_ADDITION: - if (prs_inetaddr(symbol, &value) < 0) { - return E_BAD_IPADDR; - } else { - hp->iaddr.s_addr = value; - hp->flags.iaddr = TRUE; - } - break; - case OP_DELETION: - hp->flags.iaddr = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_IA1(iaddr); + break; case SYM_LOG_SERVER: - switch (optype) { - case OP_ADDITION: - if (hp->flags.log_server && hp->log_server) { - del_iplist(hp->log_server); - } - hp->log_server = get_addresses(symbol); - hp->flags.log_server = TRUE; - break; - case OP_DELETION: - if (hp->flags.log_server && hp->log_server) { - del_iplist(hp->log_server); - } - hp->log_server = NULL; - hp->flags.log_server = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_IAL(log_server); + break; case SYM_LPR_SERVER: - switch (optype) { - case OP_ADDITION: - if (hp->flags.lpr_server && hp->lpr_server) { - del_iplist(hp->lpr_server); - } - hp->lpr_server = get_addresses(symbol); - hp->flags.lpr_server = TRUE; - break; - case OP_DELETION: - if (hp->flags.lpr_server && hp->lpr_server) { - del_iplist(hp->lpr_server); - } - hp->lpr_server = NULL; - hp->flags.lpr_server = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_IAL(lpr_server); + break; case SYM_NAME_SERVER: - switch (optype) { - case OP_ADDITION: - if (hp->flags.name_server && hp->name_server) { - del_iplist(hp->name_server); - } - hp->name_server = get_addresses(symbol); - hp->flags.name_server = TRUE; - break; - case OP_DELETION: - if (hp->flags.name_server && hp->name_server) { - del_iplist(hp->name_server); - } - hp->name_server = NULL; - hp->flags.name_server = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_IAL(name_server); + break; case SYM_RLP_SERVER: - switch (optype) { - case OP_ADDITION: - if (hp->flags.rlp_server && hp->rlp_server) { - del_iplist(hp->rlp_server); - } - hp->rlp_server = get_addresses(symbol); - hp->flags.rlp_server = TRUE; - break; - case OP_DELETION: - if (hp->flags.rlp_server && hp->rlp_server) { - del_iplist(hp->rlp_server); - } - hp->rlp_server = NULL; - hp->flags.rlp_server = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_IAL(rlp_server); + break; case SYM_SUBNET_MASK: - switch (optype) { - case OP_ADDITION: - if (prs_inetaddr(symbol, &value) < 0) { - return E_BAD_SMASK; - } else { - hp->subnet_mask.s_addr = value; - hp->flags.subnet_mask = TRUE; - } - break; - case OP_DELETION: - hp->flags.subnet_mask = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_IA1(subnet_mask); + break; case SYM_TIME_OFFSET: - switch (optype) { - case OP_ADDITION: - len = sizeof(tmpstr); - (void) get_string(symbol, tmpstr, &len); - if (!strncmp(tmpstr, "auto", 4)) { - hp->flags.timeoff_auto = TRUE; - hp->flags.time_offset = TRUE; - } else { - if (sscanf(tmpstr, "%ld", &timeoff) != 1) { - return E_BAD_TIMEOFF; + if (optype == OP_BOOLEAN) + return E_SYNTAX_ERROR; + hp->flags.time_offset = FALSE; + if (optype == OP_ADDITION) { + len = sizeof(tmpstr); + (void) get_string(symbol, tmpstr, &len); + if (!strncmp(tmpstr, "auto", 4)) { + hp->time_offset = secondswest; } else { - hp->time_offset = timeoff; - hp->flags.timeoff_auto = FALSE; - hp->flags.time_offset = TRUE; + if (sscanf(tmpstr, "%d", &timeoff) != 1) + return E_BAD_LONGWORD; + hp->time_offset = timeoff; } - } - break; - case OP_DELETION: - hp->flags.time_offset = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + hp->flags.time_offset = TRUE; + } + break; case SYM_TIME_SERVER: - switch (optype) { - case OP_ADDITION: - if (hp->flags.time_server && hp->time_server) { - del_iplist(hp->time_server); - } - hp->time_server = get_addresses(symbol); - hp->flags.time_server = TRUE; - break; - case OP_DELETION: - if (hp->flags.time_server && hp->time_server) { - del_iplist(hp->time_server); - } - hp->time_server = NULL; - hp->flags.time_server = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_IAL(time_server); + break; case SYM_VENDOR_MAGIC: - switch (optype) { - case OP_ADDITION: - if (!strncmp(*symbol, "auto", 4)) { - hp->flags.vm_auto = TRUE; /* Make it auto */ - } else if (!strncmp(*symbol, "rfc1048", 7) || - !strncmp(*symbol, "rfc1084", 7)) { - hp->flags.vm_auto = FALSE; /* Make it manual */ - bcopy(vm_rfc1048, hp->vm_cookie, 4); - } else if (!strncmp(*symbol, "cmu", 3)) { - hp->flags.vm_auto = FALSE; /* Make it manual */ - bcopy(vm_cmu, hp->vm_cookie, 4); - } else { - if (prs_inetaddr(symbol, &value) < 0) { - return E_BAD_VM_COOKIE; + if (optype == OP_BOOLEAN) + return E_SYNTAX_ERROR; + hp->flags.vm_cookie = FALSE; + if (optype == OP_ADDITION) { + if (strncmp(*symbol, "auto", 4)) { + /* The string is not "auto" */ + if (!strncmp(*symbol, "rfc", 3)) { + bcopy(vm_rfc1048, hp->vm_cookie, 4); + } else if (!strncmp(*symbol, "cmu", 3)) { + bcopy(vm_cmu, hp->vm_cookie, 4); + } else { + if (!isdigit(**symbol)) + return E_BAD_IPADDR; + if (prs_inetaddr(symbol, &value) < 0) + return E_BAD_IPADDR; + bcopy(&value, hp->vm_cookie, 4); + } + hp->flags.vm_cookie = TRUE; } - hp->flags.vm_auto = FALSE; /* Make it manual */ - ustr = hp->vm_cookie; - insert_u_long(value, &ustr); - } - hp->flags.vendor_magic = TRUE; - break; - case OP_DELETION: - hp->flags.vendor_magic = FALSE; - break; - case OP_BOOLEAN: - hp->flags.vm_auto = TRUE; - hp->flags.vendor_magic = TRUE; - break; - } - break; + } + break; case SYM_SIMILAR_ENTRY: - switch (optype) { + switch (optype) { case OP_ADDITION: - fill_defaults(hp, symbol); - break; + fill_defaults(hp, symbol); + break; default: - return E_SYNTAX_ERROR; - } - break; + return E_SYNTAX_ERROR; + } + break; case SYM_NAME_SWITCH: - switch (optype) { + switch (optype) { case OP_ADDITION: - return E_SYNTAX_ERROR; + return E_SYNTAX_ERROR; case OP_DELETION: - hp->flags.send_name = FALSE; - hp->flags.name_switch = FALSE; - break; + hp->flags.send_name = FALSE; + hp->flags.name_switch = FALSE; + break; case OP_BOOLEAN: - hp->flags.send_name = TRUE; - hp->flags.name_switch = TRUE; - break; - } - break; + hp->flags.send_name = TRUE; + hp->flags.name_switch = TRUE; + break; + } + break; case SYM_BOOTSIZE: - switch (optype) { + switch (optype) { case OP_ADDITION: - if (!strncmp(*symbol, "auto", 4)) { - hp->flags.bootsize = TRUE; - hp->flags.bootsize_auto = TRUE; - } else { - hp->bootsize = (unsigned int) get_u_long(symbol); - hp->flags.bootsize = TRUE; - hp->flags.bootsize_auto = FALSE; - } - break; + if (!strncmp(*symbol, "auto", 4)) { + hp->flags.bootsize = TRUE; + hp->flags.bootsize_auto = TRUE; + } else { + hp->bootsize = (unsigned int) get_u_long(symbol); + hp->flags.bootsize = TRUE; + hp->flags.bootsize_auto = FALSE; + } + break; case OP_DELETION: - hp->flags.bootsize = FALSE; - break; + hp->flags.bootsize = FALSE; + break; case OP_BOOLEAN: - hp->flags.bootsize = TRUE; - hp->flags.bootsize_auto = TRUE; - break; - } - break; + hp->flags.bootsize = TRUE; + hp->flags.bootsize_auto = TRUE; + break; + } + break; case SYM_BOOT_SERVER: - switch (optype) { - case OP_ADDITION: - if (prs_inetaddr(symbol, &value) < 0) { - return E_BAD_BOOT_SERVER; - } else { - hp->bootserver.s_addr = value; - hp->flags.bootserver = TRUE; - } - break; - case OP_DELETION: - hp->flags.bootserver = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_IA1(bootserver); + break; case SYM_TFTPDIR: - switch (optype) { - case OP_ADDITION: - if (ss = get_shared_string(symbol)) { - if ((ss->string)[0] == '/') { - if (hp->tftpdir) { - del_string(hp->tftpdir); - } - hp->tftpdir = ss; - hp->flags.tftpdir = TRUE; - } else { - return E_BAD_TFTPDIR; - } - } else { - return E_BAD_TFTPDIR; - } - break; - case OP_DELETION: - if (hp->tftpdir) { - del_string(hp->tftpdir); - } - hp->tftpdir = NULL; - hp->flags.tftpdir = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_STR(tftpdir); + if ((hp->tftpdir != NULL) && + (hp->tftpdir->string[0] != '/')) + return E_BAD_PATHNAME; + break; - case SYM_DUMPFILE: - switch (optype) { - case OP_ADDITION: - if (ss = get_shared_string(symbol)) { - if (hp->dumpfile) { - del_string(hp->dumpfile); - } - hp->dumpfile = ss; - hp->flags.dumpfile = TRUE; - } - break; - case OP_DELETION: - if (hp->dumpfile) { - del_string(hp->dumpfile); - } - hp->dumpfile = NULL; - hp->flags.dumpfile = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + case SYM_DUMP_FILE: + PARSE_STR(dump_file); + break; case SYM_DOMAIN_NAME: - switch (optype) { - case OP_ADDITION: - if (ss = get_shared_string(symbol)) { - if (hp->domainname) { - del_string(hp->domainname); - } - hp->domainname = ss; - hp->flags.domainname = TRUE; - } - break; - case OP_DELETION: - if (hp->domainname) { - del_string(hp->domainname); - } - hp->domainname = NULL; - hp->flags.domainname = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_STR(domain_name); + break; - case SYM_ROOTPATH: - switch (optype) { - case OP_ADDITION: - if (ss = get_shared_string(symbol)) { - if (hp->rootpath) { - del_string(hp->rootpath); - } - hp->rootpath = ss; - hp->flags.rootpath = TRUE; - } - break; - case OP_DELETION: - if (hp->rootpath) { - del_string(hp->rootpath); - } - hp->rootpath = NULL; - hp->flags.rootpath = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; - case SYM_SWAP_SERVER: - switch (optype) { - case OP_ADDITION: - if (prs_inetaddr(symbol, &value) < 0) { - return E_BAD_SWAP_SERVER; - } else { - hp->swapserver.s_addr = value; - hp->flags.swap_server = TRUE; - } - break; - case OP_DELETION: - hp->flags.swap_server = FALSE; - break; - case OP_BOOLEAN: - return E_SYNTAX_ERROR; - } - break; + PARSE_IA1(swap_server); + break; + + case SYM_ROOT_PATH: + PARSE_STR(root_path); + break; + + case SYM_EXTEN_FILE: + PARSE_STR(exten_file); + break; + + case SYM_REPLY_ADDR: + PARSE_IA1(reply_addr); + break; + + case SYM_NIS_DOMAIN: + PARSE_STR(nis_domain); + break; + + case SYM_NIS_SERVER: + PARSE_IAL(nis_server); + break; + + case SYM_NTP_SERVER: + PARSE_IAL(ntp_server); + break; + + /* XXX - Add new tags here */ default: - return E_UNKNOWN_SYMBOL; - } - return SUCCESS; -} + return E_UNKNOWN_SYMBOL; + } /* switch symbolcode */ + + return SUCCESS; +} +#undef PARSE_IA1 +#undef PARSE_IAL +#undef PARSE_STR + + /* * Read a string from the buffer indirectly pointed to through "src" and * move it into the buffer pointed to by "dest". A pointer to the maximum @@ -1296,49 +1127,50 @@ struct host *hp; * null-terminated. */ -PRIVATE char *get_string(src, dest, length) -char **src, *dest; -unsigned *length; +PRIVATE char * +get_string(src, dest, length) + char **src, *dest; + unsigned *length; { - int n, len, quoteflag; - - quoteflag = FALSE; - n = 0; - len = *length - 1; - while ((n < len) && (**src)) { - if (!quoteflag && (**src == ':')) { - break; - } - if (**src == '"') { - (*src)++; - quoteflag = !quoteflag; - continue; + int n, len, quoteflag; + + quoteflag = FALSE; + n = 0; + len = *length - 1; + while ((n < len) && (**src)) { + if (!quoteflag && (**src == ':')) { + break; + } + if (**src == '"') { + (*src)++; + quoteflag = !quoteflag; + continue; + } + if (**src == '\\') { + (*src)++; + if (!**src) { + break; + } + } + *dest++ = *(*src)++; + n++; } - if (**src == '\\') { - (*src)++; - if (! **src) { - break; - } + + /* + * Remove that troublesome trailing whitespace. . . + */ + while ((n > 0) && isspace(dest[-1])) { + dest--; + n--; } - *dest++ = *(*src)++; - n++; - } - - /* - * Remove that troublesome trailing whitespace. . . - */ - while ((n > 0) && isspace(dest[-1])) { - dest--; - n--; - } - - *dest = '\0'; - *length = n; - return dest; -} + *dest = '\0'; + *length = n; + return dest; +} + /* * Read the string indirectly pointed to by "src", update the caller's * pointer, and return a pointer to a malloc'ed shared_string structure @@ -1347,26 +1179,27 @@ unsigned *length; * The string is read using the same rules as get_string() above. */ -PRIVATE struct shared_string *get_shared_string(src) -char **src; +PRIVATE struct shared_string * +get_shared_string(src) + char **src; { - char retstring[MAXSTRINGLEN]; - struct shared_string *s; - unsigned length; + char retstring[MAXSTRINGLEN]; + struct shared_string *s; + unsigned length; - length = sizeof(retstring); - (void) get_string(src, retstring, &length); + length = sizeof(retstring); + (void) get_string(src, retstring, &length); - s = (struct shared_string *) smalloc(sizeof(struct shared_string) - + length); - s->linkcount = 1; - strcpy(s->string, retstring); + s = (struct shared_string *) smalloc(sizeof(struct shared_string) + + length); + s->linkcount = 1; + strcpy(s->string, retstring); - return s; + return s; } + - /* * Load RFC1048 generic information directly into a memory buffer. * @@ -1383,53 +1216,57 @@ char **src; * characters (the quotes are required). */ -PRIVATE void process_generic(src, dest, tagvalue) -char **src; -struct shared_bindata **dest; -byte tagvalue; +PRIVATE int +process_generic(src, dest, tagvalue) + char **src; + struct shared_bindata **dest; + u_int tagvalue; { - byte tmpbuf[MAXBUFLEN]; - byte *str; - struct shared_bindata *bdata; - int newlength, oldlength; - - str = tmpbuf; - *str++ = tagvalue; /* Store tag value */ - str++; /* Skip over length field */ - if ((*src)[0] == '"') { /* ASCII data */ - newlength = sizeof(tmpbuf) - 2; /* Set maximum allowed length */ - (void) get_string(src, str, &newlength); - } else { /* Numeric data */ - newlength = 0; - while (newlength < sizeof(tmpbuf) - 2) { - if (interp_byte(src, str++) < 0) { - break; - } else { - newlength++; - } - if (**src == '.') { - (*src)++; - } - } - } - tmpbuf[1] = (byte) (newlength & 0xFF); - oldlength = ((*dest)->length); - bdata = (struct shared_bindata *) smalloc(sizeof(struct shared_bindata) - + oldlength + newlength + 1); - if (oldlength > 0) { - bcopy((*dest)->data, bdata->data, oldlength); - } - bcopy(tmpbuf, bdata->data + oldlength, newlength + 2); - bdata->length = oldlength + newlength + 2; - bdata->linkcount = 1; - if (*dest) { - del_bindata(*dest); - } - *dest = bdata; -} + byte tmpbuf[MAXBUFLEN]; + byte *str; + struct shared_bindata *bdata; + u_int newlength, oldlength; + + str = tmpbuf; + *str++ = (tagvalue & 0xFF); /* Store tag value */ + str++; /* Skip over length field */ + if ((*src)[0] == '"') { /* ASCII data */ + newlength = sizeof(tmpbuf) - 2; /* Set maximum allowed length */ + (void) get_string(src, (char *) str, &newlength); + newlength++; /* null terminator */ + } else { /* Numeric data */ + newlength = 0; + while (newlength < sizeof(tmpbuf) - 2) { + if (interp_byte(src, str++) < 0) + break; + newlength++; + if (**src == '.') { + (*src)++; + } + } + } + if ((*src)[0] != ':') + return -1; + tmpbuf[1] = (newlength & 0xFF); + oldlength = ((*dest)->length); + bdata = (struct shared_bindata *) smalloc(sizeof(struct shared_bindata) + + oldlength + newlength + 1); + if (oldlength > 0) { + bcopy((*dest)->data, bdata->data, oldlength); + } + bcopy(tmpbuf, bdata->data + oldlength, newlength + 2); + bdata->length = oldlength + newlength + 2; + bdata->linkcount = 1; + if (*dest) { + del_bindata(*dest); + } + *dest = bdata; + return 0; +} + /* * Verify that the given string makes sense as a hostname (according to * Appendix 1, page 29 of RFC882). @@ -1437,39 +1274,44 @@ byte tagvalue; * Return TRUE for good names, FALSE otherwise. */ -PRIVATE boolean goodname(hostname) -register char *hostname; +PRIVATE boolean +goodname(hostname) + register char *hostname; { - do { - if (!isalpha(*hostname++)) { /* First character must be a letter */ - return FALSE; - } - while (isalnum(*hostname) || (*hostname == '-')) { - hostname++; /* Alphanumeric or a hyphen */ - } - if (!isalnum(hostname[-1])) { /* Last must be alphanumeric */ - return FALSE; - } - if (*hostname == '\0') { /* Done? */ - return TRUE; - } - } while (*hostname++ == '.'); /* Dot, loop for next label */ + do { + if (!isalpha(*hostname++)) { /* First character must be a letter */ + return FALSE; + } + while (isalnum(*hostname) || + (*hostname == '-') || + (*hostname == '_') ) + { + hostname++; /* Alphanumeric or a hyphen */ + } + if (!isalnum(hostname[-1])) { /* Last must be alphanumeric */ + return FALSE; + } + if (*hostname == '\0') {/* Done? */ + return TRUE; + } + } while (*hostname++ == '.'); /* Dot, loop for next label */ - return FALSE; /* If it's not a dot, lose */ + return FALSE; /* If it's not a dot, lose */ } - + /* * Null compare function -- always returns FALSE so an element is always * inserted into a hash table (i.e. there is never a collision with an * existing element). */ -PRIVATE boolean nullcmp(host1, host2) -struct host *host1, *host2; +PRIVATE boolean +nullcmp(d1, d2) + hash_datum *d1, *d2; { - return FALSE; + return FALSE; } @@ -1478,11 +1320,14 @@ struct host *host1, *host2; * structure. */ -PRIVATE boolean nmcmp(name, hp) -char *name; -struct host *hp; +boolean +nmcmp(d1, d2) + hash_datum *d1, *d2; { - return !strcmp(name, hp->hostname->string); + char *name = (char *) d1; /* XXX - OK? */ + struct host *hp = (struct host *) d2; + + return !strcmp(name, hp->hostname->string); } @@ -1498,218 +1343,161 @@ struct host *hp; * hash table. */ -PRIVATE boolean hwinscmp(host1, host2) -struct host *host1, *host2; +PRIVATE boolean +hwinscmp(d1, d2) + hash_datum *d1, *d2; { - if (host1->htype != host2->htype) { - return FALSE; - } - if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) { - return FALSE; - } - if ((host1->subnet_mask.s_addr) == (host2->subnet_mask.s_addr)) { - if (((host1->iaddr.s_addr) & (host1->subnet_mask.s_addr)) != - ((host2->iaddr.s_addr) & (host2->subnet_mask.s_addr))) { - return FALSE; + struct host *host1 = (struct host *) d1; + struct host *host2 = (struct host *) d2; + + if (host1->htype != host2->htype) { + return FALSE; + } + if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) { + return FALSE; } - } - return TRUE; + /* XXX - Is the subnet_mask field set yet? */ + if ((host1->subnet_mask.s_addr) == (host2->subnet_mask.s_addr)) { + if (((host1->iaddr.s_addr) & (host1->subnet_mask.s_addr)) != + ((host2->iaddr.s_addr) & (host2->subnet_mask.s_addr))) + { + return FALSE; + } + } + return TRUE; } - /* + * Macros for use in the function below: + */ + +#define DUP_COPY(MEMBER) do \ +{ \ + if (!hp->flags.MEMBER) { \ + if ((hp->flags.MEMBER = hp2->flags.MEMBER) != 0) { \ + hp->MEMBER = hp2->MEMBER; \ + } \ + } \ +} while (0) + +#define DUP_LINK(MEMBER) do \ +{ \ + if (!hp->flags.MEMBER) { \ + if ((hp->flags.MEMBER = hp2->flags.MEMBER) != 0) { \ + assert(hp2->MEMBER); \ + hp->MEMBER = hp2->MEMBER; \ + (hp->MEMBER->linkcount)++; \ + } \ + } \ +} while (0) + +/* * Process the "similar entry" symbol. * * The host specified as the value of the "tc" symbol is used as a template * for the current host entry. Symbol values not explicitly set in the * current host entry are inferred from the template entry. */ - -PRIVATE void fill_defaults(hp, src) -struct host *hp; -char **src; +PRIVATE void +fill_defaults(hp, src) + struct host *hp; + char **src; { - unsigned tlen, hashcode; - struct host *hp2, thp; - char tstring[MAXSTRINGLEN]; - - tlen = sizeof(tstring); - (void) get_string(src, tstring, &tlen); - if (goodname(tstring)) { - hashcode = hash_HashFunction(tstring, tlen); - hp2 = (struct host *) hash_Lookup(nmhashtable, hashcode, nmcmp, - tstring); - } else { - thp.iaddr.s_addr = inet_addr(tstring); - hashcode = hash_HashFunction(&(thp.iaddr.s_addr), 4); - hp2 = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, - &thp); - } - if (hp2 == NULL) { - report(LOG_ERR, "can't find tc=\"%s\"\n", tstring); - } else { - /* - * Assignments inside "if" conditionals are intended here. - */ - if (!hp->flags.cookie_server) { - if (hp->flags.cookie_server = hp2->flags.cookie_server) { - hp->cookie_server = hp2->cookie_server; - (hp->cookie_server->linkcount)++; - } - } - if (!hp->flags.domain_server) { - if (hp->flags.domain_server = hp2->flags.domain_server) { - hp->domain_server = hp2->domain_server; - (hp->domain_server->linkcount)++; - } - } - if (!hp->flags.gateway) { - if (hp->flags.gateway = hp2->flags.gateway) { - hp->gateway = hp2->gateway; - (hp->gateway->linkcount)++; - } - } - if (!hp->flags.impress_server) { - if (hp->flags.impress_server = hp2->flags.impress_server) { - hp->impress_server = hp2->impress_server; - (hp->impress_server->linkcount)++; - } - } - if (!hp->flags.log_server) { - if (hp->flags.log_server = hp2->flags.log_server) { - hp->log_server = hp2->log_server; - (hp->log_server->linkcount)++; - } - } - if (!hp->flags.lpr_server) { - if (hp->flags.lpr_server = hp2->flags.lpr_server) { - hp->lpr_server = hp2->lpr_server; - (hp->lpr_server->linkcount)++; - } + unsigned int tlen, hashcode; + struct host *hp2; + char tstring[MAXSTRINGLEN]; + + tlen = sizeof(tstring); + (void) get_string(src, tstring, &tlen); + hashcode = hash_HashFunction((u_char *) tstring, tlen); + hp2 = (struct host *) hash_Lookup(nmhashtable, hashcode, nmcmp, tstring); + + if (hp2 == NULL) { + report(LOG_ERR, "can't find tc=\"%s\"", tstring); + return; } - if (!hp->flags.name_server) { - if (hp->flags.name_server = hp2->flags.name_server) { - hp->name_server = hp2->name_server; - (hp->name_server->linkcount)++; - } - } - if (!hp->flags.rlp_server) { - if (hp->flags.rlp_server = hp2->flags.rlp_server) { - hp->rlp_server = hp2->rlp_server; - (hp->rlp_server->linkcount)++; - } - } - if (!hp->flags.time_server) { - if (hp->flags.time_server = hp2->flags.time_server) { - hp->time_server = hp2->time_server; - (hp->time_server->linkcount)++; - } - } - if (!hp->flags.homedir) { - if (hp->flags.homedir = hp2->flags.homedir) { - hp->homedir = hp2->homedir; - (hp->homedir->linkcount)++; - } - } - if (!hp->flags.bootfile) { - if (hp->flags.bootfile = hp2->flags.bootfile) { - hp->bootfile = hp2->bootfile; - (hp->bootfile->linkcount)++; - } - } - if (!hp->flags.generic) { - if (hp->flags.generic = hp2->flags.generic) { - hp->generic = hp2->generic; - (hp->generic->linkcount)++; - } - } - if (!hp->flags.vendor_magic) { - hp->flags.vm_auto = hp2->flags.vm_auto; - if (hp->flags.vendor_magic = hp2->flags.vendor_magic) { - bcopy(hp2->vm_cookie, hp->vm_cookie, 4); - } + DUP_LINK(bootfile); + DUP_LINK(cookie_server); + DUP_LINK(domain_server); + DUP_LINK(gateway); + /* haddr not copied */ + DUP_LINK(homedir); + DUP_COPY(htype); + + DUP_LINK(impress_server); + /* iaddr not copied */ + DUP_LINK(log_server); + DUP_LINK(lpr_server); + DUP_LINK(name_server); + DUP_LINK(rlp_server); + + DUP_COPY(subnet_mask); + DUP_COPY(time_offset); + DUP_LINK(time_server); + + if (!hp->flags.vm_cookie) { + if ((hp->flags.vm_cookie = hp2->flags.vm_cookie)) { + bcopy(hp2->vm_cookie, hp->vm_cookie, 4); + } } if (!hp->flags.name_switch) { - if (hp->flags.name_switch = hp2->flags.name_switch) { - hp->flags.send_name = hp2->flags.send_name; - } - } - if (!hp->flags.htype) { - if (hp->flags.htype = hp2->flags.htype) { - hp->htype = hp2->htype; - } - } - if (!hp->flags.time_offset) { - if (hp->flags.time_offset = hp2->flags.time_offset) { - hp->flags.timeoff_auto = hp2->flags.timeoff_auto; - hp->time_offset = hp2->time_offset; - } - } - if (!hp->flags.subnet_mask) { - if (hp->flags.subnet_mask = hp2->flags.subnet_mask) { - hp->subnet_mask.s_addr = hp2->subnet_mask.s_addr; - } - } - if (!hp->flags.swap_server) { - if (hp->flags.swap_server = hp2->flags.swap_server) { - hp->swapserver.s_addr = hp2->swapserver.s_addr; - } + if ((hp->flags.name_switch = hp2->flags.name_switch)) { + hp->flags.send_name = hp2->flags.send_name; + } } if (!hp->flags.bootsize) { - if (hp->flags.bootsize = hp2->flags.bootsize) { - hp->flags.bootsize_auto = hp2->flags.bootsize_auto; - hp->bootsize = hp2->bootsize; - } - } - if (!hp->flags.tftpdir) { - if (hp->flags.tftpdir = hp2->flags.tftpdir) { - hp->tftpdir = hp2->tftpdir; - (hp->tftpdir->linkcount)++; - } - } - if (!hp->flags.rootpath) { - if (hp->flags.rootpath = hp2->flags.rootpath) { - hp->rootpath = hp2->rootpath; - (hp->rootpath->linkcount)++; - } - } - if (!hp->flags.domainname) { - if (hp->flags.domainname = hp2->flags.domainname) { - hp->domainname = hp2->domainname; - (hp->domainname->linkcount)++; - } - } - if (!hp->flags.dumpfile) { - if (hp->flags.dumpfile = hp2->flags.dumpfile) { - hp->dumpfile = hp2->dumpfile; - (hp->dumpfile->linkcount)++; - } + if ((hp->flags.bootsize = hp2->flags.bootsize)) { + hp->flags.bootsize_auto = hp2->flags.bootsize_auto; + hp->bootsize = hp2->bootsize; + } } - } -} + DUP_COPY(bootserver); + + DUP_LINK(tftpdir); + DUP_LINK(dump_file); + DUP_LINK(domain_name); + + DUP_COPY(swap_server); + DUP_LINK(root_path); + DUP_LINK(exten_file); + + DUP_COPY(reply_addr); + + DUP_LINK(nis_domain); + DUP_LINK(nis_server); + DUP_LINK(ntp_server); + + /* XXX - Add new tags here */ + DUP_LINK(generic); + +} +#undef DUP_COPY +#undef DUP_LINK + /* * This function adjusts the caller's pointer to point just past the * first-encountered colon. If it runs into a null character, it leaves * the pointer pointing to it. */ -PRIVATE void adjust(s) -char **s; +PRIVATE void +adjust(s) + char **s; { - register char *t; - - t = *s; - while (*t && (*t != ':')) { - t++; - } - if (*t) { - t++; - } - *s = t; + register char *t; + + t = *s; + while (*t && (*t != ':')) { + t++; + } + if (*t) { + t++; + } + *s = t; } @@ -1721,16 +1509,17 @@ char **s; * the pointer pointing to it. */ -PRIVATE void eat_whitespace(s) -char **s; +PRIVATE void +eat_whitespace(s) + char **s; { - register char *t; + register char *t; - t = *s; - while (*t && isspace(*t)) { - t++; - } - *s = t; + t = *s; + while (*t && isspace(*t)) { + t++; + } + *s = t; } @@ -1739,19 +1528,20 @@ char **s; * This function converts the given string to all lowercase. */ -PRIVATE void makelower(s) -char *s; +PRIVATE void +makelower(s) + char *s; { - while (*s) { - if (isupper(*s)) { - *s = tolower(*s); + while (*s) { + if (isupper(*s)) { + *s = tolower(*s); + } + s++; } - s++; - } } - + /* * * N O T E : @@ -1765,12 +1555,12 @@ char *s; * parameter. * * I'm sure somebody out there won't like this. . . . - * + * (Yea, because it usually makes code slower... -gwr) * */ - + /* * "src" points to a character pointer which points to an ASCII string of * whitespace-separated IP addresses. A pointer to an in_addr_list @@ -1779,48 +1569,49 @@ char *s; * "src" is updated to point to the first non-address (illegal) character. */ -PRIVATE struct in_addr_list *get_addresses(src) -char **src; +PRIVATE struct in_addr_list * +get_addresses(src) + char **src; { - struct in_addr tmpaddrlist[MAXINADDRS]; - struct in_addr *address1, *address2; - struct in_addr_list *result; - unsigned addrcount, totalsize; - - address1 = tmpaddrlist; - for (addrcount = 0; addrcount < MAXINADDRS; addrcount++) { - while (**src && isspace(**src)) { /* Skip whitespace */ - (*src)++; - } - if (! **src) { /* Quit if nothing more */ - break; - } - if (prs_inetaddr(src, &(address1->s_addr)) < 0) { - break; - } - address1++; /* Point to next address slot */ - } - if (addrcount < 1) { - result = NULL; - } else { - totalsize = sizeof(struct in_addr_list) - + (addrcount - 1) * sizeof(struct in_addr); - result = (struct in_addr_list *) smalloc(totalsize); - result->linkcount = 1; - result->addrcount = addrcount; + struct in_addr tmpaddrlist[MAXINADDRS]; + struct in_addr *address1, *address2; + struct in_addr_list *result; + unsigned addrcount, totalsize; + address1 = tmpaddrlist; - address2 = result->addr; - for (; addrcount > 0; addrcount--) { - address2->s_addr = address1->s_addr; - address1++; - address2++; + for (addrcount = 0; addrcount < MAXINADDRS; addrcount++) { + while (isspace(**src) || (**src == ',')) { + (*src)++; + } + if (!**src) { /* Quit if nothing more */ + break; + } + if (prs_inetaddr(src, &(address1->s_addr)) < 0) { + break; + } + address1++; /* Point to next address slot */ } - } - return result; + if (addrcount < 1) { + result = NULL; + } else { + totalsize = sizeof(struct in_addr_list) + + (addrcount - 1) * sizeof(struct in_addr); + result = (struct in_addr_list *) smalloc(totalsize); + result->linkcount = 1; + result->addrcount = addrcount; + address1 = tmpaddrlist; + address2 = result->addr; + for (; addrcount > 0; addrcount--) { + address2->s_addr = address1->s_addr; + address1++; + address2++; + } + } + return result; } - + /* * prs_inetaddr(src, result) * @@ -1835,69 +1626,92 @@ char **src; * to by "result" is unchanged. Successful calls return 0. */ -PRIVATE prs_inetaddr(src, result) -char **src; -u_long *result; +PRIVATE int +prs_inetaddr(src, result) + char **src; + u_int32 *result; { - register u_long value; - u_long parts[4], *pp = parts; - int n; - - if (!isdigit(**src)) { - return -1; - } -loop: - value = get_u_long(src); - if (**src == '.') { + char tmpstr[MAXSTRINGLEN]; + register u_int32 value; + u_int32 parts[4], *pp; + int n; + char *s, *t; + +#if 1 /* XXX - experimental */ + /* Leading alpha char causes IP addr lookup. */ + if (isalpha(**src)) { + /* Lookup IP address. */ + s = *src; + t = tmpstr; + while ((isalnum(*s) || (*s == '.') || + (*s == '-') || (*s == '_') ) && + (t < &tmpstr[MAXSTRINGLEN - 1]) ) + *t++ = *s++; + *t = '\0'; + *src = s; + + n = lookup_ipa(tmpstr, result); + if (n < 0) + report(LOG_ERR, "can not get IP addr for %s", tmpstr); + return n; + } +#endif + /* - * Internet format: + * Parse an address in Internet format: * a.b.c.d * a.b.c (with c treated as 16-bits) * a.b (with b treated as 24 bits) */ - if (pp >= parts + 4) { - return (-1); + pp = parts; + loop: + /* If it's not a digit, return error. */ + if (!isdigit(**src)) + return -1; + *pp++ = get_u_long(src); + if (**src == '.') { + if (pp < (parts + 4)) { + (*src)++; + goto loop; + } + return (-1); } - *pp++ = value; - (*src)++; - goto loop; - } - /* - * Check for trailing characters. - */ - if (**src && !(isspace(**src) || (**src == ':'))) { - return (-1); - } - *pp++ = value; - /* - * Construct the address according to - * the number of parts specified. - */ - n = pp - parts; - switch (n) { - case 1: /* a -- 32 bits */ - value = parts[0]; - break; - case 2: /* a.b -- 8.24 bits */ - value = (parts[0] << 24) | (parts[1] & 0xFFFFFF); - break; - case 3: /* a.b.c -- 8.8.16 bits */ - value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) | - (parts[2] & 0xFFFF); - break; - case 4: /* a.b.c.d -- 8.8.8.8 bits */ - value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) | - ((parts[2] & 0xFF) << 8) | (parts[3] & 0xFF); - break; +#if 0 + /* This is handled by the caller. */ + if (**src && !(isspace(**src) || (**src == ':'))) { + return (-1); + } +#endif + + /* + * Construct the address according to + * the number of parts specified. + */ + n = pp - parts; + switch (n) { + case 1: /* a -- 32 bits */ + value = parts[0]; + break; + case 2: /* a.b -- 8.24 bits */ + value = (parts[0] << 24) | (parts[1] & 0xFFFFFF); + break; + case 3: /* a.b.c -- 8.8.16 bits */ + value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) | + (parts[2] & 0xFFFF); + break; + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) | + ((parts[2] & 0xFF) << 8) | (parts[3] & 0xFF); + break; default: - return (-1); - } - *result = htonl(value); - return (0); + return (-1); + } + *result = htonl(value); + return (0); } - + /* * "src" points to a pointer which in turn points to a hexadecimal ASCII * string. This string is interpreted as a hardware address and returned @@ -1914,29 +1728,51 @@ loop: * function returns a NULL pointer. */ -PRIVATE byte *prs_haddr(src, htype) -char **src; -byte htype; +PRIVATE byte * +prs_haddr(src, htype) + char **src; + u_int htype; { - static byte haddr[MAXHADDRLEN]; - byte *hptr; - unsigned hlen; - - hlen = haddrlength(htype); /* Get length of this address type */ - hptr = haddr; - while (hptr < haddr + hlen) { - if (**src == '.') { - (*src)++; + static byte haddr[MAXHADDRLEN]; + byte *hap; + char tmpstr[MAXSTRINGLEN]; + u_int tmplen; + unsigned hal; + char *p; + + hal = haddrlength(htype); /* Get length of this address type */ + if (hal <= 0) { + report(LOG_ERR, "Invalid addr type for HW addr parse"); + return NULL; } - if (interp_byte(src, hptr++) < 0) { - return NULL; + tmplen = sizeof(tmpstr); + get_string(src, tmpstr, &tmplen); + p = tmpstr; + +#if 1 /* XXX - experimental */ + /* If it's a valid host name, try to lookup the HW address. */ + if (goodname(p)) { + /* Lookup Hardware Address for hostname. */ + if ((hap = lookup_hwa(p, htype)) != NULL) + return hap; /* success */ + report(LOG_ERR, "Add 0x prefix if hex value starts with A-F"); + /* OK, assume it must be numeric. */ } - } - return haddr; -} +#endif + hap = haddr; + while (hap < haddr + hal) { + if (*p == '.') + p++; + if (interp_byte(&p, hap++) < 0) { + return NULL; + } + } + return haddr; +} + /* * "src" is a pointer to a character pointer which in turn points to a * hexadecimal ASCII representation of a byte. This byte is read, the @@ -1949,28 +1785,31 @@ byte htype; * Successful calls return 0. */ -PRIVATE int interp_byte(src, retbyte) -char **src; -byte *retbyte; +PRIVATE int +interp_byte(src, retbyte) + char **src; + byte *retbyte; { - int v; - - if ((*src)[0] == '0' && (*src)[1] == 'x' || (*src)[1] == 'X') { - (*src) += 2; /* allow 0x for hex, but don't require it */ - } - if (!isxdigit((*src)[0]) || !isxdigit((*src)[1])) { - return -1; - } - if (sscanf(*src, "%2x", &v) != 1) { - return -1; - } - (*src) += 2; - *retbyte = (byte) (v & 0xFF); - return 0; -} + int v; + if ((*src)[0] == '0' && + ((*src)[1] == 'x' || + (*src)[1] == 'X')) { + (*src) += 2; /* allow 0x for hex, but don't require it */ + } + if (!isxdigit((*src)[0]) || !isxdigit((*src)[1])) { + return -1; + } + if (sscanf(*src, "%2x", &v) != 1) { + return -1; + } + (*src) += 2; + *retbyte = (byte) (v & 0xFF); + return 0; +} + /* * The parameter "src" points to a character pointer which points to an * ASCII string representation of an unsigned number. The number is @@ -1978,42 +1817,45 @@ byte *retbyte; * point to the first illegal character. */ -PRIVATE u_long get_u_long(src) -char **src; +PRIVATE u_int32 +get_u_long(src) + char **src; { - register u_long value, base; - char c; - - /* - * Collect number up to first illegal character. Values are specified - * as for C: 0x=hex, 0=octal, other=decimal. - */ - value = 0; - base = 10; - if (**src == '0') { - base = 8, (*src)++; - } - if (**src == 'x' || **src == 'X') { - base = 16, (*src)++; - } - while (c = **src) { - if (isdigit(c)) { - value = (value * base) + (c - '0'); - (*src)++; - continue; + register u_int32 value, base; + char c; + + /* + * Collect number up to first illegal character. Values are specified + * as for C: 0x=hex, 0=octal, other=decimal. + */ + value = 0; + base = 10; + if (**src == '0') { + base = 8; + (*src)++; + } + if (**src == 'x' || **src == 'X') { + base = 16; + (*src)++; } - if (base == 16 && isxdigit(c)) { - value = (value << 4) + ((c & ~32) + 10 - 'A'); - (*src)++; - continue; + while ((c = **src)) { + if (isdigit(c)) { + value = (value * base) + (c - '0'); + (*src)++; + continue; + } + if (base == 16 && isxdigit(c)) { + value = (value << 4) + ((c & ~32) + 10 - 'A'); + (*src)++; + continue; + } + break; } - break; - } - return value; + return value; } - + /* * Routines for deletion of data associated with the main data structure. */ @@ -2024,10 +1866,16 @@ char **src; * pointer is NULL. */ -PRIVATE void free_host(hostptr) -struct host *hostptr; +PRIVATE void +free_host(hmp) + hash_datum *hmp; { - if (hostptr) { + struct host *hostptr = (struct host *) hmp; + if (hostptr == NULL) + return; + assert(hostptr->linkcount > 0); + if (--(hostptr->linkcount)) + return; /* Still has references */ del_iplist(hostptr->cookie_server); del_iplist(hostptr->domain_server); del_iplist(hostptr->gateway); @@ -2037,33 +1885,49 @@ struct host *hostptr; del_iplist(hostptr->name_server); del_iplist(hostptr->rlp_server); del_iplist(hostptr->time_server); + del_iplist(hostptr->nis_server); + del_iplist(hostptr->ntp_server); + + /* + * XXX - Add new tags here + * (if the value is an IP list) + */ + del_string(hostptr->hostname); del_string(hostptr->homedir); del_string(hostptr->bootfile); del_string(hostptr->tftpdir); - del_string(hostptr->rootpath); - del_string(hostptr->domainname); - del_string(hostptr->dumpfile); + del_string(hostptr->root_path); + del_string(hostptr->domain_name); + del_string(hostptr->dump_file); + del_string(hostptr->exten_file); + del_string(hostptr->nis_domain); + + /* + * XXX - Add new tags here + * (if it is a shared string) + */ + del_bindata(hostptr->generic); free((char *) hostptr); - } } - + /* * Decrements the linkcount on the given IP address data structure. If the * linkcount goes to zero, the memory associated with the data is freed. */ -PRIVATE void del_iplist(iplist) -struct in_addr_list *iplist; +PRIVATE void +del_iplist(iplist) + struct in_addr_list *iplist; { - if (iplist) { - if (! (--(iplist->linkcount))) { - free((char *) iplist); + if (iplist) { + if (!(--(iplist->linkcount))) { + free((char *) iplist); + } } - } } @@ -2074,14 +1938,15 @@ struct in_addr_list *iplist; * nothing if the passed pointer is NULL. */ -PRIVATE void del_string(stringptr) -struct shared_string *stringptr; +PRIVATE void +del_string(stringptr) + struct shared_string *stringptr; { - if (stringptr) { - if (! (--(stringptr->linkcount))) { - free((char *) stringptr); + if (stringptr) { + if (!(--(stringptr->linkcount))) { + free((char *) stringptr); + } } - } } @@ -2092,18 +1957,19 @@ struct shared_string *stringptr; * nothing if the passed pointer is NULL. */ -PRIVATE void del_bindata(dataptr) -struct shared_bindata *dataptr; +PRIVATE void +del_bindata(dataptr) + struct shared_bindata *dataptr; { - if (dataptr) { - if (! (--(dataptr->linkcount))) { - free((char *) dataptr); + if (dataptr) { + if (!(--(dataptr->linkcount))) { + free((char *) dataptr); + } } - } } + - /* smalloc() -- safe malloc() * @@ -2113,16 +1979,70 @@ struct shared_bindata *dataptr; * with a status of 1. */ -PRIVATE char *smalloc(nbytes) -unsigned nbytes; +PRIVATE char * +smalloc(nbytes) + unsigned nbytes; +{ + char *retvalue; + + retvalue = malloc(nbytes); + if (!retvalue) { + report(LOG_ERR, "malloc() failure -- exiting"); + exit(1); + } + bzero(retvalue, nbytes); + return retvalue; +} + + +/* + * 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(d1, d2) + hash_datum *d1, *d2; { - char *retvalue; - - retvalue = malloc(nbytes); - if (!retvalue) { - report(LOG_ERR, "malloc() failure -- exiting\n"); - exit(1); - } - bzero(retvalue, nbytes); - return retvalue; + struct host *host1 = (struct host *) d1; + struct host *host2 = (struct host *) d2; + + 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(d1, d2) + hash_datum *d1, *d2; +{ + struct host *host1 = (struct host *) d1; + struct host *host2 = (struct host *) d2; + + return (host1->iaddr.s_addr == host2->iaddr.s_addr); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/readfile.h b/libexec/bootpd/readfile.h new file mode 100644 index 00000000000..3913455857c --- /dev/null +++ b/libexec/bootpd/readfile.h @@ -0,0 +1,19 @@ +/* readfile.h */ + +#include "bptypes.h" +#include "hash.h" + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern boolean hwlookcmp P((hash_datum *, hash_datum *)); +extern boolean iplookcmp P((hash_datum *, hash_datum *)); +extern boolean nmcmp P((hash_datum *, hash_datum *)); +extern void readtab P((int)); +extern void rdtab_init P((void)); + +#undef P + diff --git a/libexec/bootpd/report.c b/libexec/bootpd/report.c new file mode 100644 index 00000000000..4f7f03616a2 --- /dev/null +++ b/libexec/bootpd/report.c @@ -0,0 +1,154 @@ +/* + * report() - calls syslog + */ + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include <stdio.h> +#include <syslog.h> + +#include "report.h" + +#ifndef LOG_NDELAY +#define LOG_NDELAY 0 +#endif +#ifndef LOG_DAEMON +#define LOG_DAEMON 0 +#endif +#ifndef LOG_BOOTP +#define LOG_BOOTP LOG_DAEMON +#endif + +extern int debug; +extern char *progname; + +/* + * This is initialized so you get stderr until you call + * report_init() + */ +static int stderr_only = 1; + +void +report_init(nolog) + int nolog; +{ + stderr_only = nolog; +#ifdef SYSLOG + if (!stderr_only) { + openlog(progname, LOG_PID | LOG_NDELAY, LOG_BOOTP); + } +#endif +} + +/* + * 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). + */ + +static char *levelnames[] = { +#ifdef LOG_SALERT + "level(0): ", + "alert(1): ", + "alert(2): ", + "emerg(3): ", + "error(4): ", + "crit(5): ", + "warn(6): ", + "note(7): ", + "info(8): ", + "debug(9): ", + "level(?): " +#else + "emerg(0): ", + "alert(1): ", + "crit(2): ", + "error(3): ", + "warn(4): ", + "note(5): ", + "info(6): ", + "debug(7): ", + "level(?): " +#endif +}; +static int numlevels = sizeof(levelnames) / sizeof(levelnames[0]); + + +/* + * Print a log message using syslog(3) and/or stderr. + * The message passed in should not include a newline. + */ +#ifdef __STDC__ +void +report(int priority, char *fmt,...) +#else +/*VARARGS2*/ +void +report(priority, fmt, va_alist) + int priority; + char *fmt; + va_dcl +#endif +{ + va_list ap; + static char buf[128]; + + if ((priority < 0) || (priority >= numlevels)) { + priority = numlevels - 1; + } +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vsprintf(buf, fmt, ap); + va_end(ap); + + /* + * Print the message + */ + if (stderr_only || (debug > 2)) { + fprintf(stderr, "%s: %s %s\n", + progname, levelnames[priority], buf); + } +#ifdef SYSLOG + if (!stderr_only) + syslog((priority | LOG_BOOTP), "%s", buf); +#endif +} + + + +/* + * Return pointer to static string which gives full filesystem error message. + */ +char * +get_errmsg() +{ + extern int errno; + extern char *strerror(); + + return strerror(errno); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/report.h b/libexec/bootpd/report.h new file mode 100644 index 00000000000..0bf63d60fbb --- /dev/null +++ b/libexec/bootpd/report.h @@ -0,0 +1,13 @@ +/* report.h */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern void report_init P((int nolog)); +extern void report P((int, char *, ...)); +extern char *get_errmsg P((void)); + +#undef P diff --git a/libexec/bootpd/syslog.conf b/libexec/bootpd/syslog.conf new file mode 100644 index 00000000000..2c135af4974 --- /dev/null +++ b/libexec/bootpd/syslog.conf @@ -0,0 +1,63 @@ +# +# syslog configuration file for SunOS 4.X +# (modified to do local2 separately) +# +# This file is processed by m4 so be careful to quote (`') names +# that match m4 reserved words. Also, within ifdef's, arguments +# containing commas must be quoted. +# +# Note: Have to exclude user from most lines so that user.alert +# and user.emerg are not included, because old sendmails +# will generate them for debugging information. If you +# have no 4.2BSD based systems doing network logging, you +# can remove all the special cases for "user" logging. + +#*.err;kern.debug;auth.notice;user.none /dev/console +kern.debug;user,mail.crit;auth.notice /dev/console +daemon,syslog,lpr,news,uucp,cron.err /dev/console + +#*.err;kern.debug;daemon,auth.notice;mail.crit;user.none /var/adm/messages +kern.debug;user,mail.crit;auth.notice /var/adm/messages +daemon.notice;syslog,news,uucp,cron.err /var/adm/messages + +lpr.debug /var/adm/lpd-errs + +*.alert;kern.err;daemon.err;user.none operator +*.alert;user.none root + +*.emerg;user.none * + +# for loghost machines, to have authentication messages (su, login, etc.) +# logged to a file, un-comment out the following line and adjust the file name +# as appropriate. +# +# if a non-loghost machine chooses to have such messages +# sent to the loghost machine, un-comment out the following line. +# +#auth.notice ifdef(`LOGHOST', /var/log/authlog, @loghost) + +mail.debug ifdef(`LOGHOST', /var/log/syslog, @loghost) + +# following line for compatibility with old sendmails. they will send +# messages with no facility code, which will be turned into "user" messages +# by the local syslog daemon. only the "loghost" machine needs the following +# line, to cause these old sendmail log messages to be logged in the +# mail syslog file. +# +ifdef(`LOGHOST', +user.alert /var/log/syslog +) +# +# non-loghost machines will use the following lines to cause "user" +# log messages to be logged locally. +# +ifdef(`LOGHOST', , +user.err /dev/console +user.err /var/adm/messages +user.alert `root, operator' +user.emerg * +) + +# Local2: (bootpd, pppd) +local2.debug /dev/console +#local2.debug /var/log/local2 diff --git a/libexec/bootpd/syslog.h b/libexec/bootpd/syslog.h new file mode 100644 index 00000000000..90d233fe612 --- /dev/null +++ b/libexec/bootpd/syslog.h @@ -0,0 +1,25 @@ +/* + * Fake syslog.h for systems without it (just defines priorities). + * To use this, compile with: -I/usr/include -I. + */ + +/* + * Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +/* + * Priorities (these are ordered) + */ +#define LOG_EMERG 0 /* system is unusable */ +#define LOG_ALERT 1 /* action must be taken immediately */ +#define LOG_CRIT 2 /* critical conditions */ +#define LOG_ERR 3 /* error conditions */ +#define LOG_WARNING 4 /* warning conditions */ +#define LOG_NOTICE 5 /* normal but signification condition */ +#define LOG_INFO 6 /* informational */ +#define LOG_DEBUG 7 /* debug-level messages */ + +/* XXX - Just in case: */ +#define openlog fake_syslog_openlog diff --git a/libexec/bootpd/trygetea.c b/libexec/bootpd/trygetea.c new file mode 100644 index 00000000000..e9314aede96 --- /dev/null +++ b/libexec/bootpd/trygetea.c @@ -0,0 +1,46 @@ +/* + * trygetea.c - test program for getether.c + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#if defined(SUNOS) || defined(SVR4) +#include <sys/sockio.h> +#endif + +#include <net/if.h> /* for struct ifreq */ +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <netdb.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> + +int debug = 0; +char *progname; + +main(argc, argv) + char **argv; +{ + u_char ea[16]; /* Ethernet address */ + int i; + + progname = argv[0]; /* for report */ + + if (argc < 2) { + printf("need interface name\n"); + exit(1); + } + if ((i = getether(argv[1], ea)) < 0) { + printf("Could not get Ethernet address (rc=%d)\n", i); + exit(1); + } + printf("Ether-addr"); + for (i = 0; i < 6; i++) + printf(":%x", ea[i] & 0xFF); + printf("\n"); + + exit(0); +} diff --git a/libexec/bootpd/trygetif.c b/libexec/bootpd/trygetif.c new file mode 100644 index 00000000000..c6bb098ab03 --- /dev/null +++ b/libexec/bootpd/trygetif.c @@ -0,0 +1,68 @@ +/* + * trygetif.c - test program for getif.c + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#if defined(SUNOS) || defined(SVR4) +#include <sys/sockio.h> +#endif + +#include <net/if.h> /* for struct ifreq */ +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <netdb.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> + +#include "getif.h" + +int debug = 0; +char *progname; + +main(argc, argv) + char **argv; +{ + struct hostent *hep; + struct sockaddr ea; /* Ethernet address */ + struct sockaddr_in *sip; /* Interface address */ + struct ifreq *ifr; + struct in_addr dst_addr; + struct in_addr *dap; + int i, s; + + progname = argv[0]; /* for report */ + + dap = NULL; + if (argc > 1) { + dap = &dst_addr; + if (isdigit(argv[1][0])) + dst_addr.s_addr = inet_addr(argv[1]); + else { + hep = gethostbyname(argv[1]); + if (!hep) { + printf("gethostbyname(%s)\n", argv[1]); + exit(1); + } + memcpy(&dst_addr, hep->h_addr, sizeof(dst_addr)); + } + } + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket open"); + exit(1); + } + ifr = getif(s, dap); + if (!ifr) { + printf("no interface for address\n"); + exit(1); + } + printf("Intf-name:%s\n", ifr->ifr_name); + sip = (struct sockaddr_in *) &(ifr->ifr_addr); + printf("Intf-addr:%s\n", inet_ntoa(sip->sin_addr)); + + exit(0); +} diff --git a/libexec/bootpd/trylook.c b/libexec/bootpd/trylook.c new file mode 100644 index 00000000000..40652a21159 --- /dev/null +++ b/libexec/bootpd/trylook.c @@ -0,0 +1,50 @@ +/* + * trylook.c - test program for lookup.c + */ + +#include <sys/types.h> +#include <netinet/in.h> +#include <stdio.h> + +#include "report.h" +#include "lookup.h" + +extern char *ether_ntoa(); +extern char *inet_ntoa(); + +int debug = 0; +char *progname; + +main(argc, argv) + char **argv; +{ + int i; + struct in_addr in; + char *a; + u_char *hwa; + + progname = argv[0]; /* for report */ + + for (i = 1; i < argc; i++) { + + /* Host name */ + printf("%s:", argv[i]); + + /* IP addr */ + if (lookup_ipa(argv[i], &in.s_addr)) + a = "?"; + else + a = inet_ntoa(in); + printf(" ipa=%s", a); + + /* Ether addr */ + hwa = lookup_hwa(argv[i], 1); + if (!hwa) + a = "?"; + else + a = ether_ntoa(hwa); + printf(" hwa=%s\n", a); + + } + exit(0); +} diff --git a/libexec/bootpd/tzone.c b/libexec/bootpd/tzone.c new file mode 100644 index 00000000000..4adc4ae25c6 --- /dev/null +++ b/libexec/bootpd/tzone.c @@ -0,0 +1,44 @@ +/* + * tzone.c - get the timezone + * + * This is shared by bootpd and bootpef + */ + +#ifdef SVR4 +/* XXX - Is this really SunOS specific? -gwr */ +/* This is in <time.h> but only visible if (__STDC__ == 1). */ +extern long timezone; +#else /* SVR4 */ +/* BSD or SunOS */ +# include <sys/time.h> +# include <syslog.h> +#endif /* SVR4 */ + +#include "bptypes.h" +#include "report.h" +#include "tzone.h" + +/* This is what other modules use. */ +int32 secondswest; + +/* + * Get our timezone offset so we can give it to clients if the + * configuration file doesn't specify one. + */ +void +tzone_init() +{ +#ifdef SVR4 + /* XXX - Is this really SunOS specific? -gwr */ + secondswest = timezone; +#else /* SVR4 */ + struct timezone tzp; /* Time zone offset for clients */ + struct timeval tp; /* Time (extra baggage) */ + if (gettimeofday(&tp, &tzp) < 0) { + secondswest = 0; /* Assume GMT for lack of anything better */ + report(LOG_ERR, "gettimeofday: %s", get_errmsg()); + } else { + secondswest = 60L * tzp.tz_minuteswest; /* Convert to seconds */ + } +#endif /* SVR4 */ +} diff --git a/libexec/bootpd/tzone.h b/libexec/bootpd/tzone.h new file mode 100644 index 00000000000..ddd67c4b625 --- /dev/null +++ b/libexec/bootpd/tzone.h @@ -0,0 +1,3 @@ +/* tzone.h */ +extern int32 secondswest; +extern void tzone_init(); |
