diff options
| author | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 |
|---|---|---|
| committer | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 |
| commit | a16f65c7d117419bd266c28a1901ef129a337569 (patch) | |
| tree | 2626602f66dc3551e7a7c7bc9ad763c3bc7ab40a /libexec/bootpd | |
| parent | 8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (diff) | |
Diffstat (limited to 'libexec/bootpd')
| -rw-r--r-- | libexec/bootpd/Makefile | 20 | ||||
| -rw-r--r-- | libexec/bootpd/README | 102 | ||||
| -rw-r--r-- | libexec/bootpd/Version.c | 48 | ||||
| -rw-r--r-- | libexec/bootpd/announce | 46 | ||||
| -rw-r--r-- | libexec/bootpd/announce.2.1 | 37 | ||||
| -rw-r--r-- | libexec/bootpd/announce.2.2 | 51 | ||||
| -rw-r--r-- | libexec/bootpd/bootp.h | 137 | ||||
| -rw-r--r-- | libexec/bootpd/bootpd.8 | 164 | ||||
| -rw-r--r-- | libexec/bootpd/bootpd.c | 1729 | ||||
| -rw-r--r-- | libexec/bootpd/bootpd.h | 228 | ||||
| -rw-r--r-- | libexec/bootpd/bootptab | 125 | ||||
| -rw-r--r-- | libexec/bootpd/bootptab.5 | 404 | ||||
| -rw-r--r-- | libexec/bootpd/hash.c | 386 | ||||
| -rw-r--r-- | libexec/bootpd/hash.h | 137 | ||||
| -rw-r--r-- | libexec/bootpd/newvers.sh | 24 | ||||
| -rw-r--r-- | libexec/bootpd/readfile.c | 2128 |
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; +} |
