summaryrefslogtreecommitdiffstats
path: root/libexec
diff options
context:
space:
mode:
Diffstat (limited to 'libexec')
-rw-r--r--libexec/Makefile23
-rw-r--r--libexec/Makefile.inc3
-rw-r--r--libexec/atrun/LEGAL29
-rw-r--r--libexec/atrun/Makefile26
-rw-r--r--libexec/atrun/atrun.c479
-rw-r--r--libexec/atrun/atrun.man66
-rw-r--r--libexec/atrun/gloadavg.c69
-rw-r--r--libexec/atrun/gloadavg.h29
-rw-r--r--libexec/bootpd/Announce63
-rw-r--r--libexec/bootpd/Changes245
-rwxr-xr-xlibexec/bootpd/ConvOldTab.sh141
-rw-r--r--libexec/bootpd/Installation29
-rw-r--r--libexec/bootpd/Makefile4
-rw-r--r--libexec/bootpd/Makefile.UNIX184
-rw-r--r--libexec/bootpd/Problems47
-rw-r--r--libexec/bootpd/README133
-rw-r--r--libexec/bootpd/bootp.h147
-rw-r--r--libexec/bootpd/bootpd.8305
-rw-r--r--libexec/bootpd/bootpd.c1380
-rw-r--r--libexec/bootpd/bootpd.h211
-rw-r--r--libexec/bootpd/bootptab.5395
-rw-r--r--libexec/bootpd/bootptab.cmu124
-rw-r--r--libexec/bootpd/bootptab.mcs92
-rw-r--r--libexec/bootpd/bptypes.h23
-rw-r--r--libexec/bootpd/dovend.c413
-rw-r--r--libexec/bootpd/dovend.h13
-rw-r--r--libexec/bootpd/dumptab.c382
-rw-r--r--libexec/bootpd/getether.c374
-rw-r--r--libexec/bootpd/getif.c145
-rw-r--r--libexec/bootpd/getif.h7
-rw-r--r--libexec/bootpd/hash.c425
-rw-r--r--libexec/bootpd/hash.h158
-rw-r--r--libexec/bootpd/hwaddr.c294
-rw-r--r--libexec/bootpd/hwaddr.h39
-rw-r--r--libexec/bootpd/lookup.c126
-rw-r--r--libexec/bootpd/lookup.h15
-rw-r--r--libexec/bootpd/patchlevel.h3
-rw-r--r--libexec/bootpd/readfile.c2097
-rw-r--r--libexec/bootpd/readfile.h19
-rw-r--r--libexec/bootpd/report.c154
-rw-r--r--libexec/bootpd/report.h13
-rw-r--r--libexec/bootpd/rtmsg.c30
-rw-r--r--libexec/bootpd/syslog.conf63
-rw-r--r--libexec/bootpd/tools/bootpef/bootpef.852
-rw-r--r--libexec/bootpd/tools/bootpef/bootpef.c347
-rw-r--r--libexec/bootpd/tools/bootptest/Makefile4
-rw-r--r--libexec/bootpd/tools/bootptest/bootptest.874
-rw-r--r--libexec/bootpd/tools/bootptest/bootptest.c500
-rw-r--r--libexec/bootpd/tools/bootptest/bootptest.h30
-rw-r--r--libexec/bootpd/tools/bootptest/print-bootp.c493
-rw-r--r--libexec/bootpd/trygetea.c46
-rw-r--r--libexec/bootpd/trygetif.c68
-rw-r--r--libexec/bootpd/trylook.c50
-rw-r--r--libexec/bootpd/tzone.c44
-rw-r--r--libexec/bootpd/tzone.h3
-rw-r--r--libexec/comsat/Makefile6
-rw-r--r--libexec/comsat/comsat.8100
-rw-r--r--libexec/comsat/comsat.c311
-rw-r--r--libexec/fingerd/Makefile6
-rw-r--r--libexec/fingerd/fingerd.8139
-rw-r--r--libexec/fingerd/fingerd.c207
-rw-r--r--libexec/fingerd/pathnames.h36
-rw-r--r--libexec/ftpd/Makefile15
-rw-r--r--libexec/ftpd/extern.h67
-rw-r--r--libexec/ftpd/ftpcmd.y1267
-rw-r--r--libexec/ftpd/ftpd.8310
-rw-r--r--libexec/ftpd/ftpd.c1856
-rw-r--r--libexec/ftpd/logwtmp.c75
-rw-r--r--libexec/ftpd/pathnames.h40
-rw-r--r--libexec/ftpd/popen.c171
-rw-r--r--libexec/ftpd/skey-stuff.c24
-rw-r--r--libexec/getNAME/Makefile6
-rw-r--r--libexec/getNAME/getNAME.c339
-rw-r--r--libexec/getty/Makefile8
-rw-r--r--libexec/getty/getty.88
-rw-r--r--libexec/getty/gettytab.510
-rw-r--r--libexec/getty/gettytab.c339
-rw-r--r--libexec/getty/gettytab.h14
-rw-r--r--libexec/getty/init.c6
-rw-r--r--libexec/getty/main.c179
-rw-r--r--libexec/getty/pathnames.h6
-rw-r--r--libexec/getty/subr.c163
-rw-r--r--libexec/getty/ttys.58
-rw-r--r--libexec/lfs_cleanerd/Makefile10
-rw-r--r--libexec/lfs_cleanerd/clean.h168
-rw-r--r--libexec/lfs_cleanerd/cleanerd.c500
-rw-r--r--libexec/lfs_cleanerd/lfs_cleanerd.877
-rw-r--r--libexec/lfs_cleanerd/library.c671
-rw-r--r--libexec/lfs_cleanerd/misc.c94
-rw-r--r--libexec/lfs_cleanerd/print.c218
-rw-r--r--libexec/mail.local/Makefile12
-rw-r--r--libexec/mail.local/mail.local.8105
-rw-r--r--libexec/mail.local/mail.local.c513
-rw-r--r--libexec/mail.local/pathnames.h37
-rw-r--r--libexec/makekey/Makefile9
-rw-r--r--libexec/makekey/makekey.859
-rw-r--r--libexec/makekey/makekey.c82
-rw-r--r--libexec/rbootd/Makefile7
-rw-r--r--libexec/rbootd/bpf.c2
-rw-r--r--libexec/rbootd/parseconf.c2
-rw-r--r--libexec/rbootd/rbootd.c2
-rw-r--r--libexec/revnetgroup/Makefile8
-rw-r--r--libexec/revnetgroup/hash.c210
-rw-r--r--libexec/revnetgroup/hash.h59
-rw-r--r--libexec/revnetgroup/parse_netgroup.c370
-rw-r--r--libexec/revnetgroup/revnetgroup.8137
-rw-r--r--libexec/revnetgroup/revnetgroup.c167
-rw-r--r--libexec/rexecd/Makefile10
-rw-r--r--libexec/rexecd/rexecd.8152
-rw-r--r--libexec/rexecd/rexecd.c310
-rw-r--r--libexec/rlogind/Makefile17
-rw-r--r--libexec/rlogind/pathnames.h38
-rw-r--r--libexec/rlogind/rlogind.8171
-rw-r--r--libexec/rlogind/rlogind.c772
-rw-r--r--libexec/rpc.rstatd/Makefile10
-rw-r--r--libexec/rpc.rstatd/rpc.rstatd.861
-rw-r--r--libexec/rpc.rstatd/rstat_proc.c459
-rw-r--r--libexec/rpc.rstatd/rstatd.c118
-rw-r--r--libexec/rpc.rusersd/Makefile16
-rw-r--r--libexec/rpc.rusersd/rpc.rusersd.864
-rw-r--r--libexec/rpc.rusersd/rusers_proc.c390
-rw-r--r--libexec/rpc.rusersd/rusersd.c109
-rw-r--r--libexec/rpc.rwalld/Makefile10
-rw-r--r--libexec/rpc.rwalld/rpc.rwalld.867
-rw-r--r--libexec/rpc.rwalld/rwalld.c210
-rw-r--r--libexec/rshd/Makefile16
-rw-r--r--libexec/rshd/rshd.8209
-rw-r--r--libexec/rshd/rshd.c785
-rw-r--r--libexec/rtld-aout/Makefile22
-rw-r--r--libexec/rtld-aout/i386/md-static-funcs.c16
-rw-r--r--libexec/rtld-aout/i386/md.c360
-rw-r--r--libexec/rtld-aout/i386/md.h226
-rw-r--r--libexec/rtld-aout/i386/mdprologue.S93
-rw-r--r--libexec/rtld-aout/md-prologue.c39
-rw-r--r--libexec/rtld-aout/rtld.1144
-rw-r--r--libexec/rtld-aout/rtld.1aout144
-rw-r--r--libexec/rtld-aout/rtld.c1691
-rw-r--r--libexec/rtld-aout/shlib.c279
-rw-r--r--libexec/rtld-elf/rtld.1144
-rw-r--r--libexec/talkd/Makefile8
-rw-r--r--libexec/talkd/announce.c159
-rw-r--r--libexec/talkd/print.c86
-rw-r--r--libexec/talkd/process.c227
-rw-r--r--libexec/talkd/table.c233
-rw-r--r--libexec/talkd/talkd.875
-rw-r--r--libexec/talkd/talkd.c128
-rw-r--r--libexec/telnetd/Makefile18
-rw-r--r--libexec/telnetd/defs.h296
-rw-r--r--libexec/telnetd/ext.h235
-rw-r--r--libexec/telnetd/global.c48
-rw-r--r--libexec/telnetd/pathnames.h55
-rw-r--r--libexec/telnetd/slc.c493
-rw-r--r--libexec/telnetd/state.c1546
-rw-r--r--libexec/telnetd/sys_term.c2195
-rw-r--r--libexec/telnetd/telnetd.8605
-rw-r--r--libexec/telnetd/telnetd.c1535
-rw-r--r--libexec/telnetd/telnetd.h49
-rw-r--r--libexec/telnetd/termstat.c650
-rw-r--r--libexec/telnetd/utility.c1071
-rw-r--r--libexec/tftpd/Makefile9
-rw-r--r--libexec/tftpd/tftpd.8106
-rw-r--r--libexec/tftpd/tftpd.c650
-rw-r--r--libexec/uucpd/Makefile7
-rw-r--r--libexec/uucpd/pathnames.h38
-rw-r--r--libexec/uucpd/uucpd.c264
-rw-r--r--libexec/xtend/Makefile13
-rw-r--r--libexec/xtend/packet.c317
-rw-r--r--libexec/xtend/paths.h11
-rw-r--r--libexec/xtend/status.c109
-rw-r--r--libexec/xtend/user.c166
-rw-r--r--libexec/xtend/xten.h58
-rw-r--r--libexec/xtend/xtend.8174
-rw-r--r--libexec/xtend/xtend.c368
-rw-r--r--libexec/xtend/xtend.h77
-rw-r--r--libexec/ypxfr/Makefile26
-rw-r--r--libexec/ypxfr/yp_dbwrite.c105
-rw-r--r--libexec/ypxfr/ypxfr.8207
-rw-r--r--libexec/ypxfr/ypxfr_extern.h57
-rw-r--r--libexec/ypxfr/ypxfr_getmap.c99
-rw-r--r--libexec/ypxfr/ypxfr_main.c490
-rw-r--r--libexec/ypxfr/ypxfr_misc.c240
181 files changed, 40170 insertions, 248 deletions
diff --git a/libexec/Makefile b/libexec/Makefile
new file mode 100644
index 0000000..3a1d910
--- /dev/null
+++ b/libexec/Makefile
@@ -0,0 +1,23 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+
+SUBDIR= atrun bootpd bootpgw comsat fingerd ftpd getNAME getty lfs_cleanerd \
+ mail.local makekey revnetgroup rexecd rlogind rpc.rstatd rpc.rusersd \
+ rpc.rwalld rshd talkd tftpd uucpd xtend
+
+.if !exists(../secure) || defined(NOSECURE)
+SUBDIR+=telnetd
+.else
+SUBDIR+= ../secure/libexec/telnetd
+.endif
+
+# Present but disabled: kpasswdd
+
+.if ${MACHINE} == "hp300"
+SUBDIR+=rbootd
+.elif ${MACHINE} == "i386"
+SUBDIR+=rbootd
+.elif ${MACHINE} == "luna68k"
+SUBDIR+=rbootd
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/libexec/Makefile.inc b/libexec/Makefile.inc
new file mode 100644
index 0000000..f9922b6
--- /dev/null
+++ b/libexec/Makefile.inc
@@ -0,0 +1,3 @@
+# @(#)Makefile.inc 8.1 (Berkeley) 6/4/93
+
+BINDIR?= /usr/libexec
diff --git a/libexec/atrun/LEGAL b/libexec/atrun/LEGAL
new file mode 100644
index 0000000..92b1b49
--- /dev/null
+++ b/libexec/atrun/LEGAL
@@ -0,0 +1,29 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+
+Sorry for the long wait, but there still were a few things to
+be ironed out in at, which I've finally done :-)
+
+The FreeBSD team does have my permission to use at, version 2.9,
+under the BSD license.
+
+You'll find it on sunsite.unc.edu's Incoming, hopefully; the
+md5 checksum is
+
+3ba2ca3c0e87e1a04feae2c6c1376b0d at-2.9.tgz
+
+Best regards
+ Thomas
+- --
+Thomas Koenig, Thomas.Koenig@ciw.uni-karlsruhe.de, ig25@dkauni2.bitnet.
+The joy of engineering is to find a straight line on a double
+logarithmic diagram.
+
+-----BEGIN PGP SIGNATURE-----
+Version: 2.6.2i
+
+iQCVAwUBMCjVrPBu+cbJcKCVAQFNiQP/dpWP57s/E8plVGUD3zfgOXDmKUvg8U7a
+VwRzJrIMuSgnSJs0wkpvcomc3NLicipfX7hhWLh/xatPM2YbF7O5HZoNdvWvexD2
+1Y67zJ+0HFb1mPnSBOrS5RFiQAe3KqmGec6E14Rih/qNoFQZBVRFXZ4xxuwP+0Rs
+e2U+TVTUz6A=
+=TvyW
+-----END PGP SIGNATURE-----
diff --git a/libexec/atrun/Makefile b/libexec/atrun/Makefile
new file mode 100644
index 0000000..5e47abf
--- /dev/null
+++ b/libexec/atrun/Makefile
@@ -0,0 +1,26 @@
+# $Id: Makefile,v 1.3 1995/08/21 12:34:16 ache Exp $
+
+MAINSRC= ${.CURDIR}/../../usr.bin/at
+
+.include "$(MAINSRC)/Makefile.inc"
+
+PROG= atrun
+MAN8= atrun.8
+SRCS= atrun.c gloadavg.c
+
+BINDIR= $(ATLIB_DIR)
+MANSRC= .
+CLEANFILES += ${MAN8}
+MANDEPEND = ${MAN8}
+
+CFLAGS+= -I$(MAINSRC) -I${.CURDIR}
+
+${MAN8}: atrun.man
+ sed -e \
+ "s@_ATSPOOL_DIR@$(ATSPOOL_DIR)@g; \
+ s@_ATJOB_DIR@$(ATJOB_DIR)@g; \
+ s@_ATLIB_DIR@$(ATLIB_DIR)@g; \
+ s@_LOADAVG_MX@$(LOADAVG_MX)@g;" \
+ < $? > $@
+
+.include <bsd.prog.mk>
diff --git a/libexec/atrun/atrun.c b/libexec/atrun/atrun.c
new file mode 100644
index 0000000..8ff1bfa
--- /dev/null
+++ b/libexec/atrun/atrun.c
@@ -0,0 +1,479 @@
+/*
+ * atrun.c - run jobs queued by at; run with root privileges.
+ * Copyright (C) 1993, 1994 Thomas Koenig
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* System Headers */
+
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <syslog.h>
+#ifdef __FreeBSD__
+#include <paths.h>
+#else
+#include <getopt.h>
+#endif
+
+/* Local headers */
+
+#include "gloadavg.h"
+#define MAIN
+#include "privs.h"
+
+/* Macros */
+
+#ifndef ATJOB_DIR
+#define ATJOB_DIR "/usr/spool/atjobs/"
+#endif
+
+#ifndef ATSPOOL_DIR
+#define ATSPOOL_DIR "/usr/spool/atspool/"
+#endif
+
+#ifndef LOADAVG_MX
+#define LOADAVG_MX 1.5
+#endif
+
+/* File scope variables */
+
+static char *namep;
+static char rcsid[] = "$Id: atrun.c,v 1.5 1995/08/21 12:34:17 ache Exp $";
+static debug = 0;
+
+void perr(const char *a);
+
+/* Local functions */
+static int
+write_string(int fd, const char* a)
+{
+ return write(fd, a, strlen(a));
+}
+
+#undef DEBUG_FORK
+#ifdef DEBUG_FORK
+static pid_t
+myfork()
+{
+ pid_t res;
+ res = fork();
+ if (res == 0)
+ kill(getpid(),SIGSTOP);
+ return res;
+}
+
+#define fork myfork
+#endif
+
+static void
+run_file(const char *filename, uid_t uid, gid_t gid)
+{
+/* Run a file by by spawning off a process which redirects I/O,
+ * spawns a subshell, then waits for it to complete and sends
+ * mail to the user.
+ */
+ pid_t pid;
+ int fd_out, fd_in;
+ int queue;
+ char mailbuf[9];
+ char *mailname = NULL;
+ FILE *stream;
+ int send_mail = 0;
+ struct stat buf, lbuf;
+ off_t size;
+ struct passwd *pentry;
+ int fflags;
+ long nuid;
+ long ngid;
+
+
+ PRIV_START
+
+ if (chmod(filename, S_IRUSR) != 0)
+ {
+ perr("Cannot change file permissions");
+ }
+
+ PRIV_END
+
+ pid = fork();
+ if (pid == -1)
+ perr("Cannot fork");
+
+ else if (pid != 0)
+ return;
+
+ /* Let's see who we mail to. Hopefully, we can read it from
+ * the command file; if not, send it to the owner, or, failing that,
+ * to root.
+ */
+
+ pentry = getpwuid(uid);
+ if (pentry == NULL)
+ {
+ syslog(LOG_ERR,"Userid %lu not found - aborting job %s",
+ (unsigned long) uid, filename);
+ exit(EXIT_FAILURE);
+ }
+ PRIV_START
+
+ stream=fopen(filename, "r");
+
+ PRIV_END
+
+#ifdef __FreeBSD__
+ if (pentry->pw_expire && time(NULL) >= pentry->pw_expire)
+ {
+ syslog(LOG_ERR, "Userid %lu is expired - aborting job %s",
+ (unsigned long) uid, filename);
+ exit(EXIT_FAILURE);
+ }
+#endif
+
+ if (stream == NULL)
+ perr("Cannot open input file");
+
+ if ((fd_in = dup(fileno(stream))) <0)
+ perr("Error duplicating input file descriptor");
+
+ if (fstat(fd_in, &buf) == -1)
+ perr("Error in fstat of input file descriptor");
+
+ if (lstat(filename, &lbuf) == -1)
+ perr("Error in fstat of input file");
+
+ if (S_ISLNK(lbuf.st_mode)) {
+ syslog(LOG_ERR,"Symbolic link encountered in job %s - aborting",
+ filename);
+ exit(EXIT_FAILURE);
+ }
+ if ((lbuf.st_dev != buf.st_dev) || (lbuf.st_ino != buf.st_ino) ||
+ (lbuf.st_uid != buf.st_uid) || (lbuf.st_gid != buf.st_gid) ||
+ (lbuf.st_size!=buf.st_size)) {
+ syslog(LOG_ERR,"Somebody changed files from under us for job %s - "
+ "aborting",filename);
+ exit(EXIT_FAILURE);
+ }
+ if (buf.st_nlink > 1) {
+ syslog(LOG_ERR,"Someboy is trying to run a linked script for job %s",
+ filename);
+ exit(EXIT_FAILURE);
+ }
+ if ((fflags = fcntl(fd_in, F_GETFD)) <0)
+ perr("Error in fcntl");
+
+ fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC);
+
+ if (fscanf(stream, "#!/bin/sh\n# atrun uid=%ld gid=%ld\n# mail %8s %d",
+ &nuid, &ngid, mailbuf, &send_mail) != 4)
+ {
+ syslog(LOG_ERR,"File %s is in wrong format - aborting",
+ filename);
+ exit(EXIT_FAILURE);
+ }
+ if (mailbuf[0] == '-') {
+ syslog(LOG_ERR,"illegal mail name %s in %s",mailbuf,filename);
+ exit(EXIT_FAILURE);
+ }
+ mailname = mailbuf;
+ if (nuid != uid) {
+ syslog(LOG_ERR,"Job %s - userid %d does not match file uid %d",
+ filename, nuid, uid);
+ exit(EXIT_FAILURE);
+ }
+ if (ngid != gid) {
+ syslog(LOG_ERR,"Job %s - groupid %d does not match file gid %d",
+ filename, ngid, gid);
+ exit(EXIT_FAILURE);
+ }
+ fclose(stream);
+ if (chdir(ATSPOOL_DIR) < 0)
+ perr("Cannot chdir to " ATSPOOL_DIR);
+
+ /* Create a file to hold the output of the job we are about to run.
+ * Write the mail header.
+ */
+ if((fd_out=open(filename,
+ O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) < 0)
+ perr("Cannot create output file");
+
+ write_string(fd_out, "Subject: Output from your job ");
+ write_string(fd_out, filename);
+ write_string(fd_out, "\n\n");
+ fstat(fd_out, &buf);
+ size = buf.st_size;
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ pid = fork();
+ if (pid < 0)
+ perr("Error in fork");
+
+ else if (pid == 0)
+ {
+ char *nul = NULL;
+ char **nenvp = &nul;
+
+ /* Set up things for the child; we want standard input from the input file,
+ * and standard output and error sent to our output file.
+ */
+
+ if (lseek(fd_in, (off_t) 0, SEEK_SET) < 0)
+ perr("Error in lseek");
+
+ if (dup(fd_in) != STDIN_FILENO)
+ perr("Error in I/O redirection");
+
+ if (dup(fd_out) != STDOUT_FILENO)
+ perr("Error in I/O redirection");
+
+ if (dup(fd_out) != STDERR_FILENO)
+ perr("Error in I/O redirection");
+
+ close(fd_in);
+ close(fd_out);
+ if (chdir(ATJOB_DIR) < 0)
+ perr("Cannot chdir to " ATJOB_DIR);
+
+ queue = *filename;
+
+ PRIV_START
+
+ nice(tolower(queue) - 'a');
+
+ if (chdir(pentry->pw_dir))
+ chdir("/");
+
+ if (initgroups(pentry->pw_name,pentry->pw_gid))
+ perr("Cannot delete saved userids");
+
+ if (setgid(gid) < 0)
+ perr("Cannot change group");
+
+ if (setuid(uid) < 0)
+ perr("Cannot set user id");
+
+ if(execle("/bin/sh","sh",(char *) NULL, nenvp) != 0)
+ perr("Exec failed for /bin/sh");
+
+ PRIV_END
+ }
+ /* We're the parent. Let's wait.
+ */
+ close(fd_in);
+ close(fd_out);
+ waitpid(pid, (int *) NULL, 0);
+
+ /* Send mail. Unlink the output file first, so it is deleted after
+ * the run.
+ */
+ stat(filename, &buf);
+ if (open(filename, O_RDONLY) != STDIN_FILENO)
+ perr("Open of jobfile failed");
+
+ unlink(filename);
+ if ((buf.st_size != size) || send_mail)
+ {
+ PRIV_START
+
+ if (chdir(pentry->pw_dir))
+ chdir("/");
+
+ if (initgroups(pentry->pw_name,pentry->pw_gid))
+ perr("Cannot delete saved userids");
+
+ if (setgid(gid) < 0)
+ perr("Cannot change group");
+
+ if (setuid(uid) < 0)
+ perr("Cannot set user id");
+
+#ifdef __FreeBSD__
+ execl(_PATH_SENDMAIL, "sendmail", "-F", "Atrun Service",
+ "-odi", "-oem",
+ mailname, (char *) NULL);
+#else
+ execl(MAIL_CMD, MAIL_CMD, mailname, (char *) NULL);
+#endif
+ perr("Exec failed for mail command");
+
+ PRIV_END
+ }
+ exit(EXIT_SUCCESS);
+}
+
+/* Global functions */
+
+/* Needed in gloadavg.c */
+void
+perr(const char *a)
+{
+ if (debug)
+ {
+ perror(a);
+ }
+ else
+ syslog(LOG_ERR, "%s: %m", a);
+
+ exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char *argv[])
+{
+/* Browse through ATJOB_DIR, checking all the jobfiles wether they should
+ * be executed and or deleted. The queue is coded into the first byte of
+ * the job filename, the date (in minutes since Eon) as a hex number in the
+ * following eight bytes, followed by a dot and a serial number. A file
+ * which has not been executed yet is denoted by its execute - bit set.
+ * For those files which are to be executed, run_file() is called, which forks
+ * off a child which takes care of I/O redirection, forks off another child
+ * for execution and yet another one, optionally, for sending mail.
+ * Files which already have run are removed during the next invocation.
+ */
+ DIR *spool;
+ struct dirent *dirent;
+ struct stat buf;
+ unsigned long ctm;
+ unsigned long jobno;
+ char queue;
+ time_t now, run_time;
+ char batch_name[] = "Z2345678901234";
+ uid_t batch_uid;
+ gid_t batch_gid;
+ int c;
+ int run_batch;
+ double load_avg = LOADAVG_MX;
+
+/* We don't need root privileges all the time; running under uid and gid daemon
+ * is fine.
+ */
+
+ RELINQUISH_PRIVS_ROOT(DAEMON_UID, DAEMON_GID)
+
+ openlog("atrun", LOG_PID, LOG_CRON);
+
+ opterr = 0;
+ errno = 0;
+ while((c=getopt(argc, argv, "dl:"))!= EOF)
+ {
+ switch (c)
+ {
+ case 'l':
+ if (sscanf(optarg, "%lf", &load_avg) != 1)
+ perr("garbled option -l");
+ if (load_avg <= 0.)
+ load_avg = LOADAVG_MX;
+ break;
+
+ case 'd':
+ debug ++;
+ break;
+
+ case '?':
+ perr("unknown option");
+ break;
+
+ default:
+ perr("idiotic option - aborted");
+ break;
+ }
+ }
+
+ namep = argv[0];
+ if (chdir(ATJOB_DIR) != 0)
+ perr("Cannot change to " ATJOB_DIR);
+
+ /* Main loop. Open spool directory for reading and look over all the
+ * files in there. If the filename indicates that the job should be run
+ * and the x bit is set, fork off a child which sets its user and group
+ * id to that of the files and exec a /bin/sh which executes the shell
+ * script. Unlink older files if they should no longer be run. For
+ * deletion, their r bit has to be turned on.
+ *
+ * Also, pick the oldest batch job to run, at most one per invocation of
+ * atrun.
+ */
+ if ((spool = opendir(".")) == NULL)
+ perr("Cannot read " ATJOB_DIR);
+
+ now = time(NULL);
+ run_batch = 0;
+ batch_uid = (uid_t) -1;
+ batch_gid = (gid_t) -1;
+
+ while ((dirent = readdir(spool)) != NULL) {
+ if (stat(dirent->d_name,&buf) != 0)
+ perr("Cannot stat in " ATJOB_DIR);
+
+ /* We don't want directories
+ */
+ if (!S_ISREG(buf.st_mode))
+ continue;
+
+ if (sscanf(dirent->d_name,"%c%5lx%8lx",&queue,&jobno,&ctm) != 3)
+ continue;
+
+ run_time = (time_t) ctm*60;
+
+ if ((S_IXUSR & buf.st_mode) && (run_time <=now)) {
+ if (isupper(queue) && (strcmp(batch_name,dirent->d_name) > 0)) {
+ run_batch = 1;
+ strncpy(batch_name, dirent->d_name, sizeof(batch_name));
+ batch_uid = buf.st_uid;
+ batch_gid = buf.st_gid;
+ }
+
+ /* The file is executable and old enough
+ */
+ if (islower(queue))
+ run_file(dirent->d_name, buf.st_uid, buf.st_gid);
+ }
+ /* Delete older files
+ */
+ if ((run_time < now) && !(S_IXUSR & buf.st_mode) && (S_IRUSR & buf.st_mode))
+ unlink(dirent->d_name);
+ }
+ /* run the single batch file, if any
+ */
+ if (run_batch && (gloadavg() < load_avg))
+ run_file(batch_name, batch_uid, batch_gid);
+
+ closelog();
+ exit(EXIT_SUCCESS);
+}
diff --git a/libexec/atrun/atrun.man b/libexec/atrun/atrun.man
new file mode 100644
index 0000000..abce667
--- /dev/null
+++ b/libexec/atrun/atrun.man
@@ -0,0 +1,66 @@
+.\" $Id: atrun.man,v 1.1 1994/05/10 18:23:08 kernel Exp $
+.Dd April 12, 1995
+.Dt ATRUN 8
+.Os "FreeBSD 2.1"
+.Sh NAME
+.Nm atrun
+.Nd run jobs queued for later execution
+.Sh SYNOPSIS
+.Nm atrun
+.Op Fl l Ar load_avg
+.Op Fl d
+.Sh DESCRIPTION
+.Nm Atrun
+runs jobs queued by
+.Xr at 1 .
+Root's
+.Xr crontab 5
+file
+.Pa /etc/crontab
+has to contain the line
+.nf
+*/5 * * * * root _ATLIB_DIR/atrun
+.fi
+so
+.Xr atrun 8
+gets called every five minutes.
+.Pp
+At every invocation, every job in lowercase queues whose starting time
+has passed is started.
+A maximum of one batch jobs (denoted by uppercase queues) are started
+each time
+.Nm atrun
+is invoked.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl l Ar load_avg
+Specifies a limiting load factor, over which batch jobs should
+not be run, instead of the compiled \- in value of _LOADAVG_MX.
+.It Fl d
+Debug; print error messages to standard error instead of using
+.Xr syslog 3 .
+.El
+.Sh WARNINGS
+For
+.Nm atrun
+to work, you have to start up a
+.Xr cron 8
+daemon.
+.Sh FILES
+.Bl -tag -width _ATSPOOL_DIR -compact
+.It Pa _ATSPOOL_DIR
+Directory containing output spool files
+.It Pa _ATJOB_DIR
+Directory containing job files
+.El
+.Sh SEE ALSO
+.Xr cron 8 ,
+.Xr crontab 1 ,
+.Xr crontab 5 ,
+.Xr at 1 ,
+.Xr syslog 3 .
+.Sh BUGS
+The functionality of
+.Nm atrun
+should be merged into
+.Xr cron 8 .
diff --git a/libexec/atrun/gloadavg.c b/libexec/atrun/gloadavg.c
new file mode 100644
index 0000000..6e996f0
--- /dev/null
+++ b/libexec/atrun/gloadavg.c
@@ -0,0 +1,69 @@
+/*
+ * gloadavg.c - get load average for Linux
+ * Copyright (C) 1993 Thomas Koenig
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __FreeBSD__
+#define _POSIX_SOURCE 1
+
+/* System Headers */
+
+#include <stdio.h>
+#else
+#include <stdlib.h>
+#endif
+
+/* Local headers */
+
+#include "gloadavg.h"
+
+/* File scope variables */
+
+static char rcsid[] = "$Id: gloadavg.c,v 1.2 1995/08/10 04:06:54 ache Exp $";
+
+/* Global functions */
+
+double
+gloadavg(void)
+/* return the current load average as a floating point number, or <0 for
+ * error
+ */
+{
+ double result;
+#ifndef __FreeBSD__
+ FILE *fp;
+
+ if((fp=fopen(PROC_DIR "loadavg","r")) == NULL)
+ result = -1.0;
+ else
+ {
+ if(fscanf(fp,"%lf",&result) != 1)
+ result = -1.0;
+ fclose(fp);
+ }
+#else
+ if (getloadavg(&result, 1) != 1)
+ perr("Error in getloadavg");
+#endif
+ return result;
+}
diff --git a/libexec/atrun/gloadavg.h b/libexec/atrun/gloadavg.h
new file mode 100644
index 0000000..c7e2c2c
--- /dev/null
+++ b/libexec/atrun/gloadavg.h
@@ -0,0 +1,29 @@
+/*
+ * gloadavg.h - header for atrun(8)
+ * Copyright (C) 1993 Thomas Koenig
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+double gloadavg(void);
+#if 0
+static char atrun_h_rcsid[] = "$Id: gloadavg.h,v 1.2 1995/08/10 04:06:55 ache Exp $";
+#endif
diff --git a/libexec/bootpd/Announce b/libexec/bootpd/Announce
new file mode 100644
index 0000000..e4ae04c
--- /dev/null
+++ b/libexec/bootpd/Announce
@@ -0,0 +1,63 @@
+
+This is an enhanced version of the CMU BOOTP server which was derived
+from the original BOOTP server created by Bill Croft at Stanford.
+This version merges most of the enhancements and bug-fixes from the
+NetBSD, Columbia, and other versions.
+
+New features in version 2.4 include:
+
+ Added a simple BOOTP gateway program: bootpgw
+ Allow host name anywhere IP address is expected.
+ Automatically lookup the IP address when the name of a
+ bootptab entry is a valid hostname.
+ (Dummy entries names should start with '.')
+ Merged changes from NetBSD and Columbia versions.
+ Merged changes for Solaris-2.X and SVR4 systems.
+ Combined bootptest into the bootp release.
+ Merged tag 18 support (:ef=...:) from Jason Zions.
+ Use :ef=extension_file_name: and make the
+ extension files for all clients using bootpef.
+ Merged HP compatibility (:ra=...:) from David R Linn.
+ Allows you to override the reply address.
+ (i.e. send the reply to a broadcast address)
+ Add /etc/ethers support for NetBSD.
+ More systems support getether (Ultrix, OSF, NetBSD)
+ Added RFC 1533 tags 40,41,42
+ :yd=<NIS domain>:ys=<NIS server>:nt=<NTP server>:
+ ConvOldTab.sh to convert old (1.1) bootptab to new format.
+ Permits extended-length replies with more option data.
+
+Problems fixed in this version:
+
+ Fixed references to free host structures.
+ (used to cause core dump on Solaris)
+ Remove change that added null terminator to string options.
+ (this annoyed some clients...)
+ Add missing symbols to dump routine, fix order.
+ Works (again) with no -DSYSLOGD defined.
+ Fixed several more NULL references in readfile.
+ Added proper length checks to option insertions.
+ Fixed bootptest IP address printing.
+ Cleaned-up signed/unsigned and byteorder bugs.
+ Added SVR4/Streams support to getif and getether
+ Removed extra newlines in syslog messages.
+ Specify facility code when calling syslog(3)
+ When lookup_hwa fails, assume numeric HW address.
+
+Systems on which I have seen this code work:
+ SunOS 4.X (Solaris 1.X)
+ SunOS 5.X (Solaris 2.X)
+ System V/386 Rel. 4.0
+
+Systems on which others say this code works:
+ CDC EP/IX (1.4.3, 2.1.1)
+ DEC Ultrix (4.2, 4.3)
+ NetBSD (Current-8/94)
+ OSF/1 (DEC Alpha CPU)
+
+Please direct questions, comments, and bug reports to:
+ <bootp@andrew.cmu.edu>
+
+Gordon W. Ross Mercury Computer Systems
+gwr@mc.com 199 Riverneck Road
+508-256-1300 Chelmsford, MA 01824-2820
diff --git a/libexec/bootpd/Changes b/libexec/bootpd/Changes
new file mode 100644
index 0000000..0616548
--- /dev/null
+++ b/libexec/bootpd/Changes
@@ -0,0 +1,245 @@
+Changes, most recent first
+Date, <email> Real Name
+ what...
+
+--> bootp-2.4.0
+
+08/20/94 gwr@mc.com (Gordon W. Ross)
+ Fix code to build bootfile name based on combination of
+ client requested name and bootfile specifications.
+ Behave similarly with or without CHECK_FILE_ACCESS.
+
+07/30/94 Dirk Koeppen <dirk@incom.de>
+ Add "min wait" option (mw) to cause bootpd to ignore
+ requests from clients that have not waited long enough.
+ Add code to honor client requests containing the DHCP
+ option "Maximum Message Size" and use its value to
+ determine the size of the reply message.
+
+--> bootp-2.3.8
+
+06/25/94 Christos Zoulas <christos@deshaw.com>
+ Add "-h" flag to override host name (affects default IP
+ address provided in reply messages. (Also minor bug fix)
+
+05/27/94 gwr@mc.com (Gordon W. Ross)
+ Add code to call "arp -s IPADDR HWADDR" on systems
+ that do not provide an SIOCSARP ioctl (i.e. NetBSD)
+
+--> bootp-2.3.7
+
+05/05/94 Walter Wong <wcw+@CMU.EDU>
+ Reduce noize at debug level one, where log messages
+ are generated only for hosts that are recognized
+ and replied to by bootpd. (At request of HP folks.)
+
+04/30/94 gwr@mc.com (Gordon W. Ross)
+ Use memxxx functions unless USE_BFUNCS is defined.
+ Added -f <file> option to bootptest (requested file).
+
+04/29/94 tpaquett@ita.lgc.com (Trevor Paquette)
+ Remove call to haddr_conv802() in sendreply().
+ The setarp should get the non-transformed address.
+
+04/27/94 gwr@mc.com
+ Improve logic for building bootfile pathname, so a path
+ will be put in the reply if either the client or bootpd
+ specifies a boot file. (Needed for NetBSD diskless boot)
+
+04/25/94 shamash@boxhill.com (Ari Shamash)
+ Fix prs_inetaddr() so it allows '_' in hostnames.
+
+04/16/94 gwr@mc.com (Gordon W. Ross)
+ Fix setarp for SVR4 (needs to use I_STR ioctl)
+ Thanks to several people: (all sent the same fix)
+ Barney Wolff <barney@databus.com>,
+ bear@upsys.se (Bj|rn Sj|holm),
+ Michael Kuschke <Michael.Kuschke@Materna.DE>,
+
+03/25/95 Ulrich Heuer </I=zhhi9/G=Ulrich/S=Heuer/@zhflur.ubs.ubs.ch>
+ Make option string lengths not include a null terminator.
+ The trailing null breaks some clients.
+
+03/15/94 "Edmund J. Sutcliffe" <ejs1@tower.york.ac.uk>
+ Add support for the "EX" option: Execute a program
+ before sending a BOOTREPLY to a client. Support for
+ this option is conditional on YORK_EX_OPTION.
+
+03/10/94 Nigel Metheringham <nigelm@ohm.york.ac.uk>
+ Make getether.c work on Linux.
+
+03/09/94 Koch@Math.Uni-Duisburg.DE (Peter Koch)
+ Add missing MANDIR definition to Makefile.
+
+03/08/94 Jeroen.Scheerder@let.ruu.nl
+ Fix args to report in getether code for Ultrix.
+ Run install individually for each program.
+
+--> bootp-2.3.6
+03/07/94 gwr@mc.com
+ Cleanup for release (run gnu indent, tab-size=4)
+
+02/24/94 Jeroen.Scheerder@let.ruu.nl
+ Allow underscore in host names - readfile.c:goodname()
+ Add ConvOldTab.sh - converts 1.1 bootptab to new format.
+
+02/20/94 gwr@mc.com (Gordon W. Ross)
+ Make readfile tolerant of hardware addresses that start
+ with a letter. (If lookup_hwa() fails, assume numeric.)
+ Fix whitespace skip before :vm= auto: and avoid lookup.
+
+02/12/94 walker@zk3.dec.com (Mary Walker)
+ Added support for 64-bit longs (for the DEC Alpha)
+ Allow ieee802 hardware address in bit-reversed oreder
+
+02/07/94 hl@tekla.fi (Harald Lundberg)
+ Fix conflict with DUMP_FILE in syslog.h on OSF1
+ Use int for (struct bootp).bp_xid (for DEC Alpha)
+ Added Ultrix support to bootptest (getether)
+
+02/06/94 brezak@ch.hp.com (John Brezak)
+ Add man-page and install targets to Makefile.NetBSD
+ Add getether support for NetBSD
+
+02/05/94 gwr@mc.com (Gordon W. Ross)
+ Added tags 40,41,42 (NIS domain, NIS server, NTP server)
+ Add stub to getether for machines not yet supported.
+
+--> bootp-2.3.5
+01/29/94 gwr@mc.com (Gordon W. Ross)
+ Make bootpgw put a correct address in "giaddr" when
+ the client request came via broadcast.
+
+01/22/94 gwr@mc.com (Gordon W. Ross)
+ Fix syslog call (missing "facility" code)
+ Add SVR4/Streams support to getif() and getether()
+ Fix getif bug (matched when it should not)
+ Macro-ize lots of similar cases in readfile.c
+
+12/27/93 brezak@ch.hp.com (John Brezak)
+ Remove all newlines passed to syslog(3)
+ Add /etc/ethers support for NetBSD.
+
+12/18/93 gwr@mc.com (Gordon W. Ross)
+ Fix bootptest IP address printing.
+ Fix byte-order bugs in bootpgw and bootptest.
+ Clean-up signed/unsigned mismatches.
+ Back out SLIP support changes for now
+ (code fragment saved in ToDo).
+
+--> bootp-2.3.4 (beta test release)
+12/12/93 gwr@mc.com (Gordon W. Ross)
+ Fixed several more NULL references in readfile.
+ Added proper length checks to option insertions.
+
+--> bootp-2.3.3 (beta test release)
+12/09/93 gwr@mc.com (Gordon W. Ross)
+ Added ASSERT checks to readfile.c:fill_defaults()
+
+12/08/93 brezak@ch.hp.com (John Brezak)
+ New Makefile.NetBSD
+ Added setsid() and #ifdef TIOCNOTTY
+ (bootpd.c, bootpgw.c)
+ Moved #include <net/if.h> out of #ifdef SUNOS
+ Fixed several multiple declaration problems
+
+12/04/93 gwr@mc.com (Gordon W. Ross)
+ Re-implemented Extension File support
+ based on work by Jason Zions <jazz@hal.com>
+ Added support for Reply-Address-Override to support
+ HP clients (need reply sent to broadcast address)
+ from David R. Linn <drl@vuse.vanderbilt.edu>
+
+--> bootp-2.3.2 (beta test release)
+11/27/93 gwr@mc.com (Gordon W. Ross)
+ Incorporated bootptest into the bootp release.
+ Added ANSI function prototypes everywhere.
+
+11/17/93 dpm@depend.com (David P. Maynard)
+ Added automatic SLIP address determination.
+ (This is NOT dynamic IP address assignment.)
+ Cleaned up some type warnings from gcc.
+
+11/11/93 gwr@mc.com (Gordon W. Ross)
+ Works (again) with no -DSYSLOGD defined.
+ Provide a default value for the subnet mask.
+ More #ifdef's for SunOS specific code (lookup_hwa)
+ Added a simple BOOTP gateway program: bootpgw
+ Reorganized for more code sharing (with bootpgw)
+
+--> bootp-2.3.1 (alpha test release)
+11/08/93 gwr@mc.com (Gordon W. Ross)
+ Back-out changes to honor option structure in request
+ (this needs to be a per-client option).
+ Merged changes from NetBSD and Columbia versions.
+ Allow host name anywhere IP address is expected.
+ Add null terminators to option strings.
+ Add missing symbols to dump routine, dump symbols
+ in alphabetical order, one tag per line.
+
+--> bootp-2.2.D (posted as patch 2)
+10/19/93 gwr@mc.com (Gordon W. Ross)
+ Fix references to free memory (leads to core dumps).
+
+--> bootp-2.2.C (posted as patch 1)
+10/14/93 gwr@mc.com (Gordon W. Ross)
+ Fix data access alignment problems on SPARC/Solaris.
+
+--> bootp-2.2.B (posted to usenet)
+10/11/93 gwr@mc.com (Gordon W. Ross)
+ Allow extended-length BOOTP packets (more vendor options)
+ Honor option format specified in client requests.
+ Added Solaris-2.X changes from db@sunbim.be (Danny Backx).
+
+All history before this point may be inaccurate. Please send
+changes if any of the credits are incorrect. -gwr
+
+--> bootp-2.2+NetBSD released
+08/27/93 brezak@ch.hp.com (John Brezak)
+ Added RFC 1396 support (tags 14-17)
+
+--> bootp-2.2+NetBSD (version?)
+??/??/93 mckim@lerc.nasa.gov (Jim McKim)
+ Ported to NetBSD (see Makefile.NetBSD)
+ Set server host name in responses.
+ Check all interfaces in address match routine.
+
+--> bootp-2.2+FdC released
+01/27/93 <fdc@watsun.cc.columbia.edu> Frank da Cruz
+ Added RFC 1395 information: Merit dump file,
+ client domain name, swap server address, root path.
+
+--> bootp-2.2alpha released
+11/14/91 <walt+@cmu.edu> Walter L. Wimer
+ Add "td" to TFTP directory for "secure" (chroot) TFTP.
+ Add "sa" tag to set explicit server address.
+ Automatically determine if child of inetd.
+ Use RFC 1048 format when request has magic number zero.
+ Fixed various bugs. Give bootptab a separate man page.
+
+--> bootp-2.1 released
+01/09/89 <walt+@cmu.edu> Walter L. Wimer
+ Check world read bit on TFTP boot file.
+ Add support for rfc1085 "bootfile size" tag.
+ Add generic tags. Fix byte order of rfc1048 data.
+ Fix various crashing bugs.
+
+--> bootp-2.0 released
+07/15/88 <walt+@cmu.edu> Walter L. Wimer
+ 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.
+
+--> bootp-1.3(?) released
+07/24/87 <ddp@andrew.cmu.edu> Drew D. Perkins
+ Modified to use syslog instead of Kovar's
+ routines. Add debugging dumps. Many other fixups.
+
+--> bootp-1.2(?) released
+07/30/86 David Kovar at Carnegie Mellon University
+ Modified to work at CMU.
+
+--> bootp-1.1 released
+01/22/86 Bill Croft at Stanford University
+ Original created.
diff --git a/libexec/bootpd/ConvOldTab.sh b/libexec/bootpd/ConvOldTab.sh
new file mode 100755
index 0000000..00683f0
--- /dev/null
+++ b/libexec/bootpd/ConvOldTab.sh
@@ -0,0 +1,141 @@
+#!/bin/sh
+# convert_bootptab Jeroen.Scheerder@let.ruu.nl 02/25/94
+# This script can be used to convert bootptab files in old format
+# to new (termcap-like) bootptab files
+#
+# The old format - real entries are commented out by '###'
+#
+# Old-style bootp files consist of two sections.
+# The first section has two entries:
+# First, a line that specifies the home directory
+# (where boot file paths are relative to)
+
+###/tftpboot
+
+# The next non-empty non-comment line specifies the default bootfile
+
+###no-file
+
+# End of first section - indicated by '%%' at the start of the line
+
+###%%
+
+# The remainder of this file contains one line per client
+# interface with the information shown by the table headings
+# below. The host name is also tried as a suffix for the
+# bootfile when searching the home directory (that is,
+# bootfile.host)
+#
+# Note that htype is always 1, indicating the hardware type Ethernet.
+# Conversion therefore always yields ':ha=ether:'.
+#
+# host htype haddr iaddr bootfile
+#
+
+###somehost 1 00:0b:ad:01:de:ad 128.128.128.128 dummy
+
+# That's all for the description of the old format.
+# For the new-and-improved format, see bootptab(5).
+
+set -u$DX
+
+case $#
+in 2 ) OLDTAB=$1 ; NEWTAB=$2 ;;
+ * ) echo "Usage: `basename $0` <Input> <Output>"
+ exit 1
+esac
+
+if [ ! -r $OLDTAB ]
+then
+ echo "`basename $0`: $OLDTAB does not exist or is unreadable."
+ exit 1
+fi
+
+if touch $NEWTAB 2> /dev/null
+then
+ :
+else
+ echo "`basename $0`: cannot write to $NEWTAB."
+ exit 1
+fi
+
+
+cat << END_OF_HEADER >> $NEWTAB
+# /etc/bootptab: database for bootp server (/etc/bootpd)
+# This file was generated automagically
+
+# Blank lines and lines beginning with '#' are ignored.
+#
+# Legend: (see bootptab.5)
+# first field -- hostname (not indented)
+# bf -- bootfile
+# bs -- bootfile size in 512-octet blocks
+# cs -- cookie servers
+# df -- dump file name
+# dn -- domain name
+# ds -- domain name servers
+# ef -- extension file
+# gw -- gateways
+# ha -- hardware address
+# hd -- home directory for bootfiles
+# hn -- host name set for client
+# ht -- hardware type
+# im -- impress servers
+# ip -- host IP address
+# lg -- log servers
+# lp -- LPR servers
+# ns -- IEN-116 name servers
+# ra -- reply address
+# rl -- resource location protocol servers
+# rp -- root path
+# sa -- boot server address
+# sm -- subnet mask
+# sw -- swap server
+# tc -- template host (points to similar host entry)
+# td -- TFTP directory
+# to -- time offset (seconds)
+# ts -- time servers
+# vm -- vendor magic number
+# Tn -- generic option tag n
+#
+# Be careful about including backslashes where they're needed. Weird (bad)
+# things can happen when a backslash is omitted where one is intended.
+# Also, note that generic option data must be either a string or a
+# sequence of bytes where each byte is a two-digit hex value.
+
+# First, we define a global entry which specifies the stuff every host uses.
+# (Host name lookups are relative to the domain: your.domain.name)
+
+END_OF_HEADER
+
+# Fix up HW addresses in aa:bb:cc:dd:ee:ff and aa-bb-cc-dd-ee-ff style first
+# Then awk our stuff together
+sed -e 's/[:-]//g' < $OLDTAB | \
+nawk 'BEGIN { PART = 0 ; FIELD=0 ; BOOTPATH="unset" ; BOOTFILE="unset" }
+ /^%%/ {
+ PART = 1
+ printf ".default:\\\n\t:ht=ether:\\\n\t:hn:\\\n\t:dn=your.domain.name:\\\n\t:ds=your,dns,servers:\\\n\t:sm=255.255.0.0:\\\n\t:hd=%s:\\\n\t:rp=%s:\\\n\t:td=%s:\\\n\t:bf=%s:\\\n\t:to=auto:\n\n", BOOTPATH, BOOTPATH, BOOTPATH, BOOTFILE
+ next
+ }
+ /^$/ { next }
+ /^#/ { next }
+ {
+ if ( PART == 0 && FIELD < 2 )
+ {
+ if ( FIELD == 0 ) BOOTPATH=$1
+ if ( FIELD == 1 ) BOOTFILE=$1
+ FIELD++
+ }
+ }
+ {
+ if ( PART == 1 )
+ {
+ HOST=$1
+ HA=$3
+ IP=$4
+ BF=$5
+ printf "%s:\\\n\t:tc=.default:\\\n\t:ha=0x%s:\\\n\t:ip=%s:\\\n\t:bf=%s:\n", HOST, HA, IP, BF
+ }
+ }' >> $NEWTAB
+
+exit 0
diff --git a/libexec/bootpd/Installation b/libexec/bootpd/Installation
new file mode 100644
index 0000000..466cabc
--- /dev/null
+++ b/libexec/bootpd/Installation
@@ -0,0 +1,29 @@
+
+Installation instructions for SunOS
+
+Compile the executable:
+For SunOS 4.X:
+ make sunos4
+For SunOS 5.X: (Solaris)
+ make sunos5
+
+Install the executables:
+
+ make install
+
+Edit (or create) the bootptab:
+(See bootptab.sample and bootptab.5 manual entry)
+ edit /etc/bootptab
+
+Edit /etc/services to add these two lines:
+bootps 67/udp bootp # BOOTP Server
+bootpc 68/udp # BOOTP Client
+
+Edit /etc/inetd.conf to add the line:
+bootp dgram udp wait root /usr/etc/bootpd bootpd -i
+
+If you compiled report.c with LOG_LOCAL2 (defined in the Makefile)
+then you may want to capture syslog messages from BOOTP by changing
+your syslog.conf file. (See the sample syslog.conf file here).
+Test the change with: logger -t test -p local2.info "message"
+
diff --git a/libexec/bootpd/Makefile b/libexec/bootpd/Makefile
index de4dfdb..e923968 100644
--- a/libexec/bootpd/Makefile
+++ b/libexec/bootpd/Makefile
@@ -1,8 +1,8 @@
# bootpd/Makefile
-# $Id: Makefile,v 1.2 1994/09/10 14:59:55 csgr Exp $
+# $Id: Makefile,v 1.2 1995/05/30 05:45:45 rgrimes Exp $
PROG= bootpd
-#CFLAGS+= -DETC_ETHERS
+CFLAGS+= -DETC_ETHERS
CFLAGS+= -DSYSLOG -DDEBUG -DVEND_CMU
SUBDIR= tools
diff --git a/libexec/bootpd/Makefile.UNIX b/libexec/bootpd/Makefile.UNIX
new file mode 100644
index 0000000..e333ce5
--- /dev/null
+++ b/libexec/bootpd/Makefile.UNIX
@@ -0,0 +1,184 @@
+#
+# Makefile for the BOOTP programs:
+# bootpd - BOOTP server daemon
+# bootpef - BOOTP extension file builder
+# bootpgw - BOOTP gateway daemon
+# bootptest - BOOTP tester (client)
+#
+
+# OPTion DEFinitions:
+# Remove the -DVEND_CMU if you don't wish to support the "CMU vendor format"
+# in addition to the RFC1048 format. Leaving out DEBUG saves little.
+OPTDEFS= -DSYSLOG -DVEND_CMU -DDEBUG
+
+# Uncomment and edit this to choose the facility code used for syslog.
+# LOG_FACILITY= "-DLOG_BOOTP=LOG_LOCAL2"
+
+# SYStem DEFinitions:
+# Either uncomment some of the following, or do:
+# "make sunos4" (or "make sunos5", etc.)
+# SYSDEFS= -DSUNOS -DETC_ETHERS
+# SYSDEFS= -DSVR4
+# SYSLIBS= -lsocket -lnsl
+
+# Uncomment this if your system does not provide streror(3)
+# STRERROR=strerror.o
+
+# FILE DEFinitions:
+# The next few lines may be uncommented and changed to alter the default
+# filenames bootpd uses for its configuration and dump files.
+#CONFFILE= -DCONFIG_FILE=\"/usr/etc/bootptab\"
+#DUMPFILE= -DDUMPTAB_FILE=\"/usr/etc/bootpd.dump\"
+#FILEDEFS= $(CONFFILE) $(DUMPFILE)
+
+# MORE DEFinitions (whatever you might want to add)
+# One might define NDEBUG (to remove "assert()" checks).
+MOREDEFS=
+
+INSTALL=/usr/bin/install
+DESTDIR=
+BINDIR=/usr/etc
+MANDIR=/usr/local/man
+
+CFLAGS= $(OPTDEFS) $(SYSDEFS) $(FILEDEFS) $(MOREDEFS)
+PROGS= bootpd bootpef bootpgw bootptest
+TESTS= trylook trygetif trygetea
+
+all: $(PROGS)
+
+tests: $(TESTS)
+
+system: install
+
+install: $(PROGS)
+ -for f in $(PROGS) ;\
+ do \
+ $(INSTALL) -c -s $$f $(DESTDIR)$(BINDIR) ;\
+ done
+
+MAN5= bootptab.5
+MAN8= bootpd.8 bootpef.8 bootptest.8
+install.man: $(MAN5) $(MAN8)
+ -for f in $(MAN5) ;\
+ do \
+ $(INSTALL) -c -m 644 $$f $(DESTDIR)$(MANDIR)/man5 ;\
+ done
+ -for f in $(MAN8) ;\
+ do \
+ $(INSTALL) -c -m 644 $$f $(DESTDIR)$(MANDIR)/man8 ;\
+ done
+
+clean:
+ -rm -f core *.o
+ -rm -f $(PROGS) $(TESTS)
+
+distclean:
+ -rm -f *.BAK *.CKP *~ .emacs*
+
+#
+# Handy targets for individual systems:
+#
+
+# DEC/OSF1 on the Alpha
+alpha:
+ $(MAKE) SYSDEFS="-DETC_ETHERS -Dint32=int -D_SOCKADDR_LEN" \
+ STRERROR=strerror.o
+
+# Control Data EP/IX 1.4.3 system, BSD 4.3 mode
+epix143:
+ $(MAKE) CC="cc -systype bsd43" \
+ SYSDEFS="-Dconst= -D_SIZE_T -DNO_UNISTD -DUSE_BFUNCS" \
+ STRERROR=strerror.o
+
+# Control Data EP/IX 2.1.1 system, SVR4 mode
+epix211:
+ $(MAKE) CC="cc -systype svr4" \
+ SYSDEFS="-DSVR4" \
+ SYSLIBS="-lsocket -lnsl"
+
+# Silicon Graphics IRIX (no <sys/sockio.h>, so not SVR4)
+irix:
+ $(MAKE) SYSDEFS="-DSYSV -DIRIX"
+
+# SunOS 4.X
+sunos4:
+ $(MAKE) SYSDEFS="-DSUNOS -DETC_ETHERS" \
+ STRERROR=strerror.o
+
+# Solaris 2.X (i.e. SunOS 5.X)
+sunos5:
+ $(MAKE) SYSDEFS="-DSVR4 -DETC_ETHERS" \
+ SYSLIBS="-lsocket -lnsl"
+
+# UNIX System V Rel. 4 (also: IRIX 5.X, others)
+svr4:
+ $(MAKE) SYSDEFS="-DSVR4" \
+ SYSLIBS="-lsocket -lnsl"
+
+#
+# How to build each program:
+#
+
+OBJ_D= bootpd.o dovend.o readfile.o hash.o dumptab.o \
+ lookup.o getif.o hwaddr.o tzone.o report.o $(STRERROR)
+bootpd: $(OBJ_D)
+ $(CC) -o $@ $(OBJ_D) $(SYSLIBS)
+
+OBJ_EF= bootpef.o dovend.o readfile.o hash.o dumptab.o \
+ lookup.o hwaddr.o tzone.o report.o $(STRERROR)
+bootpef: $(OBJ_EF)
+ $(CC) -o $@ $(OBJ_EF) $(SYSLIBS)
+
+OBJ_GW= bootpgw.o getif.o hwaddr.o report.o $(STRERROR)
+bootpgw: $(OBJ_GW)
+ $(CC) -o $@ $(OBJ_GW) $(SYSLIBS)
+
+OBJ_TEST= bootptest.o print-bootp.o getif.o getether.o \
+ report.o $(STRERROR)
+bootptest: $(OBJ_TEST)
+ $(CC) -o $@ $(OBJ_TEST) $(SYSLIBS)
+
+# This is just for testing the lookup functions.
+TRYLOOK= trylook.o lookup.o report.o $(STRERROR)
+trylook : $(TRYLOOK)
+ $(CC) -o $@ $(TRYLOOK) $(SYSLIBS)
+
+# This is just for testing getif.
+TRYGETIF= trygetif.o getif.o report.o $(STRERROR)
+trygetif : $(TRYGETIF)
+ $(CC) -o $@ $(TRYGETIF) $(SYSLIBS)
+
+# This is just for testing getether.
+TRYGETEA= trygetea.o getether.o report.o $(STRERROR)
+trygetea : $(TRYGETEA)
+ $(CC) -o $@ $(TRYGETEA) $(SYSLIBS)
+
+# This rule just keeps the LOG_BOOTP define localized.
+report.o : report.c
+ $(CC) $(CFLAGS) $(LOG_FACILITY) -c $<
+
+# Punt SunOS -target noise
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+#
+# Header file dependencies:
+#
+
+bootpd.o : bootp.h bptypes.h hash.h hwaddr.h bootpd.h dovend.h
+bootpd.o : readfile.h report.h tzone.h patchlevel.h getif.h
+bootpef.o : bootp.h bptypes.h hash.h hwaddr.h bootpd.h dovend.h
+bootpef.o : readfile.h report.h tzone.h patchlevel.h
+bootpgw.o : bootp.h bptypes.h getif.h hwaddr.h report.h patchlevel.h
+bootptest.o : bootp.h bptypes.h bootptest.h getif.h patchlevel.h
+dovend.o : bootp.h bptypes.h bootpd.h hash.h hwaddr.h report.h dovend.h
+dumptab.o : bootp.h bptypes.h hash.h hwaddr.h report.h patchlevel.h bootpd.h
+getif.o : getif.h report.h
+hash.o : hash.h
+hwaddr.o : bptypes.h hwaddr.h report.h
+lookup.o : bootp.h bptypes.h lookup.h report.h
+print-bootp.o : bootp.h bptypes.h bootptest.h
+readfile.o : bootp.h bptypes.h hash.h hwaddr.h lookup.h readfile.h
+readfile.o : report.h tzone.h bootpd.h
+report.o : report.h
+tzone.o : bptypes.h report.h tzone.h
diff --git a/libexec/bootpd/Problems b/libexec/bootpd/Problems
new file mode 100644
index 0000000..9478676
--- /dev/null
+++ b/libexec/bootpd/Problems
@@ -0,0 +1,47 @@
+
+Common problems and ways to work around them:
+
+Bootpd complains that it "can not get IP addr for HOSTNAME"
+
+ If the entry is a "dummy" (not a real host) used only for
+ reference by other entries, put '.' in front of the name.
+
+ If the entry is for a real client and the IP address for
+ the client can not be found using gethostbyname(), specify
+ the IP address for the client using numeric form.
+
+Bootpd takes a long time to finish parsing the bootptab file:
+
+ Excessive startup time is usually caused by waiting for
+ timeouts on failed DNS lookup operations. If this is the
+ problem, find the client names for which DNS lookup fails
+ and change the bootptab to specify the IP addresses for
+ those clients using numeric form.
+
+ When bootptab entries do not specify an ip address, bootpd
+ attempts to lookup the tagname as a host name to find the
+ IP address. To suppress this default action, either make
+ the entry a "dummy" or specify its IP numeric address.
+
+ If your DNS lookups work but are just slow, consider either
+ running bootpd on the same machine as the DNS server or
+ running a caching DNS server on the host running bootpd.
+
+My huge bootptab file causes startup time to be so long that clients
+give up waiting for a reply.
+
+ Truly huge bootptab files make "inetd" mode impractical.
+ Start bootpd in "standalone" mode when the server boots.
+
+ Another possibility is to run one bootpd on each network
+ segment so each one can have a smaller bootptab. Only one
+ instance of bootpd may run on one server, so you would need
+ to use a different server for each network segment.
+
+My bootp clients are given responses with a boot file name that is
+not a fully specified path.
+
+ Make sure the TFTP directory or home directory tags are set:
+ :td=/tftpboot: (or)
+ :hd=/usr/boot: (for example)
+
diff --git a/libexec/bootpd/README b/libexec/bootpd/README
new file mode 100644
index 0000000..c7755b7
--- /dev/null
+++ b/libexec/bootpd/README
@@ -0,0 +1,133 @@
+
+This is an enhanced version of the CMU BOOTP server which was derived
+from the original BOOTP server created by Bill Croft at Stanford.
+This version merges all the enhancements and bug-fixes from the
+NetBSD, Columbia, and other versions.
+
+Please direct questions, comments, and bug reports to the list:
+ <bootp@andrew.cmu.edu>
+
+You can subscribe to this mailing list by sending mail to:
+ bootp-request@andrew.cmu.edu
+(The body of the message should contain: "Add <your-address>")
+
+[ From the NetBSD README file: ]
+
+BOOTPD is a useful adjunct to the nfs diskless boot EPROM code.
+
+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.
+
+[ From the CMU README file: ]
+
+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 # BOOTP Server
+ bootpc 68/udp # BOOTP Client
+
+4) Edit the bootptab. It has some explanitory comments, and there
+ is a manual entry describing its format (bootptab.5)
+ If you have any questions, just let us know.
+
+Construction:
+ [ See the file Installation which is more up-to-date. -gwr ]
+
+ Make sure all of the files exist first. If anything is missing,
+ please contact either Walt Wimer or Drew Perkins by E-mail or phone.
+ Addresses and phone numbers are listed below.
+
+ 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. The following is a sample /etc/inetd.conf entry:
+ # BOOTP server
+ bootps dgram udp wait root /usr/etc/bootpd bootpd -i
+
+Care and feeding:
+ If you change the interface cards on your host or add new hosts you will
+ 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
+
+[ Contents description by file: ]
+
+Announce* Text of release announcements
+Changes Change history, reverse chronological
+Installation Instructions for building and installing
+Makefile* for "make"
+README This file
+ToDo Things not yet done
+bootp.h The protocol header file
+bootpd.8 Manual page for bootpd, boopgw
+bootpd.c BOOTP server main module
+bootpd.h header for above (and others)
+bootpef.8 Manual page for bootpef
+bootpef.c BOOTP extension file compiler
+bootpgw.c BOOTP gateway main module
+bootptab.5 A manual describing the bootptab format
+bootptab.cmu A sample database file for the server
+bootptab.mcs Another sample from <gwr@mc.com>
+bootptest.8 Manual page for bootptest
+bootptest.c BOOTP test program (fake client)
+bootptest.h header for above
+dovend.c Vendor Option builder (for bootpd, bootpef)
+dovend.h header for above
+dumptab.c Implements debugging dump for bootpd
+getether.c For bootptest (not used yet)
+getif.c Get network interface info.
+getif.h header for above
+hash.c The hash table module
+hash.h header for above
+hwaddr.c Hardware address support
+hwaddr.h header for above
+lookup.c Internet Protocol address lookup
+lookup.h header for above
+patchlevel.h Holds version numbers
+print-bootp.c Prints BOOTP packets (taken from BSD tcpdump)
+readfile.c The configuration file-reading routines
+readfile.h header for above
+report.c Does syslog-style messages
+report.h header for above
+strerror.c Library errno-to-string (for systems lacking it)
+syslog.conf Sample config file for syslogd(8)
+syslog.h For systems that lack syslog(3)
+try*.c Test programs (for debugging)
+tzone.c Get timezone offset
+tzone.h header for above
diff --git a/libexec/bootpd/bootp.h b/libexec/bootpd/bootp.h
new file mode 100644
index 0000000..0651aa5
--- /dev/null
+++ b/libexec/bootpd/bootp.h
@@ -0,0 +1,147 @@
+/************************************************************************
+ 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.
+************************************************************************/
+
+/*
+ * Bootstrap Protocol (BOOTP). RFC951 and RFC1395.
+ *
+ * $Id: bootp.h,v 1.1.1.1 1994/09/10 14:44:54 csgr Exp $
+ *
+ *
+ * This file specifies the "implementation-independent" BOOTP protocol
+ * information which is common to both client and server.
+ *
+ */
+
+#include "bptypes.h" /* for int32, u_int32 */
+
+#define BP_CHADDR_LEN 16
+#define BP_SNAME_LEN 64
+#define BP_FILE_LEN 128
+#define BP_VEND_LEN 64
+#define BP_MINPKTSZ 300 /* to check sizeof(struct bootp) */
+
+struct bootp {
+ unsigned char bp_op; /* packet opcode type */
+ unsigned char bp_htype; /* hardware addr type */
+ unsigned char bp_hlen; /* hardware addr length */
+ unsigned char bp_hops; /* gateway hops */
+ unsigned int32 bp_xid; /* transaction ID */
+ unsigned short bp_secs; /* seconds since boot began */
+ unsigned short bp_flags; /* RFC1532 broadcast, etc. */
+ 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 */
+ char bp_sname[BP_SNAME_LEN]; /* server host name */
+ char bp_file[BP_FILE_LEN]; /* boot file name */
+ unsigned char bp_vend[BP_VEND_LEN]; /* vendor-specific area */
+ /* note that bp_vend can be longer, extending to end of packet. */
+};
+
+/*
+ * 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_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 }
+
+
+
+/*
+ * Tag values used to specify what information is being supplied in
+ * the vendor (options) data area of the packet.
+ */
+/* RFC 1048 */
+#define TAG_END ((unsigned char) 255)
+#define TAG_PAD ((unsigned char) 0)
+#define TAG_SUBNET_MASK ((unsigned char) 1)
+#define TAG_TIME_OFFSET ((unsigned char) 2)
+#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_HOST_NAME ((unsigned char) 12)
+#define TAG_BOOT_SIZE ((unsigned char) 13)
+/* RFC 1395 */
+#define TAG_DUMP_FILE ((unsigned char) 14)
+#define TAG_DOMAIN_NAME ((unsigned char) 15)
+#define TAG_SWAP_SERVER ((unsigned char) 16)
+#define TAG_ROOT_PATH ((unsigned char) 17)
+/* RFC 1497 */
+#define TAG_EXTEN_FILE ((unsigned char) 18)
+/* RFC 1533 */
+#define TAG_NIS_DOMAIN ((unsigned char) 40)
+#define TAG_NIS_SERVER ((unsigned char) 41)
+#define TAG_NTP_SERVER ((unsigned char) 42)
+/* DHCP maximum message size. */
+#define TAG_MAX_MSGSZ ((unsigned char) 57)
+
+/* XXX - Add new tags here */
+
+
+/*
+ * "vendor" data permitted for CMU bootp clients.
+ */
+
+struct cmu_vend {
+ char v_magic[4]; /* magic number */
+ unsigned int32 v_flags; /* flags/opcodes, etc. */
+ struct in_addr v_smask; /* Subnet mask */
+ struct in_addr v_dgate; /* Default gateway */
+ struct in_addr v_dns1, v_dns2; /* Domain name servers */
+ struct in_addr v_ins1, v_ins2; /* IEN-116 name servers */
+ struct in_addr v_ts1, v_ts2; /* Time servers */
+ int32 v_unused[6]; /* 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 0000000..359ce53
--- /dev/null
+++ b/libexec/bootpd/bootpd.8
@@ -0,0 +1,305 @@
+.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University
+.\"
+.\" $Header: /home/ncvs/src/libexec/bootpd/bootpd.8,v 1.1.1.1 1994/09/30 05:45:04 pst Exp $
+.\"
+.TH BOOTPD 8 "November 06, 1993" "Carnegie Mellon University"
+.SH NAME
+bootpd, bootpgw \- Internet Boot Protocol server/gateway
+.SH SYNOPSIS
+.B bootpd
+[
+.B \-i
+.B \-s
+.B \-t
+timeout
+.B \-d
+level
+.B \-c
+chdir\-path
+]
+[
+.I bootptab
+[
+.I dumpfile
+] ]
+.br
+.B bootpgw
+[
+.B \-i
+.B \-s
+.B \-t
+timeout
+.B \-d
+level
+] server
+.SH DESCRIPTION
+.I Bootpd
+implements an Internet Bootstrap Protocol (BOOTP) server as defined in
+RFC951, RFC1532, and RFC1533.
+.I Bootpgw
+implements a simple BOOTP gateway which can be used to forward
+requests and responses between clients on one subnet and a
+BOOTP server (i.e.
+.IR bootpd )
+on another subnet. While either
+.I bootpd
+or
+.I bootpgw
+will forward BOOTREPLY packets, only
+.I bootpgw
+will forward BOOTREQUEST packets.
+.PP
+One host on each network segment is normally configured to run either
+.I bootpd
+or
+.I bootpgw
+from
+.I inetd
+by including one of the following lines in the file
+.IR /etc/inetd.conf :
+.IP
+bootps dgram udp wait root /usr/libexec/bootpd bootpd bootptab
+.br
+bootps dgram udp wait root /usr/libexec/bootpgw bootpgw server
+.PP
+This mode of operation is referred to as "inetd mode" and causes
+.I bootpd
+(or
+.IR bootpgw )
+to be started only when a boot request arrives. If it does not
+receive another packet within fifteen minutes of the last one
+it received, it will exit to conserve system resources. The
+.B \-t
+option controls this timeout (see OPTIONS).
+.PP
+It is also possible to run
+.I bootpd
+(or
+.IR bootpgw )
+in "standalone mode" (without
+.IR inetd )
+by simply invoking it from a shell like any other regular command.
+Standalone mode is particularly useful when
+.I bootpd
+is used with a large configuration database, where the start up
+delay might otherwise prevent timely response to client requests.
+(Automatic start up in standalone mode can be done by invoking
+.I bootpd
+from within
+.IR /etc/rc.local ,
+for example.)
+Standalone mode is less useful for
+.I bootgw
+which
+has very little start up delay because
+it does not read a configuration file.
+.PP
+Either program automatically detects whether it was invoked from inetd
+or from a shell and automatically selects the appropriate mode.
+The
+.B \-s
+or
+.B \-i
+option may be used to force standalone or inetd mode respectively
+(see OPTIONS).
+.SH OPTIONS
+.TP
+.BI \-t \ timeout
+Specifies the
+.I timeout
+value (in minutes) that a
+.I bootpd
+or
+.I bootpgw
+process will wait for a BOOTP packet before exiting.
+If no packets are recieved for
+.I timeout
+seconds, then the program will exit.
+A timeout value of zero means "run forever".
+In standalone mode, this option is forced to zero.
+.TP
+.BI \-d \ debug\-level
+Sets the
+.I debug\-level
+variable that controls the amount of debugging messages generated.
+For example, -d4 or -d 4 will set the debugging level to 4.
+For compatibility with older versions of
+.IR bootpd ,
+omitting the numeric parameter (i.e. just -d) will
+simply increment the debug level by one.
+.TP
+.BI \-c \ chdir\-path
+Sets the current directory used by
+.I bootpd
+while checking the existence and size of client boot files. This is
+useful when client boot files are specified as relative pathnames, and
+.I bootpd
+needs to use the same current directory as the TFTP server
+(typically /tftpboot). This option is not recoginzed by
+.IR bootpgw .
+.TP
+.B \-i
+Force inetd mode. This option is obsolete, but remains for
+compatibility with older versions of
+.IR bootpd .
+.TP
+.B \-s
+Force standalone mode. This option is obsolete, but remains for
+compatibility with older versions of
+.IR bootpd .
+.TP
+.I bootptab
+Specifies the name of the configuration file from which
+.I bootpd
+loads its database of known clients and client options
+.RI ( bootpd
+only).
+.TP
+.I dumpfile
+Specifies the name of the file that
+.I bootpd
+will dump its internal database into when it receives a
+SIGUSR1 signal
+.RI ( bootpd
+only). This option is only recognized if
+.I bootpd
+was compiled with the -DDEBUG flag.
+.TP
+.I server
+Specifies the name of a BOOTP server to which
+.I bootpgw
+will forward all BOOTREQUEST packets it receives
+.RI ( bootpgw
+only).
+.SH OPERATION
+.PP
+Both
+.I bootpd
+and
+.I bootpgw
+operate similarly in that both listen for any packets sent to the
+.I bootps
+port, and both simply forward any BOOTREPLY packets.
+They differ in their handling of BOOTREQUEST packets.
+.PP
+When
+.I bootpgw
+is started, it determines the address of a BOOTP server
+whose name is provided as a command line parameter. When
+.I bootpgw
+receives a BOOTREQUEST packet, it sets the "gateway address"
+and "hop count" fields in the packet and forwards the packet
+to the BOOTP server at the address determined earlier.
+Requests are forwarded only if they indicate that
+the client has been waiting for at least three seconds.
+.PP
+When
+.I bootpd
+is started it reads a configuration file, (normally
+.IR /etc/bootptab )
+that initializes the internal database of known clients and client
+options. This internal database is reloaded
+from the configuration file when
+.I bootpd
+receives a hangup signal (SIGHUP) or when it discovers that the
+configuration file has changed.
+.PP
+When
+.I bootpd
+receives a BOOTREQUEST packet, it
+.\" checks the modification time of the
+.\" configuration file and reloads the database if necessary. Then it
+looks for a database entry matching the client request.
+If the client is known,
+.I bootpd
+composes a BOOTREPLY packet using the database entry found above,
+and sends the reply to the client (possibly using a gateway).
+If the client is unknown, the request is discarded
+(with a notice if debug > 0).
+.PP
+If
+.I bootpd
+is compiled with the -DDEBUG option, receipt of a SIGUSR1 signal causes
+it to dump its internal database to the file
+.I /etc/bootpd.dump
+or the dumpfile specified as a command line parameter.
+.PP
+During initialization, both programs
+determine the UDP port numbers to be used by calling
+.I getservbyname
+(which nomally uses
+.IR /etc/services).
+Two service names (and port numbers) are used:
+.IP
+bootps \- BOOTP Server listening port
+.br
+bootpc \- BOOTP Client destination port
+.LP
+If the port numbers cannot
+be determined using
+.I getservbyname
+then the values default to boopts=67 and bootpc=68.
+.SH FILES
+.TP 20
+/etc/bootptab
+Database file read by
+.IR bootpd .
+.TP
+/etc/bootpd.dump
+Debugging dump file created by
+.IR bootpd .
+.TP
+/etc/services
+Internet service numbers.
+.TP
+/tftpboot
+Current directory typically used by the TFTP server and
+.IR bootpd .
+
+.SH BUGS
+Individual host entries must not exceed 1024 characters.
+
+.SH CREDITS
+.PP
+This distribution is currently maintained by
+Walter L. Wimer <walt+@cmu.edu>.
+.PP
+The original BOOTP server was created by
+Bill Croft at Stanford University in January 1986.
+.PP
+The current version of
+.I bootpd
+is primarily the work of David Kovar,
+Drew D. Perkins, and Walter L. Wimer,
+at Carnegie Mellon University.
+.TP
+Enhancements and bug\-fixes have been contributed by:
+(in alphabetical order)
+.br
+Danny Backx <db@sunbim.be>
+.br
+John Brezak <brezak@ch.hp.com>
+.br
+Frank da Cruz <fdc@cc.columbia.edu>
+.br
+David R. Linn <drl@vuse.vanderbilt.edu>
+.br
+Jim McKim <mckim@lerc.nasa.gov>
+.br
+Gordon W. Ross <gwr@mc.com>
+.br
+Jason Zions <jazz@hal.com>
+.SH "SEE ALSO"
+.LP
+bootptab(5), inetd(8), tftpd(8)
+.LP
+DARPA Internet Request For Comments:
+.TP 10
+RFC951
+Bootstrap Protocol
+.TP 10
+RFC1532
+Clarifications and Extensions for the Bootstrap Protocol
+.TP 10
+RFC1533
+DHCP Options and BOOTP Vendor Extensions
diff --git a/libexec/bootpd/bootpd.c b/libexec/bootpd/bootpd.c
new file mode 100644
index 0000000..cf693fd
--- /dev/null
+++ b/libexec/bootpd/bootpd.c
@@ -0,0 +1,1380 @@
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+
+#ifndef lint
+static char rcsid[] = "$Id: bootpd.c,v 1.1.1.1 1994/09/30 05:45:04 pst Exp $";
+#endif
+
+/*
+ * BOOTP (bootstrap protocol) server daemon.
+ *
+ * Answers BOOTP request packets from booting client machines.
+ * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
+ * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
+ * See RFC 1395 for option tags 14-17.
+ * See accompanying man page -- bootpd.8
+ *
+ * HISTORY
+ * See ./Changes
+ *
+ * BUGS
+ * See ./ToDo
+ */
+
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <assert.h>
+
+#ifdef NO_SETSID
+# include <fcntl.h> /* for O_RDONLY, etc */
+#endif
+
+#ifdef SVR4
+/* Using sigset() avoids the need to re-arm each time. */
+#define signal sigset
+#endif
+
+#ifndef USE_BFUNCS
+# include <memory.h>
+/* Yes, memcpy is OK here (no overlapped copies). */
+# define bcopy(a,b,c) memcpy(b,a,c)
+# define bzero(p,l) memset(p,0,l)
+# define bcmp(a,b,c) memcmp(a,b,c)
+#endif
+
+#include "bootp.h"
+#include "hash.h"
+#include "hwaddr.h"
+#include "bootpd.h"
+#include "dovend.h"
+#include "getif.h"
+#include "readfile.h"
+#include "report.h"
+#include "tzone.h"
+#include "patchlevel.h"
+
+#ifndef CONFIG_FILE
+#define CONFIG_FILE "/etc/bootptab"
+#endif
+#ifndef DUMPTAB_FILE
+#define DUMPTAB_FILE "/tmp/bootpd.dump"
+#endif
+
+
+
+/*
+ * Externals, forward declarations, and global variables
+ */
+
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+extern void dumptab P((char *));
+
+PRIVATE void catcher P((int));
+PRIVATE int chk_access P((char *, int32 *));
+#ifdef VEND_CMU
+PRIVATE void dovend_cmu P((struct bootp *, struct host *));
+#endif
+PRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32));
+PRIVATE void handle_reply P((void));
+PRIVATE void handle_request P((void));
+PRIVATE void sendreply P((int forward, int32 dest_override));
+PRIVATE void usage P((void));
+
+#undef P
+
+/*
+ * IP port numbers for client and server obtained from /etc/services
+ */
+
+u_short bootps_port, bootpc_port;
+
+
+/*
+ * Internet socket and interface config structures
+ */
+
+struct sockaddr_in bind_addr; /* Listening */
+struct sockaddr_in recv_addr; /* Packet source */
+struct sockaddr_in send_addr; /* destination */
+
+
+/*
+ * option defaults
+ */
+int debug = 0; /* Debugging flag (level) */
+struct timeval actualtimeout =
+{ /* fifteen minutes */
+ 15 * 60L, /* tv_sec */
+ 0 /* tv_usec */
+};
+
+/*
+ * General
+ */
+
+int s; /* Socket file descriptor */
+char *pktbuf; /* Receive packet buffer */
+int pktlen;
+char *progname;
+char *chdir_path;
+char hostname[MAXHOSTNAMELEN]; /* System host name */
+struct in_addr my_ip_addr;
+
+/* Flags set by signal catcher. */
+PRIVATE int do_readtab = 0;
+PRIVATE int do_dumptab = 0;
+
+/*
+ * Globals below are associated with the bootp database file (bootptab).
+ */
+
+char *bootptab = CONFIG_FILE;
+char *bootpd_dump = DUMPTAB_FILE;
+
+
+
+/*
+ * Initialization such as command-line processing is done and then the
+ * main server loop is started.
+ */
+
+void
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct timeval *timeout;
+ struct bootp *bp;
+ struct servent *servp;
+ struct hostent *hep;
+ char *stmp;
+ int n, ba_len, ra_len;
+ int nfound, readfds;
+ int standalone;
+
+ progname = strrchr(argv[0], '/');
+ if (progname) progname++;
+ else progname = argv[0];
+
+ /*
+ * Initialize logging.
+ */
+ report_init(0); /* uses progname */
+
+ /*
+ * Log startup
+ */
+ report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
+
+ /* Debugging for compilers with struct padding. */
+ assert(sizeof(struct bootp) == BP_MINPKTSZ);
+
+ /* Get space for receiving packets and composing replies. */
+ pktbuf = malloc(MAX_MSG_SIZE);
+ if (!pktbuf) {
+ report(LOG_ERR, "malloc failed");
+ exit(1);
+ }
+ bp = (struct bootp *) pktbuf;
+
+ /*
+ * Check to see if a socket was passed to us from inetd.
+ *
+ * Use getsockname() to determine if descriptor 0 is indeed a socket
+ * (and thus we are probably a child of inetd) or if it is instead
+ * something else and we are running standalone.
+ */
+ s = 0;
+ ba_len = sizeof(bind_addr);
+ bzero((char *) &bind_addr, ba_len);
+ errno = 0;
+ standalone = TRUE;
+ if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
+ /*
+ * Descriptor 0 is a socket. Assume we are a child of inetd.
+ */
+ if (bind_addr.sin_family == AF_INET) {
+ standalone = FALSE;
+ bootps_port = ntohs(bind_addr.sin_port);
+ } else {
+ /* Some other type of socket? */
+ report(LOG_ERR, "getsockname: not an INET socket");
+ }
+ }
+
+ /*
+ * Set defaults that might be changed by option switches.
+ */
+ stmp = NULL;
+ timeout = &actualtimeout;
+
+ /*
+ * Read switches.
+ */
+ for (argc--, argv++; argc > 0; argc--, argv++) {
+ if (argv[0][0] != '-')
+ break;
+ switch (argv[0][1]) {
+
+ case 'c': /* chdir_path */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (stmp[0] != '/')) {
+ fprintf(stderr,
+ "bootpd: invalid chdir specification\n");
+ break;
+ }
+ chdir_path = stmp;
+ break;
+
+ case 'd': /* debug level */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else if (argv[1] && argv[1][0] == '-') {
+ /*
+ * Backwards-compatible behavior:
+ * no parameter, so just increment the debug flag.
+ */
+ debug++;
+ break;
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
+ fprintf(stderr,
+ "%s: invalid debug level\n", progname);
+ break;
+ }
+ debug = n;
+ break;
+
+ case 'h': /* override hostname */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp) {
+ fprintf(stderr,
+ "bootpd: missing hostname\n");
+ break;
+ }
+ strncpy(hostname, stmp, sizeof(hostname)-1);
+ break;
+
+ case 'i': /* inetd mode */
+ standalone = FALSE;
+ break;
+
+ case 's': /* standalone mode */
+ standalone = TRUE;
+ break;
+
+ case 't': /* timeout */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
+ fprintf(stderr,
+ "%s: invalid timeout specification\n", progname);
+ break;
+ }
+ actualtimeout.tv_sec = (int32) (60 * n);
+ /*
+ * If the actual timeout is zero, pass a NULL pointer
+ * to select so it blocks indefinitely, otherwise,
+ * point to the actual timeout value.
+ */
+ timeout = (n > 0) ? &actualtimeout : NULL;
+ break;
+
+ default:
+ fprintf(stderr, "%s: unknown switch: -%c\n",
+ progname, argv[0][1]);
+ usage();
+ break;
+
+ } /* switch */
+ } /* for args */
+
+ /*
+ * Override default file names if specified on the command line.
+ */
+ if (argc > 0)
+ bootptab = argv[0];
+
+ if (argc > 1)
+ bootpd_dump = argv[1];
+
+ /*
+ * Get my hostname and IP address.
+ */
+ if (hostname[0] == '\0') {
+ if (gethostname(hostname, sizeof(hostname)) == -1) {
+ fprintf(stderr, "bootpd: can't get hostname\n");
+ exit(1);
+ }
+ }
+ hep = gethostbyname(hostname);
+ if (!hep) {
+ fprintf(stderr, "Can not get my IP address\n");
+ exit(1);
+ }
+ bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
+
+ if (standalone) {
+ /*
+ * Go into background and disassociate from controlling terminal.
+ */
+ if (debug < 3) {
+ if (fork())
+ exit(0);
+#ifdef NO_SETSID
+ setpgrp(0,0);
+#ifdef TIOCNOTTY
+ n = open("/dev/tty", O_RDWR);
+ if (n >= 0) {
+ ioctl(n, TIOCNOTTY, (char *) 0);
+ (void) close(n);
+ }
+#endif /* TIOCNOTTY */
+#else /* SETSID */
+ if (setsid() < 0)
+ perror("setsid");
+#endif /* SETSID */
+ } /* if debug < 3 */
+
+ /*
+ * Nuke any timeout value
+ */
+ timeout = NULL;
+
+ } /* if standalone (1st) */
+
+ /* Set the cwd (i.e. to /tftpboot) */
+ if (chdir_path) {
+ if (chdir(chdir_path) < 0)
+ report(LOG_ERR, "%s: chdir failed", chdir_path);
+ }
+
+ /* Get the timezone. */
+ tzone_init();
+
+ /* Allocate hash tables. */
+ rdtab_init();
+
+ /*
+ * Read the bootptab file.
+ */
+ readtab(1); /* force read */
+
+ if (standalone) {
+
+ /*
+ * Create a socket.
+ */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ report(LOG_ERR, "socket: %s", get_network_errmsg());
+ exit(1);
+ }
+
+ /*
+ * Get server's listening port number
+ */
+ servp = getservbyname("bootps", "udp");
+ if (servp) {
+ bootps_port = ntohs((u_short) servp->s_port);
+ } else {
+ bootps_port = (u_short) IPPORT_BOOTPS;
+ report(LOG_ERR,
+ "udp/bootps: unknown service -- assuming port %d",
+ bootps_port);
+ }
+
+ /*
+ * Bind socket to BOOTPS port.
+ */
+ bind_addr.sin_family = AF_INET;
+ bind_addr.sin_addr.s_addr = INADDR_ANY;
+ bind_addr.sin_port = htons(bootps_port);
+ if (bind(s, (struct sockaddr *) &bind_addr,
+ sizeof(bind_addr)) < 0)
+ {
+ report(LOG_ERR, "bind: %s", get_network_errmsg());
+ exit(1);
+ }
+ } /* if standalone (2nd)*/
+
+ /*
+ * Get destination port number so we can reply to client
+ */
+ servp = getservbyname("bootpc", "udp");
+ if (servp) {
+ bootpc_port = ntohs(servp->s_port);
+ } else {
+ report(LOG_ERR,
+ "udp/bootpc: unknown service -- assuming port %d",
+ IPPORT_BOOTPC);
+ bootpc_port = (u_short) IPPORT_BOOTPC;
+ }
+
+ /*
+ * Set up signals to read or dump the table.
+ */
+ if ((int) signal(SIGHUP, catcher) < 0) {
+ report(LOG_ERR, "signal: %s", get_errmsg());
+ exit(1);
+ }
+ if ((int) signal(SIGUSR1, catcher) < 0) {
+ report(LOG_ERR, "signal: %s", get_errmsg());
+ exit(1);
+ }
+
+ /*
+ * Process incoming requests.
+ */
+ for (;;) {
+ readfds = 1 << s;
+ nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, timeout);
+ if (nfound < 0) {
+ if (errno != EINTR) {
+ report(LOG_ERR, "select: %s", get_errmsg());
+ }
+ /*
+ * Call readtab() or dumptab() here to avoid the
+ * dangers of doing I/O from a signal handler.
+ */
+ if (do_readtab) {
+ do_readtab = 0;
+ readtab(1); /* force read */
+ }
+ if (do_dumptab) {
+ do_dumptab = 0;
+ dumptab(bootpd_dump);
+ }
+ continue;
+ }
+ if (!(readfds & (1 << s))) {
+ if (debug > 1)
+ report(LOG_INFO, "exiting after %ld minutes of inactivity",
+ actualtimeout.tv_sec / 60);
+ exit(0);
+ }
+ ra_len = sizeof(recv_addr);
+ n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
+ (struct sockaddr *) &recv_addr, &ra_len);
+ if (n <= 0) {
+ continue;
+ }
+ if (debug > 1) {
+ report(LOG_INFO, "recvd pkt from IP addr %s",
+ inet_ntoa(recv_addr.sin_addr));
+ }
+ if (n < sizeof(struct bootp)) {
+ if (debug) {
+ report(LOG_INFO, "received short packet");
+ }
+ continue;
+ }
+ pktlen = n;
+
+ readtab(0); /* maybe re-read bootptab */
+
+ switch (bp->bp_op) {
+ case BOOTREQUEST:
+ handle_request();
+ break;
+ case BOOTREPLY:
+ handle_reply();
+ break;
+ }
+ }
+}
+
+
+
+
+/*
+ * Print "usage" message and exit
+ */
+
+PRIVATE void
+usage()
+{
+ fprintf(stderr,
+ "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
+ fprintf(stderr, "\t -c n\tset current directory\n");
+ fprintf(stderr, "\t -d n\tset debug level\n");
+ fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
+ fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
+ fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
+ exit(1);
+}
+
+/* Signal catchers */
+PRIVATE void
+catcher(sig)
+ int sig;
+{
+ if (sig == SIGHUP)
+ do_readtab = 1;
+ if (sig == SIGUSR1)
+ do_dumptab = 1;
+#ifdef SYSV
+ /* For older "System V" derivatives with no sigset(). */
+ /* XXX - Should just do it the POSIX way (sigaction). */
+ signal(sig, catcher);
+#endif
+}
+
+
+
+/*
+ * Process BOOTREQUEST packet.
+ *
+ * Note: This version of the bootpd.c server never forwards
+ * a request to another server. That is the job of a gateway
+ * program such as the "bootpgw" program included here.
+ *
+ * (Also this version does not interpret the hostname field of
+ * the request packet; it COULD do a name->address lookup and
+ * forward the request there.)
+ */
+PRIVATE void
+handle_request()
+{
+ struct bootp *bp = (struct bootp *) pktbuf;
+ struct host *hp = NULL;
+ struct host dummyhost;
+ int32 bootsize = 0;
+ unsigned hlen, hashcode;
+ int32 dest;
+ char realpath[1024];
+ char *clntpath;
+ char *homedir, *bootfile;
+ int n;
+
+ /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
+
+ /*
+ * 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(bp->bp_sname, hostname)) {
+ if (debug)
+ report(LOG_INFO, "\
+ignoring request for server %s from client at %s address %s",
+ bp->bp_sname, netname(bp->bp_htype),
+ haddrtoa(bp->bp_chaddr, bp->bp_hlen));
+ /* XXX - Is it correct to ignore such a request? -gwr */
+ return;
+ }
+ } else {
+ strcpy(bp->bp_sname, hostname);
+ }
+
+ /* Convert the request into a reply. */
+ bp->bp_op = BOOTREPLY;
+ if (bp->bp_ciaddr.s_addr == 0) {
+ /*
+ * client doesnt know his IP address,
+ * search by hardware address.
+ */
+ if (debug > 1) {
+ report(LOG_INFO, "request from %s address %s",
+ netname(bp->bp_htype),
+ haddrtoa(bp->bp_chaddr, bp->bp_hlen));
+ }
+ hlen = haddrlength(bp->bp_htype);
+ if (hlen != bp->bp_hlen) {
+ report(LOG_NOTICE, "bad addr len from from %s address %s",
+ netname(bp->bp_htype),
+ haddrtoa(bp->bp_chaddr, hlen));
+ }
+ dummyhost.htype = bp->bp_htype;
+ bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
+ hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
+ hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
+ &dummyhost);
+ if (hp == NULL &&
+ bp->bp_htype == HTYPE_IEEE802)
+ {
+ /* Try again with address in "canonical" form. */
+ haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
+ if (debug > 1) {
+ report(LOG_INFO, "\
+HW addr type is IEEE 802. convert to %s and check again\n",
+ haddrtoa(dummyhost.haddr, bp->bp_hlen));
+ }
+ hashcode = hash_HashFunction(dummyhost.haddr, hlen);
+ hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
+ hwlookcmp, &dummyhost);
+ }
+ if (hp == NULL) {
+ /*
+ * XXX - Add dynamic IP address assignment?
+ */
+ if (debug > 1)
+ report(LOG_INFO, "unknown client %s address %s",
+ netname(bp->bp_htype),
+ haddrtoa(bp->bp_chaddr, bp->bp_hlen));
+ return; /* not found */
+ }
+ (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
+
+ } else {
+
+ /*
+ * search by IP address.
+ */
+ if (debug > 1) {
+ report(LOG_INFO, "request from IP addr %s",
+ inet_ntoa(bp->bp_ciaddr));
+ }
+ dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
+ hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
+ hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
+ &dummyhost);
+ if (hp == NULL) {
+ if (debug > 1) {
+ report(LOG_NOTICE, "IP address not found: %s",
+ inet_ntoa(bp->bp_ciaddr));
+ }
+ return;
+ }
+ }
+
+ if (debug) {
+ report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
+ hp->hostname->string);
+ }
+
+ /*
+ * If there is a response delay threshold, ignore requests
+ * with a timestamp lower than the threshold.
+ */
+ if (hp->flags.min_wait) {
+ u_int32 t = (u_int32) ntohs(bp->bp_secs);
+ if (t < hp->min_wait) {
+ if (debug > 1)
+ report(LOG_INFO,
+ "ignoring request due to timestamp (%d < %d)",
+ t, hp->min_wait);
+ return;
+ }
+ }
+
+#ifdef YORK_EX_OPTION
+ /*
+ * The need for the "ex" tag arose out of the need to empty
+ * shared networked drives on diskless PCs. This solution is
+ * not very clean but it does work fairly well.
+ * Written by Edmund J. Sutcliffe <edmund@york.ac.uk>
+ *
+ * XXX - This could compromise security if a non-trusted user
+ * managed to write an entry in the bootptab with :ex=trojan:
+ * so I would leave this turned off unless you need it. -gwr
+ */
+ /* Run a program, passing the client name as a parameter. */
+ if (hp->flags.exec_file) {
+ char tst[100];
+ /* XXX - Check string lengths? -gwr */
+ strcpy (tst, hp->exec_file->string);
+ strcat (tst, " ");
+ strcat (tst, hp->hostname->string);
+ strcat (tst, " &");
+ if (debug)
+ report(LOG_INFO, "executing %s", tst);
+ system(tst); /* Hope this finishes soon... */
+ }
+#endif /* YORK_EX_OPTION */
+
+ /*
+ * If a specific TFTP server address was specified in the bootptab file,
+ * fill it in, otherwise zero it.
+ * XXX - Rather than zero it, should it be the bootpd address? -gwr
+ */
+ (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
+ hp->bootserver.s_addr : 0L;
+
+#ifdef STANFORD_PROM_COMPAT
+ /*
+ * Stanford bootp PROMs (for a Sun?) have no way to leave
+ * the boot file name field blank (because the boot file
+ * name is automatically generated from some index).
+ * As a work-around, this little hack allows those PROMs to
+ * specify "sunboot14" with the same effect as a NULL name.
+ * (The user specifies boot device 14 or some such magic.)
+ */
+ if (strcmp(bp->bp_file, "sunboot14") == 0)
+ bp->bp_file[0] = '\0'; /* treat it as unspecified */
+#endif
+
+ /*
+ * Fill in the client's proper bootfile.
+ *
+ * If the client specifies an absolute path, try that file with a
+ * ".host" suffix and then without. If the file cannot be found, no
+ * reply is made at all.
+ *
+ * If the client specifies a null or relative file, use the following
+ * table to determine the appropriate action:
+ *
+ * Homedir Bootfile Client's file
+ * specified? specified? specification Action
+ * -------------------------------------------------------------------
+ * No No Null Send null filename
+ * No No Relative Discard request
+ * No Yes Null Send if absolute else null
+ * No Yes Relative Discard request *XXX
+ * Yes No Null Send null filename
+ * Yes No Relative Lookup with ".host"
+ * Yes Yes Null Send home/boot or bootfile
+ * Yes Yes Relative Lookup with ".host" *XXX
+ *
+ */
+
+ /*
+ * XXX - I don't like the policy of ignoring a client when the
+ * boot file is not accessible. The TFTP server might not be
+ * running on the same machine as the BOOTP server, in which
+ * case checking accessibility of the boot file is pointless.
+ *
+ * Therefore, file accessibility is now demanded ONLY if you
+ * define CHECK_FILE_ACCESS in the Makefile options. -gwr
+ */
+
+ /*
+ * The "real" path is as seen by the BOOTP daemon on this
+ * machine, while the client path is relative to the TFTP
+ * daemon chroot directory (i.e. /tftpboot).
+ */
+ if (hp->flags.tftpdir) {
+ strcpy(realpath, hp->tftpdir->string);
+ clntpath = &realpath[strlen(realpath)];
+ } else {
+ realpath[0] = '\0';
+ clntpath = realpath;
+ }
+
+ /*
+ * Determine client's requested homedir and bootfile.
+ */
+ homedir = NULL;
+ bootfile = NULL;
+ if (bp->bp_file[0]) {
+ homedir = bp->bp_file;
+ bootfile = strrchr(homedir, '/');
+ if (bootfile) {
+ if (homedir == bootfile)
+ homedir = NULL;
+ *bootfile++ = '\0';
+ } else {
+ /* no "/" in the string */
+ bootfile = homedir;
+ homedir = NULL;
+ }
+ if (debug > 2) {
+ report(LOG_INFO, "requested path=\"%s\" file=\"%s\"",
+ (homedir) ? homedir : "",
+ (bootfile) ? bootfile : "");
+ }
+ }
+
+ /*
+ * Specifications in bootptab override client requested values.
+ */
+ if (hp->flags.homedir)
+ homedir = hp->homedir->string;
+ if (hp->flags.bootfile)
+ bootfile = hp->bootfile->string;
+
+ /*
+ * Construct bootfile path.
+ */
+ if (homedir) {
+ if (homedir[0] != '/')
+ strcat(clntpath, "/");
+ strcat(clntpath, homedir);
+ homedir = NULL;
+ }
+ if (bootfile) {
+ if (bootfile[0] != '/')
+ strcat(clntpath, "/");
+ strcat(clntpath, bootfile);
+ bootfile = NULL;
+ }
+
+ /*
+ * First try to find the file with a ".host" suffix
+ */
+ n = strlen(clntpath);
+ strcat(clntpath, ".");
+ strcat(clntpath, hp->hostname->string);
+ if (chk_access(realpath, &bootsize) < 0) {
+ clntpath[n] = 0; /* Try it without the suffix */
+ if (chk_access(realpath, &bootsize) < 0) {
+ /* neither "file.host" nor "file" was found */
+#ifdef CHECK_FILE_ACCESS
+
+ if (bp->bp_file[0]) {
+ /*
+ * Client wanted specific file
+ * and we didn't have it.
+ */
+ report(LOG_NOTICE,
+ "requested file not found: \"%s\"", clntpath);
+ return;
+ }
+ /*
+ * 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 null_file_name;
+
+#else /* CHECK_FILE_ACCESS */
+
+ /* Complain only if boot file size was needed. */
+ if (hp->flags.bootsize_auto) {
+ report(LOG_ERR, "can not determine size of file \"%s\"",
+ clntpath);
+ }
+
+#endif /* CHECK_FILE_ACCESS */
+ }
+ }
+ strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
+ if (debug > 2)
+ report(LOG_INFO, "bootfile=\"%s\"", clntpath);
+
+null_file_name:
+
+
+ /*
+ * Handle vendor options based on magic number.
+ */
+
+ if (debug > 1) {
+ report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
+ (int) ((bp->bp_vend)[0]),
+ (int) ((bp->bp_vend)[1]),
+ (int) ((bp->bp_vend)[2]),
+ (int) ((bp->bp_vend)[3]));
+ }
+ /*
+ * If this host isn't set for automatic vendor info then copy the
+ * specific cookie into the bootp packet, thus forcing a certain
+ * reply format. Only force reply format if user specified it.
+ */
+ if (hp->flags.vm_cookie) {
+ /* Slam in the user specified magic number. */
+ bcopy(hp->vm_cookie, bp->bp_vend, 4);
+ }
+ /*
+ * Figure out the format for the vendor-specific info.
+ * Note that bp->bp_vend may have been set above.
+ */
+ if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
+ /* RFC1048 conformant bootp client */
+ dovend_rfc1048(bp, hp, bootsize);
+ if (debug > 1) {
+ report(LOG_INFO, "sending reply (with RFC1048 options)");
+ }
+ }
+#ifdef VEND_CMU
+ else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
+ dovend_cmu(bp, hp);
+ if (debug > 1) {
+ report(LOG_INFO, "sending reply (with CMU options)");
+ }
+ }
+#endif
+ else {
+ if (debug > 1) {
+ report(LOG_INFO, "sending reply (with no options)");
+ }
+ }
+
+ dest = (hp->flags.reply_addr) ?
+ hp->reply_addr.s_addr : 0L;
+
+ /* not forwarded */
+ sendreply(0, dest);
+}
+
+
+/*
+ * Process BOOTREPLY packet.
+ */
+PRIVATE void
+handle_reply()
+{
+ if (debug) {
+ report(LOG_INFO, "processing boot reply");
+ }
+ /* forwarded, no destination override */
+ sendreply(1, 0);
+}
+
+
+/*
+ * Send a reply packet to the client. 'forward' flag is set if we are
+ * not the originator of this reply packet.
+ */
+PRIVATE void
+sendreply(forward, dst_override)
+ int forward;
+ int32 dst_override;
+{
+ struct bootp *bp = (struct bootp *) pktbuf;
+ struct in_addr dst;
+ u_short port = bootpc_port;
+ unsigned char *ha;
+ int len;
+
+ /*
+ * XXX - Should honor bp_flags "broadcast" bit here.
+ * Temporary workaround: use the :ra=ADDR: option to
+ * set the reply address to the broadcast address.
+ */
+
+ /*
+ * If the destination address was specified explicitly
+ * (i.e. the broadcast address for HP compatiblity)
+ * then send the response to that address. Otherwise,
+ * act in accordance with RFC951:
+ * If the client IP address is specified, use that
+ * else if gateway IP address is specified, use that
+ * else make a temporary arp cache entry for the client's
+ * NEW IP/hardware address and use that.
+ */
+ if (dst_override) {
+ dst.s_addr = dst_override;
+ if (debug > 1) {
+ report(LOG_INFO, "reply address override: %s",
+ inet_ntoa(dst));
+ }
+ } else if (bp->bp_ciaddr.s_addr) {
+ dst = bp->bp_ciaddr;
+ } else if (bp->bp_giaddr.s_addr && forward == 0) {
+ dst = bp->bp_giaddr;
+ port = bootps_port;
+ if (debug > 1) {
+ report(LOG_INFO, "sending reply to gateway %s",
+ inet_ntoa(dst));
+ }
+ } else {
+ dst = bp->bp_yiaddr;
+ ha = bp->bp_chaddr;
+ len = bp->bp_hlen;
+ if (len > MAXHADDRLEN)
+ len = MAXHADDRLEN;
+
+ if (debug > 1)
+ report(LOG_INFO, "setarp %s - %s",
+ inet_ntoa(dst), haddrtoa(ha, len));
+ setarp(s, &dst, ha, len);
+ }
+
+ if ((forward == 0) &&
+ (bp->bp_siaddr.s_addr == 0))
+ {
+ struct ifreq *ifr;
+ struct in_addr siaddr;
+ /*
+ * If we are originating this reply, we
+ * need to find our own interface address to
+ * put in the bp_siaddr field of the reply.
+ * If this server is multi-homed, pick the
+ * 'best' interface (the one on the same net
+ * as the client). Of course, the client may
+ * be on the other side of a BOOTP gateway...
+ */
+ ifr = getif(s, &dst);
+ if (ifr) {
+ struct sockaddr_in *sip;
+ sip = (struct sockaddr_in *) &(ifr->ifr_addr);
+ siaddr = sip->sin_addr;
+ } else {
+ /* Just use my "official" IP address. */
+ siaddr = my_ip_addr;
+ }
+
+ /* XXX - No need to set bp_giaddr here. */
+
+ /* Finally, set the server address field. */
+ bp->bp_siaddr = siaddr;
+ }
+ /* Set up socket address for send. */
+ send_addr.sin_family = AF_INET;
+ send_addr.sin_port = htons(port);
+ send_addr.sin_addr = dst;
+
+ /* Send reply with same size packet as request used. */
+ if (sendto(s, pktbuf, pktlen, 0,
+ (struct sockaddr *) &send_addr,
+ sizeof(send_addr)) < 0)
+ {
+ report(LOG_ERR, "sendto: %s", get_network_errmsg());
+ }
+} /* sendreply */
+
+
+/* nmatch() - now in getif.c */
+/* setarp() - now in hwaddr.c */
+
+
+/*
+ * 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.
+ */
+
+PRIVATE int
+chk_access(path, filesize)
+ char *path;
+ int32 *filesize;
+{
+ struct stat st;
+
+ if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
+ *filesize = (int32) st.st_size;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+
+/*
+ * Now in dumptab.c :
+ * dumptab()
+ * dump_host()
+ * list_ipaddresses()
+ */
+
+#ifdef VEND_CMU
+
+/*
+ * Insert the CMU "vendor" data for the host pointed to by "hp" into the
+ * bootp packet pointed to by "bp".
+ */
+
+PRIVATE 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;
+ strcpy(vendp->v_magic, (char *)vm_cmu);
+ if (hp->flags.subnet_mask) {
+ (vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
+ (vendp->v_flags) |= VF_SMASK;
+ if (hp->flags.gateway) {
+ (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
+ }
+ }
+ if (hp->flags.domain_server) {
+ 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;
+ }
+ }
+ }
+ /* Log message now done by caller. */
+} /* dovend_cmu */
+
+#endif /* VEND_CMU */
+
+
+
+/*
+ * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
+ * bootp packet pointed to by "bp".
+ */
+#define NEED(LEN, MSG) do \
+ if (bytesleft < (LEN)) { \
+ report(LOG_NOTICE, noroom, \
+ hp->hostname->string, MSG); \
+ return; \
+ } while (0)
+PRIVATE void
+dovend_rfc1048(bp, hp, bootsize)
+ struct bootp *bp;
+ struct host *hp;
+ int32 bootsize;
+{
+ int bytesleft, len;
+ byte *vp;
+ char *tmpstr;
+
+ static char noroom[] = "%s: No room for \"%s\" option";
+
+ vp = bp->bp_vend;
+
+ if (hp->flags.msg_size) {
+ pktlen = hp->msg_size;
+ } else {
+ /*
+ * If the request was longer than the official length, build
+ * a response of that same length where the additional length
+ * is assumed to be part of the bp_vend (options) area.
+ */
+ if (pktlen > sizeof(*bp)) {
+ if (debug > 1)
+ report(LOG_INFO, "request message length=%d", pktlen);
+ }
+ /*
+ * Check whether the request contains the option:
+ * Maximum DHCP Message Size (RFC1533 sec. 9.8)
+ * and if so, override the response length with its value.
+ * This request must lie within the first BP_VEND_LEN
+ * bytes of the option space.
+ */
+ {
+ byte *p, *ep;
+ byte tag, len;
+ short msgsz = 0;
+
+ p = vp + 4;
+ ep = p + BP_VEND_LEN - 4;
+ while (p < ep) {
+ tag = *p++;
+ /* Check for tags with no data first. */
+ if (tag == TAG_PAD)
+ continue;
+ if (tag == TAG_END)
+ break;
+ /* Now scan the length byte. */
+ len = *p++;
+ switch (tag) {
+ case TAG_MAX_MSGSZ:
+ if (len == 2) {
+ bcopy(p, (char*)&msgsz, 2);
+ msgsz = ntohs(msgsz);
+ }
+ break;
+ case TAG_SUBNET_MASK:
+ /* XXX - Should preserve this if given... */
+ break;
+ } /* swtich */
+ p += len;
+ }
+
+ if (msgsz > sizeof(*bp)) {
+ if (debug > 1)
+ report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
+ pktlen = msgsz;
+ }
+ }
+ }
+
+ if (pktlen < sizeof(*bp)) {
+ report(LOG_ERR, "invalid response length=%d", pktlen);
+ pktlen = sizeof(*bp);
+ }
+ bytesleft = ((byte*)bp + pktlen) - vp;
+ if (pktlen > sizeof(*bp)) {
+ if (debug > 1)
+ report(LOG_INFO, "extended reply, length=%d, options=%d",
+ pktlen, bytesleft);
+ }
+
+ /* Copy in the magic cookie */
+ bcopy(vm_rfc1048, vp, 4);
+ vp += 4;
+ bytesleft -= 4;
+
+ if (hp->flags.subnet_mask) {
+ /* always enough room here. */
+ *vp++ = TAG_SUBNET_MASK;/* -1 byte */
+ *vp++ = 4; /* -1 byte */
+ insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */
+ bytesleft -= 6; /* Fix real count */
+ if (hp->flags.gateway) {
+ (void) insert_ip(TAG_GATEWAY,
+ hp->gateway,
+ &vp, &bytesleft);
+ }
+ }
+ if (hp->flags.bootsize) {
+ /* always enough room here */
+ bootsize = (hp->flags.bootsize_auto) ?
+ ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */
+ *vp++ = TAG_BOOT_SIZE;
+ *vp++ = 2;
+ *vp++ = (byte) ((bootsize >> 8) & 0xFF);
+ *vp++ = (byte) (bootsize & 0xFF);
+ bytesleft -= 4; /* Tag, length, and 16 bit blocksize */
+ }
+ /*
+ * This one is special: Remaining options go in the ext file.
+ * Only the subnet_mask, bootsize, and gateway should precede.
+ */
+ if (hp->flags.exten_file) {
+ /*
+ * Check for room for exten_file. Add 3 to account for
+ * TAG_EXTEN_FILE, length, and TAG_END.
+ */
+ len = strlen(hp->exten_file->string);
+ NEED((len + 3), "ef");
+ *vp++ = TAG_EXTEN_FILE;
+ *vp++ = (byte) (len & 0xFF);
+ bcopy(hp->exten_file->string, vp, len);
+ vp += len;
+ *vp++ = TAG_END;
+ bytesleft -= len + 3;
+ return; /* no more options here. */
+ }
+ /*
+ * The remaining options are inserted by the following
+ * function (which is shared with bootpef.c).
+ * Keep back one byte for the TAG_END.
+ */
+ len = dovend_rfc1497(hp, vp, bytesleft - 1);
+ vp += len;
+ bytesleft -= len;
+
+ /* There should be at least one byte left. */
+ NEED(1, "(end)");
+ *vp++ = TAG_END;
+ bytesleft--;
+
+ /* Log message done by caller. */
+ if (bytesleft > 0) {
+ /*
+ * Zero out any remaining part of the vendor area.
+ */
+ bzero(vp, bytesleft);
+ }
+} /* dovend_rfc1048 */
+#undef NEED
+
+
+/*
+ * Now in readfile.c:
+ * hwlookcmp()
+ * iplookcmp()
+ */
+
+/* haddrtoa() - now in hwaddr.c */
+/*
+ * Now in dovend.c:
+ * insert_ip()
+ * insert_generic()
+ * insert_u_long()
+ */
+
+/* get_errmsg() - now in report.c */
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/bootpd.h b/libexec/bootpd/bootpd.h
new file mode 100644
index 0000000..e5ce341
--- /dev/null
+++ b/libexec/bootpd/bootpd.h
@@ -0,0 +1,211 @@
+/************************************************************************
+ 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.
+************************************************************************/
+
+
+/*
+ * bootpd.h -- common header file for all the modules of the bootpd program.
+ */
+
+#include "bptypes.h"
+#include "hash.h"
+#include "hwaddr.h"
+
+#ifndef TRUE
+#define TRUE 1
+#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 MAXSTRINGLEN 80 /* Max string length */
+
+/* Local definitions: */
+#define MAX_MSG_SIZE (3*512) /* Maximum packet size */
+
+
+/*
+ * Return pointer to static string which gives full network error message.
+ */
+#define get_network_errmsg get_errmsg
+
+
+/*
+ * 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 int 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 int linkcount;
+ char string[1]; /* Dynamically extended */
+};
+
+struct shared_bindata {
+ unsigned int 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,
+ time_server :1,
+ dump_file :1,
+ domain_name :1,
+ swap_server :1,
+ root_path :1,
+ exten_file :1,
+ reply_addr :1,
+ nis_domain :1,
+ nis_server :1,
+ ntp_server :1,
+ exec_file :1,
+ msg_size :1,
+ min_wait :1,
+ /* XXX - Add new tags here */
+ vm_cookie :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 {
+ unsigned linkcount; /* hash list inserts */
+ 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,
+ *nis_server,
+ *ntp_server;
+ struct shared_string *bootfile,
+ *hostname,
+ *domain_name,
+ *homedir,
+ *tftpdir,
+ *dump_file,
+ *exten_file,
+ *root_path,
+ *nis_domain,
+ *exec_file;
+ struct shared_bindata *generic;
+ byte vm_cookie[4],
+ htype, /* RFC826 says this should be 16-bits but
+ RFC951 only allocates 1 byte. . . */
+ haddr[MAXHADDRLEN];
+ int32 time_offset;
+ unsigned int32 bootsize,
+ msg_size,
+ min_wait;
+ struct in_addr bootserver,
+ iaddr,
+ swap_server,
+ reply_addr,
+ subnet_mask;
+ /* XXX - Add new tags here (or above as appropriate) */
+};
+
+
+
+/*
+ * Variables shared among modules.
+ */
+
+extern int debug;
+extern char *bootptab;
+extern char *progname;
+
+extern u_char vm_cmu[4];
+extern u_char vm_rfc1048[4];
+
+extern hash_tbl *hwhashtable;
+extern hash_tbl *iphashtable;
+extern hash_tbl *nmhashtable;
+
diff --git a/libexec/bootpd/bootptab.5 b/libexec/bootpd/bootptab.5
new file mode 100644
index 0000000..7495383
--- /dev/null
+++ b/libexec/bootpd/bootptab.5
@@ -0,0 +1,395 @@
+.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University
+.\"
+.\" $Header: /home/ncvs/src/usr.sbin/bootpd/bootptab.5,v 1.1.1.1 1994/09/10 14:44:54 csgr 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 (or a "dummy entry"), and
+.I tg
+is a two-character tag symbol. Dummy entries have an invalid hostname
+(one with a "." as the first character) and are used to provide
+default values used by other entries via the
+.B tc=.dummy-entry
+mechanism. Most tags must be followed by an equals-sign
+and a value as above. Some may also appear in a boolean form with no
+value (i.e.
+.RI : tg :).
+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
+ ef Extension file
+.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
+ nt NTP (time) Server (RFC 1129)
+.br
+ ra Reply address override
+.br
+ rl Resource location protocol server address list
+.br
+ rp Root path to mount as root
+.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
+.br
+ yd YP (NIS) domain name
+.br
+ ys YP (NIS) server address
+
+.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 nt ,
+.BR ra ,
+.BR rl ,
+and
+.BR ts .
+The
+.BR ip ,
+.BR sa ,
+.BR sw ,
+.BR sm ,
+and
+.B ys
+tags each take a single IP address.
+All IP addresses are specified in standard Internet "dot" notation
+and may use decimal, octal, or hexadecimal numbers
+(octal numbers begin with 0, hexadecimal numbers begin with '0x' or '0X').
+Any IP addresses may alternatively be specified as a hostname, causing
+.I bootpd
+to lookup the IP address for that host name using gethostbyname(3).
+If the
+.B ip
+tag is not specified,
+.I bootpd
+will determine the IP address using the entry name as the host name.
+(Dummy entries use an invalid host name to avoid automatic IP lookup.)
+.PP
+The
+.B ht
+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 may be specified as a host name
+or in numeric form. Note that the numeric form
+.I must
+be specified in hexadecimal; optional periods and/or a leading '0x' may be
+included for readability. The
+.B ha
+tag must be preceded by the
+.B ht
+tag (either explicitly or implicitly; see
+.B tc
+below).
+If the hardware address is not specified and the type is specified
+as either "ethernet" or "ieee802", then
+.I bootpd
+will try to determine the hardware address using ether_hton(3).
+.PP
+The hostname, home directory, and bootfile are ASCII strings which may be
+optionally surrounded by double quotes ("). The client's request and the
+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 provides a file name it is left as is.
+Otherwise, if the
+.B bf
+option is specified its value is copied into the reply packet.
+If the
+.B hd
+option is specified as well, its value is prepended to the
+boot file copied into the reply packet.
+The existence of the boot file is checked only if the
+.BR bs =auto
+option is used (to determine the boot file size).
+A reply may be sent whether or not the boot file exists.
+.PP
+Some newer versions of
+.I tftpd
+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 .
+(One may alternatively use the
+.I bootpd
+"-c chdir" option.)
+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
+.nf
+ # Sample bootptab file (domain=andrew.cmu.edu)
+
+ .default:\\
+ :hd=/usr/boot:bf=null:\\
+ :ds=netserver, lancaster:\\
+ :ns=pcs2, pcs1:\\
+ :ts=pcs2, pcs1:\\
+ :sm=255.255.255.0:\\
+ :gw=gw.cs.cmu.edu:\\
+ :hn:to=-18000:
+
+ carnegie:ht=6:ha=7FF8100000AF:tc=.default:
+ baldwin:ht=1:ha=0800200159C3:tc=.default:
+ wylie:ht=1:ha=00DD00CADF00:tc=.default:
+ arnold:ht=1:ha=0800200102AD:tc=.default:
+ bairdford:ht=1:ha=08002B02A2F9:tc=.default:
+ bakerstown:ht=1:ha=08002B0287C8:tc=.default:
+
+ # Special domain name server and option tags for next host
+ butlerjct:ha=08002001560D:ds=128.2.13.42:\\
+ :T37=0x12345927AD3BCF:\\
+ :T99="Special ASCII string":\\
+ :tc=.default:
+
+ gastonville:ht=6:ha=7FFF81000A47:tc=.default:
+ hahntown:ht=6:ha=7FFF81000434:tc=.default:
+ hickman:ht=6:ha=7FFF810001BA:tc=.default:
+ lowber:ht=1:ha=00DD00CAF000:tc=.default:
+ mtoliver:ht=1:ha=00DD00FE1600:tc=.default:
+
+.fi
+.SH FILES
+/etc/bootptab
+
+.SH "SEE ALSO"
+.br
+bootpd(8), tftpd(8),
+.br
+DARPA Internet Request For Comments RFC951, RFC1048, RFC1084, Assigned Numbers
diff --git a/libexec/bootpd/bootptab.cmu b/libexec/bootpd/bootptab.cmu
new file mode 100644
index 0000000..66212d4
--- /dev/null
+++ b/libexec/bootpd/bootptab.cmu
@@ -0,0 +1,124 @@
+# /etc/bootptab: database for bootp server (/etc/bootpd)
+# (I've hacked on this but can't test it... -gwr)
+
+# Blank lines and lines beginning with '#' are ignored.
+#
+# Legend: (see bootptab.5)
+# first field -- hostname (not indented)
+# bf -- bootfile
+# bs -- bootfile size in 512-octet blocks
+# cs -- cookie servers
+# df -- dump file name
+# dn -- domain name
+# ds -- domain name servers
+# ef -- extension file
+# gw -- gateways
+# ha -- hardware address
+# hd -- home directory for bootfiles
+# hn -- host name set for client
+# ht -- hardware type
+# im -- impress servers
+# ip -- host IP address
+# lg -- log servers
+# lp -- LPR servers
+# ns -- IEN-116 name servers
+# ra -- reply address
+# rl -- resource location protocol servers
+# rp -- root path
+# sa -- boot server address
+# sm -- subnet mask
+# sw -- swap server
+# tc -- template host (points to similar host entry)
+# td -- TFTP directory
+# to -- time offset (seconds)
+# ts -- time servers
+# vm -- vendor magic number
+# Tn -- generic option tag n
+#
+# Be careful about including backslashes where they're needed. Weird (bad)
+# things can happen when a backslash is omitted where one is intended.
+# Also, note that generic option data must be either a string or a
+# sequence of bytes where each byte is a two-digit hex value.
+
+# First, we define a global entry which specifies the stuff every host uses.
+# (Host name lookups are relative to the domain: andrew.cmu.edu)
+.default:\
+ :hn:dn=cmu.edu:\
+ :hd=/usr/boot:\
+ :ds=netserver, lancaster:\
+ :ns=pcs2, pcs1:\
+ :ts=pcs2, pcs1:\
+ :sm=255.255.0.0:\
+ :gw=gw.cs.cmu.edu:\
+ to=auto:
+
+
+# Next, we can define different master entries for each subnet. . .
+.subnet13 :sm=255.255.255.0:gw=128.2.13.1 :tc=.default:
+.subnet19 :sm=255.255.255.0:gw=128.2.19.1 :tc=.default:
+.subnet232 :sm=255.255.255.0:gw=128.2.232.1 :tc=.default:
+
+#
+# We should be able to use as many levels of indirection as desired. Use
+# your imagination. . .
+#
+
+
+# Individual entries (could also have different servers for some/all of these
+# hosts, but we don't really use this feature at CMU):
+
+carnegie:tc=.subnet13:ht=ieee802:ha=7FF8100000AF:
+baldwin:tc=.subnet19:ha=0800200159C3:
+wylie:tc=.subnet232:ha=00DD00CADF00:
+arnold:tc=.subnet19:ha=0800200102AD:
+bairdford:tc=.subnet19:ha=08002B02A2F9:
+bakerstown:tc=.subnet19:ha=08002B0287C8:
+butlerjct:tc=.subnet232:ha=08002001560D:
+gastonville:tc=.subnet232:ht=ieee802:ha=7FFF81000A47:
+hahntown:tc=.subnet13:ht=ieee802:ha=7FFF81000434:
+hickman:tc=.subnet19:ht=ieee802:ha=7FFF810001BA:
+lowber:tc=.subnet13:ha=00DD00CAF000:
+mtoliver:tc=.subnet19:ha=00DD00FE1600:
+osborne:tc=.subnet232:ha=00DD00CAD600:
+russelton:tc=.subnet232:ha=080020017FC3:
+thornburg:tc=.subnet13:ha=080020012A33:
+
+
+# Hmmm. . . Let's throw in some whitespace for readability. . . .
+
+andrew: tc=.subnet19:ha=00DD00C88900:
+birdville: tc=.subnet19:ha=00DD00FE2D00:
+coudersport: tc=.subnet13:ha=00DD00CB1E00:
+bridgeville: tc=.subnet232:ha=080020011394:
+franklin: tc=.subnet19:ha=08002B02A5D5:
+hollidaysburg: tc=.subnet19:ht=ieee802:ha=7FFF810002C8:
+honesdale: tc=.subnet19:ha=08002B02F83F:
+huntingdon: tc=.subnet19:ha=08002B02E410:
+indiana: tc=.subnet13:ha=08002B029BEC:
+jimthorpe: tc=.subnet232:ha=08002B02FBBA:
+kittanning: tc=.subnet232:ha=08002B0273FC:
+lebanon: tc=.subnet232:ha=08002B037F67:
+lewisburg: tc=.subnet19:ha=50005A1A0DE4:
+middleburg: tc=.subnet232:ha=00DD00FE1200:
+aspinwall: tc=.subnet13:ha=08002B03C163:
+berlin: tc=.subnet13:ha=00DD000A4400:
+norristown: tc=.subnet13:ha=08002001455B:
+pottsville: tc=.subnet13:ha=00DD000A3700:
+ridgway: tc=.subnet19:ha=08002B029425:
+scranton: tc=.subnet232:ha=0800200113A1:
+chalfont: tc=.subnet13:ha=08002001124B:
+washington: tc=.subnet19:ha=00DD00656E00:
+wellsboro: tc=.subnet13:ha=00DD00CB1C00:
+bb1: tc=.subnet19:ha=00DD000A1F00:
+adamstown: tc=.subnet13:ha=08002B02D0E6:
+beta: tc=.subnet19:ha=02070100B197:
+carbondale: tc=.subnet232:ha=08002B022A73:
+clairton: tc=.subnet19:ha=080020010FD1:
+egypt: tc=.subnet13:ha=00DD00847B00:
+fairchance: tc=.subnet232:ha=00DD000AB100:
+fairhope: tc=.subnet232:ha=00DD00CB0800:
+galeton: tc=.subnet232:ha=08002001138C:
+imperial: tc=.subnet232:ha=08002001130C:
+kingston: tc=.subnet232:ha=080020011382:
+knox: tc=.subnet232:ha=50005A1A0D2A:
+lakecity: tc=.subnet13:ha=080020011380:
diff --git a/libexec/bootpd/bootptab.mcs b/libexec/bootpd/bootptab.mcs
new file mode 100644
index 0000000..6fa04d1
--- /dev/null
+++ b/libexec/bootpd/bootptab.mcs
@@ -0,0 +1,92 @@
+# /etc/bootptab: database for bootp server (/etc/bootpd)
+# Last update: gwr, Sun Dec 12 19:00:00 EDT 1993
+# Blank lines and lines beginning with '#' are ignored.
+#
+# Legend: (see bootptab.5)
+# first field -- hostname (not indented)
+# bf -- bootfile
+# bs -- bootfile size in 512-octet blocks
+# cs -- cookie servers
+# df -- dump file name
+# dn -- domain name
+# ds -- domain name servers
+# ef -- extension file
+# gw -- gateways
+# ha -- hardware address
+# hd -- home directory for bootfiles
+# hn -- host name set for client
+# ht -- hardware type
+# im -- impress servers
+# ip -- host IP address
+# lg -- log servers
+# lp -- LPR servers
+# ns -- IEN-116 name servers
+# ra -- reply address
+# rl -- resource location protocol servers
+# rp -- root path
+# sa -- boot server address
+# sm -- subnet mask
+# sw -- swap server
+# tc -- template host (points to similar host entry)
+# td -- TFTP directory
+# to -- time offset (seconds)
+# ts -- time servers
+# vm -- vendor magic number
+# Tn -- generic option tag n
+#
+# Be careful about including backslashes where they're needed. Weird (bad)
+# things can happen when a backslash is omitted where one is intended.
+# Also, note that generic option data must be either a string or a
+# sequence of bytes where each byte is a two-digit hex value.
+
+# First, we define a global entry which specifies the stuff every host uses.
+
+# If you leave "td" empty, run bootpd with the "-c /tftpboot" switch
+# so path names (boot files) will be interpreted relative to the same
+# directory as tftpd will use when opening files.
+.default:\
+ :hn:dn="mc.com":\
+ :td=/tftpboot:\
+ :ds=merlin, jericho:\
+ :to=auto:
+
+# Next, we can define different master entries for each subnet. . .
+
+.subnet16:\
+ :tc=.default:\
+ :sm=255.255.255.0:\
+ :gw=merlin:\
+ :sa=merlin:
+
+.subnet17:\
+ :tc=.default:\
+ :sm=255.255.255.0:\
+ :gw=merlin-gw:\
+ :sa=merlin-gw:
+
+#
+# We should be able to use as many levels of indirection as desired. Use
+# your imagination. . .
+#
+
+# Individual entries (could also have different servers for some/all of these
+# hosts, but we don't really use this feature at CMU):
+
+# Emulex terminal server
+emulex: tc=.subnet16:ha=00.00.C9.00.42.E0:bf=P4KTL0E:
+
+# Lantronix eps1
+eps1: tc=.subnet16:ha=00.80.A3.04.1D.78:
+
+# Tadpole 885 board.
+tp885: tc=.subnet17:ha=08.00.4C.00.2F.74:bf=tp885sys2.cfe:
+
+# MVME147 VxWorks board.
+#mvme147:tc=.subnet17:ha=08.00.3e.20.da.47:bf=mv147vxw.st:
+
+# These are just for testing
+
+walnut:tc=.subnet16:ha=walnut:
+banana:tc=.subnet17:ha=banana:
+thor:tc=.subnet17:ha=thor:
+classic:tc=.subnet16:ha=classic:
diff --git a/libexec/bootpd/bptypes.h b/libexec/bootpd/bptypes.h
new file mode 100644
index 0000000..537da4e
--- /dev/null
+++ b/libexec/bootpd/bptypes.h
@@ -0,0 +1,23 @@
+/* bptypes.h */
+
+#ifndef BPTYPES_H
+#define BPTYPES_H
+
+/*
+ * 32 bit integers are different types on various architectures
+ */
+
+#ifndef int32
+#define int32 long
+#endif
+typedef unsigned int32 u_int32;
+
+/*
+ * Nice typedefs. . .
+ */
+
+typedef int boolean;
+typedef unsigned char byte;
+
+
+#endif /* BPTYPES_H */
diff --git a/libexec/bootpd/dovend.c b/libexec/bootpd/dovend.c
new file mode 100644
index 0000000..ba6ab28
--- /dev/null
+++ b/libexec/bootpd/dovend.c
@@ -0,0 +1,413 @@
+/*
+ * dovend.c : Inserts all but the first few vendor options.
+ */
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+
+#ifndef USE_BFUNCS
+# include <memory.h>
+/* Yes, memcpy is OK here (no overlapped copies). */
+# define bcopy(a,b,c) memcpy(b,a,c)
+# define bzero(p,l) memset(p,0,l)
+# define bcmp(a,b,c) memcmp(a,b,c)
+# define index strchr
+#endif
+
+#include "bootp.h"
+#include "bootpd.h"
+#include "report.h"
+#include "dovend.h"
+
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+PRIVATE int insert_generic P((struct shared_bindata *, byte **, int *));
+
+/*
+ * Insert the 2nd part of the options into an option buffer.
+ * Return amount of space used.
+ *
+ * This inserts everything EXCEPT:
+ * magic cookie, subnet mask, gateway, bootsize, extension file
+ * Those are handled separately (in bootpd.c) to allow this function
+ * to be shared between bootpd and bootpef.
+ *
+ * When an "extension file" is in use, the options inserted by
+ * this function go into the exten_file, not the bootp response.
+ */
+
+int
+dovend_rfc1497(hp, buf, len)
+ struct host *hp;
+ byte *buf;
+ int len;
+{
+ int bytesleft = len;
+ byte *vp = buf;
+ char *tmpstr;
+
+ static char noroom[] = "%s: No room for \"%s\" option";
+#define NEED(LEN, MSG) do \
+ if (bytesleft < (LEN)) { \
+ report(LOG_NOTICE, noroom, \
+ hp->hostname->string, MSG); \
+ return (vp - buf); \
+ } while (0)
+
+ /*
+ * Note that the following have already been inserted:
+ * magic_cookie, subnet_mask, gateway, bootsize
+ *
+ * The remaining options are inserted in order of importance.
+ * (Of course the importance of each is a matter of opinion.)
+ * The option insertion order should probably be configurable.
+ *
+ * This is the order used in the NetBSD version. Can anyone
+ * explain why the time_offset and swap_server are first?
+ * Also, why is the hostname so far down the list? -gwr
+ */
+
+ if (hp->flags.time_offset) {
+ NEED(6, "to");
+ *vp++ = TAG_TIME_OFFSET;/* -1 byte */
+ *vp++ = 4; /* -1 byte */
+ insert_u_long(htonl(hp->time_offset), &vp); /* -4 bytes */
+ bytesleft -= 6;
+ }
+ /*
+ * swap server, root path, dump path
+ */
+ if (hp->flags.swap_server) {
+ NEED(6, "sw");
+ /* There is just one SWAP_SERVER, so it is not an iplist. */
+ *vp++ = TAG_SWAP_SERVER;/* -1 byte */
+ *vp++ = 4; /* -1 byte */
+ insert_u_long(hp->swap_server.s_addr, &vp); /* -4 bytes */
+ bytesleft -= 6; /* Fix real count */
+ }
+ if (hp->flags.root_path) {
+ /*
+ * Check for room for root_path. Add 2 to account for
+ * TAG_ROOT_PATH and length.
+ */
+ len = strlen(hp->root_path->string);
+ NEED((len + 2), "rp");
+ *vp++ = TAG_ROOT_PATH;
+ *vp++ = (byte) (len & 0xFF);
+ bcopy(hp->root_path->string, vp, len);
+ vp += len;
+ bytesleft -= len + 2;
+ }
+ if (hp->flags.dump_file) {
+ /*
+ * Check for room for dump_file. Add 2 to account for
+ * TAG_DUMP_FILE and length.
+ */
+ len = strlen(hp->dump_file->string);
+ NEED((len + 2), "df");
+ *vp++ = TAG_DUMP_FILE;
+ *vp++ = (byte) (len & 0xFF);
+ bcopy(hp->dump_file->string, vp, len);
+ vp += len;
+ bytesleft -= len + 2;
+ }
+ /*
+ * DNS server and domain
+ */
+ if (hp->flags.domain_server) {
+ if (insert_ip(TAG_DOMAIN_SERVER,
+ hp->domain_server,
+ &vp, &bytesleft))
+ NEED(8, "ds");
+ }
+ if (hp->flags.domain_name) {
+ /*
+ * Check for room for domain_name. Add 2 to account for
+ * TAG_DOMAIN_NAME and length.
+ */
+ len = strlen(hp->domain_name->string);
+ NEED((len + 2), "dn");
+ *vp++ = TAG_DOMAIN_NAME;
+ *vp++ = (byte) (len & 0xFF);
+ bcopy(hp->domain_name->string, vp, len);
+ vp += len;
+ bytesleft -= len + 2;
+ }
+ /*
+ * NIS (YP) server and domain
+ */
+ if (hp->flags.nis_server) {
+ if (insert_ip(TAG_NIS_SERVER,
+ hp->nis_server,
+ &vp, &bytesleft))
+ NEED(8, "ds");
+ }
+ if (hp->flags.nis_domain) {
+ /*
+ * Check for room for nis_domain. Add 2 to account for
+ * TAG_NIS_DOMAIN and length.
+ */
+ len = strlen(hp->nis_domain->string);
+ NEED((len + 2), "dn");
+ *vp++ = TAG_NIS_DOMAIN;
+ *vp++ = (byte) (len & 0xFF);
+ bcopy(hp->nis_domain->string, vp, len);
+ vp += len;
+ bytesleft -= len + 2;
+ }
+ /* IEN 116 name server */
+ if (hp->flags.name_server) {
+ if (insert_ip(TAG_NAME_SERVER,
+ hp->name_server,
+ &vp, &bytesleft))
+ NEED(8, "ns");
+ }
+ if (hp->flags.rlp_server) {
+ if (insert_ip(TAG_RLP_SERVER,
+ hp->rlp_server,
+ &vp, &bytesleft))
+ NEED(8, "rl");
+ }
+ /* Time server (RFC 868) */
+ if (hp->flags.time_server) {
+ if (insert_ip(TAG_TIME_SERVER,
+ hp->time_server,
+ &vp, &bytesleft))
+ NEED(8, "ts");
+ }
+ /* NTP (time) Server (RFC 1129) */
+ if (hp->flags.ntp_server) {
+ if (insert_ip(TAG_NTP_SERVER,
+ hp->ntp_server,
+ &vp, &bytesleft))
+ NEED(8, "ts");
+ }
+ /*
+ * I wonder: If the hostname were "promoted" into the BOOTP
+ * response part, might these "extension" files possibly be
+ * shared between several clients?
+ *
+ * Also, why not just use longer BOOTP packets with all the
+ * additional length used as option data. This bootpd version
+ * already supports that feature by replying with the same
+ * packet length as the client request packet. -gwr
+ */
+ if (hp->flags.name_switch && hp->flags.send_name) {
+ /*
+ * Check for room for hostname. Add 2 to account for
+ * TAG_HOST_NAME and length.
+ */
+ len = strlen(hp->hostname->string);
+#if 0
+ /*
+ * XXX - Too much magic. The user can always set the hostname
+ * to the short version in the bootptab file. -gwr
+ */
+ if ((len + 2) > bytesleft) {
+ /*
+ * Not enough room for full (domain-qualified) hostname, try
+ * stripping it down to just the first field (host).
+ */
+ tmpstr = hp->hostname->string;
+ len = 0;
+ while (*tmpstr && (*tmpstr != '.')) {
+ tmpstr++;
+ len++;
+ }
+ }
+#endif
+ NEED((len + 2), "hn");
+ *vp++ = TAG_HOST_NAME;
+ *vp++ = (byte) (len & 0xFF);
+ bcopy(hp->hostname->string, vp, len);
+ vp += len;
+ bytesleft -= len + 2;
+ }
+ /*
+ * The rest of these are less important, so they go last.
+ */
+ if (hp->flags.lpr_server) {
+ if (insert_ip(TAG_LPR_SERVER,
+ hp->lpr_server,
+ &vp, &bytesleft))
+ NEED(8, "lp");
+ }
+ if (hp->flags.cookie_server) {
+ if (insert_ip(TAG_COOKIE_SERVER,
+ hp->cookie_server,
+ &vp, &bytesleft))
+ NEED(8, "cs");
+ }
+ if (hp->flags.log_server) {
+ if (insert_ip(TAG_LOG_SERVER,
+ hp->log_server,
+ &vp, &bytesleft))
+ NEED(8, "lg");
+ }
+ /*
+ * XXX - Add new tags here (to insert options)
+ */
+ if (hp->flags.generic) {
+ if (insert_generic(hp->generic, &vp, &bytesleft))
+ NEED(64, "(generic)");
+ }
+ /*
+ * The end marker is inserted by the caller.
+ */
+ return (vp - buf);
+#undef NEED
+} /* dovend_rfc1497 */
+
+
+
+/*
+ * Insert a tag value, a length value, and a list of IP addresses into the
+ * memory buffer indirectly pointed to by "dest". "tag" is the RFC1048 tag
+ * number to use, "iplist" is a pointer to a list of IP addresses
+ * (struct in_addr_list), and "bytesleft" points to an integer which
+ * indicates the size of the "dest" buffer.
+ *
+ * Return zero if everything fits.
+ *
+ * This is used to fill the vendor-specific area of a bootp packet in
+ * conformance to RFC1048.
+ */
+
+int
+insert_ip(tag, iplist, dest, bytesleft)
+ byte tag;
+ struct in_addr_list *iplist;
+ byte **dest;
+ int *bytesleft;
+{
+ struct in_addr *addrptr;
+ unsigned addrcount = 1;
+ byte *d;
+
+ if (iplist == NULL)
+ return (0);
+
+ if (*bytesleft >= 6) {
+ d = *dest; /* Save pointer for later */
+ **dest = tag;
+ (*dest) += 2;
+ (*bytesleft) -= 2; /* Account for tag and length */
+ addrptr = iplist->addr;
+ addrcount = iplist->addrcount;
+ while ((*bytesleft >= 4) && (addrcount > 0)) {
+ insert_u_long(addrptr->s_addr, dest);
+ addrptr++;
+ addrcount--;
+ (*bytesleft) -= 4; /* Four bytes per address */
+ }
+ d[1] = (byte) ((*dest - d - 2) & 0xFF);
+ }
+ return (addrcount);
+}
+
+
+
+/*
+ * Insert generic data into a bootp packet. The data is assumed to already
+ * be in RFC1048 format. It is inserted using a first-fit algorithm which
+ * attempts to insert as many tags as possible. Tags and data which are
+ * too large to fit are skipped; any remaining tags are tried until they
+ * have all been exhausted.
+ * Return zero if everything fits.
+ */
+
+static int
+insert_generic(gendata, buff, bytesleft)
+ struct shared_bindata *gendata;
+ byte **buff;
+ int *bytesleft;
+{
+ byte *srcptr;
+ int length, numbytes;
+ int skipped = 0;
+
+ if (gendata == NULL)
+ return (0);
+
+ srcptr = gendata->data;
+ length = gendata->length;
+ while ((length > 0) && (*bytesleft > 0)) {
+ switch (*srcptr) {
+ case TAG_END:
+ length = 0; /* Force an exit on next iteration */
+ break;
+ case TAG_PAD:
+ *(*buff)++ = *srcptr++;
+ (*bytesleft)--;
+ length--;
+ break;
+ default:
+ numbytes = srcptr[1] + 2;
+ if (*bytesleft < numbytes)
+ skipped += numbytes;
+ else {
+ bcopy(srcptr, *buff, numbytes);
+ (*buff) += numbytes;
+ (*bytesleft) -= numbytes;
+ }
+ srcptr += numbytes;
+ length -= numbytes;
+ break;
+ }
+ } /* while */
+ return (skipped);
+}
+
+/*
+ * Insert the unsigned long "value" into memory starting at the byte
+ * pointed to by the byte pointer (*dest). (*dest) is updated to
+ * point to the next available byte.
+ *
+ * Since it is desirable to internally store network addresses in network
+ * byte order (in struct in_addr's), this routine expects longs to be
+ * passed in network byte order.
+ *
+ * However, due to the nature of the main algorithm, the long must be in
+ * host byte order, thus necessitating the use of ntohl() first.
+ */
+
+void
+insert_u_long(value, dest)
+ u_int32 value;
+ byte **dest;
+{
+ byte *temp;
+ int n;
+
+ value = ntohl(value); /* Must use host byte order here */
+ temp = (*dest += 4);
+ for (n = 4; n > 0; n--) {
+ *--temp = (byte) (value & 0xFF);
+ value >>= 8;
+ }
+ /* Final result is network byte order */
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/dovend.h b/libexec/bootpd/dovend.h
new file mode 100644
index 0000000..b30c982
--- /dev/null
+++ b/libexec/bootpd/dovend.h
@@ -0,0 +1,13 @@
+/* dovend.h */
+
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+extern int dovend_rfc1497 P((struct host *hp, u_char *buf, int len));
+extern int insert_ip P((int, struct in_addr_list *, u_char **, int *));
+extern void insert_u_long P((u_int32, u_char **));
+
+#undef P
diff --git a/libexec/bootpd/dumptab.c b/libexec/bootpd/dumptab.c
new file mode 100644
index 0000000..8c049b9
--- /dev/null
+++ b/libexec/bootpd/dumptab.c
@@ -0,0 +1,382 @@
+/*
+ * dumptab.c - handles dumping the database
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <time.h>
+
+#ifndef USE_BFUNCS
+#include <memory.h>
+/* Yes, memcpy is OK here (no overlapped copies). */
+#define bcopy(a,b,c) memcpy(b,a,c)
+#define bzero(p,l) memset(p,0,l)
+#define bcmp(a,b,c) memcmp(a,b,c)
+#endif
+
+#include "bootp.h"
+#include "hash.h"
+#include "hwaddr.h"
+#include "report.h"
+#include "patchlevel.h"
+#include "bootpd.h"
+
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+static void dump_generic P((FILE *, struct shared_bindata *));
+static void dump_host P((FILE *, struct host *));
+static void list_ipaddresses P((FILE *, struct in_addr_list *));
+
+#undef P
+
+#ifndef DEBUG
+void
+dumptab(filename)
+ char *filename;
+{
+ report(LOG_INFO, "No dumptab support!");
+}
+
+#else /* DEBUG */
+
+/*
+ * Dump the internal memory database to bootpd_dump.
+ */
+
+void
+dumptab(filename)
+ char *filename;
+{
+ int n;
+ struct host *hp;
+ FILE *fp;
+ long t;
+ /* Print symbols in alphabetical order for reader's convenience. */
+ static char legend[] = "#\n# Legend:\t(see bootptab.5)\n\
+#\tfirst field -- hostname (not indented)\n\
+#\tbf -- bootfile\n\
+#\tbs -- bootfile size in 512-octet blocks\n\
+#\tcs -- cookie servers\n\
+#\tdf -- dump file name\n\
+#\tdn -- domain name\n\
+#\tds -- domain name servers\n\
+#\tef -- extension file\n\
+#\tex -- exec file (YORK_EX_OPTION)\n\
+#\tgw -- gateways\n\
+#\tha -- hardware address\n\
+#\thd -- home directory for bootfiles\n\
+#\thn -- host name set for client\n\
+#\tht -- hardware type\n\
+#\tim -- impress servers\n\
+#\tip -- host IP address\n\
+#\tlg -- log servers\n\
+#\tlp -- LPR servers\n\
+#\tms -- message size\n\
+#\tmw -- min wait (secs)\n\
+#\tns -- IEN-116 name servers\n\
+#\tnt -- NTP servers (RFC 1129)\n\
+#\tra -- reply address override\n\
+#\trl -- resource location protocol servers\n\
+#\trp -- root path\n\
+#\tsa -- boot server address\n\
+#\tsm -- subnet mask\n\
+#\tsw -- swap server\n\
+#\ttc -- template host (points to similar host entry)\n\
+#\ttd -- TFTP directory\n\
+#\tto -- time offset (seconds)\n\
+#\tts -- time servers\n\
+#\tvm -- vendor magic number\n\
+#\tyd -- YP (NIS) domain\n\
+#\tys -- YP (NIS) servers\n\
+#\tTn -- generic option tag n\n\
+\n";
+
+ /*
+ * Open bootpd.dump file.
+ */
+ if ((fp = fopen(filename, "w")) == NULL) {
+ report(LOG_ERR, "error opening \"%s\": %s",
+ filename, get_errmsg());
+ exit(1);
+ }
+ t = time(NULL);
+ fprintf(fp, "\n# %s %s.%d\n", progname, VERSION, PATCHLEVEL);
+ fprintf(fp, "# %s: dump of bootp server database.\n", filename);
+ fprintf(fp, "# Dump taken %s", ctime(&t));
+ fwrite(legend, 1, sizeof(legend) - 1, fp);
+
+ n = 0;
+ for (hp = (struct host *) hash_FirstEntry(nmhashtable); hp != NULL;
+ hp = (struct host *) hash_NextEntry(nmhashtable)) {
+ dump_host(fp, hp);
+ fprintf(fp, "\n");
+ n++;
+ }
+ fclose(fp);
+
+ report(LOG_INFO, "dumped %d entries to \"%s\".", n, filename);
+}
+
+
+
+/*
+ * Dump all the available information on the host pointed to by "hp".
+ * The output is sent to the file pointed to by "fp".
+ */
+
+static void
+dump_host(fp, hp)
+ FILE *fp;
+ struct host *hp;
+{
+ /* Print symbols in alphabetical order for reader's convenience. */
+ if (hp) {
+ fprintf(fp, "%s:", (hp->hostname ?
+ hp->hostname->string : "?"));
+ if (hp->flags.bootfile) {
+ fprintf(fp, "\\\n\t:bf=%s:", hp->bootfile->string);
+ }
+ if (hp->flags.bootsize) {
+ fprintf(fp, "\\\n\t:bs=");
+ if (hp->flags.bootsize_auto) {
+ fprintf(fp, "auto:");
+ } else {
+ fprintf(fp, "%d:", hp->bootsize);
+ }
+ }
+ if (hp->flags.cookie_server) {
+ fprintf(fp, "\\\n\t:cs=");
+ list_ipaddresses(fp, hp->cookie_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.dump_file) {
+ fprintf(fp, "\\\n\t:df=%s:", hp->dump_file->string);
+ }
+ if (hp->flags.domain_name) {
+ fprintf(fp, "\\\n\t:dn=%s:", hp->domain_name->string);
+ }
+ if (hp->flags.domain_server) {
+ fprintf(fp, "\\\n\t:ds=");
+ list_ipaddresses(fp, hp->domain_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.exten_file) {
+ fprintf(fp, "\\\n\t:ef=%s:", hp->exten_file->string);
+ }
+ if (hp->flags.exec_file) {
+ fprintf(fp, "\\\n\t:ex=%s:", hp->exec_file->string);
+ }
+ if (hp->flags.gateway) {
+ fprintf(fp, "\\\n\t:gw=");
+ list_ipaddresses(fp, hp->gateway);
+ fprintf(fp, ":");
+ }
+ /* FdC: swap_server (see below) */
+ if (hp->flags.homedir) {
+ fprintf(fp, "\\\n\t:hd=%s:", hp->homedir->string);
+ }
+ /* FdC: dump_file (see above) */
+ /* FdC: domain_name (see above) */
+ /* FdC: root_path (see below) */
+ if (hp->flags.name_switch && hp->flags.send_name) {
+ fprintf(fp, "\\\n\t:hn:");
+ }
+ if (hp->flags.htype) {
+ int hlen = haddrlength(hp->htype);
+ fprintf(fp, "\\\n\t:ht=%u:", (unsigned) hp->htype);
+ if (hp->flags.haddr) {
+ fprintf(fp, "ha=\"%s\":",
+ haddrtoa(hp->haddr, hlen));
+ }
+ }
+ if (hp->flags.impress_server) {
+ fprintf(fp, "\\\n\t:im=");
+ list_ipaddresses(fp, hp->impress_server);
+ fprintf(fp, ":");
+ }
+ /* NetBSD: swap_server (see below) */
+ if (hp->flags.iaddr) {
+ fprintf(fp, "\\\n\t:ip=%s:", inet_ntoa(hp->iaddr));
+ }
+ if (hp->flags.log_server) {
+ fprintf(fp, "\\\n\t:lg=");
+ list_ipaddresses(fp, hp->log_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.lpr_server) {
+ fprintf(fp, "\\\n\t:lp=");
+ list_ipaddresses(fp, hp->lpr_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.msg_size) {
+ fprintf(fp, "\\\n\t:ms=%d:", hp->msg_size);
+ }
+ if (hp->flags.min_wait) {
+ fprintf(fp, "\\\n\t:mw=%d:", hp->min_wait);
+ }
+ if (hp->flags.name_server) {
+ fprintf(fp, "\\\n\t:ns=");
+ list_ipaddresses(fp, hp->name_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.ntp_server) {
+ fprintf(fp, "\\\n\t:nt=");
+ list_ipaddresses(fp, hp->ntp_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.reply_addr) {
+ fprintf(fp, "\\\n\t:ra=%s:", inet_ntoa(hp->reply_addr));
+ }
+ if (hp->flags.rlp_server) {
+ fprintf(fp, "\\\n\t:rl=");
+ list_ipaddresses(fp, hp->rlp_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.root_path) {
+ fprintf(fp, "\\\n\t:rp=%s:", hp->root_path->string);
+ }
+ if (hp->flags.bootserver) {
+ fprintf(fp, "\\\n\t:sa=%s:", inet_ntoa(hp->bootserver));
+ }
+ if (hp->flags.subnet_mask) {
+ fprintf(fp, "\\\n\t:sm=%s:", inet_ntoa(hp->subnet_mask));
+ }
+ if (hp->flags.swap_server) {
+ fprintf(fp, "\\\n\t:sw=%s:", inet_ntoa(hp->subnet_mask));
+ }
+ if (hp->flags.tftpdir) {
+ fprintf(fp, "\\\n\t:td=%s:", hp->tftpdir->string);
+ }
+ /* NetBSD: rootpath (see above) */
+ /* NetBSD: domainname (see above) */
+ /* NetBSD: dumpfile (see above) */
+ if (hp->flags.time_offset) {
+ fprintf(fp, "\\\n\t:to=%ld:", hp->time_offset);
+ }
+ if (hp->flags.time_server) {
+ fprintf(fp, "\\\n\t:ts=");
+ list_ipaddresses(fp, hp->time_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.vm_cookie) {
+ fprintf(fp, "\\\n\t:vm=");
+ if (!bcmp(hp->vm_cookie, vm_rfc1048, 4)) {
+ fprintf(fp, "rfc1048:");
+ } else if (!bcmp(hp->vm_cookie, vm_cmu, 4)) {
+ fprintf(fp, "cmu:");
+ } else {
+ fprintf(fp, "%d.%d.%d.%d:",
+ (int) ((hp->vm_cookie)[0]),
+ (int) ((hp->vm_cookie)[1]),
+ (int) ((hp->vm_cookie)[2]),
+ (int) ((hp->vm_cookie)[3]));
+ }
+ }
+ if (hp->flags.nis_domain) {
+ fprintf(fp, "\\\n\t:yd=%s:",
+ hp->nis_domain->string);
+ }
+ if (hp->flags.nis_server) {
+ fprintf(fp, "\\\n\t:ys=");
+ list_ipaddresses(fp, hp->nis_server);
+ fprintf(fp, ":");
+ }
+ /*
+ * XXX - Add new tags here (or above,
+ * so they print in alphabetical order).
+ */
+
+ if (hp->flags.generic) {
+ dump_generic(fp, hp->generic);
+ }
+ }
+}
+
+
+static void
+dump_generic(fp, generic)
+ FILE *fp;
+ struct shared_bindata *generic;
+{
+ u_char *bp = generic->data;
+ u_char *ep = bp + generic->length;
+ u_char tag;
+ int len;
+
+ while (bp < ep) {
+ tag = *bp++;
+ if (tag == TAG_PAD)
+ continue;
+ if (tag == TAG_END)
+ return;
+ len = *bp++;
+ if (bp + len > ep) {
+ fprintf(fp, " #junk in generic! :");
+ return;
+ }
+ fprintf(fp, "\\\n\t:T%d=", tag);
+ while (len) {
+ fprintf(fp, "%02X", *bp);
+ bp++;
+ len--;
+ if (len)
+ fprintf(fp, ".");
+ }
+ fprintf(fp, ":");
+ }
+}
+
+
+
+/*
+ * Dump an entire struct in_addr_list of IP addresses to the indicated file.
+ *
+ * The addresses are printed in standard ASCII "dot" notation and separated
+ * from one another by a single space. A single leading space is also
+ * printed before the first adddress.
+ *
+ * Null lists produce no output (and no error).
+ */
+
+static void
+list_ipaddresses(fp, ipptr)
+ FILE *fp;
+ struct in_addr_list *ipptr;
+{
+ unsigned count;
+ struct in_addr *addrptr;
+
+ if (ipptr) {
+ count = ipptr->addrcount;
+ addrptr = ipptr->addr;
+ while (count > 0) {
+ fprintf(fp, "%s", inet_ntoa(*addrptr++));
+ count--;
+ if (count)
+ fprintf(fp, ", ");
+ }
+ }
+}
+
+#endif /* DEBUG */
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/getether.c b/libexec/bootpd/getether.c
new file mode 100644
index 0000000..724a376
--- /dev/null
+++ b/libexec/bootpd/getether.c
@@ -0,0 +1,374 @@
+/*
+ * getether.c : get the ethernet address of an interface
+ *
+ * All of this code is quite system-specific. As you may well
+ * guess, it took a good bit of detective work to figure out!
+ *
+ * If you figure out how to do this on another system,
+ * please let me know. <gwr@mc.com>
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <ctype.h>
+#include <syslog.h>
+
+#include "report.h"
+#define EALEN 6
+
+#if defined(ultrix) || (defined(__osf__) && defined(__alpha))
+/*
+ * This is really easy on Ultrix! Thanks to
+ * Harald Lundberg <hl@tekla.fi> for this code.
+ *
+ * The code here is not specific to the Alpha, but that was the
+ * only symbol we could find to identify DEC's version of OSF.
+ * (Perhaps we should just define DEC in the Makefile... -gwr)
+ */
+
+#include <sys/ioctl.h>
+#include <net/if.h> /* struct ifdevea */
+
+getether(ifname, eap)
+ char *ifname, *eap;
+{
+ int rc = -1;
+ int fd;
+ struct ifdevea phys;
+ bzero(&phys, sizeof(phys));
+ strcpy(phys.ifr_name, ifname);
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ report(LOG_ERR, "getether: socket(INET,DGRAM) failed");
+ return -1;
+ }
+ if (ioctl(fd, SIOCRPHYSADDR, &phys) < 0) {
+ report(LOG_ERR, "getether: ioctl SIOCRPHYSADDR failed");
+ } else {
+ bcopy(&phys.current_pa[0], eap, EALEN);
+ rc = 0;
+ }
+ close(fd);
+ return rc;
+}
+
+#define GETETHER
+#endif /* ultrix|osf1 */
+
+
+#ifdef SUNOS
+
+#include <sys/sockio.h>
+#include <sys/time.h> /* needed by net_if.h */
+#include <net/nit_if.h> /* for NIOCBIND */
+#include <net/if.h> /* for struct ifreq */
+
+getether(ifname, eap)
+ char *ifname; /* interface name from ifconfig structure */
+ char *eap; /* Ether address (output) */
+{
+ int rc = -1;
+
+ struct ifreq ifrnit;
+ int nit;
+
+ bzero((char *) &ifrnit, sizeof(ifrnit));
+ strncpy(&ifrnit.ifr_name[0], ifname, IFNAMSIZ);
+
+ nit = open("/dev/nit", 0);
+ if (nit < 0) {
+ report(LOG_ERR, "getether: open /dev/nit: %s",
+ get_errmsg());
+ return rc;
+ }
+ do {
+ if (ioctl(nit, NIOCBIND, &ifrnit) < 0) {
+ report(LOG_ERR, "getether: NIOCBIND on nit");
+ break;
+ }
+ if (ioctl(nit, SIOCGIFADDR, &ifrnit) < 0) {
+ report(LOG_ERR, "getether: SIOCGIFADDR on nit");
+ break;
+ }
+ bcopy(&ifrnit.ifr_addr.sa_data[0], eap, EALEN);
+ rc = 0;
+ } while (0);
+ close(nit);
+ return rc;
+}
+
+#define GETETHER
+#endif /* SUNOS */
+
+
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+/* Thanks to John Brezak <brezak@ch.hp.com> for this code. */
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+getether(ifname, eap)
+ char *ifname; /* interface name from ifconfig structure */
+ char *eap; /* Ether address (output) */
+{
+ int fd, rc = -1;
+ register int n;
+ struct ifreq ibuf[16], ifr;
+ struct ifconf ifc;
+ register struct ifreq *ifrp, *ifend;
+
+ /* Fetch the interface configuration */
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ report(LOG_ERR, "getether: socket %s: %s", ifname, get_errmsg());
+ return (fd);
+ }
+ ifc.ifc_len = sizeof(ibuf);
+ ifc.ifc_buf = (caddr_t) ibuf;
+ if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0 ||
+ ifc.ifc_len < sizeof(struct ifreq)) {
+ report(LOG_ERR, "getether: SIOCGIFCONF: %s", get_errmsg);
+ goto out;
+ }
+ /* Search interface configuration list for link layer address. */
+ ifrp = ibuf;
+ ifend = (struct ifreq *) ((char *) ibuf + ifc.ifc_len);
+ while (ifrp < ifend) {
+ /* Look for interface */
+ if (strcmp(ifname, ifrp->ifr_name) == 0 &&
+ ifrp->ifr_addr.sa_family == AF_LINK &&
+ ((struct sockaddr_dl *) &ifrp->ifr_addr)->sdl_type == IFT_ETHER) {
+ bcopy(LLADDR((struct sockaddr_dl *) &ifrp->ifr_addr), eap, EALEN);
+ rc = 0;
+ break;
+ }
+ /* Bump interface config pointer */
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ n = sizeof(*ifrp);
+ ifrp = (struct ifreq *) ((char *) ifrp + n);
+ }
+
+ out:
+ close(fd);
+ return (rc);
+}
+
+#define GETETHER
+#endif /* __NetBSD__ */
+
+
+#ifdef SVR4
+/*
+ * This is for "Streams TCP/IP" by Lachman Associates.
+ * They sure made this cumbersome! -gwr
+ */
+
+#include <sys/sockio.h>
+#include <sys/dlpi.h>
+#include <stropts.h>
+#ifndef NULL
+#define NULL 0
+#endif
+
+getether(ifname, eap)
+ char *ifname; /* interface name from ifconfig structure */
+ char *eap; /* Ether address (output) */
+{
+ int rc = -1;
+ char devname[32];
+ char tmpbuf[sizeof(union DL_primitives) + 16];
+ struct strbuf cbuf;
+ int fd, flags;
+ union DL_primitives *dlp;
+ char *enaddr;
+ int unit = -1; /* which unit to attach */
+
+ sprintf(devname, "/dev/%s", ifname);
+ fd = open(devname, 2);
+ if (fd < 0) {
+ /* Try without the trailing digit. */
+ char *p = devname + 5;
+ while (isalpha(*p))
+ p++;
+ if (isdigit(*p)) {
+ unit = *p - '0';
+ *p = '\0';
+ }
+ fd = open(devname, 2);
+ if (fd < 0) {
+ report(LOG_ERR, "getether: open %s: %s",
+ devname, get_errmsg());
+ return rc;
+ }
+ }
+#ifdef DL_ATTACH_REQ
+ /*
+ * If this is a "Style 2" DLPI, then we must "attach" first
+ * to tell the driver which unit (board, port) we want.
+ * For now, decide this based on the device name.
+ * (Should do "info_req" and check dl_provider_style ...)
+ */
+ if (unit >= 0) {
+ memset(tmpbuf, 0, sizeof(tmpbuf));
+ dlp = (union DL_primitives *) tmpbuf;
+ dlp->dl_primitive = DL_ATTACH_REQ;
+ dlp->attach_req.dl_ppa = unit;
+ cbuf.buf = tmpbuf;
+ cbuf.len = DL_ATTACH_REQ_SIZE;
+ if (putmsg(fd, &cbuf, NULL, 0) < 0) {
+ report(LOG_ERR, "getether: attach: putmsg: %s", get_errmsg());
+ goto out;
+ }
+ /* Recv the ack. */
+ cbuf.buf = tmpbuf;
+ cbuf.maxlen = sizeof(tmpbuf);
+ flags = 0;
+ if (getmsg(fd, &cbuf, NULL, &flags) < 0) {
+ report(LOG_ERR, "getether: attach: getmsg: %s", get_errmsg());
+ goto out;
+ }
+ /*
+ * Check the type, etc.
+ */
+ if (dlp->dl_primitive == DL_ERROR_ACK) {
+ report(LOG_ERR, "getether: attach: dlpi_errno=%d, unix_errno=%d",
+ dlp->error_ack.dl_errno,
+ dlp->error_ack.dl_unix_errno);
+ goto out;
+ }
+ if (dlp->dl_primitive != DL_OK_ACK) {
+ report(LOG_ERR, "getether: attach: not OK or ERROR");
+ goto out;
+ }
+ } /* unit >= 0 */
+#endif /* DL_ATTACH_REQ */
+
+ /*
+ * Get the Ethernet address the same way the ARP module
+ * does when it is pushed onto a new stream (bind).
+ * One should instead be able just do an dl_info_req
+ * but many drivers do not supply the hardware address
+ * in the response to dl_info_req (they MUST supply it
+ * for dl_bind_ack because the ARP module requires it).
+ */
+ memset(tmpbuf, 0, sizeof(tmpbuf));
+ dlp = (union DL_primitives *) tmpbuf;
+ dlp->dl_primitive = DL_BIND_REQ;
+ dlp->bind_req.dl_sap = 0x8FF; /* XXX - Unused SAP */
+ cbuf.buf = tmpbuf;
+ cbuf.len = DL_BIND_REQ_SIZE;
+ if (putmsg(fd, &cbuf, NULL, 0) < 0) {
+ report(LOG_ERR, "getether: bind: putmsg: %s", get_errmsg());
+ goto out;
+ }
+ /* Recv the ack. */
+ cbuf.buf = tmpbuf;
+ cbuf.maxlen = sizeof(tmpbuf);
+ flags = 0;
+ if (getmsg(fd, &cbuf, NULL, &flags) < 0) {
+ report(LOG_ERR, "getether: bind: getmsg: %s", get_errmsg());
+ goto out;
+ }
+ /*
+ * Check the type, etc.
+ */
+ if (dlp->dl_primitive == DL_ERROR_ACK) {
+ report(LOG_ERR, "getether: bind: dlpi_errno=%d, unix_errno=%d",
+ dlp->error_ack.dl_errno,
+ dlp->error_ack.dl_unix_errno);
+ goto out;
+ }
+ if (dlp->dl_primitive != DL_BIND_ACK) {
+ report(LOG_ERR, "getether: bind: not OK or ERROR");
+ goto out;
+ }
+ if (dlp->bind_ack.dl_addr_offset == 0) {
+ report(LOG_ERR, "getether: bind: ack has no address");
+ goto out;
+ }
+ if (dlp->bind_ack.dl_addr_length < EALEN) {
+ report(LOG_ERR, "getether: bind: ack address truncated");
+ goto out;
+ }
+ /*
+ * Copy the Ethernet address out of the message.
+ */
+ enaddr = tmpbuf + dlp->bind_ack.dl_addr_offset;
+ memcpy(eap, enaddr, EALEN);
+ rc = 0;
+
+ out:
+ close(fd);
+ return rc;
+}
+
+#define GETETHER
+#endif /* SVR4 */
+
+
+#ifdef linux
+/*
+ * This is really easy on Linux! This version (for linux)
+ * written by Nigel Metheringham <nigelm@ohm.york.ac.uk>
+ *
+ * The code is almost identical to the Ultrix code - however
+ * the names are different to confuse the innocent :-)
+ * Most of this code was stolen from the Ultrix bit above.
+ */
+
+#include <sys/ioctl.h>
+#include <net/if.h> /* struct ifreq */
+
+/* In a properly configured system this should be either sys/socketio.h
+ or sys/sockios.h, but on my distribution these don't line up correctly */
+#include <linux/sockios.h> /* Needed for IOCTL defs */
+
+getether(ifname, eap)
+ char *ifname, *eap;
+{
+ int rc = -1;
+ int fd;
+ struct ifreq phys;
+ bzero(&phys, sizeof(phys));
+ strcpy(phys.ifr_name, ifname);
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ report(LOG_ERR, "getether: socket(INET,DGRAM) failed");
+ return -1;
+ }
+ if (ioctl(fd, SIOCGIFHWADDR, &phys) < 0) {
+ report(LOG_ERR, "getether: ioctl SIOCGIFHWADDR failed");
+ } else {
+ bcopy(phys.ifr_hwaddr, eap, EALEN);
+ rc = 0;
+ }
+ close(fd);
+ return rc;
+}
+
+#define GETETHER
+#endif /* linux */
+
+
+/* If we don't know how on this system, just return an error. */
+#ifndef GETETHER
+getether(ifname, eap)
+ char *ifname, *eap;
+{
+ return -1;
+}
+
+#endif /* !GETETHER */
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/getif.c b/libexec/bootpd/getif.c
new file mode 100644
index 0000000..6cc1649
--- /dev/null
+++ b/libexec/bootpd/getif.c
@@ -0,0 +1,145 @@
+/*
+ * getif.c : get an interface structure
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#if defined(SUNOS) || defined(SVR4)
+#include <sys/sockio.h>
+#endif
+#ifdef SVR4
+#include <sys/stropts.h>
+#endif
+
+#include <net/if.h> /* for struct ifreq */
+#include <netinet/in.h>
+
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+#include <syslog.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "getif.h"
+#include "report.h"
+
+#ifdef __bsdi__
+#define BSD 43
+#endif
+
+static struct ifreq ifreq[10]; /* Holds interface configuration */
+static struct ifconf ifconf; /* points to ifreq */
+
+static int nmatch();
+
+/* Return a pointer to the interface struct for the passed address. */
+struct ifreq *
+getif(s, addrp)
+ int s; /* socket file descriptor */
+ struct in_addr *addrp; /* destination address on interface */
+{
+ int maxmatch;
+ int len, m, incr;
+ struct ifreq *ifrq, *ifrmax;
+ struct sockaddr_in *sip;
+ char *p;
+
+ /* If no address was supplied, just return NULL. */
+ if (!addrp)
+ return (struct ifreq *) 0;
+
+ /* Get the interface config if not done already. */
+ if (ifconf.ifc_len == 0) {
+#ifdef SVR4
+ /*
+ * SysVr4 returns garbage if you do this the obvious way!
+ * This one took a while to figure out... -gwr
+ */
+ struct strioctl ioc;
+ ioc.ic_cmd = SIOCGIFCONF;
+ ioc.ic_timout = 0;
+ ioc.ic_len = sizeof(ifreq);
+ ioc.ic_dp = (char *) ifreq;
+ m = ioctl(s, I_STR, (char *) &ioc);
+ ifconf.ifc_len = ioc.ic_len;
+ ifconf.ifc_req = ifreq;
+#else /* SVR4 */
+ ifconf.ifc_len = sizeof(ifreq);
+ ifconf.ifc_req = ifreq;
+ m = ioctl(s, SIOCGIFCONF, (caddr_t) & ifconf);
+#endif /* SVR4 */
+ if ((m < 0) || (ifconf.ifc_len <= 0)) {
+ report(LOG_ERR, "ioctl SIOCGIFCONF");
+ return (struct ifreq *) 0;
+ }
+ }
+ maxmatch = 7; /* this many bits or less... */
+ ifrmax = (struct ifreq *) 0;/* ... is not a valid match */
+ p = (char *) ifreq;
+ len = ifconf.ifc_len;
+ while (len > 0) {
+ ifrq = (struct ifreq *) p;
+ sip = (struct sockaddr_in *) &ifrq->ifr_addr;
+ m = nmatch(addrp, &(sip->sin_addr));
+ if (m > maxmatch) {
+ maxmatch = m;
+ ifrmax = ifrq;
+ }
+ /* XXX - Could this be just #ifndef IFNAMSIZ instead? -gwr */
+#if (BSD - 0) < 43
+ /* BSD not defined or earlier than 4.3 */
+ incr = sizeof(*ifrq);
+#else /* NetBSD */
+ incr = ifrq->ifr_addr.sa_len + IFNAMSIZ;
+#endif /* NetBSD */
+
+ p += incr;
+ len -= incr;
+ }
+
+ return ifrmax;
+}
+
+/*
+ * Return the number of leading bits matching in the
+ * internet addresses supplied.
+ */
+static int
+nmatch(ca, cb)
+ u_char *ca, *cb; /* ptrs to IP address, network order */
+{
+ u_int m = 0; /* count of matching bits */
+ u_int n = 4; /* bytes left, then bitmask */
+
+ /* Count matching bytes. */
+ while (n && (*ca == *cb)) {
+ ca++;
+ cb++;
+ m += 8;
+ n--;
+ }
+ /* Now count matching bits. */
+ if (n) {
+ n = 0x80;
+ while (n && ((*ca & n) == (*cb & n))) {
+ m++;
+ n >>= 1;
+ }
+ }
+ return (m);
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/getif.h b/libexec/bootpd/getif.h
new file mode 100644
index 0000000..c51dafd
--- /dev/null
+++ b/libexec/bootpd/getif.h
@@ -0,0 +1,7 @@
+/* getif.h */
+
+#ifdef __STDC__
+extern struct ifreq *getif(int, struct in_addr *);
+#else
+extern struct ifreq *getif();
+#endif
diff --git a/libexec/bootpd/hash.c b/libexec/bootpd/hash.c
new file mode 100644
index 0000000..2ecda63
--- /dev/null
+++ b/libexec/bootpd/hash.c
@@ -0,0 +1,425 @@
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+
+#ifndef lint
+static char rcsid[] = "$Id: hash.c,v 1.1.1.1 1994/09/10 14:44:55 csgr 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 <sys/types.h>
+#include <stdlib.h>
+
+#ifndef USE_BFUNCS
+#include <memory.h>
+/* Yes, memcpy is OK here (no overlapped copies). */
+#define bcopy(a,b,c) memcpy(b,a,c)
+#define bzero(p,l) memset(p,0,l)
+#define bcmp(a,b,c) memcmp(a,b,c)
+#endif
+
+#include "hash.h"
+
+#define TRUE 1
+#define FALSE 0
+#ifndef NULL
+#define NULL 0
+#endif
+
+/*
+ * This can be changed to make internal routines visible to debuggers, etc.
+ */
+#ifndef PRIVATE
+#define PRIVATE static
+#endif
+
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+PRIVATE void hashi_FreeMembers P((hash_member *, hash_freefp));
+
+#undef P
+
+
+
+/*
+ * Hash table initialization routine.
+ *
+ * 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 */
+}
+
+
+
+/*
+ * 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_FreeMembers(bucketptr, free_data)
+ hash_member *bucketptr;
+ hash_freefp free_data;
+{
+ hash_member *nextbucket;
+ while (bucketptr) {
+ nextbucket = bucketptr->next;
+ (*free_data) (bucketptr->data);
+ free((char *) bucketptr);
+ bucketptr = nextbucket;
+ }
+}
+
+
+
+
+/*
+ * 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;
+ hash_freefp free_data;
+{
+ hash_member **bucketptr;
+ unsigned i;
+
+ bucketptr = hashtable->table;
+ for (i = 0; i < hashtable->size; i++) {
+ hashi_FreeMembers(*bucketptr, free_data);
+ *bucketptr++ = NULL;
+ }
+ hashtable->bucketnum = 0;
+ hashtable->member = (hashtable->table)[0];
+}
+
+
+
+/*
+ * Generic hash function to calculate a hash code from the given string.
+ *
+ * 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;
+ hash_cmpfp compare;
+ hash_datum *key;
+{
+ register hash_member *memberptr;
+
+ memberptr = (hashtable->table)[hashcode % (hashtable->size)];
+ while (memberptr) {
+ if ((*compare) (key, memberptr->data)) {
+ return TRUE; /* Entry does exist */
+ }
+ 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;
+ hash_cmpfp compare;
+ hash_datum *key, *element;
+{
+ hash_member *temp;
+
+ hashcode %= hashtable->size;
+ if (hash_Exists(hashtable, hashcode, compare, key)) {
+ return -1; /* At least one entry already exists */
+ }
+ temp = (hash_member *) malloc(sizeof(hash_member));
+ if (!temp)
+ return -1; /* malloc failed! */
+
+ temp->data = element;
+ temp->next = (hashtable->table)[hashcode];
+ (hashtable->table)[hashcode] = temp;
+ return 0; /* Success */
+}
+
+
+
+/*
+ * Delete all data elements which match the given key. If at least one
+ * element is found and the deletion is successful, 0 is returned.
+ * If no matching elements can be found in the hash table, -1 is returned.
+ */
+
+int
+hash_Delete(hashtable, hashcode, compare, key, free_data)
+ hash_tbl *hashtable;
+ unsigned hashcode;
+ hash_cmpfp compare;
+ hash_datum *key;
+ hash_freefp free_data;
+{
+ hash_member *memberptr, *tempptr;
+ hash_member *previous = NULL;
+ 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_FreeMembers() from deleting the whole list!
+ */
+ memberptr->next = NULL;
+ hashi_FreeMembers(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_FreeMembers(). . . .
+ */
+ tempptr->next = NULL;
+ hashi_FreeMembers(tempptr, free_data);
+ retval = 0;
+ } else {
+ previous = memberptr;
+ memberptr = memberptr->next;
+ }
+ }
+ return retval;
+}
+
+
+
+/*
+ * Locate and return the data entry associated with the given key.
+ *
+ * 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;
+ hash_cmpfp compare;
+ hash_datum *key;
+{
+ hash_member *memberptr;
+
+ memberptr = (hashtable->table)[hashcode % (hashtable->size)];
+ while (memberptr) {
+ if ((*compare) (key, memberptr->data)) {
+ return (memberptr->data);
+ }
+ 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);
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/hash.h b/libexec/bootpd/hash.h
new file mode 100644
index 0000000..51d0a5e
--- /dev/null
+++ b/libexec/bootpd/hash.h
@@ -0,0 +1,158 @@
+#ifndef HASH_H
+#define HASH_H
+/* hash.h */
+/************************************************************************
+ 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.
+************************************************************************/
+
+/*
+ * 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
+ */
+
+
+/*
+ * 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 */
+};
+
+/* ANSI function prototypes or empty arg list? */
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+typedef int (*hash_cmpfp) P((hash_datum *, hash_datum *));
+typedef void (*hash_freefp) P((hash_datum *));
+
+extern hash_tbl *hash_Init P((u_int tablesize));
+
+extern void hash_Reset P((hash_tbl *tbl, hash_freefp));
+
+extern unsigned hash_HashFunction P((u_char *str, u_int len));
+
+extern int hash_Exists P((hash_tbl *, u_int code,
+ hash_cmpfp, hash_datum *key));
+
+extern int hash_Insert P((hash_tbl *, u_int code,
+ hash_cmpfp, hash_datum *key,
+ hash_datum *element));
+
+extern int hash_Delete P((hash_tbl *, u_int code,
+ hash_cmpfp, hash_datum *key,
+ hash_freefp));
+
+extern hash_datum *hash_Lookup P((hash_tbl *, u_int code,
+ hash_cmpfp, hash_datum *key));
+
+extern hash_datum *hash_FirstEntry P((hash_tbl *));
+
+extern hash_datum *hash_NextEntry P((hash_tbl *));
+
+#undef P
+
+#endif /* HASH_H */
diff --git a/libexec/bootpd/hwaddr.c b/libexec/bootpd/hwaddr.c
new file mode 100644
index 0000000..840b953
--- /dev/null
+++ b/libexec/bootpd/hwaddr.c
@@ -0,0 +1,294 @@
+/*
+ * hwaddr.c - routines that deal with hardware addresses.
+ * (i.e. Ethernet)
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#if defined(SUNOS) || defined(SVR4)
+#include <sys/sockio.h>
+#endif
+#ifdef SVR4
+#include <sys/stream.h>
+#include <stropts.h>
+#include <fcntl.h>
+#endif
+
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+#include <syslog.h>
+
+#ifndef USE_BFUNCS
+/* Yes, memcpy is OK here (no overlapped copies). */
+#include <memory.h>
+#define bcopy(a,b,c) memcpy(b,a,c)
+#define bzero(p,l) memset(p,0,l)
+#define bcmp(a,b,c) memcmp(a,b,c)
+#endif
+
+/* For BSD 4.4, set arp entry by writing to routing socket */
+#if defined(BSD)
+#if BSD >= 199306
+extern int bsd_arp_set __P((struct in_addr *, char *, int));
+#endif
+#endif
+
+#include "bptypes.h"
+#include "hwaddr.h"
+#include "report.h"
+
+extern int debug;
+
+/*
+ * Hardware address lengths (in bytes) and network name based on hardware
+ * type code. List in order specified by Assigned Numbers RFC; Array index
+ * is hardware type code. Entries marked as zero are unknown to the author
+ * at this time. . . .
+ */
+
+struct hwinfo hwinfolist[] =
+{
+ {0, "Reserved"}, /* Type 0: Reserved (don't use this) */
+ {6, "Ethernet"}, /* Type 1: 10Mb Ethernet (48 bits) */
+ {1, "3Mb Ethernet"}, /* Type 2: 3Mb Ethernet (8 bits) */
+ {0, "AX.25"}, /* Type 3: Amateur Radio AX.25 */
+ {1, "ProNET"}, /* Type 4: Proteon ProNET Token Ring */
+ {0, "Chaos"}, /* Type 5: Chaos */
+ {6, "IEEE 802"}, /* Type 6: IEEE 802 Networks */
+ {0, "ARCNET"} /* Type 7: ARCNET */
+};
+int hwinfocnt = sizeof(hwinfolist) / sizeof(hwinfolist[0]);
+
+
+/*
+ * Setup the arp cache so that IP address 'ia' will be temporarily
+ * bound to hardware address 'ha' of length 'len'.
+ */
+void
+setarp(s, ia, ha, len)
+ int s; /* socket fd */
+ struct in_addr *ia;
+ u_char *ha;
+ int len;
+{
+#ifdef SIOCSARP
+ struct arpreq arpreq; /* Arp request ioctl block */
+ struct sockaddr_in *si;
+#ifdef SVR4
+ int fd;
+ struct strioctl iocb;
+#endif /* SVR4 */
+
+ bzero((caddr_t) & arpreq, sizeof(arpreq));
+ arpreq.arp_flags = ATF_INUSE | ATF_COM;
+
+ /* Set up the protocol address. */
+ arpreq.arp_pa.sa_family = AF_INET;
+ si = (struct sockaddr_in *) &arpreq.arp_pa;
+ si->sin_addr = *ia;
+
+ /* Set up the hardware address. */
+ bcopy(ha, arpreq.arp_ha.sa_data, len);
+
+#ifdef SVR4
+ /*
+ * And now the stuff for System V Rel 4.x which does not
+ * appear to allow SIOCxxx ioctls on a socket descriptor.
+ * Thanks to several people: (all sent the same fix)
+ * Barney Wolff <barney@databus.com>,
+ * bear@upsys.se (Bj|rn Sj|holm),
+ * Michael Kuschke <Michael.Kuschke@Materna.DE>,
+ */
+ if ((fd=open("/dev/arp", O_RDWR)) < 0) {
+ report(LOG_ERR, "open /dev/arp: %s\n", get_errmsg());
+ }
+ iocb.ic_cmd = SIOCSARP;
+ iocb.ic_timout = 0;
+ iocb.ic_dp = (char *)&arpreq;
+ iocb.ic_len = sizeof(arpreq);
+ if (ioctl(fd, I_STR, (caddr_t)&iocb) < 0) {
+ report(LOG_ERR, "ioctl I_STR: %s\n", get_errmsg());
+ }
+ close (fd);
+
+#else /* SVR4 */
+ /*
+ * On SunOS, the ioctl sometimes returns ENXIO, and it
+ * appears to happen when the ARP cache entry you tried
+ * to add is already in the cache. (Sigh...)
+ * XXX - Should this error simply be ignored? -gwr
+ */
+ if (ioctl(s, SIOCSARP, (caddr_t) & arpreq) < 0) {
+ report(LOG_ERR, "ioctl SIOCSARP: %s", get_errmsg());
+ }
+#endif /* SVR4 */
+#else /* SIOCSARP */
+#if defined(BSD) && (BSD >= 199306)
+ bsd_arp_set(ia, ha, len);
+#else /* Not BSD 4.4, and SIOCSARP not defined */
+ /*
+ * Oh well, SIOCSARP is not defined. Just run arp(8).
+ * XXX - Gag!
+ */
+ char buf[256];
+ int status;
+
+ sprintf(buf, "arp -s %s %s temp",
+ inet_ntoa(*ia), haddrtoa(ha, len));
+ if (debug > 2)
+ report(LOG_INFO, buf);
+ status = system(buf);
+ if (status)
+ report(LOG_ERR, "arp failed, exit code=0x%x", status);
+ return;
+#endif /* ! 4.4 BSD */
+#endif /* SIOCSARP */
+}
+
+
+/*
+ * Convert a hardware address to an ASCII string.
+ */
+char *
+haddrtoa(haddr, hlen)
+ u_char *haddr;
+ int hlen;
+{
+ static char haddrbuf[3 * MAXHADDRLEN + 1];
+ char *bufptr;
+
+ if (hlen > MAXHADDRLEN)
+ hlen = MAXHADDRLEN;
+
+ bufptr = haddrbuf;
+ while (hlen > 0) {
+ sprintf(bufptr, "%02X:", (unsigned) (*haddr++ & 0xFF));
+ bufptr += 3;
+ hlen--;
+ }
+ bufptr[-1] = 0;
+ return (haddrbuf);
+}
+
+
+/*
+ * haddr_conv802()
+ * --------------
+ *
+ * Converts a backwards address to a canonical address and a canonical address
+ * to a backwards address.
+ *
+ * INPUTS:
+ * adr_in - pointer to six byte string to convert (unsigned char *)
+ * addr_len - how many bytes to convert
+ *
+ * OUTPUTS:
+ * addr_out - The string is updated to contain the converted address.
+ *
+ * CALLER:
+ * many
+ *
+ * DATA:
+ * Uses conv802table to bit-reverse the address bytes.
+ */
+
+static u_char conv802table[256] =
+{
+ /* 0x00 */ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+ /* 0x08 */ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+ /* 0x10 */ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+ /* 0x18 */ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+ /* 0x20 */ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+ /* 0x28 */ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ /* 0x30 */ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+ /* 0x38 */ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+ /* 0x40 */ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+ /* 0x48 */ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+ /* 0x50 */ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+ /* 0x58 */ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ /* 0x60 */ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+ /* 0x68 */ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+ /* 0x70 */ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+ /* 0x78 */ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+ /* 0x80 */ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
+ /* 0x88 */ 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+ /* 0x90 */ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
+ /* 0x98 */ 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+ /* 0xA0 */ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+ /* 0xA8 */ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+ /* 0xB0 */ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
+ /* 0xB8 */ 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+ /* 0xC0 */ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
+ /* 0xC8 */ 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+ /* 0xD0 */ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+ /* 0xD8 */ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+ /* 0xE0 */ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
+ /* 0xE8 */ 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+ /* 0xF0 */ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
+ /* 0xF8 */ 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF,
+};
+
+void
+haddr_conv802(addr_in, addr_out, len)
+ register u_char *addr_in, *addr_out;
+ int len;
+{
+ u_char *lim;
+
+ lim = addr_out + len;
+ while (addr_out < lim)
+ *addr_out++ = conv802table[*addr_in++];
+}
+
+#if 0
+/*
+ * For the record, here is a program to generate the
+ * bit-reverse table above.
+ */
+static int
+bitrev(n)
+ int n;
+{
+ int i, r;
+
+ r = 0;
+ for (i = 0; i < 8; i++) {
+ r <<= 1;
+ r |= (n & 1);
+ n >>= 1;
+ }
+ return r;
+}
+
+main()
+{
+ int i;
+ for (i = 0; i <= 0xFF; i++) {
+ if ((i & 7) == 0)
+ printf("/* 0x%02X */", i);
+ printf(" 0x%02X,", bitrev(i));
+ if ((i & 7) == 7)
+ printf("\n");
+ }
+}
+
+#endif
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/hwaddr.h b/libexec/bootpd/hwaddr.h
new file mode 100644
index 0000000..dea7158
--- /dev/null
+++ b/libexec/bootpd/hwaddr.h
@@ -0,0 +1,39 @@
+/* hwaddr.h */
+#ifndef HWADDR_H
+#define HWADDR_H
+
+#define MAXHADDRLEN 8 /* Max hw address length in bytes */
+
+/*
+ * This structure holds information about a specific network type. The
+ * length of the network hardware address is stored in "hlen".
+ * The string pointed to by "name" is the cononical name of the network.
+ */
+struct hwinfo {
+ unsigned int hlen;
+ char *name;
+};
+
+extern struct hwinfo hwinfolist[];
+extern int hwinfocnt;
+
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+extern void setarp P((int, struct in_addr *, u_char *, int));
+extern char *haddrtoa P((u_char *, int));
+extern void haddr_conv802 P((u_char *, u_char *, int));
+
+#undef P
+
+/*
+ * Return the length in bytes of a hardware address of the given type.
+ * Return the canonical name of the network of the given type.
+ */
+#define haddrlength(type) ((hwinfolist[(int) (type)]).hlen)
+#define netname(type) ((hwinfolist[(int) (type)]).name)
+
+#endif /* HWADDR_H */
diff --git a/libexec/bootpd/lookup.c b/libexec/bootpd/lookup.c
new file mode 100644
index 0000000..2a30a59
--- /dev/null
+++ b/libexec/bootpd/lookup.c
@@ -0,0 +1,126 @@
+/*
+ * lookup.c - Lookup IP address, HW address, netmask
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#ifdef ETC_ETHERS
+#include <netinet/if_ether.h>
+extern int ether_hostton();
+#endif
+
+#include <netdb.h>
+#include <syslog.h>
+
+#ifndef USE_BFUNCS
+#include <memory.h>
+/* Yes, memcpy is OK here (no overlapped copies). */
+#define bcopy(a,b,c) memcpy(b,a,c)
+#endif
+
+#include "bootp.h"
+#include "lookup.h"
+#include "report.h"
+
+/*
+ * Lookup an Ethernet address and return it.
+ * Return NULL if addr not found.
+ */
+u_char *
+lookup_hwa(hostname, htype)
+ char *hostname;
+ int htype;
+{
+ switch (htype) {
+
+ /* XXX - How is this done on other systems? -gwr */
+#ifdef ETC_ETHERS
+ case HTYPE_ETHERNET:
+ case HTYPE_IEEE802:
+ {
+ static struct ether_addr ea;
+ /* This does a lookup in /etc/ethers */
+ if (ether_hostton(hostname, &ea)) {
+ report(LOG_ERR, "no HW addr for host \"%s\"",
+ hostname);
+ return (u_char *) 0;
+ }
+ return (u_char *) & ea;
+ }
+#endif /* ETC_ETHERS */
+
+ default:
+ report(LOG_ERR, "no lookup for HW addr type %d", htype);
+ } /* switch */
+
+ /* If the system can't do it, just return an error. */
+ return (u_char *) 0;
+}
+
+
+/*
+ * Lookup an IP address.
+ * Return non-zero on failure.
+ */
+int
+lookup_ipa(hostname, result)
+ char *hostname;
+ u_int32 *result;
+{
+ struct hostent *hp;
+ hp = gethostbyname(hostname);
+ if (!hp)
+ return -1;
+ bcopy(hp->h_addr, result, sizeof(*result));
+ return 0;
+}
+
+
+/*
+ * Lookup a netmask
+ * Return non-zero on failure.
+ *
+ * XXX - This is OK as a default, but to really make this automatic,
+ * we would need to get the subnet mask from the ether interface.
+ * If this is wrong, specify the correct value in the bootptab.
+ */
+int
+lookup_netmask(addr, result)
+ u_int32 addr; /* both in network order */
+ u_int32 *result;
+{
+ int32 m, a;
+
+ a = ntohl(addr);
+ m = 0;
+
+ if (IN_CLASSA(a))
+ m = IN_CLASSA_NET;
+
+ if (IN_CLASSB(a))
+ m = IN_CLASSB_NET;
+
+ if (IN_CLASSC(a))
+ m = IN_CLASSC_NET;
+
+ if (!m)
+ return -1;
+ *result = htonl(m);
+ return 0;
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/lookup.h b/libexec/bootpd/lookup.h
new file mode 100644
index 0000000..04805d8
--- /dev/null
+++ b/libexec/bootpd/lookup.h
@@ -0,0 +1,15 @@
+/* lookup.h */
+
+#include "bptypes.h" /* for int32, u_int32 */
+
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+extern u_char *lookup_hwa P((char *hostname, int htype));
+extern int lookup_ipa P((char *hostname, u_int32 *addr));
+extern int lookup_netmask P((u_int32 addr, u_int32 *mask));
+
+#undef P
diff --git a/libexec/bootpd/patchlevel.h b/libexec/bootpd/patchlevel.h
new file mode 100644
index 0000000..782959e
--- /dev/null
+++ b/libexec/bootpd/patchlevel.h
@@ -0,0 +1,3 @@
+/* patchlevel.h */
+#define VERSION "2.4"
+#define PATCHLEVEL 1
diff --git a/libexec/bootpd/readfile.c b/libexec/bootpd/readfile.c
new file mode 100644
index 0000000..4a9d993
--- /dev/null
+++ b/libexec/bootpd/readfile.c
@@ -0,0 +1,2097 @@
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+
+#ifndef lint
+static char rcsid[] = "$Id: readfile.c,v 1.1.1.1 1994/09/10 14:44:55 csgr 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 <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <syslog.h>
+
+#ifndef USE_BFUNCS
+#include <memory.h>
+/* Yes, memcpy is OK here (no overlapped copies). */
+#define bcopy(a,b,c) memcpy(b,a,c)
+#define bzero(p,l) memset(p,0,l)
+#define bcmp(a,b,c) memcmp(a,b,c)
+#endif
+
+#include "bootp.h"
+#include "hash.h"
+#include "hwaddr.h"
+#include "lookup.h"
+#include "readfile.h"
+#include "report.h"
+#include "tzone.h"
+#include "bootpd.h"
+
+#define HASHTABLESIZE 257 /* Hash table size (prime) */
+
+/* Non-standard hardware address type (see bootp.h) */
+#define HTYPE_DIRECT 0
+
+/* Error codes returned by eval_symbol: */
+#define SUCCESS 0
+#define E_END_OF_ENTRY (-1)
+#define E_SYNTAX_ERROR (-2)
+#define E_UNKNOWN_SYMBOL (-3)
+#define E_BAD_IPADDR (-4)
+#define E_BAD_HWADDR (-5)
+#define E_BAD_LONGWORD (-6)
+#define E_BAD_HWATYPE (-7)
+#define E_BAD_PATHNAME (-8)
+#define E_BAD_VALUE (-9)
+
+/* Tag idendities. */
+#define SYM_NULL 0
+#define SYM_BOOTFILE 1
+#define SYM_COOKIE_SERVER 2
+#define SYM_DOMAIN_SERVER 3
+#define SYM_GATEWAY 4
+#define SYM_HWADDR 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_DUMP_FILE 24
+#define SYM_DOMAIN_NAME 25
+#define SYM_SWAP_SERVER 26
+#define SYM_ROOT_PATH 27
+#define SYM_EXTEN_FILE 28
+#define SYM_REPLY_ADDR 29
+#define SYM_NIS_DOMAIN 30 /* RFC 1533 */
+#define SYM_NIS_SERVER 31 /* RFC 1533 */
+#define SYM_NTP_SERVER 32 /* RFC 1533 */
+#define SYM_EXEC_FILE 33 /* YORK_EX_OPTION */
+#define SYM_MSG_SIZE 34
+#define SYM_MIN_WAIT 35
+/* XXX - Add new tags here */
+
+#define OP_ADDITION 1 /* Operations on tags */
+#define OP_DELETION 2
+#define OP_BOOLEAN 3
+
+#define MAXINADDRS 16 /* Max size of an IP address list */
+#define MAXBUFLEN 256 /* 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 int32 modtime = 0; /* Last modification time of bootptab */
+PRIVATE char *current_hostname; /* Name of the current entry. */
+PRIVATE char current_tagname[8];
+
+/*
+ * List of symbolic names used in the bootptab file. The order and actual
+ * 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_DUMP_FILE},
+ {"dn", SYM_DOMAIN_NAME},
+ {"ds", SYM_DOMAIN_SERVER},
+ {"ef", SYM_EXTEN_FILE},
+ {"ex", SYM_EXEC_FILE}, /* YORK_EX_OPTION */
+ {"gw", SYM_GATEWAY},
+ {"ha", SYM_HWADDR},
+ {"hd", SYM_HOMEDIR},
+ {"hn", SYM_NAME_SWITCH},
+ {"ht", SYM_HTYPE},
+ {"im", SYM_IMPRESS_SERVER},
+ {"ip", SYM_IPADDR},
+ {"lg", SYM_LOG_SERVER},
+ {"lp", SYM_LPR_SERVER},
+ {"ms", SYM_MSG_SIZE},
+ {"mw", SYM_MIN_WAIT},
+ {"ns", SYM_NAME_SERVER},
+ {"nt", SYM_NTP_SERVER},
+ {"ra", SYM_REPLY_ADDR},
+ {"rl", SYM_RLP_SERVER},
+ {"rp", SYM_ROOT_PATH},
+ {"sa", SYM_BOOT_SERVER},
+ {"sm", SYM_SUBNET_MASK},
+ {"sw", SYM_SWAP_SERVER},
+ {"tc", SYM_SIMILAR_ENTRY},
+ {"td", SYM_TFTPDIR},
+ {"to", SYM_TIME_OFFSET},
+ {"ts", SYM_TIME_SERVER},
+ {"vm", SYM_VENDOR_MAGIC},
+ {"yd", SYM_NIS_DOMAIN},
+ {"ys", SYM_NIS_SERVER},
+ /* XXX - Add new tags here */
+};
+
+
+/*
+ * 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.
+ */
+
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+extern boolean iplookcmp();
+boolean nmcmp P((hash_datum *, hash_datum *));
+
+PRIVATE void
+ adjust P((char **));
+PRIVATE void
+ del_string P((struct shared_string *));
+PRIVATE void
+ del_bindata P((struct shared_bindata *));
+PRIVATE void
+ del_iplist P((struct in_addr_list *));
+PRIVATE void
+ eat_whitespace P((char **));
+PRIVATE int
+ eval_symbol P((char **, struct host *));
+PRIVATE void
+ fill_defaults P((struct host *, char **));
+PRIVATE void
+ free_host P((hash_datum *));
+PRIVATE struct in_addr_list *
+ get_addresses P((char **));
+PRIVATE struct shared_string *
+ get_shared_string P((char **));
+PRIVATE char *
+ get_string P((char **, char *, u_int *));
+PRIVATE u_int32
+ get_u_long P((char **));
+PRIVATE boolean
+ goodname P((char *));
+PRIVATE boolean
+ hwinscmp P((hash_datum *, hash_datum *));
+PRIVATE int
+ interp_byte P((char **, byte *));
+PRIVATE void
+ makelower P((char *));
+PRIVATE boolean
+ nullcmp P((hash_datum *, hash_datum *));
+PRIVATE int
+ process_entry P((struct host *, char *));
+PRIVATE int
+ process_generic P((char **, struct shared_bindata **, u_int));
+PRIVATE byte *
+ prs_haddr P((char **, u_int));
+PRIVATE int
+ prs_inetaddr P((char **, u_int32 *));
+PRIVATE void
+ read_entry P((FILE *, char *, u_int *));
+PRIVATE char *
+ smalloc P((u_int));
+
+#undef P
+
+
+/*
+ * Vendor magic cookies for CMU and RFC1048
+ */
+u_char vm_cmu[4] = VM_CMU;
+u_char vm_rfc1048[4] = VM_RFC1048;
+
+/*
+ * Main hash tables
+ */
+hash_tbl *hwhashtable;
+hash_tbl *iphashtable;
+hash_tbl *nmhashtable;
+
+/*
+ * Allocate hash tables for hardware address, ip address, and hostname
+ * (shared by bootpd and bootpef)
+ */
+void
+rdtab_init()
+{
+ hwhashtable = hash_Init(HASHTABLESIZE);
+ iphashtable = hash_Init(HASHTABLESIZE);
+ nmhashtable = hash_Init(HASHTABLESIZE);
+ if (!(hwhashtable && iphashtable && nmhashtable)) {
+ report(LOG_ERR, "Unable to allocate hash tables.");
+ exit(1);
+ }
+}
+
+
+/*
+ * Read bootptab database file. Avoid rereading the file if the
+ * write date hasn't changed since the last time we read it.
+ */
+
+void
+readtab(force)
+ int force;
+{
+ struct host *hp;
+ FILE *fp;
+ struct stat st;
+ unsigned hashcode, buflen;
+ static char buffer[MAXENTRYLEN];
+
+ /*
+ * Check the last modification time.
+ */
+ if (stat(bootptab, &st) < 0) {
+ report(LOG_ERR, "stat on \"%s\": %s",
+ bootptab, get_errmsg());
+ return;
+ }
+#ifdef DEBUG
+ if (debug > 3) {
+ char timestr[28];
+ strcpy(timestr, ctime(&(st.st_mtime)));
+ /* zap the newline */
+ timestr[24] = '\0';
+ report(LOG_INFO, "bootptab mtime: %s",
+ timestr);
+ }
+#endif
+ if ((force == 0) &&
+ (st.st_mtime == modtime) &&
+ st.st_nlink) {
+ /*
+ * hasn't been modified or deleted yet.
+ */
+ return;
+ }
+ if (debug)
+ report(LOG_INFO, "reading %s\"%s\"",
+ (modtime != 0L) ? "new " : "",
+ bootptab);
+
+ /*
+ * Open bootptab file.
+ */
+ if ((fp = fopen(bootptab, "r")) == NULL) {
+ report(LOG_ERR, "error opening \"%s\": %s", bootptab, get_errmsg());
+ return;
+ }
+ /*
+ * Record file modification time.
+ */
+ if (fstat(fileno(fp), &st) < 0) {
+ report(LOG_ERR, "fstat: %s", get_errmsg());
+ fclose(fp);
+ return;
+ }
+ 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));
+ bzero((char *) hp, sizeof(*hp));
+ /* the link count it zero */
+
+ /*
+ * Get individual info
+ */
+ if (process_entry(hp, buffer) < 0) {
+ hp->linkcount = 1;
+ free_host((hash_datum *) hp);
+ continue;
+ }
+ /*
+ * If this is not a dummy entry, and the IP or HW
+ * address is not yet set, try to get them here.
+ * Dummy entries have . as first char of name.
+ */
+ if (goodname(hp->hostname->string)) {
+ char *hn = hp->hostname->string;
+ u_int32 value;
+ if (hp->flags.iaddr == 0) {
+ if (lookup_ipa(hn, &value)) {
+ report(LOG_ERR, "can not get IP addr for %s", hn);
+ report(LOG_ERR, "(dummy names should start with '.')");
+ } else {
+ hp->iaddr.s_addr = value;
+ hp->flags.iaddr = TRUE;
+ }
+ }
+ /* Set default subnet mask. */
+ if (hp->flags.subnet_mask == 0) {
+ if (lookup_netmask(hp->iaddr.s_addr, &value)) {
+ report(LOG_ERR, "can not get netmask for %s", hn);
+ } else {
+ hp->subnet_mask.s_addr = value;
+ hp->flags.subnet_mask = TRUE;
+ }
+ }
+ }
+ if (hp->flags.iaddr) {
+ nhosts++;
+ }
+ /* Register by HW addr if known. */
+ if (hp->flags.htype && hp->flags.haddr) {
+ /* We will either insert it or free it. */
+ hp->linkcount++;
+ hashcode = hash_HashFunction(hp->haddr, haddrlength(hp->htype));
+ if (hash_Insert(hwhashtable, hashcode, hwinscmp, hp, hp) < 0) {
+ report(LOG_NOTICE, "duplicate %s address: %s",
+ netname(hp->htype),
+ haddrtoa(hp->haddr, hp->htype));
+ free_host((hash_datum *) hp);
+ continue;
+ }
+ }
+ /* Register by IP addr if known. */
+ if (hp->flags.iaddr) {
+ hashcode = hash_HashFunction((u_char *) & (hp->iaddr.s_addr), 4);
+ if (hash_Insert(iphashtable, hashcode, nullcmp, hp, hp) < 0) {
+ report(LOG_ERR,
+ "hash_Insert() failed on IP address insertion");
+ } else {
+ /* Just inserted the host struct in a new hash list. */
+ hp->linkcount++;
+ }
+ }
+ /* Register by Name (always known) */
+ hashcode = hash_HashFunction((u_char *) hp->hostname->string,
+ strlen(hp->hostname->string));
+ if (hash_Insert(nmhashtable, hashcode, nullcmp,
+ hp->hostname->string, hp) < 0) {
+ report(LOG_ERR,
+ "hash_Insert() failed on insertion of hostname: \"%s\"",
+ hp->hostname->string);
+ } else {
+ /* Just inserted the host struct in a new hash list. */
+ hp->linkcount++;
+ }
+
+ nentries++;
+ }
+
+ fclose(fp);
+ if (debug)
+ report(LOG_INFO, "read %d entries (%d hosts) from \"%s\"",
+ nentries, nhosts, bootptab);
+ return;
+}
+
+
+
+/*
+ * Read an entire host entry from the file pointed to by "fp" and insert it
+ * into the memory pointed to by "buffer". Leading whitespace and comments
+ * 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;
+ char *msg;
+
+ if (!host || *src == '\0') {
+ return -1;
+ }
+ host->hostname = get_shared_string(&src);
+#if 0
+ /* Be more liberal for the benefit of dummy tag names. */
+ if (!goodname(host->hostname->string)) {
+ report(LOG_ERR, "bad hostname: \"%s\"", host->hostname->string);
+ del_string(host->hostname);
+ return -1;
+ }
+#endif
+ current_hostname = host->hostname->string;
+ adjust(&src);
+ while (TRUE) {
+ retval = eval_symbol(&src, host);
+ if (retval == SUCCESS) {
+ adjust(&src);
+ continue;
+ }
+ if (retval == E_END_OF_ENTRY) {
+ /* The default subnet mask is set in readtab() */
+ return 0;
+ }
+ /* Some kind of error. */
+ switch (retval) {
+ case E_SYNTAX_ERROR:
+ msg = "bad syntax";
+ break;
+ case E_UNKNOWN_SYMBOL:
+ msg = "unknown symbol";
+ break;
+ case E_BAD_IPADDR:
+ msg = "bad INET address";
+ break;
+ case E_BAD_HWADDR:
+ msg = "bad hardware address";
+ break;
+ case E_BAD_LONGWORD:
+ msg = "bad longword value";
+ break;
+ case E_BAD_HWATYPE:
+ msg = "bad HW address type";
+ break;
+ case E_BAD_PATHNAME:
+ msg = "bad pathname (need leading '/')";
+ case E_BAD_VALUE:
+ msg = "bad value";
+ default:
+ msg = "unkown error";
+ break;
+ } /* switch */
+ report(LOG_ERR, "in entry named \"%s\", symbol \"%s\": %s",
+ current_hostname, current_tagname, msg);
+ return -1;
+ }
+}
+
+
+/*
+ * Macros for use in the function below:
+ */
+
+/* Parse one INET address stored directly in MEMBER. */
+#define PARSE_IA1(MEMBER) do \
+{ \
+ if (optype == OP_BOOLEAN) \
+ return E_SYNTAX_ERROR; \
+ hp->flags.MEMBER = FALSE; \
+ if (optype == OP_ADDITION) { \
+ if (prs_inetaddr(symbol, &value) < 0) \
+ return E_BAD_IPADDR; \
+ hp->MEMBER.s_addr = value; \
+ hp->flags.MEMBER = TRUE; \
+ } \
+} while (0)
+
+/* Parse a list of INET addresses pointed to by MEMBER */
+#define PARSE_IAL(MEMBER) do \
+{ \
+ if (optype == OP_BOOLEAN) \
+ return E_SYNTAX_ERROR; \
+ if (hp->flags.MEMBER) { \
+ hp->flags.MEMBER = FALSE; \
+ assert(hp->MEMBER); \
+ del_iplist(hp->MEMBER); \
+ hp->MEMBER = NULL; \
+ } \
+ if (optype == OP_ADDITION) { \
+ hp->MEMBER = get_addresses(symbol); \
+ if (hp->MEMBER == NULL) \
+ return E_SYNTAX_ERROR; \
+ hp->flags.MEMBER = TRUE; \
+ } \
+} while (0)
+
+/* Parse a shared string pointed to by MEMBER */
+#define PARSE_STR(MEMBER) do \
+{ \
+ if (optype == OP_BOOLEAN) \
+ return E_SYNTAX_ERROR; \
+ if (hp->flags.MEMBER) { \
+ hp->flags.MEMBER = FALSE; \
+ assert(hp->MEMBER); \
+ del_string(hp->MEMBER); \
+ hp->MEMBER = NULL; \
+ } \
+ if (optype == OP_ADDITION) { \
+ hp->MEMBER = get_shared_string(symbol); \
+ if (hp->MEMBER == NULL) \
+ return E_SYNTAX_ERROR; \
+ hp->flags.MEMBER = TRUE; \
+ } \
+} while (0)
+
+/* Parse an integer value for MEMBER */
+#define PARSE_INT(MEMBER) do \
+{ \
+ if (optype == OP_BOOLEAN) \
+ return E_SYNTAX_ERROR; \
+ hp->flags.MEMBER = FALSE; \
+ if (optype == OP_ADDITION) { \
+ value = get_u_long(symbol); \
+ hp->MEMBER = value; \
+ hp->flags.MEMBER = TRUE; \
+ } \
+} while (0)
+
+/*
+ * Evaluate the two-character tag symbol pointed to by "symbol" and place
+ * the data in the structure pointed to by "hp". The pointer pointed to
+ * by "symbol" is updated to point past the source string (but may not
+ * point to the next tag entry).
+ *
+ * Obviously, this need a few more comments. . . .
+ */
+PRIVATE int
+eval_symbol(symbol, hp)
+ char **symbol;
+ struct host *hp;
+{
+ char tmpstr[MAXSTRINGLEN];
+ byte *tmphaddr;
+ struct shared_string *ss;
+ struct symbolmap *symbolptr;
+ u_int32 value;
+ int32 timeoff;
+ int i, numsymbols;
+ unsigned len;
+ int optype; /* Indicates boolean, addition, or deletion */
+
+ eat_whitespace(symbol);
+
+ /* Make sure this is set before returning. */
+ current_tagname[0] = (*symbol)[0];
+ current_tagname[1] = (*symbol)[1];
+ current_tagname[2] = 0;
+
+ if ((*symbol)[0] == '\0') {
+ return E_END_OF_ENTRY;
+ }
+ if ((*symbol)[0] == ':') {
+ return SUCCESS;
+ }
+ if ((*symbol)[0] == 'T') { /* generic symbol */
+ (*symbol)++;
+ value = get_u_long(symbol);
+ sprintf(current_tagname, "T%d", value);
+ eat_whitespace(symbol);
+ if ((*symbol)[0] != '=') {
+ return E_SYNTAX_ERROR;
+ }
+ (*symbol)++;
+ if (!(hp->generic)) {
+ hp->generic = (struct shared_bindata *)
+ smalloc(sizeof(struct shared_bindata));
+ }
+ if (process_generic(symbol, &(hp->generic), (byte) (value & 0xFF)))
+ return E_SYNTAX_ERROR;
+ hp->flags.generic = TRUE;
+ return SUCCESS;
+ }
+ /*
+ * Determine the type of operation to be done on this symbol
+ */
+ switch ((*symbol)[2]) {
+ case '=':
+ optype = OP_ADDITION;
+ break;
+ 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;
+
+ eat_whitespace(symbol);
+
+ /* The cases below are in order by symbolcode value. */
+ switch (symbolptr->symbolcode) {
+
+ case SYM_BOOTFILE:
+ PARSE_STR(bootfile);
+ break;
+
+ case SYM_COOKIE_SERVER:
+ PARSE_IAL(cookie_server);
+ break;
+
+ case SYM_DOMAIN_SERVER:
+ PARSE_IAL(domain_server);
+ break;
+
+ case SYM_GATEWAY:
+ PARSE_IAL(gateway);
+ break;
+
+ case SYM_HWADDR:
+ if (optype == OP_BOOLEAN)
+ return E_SYNTAX_ERROR;
+ hp->flags.haddr = FALSE;
+ if (optype == OP_ADDITION) {
+ /* Default the HW type to Ethernet */
+ if (hp->flags.htype == 0) {
+ hp->flags.htype = TRUE;
+ hp->htype = HTYPE_ETHERNET;
+ }
+ tmphaddr = prs_haddr(symbol, hp->htype);
+ if (!tmphaddr)
+ return E_BAD_HWADDR;
+ bcopy(tmphaddr, hp->haddr, haddrlength(hp->htype));
+ hp->flags.haddr = TRUE;
+ }
+ break;
+
+ case SYM_HOMEDIR:
+ PARSE_STR(homedir);
+ break;
+
+ case SYM_HTYPE:
+ if (optype == OP_BOOLEAN)
+ return E_SYNTAX_ERROR;
+ hp->flags.htype = FALSE;
+ if (optype == OP_ADDITION) {
+ value = 0L; /* Assume an illegal value */
+ eat_whitespace(symbol);
+ if (isdigit(**symbol)) {
+ value = get_u_long(symbol);
+ } else {
+ len = sizeof(tmpstr);
+ (void) get_string(symbol, tmpstr, &len);
+ makelower(tmpstr);
+ numsymbols = sizeof(htnamemap) /
+ sizeof(struct htypename);
+ for (i = 0; i < numsymbols; i++) {
+ if (!strcmp(htnamemap[i].name, tmpstr)) {
+ break;
+ }
+ }
+ if (i < numsymbols) {
+ value = htnamemap[i].htype;
+ }
+ }
+ if (value >= hwinfocnt) {
+ return E_BAD_HWATYPE;
+ }
+ hp->htype = (byte) (value & 0xFF);
+ hp->flags.htype = TRUE;
+ }
+ break;
+
+ case SYM_IMPRESS_SERVER:
+ PARSE_IAL(impress_server);
+ break;
+
+ case SYM_IPADDR:
+ PARSE_IA1(iaddr);
+ break;
+
+ case SYM_LOG_SERVER:
+ PARSE_IAL(log_server);
+ break;
+
+ case SYM_LPR_SERVER:
+ PARSE_IAL(lpr_server);
+ break;
+
+ case SYM_NAME_SERVER:
+ PARSE_IAL(name_server);
+ break;
+
+ case SYM_RLP_SERVER:
+ PARSE_IAL(rlp_server);
+ break;
+
+ case SYM_SUBNET_MASK:
+ PARSE_IA1(subnet_mask);
+ break;
+
+ case SYM_TIME_OFFSET:
+ if (optype == OP_BOOLEAN)
+ return E_SYNTAX_ERROR;
+ hp->flags.time_offset = FALSE;
+ if (optype == OP_ADDITION) {
+ len = sizeof(tmpstr);
+ (void) get_string(symbol, tmpstr, &len);
+ if (!strncmp(tmpstr, "auto", 4)) {
+ hp->time_offset = secondswest;
+ } else {
+ if (sscanf(tmpstr, "%d", &timeoff) != 1)
+ return E_BAD_LONGWORD;
+ hp->time_offset = timeoff;
+ }
+ hp->flags.time_offset = TRUE;
+ }
+ break;
+
+ case SYM_TIME_SERVER:
+ PARSE_IAL(time_server);
+ break;
+
+ case SYM_VENDOR_MAGIC:
+ if (optype == OP_BOOLEAN)
+ return E_SYNTAX_ERROR;
+ hp->flags.vm_cookie = FALSE;
+ if (optype == OP_ADDITION) {
+ if (strncmp(*symbol, "auto", 4)) {
+ /* The string is not "auto" */
+ if (!strncmp(*symbol, "rfc", 3)) {
+ bcopy(vm_rfc1048, hp->vm_cookie, 4);
+ } else if (!strncmp(*symbol, "cmu", 3)) {
+ bcopy(vm_cmu, hp->vm_cookie, 4);
+ } else {
+ if (!isdigit(**symbol))
+ return E_BAD_IPADDR;
+ if (prs_inetaddr(symbol, &value) < 0)
+ return E_BAD_IPADDR;
+ bcopy(&value, hp->vm_cookie, 4);
+ }
+ hp->flags.vm_cookie = TRUE;
+ }
+ }
+ 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:
+ PARSE_IA1(bootserver);
+ break;
+
+ case SYM_TFTPDIR:
+ PARSE_STR(tftpdir);
+ if ((hp->tftpdir != NULL) &&
+ (hp->tftpdir->string[0] != '/'))
+ return E_BAD_PATHNAME;
+ break;
+
+ case SYM_DUMP_FILE:
+ PARSE_STR(dump_file);
+ break;
+
+ case SYM_DOMAIN_NAME:
+ PARSE_STR(domain_name);
+ break;
+
+ case SYM_SWAP_SERVER:
+ PARSE_IA1(swap_server);
+ break;
+
+ case SYM_ROOT_PATH:
+ PARSE_STR(root_path);
+ break;
+
+ case SYM_EXTEN_FILE:
+ PARSE_STR(exten_file);
+ break;
+
+ case SYM_REPLY_ADDR:
+ PARSE_IA1(reply_addr);
+ break;
+
+ case SYM_NIS_DOMAIN:
+ PARSE_STR(nis_domain);
+ break;
+
+ case SYM_NIS_SERVER:
+ PARSE_IAL(nis_server);
+ break;
+
+ case SYM_NTP_SERVER:
+ PARSE_IAL(ntp_server);
+ break;
+
+#ifdef YORK_EX_OPTION
+ case SYM_EXEC_FILE:
+ PARSE_STR(exec_file);
+ break;
+#endif
+
+ case SYM_MSG_SIZE:
+ PARSE_INT(msg_size);
+ if (hp->msg_size < BP_MINPKTSZ ||
+ hp->msg_size > MAX_MSG_SIZE)
+ return E_BAD_VALUE;
+ break;
+
+ case SYM_MIN_WAIT:
+ PARSE_INT(min_wait);
+ if (hp->min_wait < 0)
+ return E_BAD_VALUE;
+ break;
+
+ /* XXX - Add new tags here */
+
+ default:
+ return E_UNKNOWN_SYMBOL;
+
+ } /* switch symbolcode */
+
+ return SUCCESS;
+}
+#undef PARSE_IA1
+#undef PARSE_IAL
+#undef PARSE_STR
+
+
+
+
+/*
+ * Read a string from the buffer indirectly pointed to through "src" and
+ * move it into the buffer pointed to by "dest". A pointer to the maximum
+ * 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 int
+process_generic(src, dest, tagvalue)
+ char **src;
+ struct shared_bindata **dest;
+ u_int tagvalue;
+{
+ byte tmpbuf[MAXBUFLEN];
+ byte *str;
+ struct shared_bindata *bdata;
+ u_int newlength, oldlength;
+
+ str = tmpbuf;
+ *str++ = (tagvalue & 0xFF); /* Store tag value */
+ str++; /* Skip over length field */
+ if ((*src)[0] == '"') { /* ASCII data */
+ newlength = sizeof(tmpbuf) - 2; /* Set maximum allowed length */
+ (void) get_string(src, (char *) str, &newlength);
+ newlength++; /* null terminator */
+ } else { /* Numeric data */
+ newlength = 0;
+ while (newlength < sizeof(tmpbuf) - 2) {
+ if (interp_byte(src, str++) < 0)
+ break;
+ newlength++;
+ if (**src == '.') {
+ (*src)++;
+ }
+ }
+ }
+ if ((*src)[0] != ':')
+ return -1;
+
+ tmpbuf[1] = (newlength & 0xFF);
+ oldlength = ((*dest)->length);
+ bdata = (struct shared_bindata *) smalloc(sizeof(struct shared_bindata)
+ + oldlength + newlength + 1);
+ if (oldlength > 0) {
+ bcopy((*dest)->data, bdata->data, oldlength);
+ }
+ bcopy(tmpbuf, bdata->data + oldlength, newlength + 2);
+ bdata->length = oldlength + newlength + 2;
+ bdata->linkcount = 1;
+ if (*dest) {
+ del_bindata(*dest);
+ }
+ *dest = bdata;
+ return 0;
+}
+
+
+
+/*
+ * Verify that the given string makes sense as a hostname (according to
+ * Appendix 1, page 29 of RFC882).
+ *
+ * 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 == '_') )
+ {
+ 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(d1, d2)
+ hash_datum *d1, *d2;
+{
+ return FALSE;
+}
+
+
+/*
+ * Function for comparing a string with the hostname field of a host
+ * structure.
+ */
+
+boolean
+nmcmp(d1, d2)
+ hash_datum *d1, *d2;
+{
+ char *name = (char *) d1; /* XXX - OK? */
+ struct host *hp = (struct host *) d2;
+
+ 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(d1, d2)
+ hash_datum *d1, *d2;
+{
+ struct host *host1 = (struct host *) d1;
+ struct host *host2 = (struct host *) d2;
+
+ if (host1->htype != host2->htype) {
+ return FALSE;
+ }
+ if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) {
+ return FALSE;
+ }
+ /* XXX - Is the subnet_mask field set yet? */
+ if ((host1->subnet_mask.s_addr) == (host2->subnet_mask.s_addr)) {
+ if (((host1->iaddr.s_addr) & (host1->subnet_mask.s_addr)) !=
+ ((host2->iaddr.s_addr) & (host2->subnet_mask.s_addr)))
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+/*
+ * Macros for use in the function below:
+ */
+
+#define DUP_COPY(MEMBER) do \
+{ \
+ if (!hp->flags.MEMBER) { \
+ if ((hp->flags.MEMBER = hp2->flags.MEMBER) != 0) { \
+ hp->MEMBER = hp2->MEMBER; \
+ } \
+ } \
+} while (0)
+
+#define DUP_LINK(MEMBER) do \
+{ \
+ if (!hp->flags.MEMBER) { \
+ if ((hp->flags.MEMBER = hp2->flags.MEMBER) != 0) { \
+ assert(hp2->MEMBER); \
+ hp->MEMBER = hp2->MEMBER; \
+ (hp->MEMBER->linkcount)++; \
+ } \
+ } \
+} while (0)
+
+/*
+ * Process the "similar entry" symbol.
+ *
+ * The host specified as the value of the "tc" symbol is used as a template
+ * for the current host entry. Symbol values not explicitly set in the
+ * current host entry are inferred from the template entry.
+ */
+PRIVATE void
+fill_defaults(hp, src)
+ struct host *hp;
+ char **src;
+{
+ unsigned int tlen, hashcode;
+ struct host *hp2;
+ char tstring[MAXSTRINGLEN];
+
+ tlen = sizeof(tstring);
+ (void) get_string(src, tstring, &tlen);
+ hashcode = hash_HashFunction((u_char *) tstring, tlen);
+ hp2 = (struct host *) hash_Lookup(nmhashtable, hashcode, nmcmp, tstring);
+
+ if (hp2 == NULL) {
+ report(LOG_ERR, "can't find tc=\"%s\"", tstring);
+ return;
+ }
+ DUP_LINK(bootfile);
+ DUP_LINK(cookie_server);
+ DUP_LINK(domain_server);
+ DUP_LINK(gateway);
+ /* haddr not copied */
+ DUP_LINK(homedir);
+ DUP_COPY(htype);
+
+ DUP_LINK(impress_server);
+ /* iaddr not copied */
+ DUP_LINK(log_server);
+ DUP_LINK(lpr_server);
+ DUP_LINK(name_server);
+ DUP_LINK(rlp_server);
+
+ DUP_COPY(subnet_mask);
+ DUP_COPY(time_offset);
+ DUP_LINK(time_server);
+
+ if (!hp->flags.vm_cookie) {
+ if ((hp->flags.vm_cookie = hp2->flags.vm_cookie)) {
+ bcopy(hp2->vm_cookie, hp->vm_cookie, 4);
+ }
+ }
+ if (!hp->flags.name_switch) {
+ if ((hp->flags.name_switch = hp2->flags.name_switch)) {
+ hp->flags.send_name = hp2->flags.send_name;
+ }
+ }
+ if (!hp->flags.bootsize) {
+ if ((hp->flags.bootsize = hp2->flags.bootsize)) {
+ hp->flags.bootsize_auto = hp2->flags.bootsize_auto;
+ hp->bootsize = hp2->bootsize;
+ }
+ }
+ DUP_COPY(bootserver);
+
+ DUP_LINK(tftpdir);
+ DUP_LINK(dump_file);
+ DUP_LINK(domain_name);
+
+ DUP_COPY(swap_server);
+ DUP_LINK(root_path);
+ DUP_LINK(exten_file);
+
+ DUP_COPY(reply_addr);
+
+ DUP_LINK(nis_domain);
+ DUP_LINK(nis_server);
+ DUP_LINK(ntp_server);
+
+#ifdef YORK_EX_OPTION
+ DUP_LINK(exec_file);
+#endif
+
+ DUP_COPY(msg_size);
+ DUP_COPY(min_wait);
+
+ /* XXX - Add new tags here */
+
+ DUP_LINK(generic);
+
+}
+#undef DUP_COPY
+#undef DUP_LINK
+
+
+
+/*
+ * This function adjusts the caller's pointer to point just past the
+ * first-encountered colon. If it runs into a null character, it leaves
+ * the pointer pointing to it.
+ */
+
+PRIVATE void
+adjust(s)
+ char **s;
+{
+ 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. . . .
+ * (Yea, because it usually makes code slower... -gwr)
+ *
+ */
+
+
+
+/*
+ * "src" points to a character pointer which points to an ASCII string of
+ * whitespace-separated IP addresses. A pointer to an in_addr_list
+ * 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 (isspace(**src) || (**src == ',')) {
+ (*src)++;
+ }
+ if (!**src) { /* Quit if nothing more */
+ break;
+ }
+ if (prs_inetaddr(src, &(address1->s_addr)) < 0) {
+ break;
+ }
+ address1++; /* Point to next address slot */
+ }
+ 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 int
+prs_inetaddr(src, result)
+ char **src;
+ u_int32 *result;
+{
+ char tmpstr[MAXSTRINGLEN];
+ register u_int32 value;
+ u_int32 parts[4], *pp;
+ int n;
+ char *s, *t;
+
+#if 1 /* XXX - experimental */
+ /* Leading alpha char causes IP addr lookup. */
+ if (isalpha(**src)) {
+ /* Lookup IP address. */
+ s = *src;
+ t = tmpstr;
+ while ((isalnum(*s) || (*s == '.') ||
+ (*s == '-') || (*s == '_') ) &&
+ (t < &tmpstr[MAXSTRINGLEN - 1]) )
+ *t++ = *s++;
+ *t = '\0';
+ *src = s;
+
+ n = lookup_ipa(tmpstr, result);
+ if (n < 0)
+ report(LOG_ERR, "can not get IP addr for %s", tmpstr);
+ return n;
+ }
+#endif
+
+ /*
+ * Parse an address in Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16-bits)
+ * a.b (with b treated as 24 bits)
+ */
+ pp = parts;
+ loop:
+ /* If it's not a digit, return error. */
+ if (!isdigit(**src))
+ return -1;
+ *pp++ = get_u_long(src);
+ if (**src == '.') {
+ if (pp < (parts + 4)) {
+ (*src)++;
+ goto loop;
+ }
+ return (-1);
+ }
+#if 0
+ /* This is handled by the caller. */
+ if (**src && !(isspace(**src) || (**src == ':'))) {
+ return (-1);
+ }
+#endif
+
+ /*
+ * Construct the address according to
+ * the number of parts specified.
+ */
+ n = pp - parts;
+ switch (n) {
+ case 1: /* a -- 32 bits */
+ value = parts[0];
+ break;
+ case 2: /* a.b -- 8.24 bits */
+ value = (parts[0] << 24) | (parts[1] & 0xFFFFFF);
+ break;
+ case 3: /* a.b.c -- 8.8.16 bits */
+ value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) |
+ (parts[2] & 0xFFFF);
+ break;
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) |
+ ((parts[2] & 0xFF) << 8) | (parts[3] & 0xFF);
+ break;
+ default:
+ return (-1);
+ }
+ *result = htonl(value);
+ return (0);
+}
+
+
+
+/*
+ * "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;
+ u_int htype;
+{
+ static byte haddr[MAXHADDRLEN];
+ byte *hap;
+ char tmpstr[MAXSTRINGLEN];
+ u_int tmplen;
+ unsigned hal;
+ char *p;
+
+ hal = haddrlength(htype); /* Get length of this address type */
+ if (hal <= 0) {
+ report(LOG_ERR, "Invalid addr type for HW addr parse");
+ return NULL;
+ }
+ tmplen = sizeof(tmpstr);
+ get_string(src, tmpstr, &tmplen);
+ p = tmpstr;
+
+#if 1 /* XXX - experimental */
+ /* If it's a valid host name, try to lookup the HW address. */
+ if (goodname(p)) {
+ /* Lookup Hardware Address for hostname. */
+ if ((hap = lookup_hwa(p, htype)) != NULL)
+ return hap; /* success */
+ report(LOG_ERR, "Add 0x prefix if hex value starts with A-F");
+ /* OK, assume it must be numeric. */
+ }
+#endif
+
+ hap = haddr;
+ while (hap < haddr + hal) {
+ if (*p == '.')
+ p++;
+ if (interp_byte(&p, hap++) < 0) {
+ return NULL;
+ }
+ }
+ return haddr;
+}
+
+
+
+/*
+ * "src" is a pointer to a character pointer which in turn points to a
+ * hexadecimal ASCII representation of a byte. This byte is read, the
+ * 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_int32
+get_u_long(src)
+ char **src;
+{
+ register u_int32 value, base;
+ char c;
+
+ /*
+ * Collect number up to first illegal character. Values are specified
+ * as for C: 0x=hex, 0=octal, other=decimal.
+ */
+ value = 0;
+ base = 10;
+ if (**src == '0') {
+ base = 8;
+ (*src)++;
+ }
+ if (**src == 'x' || **src == 'X') {
+ base = 16;
+ (*src)++;
+ }
+ 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(hmp)
+ hash_datum *hmp;
+{
+ struct host *hostptr = (struct host *) hmp;
+ if (hostptr == NULL)
+ return;
+ assert(hostptr->linkcount > 0);
+ if (--(hostptr->linkcount))
+ return; /* Still has references */
+ del_iplist(hostptr->cookie_server);
+ del_iplist(hostptr->domain_server);
+ del_iplist(hostptr->gateway);
+ 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_iplist(hostptr->nis_server);
+ del_iplist(hostptr->ntp_server);
+
+ /*
+ * XXX - Add new tags here
+ * (if the value is an IP list)
+ */
+
+ del_string(hostptr->hostname);
+ del_string(hostptr->homedir);
+ del_string(hostptr->bootfile);
+ del_string(hostptr->tftpdir);
+ del_string(hostptr->root_path);
+ del_string(hostptr->domain_name);
+ del_string(hostptr->dump_file);
+ del_string(hostptr->exten_file);
+ del_string(hostptr->nis_domain);
+
+#ifdef YORK_EX_OPTION
+ del_string(hostptr->exec_file);
+#endif
+
+ /*
+ * XXX - Add new tags here
+ * (if it is a shared string)
+ */
+
+ del_bindata(hostptr->generic);
+ free((char *) hostptr);
+}
+
+
+
+/*
+ * Decrements the linkcount on the given IP address data structure. If the
+ * linkcount goes to zero, the memory associated with the data is freed.
+ */
+
+PRIVATE void
+del_iplist(iplist)
+ struct in_addr_list *iplist;
+{
+ 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");
+ exit(1);
+ }
+ bzero(retvalue, nbytes);
+ return retvalue;
+}
+
+
+/*
+ * Compare function to determine whether two hardware addresses are
+ * equivalent. Returns TRUE if "host1" and "host2" are equivalent, FALSE
+ * otherwise.
+ *
+ * This function is used when retrieving elements from the hardware address
+ * hash table.
+ */
+
+boolean
+hwlookcmp(d1, d2)
+ hash_datum *d1, *d2;
+{
+ struct host *host1 = (struct host *) d1;
+ struct host *host2 = (struct host *) d2;
+
+ if (host1->htype != host2->htype) {
+ return FALSE;
+ }
+ if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*
+ * Compare function for doing IP address hash table lookup.
+ */
+
+boolean
+iplookcmp(d1, d2)
+ hash_datum *d1, *d2;
+{
+ struct host *host1 = (struct host *) d1;
+ struct host *host2 = (struct host *) d2;
+
+ return (host1->iaddr.s_addr == host2->iaddr.s_addr);
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/readfile.h b/libexec/bootpd/readfile.h
new file mode 100644
index 0000000..3913455
--- /dev/null
+++ b/libexec/bootpd/readfile.h
@@ -0,0 +1,19 @@
+/* readfile.h */
+
+#include "bptypes.h"
+#include "hash.h"
+
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+extern boolean hwlookcmp P((hash_datum *, hash_datum *));
+extern boolean iplookcmp P((hash_datum *, hash_datum *));
+extern boolean nmcmp P((hash_datum *, hash_datum *));
+extern void readtab P((int));
+extern void rdtab_init P((void));
+
+#undef P
+
diff --git a/libexec/bootpd/report.c b/libexec/bootpd/report.c
new file mode 100644
index 0000000..4f7f036
--- /dev/null
+++ b/libexec/bootpd/report.c
@@ -0,0 +1,154 @@
+/*
+ * report() - calls syslog
+ */
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include <stdio.h>
+#include <syslog.h>
+
+#include "report.h"
+
+#ifndef LOG_NDELAY
+#define LOG_NDELAY 0
+#endif
+#ifndef LOG_DAEMON
+#define LOG_DAEMON 0
+#endif
+#ifndef LOG_BOOTP
+#define LOG_BOOTP LOG_DAEMON
+#endif
+
+extern int debug;
+extern char *progname;
+
+/*
+ * This is initialized so you get stderr until you call
+ * report_init()
+ */
+static int stderr_only = 1;
+
+void
+report_init(nolog)
+ int nolog;
+{
+ stderr_only = nolog;
+#ifdef SYSLOG
+ if (!stderr_only) {
+ openlog(progname, LOG_PID | LOG_NDELAY, LOG_BOOTP);
+ }
+#endif
+}
+
+/*
+ * This routine reports errors and such via stderr and syslog() if
+ * appopriate. It just helps avoid a lot of "#ifdef SYSLOG" constructs
+ * from being scattered throughout the code.
+ *
+ * The syntax is identical to syslog(3), but %m is not considered special
+ * for output to stderr (i.e. you'll see "%m" in the output. . .). Also,
+ * control strings should normally end with \n since newlines aren't
+ * automatically generated for stderr output (whereas syslog strips out all
+ * newlines and adds its own at the end).
+ */
+
+static char *levelnames[] = {
+#ifdef LOG_SALERT
+ "level(0): ",
+ "alert(1): ",
+ "alert(2): ",
+ "emerg(3): ",
+ "error(4): ",
+ "crit(5): ",
+ "warn(6): ",
+ "note(7): ",
+ "info(8): ",
+ "debug(9): ",
+ "level(?): "
+#else
+ "emerg(0): ",
+ "alert(1): ",
+ "crit(2): ",
+ "error(3): ",
+ "warn(4): ",
+ "note(5): ",
+ "info(6): ",
+ "debug(7): ",
+ "level(?): "
+#endif
+};
+static int numlevels = sizeof(levelnames) / sizeof(levelnames[0]);
+
+
+/*
+ * Print a log message using syslog(3) and/or stderr.
+ * The message passed in should not include a newline.
+ */
+#ifdef __STDC__
+void
+report(int priority, char *fmt,...)
+#else
+/*VARARGS2*/
+void
+report(priority, fmt, va_alist)
+ int priority;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ static char buf[128];
+
+ if ((priority < 0) || (priority >= numlevels)) {
+ priority = numlevels - 1;
+ }
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ /*
+ * Print the message
+ */
+ if (stderr_only || (debug > 2)) {
+ fprintf(stderr, "%s: %s %s\n",
+ progname, levelnames[priority], buf);
+ }
+#ifdef SYSLOG
+ if (!stderr_only)
+ syslog((priority | LOG_BOOTP), "%s", buf);
+#endif
+}
+
+
+
+/*
+ * Return pointer to static string which gives full filesystem error message.
+ */
+char *
+get_errmsg()
+{
+ extern int errno;
+ extern char *strerror();
+
+ return strerror(errno);
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/report.h b/libexec/bootpd/report.h
new file mode 100644
index 0000000..0bf63d6
--- /dev/null
+++ b/libexec/bootpd/report.h
@@ -0,0 +1,13 @@
+/* report.h */
+
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+extern void report_init P((int nolog));
+extern void report P((int, char *, ...));
+extern char *get_errmsg P((void));
+
+#undef P
diff --git a/libexec/bootpd/rtmsg.c b/libexec/bootpd/rtmsg.c
index fb59afe..5942b22 100644
--- a/libexec/bootpd/rtmsg.c
+++ b/libexec/bootpd/rtmsg.c
@@ -39,7 +39,7 @@
/*
* from arp.c 8.2 (Berkeley) 1/2/94
- * $Id: rtmsg.c,v 1.2 1994/09/10 15:13:28 csgr Exp $
+ * $Id: rtmsg.c,v 1.3 1995/01/30 11:11:43 dfr Exp $
*/
#include <sys/param.h>
@@ -50,6 +50,7 @@
#if BSD >= 199306
#include <sys/socket.h>
+#include <sys/filio.h>
#include <net/if.h>
#include <net/if_dl.h>
@@ -85,6 +86,18 @@ static void getsocket () {
report(LOG_ERR, "socket %s", strerror(errno));
exit(1);
}
+ } else {
+ /*
+ * Drain the socket of any unwanted routing messages.
+ */
+ int n;
+ char buf[512];
+
+ ioctl(s, FIONREAD, &n);
+ while (n > 0) {
+ read(s, buf, sizeof buf);
+ ioctl(s, FIONREAD, &n);
+ }
}
}
@@ -98,7 +111,7 @@ static struct {
} m_rtmsg;
/*
- * Set an individual arp entry
+ * Set an individual arp entry
*/
int bsd_arp_set(ia, eaddr, len)
struct in_addr *ia;
@@ -110,6 +123,7 @@ int bsd_arp_set(ia, eaddr, len)
register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
u_char *ea;
struct timeval time;
+ int op = RTM_ADD;
getsocket();
sdl_m = blank_sdl;
@@ -138,15 +152,16 @@ tryagain:
!(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
case IFT_ISO88024: case IFT_ISO88025:
+ op = RTM_CHANGE;
goto overwrite;
}
if (doing_proxy == 0) {
- report(LOG_WARNING, "set: can only proxy for %s\n",
+ report(LOG_WARNING, "set: can only proxy for %s\n",
inet_ntoa(sin->sin_addr));
return (1);
}
if (sin_m.sin_other & SIN_PROXY) {
- report(LOG_WARNING,
+ report(LOG_WARNING,
"set: proxy entry exists for non 802 device\n");
return(1);
}
@@ -157,13 +172,13 @@ tryagain:
overwrite:
if (sdl->sdl_family != AF_LINK) {
report(LOG_WARNING,
- "cannot intuit interface index and type for %s\n",
+ "cannot intuit interface index and type for %s\n",
inet_ntoa(sin->sin_addr));
return (1);
}
sdl_m.sdl_type = sdl->sdl_type;
sdl_m.sdl_index = sdl->sdl_index;
- return (rtmsg(RTM_ADD));
+ return (rtmsg(op));
}
@@ -186,6 +201,7 @@ static int rtmsg(cmd)
report(LOG_ERR, "set_arp: internal wrong cmd - exiting");
exit(1);
case RTM_ADD:
+ case RTM_CHANGE:
rtm->rtm_addrs |= RTA_GATEWAY;
rtm->rtm_rmx.rmx_expire = expire_time;
rtm->rtm_inits = RTV_EXPIRE;
@@ -225,7 +241,7 @@ static int rtmsg(cmd)
}
do {
l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
- } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != getpid()));
+ } while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq || rtm->rtm_pid != getpid()));
if (l < 0)
report(LOG_WARNING, "arp: read from routing socket: %s\n",
strerror(errno));
diff --git a/libexec/bootpd/syslog.conf b/libexec/bootpd/syslog.conf
new file mode 100644
index 0000000..2c135af
--- /dev/null
+++ b/libexec/bootpd/syslog.conf
@@ -0,0 +1,63 @@
+#
+# syslog configuration file for SunOS 4.X
+# (modified to do local2 separately)
+#
+# This file is processed by m4 so be careful to quote (`') names
+# that match m4 reserved words. Also, within ifdef's, arguments
+# containing commas must be quoted.
+#
+# Note: Have to exclude user from most lines so that user.alert
+# and user.emerg are not included, because old sendmails
+# will generate them for debugging information. If you
+# have no 4.2BSD based systems doing network logging, you
+# can remove all the special cases for "user" logging.
+
+#*.err;kern.debug;auth.notice;user.none /dev/console
+kern.debug;user,mail.crit;auth.notice /dev/console
+daemon,syslog,lpr,news,uucp,cron.err /dev/console
+
+#*.err;kern.debug;daemon,auth.notice;mail.crit;user.none /var/adm/messages
+kern.debug;user,mail.crit;auth.notice /var/adm/messages
+daemon.notice;syslog,news,uucp,cron.err /var/adm/messages
+
+lpr.debug /var/adm/lpd-errs
+
+*.alert;kern.err;daemon.err;user.none operator
+*.alert;user.none root
+
+*.emerg;user.none *
+
+# for loghost machines, to have authentication messages (su, login, etc.)
+# logged to a file, un-comment out the following line and adjust the file name
+# as appropriate.
+#
+# if a non-loghost machine chooses to have such messages
+# sent to the loghost machine, un-comment out the following line.
+#
+#auth.notice ifdef(`LOGHOST', /var/log/authlog, @loghost)
+
+mail.debug ifdef(`LOGHOST', /var/log/syslog, @loghost)
+
+# following line for compatibility with old sendmails. they will send
+# messages with no facility code, which will be turned into "user" messages
+# by the local syslog daemon. only the "loghost" machine needs the following
+# line, to cause these old sendmail log messages to be logged in the
+# mail syslog file.
+#
+ifdef(`LOGHOST',
+user.alert /var/log/syslog
+)
+#
+# non-loghost machines will use the following lines to cause "user"
+# log messages to be logged locally.
+#
+ifdef(`LOGHOST', ,
+user.err /dev/console
+user.err /var/adm/messages
+user.alert `root, operator'
+user.emerg *
+)
+
+# Local2: (bootpd, pppd)
+local2.debug /dev/console
+#local2.debug /var/log/local2
diff --git a/libexec/bootpd/tools/bootpef/bootpef.8 b/libexec/bootpd/tools/bootpef/bootpef.8
new file mode 100644
index 0000000..0f0b1fc
--- /dev/null
+++ b/libexec/bootpd/tools/bootpef/bootpef.8
@@ -0,0 +1,52 @@
+.\" bootpef.8
+.TH BOOTPEF 8 "4 Dec 1993" "MAINTENANCE COMMANDS"
+.SH NAME
+bootpef \- BOOTP Extension File compiler
+.SH SYNOPSIS
+.LP
+.B bootpef
+.RI [ "-c chdir" ]
+.RI [ "-d debug-level" ]
+.RI [ "-f config-file" ]
+.RI [ client-name " [...]]"
+.SH DESCRIPTION
+.B bootpef
+builds the
+.I Extension Path
+files described by RFC 1497 (tag 18).
+If any
+.I client-name
+arguments are specified, then
+.I bootpef
+compiles the extension files for only those clients.
+.SH OPTIONS
+.TP
+.BI \-c \ chdir\-path
+Sets the current directory used by
+.I bootpef
+while creating extension files. This is useful when the
+extension file names are specified as relative pathnames, and
+.I bootpef
+needs to use the same current directory as the TFTP server
+(typically /tftpboot).
+.TP
+.BI \-d \ debug\-level
+Sets the
+.I debug\-level
+variable that controls the amount of debugging messages generated.
+For example, -d4 or -d 4 will set the debugging level to 4.
+.TP
+.BI \-f \ config\-file
+Set the name of the config file that specifies the option
+data to be sent to each client.
+.SH "SEE ALSO"
+bootpd(8), tftpd(8)
+.SH REFERENCES
+.TP
+RFC951
+BOOTSTRAP PROTOCOL (BOOTP)
+.TP
+RFC1497
+BOOTP Vendor Information Extensions
+
+
diff --git a/libexec/bootpd/tools/bootpef/bootpef.c b/libexec/bootpd/tools/bootpef/bootpef.c
new file mode 100644
index 0000000..eb18935
--- /dev/null
+++ b/libexec/bootpd/tools/bootpef/bootpef.c
@@ -0,0 +1,347 @@
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+
+#ifndef lint
+static char rcsid[] = "$Id: bootpef.c,v 1.1.1.1 1994/09/10 14:44:54 csgr Exp $";
+#endif
+
+
+/*
+ * bootpef - BOOTP Extension File generator
+ * Makes an "Extension File" for each host entry that
+ * defines an and Extension File. (See RFC1497, tag 18.)
+ *
+ * HISTORY
+ * See ./Changes
+ *
+ * BUGS
+ * See ./ToDo
+ */
+
+
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <syslog.h>
+
+#ifndef USE_BFUNCS
+#include <memory.h>
+/* Yes, memcpy is OK here (no overlapped copies). */
+#define bcopy(a,b,c) memcpy(b,a,c)
+#define bzero(p,l) memset(p,0,l)
+#define bcmp(a,b,c) memcmp(a,b,c)
+#endif
+
+#include "bootp.h"
+#include "hash.h"
+#include "hwaddr.h"
+#include "bootpd.h"
+#include "dovend.h"
+#include "readfile.h"
+#include "report.h"
+#include "tzone.h"
+#include "patchlevel.h"
+
+#define BUFFERSIZE 0x4000
+
+#ifndef CONFIG_FILE
+#define CONFIG_FILE "/etc/bootptab"
+#endif
+
+
+
+/*
+ * Externals, forward declarations, and global variables
+ */
+
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+static void dovend_rfc1048 P((struct bootp *, struct host *, int32));
+static void mktagfile P((struct host *));
+static void usage P((void));
+
+#undef P
+
+
+/*
+ * General
+ */
+
+char *progname;
+char *chdir_path;
+int debug = 0; /* Debugging flag (level) */
+byte *buffer;
+
+/*
+ * Globals below are associated with the bootp database file (bootptab).
+ */
+
+char *bootptab = CONFIG_FILE;
+
+
+/*
+ * Print "usage" message and exit
+ */
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: $s [ -c chdir ] [-d level] [-f configfile] [host...]\n");
+ fprintf(stderr, "\t -c n\tset current directory\n");
+ fprintf(stderr, "\t -d n\tset debug level\n");
+ fprintf(stderr, "\t -f n\tconfig file name\n");
+ exit(1);
+}
+
+
+/*
+ * Initialization such as command-line processing is done and then the
+ * main server loop is started.
+ */
+void
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct host *hp;
+ char *stmp;
+ int n;
+
+ progname = strrchr(argv[0], '/');
+ if (progname) progname++;
+ else progname = argv[0];
+
+ /* Get work space for making tag 18 files. */
+ buffer = (byte *) malloc(BUFFERSIZE);
+ if (!buffer) {
+ report(LOG_ERR, "malloc failed");
+ exit(1);
+ }
+ /*
+ * Set defaults that might be changed by option switches.
+ */
+ stmp = NULL;
+
+ /*
+ * Read switches.
+ */
+ for (argc--, argv++; argc > 0; argc--, argv++) {
+ if (argv[0][0] != '-')
+ break;
+ switch (argv[0][1]) {
+
+ case 'c': /* chdir_path */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (stmp[0] != '/')) {
+ fprintf(stderr,
+ "bootpd: invalid chdir specification\n");
+ break;
+ }
+ chdir_path = stmp;
+ break;
+
+ case 'd': /* debug */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else if (argv[1] && argv[1][0] == '-') {
+ /*
+ * Backwards-compatible behavior:
+ * no parameter, so just increment the debug flag.
+ */
+ debug++;
+ break;
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
+ fprintf(stderr,
+ "bootpd: invalid debug level\n");
+ break;
+ }
+ debug = n;
+ break;
+
+ case 'f': /* config file */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ bootptab = stmp;
+ break;
+
+ default:
+ fprintf(stderr, "bootpd: unknown switch: -%c\n",
+ argv[0][1]);
+ usage();
+ break;
+ }
+ }
+
+ /* Get the timezone. */
+ tzone_init();
+
+ /* Allocate hash tables. */
+ rdtab_init();
+
+ /*
+ * Read the bootptab file.
+ */
+ readtab(1); /* force read */
+
+ /* Set the cwd (i.e. to /tftpboot) */
+ if (chdir_path) {
+ if (chdir(chdir_path) < 0)
+ report(LOG_ERR, "%s: chdir failed", chdir_path);
+ }
+ /* If there are host names on the command line, do only those. */
+ if (argc > 0) {
+ unsigned int tlen, hashcode;
+
+ while (argc) {
+ tlen = strlen(argv[0]);
+ hashcode = hash_HashFunction((u_char *)argv[0], tlen);
+ hp = (struct host *) hash_Lookup(nmhashtable,
+ hashcode,
+ nmcmp, argv[0]);
+ if (!hp) {
+ printf("%s: no matching entry\n", argv[0]);
+ exit(1);
+ }
+ if (!hp->flags.exten_file) {
+ printf("%s: no extension file\n", argv[0]);
+ exit(1);
+ }
+ mktagfile(hp);
+ argv++;
+ argc--;
+ }
+ exit(0);
+ }
+ /* No host names specified. Do them all. */
+ hp = (struct host *) hash_FirstEntry(nmhashtable);
+ while (hp != NULL) {
+ mktagfile(hp);
+ hp = (struct host *) hash_NextEntry(nmhashtable);
+ }
+}
+
+
+
+/*
+ * Make a "TAG 18" file for this host.
+ * (Insert the RFC1497 options.)
+ */
+
+static void
+mktagfile(hp)
+ struct host *hp;
+{
+ FILE *fp;
+ int bytesleft, len;
+ byte *vp;
+ char *tmpstr;
+
+ if (!hp->flags.exten_file)
+ return;
+
+ vp = buffer;
+ bytesleft = BUFFERSIZE;
+ bcopy(vm_rfc1048, vp, 4); /* Copy in the magic cookie */
+ vp += 4;
+ bytesleft -= 4;
+
+ /*
+ * The "extension file" options are appended by the following
+ * function (which is shared with bootpd.c).
+ */
+ len = dovend_rfc1497(hp, vp, bytesleft);
+ vp += len;
+ bytesleft -= len;
+
+ if (bytesleft < 1) {
+ report(LOG_ERR, "%s: too much option data",
+ hp->exten_file->string);
+ return;
+ }
+ *vp++ = TAG_END;
+ bytesleft--;
+
+ /* Write the buffer to the extension file. */
+ printf("Updating \"%s\"\n", hp->exten_file->string);
+ if ((fp = fopen(hp->exten_file->string, "w")) == NULL) {
+ report(LOG_ERR, "error opening \"%s\": %s",
+ hp->exten_file->string, get_errmsg());
+ return;
+ }
+ len = vp - buffer;
+ if (len != fwrite(buffer, 1, len, fp)) {
+ report(LOG_ERR, "write failed on \"%s\" : %s",
+ hp->exten_file->string, get_errmsg());
+ }
+ fclose(fp);
+
+} /* dovend_rfc1048 */
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/tools/bootptest/Makefile b/libexec/bootpd/tools/bootptest/Makefile
index c154d97..d137e73 100644
--- a/libexec/bootpd/tools/bootptest/Makefile
+++ b/libexec/bootpd/tools/bootptest/Makefile
@@ -1,9 +1,9 @@
# Makefile
-# $Id$
+# $Id: Makefile,v 1.2 1995/05/30 05:45:51 rgrimes Exp $
PROG= bootptest
MAN8= bootptest.8
-SRCS= bootptest.o print-bootp.o getif.o getether.o report.o
+SRCS= bootptest.c getether.c getif.c print-bootp.c report.c
SRCDIR= ${.CURDIR}/../..
CFLAGS+=-I${SRCDIR}
diff --git a/libexec/bootpd/tools/bootptest/bootptest.8 b/libexec/bootpd/tools/bootptest/bootptest.8
new file mode 100644
index 0000000..d076c8b
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/bootptest.8
@@ -0,0 +1,74 @@
+.\" bootptest.8
+.TH BOOTPTEST 8 "10 June 1993" "MAINTENANCE COMMANDS"
+.SH NAME
+bootptest \- send BOOTP queries and print responses
+.SH SYNOPSIS
+.LP
+.B bootptest
+[
+.B \-f
+.I bootfile
+]
+[
+.B \-h
+]
+[
+.B \-m
+.I magic_number
+]
+.I server\-name
+.RI [ template-file ]
+.SH DESCRIPTION
+.B bootptest
+sends BOOTP requests to the host specified as
+.I server\-name
+at one\-second intervals until either a response is received,
+or until ten requests have gone unanswered.
+After a response is received,
+.B bootptest
+will wait one more second listening for additional responses.
+.SH OPTIONS
+.TP
+.B \-f
+.I bootfile
+Fill in the boot file field of the request with
+.IR bootfile .
+.TP
+.B \-h
+Use the hardware (Ethernet) address to identify the client.
+By default, the IP address is copied into the request
+indicating that this client already knows its IP address.
+.TP
+.B \-m
+.I magic_number
+Initialize the first word of the vendor options field with
+.IR magic_number .
+.LP
+A
+.I template-file
+may be specified, in which case
+.B bootptest
+uses the (binary) contents of this file to initialize the
+.I options
+area of the request packet.
+.SH CREDITS
+.LP
+The bootptest program is a combination of original and derived works.
+The main program module (bootptest.c) is original work by
+Gordon W. Ross <gwr@mc.com>.
+The packet printing module (print-bootp.c) is a slightly modified
+version of a file from the BSD tcpdump program.
+.LP
+This program includes software developed by the University of
+California, Lawrence Berkeley Laboratory and its contributors.
+(See the copyright notice in print-bootp.c)
+.SH "SEE ALSO"
+.LP
+bootpd(8)
+.SH REFERENCES
+.TP
+RFC951
+BOOTSTRAP PROTOCOL (BOOTP)
+.TP
+RFC1048
+BOOTP Vendor Information Extensions
diff --git a/libexec/bootpd/tools/bootptest/bootptest.c b/libexec/bootpd/tools/bootptest/bootptest.c
new file mode 100644
index 0000000..bc235ac
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/bootptest.c
@@ -0,0 +1,500 @@
+/*
+ * bootptest.c - Test out a bootp server.
+ *
+ * This simple program was put together from pieces taken from
+ * various places, including the CMU BOOTP client and server.
+ * The packet printing routine is from the Berkeley "tcpdump"
+ * program with some enhancements I added. The print-bootp.c
+ * file was shared with my copy of "tcpdump" and therefore uses
+ * some unusual utility routines that would normally be provided
+ * by various parts of the tcpdump program. Gordon W. Ross
+ *
+ * Boilerplate:
+ *
+ * This program includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory and its contributors.
+ * (See the copyright notice in print-bootp.c)
+ *
+ * The remainder of this program is public domain. You may do
+ * whatever you like with it except claim that you wrote it.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * HISTORY:
+ *
+ * 12/02/93 Released version 1.4 (with bootp-2.3.2)
+ * 11/05/93 Released version 1.3
+ * 10/14/93 Released version 1.2
+ * 10/11/93 Released version 1.1
+ * 09/28/93 Released version 1.0
+ * 09/93 Original developed by Gordon W. Ross <gwr@mc.com>
+ */
+
+char *usage = "bootptest [-h] server-name [vendor-data-template-file]";
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <assert.h>
+
+#include "bootp.h"
+#include "bootptest.h"
+#include "getif.h"
+#include "patchlevel.h"
+
+#define LOG_ERR 1
+#define BUFLEN 1024
+#define WAITSECS 1
+#define MAXWAIT 10
+
+int vflag = 1;
+int tflag = 0;
+int thiszone;
+char *progname;
+unsigned char *packetp;
+unsigned char *snapend;
+int snaplen;
+
+
+/*
+ * IP port numbers for client and server obtained from /etc/services
+ */
+
+u_short bootps_port, bootpc_port;
+
+
+/*
+ * Internet socket and interface config structures
+ */
+
+struct sockaddr_in sin_server; /* where to send requests */
+struct sockaddr_in sin_client; /* for bind and listen */
+struct sockaddr_in sin_from; /* Packet source */
+u_char eaddr[16]; /* Ethernet address */
+
+/*
+ * General
+ */
+
+int debug = 1; /* Debugging flag (level) */
+char hostname[64];
+char *sndbuf; /* Send packet buffer */
+char *rcvbuf; /* Receive packet buffer */
+
+/*
+ * Vendor magic cookies for CMU and RFC1048
+ */
+
+unsigned char vm_cmu[4] = VM_CMU;
+unsigned char vm_rfc1048[4] = VM_RFC1048;
+short secs; /* How long client has waited */
+
+char *get_errmsg();
+extern void bootp_print();
+
+/*
+ * Initialization such as command-line processing is done, then
+ * the receiver loop is started. Die when interrupted.
+ */
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct bootp *bp;
+ struct servent *sep;
+ struct hostent *hep;
+
+ char *servername = NULL;
+ char *vendor_file = NULL;
+ char *bp_file = NULL;
+ int32 server_addr; /* inet addr, network order */
+ int s; /* Socket file descriptor */
+ int n, tolen, fromlen, recvcnt;
+ int use_hwa = 0;
+ int32 vend_magic;
+ int32 xid;
+
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+ argc--;
+ argv++;
+
+ if (debug)
+ printf("%s: version %s.%d\n", progname, VERSION, PATCHLEVEL);
+
+ /*
+ * Verify that "struct bootp" has the correct official size.
+ * (Catch evil compilers that do struct padding.)
+ */
+ assert(sizeof(struct bootp) == BP_MINPKTSZ);
+
+ sndbuf = malloc(BUFLEN);
+ rcvbuf = malloc(BUFLEN);
+ if (!sndbuf || !rcvbuf) {
+ printf("malloc failed\n");
+ exit(1);
+ }
+
+ /* default magic number */
+ bcopy(vm_rfc1048, (char*)&vend_magic, 4);
+
+ /* Handle option switches. */
+ while (argc > 0) {
+ if (argv[0][0] != '-')
+ break;
+ switch (argv[0][1]) {
+
+ case 'f': /* File name to reqest. */
+ if (argc < 2)
+ goto error;
+ argc--; argv++;
+ bp_file = *argv;
+ break;
+
+ case 'h': /* Use hardware address. */
+ use_hwa = 1;
+ break;
+
+ case 'm': /* Magic number value. */
+ if (argc < 2)
+ goto error;
+ argc--; argv++;
+ vend_magic = inet_addr(*argv);
+ break;
+
+ error:
+ default:
+ puts(usage);
+ exit(1);
+
+ }
+ argc--;
+ argv++;
+ }
+
+ /* Get server name (or address) for query. */
+ if (argc > 0) {
+ servername = *argv;
+ argc--;
+ argv++;
+ }
+ /* Get optional vendor-data-template-file. */
+ if (argc > 0) {
+ vendor_file = *argv;
+ argc--;
+ argv++;
+ }
+ if (!servername) {
+ printf("missing server name.\n");
+ puts(usage);
+ exit(1);
+ }
+ /*
+ * Create a socket.
+ */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ exit(1);
+ }
+ /*
+ * Get server's listening port number
+ */
+ sep = getservbyname("bootps", "udp");
+ if (sep) {
+ bootps_port = ntohs((u_short) sep->s_port);
+ } else {
+ fprintf(stderr, "udp/bootps: unknown service -- using port %d\n",
+ IPPORT_BOOTPS);
+ bootps_port = (u_short) IPPORT_BOOTPS;
+ }
+
+ /*
+ * Set up server socket address (for send)
+ */
+ if (servername) {
+ if (isdigit(servername[0]))
+ server_addr = inet_addr(servername);
+ else {
+ hep = gethostbyname(servername);
+ if (!hep) {
+ fprintf(stderr, "%s: unknown host\n", servername);
+ exit(1);
+ }
+ bcopy(hep->h_addr, &server_addr, sizeof(server_addr));
+ }
+ } else {
+ /* Get broadcast address */
+ /* XXX - not yet */
+ server_addr = INADDR_ANY;
+ }
+ sin_server.sin_family = AF_INET;
+ sin_server.sin_port = htons(bootps_port);
+ sin_server.sin_addr.s_addr = server_addr;
+
+ /*
+ * Get client's listening port number
+ */
+ sep = getservbyname("bootpc", "udp");
+ if (sep) {
+ bootpc_port = ntohs(sep->s_port);
+ } else {
+ fprintf(stderr, "udp/bootpc: unknown service -- using port %d\n",
+ IPPORT_BOOTPC);
+ bootpc_port = (u_short) IPPORT_BOOTPC;
+ }
+
+ /*
+ * Set up client socket address (for listen)
+ */
+ sin_client.sin_family = AF_INET;
+ sin_client.sin_port = htons(bootpc_port);
+ sin_client.sin_addr.s_addr = INADDR_ANY;
+
+ /*
+ * Bind client socket to BOOTPC port.
+ */
+ if (bind(s, (struct sockaddr *) &sin_client, sizeof(sin_client)) < 0) {
+ perror("bind BOOTPC port");
+ if (errno == EACCES)
+ fprintf(stderr, "You need to run this as root\n");
+ exit(1);
+ }
+ /*
+ * Build a request.
+ */
+ bp = (struct bootp *) sndbuf;
+ bzero(bp, sizeof(*bp));
+ bp->bp_op = BOOTREQUEST;
+ xid = (int32) getpid();
+ bp->bp_xid = (u_int32) htonl(xid);
+ if (bp_file)
+ strncpy(bp->bp_file, bp_file, BP_FILE_LEN);
+
+ /*
+ * Fill in the hardware address (or client IP address)
+ */
+ if (use_hwa) {
+ struct ifreq *ifr;
+
+ ifr = getif(s, &sin_server.sin_addr);
+ if (!ifr) {
+ printf("No interface for %s\n", servername);
+ exit(1);
+ }
+ if (getether(ifr->ifr_name, eaddr)) {
+ printf("Can not get ether addr for %s\n", ifr->ifr_name);
+ exit(1);
+ }
+ /* Copy Ethernet address into request packet. */
+ bp->bp_htype = 1;
+ bp->bp_hlen = 6;
+ bcopy(eaddr, bp->bp_chaddr, bp->bp_hlen);
+ } else {
+ /* Fill in the client IP address. */
+ gethostname(hostname, sizeof(hostname));
+ hep = gethostbyname(hostname);
+ if (!hep) {
+ printf("Can not get my IP address\n");
+ exit(1);
+ }
+ bcopy(hep->h_addr, &bp->bp_ciaddr, hep->h_length);
+ }
+
+ /*
+ * Copy in the default vendor data.
+ */
+ bcopy((char*)&vend_magic, bp->bp_vend, 4);
+ if (vend_magic)
+ bp->bp_vend[4] = TAG_END;
+
+ /*
+ * Read in the "options" part of the request.
+ * This also determines the size of the packet.
+ */
+ snaplen = sizeof(*bp);
+ if (vendor_file) {
+ int fd = open(vendor_file, 0);
+ if (fd < 0) {
+ perror(vendor_file);
+ exit(1);
+ }
+ /* Compute actual space for options. */
+ n = BUFLEN - sizeof(*bp) + BP_VEND_LEN;
+ n = read(fd, bp->bp_vend, n);
+ close(fd);
+ if (n < 0) {
+ perror(vendor_file);
+ exit(1);
+ }
+ printf("read %d bytes of vendor template\n", n);
+ if (n > BP_VEND_LEN) {
+ printf("warning: extended options in use (len > %d)\n",
+ BP_VEND_LEN);
+ snaplen += (n - BP_VEND_LEN);
+ }
+ }
+ /*
+ * Set globals needed by print_bootp
+ * (called by send_request)
+ */
+ packetp = (unsigned char *) eaddr;
+ snapend = (unsigned char *) sndbuf + snaplen;
+
+ /* Send a request once per second while waiting for replies. */
+ recvcnt = 0;
+ bp->bp_secs = secs = 0;
+ send_request(s);
+ while (1) {
+ struct timeval tv;
+ int readfds;
+
+ tv.tv_sec = WAITSECS;
+ tv.tv_usec = 0L;
+ readfds = (1 << s);
+ n = select(s + 1, (fd_set *) & readfds, NULL, NULL, &tv);
+ if (n < 0) {
+ perror("select");
+ break;
+ }
+ if (n == 0) {
+ /*
+ * We have not received a response in the last second.
+ * If we have ever received any responses, exit now.
+ * Otherwise, bump the "wait time" field and re-send.
+ */
+ if (recvcnt > 0)
+ exit(0);
+ secs += WAITSECS;
+ if (secs > MAXWAIT)
+ break;
+ bp->bp_secs = htons(secs);
+ send_request(s);
+ continue;
+ }
+ fromlen = sizeof(sin_from);
+ n = recvfrom(s, rcvbuf, BUFLEN, 0,
+ (struct sockaddr *) &sin_from, &fromlen);
+ if (n <= 0) {
+ continue;
+ }
+ if (n < sizeof(struct bootp)) {
+ printf("received short packet\n");
+ continue;
+ }
+ recvcnt++;
+
+ /* Print the received packet. */
+ printf("Recvd from %s", inet_ntoa(sin_from.sin_addr));
+ /* set globals needed by bootp_print() */
+ snaplen = n;
+ snapend = (unsigned char *) rcvbuf + snaplen;
+ bootp_print(rcvbuf, n, sin_from.sin_port, 0);
+ putchar('\n');
+ /*
+ * This no longer exits immediately after receiving
+ * one response because it is useful to know if the
+ * client might get multiple responses. This code
+ * will now listen for one second after a response.
+ */
+ }
+ fprintf(stderr, "no response from %s\n", servername);
+ exit(1);
+}
+
+send_request(s)
+ int s;
+{
+ /* Print the request packet. */
+ printf("Sending to %s", inet_ntoa(sin_server.sin_addr));
+ bootp_print(sndbuf, snaplen, sin_from.sin_port, 0);
+ putchar('\n');
+
+ /* Send the request packet. */
+ if (sendto(s, sndbuf, snaplen, 0,
+ (struct sockaddr *) &sin_server,
+ sizeof(sin_server)) < 0)
+ {
+ perror("sendto server");
+ exit(1);
+ }
+}
+
+/*
+ * Print out a filename (or other ascii string).
+ * Return true if truncated.
+ */
+int
+printfn(s, ep)
+ register u_char *s, *ep;
+{
+ register u_char c;
+
+ putchar('"');
+ while (c = *s++) {
+ if (s > ep) {
+ putchar('"');
+ return (1);
+ }
+ if (!isascii(c)) {
+ c = toascii(c);
+ putchar('M');
+ putchar('-');
+ }
+ if (!isprint(c)) {
+ c ^= 0x40; /* DEL to ?, others to alpha */
+ putchar('^');
+ }
+ putchar(c);
+ }
+ putchar('"');
+ return (0);
+}
+
+/*
+ * Convert an IP addr to a string.
+ * (like inet_ntoa, but ina is a pointer)
+ */
+char *
+ipaddr_string(ina)
+ struct in_addr *ina;
+{
+ static char b[24];
+ u_char *p;
+
+ p = (u_char *) ina;
+ sprintf(b, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+ return (b);
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/tools/bootptest/bootptest.h b/libexec/bootpd/tools/bootptest/bootptest.h
new file mode 100644
index 0000000..27f78ba
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/bootptest.h
@@ -0,0 +1,30 @@
+/* bootptest.h */
+/*
+ * Hacks for sharing print-bootp.c between tcpdump and bootptest.
+ */
+#define ESRC(p) (p)
+#define EDST(p) (p)
+
+#ifndef USE_BFUNCS
+/* Use mem/str functions */
+/* There are no overlapped copies, so memcpy is OK. */
+#define bcopy(a,b,c) memcpy(b,a,c)
+#define bzero(p,l) memset(p,0,l)
+#define bcmp(a,b,c) memcmp(a,b,c)
+#endif
+
+extern int vflag; /* verbose flag */
+
+/* global pointers to beginning and end of current packet (during printing) */
+extern unsigned char *packetp;
+extern unsigned char *snapend;
+
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+extern char *ipaddr_string P((struct in_addr *));
+
+#undef P
diff --git a/libexec/bootpd/tools/bootptest/print-bootp.c b/libexec/bootpd/tools/bootptest/print-bootp.c
new file mode 100644
index 0000000..f416998
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/print-bootp.c
@@ -0,0 +1,493 @@
+/*
+ * Copyright (c) 1988-1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Format and print bootp packets.
+ *
+ * This file was copied from tcpdump-2.1.1 and modified.
+ * There is an e-mail list for tcpdump: <tcpdump@ee.lbl.gov>
+ */
+#ifndef lint
+static char rcsid[] = "$Id: print-bootp.c,v 1.1.1.1 1994/09/10 14:44:55 csgr Exp $";
+/* 93/10/10 <gwr@mc.com> New data-driven option print routine. */
+#endif
+
+#include <stdio.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "bootp.h"
+#include "bootptest.h"
+
+/* These decode the vendor data. */
+static void rfc1048_print();
+static void cmu_print();
+static void other_print();
+static void dump_hex();
+
+/*
+ * Print bootp requests
+ */
+void
+bootp_print(bp, length, sport, dport)
+ struct bootp *bp;
+ int length;
+ u_short sport, dport;
+{
+ static char tstr[] = " [|bootp]";
+ static unsigned char vm_cmu[4] = VM_CMU;
+ static unsigned char vm_rfc1048[4] = VM_RFC1048;
+ u_char *ep;
+ int vdlen;
+
+#define TCHECK(var, l) if ((u_char *)&(var) > ep - l) goto trunc
+
+ /* Note funny sized packets */
+ if (length != sizeof(struct bootp))
+ (void) printf(" [len=%d]", length);
+
+ /* 'ep' points to the end of avaible data. */
+ ep = (u_char *) snapend;
+
+ switch (bp->bp_op) {
+
+ case BOOTREQUEST:
+ /* Usually, a request goes from a client to a server */
+ if (sport != IPPORT_BOOTPC || dport != IPPORT_BOOTPS)
+ printf(" (request)");
+ break;
+
+ case BOOTREPLY:
+ /* Usually, a reply goes from a server to a client */
+ if (sport != IPPORT_BOOTPS || dport != IPPORT_BOOTPC)
+ printf(" (reply)");
+ break;
+
+ default:
+ printf(" bootp-#%d", bp->bp_op);
+ }
+
+ /* The usual hardware address type is 1 (10Mb Ethernet) */
+ if (bp->bp_htype != 1)
+ printf(" htype:%d", bp->bp_htype);
+
+ /* The usual length for 10Mb Ethernet address is 6 bytes */
+ if (bp->bp_hlen != 6)
+ printf(" hlen:%d", bp->bp_hlen);
+
+ /* Client's Hardware address */
+ if (bp->bp_hlen) {
+ register struct ether_header *eh;
+ register char *e;
+
+ TCHECK(bp->bp_chaddr[0], 6);
+ eh = (struct ether_header *) packetp;
+ if (bp->bp_op == BOOTREQUEST)
+ e = (char *) ESRC(eh);
+ else if (bp->bp_op == BOOTREPLY)
+ e = (char *) EDST(eh);
+ else
+ e = 0;
+ if (e == 0 || bcmp((char *) bp->bp_chaddr, e, 6))
+ dump_hex(bp->bp_chaddr, bp->bp_hlen);
+ }
+ /* Only print interesting fields */
+ if (bp->bp_hops)
+ printf(" hops:%d", bp->bp_hops);
+
+ if (bp->bp_xid)
+ printf(" xid:%d", ntohl(bp->bp_xid));
+
+ if (bp->bp_secs)
+ printf(" secs:%d", ntohs(bp->bp_secs));
+
+ /* Client's ip address */
+ TCHECK(bp->bp_ciaddr, sizeof(bp->bp_ciaddr));
+ if (bp->bp_ciaddr.s_addr)
+ printf(" C:%s", ipaddr_string(&bp->bp_ciaddr));
+
+ /* 'your' ip address (bootp client) */
+ TCHECK(bp->bp_yiaddr, sizeof(bp->bp_yiaddr));
+ if (bp->bp_yiaddr.s_addr)
+ printf(" Y:%s", ipaddr_string(&bp->bp_yiaddr));
+
+ /* Server's ip address */
+ TCHECK(bp->bp_siaddr, sizeof(bp->bp_siaddr));
+ if (bp->bp_siaddr.s_addr)
+ printf(" S:%s", ipaddr_string(&bp->bp_siaddr));
+
+ /* Gateway's ip address */
+ TCHECK(bp->bp_giaddr, sizeof(bp->bp_giaddr));
+ if (bp->bp_giaddr.s_addr)
+ printf(" G:%s", ipaddr_string(&bp->bp_giaddr));
+
+ TCHECK(bp->bp_sname[0], sizeof(bp->bp_sname));
+ if (*bp->bp_sname) {
+ printf(" sname:");
+ if (printfn(bp->bp_sname, ep)) {
+ fputs(tstr + 1, stdout);
+ return;
+ }
+ }
+ TCHECK(bp->bp_file[0], sizeof(bp->bp_file));
+ if (*bp->bp_file) {
+ printf(" file:");
+ if (printfn(bp->bp_file, ep)) {
+ fputs(tstr + 1, stdout);
+ return;
+ }
+ }
+ /* Don't try to decode the vendor buffer unless we're verbose */
+ if (vflag <= 0)
+ return;
+
+ vdlen = sizeof(bp->bp_vend);
+ /* Vendor data can extend to the end of the packet. */
+ if (vdlen < (ep - bp->bp_vend))
+ vdlen = (ep - bp->bp_vend);
+
+ TCHECK(bp->bp_vend[0], vdlen);
+ printf(" vend");
+ if (!bcmp(bp->bp_vend, vm_rfc1048, sizeof(u_int32)))
+ rfc1048_print(bp->bp_vend, vdlen);
+ else if (!bcmp(bp->bp_vend, vm_cmu, sizeof(u_int32)))
+ cmu_print(bp->bp_vend, vdlen);
+ else
+ other_print(bp->bp_vend, vdlen);
+
+ return;
+ trunc:
+ fputs(tstr, stdout);
+#undef TCHECK
+}
+
+/*
+ * Option description data follows.
+ * These are decribed in: RFC-1048, RFC-1395, RFC-1497, RFC-1533
+ *
+ * The first char of each option string encodes the data format:
+ * ?: unknown
+ * a: ASCII
+ * b: byte (8-bit)
+ * i: inet address
+ * l: int32
+ * s: short (16-bit)
+ */
+char *
+rfc1048_opts[] = {
+ /* Originally from RFC-1048: */
+ "?PAD", /* 0: Padding - special, no data. */
+ "iSM", /* 1: subnet mask (RFC950)*/
+ "lTZ", /* 2: time offset, seconds from UTC */
+ "iGW", /* 3: gateways (or routers) */
+ "iTS", /* 4: time servers (RFC868) */
+ "iINS", /* 5: IEN name servers (IEN116) */
+ "iDNS", /* 6: domain name servers (RFC1035)(1034?) */
+ "iLOG", /* 7: MIT log servers */
+ "iCS", /* 8: cookie servers (RFC865) */
+ "iLPR", /* 9: lpr server (RFC1179) */
+ "iIPS", /* 10: impress servers (Imagen) */
+ "iRLP", /* 11: resource location servers (RFC887) */
+ "aHN", /* 12: host name (ASCII) */
+ "sBFS", /* 13: boot file size (in 512 byte blocks) */
+
+ /* Added by RFC-1395: */
+ "aDUMP", /* 14: Merit Dump File */
+ "aDNAM", /* 15: Domain Name (for DNS) */
+ "iSWAP", /* 16: Swap Server */
+ "aROOT", /* 17: Root Path */
+
+ /* Added by RFC-1497: */
+ "aEXTF", /* 18: Extensions Path (more options) */
+
+ /* Added by RFC-1533: (many, many options...) */
+#if 1 /* These might not be worth recognizing by name. */
+
+ /* IP Layer Parameters, per-host (RFC-1533, sect. 4) */
+ "bIP-forward", /* 19: IP Forwarding flag */
+ "bIP-srcroute", /* 20: IP Source Routing Enable flag */
+ "iIP-filters", /* 21: IP Policy Filter (addr pairs) */
+ "sIP-maxudp", /* 22: IP Max-UDP reassembly size */
+ "bIP-ttlive", /* 23: IP Time to Live */
+ "lIP-pmtuage", /* 24: IP Path MTU aging timeout */
+ "sIP-pmtutab", /* 25: IP Path MTU plateau table */
+
+ /* IP parameters, per-interface (RFC-1533, sect. 5) */
+ "sIP-mtu-sz", /* 26: IP MTU size */
+ "bIP-mtu-sl", /* 27: IP MTU all subnets local */
+ "bIP-bcast1", /* 28: IP Broadcast Addr ones flag */
+ "bIP-mask-d", /* 29: IP do mask discovery */
+ "bIP-mask-s", /* 30: IP do mask supplier */
+ "bIP-rt-dsc", /* 31: IP do router discovery */
+ "iIP-rt-sa", /* 32: IP router solicitation addr */
+ "iIP-routes", /* 33: IP static routes (dst,router) */
+
+ /* Link Layer parameters, per-interface (RFC-1533, sect. 6) */
+ "bLL-trailer", /* 34: do tralier encapsulation */
+ "lLL-arp-tmo", /* 35: ARP cache timeout */
+ "bLL-ether2", /* 36: Ethernet version 2 (IEEE 802.3) */
+
+ /* TCP parameters (RFC-1533, sect. 7) */
+ "bTCP-def-ttl", /* 37: default time to live */
+ "lTCP-KA-tmo", /* 38: keepalive time interval */
+ "bTCP-KA-junk", /* 39: keepalive sends extra junk */
+
+ /* Application and Service Parameters (RFC-1533, sect. 8) */
+ "aNISDOM", /* 40: NIS Domain (Sun YP) */
+ "iNISSRV", /* 41: NIS Servers */
+ "iNTPSRV", /* 42: NTP (time) Servers (RFC 1129) */
+ "?VSINFO", /* 43: Vendor Specific Info (encapsulated) */
+ "iNBiosNS", /* 44: NetBIOS Name Server (RFC-1001,1..2) */
+ "iNBiosDD", /* 45: NetBIOS Datagram Dist. Server. */
+ "bNBiosNT", /* 46: NetBIOS Note Type */
+ "?NBiosS", /* 47: NetBIOS Scope */
+ "iXW-FS", /* 48: X Window System Font Servers */
+ "iXW-DM", /* 49: X Window System Display Managers */
+
+ /* DHCP extensions (RFC-1533, sect. 9) */
+#endif
+};
+#define KNOWN_OPTIONS (sizeof(rfc1048_opts) / sizeof(rfc1048_opts[0]))
+
+static void print_string();
+
+static void
+rfc1048_print(bp, length)
+ register u_char *bp;
+ int length;
+{
+ u_char tag;
+ u_char *ep;
+ register int len, j;
+ u_int32 ul;
+ u_short us;
+ struct in_addr ia;
+ char *optstr;
+
+ printf("-rfc1395");
+
+ /* Step over magic cookie */
+ bp += sizeof(int32);
+ /* Setup end pointer */
+ ep = bp + length;
+ while (bp < ep) {
+ tag = *bp++;
+ /* Check for tags with no data first. */
+ if (tag == TAG_PAD)
+ continue;
+ if (tag == TAG_END)
+ return;
+ if (tag < KNOWN_OPTIONS) {
+ optstr = rfc1048_opts[tag];
+ printf(" %s:", optstr + 1);
+ } else {
+ printf(" T%d:", tag);
+ optstr = "?";
+ }
+ /* Now scan the length byte. */
+ len = *bp++;
+ if (bp + len > ep) {
+ /* truncated option */
+ printf(" |(%d>%d)", len, ep - bp);
+ return;
+ }
+ /* Print the option value(s). */
+ switch (optstr[0]) {
+
+ case 'a': /* ASCII string */
+ printfn(bp, bp + len);
+ bp += len;
+ len = 0;
+ break;
+
+ case 's': /* Word formats */
+ while (len >= 2) {
+ bcopy((char *) bp, (char *) &us, 2);
+ printf("%d", ntohs(us));
+ bp += 2;
+ len -= 2;
+ if (len) printf(",");
+ }
+ if (len) printf("(junk=%d)", len);
+ break;
+
+ case 'l': /* Long words */
+ while (len >= 4) {
+ bcopy((char *) bp, (char *) &ul, 4);
+ printf("%d", ntohl(ul));
+ bp += 4;
+ len -= 4;
+ if (len) printf(",");
+ }
+ if (len) printf("(junk=%d)", len);
+ break;
+
+ case 'i': /* INET addresses */
+ while (len >= 4) {
+ bcopy((char *) bp, (char *) &ia, 4);
+ printf("%s", ipaddr_string(&ia));
+ bp += 4;
+ len -= 4;
+ if (len) printf(",");
+ }
+ if (len) printf("(junk=%d)", len);
+ break;
+
+ case 'b':
+ default:
+ break;
+
+ } /* switch */
+
+ /* Print as characters, if appropriate. */
+ if (len) {
+ dump_hex(bp, len);
+ if (isascii(*bp) && isprint(*bp)) {
+ printf("(");
+ printfn(bp, bp + len);
+ printf(")");
+ }
+ bp += len;
+ len = 0;
+ }
+ } /* while bp < ep */
+}
+
+static void
+cmu_print(bp, length)
+ register u_char *bp;
+ int length;
+{
+ struct cmu_vend *v;
+ u_char *ep;
+
+ printf("-cmu");
+
+ v = (struct cmu_vend *) bp;
+ if (length < sizeof(*v)) {
+ printf(" |L=%d", length);
+ return;
+ }
+ /* Setup end pointer */
+ ep = bp + length;
+
+ /* Subnet mask */
+ if (v->v_flags & VF_SMASK) {
+ printf(" SM:%s", ipaddr_string(&v->v_smask));
+ }
+ /* Default gateway */
+ if (v->v_dgate.s_addr)
+ printf(" GW:%s", ipaddr_string(&v->v_dgate));
+
+ /* Domain name servers */
+ if (v->v_dns1.s_addr)
+ printf(" DNS1:%s", ipaddr_string(&v->v_dns1));
+ if (v->v_dns2.s_addr)
+ printf(" DNS2:%s", ipaddr_string(&v->v_dns2));
+
+ /* IEN-116 name servers */
+ if (v->v_ins1.s_addr)
+ printf(" INS1:%s", ipaddr_string(&v->v_ins1));
+ if (v->v_ins2.s_addr)
+ printf(" INS2:%s", ipaddr_string(&v->v_ins2));
+
+ /* Time servers */
+ if (v->v_ts1.s_addr)
+ printf(" TS1:%s", ipaddr_string(&v->v_ts1));
+ if (v->v_ts2.s_addr)
+ printf(" TS2:%s", ipaddr_string(&v->v_ts2));
+
+}
+
+
+/*
+ * Print out arbitrary, unknown vendor data.
+ */
+
+static void
+other_print(bp, length)
+ register u_char *bp;
+ int length;
+{
+ u_char *ep; /* end pointer */
+ u_char *zp; /* points one past last non-zero byte */
+ register int i, j;
+
+ /* Setup end pointer */
+ ep = bp + length;
+
+ /* Find the last non-zero byte. */
+ for (zp = ep; zp > bp; zp--) {
+ if (zp[-1] != 0)
+ break;
+ }
+
+ /* Print the all-zero case in a compact representation. */
+ if (zp == bp) {
+ printf("-all-zero");
+ return;
+ }
+ printf("-unknown");
+
+ /* Are there enough trailing zeros to make "00..." worthwhile? */
+ if (zp + 2 > ep)
+ zp = ep; /* print them all normally */
+
+ /* Now just print all the non-zero data. */
+ while (bp < zp) {
+ printf(".%02X", *bp);
+ bp++;
+ }
+
+ if (zp < ep)
+ printf(".00...");
+
+ return;
+}
+
+static void
+dump_hex(bp, len)
+ u_char *bp;
+ int len;
+{
+ while (len > 0) {
+ printf("%02X", *bp);
+ bp++;
+ len--;
+ if (len) printf(".");
+ }
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/trygetea.c b/libexec/bootpd/trygetea.c
new file mode 100644
index 0000000..e9314ae
--- /dev/null
+++ b/libexec/bootpd/trygetea.c
@@ -0,0 +1,46 @@
+/*
+ * trygetea.c - test program for getether.c
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#if defined(SUNOS) || defined(SVR4)
+#include <sys/sockio.h>
+#endif
+
+#include <net/if.h> /* for struct ifreq */
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#include <netdb.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+
+int debug = 0;
+char *progname;
+
+main(argc, argv)
+ char **argv;
+{
+ u_char ea[16]; /* Ethernet address */
+ int i;
+
+ progname = argv[0]; /* for report */
+
+ if (argc < 2) {
+ printf("need interface name\n");
+ exit(1);
+ }
+ if ((i = getether(argv[1], ea)) < 0) {
+ printf("Could not get Ethernet address (rc=%d)\n", i);
+ exit(1);
+ }
+ printf("Ether-addr");
+ for (i = 0; i < 6; i++)
+ printf(":%x", ea[i] & 0xFF);
+ printf("\n");
+
+ exit(0);
+}
diff --git a/libexec/bootpd/trygetif.c b/libexec/bootpd/trygetif.c
new file mode 100644
index 0000000..c6bb098
--- /dev/null
+++ b/libexec/bootpd/trygetif.c
@@ -0,0 +1,68 @@
+/*
+ * trygetif.c - test program for getif.c
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#if defined(SUNOS) || defined(SVR4)
+#include <sys/sockio.h>
+#endif
+
+#include <net/if.h> /* for struct ifreq */
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#include <netdb.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "getif.h"
+
+int debug = 0;
+char *progname;
+
+main(argc, argv)
+ char **argv;
+{
+ struct hostent *hep;
+ struct sockaddr ea; /* Ethernet address */
+ struct sockaddr_in *sip; /* Interface address */
+ struct ifreq *ifr;
+ struct in_addr dst_addr;
+ struct in_addr *dap;
+ int i, s;
+
+ progname = argv[0]; /* for report */
+
+ dap = NULL;
+ if (argc > 1) {
+ dap = &dst_addr;
+ if (isdigit(argv[1][0]))
+ dst_addr.s_addr = inet_addr(argv[1]);
+ else {
+ hep = gethostbyname(argv[1]);
+ if (!hep) {
+ printf("gethostbyname(%s)\n", argv[1]);
+ exit(1);
+ }
+ memcpy(&dst_addr, hep->h_addr, sizeof(dst_addr));
+ }
+ }
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket open");
+ exit(1);
+ }
+ ifr = getif(s, dap);
+ if (!ifr) {
+ printf("no interface for address\n");
+ exit(1);
+ }
+ printf("Intf-name:%s\n", ifr->ifr_name);
+ sip = (struct sockaddr_in *) &(ifr->ifr_addr);
+ printf("Intf-addr:%s\n", inet_ntoa(sip->sin_addr));
+
+ exit(0);
+}
diff --git a/libexec/bootpd/trylook.c b/libexec/bootpd/trylook.c
new file mode 100644
index 0000000..40652a2
--- /dev/null
+++ b/libexec/bootpd/trylook.c
@@ -0,0 +1,50 @@
+/*
+ * trylook.c - test program for lookup.c
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+#include "report.h"
+#include "lookup.h"
+
+extern char *ether_ntoa();
+extern char *inet_ntoa();
+
+int debug = 0;
+char *progname;
+
+main(argc, argv)
+ char **argv;
+{
+ int i;
+ struct in_addr in;
+ char *a;
+ u_char *hwa;
+
+ progname = argv[0]; /* for report */
+
+ for (i = 1; i < argc; i++) {
+
+ /* Host name */
+ printf("%s:", argv[i]);
+
+ /* IP addr */
+ if (lookup_ipa(argv[i], &in.s_addr))
+ a = "?";
+ else
+ a = inet_ntoa(in);
+ printf(" ipa=%s", a);
+
+ /* Ether addr */
+ hwa = lookup_hwa(argv[i], 1);
+ if (!hwa)
+ a = "?";
+ else
+ a = ether_ntoa(hwa);
+ printf(" hwa=%s\n", a);
+
+ }
+ exit(0);
+}
diff --git a/libexec/bootpd/tzone.c b/libexec/bootpd/tzone.c
new file mode 100644
index 0000000..4adc4ae
--- /dev/null
+++ b/libexec/bootpd/tzone.c
@@ -0,0 +1,44 @@
+/*
+ * tzone.c - get the timezone
+ *
+ * This is shared by bootpd and bootpef
+ */
+
+#ifdef SVR4
+/* XXX - Is this really SunOS specific? -gwr */
+/* This is in <time.h> but only visible if (__STDC__ == 1). */
+extern long timezone;
+#else /* SVR4 */
+/* BSD or SunOS */
+# include <sys/time.h>
+# include <syslog.h>
+#endif /* SVR4 */
+
+#include "bptypes.h"
+#include "report.h"
+#include "tzone.h"
+
+/* This is what other modules use. */
+int32 secondswest;
+
+/*
+ * Get our timezone offset so we can give it to clients if the
+ * configuration file doesn't specify one.
+ */
+void
+tzone_init()
+{
+#ifdef SVR4
+ /* XXX - Is this really SunOS specific? -gwr */
+ secondswest = timezone;
+#else /* SVR4 */
+ struct timezone tzp; /* Time zone offset for clients */
+ struct timeval tp; /* Time (extra baggage) */
+ if (gettimeofday(&tp, &tzp) < 0) {
+ secondswest = 0; /* Assume GMT for lack of anything better */
+ report(LOG_ERR, "gettimeofday: %s", get_errmsg());
+ } else {
+ secondswest = 60L * tzp.tz_minuteswest; /* Convert to seconds */
+ }
+#endif /* SVR4 */
+}
diff --git a/libexec/bootpd/tzone.h b/libexec/bootpd/tzone.h
new file mode 100644
index 0000000..ddd67c4
--- /dev/null
+++ b/libexec/bootpd/tzone.h
@@ -0,0 +1,3 @@
+/* tzone.h */
+extern int32 secondswest;
+extern void tzone_init();
diff --git a/libexec/comsat/Makefile b/libexec/comsat/Makefile
new file mode 100644
index 0000000..14e793d
--- /dev/null
+++ b/libexec/comsat/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+
+PROG= comsat
+MAN8= comsat.8
+
+.include <bsd.prog.mk>
diff --git a/libexec/comsat/comsat.8 b/libexec/comsat/comsat.8
new file mode 100644
index 0000000..2608676
--- /dev/null
+++ b/libexec/comsat/comsat.8
@@ -0,0 +1,100 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)comsat.8 8.1 (Berkeley) 6/4/93
+.\"
+.Dd June 4, 1993
+.Dt COMSAT 8
+.Os BSD 4.2
+.Sh NAME
+.Nm comsat
+.Nd biff server
+.Sh SYNOPSIS
+.Nm comsat
+.Sh DESCRIPTION
+.Nm Comsat
+is the server process which receives reports of incoming mail
+and notifies users if they have requested this service.
+.Nm Comsat
+receives messages on a datagram port associated with the
+.Dq biff
+service
+specification (see
+.Xr services 5
+and
+.Xr inetd 8 ) .
+The one line messages are of the form:
+.Pp
+.Dl user@mailbox-offset[:mailbox-name]
+.Pp
+If the
+.Em user
+specified is logged in to the system and the associated terminal has
+the owner execute bit turned on (by a
+.Dq Li biff y ) ,
+the
+.Em offset
+is used as a seek offset into the appropriate mailbox file and
+the first 7 lines or 560 characters of the message are printed
+on the user's terminal. Lines which appear to be part of
+the message header other than the
+.Dq From ,
+.Dq \&To ,
+.Dq Date ,
+or
+.Dq Subject
+lines are not included in the displayed message.
+.Pp
+If mailbox-name omitted, standard mailbox assumed.
+.Sh FILES
+.Bl -tag -width /var/mail/user -compact
+.It Pa /var/run/utmp
+to find out who's logged on and on what terminals
+.It Pa /var/mail/user
+standard mailbox
+.El
+.Sh SEE ALSO
+.Xr biff 1 ,
+.Xr inetd 8
+.Sh BUGS
+The message header filtering is prone to error.
+The density of the information presented is near the theoretical minimum.
+.Pp
+Users should be notified of mail which arrives on other
+machines than the one to which they are currently logged in.
+.Pp
+The notification should appear in a separate window so it
+does not mess up the screen.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/libexec/comsat/comsat.c b/libexec/comsat/comsat.c
new file mode 100644
index 0000000..f48c57c
--- /dev/null
+++ b/libexec/comsat/comsat.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)comsat.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <paths.h>
+#include <pwd.h>
+#include <sgtty.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <utmp.h>
+
+int debug = 0;
+#define dsyslog if (debug) syslog
+
+#define MAXIDLE 120
+
+char hostname[MAXHOSTNAMELEN];
+struct utmp *utmp = NULL;
+time_t lastmsgtime;
+int nutmp, uf;
+
+void jkfprintf __P((FILE *, char[], off_t));
+void mailfor __P((char *));
+void notify __P((struct utmp *, char[], off_t, int));
+void onalrm __P((int));
+void reapchildren __P((int));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct sockaddr_in from;
+ register int cc;
+ int fromlen;
+ char msgbuf[256];
+
+ /* verify proper invocation */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ (void)fprintf(stderr,
+ "comsat: getsockname: %s.\n", strerror(errno));
+ exit(1);
+ }
+ openlog("comsat", LOG_PID, LOG_DAEMON);
+ if (chdir(_PATH_MAILDIR)) {
+ syslog(LOG_ERR, "chdir: %s: %m", _PATH_MAILDIR);
+ (void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
+ exit(1);
+ }
+ if ((uf = open(_PATH_UTMP, O_RDONLY, 0)) < 0) {
+ syslog(LOG_ERR, "open: %s: %m", _PATH_UTMP);
+ (void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
+ exit(1);
+ }
+ (void)time(&lastmsgtime);
+ (void)gethostname(hostname, sizeof(hostname));
+ onalrm(0);
+ (void)signal(SIGALRM, onalrm);
+ (void)signal(SIGTTOU, SIG_IGN);
+ (void)signal(SIGCHLD, reapchildren);
+ for (;;) {
+ cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
+ if (cc <= 0) {
+ if (errno != EINTR)
+ sleep(1);
+ errno = 0;
+ continue;
+ }
+ if (!nutmp) /* no one has logged in yet */
+ continue;
+ sigblock(sigmask(SIGALRM));
+ msgbuf[cc] = '\0';
+ (void)time(&lastmsgtime);
+ mailfor(msgbuf);
+ sigsetmask(0L);
+ }
+}
+
+void
+reapchildren(signo)
+ int signo;
+{
+ while (wait3(NULL, WNOHANG, NULL) > 0);
+}
+
+void
+onalrm(signo)
+ int signo;
+{
+ static u_int utmpsize; /* last malloced size for utmp */
+ static u_int utmpmtime; /* last modification time for utmp */
+ struct stat statbf;
+
+ if (time(NULL) - lastmsgtime >= MAXIDLE)
+ exit(0);
+ (void)alarm((u_int)15);
+ (void)fstat(uf, &statbf);
+ if (statbf.st_mtime > utmpmtime) {
+ utmpmtime = statbf.st_mtime;
+ if (statbf.st_size > utmpsize) {
+ utmpsize = statbf.st_size + 10 * sizeof(struct utmp);
+ if ((utmp = realloc(utmp, utmpsize)) == NULL) {
+ syslog(LOG_ERR, "%s", strerror(errno));
+ exit(1);
+ }
+ }
+ (void)lseek(uf, (off_t)0, L_SET);
+ nutmp = read(uf, utmp, (int)statbf.st_size)/sizeof(struct utmp);
+ }
+}
+
+void
+mailfor(name)
+ char *name;
+{
+ register struct utmp *utp = &utmp[nutmp];
+ register char *cp;
+ char *file;
+ off_t offset;
+ int folder;
+ char buf[sizeof(_PATH_MAILDIR) + sizeof(utmp[0].ut_name) + 1];
+ char buf2[sizeof(_PATH_MAILDIR) + sizeof(utmp[0].ut_name) + 1];
+
+ if (!(cp = strchr(name, '@')))
+ return;
+ *cp = '\0';
+ offset = atoi(cp + 1);
+ if (!(cp = strchr(cp + 1, ':')))
+ file = name;
+ else
+ file = cp + 1;
+ sprintf(buf, "%s/%.*s", _PATH_MAILDIR, sizeof(utmp[0].ut_name), name);
+ if (*file != '/') {
+ sprintf(buf2, "%s/%.*s", _PATH_MAILDIR, sizeof(utmp[0].ut_name), file);
+ file = buf2;
+ }
+ folder = strcmp(buf, file);
+ while (--utp >= utmp)
+ if (!strncmp(utp->ut_name, name, sizeof(utmp[0].ut_name)))
+ notify(utp, file, offset, folder);
+}
+
+static char *cr;
+
+void
+notify(utp, file, offset, folder)
+ register struct utmp *utp;
+ char file[];
+ off_t offset;
+ int folder;
+{
+ FILE *tp;
+ struct stat stb;
+ struct sgttyb gttybuf;
+ char tty[20], name[sizeof(utmp[0].ut_name) + 1];
+
+ (void)snprintf(tty, sizeof(tty), "%s%.*s",
+ _PATH_DEV, (int)sizeof(utp->ut_line), utp->ut_line);
+ if (strchr(tty + sizeof(_PATH_DEV) - 1, '/')) {
+ /* A slash is an attempt to break security... */
+ syslog(LOG_AUTH | LOG_NOTICE, "'/' in \"%s\"", tty);
+ return;
+ }
+ if (stat(tty, &stb) || !(stb.st_mode & S_IEXEC)) {
+ dsyslog(LOG_DEBUG, "%s: wrong mode on %s", utp->ut_name, tty);
+ return;
+ }
+ dsyslog(LOG_DEBUG, "notify %s on %s\n", utp->ut_name, tty);
+ if (fork())
+ return;
+ (void)signal(SIGALRM, SIG_DFL);
+ (void)alarm((u_int)30);
+ if ((tp = fopen(tty, "w")) == NULL) {
+ dsyslog(LOG_ERR, "%s: %s", tty, strerror(errno));
+ _exit(-1);
+ }
+ (void)ioctl(fileno(tp), TIOCGETP, &gttybuf);
+ cr = (gttybuf.sg_flags&CRMOD) && !(gttybuf.sg_flags&RAW) ?
+ "\n" : "\n\r";
+ (void)strncpy(name, utp->ut_name, sizeof(utp->ut_name));
+ name[sizeof(name) - 1] = '\0';
+ (void)fprintf(tp, "%s\007New mail for %s@%.*s\007 has arrived%s%s%s:%s----%s",
+ cr, name, (int)sizeof(hostname), hostname,
+ folder ? cr : "", folder ? "to " : "", folder ? file : "",
+ cr, cr);
+ jkfprintf(tp, file, offset);
+ (void)fclose(tp);
+ _exit(0);
+}
+
+void
+jkfprintf(tp, name, offset)
+ register FILE *tp;
+ char name[];
+ off_t offset;
+{
+ register char *cp, ch;
+ register FILE *fi;
+ register int linecnt, charcnt, inheader;
+ register struct passwd *p;
+ char line[BUFSIZ];
+
+ /* Set effective uid to user in case mail drop is on nfs */
+ if ((p = getpwnam(name)) != NULL)
+ (void) setuid(p->pw_uid);
+
+ if ((fi = fopen(name, "r")) == NULL)
+ return;
+
+ (void)fseek(fi, offset, L_SET);
+ /*
+ * Print the first 7 lines or 560 characters of the new mail
+ * (whichever comes first). Skip header crap other than
+ * From, Subject, To, and Date.
+ */
+ linecnt = 7;
+ charcnt = 560;
+ inheader = 1;
+ while (fgets(line, sizeof(line), fi) != NULL) {
+ if (inheader) {
+ if (line[0] == '\n') {
+ inheader = 0;
+ continue;
+ }
+ if (line[0] == ' ' || line[0] == '\t' ||
+ strncmp(line, "From:", 5) &&
+ strncmp(line, "Subject:", 8))
+ continue;
+ }
+ if (linecnt <= 0 || charcnt <= 0) {
+ (void)fprintf(tp, "...more...%s", cr);
+ (void)fclose(fi);
+ return;
+ }
+ /* strip weird stuff so can't trojan horse stupid terminals */
+ for (cp = line; (ch = *cp) && ch != '\n'; ++cp, --charcnt) {
+ if (!isprint(ch)) {
+ if (ch & 0x80)
+ (void)fputs("M-", tp);
+ ch &= 0177;
+ if (!isprint(ch)) {
+ if (ch == 0177)
+ ch = '?';
+ else
+ ch |= 0x40;
+ (void)fputc('^', tp);
+ }
+ }
+ (void)fputc(ch, tp);
+ }
+ (void)fputs(cr, tp);
+ --linecnt;
+ }
+ (void)fprintf(tp, "----%s\n", cr);
+ (void)fclose(fi);
+}
diff --git a/libexec/fingerd/Makefile b/libexec/fingerd/Makefile
new file mode 100644
index 0000000..bd39992
--- /dev/null
+++ b/libexec/fingerd/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+
+PROG= fingerd
+MAN8= fingerd.8
+
+.include <bsd.prog.mk>
diff --git a/libexec/fingerd/fingerd.8 b/libexec/fingerd/fingerd.8
new file mode 100644
index 0000000..c5c0762
--- /dev/null
+++ b/libexec/fingerd/fingerd.8
@@ -0,0 +1,139 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)fingerd.8 8.1 (Berkeley) 6/4/93
+.\"
+.Dd June 4, 1993
+.Dt FINGERD 8
+.Os BSD 4.3
+.Sh NAME
+.Nm fingerd
+.Nd remote user information server
+.Sh SYNOPSIS
+.Nm fingerd
+.Op Fl s
+.Op Fl l
+.Op Fl p Ar filename
+.Sh DESCRIPTION
+.Nm Fingerd
+is a simple protocol based on
+.%T RFC1196
+that provides an interface to the
+Name and Finger programs at several network sites.
+The program is supposed to return a friendly,
+human-oriented status report on either the system at the moment
+or a particular person in depth.
+There is no required format and the
+protocol consists mostly of specifying a single
+.Dq command line .
+.Pp
+.Nm Fingerd
+listens for
+.Tn TCP
+requests at port 79.
+Once connected it reads a single command line
+terminated by a
+.Aq Tn CRLF
+which is passed to
+.Xr finger 1 .
+.Nm Fingerd
+closes its connections as soon as the output is finished.
+.Pp
+If the line is null (i.e. just a
+.Aq Tn CRLF
+is sent) then
+.Xr finger
+returns a
+.Dq default
+report that lists all people logged into
+the system at that moment.
+.Pp
+If a user name is specified (e.g.
+.Pf eric Aq Tn CRLF )
+then the
+response lists more extended information for only that particular user,
+whether logged in or not.
+Allowable
+.Dq names
+in the command line include both
+.Dq login names
+and
+.Dq user names .
+If a name is ambiguous, all possible derivations are returned.
+.Pp
+The following options may be passed to
+.Nm fingerd
+as server program arguments in
+.Pa /etc/inetd.conf :
+.Bl -tag -width Ds
+.It Fl s
+Enable secure mode.
+Queries without a user name are rejected and
+forwarding of queries to other remote hosts is denied.
+.It Fl l
+Enable logging.
+The name of the host originating the query is reported via
+.Xr syslog 3
+at LOG_NOTICE priority.
+.It Fl p
+Use an alternate program as the local information provider.
+The default local program
+executed by
+.Nm fingerd
+is
+.Xr finger 1 .
+By specifying a customized local server,
+this option allows a system manager
+to have more control over what information is
+provided to remote sites.
+.El
+.Sh SEE ALSO
+.Xr finger 1
+.Sh BUGS
+Connecting directly to the server from a
+.Tn TIP
+or an equally narrow-minded
+.Tn TELNET Ns \-protocol
+user program can result
+in meaningless attempts at option negotiation being sent to the
+server, which will foul up the command line interpretation.
+.Nm Fingerd
+should be taught to filter out
+.Tn IAC Ns \'s
+and perhaps even respond
+negatively
+.Pq Tn IAC WON'T
+to all option commands received.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/libexec/fingerd/fingerd.c b/libexec/fingerd/fingerd.c
new file mode 100644
index 0000000..455d6e9
--- /dev/null
+++ b/libexec/fingerd/fingerd.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*
+static char sccsid[] = "@(#)fingerd.c 8.1 (Berkeley) 6/4/93";
+*/
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include <unistd.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include "pathnames.h"
+
+void logerr __P((const char *, ...));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register FILE *fp;
+ register int ch;
+ register char *lp;
+ struct hostent *hp;
+ struct sockaddr_in sin;
+ int p[2], logging, secure, sval;
+#define ENTRIES 50
+ char **ap, *av[ENTRIES + 1], **comp, line[1024], *prog;
+
+ prog = _PATH_FINGER;
+ logging = secure = 0;
+ openlog("fingerd", LOG_PID | LOG_CONS, LOG_DAEMON);
+ opterr = 0;
+ while ((ch = getopt(argc, argv, "slp:")) != EOF)
+ switch (ch) {
+ case 'l':
+ logging = 1;
+ break;
+ case 'p':
+ prog = optarg;
+ break;
+ case 's':
+ secure = 1;
+ break;
+ case '?':
+ default:
+ logerr("illegal option -- %c", ch);
+ }
+
+ if (logging) {
+ sval = sizeof(sin);
+ if (getpeername(0, (struct sockaddr *)&sin, &sval) < 0)
+ logerr("getpeername: %s", strerror(errno));
+ if (hp = gethostbyaddr((char *)&sin.sin_addr.s_addr,
+ sizeof(sin.sin_addr.s_addr), AF_INET))
+ lp = hp->h_name;
+ else
+ lp = inet_ntoa(sin.sin_addr);
+ syslog(LOG_NOTICE, "query from %s", lp);
+ }
+
+ /*
+ * Enable server-side Transaction TCP.
+ */
+ {
+ int one = 1;
+ if (setsockopt(STDOUT_FILENO, IPPROTO_TCP, TCP_NOPUSH, &one,
+ sizeof one) < 0) {
+ logerr("setsockopt(TCP_NOPUSH) failed: %m");
+ }
+ }
+
+ if (!fgets(line, sizeof(line), stdin))
+ exit(1);
+
+ comp = &av[1];
+ av[2] = "--";
+ for (lp = line, ap = &av[3];;) {
+ *ap = strtok(lp, " \t\r\n");
+ if (!*ap) {
+ if (secure && ap == &av[3]) {
+ puts("must provide username\r\n");
+ exit(1);
+ }
+ break;
+ }
+ if (secure && strchr(*ap, '@')) {
+ puts("forwarding service denied\r\n");
+ exit(1);
+ }
+
+ /* RFC742: "/[Ww]" == "-l" */
+ if ((*ap)[0] == '/' && ((*ap)[1] == 'W' || (*ap)[1] == 'w')) {
+ av[1] = "-l";
+ comp = &av[0];
+ }
+ else if (++ap == av + ENTRIES)
+ break;
+ lp = NULL;
+ }
+
+ if (lp = strrchr(prog, '/'))
+ *comp = ++lp;
+ else
+ *comp = prog;
+ if (pipe(p) < 0)
+ logerr("pipe: %s", strerror(errno));
+
+ switch(vfork()) {
+ case 0:
+ (void)close(p[0]);
+ if (p[1] != 1) {
+ (void)dup2(p[1], 1);
+ (void)close(p[1]);
+ }
+ execv(prog, comp);
+ logerr("execv: %s: %s", prog, strerror(errno));
+ _exit(1);
+ case -1:
+ logerr("fork: %s", strerror(errno));
+ }
+ (void)close(p[1]);
+ if (!(fp = fdopen(p[0], "r")))
+ logerr("fdopen: %s", strerror(errno));
+ while ((ch = getc(fp)) != EOF) {
+ if (ch == '\n')
+ putchar('\r');
+ putchar(ch);
+ }
+ exit(0);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+logerr(const char *fmt, ...)
+#else
+logerr(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/libexec/fingerd/pathnames.h b/libexec/fingerd/pathnames.h
new file mode 100644
index 0000000..864a8f6
--- /dev/null
+++ b/libexec/fingerd/pathnames.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ */
+
+#define _PATH_FINGER "/usr/bin/finger"
diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile
new file mode 100644
index 0000000..4348d4f
--- /dev/null
+++ b/libexec/ftpd/Makefile
@@ -0,0 +1,15 @@
+# @(#)Makefile 8.2 (Berkeley) 4/4/94
+
+PROG= ftpd
+MAN8= ftpd.8
+SRCS= ftpd.c ftpcmd.c logwtmp.c popen.c skey-stuff.c
+
+CFLAGS+=-DSETPROCTITLE -DSKEY -DSTATS
+CFLAGS+=-DFTP_DATA_BOTTOM=40000 -DFTP_DATA_TOP=44999
+
+LDADD= -lskey -lmd -lcrypt -lutil
+DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT} ${LIBUTIL}
+
+CLEANFILES+=ftpcmd.c y.tab.h
+
+.include <bsd.prog.mk>
diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h
new file mode 100644
index 0000000..fe784c8
--- /dev/null
+++ b/libexec/ftpd/extern.h
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.2 (Berkeley) 4/4/94
+ */
+
+void blkfree __P((char **));
+char **copyblk __P((char **));
+void cwd __P((char *));
+void delete __P((char *));
+void dologout __P((int));
+void fatal __P((char *));
+int ftpd_pclose __P((FILE *));
+FILE *ftpd_popen __P((char *, char *));
+char *getline __P((char *, int, FILE *));
+void logwtmp __P((char *, char *, char *));
+void lreply __P((int, const char *, ...));
+void makedir __P((char *));
+void nack __P((char *));
+void pass __P((char *));
+void passive __P((void));
+void perror_reply __P((int, char *));
+void pwd __P((void));
+void removedir __P((char *));
+void renamecmd __P((char *, char *));
+char *renamefrom __P((char *));
+void reply __P((int, const char *, ...));
+void retrieve __P((char *, char *));
+void send_file_list __P((char *));
+#ifdef OLD_SETPROCTITLE
+void setproctitle __P((const char *, ...));
+#endif
+void statcmd __P((void));
+void statfilecmd __P((char *));
+void store __P((char *, char *, int));
+void upper __P((char *));
+void user __P((char *));
+void yyerror __P((char *));
diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y
new file mode 100644
index 0000000..4473b15
--- /dev/null
+++ b/libexec/ftpd/ftpcmd.y
@@ -0,0 +1,1267 @@
+/*
+ * Copyright (c) 1985, 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
+ */
+
+/*
+ * Grammar for FTP commands.
+ * See RFC 959.
+ */
+
+%{
+
+#ifndef lint
+static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/ftp.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <glob.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <libutil.h>
+
+#include "extern.h"
+
+extern struct sockaddr_in data_dest;
+extern int logged_in;
+extern struct passwd *pw;
+extern int guest;
+extern int logging;
+extern int type;
+extern int form;
+extern int debug;
+extern int timeout;
+extern int maxtimeout;
+extern int pdata;
+extern char hostname[], remotehost[];
+extern char proctitle[];
+extern int usedefault;
+extern int transflag;
+extern char tmpline[];
+
+off_t restart_point;
+
+static int cmd_type;
+static int cmd_form;
+static int cmd_bytesz;
+char cbuf[512];
+char *fromname;
+
+%}
+
+%union {
+ int i;
+ char *s;
+}
+
+%token
+ A B C E F I
+ L N P R S T
+
+ SP CRLF COMMA
+
+ USER PASS ACCT REIN QUIT PORT
+ PASV TYPE STRU MODE RETR STOR
+ APPE MLFL MAIL MSND MSOM MSAM
+ MRSQ MRCP ALLO REST RNFR RNTO
+ ABOR DELE CWD LIST NLST SITE
+ STAT HELP NOOP MKD RMD PWD
+ CDUP STOU SMNT SYST SIZE MDTM
+
+ UMASK IDLE CHMOD
+
+ LEXERR
+
+%token <s> STRING
+%token <i> NUMBER
+
+%type <i> check_login octal_number byte_size
+%type <i> struct_code mode_code type_code form_code
+%type <s> pathstring pathname password username
+
+%start cmd_list
+
+%%
+
+cmd_list
+ : /* empty */
+ | cmd_list cmd
+ {
+ fromname = (char *) 0;
+ restart_point = (off_t) 0;
+ }
+ | cmd_list rcmd
+ ;
+
+cmd
+ : USER SP username CRLF
+ {
+ user($3);
+ free($3);
+ }
+ | PASS SP password CRLF
+ {
+ pass($3);
+ free($3);
+ }
+ | PORT SP host_port CRLF
+ {
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ reply(200, "PORT command successful.");
+ }
+ | PASV CRLF
+ {
+ passive();
+ }
+ | TYPE SP type_code CRLF
+ {
+ switch (cmd_type) {
+
+ case TYPE_A:
+ if (cmd_form == FORM_N) {
+ reply(200, "Type set to A.");
+ type = cmd_type;
+ form = cmd_form;
+ } else
+ reply(504, "Form must be N.");
+ break;
+
+ case TYPE_E:
+ reply(504, "Type E not implemented.");
+ break;
+
+ case TYPE_I:
+ reply(200, "Type set to I.");
+ type = cmd_type;
+ break;
+
+ case TYPE_L:
+#if NBBY == 8
+ if (cmd_bytesz == 8) {
+ reply(200,
+ "Type set to L (byte size 8).");
+ type = cmd_type;
+ } else
+ reply(504, "Byte size must be 8.");
+#else /* NBBY == 8 */
+ UNIMPLEMENTED for NBBY != 8
+#endif /* NBBY == 8 */
+ }
+ }
+ | STRU SP struct_code CRLF
+ {
+ switch ($3) {
+
+ case STRU_F:
+ reply(200, "STRU F ok.");
+ break;
+
+ default:
+ reply(504, "Unimplemented STRU type.");
+ }
+ }
+ | MODE SP mode_code CRLF
+ {
+ switch ($3) {
+
+ case MODE_S:
+ reply(200, "MODE S ok.");
+ break;
+
+ default:
+ reply(502, "Unimplemented MODE type.");
+ }
+ }
+ | ALLO SP NUMBER CRLF
+ {
+ reply(202, "ALLO command ignored.");
+ }
+ | ALLO SP NUMBER SP R SP NUMBER CRLF
+ {
+ reply(202, "ALLO command ignored.");
+ }
+ | RETR check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ retrieve((char *) 0, $4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | STOR check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ store($4, "w", 0);
+ if ($4 != NULL)
+ free($4);
+ }
+ | APPE check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ store($4, "a", 0);
+ if ($4 != NULL)
+ free($4);
+ }
+ | NLST check_login CRLF
+ {
+ if ($2)
+ send_file_list(".");
+ }
+ | NLST check_login SP STRING CRLF
+ {
+ if ($2 && $4 != NULL)
+ send_file_list($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | LIST check_login CRLF
+ {
+ if ($2)
+ retrieve("/bin/ls -lgA", "");
+ }
+ | LIST check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ retrieve("/bin/ls -lgA %s", $4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | STAT check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ statfilecmd($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | STAT CRLF
+ {
+ statcmd();
+ }
+ | DELE check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ delete($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | RNTO SP pathname CRLF
+ {
+ if (fromname) {
+ renamecmd(fromname, $3);
+ free(fromname);
+ fromname = (char *) 0;
+ } else {
+ reply(503, "Bad sequence of commands.");
+ }
+ free($3);
+ }
+ | ABOR CRLF
+ {
+ reply(225, "ABOR command successful.");
+ }
+ | CWD check_login CRLF
+ {
+ if ($2)
+ cwd(pw->pw_dir);
+ }
+ | CWD check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ cwd($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | HELP CRLF
+ {
+ help(cmdtab, (char *) 0);
+ }
+ | HELP SP STRING CRLF
+ {
+ char *cp = $3;
+
+ if (strncasecmp(cp, "SITE", 4) == 0) {
+ cp = $3 + 4;
+ if (*cp == ' ')
+ cp++;
+ if (*cp)
+ help(sitetab, cp);
+ else
+ help(sitetab, (char *) 0);
+ } else
+ help(cmdtab, $3);
+ }
+ | NOOP CRLF
+ {
+ reply(200, "NOOP command successful.");
+ }
+ | MKD check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ makedir($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | RMD check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ removedir($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | PWD check_login CRLF
+ {
+ if ($2)
+ pwd();
+ }
+ | CDUP check_login CRLF
+ {
+ if ($2)
+ cwd("..");
+ }
+ | SITE SP HELP CRLF
+ {
+ help(sitetab, (char *) 0);
+ }
+ | SITE SP HELP SP STRING CRLF
+ {
+ help(sitetab, $5);
+ }
+ | SITE SP UMASK check_login CRLF
+ {
+ int oldmask;
+
+ if ($4) {
+ oldmask = umask(0);
+ (void) umask(oldmask);
+ reply(200, "Current UMASK is %03o", oldmask);
+ }
+ }
+ | SITE SP UMASK check_login SP octal_number CRLF
+ {
+ int oldmask;
+
+ if ($4) {
+ if (($6 == -1) || ($6 > 0777)) {
+ reply(501, "Bad UMASK value");
+ } else {
+ oldmask = umask($6);
+ reply(200,
+ "UMASK set to %03o (was %03o)",
+ $6, oldmask);
+ }
+ }
+ }
+ | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
+ {
+ if ($4 && ($8 != NULL)) {
+ if ($6 > 0777)
+ reply(501,
+ "CHMOD: Mode value must be between 0 and 0777");
+ else if (chmod($8, $6) < 0)
+ perror_reply(550, $8);
+ else
+ reply(200, "CHMOD command successful.");
+ }
+ if ($8 != NULL)
+ free($8);
+ }
+ | SITE SP IDLE CRLF
+ {
+ reply(200,
+ "Current IDLE time limit is %d seconds; max %d",
+ timeout, maxtimeout);
+ }
+ | SITE SP IDLE SP NUMBER CRLF
+ {
+ if ($5 < 30 || $5 > maxtimeout) {
+ reply(501,
+ "Maximum IDLE time must be between 30 and %d seconds",
+ maxtimeout);
+ } else {
+ timeout = $5;
+ (void) alarm((unsigned) timeout);
+ reply(200,
+ "Maximum IDLE time set to %d seconds",
+ timeout);
+ }
+ }
+ | STOU check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ store($4, "w", 1);
+ if ($4 != NULL)
+ free($4);
+ }
+ | SYST CRLF
+ {
+#ifdef unix
+#ifdef BSD
+ reply(215, "UNIX Type: L%d Version: BSD-%d",
+ NBBY, BSD);
+#else /* BSD */
+ reply(215, "UNIX Type: L%d", NBBY);
+#endif /* BSD */
+#else /* unix */
+ reply(215, "UNKNOWN Type: L%d", NBBY);
+#endif /* unix */
+ }
+
+ /*
+ * SIZE is not in RFC959, but Postel has blessed it and
+ * it will be in the updated RFC.
+ *
+ * Return size of file in a format suitable for
+ * using with RESTART (we just count bytes).
+ */
+ | SIZE check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ sizecmd($4);
+ if ($4 != NULL)
+ free($4);
+ }
+
+ /*
+ * MDTM is not in RFC959, but Postel has blessed it and
+ * it will be in the updated RFC.
+ *
+ * Return modification time of file as an ISO 3307
+ * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
+ * where xxx is the fractional second (of any precision,
+ * not necessarily 3 digits)
+ */
+ | MDTM check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL) {
+ struct stat stbuf;
+ if (stat($4, &stbuf) < 0)
+ reply(550, "%s: %s",
+ $4, strerror(errno));
+ else if (!S_ISREG(stbuf.st_mode)) {
+ reply(550, "%s: not a plain file.", $4);
+ } else {
+ struct tm *t;
+ t = gmtime(&stbuf.st_mtime);
+ reply(213,
+ "19%02d%02d%02d%02d%02d%02d",
+ t->tm_year, t->tm_mon+1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+ }
+ }
+ if ($4 != NULL)
+ free($4);
+ }
+ | QUIT CRLF
+ {
+ reply(221, "Goodbye.");
+ dologout(0);
+ }
+ | error CRLF
+ {
+ yyerrok;
+ }
+ ;
+rcmd
+ : RNFR check_login SP pathname CRLF
+ {
+ char *renamefrom();
+
+ restart_point = (off_t) 0;
+ if ($2 && $4) {
+ fromname = renamefrom($4);
+ if (fromname == (char *) 0 && $4) {
+ free($4);
+ }
+ }
+ }
+ | REST SP byte_size CRLF
+ {
+ fromname = (char *) 0;
+ restart_point = $3; /* XXX $3 is only "int" */
+ reply(350, "Restarting at %qd. %s", restart_point,
+ "Send STORE or RETRIEVE to initiate transfer.");
+ }
+ ;
+
+username
+ : STRING
+ ;
+
+password
+ : /* empty */
+ {
+ $$ = (char *)calloc(1, sizeof(char));
+ }
+ | STRING
+ ;
+
+byte_size
+ : NUMBER
+ ;
+
+host_port
+ : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER
+ {
+ char *a, *p;
+
+ a = (char *)&data_dest.sin_addr;
+ a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
+ p = (char *)&data_dest.sin_port;
+ p[0] = $9; p[1] = $11;
+ data_dest.sin_family = AF_INET;
+ }
+ ;
+
+form_code
+ : N
+ {
+ $$ = FORM_N;
+ }
+ | T
+ {
+ $$ = FORM_T;
+ }
+ | C
+ {
+ $$ = FORM_C;
+ }
+ ;
+
+type_code
+ : A
+ {
+ cmd_type = TYPE_A;
+ cmd_form = FORM_N;
+ }
+ | A SP form_code
+ {
+ cmd_type = TYPE_A;
+ cmd_form = $3;
+ }
+ | E
+ {
+ cmd_type = TYPE_E;
+ cmd_form = FORM_N;
+ }
+ | E SP form_code
+ {
+ cmd_type = TYPE_E;
+ cmd_form = $3;
+ }
+ | I
+ {
+ cmd_type = TYPE_I;
+ }
+ | L
+ {
+ cmd_type = TYPE_L;
+ cmd_bytesz = NBBY;
+ }
+ | L SP byte_size
+ {
+ cmd_type = TYPE_L;
+ cmd_bytesz = $3;
+ }
+ /* this is for a bug in the BBN ftp */
+ | L byte_size
+ {
+ cmd_type = TYPE_L;
+ cmd_bytesz = $2;
+ }
+ ;
+
+struct_code
+ : F
+ {
+ $$ = STRU_F;
+ }
+ | R
+ {
+ $$ = STRU_R;
+ }
+ | P
+ {
+ $$ = STRU_P;
+ }
+ ;
+
+mode_code
+ : S
+ {
+ $$ = MODE_S;
+ }
+ | B
+ {
+ $$ = MODE_B;
+ }
+ | C
+ {
+ $$ = MODE_C;
+ }
+ ;
+
+pathname
+ : pathstring
+ {
+ /*
+ * Problem: this production is used for all pathname
+ * processing, but only gives a 550 error reply.
+ * This is a valid reply in some cases but not in others.
+ */
+ if (logged_in && $1 && *$1 == '~') {
+ glob_t gl;
+ int flags =
+ GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
+
+ memset(&gl, 0, sizeof(gl));
+ if (glob($1, flags, NULL, &gl) ||
+ gl.gl_pathc == 0) {
+ reply(550, "not found");
+ $$ = NULL;
+ } else {
+ $$ = strdup(gl.gl_pathv[0]);
+ }
+ globfree(&gl);
+ free($1);
+ } else
+ $$ = $1;
+ }
+ ;
+
+pathstring
+ : STRING
+ ;
+
+octal_number
+ : NUMBER
+ {
+ int ret, dec, multby, digit;
+
+ /*
+ * Convert a number that was read as decimal number
+ * to what it would be if it had been read as octal.
+ */
+ dec = $1;
+ multby = 1;
+ ret = 0;
+ while (dec) {
+ digit = dec%10;
+ if (digit > 7) {
+ ret = -1;
+ break;
+ }
+ ret += digit * multby;
+ multby *= 8;
+ dec /= 10;
+ }
+ $$ = ret;
+ }
+ ;
+
+
+check_login
+ : /* empty */
+ {
+ if (logged_in)
+ $$ = 1;
+ else {
+ reply(530, "Please login with USER and PASS.");
+ $$ = 0;
+ }
+ }
+ ;
+
+%%
+
+extern jmp_buf errcatch;
+
+#define CMD 0 /* beginning of command */
+#define ARGS 1 /* expect miscellaneous arguments */
+#define STR1 2 /* expect SP followed by STRING */
+#define STR2 3 /* expect STRING */
+#define OSTR 4 /* optional SP then STRING */
+#define ZSTR1 5 /* SP then optional STRING */
+#define ZSTR2 6 /* optional STRING after SP */
+#define SITECMD 7 /* SITE command */
+#define NSTR 8 /* Number followed by a string */
+
+struct tab {
+ char *name;
+ short token;
+ short state;
+ short implemented; /* 1 if command is implemented */
+ char *help;
+};
+
+struct tab cmdtab[] = { /* In order defined in RFC 765 */
+ { "USER", USER, STR1, 1, "<sp> username" },
+ { "PASS", PASS, ZSTR1, 1, "<sp> password" },
+ { "ACCT", ACCT, STR1, 0, "(specify account)" },
+ { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
+ { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
+ { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
+ { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
+ { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
+ { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
+ { "STRU", STRU, ARGS, 1, "(specify file structure)" },
+ { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
+ { "RETR", RETR, STR1, 1, "<sp> file-name" },
+ { "STOR", STOR, STR1, 1, "<sp> file-name" },
+ { "APPE", APPE, STR1, 1, "<sp> file-name" },
+ { "MLFL", MLFL, OSTR, 0, "(mail file)" },
+ { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
+ { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
+ { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
+ { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
+ { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
+ { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
+ { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
+ { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
+ { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
+ { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
+ { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
+ { "DELE", DELE, STR1, 1, "<sp> file-name" },
+ { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
+ { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
+ { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
+ { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
+ { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
+ { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
+ { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
+ { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
+ { "NOOP", NOOP, ARGS, 1, "" },
+ { "MKD", MKD, STR1, 1, "<sp> path-name" },
+ { "XMKD", MKD, STR1, 1, "<sp> path-name" },
+ { "RMD", RMD, STR1, 1, "<sp> path-name" },
+ { "XRMD", RMD, STR1, 1, "<sp> path-name" },
+ { "PWD", PWD, ARGS, 1, "(return current directory)" },
+ { "XPWD", PWD, ARGS, 1, "(return current directory)" },
+ { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
+ { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
+ { "STOU", STOU, STR1, 1, "<sp> file-name" },
+ { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
+ { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
+ { NULL, 0, 0, 0, 0 }
+};
+
+struct tab sitetab[] = {
+ { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
+ { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
+ { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
+ { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
+ { NULL, 0, 0, 0, 0 }
+};
+
+static char *copy __P((char *));
+static void help __P((struct tab *, char *));
+static struct tab *
+ lookup __P((struct tab *, char *));
+static void sizecmd __P((char *));
+static void toolong __P((int));
+static int yylex __P((void));
+
+static struct tab *
+lookup(p, cmd)
+ struct tab *p;
+ char *cmd;
+{
+
+ for (; p->name != NULL; p++)
+ if (strcmp(cmd, p->name) == 0)
+ return (p);
+ return (0);
+}
+
+#include <arpa/telnet.h>
+
+/*
+ * getline - a hacked up version of fgets to ignore TELNET escape codes.
+ */
+char *
+getline(s, n, iop)
+ char *s;
+ int n;
+ FILE *iop;
+{
+ int c;
+ register char *cs;
+
+ cs = s;
+/* tmpline may contain saved command from urgent mode interruption */
+ for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
+ *cs++ = tmpline[c];
+ if (tmpline[c] == '\n') {
+ *cs++ = '\0';
+ if (debug)
+ syslog(LOG_DEBUG, "command: %s", s);
+ tmpline[0] = '\0';
+ return(s);
+ }
+ if (c == 0)
+ tmpline[0] = '\0';
+ }
+ while ((c = getc(iop)) != EOF) {
+ c &= 0377;
+ if (c == IAC) {
+ if ((c = getc(iop)) != EOF) {
+ c &= 0377;
+ switch (c) {
+ case WILL:
+ case WONT:
+ c = getc(iop);
+ printf("%c%c%c", IAC, DONT, 0377&c);
+ (void) fflush(stdout);
+ continue;
+ case DO:
+ case DONT:
+ c = getc(iop);
+ printf("%c%c%c", IAC, WONT, 0377&c);
+ (void) fflush(stdout);
+ continue;
+ case IAC:
+ break;
+ default:
+ continue; /* ignore command */
+ }
+ }
+ }
+ *cs++ = c;
+ if (--n <= 0 || c == '\n')
+ break;
+ }
+ if (c == EOF && cs == s)
+ return (NULL);
+ *cs++ = '\0';
+ if (debug) {
+ if (!guest && strncasecmp("pass ", s, 5) == 0) {
+ /* Don't syslog passwords */
+ syslog(LOG_DEBUG, "command: %.5s ???", s);
+ } else {
+ register char *cp;
+ register int len;
+
+ /* Don't syslog trailing CR-LF */
+ len = strlen(s);
+ cp = s + len - 1;
+ while (cp >= s && (*cp == '\n' || *cp == '\r')) {
+ --cp;
+ --len;
+ }
+ syslog(LOG_DEBUG, "command: %.*s", len, s);
+ }
+ }
+ return (s);
+}
+
+static void
+toolong(signo)
+ int signo;
+{
+
+ reply(421,
+ "Timeout (%d seconds): closing control connection.", timeout);
+ if (logging)
+ syslog(LOG_INFO, "User %s timed out after %d seconds",
+ (pw ? pw -> pw_name : "unknown"), timeout);
+ dologout(1);
+}
+
+static int
+yylex()
+{
+ static int cpos, state;
+ char *cp, *cp2;
+ struct tab *p;
+ int n;
+ char c;
+
+ for (;;) {
+ switch (state) {
+
+ case CMD:
+ (void) signal(SIGALRM, toolong);
+ (void) alarm((unsigned) timeout);
+ if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
+ reply(221, "You could at least say goodbye.");
+ dologout(0);
+ }
+ (void) alarm(0);
+#ifdef SETPROCTITLE
+ if (strncasecmp(cbuf, "PASS", 4) != NULL)
+ setproctitle("%s: %s", proctitle, cbuf);
+#endif /* SETPROCTITLE */
+ if ((cp = strchr(cbuf, '\r'))) {
+ *cp++ = '\n';
+ *cp = '\0';
+ }
+ if ((cp = strpbrk(cbuf, " \n")))
+ cpos = cp - cbuf;
+ if (cpos == 0)
+ cpos = 4;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ upper(cbuf);
+ p = lookup(cmdtab, cbuf);
+ cbuf[cpos] = c;
+ if (p != 0) {
+ if (p->implemented == 0) {
+ nack(p->name);
+ longjmp(errcatch,0);
+ /* NOTREACHED */
+ }
+ state = p->state;
+ yylval.s = p->name;
+ return (p->token);
+ }
+ break;
+
+ case SITECMD:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ return (SP);
+ }
+ cp = &cbuf[cpos];
+ if ((cp2 = strpbrk(cp, " \n")))
+ cpos = cp2 - cbuf;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ upper(cp);
+ p = lookup(sitetab, cp);
+ cbuf[cpos] = c;
+ if (guest == 0 && p != 0) {
+ if (p->implemented == 0) {
+ state = CMD;
+ nack(p->name);
+ longjmp(errcatch,0);
+ /* NOTREACHED */
+ }
+ state = p->state;
+ yylval.s = p->name;
+ return (p->token);
+ }
+ state = CMD;
+ break;
+
+ case OSTR:
+ if (cbuf[cpos] == '\n') {
+ state = CMD;
+ return (CRLF);
+ }
+ /* FALLTHROUGH */
+
+ case STR1:
+ case ZSTR1:
+ dostr1:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ state = state == OSTR ? STR2 : ++state;
+ return (SP);
+ }
+ break;
+
+ case ZSTR2:
+ if (cbuf[cpos] == '\n') {
+ state = CMD;
+ return (CRLF);
+ }
+ /* FALLTHROUGH */
+
+ case STR2:
+ cp = &cbuf[cpos];
+ n = strlen(cp);
+ cpos += n - 1;
+ /*
+ * Make sure the string is nonempty and \n terminated.
+ */
+ if (n > 1 && cbuf[cpos] == '\n') {
+ cbuf[cpos] = '\0';
+ yylval.s = copy(cp);
+ cbuf[cpos] = '\n';
+ state = ARGS;
+ return (STRING);
+ }
+ break;
+
+ case NSTR:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ return (SP);
+ }
+ if (isdigit(cbuf[cpos])) {
+ cp = &cbuf[cpos];
+ while (isdigit(cbuf[++cpos]))
+ ;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ yylval.i = atoi(cp);
+ cbuf[cpos] = c;
+ state = STR1;
+ return (NUMBER);
+ }
+ state = STR1;
+ goto dostr1;
+
+ case ARGS:
+ if (isdigit(cbuf[cpos])) {
+ cp = &cbuf[cpos];
+ while (isdigit(cbuf[++cpos]))
+ ;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ yylval.i = atoi(cp);
+ cbuf[cpos] = c;
+ return (NUMBER);
+ }
+ switch (cbuf[cpos++]) {
+
+ case '\n':
+ state = CMD;
+ return (CRLF);
+
+ case ' ':
+ return (SP);
+
+ case ',':
+ return (COMMA);
+
+ case 'A':
+ case 'a':
+ return (A);
+
+ case 'B':
+ case 'b':
+ return (B);
+
+ case 'C':
+ case 'c':
+ return (C);
+
+ case 'E':
+ case 'e':
+ return (E);
+
+ case 'F':
+ case 'f':
+ return (F);
+
+ case 'I':
+ case 'i':
+ return (I);
+
+ case 'L':
+ case 'l':
+ return (L);
+
+ case 'N':
+ case 'n':
+ return (N);
+
+ case 'P':
+ case 'p':
+ return (P);
+
+ case 'R':
+ case 'r':
+ return (R);
+
+ case 'S':
+ case 's':
+ return (S);
+
+ case 'T':
+ case 't':
+ return (T);
+
+ }
+ break;
+
+ default:
+ fatal("Unknown state in scanner.");
+ }
+ yyerror((char *) 0);
+ state = CMD;
+ longjmp(errcatch,0);
+ }
+}
+
+void
+upper(s)
+ char *s;
+{
+ while (*s != '\0') {
+ if (islower(*s))
+ *s = toupper(*s);
+ s++;
+ }
+}
+
+static char *
+copy(s)
+ char *s;
+{
+ char *p;
+
+ p = malloc((unsigned) strlen(s) + 1);
+ if (p == NULL)
+ fatal("Ran out of memory.");
+ (void) strcpy(p, s);
+ return (p);
+}
+
+static void
+help(ctab, s)
+ struct tab *ctab;
+ char *s;
+{
+ struct tab *c;
+ int width, NCMDS;
+ char *type;
+
+ if (ctab == sitetab)
+ type = "SITE ";
+ else
+ type = "";
+ width = 0, NCMDS = 0;
+ for (c = ctab; c->name != NULL; c++) {
+ int len = strlen(c->name);
+
+ if (len > width)
+ width = len;
+ NCMDS++;
+ }
+ width = (width + 8) &~ 7;
+ if (s == 0) {
+ int i, j, w;
+ int columns, lines;
+
+ lreply(214, "The following %scommands are recognized %s.",
+ type, "(* =>'s unimplemented)");
+ columns = 76 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ printf(" ");
+ for (j = 0; j < columns; j++) {
+ c = ctab + j * lines + i;
+ printf("%s%c", c->name,
+ c->implemented ? ' ' : '*');
+ if (c + lines >= &ctab[NCMDS])
+ break;
+ w = strlen(c->name) + 1;
+ while (w < width) {
+ putchar(' ');
+ w++;
+ }
+ }
+ printf("\r\n");
+ }
+ (void) fflush(stdout);
+ reply(214, "Direct comments to ftp-bugs@%s.", hostname);
+ return;
+ }
+ upper(s);
+ c = lookup(ctab, s);
+ if (c == (struct tab *)0) {
+ reply(502, "Unknown command %s.", s);
+ return;
+ }
+ if (c->implemented)
+ reply(214, "Syntax: %s%s %s", type, c->name, c->help);
+ else
+ reply(214, "%s%-*s\t%s; unimplemented.", type, width,
+ c->name, c->help);
+}
+
+static void
+sizecmd(filename)
+ char *filename;
+{
+ switch (type) {
+ case TYPE_L:
+ case TYPE_I: {
+ struct stat stbuf;
+ if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
+ reply(550, "%s: not a plain file.", filename);
+ else
+ reply(213, "%qu", stbuf.st_size);
+ break; }
+ case TYPE_A: {
+ FILE *fin;
+ int c;
+ off_t count;
+ struct stat stbuf;
+ fin = fopen(filename, "r");
+ if (fin == NULL) {
+ perror_reply(550, filename);
+ return;
+ }
+ if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
+ reply(550, "%s: not a plain file.", filename);
+ (void) fclose(fin);
+ return;
+ }
+
+ count = 0;
+ while((c=getc(fin)) != EOF) {
+ if (c == '\n') /* will get expanded to \r\n */
+ count++;
+ count++;
+ }
+ (void) fclose(fin);
+
+ reply(213, "%qd", count);
+ break; }
+ default:
+ reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
+ }
+}
diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8
new file mode 100644
index 0000000..6e5b199
--- /dev/null
+++ b/libexec/ftpd/ftpd.8
@@ -0,0 +1,310 @@
+.\" Copyright (c) 1985, 1988, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94
+.\"
+.Dd April 19, 1994
+.Dt FTPD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm ftpd
+.Nd
+Internet File Transfer Protocol server
+.Sh SYNOPSIS
+.Nm ftpd
+.Op Fl dl
+.Op Fl S
+.Op Fl U
+.Op Fl T Ar maxtimeout
+.Op Fl t Ar timeout
+.Sh DESCRIPTION
+.Nm Ftpd
+is the
+Internet File Transfer Protocol
+server process. The server uses the
+.Tn TCP
+protocol
+and listens at the port specified in the
+.Dq ftp
+service specification; see
+.Xr services 5 .
+.Pp
+Available options:
+.Bl -tag -width Ds
+.It Fl d
+Debugging information is written to the syslog using LOG_FTP.
+.It Fl l
+Each successful and failed
+.Xr ftp 1
+session is logged using syslog with a facility of LOG_FTP.
+If this option is specified twice, the retrieve (get), store (put), append,
+delete, make directory, remove directory and rename operations and
+their filename arguments are also logged.
+.It Fl S
+With this option set,
+.Nm ftpd
+logs all anonymous transfers to the file
+.Pa /var/log/ftpd
+when this file exists.
+.
+.It Fl U
+In previous versions of
+.Nm ftpd ,
+when a passive mode client requested a data connection to the server,
+the server would use data ports in the range 1024..4999. Now, by default,
+the server will use data ports in the range 40000..44999. Specifying this
+option will revert to the old behavior.
+.It Fl T
+A client may also request a different timeout period;
+the maximum period allowed may be set to
+.Ar timeout
+seconds with the
+.Fl T
+option.
+The default limit is 2 hours.
+.It Fl t
+The inactivity timeout period is set to
+.Ar timeout
+seconds (the default is 15 minutes).
+.El
+.Pp
+The file
+.Pa /etc/nologin
+can be used to disable ftp access.
+If the file exists,
+.Nm
+displays it and exits.
+If the file
+.Pa /etc/ftpwelcome
+exists,
+.Nm
+prints it before issuing the
+.Dq ready
+message.
+If the file
+.Pa /etc/ftpmotd
+exists,
+.Nm
+prints it after a successful login.
+.Pp
+The ftp server currently supports the following ftp requests.
+The case of the requests is ignored.
+.Bl -column "Request" -offset indent
+.It Request Ta "Description"
+.It ABOR Ta "abort previous command"
+.It ACCT Ta "specify account (ignored)"
+.It ALLO Ta "allocate storage (vacuously)"
+.It APPE Ta "append to a file"
+.It CDUP Ta "change to parent of current working directory"
+.It CWD Ta "change working directory"
+.It DELE Ta "delete a file"
+.It HELP Ta "give help information"
+.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lgA"
+.It MKD Ta "make a directory"
+.It MDTM Ta "show last modification time of file"
+.It MODE Ta "specify data transfer" Em mode
+.It NLST Ta "give name list of files in directory"
+.It NOOP Ta "do nothing"
+.It PASS Ta "specify password"
+.It PASV Ta "prepare for server-to-server transfer"
+.It PORT Ta "specify data connection port"
+.It PWD Ta "print the current working directory"
+.It QUIT Ta "terminate session"
+.It REST Ta "restart incomplete transfer"
+.It RETR Ta "retrieve a file"
+.It RMD Ta "remove a directory"
+.It RNFR Ta "specify rename-from file name"
+.It RNTO Ta "specify rename-to file name"
+.It SITE Ta "non-standard commands (see next section)"
+.It SIZE Ta "return size of file"
+.It STAT Ta "return status of server"
+.It STOR Ta "store a file"
+.It STOU Ta "store a file with a unique name"
+.It STRU Ta "specify data transfer" Em structure
+.It SYST Ta "show operating system type of server system"
+.It TYPE Ta "specify data transfer" Em type
+.It USER Ta "specify user name"
+.It XCUP Ta "change to parent of current working directory (deprecated)"
+.It XCWD Ta "change working directory (deprecated)"
+.It XMKD Ta "make a directory (deprecated)"
+.It XPWD Ta "print the current working directory (deprecated)"
+.It XRMD Ta "remove a directory (deprecated)"
+.El
+.Pp
+The following non-standard or
+.Tn UNIX
+specific commands are supported
+by the
+SITE request.
+.Pp
+.Bl -column Request -offset indent
+.It Sy Request Ta Sy Description
+.It UMASK Ta change umask, e.g. ``SITE UMASK 002''
+.It IDLE Ta set idle-timer, e.g. ``SITE IDLE 60''
+.It CHMOD Ta change mode of a file, e.g. ``SITE CHMOD 755 filename''
+.It HELP Ta give help information.
+.El
+.Pp
+The remaining ftp requests specified in Internet RFC 959
+are
+recognized, but not implemented.
+MDTM and SIZE are not specified in RFC 959, but will appear in the
+next updated FTP RFC.
+.Pp
+The ftp server will abort an active file transfer only when the
+ABOR
+command is preceded by a Telnet "Interrupt Process" (IP)
+signal and a Telnet "Synch" signal in the command Telnet stream,
+as described in Internet RFC 959.
+If a
+STAT
+command is received during a data transfer, preceded by a Telnet IP
+and Synch, transfer status will be returned.
+.Pp
+.Nm Ftpd
+interprets file names according to the
+.Dq globbing
+conventions used by
+.Xr csh 1 .
+This allows users to utilize the metacharacters
+.Dq Li \&*?[]{}~ .
+.Pp
+.Nm Ftpd
+authenticates users according to three rules.
+.Pp
+.Bl -enum -offset indent
+.It
+The login name must be in the password data base,
+.Pa /etc/passwd ,
+and not have a null password.
+In this case a password must be provided by the client before any
+file operations may be performed.
+.It
+The login name must not appear in the file
+.Pa /etc/ftpusers .
+.It
+The user must have a standard shell returned by
+.Xr getusershell 3 .
+.It
+If the user name is
+.Dq anonymous
+or
+.Dq ftp ,
+an
+anonymous ftp account must be present in the password
+file (user
+.Dq ftp ) .
+In this case the user is allowed
+to log in by specifying any password (by convention an email address for
+the user should be used as the password). When the
+.Fl S
+option is set, all transfers are logged as well.
+.El
+.Pp
+In the last case,
+.Nm ftpd
+takes special measures to restrict the client's access privileges.
+The server performs a
+.Xr chroot 2
+to the home directory of the
+.Dq ftp
+user.
+In order that system security is not breached, it is recommended
+that the
+.Dq ftp
+subtree be constructed with care, following these rules:
+.Bl -tag -width "~ftp/pub" -offset indent
+.It Pa ~ftp
+Make the home directory owned by
+.Dq root
+and unwritable by anyone.
+.It Pa ~ftp/bin
+Make this directory owned by
+.Dq root
+and unwritable by anyone (mode 555).
+The program
+.Xr ls 1
+must be present to support the list command.
+This program should be mode 111.
+.It Pa ~ftp/etc
+Make this directory owned by
+.Dq root
+and unwritable by anyone (mode 555).
+The files
+.Xr passwd 5
+and
+.Xr group 5
+must be present for the
+.Xr ls
+command to be able to produce owner names rather than numbers.
+The password field in
+.Xr passwd
+is not used, and should not contain real passwords.
+The file
+.Pa ftpmotd ,
+if present, will be printed after a successful login.
+These files should be mode 444.
+.It Pa ~ftp/pub
+Make this directory mode 777 and owned by
+.Dq ftp .
+Guests
+can then place files which are to be accessible via the anonymous
+account in this directory.
+.El
+.Sh FILES
+.Bl -tag -width /etc/ftpwelcome -compact
+.It Pa /etc/ftpusers
+List of unwelcome/restricted users.
+.It Pa /etc/ftpwelcome
+Welcome notice.
+.It Pa /etc/ftpmotd
+Welcome notice after login.
+.It Pa /etc/nologin
+Displayed and access refused.
+.It Pa /var/log/ftpd
+Log file for anonymous transfers.
+.El
+.Sh SEE ALSO
+.Xr ftp 1 ,
+.Xr getusershell 3 ,
+.Xr syslogd 8
+.Sh BUGS
+The server must run as the super-user
+to create sockets with privileged port numbers. It maintains
+an effective user id of the logged in user, reverting to
+the super-user only when binding addresses to sockets. The
+possible security holes have been extensively
+scrutinized, but are possibly incomplete.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c
new file mode 100644
index 0000000..d59d805
--- /dev/null
+++ b/libexec/ftpd/ftpd.c
@@ -0,0 +1,1856 @@
+/*
+ * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ftpd.c,v 1.13 1995/11/29 19:52:30 guido Exp $
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94";
+#endif /* not lint */
+
+/*
+ * FTP server.
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#define FTP_NAMES
+#include <arpa/ftp.h>
+#include <arpa/inet.h>
+#include <arpa/telnet.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <limits.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <libutil.h>
+
+#ifdef SKEY
+#include <skey.h>
+#endif
+
+#include "pathnames.h"
+#include "extern.h"
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+static char version[] = "Version 6.00";
+
+extern off_t restart_point;
+extern char cbuf[];
+
+struct sockaddr_in ctrl_addr;
+struct sockaddr_in data_source;
+struct sockaddr_in data_dest;
+struct sockaddr_in his_addr;
+struct sockaddr_in pasv_addr;
+
+int data;
+jmp_buf errcatch, urgcatch;
+int logged_in;
+struct passwd *pw;
+int debug;
+int timeout = 900; /* timeout after 15 minutes of inactivity */
+int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
+int logging;
+int restricted_data_ports = 1;
+int guest;
+#ifdef STATS
+int stats;
+int statfd = -1;
+#endif
+int type;
+int form;
+int stru; /* avoid C keyword */
+int mode;
+int usedefault = 1; /* for data transfers */
+int pdata = -1; /* for passive mode */
+sig_atomic_t transflag;
+off_t file_size;
+off_t byte_count;
+#if !defined(CMASK) || CMASK == 0
+#undef CMASK
+#define CMASK 027
+#endif
+int defumask = CMASK; /* default umask value */
+char tmpline[7];
+char hostname[MAXHOSTNAMELEN];
+char remotehost[MAXHOSTNAMELEN];
+#ifdef STATS
+char *ident = NULL;
+#endif
+
+/*
+ * Timeout intervals for retrying connections
+ * to hosts that don't accept PORT cmds. This
+ * is a kludge, but given the problems with TCP...
+ */
+#define SWAITMAX 90 /* wait at most 90 seconds */
+#define SWAITINT 5 /* interval between retries */
+
+int swaitmax = SWAITMAX;
+int swaitint = SWAITINT;
+
+#ifdef SETPROCTITLE
+#ifdef OLD_SETPROCTITLE
+char **Argv = NULL; /* pointer to argument vector */
+char *LastArgv = NULL; /* end of argv */
+#endif /* OLD_SETPROCTITLE */
+char proctitle[LINE_MAX]; /* initial part of title */
+#endif /* SETPROCTITLE */
+
+#ifdef SKEY
+int pwok = 0;
+char addr_string[20]; /* XXX */
+#endif
+
+#define LOGCMD(cmd, file) \
+ if (logging > 1) \
+ syslog(LOG_INFO,"%s %s%s", cmd, \
+ *(file) == '/' ? "" : curdir(), file);
+#define LOGCMD2(cmd, file1, file2) \
+ if (logging > 1) \
+ syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
+ *(file1) == '/' ? "" : curdir(), file1, \
+ *(file2) == '/' ? "" : curdir(), file2);
+#define LOGBYTES(cmd, file, cnt) \
+ if (logging > 1) { \
+ if (cnt == (off_t)-1) \
+ syslog(LOG_INFO,"%s %s%s", cmd, \
+ *(file) == '/' ? "" : curdir(), file); \
+ else \
+ syslog(LOG_INFO, "%s %s%s = %qd bytes", \
+ cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
+ }
+
+static void ack __P((char *));
+static void myoob __P((int));
+static int checkuser __P((char *));
+static FILE *dataconn __P((char *, off_t, char *));
+static void dolog __P((struct sockaddr_in *));
+static char *curdir __P((void));
+static void end_login __P((void));
+static FILE *getdatasock __P((char *));
+static char *gunique __P((char *));
+static void lostconn __P((int));
+static int receive_data __P((FILE *, FILE *));
+static void send_data __P((FILE *, FILE *, off_t, off_t, int));
+static struct passwd *
+ sgetpwnam __P((char *));
+static char *sgetsave __P((char *));
+
+static char *
+curdir()
+{
+ static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */
+
+ if (getcwd(path, sizeof(path)-2) == NULL)
+ return ("");
+ if (path[1] != '\0') /* special case for root dir. */
+ strcat(path, "/");
+ /* For guest account, skip / since it's chrooted */
+ return (guest ? path+1 : path);
+}
+
+int
+main(argc, argv, envp)
+ int argc;
+ char *argv[];
+ char **envp;
+{
+ int addrlen, ch, on = 1, tos;
+ char *cp, line[LINE_MAX];
+ FILE *fd;
+
+ tzset(); /* in case no timezone database in ~ftp */
+
+ /*
+ * LOG_NDELAY sets up the logging connection immediately,
+ * necessary for anonymous ftp's that chroot and can't do it later.
+ */
+ openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
+ addrlen = sizeof(his_addr);
+ if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
+ syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
+ exit(1);
+ }
+#ifdef SKEY
+ strcpy(addr_string, inet_ntoa(his_addr.sin_addr));
+#endif
+ addrlen = sizeof(ctrl_addr);
+ if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
+ syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
+ exit(1);
+ }
+#ifdef IP_TOS
+ tos = IPTOS_LOWDELAY;
+ if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+#endif
+ data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
+ debug = 0;
+#ifdef OLD_SETPROCTITLE
+ /*
+ * Save start and extent of argv for setproctitle.
+ */
+ Argv = argv;
+ while (*envp)
+ envp++;
+ LastArgv = envp[-1] + strlen(envp[-1]);
+#endif /* OLD_SETPROCTITLE */
+
+
+#ifdef STATS
+ while ((ch = getopt(argc, argv, "dlSt:T:u:v")) != EOF) {
+#else
+ while ((ch = getopt(argc, argv, "dlUt:T:u:v")) != EOF) {
+#endif
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+
+ case 'l':
+ logging++; /* > 1 == extra logging */
+ break;
+
+ case 'U':
+ restricted_data_ports = 0;
+ break;
+
+ case 't':
+ timeout = atoi(optarg);
+ if (maxtimeout < timeout)
+ maxtimeout = timeout;
+ break;
+#ifdef STATS
+ case 'S':
+ stats = 1;
+ break;
+#endif
+ case 'T':
+ maxtimeout = atoi(optarg);
+ if (timeout > maxtimeout)
+ timeout = maxtimeout;
+ break;
+
+ case 'u':
+ {
+ long val = 0;
+
+ val = strtol(optarg, &optarg, 8);
+ if (*optarg != '\0' || val < 0)
+ warnx("bad value for -u");
+ else
+ defumask = val;
+ break;
+ }
+
+ case 'v':
+ debug = 1;
+ break;
+
+ default:
+ warnx("unknown flag -%c ignored", optopt);
+ break;
+ }
+ }
+ (void) freopen(_PATH_DEVNULL, "w", stderr);
+ (void) signal(SIGPIPE, lostconn);
+ (void) signal(SIGCHLD, SIG_IGN);
+ if ((int)signal(SIGURG, myoob) < 0)
+ syslog(LOG_ERR, "signal: %m");
+
+ /* Try to handle urgent data inline */
+#ifdef SO_OOBINLINE
+ if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt: %m");
+#endif
+
+#ifdef F_SETOWN
+ if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
+ syslog(LOG_ERR, "fcntl F_SETOWN: %m");
+#endif
+ dolog(&his_addr);
+ /*
+ * Set up default state
+ */
+ data = -1;
+ type = TYPE_A;
+ form = FORM_N;
+ stru = STRU_F;
+ mode = MODE_S;
+ tmpline[0] = '\0';
+
+ /* If logins are disabled, print out the message. */
+ if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
+ while (fgets(line, sizeof(line), fd) != NULL) {
+ if ((cp = strchr(line, '\n')) != NULL)
+ *cp = '\0';
+ lreply(530, "%s", line);
+ }
+ (void) fflush(stdout);
+ (void) fclose(fd);
+ reply(530, "System not available.");
+ exit(0);
+ }
+ if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
+ while (fgets(line, sizeof(line), fd) != NULL) {
+ if ((cp = strchr(line, '\n')) != NULL)
+ *cp = '\0';
+ lreply(220, "%s", line);
+ }
+ (void) fflush(stdout);
+ (void) fclose(fd);
+ /* reply(220,) must follow */
+ }
+ (void) gethostname(hostname, sizeof(hostname));
+ reply(220, "%s FTP server (%s) ready.", hostname, version);
+ (void) setjmp(errcatch);
+ for (;;)
+ (void) yyparse();
+ /* NOTREACHED */
+}
+
+static void
+lostconn(signo)
+ int signo;
+{
+
+ if (debug)
+ syslog(LOG_DEBUG, "lost connection");
+ dologout(-1);
+}
+
+static char ttyline[20];
+
+/*
+ * Helper function for sgetpwnam().
+ */
+static char *
+sgetsave(s)
+ char *s;
+{
+ char *new = malloc((unsigned) strlen(s) + 1);
+
+ if (new == NULL) {
+ perror_reply(421, "Local resource failure: malloc");
+ dologout(1);
+ /* NOTREACHED */
+ }
+ (void) strcpy(new, s);
+ return (new);
+}
+
+/*
+ * Save the result of a getpwnam. Used for USER command, since
+ * the data returned must not be clobbered by any other command
+ * (e.g., globbing).
+ */
+static struct passwd *
+sgetpwnam(name)
+ char *name;
+{
+ static struct passwd save;
+ struct passwd *p;
+
+ if ((p = getpwnam(name)) == NULL)
+ return (p);
+ if (save.pw_name) {
+ free(save.pw_name);
+ free(save.pw_passwd);
+ free(save.pw_gecos);
+ free(save.pw_dir);
+ free(save.pw_shell);
+ }
+ save = *p;
+ save.pw_name = sgetsave(p->pw_name);
+ save.pw_passwd = sgetsave(p->pw_passwd);
+ save.pw_gecos = sgetsave(p->pw_gecos);
+ save.pw_dir = sgetsave(p->pw_dir);
+ save.pw_shell = sgetsave(p->pw_shell);
+ return (&save);
+}
+
+static int login_attempts; /* number of failed login attempts */
+static int askpasswd; /* had user command, ask for passwd */
+static char curname[10]; /* current USER name */
+
+/*
+ * USER command.
+ * Sets global passwd pointer pw if named account exists and is acceptable;
+ * sets askpasswd if a PASS command is expected. If logged in previously,
+ * need to reset state. If name is "ftp" or "anonymous", the name is not in
+ * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
+ * If account doesn't exist, ask for passwd anyway. Otherwise, check user
+ * requesting login privileges. Disallow anyone who does not have a standard
+ * shell as returned by getusershell(). Disallow anyone mentioned in the file
+ * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
+ */
+void
+user(name)
+ char *name;
+{
+ char *cp, *shell;
+
+ if (logged_in) {
+ if (guest) {
+ reply(530, "Can't change user from guest login.");
+ return;
+ }
+ end_login();
+ }
+
+ guest = 0;
+ if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
+ if (checkuser("ftp") || checkuser("anonymous"))
+ reply(530, "User %s access denied.", name);
+ else if ((pw = sgetpwnam("ftp")) != NULL) {
+ guest = 1;
+ askpasswd = 1;
+ reply(331,
+ "Guest login ok, send your email address as password.");
+ } else
+ reply(530, "User %s unknown.", name);
+ if (!askpasswd && logging)
+ syslog(LOG_NOTICE,
+ "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
+ return;
+ }
+ if (pw = sgetpwnam(name)) {
+ if ((shell = pw->pw_shell) == NULL || *shell == 0)
+ shell = _PATH_BSHELL;
+ while ((cp = getusershell()) != NULL)
+ if (strcmp(cp, shell) == 0)
+ break;
+ endusershell();
+
+ if (cp == NULL || checkuser(name)) {
+ reply(530, "User %s access denied.", name);
+ if (logging)
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED FROM %s, %s",
+ remotehost, name);
+ pw = (struct passwd *) NULL;
+ return;
+ }
+ }
+ if (logging)
+ strncpy(curname, name, sizeof(curname)-1);
+#ifdef SKEY
+ pwok = skeyaccess(name, NULL, remotehost, addr_string);
+ reply(331, "%s", skey_challenge(name, pw, pwok));
+#else
+ reply(331, "Password required for %s.", name);
+#endif
+ askpasswd = 1;
+ /*
+ * Delay before reading passwd after first failed
+ * attempt to slow down passwd-guessing programs.
+ */
+ if (login_attempts)
+ sleep((unsigned) login_attempts);
+}
+
+/*
+ * Check if a user is in the file _PATH_FTPUSERS
+ */
+static int
+checkuser(name)
+ char *name;
+{
+ FILE *fd;
+ int found = 0;
+ char *p, line[BUFSIZ];
+
+ if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
+ while (fgets(line, sizeof(line), fd) != NULL)
+ if ((p = strchr(line, '\n')) != NULL) {
+ *p = '\0';
+ if (line[0] == '#')
+ continue;
+ if (strcmp(line, name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ (void) fclose(fd);
+ }
+ return (found);
+}
+
+/*
+ * Terminate login as previous user, if any, resetting state;
+ * used when USER command is given or login fails.
+ */
+static void
+end_login()
+{
+
+ (void) seteuid((uid_t)0);
+ if (logged_in)
+ logwtmp(ttyline, "", "");
+ pw = NULL;
+ logged_in = 0;
+ guest = 0;
+}
+
+void
+pass(passwd)
+ char *passwd;
+{
+ char *salt, *xpasswd;
+ FILE *fd;
+ static char homedir[MAXPATHLEN];
+
+ if (logged_in || askpasswd == 0) {
+ reply(503, "Login with USER first.");
+ return;
+ }
+ askpasswd = 0;
+ if (!guest) { /* "ftp" is only account allowed no password */
+ if (pw == NULL)
+ salt = "xx";
+ else
+ salt = pw->pw_passwd;
+#ifdef SKEY
+ xpasswd = skey_crypt(passwd, salt, pw, pwok);
+ pwok = 0;
+#else
+ xpasswd = crypt(passwd, salt);
+#endif
+ /* The strcmp does not catch null passwords! */
+ if (pw == NULL || *pw->pw_passwd == '\0' ||
+ (pw->pw_expire && time(NULL) >= pw->pw_expire) ||
+ strcmp(xpasswd, pw->pw_passwd)) {
+ reply(530, "Login incorrect.");
+ if (logging)
+ syslog(LOG_NOTICE,
+ "FTP LOGIN FAILED FROM %s, %s",
+ remotehost, curname);
+ pw = NULL;
+ if (login_attempts++ >= 5) {
+ syslog(LOG_NOTICE,
+ "repeated login failures from %s",
+ remotehost);
+ exit(0);
+ }
+ return;
+ }
+ }
+ login_attempts = 0; /* this time successful */
+ if (setegid((gid_t)pw->pw_gid) < 0) {
+ reply(550, "Can't set gid.");
+ return;
+ }
+ (void) initgroups(pw->pw_name, pw->pw_gid);
+
+ /* open wtmp before chroot */
+ (void)sprintf(ttyline, "ftp%d", getpid());
+ logwtmp(ttyline, pw->pw_name, remotehost);
+ logged_in = 1;
+
+#ifdef STATS
+ if (guest && stats == 1 && statfd < 0)
+ if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0)
+ stats = 0;
+#endif
+
+ if (guest) {
+ /*
+ * We MUST do a chdir() after the chroot. Otherwise
+ * the old current directory will be accessible as "."
+ * outside the new root!
+ */
+ if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
+ reply(550, "Can't set guest privileges.");
+ goto bad;
+ }
+ } else if (chdir(pw->pw_dir) < 0) {
+ if (chdir("/") < 0) {
+ reply(530, "User %s: can't change directory to %s.",
+ pw->pw_name, pw->pw_dir);
+ goto bad;
+ } else
+ lreply(230, "No directory! Logging in with home=/");
+ }
+ if (seteuid((uid_t)pw->pw_uid) < 0) {
+ reply(550, "Can't set uid.");
+ goto bad;
+ }
+
+ /*
+ * Set home directory so that use of ~ (tilde) works correctly.
+ */
+ if (getcwd(homedir, MAXPATHLEN) != NULL)
+ setenv("HOME", homedir, 1);
+
+ /*
+ * Display a login message, if it exists.
+ * N.B. reply(230,) must follow the message.
+ */
+ if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
+ char *cp, line[LINE_MAX];
+
+ while (fgets(line, sizeof(line), fd) != NULL) {
+ if ((cp = strchr(line, '\n')) != NULL)
+ *cp = '\0';
+ lreply(230, "%s", line);
+ }
+ (void) fflush(stdout);
+ (void) fclose(fd);
+ }
+ if (guest) {
+#ifdef STATS
+ char * copy();
+
+ if (ident != NULL)
+ free(ident);
+ ident = (char *) copy(passwd);
+#endif
+ reply(230, "Guest login ok, access restrictions apply.");
+#ifdef SETPROCTITLE
+ snprintf(proctitle, sizeof(proctitle),
+ "%s: anonymous/%.*s", remotehost,
+ sizeof(proctitle) - sizeof(remotehost) -
+ sizeof(": anonymous/"), passwd);
+ setproctitle("%s", proctitle);
+#endif /* SETPROCTITLE */
+ if (logging)
+ syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
+ remotehost, passwd);
+ } else {
+ reply(230, "User %s logged in.", pw->pw_name);
+#ifdef SETPROCTITLE
+ snprintf(proctitle, sizeof(proctitle),
+ "%s: %s", remotehost, pw->pw_name);
+ setproctitle("%s", proctitle);
+#endif /* SETPROCTITLE */
+ if (logging)
+ syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
+ remotehost, pw->pw_name);
+ }
+ (void) umask(defumask);
+ return;
+bad:
+ /* Forget all about it... */
+ end_login();
+}
+
+void
+retrieve(cmd, name)
+ char *cmd, *name;
+{
+ FILE *fin, *dout;
+ struct stat st;
+ int (*closefunc) __P((FILE *));
+#ifdef STATS
+ long start;
+#endif
+
+ if (cmd == 0) {
+ fin = fopen(name, "r"), closefunc = fclose;
+ st.st_size = 0;
+ } else {
+ char line[BUFSIZ];
+
+ (void) sprintf(line, cmd, name), name = line;
+ fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
+ st.st_size = -1;
+ st.st_blksize = BUFSIZ;
+ }
+ if (fin == NULL) {
+ if (errno != 0) {
+ perror_reply(550, name);
+ if (cmd == 0) {
+ LOGCMD("get", name);
+ }
+ }
+ return;
+ }
+ byte_count = -1;
+ if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
+ reply(550, "%s: not a plain file.", name);
+ goto done;
+ }
+ if (restart_point) {
+ if (type == TYPE_A) {
+ off_t i, n;
+ int c;
+
+ n = restart_point;
+ i = 0;
+ while (i++ < n) {
+ if ((c=getc(fin)) == EOF) {
+ perror_reply(550, name);
+ goto done;
+ }
+ if (c == '\n')
+ i++;
+ }
+ } else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ }
+ dout = dataconn(name, st.st_size, "w");
+ if (dout == NULL)
+ goto done;
+#ifdef STATS
+ time(&start);
+#endif
+ send_data(fin, dout, st.st_blksize, st.st_size,
+ restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode));
+#ifdef STATS
+ if (cmd == 0 && guest && stats)
+ logxfer( name, st.st_size, start);
+#endif
+ (void) fclose(dout);
+ data = -1;
+ pdata = -1;
+done:
+ if (cmd == 0)
+ LOGBYTES("get", name, byte_count);
+ (*closefunc)(fin);
+}
+
+void
+store(name, mode, unique)
+ char *name, *mode;
+ int unique;
+{
+ FILE *fout, *din;
+ struct stat st;
+ int (*closefunc) __P((FILE *));
+
+ if (unique && stat(name, &st) == 0 &&
+ (name = gunique(name)) == NULL) {
+ LOGCMD(*mode == 'w' ? "put" : "append", name);
+ return;
+ }
+
+ if (restart_point)
+ mode = "r+";
+ fout = fopen(name, mode);
+ closefunc = fclose;
+ if (fout == NULL) {
+ perror_reply(553, name);
+ LOGCMD(*mode == 'w' ? "put" : "append", name);
+ return;
+ }
+ byte_count = -1;
+ if (restart_point) {
+ if (type == TYPE_A) {
+ off_t i, n;
+ int c;
+
+ n = restart_point;
+ i = 0;
+ while (i++ < n) {
+ if ((c=getc(fout)) == EOF) {
+ perror_reply(550, name);
+ goto done;
+ }
+ if (c == '\n')
+ i++;
+ }
+ /*
+ * We must do this seek to "current" position
+ * because we are changing from reading to
+ * writing.
+ */
+ if (fseek(fout, 0L, L_INCR) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ } else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ }
+ din = dataconn(name, (off_t)-1, "r");
+ if (din == NULL)
+ goto done;
+ if (receive_data(din, fout) == 0) {
+ if (unique)
+ reply(226, "Transfer complete (unique file name:%s).",
+ name);
+ else
+ reply(226, "Transfer complete.");
+ }
+ (void) fclose(din);
+ data = -1;
+ pdata = -1;
+done:
+ LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
+ (*closefunc)(fout);
+}
+
+static FILE *
+getdatasock(mode)
+ char *mode;
+{
+ int on = 1, s, t, tries;
+
+ if (data >= 0)
+ return (fdopen(data, mode));
+ (void) seteuid((uid_t)0);
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ goto bad;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &on, sizeof(on)) < 0)
+ goto bad;
+ /* anchor socket to avoid multi-homing problems */
+ data_source.sin_family = AF_INET;
+ data_source.sin_addr = ctrl_addr.sin_addr;
+ for (tries = 1; ; tries++) {
+ if (bind(s, (struct sockaddr *)&data_source,
+ sizeof(data_source)) >= 0)
+ break;
+ if (errno != EADDRINUSE || tries > 10)
+ goto bad;
+ sleep(tries);
+ }
+ (void) seteuid((uid_t)pw->pw_uid);
+#ifdef IP_TOS
+ on = IPTOS_THROUGHPUT;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+#endif
+#ifdef TCP_NOPUSH
+ /*
+ * Turn off push flag to keep sender TCP from sending short packets
+ * at the boundaries of each write(). Should probably do a SO_SNDBUF
+ * to set the send buffer size as well, but that may not be desirable
+ * in heavy-load situations.
+ */
+ on = 1;
+ if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0)
+ syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
+#endif
+#ifdef SO_SNDBUF
+ on = 65536;
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof on) < 0)
+ syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m");
+#endif
+
+ return (fdopen(s, mode));
+bad:
+ /* Return the real value of errno (close may change it) */
+ t = errno;
+ (void) seteuid((uid_t)pw->pw_uid);
+ (void) close(s);
+ errno = t;
+ return (NULL);
+}
+
+static FILE *
+dataconn(name, size, mode)
+ char *name;
+ off_t size;
+ char *mode;
+{
+ char sizebuf[32];
+ FILE *file;
+ int retry = 0, tos;
+
+ file_size = size;
+ byte_count = 0;
+ if (size != (off_t) -1)
+ (void) sprintf(sizebuf, " (%qd bytes)", size);
+ else
+ (void) strcpy(sizebuf, "");
+ if (pdata >= 0) {
+ struct sockaddr_in from;
+ int s, fromlen = sizeof(from);
+ struct timeval timeout;
+ fd_set set;
+
+ FD_ZERO(&set);
+ FD_SET(pdata, &set);
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 120;
+
+ if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) == 0 ||
+ (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) {
+ reply(425, "Can't open data connection.");
+ (void) close(pdata);
+ pdata = -1;
+ return (NULL);
+ }
+ (void) close(pdata);
+ pdata = s;
+#ifdef IP_TOS
+ tos = IPTOS_LOWDELAY;
+ (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
+ sizeof(int));
+#endif
+ reply(150, "Opening %s mode data connection for '%s'%s.",
+ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+ return (fdopen(pdata, mode));
+ }
+ if (data >= 0) {
+ reply(125, "Using existing data connection for '%s'%s.",
+ name, sizebuf);
+ usedefault = 1;
+ return (fdopen(data, mode));
+ }
+ if (usedefault)
+ data_dest = his_addr;
+ usedefault = 1;
+ file = getdatasock(mode);
+ if (file == NULL) {
+ reply(425, "Can't create data socket (%s,%d): %s.",
+ inet_ntoa(data_source.sin_addr),
+ ntohs(data_source.sin_port), strerror(errno));
+ return (NULL);
+ }
+ data = fileno(file);
+ while (connect(data, (struct sockaddr *)&data_dest,
+ sizeof(data_dest)) < 0) {
+ if (errno == EADDRINUSE && retry < swaitmax) {
+ sleep((unsigned) swaitint);
+ retry += swaitint;
+ continue;
+ }
+ perror_reply(425, "Can't build data connection");
+ (void) fclose(file);
+ data = -1;
+ return (NULL);
+ }
+ reply(150, "Opening %s mode data connection for '%s'%s.",
+ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+ return (file);
+}
+
+/*
+ * Tranfer the contents of "instr" to "outstr" peer using the appropriate
+ * encapsulation of the data subject to Mode, Structure, and Type.
+ *
+ * NB: Form isn't handled.
+ */
+static void
+send_data(instr, outstr, blksize, filesize, isreg)
+ FILE *instr, *outstr;
+ off_t blksize;
+ off_t filesize;
+ int isreg;
+{
+ int c, cnt, filefd, netfd;
+ char *buf, *bp;
+ size_t len;
+
+ transflag++;
+ if (setjmp(urgcatch)) {
+ transflag = 0;
+ return;
+ }
+ switch (type) {
+
+ case TYPE_A:
+ while ((c = getc(instr)) != EOF) {
+ byte_count++;
+ if (c == '\n') {
+ if (ferror(outstr))
+ goto data_err;
+ (void) putc('\r', outstr);
+ }
+ (void) putc(c, outstr);
+ }
+ fflush(outstr);
+ transflag = 0;
+ if (ferror(instr))
+ goto file_err;
+ if (ferror(outstr))
+ goto data_err;
+ reply(226, "Transfer complete.");
+ return;
+
+ case TYPE_I:
+ case TYPE_L:
+ /*
+ * isreg is only set if we are not doing restart and we
+ * are sending a regular file
+ */
+ netfd = fileno(outstr);
+ filefd = fileno(instr);
+
+ if (isreg && filesize < (off_t)16 * 1024 * 1024) {
+ buf = mmap(0, filesize, PROT_READ, MAP_SHARED, filefd,
+ (off_t)0);
+ if (!buf) {
+ syslog(LOG_WARNING, "mmap(%lu): %m",
+ (unsigned long)filesize);
+ goto oldway;
+ }
+ bp = buf;
+ len = filesize;
+ do {
+ cnt = write(netfd, bp, len);
+ len -= cnt;
+ bp += cnt;
+ if (cnt > 0) byte_count += cnt;
+ } while(cnt > 0 && len > 0);
+
+ transflag = 0;
+ munmap(buf, (size_t)filesize);
+ if (cnt < 0)
+ goto data_err;
+ reply(226, "Transfer complete.");
+ return;
+ }
+
+oldway:
+ if ((buf = malloc((u_int)blksize)) == NULL) {
+ transflag = 0;
+ perror_reply(451, "Local resource failure: malloc");
+ return;
+ }
+
+ while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
+ write(netfd, buf, cnt) == cnt)
+ byte_count += cnt;
+ transflag = 0;
+ (void)free(buf);
+ if (cnt != 0) {
+ if (cnt < 0)
+ goto file_err;
+ goto data_err;
+ }
+ reply(226, "Transfer complete.");
+ return;
+ default:
+ transflag = 0;
+ reply(550, "Unimplemented TYPE %d in send_data", type);
+ return;
+ }
+
+data_err:
+ transflag = 0;
+ perror_reply(426, "Data connection");
+ return;
+
+file_err:
+ transflag = 0;
+ perror_reply(551, "Error on input file");
+}
+
+/*
+ * Transfer data from peer to "outstr" using the appropriate encapulation of
+ * the data subject to Mode, Structure, and Type.
+ *
+ * N.B.: Form isn't handled.
+ */
+static int
+receive_data(instr, outstr)
+ FILE *instr, *outstr;
+{
+ int c;
+ int cnt, bare_lfs = 0;
+ char buf[BUFSIZ];
+
+ transflag++;
+ if (setjmp(urgcatch)) {
+ transflag = 0;
+ return (-1);
+ }
+ switch (type) {
+
+ case TYPE_I:
+ case TYPE_L:
+ while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
+ if (write(fileno(outstr), buf, cnt) != cnt)
+ goto file_err;
+ byte_count += cnt;
+ }
+ if (cnt < 0)
+ goto data_err;
+ transflag = 0;
+ return (0);
+
+ case TYPE_E:
+ reply(553, "TYPE E not implemented.");
+ transflag = 0;
+ return (-1);
+
+ case TYPE_A:
+ while ((c = getc(instr)) != EOF) {
+ byte_count++;
+ if (c == '\n')
+ bare_lfs++;
+ while (c == '\r') {
+ if (ferror(outstr))
+ goto data_err;
+ if ((c = getc(instr)) != '\n') {
+ (void) putc ('\r', outstr);
+ if (c == '\0' || c == EOF)
+ goto contin2;
+ }
+ }
+ (void) putc(c, outstr);
+ contin2: ;
+ }
+ fflush(outstr);
+ if (ferror(instr))
+ goto data_err;
+ if (ferror(outstr))
+ goto file_err;
+ transflag = 0;
+ if (bare_lfs) {
+ lreply(226,
+ "WARNING! %d bare linefeeds received in ASCII mode",
+ bare_lfs);
+ (void)printf(" File may not have transferred correctly.\r\n");
+ }
+ return (0);
+ default:
+ reply(550, "Unimplemented TYPE %d in receive_data", type);
+ transflag = 0;
+ return (-1);
+ }
+
+data_err:
+ transflag = 0;
+ perror_reply(426, "Data Connection");
+ return (-1);
+
+file_err:
+ transflag = 0;
+ perror_reply(452, "Error writing file");
+ return (-1);
+}
+
+void
+statfilecmd(filename)
+ char *filename;
+{
+ FILE *fin;
+ int c;
+ char line[LINE_MAX];
+
+ (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
+ fin = ftpd_popen(line, "r");
+ lreply(211, "status of %s:", filename);
+ while ((c = getc(fin)) != EOF) {
+ if (c == '\n') {
+ if (ferror(stdout)){
+ perror_reply(421, "control connection");
+ (void) ftpd_pclose(fin);
+ dologout(1);
+ /* NOTREACHED */
+ }
+ if (ferror(fin)) {
+ perror_reply(551, filename);
+ (void) ftpd_pclose(fin);
+ return;
+ }
+ (void) putc('\r', stdout);
+ }
+ (void) putc(c, stdout);
+ }
+ (void) ftpd_pclose(fin);
+ reply(211, "End of Status");
+}
+
+void
+statcmd()
+{
+ struct sockaddr_in *sin;
+ u_char *a, *p;
+
+ lreply(211, "%s FTP server status:", hostname, version);
+ printf(" %s\r\n", version);
+ printf(" Connected to %s", remotehost);
+ if (!isdigit(remotehost[0]))
+ printf(" (%s)", inet_ntoa(his_addr.sin_addr));
+ printf("\r\n");
+ if (logged_in) {
+ if (guest)
+ printf(" Logged in anonymously\r\n");
+ else
+ printf(" Logged in as %s\r\n", pw->pw_name);
+ } else if (askpasswd)
+ printf(" Waiting for password\r\n");
+ else
+ printf(" Waiting for user name\r\n");
+ printf(" TYPE: %s", typenames[type]);
+ if (type == TYPE_A || type == TYPE_E)
+ printf(", FORM: %s", formnames[form]);
+ if (type == TYPE_L)
+#if NBBY == 8
+ printf(" %d", NBBY);
+#else
+ printf(" %d", bytesize); /* need definition! */
+#endif
+ printf("; STRUcture: %s; transfer MODE: %s\r\n",
+ strunames[stru], modenames[mode]);
+ if (data != -1)
+ printf(" Data connection open\r\n");
+ else if (pdata != -1) {
+ printf(" in Passive mode");
+ sin = &pasv_addr;
+ goto printaddr;
+ } else if (usedefault == 0) {
+ printf(" PORT");
+ sin = &data_dest;
+printaddr:
+ a = (u_char *) &sin->sin_addr;
+ p = (u_char *) &sin->sin_port;
+#define UC(b) (((int) b) & 0xff)
+ printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
+ UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
+#undef UC
+ } else
+ printf(" No data connection\r\n");
+ reply(211, "End of status");
+}
+
+void
+fatal(s)
+ char *s;
+{
+
+ reply(451, "Error in server: %s\n", s);
+ reply(221, "Closing connection due to server error.");
+ dologout(0);
+ /* NOTREACHED */
+}
+
+void
+#if __STDC__
+reply(int n, const char *fmt, ...)
+#else
+reply(n, fmt, va_alist)
+ int n;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)printf("%d ", n);
+ (void)vprintf(fmt, ap);
+ (void)printf("\r\n");
+ (void)fflush(stdout);
+ if (debug) {
+ syslog(LOG_DEBUG, "<--- %d ", n);
+ vsyslog(LOG_DEBUG, fmt, ap);
+ }
+}
+
+void
+#if __STDC__
+lreply(int n, const char *fmt, ...)
+#else
+lreply(n, fmt, va_alist)
+ int n;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)printf("%d- ", n);
+ (void)vprintf(fmt, ap);
+ (void)printf("\r\n");
+ (void)fflush(stdout);
+ if (debug) {
+ syslog(LOG_DEBUG, "<--- %d- ", n);
+ vsyslog(LOG_DEBUG, fmt, ap);
+ }
+}
+
+static void
+ack(s)
+ char *s;
+{
+
+ reply(250, "%s command successful.", s);
+}
+
+void
+nack(s)
+ char *s;
+{
+
+ reply(502, "%s command not implemented.", s);
+}
+
+/* ARGSUSED */
+void
+yyerror(s)
+ char *s;
+{
+ char *cp;
+
+ if (cp = strchr(cbuf,'\n'))
+ *cp = '\0';
+ reply(500, "'%s': command not understood.", cbuf);
+}
+
+void
+delete(name)
+ char *name;
+{
+ struct stat st;
+
+ LOGCMD("delete", name);
+ if (stat(name, &st) < 0) {
+ perror_reply(550, name);
+ return;
+ }
+ if ((st.st_mode&S_IFMT) == S_IFDIR) {
+ if (rmdir(name) < 0) {
+ perror_reply(550, name);
+ return;
+ }
+ goto done;
+ }
+ if (unlink(name) < 0) {
+ perror_reply(550, name);
+ return;
+ }
+done:
+ ack("DELE");
+}
+
+void
+cwd(path)
+ char *path;
+{
+
+ if (chdir(path) < 0)
+ perror_reply(550, path);
+ else
+ ack("CWD");
+}
+
+void
+makedir(name)
+ char *name;
+{
+
+ LOGCMD("mkdir", name);
+ if (mkdir(name, 0777) < 0)
+ perror_reply(550, name);
+ else
+ reply(257, "MKD command successful.");
+}
+
+void
+removedir(name)
+ char *name;
+{
+
+ LOGCMD("rmdir", name);
+ if (rmdir(name) < 0)
+ perror_reply(550, name);
+ else
+ ack("RMD");
+}
+
+void
+pwd()
+{
+ char path[MAXPATHLEN + 1];
+
+ if (getwd(path) == (char *)NULL)
+ reply(550, "%s.", path);
+ else
+ reply(257, "\"%s\" is current directory.", path);
+}
+
+char *
+renamefrom(name)
+ char *name;
+{
+ struct stat st;
+
+ if (stat(name, &st) < 0) {
+ perror_reply(550, name);
+ return ((char *)0);
+ }
+ reply(350, "File exists, ready for destination name");
+ return (name);
+}
+
+void
+renamecmd(from, to)
+ char *from, *to;
+{
+
+ LOGCMD2("rename", from, to);
+ if (rename(from, to) < 0)
+ perror_reply(550, "rename");
+ else
+ ack("RNTO");
+}
+
+static void
+dolog(sin)
+ struct sockaddr_in *sin;
+{
+ struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
+ sizeof(struct in_addr), AF_INET);
+
+ if (hp)
+ (void) strncpy(remotehost, hp->h_name, sizeof(remotehost));
+ else
+ (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
+ sizeof(remotehost));
+#ifdef SETPROCTITLE
+ snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
+ setproctitle("%s", proctitle);
+#endif /* SETPROCTITLE */
+
+ if (logging)
+ syslog(LOG_INFO, "connection from %s", remotehost);
+}
+
+/*
+ * Record logout in wtmp file
+ * and exit with supplied status.
+ */
+void
+dologout(status)
+ int status;
+{
+
+ if (logged_in) {
+ (void) seteuid((uid_t)0);
+ logwtmp(ttyline, "", "");
+ }
+ /* beware of flushing buffers after a SIGPIPE */
+ _exit(status);
+}
+
+static void
+myoob(signo)
+ int signo;
+{
+ char *cp;
+
+ /* only process if transfer occurring */
+ if (!transflag)
+ return;
+ cp = tmpline;
+ if (getline(cp, 7, stdin) == NULL) {
+ reply(221, "You could at least say goodbye.");
+ dologout(0);
+ }
+ upper(cp);
+ if (strcmp(cp, "ABOR\r\n") == 0) {
+ tmpline[0] = '\0';
+ reply(426, "Transfer aborted. Data connection closed.");
+ reply(226, "Abort successful");
+ longjmp(urgcatch, 1);
+ }
+ if (strcmp(cp, "STAT\r\n") == 0) {
+ if (file_size != (off_t) -1)
+ reply(213, "Status: %qd of %qd bytes transferred",
+ byte_count, file_size);
+ else
+ reply(213, "Status: %qd bytes transferred", byte_count);
+ }
+}
+
+/*
+ * Note: a response of 425 is not mentioned as a possible response to
+ * the PASV command in RFC959. However, it has been blessed as
+ * a legitimate response by Jon Postel in a telephone conversation
+ * with Rick Adams on 25 Jan 89.
+ */
+void
+passive()
+{
+ int len;
+ u_short port;
+ char *p, *a;
+
+ pdata = socket(AF_INET, SOCK_STREAM, 0);
+ if (pdata < 0) {
+ perror_reply(425, "Can't open passive connection");
+ return;
+ }
+
+ if (restricted_data_ports) {
+ for (port = FTP_DATA_BOTTOM; port <= FTP_DATA_TOP; port++) {
+ pasv_addr = ctrl_addr;
+ pasv_addr.sin_port = htons(port);
+ (void) seteuid((uid_t)0);
+ if (bind(pdata, (struct sockaddr *)&pasv_addr,
+ sizeof(pasv_addr)) < 0) {
+ (void) seteuid((uid_t)pw->pw_uid);
+ if (errno == EADDRINUSE)
+ continue;
+ else
+ goto pasv_error;
+ }
+ (void) seteuid((uid_t)pw->pw_uid);
+ break;
+ }
+ if (port > FTP_DATA_TOP)
+ goto pasv_error;
+ } else {
+ pasv_addr = ctrl_addr;
+ pasv_addr.sin_port = 0;
+ (void) seteuid((uid_t)0);
+ if (bind(pdata, (struct sockaddr *)&pasv_addr,
+ sizeof(pasv_addr)) < 0) {
+ (void) seteuid((uid_t)pw->pw_uid);
+ goto pasv_error;
+ }
+ (void) seteuid((uid_t)pw->pw_uid);
+ }
+
+ len = sizeof(pasv_addr);
+ if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
+ goto pasv_error;
+ if (listen(pdata, 1) < 0)
+ goto pasv_error;
+ a = (char *) &pasv_addr.sin_addr;
+ p = (char *) &pasv_addr.sin_port;
+
+#define UC(b) (((int) b) & 0xff)
+
+ reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
+ UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
+ return;
+
+pasv_error:
+ (void) close(pdata);
+ pdata = -1;
+ perror_reply(425, "Can't open passive connection");
+ return;
+}
+
+/*
+ * Generate unique name for file with basename "local".
+ * The file named "local" is already known to exist.
+ * Generates failure reply on error.
+ */
+static char *
+gunique(local)
+ char *local;
+{
+ static char new[MAXPATHLEN];
+ struct stat st;
+ int count;
+ char *cp;
+
+ cp = strrchr(local, '/');
+ if (cp)
+ *cp = '\0';
+ if (stat(cp ? local : ".", &st) < 0) {
+ perror_reply(553, cp ? local : ".");
+ return ((char *) 0);
+ }
+ if (cp)
+ *cp = '/';
+ (void) strcpy(new, local);
+ cp = new + strlen(new);
+ *cp++ = '.';
+ for (count = 1; count < 100; count++) {
+ (void)sprintf(cp, "%d", count);
+ if (stat(new, &st) < 0)
+ return (new);
+ }
+ reply(452, "Unique file name cannot be created.");
+ return (NULL);
+}
+
+/*
+ * Format and send reply containing system error number.
+ */
+void
+perror_reply(code, string)
+ int code;
+ char *string;
+{
+
+ reply(code, "%s: %s.", string, strerror(errno));
+}
+
+static char *onefile[] = {
+ "",
+ 0
+};
+
+void
+send_file_list(whichf)
+ char *whichf;
+{
+ struct stat st;
+ DIR *dirp = NULL;
+ struct dirent *dir;
+ FILE *dout = NULL;
+ char **dirlist, *dirname;
+ int simple = 0;
+ int freeglob = 0;
+ glob_t gl;
+
+ if (strpbrk(whichf, "~{[*?") != NULL) {
+ int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
+
+ memset(&gl, 0, sizeof(gl));
+ freeglob = 1;
+ if (glob(whichf, flags, 0, &gl)) {
+ reply(550, "not found");
+ goto out;
+ } else if (gl.gl_pathc == 0) {
+ errno = ENOENT;
+ perror_reply(550, whichf);
+ goto out;
+ }
+ dirlist = gl.gl_pathv;
+ } else {
+ onefile[0] = whichf;
+ dirlist = onefile;
+ simple = 1;
+ }
+
+ if (setjmp(urgcatch)) {
+ transflag = 0;
+ goto out;
+ }
+ while (dirname = *dirlist++) {
+ if (stat(dirname, &st) < 0) {
+ /*
+ * If user typed "ls -l", etc, and the client
+ * used NLST, do what the user meant.
+ */
+ if (dirname[0] == '-' && *dirlist == NULL &&
+ transflag == 0) {
+ retrieve("/bin/ls %s", dirname);
+ goto out;
+ }
+ perror_reply(550, whichf);
+ if (dout != NULL) {
+ (void) fclose(dout);
+ transflag = 0;
+ data = -1;
+ pdata = -1;
+ }
+ goto out;
+ }
+
+ if (S_ISREG(st.st_mode)) {
+ if (dout == NULL) {
+ dout = dataconn("file list", (off_t)-1, "w");
+ if (dout == NULL)
+ goto out;
+ transflag++;
+ }
+ fprintf(dout, "%s%s\n", dirname,
+ type == TYPE_A ? "\r" : "");
+ byte_count += strlen(dirname) + 1;
+ continue;
+ } else if (!S_ISDIR(st.st_mode))
+ continue;
+
+ if ((dirp = opendir(dirname)) == NULL)
+ continue;
+
+ while ((dir = readdir(dirp)) != NULL) {
+ char nbuf[MAXPATHLEN];
+
+ if (dir->d_name[0] == '.' && dir->d_namlen == 1)
+ continue;
+ if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
+ dir->d_namlen == 2)
+ continue;
+
+ sprintf(nbuf, "%s/%s", dirname, dir->d_name);
+
+ /*
+ * We have to do a stat to insure it's
+ * not a directory or special file.
+ */
+ if (simple || (stat(nbuf, &st) == 0 &&
+ S_ISREG(st.st_mode))) {
+ if (dout == NULL) {
+ dout = dataconn("file list", (off_t)-1,
+ "w");
+ if (dout == NULL)
+ goto out;
+ transflag++;
+ }
+ if (nbuf[0] == '.' && nbuf[1] == '/')
+ fprintf(dout, "%s%s\n", &nbuf[2],
+ type == TYPE_A ? "\r" : "");
+ else
+ fprintf(dout, "%s%s\n", nbuf,
+ type == TYPE_A ? "\r" : "");
+ byte_count += strlen(nbuf) + 1;
+ }
+ }
+ (void) closedir(dirp);
+ }
+
+ if (dout == NULL)
+ reply(550, "No files found.");
+ else if (ferror(dout) != 0)
+ perror_reply(550, "Data connection");
+ else
+ reply(226, "Transfer complete.");
+
+ transflag = 0;
+ if (dout != NULL)
+ (void) fclose(dout);
+ data = -1;
+ pdata = -1;
+out:
+ if (freeglob) {
+ freeglob = 0;
+ globfree(&gl);
+ }
+}
+
+#ifdef OLD_SETPROCTITLE
+/*
+ * Clobber argv so ps will show what we're doing. (Stolen from sendmail.)
+ * Warning, since this is usually started from inetd.conf, it often doesn't
+ * have much of an environment or arglist to overwrite.
+ */
+void
+#if __STDC__
+setproctitle(const char *fmt, ...)
+#else
+setproctitle(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ int i;
+ va_list ap;
+ char *p, *bp, ch;
+ char buf[LINE_MAX];
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vsnprintf(buf, sizeof(buf), fmt, ap);
+
+ /* make ps print our process name */
+ p = Argv[0];
+ *p++ = '-';
+
+ i = strlen(buf);
+ if (i > LastArgv - p - 2) {
+ i = LastArgv - p - 2;
+ buf[i] = '\0';
+ }
+ bp = buf;
+ while (ch = *bp++)
+ if (ch != '\n' && ch != '\r')
+ *p++ = ch;
+ while (p < LastArgv)
+ *p++ = ' ';
+}
+#endif /* OLD_SETPROCTITLE */
+
+#ifdef STATS
+logxfer(name, size, start)
+ char *name;
+ long size;
+ long start;
+{
+ char buf[1024];
+ char path[MAXPATHLEN + 1];
+ long now;
+
+ if (statfd >= 0 && getwd(path) != NULL) {
+ time(&now);
+ sprintf(buf, "%.20s!%s!%s!%s/%s!%ld!%ld\n",
+ ctime(&now)+4, ident, remotehost,
+ path, name, size, now - start + (now == start));
+ write(statfd, buf, strlen(buf));
+ }
+}
+
+char *
+copy(s)
+ char *s;
+{
+ char *p;
+
+ p = malloc((unsigned) strlen(s) + 1);
+ if (p == NULL)
+ fatal("Ran out of memory.");
+ (void) strcpy(p, s);
+ return (p);
+}
+#endif
diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c
new file mode 100644
index 0000000..d40840c
--- /dev/null
+++ b/libexec/ftpd/logwtmp.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)logwtmp.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <utmp.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include "extern.h"
+
+static int fd = -1;
+
+/*
+ * Modified version of logwtmp that holds wtmp file open
+ * after first call, for use with ftp (which may chroot
+ * after login, but before logout).
+ */
+void
+logwtmp(line, name, host)
+ char *line, *name, *host;
+{
+ struct utmp ut;
+ struct stat buf;
+
+ if (fd < 0 && (fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0)
+ return;
+ if (fstat(fd, &buf) == 0) {
+ (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+ (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name));
+ (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host));
+ (void)time(&ut.ut_time);
+ if (write(fd, (char *)&ut, sizeof(struct utmp)) !=
+ sizeof(struct utmp))
+ (void)ftruncate(fd, buf.st_size);
+ }
+}
diff --git a/libexec/ftpd/pathnames.h b/libexec/ftpd/pathnames.h
new file mode 100644
index 0000000..85a656b
--- /dev/null
+++ b/libexec/ftpd/pathnames.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ */
+
+#include <paths.h>
+
+#define _PATH_FTPWELCOME "/etc/ftpwelcome"
+#define _PATH_FTPLOGINMESG "/etc/ftpmotd"
+#define _PATH_FTPDSTATFILE "/var/log/ftpd"
diff --git a/libexec/ftpd/popen.c b/libexec/ftpd/popen.c
new file mode 100644
index 0000000..b26732e
--- /dev/null
+++ b/libexec/ftpd/popen.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software written by Ken Arnold and
+ * published in UNIX Review, Vol. 6, No. 8.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <glob.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+/*
+ * Special version of popen which avoids call to shell. This ensures noone
+ * may create a pipe to a hidden program as a side effect of a list or dir
+ * command.
+ */
+static int *pids;
+static int fds;
+
+FILE *
+ftpd_popen(program, type)
+ char *program, *type;
+{
+ char *cp;
+ FILE *iop;
+ int argc, gargc, pdes[2], pid;
+ char **pop, *argv[100], *gargv[1000];
+
+ if (*type != 'r' && *type != 'w' || type[1])
+ return (NULL);
+
+ if (!pids) {
+ if ((fds = getdtablesize()) <= 0)
+ return (NULL);
+ if ((pids = (int *)malloc((u_int)(fds * sizeof(int)))) == NULL)
+ return (NULL);
+ memset(pids, 0, fds * sizeof(int));
+ }
+ if (pipe(pdes) < 0)
+ return (NULL);
+
+ /* break up string into pieces */
+ for (argc = 0, cp = program;; cp = NULL)
+ if (!(argv[argc++] = strtok(cp, " \t\n")))
+ break;
+
+ /* glob each piece */
+ gargv[0] = argv[0];
+ for (gargc = argc = 1; argv[argc]; argc++) {
+ glob_t gl;
+ int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
+
+ memset(&gl, 0, sizeof(gl));
+ if (glob(argv[argc], flags, NULL, &gl))
+ gargv[gargc++] = strdup(argv[argc]);
+ else
+ for (pop = gl.gl_pathv; *pop; pop++)
+ gargv[gargc++] = strdup(*pop);
+ globfree(&gl);
+ }
+ gargv[gargc] = NULL;
+
+ iop = NULL;
+ switch(pid = vfork()) {
+ case -1: /* error */
+ (void)close(pdes[0]);
+ (void)close(pdes[1]);
+ goto pfree;
+ /* NOTREACHED */
+ case 0: /* child */
+ if (*type == 'r') {
+ if (pdes[1] != STDOUT_FILENO) {
+ dup2(pdes[1], STDOUT_FILENO);
+ (void)close(pdes[1]);
+ }
+ dup2(STDOUT_FILENO, STDERR_FILENO); /* stderr too! */
+ (void)close(pdes[0]);
+ } else {
+ if (pdes[0] != STDIN_FILENO) {
+ dup2(pdes[0], STDIN_FILENO);
+ (void)close(pdes[0]);
+ }
+ (void)close(pdes[1]);
+ }
+ execv(gargv[0], gargv);
+ _exit(1);
+ }
+ /* parent; assume fdopen can't fail... */
+ if (*type == 'r') {
+ iop = fdopen(pdes[0], type);
+ (void)close(pdes[1]);
+ } else {
+ iop = fdopen(pdes[1], type);
+ (void)close(pdes[0]);
+ }
+ pids[fileno(iop)] = pid;
+
+pfree: for (argc = 1; gargv[argc] != NULL; argc++)
+ free(gargv[argc]);
+
+ return (iop);
+}
+
+int
+ftpd_pclose(iop)
+ FILE *iop;
+{
+ int fdes, omask, status;
+ pid_t pid;
+
+ /*
+ * pclose returns -1 if stream is not associated with a
+ * `popened' command, or, if already `pclosed'.
+ */
+ if (pids == 0 || pids[fdes = fileno(iop)] == 0)
+ return (-1);
+ (void)fclose(iop);
+ omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
+ while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR)
+ continue;
+ (void)sigsetmask(omask);
+ pids[fdes] = 0;
+ if (pid < 0)
+ return (pid);
+ if (WIFEXITED(status))
+ return (WEXITSTATUS(status));
+ return (1);
+}
diff --git a/libexec/ftpd/skey-stuff.c b/libexec/ftpd/skey-stuff.c
new file mode 100644
index 0000000..5c34b9b
--- /dev/null
+++ b/libexec/ftpd/skey-stuff.c
@@ -0,0 +1,24 @@
+/* Author: Wietse Venema, Eindhoven University of Technology. */
+
+#include <stdio.h>
+#include <pwd.h>
+
+#include <skey.h>
+
+/* skey_challenge - additional password prompt stuff */
+
+char *skey_challenge(name, pwd, pwok)
+char *name;
+struct passwd *pwd;
+int pwok;
+{
+ static char buf[128];
+ struct skey skey;
+
+ /* Display s/key challenge where appropriate. */
+
+ if (pwd == 0 || skeychallenge(&skey, pwd->pw_name, buf) != 0)
+ sprintf(buf, "%s required for %s.",
+ pwok ? "Password" : "S/Key password", name);
+ return (buf);
+}
diff --git a/libexec/getNAME/Makefile b/libexec/getNAME/Makefile
new file mode 100644
index 0000000..a78bc28
--- /dev/null
+++ b/libexec/getNAME/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+
+PROG= getNAME
+NOMAN= noman
+
+.include <bsd.prog.mk>
diff --git a/libexec/getNAME/getNAME.c b/libexec/getNAME/getNAME.c
new file mode 100644
index 0000000..2ab64b4
--- /dev/null
+++ b/libexec/getNAME/getNAME.c
@@ -0,0 +1,339 @@
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)getNAME.c 8.1 (Berkeley) 6/30/93";
+#endif /* not lint */
+
+/*
+ * Get name sections from manual pages.
+ * -t for building toc
+ * -i for building intro entries
+ * other apropos database
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int tocrc;
+int intro;
+int typeflag;
+
+void doname __P((char *));
+void dorefname __P((char *));
+void getfrom __P((char *));
+void split __P((char *, char *));
+void trimln __P((char *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "itw")) != EOF)
+ switch(ch) {
+ case 'i':
+ intro = 1;
+ break;
+ case 't':
+ tocrc = 1;
+ break;
+ case 'w':
+ typeflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!*argv)
+ usage();
+
+ for (; *argv; ++argv)
+ getfrom(*argv);
+ exit(0);
+}
+
+void
+getfrom(pathname)
+ char *pathname;
+{
+ int i = 0;
+ char *name, *loc;
+ char headbuf[BUFSIZ];
+ char linbuf[BUFSIZ];
+
+ if (freopen(pathname, "r", stdin) == 0) {
+ perror(pathname);
+ return;
+ }
+ if (name = strrchr(pathname, '/'))
+ name++;
+ else
+ name = pathname;
+ for (;;) {
+ if (fgets(headbuf, sizeof headbuf, stdin) == NULL) {
+ if (typeflag)
+ printf("%-60s UNKNOWN\n", pathname);
+ return;
+ }
+ if (headbuf[0] != '.')
+ continue;
+ if ((headbuf[1] == 'T' && headbuf[2] == 'H') ||
+ (headbuf[1] == 't' && headbuf[2] == 'h'))
+ break;
+ if (headbuf[1] == 'D' && headbuf[2] == 't') {
+ if (typeflag) {
+ printf("%-60s NEW\n", pathname);
+ return;
+ }
+ goto newman;
+ }
+ }
+ if (typeflag) {
+ printf("%-60s OLD\n", pathname);
+ return;
+ }
+ for (;;) {
+ if (fgets(linbuf, sizeof linbuf, stdin) == NULL)
+ return;
+ if (linbuf[0] != '.')
+ continue;
+ if (linbuf[1] == 'S' && linbuf[2] == 'H')
+ break;
+ if (linbuf[1] == 's' && linbuf[2] == 'h')
+ break;
+ }
+ trimln(headbuf);
+ if (tocrc)
+ doname(name);
+ if (!tocrc && !intro)
+ printf("%s\t", headbuf);
+ linbuf[0] = '\0';
+ for (;;) {
+ if (fgets(headbuf, sizeof headbuf, stdin) == NULL)
+ break;
+ if (headbuf[0] == '.') {
+ if (headbuf[1] == 'S' && headbuf[2] == 'H')
+ break;
+ if (headbuf[1] == 's' && headbuf[2] == 'h')
+ break;
+ }
+ if (i != 0)
+ strcat(linbuf, " ");
+ i++;
+ trimln(headbuf);
+ strcat(linbuf, headbuf);
+ }
+ if (intro)
+ split(linbuf, name);
+ else
+ printf("%s\n", linbuf);
+ return;
+
+newman:
+ for (;;) {
+ if (fgets(linbuf, sizeof linbuf, stdin) == NULL)
+ return;
+ if (linbuf[0] != '.')
+ continue;
+ if (linbuf[1] == 'S' && linbuf[2] == 'h')
+ break;
+ }
+ trimln(headbuf);
+ if (tocrc)
+ doname(name);
+ if (!tocrc && !intro)
+ printf(".TH%s\t", &headbuf[3]);
+ linbuf[0] = '\0';
+ for (;;) {
+ if (fgets(headbuf, sizeof headbuf, stdin) == NULL)
+ break;
+ if (headbuf[0] == '.') {
+ if (headbuf[1] == 'S' && headbuf[2] == 'h')
+ break;
+ }
+ if (i != 0)
+ strcat(linbuf, " ");
+ i++;
+ trimln(headbuf);
+ for (loc = strchr(headbuf, ' '); loc; loc = strchr(loc, ' '))
+ if (loc[1] == ',')
+ strcpy(loc, &loc[1]);
+ else
+ loc++;
+ if (headbuf[0] != '.') {
+ strcat(linbuf, headbuf);
+ } else {
+ /*
+ * Get rid of quotes in macros.
+ */
+ for (loc = strchr(&headbuf[4], '"'); loc; ) {
+ strcpy(loc, &loc[1]);
+ loc = strchr(loc, '"');
+ }
+ /*
+ * Handle cross references
+ */
+ if (headbuf[1] == 'X' && headbuf[2] == 'r') {
+ for (loc = &headbuf[4]; *loc != ' '; loc++)
+ continue;
+ loc[0] = '(';
+ loc[2] = ')';
+ loc[3] = '\0';
+ }
+ /*
+ * Put dash between names and description.
+ */
+ if (headbuf[1] == 'N' && headbuf[2] == 'd')
+ strcat(linbuf, "\\- ");
+ /*
+ * Skip over macro names.
+ */
+ strcat(linbuf, &headbuf[4]);
+ }
+ }
+ if (intro)
+ split(linbuf, name);
+ else
+ printf("%s\n", linbuf);
+}
+
+void
+trimln(cp)
+ register char *cp;
+{
+
+ while (*cp)
+ cp++;
+ if (*--cp == '\n')
+ *cp = 0;
+}
+
+void
+doname(name)
+ char *name;
+{
+ register char *dp = name, *ep;
+
+again:
+ while (*dp && *dp != '.')
+ putchar(*dp++);
+ if (*dp)
+ for (ep = dp+1; *ep; ep++)
+ if (*ep == '.') {
+ putchar(*dp++);
+ goto again;
+ }
+ putchar('(');
+ if (*dp)
+ dp++;
+ while (*dp)
+ putchar (*dp++);
+ putchar(')');
+ putchar(' ');
+}
+
+void
+split(line, name)
+ char *line, *name;
+{
+ register char *cp, *dp;
+ char *sp, *sep;
+
+ cp = strchr(line, '-');
+ if (cp == 0)
+ return;
+ sp = cp + 1;
+ for (--cp; *cp == ' ' || *cp == '\t' || *cp == '\\'; cp--)
+ ;
+ *++cp = '\0';
+ while (*sp && (*sp == ' ' || *sp == '\t'))
+ sp++;
+ for (sep = "", dp = line; dp && *dp; dp = cp, sep = "\n") {
+ cp = strchr(dp, ',');
+ if (cp) {
+ register char *tp;
+
+ for (tp = cp - 1; *tp == ' ' || *tp == '\t'; tp--)
+ ;
+ *++tp = '\0';
+ for (++cp; *cp == ' ' || *cp == '\t'; cp++)
+ ;
+ }
+ printf("%s%s\t", sep, dp);
+ dorefname(name);
+ printf("\t%s", sp);
+ }
+}
+
+void
+dorefname(name)
+ char *name;
+{
+ register char *dp = name, *ep;
+
+again:
+ while (*dp && *dp != '.')
+ putchar(*dp++);
+ if (*dp)
+ for (ep = dp+1; *ep; ep++)
+ if (*ep == '.') {
+ putchar(*dp++);
+ goto again;
+ }
+ putchar('.');
+ if (*dp)
+ dp++;
+ while (*dp)
+ putchar (*dp++);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: getNAME [-it] file ...\n");
+ exit(1);
+}
diff --git a/libexec/getty/Makefile b/libexec/getty/Makefile
index 7f2eabe..8113441 100644
--- a/libexec/getty/Makefile
+++ b/libexec/getty/Makefile
@@ -1,10 +1,10 @@
-# @(#)Makefile 8.1 (Berkeley) 6/4/93
+# @(#)Makefile 5.12 (Berkeley) 1/21/91
PROG= getty
-SRCS= main.c init.c subr.c ttydefaults.c
+SRCS= main.c init.c subr.c gettytab.c
DPADD= ${LIBUTIL}
LDADD= -lutil
-MAN5= gettytab.0 ttys.0
-MAN8= getty.0
+MAN5= gettytab.5 ttys.5
+MAN8= getty.8
.include <bsd.prog.mk>
diff --git a/libexec/getty/getty.8 b/libexec/getty/getty.8
index ffd5060..ce6274b 100644
--- a/libexec/getty/getty.8
+++ b/libexec/getty/getty.8
@@ -1,5 +1,5 @@
-.\" Copyright (c) 1980, 1991, 1993
-.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 1980, 1991 Regents of the University of California.
+.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@@ -29,9 +29,9 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" @(#)getty.8 8.1 (Berkeley) 6/4/93
+.\" @(#)getty.8 6.6 (Berkeley) 4/25/91
.\"
-.Dd June 4, 1993
+.Dd April 25, 1991
.Dt GETTY 8
.Os BSD 4
.Sh NAME
diff --git a/libexec/getty/gettytab.5 b/libexec/getty/gettytab.5
index a9bbd53..db6b1cc 100644
--- a/libexec/getty/gettytab.5
+++ b/libexec/getty/gettytab.5
@@ -1,5 +1,5 @@
-.\" Copyright (c) 1983, 1991, 1993
-.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 1983, 1991 The Regents of the University of California.
+.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@@ -29,9 +29,9 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" @(#)gettytab.5 8.4 (Berkeley) 4/19/94
+.\" @(#)gettytab.5 6.7 (Berkeley) 5/10/91
.\"
-.Dd April 19, 1994
+.Dd May 10, 1991
.Dt GETTYTAB 5
.Os BSD 4.2
.Sh NAME
@@ -131,7 +131,7 @@ hangup line on last close
.No "program to exec when name obtained"
.It "nd num 0 newline (line-feed) delay"
.It "nl bool false terminal has (or might have) a newline character"
-.It "np bool false terminal uses no parity (i.e. 8-bit characters)"
+.It "np bool false terminal uses no parity (8bit chars)"
.It "nx str default next table (for auto speed selection)"
.It "op bool false terminal uses odd parity"
.It "os num unused output speed"
diff --git a/libexec/getty/gettytab.c b/libexec/getty/gettytab.c
new file mode 100644
index 0000000..a864529
--- /dev/null
+++ b/libexec/getty/gettytab.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)gettytab.c 5.5 (Berkeley) 2/25/91";
+#endif /* not lint */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include "pathnames.h"
+
+#define TABBUFSIZ 512
+
+static char *tbuf;
+int hopcount; /* detect infinite loops in termcap, init 0 */
+static char *skip();
+char *getstr();
+static char *decode();
+
+/*
+ * Get an entry for terminal name in buffer bp,
+ * from the termcap file. Parse is very rudimentary;
+ * we just notice escaped newlines.
+ */
+getent(bp, name)
+ char *bp, *name;
+{
+ register char *cp;
+ register int c;
+ register int i = 0, cnt = 0;
+ char ibuf[TABBUFSIZ];
+ char *cp2;
+ int tf;
+
+ tbuf = bp;
+ tf = open(_PATH_GETTYTAB, O_RDONLY, 0);
+ if (tf < 0)
+ return (-1);
+ for (;;) {
+ cp = bp;
+ for (;;) {
+ if (i == cnt) {
+ cnt = read(tf, ibuf, TABBUFSIZ);
+ if (cnt <= 0) {
+ close(tf);
+ return (0);
+ }
+ i = 0;
+ }
+ c = ibuf[i++];
+ if (c == '\n') {
+ if (cp > bp && cp[-1] == '\\'){
+ cp--;
+ continue;
+ }
+ break;
+ }
+ if (cp >= bp+TABBUFSIZ) {
+ write(2,"Gettytab entry too long\n", 24);
+ break;
+ } else
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ /*
+ * The real work for the match.
+ */
+ if (namatch(name)) {
+ close(tf);
+ return(nchktc());
+ }
+ }
+}
+
+/*
+ * tnchktc: check the last entry, see if it's tc=xxx. If so,
+ * recursively find xxx and append that entry (minus the names)
+ * to take the place of the tc=xxx entry. This allows termcap
+ * entries to say "like an HP2621 but doesn't turn on the labels".
+ * Note that this works because of the left to right scan.
+ */
+#define MAXHOP 32
+nchktc()
+{
+ register char *p, *q;
+ char tcname[16]; /* name of similar terminal */
+ char tcbuf[TABBUFSIZ];
+ char *holdtbuf = tbuf;
+ int l;
+
+ p = tbuf + strlen(tbuf) - 2; /* before the last colon */
+ while (*--p != ':')
+ if (p<tbuf) {
+ write(2, "Bad gettytab entry\n", 19);
+ return (0);
+ }
+ p++;
+ /* p now points to beginning of last field */
+ if (p[0] != 't' || p[1] != 'c')
+ return(1);
+ strcpy(tcname,p+3);
+ q = tcname;
+ while (q && *q != ':')
+ q++;
+ *q = 0;
+ if (++hopcount > MAXHOP) {
+ write(2, "Getty: infinite tc= loop\n", 25);
+ return (0);
+ }
+ if (getent(tcbuf, tcname) != 1)
+ return(0);
+ for (q=tcbuf; *q != ':'; q++)
+ ;
+ l = p - holdtbuf + strlen(q);
+ if (l > TABBUFSIZ) {
+ write(2, "Gettytab entry too long\n", 24);
+ q[TABBUFSIZ - (p-tbuf)] = 0;
+ }
+ strcpy(p, q+1);
+ tbuf = holdtbuf;
+ return(1);
+}
+
+/*
+ * Tnamatch deals with name matching. The first field of the termcap
+ * entry is a sequence of names separated by |'s, so we compare
+ * against each such name. The normal : terminator after the last
+ * name (before the first field) stops us.
+ */
+namatch(np)
+ char *np;
+{
+ register char *Np, *Bp;
+
+ Bp = tbuf;
+ if (*Bp == '#')
+ return(0);
+ for (;;) {
+ for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
+ continue;
+ if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
+ return (1);
+ while (*Bp && *Bp != ':' && *Bp != '|')
+ Bp++;
+ if (*Bp == 0 || *Bp == ':')
+ return (0);
+ Bp++;
+ }
+}
+
+/*
+ * Skip to the next field. Notice that this is very dumb, not
+ * knowing about \: escapes or any such. If necessary, :'s can be put
+ * into the termcap file in octal.
+ */
+static char *
+skip(bp)
+ register char *bp;
+{
+
+ while (*bp && *bp != ':')
+ bp++;
+ if (*bp == ':')
+ bp++;
+ return (bp);
+}
+
+/*
+ * Return the (numeric) option id.
+ * Numeric options look like
+ * li#80
+ * i.e. the option string is separated from the numeric value by
+ * a # character. If the option is not found we return -1.
+ * Note that we handle octal numbers beginning with 0.
+ */
+long
+getnum(id)
+ char *id;
+{
+ register long i, base;
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = skip(bp);
+ if (*bp == 0)
+ return (-1);
+ if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+ continue;
+ if (*bp == '@')
+ return(-1);
+ if (*bp != '#')
+ continue;
+ bp++;
+ base = 10;
+ if (*bp == '0')
+ base = 8;
+ i = 0;
+ while (isdigit(*bp))
+ i *= base, i += *bp++ - '0';
+ return (i);
+ }
+}
+
+/*
+ * Handle a flag option.
+ * Flag options are given "naked", i.e. followed by a : or the end
+ * of the buffer. Return 1 if we find the option, or 0 if it is
+ * not given.
+ */
+getflag(id)
+ char *id;
+{
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = skip(bp);
+ if (!*bp)
+ return (-1);
+ if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
+ if (!*bp || *bp == ':')
+ return (1);
+ else if (*bp == '!')
+ return (0);
+ else if (*bp == '@')
+ return(-1);
+ }
+ }
+}
+
+/*
+ * Get a string valued option.
+ * These are given as
+ * cl=^Z
+ * Much decoding is done on the strings, and the strings are
+ * placed in area, which is a ref parameter which is updated.
+ * No checking on area overflow.
+ */
+char *
+getstr(id, area)
+ char *id, **area;
+{
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = skip(bp);
+ if (!*bp)
+ return (0);
+ if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+ continue;
+ if (*bp == '@')
+ return(0);
+ if (*bp != '=')
+ continue;
+ bp++;
+ return (decode(bp, area));
+ }
+}
+
+/*
+ * Tdecode does the grung work to decode the
+ * string capability escapes.
+ */
+static char *
+decode(str, area)
+ register char *str;
+ char **area;
+{
+ register char *cp;
+ register int c;
+ register char *dp;
+ int i;
+
+ cp = *area;
+ while ((c = *str++) && c != ':') {
+ switch (c) {
+
+ case '^':
+ c = *str++ & 037;
+ break;
+
+ case '\\':
+ dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
+ c = *str++;
+nextc:
+ if (*dp++ == c) {
+ c = *dp++;
+ break;
+ }
+ dp++;
+ if (*dp)
+ goto nextc;
+ if (isdigit(c)) {
+ c -= '0', i = 2;
+ do
+ c <<= 3, c |= *str++ - '0';
+ while (--i && isdigit(*str));
+ }
+ break;
+ }
+ *cp++ = c;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
diff --git a/libexec/getty/gettytab.h b/libexec/getty/gettytab.h
index fb2b14f..6f393ce 100644
--- a/libexec/getty/gettytab.h
+++ b/libexec/getty/gettytab.h
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1983, 1993, 1994
- * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,7 +30,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * @(#)gettytab.h 8.2 (Berkeley) 3/30/94
+ * @(#)gettytab.h 5.5 (Berkeley) 3/27/91
*/
/*
@@ -134,10 +134,10 @@ struct gettyflags {
#define DX gettyflags[20].value
#define NP gettyflags[21].value
-int getent __P((char *, char *));
-long getnum __P((char *));
-int getflag __P((char *));
-char *getstr __P((char *, char **));
+int getent();
+long getnum();
+int getflag();
+char *getstr();
extern struct gettyflags gettyflags[];
extern struct gettynums gettynums[];
diff --git a/libexec/getty/init.c b/libexec/getty/init.c
index 835acd7..ac7112f 100644
--- a/libexec/getty/init.c
+++ b/libexec/getty/init.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1983, 1993
- * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,7 +32,7 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 6/4/93";
+static char sccsid[] = "@(#)init.c 5.6 (Berkeley) 3/27/91";
#endif /* not lint */
/*
diff --git a/libexec/getty/main.c b/libexec/getty/main.c
index c27ddf4..a161ed5 100644
--- a/libexec/getty/main.c
+++ b/libexec/getty/main.c
@@ -1,6 +1,6 @@
/*-
- * Copyright (c) 1980, 1993
- * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1980 The Regents of the University of California.
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,42 +32,32 @@
*/
#ifndef lint
-static char copyright[] =
-"@(#) Copyright (c) 1980, 1993\n\
- The Regents of the University of California. All rights reserved.\n";
+char copyright[] =
+"@(#) Copyright (c) 1980 The Regents of the University of California.\n\
+ All rights reserved.\n";
#endif /* not lint */
#ifndef lint
-static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/20/93";
+static char sccsid[] = "@(#)main.c 5.16 (Berkeley) 3/27/91";
#endif /* not lint */
#define USE_OLD_TTY
#include <sys/param.h>
#include <sys/stat.h>
-#include <sys/resource.h>
-
-#include <ctype.h>
-#include <ctype.h>
+#include <signal.h>
#include <fcntl.h>
-#include <setjmp.h>
#include <sgtty.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
#include <time.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <syslog.h>
#include <unistd.h>
-
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
#include "gettytab.h"
#include "pathnames.h"
-#include "extern.h"
-
-/*
- * Set the amount of running time that getty should accumulate
- * before deciding that something is wrong and exit.
- */
-#define GETTY_TIMEOUT 60 /* seconds */
struct sgttyb tmode = {
0, 0, CERASE, CKILL, 0
@@ -94,7 +84,9 @@ char *ttyname();
#define TABBUFSIZ 512
char defent[TABBUFSIZ];
+char defstrs[TABBUFSIZ];
char tabent[TABBUFSIZ];
+char tabstrs[TABBUFSIZ];
char *env[128];
@@ -142,59 +134,26 @@ interrupt()
longjmp(intrupt, 1);
}
-/*
- * Action to take when getty is running too long.
- */
-void
-timeoverrun(signo)
- int signo;
-{
-
- syslog(LOG_ERR, "getty exiting due to excessive running time\n");
- exit(1);
-}
-
-static int getname __P((void));
-static void oflush __P((void));
-static void prompt __P((void));
-static void putchr __P((int));
-static void putf __P((char *));
-static void putpad __P((char *));
-static void puts __P((char *));
-
-int
main(argc, argv)
int argc;
- char *argv[];
+ char **argv;
{
- extern char **environ;
+ extern char **environ;
char *tname;
- long allflags;
int repcnt = 0;
- struct rlimit limit;
signal(SIGINT, SIG_IGN);
-/*
- signal(SIGQUIT, SIG_DFL);
-*/
+ signal(SIGQUIT, SIG_IGN);
+
openlog("getty", LOG_ODELAY|LOG_CONS, LOG_AUTH);
gethostname(hostname, sizeof(hostname));
if (hostname[0] == '\0')
strcpy(hostname, "Amnesiac");
-
- /*
- * Limit running time to deal with broken or dead lines.
- */
- (void)signal(SIGXCPU, timeoverrun);
- limit.rlim_max = RLIM_INFINITY;
- limit.rlim_cur = GETTY_TIMEOUT;
- (void)setrlimit(RLIMIT_CPU, &limit);
-
/*
* The following is a work around for vhangup interactions
* which cause great problems getting window systems started.
* If the tty line is "-", we do the old style getty presuming
- * that the file descriptors are already set up for us.
+ * that the file descriptors are already set up for us.
* J. Gettys - MIT Project Athena.
*/
if (argc <= 2 || strcmp(argv[2], "-") == 0)
@@ -208,10 +167,6 @@ main(argc, argv)
chown(ttyn, 0, 0);
chmod(ttyn, 0600);
revoke(ttyn);
- /*
- * Delay the open so DTR stays down long enough to be detected.
- */
- sleep(2);
while ((i = open(ttyn, O_RDWR)) == -1) {
if (repcnt % 10 == 0) {
syslog(LOG_ERR, "%s: %m", ttyn);
@@ -224,22 +179,24 @@ main(argc, argv)
}
}
- gettable("default", defent);
+ gettable("default", defent, defstrs);
gendefaults();
tname = "default";
if (argc > 1)
tname = argv[1];
for (;;) {
- int off;
+ int off = 0;
+ int flushboth = 0;
+ struct sgttyb fake;
- gettable(tname, tabent);
+ gettable(tname, tabent, tabstrs);
if (OPset || EPset || APset)
APset++, OPset++, EPset++;
setdefaults();
- off = 0;
- ioctl(0, TIOCFLUSH, &off); /* clear out the crap */
+ ioctl(0, TIOCFLUSH, &flushboth); /* clear out the crap */
ioctl(0, FIONBIO, &off); /* turn off non-blocking mode */
ioctl(0, FIOASYNC, &off); /* ditto for async mode */
+ ioctl(0, TIOCGETP, &fake); /* initialize kernel termios */
if (IS)
tmode.sg_ispeed = speed(IS);
else if (SP)
@@ -248,8 +205,7 @@ main(argc, argv)
tmode.sg_ospeed = speed(OS);
else if (SP)
tmode.sg_ospeed = speed(SP);
- tmode.sg_flags = setflags(0);
- ioctl(0, TIOCSETP, &tmode);
+ set_tmode(0);
setchars();
ioctl(0, TIOCSETC, &tc);
if (HC)
@@ -281,58 +237,39 @@ main(argc, argv)
if (getname()) {
register int i;
- oflush();
alarm(0);
signal(SIGALRM, SIG_DFL);
+ oflush();
if (name[0] == '-') {
puts("user names may not start with '-'.");
continue;
}
if (!(upper || lower || digit))
continue;
- allflags = setflags(2);
- tmode.sg_flags = allflags & 0xffff;
- allflags >>= 16;
- if (crmod || NL)
- tmode.sg_flags |= CRMOD;
- if (upper || UC)
- tmode.sg_flags |= LCASE;
- if (lower || LC)
- tmode.sg_flags &= ~LCASE;
- ioctl(0, TIOCSETP, &tmode);
+ set_tmode(2);
ioctl(0, TIOCSLTC, &ltc);
- ioctl(0, TIOCLSET, &allflags);
- signal(SIGINT, SIG_DFL);
for (i = 0; environ[i] != (char *)0; i++)
env[i] = environ[i];
makeenv(&env[i]);
- /*
- * this is what login was doing anyway.
- * soon we rewrite getty completely.
- */
- set_ttydefaults(0);
- limit.rlim_max = RLIM_INFINITY;
- limit.rlim_cur = RLIM_INFINITY;
- (void)setrlimit(RLIMIT_CPU, &limit);
execle(LO, "login", "-p", name, (char *) 0, env);
syslog(LOG_ERR, "%s: %m", LO);
exit(1);
}
+ signal(SIGINT, SIG_IGN);
alarm(0);
signal(SIGALRM, SIG_DFL);
- signal(SIGINT, SIG_IGN);
if (NX && *NX)
tname = NX;
}
}
-static int
getname()
{
register int c;
register char *np;
char cs;
+ int flushin = 1 /*FREAD*/;
/*
* Interrupt may happen if we use CBREAK mode
@@ -342,16 +279,14 @@ getname()
return (0);
}
signal(SIGINT, interrupt);
- tmode.sg_flags = setflags(0);
- ioctl(0, TIOCSETP, &tmode);
- tmode.sg_flags = setflags(1);
+ ioctl(0, TIOCFLUSH, &flushin); /* purge any input */
prompt();
+ oflush();
if (PF > 0) {
- oflush();
sleep(PF);
PF = 0;
}
- ioctl(0, TIOCSETP, &tmode);
+ set_tmode(1);
crmod = digit = lower = upper = 0;
np = name;
for (;;) {
@@ -360,7 +295,7 @@ getname()
exit(0);
if ((c = cs&0177) == 0)
return (0);
- if (c == EOT)
+ if (c == EOT || c == 4 /*^D*/)
exit(1);
if (c == '\r' || c == '\n' || np >= &name[sizeof name]) {
putf("\r\n");
@@ -370,7 +305,7 @@ getname()
lower = 1;
else if (isupper(c))
upper = 1;
- else if (c == ERASE || c == '#' || c == '\b') {
+ else if (c == ERASE || c == '\b' || c == 0177) {
if (np > name) {
np--;
if (tmode.sg_ospeed >= B1200)
@@ -379,8 +314,7 @@ getname()
putchr(cs);
}
continue;
- } else if (c == KILL || c == '@') {
- putchr(cs);
+ } else if (c == KILL || c == 025 /*^U*/) {
putchr('\r');
if (tmode.sg_ospeed < B1200)
putchr('\n');
@@ -413,7 +347,6 @@ short tmspc10[] = {
0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5, 15
};
-static void
putpad(s)
register char *s;
{
@@ -455,7 +388,6 @@ putpad(s)
putchr(*PC);
}
-static void
puts(s)
register char *s;
{
@@ -466,9 +398,7 @@ puts(s)
char outbuf[OBUFSIZ];
int obufcnt = 0;
-static void
putchr(cc)
- int cc;
{
char c;
@@ -486,7 +416,6 @@ putchr(cc)
write(STDOUT_FILENO, &c, 1);
}
-static void
oflush()
{
if (obufcnt)
@@ -494,7 +423,6 @@ oflush()
obufcnt = 0;
}
-static void
prompt()
{
@@ -503,7 +431,6 @@ prompt()
putchr('\n');
}
-static void
putf(cp)
register char *cp;
{
@@ -519,7 +446,7 @@ putf(cp)
switch (*++cp) {
case 't':
- slash = strrchr(ttyn, '/');
+ slash = rindex(ttyn, '/');
if (slash == (char *) 0)
puts(ttyn);
else
@@ -531,7 +458,7 @@ putf(cp)
break;
case 'd': {
- static char fmt[] = "%l:% %P on %A, %d %B %Y";
+ static char fmt[] = "%l:% %p on %A, %d %B %Y";
fmt[4] = 'M'; /* I *hate* SCCS... */
(void)time(&t);
@@ -547,3 +474,29 @@ putf(cp)
cp++;
}
}
+
+/*
+ * The conversions from sgttyb to termios make LITOUT and PASS8 affect
+ * the parity. So every TIOCSETP ioctl has to be paired with a TIOCLSET
+ * ioctl (at least if LITOUT or PASS8 has changed, and PASS8 may vary
+ * with 'n').
+ */
+set_tmode(n)
+ int n;
+{
+ long allflags;
+
+ allflags = setflags(n);
+ tmode.sg_flags = allflags & 0xffff;
+ allflags >>= 16;
+ if (n == 2) {
+ if (crmod || NL)
+ tmode.sg_flags |= CRMOD;
+ if (upper || UC)
+ tmode.sg_flags |= LCASE;
+ if (lower || LC)
+ tmode.sg_flags &= ~LCASE;
+ }
+ ioctl(0, TIOCSETP, &tmode);
+ ioctl(0, TIOCLSET, &allflags);
+}
diff --git a/libexec/getty/pathnames.h b/libexec/getty/pathnames.h
index 714a9cc..e02b8b9 100644
--- a/libexec/getty/pathnames.h
+++ b/libexec/getty/pathnames.h
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1989, 1993
- * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,7 +30,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ * @(#)pathnames.h 5.3 (Berkeley) 6/1/90
*/
#include <paths.h>
diff --git a/libexec/getty/subr.c b/libexec/getty/subr.c
index 4a2abb3..6e4f5f0 100644
--- a/libexec/getty/subr.c
+++ b/libexec/getty/subr.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1983, 1993
- * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -29,24 +29,28 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00150
+ * -------------------- ----- ----------------------
+ *
+ * 22 Apr 93 Rodney W. Grimes support for 57600 and 115200 baud
+ *
*/
#ifndef lint
-static char sccsid[] = "@(#)subr.c 8.1 (Berkeley) 6/4/93";
+static char sccsid[] = "@(#)subr.c 5.10 (Berkeley) 2/26/91";
#endif /* not lint */
/*
* Melbourne getty.
*/
-#define USE_OLD_TTY
-#include <stdlib.h>
+#include <sys/param.h>
#include <sgtty.h>
-#include <string.h>
#include <unistd.h>
-
+#include <string.h>
#include "gettytab.h"
-#include "extern.h"
-#include "pathnames.h"
extern struct sgttyb tmode;
extern struct tchars tc;
@@ -55,25 +59,23 @@ extern struct ltchars ltc;
/*
* Get a table entry.
*/
-void
-gettable(name, buf)
- char *name, *buf;
+gettable(name, buf, area)
+ char *name, *buf, *area;
{
register struct gettystrs *sp;
register struct gettynums *np;
register struct gettyflags *fp;
- long n;
- char *dba[2];
- dba[0] = _PATH_GETTYTAB;
- dba[1] = 0;
+ register n;
- if (cgetent(&buf, dba, name) != 0)
+ hopcount = 0; /* new lookup, start fresh */
+ if (getent(buf, name) != 1)
return;
- for (sp = gettystrs; sp->field; sp++)
- cgetstr(buf, sp->field, &sp->value);
+ for (sp = gettystrs; sp->field; sp++)
+ sp->value = getstr(sp->field, &area);
for (np = gettynums; np->field; np++) {
- if (cgetnum(buf, np->field, &n) == -1)
+ n = getnum(np->field);
+ if (n == -1)
np->set = 0;
else {
np->set = 1;
@@ -81,27 +83,16 @@ gettable(name, buf)
}
}
for (fp = gettyflags; fp->field; fp++) {
- if (cgetcap(buf, fp->field, ':') == NULL)
+ n = getflag(fp->field);
+ if (n == -1)
fp->set = 0;
else {
fp->set = 1;
- fp->value = 1 ^ fp->invrt;
+ fp->value = n ^ fp->invrt;
}
}
-#ifdef DEBUG
- printf("name=\"%s\", buf=\"%s\"\n", name, buf);
- for (sp = gettystrs; sp->field; sp++)
- printf("cgetstr: %s=%s\n", sp->field, sp->value);
- for (np = gettynums; np->field; np++)
- printf("cgetnum: %s=%d\n", np->field, np->value);
- for (fp = gettyflags; fp->field; fp++)
- printf("cgetflags: %s='%c' set='%c'\n", fp->field,
- fp->value + '0', fp->set + '0');
- exit(1);
-#endif /* DEBUG */
}
-void
gendefaults()
{
register struct gettystrs *sp;
@@ -121,7 +112,6 @@ gendefaults()
fp->defalt = fp->invrt;
}
-void
setdefaults()
{
register struct gettystrs *sp;
@@ -154,7 +144,6 @@ charvars[] = {
&ltc.t_werasc, &ltc.t_lnextc, 0
};
-void
setchars()
{
register int i;
@@ -171,7 +160,6 @@ setchars()
long
setflags(n)
- int n;
{
register long f;
@@ -198,6 +186,8 @@ setflags(n)
f |= ODDP;
else if (EP)
f |= EVENP;
+ if (NP)
+ f |= PASS8;
if (UC)
f |= LCASE;
@@ -255,45 +245,44 @@ struct delayval {
*/
struct delayval crdelay[] = {
- { 1, CR1 },
- { 2, CR2 },
- { 3, CR3 },
- { 83, CR1 },
- { 166, CR2 },
- { 0, CR3 },
+ 1, CR1,
+ 2, CR2,
+ 3, CR3,
+ 83, CR1,
+ 166, CR2,
+ 0, CR3,
};
struct delayval nldelay[] = {
- { 1, NL1 }, /* special, calculated */
- { 2, NL2 },
- { 3, NL3 },
- { 100, NL2 },
- { 0, NL3 },
+ 1, NL1, /* special, calculated */
+ 2, NL2,
+ 3, NL3,
+ 100, NL2,
+ 0, NL3,
};
struct delayval bsdelay[] = {
- { 1, BS1 },
- { 0, 0 },
+ 1, BS1,
+ 0, 0,
};
struct delayval ffdelay[] = {
- { 1, FF1 },
- { 1750, FF1 },
- { 0, FF1 },
+ 1, FF1,
+ 1750, FF1,
+ 0, FF1,
};
struct delayval tbdelay[] = {
- { 1, TAB1 },
- { 2, TAB2 },
- { 3, XTABS }, /* this is expand tabs */
- { 100, TAB1 },
- { 0, TAB2 },
+ 1, TAB1,
+ 2, TAB2,
+ 3, XTABS, /* this is expand tabs */
+ 100, TAB1,
+ 0, TAB2,
};
-int
delaybits()
{
- register int f;
+ register f;
f = adelay(CD, crdelay);
f |= adelay(ND, nldelay);
@@ -303,7 +292,6 @@ delaybits()
return (f);
}
-int
adelay(ms, dp)
register ms;
register struct delayval *dp;
@@ -315,9 +303,8 @@ adelay(ms, dp)
return (dp->bits);
}
-char editedhost[32];
+char editedhost[MAXHOSTNAMELEN];
-void
edithost(pat)
register char *pat;
{
@@ -361,50 +348,50 @@ struct speedtab {
int speed;
int uxname;
} speedtab[] = {
- { 50, B50 },
- { 75, B75 },
- { 110, B110 },
- { 134, B134 },
- { 150, B150 },
- { 200, B200 },
- { 300, B300 },
- { 600, B600 },
- { 1200, B1200 },
- { 1800, B1800 },
- { 2400, B2400 },
- { 4800, B4800 },
- { 9600, B9600 },
- { 19200, EXTA },
- { 19, EXTA }, /* for people who say 19.2K */
- { 38400, EXTB },
- { 38, EXTB },
- { 7200, EXTB }, /* alternative */
- { 0 }
+ 50, B50,
+ 75, B75,
+ 110, B110,
+ 134, B134,
+ 150, B150,
+ 200, B200,
+ 300, B300,
+ 600, B600,
+ 1200, B1200,
+ 1800, B1800,
+ 2400, B2400,
+ 4800, B4800,
+ 9600, B9600,
+ 19200, EXTA,
+ 19, EXTA, /* for people who say 19.2K */
+ 38400, EXTB,
+ 38, EXTB,
+ 7200, EXTB, /* alternative */
+ 57600, B57600,
+ 115200, B115200,
+ 0
};
-int
speed(val)
- int val;
{
register struct speedtab *sp;
- if (val <= 15)
+ if (val <= B115200)
return (val);
for (sp = speedtab; sp->speed; sp++)
if (sp->speed == val)
return (sp->uxname);
-
+
return (B300); /* default in impossible cases */
}
-void
makeenv(env)
char *env[];
{
static char termbuf[128] = "TERM=";
register char *p, *q;
register char **ep;
+ char *index();
ep = env;
if (TT && *TT) {
@@ -413,7 +400,7 @@ makeenv(env)
}
if (p = EV) {
q = p;
- while (q = strchr(q, ',')) {
+ while (q = index(q, ',')) {
*q++ = '\0';
*ep++ = p;
p = q;
diff --git a/libexec/getty/ttys.5 b/libexec/getty/ttys.5
index cbbdbb6..8d31b23 100644
--- a/libexec/getty/ttys.5
+++ b/libexec/getty/ttys.5
@@ -1,5 +1,5 @@
-.\" Copyright (c) 1985, 1991, 1993
-.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 1985, 1991 The Regents of the University of California.
+.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@@ -29,9 +29,9 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" @(#)ttys.5 8.1 (Berkeley) 6/4/93
+.\" @(#)ttys.5 6.8 (Berkeley) 5/4/91
.\"
-.Dd June 4, 1993
+.Dd May 4, 1991
.Dt TTYS 5
.Os
.Sh NAME
diff --git a/libexec/lfs_cleanerd/Makefile b/libexec/lfs_cleanerd/Makefile
new file mode 100644
index 0000000..dac065c
--- /dev/null
+++ b/libexec/lfs_cleanerd/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= lfs_cleanerd
+CFLAGS+=-I/sys/ufs/lfs -I${.CURDIR} ${DEBUG}
+MAN8= lfs_cleanerd.8
+SRCS= cleanerd.c lfs_cksum.c library.c misc.c print.c
+
+.PATH: ${.CURDIR}/../../sys/ufs/lfs
+
+.include <bsd.prog.mk>
diff --git a/libexec/lfs_cleanerd/clean.h b/libexec/lfs_cleanerd/clean.h
new file mode 100644
index 0000000..729d0f8
--- /dev/null
+++ b/libexec/lfs_cleanerd/clean.h
@@ -0,0 +1,168 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clean.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * The LFS user-level library will be used when writing cleaners and
+ * checkers for LFS file systems. It will have facilities for finding
+ * and parsing LFS segments.
+ */
+
+#define DUMP_SUM_HEADER 0x0001
+#define DUMP_INODE_ADDRS 0x0002
+#define DUMP_FINFOS 0x0004
+#define DUMP_ALL 0xFFFF
+
+#define IFILE_NAME "ifile"
+
+/*
+ * Cleaner parameters
+ * BUSY_LIM: lower bound of the number of segments currently available
+ * as a percentage of the total number of free segments possibly
+ * available.
+ * IDLE_LIM: Same as BUSY_LIM but used when the system is idle.
+ * MIN_SEGS: Minimum number of segments you should always have.
+ * I have no idea what this should be, but it should probably
+ * be a function of lfsp.
+ * NUM_TO_CLEAN: Number of segments to clean at once. Again, this
+ * should probably be based on the file system size and how
+ * full or empty the segments being cleaned are.
+ */
+
+#define BUSY_LIM 0.50
+#define IDLE_LIM 0.90
+
+#define MIN_SEGS(lfsp) (5)
+#define NUM_TO_CLEAN(fsp) (1)
+
+#define MAXLOADS 3
+#define ONE_MIN 0
+#define FIVE_MIN 1
+#define FIFTEEN_MIN 2
+
+typedef struct fs_info {
+ struct statfs *fi_statfsp; /* fsstat info from getfsstat */
+ struct lfs fi_lfs; /* superblock */
+ CLEANERINFO *fi_cip; /* Cleaner info from ifile */
+ SEGUSE *fi_segusep; /* segment usage table (from ifile) */
+ IFILE *fi_ifilep; /* ifile table (from ifile) */
+ u_long fi_daddr_shift; /* shift to get byte offset of daddr */
+ u_long fi_ifile_count; /* # entries in the ifile table */
+ off_t fi_ifile_length; /* length of the ifile */
+} FS_INFO;
+
+/*
+ * XXX: size (in bytes) of a segment
+ * should lfs_bsize be fsbtodb(fs,1), blksize(fs), or lfs_dsize?
+ */
+#define seg_size(fs) ((fs)->lfs_ssize << (fs)->lfs_bshift)
+
+/* daddr -> byte offset */
+#define datobyte(fs, da) ((da) << (fs)->fi_daddr_shift)
+#define bytetoda(fs, byte) ((byte) >> (fs)->fi_daddr_shift)
+
+#define CLEANSIZE(fsp) (fsp->fi_lfs.lfs_cleansz << fsp->fi_lfs.lfs_bshift)
+#define SEGTABSIZE(fsp) (fsp->fi_lfs.lfs_segtabsz << fsp->fi_lfs.lfs_bshift)
+
+#define IFILE_ENTRY(fs, if, i) \
+ ((IFILE *)((caddr_t)(if) + ((i) / (fs)->lfs_ifpb << (fs)->lfs_bshift)) \
+ + (i) % (fs)->lfs_ifpb)
+
+#define SEGUSE_ENTRY(fs, su, i) \
+ ((SEGUSE *)((caddr_t)(su) + (fs)->lfs_bsize * ((i) / (fs)->lfs_sepb)) +\
+ (i) % (fs)->lfs_sepb)
+
+__BEGIN_DECLS
+int dump_summary __P((struct lfs *, SEGSUM *, u_long, daddr_t **));
+void err __P((const int, const char *, ...));
+int fs_getmntinfo __P((struct statfs **, char *, int));
+int get __P((int, off_t, void *, size_t));
+FS_INFO *get_fs_info __P((struct statfs *, int));
+int lfs_segmapv __P((FS_INFO *, int, caddr_t, BLOCK_INFO **, int *));
+int mmap_segment __P((FS_INFO *, int, caddr_t *, int));
+void munmap_segment __P((FS_INFO *, caddr_t, int));
+void reread_fs_info __P((FS_INFO *, int));
+void toss __P((void *, int *, size_t,
+ int (*)(const void *, const void *, const void *), void *));
+
+/*
+ * USEFUL DEBUGGING FUNCTIONS:
+ */
+#ifdef VERBOSE
+#define PRINT_FINFO(fp, ip) { \
+ (void)printf(" %s %s%d version %d nblocks %d\n", \
+ (ip)->if_version > (fp)->fi_version ? "TOSSING" : "KEEPING", \
+ "FINFO for inode: ", (fp)->fi_ino, \
+ (fp)->fi_version, (fp)->fi_nblocks); \
+ fflush(stdout); \
+}
+
+#define PRINT_INODE(b, bip) { \
+ (void) printf("\t%s inode: %d daddr: 0x%lx create: %s\n", \
+ b ? "KEEPING" : "TOSSING", (bip)->bi_inode, (bip)->bi_daddr, \
+ ctime((time_t *)&(bip)->bi_segcreate)); \
+ fflush(stdout); \
+}
+
+#define PRINT_BINFO(bip) { \
+ (void)printf("\tinode: %d lbn: %d daddr: 0x%lx create: %s\n", \
+ (bip)->bi_inode, (bip)->bi_lbn, (bip)->bi_daddr, \
+ ctime((time_t *)&(bip)->bi_segcreate)); \
+ fflush(stdout); \
+}
+
+#define PRINT_SEGUSE(sup, n) { \
+ (void)printf("Segment %d nbytes=%lu\tflags=%c%c%c ninos=%d nsums=%d lastmod: %s\n", \
+ n, (sup)->su_nbytes, \
+ (sup)->su_flags & SEGUSE_DIRTY ? 'D' : 'C', \
+ (sup)->su_flags & SEGUSE_ACTIVE ? 'A' : ' ', \
+ (sup)->su_flags & SEGUSE_SUPERBLOCK ? 'S' : ' ', \
+ (sup)->su_ninos, (sup)->su_nsums, \
+ ctime((time_t *)&(sup)->su_lastmod)); \
+ fflush(stdout); \
+}
+
+void dump_super __P((struct lfs *));
+void dump_cleaner_info __P((void *));
+void print_SEGSUM __P(( struct lfs *, SEGSUM *));
+void print_CLEANERINFO __P((CLEANERINFO *));
+#else
+#define PRINT_FINFO(fp, ip)
+#define PRINT_INODE(b, bip)
+#define PRINT_BINFO(bip)
+#define PRINT_SEGUSE(sup, n)
+#define dump_cleaner_info(cip)
+#define dump_super(lfsp)
+#endif
+__END_DECLS
diff --git a/libexec/lfs_cleanerd/cleanerd.c b/libexec/lfs_cleanerd/cleanerd.c
new file mode 100644
index 0000000..994992e
--- /dev/null
+++ b/libexec/lfs_cleanerd/cleanerd.c
@@ -0,0 +1,500 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)cleanerd.c 8.2 (Berkeley) 1/13/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/lfs/lfs.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "clean.h"
+char *special = "cleanerd";
+int do_small = 0;
+int do_mmap = 0;
+struct cleaner_stats {
+ int blocks_read;
+ int blocks_written;
+ int segs_cleaned;
+ int segs_empty;
+ int segs_error;
+} cleaner_stats;
+
+struct seglist {
+ int sl_id; /* segment number */
+ int sl_cost; /* cleaning cost */
+ char sl_empty; /* is segment empty */
+};
+
+struct tossstruct {
+ struct lfs *lfs;
+ int seg;
+};
+
+/* function prototypes for system calls; not sure where they should go */
+int lfs_segwait __P((fsid_t *, struct timeval *));
+int lfs_segclean __P((fsid_t *, u_long));
+int lfs_bmapv __P((fsid_t *, BLOCK_INFO *, int));
+int lfs_markv __P((fsid_t *, BLOCK_INFO *, int));
+
+/* function prototypes */
+int bi_tossold __P((const void *, const void *, const void *));
+int choose_segments __P((FS_INFO *, struct seglist *,
+ int (*)(FS_INFO *, SEGUSE *)));
+void clean_fs __P((FS_INFO *, int (*)(FS_INFO *, SEGUSE *)));
+int clean_loop __P((FS_INFO *));
+int clean_segment __P((FS_INFO *, int));
+int cost_benefit __P((FS_INFO *, SEGUSE *));
+int cost_compare __P((const void *, const void *));
+void sig_report __P((int));
+
+/*
+ * Cleaning Cost Functions:
+ *
+ * These return the cost of cleaning a segment. The higher the cost value
+ * the better it is to clean the segment, so empty segments have the highest
+ * cost. (It is probably better to think of this as a priority value
+ * instead).
+ *
+ * This is the cost-benefit policy simulated and described in Rosenblum's
+ * 1991 SOSP paper.
+ */
+
+int
+cost_benefit(fsp, su)
+ FS_INFO *fsp; /* file system information */
+ SEGUSE *su;
+{
+ struct lfs *lfsp;
+ struct timeval t;
+ int age;
+ int live;
+
+ gettimeofday(&t, NULL);
+
+ live = su->su_nbytes;
+ age = t.tv_sec < su->su_lastmod ? 0 : t.tv_sec - su->su_lastmod;
+
+ lfsp = &fsp->fi_lfs;
+ if (live == 0)
+ return (t.tv_sec * lblkno(lfsp, seg_size(lfsp)));
+ else {
+ /*
+ * from lfsSegUsage.c (Mendel's code).
+ * priority calculation is done using INTEGER arithmetic.
+ * sizes are in BLOCKS (that is why we use lblkno below).
+ * age is in seconds.
+ *
+ * priority = ((seg_size - live) * age) / (seg_size + live)
+ */
+#ifdef VERBOSE
+ if (live < 0 || live > seg_size(lfsp)) {
+ err(0, "Bad segusage count: %d", live);
+ live = 0;
+ }
+#endif
+ return (lblkno(lfsp, seg_size(lfsp) - live) * age)
+ / lblkno(lfsp, seg_size(lfsp) + live);
+ }
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FS_INFO *fsp;
+ struct statfs *lstatfsp; /* file system stats */
+ struct timeval timeout; /* sleep timeout */
+ fsid_t fsid;
+ int i, nodaemon;
+ int opt, cmd_err;
+ char *fs_name; /* name of filesystem to clean */
+ extern int optind;
+
+ cmd_err = nodaemon = 0;
+ while ((opt = getopt(argc, argv, "smd")) != EOF) {
+ switch (opt) {
+ case 's': /* small writes */
+ do_small = 1;
+ break;
+ case 'm':
+ do_mmap = 1;
+ break;
+ case 'd':
+ nodaemon = 1;
+ break;
+ default:
+ ++cmd_err;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (cmd_err || (argc != 1))
+ err(1, "usage: lfs_cleanerd [-smd] fs_name");
+
+ fs_name = argv[0];
+
+ signal(SIGINT, sig_report);
+ signal(SIGUSR1, sig_report);
+ signal(SIGUSR2, sig_report);
+ if (fs_getmntinfo(&lstatfsp, fs_name, MOUNT_LFS) == 0) {
+ /* didn't find the filesystem */
+ err(1, "lfs_cleanerd: filesystem %s isn't an LFS!", fs_name);
+ }
+
+ if (!nodaemon) /* should we become a daemon, chdir to / & close fd's */
+ if (daemon(0, 0) == -1)
+ err(1, "lfs_cleanerd: couldn't become a daemon!");
+
+ timeout.tv_sec = 5*60; /* five minutes */
+ timeout.tv_usec = 0;
+ fsid.val[0] = 0;
+ fsid.val[1] = 0;
+
+ for (fsp = get_fs_info(lstatfsp, do_mmap); ;
+ reread_fs_info(fsp, do_mmap)) {
+ /*
+ * clean the filesystem, and, if it needed cleaning
+ * (i.e. it returned nonzero) try it again
+ * to make sure that some nasty process hasn't just
+ * filled the disk system up.
+ */
+ if (clean_loop(fsp))
+ continue;
+
+#ifdef VERBOSE
+ (void)printf("Cleaner going to sleep.\n");
+#endif
+ if (lfs_segwait(&fsid, &timeout) < 0)
+ err(0, "lfs_segwait: returned error\n");
+#ifdef VERBOSE
+ (void)printf("Cleaner waking up.\n");
+#endif
+ }
+}
+
+/* return the number of segments cleaned */
+int
+clean_loop(fsp)
+ FS_INFO *fsp; /* file system information */
+{
+ double loadavg[MAXLOADS];
+ time_t now;
+ u_long max_free_segs;
+
+ /*
+ * Compute the maximum possible number of free segments, given the
+ * number of free blocks.
+ */
+ max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize;
+
+ /*
+ * We will clean if there are not enough free blocks or total clean
+ * space is less than BUSY_LIM % of possible clean space.
+ */
+ now = time((time_t *)NULL);
+ if (fsp->fi_cip->clean < max_free_segs &&
+ (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) ||
+ fsp->fi_cip->clean < max_free_segs * BUSY_LIM)) {
+ printf("Cleaner Running at %s (%d of %d segments available)\n",
+ ctime(&now), fsp->fi_cip->clean, max_free_segs);
+ clean_fs(fsp, cost_benefit);
+ return (1);
+ } else {
+ /*
+ * We will also clean if the system is reasonably idle and
+ * the total clean space is less then IDLE_LIM % of possible
+ * clean space.
+ */
+ if (getloadavg(loadavg, MAXLOADS) == -1) {
+ perror("getloadavg: failed\n");
+ return (-1);
+ }
+ if (loadavg[ONE_MIN] == 0.2 && loadavg[FIVE_MIN] &&
+ fsp->fi_cip->clean < max_free_segs * IDLE_LIM) {
+ clean_fs(fsp, cost_benefit);
+ printf("Cleaner running (system idle) at %s",
+ ctime(&now));
+ return (1);
+ }
+ }
+#ifdef VERBOSE
+ printf("Cleaner not running at %s", ctime(&now));
+#endif
+ return (0);
+}
+
+
+void
+clean_fs(fsp, cost_func)
+ FS_INFO *fsp; /* file system information */
+ int (*cost_func) __P((FS_INFO *, SEGUSE *));
+{
+ struct seglist *segs, *sp;
+ int i;
+
+ if ((segs =
+ malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist))) == NULL) {
+ err(0, "malloc failed");
+ return;
+ }
+ i = choose_segments(fsp, segs, cost_func);
+#ifdef VERBOSE
+ printf("clean_fs: found %d segments to clean in file system %s\n",
+ i, fsp->fi_statfsp->f_mntonname);
+ fflush(stdout);
+#endif
+ if (i)
+ for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp) {
+ if (clean_segment(fsp, sp->sl_id) < 0)
+ perror("clean_segment failed");
+ else if (lfs_segclean(&fsp->fi_statfsp->f_fsid,
+ sp->sl_id) < 0)
+ perror("lfs_segclean failed");
+ printf("Completed cleaning segment %d\n", sp->sl_id);
+ }
+ free(segs);
+}
+
+/*
+ * Segment with the highest priority get sorted to the beginning of the
+ * list. This sort assumes that empty segments always have a higher
+ * cost/benefit than any utilized segment.
+ */
+int
+cost_compare(a, b)
+ const void *a;
+ const void *b;
+{
+ return (((struct seglist *)b)->sl_cost -
+ ((struct seglist *)a)->sl_cost);
+}
+
+
+/*
+ * Returns the number of segments to be cleaned with the elements of seglist
+ * filled in.
+ */
+int
+choose_segments(fsp, seglist, cost_func)
+ FS_INFO *fsp;
+ struct seglist *seglist;
+ int (*cost_func) __P((FS_INFO *, SEGUSE *));
+{
+ struct lfs *lfsp;
+ struct seglist *sp;
+ SEGUSE *sup;
+ int i, nsegs;
+
+ lfsp = &fsp->fi_lfs;
+
+#ifdef VERBOSE
+ (void)printf("Entering choose_segments\n");
+#endif
+ dump_super(lfsp);
+ dump_cleaner_info(fsp->fi_cip);
+
+ for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) {
+ sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i);
+ PRINT_SEGUSE(sup, i);
+ if (!(sup->su_flags & SEGUSE_DIRTY) ||
+ sup->su_flags & SEGUSE_ACTIVE)
+ continue;
+#ifdef VERBOSE
+ (void)printf("\tchoosing segment %d\n", i);
+#endif
+ sp->sl_cost = (*cost_func)(fsp, sup);
+ sp->sl_id = i;
+ sp->sl_empty = sup->su_nbytes ? 0 : 1;
+ ++sp;
+ }
+ nsegs = sp - seglist;
+ qsort(seglist, nsegs, sizeof(struct seglist), cost_compare);
+#ifdef VERBOSE
+ (void)printf("Returning %d segments\n", nsegs);
+#endif
+ return (nsegs);
+}
+
+
+int
+clean_segment(fsp, id)
+ FS_INFO *fsp; /* file system information */
+ int id; /* segment number */
+{
+ BLOCK_INFO *block_array, *bp;
+ SEGUSE *sp;
+ struct lfs *lfsp;
+ struct tossstruct t;
+ caddr_t seg_buf;
+ int num_blocks, maxblocks, clean_blocks;
+
+ lfsp = &fsp->fi_lfs;
+ sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id);
+
+#ifdef VERBOSE
+ (void)printf("cleaning segment %d: contains %lu bytes\n", id,
+ sp->su_nbytes);
+ fflush(stdout);
+#endif
+ /* XXX could add debugging to verify that segment is really empty */
+ if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE) {
+ ++cleaner_stats.segs_empty;
+ return (0);
+ }
+
+ /* map the segment into a buffer */
+ if (mmap_segment(fsp, id, &seg_buf, do_mmap) < 0) {
+ err(0, "mmap_segment failed");
+ ++cleaner_stats.segs_error;
+ return (-1);
+ }
+ /* get a list of blocks that are contained by the segment */
+ if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks) < 0) {
+ err(0, "clean_segment: lfs_segmapv failed");
+ ++cleaner_stats.segs_error;
+ return (-1);
+ }
+ cleaner_stats.blocks_read += fsp->fi_lfs.lfs_ssize;
+
+#ifdef VERBOSE
+ (void)printf("lfs_segmapv returned %d blocks\n", num_blocks);
+ fflush(stdout);
+#endif
+
+ /* get the current disk address of blocks contained by the segment */
+ if (lfs_bmapv(&fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) {
+ perror("clean_segment: lfs_bmapv failed\n");
+ ++cleaner_stats.segs_error;
+ return -1;
+ }
+
+ /* Now toss any blocks not in the current segment */
+ t.lfs = lfsp;
+ t.seg = id;
+ toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t);
+
+ /* Check if last element should be tossed */
+ if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL))
+ --num_blocks;
+
+#ifdef VERBOSE
+ {
+ BLOCK_INFO *_bip;
+ u_long *lp;
+ int i;
+
+ (void)printf("after bmapv still have %d blocks\n", num_blocks);
+ fflush(stdout);
+ if (num_blocks)
+ printf("BLOCK INFOS\n");
+ for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) {
+ PRINT_BINFO(_bip);
+ lp = (u_long *)_bip->bi_bp;
+ }
+ }
+#endif
+ cleaner_stats.blocks_written += num_blocks;
+ if (do_small)
+ maxblocks = MAXPHYS / fsp->fi_lfs.lfs_bsize - 1;
+ else
+ maxblocks = num_blocks;
+
+ for (bp = block_array; num_blocks > 0; bp += clean_blocks) {
+ clean_blocks = maxblocks < num_blocks ? maxblocks : num_blocks;
+ if (lfs_markv(&fsp->fi_statfsp->f_fsid,
+ bp, clean_blocks) < 0) {
+ err(0, "clean_segment: lfs_markv failed");
+ ++cleaner_stats.segs_error;
+ return (-1);
+ }
+ num_blocks -= clean_blocks;
+ }
+
+ free(block_array);
+ munmap_segment(fsp, seg_buf, do_mmap);
+ ++cleaner_stats.segs_cleaned;
+ return (0);
+}
+
+
+int
+bi_tossold(client, a, b)
+ const void *client;
+ const void *a;
+ const void *b;
+{
+ const struct tossstruct *t;
+
+ t = (struct tossstruct *)client;
+
+ return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR ||
+ datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg);
+}
+
+void
+sig_report(sig)
+ int sig;
+{
+ printf("lfs_cleanerd:\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n",
+ "blocks_read ", cleaner_stats.blocks_read,
+ "blocks_written ", cleaner_stats.blocks_written,
+ "segs_cleaned ", cleaner_stats.segs_cleaned,
+ "segs_empty ", cleaner_stats.segs_empty,
+ "seg_error ", cleaner_stats.segs_error);
+ if (sig == SIGUSR2) {
+ cleaner_stats.blocks_read = 0;
+ cleaner_stats.blocks_written = 0;
+ cleaner_stats.segs_cleaned = 0;
+ cleaner_stats.segs_empty = 0;
+ cleaner_stats.segs_error = 0;
+ }
+ if (sig == SIGINT)
+ exit(0);
+}
diff --git a/libexec/lfs_cleanerd/lfs_cleanerd.8 b/libexec/lfs_cleanerd/lfs_cleanerd.8
new file mode 100644
index 0000000..3d134db
--- /dev/null
+++ b/libexec/lfs_cleanerd/lfs_cleanerd.8
@@ -0,0 +1,77 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lfs_cleanerd.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd "December 11, 1993"
+.Dt LFS_CLEANERD 8
+.Os BSD 4.4
+.Sh NAME
+.Nm lfs_cleanerd
+.Nd garbage collect a log-structured file system
+.Sh SYNOPSIS
+.Nm lfs_cleanerd
+.Op Fl ds
+.Pa node
+.Sh DESCRIPTION
+The
+.Nm lfs_cleanerd
+command starts a daemon process which garbage-collects
+the log-structured file system residing at the point named by
+.Ar node
+in the global file system namespace.
+This command is normally executed by
+.Xr mount_lfs 8
+when the log-structured file system is mounted.
+The daemon will exit within a few minutes
+of when the file system it was cleaning is unmounted.
+.Pp
+Garbage collection on a log-structured file system is done by scanning
+the file system's segments for active, i.e. referenced, data and copying
+it to new segments.
+When all of the active data in a given segment has been copied to a new
+segment that segment can be marked as empty, thus reclaiming the space
+taken by the inactive data which was in it.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Run in debug mode.
+Do not become a daemon process, and print debugging information.
+.It Fl s
+When cleaning the file system, read data in small chunks.
+.El
+.Sh SEE ALSO
+.Xr mount_lfs 8
+.Sh HISTORY
+The
+.Nm lfs_cleanerd
+utility first appeared in 4.4BSD.
diff --git a/libexec/lfs_cleanerd/library.c b/libexec/lfs_cleanerd/library.c
new file mode 100644
index 0000000..62f345a
--- /dev/null
+++ b/libexec/lfs_cleanerd/library.c
@@ -0,0 +1,671 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)library.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/lfs/lfs.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "clean.h"
+
+void add_blocks __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t,
+ daddr_t, daddr_t));
+void add_inodes __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t,
+ daddr_t));
+int bi_compare __P((const void *, const void *));
+int bi_toss __P((const void *, const void *, const void *));
+void get_ifile __P((FS_INFO *, int));
+int get_superblock __P((FS_INFO *, struct lfs *));
+int pseg_valid __P((FS_INFO *, SEGSUM *));
+
+/*
+ * This function will get information on a a filesystem which matches
+ * the name and type given. If a "name" is in a filesystem of the given
+ * type, then buf is filled with that filesystem's info, and the
+ * a non-zero value is returned.
+ */
+int
+fs_getmntinfo(buf, name, type)
+ struct statfs **buf;
+ char *name;
+ int type;
+{
+ /* allocate space for the filesystem info */
+ *buf = (struct statfs *)malloc(sizeof(struct statfs));
+ if (*buf == NULL)
+ return 0;
+
+ /* grab the filesystem info */
+ if (statfs(name, *buf) < 0) {
+ free(*buf);
+ return 0;
+ }
+
+ /* check to see if it's the one we want */
+ if (((*buf)->f_type != type) ||
+ strncmp(name, (*buf)->f_mntonname, MNAMELEN)) {
+ /* "this is not the filesystem you're looking for" */
+ free(*buf);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Get all the information available on an LFS file system.
+ * Returns an pointer to an FS_INFO structure, NULL on error.
+ */
+FS_INFO *
+get_fs_info (lstatfsp, use_mmap)
+ struct statfs *lstatfsp; /* IN: pointer to statfs struct */
+ int use_mmap; /* IN: mmap or read */
+{
+ FS_INFO *fsp;
+ int i;
+
+ fsp = (FS_INFO *)malloc(sizeof(FS_INFO));
+ if (fsp == NULL)
+ return NULL;
+ bzero(fsp, sizeof(FS_INFO));
+
+ fsp->fi_statfsp = lstatfsp;
+ if (get_superblock (fsp, &fsp->fi_lfs))
+ err(1, "get_fs_info: get_superblock failed");
+ fsp->fi_daddr_shift =
+ fsp->fi_lfs.lfs_bshift - fsp->fi_lfs.lfs_fsbtodb;
+ get_ifile (fsp, use_mmap);
+ return (fsp);
+}
+
+/*
+ * If we are reading the ifile then we need to refresh it. Even if
+ * we are mmapping it, it might have grown. Finally, we need to
+ * refresh the file system information (statfs) info.
+ */
+void
+reread_fs_info(fsp, use_mmap)
+ FS_INFO *fsp; /* IN: prointer fs_infos to reread */
+ int use_mmap;
+{
+ int i;
+
+ if (statfs(fsp->fi_statfsp->f_mntonname, fsp->fi_statfsp))
+ err(1, "reread_fs_info: statfs failed");
+ get_ifile (fsp, use_mmap);
+}
+
+/*
+ * Gets the superblock from disk (possibly in face of errors)
+ */
+int
+get_superblock (fsp, sbp)
+ FS_INFO *fsp; /* local file system info structure */
+ struct lfs *sbp;
+{
+ char mntfromname[MNAMELEN+1];
+ int fid;
+
+ strcpy(mntfromname, "/dev/r");
+ strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5);
+
+ if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) {
+ err(0, "get_superblock: bad open");
+ return (-1);
+ }
+
+ get(fid, LFS_LABELPAD, sbp, sizeof(struct lfs));
+ close (fid);
+
+ return (0);
+}
+
+/*
+ * This function will map the ifile into memory. It causes a
+ * fatal error on failure.
+ */
+void
+get_ifile (fsp, use_mmap)
+ FS_INFO *fsp;
+ int use_mmap;
+
+{
+ struct stat file_stat;
+ caddr_t ifp;
+ char *ifile_name;
+ int count, fid;
+
+ ifp = NULL;
+ ifile_name = malloc(strlen(fsp->fi_statfsp->f_mntonname) +
+ strlen(IFILE_NAME)+2);
+ strcat(strcat(strcpy(ifile_name, fsp->fi_statfsp->f_mntonname), "/"),
+ IFILE_NAME);
+
+ if ((fid = open(ifile_name, O_RDWR, (mode_t)0)) < 0)
+ err(1, "get_ifile: bad open");
+
+ if (fstat (fid, &file_stat))
+ err(1, "get_ifile: fstat failed");
+
+ if (use_mmap && file_stat.st_size == fsp->fi_ifile_length) {
+ (void) close(fid);
+ return;
+ }
+
+ /* get the ifile */
+ if (use_mmap) {
+ if (fsp->fi_cip)
+ munmap((caddr_t)fsp->fi_cip, fsp->fi_ifile_length);
+ ifp = mmap ((caddr_t)0, file_stat.st_size,
+ PROT_READ|PROT_WRITE, 0, fid, (off_t)0);
+ if (ifp == (caddr_t)(-1))
+ err(1, "get_ifile: mmap failed");
+ } else {
+ if (fsp->fi_cip)
+ free(fsp->fi_cip);
+ if (!(ifp = malloc (file_stat.st_size)))
+ err (1, "get_ifile: malloc failed");
+redo_read:
+ count = read (fid, ifp, (size_t) file_stat.st_size);
+
+ if (count < 0)
+ err(1, "get_ifile: bad ifile read");
+ else if (count < file_stat.st_size) {
+ err(0, "get_ifile");
+ if (lseek(fid, 0, SEEK_SET) < 0)
+ err(1, "get_ifile: bad ifile lseek");
+ goto redo_read;
+ }
+ }
+ fsp->fi_ifile_length = file_stat.st_size;
+ close (fid);
+
+ fsp->fi_cip = (CLEANERINFO *)ifp;
+ fsp->fi_segusep = (SEGUSE *)(ifp + CLEANSIZE(fsp));
+ fsp->fi_ifilep = (IFILE *)((caddr_t)fsp->fi_segusep + SEGTABSIZE(fsp));
+
+ /*
+ * The number of ifile entries is equal to the number of blocks
+ * blocks in the ifile minus the ones allocated to cleaner info
+ * and segment usage table multiplied by the number of ifile
+ * entries per page.
+ */
+ fsp->fi_ifile_count = (fsp->fi_ifile_length >> fsp->fi_lfs.lfs_bshift -
+ fsp->fi_lfs.lfs_cleansz - fsp->fi_lfs.lfs_segtabsz) *
+ fsp->fi_lfs.lfs_ifpb;
+
+ free (ifile_name);
+}
+
+/*
+ * This function will scan a segment and return a list of
+ * <inode, blocknum> pairs which indicate which blocks were
+ * contained as live data within the segment when the segment
+ * summary was read (it may have "died" since then). Any given
+ * pair will be listed at most once.
+ */
+int
+lfs_segmapv(fsp, seg, seg_buf, blocks, bcount)
+ FS_INFO *fsp; /* pointer to local file system information */
+ int seg; /* the segment number */
+ caddr_t seg_buf; /* the buffer containing the segment's data */
+ BLOCK_INFO **blocks; /* OUT: array of block_info for live blocks */
+ int *bcount; /* OUT: number of active blocks in segment */
+{
+ BLOCK_INFO *bip;
+ SEGSUM *sp;
+ SEGUSE *sup;
+ FINFO *fip;
+ struct lfs *lfsp;
+ caddr_t s, segend;
+ daddr_t pseg_addr, seg_addr;
+ int i, nelem, nblocks, sumsize;
+ time_t timestamp;
+
+ lfsp = &fsp->fi_lfs;
+ nelem = 2 * lfsp->lfs_ssize;
+ if (!(bip = malloc(nelem * sizeof(BLOCK_INFO))))
+ goto err0;
+
+ sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, seg);
+ s = seg_buf + (sup->su_flags & SEGUSE_SUPERBLOCK ? LFS_SBPAD : 0);
+ seg_addr = sntoda(lfsp, seg);
+ pseg_addr = seg_addr + (sup->su_flags & SEGUSE_SUPERBLOCK ? btodb(LFS_SBPAD) : 0);
+#ifdef VERBOSE
+ printf("\tsegment buffer at: 0x%x\tseg_addr 0x%x\n", s, seg_addr);
+#endif /* VERBOSE */
+
+ *bcount = 0;
+ for (segend = seg_buf + seg_size(lfsp), timestamp = 0; s < segend; ) {
+ sp = (SEGSUM *)s;
+
+#ifdef VERBOSE
+ printf("\tpartial at: 0x%x\n", pseg_addr);
+ print_SEGSUM(lfsp, sp);
+ fflush(stdout);
+#endif /* VERBOSE */
+
+ nblocks = pseg_valid(fsp, sp);
+ if (nblocks <= 0)
+ break;
+
+ /* Check if we have hit old data */
+ if (timestamp > ((SEGSUM*)s)->ss_create)
+ break;
+ timestamp = ((SEGSUM*)s)->ss_create;
+
+#ifdef DIAGNOSTIC
+ /* Verify size of summary block */
+ sumsize = sizeof(SEGSUM) +
+ (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp);
+ for (fip = (FINFO *)(sp + 1); i < sp->ss_nfinfo; ++i) {
+ sumsize += sizeof(FINFO) +
+ (fip->fi_nblocks - 1) * sizeof(daddr_t);
+ fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks]);
+ }
+ if (sumsize > LFS_SUMMARY_SIZE) {
+ fprintf(stderr,
+ "Segment %d summary block too big: %d\n",
+ seg, sumsize);
+ exit(1);
+ }
+#endif
+
+ if (*bcount + nblocks + sp->ss_ninos > nelem) {
+ nelem = *bcount + nblocks + sp->ss_ninos;
+ bip = realloc (bip, nelem * sizeof(BLOCK_INFO));
+ if (!bip)
+ goto err0;
+ }
+ add_blocks(fsp, bip, bcount, sp, seg_buf, seg_addr, pseg_addr);
+ add_inodes(fsp, bip, bcount, sp, seg_buf, seg_addr);
+ pseg_addr += fsbtodb(lfsp, nblocks) +
+ bytetoda(fsp, LFS_SUMMARY_SIZE);
+ s += (nblocks << lfsp->lfs_bshift) + LFS_SUMMARY_SIZE;
+ }
+ qsort(bip, *bcount, sizeof(BLOCK_INFO), bi_compare);
+ toss(bip, bcount, sizeof(BLOCK_INFO), bi_toss, NULL);
+#ifdef VERBOSE
+ {
+ BLOCK_INFO *_bip;
+ int i;
+
+ printf("BLOCK INFOS\n");
+ for (_bip = bip, i=0; i < *bcount; ++_bip, ++i)
+ PRINT_BINFO(_bip);
+ }
+#endif
+ *blocks = bip;
+ return (0);
+
+err0: *bcount = 0;
+ return (-1);
+
+}
+
+/*
+ * This will parse a partial segment and fill in BLOCK_INFO structures
+ * for each block described in the segment summary. It will not include
+ * blocks or inodes from files with new version numbers.
+ */
+void
+add_blocks (fsp, bip, countp, sp, seg_buf, segaddr, psegaddr)
+ FS_INFO *fsp; /* pointer to super block */
+ BLOCK_INFO *bip; /* Block info array */
+ int *countp; /* IN/OUT: number of blocks in array */
+ SEGSUM *sp; /* segment summmary pointer */
+ caddr_t seg_buf; /* buffer containing segment */
+ daddr_t segaddr; /* address of this segment */
+ daddr_t psegaddr; /* address of this partial segment */
+{
+ IFILE *ifp;
+ FINFO *fip;
+ caddr_t bp;
+ daddr_t *dp, *iaddrp;
+ int db_per_block, i, j;
+ u_long page_size;
+
+#ifdef VERBOSE
+ printf("FILE INFOS\n");
+#endif
+ db_per_block = fsbtodb(&fsp->fi_lfs, 1);
+ page_size = fsp->fi_lfs.lfs_bsize;
+ bp = seg_buf + datobyte(fsp, psegaddr - segaddr) + LFS_SUMMARY_SIZE;
+ bip += *countp;
+ psegaddr += bytetoda(fsp, LFS_SUMMARY_SIZE);
+ iaddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE);
+ --iaddrp;
+ for (fip = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo;
+ ++i, fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks])) {
+
+ ifp = IFILE_ENTRY(&fsp->fi_lfs, fsp->fi_ifilep, fip->fi_ino);
+ PRINT_FINFO(fip, ifp);
+ if (ifp->if_version > fip->fi_version)
+ continue;
+ dp = &(fip->fi_blocks[0]);
+ for (j = 0; j < fip->fi_nblocks; j++, dp++) {
+ while (psegaddr == *iaddrp) {
+ psegaddr += db_per_block;
+ bp += page_size;
+ --iaddrp;
+ }
+ bip->bi_inode = fip->fi_ino;
+ bip->bi_lbn = *dp;
+ bip->bi_daddr = psegaddr;
+ bip->bi_segcreate = (time_t)(sp->ss_create);
+ bip->bi_bp = bp;
+ bip->bi_version = ifp->if_version;
+ psegaddr += db_per_block;
+ bp += page_size;
+ ++bip;
+ ++(*countp);
+ }
+ }
+}
+
+/*
+ * For a particular segment summary, reads the inode blocks and adds
+ * INODE_INFO structures to the array. Returns the number of inodes
+ * actually added.
+ */
+void
+add_inodes (fsp, bip, countp, sp, seg_buf, seg_addr)
+ FS_INFO *fsp; /* pointer to super block */
+ BLOCK_INFO *bip; /* block info array */
+ int *countp; /* pointer to current number of inodes */
+ SEGSUM *sp; /* segsum pointer */
+ caddr_t seg_buf; /* the buffer containing the segment's data */
+ daddr_t seg_addr; /* disk address of seg_buf */
+{
+ struct dinode *di;
+ struct lfs *lfsp;
+ IFILE *ifp;
+ BLOCK_INFO *bp;
+ daddr_t *daddrp;
+ ino_t inum;
+ int i;
+
+ if (sp->ss_ninos <= 0)
+ return;
+
+ bp = bip + *countp;
+ lfsp = &fsp->fi_lfs;
+#ifdef VERBOSE
+ (void) printf("INODES:\n");
+#endif
+ daddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE);
+ for (i = 0; i < sp->ss_ninos; ++i) {
+ if (i % INOPB(lfsp) == 0) {
+ --daddrp;
+ di = (struct dinode *)(seg_buf +
+ ((*daddrp - seg_addr) << fsp->fi_daddr_shift));
+ } else
+ ++di;
+
+ inum = di->di_inumber;
+ bp->bi_lbn = LFS_UNUSED_LBN;
+ bp->bi_inode = inum;
+ bp->bi_daddr = *daddrp;
+ bp->bi_bp = di;
+ bp->bi_segcreate = sp->ss_create;
+
+ if (inum == LFS_IFILE_INUM) {
+ bp->bi_version = 1; /* Ifile version should be 1 */
+ bp++;
+ ++(*countp);
+ PRINT_INODE(1, bp);
+ } else {
+ ifp = IFILE_ENTRY(lfsp, fsp->fi_ifilep, inum);
+ PRINT_INODE(ifp->if_daddr == *daddrp, bp);
+ bp->bi_version = ifp->if_version;
+ if (ifp->if_daddr == *daddrp) {
+ bp++;
+ ++(*countp);
+ }
+ }
+ }
+}
+
+/*
+ * Checks the summary checksum and the data checksum to determine if the
+ * segment is valid or not. Returns the size of the partial segment if it
+ * is valid, * and 0 otherwise. Use dump_summary to figure out size of the
+ * the partial as well as whether or not the checksum is valid.
+ */
+int
+pseg_valid (fsp, ssp)
+ FS_INFO *fsp; /* pointer to file system info */
+ SEGSUM *ssp; /* pointer to segment summary block */
+{
+ caddr_t p;
+ int i, nblocks;
+ u_long *datap;
+
+ if ((nblocks = dump_summary(&fsp->fi_lfs, ssp, 0, NULL)) <= 0 ||
+ nblocks > fsp->fi_lfs.lfs_ssize - 1)
+ return(0);
+
+ /* check data/inode block(s) checksum too */
+ datap = (u_long *)malloc(nblocks * sizeof(u_long));
+ p = (caddr_t)ssp + LFS_SUMMARY_SIZE;
+ for (i = 0; i < nblocks; ++i) {
+ datap[i] = *((u_long *)p);
+ p += fsp->fi_lfs.lfs_bsize;
+ }
+ if (cksum ((void *)datap, nblocks * sizeof(u_long)) != ssp->ss_datasum)
+ return (0);
+
+ return (nblocks);
+}
+
+
+/* #define MMAP_SEGMENT */
+/*
+ * read a segment into a memory buffer
+ */
+int
+mmap_segment (fsp, segment, segbuf, use_mmap)
+ FS_INFO *fsp; /* file system information */
+ int segment; /* segment number */
+ caddr_t *segbuf; /* pointer to buffer area */
+ int use_mmap; /* mmap instead of read */
+{
+ struct lfs *lfsp;
+ int fid; /* fildes for file system device */
+ daddr_t seg_daddr; /* base disk address of segment */
+ off_t seg_byte;
+ size_t ssize;
+ char mntfromname[MNAMELEN+2];
+
+ lfsp = &fsp->fi_lfs;
+
+ /* get the disk address of the beginning of the segment */
+ seg_daddr = sntoda(lfsp, segment);
+ seg_byte = datobyte(fsp, seg_daddr);
+ ssize = seg_size(lfsp);
+
+ strcpy(mntfromname, "/dev/r");
+ strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5);
+
+ if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) {
+ err(0, "mmap_segment: bad open");
+ return (-1);
+ }
+
+ if (use_mmap) {
+ *segbuf = mmap ((caddr_t)0, seg_size(lfsp), PROT_READ,
+ 0, fid, seg_byte);
+ if (*(long *)segbuf < 0) {
+ err(0, "mmap_segment: mmap failed");
+ return (NULL);
+ }
+ } else {
+#ifdef VERBOSE
+ printf("mmap_segment\tseg_daddr: %lu\tseg_size: %lu\tseg_offset: %qu\n",
+ seg_daddr, ssize, seg_byte);
+#endif
+ /* malloc the space for the buffer */
+ *segbuf = malloc(ssize);
+ if (!*segbuf) {
+ err(0, "mmap_segment: malloc failed");
+ return(NULL);
+ }
+
+ /* read the segment data into the buffer */
+ if (lseek (fid, seg_byte, SEEK_SET) != seg_byte) {
+ err (0, "mmap_segment: bad lseek");
+ free(*segbuf);
+ return (-1);
+ }
+
+ if (read (fid, *segbuf, ssize) != ssize) {
+ err (0, "mmap_segment: bad read");
+ free(*segbuf);
+ return (-1);
+ }
+ }
+ close (fid);
+
+ return (0);
+}
+
+void
+munmap_segment (fsp, seg_buf, use_mmap)
+ FS_INFO *fsp; /* file system information */
+ caddr_t seg_buf; /* pointer to buffer area */
+ int use_mmap; /* mmap instead of read/write */
+{
+ if (use_mmap)
+ munmap (seg_buf, seg_size(&fsp->fi_lfs));
+ else
+ free (seg_buf);
+}
+
+
+/*
+ * USEFUL DEBUGGING TOOLS:
+ */
+void
+print_SEGSUM (lfsp, p)
+ struct lfs *lfsp;
+ SEGSUM *p;
+{
+ if (p)
+ (void) dump_summary(lfsp, p, DUMP_ALL, NULL);
+ else printf("0x0");
+ fflush(stdout);
+}
+
+int
+bi_compare(a, b)
+ const void *a;
+ const void *b;
+{
+ const BLOCK_INFO *ba, *bb;
+ int diff;
+
+ ba = a;
+ bb = b;
+
+ if (diff = (int)(ba->bi_inode - bb->bi_inode))
+ return (diff);
+ if (diff = (int)(ba->bi_lbn - bb->bi_lbn)) {
+ if (ba->bi_lbn == LFS_UNUSED_LBN)
+ return(-1);
+ else if (bb->bi_lbn == LFS_UNUSED_LBN)
+ return(1);
+ else if (ba->bi_lbn < 0 && bb->bi_lbn >= 0)
+ return(1);
+ else if (bb->bi_lbn < 0 && ba->bi_lbn >= 0)
+ return(-1);
+ else
+ return (diff);
+ }
+ if (diff = (int)(ba->bi_segcreate - bb->bi_segcreate))
+ return (diff);
+ diff = (int)(ba->bi_daddr - bb->bi_daddr);
+ return (diff);
+}
+
+int
+bi_toss(dummy, a, b)
+ const void *dummy;
+ const void *a;
+ const void *b;
+{
+ const BLOCK_INFO *ba, *bb;
+
+ ba = a;
+ bb = b;
+
+ return(ba->bi_inode == bb->bi_inode && ba->bi_lbn == bb->bi_lbn);
+}
+
+void
+toss(p, nump, size, dotoss, client)
+ void *p;
+ int *nump;
+ size_t size;
+ int (*dotoss) __P((const void *, const void *, const void *));
+ void *client;
+{
+ int i;
+ void *p1;
+
+ if (*nump == 0)
+ return;
+
+ for (i = *nump; --i > 0;) {
+ p1 = p + size;
+ if (dotoss(client, p, p1)) {
+ memmove(p, p1, i * size);
+ --(*nump);
+ } else
+ p += size;
+ }
+}
diff --git a/libexec/lfs_cleanerd/misc.c b/libexec/lfs_cleanerd/misc.c
new file mode 100644
index 0000000..ad6e11a
--- /dev/null
+++ b/libexec/lfs_cleanerd/misc.c
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+extern char *special;
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const int fatal, const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "%s: ", special);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (errno)
+ (void)fprintf(stderr, " %s", strerror(errno));
+ (void)fprintf(stderr, "\n");
+ if (fatal)
+ exit(1);
+}
+
+void
+get(fd, off, p, len)
+ int fd;
+ off_t off;
+ void *p;
+ size_t len;
+{
+ int rbytes;
+
+ if (lseek(fd, off, SEEK_SET) < 0)
+ err(1, "%s: %s", special, strerror(errno));
+ if ((rbytes = read(fd, p, len)) < 0)
+ err(1, "%s: %s", special, strerror(errno));
+ if (rbytes != len)
+ err(1, "%s: short read (%d, not %d)", special, rbytes, len);
+}
diff --git a/libexec/lfs_cleanerd/print.c b/libexec/lfs_cleanerd/print.c
new file mode 100644
index 0000000..64320c5
--- /dev/null
+++ b/libexec/lfs_cleanerd/print.c
@@ -0,0 +1,218 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/lfs/lfs.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "clean.h"
+
+/*
+ * Print out a summary block; return number of blocks in segment; 0
+ * for empty segment or corrupt segment.
+ * Returns a pointer to the array of inode addresses.
+ */
+int
+dump_summary(lfsp, sp, flags, iaddrp)
+ struct lfs *lfsp;
+ SEGSUM *sp;
+ u_long flags;
+ daddr_t **iaddrp;
+{
+ int i, j, numblocks;
+ daddr_t *dp;
+
+ FINFO *fp;
+ int ck;
+
+ if (sp->ss_sumsum != (ck = cksum(&sp->ss_datasum,
+ LFS_SUMMARY_SIZE - sizeof(sp->ss_sumsum))))
+ return(-1);
+
+ if (flags & DUMP_SUM_HEADER) {
+ (void)printf(" %s0x%X\t%s%d\t%s%d\n %s0x%X\t%s0x%X",
+ "next ", sp->ss_next,
+ "nfinfo ", sp->ss_nfinfo,
+ "ninos ", sp->ss_ninos,
+ "sumsum ", sp->ss_sumsum,
+ "datasum ", sp->ss_datasum );
+ (void)printf("\tcreate %s", ctime((time_t *)&sp->ss_create));
+ }
+
+ numblocks = (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp);
+
+ /* Dump out inode disk addresses */
+ if (flags & DUMP_INODE_ADDRS)
+ printf(" Inode addresses:");
+
+ dp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE);
+ for (--dp, i = 0; i < sp->ss_ninos; --dp)
+ if (flags & DUMP_INODE_ADDRS) {
+ (void)printf("\t0x%lx", *dp);
+ if (++i % 7 == 0)
+ (void)printf("\n");
+ } else
+ ++i;
+ if (iaddrp)
+ *iaddrp = ++dp;
+ if (flags & DUMP_INODE_ADDRS)
+ printf("\n");
+
+ for (fp = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo; ++i) {
+ numblocks += fp->fi_nblocks;
+ if (flags & DUMP_FINFOS) {
+ (void)printf(" %s%d version %d nblocks %d\n",
+ "FINFO for inode: ", fp->fi_ino,
+ fp->fi_version, fp->fi_nblocks);
+ dp = &(fp->fi_blocks[0]);
+ for (j = 0; j < fp->fi_nblocks; j++, dp++) {
+ (void)printf("\t%d", *dp);
+ if ((j % 8) == 7)
+ (void)printf("\n");
+ }
+ if ((j % 8) != 0)
+ (void)printf("\n");
+ fp = (FINFO *)dp;
+ } else {
+ fp = (FINFO *)(&fp->fi_blocks[fp->fi_nblocks]);
+ }
+ }
+ return (numblocks);
+}
+
+#ifdef VERBOSE
+void
+dump_cleaner_info(ipage)
+ void *ipage;
+{
+ CLEANERINFO *cip;
+
+ cip = (CLEANERINFO *)ipage;
+ (void)printf("segments clean\t%d\tsegments dirty\t%d\n\n",
+ cip->clean, cip->dirty);
+}
+
+void
+dump_super(lfsp)
+ struct lfs *lfsp;
+{
+ int i;
+
+ (void)printf("%s0x%X\t%s0x%X\t%s%d\t%s%d\n",
+ "magic ", lfsp->lfs_magic,
+ "version ", lfsp->lfs_version,
+ "size ", lfsp->lfs_size,
+ "ssize ", lfsp->lfs_ssize);
+ (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n",
+ "dsize ", lfsp->lfs_dsize,
+ "bsize ", lfsp->lfs_bsize,
+ "fsize ", lfsp->lfs_fsize,
+ "frag ", lfsp->lfs_frag);
+
+ (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n",
+ "minfree ", lfsp->lfs_minfree,
+ "inopb ", lfsp->lfs_inopb,
+ "ifpb ", lfsp->lfs_ifpb,
+ "nindir ", lfsp->lfs_nindir);
+
+ (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n",
+ "nseg ", lfsp->lfs_nseg,
+ "nspf ", lfsp->lfs_nspf,
+ "cleansz ", lfsp->lfs_cleansz,
+ "segtabsz ", lfsp->lfs_segtabsz);
+
+ (void)printf("%s0x%X\t%s%d\t%s0x%X\t%s%d\n",
+ "segmask ", lfsp->lfs_segmask,
+ "segshift ", lfsp->lfs_segshift,
+ "bmask ", lfsp->lfs_bmask,
+ "bshift ", lfsp->lfs_bshift);
+
+ (void)printf("%s0x%X\t\t%s%d\t%s0x%X\t%s%d\n",
+ "ffmask ", lfsp->lfs_ffmask,
+ "ffshift ", lfsp->lfs_ffshift,
+ "fbmask ", lfsp->lfs_fbmask,
+ "fbshift ", lfsp->lfs_fbshift);
+
+ (void)printf("%s%d\t\t%s0x%X\t%s0x%qx\n",
+ "fsbtodb ", lfsp->lfs_fsbtodb,
+ "cksum ", lfsp->lfs_cksum,
+ "maxfilesize ", lfsp->lfs_maxfilesize);
+
+ (void)printf("Superblock disk addresses:\t");
+ for (i = 0; i < LFS_MAXNUMSB; i++) {
+ (void)printf(" 0x%X", lfsp->lfs_sboffs[i]);
+ if ( i == (LFS_MAXNUMSB >> 1))
+ (void)printf("\n\t\t\t\t");
+ }
+ (void)printf("\n");
+
+ (void)printf("Checkpoint Info\n");
+ (void)printf("%s%d\t%s0x%X\t%s%d\n",
+ "free ", lfsp->lfs_free,
+ "idaddr ", lfsp->lfs_idaddr,
+ "ifile ", lfsp->lfs_ifile);
+ (void)printf("%s%d\t%s%d\t%s%d\n",
+ "bfree ", lfsp->lfs_bfree,
+ "avail ", lfsp->lfs_avail,
+ "uinodes ", lfsp->lfs_uinodes);
+ (void)printf("%s%d\t%s0x%X\t%s0x%X\n%s0x%X\t%s0x%X\t",
+ "nfiles ", lfsp->lfs_nfiles,
+ "lastseg ", lfsp->lfs_lastseg,
+ "nextseg ", lfsp->lfs_nextseg,
+ "curseg ", lfsp->lfs_curseg,
+ "offset ", lfsp->lfs_offset);
+ (void)printf("tstamp %s", ctime((time_t *)&lfsp->lfs_tstamp));
+ (void)printf("\nIn-Memory Information\n");
+ (void)printf("%s%d\t%s0x%X\t%s%d\t%s%d\t%s%d\n",
+ "seglock ", lfsp->lfs_seglock,
+ "iocount ", lfsp->lfs_iocount,
+ "writer ", lfsp->lfs_writer,
+ "dirops ", lfsp->lfs_dirops,
+ "doifile ", lfsp->lfs_doifile );
+ (void)printf("%s%d\t%s%d\t%s0x%X\t%s%d\n",
+ "nactive ", lfsp->lfs_nactive,
+ "fmod ", lfsp->lfs_fmod,
+ "clean ", lfsp->lfs_clean,
+ "ronly ", lfsp->lfs_ronly);
+}
+#endif /* VERBOSE */
diff --git a/libexec/mail.local/Makefile b/libexec/mail.local/Makefile
new file mode 100644
index 0000000..10371e4
--- /dev/null
+++ b/libexec/mail.local/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+
+PROG= mail.local
+MAN8= mail.local.8
+.if defined(DONT_FSYNC)
+CFLAGS+= -DDONT_FSYNC
+.endif
+BINOWN= root
+BINMODE=4555
+INSTALLFLAGS=-fschg
+
+.include <bsd.prog.mk>
diff --git a/libexec/mail.local/mail.local.8 b/libexec/mail.local/mail.local.8
new file mode 100644
index 0000000..661615c
--- /dev/null
+++ b/libexec/mail.local/mail.local.8
@@ -0,0 +1,105 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mail.local.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt MAIL.LOCAL 8
+.Os
+.Sh NAME
+.Nm mail.local
+.Nd store mail in a mailbox
+.Sh SYNOPSIS
+.Nm mail.local
+.Op Fl f Ar from
+.Ar user ...
+.Sh DESCRIPTION
+.Nm Mail.local
+reads the standard input up to an end-of-file and appends it to each
+.Ar user's
+.Pa mail
+file.
+The
+.Ar user
+must be a valid user name.
+.Pp
+The options are as follows:
+.Bl -tag -width xxxfrom
+.It Fl f Ar from
+Specify the sender's name.
+.El
+.Pp
+Individual mail messages in the mailbox are delimited by an empty
+line followed by a line beginning with the string ``From ''.
+A line containing the string ``From '', the sender's name and a time stamp
+is prepended to each delivered mail message.
+A blank line is appended to each message.
+A greater-than character (``>'') is prepended to any line in the message
+which could be mistaken for a ``From '' delimiter line.
+.Pp
+The mail files are exclusively locked with
+.Xr flock 2
+while mail is appended.
+.Pp
+If the ``biff'' service is returned by
+.Xr getservbyname 3 ,
+the biff server is notified of delivered mail.
+.Pp
+The
+.Nm mail.local
+utility exits 0 on success, and >0 if an error occurs.
+.Sh ENVIRONMENT
+.Bl -tag -width indent
+.It Ev TZ
+Used to set the appropriate time zone on the timestamp.
+.El
+.Sh FILES
+.Bl -tag -width /tmp/local.XXXXXX -compact
+.It Pa /tmp/local.XXXXXX
+temporary files
+.It Pa /var/mail/user
+user's mailbox directory
+.El
+.Sh SEE ALSO
+.Xr mail 1 ,
+.Xr xsend 1 ,
+.Xr flock 2 ,
+.Xr getservbyname 3 ,
+.Xr comsat 8 ,
+.Xr sendmail 8
+.Sh HISTORY
+A superset of
+.Nm mail.local
+(handling mailbox reading as well as mail delivery)
+appeared in
+.At v7 .
+as the program
+.Nm mail .
diff --git a/libexec/mail.local/mail.local.c b/libexec/mail.local/mail.local.c
new file mode 100644
index 0000000..0d80972
--- /dev/null
+++ b/libexec/mail.local/mail.local.c
@@ -0,0 +1,513 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mail.local.c 8.6 (Berkeley) 4/8/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "pathnames.h"
+
+int eval = EX_OK; /* sysexits.h error value. */
+
+void deliver __P((int, char *));
+void e_to_sys __P((int));
+__dead void err __P((const char *, ...));
+void notifybiff __P((char *));
+int store __P((char *));
+void usage __P((void));
+void vwarn __P((const char *, _BSD_VA_LIST_));
+void warn __P((const char *, ...));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct passwd *pw;
+ int ch, fd;
+ uid_t uid;
+ char *from;
+
+ openlog("mail.local", 0, LOG_MAIL);
+
+ from = NULL;
+ while ((ch = getopt(argc, argv, "df:r:")) != EOF)
+ switch(ch) {
+ case 'd': /* Backward compatible. */
+ break;
+ case 'f':
+ case 'r': /* Backward compatible. */
+ if (from != NULL) {
+ warn("multiple -f options");
+ usage();
+ }
+ from = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!*argv)
+ usage();
+
+ /*
+ * If from not specified, use the name from getlogin() if the
+ * uid matches, otherwise, use the name from the password file
+ * corresponding to the uid.
+ */
+ uid = getuid();
+ if (!from && (!(from = getlogin()) ||
+ !(pw = getpwnam(from)) || pw->pw_uid != uid))
+ from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
+
+ /*
+ * There is no way to distinguish the error status of one delivery
+ * from the rest of the deliveries. So, if we failed hard on one
+ * or more deliveries, but had no failures on any of the others, we
+ * return a hard failure. If we failed temporarily on one or more
+ * deliveries, we return a temporary failure regardless of the other
+ * failures. This results in the delivery being reattempted later
+ * at the expense of repeated failures and multiple deliveries.
+ */
+ for (fd = store(from); *argv; ++argv)
+ deliver(fd, *argv);
+ exit(eval);
+}
+
+int
+store(from)
+ char *from;
+{
+ FILE *fp;
+ time_t tval;
+ int fd, eline;
+ char *tn, line[2048];
+
+ tn = strdup(_PATH_LOCTMP);
+ if ((fd = mkstemp(tn)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
+ e_to_sys(errno);
+ err("unable to open temporary file");
+ }
+ (void)unlink(tn);
+ free(tn);
+
+ (void)time(&tval);
+ (void)fprintf(fp, "From %s %s", from, ctime(&tval));
+
+ line[0] = '\0';
+ for (eline = 1; fgets(line, sizeof(line), stdin);) {
+ if (line[0] == '\n')
+ eline = 1;
+ else {
+ if (eline && line[0] == 'F' &&
+ !memcmp(line, "From ", 5))
+ (void)putc('>', fp);
+ eline = 0;
+ }
+ (void)fprintf(fp, "%s", line);
+ if (ferror(fp)) {
+ e_to_sys(errno);
+ err("temporary file write error");
+ }
+ }
+
+ /* If message not newline terminated, need an extra. */
+ if (!strchr(line, '\n'))
+ (void)putc('\n', fp);
+ /* Output a newline; note, empty messages are allowed. */
+ (void)putc('\n', fp);
+
+ if (fflush(fp) == EOF || ferror(fp)) {
+ e_to_sys(errno);
+ err("temporary file write error");
+ }
+ return (fd);
+}
+
+void
+deliver(fd, name)
+ int fd;
+ char *name;
+{
+ struct stat fsb, sb;
+ struct passwd *pw;
+ int mbfd, nr, nw, off;
+ char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
+ off_t curoff;
+
+ /*
+ * Disallow delivery to unknown names -- special mailboxes can be
+ * handled in the sendmail aliases file.
+ */
+ if (!(pw = getpwnam(name))) {
+ if (eval != EX_TEMPFAIL)
+ eval = EX_UNAVAILABLE;
+ warn("unknown name: %s", name);
+ return;
+ }
+
+ (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
+
+ /*
+ * If the mailbox is linked or a symlink, fail. There's an obvious
+ * race here, that the file was replaced with a symbolic link after
+ * the lstat returned, but before the open. We attempt to detect
+ * this by comparing the original stat information and information
+ * returned by an fstat of the file descriptor returned by the open.
+ *
+ * NB: this is a symptom of a larger problem, that the mail spooling
+ * directory is writeable by the wrong users. If that directory is
+ * writeable, system security is compromised for other reasons, and
+ * it cannot be fixed here.
+ *
+ * If we created the mailbox, set the owner/group. If that fails,
+ * just return. Another process may have already opened it, so we
+ * can't unlink it. Historically, binmail set the owner/group at
+ * each mail delivery. We no longer do this, assuming that if the
+ * ownership or permissions were changed there was a reason.
+ *
+ * XXX
+ * open(2) should support flock'ing the file.
+ */
+tryagain:
+ if (lstat(path, &sb)) {
+ mbfd = open(path,
+ O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
+ if (mbfd == -1) {
+ if (errno == EEXIST)
+ goto tryagain;
+ } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) {
+ e_to_sys(errno);
+ warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name);
+ return;
+ }
+ } else if (sb.st_nlink != 1 || S_ISLNK(sb.st_mode)) {
+ e_to_sys(errno);
+ warn("%s: linked file", path);
+ return;
+ } else {
+ mbfd = open(path, O_APPEND|O_WRONLY, 0);
+ if (mbfd != -1 &&
+ (fstat(mbfd, &fsb) || fsb.st_nlink != 1 ||
+ S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev ||
+ sb.st_ino != fsb.st_ino)) {
+ warn("%s: file changed after open", path);
+ (void)close(mbfd);
+ return;
+ }
+ }
+
+ if (mbfd == -1) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+ return;
+ }
+
+ /* Wait until we can get a lock on the file. */
+ if (flock(mbfd, LOCK_EX)) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+ goto err1;
+ }
+
+ /* Get the starting offset of the new message for biff. */
+ curoff = lseek(mbfd, (off_t)0, SEEK_END);
+ (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%qd\n", name, curoff);
+
+ /* Copy the message into the file. */
+ if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
+ e_to_sys(errno);
+ warn("temporary file: %s", strerror(errno));
+ goto err1;
+ }
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ for (off = 0; off < nr; nr -= nw, off += nw)
+ if ((nw = write(mbfd, buf + off, nr)) < 0) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+ goto err2;
+ }
+ if (nr < 0) {
+ e_to_sys(errno);
+ warn("temporary file: %s", strerror(errno));
+err2: (void)ftruncate(mbfd, curoff);
+err1: (void)close(mbfd);
+ return;
+ }
+
+#ifndef DONT_FSYNC
+ /* Flush to disk, don't wait for update. */
+ if (fsync(mbfd)) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+ goto err2;
+ }
+#endif
+
+ /* Close and check -- NFS doesn't write until the close. */
+ if (close(mbfd)) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+ return;
+ }
+
+ notifybiff(biffmsg);
+}
+
+void
+notifybiff(msg)
+ char *msg;
+{
+ static struct sockaddr_in addr;
+ static int f = -1;
+ struct hostent *hp;
+ struct servent *sp;
+ int len;
+
+ if (!addr.sin_family) {
+ /* Be silent if biff service not available. */
+ if (!(sp = getservbyname("biff", "udp")))
+ return;
+ if (!(hp = gethostbyname("localhost"))) {
+ warn("localhost: %s", strerror(errno));
+ return;
+ }
+ addr.sin_family = hp->h_addrtype;
+ memmove(&addr.sin_addr, hp->h_addr, hp->h_length);
+ addr.sin_port = sp->s_port;
+ }
+ if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ warn("socket: %s", strerror(errno));
+ return;
+ }
+ len = strlen(msg) + 1;
+ if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
+ != len)
+ warn("sendto biff: %s", strerror(errno));
+}
+
+void
+usage()
+{
+ eval = EX_USAGE;
+ err("usage: mail.local [-f from] user ...");
+}
+
+#if __STDC__
+void
+err(const char *fmt, ...)
+#else
+void
+err(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vwarn(fmt, ap);
+ va_end(ap);
+
+ exit(eval);
+}
+
+void
+#if __STDC__
+warn(const char *fmt, ...)
+#else
+warn(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vwarn(fmt, ap);
+ va_end(ap);
+}
+
+void
+vwarn(fmt, ap)
+ const char *fmt;
+ _BSD_VA_LIST_ ap;
+{
+ /*
+ * Log the message to stderr.
+ *
+ * Don't use LOG_PERROR as an openlog() flag to do this,
+ * it's not portable enough.
+ */
+ if (eval != EX_USAGE)
+ (void)fprintf(stderr, "mail.local: ");
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr, "\n");
+
+ /* Log the message to syslog. */
+ vsyslog(LOG_ERR, fmt, ap);
+}
+
+/*
+ * e_to_sys --
+ * Guess which errno's are temporary. Gag me.
+ */
+void
+e_to_sys(num)
+ int num;
+{
+ /* Temporary failures override hard errors. */
+ if (eval == EX_TEMPFAIL)
+ return;
+
+ switch(num) { /* Hopefully temporary errors. */
+#ifdef EAGAIN
+ case EAGAIN: /* Resource temporarily unavailable */
+#endif
+#ifdef EDQUOT
+ case EDQUOT: /* Disc quota exceeded */
+#endif
+#ifdef EBUSY
+ case EBUSY: /* Device busy */
+#endif
+#ifdef EPROCLIM
+ case EPROCLIM: /* Too many processes */
+#endif
+#ifdef EUSERS
+ case EUSERS: /* Too many users */
+#endif
+#ifdef ECONNABORTED
+ case ECONNABORTED: /* Software caused connection abort */
+#endif
+#ifdef ECONNREFUSED
+ case ECONNREFUSED: /* Connection refused */
+#endif
+#ifdef ECONNRESET
+ case ECONNRESET: /* Connection reset by peer */
+#endif
+#ifdef EDEADLK
+ case EDEADLK: /* Resource deadlock avoided */
+#endif
+#ifdef EFBIG
+ case EFBIG: /* File too large */
+#endif
+#ifdef EHOSTDOWN
+ case EHOSTDOWN: /* Host is down */
+#endif
+#ifdef EHOSTUNREACH
+ case EHOSTUNREACH: /* No route to host */
+#endif
+#ifdef EMFILE
+ case EMFILE: /* Too many open files */
+#endif
+#ifdef ENETDOWN
+ case ENETDOWN: /* Network is down */
+#endif
+#ifdef ENETRESET
+ case ENETRESET: /* Network dropped connection on reset */
+#endif
+#ifdef ENETUNREACH
+ case ENETUNREACH: /* Network is unreachable */
+#endif
+#ifdef ENFILE
+ case ENFILE: /* Too many open files in system */
+#endif
+#ifdef ENOBUFS
+ case ENOBUFS: /* No buffer space available */
+#endif
+#ifdef ENOMEM
+ case ENOMEM: /* Cannot allocate memory */
+#endif
+#ifdef ENOSPC
+ case ENOSPC: /* No space left on device */
+#endif
+#ifdef EROFS
+ case EROFS: /* Read-only file system */
+#endif
+#ifdef ESTALE
+ case ESTALE: /* Stale NFS file handle */
+#endif
+#ifdef ETIMEDOUT
+ case ETIMEDOUT: /* Connection timed out */
+#endif
+#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
+ case EWOULDBLOCK: /* Operation would block. */
+#endif
+ eval = EX_TEMPFAIL;
+ break;
+ default:
+ eval = EX_UNAVAILABLE;
+ break;
+ }
+}
diff --git a/libexec/mail.local/pathnames.h b/libexec/mail.local/pathnames.h
new file mode 100644
index 0000000..8e43925
--- /dev/null
+++ b/libexec/mail.local/pathnames.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ */
+#include <paths.h>
+
+#define _PATH_LOCTMP "/tmp/local.XXXXXX"
diff --git a/libexec/makekey/Makefile b/libexec/makekey/Makefile
new file mode 100644
index 0000000..47cc1c6
--- /dev/null
+++ b/libexec/makekey/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+
+PROG= makekey
+MAN8= makekey.8
+
+DPADD+= ${LIBCRYPT}
+LDADD+= -lcrypt
+
+.include <bsd.prog.mk>
diff --git a/libexec/makekey/makekey.8 b/libexec/makekey/makekey.8
new file mode 100644
index 0000000..c6ded2d
--- /dev/null
+++ b/libexec/makekey/makekey.8
@@ -0,0 +1,59 @@
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)makekey.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt MAKEKEY 8
+.Os
+.Sh NAME
+.Nm makekey
+.Nd make encrypted keys or passwords
+.Sh SYNOPSIS
+.Nm makekey
+.Sh DESCRIPTION
+.Nm Makekey
+encrypts a key and salt which it reads from the standard input
+and writes the result to the standard output.
+The key is expected to be
+ten bytes; the salt is expected to be two bytes.
+See
+.Xr crypt 3
+for more information on what characters the key and salt can contain
+and how the encrypted value is calculated.
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr crypt 1 ,
+.Xr crypt 3
+.Sh HISTORY
+A
+.Nm
+command appeared in Version 7 AT&T UNIX.
diff --git a/libexec/makekey/makekey.c b/libexec/makekey/makekey.c
new file mode 100644
index 0000000..95febcf
--- /dev/null
+++ b/libexec/makekey/makekey.c
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)makekey.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void get __P((char *, int));
+
+int
+main()
+{
+ int len;
+ char *r, key[9], salt[3];
+
+ get(key, sizeof(key) - 1);
+ get(salt, sizeof(salt) - 1);
+ len = strlen(r = crypt(key, salt));
+ if (write(STDOUT_FILENO, r, len) != len)
+ err(1, "stdout");
+ exit(0);
+}
+
+static void
+get(bp, len)
+ char *bp;
+ register int len;
+{
+ register int nr;
+
+ bp[len] = '\0';
+ if ((nr = read(STDIN_FILENO, bp, len)) == len)
+ return;
+ if (nr >= 0)
+ errno = EFTYPE;
+ err(1, "stdin");
+}
diff --git a/libexec/rbootd/Makefile b/libexec/rbootd/Makefile
index 6e377fc..369b1e7 100644
--- a/libexec/rbootd/Makefile
+++ b/libexec/rbootd/Makefile
@@ -2,10 +2,11 @@
PROG= rbootd
SRCS= bpf.c conf.c parseconf.c rbootd.c rmpproto.c utils.c
-MAN8= rbootd.0
+MAN8= rbootd.8
-afterinstall:
- (cd ${.CURDIR}/bootdir && install -c -o ${BINOWN} -g ${BINGRP} \
+# XXX BROKEN: afterinstall:
+XXXafterinstall:
+ (cd ${.CURDIR}/bootdir && ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} \
-m 444 * ${DESTDIR}/usr/mdec/)
.include <bsd.prog.mk>
diff --git a/libexec/rbootd/bpf.c b/libexec/rbootd/bpf.c
index 7ecd419..f5e8bce 100644
--- a/libexec/rbootd/bpf.c
+++ b/libexec/rbootd/bpf.c
@@ -253,7 +253,7 @@ BpfGetIntfName(errmsg)
#endif
ifrp = ibuf;
ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
-
+
mp = 0;
minunit = 666;
for (; ifrp < ifend; ++ifrp) {
diff --git a/libexec/rbootd/parseconf.c b/libexec/rbootd/parseconf.c
index d00436f..a01a4a5 100644
--- a/libexec/rbootd/parseconf.c
+++ b/libexec/rbootd/parseconf.c
@@ -192,7 +192,7 @@ ParseConfig()
* the entire record is invalidated.
*/
if (i == 0) {
- FreeClient(client);
+ FreeClient(client);
continue;
}
}
diff --git a/libexec/rbootd/rbootd.c b/libexec/rbootd/rbootd.c
index aa01583..0164345 100644
--- a/libexec/rbootd/rbootd.c
+++ b/libexec/rbootd/rbootd.c
@@ -379,7 +379,7 @@ DoTimeout()
** FindClient -- Find client associated with a packet.
**
** Parameters:
-** rconn - the new packet.
+** rconn - the new packet.
**
** Returns:
** Pointer to client info if found, NULL otherwise.
diff --git a/libexec/revnetgroup/Makefile b/libexec/revnetgroup/Makefile
new file mode 100644
index 0000000..dec0e7b
--- /dev/null
+++ b/libexec/revnetgroup/Makefile
@@ -0,0 +1,8 @@
+# $Id$
+
+PROG= revnetgroup
+SRCS= revnetgroup.c hash.c parse_netgroup.c
+
+MAN8= revnetgroup.8
+
+.include <bsd.prog.mk>
diff --git a/libexec/revnetgroup/hash.c b/libexec/revnetgroup/hash.c
new file mode 100644
index 0000000..b875843
--- /dev/null
+++ b/libexec/revnetgroup/hash.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "hash.h"
+
+/*
+ * This hash function is stolen directly from the
+ * Berkeley DB package. It already exists inside libc, but
+ * it's declared static which prevents us from calling it
+ * from here.
+ */
+/*
+ * OZ's original sdbm hash
+ */
+u_int32_t
+hash(keyarg, len)
+ const void *keyarg;
+ register size_t len;
+{
+ register const u_char *key;
+ register size_t loop;
+ register u_int32_t h;
+
+#define HASHC h = *key++ + 65599 * h
+
+ h = 0;
+ key = keyarg;
+ if (len > 0) {
+ loop = (len + 8 - 1) >> 3;
+
+ switch (len & (8 - 1)) {
+ case 0:
+ do {
+ HASHC;
+ /* FALLTHROUGH */
+ case 7:
+ HASHC;
+ /* FALLTHROUGH */
+ case 6:
+ HASHC;
+ /* FALLTHROUGH */
+ case 5:
+ HASHC;
+ /* FALLTHROUGH */
+ case 4:
+ HASHC;
+ /* FALLTHROUGH */
+ case 3:
+ HASHC;
+ /* FALLTHROUGH */
+ case 2:
+ HASHC;
+ /* FALLTHROUGH */
+ case 1:
+ HASHC;
+ } while (--loop);
+ }
+ }
+ return (h);
+}
+
+/*
+ * Generate a hash value for a given key (character string).
+ * We mask off all but the lower 8 bits since our table array
+ * can only hole 256 elements.
+ */
+u_int32_t hashkey(key)
+ char *key;
+{
+
+ if (key == NULL)
+ return (-1);
+ return(hash((void *)key, strlen(key)) & HASH_MASK);
+}
+
+/* Find an entry in the hash table (may be hanging off a linked list). */
+char *lookup(table, key)
+ struct group_entry *table[];
+ char *key;
+{
+ struct group_entry *cur;
+
+ cur = table[hashkey(key)];
+
+ while (cur) {
+ if (!strcmp(cur->key, key))
+ return(cur->data);
+ cur = cur->next;
+ }
+
+ return(NULL);
+}
+
+/*
+ * Store an entry in the main netgroup hash table. Here's how this
+ * works: the table can only be so big when we initialize it (TABLESIZE)
+ * but the number of netgroups in the /etc/netgroup file could easily be
+ * much larger than the table. Since our hash values are adjusted to
+ * never be greater than TABLESIZE too, this means it won't be long before
+ * we find ourselves with two keys that hash to the same value.
+ *
+ * One way to deal with this is to malloc(2) a second table and start
+ * doing indirection, but this is a pain in the butt and it's not worth
+ * going to all that trouble for a dinky littke program like this. Instead,
+ * we turn each table entry into a linked list and simply link keys
+ * with the same hash value together at the same index location within
+ * the table.
+ *
+ * That's a lot of comment for such a small piece of code, isn't it.
+ */
+void store (table, key, data)
+ struct group_entry *table[];
+ char *key, *data;
+{
+ struct group_entry *new;
+ u_int32_t i;
+
+ i = hashkey(key);
+
+ new = (struct group_entry *)malloc(sizeof(struct group_entry));
+ new->key = strdup(key);
+ new->data = strdup(data);
+ new->next = table[i];
+ table[i] = new;
+
+ return;
+}
+
+/*
+ * Store an group member entry and/or update its grouplist. This is
+ * a bit more complicated than the previous function since we have to
+ * maintain not only the hash table of group members, each group member
+ * structure also has a linked list of groups hung off it. If handed
+ * a member name that we haven't encountered before, we have to do
+ * two things: add that member to the table (possibly hanging them
+ * off the end of a linked list, as above), and add a group name to
+ * the member's grouplist list. If we're handed a name that already has
+ * an entry in the table, then we just have to do one thing, which is
+ * to update its grouplist.
+ */
+void mstore (table, key, data, domain)
+ struct member_entry *table[];
+ char *key, *data, *domain;
+{
+ struct member_entry *cur, *new;
+ struct grouplist *tmp;
+ u_int32_t i;
+
+ i = hashkey(key);
+ cur = table[i];
+
+ tmp = (struct grouplist *)malloc(sizeof(struct grouplist));
+ tmp->groupname = strdup(data);
+ tmp->next = NULL;
+
+ /* Check if all we have to do is insert a new groupname. */
+ while (cur) {
+ if (!strcmp(cur->key, key)) {
+ tmp->next = cur->groups;
+ cur->groups = tmp;
+ return;
+ }
+ cur = cur->next;
+ }
+
+ /* Didn't find a match -- add the whole mess to the table. */
+ new = (struct member_entry *)malloc(sizeof(struct member_entry));
+ new->key = strdup(key);
+ new->domain = domain ? strdup(domain) : "*";
+ new->groups = tmp;
+ new->next = table[i];
+ table[i] = new;
+
+ return;
+}
diff --git a/libexec/revnetgroup/hash.h b/libexec/revnetgroup/hash.h
new file mode 100644
index 0000000..2212534
--- /dev/null
+++ b/libexec/revnetgroup/hash.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+/* Groupname entry hung off a member_entry node. */
+struct grouplist {
+ char *groupname;
+ struct grouplist *next;
+};
+
+/* Entry in the cooked member list hash table. */
+struct member_entry {
+ char *key;
+ char *domain;
+ struct grouplist *groups;
+ struct member_entry *next;
+};
+
+/* Entry in the raw netgroup table. */
+struct group_entry {
+ char *key;
+ char *data;
+ struct grps *groups;
+ struct group_entry *next;
+};
+
+/* Table size (chosen arbitrarily). Not too big, not too small. */
+#define TABLESIZE 256
+#define HASH_MASK 0x000000FF
diff --git a/libexec/revnetgroup/parse_netgroup.c b/libexec/revnetgroup/parse_netgroup.c
new file mode 100644
index 0000000..99dfaeb
--- /dev/null
+++ b/libexec/revnetgroup/parse_netgroup.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "$Id$";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * This is a specially hacked-up version of getnetgrent.c used to parse
+ * data from the stored hash table of netgroup info rather than from a
+ * file. It's used mainly for the parse_netgroup() function. All the YP
+ * stuff and file support has been stripped out since it isn't needed.
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "hash.h"
+
+/*
+ * Static Variables and functions used by setnetgrent(), getnetgrent() and
+ * __endnetgrent().
+ * There are two linked lists:
+ * - linelist is just used by setnetgrent() to parse the net group file via.
+ * parse_netgrp()
+ * - netgrp is the list of entries for the current netgroup
+ */
+struct linelist {
+ struct linelist *l_next; /* Chain ptr. */
+ int l_parsed; /* Flag for cycles */
+ char *l_groupname; /* Name of netgroup */
+ char *l_line; /* Netgroup entrie(s) to be parsed */
+};
+
+struct netgrp {
+ struct netgrp *ng_next; /* Chain ptr */
+ char *ng_str[3]; /* Field pointers, see below */
+};
+#define NG_HOST 0 /* Host name */
+#define NG_USER 1 /* User name */
+#define NG_DOM 2 /* and Domain name */
+
+static struct linelist *linehead = (struct linelist *)0;
+static struct netgrp *nextgrp = (struct netgrp *)0;
+static struct {
+ struct netgrp *gr;
+ char *grname;
+} grouphead = {
+ (struct netgrp *)0,
+ (char *)0,
+};
+static int parse_netgrp();
+static struct linelist *read_for_group();
+void __setnetgrent(), __endnetgrent();
+int __getnetgrent();
+extern struct group_entry *gtable[];
+extern char *lookup __P(( struct group_entry *[], char * ));
+#define LINSIZ 1024 /* Length of netgroup file line */
+
+/*
+ * setnetgrent()
+ * Parse the netgroup file looking for the netgroup and build the list
+ * of netgrp structures. Let parse_netgrp() and read_for_group() do
+ * most of the work.
+ */
+void
+__setnetgrent(group)
+ char *group;
+{
+ /* Sanity check */
+
+ if (group == NULL || !strlen(group))
+ return;
+
+ if (grouphead.gr == (struct netgrp *)0 ||
+ strcmp(group, grouphead.grname)) {
+ __endnetgrent();
+ if (parse_netgrp(group))
+ __endnetgrent();
+ else {
+ grouphead.grname = (char *)
+ malloc(strlen(group) + 1);
+ strcpy(grouphead.grname, group);
+ }
+ }
+ nextgrp = grouphead.gr;
+}
+
+/*
+ * Get the next netgroup off the list.
+ */
+int
+__getnetgrent(hostp, userp, domp)
+ char **hostp, **userp, **domp;
+{
+ if (nextgrp) {
+ *hostp = nextgrp->ng_str[NG_HOST];
+ *userp = nextgrp->ng_str[NG_USER];
+ *domp = nextgrp->ng_str[NG_DOM];
+ nextgrp = nextgrp->ng_next;
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * __endnetgrent() - cleanup
+ */
+void
+__endnetgrent()
+{
+ register struct linelist *lp, *olp;
+ register struct netgrp *gp, *ogp;
+
+ lp = linehead;
+ while (lp) {
+ olp = lp;
+ lp = lp->l_next;
+ free(olp->l_groupname);
+ free(olp->l_line);
+ free((char *)olp);
+ }
+ linehead = (struct linelist *)0;
+ if (grouphead.grname) {
+ free(grouphead.grname);
+ grouphead.grname = (char *)0;
+ }
+ gp = grouphead.gr;
+ while (gp) {
+ ogp = gp;
+ gp = gp->ng_next;
+ if (ogp->ng_str[NG_HOST])
+ free(ogp->ng_str[NG_HOST]);
+ if (ogp->ng_str[NG_USER])
+ free(ogp->ng_str[NG_USER]);
+ if (ogp->ng_str[NG_DOM])
+ free(ogp->ng_str[NG_DOM]);
+ free((char *)ogp);
+ }
+ grouphead.gr = (struct netgrp *)0;
+}
+
+/*
+ * Parse the netgroup file setting up the linked lists.
+ */
+static int
+parse_netgrp(group)
+ char *group;
+{
+ register char *spos, *epos;
+ register int len, strpos;
+#ifdef DEBUG
+ register int fields;
+#endif
+ char *pos, *gpos;
+ struct netgrp *grp;
+ struct linelist *lp = linehead;
+
+ /*
+ * First, see if the line has already been read in.
+ */
+ while (lp) {
+ if (!strcmp(group, lp->l_groupname))
+ break;
+ lp = lp->l_next;
+ }
+ if (lp == (struct linelist *)0 &&
+ (lp = read_for_group(group)) == (struct linelist *)0)
+ return (1);
+ if (lp->l_parsed) {
+#ifdef DEBUG
+ /*
+ * This error message is largely superflous since the
+ * code handles the error condition sucessfully, and
+ * spewing it out from inside libc can actually hose
+ * certain programs.
+ */
+ fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname);
+#endif
+ return (1);
+ } else
+ lp->l_parsed = 1;
+ pos = lp->l_line;
+ /* Watch for null pointer dereferences, dammit! */
+ while (pos != NULL && *pos != '\0') {
+ if (*pos == '(') {
+ grp = (struct netgrp *)malloc(sizeof (struct netgrp));
+ bzero((char *)grp, sizeof (struct netgrp));
+ grp->ng_next = grouphead.gr;
+ grouphead.gr = grp;
+ pos++;
+ gpos = strsep(&pos, ")");
+#ifdef DEBUG
+ fields = 0;
+#endif
+ for (strpos = 0; strpos < 3; strpos++) {
+ if ((spos = strsep(&gpos, ","))) {
+#ifdef DEBUG
+ fields++;
+#endif
+ while (*spos == ' ' || *spos == '\t')
+ spos++;
+ if ((epos = strpbrk(spos, " \t"))) {
+ *epos = '\0';
+ len = epos - spos;
+ } else
+ len = strlen(spos);
+ if (len > 0) {
+ grp->ng_str[strpos] = (char *)
+ malloc(len + 1);
+ bcopy(spos, grp->ng_str[strpos],
+ len + 1);
+ }
+ } else {
+ /*
+ * All other systems I've tested
+ * return NULL for empty netgroup
+ * fields. It's up to user programs
+ * to handle the NULLs appropriately.
+ */
+ grp->ng_str[strpos] = NULL;
+ }
+ }
+#ifdef DEBUG
+ /*
+ * Note: on other platforms, malformed netgroup
+ * entries are not normally flagged. While we
+ * can catch bad entries and report them, we should
+ * stay silent by default for compatibility's sake.
+ */
+ if (fields < 3)
+ fprintf(stderr, "Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n",
+ grp->ng_str[NG_HOST] == NULL ? "" : grp->ng_str[NG_HOST],
+ grp->ng_str[NG_USER] == NULL ? "" : ",",
+ grp->ng_str[NG_USER] == NULL ? "" : grp->ng_str[NG_USER],
+ grp->ng_str[NG_DOM] == NULL ? "" : ",",
+ grp->ng_str[NG_DOM] == NULL ? "" : grp->ng_str[NG_DOM],
+ lp->l_groupname);
+#endif
+ } else {
+ spos = strsep(&pos, ", \t");
+ if (parse_netgrp(spos))
+ continue;
+ }
+ /* Watch for null pointer dereferences, dammit! */
+ if (pos != NULL)
+ while (*pos == ' ' || *pos == ',' || *pos == '\t')
+ pos++;
+ }
+ return (0);
+}
+
+/*
+ * Read the netgroup file and save lines until the line for the netgroup
+ * is found. Return 1 if eof is encountered.
+ */
+static struct linelist *
+read_for_group(group)
+ char *group;
+{
+ register char *pos, *spos, *linep, *olinep;
+ register int len, olen;
+ int cont;
+ struct linelist *lp;
+ char line[LINSIZ + 1];
+ char *key = NULL, *data = NULL;
+
+ data = lookup (gtable, group);
+ sprintf(line, "%s %s", group, data);
+ pos = (char *)&line;
+#ifdef CANT_HAPPEN
+ if (*pos == '#')
+ continue;
+#endif
+ while (*pos == ' ' || *pos == '\t')
+ pos++;
+ spos = pos;
+ while (*pos != ' ' && *pos != '\t' && *pos != '\n' &&
+ *pos != '\0')
+ pos++;
+ len = pos - spos;
+ while (*pos == ' ' || *pos == '\t')
+ pos++;
+ if (*pos != '\n' && *pos != '\0') {
+ lp = (struct linelist *)malloc(sizeof (*lp));
+ lp->l_parsed = 0;
+ lp->l_groupname = (char *)malloc(len + 1);
+ bcopy(spos, lp->l_groupname, len);
+ *(lp->l_groupname + len) = '\0';
+ len = strlen(pos);
+ olen = 0;
+ /*
+ * Loop around handling line continuations.
+ */
+ do {
+ if (*(pos + len - 1) == '\n')
+ len--;
+ if (*(pos + len - 1) == '\\') {
+ len--;
+ cont = 1;
+ } else
+ cont = 0;
+ if (len > 0) {
+ linep = (char *)malloc(olen + len + 1);
+ if (olen > 0) {
+ bcopy(olinep, linep, olen);
+ free(olinep);
+ }
+ bcopy(pos, linep + olen, len);
+ olen += len;
+ *(linep + olen) = '\0';
+ olinep = linep;
+ }
+#ifdef CANT_HAPPEN
+ if (cont) {
+ if (fgets(line, LINSIZ, netf)) {
+ pos = line;
+ len = strlen(pos);
+ } else
+ cont = 0;
+ }
+#endif
+ } while (cont);
+ lp->l_line = linep;
+ lp->l_next = linehead;
+ linehead = lp;
+#ifdef CANT_HAPPEN
+ /*
+ * If this is the one we wanted, we are done.
+ */
+ if (!strcmp(lp->l_groupname, group))
+#endif
+ return (lp);
+ }
+ return ((struct linelist *)0);
+}
diff --git a/libexec/revnetgroup/revnetgroup.8 b/libexec/revnetgroup/revnetgroup.8
new file mode 100644
index 0000000..0a1330e
--- /dev/null
+++ b/libexec/revnetgroup/revnetgroup.8
@@ -0,0 +1,137 @@
+.\" Copyright (c) 1995
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd October 24, 1995
+.Dt REVNETGROUP 8
+.Os
+.Sh NAME
+.Nm revnetgroup
+.Nd "generate reverse netgroup data"
+.Sh SYNOPSIS
+.Nm revnetgroup
+.Fl u
+.Fl h
+.Op Fl f Ar netgroup_file
+.Sh DESCRIPTION
+.Nm revnetgroup
+processes the contents of a file in
+.Xr netgroup 5
+format into what is called
+.Pa reverse netgroup
+form. That is, where the original file shows
+netgroup memberships in terms of which members reside in a particular
+group, the reverse netgroup format specifies what groups are associated
+with a particular member. This information is used to generate the
+.Nm netgroup.byuser
+and
+.Nm netgroup.byhosts
+NIS maps. These reverse netgroup maps are used to help speed up
+netgroup lookups, particularly for the
+.Fn innetgr
+library function.
+.Pp
+For example, the standard
+.Nm /etc/netgroup
+file may list a netgroup and a list of its members. Here, the
+netgroup is considered the
+.Pa key
+and the member names are the
+.Pa data .
+By contrast, the reverse
+.Nm netgroup.byusers
+database lists each unique
+member as the key and the netgroups to which the members belong become
+the data. Seperate databases are created to hold information pertaining
+to users and hosts; this allows netgroup username lookups
+and netgroup hostname lookups to be performed using independent keyspaces.
+.Pp
+By constructing these reverse netgroup databases (and the corresponding
+NIS maps) in advance, the
+.Xr getnetgrent 3
+library functions are spared from having to work out the dependencies
+themselves on the fly. This is important on networks with large numbers
+of users and hosts, since it can take a considerable amount of time
+to process very large netgroup databases.
+.Pp
+The
+.Nm revnetgroup
+command prints its results on the standard output. It is usually called
+only by
+.Nm /var/yp/Makefile
+when rebuilding the NIS netgroup maps.
+.Pp
+.Sh OPTIONS
+The
+.Nm revnetgroup
+command supports the following options:
+.Bl -tag -width flag
+.It Fl u
+Generate netgroup.byuser output; only username information in the
+original netgroup file is processed.
+.It Fl h
+Generate netgroup.byhost output; only hostname information in the
+original netgroup file is processed. (Note at least one of the
+.Fl u
+or
+.Fl h
+flags must be specified.)
+.It Op Fl f Ar netgroup_file
+The
+.Nm revnetgroup
+command uses
+.Nm /etc/netgroup
+as its default input file. The
+.Fl f
+flag allows the user to specify an alternate input file. Specifying ``-''
+as the input file causes
+.Nm revnetgroup
+to read from the standard input.
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/yp/Makefile
+The Makefile that calls
+.Nm yp_mkdb
+and
+.Nm revnetgroup
+to build the NIS databases.
+.It Pa /etc/netgroup
+The default netgroup database file. This file is most often found
+only on the NIS master server.
+.El
+.Sh SEE ALSO
+.Xr yp 4 ,
+.Xr netgroup 5 ,
+.Xr yp_mkdb 8 ,
+.Xr getnetgrent 3
+.Sh AUTHOR
+Bill Paul <wpaul@ctr.columbia.edu>
diff --git a/libexec/revnetgroup/revnetgroup.c b/libexec/revnetgroup/revnetgroup.c
new file mode 100644
index 0000000..4a3ab5a
--- /dev/null
+++ b/libexec/revnetgroup/revnetgroup.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * reverse netgroup map generator program
+ *
+ * Written by Bill Paul <wpaul@ctr.columbia.edu>
+ * Center for Telecommunications Research
+ * Columbia University, New York City
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "hash.h"
+
+#define LINSIZ 1024
+
+/* Default location of netgroup file. */
+char *netgroup = "/etc/netgroup";
+
+/* Stored hash table version of 'forward' netgroup database. */
+struct group_entry *gtable[TABLESIZE];
+
+/*
+ * Stored hash table of 'reverse' netgroup member database
+ * which we will construct.
+ */
+struct member_entry *mtable[TABLESIZE];
+
+extern void store __P(( struct group_entry ** , char *, char * ));
+extern void mstore __P(( struct member_entry ** , char *, char *, char * ));
+extern char *lookup __P(( struct group_entry **, char * ));
+
+void usage(prog)
+char *prog;
+{
+ fprintf (stderr,"usage: %s -u|-h [-f netgroup file]\n",prog);
+ exit(1);
+}
+
+extern char *optarg;
+
+main(argc, argv)
+int argc;
+char *argv[0];
+{
+ FILE *fp;
+ char readbuf[LINSIZ];
+ struct group_entry *gcur;
+ struct member_entry *mcur;
+ char *host, *user, *domain;
+ char ch;
+ char *key = NULL, *data = NULL;
+ int hosts, i;
+
+ if (argc < 2)
+ usage(argv[0]);
+
+ while ((ch = getopt(argc, argv, "uhf:")) != EOF) {
+ switch(ch) {
+ case 'u':
+ hosts = 0;
+ break;
+ case 'h':
+ hosts = 1;
+ break;
+ case 'f':
+ netgroup = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (strcmp(netgroup, "-")) {
+ if ((fp = fopen(netgroup, "r")) == NULL) {
+ perror(netgroup);
+ exit(1);
+ }
+ } else {
+ fp = stdin;
+ }
+
+ /* Stuff all the netgroup names and members into a hash table. */
+ while (fgets(readbuf, LINSIZ, fp)) {
+ if (readbuf[0] == '#')
+ continue;
+ if ((data = (char *)(strpbrk(readbuf, " \t") + 1)) < (char *)2)
+ continue;
+ key = (char *)&readbuf;
+ *(data - 1) = '\0';
+ store(gtable, key, data);
+ }
+
+ fclose(fp);
+
+ /*
+ * Find all members of each netgroup and keep track of which
+ * group they belong to.
+ */
+ for (i = 0; i < TABLESIZE; i++) {
+ gcur = gtable[i];
+ while(gcur) {
+ __setnetgrent(gcur->key);
+ while(__getnetgrent(&host, &user, &domain) != NULL) {
+ if (hosts ? host && strcmp(host,"-") : user && strcmp(user, "-"))
+ mstore(mtable, hosts ? host : user, gcur->key, domain);
+ }
+ gcur = gcur->next;
+ }
+ }
+
+ /* Release resources used by the netgroup parser code. */
+ __endnetgrent();
+
+ /* Spew out the results. */
+ for (i = 0; i < TABLESIZE; i++) {
+ mcur = mtable[i];
+ while(mcur) {
+ struct grouplist *tmp;
+ printf ("%s.%s\t", mcur->key, mcur->domain);
+ tmp = mcur->groups;
+ while(tmp) {
+ printf ("%s", tmp->groupname);
+ tmp = tmp->next;
+ if (tmp)
+ printf(",");
+ }
+ mcur = mcur->next;
+ printf ("\n");
+ }
+ }
+
+ /* Let the OS free all our resources. */
+ exit(0);
+}
diff --git a/libexec/rexecd/Makefile b/libexec/rexecd/Makefile
new file mode 100644
index 0000000..006a10e
--- /dev/null
+++ b/libexec/rexecd/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+
+PROG= rexecd
+MAN8= rexecd.8
+CFLAGS+= -DSKEY
+
+DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT}
+LDADD= -lskey -lmd -lcrypt
+
+.include <bsd.prog.mk>
diff --git a/libexec/rexecd/rexecd.8 b/libexec/rexecd/rexecd.8
new file mode 100644
index 0000000..34059b8
--- /dev/null
+++ b/libexec/rexecd/rexecd.8
@@ -0,0 +1,152 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rexecd.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd September 23, 1994
+.Dt REXECD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm rexecd
+.Nd remote execution server
+.Sh SYNOPSIS
+.Nm rexecd
+.Sh DESCRIPTION
+.Nm Rexecd
+is the server for the
+.Xr rexec 3
+routine. The server provides remote execution facilities
+with authentication based on user names and
+passwords.
+.Pp
+.Nm Rexecd
+listens for service requests at the port indicated in
+the ``exec'' service specification; see
+.Xr services 5 .
+When a service request is received the following protocol
+is initiated:
+.Bl -enum
+.It
+The server reads characters from the socket up
+to a NUL
+.Pq Ql \e0
+byte. The resultant string is
+interpreted as an
+.Tn ASCII
+number, base 10.
+.It
+If the number received in step 1 is non-zero,
+it is interpreted as the port number of a secondary
+stream to be used for the
+.Em stderr .
+A second connection is then created to the specified
+port on the client's machine.
+.It
+A NUL terminated user name of at most 16 characters
+is retrieved on the initial socket.
+.It
+A NUL terminated, unencrypted password of at most
+16 characters is retrieved on the initial socket.
+.It
+A NUL terminated command to be passed to a
+shell is retrieved on the initial socket. The length of
+the command is limited by the upper bound on the size of
+the system's argument list.
+.It
+.Nm Rexecd
+then validates the user as is done at login time
+and, if the authentication was successful, changes
+to the user's home directory, and establishes the user
+and group protections of the user.
+If any of these steps fail the connection is
+aborted with a diagnostic message returned.
+.It
+A NUL byte is returned on the initial socket
+and the command line is passed to the normal login
+shell of the user. The
+shell inherits the network connections established
+by
+.Nm rexecd .
+.El
+.Sh CAVEATS
+.Nm Rexecd
+will no longer allow root logins, access for users listed in /etc/ftpusers,
+or access for users with no passwords, which were all serious security holes.
+The entire concept of rexec/rexecd is a major security hole and an example
+of how not to do things.
+.Nm Rexecd
+is disabled by default in /etc/inetd.conf.
+.Sh DIAGNOSTICS
+Except for the last one listed below,
+all diagnostic messages are returned on the initial socket,
+after which any network connections are closed.
+An error is indicated by a leading byte with a value of
+1 (0 is returned in step 7 above upon successful completion
+of all the steps prior to the command execution).
+.Pp
+.Bl -tag -width Ds
+.It Sy username too long
+The name is
+longer than 16 characters.
+.It Sy password too long
+The password is longer than 16 characters.
+.It Sy command too long
+The command line passed exceeds the size of the argument
+list (as configured into the system).
+.It Sy Login incorrect.
+No password file entry for the user name existed.
+.It Sy Password incorrect.
+The wrong password was supplied.
+.It Sy \&No remote directory.
+The
+.Xr chdir
+command to the home directory failed.
+.It Sy Try again.
+A
+.Xr fork
+by the server failed.
+.It Sy <shellname>: ...
+The user's login shell could not be started.
+This message is returned
+on the connection associated with the
+.Em stderr ,
+and is not preceded by a flag byte.
+.El
+.Sh SEE ALSO
+.Xr rexec 3
+.Sh BUGS
+A facility to allow all data and password exchanges to be encrypted should be
+present.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/libexec/rexecd/rexecd.c b/libexec/rexecd/rexecd.c
new file mode 100644
index 0000000..9c6d029
--- /dev/null
+++ b/libexec/rexecd/rexecd.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rexecd.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <netdb.h>
+
+/*VARARGS1*/
+int error();
+
+char username[20] = "USER=";
+char homedir[64] = "HOME=";
+char shell[64] = "SHELL=";
+char path[sizeof(_PATH_DEFPATH) + sizeof("PATH=")] = "PATH=";
+char *envinit[] =
+ {homedir, shell, path, username, 0};
+char **environ;
+char *remote;
+
+struct sockaddr_in asin = { AF_INET };
+
+/*
+ * remote execute server:
+ * username\0
+ * password\0
+ * command\0
+ * data
+ */
+/*ARGSUSED*/
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct sockaddr_in from;
+ int fromlen;
+ struct hostent *hp;
+
+ openlog(argv[0], LOG_PID, LOG_AUTH);
+ fromlen = sizeof (from);
+ if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ (void)fprintf(stderr,
+ "rexecd: getpeername: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ hp = gethostbyaddr((char *) &from.sin_addr, sizeof(from.sin_addr),
+ from.sin_family);
+ remote = inet_ntoa(from.sin_addr);
+ remote = (hp != NULL) ? hp->h_name : inet_ntoa(from.sin_addr);
+
+ doit(0, &from);
+}
+
+doit(f, fromp)
+ int f;
+ struct sockaddr_in *fromp;
+{
+ FILE *fp;
+ char cmdbuf[NCARGS+1], *cp, *namep;
+#ifdef SKEY
+ char *skey_crypt();
+ char user[16], pass[100];
+#else /* SKEY */
+ char user[16], pass[16];
+#endif /* SKEY */
+ struct passwd *pwd;
+ int s;
+ u_short port;
+ int pv[2], pid, ready, readfrom, cc;
+ char buf[BUFSIZ], sig;
+ int one = 1;
+
+ (void) signal(SIGINT, SIG_DFL);
+ (void) signal(SIGQUIT, SIG_DFL);
+ (void) signal(SIGTERM, SIG_DFL);
+#ifdef DEBUG
+ { int t = open(_PATH_TTY, 2);
+ if (t >= 0) {
+ ioctl(t, TIOCNOTTY, (char *)0);
+ (void) close(t);
+ }
+ }
+#endif
+ dup2(f, 0);
+ dup2(f, 1);
+ dup2(f, 2);
+ (void) alarm(60);
+ port = 0;
+ for (;;) {
+ char c;
+ if (read(f, &c, 1) != 1)
+ exit(1);
+ if (c == 0)
+ break;
+ port = port * 10 + c - '0';
+ }
+ (void) alarm(0);
+ if (port != 0) {
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ exit(1);
+ if (bind(s, (struct sockaddr *)&asin, sizeof (asin)) < 0)
+ exit(1);
+ (void) alarm(60);
+ fromp->sin_port = htons(port);
+ if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0)
+ exit(1);
+ (void) alarm(0);
+ }
+ getstr(user, sizeof(user), "username");
+ getstr(pass, sizeof(pass), "password");
+ getstr(cmdbuf, sizeof(cmdbuf), "command");
+ setpwent();
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ error("Login incorrect.\n");
+ exit(1);
+ }
+ endpwent();
+ if (*pwd->pw_passwd != '\0') {
+#ifdef SKEY
+ namep = skey_crypt(pass, pwd->pw_passwd, pwd,
+ skeyaccess(user, NULL, remote));
+#else /* SKEY */
+ namep = crypt(pass, pwd->pw_passwd);
+#endif /* SKEY */
+ if (strcmp(namep, pwd->pw_passwd)) {
+ syslog(LOG_ERR, "LOGIN FAILURE from %s, %s",
+ remote, user);
+ error("Login incorrect.\n");
+ exit(1);
+ }
+ }
+
+ if (pwd->pw_uid == 0 || *pwd->pw_passwd == '\0' ||
+ (pwd->pw_expire && time(NULL) >= pwd->pw_expire)) {
+ syslog(LOG_ERR, "%s LOGIN REFUSED from %s", user, remote);
+ error("Login incorrect.\n");
+ exit(1);
+ }
+
+ if ((fp = fopen(_PATH_FTPUSERS, "r")) != NULL) {
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ if ((cp = index(buf, '\n')) != NULL)
+ *cp = '\0';
+ if (strcmp(buf, pwd->pw_name) == 0) {
+ syslog(LOG_ERR, "%s LOGIN REFUSED from %s",
+ user, remote);
+ error("Login incorrect.\n");
+ exit(1);
+ }
+ }
+ }
+ (void) fclose(fp);
+
+ syslog(LOG_INFO, "login from %s as %s", remote, user);
+
+ if (chdir(pwd->pw_dir) < 0) {
+ error("No remote directory.\n");
+ exit(1);
+ }
+ (void) write(2, "\0", 1);
+ if (port) {
+ (void) pipe(pv);
+ pid = fork();
+ if (pid == -1) {
+ error("Try again.\n");
+ exit(1);
+ }
+ if (pid) {
+ (void) close(0); (void) close(1); (void) close(2);
+ (void) close(f); (void) close(pv[1]);
+ readfrom = (1<<s) | (1<<pv[0]);
+ ioctl(pv[1], FIONBIO, (char *)&one);
+ /* should set s nbio! */
+ do {
+ ready = readfrom;
+ (void) select(16, (fd_set *)&ready,
+ (fd_set *)NULL, (fd_set *)NULL,
+ (struct timeval *)NULL);
+ if (ready & (1<<s)) {
+ if (read(s, &sig, 1) <= 0)
+ readfrom &= ~(1<<s);
+ else
+ killpg(pid, sig);
+ }
+ if (ready & (1<<pv[0])) {
+ cc = read(pv[0], buf, sizeof (buf));
+ if (cc <= 0) {
+ shutdown(s, 1+1);
+ readfrom &= ~(1<<pv[0]);
+ } else
+ (void) write(s, buf, cc);
+ }
+ } while (readfrom);
+ exit(0);
+ }
+ setpgrp(0, getpid());
+ (void) close(s); (void)close(pv[0]);
+ dup2(pv[1], 2);
+ }
+ if (*pwd->pw_shell == '\0')
+ pwd->pw_shell = _PATH_BSHELL;
+ if (f > 2)
+ (void) close(f);
+ if (setlogin(pwd->pw_name) < 0)
+ syslog(LOG_ERR, "setlogin() failed: %m");
+ (void) setgid((gid_t)pwd->pw_gid);
+ initgroups(pwd->pw_name, pwd->pw_gid);
+ (void) setuid((uid_t)pwd->pw_uid);
+ (void)strcat(path, _PATH_DEFPATH);
+ environ = envinit;
+ strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
+ strncat(shell, pwd->pw_shell, sizeof(shell)-7);
+ strncat(username, pwd->pw_name, sizeof(username)-6);
+ cp = strrchr(pwd->pw_shell, '/');
+ if (cp)
+ cp++;
+ else
+ cp = pwd->pw_shell;
+ execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
+ perror(pwd->pw_shell);
+ exit(1);
+}
+
+/*VARARGS1*/
+error(fmt, a1, a2, a3)
+ char *fmt;
+ int a1, a2, a3;
+{
+ char buf[BUFSIZ];
+
+ buf[0] = 1;
+ (void) sprintf(buf+1, fmt, a1, a2, a3);
+ (void) write(2, buf, strlen(buf));
+}
+
+getstr(buf, cnt, err)
+ char *buf;
+ int cnt;
+ char *err;
+{
+ char c;
+
+ do {
+ if (read(0, &c, 1) != 1)
+ exit(1);
+ *buf++ = c;
+ if (--cnt == 0) {
+ error("%s too long\n", err);
+ exit(1);
+ }
+ } while (c != 0);
+}
diff --git a/libexec/rlogind/Makefile b/libexec/rlogind/Makefile
new file mode 100644
index 0000000..e7c147d
--- /dev/null
+++ b/libexec/rlogind/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+
+PROG= rlogind
+SRCS= rlogind.c
+MAN8= rlogind.8
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \
+ || defined(MAKE_EBONES))
+CFLAGS+=-DKERBEROS -DCRYPT
+DPADD= ${LIBKRB} ${LIBDES}
+LDADD+= -lkrb -ldes
+DISTRIBUTION= krb
+.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/rlogind/pathnames.h b/libexec/rlogind/pathnames.h
new file mode 100644
index 0000000..5240f19
--- /dev/null
+++ b/libexec/rlogind/pathnames.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ */
+
+#include <paths.h>
+
+#define _PATH_LOGIN "/usr/bin/login"
diff --git a/libexec/rlogind/rlogind.8 b/libexec/rlogind/rlogind.8
new file mode 100644
index 0000000..68b89a6
--- /dev/null
+++ b/libexec/rlogind/rlogind.8
@@ -0,0 +1,171 @@
+.\" Copyright (c) 1983, 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rlogind.8 8.1 (Berkeley) 6/4/93
+.\"
+.Dd June 4, 1993
+.Dt RLOGIND 8
+.Os BSD 4.2
+.Sh NAME
+.Nm rlogind
+.Nd remote login server
+.Sh SYNOPSIS
+.Nm rlogind
+.Op Fl Daln
+.Sh DESCRIPTION
+.Nm Rlogind
+is the server for the
+.Xr rlogin 1
+program. The server provides a remote login facility
+with authentication based on privileged port numbers from trusted hosts.
+.Pp
+Options supported by
+.Nm rlogind :
+.Bl -tag -width Ds
+.It Fl D
+Set TCP_NODELAY socket option. This improves responsiveness at the expense of
+some additional network traffic.
+.It Fl a
+Ask hostname for verification.
+.It Fl l
+Prevent any authentication based on the user's
+.Dq Pa .rhosts
+file, unless the user is logging in as the superuser.
+.It Fl n
+Disable keep-alive messages.
+.El
+.Pp
+.Nm Rlogind
+listens for service requests at the port indicated in
+the ``login'' service specification; see
+.Xr services 5 .
+When a service request is received the following protocol
+is initiated:
+.Bl -enum
+.It
+The server checks the client's source port.
+If the port is not in the range 512-1023, the server
+aborts the connection.
+.It
+The server checks the client's source address
+and requests the corresponding host name (see
+.Xr gethostbyaddr 3 ,
+.Xr hosts 5
+and
+.Xr named 8 ) .
+If the hostname cannot be determined,
+the dot-notation representation of the host address is used.
+If the hostname is in the same domain as the server (according to
+the last two components of the domain name),
+or if the
+.Fl a
+option is given,
+the addresses for the hostname are requested,
+verifying that the name and address correspond.
+Normal authentication is bypassed if the address verification fails.
+.El
+.Pp
+Once the source port and address have been checked,
+.Nm rlogind
+proceeds with the authentication process described in
+.Xr rshd 8 .
+It then allocates a pseudo terminal (see
+.Xr pty 4 ) ,
+and manipulates file descriptors so that the slave
+half of the pseudo terminal becomes the
+.Em stdin ,
+.Em stdout ,
+and
+.Em stderr
+for a login process.
+The login process is an instance of the
+.Xr login 1
+program, invoked with the
+.Fl f
+option if authentication has succeeded.
+If automatic authentication fails, the user is
+prompted to log in as if on a standard terminal line.
+.Pp
+The parent of the login process manipulates the master side of
+the pseudo terminal, operating as an intermediary
+between the login process and the client instance of the
+.Xr rlogin
+program. In normal operation, the packet protocol described
+in
+.Xr pty 4
+is invoked to provide
+.Ql ^S/^Q
+type facilities and propagate
+interrupt signals to the remote programs. The login process
+propagates the client terminal's baud rate and terminal type,
+as found in the environment variable,
+.Ql Ev TERM ;
+see
+.Xr environ 7 .
+The screen or window size of the terminal is requested from the client,
+and window size changes from the client are propagated to the pseudo terminal.
+.Pp
+Transport-level keepalive messages are enabled unless the
+.Fl n
+option is present.
+The use of keepalive messages allows sessions to be timed out
+if the client crashes or becomes unreachable.
+.Sh DIAGNOSTICS
+All initial diagnostic messages are indicated
+by a leading byte with a value of 1,
+after which any network connections are closed.
+If there are no errors before
+.Xr login
+is invoked, a null byte is returned as in indication of success.
+.Bl -tag -width Ds
+.It Sy Try again.
+A
+.Xr fork
+by the server failed.
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr ruserok 3 ,
+.Xr rshd 8
+.Sh BUGS
+The authentication procedure used here assumes the integrity
+of each client machine and the connecting medium. This is
+insecure, but is useful in an ``open'' environment.
+.Pp
+A facility to allow all data exchanges to be encrypted should be
+present.
+.Pp
+A more extensible protocol should be used.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/libexec/rlogind/rlogind.c b/libexec/rlogind/rlogind.c
new file mode 100644
index 0000000..e88aa2e
--- /dev/null
+++ b/libexec/rlogind/rlogind.c
@@ -0,0 +1,772 @@
+/*-
+ * Copyright (c) 1983, 1988, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1988, 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rlogind.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+/*
+ * remote login server:
+ * \0
+ * remuser\0
+ * locuser\0
+ * terminal_type/speed\0
+ * data
+ */
+
+#define FD_SETSIZE 16 /* don't need many bits for select */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <termios.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <pwd.h>
+#include <syslog.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pathnames.h"
+
+#ifndef TIOCPKT_WINDOW
+#define TIOCPKT_WINDOW 0x80
+#endif
+
+#ifdef KERBEROS
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+#define SECURE_MESSAGE "This rlogin session is using DES encryption for all transmissions.\r\n"
+
+AUTH_DAT *kdata;
+KTEXT ticket;
+u_char auth_buf[sizeof(AUTH_DAT)];
+u_char tick_buf[sizeof(KTEXT_ST)];
+Key_schedule schedule;
+int doencrypt, retval, use_kerberos, vacuous;
+
+#define ARGSTR "Dalnkvx"
+#else
+#define ARGSTR "Daln"
+#endif /* KERBEROS */
+
+char *env[2];
+#define NMAX 30
+char lusername[NMAX+1], rusername[NMAX+1];
+static char term[64] = "TERM=";
+#define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */
+int keepalive = 1;
+int check_all = 0;
+int no_delay;
+
+struct passwd *pwd;
+
+void doit __P((int, struct sockaddr_in *));
+int control __P((int, char *, int));
+void protocol __P((int, int));
+void cleanup __P((int));
+void fatal __P((int, char *, int));
+int do_rlogin __P((struct sockaddr_in *));
+void getstr __P((char *, int, char *));
+void setup_term __P((int));
+int do_krb_login __P((struct sockaddr_in *));
+void usage __P((void));
+int local_domain __P((char *));
+char *topdomain __P((char *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int __check_rhosts_file;
+ struct sockaddr_in from;
+ int ch, fromlen, on;
+
+ openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH);
+
+ opterr = 0;
+ while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
+ switch (ch) {
+ case 'D':
+ no_delay = 1;
+ break;
+ case 'a':
+ check_all = 1;
+ break;
+ case 'l':
+ __check_rhosts_file = 0;
+ break;
+ case 'n':
+ keepalive = 0;
+ break;
+#ifdef KERBEROS
+ case 'k':
+ use_kerberos = 1;
+ break;
+ case 'v':
+ vacuous = 1;
+ break;
+#ifdef CRYPT
+ case 'x':
+ doencrypt = 1;
+ break;
+#endif
+#endif
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef KERBEROS
+ if (use_kerberos && vacuous) {
+ usage();
+ fatal(STDERR_FILENO, "only one of -k and -v allowed", 0);
+ }
+#endif
+ fromlen = sizeof (from);
+ if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ syslog(LOG_ERR,"Can't get peer name of remote host: %m");
+ fatal(STDERR_FILENO, "Can't get peer name of remote host", 1);
+ }
+ on = 1;
+ if (keepalive &&
+ setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
+ syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+ if (no_delay &&
+ setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
+ syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
+ on = IPTOS_LOWDELAY;
+ if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+
+ doit(0, &from);
+}
+
+int child;
+int netf;
+char line[MAXPATHLEN];
+int confirmed;
+
+struct winsize win = { 0, 0, 0, 0 };
+
+
+void
+doit(f, fromp)
+ int f;
+ struct sockaddr_in *fromp;
+{
+ int master, pid, on = 1;
+ int authenticated = 0;
+ register struct hostent *hp;
+ char hostname[2 * MAXHOSTNAMELEN + 1];
+ char c;
+
+ alarm(60);
+ read(f, &c, 1);
+
+ if (c != 0)
+ exit(1);
+#ifdef KERBEROS
+ if (vacuous)
+ fatal(f, "Remote host requires Kerberos authentication", 0);
+#endif
+
+ alarm(0);
+ fromp->sin_port = ntohs((u_short)fromp->sin_port);
+ hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof(struct in_addr),
+ fromp->sin_family);
+ if (hp)
+ (void)strcpy(hostname, hp->h_name);
+ else
+ (void)strcpy(hostname, inet_ntoa(fromp->sin_addr));
+
+#ifdef KERBEROS
+ if (use_kerberos) {
+ retval = do_krb_login(fromp);
+ if (retval == 0)
+ authenticated++;
+ else if (retval > 0)
+ fatal(f, krb_err_txt[retval], 0);
+ write(f, &c, 1);
+ confirmed = 1; /* we sent the null! */
+ } else
+#endif
+ {
+ if (fromp->sin_family != AF_INET ||
+ fromp->sin_port >= IPPORT_RESERVED ||
+ fromp->sin_port < IPPORT_RESERVED/2) {
+ syslog(LOG_NOTICE, "Connection from %s on illegal port",
+ inet_ntoa(fromp->sin_addr));
+ fatal(f, "Permission denied", 0);
+ }
+#ifdef IP_OPTIONS
+ {
+ u_char optbuf[BUFSIZ/3], *cp;
+ char lbuf[BUFSIZ], *lp;
+ int optsize = sizeof(optbuf), ipproto;
+ struct protoent *ip;
+
+ if ((ip = getprotobyname("ip")) != NULL)
+ ipproto = ip->p_proto;
+ else
+ ipproto = IPPROTO_IP;
+ if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf,
+ &optsize) == 0 && optsize != 0) {
+ lp = lbuf;
+ for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
+ sprintf(lp, " %2.2x", *cp);
+ syslog(LOG_NOTICE,
+ "Connection received using IP options (ignored):%s",
+ lbuf);
+ if (setsockopt(0, ipproto, IP_OPTIONS,
+ (char *)NULL, optsize) != 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_OPTIONS NULL: %m");
+ exit(1);
+ }
+ }
+ }
+#endif
+ if (do_rlogin(fromp) == 0)
+ authenticated++;
+ }
+ if (confirmed == 0) {
+ write(f, "", 1);
+ confirmed = 1; /* we sent the null! */
+ }
+#ifdef KERBEROS
+#ifdef CRYPT
+ if (doencrypt)
+ (void) des_write(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE) - 1);
+#endif
+#endif
+ netf = f;
+
+ pid = forkpty(&master, line, NULL, &win);
+ if (pid < 0) {
+ if (errno == ENOENT)
+ fatal(f, "Out of ptys", 0);
+ else
+ fatal(f, "Forkpty", 1);
+ }
+ if (pid == 0) {
+ if (f > 2) /* f should always be 0, but... */
+ (void) close(f);
+ setup_term(0);
+ if (*lusername=='-') {
+ syslog(LOG_ERR, "tried to pass user \"%s\" to login",
+ lusername);
+ fatal(STDERR_FILENO, "invalid user", 0);
+ }
+ if (authenticated) {
+#ifdef KERBEROS
+ if (use_kerberos && (pwd->pw_uid == 0))
+ syslog(LOG_INFO|LOG_AUTH,
+ "ROOT Kerberos login from %s.%s@%s on %s\n",
+ kdata->pname, kdata->pinst, kdata->prealm,
+ hostname);
+#endif
+
+ execl(_PATH_LOGIN, "login", "-p",
+ "-h", hostname, "-f", lusername, (char *)NULL);
+ } else
+ execl(_PATH_LOGIN, "login", "-p",
+ "-h", hostname, lusername, (char *)NULL);
+ fatal(STDERR_FILENO, _PATH_LOGIN, 1);
+ /*NOTREACHED*/
+ }
+#ifdef CRYPT
+#ifdef KERBEROS
+ /*
+ * If encrypted, don't turn on NBIO or the des read/write
+ * routines will croak.
+ */
+
+ if (!doencrypt)
+#endif
+#endif
+ ioctl(f, FIONBIO, &on);
+ ioctl(master, FIONBIO, &on);
+ ioctl(master, TIOCPKT, &on);
+ signal(SIGCHLD, cleanup);
+ protocol(f, master);
+ signal(SIGCHLD, SIG_IGN);
+ cleanup(0);
+}
+
+char magic[2] = { 0377, 0377 };
+char oobdata[] = {TIOCPKT_WINDOW};
+
+/*
+ * Handle a "control" request (signaled by magic being present)
+ * in the data stream. For now, we are only willing to handle
+ * window size changes.
+ */
+int
+control(pty, cp, n)
+ int pty;
+ char *cp;
+ int n;
+{
+ struct winsize w;
+
+ if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
+ return (0);
+ oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */
+ bcopy(cp+4, (char *)&w, sizeof(w));
+ w.ws_row = ntohs(w.ws_row);
+ w.ws_col = ntohs(w.ws_col);
+ w.ws_xpixel = ntohs(w.ws_xpixel);
+ w.ws_ypixel = ntohs(w.ws_ypixel);
+ (void)ioctl(pty, TIOCSWINSZ, &w);
+ return (4+sizeof (w));
+}
+
+/*
+ * rlogin "protocol" machine.
+ */
+void
+protocol(f, p)
+ register int f, p;
+{
+ char pibuf[1024+1], fibuf[1024], *pbp, *fbp;
+ register pcc = 0, fcc = 0;
+ int cc, nfd, n;
+ char cntl;
+
+ /*
+ * Must ignore SIGTTOU, otherwise we'll stop
+ * when we try and set slave pty's window shape
+ * (our controlling tty is the master pty).
+ */
+ (void) signal(SIGTTOU, SIG_IGN);
+ send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */
+ if (f > p)
+ nfd = f + 1;
+ else
+ nfd = p + 1;
+ if (nfd > FD_SETSIZE) {
+ syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE");
+ fatal(f, "internal error (select mask too small)", 0);
+ }
+ for (;;) {
+ fd_set ibits, obits, ebits, *omask;
+
+ FD_ZERO(&ebits);
+ FD_ZERO(&ibits);
+ FD_ZERO(&obits);
+ omask = (fd_set *)NULL;
+ if (fcc) {
+ FD_SET(p, &obits);
+ omask = &obits;
+ } else
+ FD_SET(f, &ibits);
+ if (pcc >= 0)
+ if (pcc) {
+ FD_SET(f, &obits);
+ omask = &obits;
+ } else
+ FD_SET(p, &ibits);
+ FD_SET(p, &ebits);
+ if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) {
+ if (errno == EINTR)
+ continue;
+ fatal(f, "select", 1);
+ }
+ if (n == 0) {
+ /* shouldn't happen... */
+ sleep(5);
+ continue;
+ }
+#define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
+ if (FD_ISSET(p, &ebits)) {
+ cc = read(p, &cntl, 1);
+ if (cc == 1 && pkcontrol(cntl)) {
+ cntl |= oobdata[0];
+ send(f, &cntl, 1, MSG_OOB);
+ if (cntl & TIOCPKT_FLUSHWRITE) {
+ pcc = 0;
+ FD_CLR(p, &ibits);
+ }
+ }
+ }
+ if (FD_ISSET(f, &ibits)) {
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt)
+ fcc = des_read(f, fibuf, sizeof(fibuf));
+ else
+#endif
+#endif
+ fcc = read(f, fibuf, sizeof(fibuf));
+ if (fcc < 0 && errno == EWOULDBLOCK)
+ fcc = 0;
+ else {
+ register char *cp;
+ int left, n;
+
+ if (fcc <= 0)
+ break;
+ fbp = fibuf;
+
+ top:
+ for (cp = fibuf; cp < fibuf+fcc-1; cp++)
+ if (cp[0] == magic[0] &&
+ cp[1] == magic[1]) {
+ left = fcc - (cp-fibuf);
+ n = control(p, cp, left);
+ if (n) {
+ left -= n;
+ if (left > 0)
+ bcopy(cp+n, cp, left);
+ fcc -= n;
+ goto top; /* n^2 */
+ }
+ }
+ FD_SET(p, &obits); /* try write */
+ }
+ }
+
+ if (FD_ISSET(p, &obits) && fcc > 0) {
+ cc = write(p, fbp, fcc);
+ if (cc > 0) {
+ fcc -= cc;
+ fbp += cc;
+ }
+ }
+
+ if (FD_ISSET(p, &ibits)) {
+ pcc = read(p, pibuf, sizeof (pibuf));
+ pbp = pibuf;
+ if (pcc < 0 && errno == EWOULDBLOCK)
+ pcc = 0;
+ else if (pcc <= 0)
+ break;
+ else if (pibuf[0] == 0) {
+ pbp++, pcc--;
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (!doencrypt)
+#endif
+#endif
+ FD_SET(f, &obits); /* try write */
+ } else {
+ if (pkcontrol(pibuf[0])) {
+ pibuf[0] |= oobdata[0];
+ send(f, &pibuf[0], 1, MSG_OOB);
+ }
+ pcc = 0;
+ }
+ }
+ if ((FD_ISSET(f, &obits)) && pcc > 0) {
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt)
+ cc = des_write(f, pbp, pcc);
+ else
+#endif
+#endif
+ cc = write(f, pbp, pcc);
+ if (cc < 0 && errno == EWOULDBLOCK) {
+ /*
+ * This happens when we try write after read
+ * from p, but some old kernels balk at large
+ * writes even when select returns true.
+ */
+ if (!FD_ISSET(p, &ibits))
+ sleep(5);
+ continue;
+ }
+ if (cc > 0) {
+ pcc -= cc;
+ pbp += cc;
+ }
+ }
+ }
+}
+
+void
+cleanup(signo)
+ int signo;
+{
+ char *p;
+
+ p = line + sizeof(_PATH_DEV) - 1;
+ if (logout(p))
+ logwtmp(p, "", "");
+ (void)chmod(line, 0666);
+ (void)chown(line, 0, 0);
+ *p = 'p';
+ (void)chmod(line, 0666);
+ (void)chown(line, 0, 0);
+ shutdown(netf, 2);
+ exit(1);
+}
+
+void
+fatal(f, msg, syserr)
+ int f;
+ char *msg;
+ int syserr;
+{
+ int len;
+ char buf[BUFSIZ], *bp = buf;
+
+ /*
+ * Prepend binary one to message if we haven't sent
+ * the magic null as confirmation.
+ */
+ if (!confirmed)
+ *bp++ = '\01'; /* error indicator */
+ if (syserr)
+ len = sprintf(bp, "rlogind: %s: %s.\r\n",
+ msg, strerror(errno));
+ else
+ len = sprintf(bp, "rlogind: %s.\r\n", msg);
+ (void) write(f, buf, bp + len - buf);
+ exit(1);
+}
+
+int
+do_rlogin(dest)
+ struct sockaddr_in *dest;
+{
+ getstr(rusername, sizeof(rusername), "remuser too long");
+ getstr(lusername, sizeof(lusername), "locuser too long");
+ getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");
+
+ pwd = getpwnam(lusername);
+ if (pwd == NULL)
+ return (-1);
+ /* XXX why don't we syslog() failure? */
+ return (iruserok(dest->sin_addr.s_addr, pwd->pw_uid == 0,
+ rusername, lusername));
+}
+
+void
+getstr(buf, cnt, errmsg)
+ char *buf;
+ int cnt;
+ char *errmsg;
+{
+ char c;
+
+ do {
+ if (read(0, &c, 1) != 1)
+ exit(1);
+ if (--cnt < 0)
+ fatal(STDOUT_FILENO, errmsg, 0);
+ *buf++ = c;
+ } while (c != 0);
+}
+
+extern char **environ;
+
+void
+setup_term(fd)
+ int fd;
+{
+ register char *cp = index(term+ENVSIZE, '/');
+ char *speed;
+ struct termios tt;
+
+#ifndef notyet
+ tcgetattr(fd, &tt);
+ if (cp) {
+ *cp++ = '\0';
+ speed = cp;
+ cp = index(speed, '/');
+ if (cp)
+ *cp++ = '\0';
+ cfsetspeed(&tt, atoi(speed));
+ }
+
+ tt.c_iflag = TTYDEF_IFLAG;
+ tt.c_oflag = TTYDEF_OFLAG;
+ tt.c_lflag = TTYDEF_LFLAG;
+ tcsetattr(fd, TCSAFLUSH, &tt);
+#else
+ if (cp) {
+ *cp++ = '\0';
+ speed = cp;
+ cp = index(speed, '/');
+ if (cp)
+ *cp++ = '\0';
+ tcgetattr(fd, &tt);
+ cfsetspeed(&tt, atoi(speed));
+ tcsetattr(fd, TCSAFLUSH, &tt);
+ }
+#endif
+
+ env[0] = term;
+ env[1] = 0;
+ environ = env;
+}
+
+#ifdef KERBEROS
+#define VERSION_SIZE 9
+
+/*
+ * Do the remote kerberos login to the named host with the
+ * given inet address
+ *
+ * Return 0 on valid authorization
+ * Return -1 on valid authentication, no authorization
+ * Return >0 for error conditions
+ */
+int
+do_krb_login(dest)
+ struct sockaddr_in *dest;
+{
+ int rc;
+ char instance[INST_SZ], version[VERSION_SIZE];
+ long authopts = 0L; /* !mutual */
+ struct sockaddr_in faddr;
+
+ kdata = (AUTH_DAT *) auth_buf;
+ ticket = (KTEXT) tick_buf;
+
+ instance[0] = '*';
+ instance[1] = '\0';
+
+#ifdef CRYPT
+ if (doencrypt) {
+ rc = sizeof(faddr);
+ if (getsockname(0, (struct sockaddr *)&faddr, &rc))
+ return (-1);
+ authopts = KOPT_DO_MUTUAL;
+ rc = krb_recvauth(
+ authopts, 0,
+ ticket, "rcmd",
+ instance, dest, &faddr,
+ kdata, "", schedule, version);
+ des_set_key(&kdata->session, schedule);
+
+ } else
+#endif
+ rc = krb_recvauth(
+ authopts, 0,
+ ticket, "rcmd",
+ instance, dest, (struct sockaddr_in *) 0,
+ kdata, "", (bit_64 *) 0, version);
+
+ if (rc != KSUCCESS)
+ return (rc);
+
+ getstr(lusername, sizeof(lusername), "locuser");
+ /* get the "cmd" in the rcmd protocol */
+ getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type");
+
+ pwd = getpwnam(lusername);
+ if (pwd == NULL)
+ return (-1);
+
+ /* returns nonzero for no access */
+ if (kuserok(kdata, lusername) != 0)
+ return (-1);
+
+ return (0);
+
+}
+#endif /* KERBEROS */
+
+void
+usage()
+{
+#ifdef KERBEROS
+ syslog(LOG_ERR, "usage: rlogind [-Daln] [-k | -v]");
+#else
+ syslog(LOG_ERR, "usage: rlogind [-Daln]");
+#endif
+}
+
+/*
+ * Check whether host h is in our local domain,
+ * defined as sharing the last two components of the domain part,
+ * or the entire domain part if the local domain has only one component.
+ * If either name is unqualified (contains no '.'),
+ * assume that the host is local, as it will be
+ * interpreted as such.
+ */
+int
+local_domain(h)
+ char *h;
+{
+ char localhost[MAXHOSTNAMELEN];
+ char *p1, *p2;
+
+ localhost[0] = 0;
+ (void) gethostname(localhost, sizeof(localhost));
+ p1 = topdomain(localhost);
+ p2 = topdomain(h);
+ if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
+ return (1);
+ return (0);
+}
+
+char *
+topdomain(h)
+ char *h;
+{
+ register char *p;
+ char *maybe = NULL;
+ int dots = 0;
+
+ for (p = h + strlen(h); p >= h; p--) {
+ if (*p == '.') {
+ if (++dots == 2)
+ return (p);
+ maybe = p;
+ }
+ }
+ return (maybe);
+}
diff --git a/libexec/rpc.rstatd/Makefile b/libexec/rpc.rstatd/Makefile
new file mode 100644
index 0000000..312f7cf
--- /dev/null
+++ b/libexec/rpc.rstatd/Makefile
@@ -0,0 +1,10 @@
+# $Id: Makefile,v 1.2 1993/11/10 03:45:40 smace Exp $
+
+PROG = rpc.rstatd
+SRCS = rstatd.c rstat_proc.c
+MAN8 = rpc.rstatd.8
+
+DPADD= ${LIBRPCSVC} ${LIBUTIL} ${LIBKVM}
+LDADD= -lrpcsvc -lutil -lkvm
+
+.include <bsd.prog.mk>
diff --git a/libexec/rpc.rstatd/rpc.rstatd.8 b/libexec/rpc.rstatd/rpc.rstatd.8
new file mode 100644
index 0000000..0644bbb
--- /dev/null
+++ b/libexec/rpc.rstatd/rpc.rstatd.8
@@ -0,0 +1,61 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 1985, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: rpc.rstatd.8,v 1.1 1993/09/16 00:27:45 jtc Exp $
+.\"
+.Dd June 7, 1993
+.Dt RPC.RSTATD 8
+.Os BSD 4.3
+.Sh NAME
+.Nm rpc.rstatd
+.Nd kernel statistics server
+.Sh SYNOPSIS
+.Nm /usr/libexec/rpc.rstatd
+.Sh DESCRIPTION
+.Nm rpc.rstatd
+is a server which returns performance statistics obtained from the kernel.
+These statistics are read using the
+.Xr rup 1
+command.
+The
+.Nm rpc.rstatd
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+.Nm rpc.rstatd
+uses an RPC protocol defined in
+.Pa /usr/include/rpcsvc/rstat.x .
+.Sh SEE ALSO
+.Xr rup 1 ,
+.Xr inetd 8
+
diff --git a/libexec/rpc.rstatd/rstat_proc.c b/libexec/rpc.rstatd/rstat_proc.c
new file mode 100644
index 0000000..613ce51
--- /dev/null
+++ b/libexec/rpc.rstatd/rstat_proc.c
@@ -0,0 +1,459 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#ifndef lint
+/*static char sccsid[] = "from: @(#)rpc.rstatd.c 1.1 86/09/25 Copyr 1984 Sun Micro";*/
+/*static char sccsid[] = "from: @(#)rstat_proc.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char rcsid[] = "$Id: rstat_proc.c,v 1.2 1994/10/15 13:39:54 davidg Exp $";
+#endif
+
+/*
+ * rstat service: built with rstat.x and derived from rpc.rstatd.c
+ *
+ * Copyright (c) 1984 by Sun Microsystems, Inc.
+ */
+
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <nlist.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <rpc/rpc.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#ifdef BSD
+#include <sys/vmmeter.h>
+#include <sys/dkstat.h>
+#else
+#include <sys/dk.h>
+#endif
+#include <net/if.h>
+
+#undef FSHIFT /* Use protocol's shift and scale values */
+#undef FSCALE
+#undef if_ipackets
+#undef if_ierrors
+#undef if_opackets
+#undef if_oerrors
+#undef if_collisions
+#include <rpcsvc/rstat.h>
+
+struct nlist nl[] = {
+#define X_CPTIME 0
+ { "_cp_time" },
+#define X_CNT 1
+ { "_cnt" },
+#define X_IFNET 2
+ { "_ifnet" },
+#define X_DKXFER 3
+ { "_dk_xfer" },
+#define X_BOOTTIME 4
+ { "_boottime" },
+#define X_HZ 5
+ { "_hz" },
+#ifdef vax
+#define X_AVENRUN 6
+ { "_avenrun" },
+#endif
+ "",
+};
+int firstifnet, numintfs; /* chain of ethernet interfaces */
+int stats_service();
+
+extern int from_inetd;
+int sincelastreq = 0; /* number of alarms since last request */
+extern int closedown;
+
+union {
+ struct stats s1;
+ struct statsswtch s2;
+ struct statstime s3;
+} stats_all;
+
+void updatestat();
+static stat_is_init = 0;
+static kvm_t *kd;
+extern int errno;
+
+#if defined(BSD)
+static int cp_time_xlat[RSTAT_CPUSTATES] = { CP_USER, CP_NICE, CP_SYS,
+ CP_IDLE };
+static long bsd_cp_time[CPUSTATES];
+#endif
+
+
+#ifndef FSCALE
+#define FSCALE (1 << 8)
+#endif
+
+#ifndef BSD
+/*
+ * BSD has the kvm facility for getting info from the
+ * kernel. If you aren't on BSD, this surfices.
+ */
+int kmem;
+
+kvm_read(kd, off, addr, size)
+ void * kd;
+ unsigned long off, size;
+ char *addr;
+{
+ int len;
+ if (lseek(kmem, (long)off, 0) == -1)
+ return(-1);
+ return(read(kmem, addr, size));
+}
+
+kvm_nlist(kd, nl)
+ void * kd;
+ struct nlist *nl;
+{
+ int n = nlist("/vmunix", nl);
+ if (nl[0].n_value == 0)
+ return(n);
+
+ if ((kmem = open("/dev/kmem", 0)) < 0)
+ return(-1);
+ return(0);
+}
+#endif
+
+stat_init()
+{
+ stat_is_init = 1;
+ setup();
+ updatestat();
+ (void) signal(SIGALRM, updatestat);
+ alarm(1);
+}
+
+statstime *
+rstatproc_stats_3()
+{
+ if (! stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ return(&stats_all.s3);
+}
+
+statsswtch *
+rstatproc_stats_2()
+{
+ if (! stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ return(&stats_all.s2);
+}
+
+stats *
+rstatproc_stats_1()
+{
+ if (! stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ return(&stats_all.s1);
+}
+
+u_int *
+rstatproc_havedisk_3()
+{
+ static u_int have;
+
+ if (! stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ have = havedisk();
+ return(&have);
+}
+
+u_int *
+rstatproc_havedisk_2()
+{
+ return(rstatproc_havedisk_3());
+}
+
+u_int *
+rstatproc_havedisk_1()
+{
+ return(rstatproc_havedisk_3());
+}
+
+void
+updatestat()
+{
+ int off, i, hz;
+ struct vmmeter cnt;
+ struct ifnet ifnet;
+ double avrun[3];
+ struct timeval tm, btm;
+
+#ifdef DEBUG
+ fprintf(stderr, "entering updatestat\n");
+#endif
+ if (sincelastreq >= closedown) {
+#ifdef DEBUG
+ fprintf(stderr, "about to closedown\n");
+#endif
+ if (from_inetd)
+ exit(0);
+ else {
+ stat_is_init = 0;
+ return;
+ }
+ }
+ sincelastreq++;
+
+ if (kvm_read(kd, (long)nl[X_HZ].n_value, (char *)&hz, sizeof hz) != sizeof hz) {
+ syslog(LOG_ERR, "rstat: can't read hz from kmem\n");
+ exit(1);
+ }
+#if defined(BSD)
+ if (kvm_read(kd, (long)nl[X_CPTIME].n_value, (char *)bsd_cp_time, sizeof(bsd_cp_time))
+ != sizeof(bsd_cp_time)) {
+ syslog(LOG_ERR, "rstat: can't read cp_time from kmem\n");
+ exit(1);
+ }
+ for(i = 0; i < RSTAT_CPUSTATES ; i++)
+ stats_all.s1.cp_time[i] = bsd_cp_time[cp_time_xlat[i]];
+#else
+ if (kvm_read(kd, (long)nl[X_CPTIME].n_value, (char *)stats_all.s1.cp_time, sizeof (stats_all.s1.cp_time))
+ != sizeof (stats_all.s1.cp_time)) {
+ syslog(LOG_ERR, "rstat: can't read cp_time from kmem\n");
+ exit(1);
+ }
+#endif
+#ifdef vax
+ if (kvm_read(kd, (long)nl[X_AVENRUN].n_value, (char *)avrun, sizeof (avrun)) != sizeof (avrun)) {
+ syslog(LOG_ERR, "rstat: can't read avenrun from kmem\n");
+ exit(1);
+ }
+#endif
+#ifdef BSD
+ (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0]));
+#endif
+ stats_all.s2.avenrun[0] = avrun[0] * FSCALE;
+ stats_all.s2.avenrun[1] = avrun[1] * FSCALE;
+ stats_all.s2.avenrun[2] = avrun[2] * FSCALE;
+ if (kvm_read(kd, (long)nl[X_BOOTTIME].n_value, (char *)&btm, sizeof (stats_all.s2.boottime))
+ != sizeof (stats_all.s2.boottime)) {
+ syslog(LOG_ERR, "rstat: can't read boottime from kmem\n");
+ exit(1);
+ }
+ stats_all.s2.boottime.tv_sec = btm.tv_sec;
+ stats_all.s2.boottime.tv_usec = btm.tv_usec;
+
+
+#ifdef DEBUG
+ fprintf(stderr, "%d %d %d %d\n", stats_all.s1.cp_time[0],
+ stats_all.s1.cp_time[1], stats_all.s1.cp_time[2], stats_all.s1.cp_time[3]);
+#endif
+
+ if (kvm_read(kd, (long)nl[X_CNT].n_value, (char *)&cnt, sizeof cnt) != sizeof cnt) {
+ syslog(LOG_ERR, "rstat: can't read cnt from kmem\n");
+ exit(1);
+ }
+ stats_all.s1.v_pgpgin = cnt.v_vnodepgsin;
+ stats_all.s1.v_pgpgout = cnt.v_vnodepgsout;
+ stats_all.s1.v_pswpin = cnt.v_swappgsin;
+ stats_all.s1.v_pswpout = cnt.v_swappgsout;
+ stats_all.s1.v_intr = cnt.v_intr;
+ gettimeofday(&tm, (struct timezone *) 0);
+ stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) +
+ hz*(tm.tv_usec - btm.tv_usec)/1000000;
+ stats_all.s2.v_swtch = cnt.v_swtch;
+
+ if (kvm_read(kd, (long)nl[X_DKXFER].n_value, (char *)stats_all.s1.dk_xfer, sizeof (stats_all.s1.dk_xfer))
+ != sizeof (stats_all.s1.dk_xfer)) {
+ syslog(LOG_ERR, "rstat: can't read dk_xfer from kmem\n");
+ exit(1);
+ }
+
+ stats_all.s1.if_ipackets = 0;
+ stats_all.s1.if_opackets = 0;
+ stats_all.s1.if_ierrors = 0;
+ stats_all.s1.if_oerrors = 0;
+ stats_all.s1.if_collisions = 0;
+ for (off = firstifnet, i = 0; off && i < numintfs; i++) {
+ if (kvm_read(kd, off, (char *)&ifnet, sizeof ifnet) != sizeof ifnet) {
+ syslog(LOG_ERR, "rstat: can't read ifnet from kmem\n");
+ exit(1);
+ }
+ stats_all.s1.if_ipackets += ifnet.if_data.ifi_ipackets;
+ stats_all.s1.if_opackets += ifnet.if_data.ifi_opackets;
+ stats_all.s1.if_ierrors += ifnet.if_data.ifi_ierrors;
+ stats_all.s1.if_oerrors += ifnet.if_data.ifi_oerrors;
+ stats_all.s1.if_collisions += ifnet.if_data.ifi_collisions;
+ off = (int) ifnet.if_next;
+ }
+ gettimeofday((struct timeval *)&stats_all.s3.curtime,
+ (struct timezone *) 0);
+ alarm(1);
+}
+
+setup()
+{
+ struct ifnet ifnet;
+ int off;
+ char errbuf[_POSIX2_LINE_MAX];
+
+ int en;
+
+ if ((kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL) {
+ syslog(LOG_ERR, "rpc.rstatd, %s", errbuf);
+ exit(1);
+ }
+
+ if ((en = kvm_nlist(kd, nl)) != 0) {
+ syslog(LOG_ERR, "rstatd: Can't get namelist. %d", en);
+ exit (1);
+ }
+
+ if (kvm_read(kd, (long)nl[X_IFNET].n_value, &firstifnet,
+ sizeof(int)) != sizeof(int)) {
+ syslog(LOG_ERR, "rstat: can't read firstifnet from kmem\n");
+ exit(1);
+ }
+
+ numintfs = 0;
+ for (off = firstifnet; off;) {
+ if (kvm_read(kd, off, (char *)&ifnet, sizeof ifnet) != sizeof ifnet) {
+ syslog(LOG_ERR, "rstat: can't read ifnet from kmem\n");
+ exit(1);
+ }
+ numintfs++;
+ off = (int) ifnet.if_next;
+ }
+}
+
+/*
+ * returns true if have a disk
+ */
+havedisk()
+{
+ int i, cnt;
+ long xfer[DK_NDRIVE];
+
+ if (kvm_nlist(kd, nl) != 0) {
+ syslog(LOG_ERR, "rstatd: Can't get namelist.(d)");
+ exit (1);
+ }
+
+ if (kvm_read(kd, (long)nl[X_DKXFER].n_value, (char *)xfer, sizeof xfer)!= sizeof xfer) {
+ syslog(LOG_ERR, "rstat: can't read kmem\n");
+ exit(1);
+ }
+ cnt = 0;
+ for (i=0; i < DK_NDRIVE; i++)
+ cnt += xfer[i];
+ return (cnt != 0);
+}
+
+void
+rstat_service(rqstp, transp)
+ struct svc_req *rqstp;
+ SVCXPRT *transp;
+{
+ union {
+ int fill;
+ } argument;
+ char *result;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ char *(*local)();
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, xdr_void, (char *)NULL);
+ goto leave;
+
+ case RSTATPROC_STATS:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_statstime;
+ switch (rqstp->rq_vers) {
+ case RSTATVERS_ORIG:
+ local = (char *(*)()) rstatproc_stats_1;
+ break;
+ case RSTATVERS_SWTCH:
+ local = (char *(*)()) rstatproc_stats_2;
+ break;
+ case RSTATVERS_TIME:
+ local = (char *(*)()) rstatproc_stats_3;
+ break;
+ default:
+ svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
+ goto leave;
+ /*NOTREACHED*/
+ }
+ break;
+
+ case RSTATPROC_HAVEDISK:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_u_int;
+ switch (rqstp->rq_vers) {
+ case RSTATVERS_ORIG:
+ local = (char *(*)()) rstatproc_havedisk_1;
+ break;
+ case RSTATVERS_SWTCH:
+ local = (char *(*)()) rstatproc_havedisk_2;
+ break;
+ case RSTATVERS_TIME:
+ local = (char *(*)()) rstatproc_havedisk_3;
+ break;
+ default:
+ svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
+ goto leave;
+ /*NOTREACHED*/
+ }
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ goto leave;
+ }
+ bzero((char *)&argument, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ goto leave;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, &argument)) {
+ (void)fprintf(stderr, "unable to free arguments\n");
+ exit(1);
+ }
+leave:
+ if (from_inetd)
+ exit(0);
+}
diff --git a/libexec/rpc.rstatd/rstatd.c b/libexec/rpc.rstatd/rstatd.c
new file mode 100644
index 0000000..972974c
--- /dev/null
+++ b/libexec/rpc.rstatd/rstatd.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 1993, John Brezak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: rstatd.c,v 1.2 1994/11/18 22:31:05 ats Exp $";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <signal.h>
+#include <syslog.h>
+#include <rpcsvc/rstat.h>
+
+extern void rstat_service();
+
+int from_inetd = 1; /* started from inetd ? */
+int closedown = 20; /* how long to wait before going dormant */
+
+void
+cleanup()
+{
+ (void) pmap_unset(RSTATPROG, RSTATVERS_TIME);
+ (void) pmap_unset(RSTATPROG, RSTATVERS_SWTCH);
+ (void) pmap_unset(RSTATPROG, RSTATVERS_ORIG);
+ exit(0);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ SVCXPRT *transp;
+ int sock = 0;
+ int proto = 0;
+ struct sockaddr_in from;
+ int fromlen;
+
+ if (argc == 2)
+ closedown = atoi(argv[1]);
+ if (closedown <= 0)
+ closedown = 20;
+
+ /*
+ * See if inetd started us
+ */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ from_inetd = 0;
+ sock = RPC_ANYSOCK;
+ proto = IPPROTO_UDP;
+ }
+
+ if (!from_inetd) {
+ daemon(0, 0);
+
+ (void)pmap_unset(RSTATPROG, RSTATVERS_TIME);
+ (void)pmap_unset(RSTATPROG, RSTATVERS_SWTCH);
+ (void)pmap_unset(RSTATPROG, RSTATVERS_ORIG);
+
+ (void) signal(SIGINT, cleanup);
+ (void) signal(SIGTERM, cleanup);
+ (void) signal(SIGHUP, cleanup);
+ }
+
+ openlog("rpc.rstatd", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service.");
+ exit(1);
+ }
+ if (!svc_register(transp, RSTATPROG, RSTATVERS_TIME, rstat_service, proto)) {
+ syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_TIME, udp).");
+ exit(1);
+ }
+ if (!svc_register(transp, RSTATPROG, RSTATVERS_SWTCH, rstat_service, proto)) {
+ syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_SWTCH, udp).");
+ exit(1);
+ }
+ if (!svc_register(transp, RSTATPROG, RSTATVERS_ORIG, rstat_service, proto)) {
+ syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_ORIG, udp).");
+ exit(1);
+ }
+
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ exit(1);
+}
diff --git a/libexec/rpc.rusersd/Makefile b/libexec/rpc.rusersd/Makefile
new file mode 100644
index 0000000..1c68939
--- /dev/null
+++ b/libexec/rpc.rusersd/Makefile
@@ -0,0 +1,16 @@
+# $Id: Makefile,v 1.2 1993/11/10 03:46:04 smace Exp $
+
+PROG = rpc.rusersd
+SRCS = rusersd.c rusers_proc.c
+MAN8 = rpc.rusersd.8
+
+DPADD= ${LIBRPCSVC} ${LIBUTIL}
+LDADD= -lrpcsvc -lutil
+
+.if exists(/usr/include/X11/extensions/xidle.h)
+CFLAGS+= -DXIDLE
+LDADD+= -L/usr/X386/lib -lXext -lX11
+.endif
+
+
+.include <bsd.prog.mk>
diff --git a/libexec/rpc.rusersd/rpc.rusersd.8 b/libexec/rpc.rusersd/rpc.rusersd.8
new file mode 100644
index 0000000..15947ac
--- /dev/null
+++ b/libexec/rpc.rusersd/rpc.rusersd.8
@@ -0,0 +1,64 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 1985, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: rpc.rusersd.8,v 1.1 1993/09/16 00:34:43 jtc Exp $
+.\"
+.Dd June 7, 1993
+.Dt RPC.RUSERSD 8
+.Os BSD 4.3
+.Sh NAME
+.Nm rpc.rusersd
+.Nd logged in users server
+.Sh SYNOPSIS
+.Nm /usr/libexec/rpc.rusersd
+.Sh DESCRIPTION
+.Nm rpc.rusersd
+is a server which returns information about users
+currently logged in to the system.
+.Pp
+The currently logged in users are queried using the
+.Xr rusers 1
+command.
+The
+.Nm rpc.rusersd
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+.Nm rpc.rusersd
+uses an RPC protocol defined in
+.Pa /usr/include/rpcsvc/rnusers.x .
+.Sh SEE ALSO
+.Xr rusers 1 ,
+.Xr who 1 ,
+.Xr w 1 ,
+.Xr inetd 8
diff --git a/libexec/rpc.rusersd/rusers_proc.c b/libexec/rpc.rusersd/rusers_proc.c
new file mode 100644
index 0000000..d472673
--- /dev/null
+++ b/libexec/rpc.rusersd/rusers_proc.c
@@ -0,0 +1,390 @@
+/*-
+ * Copyright (c) 1993, John Brezak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: rusers_proc.c,v 1.3 1994/11/18 23:36:18 ats Exp $";
+#endif /* not lint */
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <utmp.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <rpc/rpc.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#ifdef XIDLE
+#include <setjmp.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/xidle.h>
+#endif
+#define utmp rutmp
+#include <rpcsvc/rnusers.h>
+#undef utmp
+
+#define IGNOREUSER "sleeper"
+
+#ifdef OSF
+#define _PATH_UTMP UTMP_FILE
+#endif
+
+#ifndef _PATH_UTMP
+#define _PATH_UTMP "/etc/utmp"
+#endif
+
+#ifndef _PATH_DEV
+#define _PATH_DEV "/dev"
+#endif
+
+#ifndef UT_LINESIZE
+#define UT_LINESIZE sizeof(((struct utmp *)0)->ut_line)
+#endif
+#ifndef UT_NAMESIZE
+#define UT_NAMESIZE sizeof(((struct utmp *)0)->ut_name)
+#endif
+#ifndef UT_HOSTSIZE
+#define UT_HOSTSIZE sizeof(((struct utmp *)0)->ut_host)
+#endif
+
+typedef char ut_line_t[UT_LINESIZE+1];
+typedef char ut_name_t[UT_NAMESIZE+1];
+typedef char ut_host_t[UT_HOSTSIZE+1];
+
+utmpidle utmp_idle[MAXUSERS];
+rutmp old_utmp[MAXUSERS];
+ut_line_t line[MAXUSERS];
+ut_name_t name[MAXUSERS];
+ut_host_t host[MAXUSERS];
+
+extern int from_inetd;
+
+FILE *ufp;
+
+#ifdef XIDLE
+Display *dpy;
+
+static jmp_buf openAbort;
+
+static void
+abortOpen ()
+{
+ longjmp (openAbort, 1);
+}
+
+XqueryIdle(char *display)
+{
+ int first_event, first_error;
+ Time IdleTime;
+
+ (void) signal (SIGALRM, abortOpen);
+ (void) alarm ((unsigned) 10);
+ if (!setjmp (openAbort)) {
+ if (!(dpy= XOpenDisplay(display))) {
+ syslog(LOG_ERR, "Cannot open display %s", display);
+ return(-1);
+ }
+ if (XidleQueryExtension(dpy, &first_event, &first_error)) {
+ if (!XGetIdleTime(dpy, &IdleTime)) {
+ syslog(LOG_ERR, "%s: Unable to get idle time.", display);
+ return(-1);
+ }
+ }
+ else {
+ syslog(LOG_ERR, "%s: Xidle extension not loaded.", display);
+ return(-1);
+ }
+ XCloseDisplay(dpy);
+ }
+ else {
+ syslog(LOG_ERR, "%s: Server grabbed for over 10 seconds.", display);
+ return(-1);
+ }
+ (void) signal (SIGALRM, SIG_DFL);
+ (void) alarm ((unsigned) 0);
+
+ IdleTime /= 1000;
+ return((IdleTime + 30) / 60);
+}
+#endif
+
+static u_int
+getidle(char *tty, char *display)
+{
+ struct stat st;
+ char devname[PATH_MAX];
+ time_t now;
+ u_long idle;
+
+ /*
+ * If this is an X terminal or console, then try the
+ * XIdle extension
+ */
+#ifdef XIDLE
+ if (display && *display && (idle = XqueryIdle(display)) >= 0)
+ return(idle);
+#endif
+ idle = 0;
+ if (*tty == 'X') {
+ u_long kbd_idle, mouse_idle;
+#if !defined(__FreeBSD__)
+ kbd_idle = getidle("kbd", NULL);
+#else
+ kbd_idle = getidle("vga", NULL);
+#endif
+ mouse_idle = getidle("mouse", NULL);
+ idle = (kbd_idle < mouse_idle)?kbd_idle:mouse_idle;
+ }
+ else {
+ sprintf(devname, "%s/%s", _PATH_DEV, tty);
+ if (stat(devname, &st) < 0) {
+#ifdef DEBUG
+ printf("%s: %s\n", devname, strerror(errno));
+#endif
+ return(-1);
+ }
+ time(&now);
+#ifdef DEBUG
+ printf("%s: now=%d atime=%d\n", devname, now,
+ st.st_atime);
+#endif
+ idle = now - st.st_atime;
+ idle = (idle + 30) / 60; /* secs->mins */
+ }
+ if (idle < 0) idle = 0;
+
+ return(idle);
+}
+
+static utmpidlearr *
+do_names_2(int all)
+{
+ static utmpidlearr ut;
+ struct utmp usr;
+ int nusers = 0;
+
+ bzero((char *)&ut, sizeof(ut));
+ ut.utmpidlearr_val = &utmp_idle[0];
+
+ ufp = fopen(_PATH_UTMP, "r");
+ if (!ufp) {
+ syslog(LOG_ERR, "%m");
+ return(&ut);
+ }
+
+ /* only entries with both name and line fields */
+ while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1 &&
+ nusers < MAXUSERS)
+ if (*usr.ut_name && *usr.ut_line &&
+ strncmp(usr.ut_name, IGNOREUSER,
+ sizeof(usr.ut_name))
+#ifdef OSF
+ && usr.ut_type == USER_PROCESS
+#endif
+ ) {
+ utmp_idle[nusers].ui_utmp.ut_time =
+ usr.ut_time;
+ utmp_idle[nusers].ui_idle =
+ getidle(usr.ut_line, usr.ut_host);
+ utmp_idle[nusers].ui_utmp.ut_line = line[nusers];
+ strncpy(line[nusers], usr.ut_line, UT_LINESIZE);
+ utmp_idle[nusers].ui_utmp.ut_name = name[nusers];
+ strncpy(name[nusers], usr.ut_name, UT_NAMESIZE);
+ utmp_idle[nusers].ui_utmp.ut_host = host[nusers];
+ strncpy(host[nusers], usr.ut_host, UT_HOSTSIZE);
+
+ /* Make sure entries are NUL terminated */
+ line[nusers][UT_LINESIZE] =
+ name[nusers][UT_NAMESIZE] =
+ host[nusers][UT_HOSTSIZE] = '\0';
+ nusers++;
+ }
+
+ ut.utmpidlearr_len = nusers;
+ fclose(ufp);
+ return(&ut);
+}
+
+int *
+rusers_num()
+{
+ static int num_users = 0;
+ struct utmp usr;
+
+ ufp = fopen(_PATH_UTMP, "r");
+ if (!ufp) {
+ syslog(LOG_ERR, "%m");
+ return(0);
+ }
+
+ /* only entries with both name and line fields */
+ while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
+ if (*usr.ut_name && *usr.ut_line &&
+ strncmp(usr.ut_name, IGNOREUSER,
+ sizeof(usr.ut_name))
+#ifdef OSF
+ && usr.ut_type == USER_PROCESS
+#endif
+ ) {
+ num_users++;
+ }
+
+ fclose(ufp);
+ return(&num_users);
+}
+
+static utmparr *
+do_names_1(int all)
+{
+ utmpidlearr *utidle;
+ static utmparr ut;
+ int i;
+
+ bzero((char *)&ut, sizeof(ut));
+
+ utidle = do_names_2(all);
+ if (utidle) {
+ ut.utmparr_len = utidle->utmpidlearr_len;
+ ut.utmparr_val = &old_utmp[0];
+ for (i = 0; i < ut.utmparr_len; i++)
+ bcopy(&utmp_idle[i].ui_utmp, &old_utmp[i],
+ sizeof(old_utmp[0]));
+
+ }
+
+ return(&ut);
+}
+
+utmpidlearr *
+rusersproc_names_2()
+{
+ return(do_names_2(0));
+}
+
+utmpidlearr *
+rusersproc_allnames_2()
+{
+ return(do_names_2(1));
+}
+
+utmparr *
+rusersproc_names_1()
+{
+ return(do_names_1(0));
+}
+
+utmparr *
+rusersproc_allnames_1()
+{
+ return(do_names_1(1));
+}
+
+void
+rusers_service(rqstp, transp)
+ struct svc_req *rqstp;
+ SVCXPRT *transp;
+{
+ union {
+ int fill;
+ } argument;
+ char *result;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ char *(*local)();
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, xdr_void, (char *)NULL);
+ goto leave;
+
+ case RUSERSPROC_NUM:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_int;
+ local = (char *(*)()) rusers_num;
+ break;
+
+ case RUSERSPROC_NAMES:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_utmpidlearr;
+ switch (rqstp->rq_vers) {
+ case RUSERSVERS_ORIG:
+ local = (char *(*)()) rusersproc_names_1;
+ break;
+ case RUSERSVERS_IDLE:
+ local = (char *(*)()) rusersproc_names_2;
+ break;
+ default:
+ svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE);
+ goto leave;
+ /*NOTREACHED*/
+ }
+ break;
+
+ case RUSERSPROC_ALLNAMES:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_utmpidlearr;
+ switch (rqstp->rq_vers) {
+ case RUSERSVERS_ORIG:
+ local = (char *(*)()) rusersproc_allnames_1;
+ break;
+ case RUSERSVERS_IDLE:
+ local = (char *(*)()) rusersproc_allnames_2;
+ break;
+ default:
+ svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE);
+ goto leave;
+ /*NOTREACHED*/
+ }
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ goto leave;
+ }
+ bzero((char *)&argument, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ goto leave;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, &argument)) {
+ (void)fprintf(stderr, "unable to free arguments\n");
+ exit(1);
+ }
+leave:
+ if (from_inetd)
+ exit(0);
+}
diff --git a/libexec/rpc.rusersd/rusersd.c b/libexec/rpc.rusersd/rusersd.c
new file mode 100644
index 0000000..0182306
--- /dev/null
+++ b/libexec/rpc.rusersd/rusersd.c
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 1993, John Brezak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: rusersd.c,v 1.2 1994/11/18 22:40:11 ats Exp $";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <signal.h>
+#include <syslog.h>
+#define utmp rutmp
+#include <rpcsvc/rnusers.h>
+#undef utmp
+
+extern void rusers_service();
+
+int from_inetd = 1;
+
+void
+cleanup()
+{
+ (void) pmap_unset(RUSERSPROG, RUSERSVERS_IDLE);
+ (void) pmap_unset(RUSERSPROG, RUSERSVERS_ORIG);
+ exit(0);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ SVCXPRT *transp;
+ int sock = 0;
+ int proto = 0;
+ struct sockaddr_in from;
+ int fromlen;
+
+ /*
+ * See if inetd started us
+ */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ from_inetd = 0;
+ sock = RPC_ANYSOCK;
+ proto = IPPROTO_UDP;
+ }
+
+ if (!from_inetd) {
+ daemon(0, 0);
+
+ (void) pmap_unset(RUSERSPROG, RUSERSVERS_IDLE);
+ (void) pmap_unset(RUSERSPROG, RUSERSVERS_ORIG);
+
+ (void) signal(SIGINT, cleanup);
+ (void) signal(SIGTERM, cleanup);
+ (void) signal(SIGHUP, cleanup);
+ }
+
+ openlog("rpc.rusersd", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service.");
+ exit(1);
+ }
+ if (!svc_register(transp, RUSERSPROG, RUSERSVERS_IDLE, rusers_service, proto)) {
+ syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_IDLE, %s).", proto?"udp":"(inetd)");
+ exit(1);
+ }
+
+ if (!svc_register(transp, RUSERSPROG, RUSERSVERS_ORIG, rusers_service, proto)) {
+ syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_ORIG, %s).", proto?"udp":"(inetd)");
+ exit(1);
+ }
+
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ exit(1);
+}
diff --git a/libexec/rpc.rwalld/Makefile b/libexec/rpc.rwalld/Makefile
new file mode 100644
index 0000000..5d7b492
--- /dev/null
+++ b/libexec/rpc.rwalld/Makefile
@@ -0,0 +1,10 @@
+# $Id: Makefile,v 1.2 1993/11/10 03:46:23 smace Exp $
+
+PROG = rpc.rwalld
+SRCS = rwalld.c
+MAN8 = rpc.rwalld.8
+
+DPADD= ${LIBRPCSVC} ${LIBUTIL}
+LDADD= -lrpcsvc -lutil
+
+.include <bsd.prog.mk>
diff --git a/libexec/rpc.rwalld/rpc.rwalld.8 b/libexec/rpc.rwalld/rpc.rwalld.8
new file mode 100644
index 0000000..eadadf4
--- /dev/null
+++ b/libexec/rpc.rwalld/rpc.rwalld.8
@@ -0,0 +1,67 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 1985, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: rpc.rwalld.8,v 1.1 1993/09/16 00:36:43 jtc Exp $
+.\"
+.Dd June 7, 1993
+.Dt RPC.RWALLD 8
+.Os BSD 4.3
+.Sh NAME
+.Nm rpc.rwalld
+.Nd write messages to users currently logged in server
+.Sh SYNOPSIS
+.Nm /usr/libexec/rpc.rwalld
+.Sh DESCRIPTION
+.Nm rpc.rwalld
+is a server which will send a message to users
+currently logged in to the system. This server
+invokes the
+.Xr wall 1
+command to actually write the messages to the
+system.
+.Pp
+Messages are sent to this server by the
+.Xr rwall 1
+command.
+The
+.Nm rpc.rwalld
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+.Nm rpc.rwalld
+uses an RPC protocol defined in
+.Pa /usr/include/rpcsvc/rwall.x .
+.Sh SEE ALSO
+.Xr rwall 1 ,
+.Xr wall 1 ,
+.Xr inetd 8
diff --git a/libexec/rpc.rwalld/rwalld.c b/libexec/rpc.rwalld/rwalld.c
new file mode 100644
index 0000000..adb6c3f
--- /dev/null
+++ b/libexec/rpc.rwalld/rwalld.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 1993 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: rwalld.c,v 1.2 1994/11/18 22:50:22 ats Exp $";
+#endif /* not lint */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/rwall.h>
+
+#ifdef OSF
+#define WALL_CMD "/usr/sbin/wall"
+#else
+#define WALL_CMD "/usr/bin/wall -n"
+#endif
+
+void wallprog_1();
+void possess();
+void killkids();
+
+int nodaemon = 0;
+int from_inetd = 1;
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ SVCXPRT *transp;
+ int s, salen;
+ struct sockaddr_in sa;
+ int sock = 0;
+ int proto = 0;
+
+ if (argc == 2 && !strcmp(argv[1], "-n"))
+ nodaemon = 1;
+ if (argc != 1 && !nodaemon) {
+ printf("usage: %s [-n]\n", argv[0]);
+ exit(1);
+ }
+
+ if (geteuid() == 0) {
+ struct passwd *pep = getpwnam("nobody");
+ if (pep)
+ setuid(pep->pw_uid);
+ else
+ setuid(getuid());
+ }
+
+ /*
+ * See if inetd started us
+ */
+ salen = sizeof(sa);
+ if (getsockname(0, (struct sockaddr *)&sa, &salen) < 0) {
+ from_inetd = 0;
+ sock = RPC_ANYSOCK;
+ proto = IPPROTO_UDP;
+ }
+
+ if (!from_inetd) {
+ if (!nodaemon)
+ possess();
+
+ (void)pmap_unset(WALLPROG, WALLVERS);
+ if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ perror("socket");
+ exit(1);
+ }
+ bzero((char *)&sa, sizeof sa);
+ if (bind(s, (struct sockaddr *)&sa, sizeof sa) < 0) {
+ perror("bind");
+ exit(1);
+ }
+
+ salen = sizeof sa;
+ if (getsockname(s, (struct sockaddr *)&sa, &salen)) {
+ perror("getsockname");
+ exit(1);
+ }
+
+ pmap_set(WALLPROG, WALLVERS, IPPROTO_UDP, ntohs(sa.sin_port));
+ if (dup2(s, 0) < 0) {
+ perror("dup2");
+ exit(1);
+ }
+ (void)pmap_unset(WALLPROG, WALLVERS);
+ }
+
+ (void)signal(SIGCHLD, killkids);
+
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ (void)fprintf(stderr, "cannot create udp service.\n");
+ exit(1);
+ }
+ if (!svc_register(transp, WALLPROG, WALLVERS, wallprog_1, proto)) {
+ (void)fprintf(stderr, "unable to register (WALLPROG, WALLVERS, udp).\n");
+ exit(1);
+ }
+ svc_run();
+ (void)fprintf(stderr, "svc_run returned\n");
+ exit(1);
+
+}
+
+void possess()
+{
+ daemon(0, 0);
+}
+
+void killkids()
+{
+ while(wait4(-1, NULL, WNOHANG, NULL) > 0)
+ ;
+}
+
+void *wallproc_wall_1(s)
+ char **s;
+{
+ /* fork, popen wall with special option, and send the message */
+ if (fork() == 0) {
+ FILE *pfp;
+
+ pfp = popen(WALL_CMD, "w");
+ if (pfp != NULL) {
+ fprintf(pfp, "\007\007%s", *s);
+ pclose(pfp);
+ exit(0);
+ }
+ }
+}
+
+void
+wallprog_1(rqstp, transp)
+ struct svc_req *rqstp;
+ SVCXPRT *transp;
+{
+ union {
+ char *wallproc_wall_1_arg;
+ } argument;
+ char *result;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ char *(*local)();
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, xdr_void, (char *)NULL);
+ goto leave;
+
+ case WALLPROC_WALL:
+ xdr_argument = xdr_wrapstring;
+ xdr_result = xdr_void;
+ local = (char *(*)()) wallproc_wall_1;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ goto leave;
+ }
+ bzero((char *)&argument, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ goto leave;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, &argument)) {
+ (void)fprintf(stderr, "unable to free arguments\n");
+ exit(1);
+ }
+leave:
+ if (from_inetd)
+ exit(0);
+}
diff --git a/libexec/rshd/Makefile b/libexec/rshd/Makefile
new file mode 100644
index 0000000..984a6b7
--- /dev/null
+++ b/libexec/rshd/Makefile
@@ -0,0 +1,16 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/4/93
+# $Id: Makefile,v 1.3 1994/09/29 13:06:37 csgr Exp $
+
+PROG= rshd
+SRCS= rshd.c
+MAN8= rshd.8
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \
+ || defined(MAKE_EBONES))
+CFLAGS+=-DKERBEROS -DCRYPT
+DPADD= ${LIBKRB} ${LIBDES}
+LDADD= -lkrb -ldes
+DISTRIBUTION= krb
+.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/rshd/rshd.8 b/libexec/rshd/rshd.8
new file mode 100644
index 0000000..82e1991
--- /dev/null
+++ b/libexec/rshd/rshd.8
@@ -0,0 +1,209 @@
+.\" Copyright (c) 1983, 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rshd.8 8.1 (Berkeley) 6/4/93
+.\"
+.Dd June 4, 1993
+.Dt RSHD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm rshd
+.Nd remote shell server
+.Sh SYNOPSIS
+.Nm rshd
+.Op Fl alnL
+.Sh DESCRIPTION
+The
+.Nm rshd
+server
+is the server for the
+.Xr rcmd 3
+routine and, consequently, for the
+.Xr rsh 1
+program. The server provides remote execution facilities
+with authentication based on privileged port numbers from trusted hosts.
+.Pp
+The
+.Nm rshd
+server
+listens for service requests at the port indicated in
+the ``cmd'' service specification; see
+.Xr services 5 .
+When a service request is received the following protocol
+is initiated:
+.Bl -enum
+.It
+The server checks the client's source port.
+If the port is not in the range 512-1023, the server
+aborts the connection.
+.It
+The server reads characters from the socket up
+to a null (`\e0') byte. The resultant string is
+interpreted as an
+.Tn ASCII
+number, base 10.
+.It
+If the number received in step 2 is non-zero,
+it is interpreted as the port number of a secondary
+stream to be used for the
+.Em stderr .
+A second connection is then created to the specified
+port on the client's machine. The source port of this
+second connection is also in the range 512-1023.
+.It
+The server checks the client's source address
+and requests the corresponding host name (see
+.Xr gethostbyaddr 3 ,
+.Xr hosts 5
+and
+.Xr named 8 ) .
+If the hostname cannot be determined,
+the dot-notation representation of the host address is used.
+If the hostname is in the same domain as the server (according to
+the last two components of the domain name),
+or if the
+.Fl a
+option is given,
+the addresses for the hostname are requested,
+verifying that the name and address correspond.
+If address verification fails, the connection is aborted
+with the message, ``Host address mismatch.''
+.It
+A null terminated user name of at most 16 characters
+is retrieved on the initial socket. This user name
+is interpreted as the user identity on the
+.Em client Ns 's
+machine.
+.It
+A null terminated user name of at most 16 characters
+is retrieved on the initial socket. This user name
+is interpreted as a user identity to use on the
+.Sy server Ns 's
+machine.
+.It
+A null terminated command to be passed to a
+shell is retrieved on the initial socket. The length of
+the command is limited by the upper bound on the size of
+the system's argument list.
+.It
+.Nm Rshd
+then validates the user using
+.Xr ruserok 3 ,
+which uses the file
+.Pa /etc/hosts.equiv
+and the
+.Pa .rhosts
+file found in the user's home directory. The
+.Fl l
+option prevents
+.Xr ruserok 3
+from doing any validation based on the user's ``.rhosts'' file,
+unless the user is the superuser.
+.It
+If the file
+.Pa /etc/nologin
+exists and the user is not the superuser,
+the connection is closed.
+.It
+A null byte is returned on the initial socket
+and the command line is passed to the normal login
+shell of the user. The
+shell inherits the network connections established
+by
+.Nm rshd .
+.El
+.Pp
+Transport-level keepalive messages are enabled unless the
+.Fl n
+option is present.
+The use of keepalive messages allows sessions to be timed out
+if the client crashes or becomes unreachable.
+.Pp
+The
+.Fl L
+option causes all successful accesses to be logged to
+.Xr syslogd 8
+as
+.Li auth.info
+messages.
+.Sh DIAGNOSTICS
+Except for the last one listed below,
+all diagnostic messages
+are returned on the initial socket,
+after which any network connections are closed.
+An error is indicated by a leading byte with a value of
+1 (0 is returned in step 10 above upon successful completion
+of all the steps prior to the execution of the login shell).
+.Bl -tag -width indent
+.It Sy Locuser too long.
+The name of the user on the client's machine is
+longer than 16 characters.
+.It Sy Ruser too long.
+The name of the user on the remote machine is
+longer than 16 characters.
+.It Sy Command too long .
+The command line passed exceeds the size of the argument
+list (as configured into the system).
+.It Sy Login incorrect.
+No password file entry for the user name existed.
+.It Sy Remote directory.
+The
+.Xr chdir
+command to the home directory failed.
+.It Sy Permission denied.
+The authentication procedure described above failed.
+.It Sy Can't make pipe.
+The pipe needed for the
+.Em stderr ,
+wasn't created.
+.It Sy Can't fork; try again.
+A
+.Xr fork
+by the server failed.
+.It Sy <shellname>: ...
+The user's login shell could not be started. This message is returned
+on the connection associated with the
+.Em stderr ,
+and is not preceded by a flag byte.
+.El
+.Sh SEE ALSO
+.Xr rsh 1 ,
+.Xr rcmd 3 ,
+.Xr ruserok 3
+.Sh BUGS
+The authentication procedure used here assumes the integrity
+of each client machine and the connecting medium. This is
+insecure, but is useful in an ``open'' environment.
+.Pp
+A facility to allow all data exchanges to be encrypted should be
+present.
+.Pp
+A more extensible protocol (such as Telnet) should be used.
diff --git a/libexec/rshd/rshd.c b/libexec/rshd/rshd.c
new file mode 100644
index 0000000..e9e6d83
--- /dev/null
+++ b/libexec/rshd/rshd.c
@@ -0,0 +1,785 @@
+/*-
+ * Copyright (c) 1988, 1989, 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94";
+#endif /* not lint */
+
+/*
+ * remote shell server:
+ * [port]\0
+ * remuser\0
+ * locuser\0
+ * command\0
+ * data
+ */
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+int keepalive = 1;
+int check_all;
+int log_success; /* If TRUE, log all successful accesses */
+int sent_null;
+
+void doit __P((struct sockaddr_in *));
+void error __P((const char *, ...));
+void getstr __P((char *, int, char *));
+int local_domain __P((char *));
+char *topdomain __P((char *));
+void usage __P((void));
+
+#ifdef KERBEROS
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+#define VERSION_SIZE 9
+#define SECURE_MESSAGE "This rsh session is using DES encryption for all transmissions.\r\n"
+#define OPTIONS "alnkvxL"
+char authbuf[sizeof(AUTH_DAT)];
+char tickbuf[sizeof(KTEXT_ST)];
+int doencrypt, use_kerberos, vacuous;
+Key_schedule schedule;
+#else
+#define OPTIONS "alnL"
+#endif
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int __check_rhosts_file;
+ struct linger linger;
+ int ch, on = 1, fromlen;
+ struct sockaddr_in from;
+
+ openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
+
+ opterr = 0;
+ while ((ch = getopt(argc, argv, OPTIONS)) != EOF)
+ switch (ch) {
+ case 'a':
+ check_all = 1;
+ break;
+ case 'l':
+ __check_rhosts_file = 0;
+ break;
+ case 'n':
+ keepalive = 0;
+ break;
+#ifdef KERBEROS
+ case 'k':
+ use_kerberos = 1;
+ break;
+
+ case 'v':
+ vacuous = 1;
+ break;
+
+#ifdef CRYPT
+ case 'x':
+ doencrypt = 1;
+ break;
+#endif
+#endif
+ case 'L':
+ log_success = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+#ifdef KERBEROS
+ if (use_kerberos && vacuous) {
+ syslog(LOG_ERR, "only one of -k and -v allowed");
+ exit(2);
+ }
+#ifdef CRYPT
+ if (doencrypt && !use_kerberos) {
+ syslog(LOG_ERR, "-k is required for -x");
+ exit(2);
+ }
+#endif
+#endif
+
+ fromlen = sizeof (from);
+ if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ syslog(LOG_ERR, "getpeername: %m");
+ _exit(1);
+ }
+ if (keepalive &&
+ setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
+ sizeof(on)) < 0)
+ syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+ linger.l_onoff = 1;
+ linger.l_linger = 60; /* XXX */
+ if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger,
+ sizeof (linger)) < 0)
+ syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
+ doit(&from);
+ /* NOTREACHED */
+}
+
+char username[20] = "USER=";
+char homedir[64] = "HOME=";
+char shell[64] = "SHELL=";
+char path[100] = "PATH=";
+char *envinit[] =
+ {homedir, shell, path, username, 0};
+char **environ;
+
+void
+doit(fromp)
+ struct sockaddr_in *fromp;
+{
+ extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */
+ struct hostent *hp;
+ struct passwd *pwd;
+ u_short port;
+ fd_set ready, readfrom;
+ int cc, nfd, pv[2], pid, s;
+ int one = 1;
+ char *hostname, *errorstr, *errorhost;
+ char *cp, sig, buf[BUFSIZ];
+ char cmdbuf[NCARGS+1], locuser[16], remuser[16];
+ char remotehost[2 * MAXHOSTNAMELEN + 1];
+
+#ifdef KERBEROS
+ AUTH_DAT *kdata = (AUTH_DAT *) NULL;
+ KTEXT ticket = (KTEXT) NULL;
+ char instance[INST_SZ], version[VERSION_SIZE];
+ struct sockaddr_in fromaddr;
+ int rc;
+ long authopts;
+ int pv1[2], pv2[2];
+ fd_set wready, writeto;
+
+ fromaddr = *fromp;
+#endif
+
+ (void) signal(SIGINT, SIG_DFL);
+ (void) signal(SIGQUIT, SIG_DFL);
+ (void) signal(SIGTERM, SIG_DFL);
+#ifdef DEBUG
+ { int t = open(_PATH_TTY, 2);
+ if (t >= 0) {
+ ioctl(t, TIOCNOTTY, (char *)0);
+ (void) close(t);
+ }
+ }
+#endif
+ fromp->sin_port = ntohs((u_short)fromp->sin_port);
+ if (fromp->sin_family != AF_INET) {
+ syslog(LOG_ERR, "malformed \"from\" address (af %d)\n",
+ fromp->sin_family);
+ exit(1);
+ }
+#ifdef IP_OPTIONS
+ {
+ u_char optbuf[BUFSIZ/3], *cp;
+ char lbuf[BUFSIZ], *lp;
+ int optsize = sizeof(optbuf), ipproto;
+ struct protoent *ip;
+
+ if ((ip = getprotobyname("ip")) != NULL)
+ ipproto = ip->p_proto;
+ else
+ ipproto = IPPROTO_IP;
+ if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) &&
+ optsize != 0) {
+ lp = lbuf;
+ for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
+ sprintf(lp, " %2.2x", *cp);
+ syslog(LOG_NOTICE,
+ "Connection received from %s using IP options (ignored):%s",
+ inet_ntoa(fromp->sin_addr), lbuf);
+ if (setsockopt(0, ipproto, IP_OPTIONS,
+ (char *)NULL, optsize) != 0) {
+ syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
+ exit(1);
+ }
+ }
+ }
+#endif
+
+#ifdef KERBEROS
+ if (!use_kerberos)
+#endif
+ if (fromp->sin_port >= IPPORT_RESERVED ||
+ fromp->sin_port < IPPORT_RESERVED/2) {
+ syslog(LOG_NOTICE|LOG_AUTH,
+ "Connection from %s on illegal port %u",
+ inet_ntoa(fromp->sin_addr),
+ fromp->sin_port);
+ exit(1);
+ }
+
+ (void) alarm(60);
+ port = 0;
+ for (;;) {
+ char c;
+ if ((cc = read(STDIN_FILENO, &c, 1)) != 1) {
+ if (cc < 0)
+ syslog(LOG_NOTICE, "read: %m");
+ shutdown(0, 1+1);
+ exit(1);
+ }
+ if (c== 0)
+ break;
+ port = port * 10 + c - '0';
+ }
+
+ (void) alarm(0);
+ if (port != 0) {
+ int lport = IPPORT_RESERVED - 1;
+ s = rresvport(&lport);
+ if (s < 0) {
+ syslog(LOG_ERR, "can't get stderr port: %m");
+ exit(1);
+ }
+#ifdef KERBEROS
+ if (!use_kerberos)
+#endif
+ if (port >= IPPORT_RESERVED) {
+ syslog(LOG_ERR, "2nd port not reserved\n");
+ exit(1);
+ }
+ fromp->sin_port = htons(port);
+ if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0) {
+ syslog(LOG_INFO, "connect second port %d: %m", port);
+ exit(1);
+ }
+ }
+
+#ifdef KERBEROS
+ if (vacuous) {
+ error("rshd: remote host requires Kerberos authentication\n");
+ exit(1);
+ }
+#endif
+
+#ifdef notdef
+ /* from inetd, socket is already on 0, 1, 2 */
+ dup2(f, 0);
+ dup2(f, 1);
+ dup2(f, 2);
+#endif
+ errorstr = NULL;
+ hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr),
+ fromp->sin_family);
+ if (hp) {
+ /*
+ * If name returned by gethostbyaddr is in our domain,
+ * attempt to verify that we haven't been fooled by someone
+ * in a remote net; look up the name and check that this
+ * address corresponds to the name.
+ */
+ hostname = hp->h_name;
+#ifdef KERBEROS
+ if (!use_kerberos)
+#endif
+ if (check_all || local_domain(hp->h_name)) {
+ strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
+ remotehost[sizeof(remotehost) - 1] = 0;
+ errorhost = remotehost;
+ hp = gethostbyname(remotehost);
+ if (hp == NULL) {
+ syslog(LOG_INFO,
+ "Couldn't look up address for %s",
+ remotehost);
+ errorstr =
+ "Couldn't look up address for your host (%s)\n";
+ hostname = inet_ntoa(fromp->sin_addr);
+ } else for (; ; hp->h_addr_list++) {
+ if (hp->h_addr_list[0] == NULL) {
+ syslog(LOG_NOTICE,
+ "Host addr %s not listed for host %s",
+ inet_ntoa(fromp->sin_addr),
+ hp->h_name);
+ errorstr =
+ "Host address mismatch for %s\n";
+ hostname = inet_ntoa(fromp->sin_addr);
+ break;
+ }
+ if (!bcmp(hp->h_addr_list[0],
+ (caddr_t)&fromp->sin_addr,
+ sizeof(fromp->sin_addr))) {
+ hostname = hp->h_name;
+ break;
+ }
+ }
+ }
+ } else
+ errorhost = hostname = inet_ntoa(fromp->sin_addr);
+
+#ifdef KERBEROS
+ if (use_kerberos) {
+ kdata = (AUTH_DAT *) authbuf;
+ ticket = (KTEXT) tickbuf;
+ authopts = 0L;
+ strcpy(instance, "*");
+ version[VERSION_SIZE - 1] = '\0';
+#ifdef CRYPT
+ if (doencrypt) {
+ struct sockaddr_in local_addr;
+ rc = sizeof(local_addr);
+ if (getsockname(0, (struct sockaddr *)&local_addr,
+ &rc) < 0) {
+ syslog(LOG_ERR, "getsockname: %m");
+ error("rlogind: getsockname: %m");
+ exit(1);
+ }
+ authopts = KOPT_DO_MUTUAL;
+ rc = krb_recvauth(authopts, 0, ticket,
+ "rcmd", instance, &fromaddr,
+ &local_addr, kdata, "", schedule,
+ version);
+ des_set_key(&kdata->session, schedule);
+ } else
+#endif
+ rc = krb_recvauth(authopts, 0, ticket, "rcmd",
+ instance, &fromaddr,
+ (struct sockaddr_in *) 0,
+ kdata, "", (bit_64 *) 0, version);
+ if (rc != KSUCCESS) {
+ error("Kerberos authentication failure: %s\n",
+ krb_err_txt[rc]);
+ exit(1);
+ }
+ } else
+#endif
+ getstr(remuser, sizeof(remuser), "remuser");
+
+ getstr(locuser, sizeof(locuser), "locuser");
+ getstr(cmdbuf, sizeof(cmdbuf), "command");
+ setpwent();
+ pwd = getpwnam(locuser);
+ if (pwd == NULL) {
+ syslog(LOG_INFO|LOG_AUTH,
+ "%s@%s as %s: unknown login. cmd='%.80s'",
+ remuser, hostname, locuser, cmdbuf);
+ if (errorstr == NULL)
+ errorstr = "Login incorrect.\n";
+ goto fail;
+ }
+ if (chdir(pwd->pw_dir) < 0) {
+ (void) chdir("/");
+#ifdef notdef
+ syslog(LOG_INFO|LOG_AUTH,
+ "%s@%s as %s: no home directory. cmd='%.80s'",
+ remuser, hostname, locuser, cmdbuf);
+ error("No remote directory.\n");
+ exit(1);
+#endif
+ }
+
+#ifdef KERBEROS
+ if (use_kerberos) {
+ if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0') {
+ if (kuserok(kdata, locuser) != 0) {
+ syslog(LOG_INFO|LOG_AUTH,
+ "Kerberos rsh denied to %s.%s@%s",
+ kdata->pname, kdata->pinst, kdata->prealm);
+ error("Permission denied.\n");
+ exit(1);
+ }
+ }
+ } else
+#endif
+
+ if (errorstr ||
+ (pwd->pw_expire && time(NULL) >= pwd->pw_expire) ||
+ (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' &&
+ iruserok(fromp->sin_addr.s_addr, pwd->pw_uid == 0,
+ remuser, locuser) < 0)) {
+ if (__rcmd_errstr)
+ syslog(LOG_INFO|LOG_AUTH,
+ "%s@%s as %s: permission denied (%s). cmd='%.80s'",
+ remuser, hostname, locuser, __rcmd_errstr,
+ cmdbuf);
+ else
+ syslog(LOG_INFO|LOG_AUTH,
+ "%s@%s as %s: permission denied. cmd='%.80s'",
+ remuser, hostname, locuser, cmdbuf);
+fail:
+ if (errorstr == NULL)
+ errorstr = "Permission denied.\n";
+ error(errorstr, errorhost);
+ exit(1);
+ }
+
+ if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) {
+ error("Logins currently disabled.\n");
+ exit(1);
+ }
+#if BSD > 43
+ /* before fork, while we're session leader */
+ if (setlogin(pwd->pw_name) < 0)
+ syslog(LOG_ERR, "setlogin() failed: %m");
+#endif
+
+ (void) write(STDERR_FILENO, "\0", 1);
+ sent_null = 1;
+
+ if (port) {
+ if (pipe(pv) < 0) {
+ error("Can't make pipe.\n");
+ exit(1);
+ }
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt) {
+ if (pipe(pv1) < 0) {
+ error("Can't make 2nd pipe.\n");
+ exit(1);
+ }
+ if (pipe(pv2) < 0) {
+ error("Can't make 3rd pipe.\n");
+ exit(1);
+ }
+ }
+#endif
+#endif
+ pid = fork();
+ if (pid == -1) {
+ error("Can't fork; try again.\n");
+ exit(1);
+ }
+ if (pid) {
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt) {
+ static char msg[] = SECURE_MESSAGE;
+ (void) close(pv1[1]);
+ (void) close(pv2[1]);
+ des_write(s, msg, sizeof(msg) - 1);
+
+ } else
+#endif
+#endif
+ {
+ (void) close(0);
+ (void) close(1);
+ }
+ (void) close(2);
+ (void) close(pv[1]);
+
+ FD_ZERO(&readfrom);
+ FD_SET(s, &readfrom);
+ FD_SET(pv[0], &readfrom);
+ if (pv[0] > s)
+ nfd = pv[0];
+ else
+ nfd = s;
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt) {
+ FD_ZERO(&writeto);
+ FD_SET(pv2[0], &writeto);
+ FD_SET(pv1[0], &readfrom);
+
+ nfd = MAX(nfd, pv2[0]);
+ nfd = MAX(nfd, pv1[0]);
+ } else
+#endif
+#endif
+ ioctl(pv[0], FIONBIO, (char *)&one);
+
+ /* should set s nbio! */
+ nfd++;
+ do {
+ ready = readfrom;
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt) {
+ wready = writeto;
+ if (select(nfd, &ready,
+ &wready, (fd_set *) 0,
+ (struct timeval *) 0) < 0)
+ break;
+ } else
+#endif
+#endif
+ if (select(nfd, &ready, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0) < 0)
+ break;
+ if (FD_ISSET(s, &ready)) {
+ int ret;
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt)
+ ret = des_read(s, &sig, 1);
+ else
+#endif
+#endif
+ ret = read(s, &sig, 1);
+ if (ret <= 0)
+ FD_CLR(s, &readfrom);
+ else
+ killpg(pid, sig);
+ }
+ if (FD_ISSET(pv[0], &ready)) {
+ errno = 0;
+ cc = read(pv[0], buf, sizeof(buf));
+ if (cc <= 0) {
+ shutdown(s, 1+1);
+ FD_CLR(pv[0], &readfrom);
+ } else {
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt)
+ (void)
+ des_write(s, buf, cc);
+ else
+#endif
+#endif
+ (void)
+ write(s, buf, cc);
+ }
+ }
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt && FD_ISSET(pv1[0], &ready)) {
+ errno = 0;
+ cc = read(pv1[0], buf, sizeof(buf));
+ if (cc <= 0) {
+ shutdown(pv1[0], 1+1);
+ FD_CLR(pv1[0], &readfrom);
+ } else
+ (void) des_write(STDOUT_FILENO,
+ buf, cc);
+ }
+
+ if (doencrypt && FD_ISSET(pv2[0], &wready)) {
+ errno = 0;
+ cc = des_read(STDIN_FILENO,
+ buf, sizeof(buf));
+ if (cc <= 0) {
+ shutdown(pv2[0], 1+1);
+ FD_CLR(pv2[0], &writeto);
+ } else
+ (void) write(pv2[0], buf, cc);
+ }
+#endif
+#endif
+
+ } while (FD_ISSET(s, &readfrom) ||
+#ifdef CRYPT
+#ifdef KERBEROS
+ (doencrypt && FD_ISSET(pv1[0], &readfrom)) ||
+#endif
+#endif
+ FD_ISSET(pv[0], &readfrom));
+ exit(0);
+ }
+ setpgrp(0, getpid());
+ (void) close(s);
+ (void) close(pv[0]);
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt) {
+ close(pv1[0]); close(pv2[0]);
+ dup2(pv1[1], 1);
+ dup2(pv2[1], 0);
+ close(pv1[1]);
+ close(pv2[1]);
+ }
+#endif
+#endif
+ dup2(pv[1], 2);
+ close(pv[1]);
+ }
+ if (*pwd->pw_shell == '\0')
+ pwd->pw_shell = _PATH_BSHELL;
+ (void) setgid((gid_t)pwd->pw_gid);
+ initgroups(pwd->pw_name, pwd->pw_gid);
+ (void) setuid((uid_t)pwd->pw_uid);
+ environ = envinit;
+ strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
+ strcat(path, _PATH_DEFPATH);
+ strncat(shell, pwd->pw_shell, sizeof(shell)-7);
+ strncat(username, pwd->pw_name, sizeof(username)-6);
+ cp = strrchr(pwd->pw_shell, '/');
+ if (cp)
+ cp++;
+ else
+ cp = pwd->pw_shell;
+ endpwent();
+ if (log_success || pwd->pw_uid == 0) {
+#ifdef KERBEROS
+ if (use_kerberos)
+ syslog(LOG_INFO|LOG_AUTH,
+ "Kerberos shell from %s.%s@%s on %s as %s, cmd='%.80s'",
+ kdata->pname, kdata->pinst, kdata->prealm,
+ hostname, locuser, cmdbuf);
+ else
+#endif
+ syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
+ remuser, hostname, locuser, cmdbuf);
+ }
+ execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
+ perror(pwd->pw_shell);
+ exit(1);
+}
+
+/*
+ * Report error to client. Note: can't be used until second socket has
+ * connected to client, or older clients will hang waiting for that
+ * connection first.
+ */
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+error(const char *fmt, ...)
+#else
+error(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ int len;
+ char *bp, buf[BUFSIZ];
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ bp = buf;
+ if (sent_null == 0) {
+ *bp++ = 1;
+ len = 1;
+ } else
+ len = 0;
+ (void)vsnprintf(bp, sizeof(buf) - 1, fmt, ap);
+ (void)write(STDERR_FILENO, buf, len + strlen(bp));
+}
+
+void
+getstr(buf, cnt, err)
+ char *buf, *err;
+ int cnt;
+{
+ char c;
+
+ do {
+ if (read(STDIN_FILENO, &c, 1) != 1)
+ exit(1);
+ *buf++ = c;
+ if (--cnt == 0) {
+ error("%s too long\n", err);
+ exit(1);
+ }
+ } while (c != 0);
+}
+
+/*
+ * Check whether host h is in our local domain,
+ * defined as sharing the last two components of the domain part,
+ * or the entire domain part if the local domain has only one component.
+ * If either name is unqualified (contains no '.'),
+ * assume that the host is local, as it will be
+ * interpreted as such.
+ */
+int
+local_domain(h)
+ char *h;
+{
+ char localhost[MAXHOSTNAMELEN];
+ char *p1, *p2;
+
+ localhost[0] = 0;
+ (void) gethostname(localhost, sizeof(localhost));
+ p1 = topdomain(localhost);
+ p2 = topdomain(h);
+ if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
+ return (1);
+ return (0);
+}
+
+char *
+topdomain(h)
+ char *h;
+{
+ char *p, *maybe = NULL;
+ int dots = 0;
+
+ for (p = h + strlen(h); p >= h; p--) {
+ if (*p == '.') {
+ if (++dots == 2)
+ return (p);
+ maybe = p;
+ }
+ }
+ return (maybe);
+}
+
+void
+usage()
+{
+
+ syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS);
+ exit(2);
+}
diff --git a/libexec/rtld-aout/Makefile b/libexec/rtld-aout/Makefile
new file mode 100644
index 0000000..74e3c31
--- /dev/null
+++ b/libexec/rtld-aout/Makefile
@@ -0,0 +1,22 @@
+# $Id: Makefile,v 1.14 1995/03/04 17:46:23 nate Exp $
+
+PROG= ld.so
+SRCS= mdprologue.S rtld.c malloc.c shlib.c etc.c md.c
+MAN1= rtld.1
+LDDIR?= $(.CURDIR)/..
+PICFLAG=-fpic
+CFLAGS+=-I$(LDDIR) -I$(.CURDIR) -I$(LDDIR)/$(MACHINE) $(PICFLAG) -DRTLD
+LDFLAGS+=-Bshareable -Bsymbolic -assert nosymbolic
+ASFLAGS+=-k
+DPADD+= ${LIBC:S/c.a/c_pic.a/} ${LIBC:S/c.a/gcc_pic.a/}
+LDADD+= -lc_pic -lgcc_pic
+BINDIR= /usr/libexec
+INSTALLFLAGS+= -fschg
+MLINKS= rtld.1 ld.so.1
+
+.PATH: $(LDDIR) $(LDDIR)/$(MACHINE)
+
+$(PROG): ${OBJS} ${DPADD}
+ $(LD) -o $(PROG) $(LDFLAGS) $(OBJS) $(LDADD)
+
+.include <bsd.prog.mk>
diff --git a/libexec/rtld-aout/i386/md-static-funcs.c b/libexec/rtld-aout/i386/md-static-funcs.c
new file mode 100644
index 0000000..64491e3
--- /dev/null
+++ b/libexec/rtld-aout/i386/md-static-funcs.c
@@ -0,0 +1,16 @@
+/*
+ * $Id: md-static-funcs.c,v 1.2 1994/02/13 20:42:06 jkh Exp $
+ *
+ * Called by ld.so when onanating.
+ * This *must* be a static function, so it is not called through a jmpslot.
+ */
+
+static inline void
+md_relocate_simple(r, relocation, addr)
+struct relocation_info *r;
+long relocation;
+char *addr;
+{
+ *(long *)addr += relocation;
+}
+
diff --git a/libexec/rtld-aout/i386/md.c b/libexec/rtld-aout/i386/md.c
new file mode 100644
index 0000000..07271fc
--- /dev/null
+++ b/libexec/rtld-aout/i386/md.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: md.c,v 1.11 1994/12/23 22:31:12 nate Exp $
+ */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <err.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <stab.h>
+#include <string.h>
+
+#include "ld.h"
+
+/*
+ * Get relocation addend corresponding to relocation record RP
+ * from address ADDR
+ */
+long
+md_get_addend(rp, addr)
+struct relocation_info *rp;
+unsigned char *addr;
+{
+ switch (RELOC_TARGET_SIZE(rp)) {
+ case 0:
+ return get_byte(addr);
+ case 1:
+ return get_short(addr);
+ case 2:
+ return get_long(addr);
+ default:
+ errx(1, "Unsupported relocation size: %x",
+ RELOC_TARGET_SIZE(rp));
+ }
+}
+
+/*
+ * Put RELOCATION at ADDR according to relocation record RP.
+ */
+void
+md_relocate(rp, relocation, addr, relocatable_output)
+struct relocation_info *rp;
+long relocation;
+unsigned char *addr;
+int relocatable_output;
+{
+ switch (RELOC_TARGET_SIZE(rp)) {
+ case 0:
+ put_byte(addr, relocation);
+ break;
+ case 1:
+ put_short(addr, relocation);
+ break;
+ case 2:
+ put_long(addr, relocation);
+ break;
+ default:
+ errx(1, "Unsupported relocation size: %x",
+ RELOC_TARGET_SIZE(rp));
+ }
+}
+
+/*
+ * Machine dependent part of claim_rrs_reloc().
+ * Set RRS relocation type.
+ */
+int
+md_make_reloc(rp, r, type)
+struct relocation_info *rp, *r;
+int type;
+{
+ /* Relocation size */
+ r->r_length = rp->r_length;
+
+ if (RELOC_PCREL_P(rp))
+ r->r_pcrel = 1;
+
+ if (type & RELTYPE_RELATIVE)
+ r->r_relative = 1;
+
+ if (type & RELTYPE_COPY)
+ r->r_copy = 1;
+
+ return 0;
+}
+
+/*
+ * Set up a transfer from jmpslot at OFFSET (relative to the PLT table)
+ * to the binder slot (which is at offset 0 of the PLT).
+ */
+void
+md_make_jmpslot(sp, offset, index)
+jmpslot_t *sp;
+long offset;
+long index;
+{
+ /*
+ * i386 PC-relative "fixed point" is located right after the
+ * instruction it pertains to.
+ */
+ u_long fudge = - (sizeof(sp->opcode) + sizeof(sp->addr) + offset);
+
+ sp->opcode = CALL;
+#if 0
+ sp->addr = fudge;
+#else
+ sp->addr[0] = fudge & 0xffff;
+ sp->addr[1] = fudge >> 16;
+#endif
+ sp->reloc_index = index;
+}
+
+/*
+ * Set up a "direct" transfer (ie. not through the run-time binder) from
+ * jmpslot at OFFSET to ADDR. Used by `ld' when the SYMBOLIC flag is on,
+ * and by `ld.so' after resolving the symbol.
+ * On the i386, we use the JMP instruction which is PC relative, so no
+ * further RRS relocations will be necessary for such a jmpslot.
+ */
+void
+md_fix_jmpslot(sp, offset, addr)
+jmpslot_t *sp;
+long offset;
+u_long addr;
+{
+ u_long fudge = addr - (sizeof(sp->opcode) + sizeof(sp->addr) + offset);
+
+ sp->opcode = JUMP;
+#if 0
+ sp->addr = fudge;
+#else
+ sp->addr[0] = fudge & 0xffff;
+ sp->addr[1] = fudge >> 16;
+#endif
+ sp->reloc_index = 0;
+}
+
+/*
+ * Update the relocation record for a RRS jmpslot.
+ */
+void
+md_make_jmpreloc(rp, r, type)
+struct relocation_info *rp, *r;
+int type;
+{
+ jmpslot_t *sp;
+
+ /*
+ * Fix relocation address to point to the correct
+ * location within this jmpslot.
+ */
+ r->r_address += sizeof(sp->opcode);
+
+ /* Relocation size */
+ r->r_length = 2;
+
+ /* Set relocation type */
+ r->r_jmptable = 1;
+ if (type & RELTYPE_RELATIVE)
+ r->r_relative = 1;
+
+}
+
+/*
+ * Set relocation type for a RRS GOT relocation.
+ */
+void
+md_make_gotreloc(rp, r, type)
+struct relocation_info *rp, *r;
+int type;
+{
+ r->r_baserel = 1;
+ if (type & RELTYPE_RELATIVE)
+ r->r_relative = 1;
+
+ /* Relocation size */
+ r->r_length = 2;
+}
+
+/*
+ * Set relocation type for a RRS copy operation.
+ */
+void
+md_make_cpyreloc(rp, r)
+struct relocation_info *rp, *r;
+{
+ /* Relocation size */
+ r->r_length = 2;
+
+ r->r_copy = 1;
+}
+
+void
+md_set_breakpoint(where, savep)
+long where;
+long *savep;
+{
+ *savep = *(long *)where;
+ *(char *)where = TRAP;
+}
+
+#ifndef RTLD
+
+#ifdef __FreeBSD__
+int netzmagic;
+#endif
+
+/*
+ * Initialize (output) exec header such that useful values are
+ * obtained from subsequent N_*() macro evaluations.
+ */
+void
+md_init_header(hp, magic, flags)
+struct exec *hp;
+int magic, flags;
+{
+#ifdef NetBSD
+ if (oldmagic || magic == QMAGIC)
+ hp->a_midmag = magic;
+ else
+ N_SETMAGIC((*hp), magic, MID_I386, flags);
+#endif
+#ifdef __FreeBSD__
+ if (oldmagic)
+ hp->a_midmag = magic;
+ else if (netzmagic)
+ N_SETMAGIC_NET((*hp), magic, MID_I386, flags);
+ else
+ N_SETMAGIC((*hp), magic, MID_I386, flags);
+#endif
+
+ /* TEXT_START depends on the value of outheader.a_entry. */
+ if (!(link_mode & SHAREABLE))
+ hp->a_entry = PAGSIZ;
+}
+#endif /* RTLD */
+
+
+#ifdef NEED_SWAP
+/*
+ * Byte swap routines for cross-linking.
+ */
+
+void
+md_swapin_exec_hdr(h)
+struct exec *h;
+{
+ int skip = 0;
+
+ if (!N_BADMAG(*h))
+ skip = 1;
+
+ swap_longs((long *)h + skip, sizeof(*h)/sizeof(long) - skip);
+}
+
+void
+md_swapout_exec_hdr(h)
+struct exec *h;
+{
+ /* NetBSD: Always leave magic alone */
+ int skip = 1;
+#if 0
+ if (N_GETMAGIC(*h) == OMAGIC)
+ skip = 0;
+#endif
+
+ swap_longs((long *)h + skip, sizeof(*h)/sizeof(long) - skip);
+}
+
+
+void
+md_swapin_reloc(r, n)
+struct relocation_info *r;
+int n;
+{
+ int bits;
+
+ for (; n; n--, r++) {
+ r->r_address = md_swap_long(r->r_address);
+ bits = ((int *)r)[1];
+ r->r_symbolnum = md_swap_long(bits) & 0x00ffffff;
+ r->r_pcrel = (bits & 1);
+ r->r_length = (bits >> 1) & 3;
+ r->r_extern = (bits >> 3) & 1;
+ r->r_baserel = (bits >> 4) & 1;
+ r->r_jmptable = (bits >> 5) & 1;
+ r->r_relative = (bits >> 6) & 1;
+#ifdef N_SIZE
+ r->r_copy = (bits >> 7) & 1;
+#endif
+ }
+}
+
+void
+md_swapout_reloc(r, n)
+struct relocation_info *r;
+int n;
+{
+ int bits;
+
+ for (; n; n--, r++) {
+ r->r_address = md_swap_long(r->r_address);
+ bits = md_swap_long(r->r_symbolnum) & 0xffffff00;
+ bits |= (r->r_pcrel & 1);
+ bits |= (r->r_length & 3) << 1;
+ bits |= (r->r_extern & 1) << 3;
+ bits |= (r->r_baserel & 1) << 4;
+ bits |= (r->r_jmptable & 1) << 5;
+ bits |= (r->r_relative & 1) << 6;
+#ifdef N_SIZE
+ bits |= (r->r_copy & 1) << 7;
+#endif
+ ((int *)r)[1] = bits;
+ }
+}
+
+void
+md_swapout_jmpslot(j, n)
+jmpslot_t *j;
+int n;
+{
+ for (; n; n--, j++) {
+ j->opcode = md_swap_short(j->opcode);
+ j->addr[0] = md_swap_short(j->addr[0]);
+ j->addr[1] = md_swap_short(j->addr[1]);
+ j->reloc_index = md_swap_short(j->reloc_index);
+ }
+}
+
+#endif /* NEED_SWAP */
diff --git a/libexec/rtld-aout/i386/md.h b/libexec/rtld-aout/i386/md.h
new file mode 100644
index 0000000..da5283d
--- /dev/null
+++ b/libexec/rtld-aout/i386/md.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: md.h,v 1.11 1994/12/23 22:31:14 nate Exp $
+ */
+
+
+#if defined(CROSS_LINKER) && defined(XHOST) && XHOST==sparc
+#define NEED_SWAP
+#endif
+
+#define MAX_ALIGNMENT (sizeof (long))
+
+#ifdef NetBSD
+#define PAGSIZ __LDPGSZ
+#else
+#define PAGSIZ 4096
+#endif
+
+#if defined(NetBSD) || defined(CROSS_LINKER)
+
+#define N_SET_FLAG(ex,f) (oldmagic || N_GETMAGIC(ex)==QMAGIC ? (0) : \
+ N_SETMAGIC(ex, \
+ N_GETMAGIC(ex), \
+ MID_MACHINE, \
+ N_GETFLAG(ex)|(f)))
+
+#define N_IS_DYNAMIC(ex) ((N_GETFLAG(ex) & EX_DYNAMIC))
+
+#define N_BADMID(ex) \
+ (N_GETMID(ex) != 0 && N_GETMID(ex) != MID_MACHINE)
+
+#endif
+
+/*
+ * FreeBSD does it differently
+ */
+#ifdef __FreeBSD__
+#define N_SET_FLAG(ex,f) (oldmagic ? (0) : \
+ (netzmagic == 0 ? \
+ N_SETMAGIC(ex, \
+ N_GETMAGIC(ex), \
+ MID_MACHINE, \
+ N_GETFLAG(ex)|(f)) : \
+ N_SETMAGIC_NET(ex, \
+ N_GETMAGIC_NET(ex), \
+ MID_MACHINE, \
+ N_GETFLAG_NET(ex)|(f)) ))
+
+#define N_IS_DYNAMIC(ex) ((N_GETMAGIC_NET(ex) == ZMAGIC) ? \
+ ((N_GETFLAG_NET(ex) & EX_DYNAMIC)) : \
+ ((N_GETFLAG(ex) & EX_DYNAMIC) ))
+#define N_BADMID(ex) 0
+#endif
+
+/*
+ * Should be handled by a.out.h ?
+ */
+#define N_ADJUST(ex) (((ex).a_entry < PAGSIZ) ? -PAGSIZ : 0)
+#define TEXT_START(ex) (N_TXTADDR(ex) + N_ADJUST(ex))
+#define DATA_START(ex) (N_DATADDR(ex) + N_ADJUST(ex))
+
+#define RELOC_STATICS_THROUGH_GOT_P(r) (0)
+#define JMPSLOT_NEEDS_RELOC (0)
+
+#define md_got_reloc(r) (0)
+
+#define md_get_rt_segment_addend(r,a) md_get_addend(r,a)
+
+/* Width of a Global Offset Table entry */
+#define GOT_ENTRY_SIZE 4
+typedef long got_t;
+
+typedef struct jmpslot {
+ u_short opcode;
+ u_short addr[2];
+ u_short reloc_index;
+#define JMPSLOT_RELOC_MASK 0xffff
+} jmpslot_t;
+
+#define NOP 0x90
+#define CALL 0xe890 /* NOP + CALL opcode */
+#define JUMP 0xe990 /* NOP + JMP opcode */
+#define TRAP 0xcc /* INT 3 */
+
+/*
+ * Byte swap defs for cross linking
+ */
+
+#if !defined(NEED_SWAP)
+
+#define md_swapin_exec_hdr(h)
+#define md_swapout_exec_hdr(h)
+#define md_swapin_symbols(s,n)
+#define md_swapout_symbols(s,n)
+#define md_swapin_zsymbols(s,n)
+#define md_swapout_zsymbols(s,n)
+#define md_swapin_reloc(r,n)
+#define md_swapout_reloc(r,n)
+#define md_swapin__dynamic(l)
+#define md_swapout__dynamic(l)
+#define md_swapin_section_dispatch_table(l)
+#define md_swapout_section_dispatch_table(l)
+#define md_swapin_so_debug(d)
+#define md_swapout_so_debug(d)
+#define md_swapin_rrs_hash(f,n)
+#define md_swapout_rrs_hash(f,n)
+#define md_swapin_sod(l,n)
+#define md_swapout_sod(l,n)
+#define md_swapout_jmpslot(j,n)
+#define md_swapout_got(g,n)
+#define md_swapin_ranlib_hdr(h,n)
+#define md_swapout_ranlib_hdr(h,n)
+
+#endif /* NEED_SWAP */
+
+#ifdef CROSS_LINKER
+
+#define get_byte(p) ( ((unsigned char *)(p))[0] )
+
+#define get_short(p) ( ( ((unsigned char *)(p))[0] << 8) | \
+ ( ((unsigned char *)(p))[1] ) \
+ )
+
+#define get_long(p) ( ( ((unsigned char *)(p))[0] << 24) | \
+ ( ((unsigned char *)(p))[1] << 16) | \
+ ( ((unsigned char *)(p))[2] << 8 ) | \
+ ( ((unsigned char *)(p))[3] ) \
+ )
+
+#define put_byte(p, v) { ((unsigned char *)(p))[0] = ((unsigned long)(v)); }
+
+#define put_short(p, v) { ((unsigned char *)(p))[0] = \
+ ((((unsigned long)(v)) >> 8) & 0xff); \
+ ((unsigned char *)(p))[1] = \
+ ((((unsigned long)(v)) ) & 0xff); }
+
+#define put_long(p, v) { ((unsigned char *)(p))[0] = \
+ ((((unsigned long)(v)) >> 24) & 0xff); \
+ ((unsigned char *)(p))[1] = \
+ ((((unsigned long)(v)) >> 16) & 0xff); \
+ ((unsigned char *)(p))[2] = \
+ ((((unsigned long)(v)) >> 8) & 0xff); \
+ ((unsigned char *)(p))[3] = \
+ ((((unsigned long)(v)) ) & 0xff); }
+
+#ifdef NEED_SWAP
+
+/* Define IO byte swapping routines */
+
+void md_swapin_exec_hdr __P((struct exec *));
+void md_swapout_exec_hdr __P((struct exec *));
+void md_swapin_reloc __P((struct relocation_info *, int));
+void md_swapout_reloc __P((struct relocation_info *, int));
+void md_swapout_jmpslot __P((jmpslot_t *, int));
+
+#define md_swapin_symbols(s,n) swap_symbols(s,n)
+#define md_swapout_symbols(s,n) swap_symbols(s,n)
+#define md_swapin_zsymbols(s,n) swap_zsymbols(s,n)
+#define md_swapout_zsymbols(s,n) swap_zsymbols(s,n)
+#define md_swapin__dynamic(l) swap__dynamic(l)
+#define md_swapout__dynamic(l) swap__dynamic(l)
+#define md_swapin_section_dispatch_table(l) swap_section_dispatch_table(l)
+#define md_swapout_section_dispatch_table(l) swap_section_dispatch_table(l)
+#define md_swapin_so_debug(d) swap_so_debug(d)
+#define md_swapout_so_debug(d) swap_so_debug(d)
+#define md_swapin_rrs_hash(f,n) swap_rrs_hash(f,n)
+#define md_swapout_rrs_hash(f,n) swap_rrs_hash(f,n)
+#define md_swapin_sod(l,n) swapin_sod(l,n)
+#define md_swapout_sod(l,n) swapout_sod(l,n)
+#define md_swapout_got(g,n) swap_longs((long*)(g),n)
+#define md_swapin_ranlib_hdr(h,n) swap_ranlib_hdr(h,n)
+#define md_swapout_ranlib_hdr(h,n) swap_ranlib_hdr(h,n)
+
+#define md_swap_short(x) ( (((x) >> 8) & 0xff) | (((x) & 0xff) << 8) )
+
+#define md_swap_long(x) ( (((x) >> 24) & 0xff ) | (((x) >> 8 ) & 0xff00 ) | \
+ (((x) << 8 ) & 0xff0000) | (((x) << 24) & 0xff000000))
+
+#else /* We need not swap, but must pay attention to alignment: */
+
+#define md_swap_short(x) (x)
+#define md_swap_long(x) (x)
+
+#endif /* NEED_SWAP */
+
+#else /* Not a cross linker: use native */
+
+#define md_swap_short(x) (x)
+#define md_swap_long(x) (x)
+
+#define get_byte(where) (*(char *)(where))
+#define get_short(where) (*(short *)(where))
+#define get_long(where) (*(long *)(where))
+
+#define put_byte(where,what) (*(char *)(where) = (what))
+#define put_short(where,what) (*(short *)(where) = (what))
+#define put_long(where,what) (*(long *)(where) = (what))
+
+#endif /* CROSS_LINKER */
diff --git a/libexec/rtld-aout/i386/mdprologue.S b/libexec/rtld-aout/i386/mdprologue.S
new file mode 100644
index 0000000..1de0f72
--- /dev/null
+++ b/libexec/rtld-aout/i386/mdprologue.S
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: mdprologue.S,v 1.7 1994/12/04 07:42:44 mycroft Exp $
+ */
+
+/*
+ * i386 run-time link editor entry points.
+ */
+
+#include <sys/syscall.h>
+
+ .text
+ .globl _binder, _binder_entry
+
+/*
+ * _rtl(int version, struct crt_ldso *crtp)
+ */
+
+_rtl: # crt0 calls us here
+ pushl %ebp # Allocate stack frame
+ movl %esp,%ebp
+ pushl %ebx
+
+ call 1f # PIC function prologue
+1:
+ popl %ebx
+ addl $_GLOBAL_OFFSET_TABLE_+[.-1b],%ebx
+
+ movl 12(%ebp),%eax # Extract data from interface structure
+ movl (%eax),%eax # base address of ld.so (first field)
+ # setup arguments for rtld()
+ movl (%ebx),%ecx # 1st entry in GOT is our __DYNAMIC
+ addl %eax,%ecx # add load address
+ pushl %ecx # 3rd arg
+ pushl 12(%ebp) # 2nd arg == &crt.
+ pushl 8(%ebp) # 1st arg == version
+ addl _rtld@GOT(%ebx),%eax # relocate address of function
+ call %eax # _rtld(version, crtp, DYNAMIC)
+ addl $12,%esp # pop arguments
+
+ popl %ebx
+ leave # remove stack frame
+ ret
+
+ # First call to a procedure generally comes through here for
+ # binding.
+
+_binder_entry:
+ pushl %ebp # setup a stack frame
+ movl %esp,%ebp
+ pusha # save all regs
+
+ xorl %eax,%eax # clear
+ movl 4(%ebp),%esi # return address in PLT
+ movw (%esi),%ax # get hold of relocation number
+ subl $6,%esi # make it point to the jmpslot
+
+ pushl %eax # pushd arguments
+ pushl %esi #
+ call _binder@PLT # _binder(rpc, index)
+ addl $8,%esp # pop arguments
+ movl %eax,4(%ebp) # return value from _binder() == actual
+ # address of function
+ popa # restore regs
+ leave # remove our stack frame
+ ret
diff --git a/libexec/rtld-aout/md-prologue.c b/libexec/rtld-aout/md-prologue.c
new file mode 100644
index 0000000..dae455e
--- /dev/null
+++ b/libexec/rtld-aout/md-prologue.c
@@ -0,0 +1,39 @@
+/*
+ * rtld entry pseudo code - turn into assembler and tweak it
+ */
+
+#include <sys/types.h>
+#include <sys/types.h>
+#include <a.out.h>
+#include "link.h"
+#include "md.h"
+
+extern long _GOT_[];
+extern void (*rtld)();
+extern void (*binder())();
+
+void
+rtld_entry(version, crtp)
+int version;
+struct crt *crtp;
+{
+ register struct link_dynamic *dp;
+ register void (*f)();
+
+ /* __DYNAMIC is first entry in GOT */
+ dp = (struct link_dynamic *) (_GOT_[0]+crtp->crt_ba);
+
+ f = (void (*)())((long)rtld + crtp->crt_ba);
+ (*f)(version, crtp, dp);
+}
+
+void
+binder_entry()
+{
+ extern int PC;
+ struct jmpslot *sp;
+ void (*func)();
+
+ func = binder(PC, sp->reloc_index & 0x003fffff);
+ (*func)();
+}
diff --git a/libexec/rtld-aout/rtld.1 b/libexec/rtld-aout/rtld.1
new file mode 100644
index 0000000..2b3c75a
--- /dev/null
+++ b/libexec/rtld-aout/rtld.1
@@ -0,0 +1,144 @@
+.\" $Id$
+.\"
+.\" Copyright (c) 1995 Paul Kranenburg
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Paul Kranenburg.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd June 27, 1995
+.Dt RTLD 1
+.Os FreeBSD
+.Sh NAME
+.Nm ld.so
+.Nd run-time link-editor
+.Sh DESCRIPTION
+.Nm
+is a self-contained, position independent program image providing run-time
+support for loading and link-editing shared objects into a process'
+address space. It uses the data structures
+.Po
+see
+.Xr link 5
+.Pc
+contained within dynamically linked programs to determine which shared
+libraries are needed and loads them at a convenient virtual address
+using the
+.Xr mmap 2
+system call.
+.Pp
+After all shared libraries have been succesfully loaded,
+.Nm
+proceeds to resolve external references from both the main program and
+all objects loaded. A mechanism is provided for initialisation routines
+to be called, on a per-object basis, giving a shared object an opportunity
+to perfrom any extra set-up, before execution of the program proper begins.
+This is useful for C++ libraries that contain static constrictors.
+.Pp
+.Nm
+is itself a shared object that is initially loaded by the startup module
+.Em crt0 .
+Since
+.Xr a.out 5
+formats do not provide easy access to the file header from within a running
+process,
+.Em crt0
+uses the special symbol
+.Va _DYNAMIC
+to determine whether a program is in fact dynamically linked or not. Whenever
+the linker
+.Xr ld 1
+has relocated this symbol to a location other then 0,
+.Em crt0
+assumes the services of
+.Nm
+are needed
+.Po
+see
+.Xr link 5
+for details
+.Pc \&.
+.Em crt0
+passes control to
+.Nm
+\&'s entry point before the program's
+.Fn main
+routine is called. Thus,
+.Nm
+can complete the link-editing process before the dynamic program calls upon
+services of any dynamic library.
+.Pp
+To quickly locate the required shared objects in the filesystem,
+.Nm
+may use a
+.Dq hints
+file, prepared by the
+.Xr ldconfig 8
+utility, in which the full path specification of the shared objects can be
+looked up by hashing on the 3-tuple
+.Ao
+library-name, major-version-number, minor-version-number
+.Ac \&.
+.Pp
+.Nm
+recognises a number of environment variables that can be used to modify
+its behaviour as follows:
+.Pp
+.Bl -tag -width "LD_TRACE_LOADED_OBJECTS"
+.It Ev LD_LIBRARY_PATH
+A colon separated list of directories, overriding the default search path
+for shared libraries.
+.It Ev LD_WARN_NON_PURE_CODE
+When set, issue a warning whenever a link-editing operation requires
+modification of the text segment of some loaded object. This is usually
+indicative of an incorrectly built library.
+.It Ev LD_SUPPRESS_WARNINGS
+When set, no warning messages of any kind are issued. Normally, a warning
+is given if satisfactorily versioned library could not be found.
+.It Ev LD_TRACE_LOADED_OBJECTS
+When set, causes
+.Nm
+to exit after loading the shared objects and printing a summary which includes
+the absolute pathnames of all objects, to standard output.
+.It Ev LD_NO_INTERN_SEARCH
+When set,
+.Nm
+does not process any internal search paths that were recorded in the
+executable.
+.It Ev LD_NOSTD_PATH
+When set, do not include a set of built-in standard directory paths for
+searching. This might be useful when running on a system with a completely
+non-standard filesystem layout.
+.El
+.Pp
+.Sh FILES
+/var/run/ld.so.hints
+.Pp
+.Sh SEE ALSO
+.Xr ld 1
+.Xr ldconfig 8
+.Xr link 5
+.Sh HISTORY
+The shared library model employed first appeared in SunOS 4.0
diff --git a/libexec/rtld-aout/rtld.1aout b/libexec/rtld-aout/rtld.1aout
new file mode 100644
index 0000000..2b3c75a
--- /dev/null
+++ b/libexec/rtld-aout/rtld.1aout
@@ -0,0 +1,144 @@
+.\" $Id$
+.\"
+.\" Copyright (c) 1995 Paul Kranenburg
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Paul Kranenburg.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd June 27, 1995
+.Dt RTLD 1
+.Os FreeBSD
+.Sh NAME
+.Nm ld.so
+.Nd run-time link-editor
+.Sh DESCRIPTION
+.Nm
+is a self-contained, position independent program image providing run-time
+support for loading and link-editing shared objects into a process'
+address space. It uses the data structures
+.Po
+see
+.Xr link 5
+.Pc
+contained within dynamically linked programs to determine which shared
+libraries are needed and loads them at a convenient virtual address
+using the
+.Xr mmap 2
+system call.
+.Pp
+After all shared libraries have been succesfully loaded,
+.Nm
+proceeds to resolve external references from both the main program and
+all objects loaded. A mechanism is provided for initialisation routines
+to be called, on a per-object basis, giving a shared object an opportunity
+to perfrom any extra set-up, before execution of the program proper begins.
+This is useful for C++ libraries that contain static constrictors.
+.Pp
+.Nm
+is itself a shared object that is initially loaded by the startup module
+.Em crt0 .
+Since
+.Xr a.out 5
+formats do not provide easy access to the file header from within a running
+process,
+.Em crt0
+uses the special symbol
+.Va _DYNAMIC
+to determine whether a program is in fact dynamically linked or not. Whenever
+the linker
+.Xr ld 1
+has relocated this symbol to a location other then 0,
+.Em crt0
+assumes the services of
+.Nm
+are needed
+.Po
+see
+.Xr link 5
+for details
+.Pc \&.
+.Em crt0
+passes control to
+.Nm
+\&'s entry point before the program's
+.Fn main
+routine is called. Thus,
+.Nm
+can complete the link-editing process before the dynamic program calls upon
+services of any dynamic library.
+.Pp
+To quickly locate the required shared objects in the filesystem,
+.Nm
+may use a
+.Dq hints
+file, prepared by the
+.Xr ldconfig 8
+utility, in which the full path specification of the shared objects can be
+looked up by hashing on the 3-tuple
+.Ao
+library-name, major-version-number, minor-version-number
+.Ac \&.
+.Pp
+.Nm
+recognises a number of environment variables that can be used to modify
+its behaviour as follows:
+.Pp
+.Bl -tag -width "LD_TRACE_LOADED_OBJECTS"
+.It Ev LD_LIBRARY_PATH
+A colon separated list of directories, overriding the default search path
+for shared libraries.
+.It Ev LD_WARN_NON_PURE_CODE
+When set, issue a warning whenever a link-editing operation requires
+modification of the text segment of some loaded object. This is usually
+indicative of an incorrectly built library.
+.It Ev LD_SUPPRESS_WARNINGS
+When set, no warning messages of any kind are issued. Normally, a warning
+is given if satisfactorily versioned library could not be found.
+.It Ev LD_TRACE_LOADED_OBJECTS
+When set, causes
+.Nm
+to exit after loading the shared objects and printing a summary which includes
+the absolute pathnames of all objects, to standard output.
+.It Ev LD_NO_INTERN_SEARCH
+When set,
+.Nm
+does not process any internal search paths that were recorded in the
+executable.
+.It Ev LD_NOSTD_PATH
+When set, do not include a set of built-in standard directory paths for
+searching. This might be useful when running on a system with a completely
+non-standard filesystem layout.
+.El
+.Pp
+.Sh FILES
+/var/run/ld.so.hints
+.Pp
+.Sh SEE ALSO
+.Xr ld 1
+.Xr ldconfig 8
+.Xr link 5
+.Sh HISTORY
+The shared library model employed first appeared in SunOS 4.0
diff --git a/libexec/rtld-aout/rtld.c b/libexec/rtld-aout/rtld.c
new file mode 100644
index 0000000..2fd7d4f
--- /dev/null
+++ b/libexec/rtld-aout/rtld.c
@@ -0,0 +1,1691 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: rtld.c,v 1.31 1995/10/27 22:01:00 jdp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#ifndef MAP_COPY
+#define MAP_COPY MAP_PRIVATE
+#endif
+#include <err.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <stab.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "ld.h"
+
+#ifndef MAP_ANON
+#define MAP_ANON 0
+#define anon_open() do { \
+ if ((anon_fd = open("/dev/zero", O_RDWR, 0)) == -1) \
+ err("open: %s", "/dev/zero"); \
+} while (0)
+#define anon_close() do { \
+ (void)close(anon_fd); \
+ anon_fd = -1; \
+} while (0)
+#else
+#define anon_open()
+#define anon_close()
+#endif
+
+/*
+ * Loader private data, hung off <so_map>->som_spd
+ */
+struct somap_private {
+ int spd_version;
+ struct so_map *spd_parent;
+ int spd_refcount;
+ int spd_flags;
+#define RTLD_MAIN 1
+#define RTLD_RTLD 2
+#define RTLD_DL 4
+#define RTLD_INIT 8
+ unsigned long a_text; /* text size, if known */
+ unsigned long a_data; /* initialized data size */
+ unsigned long a_bss; /* uninitialized data size */
+
+#ifdef SUN_COMPAT
+ long spd_offset; /* Correction for Sun main programs */
+#endif
+};
+
+#define LM_PRIVATE(smp) ((struct somap_private *)(smp)->som_spd)
+
+#ifdef SUN_COMPAT
+#define LM_OFFSET(smp) (LM_PRIVATE(smp)->spd_offset)
+#else
+#define LM_OFFSET(smp) (0)
+#endif
+
+/* Base address for section_dispatch_table entries */
+#define LM_LDBASE(smp) (smp->som_addr + LM_OFFSET(smp))
+
+/* Start of text segment */
+#define LM_TXTADDR(smp) (smp->som_addr == (caddr_t)0 ? PAGSIZ : 0)
+
+/* Start of run-time relocation_info */
+#define LM_REL(smp) ((struct relocation_info *) \
+ (smp->som_addr + LM_OFFSET(smp) + LD_REL((smp)->som_dynamic)))
+
+/* Start of symbols */
+#define LM_SYMBOL(smp, i) ((struct nzlist *) \
+ (smp->som_addr + LM_OFFSET(smp) + LD_SYMBOL((smp)->som_dynamic) + \
+ i * (LD_VERSION_NZLIST_P(smp->som_dynamic->d_version) ? \
+ sizeof(struct nzlist) : sizeof(struct nlist))))
+
+/* Start of hash table */
+#define LM_HASH(smp) ((struct rrs_hash *) \
+ ((smp)->som_addr + LM_OFFSET(smp) + LD_HASH((smp)->som_dynamic)))
+
+/* Start of strings */
+#define LM_STRINGS(smp) ((char *) \
+ ((smp)->som_addr + LM_OFFSET(smp) + LD_STRINGS((smp)->som_dynamic)))
+
+/* End of text */
+#define LM_ETEXT(smp) ((char *) \
+ ((smp)->som_addr + LM_TXTADDR(smp) + LD_TEXTSZ((smp)->som_dynamic)))
+
+/* Needed shared objects */
+#define LM_NEED(smp) ((struct sod *) \
+ ((smp)->som_addr + LM_TXTADDR(smp) + LD_NEED((smp)->som_dynamic)))
+
+/* PLT is in data segment, so don't use LM_OFFSET here */
+#define LM_PLT(smp) ((jmpslot_t *) \
+ ((smp)->som_addr + LD_PLT((smp)->som_dynamic)))
+
+/* Parent of link map */
+#define LM_PARENT(smp) (LM_PRIVATE(smp)->spd_parent)
+
+char **environ;
+char *__progname;
+int errno;
+
+static uid_t uid, euid;
+static gid_t gid, egid;
+static int careful;
+static char __main_progname[] = "main";
+static char *main_progname = __main_progname;
+static char us[] = "/usr/libexec/ld.so";
+static int anon_fd = -1;
+static char *ld_library_path;
+
+struct so_map *link_map_head, *main_map;
+struct so_map **link_map_tail = &link_map_head;
+struct rt_symbol *rt_symbol_head;
+
+static void *__dlopen __P((char *, int));
+static int __dlclose __P((void *));
+static void *__dlsym __P((void *, char *));
+static char *__dlerror __P((void));
+static void __dlexit __P((void));
+
+static struct ld_entry ld_entry = {
+ __dlopen, __dlclose, __dlsym, __dlerror, __dlexit
+};
+
+ void xprintf __P((char *, ...));
+static void load_objects __P(( struct crt_ldso *,
+ struct _dynamic *));
+static struct so_map *map_object __P((struct sod *, struct so_map *));
+static int unmap_object __P((struct so_map *));
+static struct so_map *load_object __P((struct sod *, struct so_map *,
+ int, int));
+static int unload_object __P((struct so_map *));
+static struct so_map *alloc_link_map __P(( char *, struct sod *,
+ struct so_map *, caddr_t,
+ struct _dynamic *));
+static inline int check_text_reloc __P(( struct relocation_info *,
+ struct so_map *,
+ caddr_t));
+static int reloc_map __P((struct so_map *));
+static void reloc_copy __P((struct so_map *));
+static void init_map __P((struct so_map *, char *, int));
+static void call_map __P((struct so_map *, char *));
+static char *rtfindlib __P((char *, int, int, int *));
+void binder_entry __P((void));
+long binder __P((jmpslot_t *));
+static struct nzlist *lookup __P((char *, struct so_map **, int));
+static inline struct rt_symbol *lookup_rts __P((char *));
+static struct rt_symbol *enter_rts __P((char *, long, int, caddr_t,
+ long, struct so_map *));
+static void generror __P((char *, ...));
+static void maphints __P((void));
+static void unmaphints __P((void));
+
+static int dl_cascade __P((struct so_map *));
+
+static inline int
+strcmp (register const char *s1, register const char *s2)
+{
+ while (*s1 == *s2++)
+ if (*s1++ == 0)
+ return (0);
+ return (*(unsigned char *)s1 - *(unsigned char *)--s2);
+}
+
+#include "md-static-funcs.c"
+
+/*
+ * Called from assembler stub that has set up crtp (passed from crt0)
+ * and dp (our __DYNAMIC).
+ */
+int
+rtld(version, crtp, dp)
+int version;
+struct crt_ldso *crtp;
+struct _dynamic *dp;
+{
+ struct relocation_info *reloc;
+ struct relocation_info *reloc_limit; /* End+1 of relocation */
+ struct so_debug *ddp;
+ struct so_map *smp;
+
+ /* Check version */
+ if (version != CRT_VERSION_BSD_2 &&
+ version != CRT_VERSION_BSD_3 &&
+ version != CRT_VERSION_SUN)
+ return -1;
+
+ /* Fixup __DYNAMIC structure */
+ (long)dp->d_un.d_sdt += crtp->crt_ba;
+
+ /* Relocate ourselves */
+ reloc = (struct relocation_info *) (LD_REL(dp) + crtp->crt_ba);
+ reloc_limit =
+ (struct relocation_info *) ((char *) reloc + LD_RELSZ(dp));
+ while(reloc < reloc_limit) {
+ /*
+ * Objects linked with "-Bsymbolic" (in particular, ld.so
+ * itself) can end up having unused relocation entries at
+ * the end. These can be detected by the fact that they
+ * have an address of 0.
+ */
+ if(reloc->r_address == 0) /* We're done */
+ break;
+ md_relocate_simple(reloc, crtp->crt_ba,
+ reloc->r_address + crtp->crt_ba);
+ ++reloc;
+ }
+
+ __progname = "ld.so";
+ if (version >= CRT_VERSION_BSD_3)
+ main_progname = crtp->crt_prog;
+
+ /* Setup out (private) environ variable */
+ environ = crtp->crt_ep;
+
+ /* Get user and group identifiers */
+ uid = getuid(); euid = geteuid();
+ gid = getgid(); egid = getegid();
+
+ careful = (uid != euid) || (gid != egid);
+
+ if (careful) {
+ unsetenv("LD_LIBRARY_PATH");
+ unsetenv("LD_PRELOAD");
+ } else
+ ld_library_path = getenv("LD_LIBRARY_PATH");
+
+ /* Setup directory search */
+ add_search_path(ld_library_path);
+ std_search_path();
+
+ anon_open();
+ /* Load required objects into the process address space */
+ load_objects(crtp, dp);
+
+ /* Fill in some fields in main's __DYNAMIC structure */
+ crtp->crt_dp->d_entry = &ld_entry;
+ crtp->crt_dp->d_un.d_sdt->sdt_loaded = link_map_head->som_next;
+
+ /* Relocate all loaded objects according to their RRS segments */
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)
+ continue;
+ if (reloc_map(smp) < 0)
+ return -1;
+ }
+
+ /* Copy any relocated initialized data. */
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)
+ continue;
+ reloc_copy(smp);
+ }
+
+ /* Call any object initialization routines. */
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)
+ continue;
+ init_map(smp, ".init", 0);
+ }
+
+ ddp = crtp->crt_dp->d_debug;
+ ddp->dd_cc = rt_symbol_head;
+ if (ddp->dd_in_debugger) {
+ caddr_t addr = (caddr_t)((long)crtp->crt_bp & (~(PAGSIZ - 1)));
+
+ /* Set breakpoint for the benefit of debuggers */
+ if (mprotect(addr, PAGSIZ,
+ PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
+ err(1, "Cannot set breakpoint (%s)", main_progname);
+ }
+ md_set_breakpoint((long)crtp->crt_bp, (long *)&ddp->dd_bpt_shadow);
+ if (mprotect(addr, PAGSIZ, PROT_READ|PROT_EXEC) == -1) {
+ err(1, "Cannot re-protect breakpoint (%s)",
+ main_progname);
+ }
+
+ ddp->dd_bpt_addr = crtp->crt_bp;
+ if (link_map_head)
+ ddp->dd_sym_loaded = 1;
+ }
+
+ /* Close the hints file */
+ unmaphints();
+
+ /* Close our file descriptor */
+ (void)close(crtp->crt_ldfd);
+ anon_close();
+
+ return LDSO_VERSION_HAS_DLEXIT;
+}
+
+
+static void
+load_objects(crtp, dp)
+struct crt_ldso *crtp;
+struct _dynamic *dp;
+{
+ struct so_map *smp;
+ int tracing = (int)getenv("LD_TRACE_LOADED_OBJECTS");
+
+ /* Handle LD_PRELOAD's here */
+
+ /* Make an entry for the main program */
+ smp = alloc_link_map(main_progname, (struct sod *)0, (struct so_map *)0,
+ (caddr_t)0, crtp->crt_dp);
+ LM_PRIVATE(smp)->spd_refcount++;
+ LM_PRIVATE(smp)->spd_flags |= RTLD_MAIN;
+
+ /* Make an entry for ourselves */
+ smp = alloc_link_map(us, (struct sod *)0, (struct so_map *)0,
+ (caddr_t)crtp->crt_ba, dp);
+ LM_PRIVATE(smp)->spd_refcount++;
+ LM_PRIVATE(smp)->spd_flags |= RTLD_RTLD;
+
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ struct sod *sodp;
+ long next = 0;
+
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)
+ continue;
+
+ if (smp->som_dynamic)
+ next = LD_NEED(smp->som_dynamic);
+
+ while (next) {
+ struct so_map *newmap;
+
+ sodp = (struct sod *)(LM_LDBASE(smp) + next);
+ if ((newmap = map_object(sodp, smp)) == NULL) {
+ if (!tracing) {
+ errx(1, "%s: %s", main_progname,
+ __dlerror());
+ }
+ newmap = alloc_link_map(NULL, sodp, smp, 0, 0);
+ }
+ LM_PRIVATE(newmap)->spd_refcount++;
+ next = sodp->sod_next;
+ }
+ }
+
+ if (! tracing)
+ return;
+
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ struct sod *sodp;
+ char *name, *path;
+
+ if ((sodp = smp->som_sod) == NULL)
+ continue;
+ name = sodp->sod_name + LM_LDBASE(LM_PARENT(smp));
+
+ if ((path = smp->som_path) == NULL)
+ path = "not found";
+
+ if (sodp->sod_library)
+ printf("\t-l%s.%d => %s (%p)\n", name,
+ sodp->sod_major, path, smp->som_addr);
+ else
+ printf("\t%s => %s (%p)\n", name, path, smp->som_addr);
+ }
+
+ exit(0);
+}
+
+/*
+ * Allocate a new link map for shared object NAME loaded at ADDR as a
+ * result of the presence of link object LOP in the link map PARENT.
+ */
+ static struct so_map *
+alloc_link_map(path, sodp, parent, addr, dp)
+ char *path;
+ struct sod *sodp;
+ struct so_map *parent;
+ caddr_t addr;
+ struct _dynamic *dp;
+{
+ struct so_map *smp;
+ struct somap_private *smpp;
+ size_t smp_size;
+
+ /*
+ * Allocate so_map and private area with a single malloc. Round
+ * up the size of so_map so the private area is aligned.
+ */
+ smp_size = ((((sizeof(struct so_map)) + sizeof (void *) - 1) /
+ sizeof (void *)) * sizeof (void *));
+
+ smp = (struct so_map *)xmalloc(smp_size +
+ sizeof (struct somap_private));
+ smpp = (struct somap_private *) (((caddr_t) smp) + smp_size);
+ smp->som_next = NULL;
+ *link_map_tail = smp;
+ link_map_tail = &smp->som_next;
+
+ smp->som_addr = addr;
+ if (path == NULL)
+ smp->som_path = NULL;
+ else
+ smp->som_path = strdup(path);
+ smp->som_sod = sodp;
+ smp->som_dynamic = dp;
+ smp->som_spd = (caddr_t)smpp;
+
+/*XXX*/ if (addr == 0) main_map = smp;
+
+ smpp->spd_refcount = 0;
+ smpp->spd_flags = 0;
+ smpp->spd_parent = parent;
+ smpp->a_text = 0;
+ smpp->a_data = 0;
+ smpp->a_bss = 0;
+#ifdef SUN_COMPAT
+ smpp->spd_offset =
+ (addr==0 && dp && dp->d_version==LD_VERSION_SUN) ? PAGSIZ : 0;
+#endif
+ return smp;
+}
+
+ static struct so_map *
+find_object(sodp, smp)
+ struct sod *sodp;
+ struct so_map *smp;
+{
+ char *path, *name = (char *)(sodp->sod_name + LM_LDBASE(smp));
+ int usehints = 0;
+ struct so_map *p;
+
+ if (sodp->sod_library) {
+ usehints = 1;
+again:
+ path = rtfindlib(name, sodp->sod_major,
+ sodp->sod_minor, &usehints);
+ if (path == NULL) {
+ generror ("Can't find shared library \"%s\"",
+ name);
+ return NULL;
+ }
+ } else {
+ if (careful && *name != '/') {
+ generror ("Shared library path must start with \"/\" for \"%s\"",
+ name);
+ return NULL;
+ }
+ path = name;
+ }
+
+ /* Check if already loaded */
+ for (p = link_map_head; p; p = p->som_next)
+ if (p->som_path && strcmp(p->som_path, path) == 0)
+ break;
+
+ return p;
+}
+
+/*
+ * Map object identified by link object sodp which was found in link
+ * map smp. Returns a pointer to the link map for the requested object.
+ *
+ * On failure, it sets an error message that can be retrieved by __dlerror,
+ * and returns NULL.
+ */
+ static struct so_map *
+map_object(sodp, smp)
+ struct sod *sodp;
+ struct so_map *smp;
+{
+ struct _dynamic *dp;
+ char *path, *name = (char *)(sodp->sod_name + LM_LDBASE(smp));
+ int fd;
+ caddr_t addr;
+ struct exec hdr;
+ int usehints = 0;
+ struct so_map *p;
+ struct somap_private *smpp;
+
+ if (sodp->sod_library) {
+ usehints = 1;
+again:
+ path = rtfindlib(name, sodp->sod_major,
+ sodp->sod_minor, &usehints);
+ if (path == NULL) {
+ generror ("Can't find shared library"
+ " \"lib%s.so.%d.%d\"",
+ name, sodp->sod_major, sodp->sod_minor);
+ return NULL;
+ }
+ } else {
+ if (careful && *name != '/') {
+ generror ("Shared library path must start with \"/\" for \"%s\"",
+ name);
+ return NULL;
+ }
+ path = name;
+ }
+
+ /* Check if already loaded */
+ for (p = link_map_head; p; p = p->som_next)
+ if (p->som_path && strcmp(p->som_path, path) == 0)
+ break;
+
+ if (p != NULL)
+ return p;
+
+ if ((fd = open(path, O_RDONLY, 0)) == -1) {
+ if (usehints) {
+ usehints = 0;
+ goto again;
+ }
+ generror ("open failed for \"%s\" : %s",
+ path, strerror (errno));
+ return NULL;
+ }
+
+ if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+ generror ("header read failed for \"%s\"", path);
+ (void)close(fd);
+ return NULL;
+ }
+
+ if (N_BADMAG(hdr)) {
+ generror ("bad magic number in \"%s\"", path);
+ (void)close(fd);
+ return NULL;
+ }
+
+ if ((addr = mmap(0, hdr.a_text + hdr.a_data + hdr.a_bss,
+ PROT_READ|PROT_EXEC,
+ MAP_COPY, fd, 0)) == (caddr_t)-1) {
+ generror ("mmap failed for \"%s\" : %s",
+ path, strerror (errno));
+ (void)close(fd);
+ return NULL;
+ }
+
+ if (mprotect(addr + hdr.a_text, hdr.a_data,
+ PROT_READ|PROT_WRITE|PROT_EXEC) != 0) {
+ generror ("mprotect failed for \"%s\" : %s",
+ path, strerror (errno));
+ (void)close(fd);
+ return NULL;
+ }
+
+ if (mmap(addr + hdr.a_text + hdr.a_data, hdr.a_bss,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_ANON|MAP_COPY|MAP_FIXED,
+ anon_fd, 0) == (caddr_t)-1) {
+ generror ("mmap failed for \"%s\" : %s",
+ path, strerror (errno));
+ (void)close(fd);
+ return NULL;
+ }
+
+ (void)close(fd);
+
+ /* Assume _DYNAMIC is the first data item */
+ dp = (struct _dynamic *)(addr+hdr.a_text);
+
+ /* Fixup __DYNAMIC structure */
+ (long)dp->d_un.d_sdt += (long)addr;
+
+ p = alloc_link_map(path, sodp, smp, addr, dp);
+
+ /* save segment sizes for unmap. */
+ smpp = LM_PRIVATE(p);
+ smpp->a_text = hdr.a_text;
+ smpp->a_data = hdr.a_data;
+ smpp->a_bss = hdr.a_bss;
+ return p;
+}
+
+/*
+ * Unmap an object that is nolonger in use.
+ */
+ static int
+unmap_object(smp)
+ struct so_map *smp;
+{
+ struct so_map *prev, *p;
+ struct somap_private *smpp;
+
+ /* Find the object in the list and unlink it */
+
+ for (prev = NULL, p = link_map_head;
+ p != smp;
+ prev = p, p = p->som_next) continue;
+
+ if (prev == NULL) {
+ link_map_head = smp->som_next;
+ if (smp->som_next == NULL)
+ link_map_tail = &link_map_head;
+ } else {
+ prev->som_next = smp->som_next;
+ if (smp->som_next == NULL)
+ link_map_tail = &prev->som_next;
+ }
+
+ /* Unmap the sections we have mapped */
+
+ smpp = LM_PRIVATE(smp);
+
+ if (munmap(smp->som_addr, smpp->a_text + smpp->a_data) < 0) {
+ generror ("munmap failed: %s", strerror (errno));
+ return -1;
+ }
+ if (smpp->a_bss > 0) {
+ if (munmap(smp->som_addr + smpp->a_text + smpp->a_data,
+ smpp->a_bss) < 0) {
+ generror ("munmap failed: %s", strerror (errno));
+ return -1;
+ }
+ }
+ if (smp->som_path) free(smp->som_path);
+ free(smp);
+ return 0;
+}
+
+static inline int
+check_text_reloc(r, smp, addr)
+struct relocation_info *r;
+struct so_map *smp;
+caddr_t addr;
+{
+ char *sym;
+
+ if (addr >= LM_ETEXT(smp))
+ return 0;
+
+ if (RELOC_EXTERN_P(r))
+ sym = LM_STRINGS(smp) +
+ LM_SYMBOL(smp, RELOC_SYMBOL(r))->nz_strx;
+ else
+ sym = "";
+
+ if (getenv("LD_SUPPRESS_WARNINGS") == NULL &&
+ getenv("LD_WARN_NON_PURE_CODE") != NULL)
+ warnx("warning: non pure code in %s at %x (%s)",
+ smp->som_path, r->r_address, sym);
+
+ if (smp->som_write == 0 &&
+ mprotect(smp->som_addr + LM_TXTADDR(smp),
+ LD_TEXTSZ(smp->som_dynamic),
+ PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
+ generror ("mprotect failed for \"%s\" : %s",
+ smp->som_path, strerror (errno));
+ return -1;
+ }
+
+ smp->som_write = 1;
+ return 0;
+}
+
+static int
+reloc_map(smp)
+ struct so_map *smp;
+{
+ /*
+ * Caching structure for reducing the number of calls to
+ * lookup() during relocation.
+ *
+ * While relocating a given shared object, the dynamic linker
+ * maintains a caching vector that is directly indexed by
+ * the symbol number in the relocation entry. The first time
+ * a given symbol is looked up, the caching vector is
+ * filled in with a pointer to the symbol table entry, and
+ * a pointer to the so_map of the shared object in which the
+ * symbol was defined. On subsequent uses of the same symbol,
+ * that information is retrieved directly from the caching
+ * vector, without calling lookup() again.
+ *
+ * A symbol that is referenced in a relocation entry is
+ * typically referenced in many relocation entries, so this
+ * caching reduces the number of calls to lookup()
+ * dramatically. The overall improvement in the speed of
+ * dynamic linking is also dramatic -- as much as a factor
+ * of three for programs that use many shared libaries.
+ */
+ struct cacheent {
+ struct nzlist *np; /* Pointer to symbol entry */
+ struct so_map *src_map; /* Shared object that defined symbol */
+ };
+
+ struct _dynamic *dp = smp->som_dynamic;
+ struct relocation_info *r = LM_REL(smp);
+ struct relocation_info *rend = r + LD_RELSZ(dp)/sizeof(*r);
+ long symbolbase = (long)LM_SYMBOL(smp, 0);
+ char *stringbase = LM_STRINGS(smp);
+ int symsize = LD_VERSION_NZLIST_P(dp->d_version) ?
+ sizeof(struct nzlist) :
+ sizeof(struct nlist);
+ long numsyms = LD_STABSZ(dp) / symsize;
+ size_t cachebytes = numsyms * sizeof(struct cacheent);
+ struct cacheent *symcache =
+ (struct cacheent *) alloca(cachebytes);
+
+ if(symcache == NULL) {
+ generror("Cannot allocate symbol caching vector for %s",
+ smp->som_path);
+ return -1;
+ }
+ bzero(symcache, cachebytes);
+
+ if (LD_PLTSZ(dp))
+ md_fix_jmpslot(LM_PLT(smp),
+ (long)LM_PLT(smp), (long)binder_entry);
+
+ for (; r < rend; r++) {
+ char *sym;
+ caddr_t addr;
+
+ /*
+ * Objects linked with "-Bsymbolic" can end up having unused
+ * relocation entries at the end. These can be detected by
+ * the fact that they have an address of 0.
+ */
+ if(r->r_address == 0) /* Finished relocating this object */
+ break;
+
+ addr = smp->som_addr + r->r_address;
+ if (check_text_reloc(r, smp, addr) < 0)
+ return -1;
+
+ if (RELOC_EXTERN_P(r)) {
+ struct so_map *src_map = NULL;
+ struct nzlist *p, *np;
+ long relocation;
+
+ if (RELOC_LAZY_P(r))
+ continue;
+
+ p = (struct nzlist *)
+ (symbolbase + symsize * RELOC_SYMBOL(r));
+
+ if (p->nz_type == (N_SETV + N_EXT))
+ src_map = smp;
+
+ sym = stringbase + p->nz_strx;
+
+ /*
+ * Look up the symbol, checking the caching
+ * vector first.
+ */
+ np = symcache[RELOC_SYMBOL(r)].np;
+ if(np != NULL) /* Symbol already cached */
+ src_map = symcache[RELOC_SYMBOL(r)].src_map;
+ else { /* Symbol not cached yet */
+ np = lookup(sym, &src_map, 0/*XXX-jumpslots!*/);
+ /*
+ * Record the needed information about
+ * the symbol in the caching vector,
+ * so that we won't have to call
+ * lookup the next time we encounter
+ * the symbol.
+ */
+ symcache[RELOC_SYMBOL(r)].np = np;
+ symcache[RELOC_SYMBOL(r)].src_map = src_map;
+ }
+
+ if (np == NULL) {
+ generror ("Undefined symbol \"%s\" in %s:%s",
+ sym, main_progname, smp->som_path);
+ return -1;
+ }
+
+ /*
+ * Found symbol definition.
+ * If it's in a link map, adjust value
+ * according to the load address of that map.
+ * Otherwise it's a run-time allocated common
+ * whose value is already up-to-date.
+ */
+ relocation = md_get_addend(r, addr);
+ relocation += np->nz_value;
+ if (src_map)
+ relocation += (long)src_map->som_addr;
+
+ if (RELOC_PCREL_P(r))
+ relocation -= (long)smp->som_addr;
+
+ if (RELOC_COPY_P(r) && src_map) {
+ (void)enter_rts(sym,
+ (long)addr,
+ N_DATA + N_EXT,
+ src_map->som_addr + np->nz_value,
+ np->nz_size, src_map);
+ continue;
+ }
+ md_relocate(r, relocation, addr, 0);
+
+ } else {
+ md_relocate(r,
+#ifdef SUN_COMPAT
+ md_get_rt_segment_addend(r, addr)
+#else
+ md_get_addend(r, addr)
+#endif
+ + (long)smp->som_addr, addr, 0);
+ }
+
+ }
+
+ if (smp->som_write) {
+ if (mprotect(smp->som_addr + LM_TXTADDR(smp),
+ LD_TEXTSZ(smp->som_dynamic),
+ PROT_READ|PROT_EXEC) == -1) {
+ generror ("mprotect failed for \"%s\" : %s",
+ smp->som_path, strerror (errno));
+ return -1;
+ }
+ smp->som_write = 0;
+ }
+ return 0;
+}
+
+static void
+reloc_copy(smp)
+ struct so_map *smp;
+{
+ struct rt_symbol *rtsp;
+
+ for (rtsp = rt_symbol_head; rtsp; rtsp = rtsp->rt_next)
+ if ((rtsp->rt_smp == NULL || rtsp->rt_smp == smp) &&
+ rtsp->rt_sp->nz_type == N_DATA + N_EXT) {
+ bcopy(rtsp->rt_srcaddr, (caddr_t)rtsp->rt_sp->nz_value,
+ rtsp->rt_sp->nz_size);
+ }
+}
+
+static void
+init_map(smp, sym, dependants)
+ struct so_map *smp;
+ char *sym;
+ int dependants;
+{
+ struct so_map *src_map = smp;
+ struct nzlist *np;
+
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_INIT)
+ return;
+
+ LM_PRIVATE(smp)->spd_flags |= RTLD_INIT;
+
+ if (dependants) {
+ struct sod *sodp;
+ struct so_map *smp2;
+ long next;
+
+ next = LD_NEED(smp->som_dynamic);
+
+ while (next) {
+ sodp = (struct sod *)(LM_LDBASE(smp) + next);
+ smp2 = find_object(sodp, smp);
+ if (smp2)
+ init_map(smp2, sym, dependants);
+ next = sodp->sod_next;
+ }
+ }
+
+ np = lookup(sym, &src_map, 1);
+ if (np)
+ (*(void (*)())(src_map->som_addr + np->nz_value))();
+}
+
+static void
+call_map(smp, sym)
+ struct so_map *smp;
+ char *sym;
+{
+ struct so_map *src_map = smp;
+ struct nzlist *np;
+
+ np = lookup(sym, &src_map, 1);
+ if (np)
+ (*(void (*)())(src_map->som_addr + np->nz_value))();
+}
+
+/*
+ * Load an object with all its dependant objects, recording the type of the
+ * object and optionally calling its init function.
+ */
+static struct so_map *
+load_object(sodp, parent, type, init)
+ struct sod *sodp;
+ struct so_map *parent;
+ int type;
+ int init;
+{
+ struct so_map* smp;
+
+ /*
+ * Find or map the object.
+ */
+ smp = map_object(sodp, parent);
+ if (smp == NULL) return NULL;
+
+ /*
+ * The first time the object is mapped, load it's dependant objects and
+ * relocate it.
+ */
+ if (LM_PRIVATE(smp)->spd_refcount++ == 0) {
+ struct sod *sodp;
+ struct so_map *smp2;
+ long next;
+
+ next = LD_NEED(smp->som_dynamic);
+
+ /*
+ * Load dependant objects but defer initialisation until later.
+ * When all the dependants (and sub dependants, etc.) have been
+ * loaded and relocated, it is safe to call the init functions,
+ * using a recursive call to init_map. This ensures that if init
+ * code in the dependants calls code in the parent, it will work
+ * as expected.
+ */
+ while (next) {
+ sodp = (struct sod *)(LM_LDBASE(smp) + next);
+ /*
+ * Dependant objects (of both dlopen and main) don't get a
+ * specific type.
+ */
+ if ((smp2 = load_object(sodp, smp, 0, 0)) == NULL) {
+#ifdef DEBUG
+xprintf("ld.so: map_object failed on cascaded %s %s (%d.%d): %s\n",
+ smp->sod_library ? "library" : "file", smp->sod_name,
+ smp->sod_major, smp->sod_minor, strerror(errno));
+#endif
+ unload_object(smp);
+ return NULL;
+ }
+ next = sodp->sod_next;
+ }
+
+ LM_PRIVATE(smp)->spd_flags |= type;
+ if (reloc_map(smp) < 0) {
+ unload_object(smp);
+ return NULL;
+ }
+ reloc_copy(smp);
+ if (init) {
+ init_map(smp, ".init", 1);
+ }
+ }
+
+ return smp;
+}
+
+/*
+ * Unload an object, recursively unloading dependant objects.
+ */
+static int
+unload_object(smp)
+ struct so_map *smp;
+{
+ struct so_map *smp2;
+ struct sod *sodp;
+ long next;
+
+ if (--LM_PRIVATE(smp)->spd_refcount != 0)
+ return -1;
+
+ /*
+ * Call destructors for the object (before unloading its dependants
+ * since destructors may use them. Only call destructors if constructors
+ * have been called.
+ */
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_INIT)
+ call_map(smp, ".fini");
+
+ /*
+ * Unmap any dependant objects first.
+ */
+ next = LD_NEED(smp->som_dynamic);
+ while (next) {
+ sodp = (struct sod *)(LM_LDBASE(smp) + next);
+ smp2 = find_object(sodp, smp);
+ if (smp2)
+ unload_object(smp2);
+ next = sodp->sod_next;
+ }
+
+ /*
+ * Remove from address space.
+ */
+ if (unmap_object(smp) < 0)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Run-time common symbol table.
+ */
+
+#define RTC_TABSIZE 57
+static struct rt_symbol *rt_symtab[RTC_TABSIZE];
+
+/*
+ * Compute hash value for run-time symbol table
+ */
+ static inline int
+hash_string(key)
+ char *key;
+{
+ register char *cp;
+ register int k;
+
+ cp = key;
+ k = 0;
+ while (*cp)
+ k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
+
+ return k;
+}
+
+/*
+ * Lookup KEY in the run-time common symbol table.
+ */
+
+ static inline struct rt_symbol *
+lookup_rts(key)
+ char *key;
+{
+ register int hashval;
+ register struct rt_symbol *rtsp;
+
+ /* Determine which bucket. */
+
+ hashval = hash_string(key) % RTC_TABSIZE;
+
+ /* Search the bucket. */
+
+ for (rtsp = rt_symtab[hashval]; rtsp; rtsp = rtsp->rt_link)
+ if (strcmp(key, rtsp->rt_sp->nz_name) == 0)
+ return rtsp;
+
+ return NULL;
+}
+
+ static struct rt_symbol *
+enter_rts(name, value, type, srcaddr, size, smp)
+ char *name;
+ long value;
+ int type;
+ caddr_t srcaddr;
+ long size;
+ struct so_map *smp;
+{
+ register int hashval;
+ register struct rt_symbol *rtsp, **rpp;
+
+ /* Determine which bucket */
+ hashval = hash_string(name) % RTC_TABSIZE;
+
+ /* Find end of bucket */
+ for (rpp = &rt_symtab[hashval]; *rpp; rpp = &(*rpp)->rt_link)
+ continue;
+
+ /* Allocate new common symbol */
+ rtsp = (struct rt_symbol *)malloc(sizeof(struct rt_symbol));
+ rtsp->rt_sp = (struct nzlist *)malloc(sizeof(struct nzlist));
+ rtsp->rt_sp->nz_name = strdup(name);
+ rtsp->rt_sp->nz_value = value;
+ rtsp->rt_sp->nz_type = type;
+ rtsp->rt_sp->nz_size = size;
+ rtsp->rt_srcaddr = srcaddr;
+ rtsp->rt_smp = smp;
+ rtsp->rt_link = NULL;
+
+ /* Link onto linear list as well */
+ rtsp->rt_next = rt_symbol_head;
+ rt_symbol_head = rtsp;
+
+ *rpp = rtsp;
+
+ return rtsp;
+}
+
+
+/*
+ * Lookup NAME in the link maps. The link map producing a definition
+ * is returned in SRC_MAP. If SRC_MAP is not NULL on entry the search is
+ * confined to that map. If STRONG is set, the symbol returned must
+ * have a proper type (used by binder()).
+ */
+ static struct nzlist *
+lookup(name, src_map, strong)
+ char *name;
+ struct so_map **src_map; /* IN/OUT */
+ int strong;
+{
+ long common_size = 0;
+ struct so_map *smp;
+ struct rt_symbol *rtsp;
+
+ if ((rtsp = lookup_rts(name)) != NULL)
+ return rtsp->rt_sp;
+
+ /*
+ * Search all maps for a definition of NAME
+ */
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ int buckets;
+ long hashval;
+ struct rrs_hash *hp;
+ char *cp;
+ struct nzlist *np;
+
+ /* Some local caching */
+ long symbolbase;
+ struct rrs_hash *hashbase;
+ char *stringbase;
+ int symsize;
+
+ if (*src_map && smp != *src_map)
+ continue;
+
+ if ((buckets = LD_BUCKETS(smp->som_dynamic)) == 0)
+ continue;
+
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)
+ continue;
+
+restart:
+ /*
+ * Compute bucket in which the symbol might be found.
+ */
+ for (hashval = 0, cp = name; *cp; cp++)
+ hashval = (hashval << 1) + *cp;
+
+ hashval = (hashval & 0x7fffffff) % buckets;
+
+ hashbase = LM_HASH(smp);
+ hp = hashbase + hashval;
+ if (hp->rh_symbolnum == -1)
+ /* Nothing in this bucket */
+ continue;
+
+ symbolbase = (long)LM_SYMBOL(smp, 0);
+ stringbase = LM_STRINGS(smp);
+ symsize = LD_VERSION_NZLIST_P(smp->som_dynamic->d_version)?
+ sizeof(struct nzlist) :
+ sizeof(struct nlist);
+ while (hp) {
+ np = (struct nzlist *)
+ (symbolbase + hp->rh_symbolnum * symsize);
+ cp = stringbase + np->nz_strx;
+ if (strcmp(cp, name) == 0)
+ break;
+ if (hp->rh_next == 0)
+ hp = NULL;
+ else
+ hp = hashbase + hp->rh_next;
+ }
+ if (hp == NULL)
+ /* Nothing in this bucket */
+ continue;
+
+ /*
+ * We have a symbol with the name we're looking for.
+ */
+ if (np->nz_type == N_INDR+N_EXT) {
+ /*
+ * Next symbol gives the aliased name. Restart
+ * search with new name and confine to this map.
+ */
+ name = stringbase + (++np)->nz_strx;
+ *src_map = smp;
+ goto restart;
+ }
+
+ if (np->nz_value == 0)
+ /* It's not a definition */
+ continue;
+
+ if (np->nz_type == N_UNDF+N_EXT && np->nz_value != 0) {
+ if (np->nz_other == AUX_FUNC) {
+ /* It's a weak function definition */
+ if (strong)
+ continue;
+ } else {
+ /* It's a common, note value and continue search */
+ if (common_size < np->nz_value)
+ common_size = np->nz_value;
+ continue;
+ }
+ }
+
+ *src_map = smp;
+ return np;
+ }
+
+ if (common_size == 0)
+ /* Not found */
+ return NULL;
+
+ /*
+ * It's a common, enter into run-time common symbol table.
+ */
+ rtsp = enter_rts(name, (long)calloc(1, common_size),
+ N_UNDF + N_EXT, 0, common_size, NULL);
+
+#if DEBUG
+ xprintf("Allocating common: %s size %d at %#x\n", name, common_size,
+ rtsp->rt_sp->nz_value);
+#endif
+
+ return rtsp->rt_sp;
+}
+
+/*
+ * This routine is called from the jumptable to resolve
+ * procedure calls to shared objects.
+ */
+ long
+binder(jsp)
+ jmpslot_t *jsp;
+{
+ struct so_map *smp, *src_map = NULL;
+ long addr;
+ char *sym;
+ struct nzlist *np;
+ int index;
+
+ /*
+ * Find the PLT map that contains JSP.
+ */
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ if (LM_PLT(smp) < jsp &&
+ jsp < LM_PLT(smp) + LD_PLTSZ(smp->som_dynamic)/sizeof(*jsp))
+ break;
+ }
+
+ if (smp == NULL)
+ errx(1, "Call to binder from unknown location: %#x\n", jsp);
+
+ index = jsp->reloc_index & JMPSLOT_RELOC_MASK;
+
+ /* Get the local symbol this jmpslot refers to */
+ sym = LM_STRINGS(smp) +
+ LM_SYMBOL(smp,RELOC_SYMBOL(&LM_REL(smp)[index]))->nz_strx;
+
+ np = lookup(sym, &src_map, 1);
+ if (np == NULL)
+ errx(1, "Undefined symbol \"%s\" called from %s:%s at %#x",
+ sym, main_progname, smp->som_path, jsp);
+
+ /* Fixup jmpslot so future calls transfer directly to target */
+ addr = np->nz_value;
+ if (src_map)
+ addr += (long)src_map->som_addr;
+
+ md_fix_jmpslot(jsp, (long)jsp, addr);
+
+#if DEBUG
+ xprintf(" BINDER: %s located at = %#x in %s\n", sym, addr,
+ src_map->som_path);
+#endif
+ return addr;
+}
+
+
+static int hfd;
+static long hsize;
+static struct hints_header *hheader;
+static struct hints_bucket *hbuckets;
+static char *hstrtab;
+
+#define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1)
+
+ static void
+maphints __P((void))
+{
+ caddr_t addr;
+
+ if ((hfd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) {
+ hheader = (struct hints_header *)-1;
+ return;
+ }
+
+ hsize = PAGSIZ;
+ addr = mmap(0, hsize, PROT_READ, MAP_COPY, hfd, 0);
+
+ if (addr == (caddr_t)-1) {
+ close(hfd);
+ hheader = (struct hints_header *)-1;
+ return;
+ }
+
+ hheader = (struct hints_header *)addr;
+ if (HH_BADMAG(*hheader)) {
+ munmap(addr, hsize);
+ close(hfd);
+ hheader = (struct hints_header *)-1;
+ return;
+ }
+
+ if (hheader->hh_version != LD_HINTS_VERSION_1) {
+ munmap(addr, hsize);
+ close(hfd);
+ hheader = (struct hints_header *)-1;
+ return;
+ }
+
+ if (hheader->hh_ehints > hsize) {
+ if (mmap(addr+hsize, hheader->hh_ehints - hsize,
+ PROT_READ, MAP_COPY|MAP_FIXED,
+ hfd, hsize) != (caddr_t)(addr+hsize)) {
+
+ munmap((caddr_t)hheader, hsize);
+ close(hfd);
+ hheader = (struct hints_header *)-1;
+ return;
+ }
+ }
+
+ hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab);
+ hstrtab = (char *)(addr + hheader->hh_strtab);
+}
+
+ static void
+unmaphints()
+{
+
+ if (HINTS_VALID) {
+ munmap((caddr_t)hheader, hsize);
+ close(hfd);
+ hheader = NULL;
+ }
+}
+
+ int
+hinthash(cp, vmajor)
+ char *cp;
+ int vmajor;
+{
+ int k = 0;
+
+ while (*cp)
+ k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
+
+ k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
+
+ return k;
+}
+
+#undef major
+#undef minor
+
+ static char *
+findhint(name, major, minor, preferred_path)
+ char *name;
+ int major, minor;
+ char *preferred_path;
+{
+ struct hints_bucket *bp;
+
+ bp = hbuckets + (hinthash(name, major) % hheader->hh_nbucket);
+
+ while (1) {
+ /* Sanity check */
+ if (bp->hi_namex >= hheader->hh_strtab_sz) {
+ warnx("Bad name index: %#x\n", bp->hi_namex);
+ break;
+ }
+ if (bp->hi_pathx >= hheader->hh_strtab_sz) {
+ warnx("Bad path index: %#x\n", bp->hi_pathx);
+ break;
+ }
+
+ if (strcmp(name, hstrtab + bp->hi_namex) == 0) {
+ /* It's `name', check version numbers */
+ if (bp->hi_major == major &&
+ (bp->hi_ndewey < 2 || bp->hi_minor >= minor)) {
+ if (preferred_path == NULL ||
+ strcmp(preferred_path,
+ hstrtab + bp->hi_pathx) == 0) {
+ return hstrtab + bp->hi_pathx;
+ }
+ }
+ }
+
+ if (bp->hi_next == -1)
+ break;
+
+ /* Move on to next in bucket */
+ bp = &hbuckets[bp->hi_next];
+ }
+
+ /* No hints available for name */
+ return NULL;
+}
+
+ static char *
+rtfindlib(name, major, minor, usehints)
+ char *name;
+ int major, minor;
+ int *usehints;
+{
+ char *cp, *ld_path = ld_library_path;
+ int realminor;
+
+ if (hheader == NULL)
+ maphints();
+
+ if (!HINTS_VALID || !(*usehints))
+ goto lose;
+
+ if (ld_path != NULL) {
+ /* Prefer paths from LD_LIBRARY_PATH */
+ while ((cp = strsep(&ld_path, ":")) != NULL) {
+
+ cp = findhint(name, major, minor, cp);
+ if (ld_path)
+ *(ld_path-1) = ':';
+ if (cp)
+ return cp;
+ }
+ /* Not found in hints, try directory search */
+ realminor = -1;
+ cp = (char *)findshlib(name, &major, &realminor, 0);
+ if (cp && realminor >= minor)
+ return cp;
+ }
+
+ /* No LD_LIBRARY_PATH or lib not found in there; check default */
+ cp = findhint(name, major, minor, NULL);
+ if (cp)
+ return cp;
+
+lose:
+ /* No hints available for name */
+ *usehints = 0;
+ realminor = -1;
+ cp = (char *)findshlib(name, &major, &realminor, 0);
+ if (cp) {
+ if (realminor < minor && getenv("LD_SUPPRESS_WARNINGS") == NULL)
+ warnx("warning: lib%s.so.%d.%d: "
+ "minor version < %d expected, using it anyway",
+ name, major, realminor, minor);
+ return cp;
+ }
+ generror ("Can't find shared library \"%s\"",
+ name);
+ return NULL;
+}
+
+static struct somap_private dlmap_private = {
+ 0,
+ (struct so_map *)0,
+ 0,
+#ifdef SUN_COMPAT
+ 0,
+#endif
+};
+
+static struct so_map dlmap = {
+ (caddr_t)0,
+ "internal",
+ (struct so_map *)0,
+ (struct sod *)0,
+ (caddr_t)0,
+ (u_int)0,
+ (struct _dynamic *)0,
+ (caddr_t)&dlmap_private
+};
+
+/*
+ * Buffer for error messages and a pointer that is set to point to the buffer
+ * when a error occurs. It acts as a last error flag, being set to NULL
+ * after an error is returned.
+ */
+#define DLERROR_BUF_SIZE 512
+static char dlerror_buf [DLERROR_BUF_SIZE];
+static char *dlerror_msg = NULL;
+
+
+ static void *
+__dlopen(name, mode)
+ char *name;
+ int mode;
+{
+ struct sod *sodp;
+ struct so_map *smp;
+
+ /*
+ * A NULL argument returns the current set of mapped objects.
+ */
+ if (name == NULL) {
+ LM_PRIVATE(link_map_head)->spd_refcount++;
+ return link_map_head;
+ }
+ if ((sodp = (struct sod *)malloc(sizeof(struct sod))) == NULL) {
+ generror ("malloc failed: %s", strerror (errno));
+ return NULL;
+ }
+
+ sodp->sod_name = (long)strdup(name);
+ sodp->sod_library = 0;
+ sodp->sod_major = sodp->sod_minor = 0;
+
+ if ((smp = load_object(sodp, &dlmap, RTLD_DL, 1)) == NULL) {
+#ifdef DEBUG
+ xprintf("%s: %s\n", name, dlerror_buf);
+#endif
+ return NULL;
+ }
+
+ /*
+ * If this was newly loaded, call the _init() function in the
+ * object as per manpage.
+ */
+ if (LM_PRIVATE(smp)->spd_refcount == 1)
+ call_map(smp, "__init");
+
+ return smp;
+}
+
+ static int
+__dlclose(fd)
+ void *fd;
+{
+ struct so_map *smp = (struct so_map *)fd;
+
+#ifdef DEBUG
+ xprintf("dlclose(%s): refcount = %d\n", smp->som_path,
+ LM_PRIVATE(smp)->spd_refcount);
+#endif
+
+ if (smp == NULL) {
+ generror("NULL argument to dlclose");
+ return -1;
+ }
+
+ if (LM_PRIVATE(smp)->spd_refcount > 1) {
+ LM_PRIVATE(smp)->spd_refcount--;
+ return 0;
+ }
+
+ /*
+ * Call the function _fini() in the object as per manpage.
+ */
+ call_map(smp, "__fini");
+
+ free((void*) smp->som_sod->sod_name);
+ free(smp->som_sod);
+ if (unload_object(smp) < 0)
+ return -1;
+
+ return 0;
+}
+
+ static void *
+__dlsym(fd, sym)
+ void *fd;
+ char *sym;
+{
+ struct so_map *smp = (struct so_map *)fd, *src_map = NULL;
+ struct nzlist *np;
+ long addr;
+
+ /*
+ * Restrict search to passed map if dlopen()ed.
+ */
+ if (smp && LM_PRIVATE(smp)->spd_flags & RTLD_DL)
+ src_map = smp;
+
+ np = lookup(sym, &src_map, 1);
+ if (np == NULL) {
+ generror ("Symbol \"%s\" not found", sym);
+ return NULL;
+ }
+ /* Fixup jmpslot so future calls transfer directly to target */
+ addr = np->nz_value;
+ if (src_map)
+ addr += (long)src_map->som_addr;
+
+ return (void *)addr;
+}
+
+ static char *
+__dlerror __P((void))
+{
+ char *err;
+
+ err = dlerror_msg;
+ dlerror_msg = NULL; /* Next call will return NULL */
+
+ return err;
+}
+
+ static void
+__dlexit __P((void))
+{
+ struct so_map *smp;
+
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ if (LM_PRIVATE(smp)->spd_flags & (RTLD_RTLD|RTLD_MAIN))
+ continue;
+ call_map(smp, ".fini");
+ }
+}
+
+/*
+ * Generate an error message that can be later be retrieved via dlerror.
+ */
+static void
+#if __STDC__
+generror(char *fmt, ...)
+#else
+generror(fmt, va_alist)
+char *fmt;
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vsnprintf (dlerror_buf, DLERROR_BUF_SIZE, fmt, ap);
+ dlerror_msg = dlerror_buf;
+
+ va_end(ap);
+}
+
+void
+#if __STDC__
+xprintf(char *fmt, ...)
+#else
+xprintf(fmt, va_alist)
+char *fmt;
+#endif
+{
+ char buf[256];
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ vsprintf(buf, fmt, ap);
+ (void)write(1, buf, strlen(buf));
+ va_end(ap);
+}
+
diff --git a/libexec/rtld-aout/shlib.c b/libexec/rtld-aout/shlib.c
new file mode 100644
index 0000000..f256508
--- /dev/null
+++ b/libexec/rtld-aout/shlib.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: shlib.c,v 1.12 1995/03/04 17:46:09 nate Exp $
+ */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <err.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <a.out.h>
+
+#include "ld.h"
+
+#ifdef SUNOS4
+char *strsep();
+#endif
+
+/*
+ * Standard directories to search for files specified by -l.
+ */
+#ifndef STANDARD_SEARCH_DIRS
+#define STANDARD_SEARCH_DIRS "/usr/lib"
+#endif
+
+/*
+ * Actual vector of library search directories,
+ * including `-L'ed and LD_LIBARAY_PATH spec'd ones.
+ */
+char **search_dirs;
+int n_search_dirs;
+
+char *standard_search_dirs[] = {
+ STANDARD_SEARCH_DIRS
+};
+
+
+void
+add_search_dir(name)
+ char *name;
+{
+ n_search_dirs++;
+ search_dirs = (char **)
+ xrealloc(search_dirs, n_search_dirs * sizeof(char *));
+ search_dirs[n_search_dirs - 1] = strdup(name);
+}
+
+void
+add_search_path(path)
+char *path;
+{
+ register char *cp;
+
+ if (path == NULL)
+ return;
+
+ /* Add search directories from `paths' */
+ while ((cp = strsep(&path, ":")) != NULL) {
+ add_search_dir(cp);
+ if (path)
+ *(path-1) = ':';
+ }
+}
+
+void
+std_search_path()
+{
+ int i, n;
+
+ /* Append standard search directories */
+ n = sizeof standard_search_dirs / sizeof standard_search_dirs[0];
+ for (i = 0; i < n; i++)
+ add_search_dir(standard_search_dirs[i]);
+}
+
+/*
+ * Return true if CP points to a valid dewey number.
+ * Decode and leave the result in the array DEWEY.
+ * Return the number of decoded entries in DEWEY.
+ */
+
+int
+getdewey(dewey, cp)
+int dewey[];
+char *cp;
+{
+ int i, n;
+
+ for (n = 0, i = 0; i < MAXDEWEY; i++) {
+ if (*cp == '\0')
+ break;
+
+ if (*cp == '.') cp++;
+ if (!isdigit(*cp))
+ return 0;
+
+ dewey[n++] = strtol(cp, &cp, 10);
+ }
+
+ return n;
+}
+
+/*
+ * Compare two dewey arrays.
+ * Return -1 if `d1' represents a smaller value than `d2'.
+ * Return 1 if `d1' represents a greater value than `d2'.
+ * Return 0 if equal.
+ */
+int
+cmpndewey(d1, n1, d2, n2)
+int d1[], d2[];
+int n1, n2;
+{
+ register int i;
+
+ for (i = 0; i < n1 && i < n2; i++) {
+ if (d1[i] < d2[i])
+ return -1;
+ if (d1[i] > d2[i])
+ return 1;
+ }
+
+ if (n1 == n2)
+ return 0;
+
+ if (i == n1)
+ return -1;
+
+ if (i == n2)
+ return 1;
+
+ errx(1, "cmpndewey: cant happen");
+ return 0;
+}
+
+/*
+ * Search directories for a shared library matching the given
+ * major and minor version numbers.
+ *
+ * MAJOR == -1 && MINOR == -1 --> find highest version
+ * MAJOR != -1 && MINOR == -1 --> find highest minor version
+ * MAJOR == -1 && MINOR != -1 --> invalid
+ * MAJOR != -1 && MINOR != -1 --> find highest micro version
+ */
+
+/* Not interested in devices right now... */
+#undef major
+#undef minor
+
+char *
+findshlib(name, majorp, minorp, do_dot_a)
+char *name;
+int *majorp, *minorp;
+int do_dot_a;
+{
+ int dewey[MAXDEWEY];
+ int ndewey;
+ int tmp[MAXDEWEY];
+ int i;
+ int len;
+ char *lname, *path = NULL;
+ int major = *majorp, minor = *minorp;
+
+ len = strlen(name);
+ lname = (char *)alloca(len + sizeof("lib"));
+ sprintf(lname, "lib%s", name);
+ len += 3;
+
+ ndewey = 0;
+
+ for (i = 0; i < n_search_dirs; i++) {
+ DIR *dd = opendir(search_dirs[i]);
+ struct dirent *dp;
+ int found_dot_a = 0;
+
+ if (dd == NULL)
+ continue;
+
+ while ((dp = readdir(dd)) != NULL) {
+ int n, might_take_it = 0;
+
+ if (do_dot_a && path == NULL &&
+ dp->d_namlen == len + 2 &&
+ strncmp(dp->d_name, lname, len) == 0 &&
+ (dp->d_name+len)[0] == '.' &&
+ (dp->d_name+len)[1] == 'a') {
+
+ path = concat(search_dirs[i], "/", dp->d_name);
+ found_dot_a = 1;
+ }
+
+ if (dp->d_namlen < len + 4)
+ continue;
+ if (strncmp(dp->d_name, lname, len) != 0)
+ continue;
+ if (strncmp(dp->d_name+len, ".so.", 4) != 0)
+ continue;
+
+ if ((n = getdewey(tmp, dp->d_name+len+4)) == 0)
+ continue;
+
+ if (major != -1 && found_dot_a) { /* XXX */
+ free(path);
+ path = NULL;
+ found_dot_a = 0;
+ }
+
+ if (major == -1 && minor == -1) {
+ might_take_it = 1;
+ } else if (major != -1 && minor == -1) {
+ if (tmp[0] == major)
+ might_take_it = 1;
+ } else if (major != -1 && minor != -1) {
+ if (tmp[0] == major)
+ if (n == 1 || tmp[1] >= minor)
+ might_take_it = 1;
+ }
+
+ if (!might_take_it)
+ continue;
+
+ if (cmpndewey(tmp, n, dewey, ndewey) <= 0)
+ continue;
+
+ /* We have a better version */
+ if (path)
+ free(path);
+ path = concat(search_dirs[i], "/", dp->d_name);
+ found_dot_a = 0;
+ bcopy(tmp, dewey, sizeof(dewey));
+ ndewey = n;
+ *majorp = dewey[0];
+ *minorp = dewey[1];
+ }
+ closedir(dd);
+
+ if (found_dot_a)
+ /*
+ * There's a .a archive here.
+ */
+ return path;
+ }
+
+ return path;
+}
diff --git a/libexec/rtld-elf/rtld.1 b/libexec/rtld-elf/rtld.1
new file mode 100644
index 0000000..2b3c75a
--- /dev/null
+++ b/libexec/rtld-elf/rtld.1
@@ -0,0 +1,144 @@
+.\" $Id$
+.\"
+.\" Copyright (c) 1995 Paul Kranenburg
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Paul Kranenburg.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd June 27, 1995
+.Dt RTLD 1
+.Os FreeBSD
+.Sh NAME
+.Nm ld.so
+.Nd run-time link-editor
+.Sh DESCRIPTION
+.Nm
+is a self-contained, position independent program image providing run-time
+support for loading and link-editing shared objects into a process'
+address space. It uses the data structures
+.Po
+see
+.Xr link 5
+.Pc
+contained within dynamically linked programs to determine which shared
+libraries are needed and loads them at a convenient virtual address
+using the
+.Xr mmap 2
+system call.
+.Pp
+After all shared libraries have been succesfully loaded,
+.Nm
+proceeds to resolve external references from both the main program and
+all objects loaded. A mechanism is provided for initialisation routines
+to be called, on a per-object basis, giving a shared object an opportunity
+to perfrom any extra set-up, before execution of the program proper begins.
+This is useful for C++ libraries that contain static constrictors.
+.Pp
+.Nm
+is itself a shared object that is initially loaded by the startup module
+.Em crt0 .
+Since
+.Xr a.out 5
+formats do not provide easy access to the file header from within a running
+process,
+.Em crt0
+uses the special symbol
+.Va _DYNAMIC
+to determine whether a program is in fact dynamically linked or not. Whenever
+the linker
+.Xr ld 1
+has relocated this symbol to a location other then 0,
+.Em crt0
+assumes the services of
+.Nm
+are needed
+.Po
+see
+.Xr link 5
+for details
+.Pc \&.
+.Em crt0
+passes control to
+.Nm
+\&'s entry point before the program's
+.Fn main
+routine is called. Thus,
+.Nm
+can complete the link-editing process before the dynamic program calls upon
+services of any dynamic library.
+.Pp
+To quickly locate the required shared objects in the filesystem,
+.Nm
+may use a
+.Dq hints
+file, prepared by the
+.Xr ldconfig 8
+utility, in which the full path specification of the shared objects can be
+looked up by hashing on the 3-tuple
+.Ao
+library-name, major-version-number, minor-version-number
+.Ac \&.
+.Pp
+.Nm
+recognises a number of environment variables that can be used to modify
+its behaviour as follows:
+.Pp
+.Bl -tag -width "LD_TRACE_LOADED_OBJECTS"
+.It Ev LD_LIBRARY_PATH
+A colon separated list of directories, overriding the default search path
+for shared libraries.
+.It Ev LD_WARN_NON_PURE_CODE
+When set, issue a warning whenever a link-editing operation requires
+modification of the text segment of some loaded object. This is usually
+indicative of an incorrectly built library.
+.It Ev LD_SUPPRESS_WARNINGS
+When set, no warning messages of any kind are issued. Normally, a warning
+is given if satisfactorily versioned library could not be found.
+.It Ev LD_TRACE_LOADED_OBJECTS
+When set, causes
+.Nm
+to exit after loading the shared objects and printing a summary which includes
+the absolute pathnames of all objects, to standard output.
+.It Ev LD_NO_INTERN_SEARCH
+When set,
+.Nm
+does not process any internal search paths that were recorded in the
+executable.
+.It Ev LD_NOSTD_PATH
+When set, do not include a set of built-in standard directory paths for
+searching. This might be useful when running on a system with a completely
+non-standard filesystem layout.
+.El
+.Pp
+.Sh FILES
+/var/run/ld.so.hints
+.Pp
+.Sh SEE ALSO
+.Xr ld 1
+.Xr ldconfig 8
+.Xr link 5
+.Sh HISTORY
+The shared library model employed first appeared in SunOS 4.0
diff --git a/libexec/talkd/Makefile b/libexec/talkd/Makefile
new file mode 100644
index 0000000..4b4a8399
--- /dev/null
+++ b/libexec/talkd/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+
+PROG= ntalkd
+SRCS= talkd.c announce.c process.c table.c print.c ttymsg.c
+.PATH: ${.CURDIR}/../../usr.bin/wall
+MAN8= talkd.8
+
+.include <bsd.prog.mk>
diff --git a/libexec/talkd/announce.c b/libexec/talkd/announce.c
new file mode 100644
index 0000000..db03bbf
--- /dev/null
+++ b/libexec/talkd/announce.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)announce.c 8.2 (Berkeley) 1/7/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <protocols/talkd.h>
+#include <sgtty.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <paths.h>
+
+extern char hostname[];
+
+/*
+ * Announce an invitation to talk.
+ */
+
+/*
+ * See if the user is accepting messages. If so, announce that
+ * a talk is requested.
+ */
+announce(request, remote_machine)
+ CTL_MSG *request;
+ char *remote_machine;
+{
+ char full_tty[32];
+ FILE *tf;
+ struct stat stbuf;
+
+ (void)snprintf(full_tty, sizeof(full_tty),
+ "%s%s", _PATH_DEV, request->r_tty);
+ if (stat(full_tty, &stbuf) < 0 || (stbuf.st_mode&020) == 0)
+ return (PERMISSION_DENIED);
+ return (print_mesg(request->r_tty, tf, request, remote_machine));
+}
+
+#define max(a,b) ( (a) > (b) ? (a) : (b) )
+#define N_LINES 5
+#define N_CHARS 120
+
+/*
+ * Build a block of characters containing the message.
+ * It is sent blank filled and in a single block to
+ * try to keep the message in one piece if the recipient
+ * in in vi at the time
+ */
+print_mesg(tty, tf, request, remote_machine)
+ char *tty;
+ FILE *tf;
+ CTL_MSG *request;
+ char *remote_machine;
+{
+ struct timeval clock;
+ struct timezone zone;
+ struct tm *localtime();
+ struct tm *localclock;
+ struct iovec iovec;
+ char line_buf[N_LINES][N_CHARS];
+ int sizes[N_LINES];
+ char big_buf[N_LINES*N_CHARS];
+ char *bptr, *lptr, *ttymsg();
+ int i, j, max_size;
+
+ i = 0;
+ max_size = 0;
+ gettimeofday(&clock, &zone);
+ localclock = localtime( &clock.tv_sec );
+ (void)sprintf(line_buf[i], " ");
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ (void)sprintf(line_buf[i], "Message from Talk_Daemon@%s at %d:%02d ...",
+ hostname, localclock->tm_hour , localclock->tm_min );
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ (void)sprintf(line_buf[i], "talk: connection requested by %s@%s",
+ request->l_name, remote_machine);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ (void)sprintf(line_buf[i], "talk: respond with: talk %s@%s",
+ request->l_name, remote_machine);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ (void)sprintf(line_buf[i], " ");
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ bptr = big_buf;
+ *bptr++ = '\007'; /* send something to wake them up */
+ *bptr++ = '\r'; /* add a \r in case of raw mode */
+ *bptr++ = '\n';
+ for (i = 0; i < N_LINES; i++) {
+ /* copy the line into the big buffer */
+ lptr = line_buf[i];
+ while (*lptr != '\0')
+ *(bptr++) = *(lptr++);
+ /* pad out the rest of the lines with blanks */
+ for (j = sizes[i]; j < max_size + 2; j++)
+ *(bptr++) = ' ';
+ *(bptr++) = '\r'; /* add a \r in case of raw mode */
+ *(bptr++) = '\n';
+ }
+ *bptr = '\0';
+ iovec.iov_base = big_buf;
+ iovec.iov_len = bptr - big_buf;
+ /*
+ * we choose a timeout of RING_WAIT-5 seconds so that we don't
+ * stack up processes trying to write messages to a tty
+ * that is permanently blocked.
+ */
+ if (ttymsg(&iovec, 1, tty, RING_WAIT - 5) != NULL)
+ return (FAILED);
+
+ return (SUCCESS);
+}
diff --git a/libexec/talkd/print.c b/libexec/talkd/print.c
new file mode 100644
index 0000000..0769d97
--- /dev/null
+++ b/libexec/talkd/print.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+/* debug print routines */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <protocols/talkd.h>
+#include <syslog.h>
+#include <stdio.h>
+
+static char *types[] =
+ { "leave_invite", "look_up", "delete", "announce" };
+#define NTYPES (sizeof (types) / sizeof (types[0]))
+static char *answers[] =
+ { "success", "not_here", "failed", "machine_unknown", "permission_denied",
+ "unknown_request", "badversion", "badaddr", "badctladdr" };
+#define NANSWERS (sizeof (answers) / sizeof (answers[0]))
+
+print_request(cp, mp)
+ char *cp;
+ register CTL_MSG *mp;
+{
+ char tbuf[80], *tp;
+
+ if (mp->type > NTYPES) {
+ (void)sprintf(tbuf, "type %d", mp->type);
+ tp = tbuf;
+ } else
+ tp = types[mp->type];
+ syslog(LOG_DEBUG, "%s: %s: id %d, l_user %s, r_user %s, r_tty %s",
+ cp, tp, mp->id_num, mp->l_name, mp->r_name, mp->r_tty);
+}
+
+print_response(cp, rp)
+ char *cp;
+ register CTL_RESPONSE *rp;
+{
+ char tbuf[80], *tp, abuf[80], *ap;
+
+ if (rp->type > NTYPES) {
+ (void)sprintf(tbuf, "type %d", rp->type);
+ tp = tbuf;
+ } else
+ tp = types[rp->type];
+ if (rp->answer > NANSWERS) {
+ (void)sprintf(abuf, "answer %d", rp->answer);
+ ap = abuf;
+ } else
+ ap = answers[rp->answer];
+ syslog(LOG_DEBUG, "%s: %s: %s, id %d", cp, tp, ap, ntohl(rp->id_num));
+}
diff --git a/libexec/talkd/process.c b/libexec/talkd/process.c
new file mode 100644
index 0000000..a83492b
--- /dev/null
+++ b/libexec/talkd/process.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)process.c 8.2 (Berkeley) 11/16/93";
+#endif /* not lint */
+
+/*
+ * process.c handles the requests, which can be of three types:
+ * ANNOUNCE - announce to a user that a talk is wanted
+ * LEAVE_INVITE - insert the request into the table
+ * LOOK_UP - look up to see if a request is waiting in
+ * in the table for the local user
+ * DELETE - delete invitation
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <protocols/talkd.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <paths.h>
+
+CTL_MSG *find_request();
+CTL_MSG *find_match();
+
+process_request(mp, rp)
+ register CTL_MSG *mp;
+ register CTL_RESPONSE *rp;
+{
+ register CTL_MSG *ptr;
+ extern int debug;
+ char *s;
+
+ rp->vers = TALK_VERSION;
+ rp->type = mp->type;
+ rp->id_num = htonl(0);
+ if (mp->vers != TALK_VERSION) {
+ syslog(LOG_WARNING, "Bad protocol version %d", mp->vers);
+ rp->answer = BADVERSION;
+ return;
+ }
+ mp->id_num = ntohl(mp->id_num);
+ mp->addr.sa_family = ntohs(mp->addr.sa_family);
+ if (mp->addr.sa_family != AF_INET) {
+ syslog(LOG_WARNING, "Bad address, family %d",
+ mp->addr.sa_family);
+ rp->answer = BADADDR;
+ return;
+ }
+ mp->ctl_addr.sa_family = ntohs(mp->ctl_addr.sa_family);
+ if (mp->ctl_addr.sa_family != AF_INET) {
+ syslog(LOG_WARNING, "Bad control address, family %d",
+ mp->ctl_addr.sa_family);
+ rp->answer = BADCTLADDR;
+ return;
+ }
+ for (s = mp->l_name; *s; s++)
+ if (!isprint(*s)) {
+ syslog(LOG_NOTICE, "Illegal user name. Aborting");
+ rp->answer = FAILED;
+ return;
+ }
+ mp->pid = ntohl(mp->pid);
+ if (debug)
+ print_request("process_request", mp);
+ switch (mp->type) {
+
+ case ANNOUNCE:
+ do_announce(mp, rp);
+ break;
+
+ case LEAVE_INVITE:
+ ptr = find_request(mp);
+ if (ptr != (CTL_MSG *)0) {
+ rp->id_num = htonl(ptr->id_num);
+ rp->answer = SUCCESS;
+ } else
+ insert_table(mp, rp);
+ break;
+
+ case LOOK_UP:
+ ptr = find_match(mp);
+ if (ptr != (CTL_MSG *)0) {
+ rp->id_num = htonl(ptr->id_num);
+ rp->addr = ptr->addr;
+ rp->addr.sa_family = htons(ptr->addr.sa_family);
+ rp->answer = SUCCESS;
+ } else
+ rp->answer = NOT_HERE;
+ break;
+
+ case DELETE:
+ rp->answer = delete_invite(mp->id_num);
+ break;
+
+ default:
+ rp->answer = UNKNOWN_REQUEST;
+ break;
+ }
+ if (debug)
+ print_response("process_request", rp);
+}
+
+do_announce(mp, rp)
+ register CTL_MSG *mp;
+ CTL_RESPONSE *rp;
+{
+ struct hostent *hp;
+ CTL_MSG *ptr;
+ int result;
+
+ /* see if the user is logged */
+ result = find_user(mp->r_name, mp->r_tty);
+ if (result != SUCCESS) {
+ rp->answer = result;
+ return;
+ }
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+ hp = gethostbyaddr((char *)&satosin(&mp->ctl_addr)->sin_addr,
+ sizeof (struct in_addr), AF_INET);
+ if (hp == (struct hostent *)0) {
+ rp->answer = MACHINE_UNKNOWN;
+ return;
+ }
+ ptr = find_request(mp);
+ if (ptr == (CTL_MSG *) 0) {
+ insert_table(mp, rp);
+ rp->answer = announce(mp, hp->h_name);
+ return;
+ }
+ if (mp->id_num > ptr->id_num) {
+ /*
+ * This is an explicit re-announce, so update the id_num
+ * field to avoid duplicates and re-announce the talk.
+ */
+ ptr->id_num = new_id();
+ rp->id_num = htonl(ptr->id_num);
+ rp->answer = announce(mp, hp->h_name);
+ } else {
+ /* a duplicated request, so ignore it */
+ rp->id_num = htonl(ptr->id_num);
+ rp->answer = SUCCESS;
+ }
+}
+
+#include <utmp.h>
+
+/*
+ * Search utmp for the local user
+ */
+find_user(name, tty)
+ char *name, *tty;
+{
+ struct utmp ubuf;
+ int status;
+ FILE *fd;
+ struct stat statb;
+ char line[sizeof(ubuf.ut_line) + 1];
+ char ftty[sizeof(_PATH_DEV) - 1 + sizeof(line)];
+
+ if ((fd = fopen(_PATH_UTMP, "r")) == NULL) {
+ fprintf(stderr, "talkd: can't read %s.\n", _PATH_UTMP);
+ return (FAILED);
+ }
+#define SCMPN(a, b) strncmp(a, b, sizeof (a))
+ status = NOT_HERE;
+ (void) strcpy(ftty, _PATH_DEV);
+ while (fread((char *) &ubuf, sizeof ubuf, 1, fd) == 1)
+ if (SCMPN(ubuf.ut_name, name) == 0) {
+ strncpy(line, ubuf.ut_line, sizeof(ubuf.ut_line));
+ line[sizeof(ubuf.ut_line)] = '\0';
+ if (*tty == '\0') {
+ status = PERMISSION_DENIED;
+ /* no particular tty was requested */
+ (void) strcpy(ftty + sizeof(_PATH_DEV) - 1,
+ line);
+ if (stat(ftty, &statb) == 0) {
+ if (!(statb.st_mode & 020))
+ continue;
+ (void) strcpy(tty, line);
+ status = SUCCESS;
+ break;
+ }
+ }
+ if (strcmp(line, tty) == 0) {
+ status = SUCCESS;
+ break;
+ }
+ }
+ fclose(fd);
+ return (status);
+}
diff --git a/libexec/talkd/table.c b/libexec/talkd/table.c
new file mode 100644
index 0000000..ef57206
--- /dev/null
+++ b/libexec/talkd/table.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)table.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+/*
+ * Routines to handle insertion, deletion, etc on the table
+ * of requests kept by the daemon. Nothing fancy here, linear
+ * search on a double-linked list. A time is kept with each
+ * entry so that overly old invitations can be eliminated.
+ *
+ * Consider this a mis-guided attempt at modularity
+ */
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <protocols/talkd.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_ID 16000 /* << 2^15 so I don't have sign troubles */
+
+#define NIL ((TABLE_ENTRY *)0)
+
+extern int debug;
+struct timeval tp;
+struct timezone txp;
+
+typedef struct table_entry TABLE_ENTRY;
+
+struct table_entry {
+ CTL_MSG request;
+ long time;
+ TABLE_ENTRY *next;
+ TABLE_ENTRY *last;
+};
+
+TABLE_ENTRY *table = NIL;
+CTL_MSG *find_request();
+CTL_MSG *find_match();
+
+/*
+ * Look in the table for an invitation that matches the current
+ * request looking for an invitation
+ */
+CTL_MSG *
+find_match(request)
+ register CTL_MSG *request;
+{
+ register TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ gettimeofday(&tp, &txp);
+ current_time = tp.tv_sec;
+ if (debug)
+ print_request("find_match", request);
+ for (ptr = table; ptr != NIL; ptr = ptr->next) {
+ if ((ptr->time - current_time) > MAX_LIFE) {
+ /* the entry is too old */
+ if (debug)
+ print_request("deleting expired entry",
+ &ptr->request);
+ delete(ptr);
+ continue;
+ }
+ if (debug)
+ print_request("", &ptr->request);
+ if (strcmp(request->l_name, ptr->request.r_name) == 0 &&
+ strcmp(request->r_name, ptr->request.l_name) == 0 &&
+ ptr->request.type == LEAVE_INVITE)
+ return (&ptr->request);
+ }
+ return ((CTL_MSG *)0);
+}
+
+/*
+ * Look for an identical request, as opposed to a complimentary
+ * one as find_match does
+ */
+CTL_MSG *
+find_request(request)
+ register CTL_MSG *request;
+{
+ register TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ gettimeofday(&tp, &txp);
+ current_time = tp.tv_sec;
+ /*
+ * See if this is a repeated message, and check for
+ * out of date entries in the table while we are it.
+ */
+ if (debug)
+ print_request("find_request", request);
+ for (ptr = table; ptr != NIL; ptr = ptr->next) {
+ if ((ptr->time - current_time) > MAX_LIFE) {
+ /* the entry is too old */
+ if (debug)
+ print_request("deleting expired entry",
+ &ptr->request);
+ delete(ptr);
+ continue;
+ }
+ if (debug)
+ print_request("", &ptr->request);
+ if (strcmp(request->r_name, ptr->request.r_name) == 0 &&
+ strcmp(request->l_name, ptr->request.l_name) == 0 &&
+ request->type == ptr->request.type &&
+ request->pid == ptr->request.pid) {
+ /* update the time if we 'touch' it */
+ ptr->time = current_time;
+ return (&ptr->request);
+ }
+ }
+ return ((CTL_MSG *)0);
+}
+
+insert_table(request, response)
+ CTL_MSG *request;
+ CTL_RESPONSE *response;
+{
+ register TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ gettimeofday(&tp, &txp);
+ current_time = tp.tv_sec;
+ request->id_num = new_id();
+ response->id_num = htonl(request->id_num);
+ /* insert a new entry into the top of the list */
+ ptr = (TABLE_ENTRY *)malloc(sizeof(TABLE_ENTRY));
+ if (ptr == NIL) {
+ syslog(LOG_ERR, "insert_table: Out of memory");
+ _exit(1);
+ }
+ ptr->time = current_time;
+ ptr->request = *request;
+ ptr->next = table;
+ if (ptr->next != NIL)
+ ptr->next->last = ptr;
+ ptr->last = NIL;
+ table = ptr;
+}
+
+/*
+ * Generate a unique non-zero sequence number
+ */
+new_id()
+{
+ static int current_id = 0;
+
+ current_id = (current_id + 1) % MAX_ID;
+ /* 0 is reserved, helps to pick up bugs */
+ if (current_id == 0)
+ current_id = 1;
+ return (current_id);
+}
+
+/*
+ * Delete the invitation with id 'id_num'
+ */
+delete_invite(id_num)
+ int id_num;
+{
+ register TABLE_ENTRY *ptr;
+
+ ptr = table;
+ if (debug)
+ syslog(LOG_DEBUG, "delete_invite(%d)", id_num);
+ for (ptr = table; ptr != NIL; ptr = ptr->next) {
+ if (ptr->request.id_num == id_num)
+ break;
+ if (debug)
+ print_request("", &ptr->request);
+ }
+ if (ptr != NIL) {
+ delete(ptr);
+ return (SUCCESS);
+ }
+ return (NOT_HERE);
+}
+
+/*
+ * Classic delete from a double-linked list
+ */
+delete(ptr)
+ register TABLE_ENTRY *ptr;
+{
+
+ if (debug)
+ print_request("delete", &ptr->request);
+ if (table == ptr)
+ table = ptr->next;
+ else if (ptr->last != NIL)
+ ptr->last->next = ptr->next;
+ if (ptr->next != NIL)
+ ptr->next->last = ptr->last;
+ free((char *)ptr);
+}
diff --git a/libexec/talkd/talkd.8 b/libexec/talkd/talkd.8
new file mode 100644
index 0000000..36dfb28
--- /dev/null
+++ b/libexec/talkd/talkd.8
@@ -0,0 +1,75 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)talkd.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt TALKD 8
+.Os BSD 4.3
+.Sh NAME
+.Nm talkd
+.Nd remote user communication server
+.Sh SYNOPSIS
+.Nm talkd
+.Sh DESCRIPTION
+.Nm Talkd
+is the server that notifies a user that someone else wants to
+initiate a conversation.
+It acts as a repository of invitations, responding to requests
+by clients wishing to rendezvous to hold a conversation.
+In normal operation, a client, the caller,
+initiates a rendezvous by sending a
+.Tn CTL_MSG
+to the server of
+type
+.Tn LOOK_UP
+(see
+.Aq Pa protocols/talkd.h ) .
+This causes the server to search its invitation
+tables to check if an invitation currently exists for the caller
+(to speak to the callee specified in the message).
+If the lookup fails,
+the caller then sends an
+.Tn ANNOUNCE
+message causing the server to
+broadcast an announcement on the callee's login ports requesting contact.
+When the callee responds, the local server uses the
+recorded invitation to respond with the appropriate rendezvous
+address and the caller and callee client programs establish a
+stream connection through which the conversation takes place.
+.Sh SEE ALSO
+.Xr talk 1 ,
+.Xr write 1
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/libexec/talkd/talkd.c b/libexec/talkd/talkd.c
new file mode 100644
index 0000000..9fd5950
--- /dev/null
+++ b/libexec/talkd/talkd.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)talkd.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+/*
+ * The top level of the daemon, the format is heavily borrowed
+ * from rwhod.c. Basically: find out who and where you are;
+ * disconnect all descriptors and ttys, and then endless
+ * loop on waiting for and processing requests
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <protocols/talkd.h>
+#include <signal.h>
+#include <syslog.h>
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <paths.h>
+
+CTL_MSG request;
+CTL_RESPONSE response;
+
+int sockt;
+int debug = 0;
+void timeout();
+long lastmsgtime;
+
+char hostname[MAXHOSTNAMELEN];
+
+#define TIMEOUT 30
+#define MAXIDLE 120
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register CTL_MSG *mp = &request;
+ int cc;
+
+ if (getuid()) {
+ fprintf(stderr, "%s: getuid: not super-user\n", argv[0]);
+ exit(1);
+ }
+ openlog("talkd", LOG_PID, LOG_DAEMON);
+ if (gethostname(hostname, sizeof (hostname) - 1) < 0) {
+ syslog(LOG_ERR, "gethostname: %m");
+ _exit(1);
+ }
+ if (chdir(_PATH_DEV) < 0) {
+ syslog(LOG_ERR, "chdir: %s: %m", _PATH_DEV);
+ _exit(1);
+ }
+ if (argc > 1 && strcmp(argv[1], "-d") == 0)
+ debug = 1;
+ signal(SIGALRM, timeout);
+ alarm(TIMEOUT);
+ for (;;) {
+ extern int errno;
+
+ cc = recv(0, (char *)mp, sizeof (*mp), 0);
+ if (cc != sizeof (*mp)) {
+ if (cc < 0 && errno != EINTR)
+ syslog(LOG_WARNING, "recv: %m");
+ continue;
+ }
+ lastmsgtime = time(0);
+ process_request(mp, &response);
+ /* can block here, is this what I want? */
+ cc = sendto(sockt, (char *)&response,
+ sizeof (response), 0, (struct sockaddr *)&mp->ctl_addr,
+ sizeof (mp->ctl_addr));
+ if (cc != sizeof (response))
+ syslog(LOG_WARNING, "sendto: %m");
+ }
+}
+
+void
+timeout()
+{
+
+ if (time(0) - lastmsgtime >= MAXIDLE)
+ _exit(0);
+ alarm(TIMEOUT);
+}
diff --git a/libexec/telnetd/Makefile b/libexec/telnetd/Makefile
new file mode 100644
index 0000000..0164c5d
--- /dev/null
+++ b/libexec/telnetd/Makefile
@@ -0,0 +1,18 @@
+# @(#)Makefile 8.2 (Berkeley) 12/15/93
+
+PROG= telnetd
+CFLAGS+=-DLINEMODE -DUSE_TERMIO -DDIAGNOSTICS
+#CFLAGS+=-DKLUDGELINEMODE
+CFLAGS+=-DOLD_ENVIRON -DENV_HACK
+CFLAGS+=-I${.CURDIR}/../../lib
+#CFLAGS+=-DAUTHENTICATION -DENCRYPTION
+SRCS= global.c slc.c state.c sys_term.c telnetd.c \
+ termstat.c utility.c
+#SRCS+= authenc.c
+DPADD= ${LIBUTIL} ${LIBTERMCAP}
+LDADD= -lutil -ltermcap -ltelnet
+#LDADD+= -lkrb -ldes
+MAN8= telnetd.8
+
+
+.include <bsd.prog.mk>
diff --git a/libexec/telnetd/defs.h b/libexec/telnetd/defs.h
new file mode 100644
index 0000000..a73d4a6
--- /dev/null
+++ b/libexec/telnetd/defs.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)defs.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * Telnet server defines
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifndef BSD
+# define BSD 43
+#endif
+
+#if defined(CRAY) && !defined(LINEMODE)
+# define SYSV_TERMIO
+# define LINEMODE
+# define KLUDGELINEMODE
+# define DIAGNOSTICS
+# if defined(UNICOS50) && !defined(UNICOS5)
+# define UNICOS5
+# endif
+# if !defined(UNICOS5)
+# define BFTPDAEMON
+# define HAS_IP_TOS
+# endif
+#endif /* CRAY */
+#if defined(UNICOS5) && !defined(NO_SETSID)
+# define NO_SETSID
+#endif
+
+#if defined(PRINTOPTIONS) && defined(DIAGNOSTICS)
+#define TELOPTS
+#define TELCMDS
+#define SLC_NAMES
+#endif
+
+#if defined(SYSV_TERMIO) && !defined(USE_TERMIO)
+# define USE_TERMIO
+#endif
+
+#include <sys/socket.h>
+#ifndef CRAY
+#include <sys/wait.h>
+#endif /* CRAY */
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#ifndef FILIO_H
+#include <sys/ioctl.h>
+#else
+#include <sys/filio.h>
+#endif
+
+#include <netinet/in.h>
+
+#include <arpa/telnet.h>
+
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#endif
+#include <signal.h>
+#include <errno.h>
+#include <netdb.h>
+#include <syslog.h>
+#ifndef LOG_DAEMON
+#define LOG_DAEMON 0
+#endif
+#ifndef LOG_ODELAY
+#define LOG_ODELAY 0
+#endif
+#include <ctype.h>
+#ifndef NO_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+#ifndef USE_TERMIO
+#include <sgtty.h>
+#else
+# ifdef SYSV_TERMIO
+# include <termio.h>
+# else
+# include <termios.h>
+# endif
+#endif
+#if !defined(USE_TERMIO) || defined(NO_CC_T)
+typedef unsigned char cc_t;
+#endif
+
+#ifdef __STDC__
+#include <unistd.h>
+#endif
+
+#ifndef _POSIX_VDISABLE
+# ifdef VDISABLE
+# define _POSIX_VDISABLE VDISABLE
+# else
+# define _POSIX_VDISABLE ((unsigned char)'\377')
+# endif
+#endif
+
+
+#ifdef CRAY
+# ifdef CRAY1
+# include <sys/pty.h>
+# ifndef FD_ZERO
+# include <sys/select.h>
+# endif /* FD_ZERO */
+# endif /* CRAY1 */
+
+#include <memory.h>
+#endif /* CRAY */
+
+#ifdef __hpux
+#include <sys/ptyio.h>
+#endif
+
+#if !defined(TIOCSCTTY) && defined(TCSETCTTY)
+# define TIOCSCTTY TCSETCTTY
+#endif
+
+#ifndef FD_SET
+#ifndef HAVE_fd_set
+typedef struct fd_set { int fds_bits[1]; } fd_set;
+#endif
+
+#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n)))
+#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n)))
+#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n)))
+#define FD_ZERO(p) ((p)->fds_bits[0] = 0)
+#endif /* FD_SET */
+
+/*
+ * I/O data buffers defines
+ */
+#define NETSLOP 64
+#ifdef CRAY
+#undef BUFSIZ
+#define BUFSIZ 2048
+#endif
+
+#define NIACCUM(c) { *netip++ = c; \
+ ncc++; \
+ }
+
+/* clock manipulations */
+#define settimer(x) (clocks.x = ++clocks.system)
+#define sequenceIs(x,y) (clocks.x < clocks.y)
+
+/*
+ * Linemode support states, in decreasing order of importance
+ */
+#define REAL_LINEMODE 0x04
+#define KLUDGE_OK 0x03
+#define NO_AUTOKLUDGE 0x02
+#define KLUDGE_LINEMODE 0x01
+#define NO_LINEMODE 0x00
+
+/*
+ * Structures of information for each special character function.
+ */
+typedef struct {
+ unsigned char flag; /* the flags for this function */
+ cc_t val; /* the value of the special character */
+} slcent, *Slcent;
+
+typedef struct {
+ slcent defset; /* the default settings */
+ slcent current; /* the current settings */
+ cc_t *sptr; /* a pointer to the char in */
+ /* system data structures */
+} slcfun, *Slcfun;
+
+#ifdef DIAGNOSTICS
+/*
+ * Diagnostics capabilities
+ */
+#define TD_REPORT 0x01 /* Report operations to client */
+#define TD_EXERCISE 0x02 /* Exercise client's implementation */
+#define TD_NETDATA 0x04 /* Display received data stream */
+#define TD_PTYDATA 0x08 /* Display data passed to pty */
+#define TD_OPTIONS 0x10 /* Report just telnet options */
+#endif /* DIAGNOSTICS */
+
+/*
+ * We keep track of each side of the option negotiation.
+ */
+
+#define MY_STATE_WILL 0x01
+#define MY_WANT_STATE_WILL 0x02
+#define MY_STATE_DO 0x04
+#define MY_WANT_STATE_DO 0x08
+
+/*
+ * Macros to check the current state of things
+ */
+
+#define my_state_is_do(opt) (options[opt]&MY_STATE_DO)
+#define my_state_is_will(opt) (options[opt]&MY_STATE_WILL)
+#define my_want_state_is_do(opt) (options[opt]&MY_WANT_STATE_DO)
+#define my_want_state_is_will(opt) (options[opt]&MY_WANT_STATE_WILL)
+
+#define my_state_is_dont(opt) (!my_state_is_do(opt))
+#define my_state_is_wont(opt) (!my_state_is_will(opt))
+#define my_want_state_is_dont(opt) (!my_want_state_is_do(opt))
+#define my_want_state_is_wont(opt) (!my_want_state_is_will(opt))
+
+#define set_my_state_do(opt) (options[opt] |= MY_STATE_DO)
+#define set_my_state_will(opt) (options[opt] |= MY_STATE_WILL)
+#define set_my_want_state_do(opt) (options[opt] |= MY_WANT_STATE_DO)
+#define set_my_want_state_will(opt) (options[opt] |= MY_WANT_STATE_WILL)
+
+#define set_my_state_dont(opt) (options[opt] &= ~MY_STATE_DO)
+#define set_my_state_wont(opt) (options[opt] &= ~MY_STATE_WILL)
+#define set_my_want_state_dont(opt) (options[opt] &= ~MY_WANT_STATE_DO)
+#define set_my_want_state_wont(opt) (options[opt] &= ~MY_WANT_STATE_WILL)
+
+/*
+ * Tricky code here. What we want to know is if the MY_STATE_WILL
+ * and MY_WANT_STATE_WILL bits have the same value. Since the two
+ * bits are adjacent, a little arithmatic will show that by adding
+ * in the lower bit, the upper bit will be set if the two bits were
+ * different, and clear if they were the same.
+ */
+#define my_will_wont_is_changing(opt) \
+ ((options[opt]+MY_STATE_WILL) & MY_WANT_STATE_WILL)
+
+#define my_do_dont_is_changing(opt) \
+ ((options[opt]+MY_STATE_DO) & MY_WANT_STATE_DO)
+
+/*
+ * Make everything symetrical
+ */
+
+#define HIS_STATE_WILL MY_STATE_DO
+#define HIS_WANT_STATE_WILL MY_WANT_STATE_DO
+#define HIS_STATE_DO MY_STATE_WILL
+#define HIS_WANT_STATE_DO MY_WANT_STATE_WILL
+
+#define his_state_is_do my_state_is_will
+#define his_state_is_will my_state_is_do
+#define his_want_state_is_do my_want_state_is_will
+#define his_want_state_is_will my_want_state_is_do
+
+#define his_state_is_dont my_state_is_wont
+#define his_state_is_wont my_state_is_dont
+#define his_want_state_is_dont my_want_state_is_wont
+#define his_want_state_is_wont my_want_state_is_dont
+
+#define set_his_state_do set_my_state_will
+#define set_his_state_will set_my_state_do
+#define set_his_want_state_do set_my_want_state_will
+#define set_his_want_state_will set_my_want_state_do
+
+#define set_his_state_dont set_my_state_wont
+#define set_his_state_wont set_my_state_dont
+#define set_his_want_state_dont set_my_want_state_wont
+#define set_his_want_state_wont set_my_want_state_dont
+
+#define his_will_wont_is_changing my_do_dont_is_changing
+#define his_do_dont_is_changing my_will_wont_is_changing
diff --git a/libexec/telnetd/ext.h b/libexec/telnetd/ext.h
new file mode 100644
index 0000000..2460cf6
--- /dev/null
+++ b/libexec/telnetd/ext.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ext.h 8.2 (Berkeley) 12/15/93
+ */
+
+/*
+ * Telnet server variable declarations
+ */
+extern char options[256];
+extern char do_dont_resp[256];
+extern char will_wont_resp[256];
+extern int linemode; /* linemode on/off */
+#ifdef LINEMODE
+extern int uselinemode; /* what linemode to use (on/off) */
+extern int editmode; /* edit modes in use */
+extern int useeditmode; /* edit modes to use */
+extern int alwayslinemode; /* command line option */
+# ifdef KLUDGELINEMODE
+extern int lmodetype; /* Client support for linemode */
+# endif /* KLUDGELINEMODE */
+#endif /* LINEMODE */
+extern int flowmode; /* current flow control state */
+extern int restartany; /* restart output on any character state */
+#ifdef DIAGNOSTICS
+extern int diagnostic; /* telnet diagnostic capabilities */
+#endif /* DIAGNOSTICS */
+#ifdef BFTPDAEMON
+extern int bftpd; /* behave as bftp daemon */
+#endif /* BFTPDAEMON */
+#if defined(SecurID)
+extern int require_SecurID;
+#endif
+#if defined(AUTHENTICATION)
+extern int auth_level;
+#endif
+
+extern slcfun slctab[NSLC + 1]; /* slc mapping table */
+
+char *terminaltype;
+
+/*
+ * I/O data buffers, pointers, and counters.
+ */
+extern char ptyobuf[BUFSIZ+NETSLOP], *pfrontp, *pbackp;
+
+extern char netibuf[BUFSIZ], *netip;
+
+extern char netobuf[BUFSIZ+NETSLOP], *nfrontp, *nbackp;
+extern char *neturg; /* one past last bye of urgent data */
+
+extern int pcc, ncc;
+
+#if defined(CRAY2) && defined(UNICOS5)
+extern int unpcc; /* characters left unprocessed by CRAY-2 terminal routine */
+extern char *unptyip; /* pointer to remaining characters in buffer */
+#endif
+
+extern int pty, net;
+extern char *line;
+extern int SYNCHing; /* we are in TELNET SYNCH mode */
+
+#ifndef P
+# ifdef __STDC__
+# define P(x) x
+# else
+# define P(x) ()
+# endif
+#endif
+
+extern void
+ _termstat P((void)),
+ add_slc P((int, int, int)),
+ check_slc P((void)),
+ change_slc P((int, int, int)),
+ cleanup P((int)),
+ clientstat P((int, int, int)),
+ copy_termbuf P((char *, int)),
+ deferslc P((void)),
+ defer_terminit P((void)),
+ do_opt_slc P((unsigned char *, int)),
+ doeof P((void)),
+ dooption P((int)),
+ dontoption P((int)),
+ edithost P((char *, char *)),
+ fatal P((int, char *)),
+ fatalperror P((int, char *)),
+ get_slc_defaults P((void)),
+ init_env P((void)),
+ init_termbuf P((void)),
+ interrupt P((void)),
+ localstat P((void)),
+ flowstat P((void)),
+ netclear P((void)),
+ netflush P((void)),
+#ifdef DIAGNOSTICS
+ printoption P((char *, int)),
+ printdata P((char *, char *, int)),
+ printsub P((int, unsigned char *, int)),
+#endif
+ ptyflush P((void)),
+ putchr P((int)),
+ putf P((char *, char *)),
+ recv_ayt P((void)),
+ send_do P((int, int)),
+ send_dont P((int, int)),
+ send_slc P((void)),
+ send_status P((void)),
+ send_will P((int, int)),
+ send_wont P((int, int)),
+ sendbrk P((void)),
+ sendsusp P((void)),
+ set_termbuf P((void)),
+ start_login P((char *, int, char *)),
+ start_slc P((int)),
+#if defined(AUTHENTICATION)
+ start_slave P((char *)),
+#else
+ start_slave P((char *, int, char *)),
+#endif
+ suboption P((void)),
+ telrcv P((void)),
+ ttloop P((void)),
+ tty_binaryin P((int)),
+ tty_binaryout P((int));
+
+extern int
+ end_slc P((unsigned char **)),
+ getnpty P((void)),
+#ifndef convex
+ getpty P((int *)),
+#endif
+ login_tty P((int)),
+ spcset P((int, cc_t *, cc_t **)),
+ stilloob P((int)),
+ terminit P((void)),
+ termstat P((void)),
+ tty_flowmode P((void)),
+ tty_restartany P((void)),
+ tty_isbinaryin P((void)),
+ tty_isbinaryout P((void)),
+ tty_iscrnl P((void)),
+ tty_isecho P((void)),
+ tty_isediting P((void)),
+ tty_islitecho P((void)),
+ tty_isnewmap P((void)),
+ tty_israw P((void)),
+ tty_issofttab P((void)),
+ tty_istrapsig P((void)),
+ tty_linemode P((void));
+
+extern void
+ tty_rspeed P((int)),
+ tty_setecho P((int)),
+ tty_setedit P((int)),
+ tty_setlinemode P((int)),
+ tty_setlitecho P((int)),
+ tty_setsig P((int)),
+ tty_setsofttab P((int)),
+ tty_tspeed P((int)),
+ willoption P((int)),
+ wontoption P((int)),
+ writenet P((unsigned char *, int));
+
+
+
+/*
+ * The following are some clocks used to decide how to interpret
+ * the relationship between various variables.
+ */
+
+extern struct {
+ int
+ system, /* what the current time is */
+ echotoggle, /* last time user entered echo character */
+ modenegotiated, /* last time operating mode negotiated */
+ didnetreceive, /* last time we read data from network */
+ ttypesubopt, /* ttype subopt is received */
+ tspeedsubopt, /* tspeed subopt is received */
+ environsubopt, /* environ subopt is received */
+ oenvironsubopt, /* old environ subopt is received */
+ xdisplocsubopt, /* xdisploc subopt is received */
+ baseline, /* time started to do timed action */
+ gotDM; /* when did we last see a data mark */
+} clocks;
+
+
+#if defined(CRAY2) && defined(UNICOS5)
+extern int needtermstat;
+#endif
+
+#ifndef DEFAULT_IM
+# ifdef CRAY
+# define DEFAULT_IM "\r\n\r\nCray UNICOS (%h) (%t)\r\n\r\r\n\r"
+# else
+# ifdef sun
+# define DEFAULT_IM "\r\n\r\nSunOS UNIX (%h) (%t)\r\n\r\r\n\r"
+# else
+# ifdef ultrix
+# define DEFAULT_IM "\r\n\r\nULTRIX (%h) (%t)\r\n\r\r\n\r"
+# else
+# define DEFAULT_IM "\r\n\r\nFreeBSD (%h) (%t)\r\n\r\r\n\r"
+# endif
+# endif
+# endif
+#endif
diff --git a/libexec/telnetd/global.c b/libexec/telnetd/global.c
new file mode 100644
index 0000000..af21acc
--- /dev/null
+++ b/libexec/telnetd/global.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)global.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+/*
+ * Allocate global variables. We do this
+ * by including the header file that defines
+ * them all as externs, but first we define
+ * the keyword "extern" to be nothing, so that
+ * we will actually allocate the space.
+ */
+
+#include "defs.h"
+#define extern
+#include "ext.h"
diff --git a/libexec/telnetd/pathnames.h b/libexec/telnetd/pathnames.h
new file mode 100644
index 0000000..4e14a88
--- /dev/null
+++ b/libexec/telnetd/pathnames.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ */
+
+#if BSD > 43
+
+# include <paths.h>
+
+# ifndef _PATH_LOGIN
+# define _PATH_LOGIN "/usr/bin/login"
+# endif
+
+#else
+
+# define _PATH_TTY "/dev/tty"
+# ifndef _PATH_LOGIN
+# define _PATH_LOGIN "/bin/login"
+# endif
+
+#endif
+
+#ifdef BFTPDAEMON
+#define BFTPPATH "/usr/ucb/bftp"
+#endif /* BFTPDAEMON */
diff --git a/libexec/telnetd/slc.c b/libexec/telnetd/slc.c
new file mode 100644
index 0000000..a03bd9d
--- /dev/null
+++ b/libexec/telnetd/slc.c
@@ -0,0 +1,493 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)slc.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include "telnetd.h"
+
+#ifdef LINEMODE
+/*
+ * local varibles
+ */
+static unsigned char *def_slcbuf = (unsigned char *)0;
+static int def_slclen = 0;
+static int slcchange; /* change to slc is requested */
+static unsigned char *slcptr; /* pointer into slc buffer */
+static unsigned char slcbuf[NSLC*6]; /* buffer for slc negotiation */
+
+/*
+ * send_slc
+ *
+ * Write out the current special characters to the client.
+ */
+ void
+send_slc()
+{
+ register int i;
+
+ /*
+ * Send out list of triplets of special characters
+ * to client. We only send info on the characters
+ * that are currently supported.
+ */
+ for (i = 1; i <= NSLC; i++) {
+ if ((slctab[i].defset.flag & SLC_LEVELBITS) == SLC_NOSUPPORT)
+ continue;
+ add_slc((unsigned char)i, slctab[i].current.flag,
+ slctab[i].current.val);
+ }
+
+} /* end of send_slc */
+
+/*
+ * default_slc
+ *
+ * Set pty special characters to all the defaults.
+ */
+ void
+default_slc()
+{
+ register int i;
+
+ for (i = 1; i <= NSLC; i++) {
+ slctab[i].current.val = slctab[i].defset.val;
+ if (slctab[i].current.val == (cc_t)(_POSIX_VDISABLE))
+ slctab[i].current.flag = SLC_NOSUPPORT;
+ else
+ slctab[i].current.flag = slctab[i].defset.flag;
+ if (slctab[i].sptr) {
+ *(slctab[i].sptr) = slctab[i].defset.val;
+ }
+ }
+ slcchange = 1;
+
+} /* end of default_slc */
+#endif /* LINEMODE */
+
+/*
+ * get_slc_defaults
+ *
+ * Initialize the slc mapping table.
+ */
+ void
+get_slc_defaults()
+{
+ register int i;
+
+ init_termbuf();
+
+ for (i = 1; i <= NSLC; i++) {
+ slctab[i].defset.flag =
+ spcset(i, &slctab[i].defset.val, &slctab[i].sptr);
+ slctab[i].current.flag = SLC_NOSUPPORT;
+ slctab[i].current.val = 0;
+ }
+
+} /* end of get_slc_defaults */
+
+#ifdef LINEMODE
+/*
+ * add_slc
+ *
+ * Add an slc triplet to the slc buffer.
+ */
+ void
+add_slc(func, flag, val)
+ register char func, flag;
+ register cc_t val;
+{
+
+ if ((*slcptr++ = (unsigned char)func) == 0xff)
+ *slcptr++ = 0xff;
+
+ if ((*slcptr++ = (unsigned char)flag) == 0xff)
+ *slcptr++ = 0xff;
+
+ if ((*slcptr++ = (unsigned char)val) == 0xff)
+ *slcptr++ = 0xff;
+
+} /* end of add_slc */
+
+/*
+ * start_slc
+ *
+ * Get ready to process incoming slc's and respond to them.
+ *
+ * The parameter getit is non-zero if it is necessary to grab a copy
+ * of the terminal control structures.
+ */
+ void
+start_slc(getit)
+ register int getit;
+{
+
+ slcchange = 0;
+ if (getit)
+ init_termbuf();
+ (void) sprintf((char *)slcbuf, "%c%c%c%c",
+ IAC, SB, TELOPT_LINEMODE, LM_SLC);
+ slcptr = slcbuf + 4;
+
+} /* end of start_slc */
+
+/*
+ * end_slc
+ *
+ * Finish up the slc negotiation. If something to send, then send it.
+ */
+ int
+end_slc(bufp)
+ register unsigned char **bufp;
+{
+ register int len;
+ void netflush();
+
+ /*
+ * If a change has occured, store the new terminal control
+ * structures back to the terminal driver.
+ */
+ if (slcchange) {
+ set_termbuf();
+ }
+
+ /*
+ * If the pty state has not yet been fully processed and there is a
+ * deferred slc request from the client, then do not send any
+ * sort of slc negotiation now. We will respond to the client's
+ * request very soon.
+ */
+ if (def_slcbuf && (terminit() == 0)) {
+ return(0);
+ }
+
+ if (slcptr > (slcbuf + 4)) {
+ if (bufp) {
+ *bufp = &slcbuf[4];
+ return(slcptr - slcbuf - 4);
+ } else {
+ (void) sprintf((char *)slcptr, "%c%c", IAC, SE);
+ slcptr += 2;
+ len = slcptr - slcbuf;
+ writenet(slcbuf, len);
+ netflush(); /* force it out immediately */
+ DIAG(TD_OPTIONS, printsub('>', slcbuf+2, len-2););
+ }
+ }
+ return (0);
+
+} /* end of end_slc */
+
+/*
+ * process_slc
+ *
+ * Figure out what to do about the client's slc
+ */
+ void
+process_slc(func, flag, val)
+ register unsigned char func, flag;
+ register cc_t val;
+{
+ register int hislevel, mylevel, ack;
+
+ /*
+ * Ensure that we know something about this function
+ */
+ if (func > NSLC) {
+ add_slc(func, SLC_NOSUPPORT, 0);
+ return;
+ }
+
+ /*
+ * Process the special case requests of 0 SLC_DEFAULT 0
+ * and 0 SLC_VARIABLE 0. Be a little forgiving here, don't
+ * worry about whether the value is actually 0 or not.
+ */
+ if (func == 0) {
+ if ((flag = flag & SLC_LEVELBITS) == SLC_DEFAULT) {
+ default_slc();
+ send_slc();
+ } else if (flag == SLC_VARIABLE) {
+ send_slc();
+ }
+ return;
+ }
+
+ /*
+ * Appears to be a function that we know something about. So
+ * get on with it and see what we know.
+ */
+
+ hislevel = flag & SLC_LEVELBITS;
+ mylevel = slctab[func].current.flag & SLC_LEVELBITS;
+ ack = flag & SLC_ACK;
+ /*
+ * ignore the command if:
+ * the function value and level are the same as what we already have;
+ * or the level is the same and the ack bit is set
+ */
+ if (hislevel == mylevel && (val == slctab[func].current.val || ack)) {
+ return;
+ } else if (ack) {
+ /*
+ * If we get here, we got an ack, but the levels don't match.
+ * This shouldn't happen. If it does, it is probably because
+ * we have sent two requests to set a variable without getting
+ * a response between them, and this is the first response.
+ * So, ignore it, and wait for the next response.
+ */
+ return;
+ } else {
+ change_slc(func, flag, val);
+ }
+
+} /* end of process_slc */
+
+/*
+ * change_slc
+ *
+ * Process a request to change one of our special characters.
+ * Compare client's request with what we are capable of supporting.
+ */
+ void
+change_slc(func, flag, val)
+ register char func, flag;
+ register cc_t val;
+{
+ register int hislevel, mylevel;
+
+ hislevel = flag & SLC_LEVELBITS;
+ mylevel = slctab[func].defset.flag & SLC_LEVELBITS;
+ /*
+ * If client is setting a function to NOSUPPORT
+ * or DEFAULT, then we can easily and directly
+ * accomodate the request.
+ */
+ if (hislevel == SLC_NOSUPPORT) {
+ slctab[func].current.flag = flag;
+ slctab[func].current.val = (cc_t)_POSIX_VDISABLE;
+ flag |= SLC_ACK;
+ add_slc(func, flag, val);
+ return;
+ }
+ if (hislevel == SLC_DEFAULT) {
+ /*
+ * Special case here. If client tells us to use
+ * the default on a function we don't support, then
+ * return NOSUPPORT instead of what we may have as a
+ * default level of DEFAULT.
+ */
+ if (mylevel == SLC_DEFAULT) {
+ slctab[func].current.flag = SLC_NOSUPPORT;
+ } else {
+ slctab[func].current.flag = slctab[func].defset.flag;
+ }
+ slctab[func].current.val = slctab[func].defset.val;
+ add_slc(func, slctab[func].current.flag,
+ slctab[func].current.val);
+ return;
+ }
+
+ /*
+ * Client wants us to change to a new value or he
+ * is telling us that he can't change to our value.
+ * Some of the slc's we support and can change,
+ * some we do support but can't change,
+ * and others we don't support at all.
+ * If we can change it then we have a pointer to
+ * the place to put the new value, so change it,
+ * otherwise, continue the negotiation.
+ */
+ if (slctab[func].sptr) {
+ /*
+ * We can change this one.
+ */
+ slctab[func].current.val = val;
+ *(slctab[func].sptr) = val;
+ slctab[func].current.flag = flag;
+ flag |= SLC_ACK;
+ slcchange = 1;
+ add_slc(func, flag, val);
+ } else {
+ /*
+ * It is not possible for us to support this
+ * request as he asks.
+ *
+ * If our level is DEFAULT, then just ack whatever was
+ * sent.
+ *
+ * If he can't change and we can't change,
+ * then degenerate to NOSUPPORT.
+ *
+ * Otherwise we send our level back to him, (CANTCHANGE
+ * or NOSUPPORT) and if CANTCHANGE, send
+ * our value as well.
+ */
+ if (mylevel == SLC_DEFAULT) {
+ slctab[func].current.flag = flag;
+ slctab[func].current.val = val;
+ flag |= SLC_ACK;
+ } else if (hislevel == SLC_CANTCHANGE &&
+ mylevel == SLC_CANTCHANGE) {
+ flag &= ~SLC_LEVELBITS;
+ flag |= SLC_NOSUPPORT;
+ slctab[func].current.flag = flag;
+ } else {
+ flag &= ~SLC_LEVELBITS;
+ flag |= mylevel;
+ slctab[func].current.flag = flag;
+ if (mylevel == SLC_CANTCHANGE) {
+ slctab[func].current.val =
+ slctab[func].defset.val;
+ val = slctab[func].current.val;
+ }
+
+ }
+ add_slc(func, flag, val);
+ }
+
+} /* end of change_slc */
+
+#if defined(USE_TERMIO) && (VEOF == VMIN)
+cc_t oldeofc = '\004';
+#endif
+
+/*
+ * check_slc
+ *
+ * Check the special characters in use and notify the client if any have
+ * changed. Only those characters that are capable of being changed are
+ * likely to have changed. If a local change occurs, kick the support level
+ * and flags up to the defaults.
+ */
+ void
+check_slc()
+{
+ register int i;
+
+ for (i = 1; i <= NSLC; i++) {
+#if defined(USE_TERMIO) && (VEOF == VMIN)
+ /*
+ * In a perfect world this would be a neat little
+ * function. But in this world, we should not notify
+ * client of changes to the VEOF char when
+ * ICANON is off, because it is not representing
+ * a special character.
+ */
+ if (i == SLC_EOF) {
+ if (!tty_isediting())
+ continue;
+ else if (slctab[i].sptr)
+ oldeofc = *(slctab[i].sptr);
+ }
+#endif /* defined(USE_TERMIO) && defined(SYSV_TERMIO) */
+ if (slctab[i].sptr &&
+ (*(slctab[i].sptr) != slctab[i].current.val)) {
+ slctab[i].current.val = *(slctab[i].sptr);
+ if (*(slctab[i].sptr) == (cc_t)_POSIX_VDISABLE)
+ slctab[i].current.flag = SLC_NOSUPPORT;
+ else
+ slctab[i].current.flag = slctab[i].defset.flag;
+ add_slc((unsigned char)i, slctab[i].current.flag,
+ slctab[i].current.val);
+ }
+ }
+
+} /* check_slc */
+
+/*
+ * do_opt_slc
+ *
+ * Process an slc option buffer. Defer processing of incoming slc's
+ * until after the terminal state has been processed. Save the first slc
+ * request that comes along, but discard all others.
+ *
+ * ptr points to the beginning of the buffer, len is the length.
+ */
+ void
+do_opt_slc(ptr, len)
+ register unsigned char *ptr;
+ register int len;
+{
+ register unsigned char func, flag;
+ cc_t val;
+ register unsigned char *end = ptr + len;
+
+ if (terminit()) { /* go ahead */
+ while (ptr < end) {
+ func = *ptr++;
+ if (ptr >= end) break;
+ flag = *ptr++;
+ if (ptr >= end) break;
+ val = (cc_t)*ptr++;
+
+ process_slc(func, flag, val);
+
+ }
+ } else {
+ /*
+ * save this slc buffer if it is the first, otherwise dump
+ * it.
+ */
+ if (def_slcbuf == (unsigned char *)0) {
+ def_slclen = len;
+ def_slcbuf = (unsigned char *)malloc((unsigned)len);
+ if (def_slcbuf == (unsigned char *)0)
+ return; /* too bad */
+ bcopy(ptr, def_slcbuf, len);
+ }
+ }
+
+} /* end of do_opt_slc */
+
+/*
+ * deferslc
+ *
+ * Do slc stuff that was deferred.
+ */
+ void
+deferslc()
+{
+ if (def_slcbuf) {
+ start_slc(1);
+ do_opt_slc(def_slcbuf, def_slclen);
+ (void) end_slc(0);
+ free(def_slcbuf);
+ def_slcbuf = (unsigned char *)0;
+ def_slclen = 0;
+ }
+
+} /* end of deferslc */
+
+#endif /* LINEMODE */
diff --git a/libexec/telnetd/state.c b/libexec/telnetd/state.c
new file mode 100644
index 0000000..db9fbe8
--- /dev/null
+++ b/libexec/telnetd/state.c
@@ -0,0 +1,1546 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)state.c 8.2 (Berkeley) 12/15/93";
+#endif /* not lint */
+
+#include "telnetd.h"
+#if defined(AUTHENTICATION)
+#include <libtelnet/auth.h>
+#endif
+
+unsigned char doopt[] = { IAC, DO, '%', 'c', 0 };
+unsigned char dont[] = { IAC, DONT, '%', 'c', 0 };
+unsigned char will[] = { IAC, WILL, '%', 'c', 0 };
+unsigned char wont[] = { IAC, WONT, '%', 'c', 0 };
+int not42 = 1;
+
+/*
+ * Buffer for sub-options, and macros
+ * for suboptions buffer manipulations
+ */
+unsigned char subbuffer[512], *subpointer= subbuffer, *subend= subbuffer;
+
+#define SB_CLEAR() subpointer = subbuffer
+#define SB_TERM() { subend = subpointer; SB_CLEAR(); }
+#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
+ *subpointer++ = (c); \
+ }
+#define SB_GET() ((*subpointer++)&0xff)
+#define SB_EOF() (subpointer >= subend)
+#define SB_LEN() (subend - subpointer)
+
+#ifdef ENV_HACK
+unsigned char *subsave;
+#define SB_SAVE() subsave = subpointer;
+#define SB_RESTORE() subpointer = subsave;
+#endif
+
+
+/*
+ * State for recv fsm
+ */
+#define TS_DATA 0 /* base state */
+#define TS_IAC 1 /* look for double IAC's */
+#define TS_CR 2 /* CR-LF ->'s CR */
+#define TS_SB 3 /* throw away begin's... */
+#define TS_SE 4 /* ...end's (suboption negotiation) */
+#define TS_WILL 5 /* will option negotiation */
+#define TS_WONT 6 /* wont " */
+#define TS_DO 7 /* do " */
+#define TS_DONT 8 /* dont " */
+
+ void
+telrcv()
+{
+ register int c;
+ static int state = TS_DATA;
+#if defined(CRAY2) && defined(UNICOS5)
+ char *opfrontp = pfrontp;
+#endif
+
+ while (ncc > 0) {
+ if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
+ break;
+ c = *netip++ & 0377, ncc--;
+ switch (state) {
+
+ case TS_CR:
+ state = TS_DATA;
+ /* Strip off \n or \0 after a \r */
+ if ((c == 0) || (c == '\n')) {
+ break;
+ }
+ /* FALL THROUGH */
+
+ case TS_DATA:
+ if (c == IAC) {
+ state = TS_IAC;
+ break;
+ }
+ /*
+ * We now map \r\n ==> \r for pragmatic reasons.
+ * Many client implementations send \r\n when
+ * the user hits the CarriageReturn key.
+ *
+ * We USED to map \r\n ==> \n, since \r\n says
+ * that we want to be in column 1 of the next
+ * printable line, and \n is the standard
+ * unix way of saying that (\r is only good
+ * if CRMOD is set, which it normally is).
+ */
+ if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
+ int nc = *netip;
+#ifdef LINEMODE
+ /*
+ * If we are operating in linemode,
+ * convert to local end-of-line.
+ */
+ if (linemode && (ncc > 0) && (('\n' == nc) ||
+ ((0 == nc) && tty_iscrnl())) ) {
+ netip++; ncc--;
+ c = '\n';
+ } else
+#endif
+ {
+ state = TS_CR;
+ }
+ }
+ *pfrontp++ = c;
+ break;
+
+ case TS_IAC:
+gotiac: switch (c) {
+
+ /*
+ * Send the process on the pty side an
+ * interrupt. Do this with a NULL or
+ * interrupt char; depending on the tty mode.
+ */
+ case IP:
+ DIAG(TD_OPTIONS,
+ printoption("td: recv IAC", c));
+ interrupt();
+ break;
+
+ case BREAK:
+ DIAG(TD_OPTIONS,
+ printoption("td: recv IAC", c));
+ sendbrk();
+ break;
+
+ /*
+ * Are You There?
+ */
+ case AYT:
+ DIAG(TD_OPTIONS,
+ printoption("td: recv IAC", c));
+ recv_ayt();
+ break;
+
+ /*
+ * Abort Output
+ */
+ case AO:
+ {
+ DIAG(TD_OPTIONS,
+ printoption("td: recv IAC", c));
+ ptyflush(); /* half-hearted */
+ init_termbuf();
+
+ if (slctab[SLC_AO].sptr &&
+ *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
+ *pfrontp++ =
+ (unsigned char)*slctab[SLC_AO].sptr;
+ }
+
+ netclear(); /* clear buffer back */
+ *nfrontp++ = IAC;
+ *nfrontp++ = DM;
+ neturg = nfrontp-1; /* off by one XXX */
+ DIAG(TD_OPTIONS,
+ printoption("td: send IAC", DM));
+ break;
+ }
+
+ /*
+ * Erase Character and
+ * Erase Line
+ */
+ case EC:
+ case EL:
+ {
+ cc_t ch;
+
+ DIAG(TD_OPTIONS,
+ printoption("td: recv IAC", c));
+ ptyflush(); /* half-hearted */
+ init_termbuf();
+ if (c == EC)
+ ch = *slctab[SLC_EC].sptr;
+ else
+ ch = *slctab[SLC_EL].sptr;
+ if (ch != (cc_t)(_POSIX_VDISABLE))
+ *pfrontp++ = (unsigned char)ch;
+ break;
+ }
+
+ /*
+ * Check for urgent data...
+ */
+ case DM:
+ DIAG(TD_OPTIONS,
+ printoption("td: recv IAC", c));
+ SYNCHing = stilloob(net);
+ settimer(gotDM);
+ break;
+
+
+ /*
+ * Begin option subnegotiation...
+ */
+ case SB:
+ state = TS_SB;
+ SB_CLEAR();
+ continue;
+
+ case WILL:
+ state = TS_WILL;
+ continue;
+
+ case WONT:
+ state = TS_WONT;
+ continue;
+
+ case DO:
+ state = TS_DO;
+ continue;
+
+ case DONT:
+ state = TS_DONT;
+ continue;
+ case EOR:
+ if (his_state_is_will(TELOPT_EOR))
+ doeof();
+ break;
+
+ /*
+ * Handle RFC 10xx Telnet linemode option additions
+ * to command stream (EOF, SUSP, ABORT).
+ */
+ case xEOF:
+ doeof();
+ break;
+
+ case SUSP:
+ sendsusp();
+ break;
+
+ case ABORT:
+ sendbrk();
+ break;
+
+ case IAC:
+ *pfrontp++ = c;
+ break;
+ }
+ state = TS_DATA;
+ break;
+
+ case TS_SB:
+ if (c == IAC) {
+ state = TS_SE;
+ } else {
+ SB_ACCUM(c);
+ }
+ break;
+
+ case TS_SE:
+ if (c != SE) {
+ if (c != IAC) {
+ /*
+ * bad form of suboption negotiation.
+ * handle it in such a way as to avoid
+ * damage to local state. Parse
+ * suboption buffer found so far,
+ * then treat remaining stream as
+ * another command sequence.
+ */
+
+ /* for DIAGNOSTICS */
+ SB_ACCUM(IAC);
+ SB_ACCUM(c);
+ subpointer -= 2;
+
+ SB_TERM();
+ suboption();
+ state = TS_IAC;
+ goto gotiac;
+ }
+ SB_ACCUM(c);
+ state = TS_SB;
+ } else {
+ /* for DIAGNOSTICS */
+ SB_ACCUM(IAC);
+ SB_ACCUM(SE);
+ subpointer -= 2;
+
+ SB_TERM();
+ suboption(); /* handle sub-option */
+ state = TS_DATA;
+ }
+ break;
+
+ case TS_WILL:
+ willoption(c);
+ state = TS_DATA;
+ continue;
+
+ case TS_WONT:
+ wontoption(c);
+ state = TS_DATA;
+ continue;
+
+ case TS_DO:
+ dooption(c);
+ state = TS_DATA;
+ continue;
+
+ case TS_DONT:
+ dontoption(c);
+ state = TS_DATA;
+ continue;
+
+ default:
+ syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
+ printf("telnetd: panic state=%d\n", state);
+ exit(1);
+ }
+ }
+#if defined(CRAY2) && defined(UNICOS5)
+ if (!linemode) {
+ char xptyobuf[BUFSIZ+NETSLOP];
+ char xbuf2[BUFSIZ];
+ register char *cp;
+ int n = pfrontp - opfrontp, oc;
+ bcopy(opfrontp, xptyobuf, n);
+ pfrontp = opfrontp;
+ pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
+ xbuf2, &oc, BUFSIZ);
+ for (cp = xbuf2; oc > 0; --oc)
+ if ((*nfrontp++ = *cp++) == IAC)
+ *nfrontp++ = IAC;
+ }
+#endif /* defined(CRAY2) && defined(UNICOS5) */
+} /* end of telrcv */
+
+/*
+ * The will/wont/do/dont state machines are based on Dave Borman's
+ * Telnet option processing state machine.
+ *
+ * These correspond to the following states:
+ * my_state = the last negotiated state
+ * want_state = what I want the state to go to
+ * want_resp = how many requests I have sent
+ * All state defaults are negative, and resp defaults to 0.
+ *
+ * When initiating a request to change state to new_state:
+ *
+ * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
+ * do nothing;
+ * } else {
+ * want_state = new_state;
+ * send new_state;
+ * want_resp++;
+ * }
+ *
+ * When receiving new_state:
+ *
+ * if (want_resp) {
+ * want_resp--;
+ * if (want_resp && (new_state == my_state))
+ * want_resp--;
+ * }
+ * if ((want_resp == 0) && (new_state != want_state)) {
+ * if (ok_to_switch_to new_state)
+ * want_state = new_state;
+ * else
+ * want_resp++;
+ * send want_state;
+ * }
+ * my_state = new_state;
+ *
+ * Note that new_state is implied in these functions by the function itself.
+ * will and do imply positive new_state, wont and dont imply negative.
+ *
+ * Finally, there is one catch. If we send a negative response to a
+ * positive request, my_state will be the positive while want_state will
+ * remain negative. my_state will revert to negative when the negative
+ * acknowlegment arrives from the peer. Thus, my_state generally tells
+ * us not only the last negotiated state, but also tells us what the peer
+ * wants to be doing as well. It is important to understand this difference
+ * as we may wish to be processing data streams based on our desired state
+ * (want_state) or based on what the peer thinks the state is (my_state).
+ *
+ * This all works fine because if the peer sends a positive request, the data
+ * that we receive prior to negative acknowlegment will probably be affected
+ * by the positive state, and we can process it as such (if we can; if we
+ * can't then it really doesn't matter). If it is that important, then the
+ * peer probably should be buffering until this option state negotiation
+ * is complete.
+ *
+ */
+ void
+send_do(option, init)
+ int option, init;
+{
+ if (init) {
+ if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
+ his_want_state_is_will(option))
+ return;
+ /*
+ * Special case for TELOPT_TM: We send a DO, but pretend
+ * that we sent a DONT, so that we can send more DOs if
+ * we want to.
+ */
+ if (option == TELOPT_TM)
+ set_his_want_state_wont(option);
+ else
+ set_his_want_state_will(option);
+ do_dont_resp[option]++;
+ }
+ (void) sprintf(nfrontp, (char *)doopt, option);
+ nfrontp += sizeof (dont) - 2;
+
+ DIAG(TD_OPTIONS, printoption("td: send do", option));
+}
+
+#ifdef AUTHENTICATION
+extern void auth_request();
+#endif
+#ifdef LINEMODE
+extern void doclientstat();
+#endif
+
+ void
+willoption(option)
+ int option;
+{
+ int changeok = 0;
+ void (*func)() = 0;
+
+ /*
+ * process input from peer.
+ */
+
+ DIAG(TD_OPTIONS, printoption("td: recv will", option));
+
+ if (do_dont_resp[option]) {
+ do_dont_resp[option]--;
+ if (do_dont_resp[option] && his_state_is_will(option))
+ do_dont_resp[option]--;
+ }
+ if (do_dont_resp[option] == 0) {
+ if (his_want_state_is_wont(option)) {
+ switch (option) {
+
+ case TELOPT_BINARY:
+ init_termbuf();
+ tty_binaryin(1);
+ set_termbuf();
+ changeok++;
+ break;
+
+ case TELOPT_ECHO:
+ /*
+ * See comments below for more info.
+ */
+ not42 = 0; /* looks like a 4.2 system */
+ break;
+
+ case TELOPT_TM:
+#if defined(LINEMODE) && defined(KLUDGELINEMODE)
+ /*
+ * This telnetd implementation does not really
+ * support timing marks, it just uses them to
+ * support the kludge linemode stuff. If we
+ * receive a will or wont TM in response to our
+ * do TM request that may have been sent to
+ * determine kludge linemode support, process
+ * it, otherwise TM should get a negative
+ * response back.
+ */
+ /*
+ * Handle the linemode kludge stuff.
+ * If we are not currently supporting any
+ * linemode at all, then we assume that this
+ * is the client telling us to use kludge
+ * linemode in response to our query. Set the
+ * linemode type that is to be supported, note
+ * that the client wishes to use linemode, and
+ * eat the will TM as though it never arrived.
+ */
+ if (lmodetype < KLUDGE_LINEMODE) {
+ lmodetype = KLUDGE_LINEMODE;
+ clientstat(TELOPT_LINEMODE, WILL, 0);
+ send_wont(TELOPT_SGA, 1);
+ } else if (lmodetype == NO_AUTOKLUDGE) {
+ lmodetype = KLUDGE_OK;
+ }
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+ /*
+ * We never respond to a WILL TM, and
+ * we leave the state WONT.
+ */
+ return;
+
+ case TELOPT_LFLOW:
+ /*
+ * If we are going to support flow control
+ * option, then don't worry peer that we can't
+ * change the flow control characters.
+ */
+ slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
+ slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
+ slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
+ slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
+ case TELOPT_TTYPE:
+ case TELOPT_SGA:
+ case TELOPT_NAWS:
+ case TELOPT_TSPEED:
+ case TELOPT_XDISPLOC:
+ case TELOPT_NEW_ENVIRON:
+ case TELOPT_OLD_ENVIRON:
+ changeok++;
+ break;
+
+#ifdef LINEMODE
+ case TELOPT_LINEMODE:
+# ifdef KLUDGELINEMODE
+ /*
+ * Note client's desire to use linemode.
+ */
+ lmodetype = REAL_LINEMODE;
+# endif /* KLUDGELINEMODE */
+ func = doclientstat;
+ changeok++;
+ break;
+#endif /* LINEMODE */
+
+#ifdef AUTHENTICATION
+ case TELOPT_AUTHENTICATION:
+ func = auth_request;
+ changeok++;
+ break;
+#endif
+
+
+ default:
+ break;
+ }
+ if (changeok) {
+ set_his_want_state_will(option);
+ send_do(option, 0);
+ } else {
+ do_dont_resp[option]++;
+ send_dont(option, 0);
+ }
+ } else {
+ /*
+ * Option processing that should happen when
+ * we receive conformation of a change in
+ * state that we had requested.
+ */
+ switch (option) {
+ case TELOPT_ECHO:
+ not42 = 0; /* looks like a 4.2 system */
+ /*
+ * Egads, he responded "WILL ECHO". Turn
+ * it off right now!
+ */
+ send_dont(option, 1);
+ /*
+ * "WILL ECHO". Kludge upon kludge!
+ * A 4.2 client is now echoing user input at
+ * the tty. This is probably undesireable and
+ * it should be stopped. The client will
+ * respond WONT TM to the DO TM that we send to
+ * check for kludge linemode. When the WONT TM
+ * arrives, linemode will be turned off and a
+ * change propogated to the pty. This change
+ * will cause us to process the new pty state
+ * in localstat(), which will notice that
+ * linemode is off and send a WILL ECHO
+ * so that we are properly in character mode and
+ * all is well.
+ */
+ break;
+#ifdef LINEMODE
+ case TELOPT_LINEMODE:
+# ifdef KLUDGELINEMODE
+ /*
+ * Note client's desire to use linemode.
+ */
+ lmodetype = REAL_LINEMODE;
+# endif /* KLUDGELINEMODE */
+ func = doclientstat;
+ break;
+#endif /* LINEMODE */
+
+#ifdef AUTHENTICATION
+ case TELOPT_AUTHENTICATION:
+ func = auth_request;
+ break;
+#endif
+
+ case TELOPT_LFLOW:
+ func = flowstat;
+ break;
+ }
+ }
+ }
+ set_his_state_will(option);
+ if (func)
+ (*func)();
+} /* end of willoption */
+
+ void
+send_dont(option, init)
+ int option, init;
+{
+ if (init) {
+ if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
+ his_want_state_is_wont(option))
+ return;
+ set_his_want_state_wont(option);
+ do_dont_resp[option]++;
+ }
+ (void) sprintf(nfrontp, (char *)dont, option);
+ nfrontp += sizeof (doopt) - 2;
+
+ DIAG(TD_OPTIONS, printoption("td: send dont", option));
+}
+
+ void
+wontoption(option)
+ int option;
+{
+ /*
+ * Process client input.
+ */
+
+ DIAG(TD_OPTIONS, printoption("td: recv wont", option));
+
+ if (do_dont_resp[option]) {
+ do_dont_resp[option]--;
+ if (do_dont_resp[option] && his_state_is_wont(option))
+ do_dont_resp[option]--;
+ }
+ if (do_dont_resp[option] == 0) {
+ if (his_want_state_is_will(option)) {
+ /* it is always ok to change to negative state */
+ switch (option) {
+ case TELOPT_ECHO:
+ not42 = 1; /* doesn't seem to be a 4.2 system */
+ break;
+
+ case TELOPT_BINARY:
+ init_termbuf();
+ tty_binaryin(0);
+ set_termbuf();
+ break;
+
+#ifdef LINEMODE
+ case TELOPT_LINEMODE:
+# ifdef KLUDGELINEMODE
+ /*
+ * If real linemode is supported, then client is
+ * asking to turn linemode off.
+ */
+ if (lmodetype != REAL_LINEMODE)
+ break;
+ lmodetype = KLUDGE_LINEMODE;
+# endif /* KLUDGELINEMODE */
+ clientstat(TELOPT_LINEMODE, WONT, 0);
+ break;
+#endif /* LINEMODE */
+
+ case TELOPT_TM:
+ /*
+ * If we get a WONT TM, and had sent a DO TM,
+ * don't respond with a DONT TM, just leave it
+ * as is. Short circut the state machine to
+ * achive this.
+ */
+ set_his_want_state_wont(TELOPT_TM);
+ return;
+
+ case TELOPT_LFLOW:
+ /*
+ * If we are not going to support flow control
+ * option, then let peer know that we can't
+ * change the flow control characters.
+ */
+ slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
+ slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
+ slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
+ slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
+ break;
+
+#if defined(AUTHENTICATION)
+ case TELOPT_AUTHENTICATION:
+ auth_finished(0, AUTH_REJECT);
+ break;
+#endif
+
+ /*
+ * For options that we might spin waiting for
+ * sub-negotiation, if the client turns off the
+ * option rather than responding to the request,
+ * we have to treat it here as if we got a response
+ * to the sub-negotiation, (by updating the timers)
+ * so that we'll break out of the loop.
+ */
+ case TELOPT_TTYPE:
+ settimer(ttypesubopt);
+ break;
+
+ case TELOPT_TSPEED:
+ settimer(tspeedsubopt);
+ break;
+
+ case TELOPT_XDISPLOC:
+ settimer(xdisplocsubopt);
+ break;
+
+ case TELOPT_OLD_ENVIRON:
+ settimer(oenvironsubopt);
+ break;
+
+ case TELOPT_NEW_ENVIRON:
+ settimer(environsubopt);
+ break;
+
+ default:
+ break;
+ }
+ set_his_want_state_wont(option);
+ if (his_state_is_will(option))
+ send_dont(option, 0);
+ } else {
+ switch (option) {
+ case TELOPT_TM:
+#if defined(LINEMODE) && defined(KLUDGELINEMODE)
+ if (lmodetype < NO_AUTOKLUDGE) {
+ lmodetype = NO_LINEMODE;
+ clientstat(TELOPT_LINEMODE, WONT, 0);
+ send_will(TELOPT_SGA, 1);
+ send_will(TELOPT_ECHO, 1);
+ }
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+ break;
+
+#if defined(AUTHENTICATION)
+ case TELOPT_AUTHENTICATION:
+ auth_finished(0, AUTH_REJECT);
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ }
+ set_his_state_wont(option);
+
+} /* end of wontoption */
+
+ void
+send_will(option, init)
+ int option, init;
+{
+ if (init) {
+ if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
+ my_want_state_is_will(option))
+ return;
+ set_my_want_state_will(option);
+ will_wont_resp[option]++;
+ }
+ (void) sprintf(nfrontp, (char *)will, option);
+ nfrontp += sizeof (doopt) - 2;
+
+ DIAG(TD_OPTIONS, printoption("td: send will", option));
+}
+
+#if !defined(LINEMODE) || !defined(KLUDGELINEMODE)
+/*
+ * When we get a DONT SGA, we will try once to turn it
+ * back on. If the other side responds DONT SGA, we
+ * leave it at that. This is so that when we talk to
+ * clients that understand KLUDGELINEMODE but not LINEMODE,
+ * we'll keep them in char-at-a-time mode.
+ */
+int turn_on_sga = 0;
+#endif
+
+ void
+dooption(option)
+ int option;
+{
+ int changeok = 0;
+
+ /*
+ * Process client input.
+ */
+
+ DIAG(TD_OPTIONS, printoption("td: recv do", option));
+
+ if (will_wont_resp[option]) {
+ will_wont_resp[option]--;
+ if (will_wont_resp[option] && my_state_is_will(option))
+ will_wont_resp[option]--;
+ }
+ if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {
+ switch (option) {
+ case TELOPT_ECHO:
+#ifdef LINEMODE
+# ifdef KLUDGELINEMODE
+ if (lmodetype == NO_LINEMODE)
+# else
+ if (his_state_is_wont(TELOPT_LINEMODE))
+# endif
+#endif
+ {
+ init_termbuf();
+ tty_setecho(1);
+ set_termbuf();
+ }
+ changeok++;
+ break;
+
+ case TELOPT_BINARY:
+ init_termbuf();
+ tty_binaryout(1);
+ set_termbuf();
+ changeok++;
+ break;
+
+ case TELOPT_SGA:
+#if defined(LINEMODE) && defined(KLUDGELINEMODE)
+ /*
+ * If kludge linemode is in use, then we must
+ * process an incoming do SGA for linemode
+ * purposes.
+ */
+ if (lmodetype == KLUDGE_LINEMODE) {
+ /*
+ * Receipt of "do SGA" in kludge
+ * linemode is the peer asking us to
+ * turn off linemode. Make note of
+ * the request.
+ */
+ clientstat(TELOPT_LINEMODE, WONT, 0);
+ /*
+ * If linemode did not get turned off
+ * then don't tell peer that we did.
+ * Breaking here forces a wont SGA to
+ * be returned.
+ */
+ if (linemode)
+ break;
+ }
+#else
+ turn_on_sga = 0;
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+ changeok++;
+ break;
+
+ case TELOPT_STATUS:
+ changeok++;
+ break;
+
+ case TELOPT_TM:
+ /*
+ * Special case for TM. We send a WILL, but
+ * pretend we sent a WONT.
+ */
+ send_will(option, 0);
+ set_my_want_state_wont(option);
+ set_my_state_wont(option);
+ return;
+
+ case TELOPT_LOGOUT:
+ /*
+ * When we get a LOGOUT option, respond
+ * with a WILL LOGOUT, make sure that
+ * it gets written out to the network,
+ * and then just go away...
+ */
+ set_my_want_state_will(TELOPT_LOGOUT);
+ send_will(TELOPT_LOGOUT, 0);
+ set_my_state_will(TELOPT_LOGOUT);
+ (void)netflush();
+ cleanup(0);
+ /* NOT REACHED */
+ break;
+
+ case TELOPT_LINEMODE:
+ case TELOPT_TTYPE:
+ case TELOPT_NAWS:
+ case TELOPT_TSPEED:
+ case TELOPT_LFLOW:
+ case TELOPT_XDISPLOC:
+#ifdef TELOPT_ENVIRON
+ case TELOPT_NEW_ENVIRON:
+#endif
+ case TELOPT_OLD_ENVIRON:
+ default:
+ break;
+ }
+ if (changeok) {
+ set_my_want_state_will(option);
+ send_will(option, 0);
+ } else {
+ will_wont_resp[option]++;
+ send_wont(option, 0);
+ }
+ }
+ set_my_state_will(option);
+
+} /* end of dooption */
+
+ void
+send_wont(option, init)
+ int option, init;
+{
+ if (init) {
+ if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
+ my_want_state_is_wont(option))
+ return;
+ set_my_want_state_wont(option);
+ will_wont_resp[option]++;
+ }
+ (void) sprintf(nfrontp, (char *)wont, option);
+ nfrontp += sizeof (wont) - 2;
+
+ DIAG(TD_OPTIONS, printoption("td: send wont", option));
+}
+
+ void
+dontoption(option)
+ int option;
+{
+ /*
+ * Process client input.
+ */
+
+
+ DIAG(TD_OPTIONS, printoption("td: recv dont", option));
+
+ if (will_wont_resp[option]) {
+ will_wont_resp[option]--;
+ if (will_wont_resp[option] && my_state_is_wont(option))
+ will_wont_resp[option]--;
+ }
+ if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {
+ switch (option) {
+ case TELOPT_BINARY:
+ init_termbuf();
+ tty_binaryout(0);
+ set_termbuf();
+ break;
+
+ case TELOPT_ECHO: /* we should stop echoing */
+#ifdef LINEMODE
+# ifdef KLUDGELINEMODE
+ if ((lmodetype != REAL_LINEMODE) &&
+ (lmodetype != KLUDGE_LINEMODE))
+# else
+ if (his_state_is_wont(TELOPT_LINEMODE))
+# endif
+#endif
+ {
+ init_termbuf();
+ tty_setecho(0);
+ set_termbuf();
+ }
+ break;
+
+ case TELOPT_SGA:
+#if defined(LINEMODE) && defined(KLUDGELINEMODE)
+ /*
+ * If kludge linemode is in use, then we
+ * must process an incoming do SGA for
+ * linemode purposes.
+ */
+ if ((lmodetype == KLUDGE_LINEMODE) ||
+ (lmodetype == KLUDGE_OK)) {
+ /*
+ * The client is asking us to turn
+ * linemode on.
+ */
+ lmodetype = KLUDGE_LINEMODE;
+ clientstat(TELOPT_LINEMODE, WILL, 0);
+ /*
+ * If we did not turn line mode on,
+ * then what do we say? Will SGA?
+ * This violates design of telnet.
+ * Gross. Very Gross.
+ */
+ }
+ break;
+#else
+ set_my_want_state_wont(option);
+ if (my_state_is_will(option))
+ send_wont(option, 0);
+ set_my_state_wont(option);
+ if (turn_on_sga ^= 1)
+ send_will(option, 1);
+ return;
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+
+ default:
+ break;
+ }
+
+ set_my_want_state_wont(option);
+ if (my_state_is_will(option))
+ send_wont(option, 0);
+ }
+ set_my_state_wont(option);
+
+} /* end of dontoption */
+
+#ifdef ENV_HACK
+int env_ovar = -1;
+int env_ovalue = -1;
+#else /* ENV_HACK */
+# define env_ovar OLD_ENV_VAR
+# define env_ovalue OLD_ENV_VALUE
+#endif /* ENV_HACK */
+
+/*
+ * suboption()
+ *
+ * Look at the sub-option buffer, and try to be helpful to the other
+ * side.
+ *
+ * Currently we recognize:
+ *
+ * Terminal type is
+ * Linemode
+ * Window size
+ * Terminal speed
+ */
+ void
+suboption()
+{
+ register int subchar;
+
+ DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);});
+
+ subchar = SB_GET();
+ switch (subchar) {
+ case TELOPT_TSPEED: {
+ register int xspeed, rspeed;
+
+ if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */
+ break;
+
+ settimer(tspeedsubopt);
+
+ if (SB_EOF() || SB_GET() != TELQUAL_IS)
+ return;
+
+ xspeed = atoi((char *)subpointer);
+
+ while (SB_GET() != ',' && !SB_EOF());
+ if (SB_EOF())
+ return;
+
+ rspeed = atoi((char *)subpointer);
+ clientstat(TELOPT_TSPEED, xspeed, rspeed);
+
+ break;
+
+ } /* end of case TELOPT_TSPEED */
+
+ case TELOPT_TTYPE: { /* Yaaaay! */
+ static char terminalname[41];
+
+ if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */
+ break;
+ settimer(ttypesubopt);
+
+ if (SB_EOF() || SB_GET() != TELQUAL_IS) {
+ return; /* ??? XXX but, this is the most robust */
+ }
+
+ terminaltype = terminalname;
+
+ while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
+ !SB_EOF()) {
+ register int c;
+
+ c = SB_GET();
+ if (isupper(c)) {
+ c = tolower(c);
+ }
+ *terminaltype++ = c; /* accumulate name */
+ }
+ *terminaltype = 0;
+ terminaltype = terminalname;
+ break;
+ } /* end of case TELOPT_TTYPE */
+
+ case TELOPT_NAWS: {
+ register int xwinsize, ywinsize;
+
+ if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */
+ break;
+
+ if (SB_EOF())
+ return;
+ xwinsize = SB_GET() << 8;
+ if (SB_EOF())
+ return;
+ xwinsize |= SB_GET();
+ if (SB_EOF())
+ return;
+ ywinsize = SB_GET() << 8;
+ if (SB_EOF())
+ return;
+ ywinsize |= SB_GET();
+ clientstat(TELOPT_NAWS, xwinsize, ywinsize);
+
+ break;
+
+ } /* end of case TELOPT_NAWS */
+
+#ifdef LINEMODE
+ case TELOPT_LINEMODE: {
+ register int request;
+
+ if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */
+ break;
+ /*
+ * Process linemode suboptions.
+ */
+ if (SB_EOF())
+ break; /* garbage was sent */
+ request = SB_GET(); /* get will/wont */
+
+ if (SB_EOF())
+ break; /* another garbage check */
+
+ if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */
+ /*
+ * Process suboption buffer of slc's
+ */
+ start_slc(1);
+ do_opt_slc(subpointer, subend - subpointer);
+ (void) end_slc(0);
+ break;
+ } else if (request == LM_MODE) {
+ if (SB_EOF())
+ return;
+ useeditmode = SB_GET(); /* get mode flag */
+ clientstat(LM_MODE, 0, 0);
+ break;
+ }
+
+ if (SB_EOF())
+ break;
+ switch (SB_GET()) { /* what suboption? */
+ case LM_FORWARDMASK:
+ /*
+ * According to spec, only server can send request for
+ * forwardmask, and client can only return a positive response.
+ * So don't worry about it.
+ */
+
+ default:
+ break;
+ }
+ break;
+ } /* end of case TELOPT_LINEMODE */
+#endif
+ case TELOPT_STATUS: {
+ int mode;
+
+ if (SB_EOF())
+ break;
+ mode = SB_GET();
+ switch (mode) {
+ case TELQUAL_SEND:
+ if (my_state_is_will(TELOPT_STATUS))
+ send_status();
+ break;
+
+ case TELQUAL_IS:
+ break;
+
+ default:
+ break;
+ }
+ break;
+ } /* end of case TELOPT_STATUS */
+
+ case TELOPT_XDISPLOC: {
+ if (SB_EOF() || SB_GET() != TELQUAL_IS)
+ return;
+ settimer(xdisplocsubopt);
+ subpointer[SB_LEN()] = '\0';
+ (void)setenv("DISPLAY", (char *)subpointer, 1);
+ break;
+ } /* end of case TELOPT_XDISPLOC */
+
+#ifdef TELOPT_NEW_ENVIRON
+ case TELOPT_NEW_ENVIRON:
+#endif
+ case TELOPT_OLD_ENVIRON: {
+ register int c;
+ register char *cp, *varp, *valp;
+
+ if (SB_EOF())
+ return;
+ c = SB_GET();
+ if (c == TELQUAL_IS) {
+ if (subchar == TELOPT_OLD_ENVIRON)
+ settimer(oenvironsubopt);
+ else
+ settimer(environsubopt);
+ } else if (c != TELQUAL_INFO) {
+ return;
+ }
+
+#ifdef TELOPT_NEW_ENVIRON
+ if (subchar == TELOPT_NEW_ENVIRON) {
+ while (!SB_EOF()) {
+ c = SB_GET();
+ if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
+ break;
+ }
+ } else
+#endif
+ {
+#ifdef ENV_HACK
+ /*
+ * We only want to do this if we haven't already decided
+ * whether or not the other side has its VALUE and VAR
+ * reversed.
+ */
+ if (env_ovar < 0) {
+ register int last = -1; /* invalid value */
+ int empty = 0;
+ int got_var = 0, got_value = 0, got_uservar = 0;
+
+ /*
+ * The other side might have its VALUE and VAR values
+ * reversed. To be interoperable, we need to determine
+ * which way it is. If the first recognized character
+ * is a VAR or VALUE, then that will tell us what
+ * type of client it is. If the fist recognized
+ * character is a USERVAR, then we continue scanning
+ * the suboption looking for two consecutive
+ * VAR or VALUE fields. We should not get two
+ * consecutive VALUE fields, so finding two
+ * consecutive VALUE or VAR fields will tell us
+ * what the client is.
+ */
+ SB_SAVE();
+ while (!SB_EOF()) {
+ c = SB_GET();
+ switch(c) {
+ case OLD_ENV_VAR:
+ if (last < 0 || last == OLD_ENV_VAR
+ || (empty && (last == OLD_ENV_VALUE)))
+ goto env_ovar_ok;
+ got_var++;
+ last = OLD_ENV_VAR;
+ break;
+ case OLD_ENV_VALUE:
+ if (last < 0 || last == OLD_ENV_VALUE
+ || (empty && (last == OLD_ENV_VAR)))
+ goto env_ovar_wrong;
+ got_value++;
+ last = OLD_ENV_VALUE;
+ break;
+ case ENV_USERVAR:
+ /* count strings of USERVAR as one */
+ if (last != ENV_USERVAR)
+ got_uservar++;
+ if (empty) {
+ if (last == OLD_ENV_VALUE)
+ goto env_ovar_ok;
+ if (last == OLD_ENV_VAR)
+ goto env_ovar_wrong;
+ }
+ last = ENV_USERVAR;
+ break;
+ case ENV_ESC:
+ if (!SB_EOF())
+ c = SB_GET();
+ /* FALL THROUGH */
+ default:
+ empty = 0;
+ continue;
+ }
+ empty = 1;
+ }
+ if (empty) {
+ if (last == OLD_ENV_VALUE)
+ goto env_ovar_ok;
+ if (last == OLD_ENV_VAR)
+ goto env_ovar_wrong;
+ }
+ /*
+ * Ok, the first thing was a USERVAR, and there
+ * are not two consecutive VAR or VALUE commands,
+ * and none of the VAR or VALUE commands are empty.
+ * If the client has sent us a well-formed option,
+ * then the number of VALUEs received should always
+ * be less than or equal to the number of VARs and
+ * USERVARs received.
+ *
+ * If we got exactly as many VALUEs as VARs and
+ * USERVARs, the client has the same definitions.
+ *
+ * If we got exactly as many VARs as VALUEs and
+ * USERVARS, the client has reversed definitions.
+ */
+ if (got_uservar + got_var == got_value) {
+ env_ovar_ok:
+ env_ovar = OLD_ENV_VAR;
+ env_ovalue = OLD_ENV_VALUE;
+ } else if (got_uservar + got_value == got_var) {
+ env_ovar_wrong:
+ env_ovar = OLD_ENV_VALUE;
+ env_ovalue = OLD_ENV_VAR;
+ DIAG(TD_OPTIONS, {sprintf(nfrontp,
+ "ENVIRON VALUE and VAR are reversed!\r\n");
+ nfrontp += strlen(nfrontp);});
+
+ }
+ }
+ SB_RESTORE();
+#endif
+
+ while (!SB_EOF()) {
+ c = SB_GET();
+ if ((c == env_ovar) || (c == ENV_USERVAR))
+ break;
+ }
+ }
+
+ if (SB_EOF())
+ return;
+
+ cp = varp = (char *)subpointer;
+ valp = 0;
+
+ while (!SB_EOF()) {
+ c = SB_GET();
+ if (subchar == TELOPT_OLD_ENVIRON) {
+ if (c == env_ovar)
+ c = NEW_ENV_VAR;
+ else if (c == env_ovalue)
+ c = NEW_ENV_VALUE;
+ }
+ switch (c) {
+
+ case NEW_ENV_VALUE:
+ *cp = '\0';
+ cp = valp = (char *)subpointer;
+ break;
+
+ case NEW_ENV_VAR:
+ case ENV_USERVAR:
+ *cp = '\0';
+ if (valp)
+ (void)setenv(varp, valp, 1);
+ else
+ unsetenv(varp);
+ cp = varp = (char *)subpointer;
+ valp = 0;
+ break;
+
+ case ENV_ESC:
+ if (SB_EOF())
+ break;
+ c = SB_GET();
+ /* FALL THROUGH */
+ default:
+ *cp++ = c;
+ break;
+ }
+ }
+ *cp = '\0';
+ if (valp)
+ (void)setenv(varp, valp, 1);
+ else
+ unsetenv(varp);
+ break;
+ } /* end of case TELOPT_NEW_ENVIRON */
+#if defined(AUTHENTICATION)
+ case TELOPT_AUTHENTICATION:
+ if (SB_EOF())
+ break;
+ switch(SB_GET()) {
+ case TELQUAL_SEND:
+ case TELQUAL_REPLY:
+ /*
+ * These are sent by us and cannot be sent by
+ * the client.
+ */
+ break;
+ case TELQUAL_IS:
+ auth_is(subpointer, SB_LEN());
+ break;
+ case TELQUAL_NAME:
+ auth_name(subpointer, SB_LEN());
+ break;
+ }
+ break;
+#endif
+
+ default:
+ break;
+ } /* end of switch */
+
+} /* end of suboption */
+
+ void
+doclientstat()
+{
+ clientstat(TELOPT_LINEMODE, WILL, 0);
+}
+
+#define ADD(c) *ncp++ = c;
+#define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
+ void
+send_status()
+{
+ unsigned char statusbuf[256];
+ register unsigned char *ncp;
+ register unsigned char i;
+
+ ncp = statusbuf;
+
+ netflush(); /* get rid of anything waiting to go out */
+
+ ADD(IAC);
+ ADD(SB);
+ ADD(TELOPT_STATUS);
+ ADD(TELQUAL_IS);
+
+ /*
+ * We check the want_state rather than the current state,
+ * because if we received a DO/WILL for an option that we
+ * don't support, and the other side didn't send a DONT/WONT
+ * in response to our WONT/DONT, then the "state" will be
+ * WILL/DO, and the "want_state" will be WONT/DONT. We
+ * need to go by the latter.
+ */
+ for (i = 0; i < (unsigned char)NTELOPTS; i++) {
+ if (my_want_state_is_will(i)) {
+ ADD(WILL);
+ ADD_DATA(i);
+ if (i == IAC)
+ ADD(IAC);
+ }
+ if (his_want_state_is_will(i)) {
+ ADD(DO);
+ ADD_DATA(i);
+ if (i == IAC)
+ ADD(IAC);
+ }
+ }
+
+ if (his_want_state_is_will(TELOPT_LFLOW)) {
+ ADD(SB);
+ ADD(TELOPT_LFLOW);
+ if (flowmode) {
+ ADD(LFLOW_ON);
+ } else {
+ ADD(LFLOW_OFF);
+ }
+ ADD(SE);
+
+ if (restartany >= 0) {
+ ADD(SB)
+ ADD(TELOPT_LFLOW);
+ if (restartany) {
+ ADD(LFLOW_RESTART_ANY);
+ } else {
+ ADD(LFLOW_RESTART_XON);
+ }
+ ADD(SE)
+ ADD(SB);
+ }
+ }
+
+#ifdef LINEMODE
+ if (his_want_state_is_will(TELOPT_LINEMODE)) {
+ unsigned char *cp, *cpe;
+ int len;
+
+ ADD(SB);
+ ADD(TELOPT_LINEMODE);
+ ADD(LM_MODE);
+ ADD_DATA(editmode);
+ if (editmode == IAC)
+ ADD(IAC);
+ ADD(SE);
+
+ ADD(SB);
+ ADD(TELOPT_LINEMODE);
+ ADD(LM_SLC);
+ start_slc(0);
+ send_slc();
+ len = end_slc(&cp);
+ for (cpe = cp + len; cp < cpe; cp++)
+ ADD_DATA(*cp);
+ ADD(SE);
+ }
+#endif /* LINEMODE */
+
+ ADD(IAC);
+ ADD(SE);
+
+ writenet(statusbuf, ncp - statusbuf);
+ netflush(); /* Send it on its way */
+
+ DIAG(TD_OPTIONS,
+ {printsub('>', statusbuf, ncp - statusbuf); netflush();});
+}
diff --git a/libexec/telnetd/sys_term.c b/libexec/telnetd/sys_term.c
new file mode 100644
index 0000000..40610c2
--- /dev/null
+++ b/libexec/telnetd/sys_term.c
@@ -0,0 +1,2195 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sys_term.c 8.2 (Berkeley) 12/15/93";
+#endif /* not lint */
+
+#include "telnetd.h"
+#include "pathnames.h"
+
+#if defined(AUTHENTICATION)
+#include <libtelnet/auth.h>
+#endif
+
+#if defined(CRAY) || defined(__hpux)
+# define PARENT_DOES_UTMP
+#endif
+
+int utmp_len = MAXHOSTNAMELEN;
+#ifdef NEWINIT
+#include <initreq.h>
+#else /* NEWINIT*/
+# ifdef UTMPX
+# include <utmpx.h>
+struct utmpx wtmp;
+# else
+# include <utmp.h>
+struct utmp wtmp;
+# endif /* UTMPX */
+
+# ifndef PARENT_DOES_UTMP
+#ifdef _PATH_WTMP
+char wtmpf[] = _PATH_WTMP;
+#else
+char wtmpf[] = "/usr/adm/wtmp";
+#endif
+#ifdef _PATH_UTMP
+char utmpf[] = _PATH_UTMP;
+#else
+char utmpf[] = "/etc/utmp";
+#endif
+# else /* PARENT_DOES_UTMP */
+char wtmpf[] = "/etc/wtmp";
+# endif /* PARENT_DOES_UTMP */
+
+# ifdef CRAY
+#include <tmpdir.h>
+#include <sys/wait.h>
+# if defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY)
+ /*
+ * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can
+ * use it to tell us to turn off all the socket security code,
+ * since that is only used in UNICOS 7.0 and later.
+ */
+# undef _SC_CRAY_SECURE_SYS
+# endif
+
+# if defined(_SC_CRAY_SECURE_SYS)
+#include <sys/sysv.h>
+#include <sys/secstat.h>
+extern int secflag;
+extern struct sysv sysv;
+# endif /* _SC_CRAY_SECURE_SYS */
+# endif /* CRAY */
+#endif /* NEWINIT */
+
+#ifdef STREAMSPTY
+#include <sac.h>
+#include <sys/stropts.h>
+#endif
+
+#define SCPYN(a, b) (void) strncpy(a, b, sizeof(a))
+#define SCMPN(a, b) strncmp(a, b, sizeof(a))
+
+#ifdef STREAMS
+#include <sys/stream.h>
+#endif
+#ifdef __hpux
+#include <sys/resource.h>
+#include <sys/proc.h>
+#endif
+#include <sys/tty.h>
+#ifdef t_erase
+#undef t_erase
+#undef t_kill
+#undef t_intrc
+#undef t_quitc
+#undef t_startc
+#undef t_stopc
+#undef t_eofc
+#undef t_brkc
+#undef t_suspc
+#undef t_dsuspc
+#undef t_rprntc
+#undef t_flushc
+#undef t_werasc
+#undef t_lnextc
+#endif
+
+#if defined(UNICOS5) && defined(CRAY2) && !defined(EXTPROC)
+# define EXTPROC 0400
+#endif
+
+#ifndef USE_TERMIO
+struct termbuf {
+ struct sgttyb sg;
+ struct tchars tc;
+ struct ltchars ltc;
+ int state;
+ int lflags;
+} termbuf, termbuf2;
+# define cfsetospeed(tp, val) (tp)->sg.sg_ospeed = (val)
+# define cfsetispeed(tp, val) (tp)->sg.sg_ispeed = (val)
+# define cfgetospeed(tp) (tp)->sg.sg_ospeed
+# define cfgetispeed(tp) (tp)->sg.sg_ispeed
+#else /* USE_TERMIO */
+# ifdef SYSV_TERMIO
+# define termios termio
+# endif
+# ifndef TCSANOW
+# ifdef TCSETS
+# define TCSANOW TCSETS
+# define TCSADRAIN TCSETSW
+# define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t)
+# else
+# ifdef TCSETA
+# define TCSANOW TCSETA
+# define TCSADRAIN TCSETAW
+# define tcgetattr(f, t) ioctl(f, TCGETA, (char *)t)
+# else
+# define TCSANOW TIOCSETA
+# define TCSADRAIN TIOCSETAW
+# define tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t)
+# endif
+# endif
+# define tcsetattr(f, a, t) ioctl(f, a, t)
+# define cfsetospeed(tp, val) (tp)->c_cflag &= ~CBAUD; \
+ (tp)->c_cflag |= (val)
+# define cfgetospeed(tp) ((tp)->c_cflag & CBAUD)
+# ifdef CIBAUD
+# define cfsetispeed(tp, val) (tp)->c_cflag &= ~CIBAUD; \
+ (tp)->c_cflag |= ((val)<<IBSHIFT)
+# define cfgetispeed(tp) (((tp)->c_cflag & CIBAUD)>>IBSHIFT)
+# else
+# define cfsetispeed(tp, val) (tp)->c_cflag &= ~CBAUD; \
+ (tp)->c_cflag |= (val)
+# define cfgetispeed(tp) ((tp)->c_cflag & CBAUD)
+# endif
+# endif /* TCSANOW */
+struct termios termbuf, termbuf2; /* pty control structure */
+# ifdef STREAMSPTY
+int ttyfd = -1;
+# endif
+#endif /* USE_TERMIO */
+
+/*
+ * init_termbuf()
+ * copy_termbuf(cp)
+ * set_termbuf()
+ *
+ * These three routines are used to get and set the "termbuf" structure
+ * to and from the kernel. init_termbuf() gets the current settings.
+ * copy_termbuf() hands in a new "termbuf" to write to the kernel, and
+ * set_termbuf() writes the structure into the kernel.
+ */
+
+ void
+init_termbuf()
+{
+#ifndef USE_TERMIO
+ (void) ioctl(pty, TIOCGETP, (char *)&termbuf.sg);
+ (void) ioctl(pty, TIOCGETC, (char *)&termbuf.tc);
+ (void) ioctl(pty, TIOCGLTC, (char *)&termbuf.ltc);
+# ifdef TIOCGSTATE
+ (void) ioctl(pty, TIOCGSTATE, (char *)&termbuf.state);
+# endif
+#else
+# ifdef STREAMSPTY
+ (void) tcgetattr(ttyfd, &termbuf);
+# else
+ (void) tcgetattr(pty, &termbuf);
+# endif
+#endif
+ termbuf2 = termbuf;
+}
+
+#if defined(LINEMODE) && defined(TIOCPKT_IOCTL)
+ void
+copy_termbuf(cp, len)
+ char *cp;
+ int len;
+{
+ if (len > sizeof(termbuf))
+ len = sizeof(termbuf);
+ bcopy(cp, (char *)&termbuf, len);
+ termbuf2 = termbuf;
+}
+#endif /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */
+
+ void
+set_termbuf()
+{
+ /*
+ * Only make the necessary changes.
+ */
+#ifndef USE_TERMIO
+ if (bcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, sizeof(termbuf.sg)))
+ (void) ioctl(pty, TIOCSETN, (char *)&termbuf.sg);
+ if (bcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, sizeof(termbuf.tc)))
+ (void) ioctl(pty, TIOCSETC, (char *)&termbuf.tc);
+ if (bcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc,
+ sizeof(termbuf.ltc)))
+ (void) ioctl(pty, TIOCSLTC, (char *)&termbuf.ltc);
+ if (termbuf.lflags != termbuf2.lflags)
+ (void) ioctl(pty, TIOCLSET, (char *)&termbuf.lflags);
+#else /* USE_TERMIO */
+ if (bcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf)))
+# ifdef STREAMSPTY
+ (void) tcsetattr(ttyfd, TCSANOW, &termbuf);
+# else
+ (void) tcsetattr(pty, TCSANOW, &termbuf);
+# endif
+# if defined(CRAY2) && defined(UNICOS5)
+ needtermstat = 1;
+# endif
+#endif /* USE_TERMIO */
+}
+
+
+/*
+ * spcset(func, valp, valpp)
+ *
+ * This function takes various special characters (func), and
+ * sets *valp to the current value of that character, and
+ * *valpp to point to where in the "termbuf" structure that
+ * value is kept.
+ *
+ * It returns the SLC_ level of support for this function.
+ */
+
+#ifndef USE_TERMIO
+ int
+spcset(func, valp, valpp)
+ int func;
+ cc_t *valp;
+ cc_t **valpp;
+{
+ switch(func) {
+ case SLC_EOF:
+ *valp = termbuf.tc.t_eofc;
+ *valpp = (cc_t *)&termbuf.tc.t_eofc;
+ return(SLC_VARIABLE);
+ case SLC_EC:
+ *valp = termbuf.sg.sg_erase;
+ *valpp = (cc_t *)&termbuf.sg.sg_erase;
+ return(SLC_VARIABLE);
+ case SLC_EL:
+ *valp = termbuf.sg.sg_kill;
+ *valpp = (cc_t *)&termbuf.sg.sg_kill;
+ return(SLC_VARIABLE);
+ case SLC_IP:
+ *valp = termbuf.tc.t_intrc;
+ *valpp = (cc_t *)&termbuf.tc.t_intrc;
+ return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
+ case SLC_ABORT:
+ *valp = termbuf.tc.t_quitc;
+ *valpp = (cc_t *)&termbuf.tc.t_quitc;
+ return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
+ case SLC_XON:
+ *valp = termbuf.tc.t_startc;
+ *valpp = (cc_t *)&termbuf.tc.t_startc;
+ return(SLC_VARIABLE);
+ case SLC_XOFF:
+ *valp = termbuf.tc.t_stopc;
+ *valpp = (cc_t *)&termbuf.tc.t_stopc;
+ return(SLC_VARIABLE);
+ case SLC_AO:
+ *valp = termbuf.ltc.t_flushc;
+ *valpp = (cc_t *)&termbuf.ltc.t_flushc;
+ return(SLC_VARIABLE);
+ case SLC_SUSP:
+ *valp = termbuf.ltc.t_suspc;
+ *valpp = (cc_t *)&termbuf.ltc.t_suspc;
+ return(SLC_VARIABLE);
+ case SLC_EW:
+ *valp = termbuf.ltc.t_werasc;
+ *valpp = (cc_t *)&termbuf.ltc.t_werasc;
+ return(SLC_VARIABLE);
+ case SLC_RP:
+ *valp = termbuf.ltc.t_rprntc;
+ *valpp = (cc_t *)&termbuf.ltc.t_rprntc;
+ return(SLC_VARIABLE);
+ case SLC_LNEXT:
+ *valp = termbuf.ltc.t_lnextc;
+ *valpp = (cc_t *)&termbuf.ltc.t_lnextc;
+ return(SLC_VARIABLE);
+ case SLC_FORW1:
+ *valp = termbuf.tc.t_brkc;
+ *valpp = (cc_t *)&termbuf.ltc.t_lnextc;
+ return(SLC_VARIABLE);
+ case SLC_BRK:
+ case SLC_SYNCH:
+ case SLC_AYT:
+ case SLC_EOR:
+ *valp = (cc_t)0;
+ *valpp = (cc_t *)0;
+ return(SLC_DEFAULT);
+ default:
+ *valp = (cc_t)0;
+ *valpp = (cc_t *)0;
+ return(SLC_NOSUPPORT);
+ }
+}
+
+#else /* USE_TERMIO */
+
+ int
+spcset(func, valp, valpp)
+ int func;
+ cc_t *valp;
+ cc_t **valpp;
+{
+
+#define setval(a, b) *valp = termbuf.c_cc[a]; \
+ *valpp = &termbuf.c_cc[a]; \
+ return(b);
+#define defval(a) *valp = ((cc_t)a); *valpp = (cc_t *)0; return(SLC_DEFAULT);
+
+ switch(func) {
+ case SLC_EOF:
+ setval(VEOF, SLC_VARIABLE);
+ case SLC_EC:
+ setval(VERASE, SLC_VARIABLE);
+ case SLC_EL:
+ setval(VKILL, SLC_VARIABLE);
+ case SLC_IP:
+ setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
+ case SLC_ABORT:
+ setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
+ case SLC_XON:
+#ifdef VSTART
+ setval(VSTART, SLC_VARIABLE);
+#else
+ defval(0x13);
+#endif
+ case SLC_XOFF:
+#ifdef VSTOP
+ setval(VSTOP, SLC_VARIABLE);
+#else
+ defval(0x11);
+#endif
+ case SLC_EW:
+#ifdef VWERASE
+ setval(VWERASE, SLC_VARIABLE);
+#else
+ defval(0);
+#endif
+ case SLC_RP:
+#ifdef VREPRINT
+ setval(VREPRINT, SLC_VARIABLE);
+#else
+ defval(0);
+#endif
+ case SLC_LNEXT:
+#ifdef VLNEXT
+ setval(VLNEXT, SLC_VARIABLE);
+#else
+ defval(0);
+#endif
+ case SLC_AO:
+#if !defined(VDISCARD) && defined(VFLUSHO)
+# define VDISCARD VFLUSHO
+#endif
+#ifdef VDISCARD
+ setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT);
+#else
+ defval(0);
+#endif
+ case SLC_SUSP:
+#ifdef VSUSP
+ setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN);
+#else
+ defval(0);
+#endif
+#ifdef VEOL
+ case SLC_FORW1:
+ setval(VEOL, SLC_VARIABLE);
+#endif
+#ifdef VEOL2
+ case SLC_FORW2:
+ setval(VEOL2, SLC_VARIABLE);
+#endif
+ case SLC_AYT:
+#ifdef VSTATUS
+ setval(VSTATUS, SLC_VARIABLE);
+#else
+ defval(0);
+#endif
+
+ case SLC_BRK:
+ case SLC_SYNCH:
+ case SLC_EOR:
+ defval(0);
+
+ default:
+ *valp = 0;
+ *valpp = 0;
+ return(SLC_NOSUPPORT);
+ }
+}
+#endif /* USE_TERMIO */
+
+#ifdef CRAY
+/*
+ * getnpty()
+ *
+ * Return the number of pty's configured into the system.
+ */
+ int
+getnpty()
+{
+#ifdef _SC_CRAY_NPTY
+ int numptys;
+
+ if ((numptys = sysconf(_SC_CRAY_NPTY)) != -1)
+ return numptys;
+ else
+#endif /* _SC_CRAY_NPTY */
+ return 128;
+}
+#endif /* CRAY */
+
+#ifndef convex
+/*
+ * getpty()
+ *
+ * Allocate a pty. As a side effect, the external character
+ * array "line" contains the name of the slave side.
+ *
+ * Returns the file descriptor of the opened pty.
+ */
+#ifndef __GNUC__
+char *line = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+#else
+static char Xline[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+char *line = Xline;
+#endif
+#ifdef CRAY
+char *myline = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+#endif /* CRAY */
+
+ int
+getpty(ptynum)
+int *ptynum;
+{
+ register int p;
+#ifdef STREAMSPTY
+ int t;
+ char *ptsname();
+
+ p = open("/dev/ptmx", 2);
+ if (p > 0) {
+ grantpt(p);
+ unlockpt(p);
+ strcpy(line, ptsname(p));
+ return(p);
+ }
+
+#else /* ! STREAMSPTY */
+#ifndef CRAY
+ register char *cp, *p1, *p2;
+ register int i;
+#if defined(sun) && defined(TIOCGPGRP) && BSD < 199207
+ int dummy;
+#endif
+
+#ifndef __hpux
+ (void) sprintf(line, "/dev/ptyXX");
+ p1 = &line[8];
+ p2 = &line[9];
+#else
+ (void) sprintf(line, "/dev/ptym/ptyXX");
+ p1 = &line[13];
+ p2 = &line[14];
+#endif
+
+ for (cp = "pqrsPQRS"; *cp; cp++) {
+ struct stat stb;
+
+ *p1 = *cp;
+ *p2 = '0';
+ /*
+ * This stat() check is just to keep us from
+ * looping through all 256 combinations if there
+ * aren't that many ptys available.
+ */
+ if (stat(line, &stb) < 0)
+ break;
+ for (i = 0; i < 32; i++) {
+ *p2 = "0123456789abcdefghijklmnopqrstuv"[i];
+ p = open(line, 2);
+ if (p > 0) {
+#ifndef __hpux
+ line[5] = 't';
+#else
+ for (p1 = &line[8]; *p1; p1++)
+ *p1 = *(p1+1);
+ line[9] = 't';
+#endif
+ chown(line, 0, 0);
+ chmod(line, 0600);
+#if defined(sun) && defined(TIOCGPGRP) && BSD < 199207
+ if (ioctl(p, TIOCGPGRP, &dummy) == 0
+ || errno != EIO) {
+ chmod(line, 0666);
+ close(p);
+ line[5] = 'p';
+ } else
+#endif /* defined(sun) && defined(TIOCGPGRP) && BSD < 199207 */
+ return(p);
+ }
+ }
+ }
+#else /* CRAY */
+ extern lowpty, highpty;
+ struct stat sb;
+
+ for (*ptynum = lowpty; *ptynum <= highpty; (*ptynum)++) {
+ (void) sprintf(myline, "/dev/pty/%03d", *ptynum);
+ p = open(myline, 2);
+ if (p < 0)
+ continue;
+ (void) sprintf(line, "/dev/ttyp%03d", *ptynum);
+ /*
+ * Here are some shenanigans to make sure that there
+ * are no listeners lurking on the line.
+ */
+ if(stat(line, &sb) < 0) {
+ (void) close(p);
+ continue;
+ }
+ if(sb.st_uid || sb.st_gid || sb.st_mode != 0600) {
+ chown(line, 0, 0);
+ chmod(line, 0600);
+ (void)close(p);
+ p = open(myline, 2);
+ if (p < 0)
+ continue;
+ }
+ /*
+ * Now it should be safe...check for accessability.
+ */
+ if (access(line, 6) == 0)
+ return(p);
+ else {
+ /* no tty side to pty so skip it */
+ (void) close(p);
+ }
+ }
+#endif /* CRAY */
+#endif /* STREAMSPTY */
+ return(-1);
+}
+#endif /* convex */
+
+#ifdef LINEMODE
+/*
+ * tty_flowmode() Find out if flow control is enabled or disabled.
+ * tty_linemode() Find out if linemode (external processing) is enabled.
+ * tty_setlinemod(on) Turn on/off linemode.
+ * tty_isecho() Find out if echoing is turned on.
+ * tty_setecho(on) Enable/disable character echoing.
+ * tty_israw() Find out if terminal is in RAW mode.
+ * tty_binaryin(on) Turn on/off BINARY on input.
+ * tty_binaryout(on) Turn on/off BINARY on output.
+ * tty_isediting() Find out if line editing is enabled.
+ * tty_istrapsig() Find out if signal trapping is enabled.
+ * tty_setedit(on) Turn on/off line editing.
+ * tty_setsig(on) Turn on/off signal trapping.
+ * tty_issofttab() Find out if tab expansion is enabled.
+ * tty_setsofttab(on) Turn on/off soft tab expansion.
+ * tty_islitecho() Find out if typed control chars are echoed literally
+ * tty_setlitecho() Turn on/off literal echo of control chars
+ * tty_tspeed(val) Set transmit speed to val.
+ * tty_rspeed(val) Set receive speed to val.
+ */
+
+#ifdef convex
+static int linestate;
+#endif
+
+ int
+tty_linemode()
+{
+#ifndef convex
+#ifndef USE_TERMIO
+ return(termbuf.state & TS_EXTPROC);
+#else
+ return(termbuf.c_lflag & EXTPROC);
+#endif
+#else
+ return(linestate);
+#endif
+}
+
+ void
+tty_setlinemode(on)
+ int on;
+{
+#ifdef TIOCEXT
+# ifndef convex
+ set_termbuf();
+# else
+ linestate = on;
+# endif
+ (void) ioctl(pty, TIOCEXT, (char *)&on);
+# ifndef convex
+ init_termbuf();
+# endif
+#else /* !TIOCEXT */
+# ifdef EXTPROC
+ if (on)
+ termbuf.c_lflag |= EXTPROC;
+ else
+ termbuf.c_lflag &= ~EXTPROC;
+# endif
+#endif /* TIOCEXT */
+}
+#endif /* LINEMODE */
+
+ int
+tty_isecho()
+{
+#ifndef USE_TERMIO
+ return (termbuf.sg.sg_flags & ECHO);
+#else
+ return (termbuf.c_lflag & ECHO);
+#endif
+}
+
+ int
+tty_flowmode()
+{
+#ifndef USE_TERMIO
+ return(((termbuf.tc.t_startc) > 0 && (termbuf.tc.t_stopc) > 0) ? 1 : 0);
+#else
+ return((termbuf.c_iflag & IXON) ? 1 : 0);
+#endif
+}
+
+ int
+tty_restartany()
+{
+#ifndef USE_TERMIO
+# ifdef DECCTQ
+ return((termbuf.lflags & DECCTQ) ? 0 : 1);
+# else
+ return(-1);
+# endif
+#else
+ return((termbuf.c_iflag & IXANY) ? 1 : 0);
+#endif
+}
+
+ void
+tty_setecho(on)
+ int on;
+{
+#ifndef USE_TERMIO
+ if (on)
+ termbuf.sg.sg_flags |= ECHO|CRMOD;
+ else
+ termbuf.sg.sg_flags &= ~(ECHO|CRMOD);
+#else
+ if (on)
+ termbuf.c_lflag |= ECHO;
+ else
+ termbuf.c_lflag &= ~ECHO;
+#endif
+}
+
+ int
+tty_israw()
+{
+#ifndef USE_TERMIO
+ return(termbuf.sg.sg_flags & RAW);
+#else
+ return(!(termbuf.c_lflag & ICANON));
+#endif
+}
+
+#if defined (AUTHENTICATION) && defined(NO_LOGIN_F) && defined(LOGIN_R)
+ int
+tty_setraw(on)
+{
+# ifndef USE_TERMIO
+ if (on)
+ termbuf.sg.sg_flags |= RAW;
+ else
+ termbuf.sg.sg_flags &= ~RAW;
+# else
+ if (on)
+ termbuf.c_lflag &= ~ICANON;
+ else
+ termbuf.c_lflag |= ICANON;
+# endif
+}
+#endif
+
+ void
+tty_binaryin(on)
+ int on;
+{
+#ifndef USE_TERMIO
+ if (on)
+ termbuf.lflags |= LPASS8;
+ else
+ termbuf.lflags &= ~LPASS8;
+#else
+ if (on) {
+ termbuf.c_iflag &= ~ISTRIP;
+ } else {
+ termbuf.c_iflag |= ISTRIP;
+ }
+#endif
+}
+
+ void
+tty_binaryout(on)
+ int on;
+{
+#ifndef USE_TERMIO
+ if (on)
+ termbuf.lflags |= LLITOUT;
+ else
+ termbuf.lflags &= ~LLITOUT;
+#else
+ if (on) {
+ termbuf.c_cflag &= ~(CSIZE|PARENB);
+ termbuf.c_cflag |= CS8;
+ termbuf.c_oflag &= ~OPOST;
+ } else {
+ termbuf.c_cflag &= ~CSIZE;
+ termbuf.c_cflag |= CS7|PARENB;
+ termbuf.c_oflag |= OPOST;
+ }
+#endif
+}
+
+ int
+tty_isbinaryin()
+{
+#ifndef USE_TERMIO
+ return(termbuf.lflags & LPASS8);
+#else
+ return(!(termbuf.c_iflag & ISTRIP));
+#endif
+}
+
+ int
+tty_isbinaryout()
+{
+#ifndef USE_TERMIO
+ return(termbuf.lflags & LLITOUT);
+#else
+ return(!(termbuf.c_oflag&OPOST));
+#endif
+}
+
+#ifdef LINEMODE
+ int
+tty_isediting()
+{
+#ifndef USE_TERMIO
+ return(!(termbuf.sg.sg_flags & (CBREAK|RAW)));
+#else
+ return(termbuf.c_lflag & ICANON);
+#endif
+}
+
+ int
+tty_istrapsig()
+{
+#ifndef USE_TERMIO
+ return(!(termbuf.sg.sg_flags&RAW));
+#else
+ return(termbuf.c_lflag & ISIG);
+#endif
+}
+
+ void
+tty_setedit(on)
+ int on;
+{
+#ifndef USE_TERMIO
+ if (on)
+ termbuf.sg.sg_flags &= ~CBREAK;
+ else
+ termbuf.sg.sg_flags |= CBREAK;
+#else
+ if (on)
+ termbuf.c_lflag |= ICANON;
+ else
+ termbuf.c_lflag &= ~ICANON;
+#endif
+}
+
+ void
+tty_setsig(on)
+ int on;
+{
+#ifndef USE_TERMIO
+ if (on)
+ ;
+#else
+ if (on)
+ termbuf.c_lflag |= ISIG;
+ else
+ termbuf.c_lflag &= ~ISIG;
+#endif
+}
+#endif /* LINEMODE */
+
+ int
+tty_issofttab()
+{
+#ifndef USE_TERMIO
+ return (termbuf.sg.sg_flags & XTABS);
+#else
+# ifdef OXTABS
+ return (termbuf.c_oflag & OXTABS);
+# endif
+# ifdef TABDLY
+ return ((termbuf.c_oflag & TABDLY) == TAB3);
+# endif
+#endif
+}
+
+ void
+tty_setsofttab(on)
+ int on;
+{
+#ifndef USE_TERMIO
+ if (on)
+ termbuf.sg.sg_flags |= XTABS;
+ else
+ termbuf.sg.sg_flags &= ~XTABS;
+#else
+ if (on) {
+# ifdef OXTABS
+ termbuf.c_oflag |= OXTABS;
+# endif
+# ifdef TABDLY
+ termbuf.c_oflag &= ~TABDLY;
+ termbuf.c_oflag |= TAB3;
+# endif
+ } else {
+# ifdef OXTABS
+ termbuf.c_oflag &= ~OXTABS;
+# endif
+# ifdef TABDLY
+ termbuf.c_oflag &= ~TABDLY;
+ termbuf.c_oflag |= TAB0;
+# endif
+ }
+#endif
+}
+
+ int
+tty_islitecho()
+{
+#ifndef USE_TERMIO
+ return (!(termbuf.lflags & LCTLECH));
+#else
+# ifdef ECHOCTL
+ return (!(termbuf.c_lflag & ECHOCTL));
+# endif
+# ifdef TCTLECH
+ return (!(termbuf.c_lflag & TCTLECH));
+# endif
+# if !defined(ECHOCTL) && !defined(TCTLECH)
+ return (0); /* assumes ctl chars are echoed '^x' */
+# endif
+#endif
+}
+
+ void
+tty_setlitecho(on)
+ int on;
+{
+#ifndef USE_TERMIO
+ if (on)
+ termbuf.lflags &= ~LCTLECH;
+ else
+ termbuf.lflags |= LCTLECH;
+#else
+# ifdef ECHOCTL
+ if (on)
+ termbuf.c_lflag &= ~ECHOCTL;
+ else
+ termbuf.c_lflag |= ECHOCTL;
+# endif
+# ifdef TCTLECH
+ if (on)
+ termbuf.c_lflag &= ~TCTLECH;
+ else
+ termbuf.c_lflag |= TCTLECH;
+# endif
+#endif
+}
+
+ int
+tty_iscrnl()
+{
+#ifndef USE_TERMIO
+ return (termbuf.sg.sg_flags & CRMOD);
+#else
+ return (termbuf.c_iflag & ICRNL);
+#endif
+}
+
+/*
+ * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD).
+ */
+#if B4800 != 4800
+#define DECODE_BAUD
+#endif
+
+#ifdef DECODE_BAUD
+/*
+ * A table of available terminal speeds
+ */
+struct termspeeds {
+ int speed;
+ int value;
+} termspeeds[] = {
+ { 0, B0 }, { 50, B50 }, { 75, B75 },
+ { 110, B110 }, { 134, B134 }, { 150, B150 },
+ { 200, B200 }, { 300, B300 }, { 600, B600 },
+ { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 },
+ { 4800, B4800 }, { 9600, B9600 }, { 19200, B9600 },
+ { 38400, B9600 }, { -1, B9600 }
+};
+#endif /* DECODE_BAUD */
+
+ void
+tty_tspeed(val)
+ int val;
+{
+#ifdef DECODE_BAUD
+ register struct termspeeds *tp;
+
+ for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
+ ;
+ cfsetospeed(&termbuf, tp->value);
+#else /* DECODE_BAUD */
+ cfsetospeed(&termbuf, val);
+#endif /* DECODE_BAUD */
+}
+
+ void
+tty_rspeed(val)
+ int val;
+{
+#ifdef DECODE_BAUD
+ register struct termspeeds *tp;
+
+ for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
+ ;
+ cfsetispeed(&termbuf, tp->value);
+#else /* DECODE_BAUD */
+ cfsetispeed(&termbuf, val);
+#endif /* DECODE_BAUD */
+}
+
+#if defined(CRAY2) && defined(UNICOS5)
+ int
+tty_isnewmap()
+{
+ return((termbuf.c_oflag & OPOST) && (termbuf.c_oflag & ONLCR) &&
+ !(termbuf.c_oflag & ONLRET));
+}
+#endif
+
+#ifdef PARENT_DOES_UTMP
+# ifndef NEWINIT
+extern struct utmp wtmp;
+extern char wtmpf[];
+# else /* NEWINIT */
+int gotalarm;
+
+ /* ARGSUSED */
+ void
+nologinproc(sig)
+ int sig;
+{
+ gotalarm++;
+}
+# endif /* NEWINIT */
+#endif /* PARENT_DOES_UTMP */
+
+#ifndef NEWINIT
+# ifdef PARENT_DOES_UTMP
+extern void utmp_sig_init P((void));
+extern void utmp_sig_reset P((void));
+extern void utmp_sig_wait P((void));
+extern void utmp_sig_notify P((int));
+# endif /* PARENT_DOES_UTMP */
+#endif
+
+/*
+ * getptyslave()
+ *
+ * Open the slave side of the pty, and do any initialization
+ * that is necessary. The return value is a file descriptor
+ * for the slave side.
+ */
+ int
+getptyslave()
+{
+ register int t = -1;
+ char erase;
+
+#if !defined(CRAY) || !defined(NEWINIT)
+# ifdef LINEMODE
+ int waslm;
+# endif
+# ifdef TIOCGWINSZ
+ struct winsize ws;
+ extern int def_row, def_col;
+# endif
+ extern int def_tspeed, def_rspeed;
+ /*
+ * Opening the slave side may cause initilization of the
+ * kernel tty structure. We need remember the state of
+ * if linemode was turned on
+ * terminal window size
+ * terminal speed
+ * erase character
+ * so that we can re-set them if we need to.
+ */
+# ifdef LINEMODE
+ waslm = tty_linemode();
+# endif
+ erase = termbuf.c_cc[VERASE];
+
+ /*
+ * Make sure that we don't have a controlling tty, and
+ * that we are the session (process group) leader.
+ */
+# ifdef TIOCNOTTY
+ t = open(_PATH_TTY, O_RDWR);
+ if (t >= 0) {
+ (void) ioctl(t, TIOCNOTTY, (char *)0);
+ (void) close(t);
+ }
+# endif
+
+
+# ifdef PARENT_DOES_UTMP
+ /*
+ * Wait for our parent to get the utmp stuff to get done.
+ */
+ utmp_sig_wait();
+# endif
+
+ t = cleanopen(line);
+ if (t < 0)
+ fatalperror(net, line);
+
+#ifdef STREAMSPTY
+#ifdef USE_TERMIO
+ ttyfd = t;
+#endif
+ if (ioctl(t, I_PUSH, "ptem") < 0)
+ fatal(net, "I_PUSH ptem");
+ if (ioctl(t, I_PUSH, "ldterm") < 0)
+ fatal(net, "I_PUSH ldterm");
+ if (ioctl(t, I_PUSH, "ttcompat") < 0)
+ fatal(net, "I_PUSH ttcompat");
+ if (ioctl(pty, I_PUSH, "pckt") < 0)
+ fatal(net, "I_PUSH pckt");
+#endif
+
+ /*
+ * set up the tty modes as we like them to be.
+ */
+ init_termbuf();
+# ifdef TIOCGWINSZ
+ if (def_row || def_col) {
+ bzero((char *)&ws, sizeof(ws));
+ ws.ws_col = def_col;
+ ws.ws_row = def_row;
+ (void)ioctl(t, TIOCSWINSZ, (char *)&ws);
+ }
+# endif
+
+ /*
+ * Settings for sgtty based systems
+ */
+# ifndef USE_TERMIO
+ termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO|XTABS;
+# endif /* USE_TERMIO */
+
+ /*
+ * Settings for UNICOS (and HPUX)
+ */
+# if defined(CRAY) || defined(__hpux)
+ termbuf.c_oflag = OPOST|ONLCR|TAB3;
+ termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON;
+ termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;
+ termbuf.c_cflag = EXTB|HUPCL|CS8;
+# endif
+
+ /*
+ * Settings for all other termios/termio based
+ * systems, other than 4.4BSD. In 4.4BSD the
+ * kernel does the initial terminal setup.
+ */
+# if defined(USE_TERMIO) && !(defined(CRAY) || defined(__hpux)) && (BSD <= 43)
+# ifndef OXTABS
+# define OXTABS 0
+# endif
+ termbuf.c_lflag |= ECHO;
+ termbuf.c_oflag |= ONLCR|OXTABS;
+ termbuf.c_iflag |= ICRNL;
+ termbuf.c_iflag &= ~IXOFF;
+# endif /* defined(USE_TERMIO) && !defined(CRAY) && (BSD <= 43) */
+ tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600);
+ tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600);
+ if (erase)
+ termbuf.c_cc[VERASE] = erase;
+# ifdef LINEMODE
+ if (waslm)
+ tty_setlinemode(1);
+# endif /* LINEMODE */
+
+ /*
+ * Set the tty modes, and make this our controlling tty.
+ */
+ set_termbuf();
+ if (login_tty(t) == -1)
+ fatalperror(net, "login_tty");
+#endif /* !defined(CRAY) || !defined(NEWINIT) */
+ if (net > 2)
+ (void) close(net);
+#if defined(AUTHENTICATION) && defined(NO_LOGIN_F) && defined(LOGIN_R)
+ /*
+ * Leave the pty open so that we can write out the rlogin
+ * protocol for /bin/login, if the authentication works.
+ */
+#else
+ if (pty > 2) {
+ (void) close(pty);
+ pty = -1;
+ }
+#endif
+}
+
+#if !defined(CRAY) || !defined(NEWINIT)
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+/*
+ * Open the specified slave side of the pty,
+ * making sure that we have a clean tty.
+ */
+ int
+cleanopen(line)
+ char *line;
+{
+ register int t;
+#if defined(_SC_CRAY_SECURE_SYS)
+ struct secstat secbuf;
+#endif /* _SC_CRAY_SECURE_SYS */
+
+#ifndef STREAMSPTY
+ /*
+ * Make sure that other people can't open the
+ * slave side of the connection.
+ */
+ (void) chown(line, 0, 0);
+ (void) chmod(line, 0600);
+#endif
+
+# if !defined(CRAY) && (BSD > 43)
+ (void) revoke(line);
+# endif
+#if defined(_SC_CRAY_SECURE_SYS)
+ if (secflag) {
+ if (secstat(line, &secbuf) < 0)
+ return(-1);
+ if (setulvl(secbuf.st_slevel) < 0)
+ return(-1);
+ if (setucmp(secbuf.st_compart) < 0)
+ return(-1);
+ }
+#endif /* _SC_CRAY_SECURE_SYS */
+
+ t = open(line, O_RDWR|O_NOCTTY);
+
+#if defined(_SC_CRAY_SECURE_SYS)
+ if (secflag) {
+ if (setulvl(sysv.sy_minlvl) < 0)
+ return(-1);
+ if (setucmp(0) < 0)
+ return(-1);
+ }
+#endif /* _SC_CRAY_SECURE_SYS */
+
+ if (t < 0)
+ return(-1);
+
+ /*
+ * Hangup anybody else using this ttyp, then reopen it for
+ * ourselves.
+ */
+# if !(defined(CRAY) || defined(__hpux)) && (BSD <= 43) && !defined(STREAMSPTY)
+ (void) signal(SIGHUP, SIG_IGN);
+ vhangup();
+ (void) signal(SIGHUP, SIG_DFL);
+ t = open(line, O_RDWR|O_NOCTTY);
+ if (t < 0)
+ return(-1);
+# endif
+# if defined(CRAY) && defined(TCVHUP)
+ {
+ register int i;
+ (void) signal(SIGHUP, SIG_IGN);
+ (void) ioctl(t, TCVHUP, (char *)0);
+ (void) signal(SIGHUP, SIG_DFL);
+ setpgrp();
+
+#if defined(_SC_CRAY_SECURE_SYS)
+ if (secflag) {
+ if (secstat(line, &secbuf) < 0)
+ return(-1);
+ if (setulvl(secbuf.st_slevel) < 0)
+ return(-1);
+ if (setucmp(secbuf.st_compart) < 0)
+ return(-1);
+ }
+#endif /* _SC_CRAY_SECURE_SYS */
+
+ i = open(line, O_RDWR);
+
+#if defined(_SC_CRAY_SECURE_SYS)
+ if (secflag) {
+ if (setulvl(sysv.sy_minlvl) < 0)
+ return(-1);
+ if (setucmp(0) < 0)
+ return(-1);
+ }
+#endif /* _SC_CRAY_SECURE_SYS */
+
+ if (i < 0)
+ return(-1);
+ (void) close(t);
+ t = i;
+ }
+# endif /* defined(CRAY) && defined(TCVHUP) */
+ return(t);
+}
+#endif /* !defined(CRAY) || !defined(NEWINIT) */
+
+#if BSD <= 43
+
+ int
+login_tty(t)
+ int t;
+{
+ if (setsid() < 0) {
+#ifdef ultrix
+ /*
+ * The setsid() may have failed because we
+ * already have a pgrp == pid. Zero out
+ * our pgrp and try again...
+ */
+ if ((setpgrp(0, 0) < 0) || (setsid() < 0))
+#endif
+ fatalperror(net, "setsid()");
+ }
+# ifdef TIOCSCTTY
+ if (ioctl(t, TIOCSCTTY, (char *)0) < 0)
+ fatalperror(net, "ioctl(sctty)");
+# if defined(CRAY)
+ /*
+ * Close the hard fd to /dev/ttypXXX, and re-open through
+ * the indirect /dev/tty interface.
+ */
+ close(t);
+ if ((t = open("/dev/tty", O_RDWR)) < 0)
+ fatalperror(net, "open(/dev/tty)");
+# endif
+# else
+ /*
+ * We get our controlling tty assigned as a side-effect
+ * of opening up a tty device. But on BSD based systems,
+ * this only happens if our process group is zero. The
+ * setsid() call above may have set our pgrp, so clear
+ * it out before opening the tty...
+ */
+ (void) setpgrp(0, 0);
+ close(open(line, O_RDWR));
+# endif
+ if (t != 0)
+ (void) dup2(t, 0);
+ if (t != 1)
+ (void) dup2(t, 1);
+ if (t != 2)
+ (void) dup2(t, 2);
+ if (t > 2)
+ close(t);
+ return(0);
+}
+#endif /* BSD <= 43 */
+
+#ifdef NEWINIT
+char *gen_id = "fe";
+#endif
+
+/*
+ * startslave(host)
+ *
+ * Given a hostname, do whatever
+ * is necessary to startup the login process on the slave side of the pty.
+ */
+
+/* ARGSUSED */
+ void
+startslave(host, autologin, autoname)
+ char *host;
+ int autologin;
+ char *autoname;
+{
+ register int i;
+ long time();
+ char name[256];
+#ifdef NEWINIT
+ extern char *ptyip;
+ struct init_request request;
+ void nologinproc();
+ register int n;
+#endif /* NEWINIT */
+
+#if defined(AUTHENTICATION)
+ if (!autoname || !autoname[0])
+ autologin = 0;
+
+ if (autologin < auth_level) {
+ fatal(net, "Authorization failed");
+ exit(1);
+ }
+#endif
+
+#ifndef NEWINIT
+# ifdef PARENT_DOES_UTMP
+ utmp_sig_init();
+# endif /* PARENT_DOES_UTMP */
+
+ if ((i = fork()) < 0)
+ fatalperror(net, "fork");
+ if (i) {
+# ifdef PARENT_DOES_UTMP
+ /*
+ * Cray parent will create utmp entry for child and send
+ * signal to child to tell when done. Child waits for signal
+ * before doing anything important.
+ */
+ register int pid = i;
+ void sigjob P((int));
+
+ setpgrp();
+ utmp_sig_reset(); /* reset handler to default */
+ /*
+ * Create utmp entry for child
+ */
+ (void) time(&wtmp.ut_time);
+ wtmp.ut_type = LOGIN_PROCESS;
+ wtmp.ut_pid = pid;
+ SCPYN(wtmp.ut_user, "LOGIN");
+ SCPYN(wtmp.ut_host, host);
+ SCPYN(wtmp.ut_line, line + sizeof("/dev/") - 1);
+#ifndef __hpux
+ SCPYN(wtmp.ut_id, wtmp.ut_line+3);
+#else
+ SCPYN(wtmp.ut_id, wtmp.ut_line+7);
+#endif
+ pututline(&wtmp);
+ endutent();
+ if ((i = open(wtmpf, O_WRONLY|O_APPEND)) >= 0) {
+ (void) write(i, (char *)&wtmp, sizeof(struct utmp));
+ (void) close(i);
+ }
+#ifdef CRAY
+ (void) signal(WJSIGNAL, sigjob);
+#endif
+ utmp_sig_notify(pid);
+# endif /* PARENT_DOES_UTMP */
+ } else {
+ getptyslave(autologin);
+ start_login(host, autologin, autoname);
+ /*NOTREACHED*/
+ }
+#else /* NEWINIT */
+
+ /*
+ * Init will start up login process if we ask nicely. We only wait
+ * for it to start up and begin normal telnet operation.
+ */
+ if ((i = open(INIT_FIFO, O_WRONLY)) < 0) {
+ char tbuf[128];
+ (void) sprintf(tbuf, "Can't open %s\n", INIT_FIFO);
+ fatalperror(net, tbuf);
+ }
+ memset((char *)&request, 0, sizeof(request));
+ request.magic = INIT_MAGIC;
+ SCPYN(request.gen_id, gen_id);
+ SCPYN(request.tty_id, &line[8]);
+ SCPYN(request.host, host);
+ SCPYN(request.term_type, terminaltype ? terminaltype : "network");
+#if !defined(UNICOS5)
+ request.signal = SIGCLD;
+ request.pid = getpid();
+#endif
+#ifdef BFTPDAEMON
+ /*
+ * Are we working as the bftp daemon?
+ */
+ if (bftpd) {
+ SCPYN(request.exec_name, BFTPPATH);
+ }
+#endif /* BFTPDAEMON */
+ if (write(i, (char *)&request, sizeof(request)) < 0) {
+ char tbuf[128];
+ (void) sprintf(tbuf, "Can't write to %s\n", INIT_FIFO);
+ fatalperror(net, tbuf);
+ }
+ (void) close(i);
+ (void) signal(SIGALRM, nologinproc);
+ for (i = 0; ; i++) {
+ char tbuf[128];
+ alarm(15);
+ n = read(pty, ptyip, BUFSIZ);
+ if (i == 3 || n >= 0 || !gotalarm)
+ break;
+ gotalarm = 0;
+ sprintf(tbuf, "telnetd: waiting for /etc/init to start login process on %s\r\n", line);
+ (void) write(net, tbuf, strlen(tbuf));
+ }
+ if (n < 0 && gotalarm)
+ fatal(net, "/etc/init didn't start login process");
+ pcc += n;
+ alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+
+ return;
+#endif /* NEWINIT */
+}
+
+char *envinit[3];
+extern char **environ;
+
+ void
+init_env()
+{
+ extern char *getenv();
+ char **envp;
+
+ envp = envinit;
+ if (*envp = getenv("TZ"))
+ *envp++ -= 3;
+#if defined(CRAY) || defined(__hpux)
+ else
+ *envp++ = "TZ=GMT0";
+#endif
+ *envp = 0;
+ environ = envinit;
+}
+
+#ifndef NEWINIT
+
+/*
+ * start_login(host)
+ *
+ * Assuming that we are now running as a child processes, this
+ * function will turn us into the login process.
+ */
+
+ void
+start_login(host, autologin, name)
+ char *host;
+ int autologin;
+ char *name;
+{
+ register char *cp;
+ register char **argv;
+ char **addarg(), *user;
+ extern char *getenv();
+#ifdef UTMPX
+ register int pid = getpid();
+ struct utmpx utmpx;
+#endif
+#ifdef SOLARIS
+ char *term;
+ char termbuf[64];
+#endif
+
+#ifdef UTMPX
+ /*
+ * Create utmp entry for child
+ */
+
+ bzero(&utmpx, sizeof(utmpx));
+ SCPYN(utmpx.ut_user, ".telnet");
+ SCPYN(utmpx.ut_line, line + sizeof("/dev/") - 1);
+ utmpx.ut_pid = pid;
+ utmpx.ut_id[0] = 't';
+ utmpx.ut_id[1] = 'n';
+ utmpx.ut_id[2] = SC_WILDC;
+ utmpx.ut_id[3] = SC_WILDC;
+ utmpx.ut_type = LOGIN_PROCESS;
+ (void) time(&utmpx.ut_tv.tv_sec);
+ if (makeutx(&utmpx) == NULL)
+ fatal(net, "makeutx failed");
+#endif
+
+ scrub_env();
+
+ /*
+ * -h : pass on name of host.
+ * WARNING: -h is accepted by login if and only if
+ * getuid() == 0.
+ * -p : don't clobber the environment (so terminal type stays set).
+ *
+ * -f : force this login, he has already been authenticated
+ */
+ argv = addarg(0, "login");
+
+#if !defined(NO_LOGIN_H)
+
+# if defined (AUTHENTICATION) && defined(NO_LOGIN_F) && defined(LOGIN_R)
+ /*
+ * Don't add the "-h host" option if we are going
+ * to be adding the "-r host" option down below...
+ */
+ if ((auth_level < 0) || (autologin != AUTH_VALID))
+# endif
+ {
+ argv = addarg(argv, "-h");
+ argv = addarg(argv, host);
+#ifdef SOLARIS
+ /*
+ * SVR4 version of -h takes TERM= as second arg, or -
+ */
+ term = getenv("TERM");
+ if (term == NULL || term[0] == 0) {
+ term = "-";
+ } else {
+ strcpy(termbuf, "TERM=");
+ strncat(termbuf, term, sizeof(termbuf) - 6);
+ term = termbuf;
+ }
+ argv = addarg(argv, term);
+#endif
+ }
+#endif
+#if !defined(NO_LOGIN_P)
+ argv = addarg(argv, "-p");
+#endif
+#ifdef BFTPDAEMON
+ /*
+ * Are we working as the bftp daemon? If so, then ask login
+ * to start bftp instead of shell.
+ */
+ if (bftpd) {
+ argv = addarg(argv, "-e");
+ argv = addarg(argv, BFTPPATH);
+ } else
+#endif
+#if defined (SecurID)
+ /*
+ * don't worry about the -f that might get sent.
+ * A -s is supposed to override it anyhow.
+ */
+ if (require_SecurID)
+ argv = addarg(argv, "-s");
+#endif
+#if defined (AUTHENTICATION)
+ if (auth_level >= 0 && autologin == AUTH_VALID) {
+# if !defined(NO_LOGIN_F)
+ argv = addarg(argv, "-f");
+ argv = addarg(argv, name);
+# else
+# if defined(LOGIN_R)
+ /*
+ * We don't have support for "login -f", but we
+ * can fool /bin/login into thinking that we are
+ * rlogind, and allow us to log in without a
+ * password. The rlogin protocol expects
+ * local-user\0remote-user\0term/speed\0
+ */
+
+ if (pty > 2) {
+ register char *cp;
+ char speed[128];
+ int isecho, israw, xpty, len;
+ extern int def_rspeed;
+# ifndef LOGIN_HOST
+ /*
+ * Tell login that we are coming from "localhost".
+ * If we passed in the real host name, then the
+ * user would have to allow .rhost access from
+ * every machine that they want authenticated
+ * access to work from, which sort of defeats
+ * the purpose of an authenticated login...
+ * So, we tell login that the session is coming
+ * from "localhost", and the user will only have
+ * to have "localhost" in their .rhost file.
+ */
+# define LOGIN_HOST "localhost"
+# endif
+ argv = addarg(argv, "-r");
+ argv = addarg(argv, LOGIN_HOST);
+
+ xpty = pty;
+# ifndef STREAMSPTY
+ pty = 0;
+# else
+ ttyfd = 0;
+# endif
+ init_termbuf();
+ isecho = tty_isecho();
+ israw = tty_israw();
+ if (isecho || !israw) {
+ tty_setecho(0); /* Turn off echo */
+ tty_setraw(1); /* Turn on raw */
+ set_termbuf();
+ }
+ len = strlen(name)+1;
+ write(xpty, name, len);
+ write(xpty, name, len);
+ sprintf(speed, "%s/%d", (cp = getenv("TERM")) ? cp : "",
+ (def_rspeed > 0) ? def_rspeed : 9600);
+ len = strlen(speed)+1;
+ write(xpty, speed, len);
+
+ if (isecho || !israw) {
+ init_termbuf();
+ tty_setecho(isecho);
+ tty_setraw(israw);
+ set_termbuf();
+ if (!israw) {
+ /*
+ * Write a newline to ensure
+ * that login will be able to
+ * read the line...
+ */
+ write(xpty, "\n", 1);
+ }
+ }
+ pty = xpty;
+ }
+# else
+ argv = addarg(argv, name);
+# endif
+# endif
+ } else
+#endif
+ if (user = getenv("USER")) {
+ if (strchr(user, '-')) {
+ syslog(LOG_ERR, "tried to pass user \"%s\" to login",
+ user);
+ fatal(net, "invalid user");
+ }
+ argv = addarg(argv, getenv("USER"));
+#if defined(LOGIN_ARGS) && defined(NO_LOGIN_P)
+ {
+ register char **cpp;
+ for (cpp = environ; *cpp; cpp++)
+ argv = addarg(argv, *cpp);
+ }
+#endif
+ /*
+ * Assume that login will set the USER variable
+ * correctly. For SysV systems, this means that
+ * USER will no longer be set, just LOGNAME by
+ * login. (The problem is that if the auto-login
+ * fails, and the user then specifies a different
+ * account name, he can get logged in with both
+ * LOGNAME and USER in his environment, but the
+ * USER value will be wrong.
+ */
+ unsetenv("USER");
+ }
+#if defined(AUTHENTICATION) && defined(NO_LOGIN_F) && defined(LOGIN_R)
+ if (pty > 2)
+ close(pty);
+#endif
+ closelog();
+ execv(_PATH_LOGIN, argv);
+
+ syslog(LOG_ERR, "%s: %m\n", _PATH_LOGIN);
+ fatalperror(net, _PATH_LOGIN);
+ /*NOTREACHED*/
+}
+
+ char **
+addarg(argv, val)
+ register char **argv;
+ register char *val;
+{
+ register char **cpp;
+
+ if (argv == NULL) {
+ /*
+ * 10 entries, a leading length, and a null
+ */
+ argv = (char **)malloc(sizeof(*argv) * 12);
+ if (argv == NULL)
+ return(NULL);
+ *argv++ = (char *)10;
+ *argv = (char *)0;
+ }
+ for (cpp = argv; *cpp; cpp++)
+ ;
+ if (cpp == &argv[(int)argv[-1]]) {
+ --argv;
+ *argv = (char *)((int)(*argv) + 10);
+ argv = (char **)realloc(argv, (int)(*argv) + 2);
+ if (argv == NULL)
+ return(NULL);
+ argv++;
+ cpp = &argv[(int)argv[-1] - 10];
+ }
+ *cpp++ = val;
+ *cpp = 0;
+ return(argv);
+}
+#endif /* NEWINIT */
+
+/*
+ * scrub_env()
+ *
+ * Remove a few things from the environment that
+ * don't need to be there.
+ */
+scrub_env()
+{
+ register char **cpp, **cpp2;
+
+ for (cpp2 = cpp = environ; *cpp; cpp++) {
+#ifdef __FreeBSD__
+ if (strncmp(*cpp, "LD_LIBRARY_PATH=", 16) &&
+ strncmp(*cpp, "LD_PRELOAD=", 11) &&
+#else
+ if (strncmp(*cpp, "LD_", 3) &&
+ strncmp(*cpp, "_RLD_", 5) &&
+ strncmp(*cpp, "LIBPATH=", 8) &&
+#endif
+ strncmp(*cpp, "IFS=", 4))
+ *cpp2++ = *cpp;
+ }
+ *cpp2 = 0;
+}
+
+/*
+ * cleanup()
+ *
+ * This is the routine to call when we are all through, to
+ * clean up anything that needs to be cleaned up.
+ */
+ /* ARGSUSED */
+ void
+cleanup(sig)
+ int sig;
+{
+#ifndef PARENT_DOES_UTMP
+# if (BSD > 43) || defined(convex)
+ char *p;
+
+ p = line + sizeof("/dev/") - 1;
+ if (logout(p))
+ logwtmp(p, "", "");
+ (void)chmod(line, 0666);
+ (void)chown(line, 0, 0);
+ *p = 'p';
+ (void)chmod(line, 0666);
+ (void)chown(line, 0, 0);
+ (void) shutdown(net, 2);
+ exit(1);
+# else
+ void rmut();
+
+ rmut();
+ vhangup(); /* XXX */
+ (void) shutdown(net, 2);
+ exit(1);
+# endif
+#else /* PARENT_DOES_UTMP */
+# ifdef NEWINIT
+ (void) shutdown(net, 2);
+ exit(1);
+# else /* NEWINIT */
+# ifdef CRAY
+ static int incleanup = 0;
+ register int t;
+
+ /*
+ * 1: Pick up the zombie, if we are being called
+ * as the signal handler.
+ * 2: If we are a nested cleanup(), return.
+ * 3: Try to clean up TMPDIR.
+ * 4: Fill in utmp with shutdown of process.
+ * 5: Close down the network and pty connections.
+ * 6: Finish up the TMPDIR cleanup, if needed.
+ */
+ if (sig == SIGCHLD)
+ while (waitpid(-1, 0, WNOHANG) > 0)
+ ; /* VOID */
+ t = sigblock(sigmask(SIGCHLD));
+ if (incleanup) {
+ sigsetmask(t);
+ return;
+ }
+ incleanup = 1;
+ sigsetmask(t);
+ if (secflag) {
+ /*
+ * We need to set ourselves back to a null
+ * label to clean up.
+ */
+
+ setulvl(sysv.sy_minlvl);
+ setucmp((long)0);
+ }
+
+ t = cleantmp(&wtmp);
+ setutent(); /* just to make sure */
+# endif /* CRAY */
+ rmut(line);
+ close(pty);
+ (void) shutdown(net, 2);
+# ifdef CRAY
+ if (t == 0)
+ cleantmp(&wtmp);
+# endif /* CRAY */
+ exit(1);
+# endif /* NEWINT */
+#endif /* PARENT_DOES_UTMP */
+}
+
+#if defined(PARENT_DOES_UTMP) && !defined(NEWINIT)
+/*
+ * _utmp_sig_rcv
+ * utmp_sig_init
+ * utmp_sig_wait
+ * These three functions are used to coordinate the handling of
+ * the utmp file between the server and the soon-to-be-login shell.
+ * The server actually creates the utmp structure, the child calls
+ * utmp_sig_wait(), until the server calls utmp_sig_notify() and
+ * signals the future-login shell to proceed.
+ */
+static int caught=0; /* NZ when signal intercepted */
+static void (*func)(); /* address of previous handler */
+
+ void
+_utmp_sig_rcv(sig)
+ int sig;
+{
+ caught = 1;
+ (void) signal(SIGUSR1, func);
+}
+
+ void
+utmp_sig_init()
+{
+ /*
+ * register signal handler for UTMP creation
+ */
+ if ((int)(func = signal(SIGUSR1, _utmp_sig_rcv)) == -1)
+ fatalperror(net, "telnetd/signal");
+}
+
+ void
+utmp_sig_reset()
+{
+ (void) signal(SIGUSR1, func); /* reset handler to default */
+}
+
+# ifdef __hpux
+# define sigoff() /* do nothing */
+# define sigon() /* do nothing */
+# endif
+
+ void
+utmp_sig_wait()
+{
+ /*
+ * Wait for parent to write our utmp entry.
+ */
+ sigoff();
+ while (caught == 0) {
+ pause(); /* wait until we get a signal (sigon) */
+ sigoff(); /* turn off signals while we check caught */
+ }
+ sigon(); /* turn on signals again */
+}
+
+ void
+utmp_sig_notify(pid)
+{
+ kill(pid, SIGUSR1);
+}
+
+# ifdef CRAY
+static int gotsigjob = 0;
+
+ /*ARGSUSED*/
+ void
+sigjob(sig)
+ int sig;
+{
+ register int jid;
+ register struct jobtemp *jp;
+
+ while ((jid = waitjob(NULL)) != -1) {
+ if (jid == 0) {
+ return;
+ }
+ gotsigjob++;
+ jobend(jid, NULL, NULL);
+ }
+}
+
+/*
+ * Clean up the TMPDIR that login created.
+ * The first time this is called we pick up the info
+ * from the utmp. If the job has already gone away,
+ * then we'll clean up and be done. If not, then
+ * when this is called the second time it will wait
+ * for the signal that the job is done.
+ */
+ int
+cleantmp(wtp)
+ register struct utmp *wtp;
+{
+ struct utmp *utp;
+ static int first = 1;
+ register int mask, omask, ret;
+ extern struct utmp *getutid P((const struct utmp *_Id));
+
+
+ mask = sigmask(WJSIGNAL);
+
+ if (first == 0) {
+ omask = sigblock(mask);
+ while (gotsigjob == 0)
+ sigpause(omask);
+ return(1);
+ }
+ first = 0;
+ setutent(); /* just to make sure */
+
+ utp = getutid(wtp);
+ if (utp == 0) {
+ syslog(LOG_ERR, "Can't get /etc/utmp entry to clean TMPDIR");
+ return(-1);
+ }
+ /*
+ * Nothing to clean up if the user shell was never started.
+ */
+ if (utp->ut_type != USER_PROCESS || utp->ut_jid == 0)
+ return(1);
+
+ /*
+ * Block the WJSIGNAL while we are in jobend().
+ */
+ omask = sigblock(mask);
+ ret = jobend(utp->ut_jid, utp->ut_tpath, utp->ut_user);
+ sigsetmask(omask);
+ return(ret);
+}
+
+ int
+jobend(jid, path, user)
+ register int jid;
+ register char *path;
+ register char *user;
+{
+ static int saved_jid = 0;
+ static char saved_path[sizeof(wtmp.ut_tpath)+1];
+ static char saved_user[sizeof(wtmp.ut_user)+1];
+
+ if (path) {
+ strncpy(saved_path, path, sizeof(wtmp.ut_tpath));
+ strncpy(saved_user, user, sizeof(wtmp.ut_user));
+ saved_path[sizeof(saved_path)] = '\0';
+ saved_user[sizeof(saved_user)] = '\0';
+ }
+ if (saved_jid == 0) {
+ saved_jid = jid;
+ return(0);
+ }
+ cleantmpdir(jid, saved_path, saved_user);
+ return(1);
+}
+
+/*
+ * Fork a child process to clean up the TMPDIR
+ */
+cleantmpdir(jid, tpath, user)
+ register int jid;
+ register char *tpath;
+ register char *user;
+{
+ switch(fork()) {
+ case -1:
+ syslog(LOG_ERR, "TMPDIR cleanup(%s): fork() failed: %m\n",
+ tpath);
+ break;
+ case 0:
+ execl(CLEANTMPCMD, CLEANTMPCMD, user, tpath, 0);
+ syslog(LOG_ERR, "TMPDIR cleanup(%s): execl(%s) failed: %m\n",
+ tpath, CLEANTMPCMD);
+ exit(1);
+ default:
+ /*
+ * Forget about child. We will exit, and
+ * /etc/init will pick it up.
+ */
+ break;
+ }
+}
+# endif /* CRAY */
+#endif /* defined(PARENT_DOES_UTMP) && !defined(NEWINIT) */
+
+/*
+ * rmut()
+ *
+ * This is the function called by cleanup() to
+ * remove the utmp entry for this person.
+ */
+
+#ifdef UTMPX
+ void
+rmut()
+{
+ register f;
+ int found = 0;
+ struct utmp *u, *utmp;
+ int nutmp;
+ struct stat statbf;
+
+ struct utmpx *utxp, utmpx;
+
+ /*
+ * This updates the utmpx and utmp entries and make a wtmp/x entry
+ */
+
+ SCPYN(utmpx.ut_line, line + sizeof("/dev/") - 1);
+ utxp = getutxline(&utmpx);
+ if (utxp) {
+ utxp->ut_type = DEAD_PROCESS;
+ utxp->ut_exit.e_termination = 0;
+ utxp->ut_exit.e_exit = 0;
+ (void) time(&utmpx.ut_tv.tv_sec);
+ utmpx.ut_tv.tv_usec = 0;
+ modutx(utxp);
+ }
+ endutxent();
+} /* end of rmut */
+#endif
+
+#if !defined(UTMPX) && !(defined(CRAY) || defined(__hpux)) && BSD <= 43
+ void
+rmut()
+{
+ register f;
+ int found = 0;
+ struct utmp *u, *utmp;
+ int nutmp;
+ struct stat statbf;
+
+ f = open(utmpf, O_RDWR);
+ if (f >= 0) {
+ (void) fstat(f, &statbf);
+ utmp = (struct utmp *)malloc((unsigned)statbf.st_size);
+ if (!utmp)
+ syslog(LOG_ERR, "utmp malloc failed");
+ if (statbf.st_size && utmp) {
+ nutmp = read(f, (char *)utmp, (int)statbf.st_size);
+ nutmp /= sizeof(struct utmp);
+
+ for (u = utmp ; u < &utmp[nutmp] ; u++) {
+ if (SCMPN(u->ut_line, line+5) ||
+ u->ut_name[0]==0)
+ continue;
+ (void) lseek(f, ((long)u)-((long)utmp), L_SET);
+ SCPYN(u->ut_name, "");
+ SCPYN(u->ut_host, "");
+ (void) time(&u->ut_time);
+ (void) write(f, (char *)u, sizeof(wtmp));
+ found++;
+ }
+ }
+ (void) close(f);
+ }
+ if (found) {
+ f = open(wtmpf, O_WRONLY|O_APPEND);
+ if (f >= 0) {
+ SCPYN(wtmp.ut_line, line+5);
+ SCPYN(wtmp.ut_name, "");
+ SCPYN(wtmp.ut_host, "");
+ (void) time(&wtmp.ut_time);
+ (void) write(f, (char *)&wtmp, sizeof(wtmp));
+ (void) close(f);
+ }
+ }
+ (void) chmod(line, 0666);
+ (void) chown(line, 0, 0);
+ line[strlen("/dev/")] = 'p';
+ (void) chmod(line, 0666);
+ (void) chown(line, 0, 0);
+} /* end of rmut */
+#endif /* CRAY */
+
+#ifdef __hpux
+rmut (line)
+char *line;
+{
+ struct utmp utmp;
+ struct utmp *utptr;
+ int fd; /* for /etc/wtmp */
+
+ utmp.ut_type = USER_PROCESS;
+ (void) strncpy(utmp.ut_id, line+12, sizeof(utmp.ut_id));
+ (void) setutent();
+ utptr = getutid(&utmp);
+ /* write it out only if it exists */
+ if (utptr) {
+ utptr->ut_type = DEAD_PROCESS;
+ utptr->ut_time = time((long *) 0);
+ (void) pututline(utptr);
+ /* set wtmp entry if wtmp file exists */
+ if ((fd = open(wtmpf, O_WRONLY | O_APPEND)) >= 0) {
+ (void) write(fd, utptr, sizeof(utmp));
+ (void) close(fd);
+ }
+ }
+ (void) endutent();
+
+ (void) chmod(line, 0666);
+ (void) chown(line, 0, 0);
+ line[14] = line[13];
+ line[13] = line[12];
+ line[8] = 'm';
+ line[9] = '/';
+ line[10] = 'p';
+ line[11] = 't';
+ line[12] = 'y';
+ (void) chmod(line, 0666);
+ (void) chown(line, 0, 0);
+}
+#endif
diff --git a/libexec/telnetd/telnetd.8 b/libexec/telnetd/telnetd.8
new file mode 100644
index 0000000..fee5526
--- /dev/null
+++ b/libexec/telnetd/telnetd.8
@@ -0,0 +1,605 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)telnetd.8 8.3 (Berkeley) 3/1/94
+.\"
+.Dd March 1, 1994
+.Dt TELNETD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm telnetd
+.Nd DARPA
+.Tn TELNET
+protocol server
+.Sh SYNOPSIS
+.Nm /usr/libexec/telnetd
+.Op Fl BUhlkns
+.Op Fl D Ar debugmode
+.Op Fl I Ns Ar initid
+.Op Fl S Ar tos
+.Op Fl X Ar authtype
+.Op Fl a Ar authmode
+.Op Fl edebug
+.Op Fl r Ns Ar lowpty-highpty
+.Op Fl u Ar len
+.Op Fl debug Op Ar port
+.Sh DESCRIPTION
+The
+.Nm telnetd
+command is a server which supports the
+.Tn DARPA
+standard
+.Tn TELNET
+virtual terminal protocol.
+.Nm Telnetd
+is normally invoked by the internet server (see
+.Xr inetd 8 )
+for requests to connect to the
+.Tn TELNET
+port as indicated by the
+.Pa /etc/services
+file (see
+.Xr services 5 ) .
+The
+.Fl debug
+option may be used to start up
+.Nm telnetd
+manually, instead of through
+.Xr inetd 8 .
+If started up this way,
+.Ar port
+may be specified to run
+.Nm telnetd
+on an alternate
+.Tn TCP
+port number.
+.Pp
+The
+.Nm telnetd
+command accepts the following options:
+.Bl -tag -width "-a authmode"
+.It Fl a Ar authmode
+This option may be used for specifying what mode should
+be used for authentication.
+Note that this option is only useful if
+.Nm telnetd
+has been compiled with support for the
+.Dv AUTHENTICATION
+option.
+There are several valid values for
+.Ar authmode:
+.Bl -tag -width debug
+.It debug
+Turns on authentication debugging code.
+.It user
+Only allow connections when the remote user
+can provide valid authentication information
+to identify the remote user,
+and is allowed access to the specified account
+without providing a password.
+.It valid
+Only allow connections when the remote user
+can provide valid authentication information
+to identify the remote user.
+The
+.Xr login 1
+command will provide any additional user verification
+needed if the remote user is not allowed automatic
+access to the specified account.
+.It other
+Only allow connections that supply some authentication information.
+This option is currently not supported
+by any of the existing authentication mechanisms,
+and is thus the same as specifying
+.Fl a
+.Cm valid .
+.It none
+This is the default state.
+Authentication information is not required.
+If no or insufficient authentication information
+is provided, then the
+.Xr login 1
+program will provide the necessary user
+verification.
+.It off
+This disables the authentication code.
+All user verification will happen through the
+.Xr login 1
+program.
+.El
+.It Fl B
+Specifies bftp server mode. In this mode,
+.Nm telnetd
+causes login to start a
+.Xr bftp 1
+session rather than the user's
+normal shell. In bftp daemon mode normal
+logins are not supported, and it must be used
+on a port other than the normal
+.Tn TELNET
+port.
+.It Fl D Ar debugmode
+This option may be used for debugging purposes.
+This allows
+.Nm telnetd
+to print out debugging information
+to the connection, allowing the user to see what
+.Nm telnetd
+is doing.
+There are several possible values for
+.Ar debugmode:
+.Bl -tag -width exercise
+.It Cm options
+Prints information about the negotiation of
+.Tn TELNET
+options.
+.It Cm report
+Prints the
+.Cm options
+information, plus some additional information
+about what processing is going on.
+.It Cm netdata
+Displays the data stream received by
+.Nm telnetd.
+.It Cm ptydata
+Displays data written to the pty.
+.It Cm exercise
+Has not been implemented yet.
+.El
+.It Fl debug
+Enables debugging on each socket created by
+.Nm telnetd
+(see
+.Dv SO_DEBUG
+in
+.Xr socket 2 ) .
+.It Fl edebug
+If
+.Nm telnetd
+has been compiled with support for data encryption, then the
+.Fl edebug
+option may be used to enable encryption debugging code.
+.It Fl h
+Disables the printing of host-specific information before
+login has been completed.
+.It Fl I Ar initid
+This option is only applicable to
+.Tn UNICOS
+systems prior to 7.0.
+It specifies the
+.Dv ID
+from
+.Pa /etc/inittab
+to use when init starts login sessions. The default
+.Dv ID
+is
+.Dv fe.
+.It Fl k
+This option is only useful if
+.Nm telnetd
+has been compiled with both linemode and kludge linemode
+support. If the
+.Fl k
+option is specified, then if the remote client does not
+support the
+.Dv LINEMODE
+option, then
+.Nm telnetd
+will operate in character at a time mode.
+It will still support kludge linemode, but will only
+go into kludge linemode if the remote client requests
+it.
+(This is done by by the client sending
+.Dv DONT SUPPRESS-GO-AHEAD
+and
+.Dv DONT ECHO . )
+The
+.Fl k
+option is most useful when there are remote clients
+that do not support kludge linemode, but pass the heuristic
+(if they respond with
+.Dv WILL TIMING-MARK
+in response to a
+.Dv DO TIMING-MARK)
+for kludge linemode support.
+.It Fl l
+Specifies line mode. Tries to force clients to use line-
+at-a-time mode.
+If the
+.Dv LINEMODE
+option is not supported, it will go
+into kludge linemode.
+.It Fl n
+Disable
+.Dv TCP
+keep-alives. Normally
+.Nm telnetd
+enables the
+.Tn TCP
+keep-alive mechanism to probe connections that
+have been idle for some period of time to determine
+if the client is still there, so that idle connections
+from machines that have crashed or can no longer
+be reached may be cleaned up.
+.It Fl r Ar lowpty-highpty
+This option is only enabled when
+.Nm telnetd
+is compiled for
+.Dv UNICOS.
+It specifies an inclusive range of pseudo-terminal devices to
+use. If the system has sysconf variable
+.Dv _SC_CRAY_NPTY
+configured, the default pty search range is 0 to
+.Dv _SC_CRAY_NPTY;
+otherwise, the default range is 0 to 128. Either
+.Ar lowpty
+or
+.Ar highpty
+may be omitted to allow changing
+either end of the search range. If
+.Ar lowpty
+is omitted, the - character is still required so that
+.Nm telnetd
+can differentiate
+.Ar highpty
+from
+.Ar lowpty .
+.It Fl s
+This option is only enabled if
+.Nm telnetd
+is compiled with support for
+.Tn SecurID
+cards.
+It causes the
+.Fl s
+option to be passed on to
+.Xr login 1 ,
+and thus is only useful if
+.Xr login 1
+supports the
+.Fl s
+flag to indicate that only
+.Tn SecurID
+validated logins are allowed, and is
+usually useful for controlling remote logins
+from outside of a firewall.
+.It Fl S Ar tos
+.It Fl u Ar len
+This option is used to specify the size of the field
+in the
+.Dv utmp
+structure that holds the remote host name.
+If the resolved host name is longer than
+.Ar len ,
+the dotted decimal value will be used instead.
+This allows hosts with very long host names that
+overflow this field to still be uniquely identified.
+Specifying
+.Fl u0
+indicates that only dotted decimal addresses
+should be put into the
+.Pa utmp
+file.
+.It Fl U
+This option causes
+.Nm telnetd
+to refuse connections from addresses that
+cannot be mapped back into a symbolic name
+via the
+.Xr gethostbyaddr 3
+routine.
+.It Fl X Ar authtype
+This option is only valid if
+.Nm telnetd
+has been built with support for the authentication option.
+It disables the use of
+.Ar authtype
+authentication, and
+can be used to temporarily disable
+a specific authentication type without having to recompile
+.Nm telnetd .
+.El
+.Pp
+.Nm Telnetd
+operates by allocating a pseudo-terminal device (see
+.Xr pty 4 )
+for a client, then creating a login process which has
+the slave side of the pseudo-terminal as
+.Dv stdin ,
+.Dv stdout
+and
+.Dv stderr .
+.Nm Telnetd
+manipulates the master side of the pseudo-terminal,
+implementing the
+.Tn TELNET
+protocol and passing characters
+between the remote client and the login process.
+.Pp
+When a
+.Tn TELNET
+session is started up,
+.Nm telnetd
+sends
+.Tn TELNET
+options to the client side indicating
+a willingness to do the
+following
+.Tn TELNET
+options, which are described in more detail below:
+.Bd -literal -offset indent
+DO AUTHENTICATION
+WILL ENCRYPT
+DO TERMINAL TYPE
+DO TSPEED
+DO XDISPLOC
+DO NEW-ENVIRON
+DO ENVIRON
+WILL SUPPRESS GO AHEAD
+DO ECHO
+DO LINEMODE
+DO NAWS
+WILL STATUS
+DO LFLOW
+DO TIMING-MARK
+.Ed
+.Pp
+The pseudo-terminal allocated to the client is configured
+to operate in \*(lqcooked\*(rq mode, and with
+.Dv XTABS and
+.Dv CRMOD
+enabled (see
+.Xr tty 4 ) .
+.Pp
+.Nm Telnetd
+has support for enabling locally the following
+.Tn TELNET
+options:
+.Bl -tag -width "DO AUTHENTICATION"
+.It "WILL ECHO"
+When the
+.Dv LINEMODE
+option is enabled, a
+.Dv WILL ECHO
+or
+.Dv WONT ECHO
+will be sent to the client to indicate the
+current state of terminal echoing.
+When terminal echo is not desired, a
+.Dv WILL ECHO
+is sent to indicate that
+.Tn telnetd
+will take care of echoing any data that needs to be
+echoed to the terminal, and then nothing is echoed.
+When terminal echo is desired, a
+.Dv WONT ECHO
+is sent to indicate that
+.Tn telnetd
+will not be doing any terminal echoing, so the
+client should do any terminal echoing that is needed.
+.It "WILL BINARY"
+Indicates that the client is willing to send a
+8 bits of data, rather than the normal 7 bits
+of the Network Virtual Terminal.
+.It "WILL SGA"
+Indicates that it will not be sending
+.Dv IAC GA,
+go ahead, commands.
+.It "WILL STATUS"
+Indicates a willingness to send the client, upon
+request, of the current status of all
+.Tn TELNET
+options.
+.It "WILL TIMING-MARK"
+Whenever a
+.Dv DO TIMING-MARK
+command is received, it is always responded
+to with a
+.Dv WILL TIMING-MARK
+.It "WILL LOGOUT"
+When a
+.Dv DO LOGOUT
+is received, a
+.Dv WILL LOGOUT
+is sent in response, and the
+.Tn TELNET
+session is shut down.
+.It "WILL ENCRYPT"
+Only sent if
+.Nm telnetd
+is compiled with support for data encryption, and
+indicates a willingness to decrypt
+the data stream.
+.El
+.Pp
+.Nm Telnetd
+has support for enabling remotely the following
+.Tn TELNET
+options:
+.Bl -tag -width "DO AUTHENTICATION"
+.It "DO BINARY"
+Sent to indicate that
+.Tn telnetd
+is willing to receive an 8 bit data stream.
+.It "DO LFLOW"
+Requests that the client handle flow control
+characters remotely.
+.It "DO ECHO"
+This is not really supported, but is sent to identify a 4.2BSD
+.Xr telnet 1
+client, which will improperly respond with
+.Dv WILL ECHO.
+If a
+.Dv WILL ECHO
+is received, a
+.Dv DONT ECHO
+will be sent in response.
+.It "DO TERMINAL-TYPE"
+Indicates a desire to be able to request the
+name of the type of terminal that is attached
+to the client side of the connection.
+.It "DO SGA"
+Indicates that it does not need to receive
+.Dv IAC GA,
+the go ahead command.
+.It "DO NAWS"
+Requests that the client inform the server when
+the window (display) size changes.
+.It "DO TERMINAL-SPEED"
+Indicates a desire to be able to request information
+about the speed of the serial line to which
+the client is attached.
+.It "DO XDISPLOC"
+Indicates a desire to be able to request the name
+of the X windows display that is associated with
+the telnet client.
+.It "DO NEW-ENVIRON"
+Indicates a desire to be able to request environment
+variable information, as described in RFC 1572.
+.It "DO ENVIRON"
+Indicates a desire to be able to request environment
+variable information, as described in RFC 1408.
+.It "DO LINEMODE"
+Only sent if
+.Nm telnetd
+is compiled with support for linemode, and
+requests that the client do line by line processing.
+.It "DO TIMING-MARK"
+Only sent if
+.Nm telnetd
+is compiled with support for both linemode and
+kludge linemode, and the client responded with
+.Dv WONT LINEMODE.
+If the client responds with
+.Dv WILL TM,
+the it is assumed that the client supports
+kludge linemode.
+Note that the
+.Op Fl k
+option can be used to disable this.
+.It "DO AUTHENTICATION"
+Only sent if
+.Nm telnetd
+is compiled with support for authentication, and
+indicates a willingness to receive authentication
+information for automatic login.
+.It "DO ENCRYPT"
+Only sent if
+.Nm telnetd
+is compiled with support for data encryption, and
+indicates a willingness to decrypt
+the data stream.
+.Sh ENVIRONMENT
+.Sh FILES
+.Pa /etc/services
+.br
+.Pa /etc/inittab
+(UNICOS systems only)
+.br
+.Pa /etc/iptos
+(if supported)
+.br
+.Pa /usr/ucb/bftp
+(if supported)
+.Sh "SEE ALSO"
+.Xr telnet 1 ,
+.Xr login 1 ,
+.Xr bftp 1
+(if supported)
+.Sh STANDARDS
+.Bl -tag -compact -width RFC-1572
+.It Cm RFC-854
+.Tn TELNET
+PROTOCOL SPECIFICATION
+.It Cm RFC-855
+TELNET OPTION SPECIFICATIONS
+.It Cm RFC-856
+TELNET BINARY TRANSMISSION
+.It Cm RFC-857
+TELNET ECHO OPTION
+.It Cm RFC-858
+TELNET SUPPRESS GO AHEAD OPTION
+.It Cm RFC-859
+TELNET STATUS OPTION
+.It Cm RFC-860
+TELNET TIMING MARK OPTION
+.It Cm RFC-861
+TELNET EXTENDED OPTIONS - LIST OPTION
+.It Cm RFC-885
+TELNET END OF RECORD OPTION
+.It Cm RFC-1073
+Telnet Window Size Option
+.It Cm RFC-1079
+Telnet Terminal Speed Option
+.It Cm RFC-1091
+Telnet Terminal-Type Option
+.It Cm RFC-1096
+Telnet X Display Location Option
+.It Cm RFC-1123
+Requirements for Internet Hosts -- Application and Support
+.It Cm RFC-1184
+Telnet Linemode Option
+.It Cm RFC-1372
+Telnet Remote Flow Control Option
+.It Cm RFC-1416
+Telnet Authentication Option
+.It Cm RFC-1411
+Telnet Authentication: Kerberos Version 4
+.It Cm RFC-1412
+Telnet Authentication: SPX
+.It Cm RFC-1571
+Telnet Environment Option Interoperability Issues
+.It Cm RFC-1572
+Telnet Environment Option
+.Sh BUGS
+Some
+.Tn TELNET
+commands are only partially implemented.
+.Pp
+Because of bugs in the original 4.2 BSD
+.Xr telnet 1 ,
+.Nm telnetd
+performs some dubious protocol exchanges to try to discover if the remote
+client is, in fact, a 4.2 BSD
+.Xr telnet 1 .
+.Pp
+Binary mode
+has no common interpretation except between similar operating systems
+(Unix in this case).
+.Pp
+The terminal type name received from the remote client is converted to
+lower case.
+.Pp
+.Nm Telnetd
+never sends
+.Tn TELNET
+.Dv IAC GA
+(go ahead) commands.
diff --git a/libexec/telnetd/telnetd.c b/libexec/telnetd/telnetd.c
new file mode 100644
index 0000000..b83b6a0
--- /dev/null
+++ b/libexec/telnetd/telnetd.c
@@ -0,0 +1,1535 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)telnetd.c 8.2 (Berkeley) 12/15/93";
+#endif /* not lint */
+
+#include "telnetd.h"
+#include "pathnames.h"
+
+#if defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY)
+/*
+ * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can
+ * use it to tell us to turn off all the socket security code,
+ * since that is only used in UNICOS 7.0 and later.
+ */
+# undef _SC_CRAY_SECURE_SYS
+#endif
+
+#if defined(_SC_CRAY_SECURE_SYS)
+#include <sys/sysv.h>
+#include <sys/secdev.h>
+# ifdef SO_SEC_MULTI /* 8.0 code */
+#include <sys/secparm.h>
+#include <sys/usrv.h>
+# endif /* SO_SEC_MULTI */
+int secflag;
+char tty_dev[16];
+struct secdev dv;
+struct sysv sysv;
+# ifdef SO_SEC_MULTI /* 8.0 code */
+struct socksec ss;
+# else /* SO_SEC_MULTI */ /* 7.0 code */
+struct socket_security ss;
+# endif /* SO_SEC_MULTI */
+#endif /* _SC_CRAY_SECURE_SYS */
+
+#if defined(AUTHENTICATION)
+#include <libtelnet/auth.h>
+int auth_level = 0;
+#endif
+#if defined(SecurID)
+int require_SecurID = 0;
+#endif
+
+extern int utmp_len;
+int registerd_host_only = 0;
+
+#ifdef STREAMSPTY
+# include <stropts.h>
+# include <termio.h>
+/* make sure we don't get the bsd version */
+# include "/usr/include/sys/tty.h"
+# include <sys/ptyvar.h>
+
+/*
+ * Because of the way ptyibuf is used with streams messages, we need
+ * ptyibuf+1 to be on a full-word boundary. The following wierdness
+ * is simply to make that happen.
+ */
+long ptyibufbuf[BUFSIZ/sizeof(long)+1];
+char *ptyibuf = ((char *)&ptyibufbuf[1])-1;
+char *ptyip = ((char *)&ptyibufbuf[1])-1;
+char ptyibuf2[BUFSIZ];
+unsigned char ctlbuf[BUFSIZ];
+struct strbuf strbufc, strbufd;
+
+int readstream();
+
+#else /* ! STREAMPTY */
+
+/*
+ * I/O data buffers,
+ * pointers, and counters.
+ */
+char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
+char ptyibuf2[BUFSIZ];
+
+#endif /* ! STREAMPTY */
+
+int hostinfo = 1; /* do we print login banner? */
+
+#ifdef CRAY
+extern int newmap; /* nonzero if \n maps to ^M^J */
+int lowpty = 0, highpty; /* low, high pty numbers */
+#endif /* CRAY */
+
+int debug = 0;
+int keepalive = 1;
+char *progname;
+
+extern void usage P((void));
+
+/*
+ * The string to pass to getopt(). We do it this way so
+ * that only the actual options that we support will be
+ * passed off to getopt().
+ */
+char valid_opts[] = {
+ 'd', ':', 'h', 'k', 'n', 'S', ':', 'u', ':', 'U',
+#ifdef AUTHENTICATION
+ 'a', ':', 'X', ':',
+#endif
+#ifdef BFTPDAEMON
+ 'B',
+#endif
+#ifdef DIAGNOSTICS
+ 'D', ':',
+#endif
+#if defined(CRAY) && defined(NEWINIT)
+ 'I', ':',
+#endif
+#ifdef LINEMODE
+ 'l',
+#endif
+#ifdef CRAY
+ 'r', ':',
+#endif
+#ifdef SecurID
+ 's',
+#endif
+ '\0'
+};
+
+main(argc, argv)
+ char *argv[];
+{
+ struct sockaddr_in from;
+ int on = 1, fromlen;
+ register int ch;
+ extern char *optarg;
+ extern int optind;
+#if defined(IPPROTO_IP) && defined(IP_TOS)
+ int tos = -1;
+#endif
+
+ pfrontp = pbackp = ptyobuf;
+ netip = netibuf;
+ nfrontp = nbackp = netobuf;
+
+ progname = *argv;
+
+#ifdef CRAY
+ /*
+ * Get number of pty's before trying to process options,
+ * which may include changing pty range.
+ */
+ highpty = getnpty();
+#endif /* CRAY */
+
+ while ((ch = getopt(argc, argv, valid_opts)) != EOF) {
+ switch(ch) {
+
+#ifdef AUTHENTICATION
+ case 'a':
+ /*
+ * Check for required authentication level
+ */
+ if (strcmp(optarg, "debug") == 0) {
+ extern int auth_debug_mode;
+ auth_debug_mode = 1;
+ } else if (strcasecmp(optarg, "none") == 0) {
+ auth_level = 0;
+ } else if (strcasecmp(optarg, "other") == 0) {
+ auth_level = AUTH_OTHER;
+ } else if (strcasecmp(optarg, "user") == 0) {
+ auth_level = AUTH_USER;
+ } else if (strcasecmp(optarg, "valid") == 0) {
+ auth_level = AUTH_VALID;
+ } else if (strcasecmp(optarg, "off") == 0) {
+ /*
+ * This hack turns off authentication
+ */
+ auth_level = -1;
+ } else {
+ fprintf(stderr,
+ "telnetd: unknown authorization level for -a\n");
+ }
+ break;
+#endif /* AUTHENTICATION */
+
+#ifdef BFTPDAEMON
+ case 'B':
+ bftpd++;
+ break;
+#endif /* BFTPDAEMON */
+
+ case 'd':
+ if (strcmp(optarg, "ebug") == 0) {
+ debug++;
+ break;
+ }
+ usage();
+ /* NOTREACHED */
+ break;
+
+#ifdef DIAGNOSTICS
+ case 'D':
+ /*
+ * Check for desired diagnostics capabilities.
+ */
+ if (!strcmp(optarg, "report")) {
+ diagnostic |= TD_REPORT|TD_OPTIONS;
+ } else if (!strcmp(optarg, "exercise")) {
+ diagnostic |= TD_EXERCISE;
+ } else if (!strcmp(optarg, "netdata")) {
+ diagnostic |= TD_NETDATA;
+ } else if (!strcmp(optarg, "ptydata")) {
+ diagnostic |= TD_PTYDATA;
+ } else if (!strcmp(optarg, "options")) {
+ diagnostic |= TD_OPTIONS;
+ } else {
+ usage();
+ /* NOT REACHED */
+ }
+ break;
+#endif /* DIAGNOSTICS */
+
+
+ case 'h':
+ hostinfo = 0;
+ break;
+
+#if defined(CRAY) && defined(NEWINIT)
+ case 'I':
+ {
+ extern char *gen_id;
+ gen_id = optarg;
+ break;
+ }
+#endif /* defined(CRAY) && defined(NEWINIT) */
+
+#ifdef LINEMODE
+ case 'l':
+ alwayslinemode = 1;
+ break;
+#endif /* LINEMODE */
+
+ case 'k':
+#if defined(LINEMODE) && defined(KLUDGELINEMODE)
+ lmodetype = NO_AUTOKLUDGE;
+#else
+ /* ignore -k option if built without kludge linemode */
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+ break;
+
+ case 'n':
+ keepalive = 0;
+ break;
+
+#ifdef CRAY
+ case 'r':
+ {
+ char *strchr();
+ char *c;
+
+ /*
+ * Allow the specification of alterations
+ * to the pty search range. It is legal to
+ * specify only one, and not change the
+ * other from its default.
+ */
+ c = strchr(optarg, '-');
+ if (c) {
+ *c++ = '\0';
+ highpty = atoi(c);
+ }
+ if (*optarg != '\0')
+ lowpty = atoi(optarg);
+ if ((lowpty > highpty) || (lowpty < 0) ||
+ (highpty > 32767)) {
+ usage();
+ /* NOT REACHED */
+ }
+ break;
+ }
+#endif /* CRAY */
+
+#ifdef SecurID
+ case 's':
+ /* SecurID required */
+ require_SecurID = 1;
+ break;
+#endif /* SecurID */
+ case 'S':
+#ifdef HAS_GETTOS
+ if ((tos = parsetos(optarg, "tcp")) < 0)
+ fprintf(stderr, "%s%s%s\n",
+ "telnetd: Bad TOS argument '", optarg,
+ "'; will try to use default TOS");
+#else
+ fprintf(stderr, "%s%s\n", "TOS option unavailable; ",
+ "-S flag not supported\n");
+#endif
+ break;
+
+ case 'u':
+ utmp_len = atoi(optarg);
+ break;
+
+ case 'U':
+ registerd_host_only = 1;
+ break;
+
+#ifdef AUTHENTICATION
+ case 'X':
+ /*
+ * Check for invalid authentication types
+ */
+ auth_disable_name(optarg);
+ break;
+#endif /* AUTHENTICATION */
+
+ default:
+ fprintf(stderr, "telnetd: %c: unknown option\n", ch);
+ /* FALLTHROUGH */
+ case '?':
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (debug) {
+ int s, ns, foo;
+ struct servent *sp;
+ static struct sockaddr_in sin = { AF_INET };
+
+ if (argc > 1) {
+ usage();
+ /* NOT REACHED */
+ } else if (argc == 1) {
+ if (sp = getservbyname(*argv, "tcp")) {
+ sin.sin_port = sp->s_port;
+ } else {
+ sin.sin_port = atoi(*argv);
+ if ((int)sin.sin_port <= 0) {
+ fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
+ usage();
+ /* NOT REACHED */
+ }
+ sin.sin_port = htons((u_short)sin.sin_port);
+ }
+ } else {
+ sp = getservbyname("telnet", "tcp");
+ if (sp == 0) {
+ fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
+ exit(1);
+ }
+ sin.sin_port = sp->s_port;
+ }
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("telnetd: socket");;
+ exit(1);
+ }
+ (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof(on));
+ if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
+ perror("bind");
+ exit(1);
+ }
+ if (listen(s, 1) < 0) {
+ perror("listen");
+ exit(1);
+ }
+ foo = sizeof sin;
+ ns = accept(s, (struct sockaddr *)&sin, &foo);
+ if (ns < 0) {
+ perror("accept");
+ exit(1);
+ }
+ (void) dup2(ns, 0);
+ (void) close(ns);
+ (void) close(s);
+#ifdef convex
+ } else if (argc == 1) {
+ ; /* VOID*/ /* Just ignore the host/port name */
+#endif
+ } else if (argc > 0) {
+ usage();
+ /* NOT REACHED */
+ }
+
+#if defined(_SC_CRAY_SECURE_SYS)
+ secflag = sysconf(_SC_CRAY_SECURE_SYS);
+
+ /*
+ * Get socket's security label
+ */
+ if (secflag) {
+ int szss = sizeof(ss);
+#ifdef SO_SEC_MULTI /* 8.0 code */
+ int sock_multi;
+ int szi = sizeof(int);
+#endif /* SO_SEC_MULTI */
+
+ bzero((char *)&dv, sizeof(dv));
+
+ if (getsysv(&sysv, sizeof(struct sysv)) != 0) {
+ perror("getsysv");
+ exit(1);
+ }
+
+ /*
+ * Get socket security label and set device values
+ * {security label to be set on ttyp device}
+ */
+#ifdef SO_SEC_MULTI /* 8.0 code */
+ if ((getsockopt(0, SOL_SOCKET, SO_SECURITY,
+ (char *)&ss, &szss) < 0) ||
+ (getsockopt(0, SOL_SOCKET, SO_SEC_MULTI,
+ (char *)&sock_multi, &szi) < 0)) {
+ perror("getsockopt");
+ exit(1);
+ } else {
+ dv.dv_actlvl = ss.ss_actlabel.lt_level;
+ dv.dv_actcmp = ss.ss_actlabel.lt_compart;
+ if (!sock_multi) {
+ dv.dv_minlvl = dv.dv_maxlvl = dv.dv_actlvl;
+ dv.dv_valcmp = dv.dv_actcmp;
+ } else {
+ dv.dv_minlvl = ss.ss_minlabel.lt_level;
+ dv.dv_maxlvl = ss.ss_maxlabel.lt_level;
+ dv.dv_valcmp = ss.ss_maxlabel.lt_compart;
+ }
+ dv.dv_devflg = 0;
+ }
+#else /* SO_SEC_MULTI */ /* 7.0 code */
+ if (getsockopt(0, SOL_SOCKET, SO_SECURITY,
+ (char *)&ss, &szss) >= 0) {
+ dv.dv_actlvl = ss.ss_slevel;
+ dv.dv_actcmp = ss.ss_compart;
+ dv.dv_minlvl = ss.ss_minlvl;
+ dv.dv_maxlvl = ss.ss_maxlvl;
+ dv.dv_valcmp = ss.ss_maxcmp;
+ }
+#endif /* SO_SEC_MULTI */
+ }
+#endif /* _SC_CRAY_SECURE_SYS */
+
+ openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
+ fromlen = sizeof (from);
+ if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ perror("getpeername");
+ _exit(1);
+ }
+ if (keepalive &&
+ setsockopt(0, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&on, sizeof (on)) < 0) {
+ syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+ }
+
+#if defined(IPPROTO_IP) && defined(IP_TOS)
+ {
+# if defined(HAS_GETTOS)
+ struct tosent *tp;
+ if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))
+ tos = tp->t_tos;
+# endif
+ if (tos < 0)
+ tos = 020; /* Low Delay bit */
+ if (tos
+ && (setsockopt(0, IPPROTO_IP, IP_TOS,
+ (char *)&tos, sizeof(tos)) < 0)
+ && (errno != ENOPROTOOPT) )
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+ }
+#endif /* defined(IPPROTO_IP) && defined(IP_TOS) */
+ net = 0;
+ doit(&from);
+ /* NOTREACHED */
+} /* end of main */
+
+ void
+usage()
+{
+ fprintf(stderr, "Usage: telnetd");
+#ifdef AUTHENTICATION
+ fprintf(stderr, " [-a (debug|other|user|valid|off|none)]\n\t");
+#endif
+#ifdef BFTPDAEMON
+ fprintf(stderr, " [-B]");
+#endif
+ fprintf(stderr, " [-debug]");
+#ifdef DIAGNOSTICS
+ fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t");
+#endif
+#ifdef AUTHENTICATION
+ fprintf(stderr, " [-edebug]");
+#endif
+ fprintf(stderr, " [-h]");
+#if defined(CRAY) && defined(NEWINIT)
+ fprintf(stderr, " [-Iinitid]");
+#endif
+#if defined(LINEMODE) && defined(KLUDGELINEMODE)
+ fprintf(stderr, " [-k]");
+#endif
+#ifdef LINEMODE
+ fprintf(stderr, " [-l]");
+#endif
+ fprintf(stderr, " [-n]");
+#ifdef CRAY
+ fprintf(stderr, " [-r[lowpty]-[highpty]]");
+#endif
+ fprintf(stderr, "\n\t");
+#ifdef SecurID
+ fprintf(stderr, " [-s]");
+#endif
+#ifdef HAS_GETTOS
+ fprintf(stderr, " [-S tos]");
+#endif
+#ifdef AUTHENTICATION
+ fprintf(stderr, " [-X auth-type]");
+#endif
+ fprintf(stderr, " [-u utmp_hostname_length] [-U]");
+ fprintf(stderr, " [port]\n");
+ exit(1);
+}
+
+/*
+ * getterminaltype
+ *
+ * Ask the other end to send along its terminal type and speed.
+ * Output is the variable terminaltype filled in.
+ */
+static unsigned char ttytype_sbbuf[] = {
+ IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE
+};
+
+ int
+getterminaltype(name)
+ char *name;
+{
+ int retval = -1;
+ void _gettermname();
+
+ settimer(baseline);
+#if defined(AUTHENTICATION)
+ /*
+ * Handle the Authentication option before we do anything else.
+ */
+ send_do(TELOPT_AUTHENTICATION, 1);
+ while (his_will_wont_is_changing(TELOPT_AUTHENTICATION))
+ ttloop();
+ if (his_state_is_will(TELOPT_AUTHENTICATION)) {
+ retval = auth_wait(name);
+ }
+#endif
+
+ send_do(TELOPT_TTYPE, 1);
+ send_do(TELOPT_TSPEED, 1);
+ send_do(TELOPT_XDISPLOC, 1);
+ send_do(TELOPT_NEW_ENVIRON, 1);
+ send_do(TELOPT_OLD_ENVIRON, 1);
+ while (
+ his_will_wont_is_changing(TELOPT_TTYPE) ||
+ his_will_wont_is_changing(TELOPT_TSPEED) ||
+ his_will_wont_is_changing(TELOPT_XDISPLOC) ||
+ his_will_wont_is_changing(TELOPT_NEW_ENVIRON) ||
+ his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) {
+ ttloop();
+ }
+ if (his_state_is_will(TELOPT_TSPEED)) {
+ static unsigned char sb[] =
+ { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
+
+ bcopy(sb, nfrontp, sizeof sb);
+ nfrontp += sizeof sb;
+ }
+ if (his_state_is_will(TELOPT_XDISPLOC)) {
+ static unsigned char sb[] =
+ { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE };
+
+ bcopy(sb, nfrontp, sizeof sb);
+ nfrontp += sizeof sb;
+ }
+ if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
+ static unsigned char sb[] =
+ { IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE };
+
+ bcopy(sb, nfrontp, sizeof sb);
+ nfrontp += sizeof sb;
+ }
+ else if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
+ static unsigned char sb[] =
+ { IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE };
+
+ bcopy(sb, nfrontp, sizeof sb);
+ nfrontp += sizeof sb;
+ }
+ if (his_state_is_will(TELOPT_TTYPE)) {
+
+ bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
+ nfrontp += sizeof ttytype_sbbuf;
+ }
+ if (his_state_is_will(TELOPT_TSPEED)) {
+ while (sequenceIs(tspeedsubopt, baseline))
+ ttloop();
+ }
+ if (his_state_is_will(TELOPT_XDISPLOC)) {
+ while (sequenceIs(xdisplocsubopt, baseline))
+ ttloop();
+ }
+ if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
+ while (sequenceIs(environsubopt, baseline))
+ ttloop();
+ }
+ if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
+ while (sequenceIs(oenvironsubopt, baseline))
+ ttloop();
+ }
+ if (his_state_is_will(TELOPT_TTYPE)) {
+ char first[256], last[256];
+
+ while (sequenceIs(ttypesubopt, baseline))
+ ttloop();
+
+ /*
+ * If the other side has already disabled the option, then
+ * we have to just go with what we (might) have already gotten.
+ */
+ if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {
+ (void) strncpy(first, terminaltype, sizeof(first));
+ for(;;) {
+ /*
+ * Save the unknown name, and request the next name.
+ */
+ (void) strncpy(last, terminaltype, sizeof(last));
+ _gettermname();
+ if (terminaltypeok(terminaltype))
+ break;
+ if ((strncmp(last, terminaltype, sizeof(last)) == 0) ||
+ his_state_is_wont(TELOPT_TTYPE)) {
+ /*
+ * We've hit the end. If this is the same as
+ * the first name, just go with it.
+ */
+ if (strncmp(first, terminaltype, sizeof(first)) == 0)
+ break;
+ /*
+ * Get the terminal name one more time, so that
+ * RFC1091 compliant telnets will cycle back to
+ * the start of the list.
+ */
+ _gettermname();
+ if (strncmp(first, terminaltype, sizeof(first)) != 0)
+ (void) strncpy(terminaltype, first, sizeof(first));
+ break;
+ }
+ }
+ }
+ }
+ return(retval);
+} /* end of getterminaltype */
+
+ void
+_gettermname()
+{
+ /*
+ * If the client turned off the option,
+ * we can't send another request, so we
+ * just return.
+ */
+ if (his_state_is_wont(TELOPT_TTYPE))
+ return;
+ settimer(baseline);
+ bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
+ nfrontp += sizeof ttytype_sbbuf;
+ while (sequenceIs(ttypesubopt, baseline))
+ ttloop();
+}
+
+ int
+terminaltypeok(s)
+ char *s;
+{
+ char buf[1024];
+
+ if (terminaltype == NULL)
+ return(1);
+
+ /*
+ * tgetent() will return 1 if the type is known, and
+ * 0 if it is not known. If it returns -1, it couldn't
+ * open the database. But if we can't open the database,
+ * it won't help to say we failed, because we won't be
+ * able to verify anything else. So, we treat -1 like 1.
+ */
+ if (tgetent(buf, s) == 0)
+ return(0);
+ return(1);
+}
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif /* MAXHOSTNAMELEN */
+
+char *hostname;
+char host_name[MAXHOSTNAMELEN];
+char remote_host_name[MAXHOSTNAMELEN];
+
+extern void telnet P((int, int, char *));
+
+int level;
+char user_name[256];
+/*
+ * Get a pty, scan input lines.
+ */
+doit(who)
+ struct sockaddr_in *who;
+{
+ char *host, *inet_ntoa();
+ int t;
+ struct hostent *hp;
+ int ptynum;
+
+ /*
+ * Find an available pty to use.
+ */
+#ifndef convex
+ pty = getpty(&ptynum);
+ if (pty < 0)
+ fatal(net, "All network ports in use");
+#else
+ for (;;) {
+ char *lp;
+ extern char *line, *getpty();
+
+ if ((lp = getpty()) == NULL)
+ fatal(net, "Out of ptys");
+
+ if ((pty = open(lp, 2)) >= 0) {
+ strcpy(line,lp);
+ line[5] = 't';
+ break;
+ }
+ }
+#endif
+
+#if defined(_SC_CRAY_SECURE_SYS)
+ /*
+ * set ttyp line security label
+ */
+ if (secflag) {
+ char slave_dev[16];
+
+ sprintf(tty_dev, "/dev/pty/%03d", ptynum);
+ if (setdevs(tty_dev, &dv) < 0)
+ fatal(net, "cannot set pty security");
+ sprintf(slave_dev, "/dev/ttyp%03d", ptynum);
+ if (setdevs(slave_dev, &dv) < 0)
+ fatal(net, "cannot set tty security");
+ }
+#endif /* _SC_CRAY_SECURE_SYS */
+
+ /* get name of connected client */
+ hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
+ who->sin_family);
+
+ if (hp == NULL && registerd_host_only) {
+ fatal(net, "Couldn't resolve your address into a host name.\r\n\
+ Please contact your net administrator");
+ } else if (hp &&
+ (strlen(hp->h_name) <= ((utmp_len < 0) ? -utmp_len : utmp_len))) {
+ host = hp->h_name;
+ } else {
+ host = inet_ntoa(who->sin_addr);
+ }
+ /*
+ * We must make a copy because Kerberos is probably going
+ * to also do a gethost* and overwrite the static data...
+ */
+ strncpy(remote_host_name, host, sizeof(remote_host_name)-1);
+ remote_host_name[sizeof(remote_host_name)-1] = 0;
+ host = remote_host_name;
+
+ (void) gethostname(host_name, sizeof (host_name));
+ hostname = host_name;
+
+#if defined(AUTHENTICATION)
+ auth_encrypt_init(hostname, host, "TELNETD", 1);
+#endif
+
+ init_env();
+ /*
+ * get terminal type.
+ */
+ *user_name = 0;
+ level = getterminaltype(user_name);
+ setenv("TERM", terminaltype ? terminaltype : "network", 1);
+
+#if defined(_SC_CRAY_SECURE_SYS)
+ if (secflag) {
+ if (setulvl(dv.dv_actlvl) < 0)
+ fatal(net,"cannot setulvl()");
+ if (setucmp(dv.dv_actcmp) < 0)
+ fatal(net, "cannot setucmp()");
+ }
+#endif /* _SC_CRAY_SECURE_SYS */
+
+ telnet(net, pty, host); /* begin server process */
+
+ /*NOTREACHED*/
+} /* end of doit */
+
+#if defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50)
+ int
+Xterm_output(ibufp, obuf, icountp, ocount)
+ char **ibufp, *obuf;
+ int *icountp, ocount;
+{
+ int ret;
+ ret = term_output(*ibufp, obuf, *icountp, ocount);
+ *ibufp += *icountp;
+ *icountp = 0;
+ return(ret);
+}
+#define term_output Xterm_output
+#endif /* defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50) */
+
+/*
+ * Main loop. Select from pty and network, and
+ * hand data to telnet receiver finite state machine.
+ */
+ void
+telnet(f, p, host)
+ int f, p;
+ char *host;
+{
+ int on = 1;
+#define TABBUFSIZ 512
+ char defent[TABBUFSIZ];
+ char defstrs[TABBUFSIZ];
+#undef TABBUFSIZ
+ char *HE;
+ char *HN;
+ char *IM;
+ void netflush();
+
+ /*
+ * Initialize the slc mapping table.
+ */
+ get_slc_defaults();
+
+ /*
+ * Do some tests where it is desireable to wait for a response.
+ * Rather than doing them slowly, one at a time, do them all
+ * at once.
+ */
+ if (my_state_is_wont(TELOPT_SGA))
+ send_will(TELOPT_SGA, 1);
+ /*
+ * Is the client side a 4.2 (NOT 4.3) system? We need to know this
+ * because 4.2 clients are unable to deal with TCP urgent data.
+ *
+ * To find out, we send out a "DO ECHO". If the remote system
+ * answers "WILL ECHO" it is probably a 4.2 client, and we note
+ * that fact ("WILL ECHO" ==> that the client will echo what
+ * WE, the server, sends it; it does NOT mean that the client will
+ * echo the terminal input).
+ */
+ send_do(TELOPT_ECHO, 1);
+
+#ifdef LINEMODE
+ if (his_state_is_wont(TELOPT_LINEMODE)) {
+ /* Query the peer for linemode support by trying to negotiate
+ * the linemode option.
+ */
+ linemode = 0;
+ editmode = 0;
+ send_do(TELOPT_LINEMODE, 1); /* send do linemode */
+ }
+#endif /* LINEMODE */
+
+ /*
+ * Send along a couple of other options that we wish to negotiate.
+ */
+ send_do(TELOPT_NAWS, 1);
+ send_will(TELOPT_STATUS, 1);
+ flowmode = 1; /* default flow control state */
+ restartany = -1; /* uninitialized... */
+ send_do(TELOPT_LFLOW, 1);
+
+ /*
+ * Spin, waiting for a response from the DO ECHO. However,
+ * some REALLY DUMB telnets out there might not respond
+ * to the DO ECHO. So, we spin looking for NAWS, (most dumb
+ * telnets so far seem to respond with WONT for a DO that
+ * they don't understand...) because by the time we get the
+ * response, it will already have processed the DO ECHO.
+ * Kludge upon kludge.
+ */
+ while (his_will_wont_is_changing(TELOPT_NAWS))
+ ttloop();
+
+ /*
+ * But...
+ * The client might have sent a WILL NAWS as part of its
+ * startup code; if so, we'll be here before we get the
+ * response to the DO ECHO. We'll make the assumption
+ * that any implementation that understands about NAWS
+ * is a modern enough implementation that it will respond
+ * to our DO ECHO request; hence we'll do another spin
+ * waiting for the ECHO option to settle down, which is
+ * what we wanted to do in the first place...
+ */
+ if (his_want_state_is_will(TELOPT_ECHO) &&
+ his_state_is_will(TELOPT_NAWS)) {
+ while (his_will_wont_is_changing(TELOPT_ECHO))
+ ttloop();
+ }
+ /*
+ * On the off chance that the telnet client is broken and does not
+ * respond to the DO ECHO we sent, (after all, we did send the
+ * DO NAWS negotiation after the DO ECHO, and we won't get here
+ * until a response to the DO NAWS comes back) simulate the
+ * receipt of a will echo. This will also send a WONT ECHO
+ * to the client, since we assume that the client failed to
+ * respond because it believes that it is already in DO ECHO
+ * mode, which we do not want.
+ */
+ if (his_want_state_is_will(TELOPT_ECHO)) {
+ DIAG(TD_OPTIONS,
+ {sprintf(nfrontp, "td: simulating recv\r\n");
+ nfrontp += strlen(nfrontp);});
+ willoption(TELOPT_ECHO);
+ }
+
+ /*
+ * Finally, to clean things up, we turn on our echo. This
+ * will break stupid 4.2 telnets out of local terminal echo.
+ */
+
+ if (my_state_is_wont(TELOPT_ECHO))
+ send_will(TELOPT_ECHO, 1);
+
+#ifndef STREAMSPTY
+ /*
+ * Turn on packet mode
+ */
+ (void) ioctl(p, TIOCPKT, (char *)&on);
+#endif
+
+#if defined(LINEMODE) && defined(KLUDGELINEMODE)
+ /*
+ * Continuing line mode support. If client does not support
+ * real linemode, attempt to negotiate kludge linemode by sending
+ * the do timing mark sequence.
+ */
+ if (lmodetype < REAL_LINEMODE)
+ send_do(TELOPT_TM, 1);
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+
+ /*
+ * Call telrcv() once to pick up anything received during
+ * terminal type negotiation, 4.2/4.3 determination, and
+ * linemode negotiation.
+ */
+ telrcv();
+
+ (void) ioctl(f, FIONBIO, (char *)&on);
+ (void) ioctl(p, FIONBIO, (char *)&on);
+#if defined(CRAY2) && defined(UNICOS5)
+ init_termdriver(f, p, interrupt, sendbrk);
+#endif
+
+#if defined(SO_OOBINLINE)
+ (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE,
+ (char *)&on, sizeof on);
+#endif /* defined(SO_OOBINLINE) */
+
+#ifdef SIGTSTP
+ (void) signal(SIGTSTP, SIG_IGN);
+#endif
+#ifdef SIGTTOU
+ /*
+ * Ignoring SIGTTOU keeps the kernel from blocking us
+ * in ttioct() in /sys/tty.c.
+ */
+ (void) signal(SIGTTOU, SIG_IGN);
+#endif
+
+ (void) signal(SIGCHLD, cleanup);
+
+#if defined(CRAY2) && defined(UNICOS5)
+ /*
+ * Cray-2 will send a signal when pty modes are changed by slave
+ * side. Set up signal handler now.
+ */
+ if ((int)signal(SIGUSR1, termstat) < 0)
+ perror("signal");
+ else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
+ perror("ioctl:TCSIGME");
+ /*
+ * Make processing loop check terminal characteristics early on.
+ */
+ termstat();
+#endif
+
+#ifdef TIOCNOTTY
+ {
+ register int t;
+ t = open(_PATH_TTY, O_RDWR);
+ if (t >= 0) {
+ (void) ioctl(t, TIOCNOTTY, (char *)0);
+ (void) close(t);
+ }
+ }
+#endif
+
+#if defined(CRAY) && defined(NEWINIT) && defined(TIOCSCTTY)
+ (void) setsid();
+ ioctl(p, TIOCSCTTY, 0);
+#endif
+
+ /*
+ * Show banner that getty never gave.
+ *
+ * We put the banner in the pty input buffer. This way, it
+ * gets carriage return null processing, etc., just like all
+ * other pty --> client data.
+ */
+
+#if !defined(CRAY) || !defined(NEWINIT)
+ if (getenv("USER"))
+ hostinfo = 0;
+#endif
+
+ if (getent(defent, "default") == 1) {
+ char *getstr();
+ char *cp=defstrs;
+
+ HE = getstr("he", &cp);
+ HN = getstr("hn", &cp);
+ IM = getstr("im", &cp);
+ if (HN && *HN)
+ (void) strcpy(host_name, HN);
+ if (IM == 0)
+ IM = "";
+ } else {
+ IM = DEFAULT_IM;
+ HE = 0;
+ }
+ edithost(HE, host_name);
+ if (hostinfo && *IM)
+ putf(IM, ptyibuf2);
+
+ if (pcc)
+ (void) strncat(ptyibuf2, ptyip, pcc+1);
+ ptyip = ptyibuf2;
+ pcc = strlen(ptyip);
+#ifdef LINEMODE
+ /*
+ * Last check to make sure all our states are correct.
+ */
+ init_termbuf();
+ localstat();
+#endif /* LINEMODE */
+
+ DIAG(TD_REPORT,
+ {sprintf(nfrontp, "td: Entering processing loop\r\n");
+ nfrontp += strlen(nfrontp);});
+
+ /*
+ * Startup the login process on the slave side of the terminal
+ * now. We delay this until here to insure option negotiation
+ * is complete.
+ */
+ startslave(host, level, user_name);
+
+ for (;;) {
+ fd_set ibits, obits, xbits;
+ register int c;
+
+ if (ncc < 0 && pcc < 0)
+ break;
+
+#if defined(CRAY2) && defined(UNICOS5)
+ if (needtermstat)
+ _termstat();
+#endif /* defined(CRAY2) && defined(UNICOS5) */
+ FD_ZERO(&ibits);
+ FD_ZERO(&obits);
+ FD_ZERO(&xbits);
+ /*
+ * Never look for input if there's still
+ * stuff in the corresponding output buffer
+ */
+ if (nfrontp - nbackp || pcc > 0) {
+ FD_SET(f, &obits);
+ } else {
+ FD_SET(p, &ibits);
+ }
+ if (pfrontp - pbackp || ncc > 0) {
+ FD_SET(p, &obits);
+ } else {
+ FD_SET(f, &ibits);
+ }
+ if (!SYNCHing) {
+ FD_SET(f, &xbits);
+ }
+ if ((c = select(16, &ibits, &obits, &xbits,
+ (struct timeval *)0)) < 1) {
+ if (c == -1) {
+ if (errno == EINTR) {
+ continue;
+ }
+ }
+ sleep(5);
+ continue;
+ }
+
+ /*
+ * Any urgent data?
+ */
+ if (FD_ISSET(net, &xbits)) {
+ SYNCHing = 1;
+ }
+
+ /*
+ * Something to read from the network...
+ */
+ if (FD_ISSET(net, &ibits)) {
+#if !defined(SO_OOBINLINE)
+ /*
+ * In 4.2 (and 4.3 beta) systems, the
+ * OOB indication and data handling in the kernel
+ * is such that if two separate TCP Urgent requests
+ * come in, one byte of TCP data will be overlaid.
+ * This is fatal for Telnet, but we try to live
+ * with it.
+ *
+ * In addition, in 4.2 (and...), a special protocol
+ * is needed to pick up the TCP Urgent data in
+ * the correct sequence.
+ *
+ * What we do is: if we think we are in urgent
+ * mode, we look to see if we are "at the mark".
+ * If we are, we do an OOB receive. If we run
+ * this twice, we will do the OOB receive twice,
+ * but the second will fail, since the second
+ * time we were "at the mark", but there wasn't
+ * any data there (the kernel doesn't reset
+ * "at the mark" until we do a normal read).
+ * Once we've read the OOB data, we go ahead
+ * and do normal reads.
+ *
+ * There is also another problem, which is that
+ * since the OOB byte we read doesn't put us
+ * out of OOB state, and since that byte is most
+ * likely the TELNET DM (data mark), we would
+ * stay in the TELNET SYNCH (SYNCHing) state.
+ * So, clocks to the rescue. If we've "just"
+ * received a DM, then we test for the
+ * presence of OOB data when the receive OOB
+ * fails (and AFTER we did the normal mode read
+ * to clear "at the mark").
+ */
+ if (SYNCHing) {
+ int atmark;
+
+ (void) ioctl(net, SIOCATMARK, (char *)&atmark);
+ if (atmark) {
+ ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
+ if ((ncc == -1) && (errno == EINVAL)) {
+ ncc = read(net, netibuf, sizeof (netibuf));
+ if (sequenceIs(didnetreceive, gotDM)) {
+ SYNCHing = stilloob(net);
+ }
+ }
+ } else {
+ ncc = read(net, netibuf, sizeof (netibuf));
+ }
+ } else {
+ ncc = read(net, netibuf, sizeof (netibuf));
+ }
+ settimer(didnetreceive);
+#else /* !defined(SO_OOBINLINE)) */
+ ncc = read(net, netibuf, sizeof (netibuf));
+#endif /* !defined(SO_OOBINLINE)) */
+ if (ncc < 0 && errno == EWOULDBLOCK)
+ ncc = 0;
+ else {
+ if (ncc <= 0) {
+ break;
+ }
+ netip = netibuf;
+ }
+ DIAG((TD_REPORT | TD_NETDATA),
+ {sprintf(nfrontp, "td: netread %d chars\r\n", ncc);
+ nfrontp += strlen(nfrontp);});
+ DIAG(TD_NETDATA, printdata("nd", netip, ncc));
+ }
+
+ /*
+ * Something to read from the pty...
+ */
+ if (FD_ISSET(p, &ibits)) {
+#ifndef STREAMSPTY
+ pcc = read(p, ptyibuf, BUFSIZ);
+#else
+ pcc = readstream(p, ptyibuf, BUFSIZ);
+#endif
+ /*
+ * On some systems, if we try to read something
+ * off the master side before the slave side is
+ * opened, we get EIO.
+ */
+ if (pcc < 0 && (errno == EWOULDBLOCK ||
+#ifdef EAGAIN
+ errno == EAGAIN ||
+#endif
+ errno == EIO)) {
+ pcc = 0;
+ } else {
+ if (pcc <= 0)
+ break;
+#if !defined(CRAY2) || !defined(UNICOS5)
+#ifdef LINEMODE
+ /*
+ * If ioctl from pty, pass it through net
+ */
+ if (ptyibuf[0] & TIOCPKT_IOCTL) {
+ copy_termbuf(ptyibuf+1, pcc-1);
+ localstat();
+ pcc = 1;
+ }
+#endif /* LINEMODE */
+ if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
+ netclear(); /* clear buffer back */
+#ifndef NO_URGENT
+ /*
+ * There are client telnets on some
+ * operating systems get screwed up
+ * royally if we send them urgent
+ * mode data.
+ */
+ *nfrontp++ = IAC;
+ *nfrontp++ = DM;
+ neturg = nfrontp-1; /* off by one XXX */
+#endif
+ }
+ if (his_state_is_will(TELOPT_LFLOW) &&
+ (ptyibuf[0] &
+ (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
+ int newflow =
+ ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0;
+ if (newflow != flowmode) {
+ flowmode = newflow;
+ (void) sprintf(nfrontp,
+ "%c%c%c%c%c%c",
+ IAC, SB, TELOPT_LFLOW,
+ flowmode ? LFLOW_ON
+ : LFLOW_OFF,
+ IAC, SE);
+ nfrontp += 6;
+ }
+ }
+ pcc--;
+ ptyip = ptyibuf+1;
+#else /* defined(CRAY2) && defined(UNICOS5) */
+ if (!uselinemode) {
+ unpcc = pcc;
+ unptyip = ptyibuf;
+ pcc = term_output(&unptyip, ptyibuf2,
+ &unpcc, BUFSIZ);
+ ptyip = ptyibuf2;
+ } else
+ ptyip = ptyibuf;
+#endif /* defined(CRAY2) && defined(UNICOS5) */
+ }
+ }
+
+ while (pcc > 0) {
+ if ((&netobuf[BUFSIZ] - nfrontp) < 2)
+ break;
+ c = *ptyip++ & 0377, pcc--;
+ if (c == IAC)
+ *nfrontp++ = c;
+#if defined(CRAY2) && defined(UNICOS5)
+ else if (c == '\n' &&
+ my_state_is_wont(TELOPT_BINARY) && newmap)
+ *nfrontp++ = '\r';
+#endif /* defined(CRAY2) && defined(UNICOS5) */
+ *nfrontp++ = c;
+ if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) {
+ if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
+ *nfrontp++ = *ptyip++ & 0377;
+ pcc--;
+ } else
+ *nfrontp++ = '\0';
+ }
+ }
+#if defined(CRAY2) && defined(UNICOS5)
+ /*
+ * If chars were left over from the terminal driver,
+ * note their existence.
+ */
+ if (!uselinemode && unpcc) {
+ pcc = unpcc;
+ unpcc = 0;
+ ptyip = unptyip;
+ }
+#endif /* defined(CRAY2) && defined(UNICOS5) */
+
+ if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
+ netflush();
+ if (ncc > 0)
+ telrcv();
+ if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
+ ptyflush();
+ }
+ cleanup(0);
+} /* end of telnet */
+
+#ifndef TCSIG
+# ifdef TIOCSIG
+# define TCSIG TIOCSIG
+# endif
+#endif
+
+#ifdef STREAMSPTY
+
+int flowison = -1; /* current state of flow: -1 is unknown */
+
+int readstream(p, ibuf, bufsize)
+ int p;
+ char *ibuf;
+ int bufsize;
+{
+ int flags = 0;
+ int ret = 0;
+ struct termios *tsp;
+ struct termio *tp;
+ struct iocblk *ip;
+ char vstop, vstart;
+ int ixon;
+ int newflow;
+
+ strbufc.maxlen = BUFSIZ;
+ strbufc.buf = (char *)ctlbuf;
+ strbufd.maxlen = bufsize-1;
+ strbufd.len = 0;
+ strbufd.buf = ibuf+1;
+ ibuf[0] = 0;
+
+ ret = getmsg(p, &strbufc, &strbufd, &flags);
+ if (ret < 0) /* error of some sort -- probably EAGAIN */
+ return(-1);
+
+ if (strbufc.len <= 0 || ctlbuf[0] == M_DATA) {
+ /* data message */
+ if (strbufd.len > 0) { /* real data */
+ return(strbufd.len + 1); /* count header char */
+ } else {
+ /* nothing there */
+ errno = EAGAIN;
+ return(-1);
+ }
+ }
+
+ /*
+ * It's a control message. Return 1, to look at the flag we set
+ */
+
+ switch (ctlbuf[0]) {
+ case M_FLUSH:
+ if (ibuf[1] & FLUSHW)
+ ibuf[0] = TIOCPKT_FLUSHWRITE;
+ return(1);
+
+ case M_IOCTL:
+ ip = (struct iocblk *) (ibuf+1);
+
+ switch (ip->ioc_cmd) {
+ case TCSETS:
+ case TCSETSW:
+ case TCSETSF:
+ tsp = (struct termios *)
+ (ibuf+1 + sizeof(struct iocblk));
+ vstop = tsp->c_cc[VSTOP];
+ vstart = tsp->c_cc[VSTART];
+ ixon = tsp->c_iflag & IXON;
+ break;
+ case TCSETA:
+ case TCSETAW:
+ case TCSETAF:
+ tp = (struct termio *) (ibuf+1 + sizeof(struct iocblk));
+ vstop = tp->c_cc[VSTOP];
+ vstart = tp->c_cc[VSTART];
+ ixon = tp->c_iflag & IXON;
+ break;
+ default:
+ errno = EAGAIN;
+ return(-1);
+ }
+
+ newflow = (ixon && (vstart == 021) && (vstop == 023)) ? 1 : 0;
+ if (newflow != flowison) { /* it's a change */
+ flowison = newflow;
+ ibuf[0] = newflow ? TIOCPKT_DOSTOP : TIOCPKT_NOSTOP;
+ return(1);
+ }
+ }
+
+ /* nothing worth doing anything about */
+ errno = EAGAIN;
+ return(-1);
+}
+#endif /* STREAMSPTY */
+
+/*
+ * Send interrupt to process on other side of pty.
+ * If it is in raw mode, just write NULL;
+ * otherwise, write intr char.
+ */
+ void
+interrupt()
+{
+ ptyflush(); /* half-hearted */
+
+#ifdef TCSIG
+ (void) ioctl(pty, TCSIG, (char *)SIGINT);
+#else /* TCSIG */
+ init_termbuf();
+ *pfrontp++ = slctab[SLC_IP].sptr ?
+ (unsigned char)*slctab[SLC_IP].sptr : '\177';
+#endif /* TCSIG */
+}
+
+/*
+ * Send quit to process on other side of pty.
+ * If it is in raw mode, just write NULL;
+ * otherwise, write quit char.
+ */
+ void
+sendbrk()
+{
+ ptyflush(); /* half-hearted */
+#ifdef TCSIG
+ (void) ioctl(pty, TCSIG, (char *)SIGQUIT);
+#else /* TCSIG */
+ init_termbuf();
+ *pfrontp++ = slctab[SLC_ABORT].sptr ?
+ (unsigned char)*slctab[SLC_ABORT].sptr : '\034';
+#endif /* TCSIG */
+}
+
+ void
+sendsusp()
+{
+#ifdef SIGTSTP
+ ptyflush(); /* half-hearted */
+# ifdef TCSIG
+ (void) ioctl(pty, TCSIG, (char *)SIGTSTP);
+# else /* TCSIG */
+ *pfrontp++ = slctab[SLC_SUSP].sptr ?
+ (unsigned char)*slctab[SLC_SUSP].sptr : '\032';
+# endif /* TCSIG */
+#endif /* SIGTSTP */
+}
+
+/*
+ * When we get an AYT, if ^T is enabled, use that. Otherwise,
+ * just send back "[Yes]".
+ */
+ void
+recv_ayt()
+{
+#if defined(SIGINFO) && defined(TCSIG)
+ if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {
+ (void) ioctl(pty, TCSIG, (char *)SIGINFO);
+ return;
+ }
+#endif
+ (void) strcpy(nfrontp, "\r\n[Yes]\r\n");
+ nfrontp += 9;
+}
+
+ void
+doeof()
+{
+ init_termbuf();
+
+#if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN)
+ if (!tty_isediting()) {
+ extern char oldeofc;
+ *pfrontp++ = oldeofc;
+ return;
+ }
+#endif
+ *pfrontp++ = slctab[SLC_EOF].sptr ?
+ (unsigned char)*slctab[SLC_EOF].sptr : '\004';
+}
diff --git a/libexec/telnetd/telnetd.h b/libexec/telnetd/telnetd.h
new file mode 100644
index 0000000..234b973
--- /dev/null
+++ b/libexec/telnetd/telnetd.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)telnetd.h 8.1 (Berkeley) 6/4/93
+ */
+
+
+#include "defs.h"
+#include "ext.h"
+
+#ifdef DIAGNOSTICS
+#define DIAG(a,b) if (diagnostic & (a)) b
+#else
+#define DIAG(a,b)
+#endif
+
+/* other external variables */
+extern char **environ;
+extern int errno;
+
diff --git a/libexec/telnetd/termstat.c b/libexec/telnetd/termstat.c
new file mode 100644
index 0000000..b08e50f
--- /dev/null
+++ b/libexec/telnetd/termstat.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)termstat.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include "telnetd.h"
+
+/*
+ * local variables
+ */
+int def_tspeed = -1, def_rspeed = -1;
+#ifdef TIOCSWINSZ
+int def_row = 0, def_col = 0;
+#endif
+#ifdef LINEMODE
+static int _terminit = 0;
+#endif /* LINEMODE */
+
+#if defined(CRAY2) && defined(UNICOS5)
+int newmap = 1; /* nonzero if \n maps to ^M^J */
+#endif
+
+#ifdef LINEMODE
+/*
+ * localstat
+ *
+ * This function handles all management of linemode.
+ *
+ * Linemode allows the client to do the local editing of data
+ * and send only complete lines to the server. Linemode state is
+ * based on the state of the pty driver. If the pty is set for
+ * external processing, then we can use linemode. Further, if we
+ * can use real linemode, then we can look at the edit control bits
+ * in the pty to determine what editing the client should do.
+ *
+ * Linemode support uses the following state flags to keep track of
+ * current and desired linemode state.
+ * alwayslinemode : true if -l was specified on the telnetd
+ * command line. It means to have linemode on as much as
+ * possible.
+ *
+ * lmodetype: signifies whether the client can
+ * handle real linemode, or if use of kludgeomatic linemode
+ * is preferred. It will be set to one of the following:
+ * REAL_LINEMODE : use linemode option
+ * NO_KLUDGE : don't initiate kludge linemode.
+ * KLUDGE_LINEMODE : use kludge linemode
+ * NO_LINEMODE : client is ignorant of linemode
+ *
+ * linemode, uselinemode : linemode is true if linemode
+ * is currently on, uselinemode is the state that we wish
+ * to be in. If another function wishes to turn linemode
+ * on or off, it sets or clears uselinemode.
+ *
+ * editmode, useeditmode : like linemode/uselinemode, but
+ * these contain the edit mode states (edit and trapsig).
+ *
+ * The state variables correspond to some of the state information
+ * in the pty.
+ * linemode:
+ * In real linemode, this corresponds to whether the pty
+ * expects external processing of incoming data.
+ * In kludge linemode, this more closely corresponds to the
+ * whether normal processing is on or not. (ICANON in
+ * system V, or COOKED mode in BSD.)
+ * If the -l option was specified (alwayslinemode), then
+ * an attempt is made to force external processing on at
+ * all times.
+ *
+ * The following heuristics are applied to determine linemode
+ * handling within the server.
+ * 1) Early on in starting up the server, an attempt is made
+ * to negotiate the linemode option. If this succeeds
+ * then lmodetype is set to REAL_LINEMODE and all linemode
+ * processing occurs in the context of the linemode option.
+ * 2) If the attempt to negotiate the linemode option failed,
+ * and the "-k" (don't initiate kludge linemode) isn't set,
+ * then we try to use kludge linemode. We test for this
+ * capability by sending "do Timing Mark". If a positive
+ * response comes back, then we assume that the client
+ * understands kludge linemode (ech!) and the
+ * lmodetype flag is set to KLUDGE_LINEMODE.
+ * 3) Otherwise, linemode is not supported at all and
+ * lmodetype remains set to NO_LINEMODE (which happens
+ * to be 0 for convenience).
+ * 4) At any time a command arrives that implies a higher
+ * state of linemode support in the client, we move to that
+ * linemode support.
+ *
+ * A short explanation of kludge linemode is in order here.
+ * 1) The heuristic to determine support for kludge linemode
+ * is to send a do timing mark. We assume that a client
+ * that supports timing marks also supports kludge linemode.
+ * A risky proposition at best.
+ * 2) Further negotiation of linemode is done by changing the
+ * the server's state regarding SGA. If server will SGA,
+ * then linemode is off, if server won't SGA, then linemode
+ * is on.
+ */
+ void
+localstat()
+{
+ void netflush();
+ int need_will_echo = 0;
+
+#if defined(CRAY2) && defined(UNICOS5)
+ /*
+ * Keep track of that ol' CR/NL mapping while we're in the
+ * neighborhood.
+ */
+ newmap = tty_isnewmap();
+#endif /* defined(CRAY2) && defined(UNICOS5) */
+
+ /*
+ * Check for changes to flow control if client supports it.
+ */
+ flowstat();
+
+ /*
+ * Check linemode on/off state
+ */
+ uselinemode = tty_linemode();
+
+ /*
+ * If alwayslinemode is on, and pty is changing to turn it off, then
+ * force linemode back on.
+ */
+ if (alwayslinemode && linemode && !uselinemode) {
+ uselinemode = 1;
+ tty_setlinemode(uselinemode);
+ }
+
+ if (uselinemode) {
+
+ /*
+ * Check for state of BINARY options.
+ *
+ * We only need to do the binary dance if we are actually going
+ * to use linemode. As this confuses some telnet clients that dont
+ * support linemode, and doesnt gain us anything, we dont do it
+ * unless we're doing linemode. -Crh (henrich@msu.edu)
+ */
+
+ if (tty_isbinaryin()) {
+ if (his_want_state_is_wont(TELOPT_BINARY))
+ send_do(TELOPT_BINARY, 1);
+ } else {
+ if (his_want_state_is_will(TELOPT_BINARY))
+ send_dont(TELOPT_BINARY, 1);
+ }
+
+ if (tty_isbinaryout()) {
+ if (my_want_state_is_wont(TELOPT_BINARY))
+ send_will(TELOPT_BINARY, 1);
+ } else {
+ if (my_want_state_is_will(TELOPT_BINARY))
+ send_wont(TELOPT_BINARY, 1);
+ }
+
+ }
+
+ /*
+ * Do echo mode handling as soon as we know what the
+ * linemode is going to be.
+ * If the pty has echo turned off, then tell the client that
+ * the server will echo. If echo is on, then the server
+ * will echo if in character mode, but in linemode the
+ * client should do local echoing. The state machine will
+ * not send anything if it is unnecessary, so don't worry
+ * about that here.
+ *
+ * If we need to send the WILL ECHO (because echo is off),
+ * then delay that until after we have changed the MODE.
+ * This way, when the user is turning off both editing
+ * and echo, the client will get editing turned off first.
+ * This keeps the client from going into encryption mode
+ * and then right back out if it is doing auto-encryption
+ * when passwords are being typed.
+ */
+ if (uselinemode) {
+ if (tty_isecho())
+ send_wont(TELOPT_ECHO, 1);
+ else
+ need_will_echo = 1;
+#ifdef KLUDGELINEMODE
+ if (lmodetype == KLUDGE_OK)
+ lmodetype = KLUDGE_LINEMODE;
+#endif
+ }
+
+ /*
+ * If linemode is being turned off, send appropriate
+ * command and then we're all done.
+ */
+ if (!uselinemode && linemode) {
+# ifdef KLUDGELINEMODE
+ if (lmodetype == REAL_LINEMODE) {
+# endif /* KLUDGELINEMODE */
+ send_dont(TELOPT_LINEMODE, 1);
+# ifdef KLUDGELINEMODE
+ } else if (lmodetype == KLUDGE_LINEMODE)
+ send_will(TELOPT_SGA, 1);
+# endif /* KLUDGELINEMODE */
+ send_will(TELOPT_ECHO, 1);
+ linemode = uselinemode;
+ goto done;
+ }
+
+# ifdef KLUDGELINEMODE
+ /*
+ * If using real linemode check edit modes for possible later use.
+ * If we are in kludge linemode, do the SGA negotiation.
+ */
+ if (lmodetype == REAL_LINEMODE) {
+# endif /* KLUDGELINEMODE */
+ useeditmode = 0;
+ if (tty_isediting())
+ useeditmode |= MODE_EDIT;
+ if (tty_istrapsig())
+ useeditmode |= MODE_TRAPSIG;
+ if (tty_issofttab())
+ useeditmode |= MODE_SOFT_TAB;
+ if (tty_islitecho())
+ useeditmode |= MODE_LIT_ECHO;
+# ifdef KLUDGELINEMODE
+ } else if (lmodetype == KLUDGE_LINEMODE) {
+ if (tty_isediting() && uselinemode)
+ send_wont(TELOPT_SGA, 1);
+ else
+ send_will(TELOPT_SGA, 1);
+ }
+# endif /* KLUDGELINEMODE */
+
+ /*
+ * Negotiate linemode on if pty state has changed to turn it on.
+ * Send appropriate command and send along edit mode, then all done.
+ */
+ if (uselinemode && !linemode) {
+# ifdef KLUDGELINEMODE
+ if (lmodetype == KLUDGE_LINEMODE) {
+ send_wont(TELOPT_SGA, 1);
+ } else if (lmodetype == REAL_LINEMODE) {
+# endif /* KLUDGELINEMODE */
+ send_do(TELOPT_LINEMODE, 1);
+ /* send along edit modes */
+ (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB,
+ TELOPT_LINEMODE, LM_MODE, useeditmode,
+ IAC, SE);
+ nfrontp += 7;
+ editmode = useeditmode;
+# ifdef KLUDGELINEMODE
+ }
+# endif /* KLUDGELINEMODE */
+ linemode = uselinemode;
+ goto done;
+ }
+
+# ifdef KLUDGELINEMODE
+ /*
+ * None of what follows is of any value if not using
+ * real linemode.
+ */
+ if (lmodetype < REAL_LINEMODE)
+ goto done;
+# endif /* KLUDGELINEMODE */
+
+ if (linemode && his_state_is_will(TELOPT_LINEMODE)) {
+ /*
+ * If edit mode changed, send edit mode.
+ */
+ if (useeditmode != editmode) {
+ /*
+ * Send along appropriate edit mode mask.
+ */
+ (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB,
+ TELOPT_LINEMODE, LM_MODE, useeditmode,
+ IAC, SE);
+ nfrontp += 7;
+ editmode = useeditmode;
+ }
+
+
+ /*
+ * Check for changes to special characters in use.
+ */
+ start_slc(0);
+ check_slc();
+ (void) end_slc(0);
+ }
+
+done:
+ if (need_will_echo)
+ send_will(TELOPT_ECHO, 1);
+ /*
+ * Some things should be deferred until after the pty state has
+ * been set by the local process. Do those things that have been
+ * deferred now. This only happens once.
+ */
+ if (_terminit == 0) {
+ _terminit = 1;
+ defer_terminit();
+ }
+
+ netflush();
+ set_termbuf();
+ return;
+
+} /* end of localstat */
+#endif /* LINEMODE */
+
+/*
+ * flowstat
+ *
+ * Check for changes to flow control
+ */
+ void
+flowstat()
+{
+ if (his_state_is_will(TELOPT_LFLOW)) {
+ if (tty_flowmode() != flowmode) {
+ flowmode = tty_flowmode();
+ (void) sprintf(nfrontp, "%c%c%c%c%c%c",
+ IAC, SB, TELOPT_LFLOW,
+ flowmode ? LFLOW_ON : LFLOW_OFF,
+ IAC, SE);
+ nfrontp += 6;
+ }
+ if (tty_restartany() != restartany) {
+ restartany = tty_restartany();
+ (void) sprintf(nfrontp, "%c%c%c%c%c%c",
+ IAC, SB, TELOPT_LFLOW,
+ restartany ? LFLOW_RESTART_ANY
+ : LFLOW_RESTART_XON,
+ IAC, SE);
+ nfrontp += 6;
+ }
+ }
+}
+
+/*
+ * clientstat
+ *
+ * Process linemode related requests from the client.
+ * Client can request a change to only one of linemode, editmode or slc's
+ * at a time, and if using kludge linemode, then only linemode may be
+ * affected.
+ */
+ void
+clientstat(code, parm1, parm2)
+ register int code, parm1, parm2;
+{
+ void netflush();
+
+ /*
+ * Get a copy of terminal characteristics.
+ */
+ init_termbuf();
+
+ /*
+ * Process request from client. code tells what it is.
+ */
+ switch (code) {
+#ifdef LINEMODE
+ case TELOPT_LINEMODE:
+ /*
+ * Don't do anything unless client is asking us to change
+ * modes.
+ */
+ uselinemode = (parm1 == WILL);
+ if (uselinemode != linemode) {
+# ifdef KLUDGELINEMODE
+ /*
+ * If using kludge linemode, make sure that
+ * we can do what the client asks.
+ * We can not turn off linemode if alwayslinemode
+ * and the ICANON bit is set.
+ */
+ if (lmodetype == KLUDGE_LINEMODE) {
+ if (alwayslinemode && tty_isediting()) {
+ uselinemode = 1;
+ }
+ }
+
+ /*
+ * Quit now if we can't do it.
+ */
+ if (uselinemode == linemode)
+ return;
+
+ /*
+ * If using real linemode and linemode is being
+ * turned on, send along the edit mode mask.
+ */
+ if (lmodetype == REAL_LINEMODE && uselinemode)
+# else /* KLUDGELINEMODE */
+ if (uselinemode)
+# endif /* KLUDGELINEMODE */
+ {
+ useeditmode = 0;
+ if (tty_isediting())
+ useeditmode |= MODE_EDIT;
+ if (tty_istrapsig)
+ useeditmode |= MODE_TRAPSIG;
+ if (tty_issofttab())
+ useeditmode |= MODE_SOFT_TAB;
+ if (tty_islitecho())
+ useeditmode |= MODE_LIT_ECHO;
+ (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC,
+ SB, TELOPT_LINEMODE, LM_MODE,
+ useeditmode, IAC, SE);
+ nfrontp += 7;
+ editmode = useeditmode;
+ }
+
+
+ tty_setlinemode(uselinemode);
+
+ linemode = uselinemode;
+
+ if (!linemode)
+ send_will(TELOPT_ECHO, 1);
+ }
+ break;
+
+ case LM_MODE:
+ {
+ register int ack, changed;
+
+ /*
+ * Client has sent along a mode mask. If it agrees with
+ * what we are currently doing, ignore it; if not, it could
+ * be viewed as a request to change. Note that the server
+ * will change to the modes in an ack if it is different from
+ * what we currently have, but we will not ack the ack.
+ */
+ useeditmode &= MODE_MASK;
+ ack = (useeditmode & MODE_ACK);
+ useeditmode &= ~MODE_ACK;
+
+ if (changed = (useeditmode ^ editmode)) {
+ /*
+ * This check is for a timing problem. If the
+ * state of the tty has changed (due to the user
+ * application) we need to process that info
+ * before we write in the state contained in the
+ * ack!!! This gets out the new MODE request,
+ * and when the ack to that command comes back
+ * we'll set it and be in the right mode.
+ */
+ if (ack)
+ localstat();
+ if (changed & MODE_EDIT)
+ tty_setedit(useeditmode & MODE_EDIT);
+
+ if (changed & MODE_TRAPSIG)
+ tty_setsig(useeditmode & MODE_TRAPSIG);
+
+ if (changed & MODE_SOFT_TAB)
+ tty_setsofttab(useeditmode & MODE_SOFT_TAB);
+
+ if (changed & MODE_LIT_ECHO)
+ tty_setlitecho(useeditmode & MODE_LIT_ECHO);
+
+ set_termbuf();
+
+ if (!ack) {
+ (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC,
+ SB, TELOPT_LINEMODE, LM_MODE,
+ useeditmode|MODE_ACK,
+ IAC, SE);
+ nfrontp += 7;
+ }
+
+ editmode = useeditmode;
+ }
+
+ break;
+
+ } /* end of case LM_MODE */
+#endif /* LINEMODE */
+
+ case TELOPT_NAWS:
+#ifdef TIOCSWINSZ
+ {
+ struct winsize ws;
+
+ def_col = parm1;
+ def_row = parm2;
+#ifdef LINEMODE
+ /*
+ * Defer changing window size until after terminal is
+ * initialized.
+ */
+ if (terminit() == 0)
+ return;
+#endif /* LINEMODE */
+
+ /*
+ * Change window size as requested by client.
+ */
+
+ ws.ws_col = parm1;
+ ws.ws_row = parm2;
+ (void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
+ }
+#endif /* TIOCSWINSZ */
+
+ break;
+
+ case TELOPT_TSPEED:
+ {
+ def_tspeed = parm1;
+ def_rspeed = parm2;
+#ifdef LINEMODE
+ /*
+ * Defer changing the terminal speed.
+ */
+ if (terminit() == 0)
+ return;
+#endif /* LINEMODE */
+ /*
+ * Change terminal speed as requested by client.
+ * We set the receive speed first, so that if we can't
+ * store seperate receive and transmit speeds, the transmit
+ * speed will take precedence.
+ */
+ tty_rspeed(parm2);
+ tty_tspeed(parm1);
+ set_termbuf();
+
+ break;
+
+ } /* end of case TELOPT_TSPEED */
+
+ default:
+ /* What? */
+ break;
+ } /* end of switch */
+
+#if defined(CRAY2) && defined(UNICOS5)
+ /*
+ * Just in case of the likely event that we changed the pty state.
+ */
+ rcv_ioctl();
+#endif /* defined(CRAY2) && defined(UNICOS5) */
+
+ netflush();
+
+} /* end of clientstat */
+
+#if defined(CRAY2) && defined(UNICOS5)
+ void
+termstat()
+{
+ needtermstat = 1;
+}
+
+ void
+_termstat()
+{
+ needtermstat = 0;
+ init_termbuf();
+ localstat();
+ rcv_ioctl();
+}
+#endif /* defined(CRAY2) && defined(UNICOS5) */
+
+#ifdef LINEMODE
+/*
+ * defer_terminit
+ *
+ * Some things should not be done until after the login process has started
+ * and all the pty modes are set to what they are supposed to be. This
+ * function is called when the pty state has been processed for the first time.
+ * It calls other functions that do things that were deferred in each module.
+ */
+ void
+defer_terminit()
+{
+
+ /*
+ * local stuff that got deferred.
+ */
+ if (def_tspeed != -1) {
+ clientstat(TELOPT_TSPEED, def_tspeed, def_rspeed);
+ def_tspeed = def_rspeed = 0;
+ }
+
+#ifdef TIOCSWINSZ
+ if (def_col || def_row) {
+ struct winsize ws;
+
+ bzero((char *)&ws, sizeof(ws));
+ ws.ws_col = def_col;
+ ws.ws_row = def_row;
+ (void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
+ }
+#endif
+
+ /*
+ * The only other module that currently defers anything.
+ */
+ deferslc();
+
+} /* end of defer_terminit */
+
+/*
+ * terminit
+ *
+ * Returns true if the pty state has been processed yet.
+ */
+ int
+terminit()
+{
+ return(_terminit);
+
+} /* end of terminit */
+#endif /* LINEMODE */
diff --git a/libexec/telnetd/utility.c b/libexec/telnetd/utility.c
new file mode 100644
index 0000000..4f5c2e6
--- /dev/null
+++ b/libexec/telnetd/utility.c
@@ -0,0 +1,1071 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)utility.c 8.2 (Berkeley) 12/15/93";
+#endif /* not lint */
+
+#define PRINTOPTIONS
+#include "telnetd.h"
+
+/*
+ * utility functions performing io related tasks
+ */
+
+/*
+ * ttloop
+ *
+ * A small subroutine to flush the network output buffer, get some data
+ * from the network, and pass it through the telnet state machine. We
+ * also flush the pty input buffer (by dropping its data) if it becomes
+ * too full.
+ */
+
+ void
+ttloop()
+{
+ void netflush();
+
+ DIAG(TD_REPORT, {sprintf(nfrontp, "td: ttloop\r\n");
+ nfrontp += strlen(nfrontp);});
+ if (nfrontp-nbackp) {
+ netflush();
+ }
+ ncc = read(net, netibuf, sizeof netibuf);
+ if (ncc < 0) {
+ syslog(LOG_INFO, "ttloop: read: %m\n");
+ exit(1);
+ } else if (ncc == 0) {
+ syslog(LOG_INFO, "ttloop: peer died: %m\n");
+ exit(1);
+ }
+ DIAG(TD_REPORT, {sprintf(nfrontp, "td: ttloop read %d chars\r\n", ncc);
+ nfrontp += strlen(nfrontp);});
+ netip = netibuf;
+ telrcv(); /* state machine */
+ if (ncc > 0) {
+ pfrontp = pbackp = ptyobuf;
+ telrcv();
+ }
+} /* end of ttloop */
+
+/*
+ * Check a descriptor to see if out of band data exists on it.
+ */
+ int
+stilloob(s)
+ int s; /* socket number */
+{
+ static struct timeval timeout = { 0 };
+ fd_set excepts;
+ int value;
+
+ do {
+ FD_ZERO(&excepts);
+ FD_SET(s, &excepts);
+ value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
+ } while ((value == -1) && (errno == EINTR));
+
+ if (value < 0) {
+ fatalperror(pty, "select");
+ }
+ if (FD_ISSET(s, &excepts)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+ void
+ptyflush()
+{
+ int n;
+
+ if ((n = pfrontp - pbackp) > 0) {
+ DIAG((TD_REPORT | TD_PTYDATA),
+ { sprintf(nfrontp, "td: ptyflush %d chars\r\n", n);
+ nfrontp += strlen(nfrontp); });
+ DIAG(TD_PTYDATA, printdata("pd", pbackp, n));
+ n = write(pty, pbackp, n);
+ }
+ if (n < 0) {
+ if (errno == EWOULDBLOCK || errno == EINTR)
+ return;
+ cleanup(0);
+ }
+ pbackp += n;
+ if (pbackp == pfrontp)
+ pbackp = pfrontp = ptyobuf;
+}
+
+/*
+ * nextitem()
+ *
+ * Return the address of the next "item" in the TELNET data
+ * stream. This will be the address of the next character if
+ * the current address is a user data character, or it will
+ * be the address of the character following the TELNET command
+ * if the current address is a TELNET IAC ("I Am a Command")
+ * character.
+ */
+ char *
+nextitem(current)
+ char *current;
+{
+ if ((*current&0xff) != IAC) {
+ return current+1;
+ }
+ switch (*(current+1)&0xff) {
+ case DO:
+ case DONT:
+ case WILL:
+ case WONT:
+ return current+3;
+ case SB: /* loop forever looking for the SE */
+ {
+ register char *look = current+2;
+
+ for (;;) {
+ if ((*look++&0xff) == IAC) {
+ if ((*look++&0xff) == SE) {
+ return look;
+ }
+ }
+ }
+ }
+ default:
+ return current+2;
+ }
+} /* end of nextitem */
+
+
+/*
+ * netclear()
+ *
+ * We are about to do a TELNET SYNCH operation. Clear
+ * the path to the network.
+ *
+ * Things are a bit tricky since we may have sent the first
+ * byte or so of a previous TELNET command into the network.
+ * So, we have to scan the network buffer from the beginning
+ * until we are up to where we want to be.
+ *
+ * A side effect of what we do, just to keep things
+ * simple, is to clear the urgent data pointer. The principal
+ * caller should be setting the urgent data pointer AFTER calling
+ * us in any case.
+ */
+ void
+netclear()
+{
+ register char *thisitem, *next;
+ char *good;
+#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
+ ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
+
+ thisitem = netobuf;
+
+ while ((next = nextitem(thisitem)) <= nbackp) {
+ thisitem = next;
+ }
+
+ /* Now, thisitem is first before/at boundary. */
+
+ good = netobuf; /* where the good bytes go */
+
+ while (nfrontp > thisitem) {
+ if (wewant(thisitem)) {
+ int length;
+
+ next = thisitem;
+ do {
+ next = nextitem(next);
+ } while (wewant(next) && (nfrontp > next));
+ length = next-thisitem;
+ bcopy(thisitem, good, length);
+ good += length;
+ thisitem = next;
+ } else {
+ thisitem = nextitem(thisitem);
+ }
+ }
+
+ nbackp = netobuf;
+ nfrontp = good; /* next byte to be sent */
+ neturg = 0;
+} /* end of netclear */
+
+/*
+ * netflush
+ * Send as much data as possible to the network,
+ * handling requests for urgent data.
+ */
+ void
+netflush()
+{
+ int n;
+ extern int not42;
+
+ if ((n = nfrontp - nbackp) > 0) {
+ DIAG(TD_REPORT,
+ { sprintf(nfrontp, "td: netflush %d chars\r\n", n);
+ n += strlen(nfrontp); /* get count first */
+ nfrontp += strlen(nfrontp); /* then move pointer */
+ });
+ /*
+ * if no urgent data, or if the other side appears to be an
+ * old 4.2 client (and thus unable to survive TCP urgent data),
+ * write the entire buffer in non-OOB mode.
+ */
+ if ((neturg == 0) || (not42 == 0)) {
+ n = write(net, nbackp, n); /* normal write */
+ } else {
+ n = neturg - nbackp;
+ /*
+ * In 4.2 (and 4.3) systems, there is some question about
+ * what byte in a sendOOB operation is the "OOB" data.
+ * To make ourselves compatible, we only send ONE byte
+ * out of band, the one WE THINK should be OOB (though
+ * we really have more the TCP philosophy of urgent data
+ * rather than the Unix philosophy of OOB data).
+ */
+ if (n > 1) {
+ n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */
+ } else {
+ n = send(net, nbackp, n, MSG_OOB); /* URGENT data */
+ }
+ }
+ }
+ if (n < 0) {
+ if (errno == EWOULDBLOCK || errno == EINTR)
+ return;
+ cleanup(0);
+ }
+ nbackp += n;
+ if (nbackp >= neturg) {
+ neturg = 0;
+ }
+ if (nbackp == nfrontp) {
+ nbackp = nfrontp = netobuf;
+ }
+ return;
+} /* end of netflush */
+
+
+/*
+ * writenet
+ *
+ * Just a handy little function to write a bit of raw data to the net.
+ * It will force a transmit of the buffer if necessary
+ *
+ * arguments
+ * ptr - A pointer to a character string to write
+ * len - How many bytes to write
+ */
+ void
+writenet(ptr, len)
+ register unsigned char *ptr;
+ register int len;
+{
+ /* flush buffer if no room for new data) */
+ if ((&netobuf[BUFSIZ] - nfrontp) < len) {
+ /* if this fails, don't worry, buffer is a little big */
+ netflush();
+ }
+
+ bcopy(ptr, nfrontp, len);
+ nfrontp += len;
+
+} /* end of writenet */
+
+
+/*
+ * miscellaneous functions doing a variety of little jobs follow ...
+ */
+
+
+ void
+fatal(f, msg)
+ int f;
+ char *msg;
+{
+ char buf[BUFSIZ];
+
+ (void) sprintf(buf, "telnetd: %s.\r\n", msg);
+ (void) write(f, buf, (int)strlen(buf));
+ sleep(1); /*XXX*/
+ exit(1);
+}
+
+ void
+fatalperror(f, msg)
+ int f;
+ char *msg;
+{
+ char buf[BUFSIZ], *strerror();
+
+ (void) sprintf(buf, "%s: %s\r\n", msg, strerror(errno));
+ fatal(f, buf);
+}
+
+char editedhost[32];
+
+ void
+edithost(pat, host)
+ register char *pat;
+ register char *host;
+{
+ register char *res = editedhost;
+ char *strncpy();
+
+ if (!pat)
+ pat = "";
+ while (*pat) {
+ switch (*pat) {
+
+ case '#':
+ if (*host)
+ host++;
+ break;
+
+ case '@':
+ if (*host)
+ *res++ = *host++;
+ break;
+
+ default:
+ *res++ = *pat;
+ break;
+ }
+ if (res == &editedhost[sizeof editedhost - 1]) {
+ *res = '\0';
+ return;
+ }
+ pat++;
+ }
+ if (*host)
+ (void) strncpy(res, host,
+ sizeof editedhost - (res - editedhost) -1);
+ else
+ *res = '\0';
+ editedhost[sizeof editedhost - 1] = '\0';
+}
+
+static char *putlocation;
+
+ void
+putstr(s)
+ register char *s;
+{
+
+ while (*s)
+ putchr(*s++);
+}
+
+ void
+putchr(cc)
+ int cc;
+{
+ *putlocation++ = cc;
+}
+
+/*
+ * This is split on two lines so that SCCS will not see the M
+ * between two % signs and expand it...
+ */
+static char fmtstr[] = { "%l:%M\
+%P on %A, %d %B %Y" };
+
+ void
+putf(cp, where)
+ register char *cp;
+ char *where;
+{
+ char *slash;
+ time_t t;
+ char db[100];
+#ifdef STREAMSPTY
+ extern char *index();
+#else
+ extern char *rindex();
+#endif
+
+ putlocation = where;
+
+ while (*cp) {
+ if (*cp != '%') {
+ putchr(*cp++);
+ continue;
+ }
+ switch (*++cp) {
+
+ case 't':
+#ifdef STREAMSPTY
+ /* names are like /dev/pts/2 -- we want pts/2 */
+ slash = index(line+1, '/');
+#else
+ slash = rindex(line, '/');
+#endif
+ if (slash == (char *) 0)
+ putstr(line);
+ else
+ putstr(&slash[1]);
+ break;
+
+ case 'h':
+ putstr(editedhost);
+ break;
+
+ case 'd':
+ (void)time(&t);
+ (void)strftime(db, sizeof(db), fmtstr, localtime(&t));
+ putstr(db);
+ break;
+
+ case '%':
+ putchr('%');
+ break;
+ }
+ cp++;
+ }
+}
+
+#ifdef DIAGNOSTICS
+/*
+ * Print telnet options and commands in plain text, if possible.
+ */
+ void
+printoption(fmt, option)
+ register char *fmt;
+ register int option;
+{
+ if (TELOPT_OK(option))
+ sprintf(nfrontp, "%s %s\r\n", fmt, TELOPT(option));
+ else if (TELCMD_OK(option))
+ sprintf(nfrontp, "%s %s\r\n", fmt, TELCMD(option));
+ else
+ sprintf(nfrontp, "%s %d\r\n", fmt, option);
+ nfrontp += strlen(nfrontp);
+ return;
+}
+
+ void
+printsub(direction, pointer, length)
+ char direction; /* '<' or '>' */
+ unsigned char *pointer; /* where suboption data sits */
+ int length; /* length of suboption data */
+{
+ register int i;
+ char buf[512];
+
+ if (!(diagnostic & TD_OPTIONS))
+ return;
+
+ if (direction) {
+ sprintf(nfrontp, "td: %s suboption ",
+ direction == '<' ? "recv" : "send");
+ nfrontp += strlen(nfrontp);
+ if (length >= 3) {
+ register int j;
+
+ i = pointer[length-2];
+ j = pointer[length-1];
+
+ if (i != IAC || j != SE) {
+ sprintf(nfrontp, "(terminated by ");
+ nfrontp += strlen(nfrontp);
+ if (TELOPT_OK(i))
+ sprintf(nfrontp, "%s ", TELOPT(i));
+ else if (TELCMD_OK(i))
+ sprintf(nfrontp, "%s ", TELCMD(i));
+ else
+ sprintf(nfrontp, "%d ", i);
+ nfrontp += strlen(nfrontp);
+ if (TELOPT_OK(j))
+ sprintf(nfrontp, "%s", TELOPT(j));
+ else if (TELCMD_OK(j))
+ sprintf(nfrontp, "%s", TELCMD(j));
+ else
+ sprintf(nfrontp, "%d", j);
+ nfrontp += strlen(nfrontp);
+ sprintf(nfrontp, ", not IAC SE!) ");
+ nfrontp += strlen(nfrontp);
+ }
+ }
+ length -= 2;
+ }
+ if (length < 1) {
+ sprintf(nfrontp, "(Empty suboption??\?)");
+ nfrontp += strlen(nfrontp);
+ return;
+ }
+ switch (pointer[0]) {
+ case TELOPT_TTYPE:
+ sprintf(nfrontp, "TERMINAL-TYPE ");
+ nfrontp += strlen(nfrontp);
+ switch (pointer[1]) {
+ case TELQUAL_IS:
+ sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2);
+ break;
+ case TELQUAL_SEND:
+ sprintf(nfrontp, "SEND");
+ break;
+ default:
+ sprintf(nfrontp,
+ "- unknown qualifier %d (0x%x).",
+ pointer[1], pointer[1]);
+ }
+ nfrontp += strlen(nfrontp);
+ break;
+ case TELOPT_TSPEED:
+ sprintf(nfrontp, "TERMINAL-SPEED");
+ nfrontp += strlen(nfrontp);
+ if (length < 2) {
+ sprintf(nfrontp, " (empty suboption??\?)");
+ nfrontp += strlen(nfrontp);
+ break;
+ }
+ switch (pointer[1]) {
+ case TELQUAL_IS:
+ sprintf(nfrontp, " IS %.*s", length-2, (char *)pointer+2);
+ nfrontp += strlen(nfrontp);
+ break;
+ default:
+ if (pointer[1] == 1)
+ sprintf(nfrontp, " SEND");
+ else
+ sprintf(nfrontp, " %d (unknown)", pointer[1]);
+ nfrontp += strlen(nfrontp);
+ for (i = 2; i < length; i++) {
+ sprintf(nfrontp, " ?%d?", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ }
+ break;
+ }
+ break;
+
+ case TELOPT_LFLOW:
+ sprintf(nfrontp, "TOGGLE-FLOW-CONTROL");
+ nfrontp += strlen(nfrontp);
+ if (length < 2) {
+ sprintf(nfrontp, " (empty suboption??\?)");
+ nfrontp += strlen(nfrontp);
+ break;
+ }
+ switch (pointer[1]) {
+ case LFLOW_OFF:
+ sprintf(nfrontp, " OFF"); break;
+ case LFLOW_ON:
+ sprintf(nfrontp, " ON"); break;
+ case LFLOW_RESTART_ANY:
+ sprintf(nfrontp, " RESTART-ANY"); break;
+ case LFLOW_RESTART_XON:
+ sprintf(nfrontp, " RESTART-XON"); break;
+ default:
+ sprintf(nfrontp, " %d (unknown)", pointer[1]);
+ }
+ nfrontp += strlen(nfrontp);
+ for (i = 2; i < length; i++) {
+ sprintf(nfrontp, " ?%d?", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ }
+ break;
+
+ case TELOPT_NAWS:
+ sprintf(nfrontp, "NAWS");
+ nfrontp += strlen(nfrontp);
+ if (length < 2) {
+ sprintf(nfrontp, " (empty suboption??\?)");
+ nfrontp += strlen(nfrontp);
+ break;
+ }
+ if (length == 2) {
+ sprintf(nfrontp, " ?%d?", pointer[1]);
+ nfrontp += strlen(nfrontp);
+ break;
+ }
+ sprintf(nfrontp, " %d %d (%d)",
+ pointer[1], pointer[2],
+ (int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
+ nfrontp += strlen(nfrontp);
+ if (length == 4) {
+ sprintf(nfrontp, " ?%d?", pointer[3]);
+ nfrontp += strlen(nfrontp);
+ break;
+ }
+ sprintf(nfrontp, " %d %d (%d)",
+ pointer[3], pointer[4],
+ (int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
+ nfrontp += strlen(nfrontp);
+ for (i = 5; i < length; i++) {
+ sprintf(nfrontp, " ?%d?", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ }
+ break;
+
+ case TELOPT_LINEMODE:
+ sprintf(nfrontp, "LINEMODE ");
+ nfrontp += strlen(nfrontp);
+ if (length < 2) {
+ sprintf(nfrontp, " (empty suboption??\?)");
+ nfrontp += strlen(nfrontp);
+ break;
+ }
+ switch (pointer[1]) {
+ case WILL:
+ sprintf(nfrontp, "WILL ");
+ goto common;
+ case WONT:
+ sprintf(nfrontp, "WONT ");
+ goto common;
+ case DO:
+ sprintf(nfrontp, "DO ");
+ goto common;
+ case DONT:
+ sprintf(nfrontp, "DONT ");
+ common:
+ nfrontp += strlen(nfrontp);
+ if (length < 3) {
+ sprintf(nfrontp, "(no option??\?)");
+ nfrontp += strlen(nfrontp);
+ break;
+ }
+ switch (pointer[2]) {
+ case LM_FORWARDMASK:
+ sprintf(nfrontp, "Forward Mask");
+ nfrontp += strlen(nfrontp);
+ for (i = 3; i < length; i++) {
+ sprintf(nfrontp, " %x", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ }
+ break;
+ default:
+ sprintf(nfrontp, "%d (unknown)", pointer[2]);
+ nfrontp += strlen(nfrontp);
+ for (i = 3; i < length; i++) {
+ sprintf(nfrontp, " %d", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ }
+ break;
+ }
+ break;
+
+ case LM_SLC:
+ sprintf(nfrontp, "SLC");
+ nfrontp += strlen(nfrontp);
+ for (i = 2; i < length - 2; i += 3) {
+ if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
+ sprintf(nfrontp, " %s", SLC_NAME(pointer[i+SLC_FUNC]));
+ else
+ sprintf(nfrontp, " %d", pointer[i+SLC_FUNC]);
+ nfrontp += strlen(nfrontp);
+ switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
+ case SLC_NOSUPPORT:
+ sprintf(nfrontp, " NOSUPPORT"); break;
+ case SLC_CANTCHANGE:
+ sprintf(nfrontp, " CANTCHANGE"); break;
+ case SLC_VARIABLE:
+ sprintf(nfrontp, " VARIABLE"); break;
+ case SLC_DEFAULT:
+ sprintf(nfrontp, " DEFAULT"); break;
+ }
+ nfrontp += strlen(nfrontp);
+ sprintf(nfrontp, "%s%s%s",
+ pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
+ pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
+ pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
+ nfrontp += strlen(nfrontp);
+ if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
+ SLC_FLUSHOUT| SLC_LEVELBITS)) {
+ sprintf(nfrontp, "(0x%x)", pointer[i+SLC_FLAGS]);
+ nfrontp += strlen(nfrontp);
+ }
+ sprintf(nfrontp, " %d;", pointer[i+SLC_VALUE]);
+ nfrontp += strlen(nfrontp);
+ if ((pointer[i+SLC_VALUE] == IAC) &&
+ (pointer[i+SLC_VALUE+1] == IAC))
+ i++;
+ }
+ for (; i < length; i++) {
+ sprintf(nfrontp, " ?%d?", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ }
+ break;
+
+ case LM_MODE:
+ sprintf(nfrontp, "MODE ");
+ nfrontp += strlen(nfrontp);
+ if (length < 3) {
+ sprintf(nfrontp, "(no mode??\?)");
+ nfrontp += strlen(nfrontp);
+ break;
+ }
+ {
+ char tbuf[32];
+ sprintf(tbuf, "%s%s%s%s%s",
+ pointer[2]&MODE_EDIT ? "|EDIT" : "",
+ pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
+ pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
+ pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
+ pointer[2]&MODE_ACK ? "|ACK" : "");
+ sprintf(nfrontp, "%s", tbuf[1] ? &tbuf[1] : "0");
+ nfrontp += strlen(nfrontp);
+ }
+ if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
+ sprintf(nfrontp, " (0x%x)", pointer[2]);
+ nfrontp += strlen(nfrontp);
+ }
+ for (i = 3; i < length; i++) {
+ sprintf(nfrontp, " ?0x%x?", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ }
+ break;
+ default:
+ sprintf(nfrontp, "%d (unknown)", pointer[1]);
+ nfrontp += strlen(nfrontp);
+ for (i = 2; i < length; i++) {
+ sprintf(nfrontp, " %d", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ }
+ }
+ break;
+
+ case TELOPT_STATUS: {
+ register char *cp;
+ register int j, k;
+
+ sprintf(nfrontp, "STATUS");
+ nfrontp += strlen(nfrontp);
+
+ switch (pointer[1]) {
+ default:
+ if (pointer[1] == TELQUAL_SEND)
+ sprintf(nfrontp, " SEND");
+ else
+ sprintf(nfrontp, " %d (unknown)", pointer[1]);
+ nfrontp += strlen(nfrontp);
+ for (i = 2; i < length; i++) {
+ sprintf(nfrontp, " ?%d?", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ }
+ break;
+ case TELQUAL_IS:
+ sprintf(nfrontp, " IS\r\n");
+ nfrontp += strlen(nfrontp);
+
+ for (i = 2; i < length; i++) {
+ switch(pointer[i]) {
+ case DO: cp = "DO"; goto common2;
+ case DONT: cp = "DONT"; goto common2;
+ case WILL: cp = "WILL"; goto common2;
+ case WONT: cp = "WONT"; goto common2;
+ common2:
+ i++;
+ if (TELOPT_OK(pointer[i]))
+ sprintf(nfrontp, " %s %s", cp, TELOPT(pointer[i]));
+ else
+ sprintf(nfrontp, " %s %d", cp, pointer[i]);
+ nfrontp += strlen(nfrontp);
+
+ sprintf(nfrontp, "\r\n");
+ nfrontp += strlen(nfrontp);
+ break;
+
+ case SB:
+ sprintf(nfrontp, " SB ");
+ nfrontp += strlen(nfrontp);
+ i++;
+ j = k = i;
+ while (j < length) {
+ if (pointer[j] == SE) {
+ if (j+1 == length)
+ break;
+ if (pointer[j+1] == SE)
+ j++;
+ else
+ break;
+ }
+ pointer[k++] = pointer[j++];
+ }
+ printsub(0, &pointer[i], k - i);
+ if (i < length) {
+ sprintf(nfrontp, " SE");
+ nfrontp += strlen(nfrontp);
+ i = j;
+ } else
+ i = j - 1;
+
+ sprintf(nfrontp, "\r\n");
+ nfrontp += strlen(nfrontp);
+
+ break;
+
+ default:
+ sprintf(nfrontp, " %d", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ }
+
+ case TELOPT_XDISPLOC:
+ sprintf(nfrontp, "X-DISPLAY-LOCATION ");
+ nfrontp += strlen(nfrontp);
+ switch (pointer[1]) {
+ case TELQUAL_IS:
+ sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2);
+ break;
+ case TELQUAL_SEND:
+ sprintf(nfrontp, "SEND");
+ break;
+ default:
+ sprintf(nfrontp, "- unknown qualifier %d (0x%x).",
+ pointer[1], pointer[1]);
+ }
+ nfrontp += strlen(nfrontp);
+ break;
+
+ case TELOPT_NEW_ENVIRON:
+ sprintf(nfrontp, "NEW-ENVIRON ");
+ goto env_common1;
+ case TELOPT_OLD_ENVIRON:
+ sprintf(nfrontp, "OLD-ENVIRON");
+ env_common1:
+ nfrontp += strlen(nfrontp);
+ switch (pointer[1]) {
+ case TELQUAL_IS:
+ sprintf(nfrontp, "IS ");
+ goto env_common;
+ case TELQUAL_SEND:
+ sprintf(nfrontp, "SEND ");
+ goto env_common;
+ case TELQUAL_INFO:
+ sprintf(nfrontp, "INFO ");
+ env_common:
+ nfrontp += strlen(nfrontp);
+ {
+ register int noquote = 2;
+ for (i = 2; i < length; i++ ) {
+ switch (pointer[i]) {
+ case NEW_ENV_VAR:
+ sprintf(nfrontp, "\" VAR " + noquote);
+ nfrontp += strlen(nfrontp);
+ noquote = 2;
+ break;
+
+ case NEW_ENV_VALUE:
+ sprintf(nfrontp, "\" VALUE " + noquote);
+ nfrontp += strlen(nfrontp);
+ noquote = 2;
+ break;
+
+ case ENV_ESC:
+ sprintf(nfrontp, "\" ESC " + noquote);
+ nfrontp += strlen(nfrontp);
+ noquote = 2;
+ break;
+
+ case ENV_USERVAR:
+ sprintf(nfrontp, "\" USERVAR " + noquote);
+ nfrontp += strlen(nfrontp);
+ noquote = 2;
+ break;
+
+ default:
+ def_case:
+ if (isprint(pointer[i]) && pointer[i] != '"') {
+ if (noquote) {
+ *nfrontp++ = '"';
+ noquote = 0;
+ }
+ *nfrontp++ = pointer[i];
+ } else {
+ sprintf(nfrontp, "\" %03o " + noquote,
+ pointer[i]);
+ nfrontp += strlen(nfrontp);
+ noquote = 2;
+ }
+ break;
+ }
+ }
+ if (!noquote)
+ *nfrontp++ = '"';
+ break;
+ }
+ }
+ break;
+
+#if defined(AUTHENTICATION)
+ case TELOPT_AUTHENTICATION:
+ sprintf(nfrontp, "AUTHENTICATION");
+ nfrontp += strlen(nfrontp);
+
+ if (length < 2) {
+ sprintf(nfrontp, " (empty suboption??\?)");
+ nfrontp += strlen(nfrontp);
+ break;
+ }
+ switch (pointer[1]) {
+ case TELQUAL_REPLY:
+ case TELQUAL_IS:
+ sprintf(nfrontp, " %s ", (pointer[1] == TELQUAL_IS) ?
+ "IS" : "REPLY");
+ nfrontp += strlen(nfrontp);
+ if (AUTHTYPE_NAME_OK(pointer[2]))
+ sprintf(nfrontp, "%s ", AUTHTYPE_NAME(pointer[2]));
+ else
+ sprintf(nfrontp, "%d ", pointer[2]);
+ nfrontp += strlen(nfrontp);
+ if (length < 3) {
+ sprintf(nfrontp, "(partial suboption??\?)");
+ nfrontp += strlen(nfrontp);
+ break;
+ }
+ sprintf(nfrontp, "%s|%s",
+ ((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
+ "CLIENT" : "SERVER",
+ ((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
+ "MUTUAL" : "ONE-WAY");
+ nfrontp += strlen(nfrontp);
+
+ auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
+ sprintf(nfrontp, "%s", buf);
+ nfrontp += strlen(nfrontp);
+ break;
+
+ case TELQUAL_SEND:
+ i = 2;
+ sprintf(nfrontp, " SEND ");
+ nfrontp += strlen(nfrontp);
+ while (i < length) {
+ if (AUTHTYPE_NAME_OK(pointer[i]))
+ sprintf(nfrontp, "%s ", AUTHTYPE_NAME(pointer[i]));
+ else
+ sprintf(nfrontp, "%d ", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ if (++i >= length) {
+ sprintf(nfrontp, "(partial suboption??\?)");
+ nfrontp += strlen(nfrontp);
+ break;
+ }
+ sprintf(nfrontp, "%s|%s ",
+ ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
+ "CLIENT" : "SERVER",
+ ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
+ "MUTUAL" : "ONE-WAY");
+ nfrontp += strlen(nfrontp);
+ ++i;
+ }
+ break;
+
+ case TELQUAL_NAME:
+ i = 2;
+ sprintf(nfrontp, " NAME \"");
+ nfrontp += strlen(nfrontp);
+ while (i < length)
+ *nfrontp += pointer[i++];
+ *nfrontp += '"';
+ break;
+
+ default:
+ for (i = 2; i < length; i++) {
+ sprintf(nfrontp, " ?%d?", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ }
+ break;
+ }
+ break;
+#endif
+
+
+ default:
+ if (TELOPT_OK(pointer[0]))
+ sprintf(nfrontp, "%s (unknown)", TELOPT(pointer[0]));
+ else
+ sprintf(nfrontp, "%d (unknown)", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ for (i = 1; i < length; i++) {
+ sprintf(nfrontp, " %d", pointer[i]);
+ nfrontp += strlen(nfrontp);
+ }
+ break;
+ }
+ sprintf(nfrontp, "\r\n");
+ nfrontp += strlen(nfrontp);
+}
+
+/*
+ * Dump a data buffer in hex and ascii to the output data stream.
+ */
+ void
+printdata(tag, ptr, cnt)
+ register char *tag;
+ register char *ptr;
+ register int cnt;
+{
+ register int i;
+ char xbuf[30];
+
+ while (cnt) {
+ /* flush net output buffer if no room for new data) */
+ if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
+ netflush();
+ }
+
+ /* add a line of output */
+ sprintf(nfrontp, "%s: ", tag);
+ nfrontp += strlen(nfrontp);
+ for (i = 0; i < 20 && cnt; i++) {
+ sprintf(nfrontp, "%02x", *ptr);
+ nfrontp += strlen(nfrontp);
+ if (isprint(*ptr)) {
+ xbuf[i] = *ptr;
+ } else {
+ xbuf[i] = '.';
+ }
+ if (i % 2) {
+ *nfrontp = ' ';
+ nfrontp++;
+ }
+ cnt--;
+ ptr++;
+ }
+ xbuf[i] = '\0';
+ sprintf(nfrontp, " %s\r\n", xbuf );
+ nfrontp += strlen(nfrontp);
+ }
+}
+#endif /* DIAGNOSTICS */
diff --git a/libexec/tftpd/Makefile b/libexec/tftpd/Makefile
new file mode 100644
index 0000000..728e2a2
--- /dev/null
+++ b/libexec/tftpd/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+
+PROG= tftpd
+SRCS= tftpd.c tftpsubs.c
+MAN8= tftpd.8
+CFLAGS+=-I${.CURDIR}/../../usr.bin/tftp
+.PATH: ${.CURDIR}/../../usr.bin/tftp
+
+.include <bsd.prog.mk>
diff --git a/libexec/tftpd/tftpd.8 b/libexec/tftpd/tftpd.8
new file mode 100644
index 0000000..430c1c4
--- /dev/null
+++ b/libexec/tftpd/tftpd.8
@@ -0,0 +1,106 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tftpd.8 8.1 (Berkeley) 6/4/93
+.\"
+.Dd June 4, 1993
+.Dt TFTPD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm tftpd
+.Nd
+Internet Trivial File Transfer Protocol server
+.Sh SYNOPSIS
+.Nm tftpd
+.Op Fl l
+.Op Fl n
+.Op Ar directory ...
+.Sh DESCRIPTION
+.Nm Tftpd
+is a server which supports the
+Internet Trivial File Transfer
+Protocol (\c
+.Tn RFC 783).
+The
+.Tn TFTP
+server operates
+at the port indicated in the
+.Ql tftp
+service description;
+see
+.Xr services 5 .
+The server is normally started by
+.Xr inetd 8 .
+.Pp
+The use of
+.Xr tftp 1
+does not require an account or password on the remote system.
+Due to the lack of authentication information,
+.Nm tftpd
+will allow only publicly readable files to be
+accessed.
+Files containing the string ``/\|\fB.\|.\fP\|/'' are not allowed.
+Files may be written only if they already exist and are publicly writable.
+Note that this extends the concept of
+.Dq public
+to include
+all users on all hosts that can be reached through the network;
+this may not be appropriate on all systems, and its implications
+should be considered before enabling tftp service.
+The server should have the user ID with the lowest possible privilege.
+.Pp
+Access to files may be restricted by invoking
+.Nm tftpd
+with a list of directories by including up to 20 pathnames
+as server program arguments in
+.Pa /etc/inetd.conf .
+In this case access is restricted to files whose
+names are prefixed by the one of the given directories.
+The given directories are also treated as a search path for
+relative filename requests.
+.Pp
+The options are:
+.Bl -tag -width Ds
+.It Fl l
+Logs all requests using
+.Xr syslog 3 .
+.It Fl n
+Suppresses negative acknowledgement of requests for nonexistent
+relative filenames.
+.El
+.Sh SEE ALSO
+.Xr tftp 1 ,
+.Xr inetd 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/libexec/tftpd/tftpd.c b/libexec/tftpd/tftpd.c
new file mode 100644
index 0000000..7500abf
--- /dev/null
+++ b/libexec/tftpd/tftpd.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+/*
+ * Trivial file transfer protocol server.
+ *
+ * This version includes many modifications by Jim Guyton
+ * <guyton@rand-unix>.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "tftpsubs.h"
+
+#define TIMEOUT 5
+
+int peer;
+int rexmtval = TIMEOUT;
+int maxtimeout = 5*TIMEOUT;
+
+#define PKTSIZE SEGSIZE+4
+char buf[PKTSIZE];
+char ackbuf[PKTSIZE];
+struct sockaddr_in from;
+int fromlen;
+
+void tftp __P((struct tftphdr *, int));
+
+/*
+ * Null-terminated directory prefix list for absolute pathname requests and
+ * search list for relative pathname requests.
+ *
+ * MAXDIRS should be at least as large as the number of arguments that
+ * inetd allows (currently 20).
+ */
+#define MAXDIRS 20
+static struct dirlist {
+ char *name;
+ int len;
+} dirs[MAXDIRS+1];
+static int suppress_naks;
+static int logging;
+
+static char *errtomsg __P((int));
+static void nak __P((int));
+static char *verifyhost __P((struct sockaddr_in *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct tftphdr *tp;
+ register int n;
+ int ch, on;
+ struct sockaddr_in sin;
+
+ openlog("tftpd", LOG_PID, LOG_FTP);
+ while ((ch = getopt(argc, argv, "ln")) != EOF) {
+ switch (ch) {
+ case 'l':
+ logging = 1;
+ break;
+ case 'n':
+ suppress_naks = 1;
+ break;
+ default:
+ syslog(LOG_WARNING, "ignoring unknown option -%c", ch);
+ }
+ }
+ if (optind < argc) {
+ struct dirlist *dirp;
+
+ /* Get list of directory prefixes. Skip relative pathnames. */
+ for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS];
+ optind++) {
+ if (argv[optind][0] == '/') {
+ dirp->name = argv[optind];
+ dirp->len = strlen(dirp->name);
+ dirp++;
+ }
+ }
+ }
+
+ on = 1;
+ if (ioctl(0, FIONBIO, &on) < 0) {
+ syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
+ exit(1);
+ }
+ fromlen = sizeof (from);
+ n = recvfrom(0, buf, sizeof (buf), 0,
+ (struct sockaddr *)&from, &fromlen);
+ if (n < 0) {
+ syslog(LOG_ERR, "recvfrom: %m\n");
+ exit(1);
+ }
+ /*
+ * Now that we have read the message out of the UDP
+ * socket, we fork and exit. Thus, inetd will go back
+ * to listening to the tftp port, and the next request
+ * to come in will start up a new instance of tftpd.
+ *
+ * We do this so that inetd can run tftpd in "wait" mode.
+ * The problem with tftpd running in "nowait" mode is that
+ * inetd may get one or more successful "selects" on the
+ * tftp port before we do our receive, so more than one
+ * instance of tftpd may be started up. Worse, if tftpd
+ * break before doing the above "recvfrom", inetd would
+ * spawn endless instances, clogging the system.
+ */
+ {
+ int pid;
+ int i, j;
+
+ for (i = 1; i < 20; i++) {
+ pid = fork();
+ if (pid < 0) {
+ sleep(i);
+ /*
+ * flush out to most recently sent request.
+ *
+ * This may drop some request, but those
+ * will be resent by the clients when
+ * they timeout. The positive effect of
+ * this flush is to (try to) prevent more
+ * than one tftpd being started up to service
+ * a single request from a single client.
+ */
+ j = sizeof from;
+ i = recvfrom(0, buf, sizeof (buf), 0,
+ (struct sockaddr *)&from, &j);
+ if (i > 0) {
+ n = i;
+ fromlen = j;
+ }
+ } else {
+ break;
+ }
+ }
+ if (pid < 0) {
+ syslog(LOG_ERR, "fork: %m\n");
+ exit(1);
+ } else if (pid != 0) {
+ exit(0);
+ }
+ }
+ from.sin_family = AF_INET;
+ alarm(0);
+ close(0);
+ close(1);
+ peer = socket(AF_INET, SOCK_DGRAM, 0);
+ if (peer < 0) {
+ syslog(LOG_ERR, "socket: %m\n");
+ exit(1);
+ }
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+ syslog(LOG_ERR, "bind: %m\n");
+ exit(1);
+ }
+ if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
+ syslog(LOG_ERR, "connect: %m\n");
+ exit(1);
+ }
+ tp = (struct tftphdr *)buf;
+ tp->th_opcode = ntohs(tp->th_opcode);
+ if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
+ tftp(tp, n);
+ exit(1);
+}
+
+struct formats;
+int validate_access __P((char **, int));
+void sendfile __P((struct formats *));
+void recvfile __P((struct formats *));
+
+struct formats {
+ char *f_mode;
+ int (*f_validate) __P((char **, int));
+ void (*f_send) __P((struct formats *));
+ void (*f_recv) __P((struct formats *));
+ int f_convert;
+} formats[] = {
+ { "netascii", validate_access, sendfile, recvfile, 1 },
+ { "octet", validate_access, sendfile, recvfile, 0 },
+#ifdef notdef
+ { "mail", validate_user, sendmail, recvmail, 1 },
+#endif
+ { 0 }
+};
+
+/*
+ * Handle initial connection protocol.
+ */
+void
+tftp(tp, size)
+ struct tftphdr *tp;
+ int size;
+{
+ register char *cp;
+ int first = 1, ecode;
+ register struct formats *pf;
+ char *filename, *mode;
+
+ filename = cp = tp->th_stuff;
+again:
+ while (cp < buf + size) {
+ if (*cp == '\0')
+ break;
+ cp++;
+ }
+ if (*cp != '\0') {
+ nak(EBADOP);
+ exit(1);
+ }
+ if (first) {
+ mode = ++cp;
+ first = 0;
+ goto again;
+ }
+ for (cp = mode; *cp; cp++)
+ if (isupper(*cp))
+ *cp = tolower(*cp);
+ for (pf = formats; pf->f_mode; pf++)
+ if (strcmp(pf->f_mode, mode) == 0)
+ break;
+ if (pf->f_mode == 0) {
+ nak(EBADOP);
+ exit(1);
+ }
+ ecode = (*pf->f_validate)(&filename, tp->th_opcode);
+ if (logging) {
+ syslog(LOG_INFO, "%s: %s request for %s: %s",
+ verifyhost(&from),
+ tp->th_opcode == WRQ ? "write" : "read",
+ filename, errtomsg(ecode));
+ }
+ if (ecode) {
+ /*
+ * Avoid storms of naks to a RRQ broadcast for a relative
+ * bootfile pathname from a diskless Sun.
+ */
+ if (suppress_naks && *filename != '/' && ecode == ENOTFOUND)
+ exit(0);
+ nak(ecode);
+ exit(1);
+ }
+ if (tp->th_opcode == WRQ)
+ (*pf->f_recv)(pf);
+ else
+ (*pf->f_send)(pf);
+ exit(0);
+}
+
+
+FILE *file;
+
+/*
+ * Validate file access. Since we
+ * have no uid or gid, for now require
+ * file to exist and be publicly
+ * readable/writable.
+ * If we were invoked with arguments
+ * from inetd then the file must also be
+ * in one of the given directory prefixes.
+ * Note also, full path name must be
+ * given as we have no login directory.
+ */
+int
+validate_access(filep, mode)
+ char **filep;
+ int mode;
+{
+ struct stat stbuf;
+ int fd;
+ struct dirlist *dirp;
+ static char pathname[MAXPATHLEN];
+ char *filename = *filep;
+
+ /*
+ * Prevent tricksters from getting around the directory restrictions
+ */
+ if (strstr(filename, "/../"))
+ return (EACCESS);
+
+ if (*filename == '/') {
+ /*
+ * Allow the request if it's in one of the approved locations.
+ * Special case: check the null prefix ("/") by looking
+ * for length = 1 and relying on the arg. processing that
+ * it's a /.
+ */
+ for (dirp = dirs; dirp->name != NULL; dirp++) {
+ if (dirp->len == 1 ||
+ (!strncmp(filename, dirp->name, dirp->len) &&
+ filename[dirp->len] == '/'))
+ break;
+ }
+ /* If directory list is empty, allow access to any file */
+ if (dirp->name == NULL && dirp != dirs)
+ return (EACCESS);
+ if (stat(filename, &stbuf) < 0)
+ return (errno == ENOENT ? ENOTFOUND : EACCESS);
+ if ((stbuf.st_mode & S_IFMT) != S_IFREG)
+ return (ENOTFOUND);
+ if (mode == RRQ) {
+ if ((stbuf.st_mode & S_IROTH) == 0)
+ return (EACCESS);
+ } else {
+ if ((stbuf.st_mode & S_IWOTH) == 0)
+ return (EACCESS);
+ }
+ } else {
+ int err;
+
+ /*
+ * Relative file name: search the approved locations for it.
+ * Don't allow write requests that avoid directory
+ * restrictions.
+ */
+
+ if (!strncmp(filename, "../", 3))
+ return (EACCESS);
+
+ /*
+ * If the file exists in one of the directories and isn't
+ * readable, continue looking. However, change the error code
+ * to give an indication that the file exists.
+ */
+ err = ENOTFOUND;
+ for (dirp = dirs; dirp->name != NULL; dirp++) {
+ sprintf(pathname, "%s/%s", dirp->name, filename);
+ if (stat(pathname, &stbuf) == 0 &&
+ (stbuf.st_mode & S_IFMT) == S_IFREG) {
+ if ((stbuf.st_mode & S_IROTH) != 0) {
+ break;
+ }
+ err = EACCESS;
+ }
+ }
+ if (dirp->name == NULL)
+ return (err);
+ *filep = filename = pathname;
+ }
+ fd = open(filename, mode == RRQ ? 0 : 1);
+ if (fd < 0)
+ return (errno + 100);
+ file = fdopen(fd, (mode == RRQ)? "r":"w");
+ if (file == NULL) {
+ return errno+100;
+ }
+ return (0);
+}
+
+int timeout;
+jmp_buf timeoutbuf;
+
+void
+timer()
+{
+
+ timeout += rexmtval;
+ if (timeout >= maxtimeout)
+ exit(1);
+ longjmp(timeoutbuf, 1);
+}
+
+/*
+ * Send the requested file.
+ */
+void
+sendfile(pf)
+ struct formats *pf;
+{
+ struct tftphdr *dp, *r_init();
+ register struct tftphdr *ap; /* ack packet */
+ register int size, n;
+ volatile int block;
+
+ signal(SIGALRM, timer);
+ dp = r_init();
+ ap = (struct tftphdr *)ackbuf;
+ block = 1;
+ do {
+ size = readit(file, &dp, pf->f_convert);
+ if (size < 0) {
+ nak(errno + 100);
+ goto abort;
+ }
+ dp->th_opcode = htons((u_short)DATA);
+ dp->th_block = htons((u_short)block);
+ timeout = 0;
+ (void)setjmp(timeoutbuf);
+
+send_data:
+ if (send(peer, dp, size + 4, 0) != size + 4) {
+ syslog(LOG_ERR, "tftpd: write: %m\n");
+ goto abort;
+ }
+ read_ahead(file, pf->f_convert);
+ for ( ; ; ) {
+ alarm(rexmtval); /* read the ack */
+ n = recv(peer, ackbuf, sizeof (ackbuf), 0);
+ alarm(0);
+ if (n < 0) {
+ syslog(LOG_ERR, "tftpd: read: %m\n");
+ goto abort;
+ }
+ ap->th_opcode = ntohs((u_short)ap->th_opcode);
+ ap->th_block = ntohs((u_short)ap->th_block);
+
+ if (ap->th_opcode == ERROR)
+ goto abort;
+
+ if (ap->th_opcode == ACK) {
+ if (ap->th_block == block)
+ break;
+ /* Re-synchronize with the other side */
+ (void) synchnet(peer);
+ if (ap->th_block == (block -1))
+ goto send_data;
+ }
+
+ }
+ block++;
+ } while (size == SEGSIZE);
+abort:
+ (void) fclose(file);
+}
+
+void
+justquit()
+{
+ exit(0);
+}
+
+
+/*
+ * Receive a file.
+ */
+void
+recvfile(pf)
+ struct formats *pf;
+{
+ struct tftphdr *dp, *w_init();
+ register struct tftphdr *ap; /* ack buffer */
+ register int n, size;
+ volatile int block;
+
+ signal(SIGALRM, timer);
+ dp = w_init();
+ ap = (struct tftphdr *)ackbuf;
+ block = 0;
+ do {
+ timeout = 0;
+ ap->th_opcode = htons((u_short)ACK);
+ ap->th_block = htons((u_short)block);
+ block++;
+ (void) setjmp(timeoutbuf);
+send_ack:
+ if (send(peer, ackbuf, 4, 0) != 4) {
+ syslog(LOG_ERR, "tftpd: write: %m\n");
+ goto abort;
+ }
+ write_behind(file, pf->f_convert);
+ for ( ; ; ) {
+ alarm(rexmtval);
+ n = recv(peer, dp, PKTSIZE, 0);
+ alarm(0);
+ if (n < 0) { /* really? */
+ syslog(LOG_ERR, "tftpd: read: %m\n");
+ goto abort;
+ }
+ dp->th_opcode = ntohs((u_short)dp->th_opcode);
+ dp->th_block = ntohs((u_short)dp->th_block);
+ if (dp->th_opcode == ERROR)
+ goto abort;
+ if (dp->th_opcode == DATA) {
+ if (dp->th_block == block) {
+ break; /* normal */
+ }
+ /* Re-synchronize with the other side */
+ (void) synchnet(peer);
+ if (dp->th_block == (block-1))
+ goto send_ack; /* rexmit */
+ }
+ }
+ /* size = write(file, dp->th_data, n - 4); */
+ size = writeit(file, &dp, n - 4, pf->f_convert);
+ if (size != (n-4)) { /* ahem */
+ if (size < 0) nak(errno + 100);
+ else nak(ENOSPACE);
+ goto abort;
+ }
+ } while (size == SEGSIZE);
+ write_behind(file, pf->f_convert);
+ (void) fclose(file); /* close data file */
+
+ ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */
+ ap->th_block = htons((u_short)(block));
+ (void) send(peer, ackbuf, 4, 0);
+
+ signal(SIGALRM, justquit); /* just quit on timeout */
+ alarm(rexmtval);
+ n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
+ alarm(0);
+ if (n >= 4 && /* if read some data */
+ dp->th_opcode == DATA && /* and got a data block */
+ block == dp->th_block) { /* then my last ack was lost */
+ (void) send(peer, ackbuf, 4, 0); /* resend final ack */
+ }
+abort:
+ return;
+}
+
+struct errmsg {
+ int e_code;
+ char *e_msg;
+} errmsgs[] = {
+ { EUNDEF, "Undefined error code" },
+ { ENOTFOUND, "File not found" },
+ { EACCESS, "Access violation" },
+ { ENOSPACE, "Disk full or allocation exceeded" },
+ { EBADOP, "Illegal TFTP operation" },
+ { EBADID, "Unknown transfer ID" },
+ { EEXISTS, "File already exists" },
+ { ENOUSER, "No such user" },
+ { -1, 0 }
+};
+
+static char *
+errtomsg(error)
+ int error;
+{
+ static char buf[20];
+ register struct errmsg *pe;
+ if (error == 0)
+ return "success";
+ for (pe = errmsgs; pe->e_code >= 0; pe++)
+ if (pe->e_code == error)
+ return pe->e_msg;
+ sprintf(buf, "error %d", error);
+ return buf;
+}
+
+/*
+ * Send a nak packet (error message).
+ * Error code passed in is one of the
+ * standard TFTP codes, or a UNIX errno
+ * offset by 100.
+ */
+static void
+nak(error)
+ int error;
+{
+ register struct tftphdr *tp;
+ int length;
+ register struct errmsg *pe;
+
+ tp = (struct tftphdr *)buf;
+ tp->th_opcode = htons((u_short)ERROR);
+ tp->th_code = htons((u_short)error);
+ for (pe = errmsgs; pe->e_code >= 0; pe++)
+ if (pe->e_code == error)
+ break;
+ if (pe->e_code < 0) {
+ pe->e_msg = strerror(error - 100);
+ tp->th_code = EUNDEF; /* set 'undef' errorcode */
+ }
+ strcpy(tp->th_msg, pe->e_msg);
+ length = strlen(pe->e_msg);
+ tp->th_msg[length] = '\0';
+ length += 5;
+ if (send(peer, buf, length, 0) != length)
+ syslog(LOG_ERR, "nak: %m\n");
+}
+
+static char *
+verifyhost(fromp)
+ struct sockaddr_in *fromp;
+{
+ struct hostent *hp;
+
+ hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (fromp->sin_addr),
+ fromp->sin_family);
+ if (hp)
+ return hp->h_name;
+ else
+ return inet_ntoa(fromp->sin_addr);
+}
diff --git a/libexec/uucpd/Makefile b/libexec/uucpd/Makefile
new file mode 100644
index 0000000..986c216
--- /dev/null
+++ b/libexec/uucpd/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+
+PROG= uucpd
+NOMAN= noman
+LDADD= -lcrypt -lutil
+DPADD= ${LIBCRYPT} ${LIBUTIL}
+.include <bsd.prog.mk>
diff --git a/libexec/uucpd/pathnames.h b/libexec/uucpd/pathnames.h
new file mode 100644
index 0000000..46310a4
--- /dev/null
+++ b/libexec/uucpd/pathnames.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ */
+
+#include <paths.h>
+
+#define _PATH_UUCICO "/usr/libexec/uucp/uucico"
diff --git a/libexec/uucpd/uucpd.c b/libexec/uucpd/uucpd.c
new file mode 100644
index 0000000..f645eea
--- /dev/null
+++ b/libexec/uucpd/uucpd.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Adams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1985, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)uucpd.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+/*
+ * 4.2BSD TCP/IP server for uucico
+ * uucico's TCP channel causes this server to be run at the remote end.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <utmp.h>
+#include <syslog.h>
+#include "pathnames.h"
+
+#define SCPYN(a, b) strncpy(a, b, sizeof (a))
+
+struct sockaddr_in hisctladdr;
+int hisaddrlen = sizeof hisctladdr;
+struct sockaddr_in myctladdr;
+int mypid;
+
+char Username[64], Logname[64];
+char *nenv[] = {
+ Username,
+ Logname,
+ NULL,
+};
+extern char **environ;
+extern void logwtmp(char *line, char *name, char *host);
+
+void doit(struct sockaddr_in *sinp);
+void dologout(void);
+int readline(char start[], int num, int passw);
+void dologin(struct passwd *pw, struct sockaddr_in *sin);
+
+void main(int argc, char **argv)
+{
+ environ = nenv;
+ close(1); close(2);
+ dup(0); dup(0);
+ hisaddrlen = sizeof (hisctladdr);
+ openlog("uucpd", LOG_PID, LOG_DAEMON);
+ if (getpeername(0, (struct sockaddr *)&hisctladdr, &hisaddrlen) < 0) {
+ syslog(LOG_ERR, "getpeername: %m");
+ _exit(1);
+ }
+ doit(&hisctladdr);
+ dologout();
+ exit(0);
+}
+
+void badlogin(char *name, struct sockaddr_in *sin)
+{
+ char remotehost[32];
+ struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
+ sizeof (struct in_addr), AF_INET);
+
+ if (hp) {
+ strncpy(remotehost, hp->h_name, sizeof (remotehost));
+ endhostent();
+ } else
+ strncpy(remotehost, inet_ntoa(sin->sin_addr),
+ sizeof (remotehost));
+
+ syslog(LOG_NOTICE, "LOGIN FAILURE FROM %s", remotehost);
+ syslog(LOG_AUTHPRIV|LOG_NOTICE,
+ "LOGIN FAILURE FROM %s, %s", remotehost, name);
+
+ fprintf(stderr, "Login incorrect.\n");
+ exit(1);
+}
+
+void login_incorrect(char *name, struct sockaddr_in *sinp)
+{
+ char passwd[64];
+
+ printf("Password: "); fflush(stdout);
+ if (readline(passwd, sizeof passwd, 1) < 0) {
+ syslog(LOG_WARNING, "passwd read: %m");
+ _exit(1);
+ }
+ badlogin(name, sinp);
+}
+
+void doit(struct sockaddr_in *sinp)
+{
+ char user[64], passwd[64];
+ char *xpasswd, *crypt();
+ struct passwd *pw;
+ pid_t s;
+
+ alarm(60);
+ printf("login: "); fflush(stdout);
+ if (readline(user, sizeof user, 0) < 0) {
+ syslog(LOG_WARNING, "login read: %m");
+ _exit(1);
+ }
+ /* truncate username to 8 characters */
+ user[8] = '\0';
+ pw = getpwnam(user);
+ if (pw == NULL)
+ login_incorrect(user, sinp);
+ if (strcmp(pw->pw_shell, _PATH_UUCICO))
+ login_incorrect(user, sinp);
+ if (pw->pw_expire && time(NULL) >= pw->pw_expire)
+ login_incorrect(user, sinp);
+ if (pw->pw_passwd && *pw->pw_passwd != '\0') {
+ printf("Password: "); fflush(stdout);
+ if (readline(passwd, sizeof passwd, 1) < 0) {
+ syslog(LOG_WARNING, "passwd read: %m");
+ _exit(1);
+ }
+ xpasswd = crypt(passwd, pw->pw_passwd);
+ if (strcmp(xpasswd, pw->pw_passwd))
+ badlogin(user, sinp);
+ }
+ alarm(0);
+ sprintf(Username, "USER=%s", pw->pw_name);
+ sprintf(Logname, "LOGNAME=%s", pw->pw_name);
+ if ((s = fork()) < 0) {
+ syslog(LOG_ERR, "fork: %m");
+ _exit(1);
+ } else if (s == 0) {
+ dologin(pw, sinp);
+ setgid(pw->pw_gid);
+ initgroups(pw->pw_name, pw->pw_gid);
+ chdir(pw->pw_dir);
+ setuid(pw->pw_uid);
+ execl(pw->pw_shell, "uucico", NULL);
+ syslog(LOG_ERR, "execl: %m");
+ _exit(1);
+ }
+}
+
+int readline(char start[], int num, int passw)
+{
+ char c;
+ register char *p = start;
+ register int n = num;
+
+ while (n-- > 0) {
+ if (read(0, &c, 1) <= 0)
+ return(-1);
+ c &= 0177;
+ if (c == '\n' || c == '\r' || c == '\0') {
+ if (p == start && passw) {
+ n++;
+ continue;
+ }
+ *p = '\0';
+ return(0);
+ }
+ if (c == 025) {
+ n = num;
+ p = start;
+ continue;
+ }
+ *p++ = c;
+ }
+ return(-1);
+}
+
+void dologout(void)
+{
+ union wait status;
+ pid_t pid;
+ char line[32];
+
+ while ((pid=wait((int *)&status)) > 0) {
+ sprintf(line, "uucp%ld", pid);
+ logwtmp(line, "", "");
+ }
+}
+
+/*
+ * Record login in wtmp file.
+ */
+void dologin(struct passwd *pw, struct sockaddr_in *sin)
+{
+ char line[32];
+ char remotehost[32];
+ int f;
+ time_t cur_time;
+ struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
+ sizeof (struct in_addr), AF_INET);
+
+ if (hp) {
+ strncpy(remotehost, hp->h_name, sizeof (remotehost));
+ endhostent();
+ } else
+ strncpy(remotehost, inet_ntoa(sin->sin_addr),
+ sizeof (remotehost));
+ /* hack, but must be unique and no tty line */
+ sprintf(line, "uucp%ld", getpid());
+ time(&cur_time);
+ if ((f = open(_PATH_LASTLOG, O_RDWR)) >= 0) {
+ struct lastlog ll;
+
+ ll.ll_time = cur_time;
+ lseek(f, (off_t)pw->pw_uid * sizeof(struct lastlog), L_SET);
+ SCPYN(ll.ll_line, line);
+ SCPYN(ll.ll_host, remotehost);
+ (void) write(f, (char *) &ll, sizeof ll);
+ (void) close(f);
+ }
+ logwtmp(line, pw->pw_name, remotehost);
+}
diff --git a/libexec/xtend/Makefile b/libexec/xtend/Makefile
new file mode 100644
index 0000000..75fedef
--- /dev/null
+++ b/libexec/xtend/Makefile
@@ -0,0 +1,13 @@
+# Makefile for xtend (Stark) 10/30/93
+# $Id$
+
+BINMODE= 555
+
+PROG= xtend
+SRCS= xtend.c status.c packet.c user.c
+CFLAGS+=-I. -DXTENUNAME=\"xten\" -DXTENGNAME=\"xten\"
+LDADD+= -lutil
+
+MAN8= xtend.8
+
+.include <bsd.prog.mk>
diff --git a/libexec/xtend/packet.c b/libexec/xtend/packet.c
new file mode 100644
index 0000000..8dfad88
--- /dev/null
+++ b/libexec/xtend/packet.c
@@ -0,0 +1,317 @@
+/*-
+ * Copyright (c) 1992, 1993, 1995 Eugene W. Stark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Eugene W. Stark.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <sys/time.h>
+#include "xtend.h"
+#include "xten.h"
+
+char *X10housenames[] = {
+ "A", "B", "C", "D", "E", "F", "G", "H",
+ "I", "J", "K", "L", "M", "N", "O", "P",
+ NULL
+};
+
+char *X10cmdnames[] = {
+ "1", "2", "3", "4", "5", "6", "7", "8",
+ "9", "10", "11", "12", "13", "14", "15", "16",
+ "AllUnitsOff", "AllLightsOn", "On", "Off", "Dim", "Bright", "AllLightsOff",
+ "ExtendedCode", "HailRequest", "HailAcknowledge", "PreSetDim0", "PreSetDim1",
+ "ExtendedData", "StatusOn", "StatusOff", "StatusRequest",
+ NULL
+};
+
+/*
+ * Log a packet and update device status accordingly
+ */
+
+logpacket(p)
+unsigned char *p;
+{
+ fprintf(Log, "%s: %s %s ", thedate(),
+ X10housenames[p[1]], X10cmdnames[p[2]]);
+ if(p[0] & TW_RCV_LOCAL) fprintf(Log, "(loc,");
+ else fprintf(Log, "(rem,");
+ if(p[0] & TW_RCV_ERROR) fprintf(Log, "err)");
+ else fprintf(Log, " ok)");
+ fprintf(Log, "\n");
+}
+
+/*
+ * Process a received packet p, updating device status information both
+ * in core and on disk.
+ */
+
+processpacket(p)
+unsigned char *p;
+{
+ int i, j, h, k;
+ STATUS *s;
+
+ /*
+ * If the packet had the error flag set, there is no other useful info.
+ */
+ if(p[0] & TW_RCV_ERROR) return;
+ /*
+ * First update in-core status information for the device.
+ */
+ h = p[1]; k = p[2];
+ if(k < 16) { /* We received a unit code, to select a particular device */
+ s = &Status[h][k];
+ s->selected = SELECTED;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ } else { /* We received a key code, to execute some function */
+ /*
+ * Change in status depends on the key code received
+ */
+ if(k == DIM) {
+ /*
+ * We can't really track DIM/BRIGHT properly the way things are right
+ * now. The TW523 reports the first, fourth, seventh, etc. Dim packet.
+ * We don't really have any way to tell when gaps occur, to cancel
+ * selection. For now, we'll assume that successive sequences of
+ * Dim/Bright commands are never transmitted without some other
+ * intervening command, and we make a good guess about how many units of
+ * dim/bright are represented by each packet actually reported by the
+ * TW523.
+ */
+ for(i = 0; i < 16; i++) {
+ s = &Status[h][i];
+ switch(s->selected) {
+ case SELECTED: /* Selected, but not being dimmed or brightened */
+ if(s->onoff == 0) {
+ s->onoff = 1;
+ s->brightness = 15;
+ }
+ s->brightness -= 2;
+ if(s->brightness < 0) s->brightness = 0;
+ s->selected = DIMMING;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ break;
+ case DIMMING: /* Selected and being dimmed */
+ s->brightness -=3;
+ if(s->brightness < 0) s->brightness = 0;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ break;
+ case BRIGHTENING: /* Selected and being brightened (an error) */
+ s->selected = IDLE;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ } else if(k == BRIGHT) {
+ /*
+ * Same problem here...
+ */
+ for(i = 0; i < 16; i++) {
+ s = &Status[h][i];
+ switch(s->selected) {
+ case SELECTED: /* Selected, but not being dimmed or brightened */
+ if(s->onoff == 0) {
+ s->onoff = 1;
+ s->brightness = 15;
+ }
+ s->brightness += 2;
+ if(s->brightness > 15) s->brightness = 15;
+ s->selected = BRIGHTENING;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ break;
+ case DIMMING: /* Selected and being dimmed (an error) */
+ s->selected = IDLE;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ break;
+ case BRIGHTENING: /* Selected and being brightened */
+ s->brightness +=3;
+ if(s->brightness > 15) s->brightness = 15;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ } else { /* Other key codes besides Bright and Dim */
+ /*
+ * We cancel brightening and dimming on ALL units on ALL house codes,
+ * because the arrival of a different packet indicates a gap that
+ * terminates any prior sequence of brightening and dimming
+ */
+ for(j = 0; j < 16; j++) {
+ for(i = 0; i < 16; i++) {
+ s = &Status[j][i];
+ if(s->selected == BRIGHTENING || s->selected == DIMMING) {
+ s->selected = IDLE;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ }
+ }
+ }
+ switch(k) {
+ case ALLUNITSOFF:
+ for(i = 0; i < 16; i++) {
+ s = &Status[h][i];
+ s->onoff = 0;
+ s->selected = IDLE;
+ s->brightness = 0;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ }
+ break;
+ case ALLLIGHTSON:
+ /* Does AllLightsOn cancel selectedness of non-lights? */
+ for(i = 0; i < 16; i++) {
+ s = &Status[h][i];
+ if(s->devcap & ISLIGHT) {
+ s->onoff = 1;
+ s->selected = IDLE;
+ s->brightness = 15;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ }
+ }
+ break;
+ case UNITON:
+ for(i = 0; i < 16; i++) {
+ s = &Status[h][i];
+ if(s->selected == SELECTED) {
+ s->onoff = 1;
+ s->selected = IDLE;
+ s->brightness = 15;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ }
+ }
+ break;
+ case UNITOFF:
+ for(i = 0; i < 16; i++) {
+ s = &Status[h][i];
+ if(s->selected == SELECTED) {
+ s->onoff = 0;
+ s->selected = IDLE;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ }
+ }
+ break;
+ case ALLLIGHTSOFF:
+ /* Does AllLightsOff cancel selectedness of non-lights? */
+ for(i = 0; i < 16; i++) {
+ s = &Status[h][i];
+ if(s->devcap & ISLIGHT) {
+ s->onoff = 0;
+ s->selected = IDLE;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ }
+ }
+ break;
+ case EXTENDEDCODE:
+ break;
+ case HAILREQUEST:
+ for(i = 0; i < 16; i++) {
+ s = &Status[h][i];
+ if(s->selected == SELECTED) {
+ s->selected = HAILED;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ }
+ }
+ break;
+ case HAILACKNOWLEDGE:
+ /* Do these commands cancel selection of devices not affected? */
+ for(i = 0; i < 16; i++) {
+ s = &Status[h][i];
+ if(s->selected == HAILED) {
+ s->selected = IDLE;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ }
+ }
+ break;
+ case PRESETDIM0:
+ case PRESETDIM1:
+ /* I don't really understand these */
+ for(i = 0; i < 16; i++) {
+ s = &Status[h][i];
+ if(s->selected == SELECTED) {
+ s->selected = IDLE;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ }
+ }
+ break;
+ case EXTENDEDDATA:
+ /* Who knows? The TW523 can't receive these anyway. */
+ break;
+ case STATUSON:
+ for(i = 0; i < 16; i++) {
+ s = &Status[h][i];
+ if(s->selected == REQUESTED) {
+ s->onoff = 1;
+ s->selected = IDLE;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ }
+ }
+ break;
+ case STATUSOFF:
+ for(i = 0; i < 16; i++) {
+ if(s->selected == REQUESTED) {
+ s = &Status[h][i];
+ s->onoff = 0;
+ s->selected = IDLE;
+ s->brightness = 0;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ }
+ }
+ case STATUSREQUEST:
+ for(i = 0; i < 16; i++) {
+ s = &Status[h][i];
+ if(s->selected) {
+ s->selected = REQUESTED;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/libexec/xtend/paths.h b/libexec/xtend/paths.h
new file mode 100644
index 0000000..77c6ae9
--- /dev/null
+++ b/libexec/xtend/paths.h
@@ -0,0 +1,11 @@
+/*
+ * Pathnames for files used by xtend
+ */
+
+#define X10DIR "/var/spool/xten"
+#define X10LOGNAME "Log"
+#define X10STATNAME "Status"
+#define X10DUMPNAME "status.out"
+#define TWPATH "/dev/tw0"
+#define SOCKPATH "/var/run/tw523"
+#define PIDPATH "/var/run/xtend.pid"
diff --git a/libexec/xtend/status.c b/libexec/xtend/status.c
new file mode 100644
index 0000000..2ee3812
--- /dev/null
+++ b/libexec/xtend/status.c
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 1992, 1993, 1995 Eugene W. Stark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Eugene W. Stark.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include "xtend.h"
+#include "xten.h"
+#include "paths.h"
+
+/*
+ * Initialize the status table from the status files
+ */
+
+initstatus()
+{
+ int h, i;
+
+ if(lseek(status, 0, SEEK_SET) != 0) {
+ fprintf(Log, "%s: Seek error on status file\n", thedate());
+ return;
+ }
+ if(read(status, Status, 16*16*sizeof(STATUS)) != 16*16*sizeof(STATUS)) {
+ fprintf(Log, "%s: Read error on status file\n", thedate());
+ return;
+ }
+}
+
+/*
+ * Checkpoint status of any devices whose status has changed
+ * and notify anyone monitoring those devices.
+ */
+
+checkpoint_status()
+{
+ int h, i, k, offset;
+
+ offset = 0;
+ for(h = 0; h < 16; h++) {
+ for(i = 0; i < 16; i++) {
+ if(Status[h][i].changed) {
+ if(lseek(status, offset, SEEK_SET) != offset) {
+ fprintf(Log, "%s: Seek error on status file\n", thedate());
+ } else {
+ if(write(status, &Status[h][i], sizeof(STATUS)) != sizeof(STATUS)) {
+ fprintf(Log, "%s: Write error on status file\n", thedate());
+ }
+ }
+ Status[h][i].changed = 0;
+ for(k = 0; k < MAXMON; k++) {
+ if(Monitor[k].inuse
+ && Monitor[k].house == h && Monitor[k].unit == i) {
+ /*
+ * Arrange to catch SIGPIPE in case client has gone away.
+ */
+ extern int client;
+ extern void clientgone();
+ void (*prev)();
+
+ client = k;
+ prev = signal(SIGPIPE, clientgone);
+ printstatus(Monitor[k].user, &Status[h][i]);
+ fflush(Monitor[k].user);
+ signal(SIGPIPE, prev);
+ }
+ }
+ }
+ offset += sizeof(STATUS);
+ }
+ }
+}
+
+int client;
+
+void clientgone()
+{
+ fprintf(Log, "%s: Deleting monitor table entry %d, client gone\n", thedate(), client);
+ fclose(Monitor[client].user);
+ Monitor[client].inuse = 0;
+}
diff --git a/libexec/xtend/user.c b/libexec/xtend/user.c
new file mode 100644
index 0000000..f029f5f
--- /dev/null
+++ b/libexec/xtend/user.c
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 1992, 1993, 1995 Eugene W. Stark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Eugene W. Stark.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include "xtend.h"
+#include "xten.h"
+#include "paths.h"
+
+MONENTRY Monitor[MAXMON];
+
+/*
+ * Process a user command
+ */
+
+user_command()
+{
+ char h;
+ int i, k, c, n, error;
+ char cmd[512], dumppath[MAXPATHLEN+1], pkt[3];
+ FILE *dumpf;
+
+ error = 0;
+ if(fgets(cmd, 512, User) != NULL) {
+ if(sscanf(cmd, "status %c %d", &h, &i) == 2
+ && h >= 'A' && h <= 'P' && i >= 1 && i <= 16) {
+ h -= 'A';
+ i--;
+ printstatus(User, &Status[h][i]);
+ } else if(sscanf(cmd, "send %c %s %d", &h, cmd, &n) == 3
+ && h >= 'A' && h <= 'P' && (i = find(cmd, X10cmdnames)) >= 0) {
+ h -= 'A';
+ pkt[0] = h;
+ pkt[1] = i;
+ pkt[2] = n;
+ if(write(tw523, pkt, 3) != 3) {
+ fprintf(Log, "%s: Transmission error (packet [%s %s]:%d).\n",
+ thedate(), X10housenames[h], X10cmdnames[i], n);
+ error++;
+ } else {
+ fprintf(User, "OK\n");
+ }
+ } else if(!strcmp("dump\n", cmd)) {
+ strcpy(dumppath, X10DIR);
+ strcat(dumppath, X10DUMPNAME);
+ if((dumpf = fopen(dumppath, "w")) != NULL) {
+ for(h = 0; h < 16; h++) {
+ for(i = 0; i < 16; i++) {
+ if(Status[h][i].lastchange) {
+ fprintf(dumpf, "%s%d\t", X10housenames[h], i+1);
+ printstatus(dumpf, &Status[h][i]);
+ }
+ }
+ }
+ fclose(dumpf);
+ fprintf(User, "OK\n");
+ } else {
+ error++;
+ }
+ } else if(sscanf(cmd, "monitor %c %d", &h, &i) == 2
+ && h >= 'A' && h <= 'P' && i >= 1 && i <= 16) {
+ h -= 'A';
+ i--;
+ for(k = 0; k < MAXMON; k++) {
+ if(!Monitor[k].inuse) break;
+ }
+ if(k == MAXMON) {
+ error++;
+ } else {
+ Monitor[k].house = h;
+ Monitor[k].unit = i;
+ Monitor[k].user = User;
+ Monitor[k].inuse = 1;
+ fprintf(Log, "%s: Adding %c %d to monitor list (entry %d)\n",
+ thedate(), h+'A', i+1, k);
+ fprintf(User, "OK\n");
+ fflush(User);
+ User = NULL;
+ return(0); /* We don't want caller to close stream */
+ }
+ } else if(!strcmp("done\n", cmd)) {
+ fprintf(User, "OK\n");
+ fflush(User);
+ return(1);
+ } else {
+ if(feof(User)) {
+ return(1);
+ } else {
+ error++;
+ }
+ }
+ } else {
+ error++;
+ }
+ if(error) {
+ fprintf(User, "ERROR\n");
+ }
+ fflush(User);
+ return(0);
+}
+
+find(s, tab)
+char *s;
+char *tab[];
+{
+ int i;
+
+ for(i = 0; tab[i] != NULL; i++) {
+ if(strcmp(s, tab[i]) == 0) return(i);
+ }
+ return(-1);
+}
+
+printstatus(f, s)
+FILE *f;
+STATUS *s;
+{
+ fprintf(f, "%s:%d", s->onoff ? "On" : "Off", s->brightness);
+ switch(s->selected) {
+ case IDLE:
+ fprintf(f, " (normal) "); break;
+ case SELECTED:
+ fprintf(f, " (selected) "); break;
+ case DIMMING:
+ fprintf(f, " (dimming) "); break;
+ case BRIGHTENING:
+ fprintf(f, " (brightening) "); break;
+ case REQUESTED:
+ fprintf(f, " (requested) "); break;
+ case HAILED:
+ fprintf(f, " (hailed) "); break;
+ default:
+ fprintf(f, " (bogus) "); break;
+ }
+ fprintf(f, "%s", ctime(&s->lastchange));
+}
+
diff --git a/libexec/xtend/xten.h b/libexec/xtend/xten.h
new file mode 100644
index 0000000..e782f15
--- /dev/null
+++ b/libexec/xtend/xten.h
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1992, 1993, 1995 Eugene W. Stark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Eugene W. Stark.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern char *X10housenames[];
+extern char *X10cmdnames[];
+
+#define ALLUNITSOFF 16
+#define ALLLIGHTSON 17
+#define UNITON 18
+#define UNITOFF 19
+#define DIM 20
+#define BRIGHT 21
+#define ALLLIGHTSOFF 22
+#define EXTENDEDCODE 23
+#define HAILREQUEST 24
+#define HAILACKNOWLEDGE 25
+#define PRESETDIM0 26
+#define PRESETDIM1 27
+#define EXTENDEDDATA 28
+#define STATUSON 29
+#define STATUSOFF 30
+#define STATUSREQUEST 31
+
+/*
+ * Flags for first byte of received packet
+ */
+
+#define TW_RCV_LOCAL 1 /* The packet arrived during a local transmission */
+#define TW_RCV_ERROR 2 /* An invalid/corrupted packet was received */
+
diff --git a/libexec/xtend/xtend.8 b/libexec/xtend/xtend.8
new file mode 100644
index 0000000..e6ac50a
--- /dev/null
+++ b/libexec/xtend/xtend.8
@@ -0,0 +1,174 @@
+.\" Copyright (c) 1992, 1993 Eugene W. Stark
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Eugene W. Stark.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Th XTEND 8 "30 Oct 1993"
+.Dd Oct 30, 1993
+.Dt XTEND 8
+.Os BSD FreeBSD
+.Sh NAME
+xtend \- X-10 daemon
+.Sh SYNOPSIS
+.Nm xtend
+.Sh DESCRIPTION
+.Nm Xtend
+interfaces between user-level programs and the TW523 X-10 controller.
+It logs all packets received from the TW523, attempts to track the
+status of all X-10 devices, and accepts socket connections from user-level
+client programs that need to manipulate X-10 devices.
+.Pp
+When
+.Nm xtend
+is started, it forks, releases the controlling terminal, then opens
+its log file, where it subsequently records all X-10 activity and
+diagnostic messages. It then begins processing packets received from
+the TW523 and accepting connections one at a time from clients
+wishing to issue X-10 commands. The usual place to start xtend would
+be from the
+.Pa /etc/rc.local
+startup script.
+.Pp
+Sending
+.Nm xtend
+a SIGHUP causes it to close and reopen its log file. This is useful
+in shell scripts that rotate the log files to keep them from growing
+indefinitely.
+If
+.Nm xtend
+receives a SIGTERM, it shuts down gracefully and exits.
+A SIGPIPE causes
+.Nm xtend
+to abort the current client connection.
+.Pp
+.Nm Xtend
+communicates with client processes by a simple protocol in which a one-line
+command is sent by the client, and is acknowledged by a one-line response
+from the daemon.
+.Pp
+.Nm Xtend
+understands four types of commands. The command
+.Bl -tag
+.It status H U
+.El
+.Pp
+where H is a single letter house code, and U is a numeric unit code,
+causes
+.Nm xtend
+to respond with one line of status information about the specified device.
+The command
+.Bl -tag
+.It send H U N
+.El
+.Pp
+where H is a single-letter house code, U is either a numeric unit code
+or a function code (see source file
+.Pa xtend/packet.c
+) for a list, and N is a number indicating the number of times (usually 2)
+the packet is to be transmitted without gaps,
+causes
+.Nm xtend
+to perform the specified X-10 transmission. If the transmission was apparently
+successful, a single-line response containing
+.B
+OK
+is issued, otherwise a single-line response containing
+.B
+ERROR
+is produced.
+The command
+.Bl -tag
+.It dump
+.El
+.Pp
+causes
+.Nm xtend
+to dump the current status of all devices to an ASCII file in the spool
+directory. The response
+.B
+OK
+is issued, regardless of whether the status dump was successful.
+The command
+.Bl -tag
+.It monitor H U
+.El
+.Pp
+causes
+.Nm xtend
+to add the current client socket connection to a list of clients that are to
+be notified about activity concerning the specified X-10 device.
+The single-line acknowledgement
+.B
+OK
+is returned if the maximum (currently 5) number of such clients was not
+exceeded, otherwise
+.B
+ERROR
+is returned.
+.Nm Xtend
+then returns to its normal mode of accepting connections from clients.
+However, each subsequent change in the status of the specified device will
+cause
+.Nm xtend
+to write one line of status information for the device (in the same
+format as produced by the
+.B
+status
+command) to the saved socket. This feature is useful for writing programs
+that need to monitor the activity of devices, like motion detectors, that can
+perform X-10 transmissions.
+.Sh OPTIONS
+None.
+.Sh SEE ALSO
+.Xr xten 1
+.Xr tw 4
+.Sh FILES
+.Bl -tag -width /var/spool/xten/Status -compact
+.It Pa /dev/tw0
+the TW523 special file
+.It Pa /var/run/tw523
+socket for client connections
+.It Pa /var/run/xtend.pid
+pid file
+.It Pa /var/spool/xten/Log
+log file
+.It Pa /var/spool/xten/Status
+device status file (binary)
+.It Pa /var/spool/status.out
+ASCII dump of device status
+.El
+.Sh BUGS
+There is currently no timeout on client socket connections, so a hung
+client program can prevent other clients from accessing the daemon.
+.Pp
+.Nm Xtend
+does the best it can at trying to track device status, but there is
+usually no way it can tell when a device has been operated manually.
+This is due to the fact that most X-10 devices are not able to
+respond to queries about their status.
+.Sh AUTHOR
+Eugene W. Stark (stark@cs.sunysb.edu)
diff --git a/libexec/xtend/xtend.c b/libexec/xtend/xtend.c
new file mode 100644
index 0000000..609bc7f
--- /dev/null
+++ b/libexec/xtend/xtend.c
@@ -0,0 +1,368 @@
+/*-
+ * Copyright (c) 1992, 1993, 1995 Eugene W. Stark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Eugene W. Stark.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * xtend - X-10 daemon
+ * Eugene W. Stark (stark@cs.sunysb.edu)
+ * January 14, 1993
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "xtend.h"
+#include "xten.h"
+#include "paths.h"
+
+FILE *Log; /* Log file */
+FILE *User; /* User connection */
+STATUS Status[16][16]; /* Device status table */
+int status; /* Status file descriptor */
+int tw523; /* tw523 controller */
+int sock; /* socket for user */
+jmp_buf mainloop; /* longjmp point after SIGHUP */
+void onhup(); /* SIGHUP handler */
+void onterm(); /* SIGTERM handler */
+void onpipe(); /* SIGPIPE handler */
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ char *twpath = TWPATH;
+ char *sockpath = SOCKPATH;
+ char logpath[MAXPATHLEN+1];
+ char statpath[MAXPATHLEN+1];
+ struct sockaddr_un sa;
+ struct timeval tv;
+ struct passwd *pw;
+ struct group *gr;
+ struct stat sb;
+ int user;
+ int fd;
+ FILE *pidf;
+
+ /*
+ * Make sure we start out running as root
+ */
+ if(geteuid() != 0) {
+ fprintf(stderr, "You must be root to run %s\n", argv[0]);
+ exit(1);
+ }
+
+ /*
+ * Find out what UID/GID we are to run as
+ */
+ if((pw = getpwnam(XTENUNAME)) == NULL) {
+ fprintf(stderr, "%s: No such user '%s'\n", argv[0], XTENUNAME);
+ exit(1);
+ }
+ if((gr = getgrnam(XTENGNAME)) == NULL) {
+ fprintf(stderr, "%s: No such group '%s'\n", argv[0], XTENGNAME);
+ exit(1);
+ }
+
+ /*
+ * Open the log file before doing anything else
+ */
+ strcpy(logpath, X10DIR);
+ if(stat(logpath, &sb) == -1 && errno == ENOENT) {
+ if(mkdir(logpath, 0755) != -1) {
+ chown(logpath, pw->pw_uid, gr->gr_gid);
+ } else {
+ fprintf(stderr, "%s: Can't create directory '%s'\n", argv[0], logpath);
+ exit(1);
+ }
+ }
+ strcat(logpath, "/");
+ strcat(logpath, X10LOGNAME);
+ if((Log = fopen(logpath, "a")) == NULL) {
+ fprintf(stderr, "Can't open log file %s\n", logpath);
+ exit(1);
+ }
+ chown(logpath, pw->pw_uid, gr->gr_gid);
+
+ /*
+ * Become a daemon
+ */
+ if(daemon(0, 0) == -1) {
+ fprintf(Log, "%s: Unable to become a daemon\n", thedate());
+ fclose(Log);
+ exit(1);
+ }
+ fprintf(Log, "%s: %s [%d] started\n", thedate(), argv[0], getpid());
+
+ /*
+ * Get ahold of the TW523 device
+ */
+ if((tw523 = open(twpath, O_RDWR)) < 0) {
+ fprintf(Log, "%s: Can't open %s\n", thedate(), twpath);
+ fclose(Log);
+ exit(1);
+ }
+ fprintf(Log, "%s: %s successfully opened\n", thedate(), twpath);
+
+ /*
+ * Put our pid in a file so we can be signalled by shell scripts
+ */
+ if((pidf = fopen(PIDPATH, "w")) == NULL) {
+ fprintf(Log, "%s: Error writing pid file: %s\n", thedate(), PIDPATH);
+ fclose(Log);
+ exit(1);
+ }
+ fprintf(pidf, "%d\n", getpid());
+ fclose(pidf);
+
+ /*
+ * Set up socket to accept user commands
+ */
+ if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ fprintf(Log, "%s: Can't create socket\n", thedate());
+ fclose(Log);
+ exit(1);
+ }
+ strcpy(sa.sun_path, sockpath);
+ sa.sun_family = AF_UNIX;
+ unlink(sockpath);
+ if(bind(sock, (struct sockaddr *)(&sa), strlen(sa.sun_path) + 2) < 0) {
+ fprintf(Log, "%s: Can't bind socket to %s\n", thedate(), sockpath);
+ fclose(Log);
+ exit(1);
+ }
+ if(listen(sock, 5) < 0) {
+ fprintf(Log, "%s: Can't listen on socket\n", thedate());
+ fclose(Log);
+ exit(1);
+ }
+
+ /*
+ * Set proper ownership and permissions on the socket
+ */
+ if(chown(sockpath, pw->pw_uid, gr->gr_gid) == -1 ||
+ chmod(sockpath, 0660) == -1) {
+ fprintf(Log, "%s: Can't set owner/permissions on socket\n", thedate());
+ fclose(Log);
+ exit(1);
+ }
+
+ /*
+ * Give up root privileges
+ */
+ setgid(pw->pw_gid);
+ setuid(pw->pw_uid);
+
+ /*
+ * Initialize the status table
+ */
+ strcpy(statpath, X10DIR);
+ strcat(statpath, "/");
+ strcat(statpath, X10STATNAME);
+ if((status = open(statpath, O_RDWR)) < 0) {
+ if((status = open(statpath, O_RDWR | O_CREAT, 0666)) < 0) {
+ fprintf(Log, "%s: Can't open %s\n", thedate(), statpath);
+ fclose(Log);
+ exit(1);
+ }
+ if(write(status, Status, 16 * 16 * sizeof(STATUS))
+ != 16 * 16 * sizeof(STATUS)) {
+ fprintf(Log, "%s: Error initializing status file\n", thedate());
+ fclose(Log);
+ exit(1);
+ }
+ }
+ initstatus();
+
+ /*
+ * Return here on SIGHUP after closing and reopening log file.
+ * Also on SIGPIPE after closing user connection.
+ */
+ signal(SIGTERM, onterm);
+ signal(SIGHUP, onhup);
+ signal(SIGPIPE, onpipe);
+ setjmp(mainloop);
+
+ /*
+ * Now start the main processing loop.
+ */
+ tv.tv_sec = 0;
+ tv.tv_usec = 250000;
+ while(1) {
+ fd_set fs;
+ unsigned char rpkt[3];
+ int sel, h, k;
+ STATUS *s;
+
+ FD_ZERO(&fs);
+ FD_SET(tw523, &fs);
+ if(User != NULL) FD_SET(user, &fs);
+ else FD_SET(sock, &fs);
+ sel = select(FD_SETSIZE, &fs, 0, 0, &tv);
+ if(sel == 0) {
+ /*
+ * Cancel brightening and dimming on ALL units on ALL house codes,
+ * because the fact that we haven't gotten a packet for awhile means
+ * that there was a gap in transmission.
+ */
+ for(h = 0; h < 16; h++) {
+ for(k = 0; k < 16; k++) {
+ s = &Status[h][k];
+ if(s->selected == BRIGHTENING || s->selected == DIMMING) {
+ s->selected = IDLE;
+ s->lastchange = time(NULL);
+ s->changed = 1;
+ }
+ }
+ }
+ fflush(Log);
+ checkpoint_status();
+ /*
+ * Now that we've done this stuff, we'll set the timeout a little
+ * longer, so we don't keep looping too frequently.
+ */
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+ continue;
+ }
+ /*
+ * While there is stuff happening, we keep a short timeout, so we
+ * don't get stuck for some unknown reason, and so we can keep the
+ * brightening and dimming data up-to-date.
+ */
+ tv.tv_sec = 0;
+ tv.tv_usec = 250000;
+ if(FD_ISSET(tw523, &fs)) { /* X10 data arriving from TW523 */
+ if(read(tw523, rpkt, 3) < 3) {
+ fprintf(Log, "%s: Error reading from TW523\n", thedate());
+ } else {
+ logpacket(rpkt);
+ processpacket(rpkt);
+ }
+ } else if(FD_ISSET(user, &fs)) {
+ if(User != NULL) {
+ if(user_command()) {
+ fprintf(Log, "%s: Closing user connection\n", thedate());
+ fclose(User);
+ User = NULL;
+ }
+ } else {
+ /* "Can't" happen */
+ }
+ } else if(FD_ISSET(sock, &fs)) { /* Accept a connection */
+ if (User == NULL) {
+ int len = sizeof(struct sockaddr_un);
+ if((user = accept(sock, (struct sockaddr *)(&sa), &len)) >= 0) {
+ fprintf(Log, "%s: Accepting user connection\n", thedate());
+ if((User = fdopen(user, "w+")) == NULL) {
+ fprintf(Log, "%s: Can't attach socket to stream\n", thedate());
+ }
+ } else {
+ fprintf(Log, "%s: Failure in attempt to accept connection\n", thedate());
+ }
+ } else {
+ /* "Can't happen */
+ }
+ }
+ }
+ /* Not reached */
+}
+
+char *thedate()
+{
+ char *cp, *cp1;
+ time_t tod;
+
+ tod = time(NULL);
+ cp = cp1 = ctime(&tod);
+ while(*cp1 != '\n') cp1++;
+ *cp1 = '\0';
+ return(cp);
+}
+
+/*
+ * When SIGHUP received, close and reopen the Log file
+ */
+
+void onhup()
+{
+ char logpath[MAXPATHLEN+1];
+
+ fprintf(Log, "%s: SIGHUP received, reopening Log\n", thedate());
+ fclose(Log);
+ strcpy(logpath, X10DIR);
+ strcat(logpath, "/");
+ strcat(logpath, X10LOGNAME);
+ if((Log = fopen(logpath, "a")) == NULL) {
+ fprintf(stderr, "Can't open log file %s\n", logpath);
+ exit(1);
+ }
+ longjmp(mainloop, 1);
+ /* No return */
+}
+
+/*
+ * When SIGTERM received, just exit normally
+ */
+
+void onterm()
+{
+ fprintf(Log, "%s: SIGTERM received, shutting down\n", thedate());
+ fclose(Log);
+ exit(0);
+}
+
+/*
+ * When SIGPIPE received, reset user connection
+ */
+
+void onpipe()
+{
+ fprintf(Log, "%s: SIGPIPE received, resetting user connection\n",
+ thedate());
+ if(User != NULL) {
+ fclose(User);
+ User = NULL;
+ }
+ longjmp(mainloop, 1);
+ /* No return */
+}
diff --git a/libexec/xtend/xtend.h b/libexec/xtend/xtend.h
new file mode 100644
index 0000000..1b8c706
--- /dev/null
+++ b/libexec/xtend/xtend.h
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 1992, 1993, 1995 Eugene W. Stark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Eugene W. Stark.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Device capabilities
+ */
+
+#define ISLIGHT 1 /* Is device a light? */
+#define CANQUERY 2 /* Responds to status query */
+
+/*
+ * Device status
+ */
+
+typedef enum {
+ IDLE,
+ SELECTED,
+ DIMMING,
+ BRIGHTENING,
+ REQUESTED,
+ HAILED
+ } SELECT;
+
+typedef struct {
+ unsigned int devcap; /* device capabilities */
+ unsigned int changed; /* status changed since last checkpoint? */
+ time_t lastchange; /* time status last changed */
+ SELECT selected; /* select status of device */
+ unsigned int onoff; /* nonzero if on */
+ unsigned int brightness; /* value in range 0-15 */
+} STATUS;
+
+typedef struct {
+ int inuse; /* Is entry in use? */
+ FILE *user; /* Socket to notify user */
+ int house; /* House code of device to monitor */
+ int unit; /* Unit code of device to monitor */
+} MONENTRY;
+
+#define MAXMON 5 /* Maximum number of monitor entries */
+
+extern FILE *Log; /* Log file */
+extern FILE *User; /* User connection */
+extern STATUS Status[16][16]; /* Device status table */
+extern int status; /* Status file descriptor */
+extern int tw523; /* tw523 controller */
+extern MONENTRY Monitor[MAXMON];/* Monitor table */
+
+extern char *thedate();
diff --git a/libexec/ypxfr/Makefile b/libexec/ypxfr/Makefile
new file mode 100644
index 0000000..0339aee
--- /dev/null
+++ b/libexec/ypxfr/Makefile
@@ -0,0 +1,26 @@
+# $Id: Makefile,v 1.6 1995/12/16 04:03:02 wpaul Exp $
+
+PROG= ypxfr
+SRCS= ypxfr_clnt.c yp_clnt.c ypxfr_getmap.c yp_dblookup.c \
+ yp_error.c ypxfr_misc.c ypxfr_main.c yp_dbwrite.c
+
+.PATH: ${.CURDIR}/../../usr.sbin/ypserv
+
+MAN8= ypxfr.8
+CFLAGS+=-I.
+
+CLEANFILES= yp.h yp_clnt.c ypxfr_clnt.c
+
+RPCSRC= ${.DESTDIR}/usr/include/rpcsvc/yp.x
+RPCGEN= rpcgen -I -C
+
+ypxfr_clnt.c: ${RPCSRC} yp.h
+ ${RPCGEN} -DYPPUSH_ONLY -l -o ${.TARGET} ${RPCSRC}
+
+yp_clnt.c: ${RPCSRC} yp.h
+ ${RPCGEN} -DYPSERV_ONLY -l -o ${.TARGET} ${RPCSRC}
+
+yp.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+.include <bsd.prog.mk>
diff --git a/libexec/ypxfr/yp_dbwrite.c b/libexec/ypxfr/yp_dbwrite.c
new file mode 100644
index 0000000..f874e9b
--- /dev/null
+++ b/libexec/ypxfr/yp_dbwrite.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: yp_dbwrite.c,v 1.7 1995/12/24 04:40:58 wpaul Exp $
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <db.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <paths.h>
+#include "yp.h"
+#include "ypxfr_extern.h"
+
+#ifndef lint
+static const char rcsid[] = "$Id: yp_dbwrite.c,v 1.7 1995/12/24 04:40:58 wpaul Exp $";
+#endif
+
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+
+/*
+ * Open a DB database read/write
+ */
+DB *yp_open_db_rw(domain, map)
+ const char *domain;
+ const char *map;
+{
+ DB *dbp;
+ char buf[1025];
+
+
+ yp_errno = YP_TRUE;
+
+ if (map[0] == '.' || strchr(map, '/')) {
+ yp_errno = YP_BADARGS;
+ return (NULL);
+ }
+
+ snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map);
+
+ dbp = dbopen(buf,O_RDWR|O_EXCL|O_CREAT, PERM_SECURE, DB_HASH, &openinfo);
+
+ if (dbp == NULL) {
+ switch(errno) {
+ case ENOENT:
+ yp_errno = YP_NOMAP;
+ break;
+ case EFTYPE:
+ yp_errno = YP_BADDB;
+ break;
+ default:
+ yp_errno = YP_YPERR;
+ break;
+ }
+ }
+
+ return (dbp);
+}
+
+int yp_put_record(dbp,key,data)
+ DB *dbp;
+ DBT *key;
+ DBT *data;
+{
+
+ if ((dbp->put)(dbp,key,data,0)) {
+ (void)(dbp->close)(dbp);
+ return(YP_BADDB);
+ }
+
+ return(YP_TRUE);
+}
diff --git a/libexec/ypxfr/ypxfr.8 b/libexec/ypxfr/ypxfr.8
new file mode 100644
index 0000000..5e41b08
--- /dev/null
+++ b/libexec/ypxfr/ypxfr.8
@@ -0,0 +1,207 @@
+.\" Copyright (c) 1995
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: ypxfr.8,v 1.2 1995/12/25 02:54:39 wpaul Exp $
+.\"
+.Dd February 5, 1995
+.Dt YPXFR 8
+.Os
+.Sh NAME
+.Nm ypxfr
+.Nd "transfer NIS database from remote server to local host"
+.Sh SYNOPSIS
+.Nm ypxfr
+.Op Fl f
+.Op Fl c
+.Op Fl d Ar target domain
+.Op Fl h Ar source host
+.Op Fl s Ar source domain
+.Op Fl p Ar path
+.Op Fl C Ar taskid program-number ipaddr port
+.Ar mapname
+.Sh DESCRIPTION
+.Nm ypxfr
+copies an NIS database (or
+.Pa map )
+from one NIS server to another using NIS services. In FreeBSD,
+.Nm ypxfr
+is generally invoked by
+.Xr ypserv 8
+when it receives a map transfer request from
+.Xr yppush 8 .
+.Nm ypxfr
+is used primarily in environments where several NIS servers
+are in use in a single domain. One server, the NIS master, maintains
+the canonical copies of all NIS maps, and all the other servers,
+the NIS slaves, copy new versions of the maps from the master whenever
+any updates are made (i.e. when a user updates their password via
+.Xr yppasswd 1
+).
+.Pp
+When run,
+.Nm ypxfr
+creates a temporary database file in
+.Pa /var/yp/[domainmame] ,
+and fills it with the contents of
+.Ar mapname
+as supplied by the specified
+.Ar source host .
+When the entire map has been transfered,
+.Nm ypxfr
+deletes the original copy of
+.Ar mapname
+and moves the temporary copy into its place. When the transfer is
+complete,
+.Nm ypxfr
+will attempt to send a 'clear current map' request to the local
+.Xr ypserv 8
+process to clear any possible references it may still have to the
+stale map.
+.Pp
+Note that all files created by
+.Nm ypxfr
+are owner readable and writable only for security reasons. Since the
+NIS maps and the directory in which they reside are normally owned by
+root, this prevents non-privleged users from making unauthorized
+modifications.
+.Pp
+In order to maintain consistency across all NIS servers,
+.Nm ypxfr
+can be run periodically in a
+.Xr cron 8
+job. Maps which change infrequently
+need only be updated once a day (preferably late at night when system
+usage is lowest), whereas those that are subject to frequent changes
+(such a
+.Pa passwd.byname
+and
+.Pa passwd.byuid )
+should be updated perhaps once every hour. Using
+.Xr cron 8
+to automatically
+update the NIS maps is not strictly mandatory since all updates should
+be propagated by
+.Xr yppush 8
+when
+.Pa /var/yp/Makefile
+is run on the NIS master server, however it is good practice
+on large networks where possible outages could cause NIS servers to
+fall out of sync with each other.
+.Pp
+When
+.Nm ypxfr
+is invoked without a controlling terminal, e.g. from inside
+.Xr ypserv 8 ,
+it logs all its output using the
+.Xr syslog 3
+facility.
+.Sh OPTIONS
+The following options and flags are supported by
+.Nm ypxfr :
+.Bl -tag -width flag
+.It Fl f
+Force a map transfer. Normally,
+.Nm ypxfr
+will not transfer a map if it determines that the NIS master's copy
+is not newer than the existing copy already on the local host: the
+.Fl f
+flag forces a transfer regardless of which server's version is more recent.
+.It Fl c
+Do not send a 'clear current map' request to the
+.Xr ypserv 8
+process running on the local host. This flag is normally used when
+invoking
+.Nm ypxfr
+manually on a machine that is not yet running
+.Xr ypserv 8 .
+Without this flag, failure to contact the local NIS server will cause
+.Nm ypxfr
+to abort the transfer.
+.It Fl d Ar target domain
+Specify a target domain other than the current NIS domain.
+.It Fl h Ar source host
+Specify the name of the host from which to copy the NIS maps. This option
+is used to insure that
+.Nm ypxfr
+only copies maps from the NIS master server.
+.It Fl s Ar source domain
+Specify the domain from which to transfer a map, in the event that
+the transfer is being done across two different NIS domains.
+.It Fl p Ar path
+Specify the top level directory containing the NIS maps. By
+default, this path is
+.Pa /var/yp .
+The
+.Fl p
+flag allows you to specify an alternate path should you wish to
+store your NIS maps in a different part of the filesystem. The
+NIS server,
+.Xr ypserv 8 ,
+passes this flag to
+.Nm ypxfr
+if it too has been told to use an alternate path.
+.It Fl C Ar taskid program-number ipaddr port
+These options are used only when
+.Nm ypxfr
+is invoked by
+.Xr ypserv 8
+in response to a map transfer request initiated by
+.Xr yppush 8 .
+In this instance,
+.Nm ypxfr
+needs to 'callback' to the
+.Xr yppush 8
+process and interact with it, so
+.Xr yppush 8
+passes to it an IP address
+.Ar ipaddr ,
+port number
+.Ar port ,
+registered program number
+.Ar program-number
+and a transaction ID
+.Ar taskid
+that it can use to contact the waiting
+.Xr yppush 8
+process on the master server.
+.It Ar mapname
+The name of the map to transfer.
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/yp/[domainname]/[maps]
+The NIS maps for a particular NIS domain.
+.El
+.Sh SEE ALSO
+.Xr ypserv 8 ,
+.Xr yppush 8 ,
+.Xr yp 4
+.Sh AUTHOR
+Bill Paul <wpaul@ctr.columbia.edu>
diff --git a/libexec/ypxfr/ypxfr_extern.h b/libexec/ypxfr/ypxfr_extern.h
new file mode 100644
index 0000000..b2eda88
--- /dev/null
+++ b/libexec/ypxfr/ypxfr_extern.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ypxfr_extern.h,v 1.5 1995/12/24 04:37:22 wpaul Exp $
+ */
+#include <sys/types.h>
+#include <limits.h>
+#include <db.h>
+#include <paths.h>
+
+#ifndef _PATH_YP
+#define _PATH_YP "/var/yp/"
+#endif
+
+extern char *yp_dir;
+extern int debug;
+extern HASHINFO openinfo;
+extern int yp_errno;
+extern void yp_error __P(( const char *, ... ));
+extern int _yp_check __P(( char ** ));
+extern char *ypxfrerr_string __P(( ypxfrstat ));
+extern DB *yp_open_db_rw __P(( const char *, const char *));
+extern int yp_put_record __P(( DB *, DBT *, DBT * ));
+extern int yp_get_record __P(( const char *, const char *, const DBT *, DBT *, int ));
+extern int ypxfr_get_map __P(( char *, char *, char *, int (*)() ));
+extern char *ypxfr_get_master __P(( char *, char *, char *, const int ));
+extern unsigned long ypxfr_get_order __P(( char *, char *, char *, const int ));
+extern char *ypxfxerr_string __P(( ypxfrstat ));
+extern int callrpc __P(( char *, int, int, int, xdrproc_t, char *, xdrproc_t, char *));
diff --git a/libexec/ypxfr/ypxfr_getmap.c b/libexec/ypxfr/ypxfr_getmap.c
new file mode 100644
index 0000000..702b5cb
--- /dev/null
+++ b/libexec/ypxfr/ypxfr_getmap.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ypxfr_getmap.c,v 1.6 1995/12/24 04:39:04 wpaul Exp $
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp.h>
+#include "ypxfr_extern.h"
+
+#ifndef lint
+static const char rcsid[] = "$Id: ypxfr_getmap.c,v 1.6 1995/12/24 04:39:04 wpaul Exp $";
+#endif
+
+extern bool_t xdr_ypresp_all_seq __P(( XDR *, unsigned long * ));
+
+int (*ypresp_allfn)();
+void *ypresp_data;
+extern DB *specdbp;
+extern int yp_errno;
+
+/*
+ * This is largely the same as yp_all() except we do the transfer
+ * from a specific server without the aid of ypbind(8). We need to
+ * be able to specify the source host explicitly since ypxfr may
+ * only transfer maps from the NIS master server for any given domain.
+ * However, if we use the libc version of yp_all(), we could end up
+ * talking to one of the slaves instead. We do need to dig into libc
+ * a little though, since it contains the magic XDR function we need.
+ */
+int ypxfr_get_map(map, domain, host, callback)
+ char *map;
+ char *domain;
+ char *host;
+ int (*callback)();
+{
+ CLIENT *clnt;
+ ypreq_nokey req;
+ unsigned long status;
+ struct timeval timeout;
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 10;
+
+ /* YPPROC_ALL is a TCP service */
+ if ((clnt = clnt_create(host, YPPROG, YPVERS, "tcp")) == NULL) {
+ yp_error("%s", clnt_spcreateerror("failed to tcp handle"));
+ yp_errno = YPXFR_YPERR;
+ return(1);
+ }
+
+ req.domain = domain;
+ req.map = map;
+ ypresp_allfn = callback;
+ ypresp_data = NULL;
+
+ (void)clnt_call(clnt, YPPROC_ALL, xdr_ypreq_nokey, &req,
+ xdr_ypresp_all_seq, &status, timeout);
+
+ clnt_destroy(clnt);
+
+ if (status != YP_TRUE) {
+ yp_errno = YPXFR_YPERR;
+ return(1);
+ }
+
+ return(0);
+}
diff --git a/libexec/ypxfr/ypxfr_main.c b/libexec/ypxfr/ypxfr_main.c
new file mode 100644
index 0000000..e9bddb4
--- /dev/null
+++ b/libexec/ypxfr/ypxfr_main.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ypxfr_main.c,v 1.11 1995/12/25 02:53:33 wpaul Exp $
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+struct dom_binding {};
+#include <rpcsvc/ypclnt.h>
+#include "ypxfr_extern.h"
+
+#ifndef lint
+static const char rcsid[] = "$Id: ypxfr_main.c,v 1.11 1995/12/25 02:53:33 wpaul Exp $";
+#endif
+
+char *progname = "ypxfr";
+char *yp_dir = _PATH_YP;
+int _rpcpmstart = 0;
+int ypxfr_use_yplib = 0; /* Assume the worst. */
+int ypxfr_clear = 1;
+int ypxfr_prognum = 0;
+struct sockaddr_in ypxfr_callback_addr;
+struct yppushresp_xfr ypxfr_resp;
+DB *dbp;
+
+static void ypxfr_exit(retval, temp)
+ ypxfrstat retval;
+ char *temp;
+{
+ CLIENT *clnt;
+ int sock = RPC_ANYSOCK;
+ struct timeval timeout;
+
+ /* Clean up no matter what happened previously. */
+ if (temp != NULL) {
+ (void)(dbp->close)(dbp);
+ if (unlink(temp) == -1) {
+ yp_error("failed to unlink %s",strerror(errno));
+ }
+ }
+
+ if (_rpcpmstart) {
+ timeout.tv_sec = 20;
+ timeout.tv_usec = 0;
+
+ if ((clnt = clntudp_create(&ypxfr_callback_addr, ypxfr_prognum,
+ 1, timeout, &sock)) == NULL) {
+ yp_error("%s", clnt_spcreateerror("failed to establish callback handle"));
+ exit(1);
+ }
+
+ ypxfr_resp.status = retval;
+
+ if (yppushproc_xfrresp_1(&ypxfr_resp, clnt) == NULL) {
+ yp_error("%s", clnt_sperror(clnt, "callback failed"));
+ clnt_destroy(clnt);
+ exit(1);
+ }
+ clnt_destroy(clnt);
+ } else {
+ yp_error("Exiting: %s", ypxfrerr_string(retval));
+ }
+
+ exit(0);
+}
+
+static void usage()
+{
+ if (_rpcpmstart) {
+ ypxfr_exit(YPXFR_BADARGS,NULL);
+ } else {
+ fprintf(stderr,"usage: %s [-f] [-c] [-d target domain] \
+[-h source host] [-s source domain]\n", progname);
+ fprintf(stderr,"\t [-p path] [-C taskid program-number \
+ipaddr port] mapname\n");
+ exit(1);
+ }
+}
+
+int ypxfr_foreach(status, key, keylen, val, vallen, data)
+ int status;
+ char *key;
+ int keylen;
+ char *val;
+ int vallen;
+ char *data;
+{
+ DBT dbkey, dbval;
+
+ if (status != YP_TRUE)
+ return (status);
+
+ dbkey.data = key;
+ dbkey.size = keylen;
+ dbval.data = val;
+ dbval.size = vallen;
+
+ if (yp_put_record(dbp, &dbkey, &dbval) != YP_TRUE)
+ return(yp_errno);
+
+ return (0);
+}
+
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int ypxfr_force = 0;
+ char *ypxfr_dest_domain = NULL;
+ char *ypxfr_source_host = NULL;
+ char *ypxfr_source_domain = NULL;
+ char *ypxfr_local_domain = NULL;
+ char *ypxfr_master = NULL;
+ unsigned long ypxfr_order = -1, ypxfr_skew_check = -1;
+ char *ypxfr_mapname = NULL;
+ int ypxfr_args = 0;
+ char ypxfr_temp_map[MAXPATHLEN + 2];
+ char tempmap[MAXPATHLEN + 2];
+ char buf[MAXPATHLEN + 2];
+ DBT key, data;
+
+ debug = 1;
+
+ if (!isatty(fileno(stderr))) {
+ openlog(progname, LOG_PID, LOG_DAEMON);
+ _rpcpmstart = 1;
+ }
+
+ if (argc < 2)
+ usage();
+
+ while ((ch = getopt(argc, argv, "fcd:h:s:p:C:")) != EOF) {
+ int my_optind;
+ switch(ch) {
+ case 'f':
+ ypxfr_force++;
+ ypxfr_args++;
+ break;
+ case 'c':
+ ypxfr_clear = 0;
+ ypxfr_args++;
+ break;
+ case 'd':
+ ypxfr_dest_domain = optarg;
+ ypxfr_args += 2;
+ break;
+ case 'h':
+ ypxfr_source_host = optarg;
+ ypxfr_args += 2;
+ break;
+ case 's':
+ ypxfr_source_domain = optarg;
+ ypxfr_args += 2;
+ break;
+ case 'p':
+ yp_dir = optarg;
+ ypxfr_args += 2;
+ break;
+ case 'C':
+ /*
+ * Whoever decided that the -C flag should take
+ * four arguments is a twit.
+ */
+ my_optind = optind - 1;
+ if (argv[my_optind] == NULL || !strlen(argv[my_optind])) {
+ yp_error("transaction ID not specified");
+ usage();
+ }
+ ypxfr_resp.transid = atol(argv[my_optind]);
+ my_optind++;
+ if (argv[my_optind] == NULL || !strlen(argv[my_optind])) {
+ yp_error("RPC program number not specified");
+ usage();
+ }
+ ypxfr_prognum = atol(argv[my_optind]);
+ my_optind++;
+ if (argv[my_optind] == NULL || !strlen(argv[my_optind])) {
+ yp_error("address not specified");
+ usage();
+ }
+ if (!inet_aton(argv[my_optind], &ypxfr_callback_addr.sin_addr)) {
+ yp_error("failed to convert '%s' to IP addr",
+ argv[my_optind]);
+ exit(1);
+ }
+ my_optind++;
+ if (argv[my_optind] == NULL || !strlen(argv[my_optind])) {
+ yp_error("port not specified");
+ usage();
+ }
+ ypxfr_callback_addr.sin_port = htons((u_short)atoi(argv[my_optind]));
+ ypxfr_args += 5;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ ypxfr_mapname = argv[ypxfr_args + 1];
+
+ if (ypxfr_mapname == NULL) {
+ yp_error("no map name specified");
+ usage();
+ }
+
+ /* Always the case. */
+ ypxfr_callback_addr.sin_family = AF_INET;
+
+ /* Determine if local NIS client facilities are turned on. */
+ if (!yp_get_default_domain(&ypxfr_local_domain) &&
+ _yp_check(&ypxfr_local_domain))
+ ypxfr_use_yplib = 1;
+
+ /*
+ * If no destination domain is specified, assume that the
+ * local default domain is to be used and try to obtain it.
+ * Fails if NIS client facilities are turned off.
+ */
+ if (ypxfr_dest_domain == NULL) {
+ if (ypxfr_use_yplib) {
+ yp_get_default_domain(&ypxfr_dest_domain);
+ } else {
+ yp_error("no destination domain specified and \
+the local domain name isn't set");
+ ypxfr_exit(YPXFR_BADARGS,NULL);
+ }
+ }
+
+ /*
+ * If a source domain is not specified, assume it to
+ * be the same as the destination domain.
+ */
+ if (ypxfr_source_domain == NULL) {
+ ypxfr_source_domain = ypxfr_dest_domain;
+ }
+
+ /*
+ * If the source host is not specified, assume it to be the
+ * master for the specified map. If local NIS client facilities
+ * are turned on, we can figure this out using yp_master().
+ * If not, we have to see if a local copy of the map exists
+ * and extract its YP_MASTER_NAME record. If _that_ fails,
+ * we are stuck and must ask the user for more information.
+ */
+ if (ypxfr_source_host == NULL) {
+ if (!ypxfr_use_yplib) {
+ /*
+ * Double whammy: NIS isn't turned on and the user
+ * didn't specify a source host.
+ */
+ char *dptr;
+ key.data = "YP_MASTER_NAME";
+ key.size = sizeof("YP_MASTER_NAME") - 1;
+
+ if (yp_get_record(ypxfr_dest_domain, ypxfr_mapname,
+ &key, &data, 1) != YP_TRUE) {
+ yp_error("no source host specified");
+ ypxfr_exit(YPXFR_BADARGS,NULL);
+ }
+ dptr = data.data;
+ dptr[data.size] = '\0';
+ ypxfr_master = ypxfr_source_host = strdup(dptr);
+ }
+ } else {
+ if (ypxfr_use_yplib)
+ ypxfr_use_yplib = 0;
+ }
+
+ if (ypxfr_master == NULL) {
+ if ((ypxfr_master = ypxfr_get_master(ypxfr_source_domain,
+ ypxfr_mapname,
+ ypxfr_source_host,
+ ypxfr_use_yplib)) == NULL) {
+ yp_error("failed to find master of %s in domain %s",
+ ypxfr_mapname, ypxfr_source_domain);
+ ypxfr_exit(YPXFR_MADDR,NULL);
+ }
+ }
+
+ /*
+ * If we got here and ypxfr_source_host is still undefined,
+ * it means we had to resort to using yp_master() to find the
+ * master server for the map. The source host and master should
+ * be identical.
+ */
+ if (ypxfr_source_host == NULL)
+ ypxfr_source_host = ypxfr_master;
+
+ if ((ypxfr_order = ypxfr_get_order(ypxfr_source_domain,
+ ypxfr_mapname,
+ ypxfr_master, 0)) == 0) {
+ yp_error("failed to get order number of %s",
+ ypxfr_mapname);
+ ypxfr_exit(YPXFR_YPERR,NULL);
+ }
+
+ key.data = "YP_LAST_MODIFIED";
+ key.size = sizeof("YP_LAST_MODIFIED") - 1;
+
+ /* The order number is immaterial when the 'force' flag is set. */
+
+ if (!ypxfr_force) {
+ int ignore = 0;
+ if (yp_get_record(ypxfr_dest_domain,ypxfr_mapname,&key,&data,1) != YP_TRUE) {
+ switch(yp_errno) {
+ case YP_NOKEY:
+ ypxfr_exit(YPXFR_FORCE,NULL);
+ break;
+ case YP_NOMAP:
+ /*
+ * If the map doesn't exist, we're
+ * creating it. Ignore the error.
+ */
+ ignore++;
+ break;
+ case YP_BADDB:
+ default:
+ ypxfr_exit(YPXFR_DBM,NULL);
+ break;
+ }
+ }
+ if (!ignore && ypxfr_order <= atoi(data.data))
+ ypxfr_exit(YPXFR_AGE, NULL);
+
+ }
+
+ /* Construct a temporary map file name */
+ snprintf(tempmap, sizeof(tempmap), "%s.%d",ypxfr_mapname, getpid());
+ snprintf(ypxfr_temp_map, sizeof(ypxfr_temp_map), "%s/%s/%s", yp_dir,
+ ypxfr_dest_domain, tempmap);
+
+ /* Open the temporary map read/write. */
+ if ((dbp = yp_open_db_rw(ypxfr_dest_domain, tempmap)) == NULL) {
+ yp_error("failed to open temporary map file");
+ ypxfr_exit(YPXFR_DBM,NULL);
+ }
+
+ /*
+ * Fill in the keys we already know, such as the order number,
+ * master name, input file name (we actually make up a bogus
+ * name for that) and output file name.
+ */
+ snprintf(buf, sizeof(buf), "%d", ypxfr_order);
+ data.data = buf;
+ data.size = strlen(buf);
+
+ if (yp_put_record(dbp, &key, &data) != YP_TRUE) {
+ yp_error("failed to write order number to database");
+ ypxfr_exit(YPXFR_DBM,&ypxfr_temp_map);
+ }
+
+ key.data = "YP_MASTER_NAME";
+ key.size = sizeof("YP_MASTER_NAME") - 1;
+ data.data = ypxfr_master;
+ data.size = strlen(ypxfr_master);
+
+ if (yp_put_record(dbp, &key, &data) != YP_TRUE) {
+ yp_error("failed to write master name to database");
+ ypxfr_exit(YPXFR_DBM,&ypxfr_temp_map);
+ }
+
+ key.data = "YP_DOMAIN_NAME";
+ key.size = sizeof("YP_DOMAIN_NAME") - 1;
+ data.data = ypxfr_dest_domain;
+ data.size = strlen(ypxfr_dest_domain);
+
+ if (yp_put_record(dbp, &key, &data) != YP_TRUE) {
+ yp_error("failed to write domain name to database");
+ ypxfr_exit(YPXFR_DBM,&ypxfr_temp_map);
+ }
+
+ snprintf (buf, sizeof(buf), "%s:%s", ypxfr_source_host, ypxfr_mapname);
+
+ key.data = "YP_INPUT_NAME";
+ key.size = sizeof("YP_INPUT_NAME") - 1;
+ data.data = &buf;
+ data.size = strlen(buf);
+
+ if (yp_put_record(dbp, &key, &data) != YP_TRUE) {
+ yp_error("failed to write input name to database");
+ ypxfr_exit(YPXFR_DBM,&ypxfr_temp_map);
+
+ }
+
+ snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, ypxfr_dest_domain,
+ ypxfr_mapname);
+
+ key.data = "YP_OUTPUT_NAME";
+ key.size = sizeof("YP_OUTPUT_NAME") - 1;
+ data.data = &buf;
+ data.size = strlen(buf);
+
+ if (yp_put_record(dbp, &key, &data) != YP_TRUE) {
+ yp_error("failed to write output name to database");
+ ypxfr_exit(YPXFR_DBM,&ypxfr_temp_map);
+ }
+
+ /* Now suck over the contents of the map from the master. */
+
+ if (ypxfr_get_map(ypxfr_mapname,ypxfr_source_domain,
+ ypxfr_source_host, ypxfr_foreach)){
+ yp_error("failed to retrieve map from source host");
+ ypxfr_exit(YPXFR_YPERR,&ypxfr_temp_map);
+ }
+
+ (void)(dbp->close)(dbp);
+
+ /* Peek at the order number again and check for skew. */
+ if ((ypxfr_skew_check = ypxfr_get_order(ypxfr_source_domain,
+ ypxfr_mapname,
+ ypxfr_master, 0)) == 0) {
+ yp_error("failed to get order number of %s",
+ ypxfr_mapname);
+ ypxfr_exit(YPXFR_YPERR,&ypxfr_temp_map);
+ }
+
+ if (ypxfr_order != ypxfr_skew_check)
+ ypxfr_exit(YPXFR_SKEW,&ypxfr_temp_map);
+
+ /*
+ * Send a YPPROC_CLEAR to the local ypserv.
+ * The FreeBSD ypserv doesn't really need this, but we send it
+ * here anyway for the sake of consistency.
+ */
+ if (!ypxfr_clear) {
+ char in = 0;
+ char *out = NULL;
+ if (callrpc("localhost",YPPROG,YPVERS,YPPROC_CLEAR, xdr_void,
+ (void *)&in, xdr_void, (void *)out) == NULL) {
+ yp_error("failed to send 'clear' to local ypserv");
+ ypxfr_exit(YPXFR_YPERR, &ypxfr_temp_map);
+ }
+ }
+
+ if (unlink(buf) == -1 && errno != ENOENT) {
+ yp_error("unlink(%s) failed: %s", buf, strerror(errno));
+ ypxfr_exit(YPXFR_FILE,NULL);
+ }
+
+ if (rename(ypxfr_temp_map, buf) == -1) {
+ yp_error("rename(%s,%s) failed: %s", ypxfr_temp_map, buf,
+ strerror(errno));
+ ypxfr_exit(YPXFR_FILE,NULL);
+ }
+
+ ypxfr_exit(YPXFR_SUCC,NULL);
+
+ return(1);
+}
diff --git a/libexec/ypxfr/ypxfr_misc.c b/libexec/ypxfr/ypxfr_misc.c
new file mode 100644
index 0000000..4093dae
--- /dev/null
+++ b/libexec/ypxfr/ypxfr_misc.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ypxfr_misc.c,v 1.6 1995/12/24 04:39:55 wpaul Exp $
+ */
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+struct dom_binding {};
+#include <rpcsvc/ypclnt.h>
+#include "ypxfr_extern.h"
+
+#ifndef lint
+static const char rcsid[] = "$Id: ypxfr_misc.c,v 1.6 1995/12/24 04:39:55 wpaul Exp $";
+#endif
+
+char *ypxfrerr_string(code)
+ ypxfrstat code;
+{
+ switch(code) {
+ case YPXFR_SUCC:
+ return ("Map successfully transfered");
+ break;
+ case YPXFR_AGE:
+ return ("Master's version not newer");
+ break;
+ case YPXFR_NOMAP:
+ return ("No such map in server's domain");
+ break;
+ case YPXFR_NODOM:
+ return ("Domain not supported by server");
+ break;
+ case YPXFR_RSRC:
+ return ("Local resource allocation failure");
+ break;
+ case YPXFR_RPC:
+ return ("RPC failure talking to server");
+ break;
+ case YPXFR_MADDR:
+ return ("Could not get master server address");
+ break;
+ case YPXFR_YPERR:
+ return ("NIS server/map database error");
+ break;
+ case YPXFR_BADARGS:
+ return ("Request arguments bad");
+ break;
+ case YPXFR_DBM:
+ return ("Local database operation failed");
+ break;
+ case YPXFR_FILE:
+ return ("Local file I/O operation failed");
+ break;
+ case YPXFR_SKEW:
+ return ("Map version skew during transfer");
+ break;
+ case YPXFR_CLEAR:
+ return ("Couldn't send \"clear\" request to local ypserv");
+ break;
+ case YPXFR_FORCE:
+ return ("No local order number in map -- use -f flag");
+ break;
+ case YPXFR_XFRERR:
+ return ("General ypxfr error");
+ break;
+ case YPXFR_REFUSED:
+ return ("Transfer request refused by ypserv");
+ break;
+ default:
+ return ("Unknown error code");
+ break;
+ }
+}
+
+/*
+ * These are wrappers for the usual yp_master() and yp_order() functions.
+ * They can use either local yplib functions (the real yp_master() and
+ * yp_order()) or do direct RPCs to a specified server. The latter is
+ * necessary if ypxfr is run on a machine that isn't configured as an
+ * NIS client (this can happen very easily: a given machine need not be
+ * an NIS client in order to be an NIS server).
+ */
+
+char *ypxfr_get_master(domain,map,source,yplib)
+ char *domain;
+ char *map;
+ char *source;
+ const int yplib;
+{
+ if (yplib) {
+ int res;
+ char *master;
+ if ((res = yp_master(domain, map, &master))) {
+ switch (res) {
+ case YPERR_DOMAIN:
+ yp_errno = YPXFR_NODOM;
+ break;
+ case YPERR_MAP:
+ yp_errno = YPXFR_NOMAP;
+ break;
+ case YPERR_YPERR:
+ default:
+ yp_errno = YPXFR_YPERR;
+ break;
+ }
+ return(NULL);
+ } else
+ return(master);
+ } else {
+ CLIENT *clnt;
+ ypresp_master *resp;
+ ypreq_nokey req;
+
+ if ((clnt = clnt_create(source,YPPROG,YPVERS,"udp")) == NULL) {
+ yp_error("%s",clnt_spcreateerror("failed to \
+create udp handle to ypserv"));
+ yp_errno = YPXFR_RPC;
+ return(NULL);
+ }
+
+ req.map = map;
+ req.domain = domain;
+ if ((resp = ypproc_master_2(&req, clnt)) == NULL) {
+ yp_error("%s",clnt_sperror(clnt,"YPPROC_MASTER \
+failed"));
+ clnt_destroy(clnt);
+ yp_errno = YPXFR_RPC;
+ return(NULL);
+ }
+ clnt_destroy(clnt);
+ if (resp->stat != YP_TRUE) {
+ switch (resp->stat) {
+ case YP_NODOM:
+ yp_errno = YPXFR_NODOM;
+ break;
+ case YP_NOMAP:
+ yp_errno = YPXFR_NOMAP;
+ break;
+ case YP_YPERR:
+ default:
+ yp_errno = YPXFR_YPERR;
+ break;
+ }
+ return(NULL);
+ }
+ return(resp->peer);
+ }
+}
+
+unsigned long ypxfr_get_order(domain, map, source, yplib)
+ char *domain;
+ char *map;
+ char *source;
+ const int yplib;
+{
+ if (yplib) {
+ unsigned long order;
+ int res;
+ if ((res = yp_order(domain, map, (int *)&order))) {
+ switch (res) {
+ case (YPERR_DOMAIN):
+ yp_errno = YPXFR_NODOM;
+ break;
+ case (YPERR_MAP):
+ yp_errno = YPXFR_NOMAP;
+ break;
+ case (YPERR_YPERR):
+ default:
+ yp_errno = YPXFR_YPERR;
+ break;
+ }
+ return(0);
+ } else
+ return(order);
+ } else {
+ CLIENT *clnt;
+ ypresp_order *resp;
+ ypreq_nokey req;
+
+ if ((clnt = clnt_create(source,YPPROG,YPVERS,"udp")) == NULL) {
+ yp_error("%s",clnt_spcreateerror("couldn't create \
+udp handle to ypserv"));
+ yp_errno = YPXFR_RPC;
+ return(0);
+ }
+ req.map = map;
+ req.domain = domain;
+ if ((resp = ypproc_order_2(&req, clnt)) == NULL) {
+ yp_error("%s", clnt_sperror(clnt, "YPPROC_ORDER \
+failed"));
+ clnt_destroy(clnt);
+ yp_errno = YPXFR_RPC;
+ return(0);
+ }
+ clnt_destroy(clnt);
+ if (resp->stat != YP_TRUE) {
+ switch (resp->stat) {
+ case (YP_NODOM):
+ yp_errno = YPXFR_NODOM;
+ break;
+ case (YP_NOMAP):
+ yp_errno = YPXFR_NOMAP;
+ break;
+ case (YP_YPERR):
+ default:
+ yp_errno = YPXFR_YPERR;
+ break;
+ }
+ return(0);
+ }
+ return(resp->ordernum);
+ }
+}
OpenPOWER on IntegriCloud