summaryrefslogtreecommitdiff
path: root/libexec/bootpd
diff options
context:
space:
mode:
authorsvn2git <svn2git@FreeBSD.org>1994-05-01 08:00:00 +0000
committersvn2git <svn2git@FreeBSD.org>1994-05-01 08:00:00 +0000
commita16f65c7d117419bd266c28a1901ef129a337569 (patch)
tree2626602f66dc3551e7a7c7bc9ad763c3bc7ab40a /libexec/bootpd
parent8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (diff)
Diffstat (limited to 'libexec/bootpd')
-rw-r--r--libexec/bootpd/Makefile20
-rw-r--r--libexec/bootpd/README102
-rw-r--r--libexec/bootpd/Version.c48
-rw-r--r--libexec/bootpd/announce46
-rw-r--r--libexec/bootpd/announce.2.137
-rw-r--r--libexec/bootpd/announce.2.251
-rw-r--r--libexec/bootpd/bootp.h137
-rw-r--r--libexec/bootpd/bootpd.8164
-rw-r--r--libexec/bootpd/bootpd.c1729
-rw-r--r--libexec/bootpd/bootpd.h228
-rw-r--r--libexec/bootpd/bootptab125
-rw-r--r--libexec/bootpd/bootptab.5404
-rw-r--r--libexec/bootpd/hash.c386
-rw-r--r--libexec/bootpd/hash.h137
-rw-r--r--libexec/bootpd/newvers.sh24
-rw-r--r--libexec/bootpd/readfile.c2128
16 files changed, 5766 insertions, 0 deletions
diff --git a/libexec/bootpd/Makefile b/libexec/bootpd/Makefile
new file mode 100644
index 000000000000..00d9d6fcfce7
--- /dev/null
+++ b/libexec/bootpd/Makefile
@@ -0,0 +1,20 @@
+# 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=-DDUMP_FILE=\"/usr/etc/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 -DPRIVATE=static ${FILESPECS} -traditional
+SRCS= bootpd.c readfile.c hash.c version.c
+MAN5= bootptab.5
+MAN8= bootpd.8
+CLEANFILES+=version.c version
+
+version.c:
+ sh ${.CURDIR}/newvers.sh ${.CURDIR}/Version.c
+
+.include <bsd.prog.mk>
diff --git a/libexec/bootpd/README b/libexec/bootpd/README
new file mode 100644
index 000000000000..f99f2efc341a
--- /dev/null
+++ b/libexec/bootpd/README
@@ -0,0 +1,102 @@
+This is a port of bootpd (CMU version 2.2a) to NetBSD-current.
+
+BOOTPD is a useful adjunct to the nfs diskless boot EPROM code.
+
+The alternatives for initiating a boot of a kernel across a network
+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
+
+
+Notes:
+1) BOOTP was originally designed and implemented by Bill Croft at Stanford.
+ Much of the credit for the ideas and the code goes to him. We've added
+ code to support the vendor specific area of the packet as specified in
+ RFC1048. We've also improved the host lookup algorithm and added some
+ extra logging.
+
+2) The server now uses syslog to do logging. Specifically it uses the 4.3bsd
+ version. I've #ifdef'd all of these calls. If you are running 4.2 you
+ 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
+
+4) The code and the bootptab should be reasonably well commented. If you
+ have any questions, just let us know.
+
+Construction:
+ 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.
+
+ Type 'make'. The options at present are: -DSYSLOG which enables logging
+ code, -DDEBUG which enables table dumping via signals, and -DVEND_CMU
+ which enables the CMU extensions for CMU PC/IP.
+
+ Edit the bootptab. The man page and the comments in the file should
+ explain how to go about doing so. If you have any problems, let me know.
+
+ 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.
+
+Care and feeding:
+ If you change the interface cards on your host or add new hosts you will
+ need to update /etc/bootptab. Just edit it as before. Once you write
+ it back out, bootpd will notice that there is a new copy and will
+ reread it the next time it gets a request.
+
+ If your bootp clients don't get a response then several things might be
+ wrong. Most often, the entry for that host is not in the database.
+ Check the hardware address and then check the entry and make sure
+ everything is right. Other problems include the server machine crashing,
+ bad cables, and the like. If your network is very congested you should
+ try making your bootp clients send additional requests before giving up.
+
+
+November 7, 1988
+
+
+Walter L. Wimer Drew D. Perkins
+ww0n@andrew.cmu.edu ddp@andrew.cmu.edu
+(412) 268-6252 (412) 268-8576
+
+4910 Forbes Ave
+Pittsburgh, PA 15213
+
diff --git a/libexec/bootpd/Version.c b/libexec/bootpd/Version.c
new file mode 100644
index 000000000000..cc908f5115cc
--- /dev/null
+++ b/libexec/bootpd/Version.c
@@ -0,0 +1,48 @@
+#ifndef _BLURB_
+#define _BLURB_
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+#endif /* _BLURB_ */
+
+
+/*
+ * Version.c (this file):
+ *
+ * Copyright (c) 1986, 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at Berkeley. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ *
+ * @(#)Version.c 4.8 (Berkeley) 4/7/88
+ */
+
+#ifndef lint
+char sccsid[] = "@(#)bootpd 2.2-ALPHA %VERSION% %WHOANDWHERE%\n";
+char rcsid[] = "$Header: /home/cvs/386BSD/src/libexec/bootpd/Version.c,v 1.1 1994/01/25 22:53:29 martin Exp $";
+#endif /* not lint */
+
+char Version[] = "bootpd 2.2-ALPHA %VERSION%\n\t%WHOANDWHERE%\n";
diff --git a/libexec/bootpd/announce b/libexec/bootpd/announce
new file mode 100644
index 000000000000..a3d2cfb40b1c
--- /dev/null
+++ b/libexec/bootpd/announce
@@ -0,0 +1,46 @@
+Subject: RFC-1048 compatible BOOTP server now available
+
+
+An RFC-1048 (BOOTP Vendor Information Extensions) compatible BOOTP (RFC-951)
+server 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.0.tar. This is
+an enhanced version of the existing CMU BOOTP server which was derived from
+the original BOOTP server created by Bill Croft at Stanford.
+
+New features and changes in version 2.0 include:
+
+o Full support for the vendor information extensions described in RFC-1048.
+o Faster response time (host lookup via hash table instead of linear search).
+o New termcap-like configuration file format which allows greater flexibility
+ in specifying the variable vendor information of RFC-1048. Host entries
+ may refer to other hosts as templates so that redundant information need
+ be specified only once.
+o Continued support for the CMU vendor information format. The server may
+ be configured on a per-host basis to always reply with a certain vendor
+ information format or to reply based on the client's request.
+o Expanded logging.
+o The server may now be run by inetd or as a standalone program like the
+ old version.
+o The configuration and debugging dump files may be specified on the command
+ line.
+
+
+The server has been successfully tested on the following machines:
+
+ IBM RT PC running ACIS 4.3 (4.3 BSD)
+ Sun 3/50 running SunOS 3.5
+ DEC MicroVAX II running Ultrix 1.1
+ DEC MicroVAX II running Ultrix 2.2
+
+
+
+Please direct questions, comments, and bug reports to
+Walt Wimer <ww0n@andrew.cmu.edu> or Drew Perkins <ddp@andrew.cmu.edu>.
+
+
+
+Sincerely,
+
+Walt Wimer
+Network Development
+Carnegie Mellon University
diff --git a/libexec/bootpd/announce.2.1 b/libexec/bootpd/announce.2.1
new file mode 100644
index 000000000000..28ddca6a63ee
--- /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 000000000000..34f2e3982a5b
--- /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/bootp.h b/libexec/bootpd/bootp.h
new file mode 100644
index 000000000000..5b373e2b284d
--- /dev/null
+++ b/libexec/bootpd/bootp.h
@@ -0,0 +1,137 @@
+#ifndef _BLURB_
+#define _BLURB_
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+#endif /* _BLURB_ */
+
+/*
+ * Bootstrap Protocol (BOOTP). RFC951 and RFC1395.
+ *
+ * $Header: /home/cvs/386BSD/src/libexec/bootpd/bootp.h,v 1.1 1994/01/25 22:53:32 martin Exp $
+ *
+ *
+ * This file specifies the "implementation-independent" BOOTP protocol
+ * information which is common to both client and server.
+ *
+ */
+
+#define BP_CHADDR_LEN 16
+#define BP_SNAME_LEN 64
+#define BP_FILE_LEN 128
+#define BP_VEND_LEN 64
+
+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 short bp_secs; /* seconds since boot began */
+ unsigned short bp_unused;
+ struct in_addr bp_ciaddr; /* client IP address */
+ struct in_addr bp_yiaddr; /* 'your' IP address */
+ 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 */
+ unsigned char bp_vend[BP_VEND_LEN]; /* vendor-specific area */
+};
+
+/*
+ * UDP port numbers, server and client.
+ */
+#define IPPORT_BOOTPS 67
+#define IPPORT_BOOTPC 68
+
+#define BOOTREPLY 2
+#define BOOTREQUEST 1
+
+/*
+ * 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
+#define HTYPE_PRONET 4
+#define HTYPE_CHAOS 5
+#define HTYPE_IEEE802 6
+#define HTYPE_ARCNET 7
+
+/*
+ * Vendor magic cookie (v_magic) for CMU
+ */
+#define VM_CMU "CMU"
+
+/*
+ * Vendor magic cookie (v_magic) for RFC1048
+ */
+#define VM_RFC1048 { 99, 130, 83, 99 }
+
+
+
+/*
+ * RFC1395 tag values used to specify what information is being supplied in
+ * the vendor field of the packet.
+ */
+
+#define TAG_PAD ((unsigned char) 0)
+#define TAG_SUBNET_MASK ((unsigned char) 1)
+#define TAG_TIME_OFFSET ((unsigned char) 2)
+#define TAG_GATEWAY ((unsigned char) 3)
+#define TAG_TIME_SERVER ((unsigned char) 4)
+#define TAG_NAME_SERVER ((unsigned char) 5)
+#define TAG_DOMAIN_SERVER ((unsigned char) 6)
+#define TAG_LOG_SERVER ((unsigned char) 7)
+#define TAG_COOKIE_SERVER ((unsigned char) 8)
+#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)
+
+
+
+/*
+ * "vendor" data permitted for CMU bootp clients.
+ */
+
+struct cmu_vend {
+ unsigned char v_magic[4]; /* magic number */
+ unsigned long 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 */
+};
+
+
+/* v_flags values */
+#define VF_SMASK 1 /* Subnet mask field contains valid data */
diff --git a/libexec/bootpd/bootpd.8 b/libexec/bootpd/bootpd.8
new file mode 100644
index 000000000000..bf0c69a62beb
--- /dev/null
+++ b/libexec/bootpd/bootpd.8
@@ -0,0 +1,164 @@
+.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University
+.\"
+.\" $Header: /home/cvs/386BSD/src/libexec/bootpd/bootpd.8,v 1.1.2.1 1994/05/01 16:06:34 jkh Exp $
+.\"
+.TH BOOTPD 8 "November 11, 1991" "Carnegie Mellon University"
+.UC 6
+
+.SH NAME
+bootpd \- Internet Boot Protocol server
+.SH SYNOPSIS
+.B /usr/libexec/bootpd
+[
+.B \-i
+.B \-s
+.B \-t
+timeout
+.B \-d
+level
+]
+[
+.I configfile
+[
+.I dumpfile
+] ]
+.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 :
+.PP
+.br
+ bootps dgram udp wait root /usr/libexec/bootpd bootpd
+.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
+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.
+.PP
+It is also possible to run
+.I bootpd
+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
+.I bootpd
+once during the server machine's bootup sequence. This 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.
+.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
+.B \-s
+switch may be used to force standalone operation. Similarly, the
+.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.
+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.
+.PP
+Upon startup,
+.I bootpd
+first reads its configuration file,
+.IR /etc/bootptab ,
+and then begins listening for 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.
+.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
+.I bootpd
+is compiled with the -DDEBUG option, receipt of a SIGUSR1 signal causes it
+to dump its memory-resident database to
+the file
+.I /etc/bootpd.dump
+or the command-line-specified dumpfile.
+
+.SH FILES
+/etc/bootptab
+.br
+/etc/bootpd.dump
+.br
+/etc/services
+
+.SH BUGS
+Individual host entries must not exceed 1024 characters.
+
+.SH HISTORY
+.TP
+22-Jan-86 Bill Croft at Stanford University
+.br
+Created.
+
+.TP
+30-Jul-86 David Kovar at Carnegie Mellon University
+.br
+Modified to CMU specifications.
+
+.TP
+24-Jul-87 Drew D. Perkins at Carnegie Mellon University
+.br
+Modified to use syslog. Added debugging dumps. Other bug fixes.
+
+.TP
+17-Jul-88 Walter L. Wimer at Carnegie Mellon University
+.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
+.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"
+.br
+bootptab(5), inetd(8), tftpd(8),
+.br
+DARPA Internet Request For Comments RFC951, RFC1048, RFC1084, Assigned Numbers
diff --git a/libexec/bootpd/bootpd.c b/libexec/bootpd/bootpd.c
new file mode 100644
index 000000000000..dd119e426c3d
--- /dev/null
+++ b/libexec/bootpd/bootpd.c
@@ -0,0 +1,1729 @@
+#ifndef _BLURB_
+#define _BLURB_
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+#endif /* _BLURB_ */
+
+
+#ifndef lint
+static char sccsid[] = "@(#)bootp.c 1.1 (Stanford) 1/22/86";
+static char rcsid[] = "$Header: /home/cvs/386BSD/src/libexec/bootpd/bootpd.c,v 1.1 1994/01/25 22:53:36 martin 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 accompanying man page -- bootpd.8
+ *
+ *
+ * HISTORY
+ *
+ * 01/22/86 Bill Croft at Stanford University
+ * Created.
+ *
+ * 07/30/86 David Kovar at Carnegie Mellon University
+ * Modified to work at CMU.
+ *
+ * 07/24/87 Drew D. Perkins at Carnegie Mellon University
+ * Modified to use syslog instead of Kovar's
+ * routines. Add debugging dumps. Many other fixups.
+ *
+ * 07/15/88 Walter L. Wimer at Carnegie Mellon University
+ * Added vendor information to conform to RFC1048.
+ * Adopted termcap-like file format to support above.
+ * Added hash table lookup instead of linear search.
+ * Other cleanups.
+ *
+ *
+ * BUGS
+ *
+ * Currently mallocs memory in a very haphazard manner. As such, most of
+ * the program ends up core-resident all the time just to follow all the
+ * stupid pointers around. . . .
+ *
+ */
+
+
+
+
+#include <sys/types.h>
+#include <sys/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>
+#ifdef SUNOS40
+#include <sys/sockio.h>
+#include <net/if_arp.h>
+#endif
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdio.h>
+#include <strings.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <syslog.h>
+
+#include "bootp.h"
+#include "hash.h"
+#include "bootpd.h"
+
+#define HASHTABLESIZE 257 /* Hash table size (prime) */
+#define DEFAULT_TIMEOUT 15L /* Default timeout in minutes */
+
+#ifndef CONFIG_FILE
+#define CONFIG_FILE "/etc/bootptab"
+#endif
+#ifndef DUMP_FILE
+#define DUMP_FILE "/etc/bootpd.dump"
+#endif
+
+
+
+/*
+ * Externals, forward declarations, and global variables
+ */
+
+extern char Version[];
+extern char *sys_errlist[];
+extern int errno, sys_nerr;
+
+void usage();
+void insert_u_long();
+void dump_host();
+void list_ipaddresses();
+#ifdef VEND_CMU
+void dovend_cmu();
+#endif
+void dovend_rfc1048();
+boolean hwlookcmp();
+boolean iplookcmp();
+void insert_generic();
+void insert_ip();
+int dumptab();
+int chk_access();
+void report();
+char *get_errmsg();
+
+/*
+ * 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 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 */
+
+
+/*
+ * General
+ */
+
+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 */
+
+/*
+ * Globals below are associated with the bootp database file (bootptab).
+ */
+
+char *bootptab = NULL;
+#ifdef DEBUG
+char *bootpd_dump = NULL;
+#endif
+
+
+
+/*
+ * Vendor magic cookies for CMU and RFC1048
+ */
+
+unsigned char vm_cmu[4] = VM_CMU;
+unsigned char vm_rfc1048[4] = VM_RFC1048;
+
+
+/*
+ * 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[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 */
+};
+
+
+/*
+ * Main hash tables
+ */
+
+hash_tbl *hwhashtable;
+hash_tbl *iphashtable;
+hash_tbl *nmhashtable;
+
+
+
+
+/*
+ * Initialization such as command-line processing is done and then the main
+ * server loop is started.
+ */
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct timeval actualtimeout, *timeout;
+ struct bootp *bp = (struct 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, &s_in, &tolen) == 0) {
+ /*
+ * Descriptor 0 is a socket. Assume we're running as a child of inetd.
+ */
+ 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");
+ 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] == '-') {
+ /*
+ * Backwards-compatible behavior:
+ * no parameter, so just increment the debug flag.
+ */
+ debug++;
+ break;
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
+ fprintf(stderr,
+ "bootpd: invalid debug level\n");
+ break;
+ }
+ debug = n;
+ break;
+ case 's':
+ standalone = TRUE;
+ break;
+ case 'i':
+ standalone = FALSE;
+ break;
+ default:
+ fprintf(stderr, "bootpd: unknown switch: -%c\n",
+ argv[0][1]);
+ usage();
+ break;
+ }
+ } else {
+ if (!bootptab) {
+ bootptab = argv[0];
+#ifdef DEBUG
+ } else if (!bootpd_dump) {
+ bootpd_dump = argv[0];
+#endif
+ } else {
+ fprintf(stderr, "bootpd: unknown argument: %s\n", argv[0]);
+ usage();
+ }
+ }
+ }
+
+ /*
+ * 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
+
+
+ if (standalone) {
+ /*
+ * Go into background and disassociate from controlling terminal.
+ */
+ if (debug < 3) {
+ if (fork())
+ exit(0);
+ for (n = 0; n < 10; n++)
+ (void) close(n);
+ (void) open("/", O_RDONLY);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+ n = open("/dev/tty", O_RDWR);
+ if (n >= 0) {
+ ioctl(n, TIOCNOTTY, (char *) 0);
+ (void) close(n);
+ }
+ }
+ /*
+ * Nuke any timeout value
+ */
+ timeout = NULL;
+ }
+
+
+#ifdef SYSLOG
+ /*
+ * Initialize logging.
+ */
+#ifndef LOG_CONS
+#define LOG_CONS 0 /* Don't bother if not defined... */
+#endif
+#ifndef LOG_DAEMON
+#define LOG_DAEMON 0
+#endif
+ openlog("bootpd", LOG_PID | LOG_CONS, LOG_DAEMON);
+#endif
+
+ /*
+ * Log startup
+ */
+ report(LOG_INFO, "%s", Version);
+
+ /*
+ * Get our timezone offset so we can give it to clients if the
+ * configuration file doesn't specify one.
+ */
+ if (gettimeofday(&tp, &tzp) < 0) {
+ secondswest = 0L; /* Assume GMT for lack of anything better */
+ report(LOG_ERR, "gettimeofday: %s\n", get_errmsg());
+ } else {
+ secondswest = 60L * tzp.tz_minuteswest; /* Convert to seconds */
+ }
+
+ /*
+ * Allocate hash tables for hardware address, ip address, and hostname
+ */
+ hwhashtable = hash_Init(HASHTABLESIZE);
+ iphashtable = hash_Init(HASHTABLESIZE);
+ nmhashtable = hash_Init(HASHTABLESIZE);
+ if (!(hwhashtable && iphashtable && nmhashtable)) {
+ report(LOG_ERR, "Unable to allocate hash tables.\n");
+ exit(1);
+ }
+
+
+ /*
+ * Read the bootptab file once immediately upon startup.
+ */
+ readtab();
+
+
+ if (standalone) {
+ /*
+ * Create a socket.
+ */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ report(LOG_ERR, "socket: %s\n", get_network_errmsg());
+ exit(1);
+ }
+
+ /*
+ * Get server's listening port number
+ */
+ servp = getservbyname("bootps", "udp");
+ if (servp) {
+ bootps_port = ntohs((u_short) servp->s_port);
+ } else {
+ report(LOG_ERR,
+ "udp/bootps: unknown service -- assuming port %d\n",
+ IPPORT_BOOTPS);
+ bootps_port = (u_short) IPPORT_BOOTPS;
+ }
+
+ /*
+ * Bind socket to BOOTPS port.
+ */
+ s_in.sin_family = AF_INET;
+ s_in.sin_addr.s_addr = INADDR_ANY;
+ s_in.sin_port = htons(bootps_port);
+ if (bind(s, &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 (!(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, &from, &fromlen);
+ if (n <= 0) {
+ continue;
+ }
+
+ if (n < sizeof(struct bootp)) {
+ if (debug) {
+ report(LOG_INFO, "received short packet\n");
+ }
+ continue;
+ }
+
+ readtab(); /* maybe re-read bootptab */
+ switch (bp->bp_op) {
+ case BOOTREQUEST:
+ request();
+ break;
+
+ case BOOTREPLY:
+ reply();
+ break;
+ }
+ }
+}
+
+
+
+
+/*
+ * Print "usage" message and exit
+ */
+
+void usage()
+{
+ fprintf(stderr,
+"usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
+ fprintf(stderr, "\t -d n\tset debug level\n");
+ fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
+ fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
+ fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
+ exit(1);
+}
+
+
+
+/*
+ * Process BOOTREQUEST packet.
+ *
+ * (Note, this version of the bootpd.c server never forwards
+ * the request to another server. In our environment the
+ * stand-alone gateways perform that function.)
+ *
+ * (Also this version does not interpret the hostname field of
+ * the request packet; it COULD do a name->address lookup and
+ * forward the request there.)
+ */
+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) {
+ /*
+ * client doesnt know his IP address,
+ * search by hardware address.
+ */
+ if (debug) {
+ report(LOG_INFO, "request from %s address %s\n",
+ netname(bp->bp_htype),
+ haddrtoa(bp->bp_chaddr, bp->bp_htype));
+ }
+
+ dummyhost.htype = bp->bp_htype;
+ hlen = haddrlength(bp->bp_htype);
+ bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
+ hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
+ hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
+ &dummyhost);
+ if (hp == NULL) {
+ 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_yiaddr).s_addr = hp->iaddr.s_addr;
+
+ } else {
+
+ /*
+ * search by IP address.
+ */
+ if (debug) {
+ report(LOG_INFO, "request from IP addr %s\n",
+ inet_ntoa(bp->bp_ciaddr));
+ }
+ dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
+ hashcode = hash_HashFunction((char *) &(bp->bp_ciaddr.s_addr), 4);
+ hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
+ &dummyhost);
+ if (hp == NULL) {
+ report(LOG_NOTICE,
+ "IP address not found: %s\n", inet_ntoa(bp->bp_ciaddr));
+ return;
+ }
+ }
+
+ if (debug) {
+ report(LOG_INFO, "found %s %s\n", inet_ntoa(hp->iaddr),
+ hp->hostname->string);
+ }
+
+ /*
+ * If a specific TFTP server address was specified in the bootptab file,
+ * fill it in, otherwise zero it.
+ */
+ (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
+ hp->bootserver.s_addr : 0L;
+
+ /*
+ * This next line is a bit of a mystery. It seems to be vestigial
+ * code (from Stanford???) which should probably be axed.
+ */
+ if (strcmp(bp->bp_file, "sunboot14") == 0)
+ bp->bp_file[0] = 0; /* pretend it's null */
+
+
+ /*
+ * Fill in the client's proper bootfile.
+ *
+ * If the client specifies an absolute path, try that file with a
+ * ".host" suffix and then without. If the file cannot be found, no
+ * reply is made at all.
+ *
+ * If the client specifies a null or relative file, use the following
+ * table to determine the appropriate action:
+ *
+ * Homedir Bootfile Client's file
+ * specified? specified? specification Action
+ * -------------------------------------------------------------------
+ * No No Null Send null filename
+ * No No Relative Discard request
+ * No Yes Null Send if absolute else null
+ * No Yes Relative Discard request
+ * Yes No Null Send null filename
+ * Yes No Relative Lookup with ".host"
+ * Yes Yes Null Send home/boot or bootfile
+ * Yes Yes Relative Lookup with ".host"
+ *
+ */
+
+ if (hp->flags.tftpdir) {
+ strcpy(realpath, hp->tftpdir->string);
+ path = &realpath[strlen(realpath)];
+ } else {
+ path = realpath;
+ }
+
+ if (bp->bp_file[0]) {
+ /*
+ * The client specified a file.
+ */
+ if (bp->bp_file[0] == '/') {
+ strcpy(path, bp->bp_file); /* Absolute pathname */
+ } else {
+ if (hp->flags.homedir) {
+ strcpy(path, hp->homedir->string);
+ strcat(path, "/");
+ strcat(path, bp->bp_file);
+ } else {
+ report(LOG_NOTICE,
+ "requested file \"%s\" not found: hd unspecified\n",
+ bp->bp_file);
+ return;
+ }
+ }
+ } else {
+ /*
+ * No file specified by the client.
+ */
+ if (hp->flags.bootfile && ((hp->bootfile->string)[0] == '/')) {
+ strcpy(path, hp->bootfile->string);
+ } else if (hp->flags.homedir && hp->flags.bootfile) {
+ strcpy(path, hp->homedir->string);
+ strcat(path, "/");
+ strcat(path, hp->bootfile->string);
+ } else {
+ bzero(bp->bp_file, sizeof(bp->bp_file));
+ goto skip_file; /* Don't bother trying to access the file */
+ }
+ }
+
+ /*
+ * First try to find the file with a ".host" suffix
+ */
+ n = strlen(path);
+ strcat(path, ".");
+ strcat(path, hp->hostname->string);
+ if (chk_access(realpath, &bootsize) < 0) {
+ path[n] = 0; /* Try it without the suffix */
+ if (chk_access(realpath, &bootsize) < 0) {
+ if (bp->bp_file[0]) {
+ /*
+ * Client wanted specific file
+ * and we didn't have it.
+ */
+ report(LOG_NOTICE,
+ "requested file not found: \"%s\"\n", path);
+ return;
+ } else {
+ /*
+ * Client didn't ask for a specific file and we couldn't
+ * access the default file, so just zero-out the bootfile
+ * field in the packet and continue processing the reply.
+ */
+ bzero(bp->bp_file, sizeof(bp->bp_file));
+ goto skip_file;
+ }
+ }
+ }
+ strcpy(bp->bp_file, path);
+
+skip_file: ;
+
+
+
+ if (debug > 1) {
+ report(LOG_INFO, "vendor magic field is %d.%d.%d.%d\n",
+ (int) ((bp->bp_vend)[0]),
+ (int) ((bp->bp_vend)[1]),
+ (int) ((bp->bp_vend)[2]),
+ (int) ((bp->bp_vend)[3]));
+ }
+
+ /*
+ * If this host isn't set for automatic vendor info then copy the
+ * specific cookie into the bootp packet, thus forcing a certain
+ * reply format.
+ */
+ if (!hp->flags.vm_auto) {
+ 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 */
+#ifdef VEND_CMU
+ dovend_cmu(bp, hp);
+#else
+ dovend_rfc1048(bp, hp, bootsize);
+#endif
+ } else {
+ /* RFC1048 conformant bootp client */
+ dovend_rfc1048(bp, hp, bootsize);
+ }
+ sendreply(0);
+}
+
+
+
+/*
+ * Process BOOTREPLY packet (something is using us as a gateway).
+ */
+
+reply()
+{
+ if (debug) {
+ report(LOG_INFO, "processing boot reply\n");
+ }
+ sendreply(1);
+}
+
+
+
+/*
+ * Send a reply packet to the client. 'forward' flag is set if we are
+ * not the originator of this reply packet.
+ */
+sendreply(forward)
+ int forward;
+{
+ struct bootp *bp = (struct bootp *) buf;
+ struct in_addr dst;
+ struct sockaddr_in to;
+
+ to = s_in;
+
+ to.sin_port = htons(bootpc_port);
+ /*
+ * If the client IP address is specified, use that
+ * else if gateway IP address is specified, use that
+ * else make a temporary arp cache entry for the client's NEW
+ * IP/hardware address and use that.
+ */
+ if (bp->bp_ciaddr.s_addr) {
+ dst = bp->bp_ciaddr;
+ } else if (bp->bp_giaddr.s_addr && forward == 0) {
+ dst = bp->bp_giaddr;
+ to.sin_port = htons(bootps_port);
+ } else {
+ dst = bp->bp_yiaddr;
+ setarp(&dst, bp->bp_chaddr, bp->bp_hlen);
+ }
+
+ if (forward == 0) {
+ /*
+ * If we are originating this reply, we
+ * need to find our own interface address to
+ * put in the bp_siaddr field of the reply.
+ * If this server is multi-homed, pick the
+ * 'best' interface (the one on the same net
+ * as the client).
+ */
+#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;
+ }
+
+ if (bp->bp_giaddr.s_addr == 0) {
+ if (maxmatch == 0) {
+ return;
+ }
+ bp->bp_giaddr = ((struct sockaddr_in *)
+ (&ifrmax->ifr_addr))->sin_addr;
+ }
+
+ /*
+ * If a specific TFTP server address wasn't specified
+ * in the bootptab file, fill in our own address.
+ */
+ if (bp->bp_siaddr.s_addr == 0) {
+ bp->bp_siaddr = ((struct sockaddr_in *)
+ (&ifrmax->ifr_addr))->sin_addr;
+ }
+ }
+
+ to.sin_addr = dst;
+ if (sendto(s, bp, sizeof(struct bootp), 0, &to, sizeof(to)) < 0) {
+ report(LOG_ERR, "sendto: %s\n", get_network_errmsg());
+ }
+}
+
+
+
+/*
+ * 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);
+}
+
+
+
+/*
+ * Setup the arp cache so that IP address 'ia' will be temporarily
+ * bound to hardware address 'ha' of length 'len'.
+ */
+setarp(ia, ha, len)
+ struct in_addr *ia;
+ byte *ha;
+ int len;
+{
+ struct sockaddr_in *si;
+
+ bzero((caddr_t)&arpreq, sizeof(arpreq));
+
+ arpreq.arp_pa.sa_family = AF_INET;
+ si = (struct sockaddr_in *) &arpreq.arp_pa;
+ si->sin_addr = *ia;
+
+ arpreq.arp_flags = ATF_INUSE | ATF_COM;
+
+ bcopy(ha, arpreq.arp_ha.sa_data, len);
+
+ if (ioctl(s, SIOCSARP, (caddr_t)&arpreq) < 0) {
+ report(LOG_ERR, "ioctl(SIOCSARP): %s\n", get_network_errmsg());
+ }
+}
+
+
+
+/*
+ * This call checks read access to a file. It returns 0 if the file given
+ * by "path" exists and is publically readable. A value of -1 is returned if
+ * access is not permitted or an error occurs. Successful calls also
+ * return the file size in bytes using the long pointer "filesize".
+ *
+ * The read permission bit for "other" users is checked. This bit must be
+ * set for tftpd(8) to allow clients to read the file.
+ */
+
+int chk_access(path, filesize)
+char *path;
+long *filesize;
+{
+ struct stat buf;
+
+ if ((stat(path, &buf) == 0) && (buf.st_mode & (S_IREAD >> 6))) {
+ *filesize = (long) buf.st_size;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+
+
+#ifdef DEBUG
+
+/*
+ * Dump the internal memory database to bootpd_dump.
+ */
+
+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);
+}
+
+
+
+/*
+ * Dump all the available information on the host pointed to by "hp".
+ * The output is sent to the file pointed to by "fp".
+ */
+
+void dump_host(fp, hp)
+FILE *fp;
+struct host *hp;
+{
+ int i;
+ byte *dataptr;
+
+ if (hp) {
+ if (hp->hostname) {
+ fprintf(fp, "%s:", hp->hostname->string);
+ }
+ if (hp->flags.bootfile) {
+ fprintf(fp, "bf=%s:", hp->bootfile->string);
+ }
+ if (hp->flags.bootsize) {
+ fprintf(fp, "bs=");
+ if (hp->flags.bootsize_auto) {
+ fprintf(fp, "auto:");
+ } else {
+ fprintf(fp, "%d:", hp->bootsize);
+ }
+ }
+ if (hp->flags.cookie_server) {
+ fprintf(fp, "cs=");
+ list_ipaddresses(fp, hp->cookie_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.domain_server) {
+ fprintf(fp, "ds=");
+ list_ipaddresses(fp, hp->domain_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.gateway) {
+ fprintf(fp, "gw=");
+ list_ipaddresses(fp, hp->gateway);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.homedir) {
+ fprintf(fp, "hd=%s:", hp->homedir->string);
+ }
+ if (hp->flags.name_switch && hp->flags.send_name) {
+ fprintf(fp, "hn:");
+ }
+ if (hp->flags.htype) {
+ fprintf(fp, "ht=%u:", (unsigned) hp->htype);
+ if (hp->flags.haddr) {
+ fprintf(fp, "ha=%s:", haddrtoa(hp->haddr, hp->htype));
+ }
+ }
+ if (hp->flags.impress_server) {
+ fprintf(fp, "im=");
+ list_ipaddresses(fp, hp->impress_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.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, ":");
+ }
+ 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);
+ }
+ }
+ 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++));
+ }
+ }
+ }
+}
+#endif /* DEBUG */
+
+
+
+#ifdef VEND_CMU
+
+/*
+ * Insert the CMU "vendor" data for the host pointed to by "hp" into the
+ * bootp packet pointed to by "bp".
+ */
+
+void dovend_cmu(bp, hp)
+ struct bootp *bp;
+ struct host *hp;
+{
+ 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");
+ }
+}
+
+#endif /* VEND_CMU */
+
+
+
+/*
+ * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
+ * bootp packet pointed to by "bp".
+ */
+
+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.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.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.
+ */
+ 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.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.
+ */
+ bzero(vp, bytesleft);
+ }
+}
+
+
+
+/*
+ * 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.
+ */
+
+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;
+ }
+ }
+ }
+}
+
+
+
+
+/*
+ * Convert a hardware address to an ASCII string.
+ */
+
+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);
+}
+
+
+
+/*
+ * Insert the unsigned long "value" into memory starting at the byte
+ * pointed to by the byte pointer (*dest). (*dest) is updated to
+ * point to the next available byte.
+ *
+ * Since it is desirable to internally store network addresses in network
+ * byte order (in struct in_addr's), this routine expects longs to be
+ * passed in network byte order.
+ *
+ * However, due to the nature of the main algorithm, the long must be in
+ * host byte order, thus necessitating the use of ntohl() first.
+ */
+
+void insert_u_long(value, dest)
+ unsigned long value;
+ byte **dest;
+{
+ 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 */
+}
+
+
+
+/*
+ * Return pointer to static string which gives full filesystem error message.
+ */
+
+char *get_errmsg()
+{
+ static char errmsg[80];
+
+ if (errno < sys_nerr) {
+ return sys_errlist[errno];
+ } else {
+ sprintf(errmsg, "Error %d", errno);
+ return errmsg;
+ }
+}
+
+
+
+/*
+ * This routine reports errors and such via stderr and syslog() if
+ * appopriate. It just helps avoid a lot of "#ifdef SYSLOG" constructs
+ * from being scattered throughout the code.
+ *
+ * The syntax is identical to syslog(3), but %m is not considered special
+ * for output to stderr (i.e. you'll see "%m" in the output. . .). Also,
+ * control strings should normally end with \n since newlines aren't
+ * automatically generated for stderr output (whereas syslog strips out all
+ * newlines and adds its own at the end).
+ */
+
+/*VARARGS2*/
+void report(priority, fmt, p0, p1, p2, p3, p4)
+ int priority;
+ char *fmt;
+{
+#ifdef LOG_SALERT
+ static char *levelnames[] = {
+ "unknown level: ",
+ "alert(1): ",
+ "subalert(2): ",
+ "emergency(3): ",
+ "error(4): ",
+ "critical(5): ",
+ "warning(6): ",
+ "notice(7): ",
+ "information(8):",
+ "debug(9): ",
+ "unknown level: "
+ };
+#else
+ static char *levelnames[] = {
+ "emergency(0): ",
+ "alert(1): ",
+ "critical(2): ",
+ "error(3): ",
+ "warning(4): ",
+ "notice(5): ",
+ "information(6):",
+ "debug(7): ",
+ "unknown level: "
+ };
+#endif
+
+ if ((priority < 0) || (priority >= sizeof(levelnames)/sizeof(char *))) {
+ priority = sizeof(levelnames) / sizeof(char *) - 1;
+ }
+
+ /*
+ * Print the message
+ */
+ if (debug > 2) {
+ fprintf(stderr, "bootpd: %s ", levelnames[priority]);
+ fprintf(stderr, fmt, p0, p1, p2, p3, p4);
+ }
+#ifdef SYSLOG
+ syslog(priority, fmt, p0, p1, p2, p3, p4);
+#endif
+}
diff --git a/libexec/bootpd/bootpd.h b/libexec/bootpd/bootpd.h
new file mode 100644
index 000000000000..94fb86e855f1
--- /dev/null
+++ b/libexec/bootpd/bootpd.h
@@ -0,0 +1,228 @@
+#ifndef _BLURB_
+#define _BLURB_
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+#endif /* _BLURB_ */
+
+
+/*
+ * bootpd.h -- common header file for all the modules of the bootpd program.
+ */
+
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef PRIVATE
+#define PRIVATE static
+#endif
+
+#ifndef SIGUSR1
+#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.
+ */
+#define get_network_errmsg get_errmsg
+
+
+/*
+ * 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;
+ struct in_addr addr[1]; /* Dynamically extended */
+};
+
+
+/*
+ * Data structures used to hold shared strings and shared binary data.
+ * The linkcount must be set appropriately.
+ */
+
+struct shared_string {
+ unsigned linkcount;
+ char string[1]; /* Dynamically extended */
+};
+
+struct shared_bindata {
+ unsigned linkcount, length;
+ byte data[1]; /* Dynamically extended */
+};
+
+
+/*
+ * Flag structure which indicates which symbols have been defined for a
+ * given host. This information is used to determine which data should or
+ * should not be reported in the bootp packet vendor info field.
+ */
+
+struct flag {
+ unsigned bootfile :1,
+ bootserver :1,
+ bootsize :1,
+ bootsize_auto :1,
+ cookie_server :1,
+ domain_server :1,
+ gateway :1,
+ generic :1,
+ haddr :1,
+ homedir :1,
+ htype :1,
+ impress_server :1,
+ iaddr :1,
+ log_server :1,
+ lpr_server :1,
+ name_server :1,
+ name_switch :1,
+ rlp_server :1,
+ send_name :1,
+ subnet_mask :1,
+ tftpdir :1,
+ time_offset :1,
+ timeoff_auto :1,
+ time_server :1,
+ vendor_magic :1,
+ dumpfile :1,
+ domainname :1,
+ swap_server :1,
+ rootpath :1,
+ vm_auto :1;
+};
+
+
+
+/*
+ * The flags structure contains TRUE flags for all the fields which
+ * are considered valid, regardless of whether they were explicitly
+ * specified or indirectly inferred from another entry.
+ *
+ * The gateway and the various server fields all point to a shared list of
+ * IP addresses.
+ *
+ * The hostname, home directory, and bootfile are all shared strings.
+ *
+ * The generic data field is a shared binary data structure. It is used to
+ * hold future RFC1048 vendor data until bootpd is updated to understand it.
+ *
+ * The vm_cookie field specifies the four-octet vendor magic cookie to use
+ * if it is desired to always send the same response to a given host.
+ *
+ * Hopefully, the rest is self-explanatory.
+ */
+
+struct host {
+ struct flag flags; /* ALL valid fields */
+ struct in_addr_list *cookie_server,
+ *domain_server,
+ *gateway,
+ *impress_server,
+ *log_server,
+ *lpr_server,
+ *name_server,
+ *rlp_server,
+ *time_server;
+ struct shared_string *bootfile,
+ *hostname,
+ *domainname,
+ *homedir,
+ *tftpdir,
+ *dumpfile,
+ *rootpath;
+ 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;
+ unsigned int bootsize;
+ struct in_addr bootserver,
+ iaddr,
+ swapserver,
+ subnet_mask;
+};
diff --git a/libexec/bootpd/bootptab b/libexec/bootpd/bootptab
new file mode 100644
index 000000000000..4b1e5464da25
--- /dev/null
+++ b/libexec/bootpd/bootptab
@@ -0,0 +1,125 @@
+# /etc/bootptab: database for bootp server (/etc/bootpd)
+# Last update Mon 11/7/88 18:03
+# Blank lines and lines beginning with '#' are ignored.
+#
+# Legend:
+#
+# first field -- hostname
+# (may be full domain name and probably should be)
+#
+# hd -- home directory
+# bf -- bootfile
+# cs -- cookie servers
+# df -- dumpfile
+# dn -- domain name
+# ds -- domain name servers
+# gw -- gateways
+# ha -- hardware address
+# ht -- hardware type
+# im -- impress servers
+# ip -- host IP address
+# lg -- log servers
+# lp -- LPR servers
+# ns -- IEN-116 name servers
+# rl -- resource location protocol servers
+# rp -- root path
+# sm -- subnet mask
+# sw -- swap server
+# tc -- template host (points to similar host entry)
+# to -- time offset (seconds)
+# ts -- time servers
+
+#
+# Be careful about including backslashes where they're needed. Weird (bad)
+# things can happen when a backslash is omitted where one is intended.
+#
+
+
+# First, we define a global entry which specifies the stuff every host uses.
+
+global.dummy:\
+ :sm=255.255.255.0:\
+ :hd=/usr/boot:bf=null:\
+ :ds=128.2.35.50 128.2.13.21:\
+ :ns=0x80020b4d 0x80020ffd:\
+ :ts=0x80020b4d 0x80020ffd:\
+ :to=auto:\
+ :dn=cmu.edu:
+
+
+# Next, we can define different master entries for each subnet. . .
+
+subnet13.dummy:\
+ :tc=global.dummy:gw=128.2.13.1:
+
+subnet232.dummy:\
+ :tc=global.dummy:gw=128.2.232.1:
+
+subnet19.dummy:\
+ :tc=global.dummy:gw=128.2.19.1:
+
+
+#
+# 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.dummy:ht=ieee802:ha=7FF8100000AF:ip=128.2.11.1:
+baldwin:tc=subnet19.dummy:ht=ethernet:ha=0800200159C3:ip=128.2.11.10:
+wylie:tc=subnet232.dummy:ht=ethernet:ha=00DD00CADF00:ip=128.2.11.100:
+arnold:tc=subnet19.dummy:ht=ethernet:ha=0800200102AD:ip=128.2.11.102:
+bairdford:tc=subnet19.dummy:ht=ethernet:ha=08002B02A2F9:ip=128.2.11.103:
+bakerstown:tc=subnet19.dummy:ht=ethernet:ha=08002B0287C8:ip=128.2.11.104:
+butlerjct:tc=subnet232.dummy:ht=ethernet:ha=08002001560D:ip=128.2.11.108:
+gastonville:tc=subnet232.dummy:ht=ieee802:ha=7FFF81000A47:ip=128.2.11.115:
+hahntown:tc=subnet13.dummy:ht=ieee802:ha=7FFF81000434:ip=128.2.11.117:
+hickman:tc=subnet19.dummy:ht=ieee802:ha=7FFF810001BA:ip=128.2.11.118:
+lowber:tc=subnet13.dummy:ht=ethernet:ha=00DD00CAF000:ip=128.2.11.121:
+mtoliver:tc=subnet19.dummy:ht=ethernet:ha=00DD00FE1600:ip=128.2.11.122:
+osborne:tc=subnet232.dummy:ht=ethernet:ha=00DD00CAD600:ip=128.2.11.124:
+russelton:tc=subnet232.dummy:ht=ethernet:ha=080020017FC3:ip=128.2.11.126:
+thornburg:tc=subnet13.dummy:ht=ethernet:ha=080020012A33:ip=128.2.11.128:
+
+
+# Hmmm. . . Let's throw in some whitespace for readability. . . .
+
+andrew: tc=subnet19.dummy: ht=ethernet:ha=00DD00C88900: ip=128.2.11.131:
+birdville: tc=subnet19.dummy: ht=ethernet:ha=00DD00FE2D00: ip=128.2.11.14:
+coudersport: tc=subnet13.dummy: ht=ethernet:ha=00DD00CB1E00: ip=128.2.11.143:
+bridgeville: tc=subnet232.dummy: ht=ethernet:ha=080020011394: ip=128.2.11.15:
+franklin: tc=subnet19.dummy: ht=ethernet:ha=08002B02A5D5: ip=128.2.11.150:
+hollidaysburg: tc=subnet19.dummy: ht=ieee802:ha=7FFF810002C8: ip=128.2.11.154:
+honesdale: tc=subnet19.dummy: ht=ethernet:ha=08002B02F83F: ip=128.2.11.155:
+huntingdon: tc=subnet19.dummy: ht=ethernet:ha=08002B02E410: ip=128.2.11.156:
+indiana: tc=subnet13.dummy: ht=ethernet:ha=08002B029BEC: ip=128.2.11.157:
+jimthorpe: tc=subnet232.dummy: ht=ethernet:ha=08002B02FBBA: ip=128.2.11.158:
+kittanning: tc=subnet232.dummy: ht=ethernet:ha=08002B0273FC: ip=128.2.11.159:
+lebanon: tc=subnet232.dummy: ht=ethernet:ha=08002B037F67: ip=128.2.11.162:
+lewisburg: tc=subnet19.dummy: ht=ethernet:ha=50005A1A0DE4: ip=128.2.11.163:
+middleburg: tc=subnet232.dummy: ht=ethernet:ha=00DD00FE1200: ip=128.2.11.169:
+aspinwall: tc=subnet13.dummy: ht=ethernet:ha=08002B03C163: ip=128.2.11.17:
+berlin: tc=subnet13.dummy: ht=ethernet:ha=00DD000A4400: ip=128.2.11.171:
+norristown: tc=subnet13.dummy: ht=ethernet:ha=08002001455B: ip=128.2.11.174:
+pottsville: tc=subnet13.dummy: ht=ethernet:ha=00DD000A3700: ip=128.2.11.177:
+ridgway: tc=subnet19.dummy: ht=ethernet:ha=08002B029425: ip=128.2.11.179:
+scranton: tc=subnet232.dummy: ht=ethernet:ha=0800200113A1: ip=128.2.11.180:
+chalfont: tc=subnet13.dummy: ht=ethernet:ha=08002001124B: ip=128.2.11.19:
+washington: tc=subnet19.dummy: ht=ethernet:ha=00DD00656E00: ip=128.2.11.190:
+wellsboro: tc=subnet13.dummy: ht=ethernet:ha=00DD00CB1C00: ip=128.2.11.192:
+bb1: tc=subnet19.dummy: ht=ethernet:ha=00DD000A1F00: ip=128.2.11.197:
+adamstown: tc=subnet13.dummy: ht=ethernet:ha=08002B02D0E6: ip=128.2.11.199:
+beta: tc=subnet19.dummy: ht=ethernet:ha=02070100B197: ip=128.2.11.2:
+carbondale: tc=subnet232.dummy: ht=ethernet:ha=08002B022A73: ip=128.2.11.206:
+clairton: tc=subnet19.dummy: ht=ethernet:ha=080020010FD1: ip=128.2.11.21:
+egypt: tc=subnet13.dummy: ht=ethernet:ha=00DD00847B00: ip=128.2.11.211:
+fairchance: tc=subnet232.dummy: ht=ethernet:ha=00DD000AB100: ip=128.2.11.212:
+fairhope: tc=subnet232.dummy: ht=ethernet:ha=00DD00CB0800: ip=128.2.11.213:
+galeton: tc=subnet232.dummy: ht=ethernet:ha=08002001138C: ip=128.2.11.215:
+imperial: tc=subnet232.dummy: ht=ethernet:ha=08002001130C: ip=128.2.11.219:
+kingston: tc=subnet232.dummy: ht=ethernet:ha=080020011382: ip=128.2.11.224:
+knox: tc=subnet232.dummy: ht=ethernet:ha=50005A1A0D2A: ip=128.2.11.225:
+lakecity: tc=subnet13.dummy: ht=ethernet:ha=080020011380: ip=128.2.11.226:
diff --git a/libexec/bootpd/bootptab.5 b/libexec/bootpd/bootptab.5
new file mode 100644
index 000000000000..874ed48bad56
--- /dev/null
+++ b/libexec/bootpd/bootptab.5
@@ -0,0 +1,404 @@
+.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University
+.\"
+.\" $Header: /home/cvs/386BSD/src/libexec/bootpd/bootptab.5,v 1.1.2.1 1994/05/01 16:06:36 jkh Exp $
+.\"
+.TH BOOTPTAB 5 "October 31, 1991" "Carnegie Mellon University"
+.UC 6
+
+.SH NAME
+bootptab \- Internet Bootstrap Protocol server database
+.SH DESCRIPTION
+The
+.I bootptab
+file is the configuration database file for
+.IR bootpd ,
+the Internet Bootstrap Protocol server.
+It's format is similar to that of
+.IR termcap (5)
+in which two-character case-sensitive tag symbols are used to
+represent host parameters. These parameter declarations are separated by
+colons (:), with a general format of:
+.PP
+.I " hostname:tg=value. . . :tg=value. . . :tg=value. . . ."
+.PP
+where
+.I hostname
+is the actual name of a bootp client and
+.I tg
+is a two-character tag symbol. 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 :).
+The currently recognized tags are:
+.PP
+.br
+ bf Bootfile
+.br
+ bs Bootfile size in 512-octet blocks
+.br
+ cs Cookie server address list
+.br
+ df Merit dump file
+.br
+ dn Domain name
+.br
+ ds Domain name server address list
+.br
+ gw Gateway address list
+.br
+ ha Host hardware address
+.br
+ hd Bootfile home directory
+.br
+ hn Send client's hostname to client
+.br
+ ht Host hardware type (see Assigned Numbers RFC)
+.br
+ im Impress server address list
+.br
+ ip Host IP address
+.br
+ lg Log server address list
+.br
+ lp LPR server address list
+.br
+ ns IEN-116 name server address list
+.br
+ rl Resource location protocol server address list
+.br
+ rp Root path to mount as root
+.br
+ sa TFTP server address client should use
+.br
+ sm Host subnet mask
+.br
+ sw Swap server address
+.br
+ tc Table continuation (points to similar "template" host entry)
+.br
+ td TFTP root directory used by "secure" TFTP servers
+.br
+ to Time offset in seconds from UTC
+.br
+ ts Time server address list
+.br
+ vm Vendor magic cookie selector
+
+.PP
+There is also a generic tag,
+.RI T n ,
+where
+.I n
+is an RFC1084 vendor field tag number. Thus it is possible to immediately
+take advantage of future extensions to RFC1084 without being forced to modify
+.I bootpd
+first. Generic data may be represented as either a stream of hexadecimal
+numbers or as a quoted string of ASCII characters. The length of the generic
+data is automatically determined and inserted into the proper field(s) of the
+RFC1084-style bootp reply.
+.PP
+The following tags take a whitespace-separated list of IP addresses:
+.BR cs ,
+.BR ds ,
+.BR gw ,
+.BR im ,
+.BR lg ,
+.BR lp ,
+.BR ns ,
+.BR rl ,
+and
+.BR ts .
+The
+.BR ip ,
+.BR sa ,
+.BR sw ,
+and
+.B sm
+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').
+.PP
+The
+.B ht
+tag specifies the hardware type code as either an unsigned decimal, octal, or
+hexadecimal integer or one of the following symbolic names:
+.B ethernet
+or
+.B ether
+for 10Mb Ethernet,
+.B ethernet3
+or
+.B ether3
+for 3Mb experimental Ethernet,
+.BR ieee802 ,
+.BR tr ,
+or
+.B token-ring
+for IEEE 802 networks,
+.B pronet
+for Proteon ProNET Token Ring, or
+.BR chaos ,
+.BR arcnet ,
+or
+.B ax.25
+for Chaos, ARCNET, and AX.25 Amateur Radio networks, respectively.
+The
+.B ha
+tag takes a hardware address which
+.I must
+be specified in hexadecimal; optional periods and/or a leading '0x' may be
+included for readability. The
+.B ha
+tag must be preceded by the
+.B ht
+tag (either explicitly or implicitly; see
+.B tc
+below).
+.PP
+The hostname, home directory, and bootfile are ASCII strings which may be
+optionally surrounded by double quotes ("). The client's request and the
+values of the
+.B hd
+and
+.B bf
+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
+.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
+.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.
+.PP
+Some newer versions of
+.I tftpd
+provide a security feature to change their root directory using
+the
+.IR chroot (2)
+system call.
+The
+.B td
+tag may be used to inform
+.I bootpd
+of this special root directory used by
+.IR tftpd .
+The
+.B hd
+tag is actually relative to the root directory specified by the
+.B td
+tag.
+For example, if the real absolute path to your BOOTP client bootfile is
+/tftpboot/bootfiles/bootimage, and
+.IR tftpd
+uses /tftpboot as its "secure" directory, then specify the following in
+.IR bootptab :
+.PP
+.br
+ :td=/tftpboot:hd=/bootfiles:bf=bootimage:
+.PP
+If your bootfiles are located directly in /tftpboot, use:
+.PP
+.br
+ :td=/tftpboot:hd=/:bf=bootimage:
+.PP
+The
+.B sa
+tag may be used to specify the IP address of the particular TFTP server
+you wish the client to use. In the absence of this tag,
+.I bootpd
+will tell the client to perform TFTP to the same machine
+.I bootpd
+is running on.
+.PP
+The time offset
+.B to
+may be either a signed decimal integer specifying the client's
+time zone offset in seconds from UTC, or the keyword
+.B auto
+which uses the server's time zone offset. Specifying the
+.B to
+symbol as a boolean has the same effect as specifying
+.B auto
+as its value.
+.PP
+The bootfile size
+.B bs
+may be either a decimal, octal, or hexadecimal integer specifying the size of
+the bootfile in 512-octet blocks, or the keyword
+.B auto
+which causes the server to automatically calculate the bootfile size at each
+request. As with the time offset, specifying the
+.B bs
+symbol as a boolean has the same effect as specifying
+.B auto
+as its value.
+.PP
+The vendor magic cookie selector (the
+.B vm
+tag) may take one of the following keywords:
+.B auto
+(indicating that vendor information is determined by the client's request),
+.B rfc1048
+or
+.B rfc1084
+(which always forces an RFC1084-style reply), or
+.B cmu
+(which always forces a CMU-style reply).
+.PP
+The
+.B hn
+tag is strictly a boolean tag; it does not take the usual equals-sign and
+value. It's presence indicates that the hostname should be sent to RFC1084
+clients.
+.I Bootpd
+attempts to send the entire hostname as it is specified in the configuration
+file; if this will not fit into the reply packet, the name is shortened to
+just the host field (up to the first period, if present) and then tried.
+In no case is an arbitrarily-truncated hostname sent (if nothing reasonable
+will fit, nothing is sent).
+.PP
+Often, many host entries share common values for certain tags (such as name
+servers, etc.). Rather than repeatedly specifying these tags, a full
+specification can be listed for one host entry and shared by others via the
+.B tc
+(table continuation) mechanism.
+Often, the template entry is a dummy host which doesn't actually exist and
+never sends bootp requests. This feature is similar to the
+.B tc
+feature of
+.IR termcap (5)
+for similar terminals. Note that
+.I bootpd
+allows the
+.B tc
+tag symbol to appear anywhere in the host entry, unlike
+.I termcap
+which requires it to be the last tag. Information explicitly specified for a
+host always overrides information implied by a
+.B tc
+tag symbol, regardless of its location within the entry. The
+value of the
+.B tc
+tag may be the hostname or IP address of any host entry
+previously listed in the configuration file.
+.PP
+Sometimes it is necessary to delete a specific tag after it has been inferred
+via
+.BR tc .
+This can be done using the construction
+.IB tag @
+which removes the effect of
+.I tag
+as in
+.IR termcap (5).
+For example, to completely undo an IEN-116 name server specification, use
+":ns@:" at an appropriate place in the configuration entry. After removal
+with
+.BR @ ,
+a tag is eligible to be set again through the
+.B tc
+mechanism.
+.PP
+Blank lines and lines beginning with "#" are ignored in the configuration
+file. Host entries are separated from one another by newlines; a single host
+entry may be extended over multiple lines if the lines end with a backslash
+(\\). It is also acceptable for lines to be longer than 80 characters. Tags
+may appear in any order, with the following exceptions: the hostname must be
+the very first field in an entry, and the hardware type must precede the
+hardware address.
+.PP
+An example
+.I /etc/bootptab
+file follows:
+.PP
+
+.br
+ # Sample bootptab file
+
+.br
+ default1:\\
+.br
+ :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":
+
+.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:
+
+.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:
+
+
+.SH FILES
+/etc/bootptab
+
+.SH "SEE ALSO"
+.br
+bootpd(8), tftpd(8),
+.br
+DARPA Internet Request For Comments RFC951, RFC1048, RFC1084, Assigned Numbers
diff --git a/libexec/bootpd/hash.c b/libexec/bootpd/hash.c
new file mode 100644
index 000000000000..839b5f706cdc
--- /dev/null
+++ b/libexec/bootpd/hash.c
@@ -0,0 +1,386 @@
+#ifndef _BLURB_
+#define _BLURB_
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+#endif /* _BLURB_ */
+
+
+#ifndef lint
+static char rcsid[] = "$Header: /home/cvs/386BSD/src/libexec/bootpd/hash.c,v 1.1 1994/01/25 22:53:45 martin Exp $";
+#endif
+
+
+/*
+ * Generalized hash table ADT
+ *
+ * Provides multiple, dynamically-allocated, variable-sized hash tables on
+ * various data and keys.
+ *
+ * This package attempts to follow some of the coding conventions suggested
+ * by Bob Sidebotham and the AFS Clean Code Committee of the
+ * Information Technology Center at Carnegie Mellon.
+ */
+
+
+#include "hash.h"
+
+#define TRUE 1
+#define FALSE 0
+#define NULL 0
+
+/*
+ * This can be changed to make internal routines visible to debuggers, etc.
+ */
+#define PRIVATE static
+
+
+
+/*
+ * Hash table initialization routine.
+ *
+ * This routine creates and intializes a hash table of size "tablesize"
+ * entries. Successful calls return a pointer to the hash table (which must
+ * be passed to other hash routines to identify the hash table). Failed
+ * calls return NULL.
+ */
+
+hash_tbl *hash_Init(tablesize)
+unsigned tablesize;
+{
+ register hash_tbl *hashtblptr;
+ register unsigned totalsize;
+
+ 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];
+ }
+ } else {
+ hashtblptr = NULL; /* Disallow zero-length tables */
+ }
+ return hashtblptr; /* NULL if failure */
+}
+
+
+
+/*
+ * Recursively 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)();
+{
+ if (bucketptr) {
+ /*
+ * Free next member if necessary
+ */
+ hashi_FreeMember(bucketptr->next, free_data);
+ (*free_data)(bucketptr->data);
+ free((char *) bucketptr);
+ }
+}
+
+
+
+
+/*
+ * This routine re-initializes the hash table. It frees all the allocated
+ * memory and resets all bucket pointers to NULL.
+ */
+
+void hash_Reset(hashtable, free_data)
+hash_tbl *hashtable;
+void (*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];
+}
+
+
+
+/*
+ * Generic hash function to calculate a hash code from the given string.
+ *
+ * For each byte of the string, this function left-shifts the value in an
+ * accumulator and then adds the byte into the accumulator. The contents of
+ * the accumulator is returned after the entire string has been processed.
+ * It is assumed that this result will be used as the "hashcode" parameter in
+ * calls to other functions in this package. These functions automatically
+ * adjust the hashcode for the size of each hashtable.
+ *
+ * This algorithm probably works best when the hash table size is a prime
+ * number.
+ *
+ * Hopefully, this function is better than the previous one which returned
+ * the sum of the squares of all the bytes. I'm still open to other
+ * suggestions for a default hash function. The programmer is more than
+ * welcome to supply his/her own hash function as that is one of the design
+ * features of this package.
+ */
+
+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;
+}
+
+
+
+/*
+ * 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;
+{
+ 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;
+ }
+ 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
+ * its uniqueness.
+ *
+ * If the insertion is successful 0 is returned. If a matching entry
+ * already exists in the given bucket of the hash table, or some other error
+ * 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;
+{
+ 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;
+ temp->data = element;
+ temp->next = memberptr;
+ return 0; /* Success */
+ } else {
+ return -1; /* malloc failed! */
+ }
+}
+
+
+
+/*
+ * 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)();
+{
+ 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;
+ /*
+ * Stop hashi_FreeMember() from recursively deleting the whole list!
+ */
+ 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;
+ }
+ }
+ return retval;
+}
+
+
+
+/*
+ * Locate and return the data entry associated with the given key.
+ *
+ * If the data entry is found, a pointer to it is returned. Otherwise,
+ * NULL is returned.
+ */
+
+hash_datum *hash_Lookup(hashtable, hashcode, compare, key)
+hash_tbl *hashtable;
+unsigned hashcode;
+int (*compare)();
+hash_datum *key;
+{
+ hash_member *memberptr;
+
+ memberptr = (hashtable->table)[hashcode % (hashtable->size)];
+ while (memberptr) {
+ if ((*compare)(key, memberptr->data)) {
+ return (memberptr->data);
+ }
+ memberptr = memberptr->next;
+ }
+ return NULL;
+}
+
+
+
+/*
+ * Return the next available entry in the hashtable for a linear search
+ */
+
+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) {
+ /*
+ * 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;
+{
+ hashtable->bucketnum = 0;
+ hashtable->member = (hashtable->table)[0];
+ return hash_NextEntry(hashtable);
+}
diff --git a/libexec/bootpd/hash.h b/libexec/bootpd/hash.h
new file mode 100644
index 000000000000..2a1d8b8e5a3b
--- /dev/null
+++ b/libexec/bootpd/hash.h
@@ -0,0 +1,137 @@
+#ifndef _BLURB_
+#define _BLURB_
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+#endif /* _BLURB_ */
+
+
+/*
+ * Generalized hash table ADT
+ *
+ * Provides multiple, dynamically-allocated, variable-sized hash tables on
+ * various data and keys.
+ *
+ * This package attempts to follow some of the coding conventions suggested
+ * by Bob Sidebotham and the AFS Clean Code Committee.
+ */
+
+
+/*
+ * The user must supply the following:
+ *
+ * 1. A comparison function which is declared as:
+ *
+ * int compare(data1, data2)
+ * hash_datum *data1, *data2;
+ *
+ * This function must compare the desired fields of data1 and
+ * data2 and return TRUE (1) if the data should be considered
+ * equivalent (i.e. have the same key value) or FALSE (0)
+ * otherwise. This function is called through a pointer passed to
+ * the various hashtable functions (thus pointers to different
+ * functions may be passed to effect different tests on different
+ * hash tables).
+ *
+ * Internally, all the functions of this package always call the
+ * compare function with the "key" parameter as the first parameter,
+ * and a full data element as the second parameter. Thus, the key
+ * and element arguments to functions such as hash_Lookup() may
+ * actually be of different types and the programmer may provide a
+ * compare function which compares the two different object types
+ * as desired.
+ *
+ * Example:
+ *
+ * int compare(key, element)
+ * char *key;
+ * struct some_complex_structure *element;
+ * {
+ * return !strcmp(key, element->name);
+ * }
+ *
+ * key = "John C. Doe"
+ * element = &some_complex_structure
+ * hash_Lookup(table, hashcode, compare, key);
+ *
+ * 2. A hash function yielding an unsigned integer value to be used
+ * as the hashcode (index into the hashtable). Thus, the user
+ * may hash on whatever data is desired and may use several
+ * different hash functions for various different hash tables.
+ * The actual hash table index will be the passed hashcode modulo
+ * the hash table size.
+ *
+ * A generalized hash function, hash_HashFunction(), is included
+ * with this package to make things a little easier. It is not
+ * guarenteed to use the best hash algorithm in existence. . . .
+ */
+
+
+
+/*
+ * Various hash table definitions
+ */
+
+
+#ifndef __HASHXYZ973__
+
+#define __HASHXYZ973__
+
+/*
+ * Define "hash_datum" as a universal data type
+ */
+#ifdef __STDC__
+typedef void hash_datum;
+#else
+typedef char hash_datum;
+#endif
+
+typedef struct hash_memberstruct hash_member;
+typedef struct hash_tblstruct hash_tbl;
+typedef struct hash_tblstruct_hdr hash_tblhdr;
+
+struct hash_memberstruct {
+ hash_member *next;
+ hash_datum *data;
+};
+
+struct hash_tblstruct_hdr {
+ unsigned size, bucketnum;
+ hash_member *member;
+};
+
+struct hash_tblstruct {
+ unsigned size, bucketnum;
+ hash_member *member; /* Used for linear dump */
+ 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();
+
+#endif
diff --git a/libexec/bootpd/newvers.sh b/libexec/bootpd/newvers.sh
new file mode 100644
index 000000000000..c98259d12a52
--- /dev/null
+++ b/libexec/bootpd/newvers.sh
@@ -0,0 +1,24 @@
+#!/bin/sh -
+#
+# Copyright (c) 1987 Regents of the University of California.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are permitted
+# provided that this notice is preserved and that due credit is given
+# to the University of California at Berkeley. The name of the University
+# may not be used to endorse or promote products derived from this
+# software without specific prior written permission. This software
+# is provided ``as is'' without express or implied warranty.
+#
+# @(#)newvers.sh 4.4 (Berkeley) 3/28/88
+#
+if [ ! -r version ]
+then
+ echo 0 > version
+fi
+touch version
+rm -f version.[oc]
+v=`cat version` u=${USER-root} d=`pwd` h=`hostname` t=`date`
+sed -e "s|%VERSION%|#${v}: ${t}|" -e "s|%WHOANDWHERE%|${u}@${h}:${d}|" \
+ < $1 >version.c
+/bin/echo `expr ${v} + 1` > version
diff --git a/libexec/bootpd/readfile.c b/libexec/bootpd/readfile.c
new file mode 100644
index 000000000000..e437193eb1b2
--- /dev/null
+++ b/libexec/bootpd/readfile.c
@@ -0,0 +1,2128 @@
+#ifndef _BLURB_
+#define _BLURB_
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+#endif /* _BLURB_ */
+
+
+#ifndef lint
+static char rcsid[] = "$Header: /home/cvs/386BSD/src/libexec/bootpd/readfile.c,v 1.1 1994/01/25 22:53:48 martin Exp $";
+#endif
+
+
+/*
+ * bootpd configuration file reading code.
+ *
+ * The routines in this file deal with reading, interpreting, and storing
+ * the information found in the bootpd configuration file (usually
+ * /etc/bootptab).
+ */
+
+
+#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 <syslog.h>
+#endif
+#include "bootp.h"
+#include "hash.h"
+#include "bootpd.h"
+
+
+#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 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_HOMEDIR 6
+#define SYM_HTYPE 7
+#define SYM_IMPRESS_SERVER 8
+#define SYM_IPADDR 9
+#define SYM_LOG_SERVER 10
+#define SYM_LPR_SERVER 11
+#define SYM_NAME_SERVER 12
+#define SYM_RLP_SERVER 13
+#define SYM_SUBNET_MASK 14
+#define SYM_TIME_OFFSET 15
+#define SYM_TIME_SERVER 16
+#define SYM_VENDOR_MAGIC 17
+#define SYM_SIMILAR_ENTRY 18
+#define SYM_NAME_SWITCH 19
+#define SYM_BOOTSIZE 20
+#define SYM_BOOT_SERVER 22
+#define SYM_TFTPDIR 23
+#define SYM_DUMPFILE 24
+#define SYM_DOMAIN_NAME 25
+#define SYM_SWAP_SERVER 26
+#define SYM_ROOTPATH 27
+
+#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 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;
+};
+
+
+struct htypename {
+ 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 */
+
+
+/*
+ * List of symbolic names used in the bootptab file. The order and actual
+ * values of the symbol codes (SYM_. . .) are unimportant, but they must
+ * all be unique.
+ */
+
+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 },
+};
+
+
+/*
+ * List of symbolic names for hardware types. Name translates into
+ * hardware type code listed with it. Names must begin with a letter
+ * and must be all lowercase. This is searched linearly, so put
+ * commonly-used entries near the beginning.
+ */
+
+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 }
+};
+
+
+
+/*
+ * Externals and forward declarations.
+ */
+
+extern char *malloc();
+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();
+
+
+
+
+
+/*
+ * Read bootptab database file. Avoid rereading the file if the
+ * write date hasn't changed since the last time we read it.
+ */
+
+int readtab()
+{
+ struct host *hp;
+ FILE *fp;
+ struct stat st;
+ unsigned hashcode, buflen;
+ static char buffer[MAXENTRYLEN];
+#ifdef DEBUG
+ char timestr[26];
+#endif
+
+ /*
+ * Check the last modification time.
+ */
+ if (stat(bootptab, &st) < 0) {
+ report(LOG_ERR, "stat on \"%s\": %s\n", bootptab, get_errmsg());
+ return -1;
+ }
+#ifdef DEBUG
+ if (debug > 3) {
+ strcpy(timestr, ctime(&(st.st_mtime)));
+ report(LOG_INFO, "bootptab mtime is %s",
+ timestr);
+ }
+#endif
+ if (st.st_mtime == modtime && st.st_nlink) {
+ /*
+ * hasn't been modified or deleted yet.
+ */
+ 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;
+ }
+ hp = (struct host *) smalloc(sizeof(struct host));
+
+ /*
+ * Get individual info
+ */
+ hp->flags.vm_auto = TRUE;
+ bcopy(vm_rfc1048, hp->vm_cookie, 4);
+ if (process_entry(hp, buffer) < 0) {
+ free_host(hp);
+ continue;
+ }
+
+ 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");
+ }
+ }
+
+ 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++;
+ }
+
+done:
+ fclose(fp);
+ report(LOG_INFO, "read %d entries (%d hosts) from \"%s\"\n",
+ nentries, nhosts, bootptab);
+ return 0;
+}
+
+
+
+/*
+ * 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
+ * starting with "#" are ignored (removed). Backslashes (\) always quote
+ * the next character except that newlines preceeded by a backslash cause
+ * line-continuation onto the next line. The entry is terminated by a
+ * newline character which is not preceeded by a backslash. Sequences
+ * surrounded by double quotes are taken literally (including newlines, but
+ * not backslashes).
+ *
+ * The "bufsiz" parameter points to an unsigned int which specifies the
+ * maximum permitted buffer size. Upon return, this value will be replaced
+ * with the actual length of the entry (not including the null terminator).
+ *
+ * This code is a little scary. . . . I don't like using gotos in C
+ * either, but I first wrote this as an FSM diagram and gotos seemed like
+ * 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;
+{
+ 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 */
+ }
+ }
+ }
+ 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 (\).
+ */
+
+mainloop:
+ c = fgetc(fp);
+ switch (c) {
+ case EOF:
+ case '\n':
+ 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;
+ }
+ 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;
+ }
+ }
+ }
+ 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;
+ }
+ } 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 */
+ }
+ }
+ /* 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 */
+}
+
+
+
+/*
+ * 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
+ * host structure pointed to by "host". If there is any problem with the
+ * entry, an error message is reported via report(), no further processing
+ * is done, and -1 is returned. Successful calls return 0.
+ *
+ * (Some errors probably shouldn't be so completely fatal. . . .)
+ */
+
+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);
+ return -1;
+ case E_END_OF_ENTRY:
+ default:
+#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;
+ }
+ adjust(&src);
+ }
+}
+
+
+
+/*
+ * 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
+ * point to the next tag entry).
+ *
+ * Obviously, this need a few more comments. . . .
+ */
+
+PRIVATE 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);
+ 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);
+
+ /*
+ * Determine the type of operation to be done on this symbol
+ */
+ switch ((*symbol)[2]) {
+ case '=':
+ optype = OP_ADDITION;
+ break;
+ case '@':
+ optype = OP_DELETION;
+ break;
+ case ':':
+ case '\0':
+ 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;
+ }
+ 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) {
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ case SYM_HADDR:
+ switch (optype) {
+ case OP_ADDITION:
+ if (hp->flags.htype && hp->htype &&
+ (tmphaddr = prs_haddr(symbol, hp->htype))) {
+ 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;
+
+ 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;
+
+ 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 (i < numsymbols) {
+ value = htnamemap[i].htype;
+ }
+ }
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+ } else {
+ hp->time_offset = timeoff;
+ hp->flags.timeoff_auto = FALSE;
+ hp->flags.time_offset = TRUE;
+ }
+ }
+ break;
+ case OP_DELETION:
+ hp->flags.time_offset = FALSE;
+ break;
+ case OP_BOOLEAN:
+ return E_SYNTAX_ERROR;
+ }
+ 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;
+
+ 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;
+ }
+ 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;
+
+ case SYM_SIMILAR_ENTRY:
+ switch (optype) {
+ case OP_ADDITION:
+ fill_defaults(hp, symbol);
+ break;
+ default:
+ return E_SYNTAX_ERROR;
+ }
+ break;
+
+ case SYM_NAME_SWITCH:
+ switch (optype) {
+ case OP_ADDITION:
+ return E_SYNTAX_ERROR;
+ case OP_DELETION:
+ 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;
+
+ case SYM_BOOTSIZE:
+ 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;
+ case OP_DELETION:
+ hp->flags.bootsize = FALSE;
+ break;
+ case OP_BOOLEAN:
+ 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;
+
+ 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;
+
+ 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_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;
+
+ 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;
+
+ default:
+ return E_UNKNOWN_SYMBOL;
+ }
+ return SUCCESS;
+}
+
+
+
+/*
+ * 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
+ * allowable length of the string (including null-terminator) is passed as
+ * "length". The actual length of the string which was read is returned in
+ * the unsigned integer pointed to by "length". This value is the same as
+ * that which would be returned by applying the strlen() function on the
+ * destination string (i.e the terminating null is not counted as a
+ * character). Trailing whitespace is removed from the string. For
+ * convenience, the function returns the new value of "dest".
+ *
+ * The string is read until the maximum number of characters, an unquoted
+ * colon (:), or a null character is read. The return string in "dest" is
+ * null-terminated.
+ */
+
+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;
+ }
+ if (**src == '\\') {
+ (*src)++;
+ if (! **src) {
+ break;
+ }
+ }
+ *dest++ = *(*src)++;
+ n++;
+ }
+
+ /*
+ * Remove that troublesome trailing whitespace. . .
+ */
+ while ((n > 0) && isspace(dest[-1])) {
+ dest--;
+ n--;
+ }
+
+ *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
+ * containing the string.
+ *
+ * The string is read using the same rules as get_string() above.
+ */
+
+PRIVATE struct shared_string *get_shared_string(src)
+char **src;
+{
+ char retstring[MAXSTRINGLEN];
+ struct shared_string *s;
+ unsigned 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);
+
+ return s;
+}
+
+
+
+/*
+ * Load RFC1048 generic information directly into a memory buffer.
+ *
+ * "src" indirectly points to the ASCII representation of the generic data.
+ * "dest" points to a string structure which is updated to point to a new
+ * string with the new data appended to the old string. The old string is
+ * freed.
+ *
+ * The given tag value is inserted with the new data.
+ *
+ * The data may be represented as either a stream of hexadecimal numbers
+ * representing bytes (any or all bytes may optionally start with '0x' and
+ * be separated with periods ".") or as a quoted string of ASCII
+ * characters (the quotes are required).
+ */
+
+PRIVATE void process_generic(src, dest, tagvalue)
+char **src;
+struct shared_bindata **dest;
+byte 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;
+}
+
+
+
+/*
+ * Verify that the given string makes sense as a hostname (according to
+ * Appendix 1, page 29 of RFC882).
+ *
+ * Return TRUE for good names, FALSE otherwise.
+ */
+
+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 */
+
+ 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;
+{
+ return FALSE;
+}
+
+
+/*
+ * Function for comparing a string with the hostname field of a host
+ * structure.
+ */
+
+PRIVATE boolean nmcmp(name, hp)
+char *name;
+struct host *hp;
+{
+ return !strcmp(name, hp->hostname->string);
+}
+
+
+/*
+ * Compare function to determine whether two hardware addresses are
+ * equivalent. Returns TRUE if "host1" and "host2" are equivalent, FALSE
+ * otherwise.
+ *
+ * If the hardware addresses of "host1" and "host2" are identical, but
+ * they are on different IP subnets, this function returns FALSE.
+ *
+ * This function is used when inserting elements into the hardware address
+ * hash table.
+ */
+
+PRIVATE boolean hwinscmp(host1, host2)
+struct host *host1, *host2;
+{
+ 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;
+ }
+ }
+ return TRUE;
+}
+
+
+
+/*
+ * 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;
+{
+ 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)++;
+ }
+ }
+ 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);
+ }
+ }
+ 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.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)++;
+ }
+ }
+ }
+}
+
+
+
+/*
+ * 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;
+{
+ register char *t;
+
+ t = *s;
+ while (*t && (*t != ':')) {
+ t++;
+ }
+ if (*t) {
+ t++;
+ }
+ *s = t;
+}
+
+
+
+
+/*
+ * This function adjusts the caller's pointer to point to the first
+ * non-whitespace character. If it runs into a null character, it leaves
+ * the pointer pointing to it.
+ */
+
+PRIVATE void eat_whitespace(s)
+char **s;
+{
+ register char *t;
+
+ t = *s;
+ while (*t && isspace(*t)) {
+ t++;
+ }
+ *s = t;
+}
+
+
+
+/*
+ * This function converts the given string to all lowercase.
+ */
+
+PRIVATE void makelower(s)
+char *s;
+{
+ while (*s) {
+ if (isupper(*s)) {
+ *s = tolower(*s);
+ }
+ s++;
+ }
+}
+
+
+
+/*
+ *
+ * N O T E :
+ *
+ * In many of the functions which follow, a parameter such as "src" or
+ * "symbol" is passed as a pointer to a pointer to something. This is
+ * done for the purpose of letting the called function update the
+ * caller's copy of the parameter (i.e. to effect call-by-reference
+ * parameter passing). The value of the actual parameter is only used
+ * to locate the real parameter of interest and then update this indirect
+ * parameter.
+ *
+ * I'm sure somebody out there won't like this. . . .
+ *
+ *
+ */
+
+
+
+/*
+ * "src" points to a character pointer which points to an ASCII string of
+ * whitespace-separated IP addresses. A pointer to an in_addr_list
+ * structure containing the list of addresses is returned. NULL is
+ * returned if no addresses were found at all. The pointer pointed to by
+ * "src" is updated to point to the first non-address (illegal) character.
+ */
+
+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;
+ address1 = tmpaddrlist;
+ address2 = result->addr;
+ for (; addrcount > 0; addrcount--) {
+ address2->s_addr = address1->s_addr;
+ address1++;
+ address2++;
+ }
+ }
+ return result;
+}
+
+
+
+/*
+ * prs_inetaddr(src, result)
+ *
+ * "src" is a value-result parameter; the pointer it points to is updated
+ * to point to the next data position. "result" points to an unsigned long
+ * in which an address is returned.
+ *
+ * This function parses the IP address string in ASCII "dot notation" pointed
+ * to by (*src) and places the result (in network byte order) in the unsigned
+ * long pointed to by "result". For malformed addresses, -1 is returned,
+ * (*src) points to the first illegal character, and the unsigned long pointed
+ * to by "result" is unchanged. Successful calls return 0.
+ */
+
+PRIVATE prs_inetaddr(src, result)
+char **src;
+u_long *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 == '.') {
+ /*
+ * 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++ = 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;
+ default:
+ 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
+ * as a pointer to the actual hardware address, represented as an array of
+ * bytes.
+ *
+ * The ASCII string must have the proper number of digits for the specified
+ * hardware type (e.g. twelve digits for a 48-bit Ethernet address).
+ * Two-digit sequences (bytes) may be separated with periods (.) and/or
+ * prefixed with '0x' for readability, but this is not required.
+ *
+ * For bad addresses, the pointer which "src" points to is updated to point
+ * to the start of the first two-digit sequence which was bad, and the
+ * function returns a NULL pointer.
+ */
+
+PRIVATE byte *prs_haddr(src, htype)
+char **src;
+byte 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)++;
+ }
+ if (interp_byte(src, hptr++) < 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
+ * character pointer is updated, and the result is deposited into the
+ * byte pointed to by "retbyte".
+ *
+ * The usual '0x' notation is allowed but not required. The number must be
+ * a two digit hexadecimal number. If the number is invalid, "src" and
+ * "retbyte" are left untouched and -1 is returned as the function value.
+ * Successful calls return 0.
+ */
+
+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;
+}
+
+
+
+/*
+ * The parameter "src" points to a character pointer which points to an
+ * ASCII string representation of an unsigned number. The number is
+ * returned as an unsigned long and the character pointer is updated to
+ * point to the first illegal character.
+ */
+
+PRIVATE u_long 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;
+ }
+ if (base == 16 && isxdigit(c)) {
+ value = (value << 4) + ((c & ~32) + 10 - 'A');
+ (*src)++;
+ continue;
+ }
+ break;
+ }
+ return value;
+}
+
+
+
+/*
+ * Routines for deletion of data associated with the main data structure.
+ */
+
+
+/*
+ * Frees the entire host data structure given. Does nothing if the passed
+ * pointer is NULL.
+ */
+
+PRIVATE void free_host(hostptr)
+struct host *hostptr;
+{
+ if (hostptr) {
+ del_iplist(hostptr->cookie_server);
+ del_iplist(hostptr->domain_server);
+ del_iplist(hostptr->gateway);
+ del_iplist(hostptr->impress_server);
+ del_iplist(hostptr->log_server);
+ del_iplist(hostptr->lpr_server);
+ del_iplist(hostptr->name_server);
+ del_iplist(hostptr->rlp_server);
+ del_iplist(hostptr->time_server);
+ 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_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;
+{
+ if (iplist) {
+ if (! (--(iplist->linkcount))) {
+ free((char *) iplist);
+ }
+ }
+}
+
+
+
+/*
+ * Decrements the linkcount on a string data structure. If the count
+ * goes to zero, the memory associated with the string is freed. Does
+ * nothing if the passed pointer is NULL.
+ */
+
+PRIVATE void del_string(stringptr)
+struct shared_string *stringptr;
+{
+ if (stringptr) {
+ if (! (--(stringptr->linkcount))) {
+ free((char *) stringptr);
+ }
+ }
+}
+
+
+
+/*
+ * Decrements the linkcount on a shared_bindata data structure. If the
+ * count goes to zero, the memory associated with the data is freed. Does
+ * nothing if the passed pointer is NULL.
+ */
+
+PRIVATE void del_bindata(dataptr)
+struct shared_bindata *dataptr;
+{
+ if (dataptr) {
+ if (! (--(dataptr->linkcount))) {
+ free((char *) dataptr);
+ }
+ }
+}
+
+
+
+
+/* smalloc() -- safe malloc()
+ *
+ * Always returns a valid pointer (if it returns at all). The allocated
+ * memory is initialized to all zeros. If malloc() returns an error, a
+ * message is printed using the report() function and the program aborts
+ * with a status of 1.
+ */
+
+PRIVATE char *smalloc(nbytes)
+unsigned nbytes;
+{
+ char *retvalue;
+
+ retvalue = malloc(nbytes);
+ if (!retvalue) {
+ report(LOG_ERR, "malloc() failure -- exiting\n");
+ exit(1);
+ }
+ bzero(retvalue, nbytes);
+ return retvalue;
+}