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.877
-rw-r--r--libexec/atrun/atrun.c479
-rw-r--r--libexec/atrun/atrun.h33
-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/Makefile16
-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.c252
-rw-r--r--libexec/bootpd/syslog.conf63
-rw-r--r--libexec/bootpd/tools/Makefile6
-rw-r--r--libexec/bootpd/tools/Makefile.inc4
-rw-r--r--libexec/bootpd/tools/bootpef/Makefile13
-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/Makefile12
-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/bootpgw/Makefile12
-rw-r--r--libexec/bootpgw/bootpgw.c675
-rw-r--r--libexec/bugfiler/Makefile17
-rw-r--r--libexec/bugfiler/bug.h92
-rw-r--r--libexec/bugfiler/bugfiler.8290
-rw-r--r--libexec/bugfiler/bugfiler.c167
-rw-r--r--libexec/bugfiler/bugformat32
-rw-r--r--libexec/bugfiler/error.c89
-rw-r--r--libexec/bugfiler/extern.h41
-rw-r--r--libexec/bugfiler/gethead.c165
-rw-r--r--libexec/bugfiler/pathnames.h38
-rw-r--r--libexec/bugfiler/process.c115
-rw-r--r--libexec/bugfiler/redist.c131
-rw-r--r--libexec/bugfiler/reply.c118
-rw-r--r--libexec/bugfiler/sendbug.185
-rw-r--r--libexec/bugfiler/sendbug.sh66
-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.h65
-rw-r--r--libexec/ftpd/ftpcmd.y1266
-rw-r--r--libexec/ftpd/ftpd.8310
-rw-r--r--libexec/ftpd/ftpd.c1853
-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/Makefile10
-rw-r--r--libexec/getty/extern.h56
-rw-r--r--libexec/getty/fbtab_stuff.c93
-rw-r--r--libexec/getty/getty.8127
-rw-r--r--libexec/getty/gettytab.5324
-rw-r--r--libexec/getty/gettytab.c339
-rw-r--r--libexec/getty/gettytab.h145
-rw-r--r--libexec/getty/init.c121
-rw-r--r--libexec/getty/main.c502
-rw-r--r--libexec/getty/pathnames.h39
-rw-r--r--libexec/getty/subr.c518
-rw-r--r--libexec/getty/ttydefaults.c54
-rw-r--r--libexec/getty/ttys.5139
-rw-r--r--libexec/kpasswdd/Makefile20
-rw-r--r--libexec/kpasswdd/kpasswdd.860
-rw-r--r--libexec/kpasswdd/kpasswdd.c271
-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/Makefile12
-rw-r--r--libexec/rbootd/bpf.c422
-rw-r--r--libexec/rbootd/conf.c90
-rw-r--r--libexec/rbootd/defs.h185
-rw-r--r--libexec/rbootd/parseconf.c359
-rw-r--r--libexec/rbootd/pathnames.h51
-rw-r--r--libexec/rbootd/rbootd.8156
-rw-r--r--libexec/rbootd/rbootd.c508
-rw-r--r--libexec/rbootd/rmp.h95
-rw-r--r--libexec/rbootd/rmp_var.h244
-rw-r--r--libexec/rbootd/rmpproto.c593
-rw-r--r--libexec/rbootd/utils.c557
-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/authenc.c91
-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
217 files changed, 48347 insertions, 0 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.8 b/libexec/atrun/atrun.8
new file mode 100644
index 0000000..ed6e359
--- /dev/null
+++ b/libexec/atrun/atrun.8
@@ -0,0 +1,77 @@
+.\"
+.\" 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. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Christopher G. Demetriou.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software withough 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: atrun.8,v 1.1 1994/01/05 01:02:58 nate Exp $
+.\"
+.Dd December 5, 1993
+.Dt ATRUN 8
+.Os FreeBSD 1.1
+.Sh NAME
+.Nm atrun
+.Nd run jobs queued for later execution
+.\"
+.Sh SYOPSIS
+.Nm atrun
+.Sh DESCRIPTION
+The
+.Nm atrun
+utility runs commands queued by
+.Xr at 1 .
+It is usually invoked by
+.Xr crond 8
+every ten minutes.
+.Sh FILES
+.Bl -tag -width /var/at/lockfile -compact
+.It Pa /var/at/jobs
+Directory containing job files
+.It Pa /var/at/spool
+Directory containing output spool files
+.It Pa /var/at/lockfile
+Job-creation lock file.
+.El
+.Sh SEE ALSO
+.Xr crond 8 ,
+.Xr at 1
+.Sh AUTHOR
+.Bl -tag
+Thomas Koenig, ig25@rz.uni-karlsruhe.de
+.El
+.Sh BUGS
+The functionality of
+.Nm atrun
+should arguaby be merged into
+.Xr crond 8 .
+.Sh CAVEATS
+Since the default configuration causes
+.Nm atrun
+to be invoked every ten minutes,
+commands queued by
+.Xr at 1
+may end up being executed up to nine minutes
+later than would be otherwise expected.
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.h b/libexec/atrun/atrun.h
new file mode 100644
index 0000000..be0588a
--- /dev/null
+++ b/libexec/atrun/atrun.h
@@ -0,0 +1,33 @@
+/*
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software withough 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: atrun.h,v 1.1 1993/12/05 11:36:45 cgd Exp $
+ */
+
+#define ATRUN_MAXLOAD 1.5
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
new file mode 100644
index 0000000..e923968
--- /dev/null
+++ b/libexec/bootpd/Makefile
@@ -0,0 +1,16 @@
+# bootpd/Makefile
+# $Id: Makefile,v 1.2 1995/05/30 05:45:45 rgrimes Exp $
+
+PROG= bootpd
+CFLAGS+= -DETC_ETHERS
+CFLAGS+= -DSYSLOG -DDEBUG -DVEND_CMU
+
+SUBDIR= tools
+
+SRCS= bootpd.c dovend.c readfile.c hash.c dumptab.c \
+ lookup.c getif.c hwaddr.c report.c tzone.c rtmsg.c
+
+MAN5= bootptab.5
+MAN8= bootpd.8
+
+.include <bsd.prog.mk>
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
new file mode 100644
index 0000000..5942b22
--- /dev/null
+++ b/libexec/bootpd/rtmsg.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 1984, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1994
+ * Geoffrey M. Rehmet, All rights reserved.
+ *
+ * This code is derived from software which forms part of the 4.4-Lite
+ * Berkeley software distribution, which was in derived from software
+ * contributed to Berkeley by Sun Microsystems, Inc.
+ *
+ * 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.
+ */
+
+/*
+ * from arp.c 8.2 (Berkeley) 1/2/94
+ * $Id: rtmsg.c,v 1.3 1995/01/30 11:11:43 dfr Exp $
+ */
+
+#include <sys/param.h>
+/*
+ * Verify that we are at least 4.4 BSD
+ */
+#if defined(BSD)
+#if BSD >= 199306
+
+#include <sys/socket.h>
+#include <sys/filio.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "report.h"
+
+
+static int rtmsg __P((int));
+
+static int s = -1; /* routing socket */
+
+
+/*
+ * Open the routing socket
+ */
+static void getsocket () {
+ if (s < 0) {
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0) {
+ 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);
+ }
+ }
+}
+
+static struct sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}};
+static struct sockaddr_inarp blank_sin = {sizeof(blank_sin), AF_INET }, sin_m;
+static struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
+static int expire_time, flags, export_only, doing_proxy;
+static struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+/*
+ * Set an individual arp entry
+ */
+int bsd_arp_set(ia, eaddr, len)
+ struct in_addr *ia;
+ char *eaddr;
+ int len;
+{
+ register struct sockaddr_inarp *sin = &sin_m;
+ register struct sockaddr_dl *sdl;
+ register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
+ u_char *ea;
+ struct timeval time;
+ int op = RTM_ADD;
+
+ getsocket();
+ sdl_m = blank_sdl;
+ sin_m = blank_sin;
+ sin->sin_addr = *ia;
+
+ ea = (u_char *)LLADDR(&sdl_m);
+ bcopy(eaddr, ea, len);
+ sdl_m.sdl_alen = len;
+ doing_proxy = flags = export_only = expire_time = 0;
+
+ /* make arp entry temporary */
+ gettimeofday(&time, 0);
+ expire_time = time.tv_sec + 20 * 60;
+
+tryagain:
+ if (rtmsg(RTM_GET) < 0) {
+ report(LOG_WARNING, "rtmget: %s", strerror(errno));
+ return (1);
+ }
+ sin = (struct sockaddr_inarp *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin);
+ if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
+ if (sdl->sdl_family == AF_LINK &&
+ (rtm->rtm_flags & RTF_LLINFO) &&
+ !(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",
+ inet_ntoa(sin->sin_addr));
+ return (1);
+ }
+ if (sin_m.sin_other & SIN_PROXY) {
+ report(LOG_WARNING,
+ "set: proxy entry exists for non 802 device\n");
+ return(1);
+ }
+ sin_m.sin_other = SIN_PROXY;
+ export_only = 1;
+ goto tryagain;
+ }
+overwrite:
+ if (sdl->sdl_family != AF_LINK) {
+ report(LOG_WARNING,
+ "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(op));
+}
+
+
+static int rtmsg(cmd)
+ int cmd;
+{
+ static int seq;
+ int rlen;
+ register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+ register char *cp = m_rtmsg.m_space;
+ register int l;
+
+ errno = 0;
+ bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
+ rtm->rtm_flags = flags;
+ rtm->rtm_version = RTM_VERSION;
+
+ switch (cmd) {
+ default:
+ 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;
+ rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
+ sin_m.sin_other = 0;
+ if (doing_proxy) {
+ if (export_only)
+ sin_m.sin_other = SIN_PROXY;
+ else {
+ rtm->rtm_addrs |= RTA_NETMASK;
+ rtm->rtm_flags &= ~RTF_HOST;
+ }
+ }
+ /* FALLTHROUGH */
+ case RTM_GET:
+ rtm->rtm_addrs |= RTA_DST;
+ }
+#define NEXTADDR(w, s) \
+ if (rtm->rtm_addrs & (w)) { \
+ bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);}
+
+ NEXTADDR(RTA_DST, sin_m);
+ NEXTADDR(RTA_GATEWAY, sdl_m);
+ NEXTADDR(RTA_NETMASK, so_mask);
+
+ rtm->rtm_msglen = cp - (char *)&m_rtmsg;
+
+ l = rtm->rtm_msglen;
+ rtm->rtm_seq = ++seq;
+ rtm->rtm_type = cmd;
+ if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
+ if ((errno != ESRCH) && !(errno == EEXIST && cmd == RTM_ADD)){
+ report(LOG_WARNING, "writing to routing socket: %s",
+ strerror(errno));
+ return (-1);
+ }
+ }
+ do {
+ l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ } 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));
+ return (0);
+}
+
+#endif /* BSD */
+#endif /* BSD >= 199306 */
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/Makefile b/libexec/bootpd/tools/Makefile
new file mode 100644
index 0000000..b1f38e7
--- /dev/null
+++ b/libexec/bootpd/tools/Makefile
@@ -0,0 +1,6 @@
+# Makefile
+# $Id$
+
+SUBDIR= bootpef bootptest
+
+.include <bsd.subdir.mk>
diff --git a/libexec/bootpd/tools/Makefile.inc b/libexec/bootpd/tools/Makefile.inc
new file mode 100644
index 0000000..0a2ab66
--- /dev/null
+++ b/libexec/bootpd/tools/Makefile.inc
@@ -0,0 +1,4 @@
+# Makefile.inc
+# $Id$
+
+BINDIR=/usr/sbin
diff --git a/libexec/bootpd/tools/bootpef/Makefile b/libexec/bootpd/tools/bootpef/Makefile
new file mode 100644
index 0000000..7d17606
--- /dev/null
+++ b/libexec/bootpd/tools/bootpef/Makefile
@@ -0,0 +1,13 @@
+# Makefile
+# $Id$
+
+PROG= bootpef
+MAN8= bootpef.8
+SRCS= bootpef.c dovend.c readfile.c hash.c dumptab.c lookup.c \
+ hwaddr.c report.c tzone.c rtmsg.c
+
+SRCDIR= ${.CURDIR}/../..
+CFLAGS+=-I${SRCDIR}
+.PATH: ${SRCDIR}
+
+.include <bsd.prog.mk>
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
new file mode 100644
index 0000000..d137e73
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/Makefile
@@ -0,0 +1,12 @@
+# Makefile
+# $Id: Makefile,v 1.2 1995/05/30 05:45:51 rgrimes Exp $
+
+PROG= bootptest
+MAN8= bootptest.8
+SRCS= bootptest.c getether.c getif.c print-bootp.c report.c
+
+SRCDIR= ${.CURDIR}/../..
+CFLAGS+=-I${SRCDIR}
+.PATH: ${SRCDIR}
+
+.include <bsd.prog.mk>
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/bootpgw/Makefile b/libexec/bootpgw/Makefile
new file mode 100644
index 0000000..a155e0e
--- /dev/null
+++ b/libexec/bootpgw/Makefile
@@ -0,0 +1,12 @@
+# Makefile
+# $Id$
+
+PROG= bootpgw
+NOMAN= true
+SRCS= bootpgw.c getif.c hwaddr.c report.c rtmsg.c
+
+SRCDIR= ${.CURDIR}/../bootpd
+CFLAGS+=-I${SRCDIR}
+.PATH: ${SRCDIR}
+
+.include <bsd.prog.mk>
diff --git a/libexec/bootpgw/bootpgw.c b/libexec/bootpgw/bootpgw.c
new file mode 100644
index 0000000..4e7538b
--- /dev/null
+++ b/libexec/bootpgw/bootpgw.c
@@ -0,0 +1,675 @@
+/*
+ * bootpgw.c - BOOTP GateWay
+ * This program forwards BOOTP Request packets to a BOOTP server.
+ */
+
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+
+#ifndef lint
+static char rcsid[] = "$Id: bootpgw.c,v 1.1.1.1 1994/09/10 14:44:54 csgr Exp $";
+#endif
+
+/*
+ * BOOTPGW is typically used to forward BOOTP client requests from
+ * one subnet to a BOOTP server on a different subnet.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <assert.h>
+
+#ifdef NO_SETSID
+# include <fcntl.h> /* for O_RDONLY, etc */
+#endif
+
+#ifndef USE_BFUNCS
+# include <memory.h>
+/* Yes, memcpy is OK here (no overlapped copies). */
+# define bcopy(a,b,c) memcpy(b,a,c)
+# define bzero(p,l) memset(p,0,l)
+# define bcmp(a,b,c) memcmp(a,b,c)
+#endif
+
+#include "bootp.h"
+#include "getif.h"
+#include "hwaddr.h"
+#include "report.h"
+#include "patchlevel.h"
+
+/* Local definitions: */
+#define MAX_MSG_SIZE (3*512) /* Maximum packet size */
+#define TRUE 1
+#define FALSE 0
+#define get_network_errmsg get_errmsg
+
+
+
+/*
+ * Externals, forward declarations, and global variables
+ */
+
+#ifdef __STDC__
+#define P(args) args
+#else
+#define P(args) ()
+#endif
+
+static void usage P((void));
+static void handle_reply P((void));
+static void handle_request P((void));
+
+#undef P
+
+/*
+ * IP port numbers for client and server obtained from /etc/services
+ */
+
+u_short bootps_port, bootpc_port;
+
+
+/*
+ * Internet socket and interface config structures
+ */
+
+struct sockaddr_in bind_addr; /* Listening */
+struct sockaddr_in recv_addr; /* Packet source */
+struct sockaddr_in send_addr; /* destination */
+
+
+/*
+ * option defaults
+ */
+int debug = 0; /* Debugging flag (level) */
+struct timeval actualtimeout =
+{ /* fifteen minutes */
+ 15 * 60L, /* tv_sec */
+ 0 /* tv_usec */
+};
+u_int maxhops = 4; /* Number of hops allowed for requests. */
+u_int minwait = 3; /* Number of seconds client must wait before
+ its bootrequest packets are forwarded. */
+
+/*
+ * General
+ */
+
+int s; /* Socket file descriptor */
+char *pktbuf; /* Receive packet buffer */
+int pktlen;
+char *progname;
+char *servername;
+int32 server_ipa; /* Real server IP address, network order. */
+
+char myhostname[64];
+struct in_addr my_ip_addr;
+
+
+
+
+/*
+ * Initialization such as command-line processing is done and then the
+ * main server loop is started.
+ */
+
+void
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct timeval *timeout;
+ struct bootp *bp;
+ struct servent *servp;
+ struct hostent *hep;
+ char *stmp;
+ int n, ba_len, ra_len;
+ int nfound, readfds;
+ int standalone;
+
+ progname = strrchr(argv[0], '/');
+ if (progname) progname++;
+ else progname = argv[0];
+
+ /*
+ * Initialize logging.
+ */
+ report_init(0); /* uses progname */
+
+ /*
+ * Log startup
+ */
+ report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
+
+ /* Debugging for compilers with struct padding. */
+ assert(sizeof(struct bootp) == BP_MINPKTSZ);
+
+ /* Get space for receiving packets and composing replies. */
+ pktbuf = malloc(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_INFO, "getsockname: not an INET socket");
+ }
+ }
+ /*
+ * Set defaults that might be changed by option switches.
+ */
+ stmp = NULL;
+ timeout = &actualtimeout;
+ gethostname(myhostname, sizeof(myhostname));
+ hep = gethostbyname(myhostname);
+ if (!hep) {
+ printf("Can not get my IP address\n");
+ exit(1);
+ }
+ bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
+
+ /*
+ * Read switches.
+ */
+ for (argc--, argv++; argc > 0; argc--, argv++) {
+ if (argv[0][0] != '-')
+ break;
+ switch (argv[0][1]) {
+
+ case 'd': /* debug level */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else if (argv[1] && argv[1][0] == '-') {
+ /*
+ * Backwards-compatible behavior:
+ * no parameter, so just increment the debug flag.
+ */
+ debug++;
+ break;
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
+ fprintf(stderr,
+ "%s: invalid debug level\n", progname);
+ break;
+ }
+ debug = n;
+ break;
+
+ case 'h': /* hop count limit */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
+ (n < 0) || (n > 16))
+ {
+ fprintf(stderr,
+ "bootpgw: invalid hop count limit\n");
+ break;
+ }
+ maxhops = (u_int)n;
+ break;
+
+ case 'i': /* inetd mode */
+ standalone = FALSE;
+ break;
+
+ case 's': /* standalone mode */
+ standalone = TRUE;
+ break;
+
+ case 't': /* timeout */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
+ fprintf(stderr,
+ "%s: invalid timeout specification\n", progname);
+ break;
+ }
+ actualtimeout.tv_sec = (int32) (60 * n);
+ /*
+ * If the actual timeout is zero, pass a NULL pointer
+ * to select so it blocks indefinitely, otherwise,
+ * point to the actual timeout value.
+ */
+ timeout = (n > 0) ? &actualtimeout : NULL;
+ break;
+
+ case 'w': /* wait time */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
+ (n < 0) || (n > 60))
+ {
+ fprintf(stderr,
+ "bootpgw: invalid wait time\n");
+ break;
+ }
+ minwait = (u_int)n;
+ break;
+
+ default:
+ fprintf(stderr, "%s: unknown switch: -%c\n",
+ progname, argv[0][1]);
+ usage();
+ break;
+
+ } /* switch */
+ } /* for args */
+
+ /* Make sure server name argument is suplied. */
+ servername = argv[0];
+ if (!servername) {
+ fprintf(stderr, "bootpgw: missing server name\n");
+ usage();
+ }
+ /*
+ * Get address of real bootp server.
+ */
+ if (isdigit(servername[0]))
+ server_ipa = inet_addr(servername);
+ else {
+ hep = gethostbyname(servername);
+ if (!hep) {
+ fprintf(stderr, "bootpgw: can't get addr for %s\n", servername);
+ exit(1);
+ }
+ bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa));
+ }
+
+ if (standalone) {
+ /*
+ * Go into background and disassociate from controlling terminal.
+ * XXX - This is not the POSIX way (Should use setsid). -gwr
+ */
+ if (debug < 3) {
+ if (fork())
+ exit(0);
+#ifdef NO_SETSID
+ setpgrp(0,0);
+#ifdef TIOCNOTTY
+ n = open("/dev/tty", O_RDWR);
+ if (n >= 0) {
+ ioctl(n, TIOCNOTTY, (char *) 0);
+ (void) close(n);
+ }
+#endif /* TIOCNOTTY */
+#else /* SETSID */
+ if (setsid() < 0)
+ perror("setsid");
+#endif /* SETSID */
+ } /* if debug < 3 */
+ /*
+ * Nuke any timeout value
+ */
+ timeout = NULL;
+
+ /*
+ * Here, bootpd would do:
+ * chdir
+ * tzone_init
+ * rdtab_init
+ * readtab
+ */
+
+ /*
+ * Create a socket.
+ */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ report(LOG_ERR, "socket: %s", get_network_errmsg());
+ exit(1);
+ }
+ /*
+ * Get server's listening port number
+ */
+ servp = getservbyname("bootps", "udp");
+ if (servp) {
+ bootps_port = ntohs((u_short) servp->s_port);
+ } else {
+ bootps_port = (u_short) IPPORT_BOOTPS;
+ report(LOG_ERR,
+ "udp/bootps: unknown service -- assuming port %d",
+ bootps_port);
+ }
+
+ /*
+ * Bind socket to BOOTPS port.
+ */
+ bind_addr.sin_family = AF_INET;
+ bind_addr.sin_port = htons(bootps_port);
+ bind_addr.sin_addr.s_addr = INADDR_ANY;
+ if (bind(s, (struct sockaddr *) &bind_addr,
+ sizeof(bind_addr)) < 0)
+ {
+ report(LOG_ERR, "bind: %s", get_network_errmsg());
+ exit(1);
+ }
+ } /* if standalone */
+ /*
+ * Get destination port number so we can reply to client
+ */
+ servp = getservbyname("bootpc", "udp");
+ if (servp) {
+ bootpc_port = ntohs(servp->s_port);
+ } else {
+ report(LOG_ERR,
+ "udp/bootpc: unknown service -- assuming port %d",
+ IPPORT_BOOTPC);
+ bootpc_port = (u_short) IPPORT_BOOTPC;
+ }
+
+ /* no signal catchers */
+
+ /*
+ * Process incoming requests.
+ */
+ for (;;) {
+ readfds = 1 << s;
+ nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, timeout);
+ if (nfound < 0) {
+ if (errno != EINTR) {
+ report(LOG_ERR, "select: %s", get_errmsg());
+ }
+ continue;
+ }
+ if (!(readfds & (1 << s))) {
+ report(LOG_INFO, "exiting after %ld minutes of inactivity",
+ actualtimeout.tv_sec / 60);
+ exit(0);
+ }
+ ra_len = sizeof(recv_addr);
+ n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
+ (struct sockaddr *) &recv_addr, &ra_len);
+ if (n <= 0) {
+ continue;
+ }
+ if (debug > 3) {
+ report(LOG_INFO, "recvd pkt from IP addr %s",
+ inet_ntoa(recv_addr.sin_addr));
+ }
+ if (n < sizeof(struct bootp)) {
+ if (debug) {
+ report(LOG_INFO, "received short packet");
+ }
+ continue;
+ }
+ pktlen = n;
+
+ switch (bp->bp_op) {
+ case BOOTREQUEST:
+ handle_request();
+ break;
+ case BOOTREPLY:
+ handle_reply();
+ break;
+ }
+ }
+}
+
+
+
+
+/*
+ * Print "usage" message and exit
+ */
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n");
+ fprintf(stderr, "\t -d n\tset debug level\n");
+ fprintf(stderr, "\t -h n\tset max hop count\n");
+ fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
+ fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
+ fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
+ fprintf(stderr, "\t -w n\tset min wait time (secs)\n");
+ exit(1);
+}
+
+
+
+/*
+ * Process BOOTREQUEST packet.
+ *
+ * Note, this just forwards the request to a real server.
+ */
+static void
+handle_request()
+{
+ struct bootp *bp = (struct bootp *) pktbuf;
+ struct ifreq *ifr;
+ u_short secs, hops;
+
+ /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
+
+ if (debug) {
+ report(LOG_INFO, "request from %s",
+ inet_ntoa(recv_addr.sin_addr));
+ }
+ /* Has the client been waiting long enough? */
+ secs = ntohs(bp->bp_secs);
+ if (secs < minwait)
+ return;
+
+ /* Has this packet hopped too many times? */
+ hops = ntohs(bp->bp_hops);
+ if (++hops > maxhops) {
+ report(LOG_NOTICE, "reqest from %s reached hop limit",
+ inet_ntoa(recv_addr.sin_addr));
+ return;
+ }
+ bp->bp_hops = htons(hops);
+
+ /*
+ * Here one might discard a request from the same subnet as the
+ * real server, but we can assume that the real server will send
+ * a reply to the client before it waits for minwait seconds.
+ */
+
+ /* If gateway address is not set, put in local interface addr. */
+ if (bp->bp_giaddr.s_addr == 0) {
+#if 0 /* BUG */
+ struct sockaddr_in *sip;
+ /*
+ * XXX - This picks the wrong interface when the receive addr
+ * is the broadcast address. There is no portable way to
+ * find out which interface a broadcast was received on. -gwr
+ * (Thanks to <walker@zk3.dec.com> for finding this bug!)
+ */
+ ifr = getif(s, &recv_addr.sin_addr);
+ if (!ifr) {
+ report(LOG_NOTICE, "no interface for request from %s",
+ inet_ntoa(recv_addr.sin_addr));
+ return;
+ }
+ sip = (struct sockaddr_in *) &(ifr->ifr_addr);
+ bp->bp_giaddr = sip->sin_addr;
+#else /* BUG */
+ /*
+ * XXX - Just set "giaddr" to our "official" IP address.
+ * RFC 1532 says giaddr MUST be set to the address of the
+ * interface on which the request was received. Setting
+ * it to our "default" IP address is not strictly correct,
+ * but is good enough to allow the real BOOTP server to
+ * get the reply back here. Then, before we forward the
+ * reply to the client, the giaddr field is corrected.
+ * (In case the client uses giaddr, which it should not.)
+ * See handle_reply()
+ */
+ bp->bp_giaddr = my_ip_addr;
+#endif /* BUG */
+
+ /*
+ * XXX - DHCP says to insert a subnet mask option into the
+ * options area of the request (if vendor magic == std).
+ */
+ }
+ /* Set up socket address for send. */
+ send_addr.sin_family = AF_INET;
+ send_addr.sin_port = htons(bootps_port);
+ send_addr.sin_addr.s_addr = server_ipa;
+
+ /* Send reply with same size packet as request used. */
+ if (sendto(s, pktbuf, pktlen, 0,
+ (struct sockaddr *) &send_addr,
+ sizeof(send_addr)) < 0)
+ {
+ report(LOG_ERR, "sendto: %s", get_network_errmsg());
+ }
+}
+
+
+
+/*
+ * Process BOOTREPLY packet.
+ */
+static void
+handle_reply()
+{
+ struct bootp *bp = (struct bootp *) pktbuf;
+ struct ifreq *ifr;
+ struct sockaddr_in *sip;
+ u_char canon_haddr[MAXHADDRLEN];
+ unsigned char *ha;
+ int len;
+
+ if (debug) {
+ report(LOG_INFO, " reply for %s",
+ inet_ntoa(bp->bp_yiaddr));
+ }
+ /* Make sure client is directly accessible. */
+ ifr = getif(s, &(bp->bp_yiaddr));
+ if (!ifr) {
+ report(LOG_NOTICE, "no interface for reply to %s",
+ inet_ntoa(bp->bp_yiaddr));
+ return;
+ }
+#if 1 /* Experimental (see BUG above) */
+/* #ifdef CATER_TO_OLD_CLIENTS ? */
+ /*
+ * The giaddr field has been set to our "default" IP address
+ * which might not be on the same interface as the client.
+ * In case the client looks at giaddr, (which it should not)
+ * giaddr is now set to the address of the correct interface.
+ */
+ sip = (struct sockaddr_in *) &(ifr->ifr_addr);
+ bp->bp_giaddr = sip->sin_addr;
+#endif
+
+ /* Set up socket address for send to client. */
+ send_addr.sin_family = AF_INET;
+ send_addr.sin_addr = bp->bp_yiaddr;
+ send_addr.sin_port = htons(bootpc_port);
+
+ /* Create an ARP cache entry for the client. */
+ ha = bp->bp_chaddr;
+ len = bp->bp_hlen;
+ if (len > MAXHADDRLEN)
+ len = MAXHADDRLEN;
+ if (bp->bp_htype == HTYPE_IEEE802) {
+ haddr_conv802(ha, canon_haddr, len);
+ ha = canon_haddr;
+ }
+ if (debug > 1)
+ report(LOG_INFO, "setarp %s - %s",
+ inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len));
+ setarp(s, &bp->bp_yiaddr, ha, len);
+
+ /* Send reply with same size packet as request used. */
+ if (sendto(s, pktbuf, pktlen, 0,
+ (struct sockaddr *) &send_addr,
+ sizeof(send_addr)) < 0)
+ {
+ report(LOG_ERR, "sendto: %s", get_network_errmsg());
+ }
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bugfiler/Makefile b/libexec/bugfiler/Makefile
new file mode 100644
index 0000000..fb77633
--- /dev/null
+++ b/libexec/bugfiler/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+
+PROG= bugfiler
+CFLAGS+=-I${.CURDIR}
+SRCS= bugfiler.c error.c gethead.c process.c redist.c reply.c
+BINOWN= root
+BINMODE=4555
+MAN1= sendbug.1
+MAN8= bugfiler.8
+
+beforeinstall:
+ install -c -o bin -g ${BINGRP} -m 555 \
+ ${.CURDIR}/sendbug.sh ${DESTDIR}/usr/bin/sendbug
+ install -c -o bin -g ${BINGRP} -m 444 ${.CURDIR}/bugformat \
+ ${DESTDIR}/usr/share/misc
+
+.include <bsd.prog.mk>
diff --git a/libexec/bugfiler/bug.h b/libexec/bugfiler/bug.h
new file mode 100644
index 0000000..69ea986
--- /dev/null
+++ b/libexec/bugfiler/bug.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1986, 1987, 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.
+ *
+ * @(#)bug.h 8.1 (Berkeley) 6/4/93
+ */
+
+#define BUGS_HOME "owner-bugs@ucbvax.Berkeley.EDU"
+#define BUGS_ID "bugs"
+
+/*
+ * the METOO definition has the bugfiler exit with an error (-1) status
+ * if there's a problem. This causes sendmail to send off a copy of the
+ * report (as failed mail) to the "owner" of the mail alias that executed
+ * the bugfiler. This is great if you would have otherwise lost the bug
+ * report. It's not so great if you get a whole bunch of mail that you
+ * really don't want.
+ */
+#define METOO
+
+/* files */
+#define ACK_FILE "bug:ack" /* acknowledge file */
+#define DIST_FILE "bug:redist" /* redistribution file */
+#define ERROR_FILE "log" /* error file */
+#define LOCK_FILE "bug:lock" /* lock file name */
+#define SUMMARY_FILE "summary" /* summary file */
+#define TMP_BUG "errors/BUG_XXXXXX" /* tmp bug report */
+#define TMP_DIR "errors" /* tmp directory */
+
+#define CHN (char *)NULL /* null arg string */
+#define COMMENT '#' /* comment in redist file */
+#define EOS (char)NULL /* end of string */
+#define ERR -1 /* error return */
+#define MAXLINELEN 200 /* max line length in message */
+#define NO 0 /* no/false */
+#define OK 0 /* okay return */
+#define YES 1 /* yes/true */
+
+typedef struct {
+ short found, /* line number if found */
+ redist; /* if part of redist headers */
+ int (*valid)(); /* validation routine */
+ short len; /* length of tag */
+ char *tag, /* leading tag */
+ *line; /* actual line */
+} HEADER;
+extern HEADER mailhead[];
+
+#define DATE_TAG 0 /* "Date:" offset */
+#define FROM_TAG 1 /* "From " offset */
+#define CFROM_TAG 2 /* "From:" offset */
+#define INDX_TAG 3 /* "Index:" offset */
+#define MSG_TAG 4 /* "Message-Id:" offset */
+#define RPLY_TAG 5 /* "Reply-To:" offset */
+#define RET_TAG 6 /* "Return-Path:" offset */
+#define SUBJ_TAG 7 /* "Subject:" offset */
+#define TO_TAG 8 /* "To:" offset */
+#define APPAR_TO_TAG 9 /* "Apparently-To:" offset */
+
+/* so sizeof doesn't return 0 */
+extern char bfr[MAXBSIZE], /* general I/O buffer */
+ dir[MAXNAMLEN], /* subject and folder */
+ folder[MAXNAMLEN],
+ tmpname[sizeof(TMP_BUG) + 5]; /* temp bug file */
diff --git a/libexec/bugfiler/bugfiler.8 b/libexec/bugfiler/bugfiler.8
new file mode 100644
index 0000000..1fc86d2
--- /dev/null
+++ b/libexec/bugfiler/bugfiler.8
@@ -0,0 +1,290 @@
+.\" 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.
+.\"
+.\" @(#)bugfiler.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt BUGFILER 8
+.Os BSD 4.2
+.Sh NAME
+.Nm bugfiler
+.Nd file bug reports in folders automatically
+.Sh SYNOPSIS
+.Nm bugfiler
+.Op Fl ar
+.Op Fl v Ar version
+.Sh DESCRIPTION
+.Nm Bugfiler
+is a program to automatically intercept, acknowledge,
+redistribute and store bug reports.
+.Nm Bugfiler
+is normally invoked
+by the mail delivery program with a line similar to the following in
+.Pa /etc/aliases .
+.Bd -literal -offset indent
+bugs: "|bugfiler"
+.Ed
+.Pp
+It should be noted that the login
+.Dq bugs
+must exist for the bugfiler
+to run. Unless otherwise noted all paths used by
+.Nm bugfiler
+are
+relative to the home directory of this login.
+.Nm Bugfiler
+also
+expects all of its files and directories to be owned by
+.Dq bugs .
+.Pp
+Available options.
+.Bl -tag -width Ds
+.It Fl a
+Do not send automatic mail acknowledgement to the bug report filer.
+(The default is to send the acknowledgement with the file
+.Pa ~bugs/version/bug:ack
+appended).
+.It Fl r
+Do not redistribute.
+.It Fl v Ar version
+Override the
+.Ar version
+provided within the bug report itself.
+.El
+.Pp
+For the bug report to be correctly filed, it must contain a line
+in the following format:
+.Pp
+.Bd -filled -offset indent -compact
+.Bl -column Index folder
+.It Index: Ta Em folder Ta Ar version
+.El
+.Ed
+.Pp
+The directories
+.Pa ~bugs/ Ns Ar version
+and
+.Pa ~bugs/ Ns Ar version/ Ns Em folder
+must exist before
+.Nm bugfiler
+attempts to store the bug report. Bug
+reports will be stored in files named by the concatenation of
+.Ar version ,
+.Em folder ,
+and sequential numbers, i.e. if
+.Ar version
+is
+.Dq 4.3 Tn BSD
+and
+.Em folder
+is
+.Dq ucb
+the first bug report will be placed in
+.Pa ~bugs/4.3BSD/ucb/1 .
+If
+.Em folder
+contains more than one component only
+the first one will be used, e.g. if
+.Em folder
+is
+.Dq bin/from.c
+or
+.Dq bin/adb/con.c
+it will be treated as if it were simply
+.Dq bin .
+.Pp
+.Pp
+If the
+.Fl r
+flag is not supplied, redistribution of the bug reports
+is done as specified in the file
+.Pa ~bugs/version/bug:redist .
+This file
+is in the format of the
+.Xr aliases 5
+file, including comments and
+entries requiring multiple lines, with the single exception that the
+.Em folder
+component of the
+.Dq Index:
+line replaces the name to alias.
+The special folder
+.Dq all:
+receives a redistribution of all bug reports
+sent to this
+.Ar version .
+For example, the
+.Pa bug:redist
+file
+.Pp
+.Bd -literal -offset indent -compact
+# bigbug gets a copy of everything
+all: bigbug
+# ucb folder redistribution list
+ucb: karels, kjd@coke.berkeley.edu
+ ra@beno.css.gov
+.Ed
+.Pp
+will send copies of all bug reports with
+.Dq ucb
+as the
+.Em folder
+to bigbug, karels, kjd, and ra.
+.Pp
+Reports that cannot be filed, due to an invalid
+.Dq Index:
+line or
+some other error, are placed in the directory
+.Pa ~bugs/errors .
+The
+.Nm bugfiler
+maintainer should correct these bug reports and then
+run
+.Nm bugfiler ,
+with the corrected report as its standard input,
+as bug reports with errors are neither acknowledged or redistributed.
+All reports that
+.Nm bugfiler
+handles are logged in
+.Pa ~bugs/log.
+.Pp
+Valid bugs are also logged in the file
+.Pa ~bugs/version/summary.
+This file has an entry for each bug report for
+.Ar version
+in the
+format:
+.Pp
+.Bd -literal -offset indent -compact
+Filename Date
+ Subject:
+ Index:
+ Owner: Bugs Bunny
+ Status: Received
+.Ed
+.Pp
+.Li Filename
+is the concatenation of
+.Ar version ,
+.Em folder ,
+and a number
+as described above.
+.Xr Date
+is the date as reported by the system
+clock, using
+.Xr ctime 3 .
+The
+.Li Subject:
+and
+.Li Index:
+lines are
+copies of the
+.Dq Subject:
+and
+.Dq index:
+lines contained in the bug
+report. The
+.Li Owner
+and
+.Li Status
+fields are intended to provide a
+rudimentary method of tracking the status of bug reports.
+.Pp
+The file
+.Pa ~bugs/bug:lock
+is the focus of all locking for
+.Nm bugfiler .
+If you wish to manipulate any of the log or error files, rename or remove
+it and
+.Nm bugfiler
+will treat all bug reports that it receives as if
+they were incorrectly formatted, i.e. it will place them in the directory
+.Pa ~bugs/errors ,
+for later recovery by the
+.Nm bugfiler
+maintainer.
+Obviously, this file must be created when you first install
+.Nm bugfiler .
+.Pp
+All errors that occur before
+.Pa ~bugs/log
+is found are logged into the system
+log file, using
+.Xr syslog 8 .
+.Sh FILES
+.Bl -tag -width /usr/share/misc/bugformatxx -compact
+.It Pa ~bugs/bug:ack
+the acknowledgement message
+.It Pa ~bugs/bug:redist
+the redistribution list
+.It Pa ~bugs/bug:lock
+the locking file
+.It Pa ~bugs/errors/BUG_??????
+bug reports with format errors
+.It Pa ~bugs/log
+the log file
+.It Pa ~bugs/folder/summary
+the summary files
+.It Pa /usr/sbin/sendmail
+the mail delivery program
+.It Pa /usr/share/misc/bugformat
+a sample bug report format
+.El
+.Sh SEE ALSO
+.Xr sendbug 1 ,
+.Xr aliases 5 ,
+.Xr syslog 8
+.Sh BUGS
+Since mail can be forwarded in a number of different ways,
+.Nm bugfiler
+does not recognize forwarded mail and will acknowledge to the forwarder
+instead of the original sender unless there is a
+.Dq Reply-To
+field in the
+header.
+.Pp
+This version of
+.Nm bugfiler
+is not compatible with the version
+released with
+.Bx 4.3
+in that it doesn't complain to the sender about
+incorrectly formatted bug reports.
+Frankly, we got tired of the profanity, not to mention the extended
+conversations
+.Nm bugfiler
+was holding with
+.Xr vacation 1 .
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/libexec/bugfiler/bugfiler.c b/libexec/bugfiler/bugfiler.c
new file mode 100644
index 0000000..75dbef3
--- /dev/null
+++ b/libexec/bugfiler/bugfiler.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 1983, 1986, 1987, 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, 1986, 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)bugfiler.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+/*
+ * Bug report processing program, designed to be invoked
+ * through aliases(5).
+ */
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bug.h"
+#include "extern.h"
+
+char bfr[MAXBSIZE], /* general I/O buffer */
+ tmpname[sizeof(TMP_BUG) + 5]; /* temp bug file */
+
+static void logit __P((void));
+static void make_copy __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg; /* getopt arguments */
+ register struct passwd *pwd; /* bugs password entry */
+ register int ch; /* getopts char */
+ int do_ack, /* acknowledge bug report */
+ do_redist; /* redistribut BR */
+ char *argversion; /* folder name provided */
+
+ do_ack = do_redist = YES;
+ argversion = NULL;
+ while ((ch = getopt(argc, argv, "av:r")) != EOF)
+ switch(ch) {
+ case 'a':
+ do_ack = NO;
+ break;
+ case 'v':
+ argversion = optarg;
+ break;
+ case 'r':
+ do_redist = NO;
+ break;
+ case '?':
+ default:
+ fputs("usage: bugfiler [-ar] [-v version]\n", stderr);
+ error("usage: bugfiler [-ar] [-v version]", CHN);
+ }
+
+ if (!(pwd = getpwnam(BUGS_ID)))
+ error("can't find bugs login.", BUGS_ID);
+
+ if (chdir(pwd->pw_dir)) /* change to bugs home directory */
+ error("can't chdir to %s.", pwd->pw_dir);
+
+ if (seteuid(pwd->pw_uid))
+ error("can't set id to %s.", BUGS_ID);
+
+ (void)umask(02); /* everything is 664 */
+ seterr(); /* redirect to log file */
+ logit(); /* log report arrival */
+ make_copy(); /* save copy in case */
+ gethead(do_redist);
+
+ if (argversion) /* specific folder requested */
+ (void)strcpy(dir, argversion);
+
+ process();
+
+ if (seteuid(0))
+ error("can't set id to root.", CHN);
+ if (do_ack)
+ reply();
+ if (do_redist)
+ redist();
+ (void)unlink(tmpname);
+ exit(OK);
+}
+
+/*
+ * make_copy --
+ * make a copy of bug report in error folder
+ */
+static void
+make_copy()
+{
+ register int cnt, /* read return value */
+ tfd; /* temp file descriptor */
+
+ if (access(TMP_DIR, F_OK))
+ (void)mkdir(TMP_DIR, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH);
+ (void)strcpy(tmpname, TMP_BUG);
+ if (tfd = mkstemp(tmpname)) {
+ while ((cnt = read(fileno(stdin),
+ bfr, sizeof(bfr))) != ERR && cnt)
+ write(tfd, bfr, cnt);
+ (void)close(tfd);
+ return;
+ }
+ error("can't make copy using %s.", tmpname);
+}
+
+/*
+ * logit --
+ * log this run of the bugfiler
+ */
+static void
+logit()
+{
+ struct timeval tp;
+ char *C1, *C2;
+
+ if (gettimeofday(&tp, (struct timezone *)NULL))
+ error("can't get time of day.", CHN);
+ for (C1 = C2 = ctime(&tp.tv_sec); *C1 && *C1 != '\n'; ++C1);
+ *C1 = EOS;
+ fputs(C2, stderr);
+}
diff --git a/libexec/bugfiler/bugformat b/libexec/bugfiler/bugformat
new file mode 100644
index 0000000..97a767d
--- /dev/null
+++ b/libexec/bugfiler/bugformat
@@ -0,0 +1,32 @@
+Subject: Short summary of the problem (please make this meaningful!)
+Index: folder 4.4BSD-alpha
+
+Description:
+ Detailed description of the problem, suggestion, or complaint.
+Repeat-By:
+ Describe the sequence of events that causes the problem
+ to occur.
+Fix:
+ Description of how to fix the problem. If you don't know a
+ fix for the problem, don't include this section.
+
+-------- Remove this line and what's below it, for reference only. --------
+
+To ensure that your bug report is handled correctly by bugfiler(8),
+you must replace "folder" (on the line above starting with "Index:")
+with one of the following values:
+
+ folder ::= bin | doc | etc | games | ideas | include | lib
+ | local | man | misc | new | sys | ucb
+ | usr.bin | usr.lib
+
+If you're not running 4.3BSD, you should also replace "4.3BSD" on
+the same line with one of the following values.
+
+ version ::= 4.3BSD | 4.3BSD-tahoe | 4.3BSD-reno | net2
+ | 4.4BSD-alpha
+
+For example, if your bug concerns the program "/usr/bin/file" and
+you're currently running 4.3BSD-Reno, you should replace "folder"
+with "usr.bin/file", and "4.4BSD-alpha" with "4.3BSD-Reno". The folder
+"ideas" is for suggestions.
diff --git a/libexec/bugfiler/error.c b/libexec/bugfiler/error.c
new file mode 100644
index 0000000..bd00b2b
--- /dev/null
+++ b/libexec/bugfiler/error.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1986, 1987, 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[] = "@(#)error.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "bug.h"
+#include "extern.h"
+
+static short err_redir; /* stderr redirected */
+
+/*
+ * seterr --
+ * redirect stderr for error processing
+ */
+void
+seterr()
+{
+ if (!freopen(ERROR_FILE, "a", stderr))
+ error("can't open error file %s.", ERROR_FILE);
+ err_redir = YES;
+}
+
+/*
+ * error --
+ * write errors to log file and die
+ */
+void
+error(fmt, arg)
+ register char *fmt,
+ *arg;
+{
+ static char logmsg[MAXLINELEN]; /* syslog message */
+
+ if (err_redir) {
+ /* don't combine these, "fmt" may not require "arg" */
+ fprintf(stderr, "\t%s\n\t", tmpname);
+ fprintf(stderr, fmt, arg);
+ fputc('\n', stderr);
+ }
+ else {
+ sprintf(logmsg, "bugfiler: %s", fmt);
+ syslog(LOG_ERR, logmsg, arg);
+ }
+#ifdef METOO
+ exit(ERR);
+#else
+ exit(OK);
+#endif
+}
diff --git a/libexec/bugfiler/extern.h b/libexec/bugfiler/extern.h
new file mode 100644
index 0000000..b096f3d
--- /dev/null
+++ b/libexec/bugfiler/extern.h
@@ -0,0 +1,41 @@
+/*-
+ * 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/4/93
+ */
+
+void error __P((char *, char *));
+void gethead __P((int));
+int process __P((void));
+void redist __P((void));
+void reply __P((void));
+void seterr __P((void));
diff --git a/libexec/bugfiler/gethead.c b/libexec/bugfiler/gethead.c
new file mode 100644
index 0000000..ba7e361
--- /dev/null
+++ b/libexec/bugfiler/gethead.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 1986, 1987, 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[] = "@(#)gethead.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+#include "bug.h"
+#include "extern.h"
+
+static int chk1 __P((char *));
+static int pbuf __P((char *));
+
+#define ENT(X) sizeof(X) - 1, X
+HEADER mailhead[] = { /* mail headers */
+ { NO, YES, NULL, ENT("Date:"), },
+ { NO, NO, NULL, ENT("From "), },
+ { NO, YES, NULL, ENT("From:"), },
+ { NO, NO, chk1, ENT("Index:"), },
+ { NO, YES, NULL, ENT("Message-Id:"), },
+ { NO, YES, NULL, ENT("Reply-To:"), },
+ { NO, YES, NULL, ENT("Return-Path:"), },
+ { NO, NO, pbuf, ENT("Subject:"), },
+ { NO, YES, NULL, ENT("To:"), },
+ { NO, NO, NULL, ENT("Apparently-To:"), },
+ { ERR, }
+};
+
+FILE *dfp; /* distf file pointer */
+char dir[MAXNAMLEN], /* subject and folder */
+ folder[MAXNAMLEN];
+
+/*
+ * gethead --
+ * read mail and bug headers from bug report, construct redist headers
+ */
+void
+gethead(redist)
+ int redist;
+{
+ register HEADER *hp; /* mail header pointer */
+
+ if (redist) {
+ int fd;
+ char *distf;
+
+ distf = strdup(_PATH_TMP);
+ if (!(fd = mkstemp(distf)) || !(dfp = fdopen(fd, "w+")))
+ error("can't create redistribution file %s.", distf);
+ /* disappear after last reference is closed */
+ (void)unlink(distf);
+ free(distf);
+ }
+ if (!freopen(tmpname, "r", stdin))
+ error("can't read temporary bug file %s.", tmpname);
+
+ while (fgets(bfr, sizeof(bfr), stdin)) {
+ for (hp = mailhead; hp->found != ERR; ++hp)
+ if (!hp->found)
+ if (!strncmp(hp->tag, bfr, hp->len)) {
+ if (hp->valid && !((*(hp->valid))(bfr)))
+ break;
+ if (!(hp->line =
+ malloc((u_int)(strlen(bfr) + 1))))
+ error("malloc failed.", CHN);
+ (void)strcpy(hp->line, bfr);
+ hp->found = YES;
+ break;
+ }
+ if ((hp->found == ERR || hp->redist) && redist)
+ fputs(bfr, dfp);
+ }
+
+ if (!mailhead[INDX_TAG].found)
+ error("no readable \"Index:\" header in bug report.", CHN);
+}
+
+/*
+ * chk1 --
+ * parse the "Index:" line into folder and directory
+ */
+static int
+chk1(line)
+ char *line;
+{
+ register char *C; /* tmp pointer */
+ struct stat sbuf; /* existence check */
+
+ if (sscanf(line, " Index: %s %s ", folder, dir) != 2)
+ return(NO);
+ if (C = strchr(folder, '/')) { /* deal with "bin/from.c" */
+ if (C == folder)
+ return(NO);
+ *C = EOS;
+ }
+ if (stat(dir, &sbuf) || (sbuf.st_mode & S_IFMT) != S_IFDIR)
+ return(NO);
+ (void)pbuf(line);
+ return(YES);
+}
+
+/*
+ * pbuf --
+ * kludge so that summary file looks pretty
+ */
+static int
+pbuf(line)
+ char *line;
+{
+ register char *rp, /* tmp pointers */
+ *wp;
+
+ for (rp = line; *rp == ' ' || *rp == '\t'; ++rp);
+ for (wp = line; *rp; ++wp) {
+ if ((*wp = *rp++) != ' ' && *wp != '\t')
+ continue;
+ *wp = ' ';
+ while (*rp == ' ' || *rp == '\t')
+ ++rp;
+ }
+ if (wp[-1] == ' ') /* wp can't == line */
+ --wp;
+ *wp = EOS;
+ return(YES);
+}
diff --git a/libexec/bugfiler/pathnames.h b/libexec/bugfiler/pathnames.h
new file mode 100644
index 0000000..7ff5ab6
--- /dev/null
+++ b/libexec/bugfiler/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
+ */
+
+#define MAIL_CMD "/usr/sbin/sendmail -i -t -F \"Bugs Bunny\" -f owner-bugs"
+#undef _PATH_TMP
+#define _PATH_TMP "/tmp/BUG_XXXXXX"
diff --git a/libexec/bugfiler/process.c b/libexec/bugfiler/process.c
new file mode 100644
index 0000000..6d376fd
--- /dev/null
+++ b/libexec/bugfiler/process.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1986, 1987, 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.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "bug.h"
+#include "extern.h"
+
+char pfile[MAXPATHLEN]; /* permanent file name */
+
+static int getnext __P((void));
+
+/*
+ * process --
+ * copy report to permanent file,
+ * update summary file.
+ */
+int
+process()
+{
+ register int rval; /* read return value */
+ struct timeval tp; /* time of day */
+ int lfd; /* lock file descriptor */
+
+ if (access(LOCK_FILE, R_OK) || (lfd = open(LOCK_FILE, O_RDONLY, 0)) < 0)
+ error("can't find lock file %s.", LOCK_FILE);
+ if (flock(lfd, LOCK_EX))
+ error("can't get lock.", CHN);
+ sprintf(pfile, "%s/%s/%d", dir, folder, getnext());
+ fprintf(stderr, "\t%s\n", pfile);
+ if (!(freopen(pfile, "w", stdout)))
+ error("can't create %s.", pfile);
+ rewind(stdin);
+ while ((rval = read(fileno(stdin), bfr, sizeof(bfr))) != ERR && rval)
+ if (write(fileno(stdout), bfr, rval) != rval)
+ error("write to %s failed.", pfile);
+
+ /* append information to the summary file */
+ sprintf(bfr, "%s/%s", dir, SUMMARY_FILE);
+ if (!(freopen(bfr, "a", stdout)))
+ error("can't append to summary file %s.", bfr);
+ if (gettimeofday(&tp, (struct timezone *)NULL))
+ error("can't get time of day.", CHN);
+ printf("\n%s\t\t%s\t%s\t%s\tOwner: Bugs Bunny\n\tStatus: Received\n",
+ pfile, ctime(&tp.tv_sec), mailhead[INDX_TAG].line,
+ mailhead[SUBJ_TAG].found ? mailhead[SUBJ_TAG].line : "Subject:\n");
+ (void)flock(lfd, LOCK_UN);
+ (void)fclose(stdout);
+}
+
+/*
+ * getnext --
+ * get next file name (number)
+ */
+static int
+getnext()
+{
+ register struct dirent *d; /* directory structure */
+ register DIR *dirp; /* directory pointer */
+ register int highval, newval;
+ register char *p;
+
+ (void)sprintf(bfr, "%s/%s", dir, folder);
+ if (!(dirp = opendir(bfr)))
+ error("can't read folder directory %s.", bfr);
+ for (highval = -1; d = readdir(dirp);) {
+ for (p = d->d_name; *p && isdigit(*p); ++p);
+ if (!*p && (newval = atoi(d->d_name)) > highval)
+ highval = newval;
+ }
+ closedir(dirp);
+ return(++highval);
+}
diff --git a/libexec/bugfiler/redist.c b/libexec/bugfiler/redist.c
new file mode 100644
index 0000000..87a18b6
--- /dev/null
+++ b/libexec/bugfiler/redist.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1986, 1987, 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[] = "@(#)redist.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "bug.h"
+#include "pathnames.h"
+#include "extern.h"
+
+/*
+ * redist --
+ * Redistribute a bug report to those people indicated in the
+ * redistribution list file.
+ */
+void
+redist()
+{
+ extern FILE *dfp; /* dist file fp */
+ extern char pfile[]; /* permanent bug file */
+ register char *C1, *C2;
+ FILE *pf;
+ int group;
+
+ (void)sprintf(bfr, "%s/%s", dir, DIST_FILE);
+ if (!freopen(bfr, "r", stdin))
+ return;
+ for (pf = NULL, group = 0; fgets(bfr, sizeof(bfr), stdin);) {
+ if (C1 = strchr(bfr, '\n'))
+ *C1 = '\0';
+nextline: if (*bfr == COMMENT ||
+ isspace(*bfr) || !(C1 = index(bfr, ':')))
+ continue;
+ *C1 = EOS;
+ if (!strcmp(bfr, folder) || !strcmp(bfr, "all")) {
+ for (++C1; *C1 && (*C1 == ' ' || *C1 == '\t'); ++C1);
+ if (!*C1) /* if empty list */
+ continue;
+ if (!pf) {
+ if (!(pf = popen(MAIL_CMD, "w")))
+ error("sendmail pipe failed.", CHN);
+ if (mailhead[SUBJ_TAG].found)
+ fprintf(pf,
+ "%s", mailhead[SUBJ_TAG].line);
+ else
+ fprintf(pf,
+ "Subject: Untitled Bug Report\n");
+ if (!mailhead[TO_TAG].line) {
+ if (mailhead[APPAR_TO_TAG].line)
+ fprintf(pf, "To%s",
+ strchr(mailhead[APPAR_TO_TAG].line,
+ ':'));
+ else
+ fprintf(pf, "To: %s\n", BUGS_ID);
+ }
+ fputs("Resent-To: ", pf);
+ }
+ /*
+ * write out first entry, then succeeding entries
+ * backward compatible, handles back slashes at end
+ * of line
+ */
+ if (group++)
+ fputs(", ", pf);
+ for (;;) {
+ if (C2 = strchr(C1, '\\'))
+ *C2 = EOS;
+ fputs(C1, pf);
+ if (!fgets(bfr, sizeof(bfr), stdin))
+ break;
+ if (C1 = strchr(bfr, '\n'))
+ *C1 = '\0';
+ if (*bfr != ' ' && *bfr != '\t')
+ goto nextline;
+ for (C1 = bfr;
+ *C1 && (*C1 == ' ' || *C1 == '\t'); ++C1);
+ }
+ }
+ }
+ if (!pf)
+ return;
+
+ putc('\n', pf);
+
+ rewind(dfp);
+ /* add Reference header and copy bug report out */
+ while (fgets(bfr, sizeof(bfr), dfp) && *bfr != '\n')
+ fputs(bfr, pf);
+ fprintf(pf, "\n%sReference: %s\n\n", mailhead[INDX_TAG].line, pfile);
+ while (fgets(bfr, sizeof(bfr), dfp))
+ fputs(bfr, pf);
+ (void)pclose(pf);
+}
diff --git a/libexec/bugfiler/reply.c b/libexec/bugfiler/reply.c
new file mode 100644
index 0000000..0f99401
--- /dev/null
+++ b/libexec/bugfiler/reply.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 1986, 1987, 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[] = "@(#)reply.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bug.h"
+#include "extern.h"
+#include "pathnames.h"
+
+/*
+ * reply --
+ * tell the user we got their silly little bug report
+ */
+void
+reply()
+{
+ register char *C, /* traveling pointer */
+ *to; /* who we're replying to */
+ register int afd, /* ack file descriptor */
+ rval; /* return value */
+ FILE *pf; /* pipe pointer */
+
+ if (mailhead[RPLY_TAG].found) {
+ for (C = mailhead[RPLY_TAG].line + mailhead[RPLY_TAG].len;
+ *C != '\n' && (*C == ' ' || *C == '\t');++C);
+ if (*C)
+ goto gotone;
+ }
+ if (mailhead[FROM_TAG].found) {
+ for (C = mailhead[FROM_TAG].line + mailhead[FROM_TAG].len;
+ *C != '\n' && (*C == ' ' || *C == '\t');++C);
+ if (*C)
+ goto gotone;
+ }
+ if (mailhead[CFROM_TAG].found) {
+ for (C = mailhead[CFROM_TAG].line + mailhead[CFROM_TAG].len;
+ *C != '\n' && (*C == ' ' || *C == '\t');++C);
+ if (*C)
+ goto gotone;
+ }
+ return;
+
+ /* if it's a foo <XXX>, get the XXX, else get foo (first string) */
+gotone: if (to = strchr(C, '<'))
+ for (C = ++to;
+ *C != '\n' && *C != ' ' && *C != '\t' && *C != '>';++C);
+ else {
+ to = C;
+ for (to = C++;*C != '\n' && *C != ' ' && *C != '\t';++C);
+ }
+ *C = EOS;
+
+ if (!(pf = popen(MAIL_CMD, "w")))
+ error("sendmail pipe failed.", CHN);
+
+ fprintf(pf, "Reply-To: %s\nFrom: %s (Bugs Bunny)\nTo: %s\n",
+ BUGS_HOME, BUGS_HOME, to);
+ if (mailhead[SUBJ_TAG].found)
+ fprintf(pf, "Subject: Re:%s",
+ mailhead[SUBJ_TAG].line + mailhead[SUBJ_TAG].len);
+ else
+ fputs("Subject: Bug report acknowledgement.\n", pf);
+ if (mailhead[DATE_TAG].found)
+ fprintf(pf, "In-Acknowledgement-Of: Your message of %s",
+ mailhead[DATE_TAG].line + mailhead[DATE_TAG].len);
+ if (mailhead[MSG_TAG].found)
+ fprintf(pf, "\t\t%s", mailhead[MSG_TAG].line);
+ fputs("Precedence: bulk\n\n", pf); /* vacation(1) uses this... */
+ fflush(pf);
+
+ (void)sprintf(bfr, "%s/%s", dir, ACK_FILE);
+ if ((afd = open(bfr, O_RDONLY, 0)) >= 0) {
+ while ((rval = read(afd, bfr, sizeof(bfr))) != ERR && rval)
+ (void)write(fileno(pf), bfr, rval);
+ (void)close(afd);
+ }
+ pclose(pf);
+}
diff --git a/libexec/bugfiler/sendbug.1 b/libexec/bugfiler/sendbug.1
new file mode 100644
index 0000000..31ca802
--- /dev/null
+++ b/libexec/bugfiler/sendbug.1
@@ -0,0 +1,85 @@
+.\" Copyright (c) 1983, 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.
+.\"
+.\" @(#)sendbug.1 8.1 (Berkeley) 6/4/93
+.\"
+.Dd June 4, 1993
+.Dt SENDBUG 1
+.Os BSD 4.2
+.Sh NAME
+.Nm sendbug
+.Nd mail a system bug report to 4bsd-bugs
+.Sh SYNOPSIS
+.Nm sendbug
+.Op Ar address
+.Sh DESCRIPTION
+Bug reports sent to `4bsd-bugs@Berkeley.EDU' are intercepted
+by a program which expects bug reports to conform to a standard format.
+.Nm Sendbug
+is a shell script to help the user compose and mail bug reports
+in the correct format.
+.Nm Sendbug
+works by invoking the editor specified by the environment variable
+.Ev EDITOR
+on a temporary copy of the bug report format outline. The user must fill in the
+appropriate fields and exit the editor.
+.Nm Sendbug
+then mails the completed report to `4bsd-bugs@Berkeley.EDU' or the
+.Ar address
+specified on the command line.
+.Sh ENVIRONMENT
+.Nm Sendbug
+will utilize the following environment variable if it exists:
+.Bl -tag -width EDITOR
+.It Ev EDITOR
+Specifies the preferred editor. If
+.Ev EDITOR
+is not set,
+.Nm
+defaults to
+.Xr vi 1 .
+.El
+.Sh FILES
+.Bl -tag -width /usr/share/misc/bugformat -compact
+.It Pa /usr/share/misc/bugformat
+Contains the bug report outline.
+.El
+.Sh SEE ALSO
+.Xr vi 1 ,
+.Xr environ 7 ,
+.Xr bugfiler 8 ,
+.Xr sendmail 8
+.Sh HISTORY
+The
+.Nm sendbug
+command
+appeared in
+.Bx 4.2 .
diff --git a/libexec/bugfiler/sendbug.sh b/libexec/bugfiler/sendbug.sh
new file mode 100644
index 0000000..911ef9d
--- /dev/null
+++ b/libexec/bugfiler/sendbug.sh
@@ -0,0 +1,66 @@
+#!/bin/sh -
+#
+# 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.
+#
+# @(#)sendbug.sh 8.1 (Berkeley) 6/4/93
+#
+
+# create a bug report and mail it to '4bsd-bugs'.
+
+PATH=/bin:/sbin:/usr/sbin:/usr/bin
+export PATH
+
+TEMP=/tmp/bug$$
+FORMAT=/usr/share/misc/bugformat
+
+# uucp sites should use ": ${BUGADDR=ucbvax!4bsd-bugs}" with a suitable path.
+: ${BUGADDR=4bsd-bugs@CS.Berkeley.EDU}
+: ${EDITOR=vi}
+
+trap 'rm -f $TEMP ; exit 1' 1 2 3 13 15
+
+cp $FORMAT $TEMP
+chmod u+w $TEMP
+if $EDITOR $TEMP
+then
+ if cmp -s $FORMAT $TEMP
+ then
+ echo "File not changed, no bug report submitted."
+ exit
+ fi
+ case "$#" in
+ 0) sendmail -t -oi $BUGADDR < $TEMP ;;
+ *) sendmail -t -oi "$@" < $TEMP ;;
+ esac
+fi
+
+rm -f $TEMP
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..a218380
--- /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
+DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT}
+
+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..e3336b5
--- /dev/null
+++ b/libexec/ftpd/extern.h
@@ -0,0 +1,65 @@
+/*-
+ * 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 *));
+void setproctitle __P((const char *, ...));
+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..151c3d7
--- /dev/null
+++ b/libexec/ftpd/ftpcmd.y
@@ -0,0 +1,1266 @@
+/*
+ * 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 "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..0e256bf
--- /dev/null
+++ b/libexec/ftpd/ftpd.c
@@ -0,0 +1,1853 @@
+/*
+ * 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.12 1995/08/28 21:30:49 mpp 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>
+
+#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
+char **Argv = NULL; /* pointer to argument vector */
+char *LastArgv = NULL; /* end of argv */
+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 SETPROCTITLE
+ /*
+ * Save start and extent of argv for setproctitle.
+ */
+ Argv = argv;
+ while (*envp)
+ envp++;
+ LastArgv = envp[-1] + strlen(envp[-1]);
+#endif /* 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(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(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(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 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 /* 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
new file mode 100644
index 0000000..8113441
--- /dev/null
+++ b/libexec/getty/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 5.12 (Berkeley) 1/21/91
+
+PROG= getty
+SRCS= main.c init.c subr.c gettytab.c
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+MAN5= gettytab.5 ttys.5
+MAN8= getty.8
+
+.include <bsd.prog.mk>
diff --git a/libexec/getty/extern.h b/libexec/getty/extern.h
new file mode 100644
index 0000000..644f93f
--- /dev/null
+++ b/libexec/getty/extern.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/4/93
+ */
+
+struct delayval;
+
+int adelay __P((int, struct delayval *));
+char *autobaud __P((void));
+int delaybits __P((void));
+void edithost __P((char *));
+void gendefaults __P((void));
+int getent __P((char *, char *));
+int getflag __P((char *));
+long getnum __P((char *));
+char *getstr __P((char *, char **));
+void gettable __P((char *, char *));
+void makeenv __P((char *[]));
+char *portselector __P((void));
+void set_ttydefaults __P((int));
+void setchars __P((void));
+void setdefaults __P((void));
+long setflags __P((int));
+int speed __P((int));
+
+int login_tty __P((int)); /* From libutil. */
diff --git a/libexec/getty/fbtab_stuff.c b/libexec/getty/fbtab_stuff.c
new file mode 100644
index 0000000..2ae5394
--- /dev/null
+++ b/libexec/getty/fbtab_stuff.c
@@ -0,0 +1,93 @@
+#include <sys/types.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+
+#include "pathnames.h"
+
+#define WSPACE " \t\n"
+
+void reset_fbtab __P((char *tty));
+void reset_protect __P((char *table, char *path, int mask));
+
+/*
+ * reset_fbtab - reset ownership to root/wheel and apply protections
+ * specified in /etc/fbtab or logindevperm
+ */
+
+void
+reset_fbtab(tty)
+char *tty;
+{
+ FILE *fp;
+ char buf[BUFSIZ];
+ char *devname;
+ char *cp;
+ int prot;
+ char *table;
+
+ if ((fp = fopen(table = _PATH_FBTAB, "r")) == 0
+ && (fp = fopen(table = _PATH_LOGINDEVPERM, "r")) == 0)
+ return;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (cp = strchr(buf, '#'))
+ *cp = 0; /* strip comment */
+ if ((cp = devname = strtok(buf, WSPACE)) == 0)
+ continue; /* empty or comment */
+ if (strncmp(devname, "/dev/", 5) != 0
+ || (cp = strtok((char *) 0, WSPACE)) == 0
+ || *cp != '0'
+ || sscanf(cp, "%o", &prot) == 0
+ || prot == 0
+ || (prot & 0777) != prot
+ || (cp = strtok((char *) 0, WSPACE)) == 0) {
+ syslog(LOG_ERR, "%s: bad entry: %s", table, cp ? cp : "(null)");
+ continue;
+ }
+ if (strcmp(devname, tty) == 0) {
+ for (cp = strtok(cp, ":"); cp; cp = strtok((char *) 0, ":")) {
+ reset_protect(table, cp, prot);
+ }
+ }
+ }
+ fclose(fp);
+}
+
+/* reset_protect - protect one device entry */
+
+void
+reset_protect(table, path, mask)
+char *table;
+char *path;
+int mask;
+{
+ char buf[BUFSIZ];
+ int pathlen = strlen(path);
+ struct dirent *ent;
+ DIR *dir;
+
+ if (strcmp("/*", path + pathlen - 2) != 0) {
+ if (chmod(path, mask) && errno != ENOENT)
+ syslog(LOG_ERR, "%s: chmod(%s): %m", table, path);
+ if (chown(path, 0, 0) && errno != ENOENT)
+ syslog(LOG_ERR, "%s: chown(%s): %m", table, path);
+ } else {
+ strcpy(buf, path);
+ buf[pathlen - 1] = 0;
+ if ((dir = opendir(buf)) == 0) {
+ syslog(LOG_ERR, "%s: opendir(%s): %m", table, path);
+ } else {
+ while ((ent = readdir(dir)) != 0) {
+ if (strcmp(ent->d_name, ".") != 0
+ && strcmp(ent->d_name, "..") != 0) {
+ strcpy(buf + pathlen - 1, ent->d_name);
+ reset_protect(table, buf, mask);
+ }
+ }
+ closedir(dir);
+ }
+ }
+}
diff --git a/libexec/getty/getty.8 b/libexec/getty/getty.8
new file mode 100644
index 0000000..ce6274b
--- /dev/null
+++ b/libexec/getty/getty.8
@@ -0,0 +1,127 @@
+.\" 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
+.\" 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.
+.\"
+.\" @(#)getty.8 6.6 (Berkeley) 4/25/91
+.\"
+.Dd April 25, 1991
+.Dt GETTY 8
+.Os BSD 4
+.Sh NAME
+.Nm getty
+.Nd set terminal mode
+.Sh SYNOPSIS
+.Nm getty
+.Oo
+.Ar type
+.Op Ar tty
+.Oc
+.Sh DESCRIPTION
+The
+.Nm getty
+program
+is called by
+.Xr init 8
+to open and initialize the tty line, read a login name, and invoke
+.Xr login 1 .
+.Pp
+The argument
+.Ar tty
+is the special device file in
+.Pa /dev
+to open for the terminal (for example, ``ttyh0'').
+If there is no argument or the argument is
+.Ql Fl ,
+the tty line is assumed to be open as file descriptor 0.
+.Pp
+The
+.Ar type
+argument can be used to make
+.Nm getty
+treat the terminal line specially.
+This argument is used as an index into the
+.Nm gettytab 5
+database, to determine the characteristics of the line.
+If there is no argument, or there is no such table, the
+.Em default
+table is used.
+If there is no
+.Pa /etc/gettytab
+a set of system defaults is used.
+If indicated by the table located,
+.Nm getty
+will clear the terminal screen,
+print a banner heading,
+and prompt for a login name.
+Usually either the banner or the login prompt will include
+the system hostname.
+.Pp
+Most of the default actions of
+.Nm getty
+can be circumvented, or modified, by a suitable
+.Nm gettytab
+table.
+.Pp
+The
+.Nm getty
+program
+can be set to timeout after some interval,
+which will cause dial up lines to hang up
+if the login name is not entered reasonably quickly.
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "ttyxx: No such device or address."
+.It "ttyxx: No such file or address."
+A terminal which is turned
+on in the
+.Xr ttys
+file cannot be opened, likely because the requisite
+lines are either not configured into the system, the associated device
+was not attached during boot-time system configuration,
+or the special file in
+.Pa /dev
+does not exist.
+.El
+.Sh FILES
+.Bl -tag -width /etc/gettytab -compact
+.It Pa /etc/gettytab
+.El
+.Sh SEE ALSO
+.Xr gettytab 5 ,
+.Xr init 8 ,
+.Xr login 1 ,
+.Xr ioctl 2 ,
+.Xr tty 4 ,
+.Xr ttys 5
+.Sh HISTORY
+A
+.Nm getty
+program appeared in
+.At v6 .
diff --git a/libexec/getty/gettytab.5 b/libexec/getty/gettytab.5
new file mode 100644
index 0000000..db6b1cc
--- /dev/null
+++ b/libexec/getty/gettytab.5
@@ -0,0 +1,324 @@
+.\" 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
+.\" 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.
+.\"
+.\" @(#)gettytab.5 6.7 (Berkeley) 5/10/91
+.\"
+.Dd May 10, 1991
+.Dt GETTYTAB 5
+.Os BSD 4.2
+.Sh NAME
+.Nm gettytab
+.Nd terminal configuration data base
+.Sh SYNOPSIS
+.Nm gettytab
+.Sh DESCRIPTION
+The
+.Nm gettytab
+file
+is a simplified version of the
+.Xr termcap 5
+data base
+used to describe terminal lines.
+The initial terminal login process
+.Xr getty 8
+accesses the
+.Nm gettytab
+file each time it starts, allowing simpler
+reconfiguration of terminal characteristics.
+Each entry in the data base
+is used to describe one class of terminals.
+.Pp
+There is a default terminal class,
+.Em default ,
+that is used to set global defaults for all other classes.
+(That is, the
+.Em default
+entry is read, then the entry for the class required
+is used to override particular settings.)
+.Sh CAPABILITIES
+Refer to
+.Xr termcap 5
+for a description of the file layout.
+The
+.Em default
+column below lists defaults obtained if there is
+no entry in the table obtained, nor one in the special
+.Em default
+table.
+.Bl -column Namexx /usr/bin/login Default
+.It Sy Name Type Default Description
+.It "ap bool false terminal uses any parity"
+.It "bd num 0 backspace delay"
+.It "bk str 0377 alternate end of line character (input break)"
+.It "cb bool false use crt backspace mode"
+.It "cd num 0 carriage-return delay"
+.It "ce bool false use crt erase algorithm"
+.It "ck bool false use crt kill algorithm"
+.It "cl str" Ta Dv NULL Ta
+.No "screen clear sequence"
+.It "co bool false console - add"
+.Ql \en
+after login prompt
+.It "ds str" Ta So Li ^Y Sc Ta
+.No "delayed suspend character"
+.It "dx bool false set"
+.Dv DECCTLQ
+.It "ec bool false leave echo"
+.Tn OFF
+.It "ep bool false terminal uses even parity"
+.It "er str" Ta So Li ^? Sc Ta
+.No "erase character"
+.It "et str" Ta So Li ^D Sc Ta
+.No "end of text"
+.Pq Dv EOF
+character
+.It "ev str" Ta Dv NULL Ta
+.No "initial environment"
+.It "f0 num unused tty mode flags to write messages"
+.It "f1 num unused tty mode flags to read login name"
+.It "f2 num unused tty mode flags to leave terminal as"
+.It "fd num 0 form-feed (vertical motion) delay"
+.It "fl str" Ta So Li ^O Sc Ta
+.No "output flush character"
+.It "hc bool false do"
+.Tn NOT
+hangup line on last close
+.It "he str" Ta Dv NULL Ta
+.No "hostname editing string"
+.It "hn str hostname hostname"
+.It "ht bool false terminal has real tabs"
+.It "ig bool false ignore garbage characters in login name"
+.It "im str" Ta Dv NULL Ta
+.No "initial (banner) message"
+.It "in str" Ta So Li ^C Sc Ta
+.No "interrupt character"
+.It "is num unused input speed"
+.It "kl str" Ta So Li ^U Sc Ta
+.No "kill character"
+.It "lc bool false terminal has lower case"
+.It "lm str login: login prompt"
+.It "ln str" Ta So Li ^V Sc Ta
+.No "``literal next'' character"
+.It "lo str" Ta Pa /usr/bin/login Ta
+.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 (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"
+.It "pc str" Ta So Li \e0 Sc Ta
+.No "pad character"
+.It "pe bool false use printer (hard copy) erase algorithm"
+.It "pf num 0 delay"
+between first prompt and following flush (seconds)
+.It "ps bool false line connected to a"
+.Tn MICOM
+port selector
+.It "qu str" Ta So Li \&^\e Sc Ta
+.No "quit character"
+.It "rp str" Ta So Li ^R Sc Ta
+.No "line retype character"
+.It "rw bool false do"
+.Tn NOT
+use raw for input, use cbreak
+.It "sp num unused line speed (input and output)"
+.It "su str" Ta So Li ^Z Sc Ta
+.No "suspend character"
+.It "tc str none table continuation"
+.It "to num 0 timeout (seconds)"
+.It "tt str" Ta Dv NULL Ta
+.No "terminal type (for environment)"
+.It "ub bool false do unbuffered output (of prompts etc)"
+.It "uc bool false terminal is known upper case only"
+.It "we str" Ta So Li ^W Sc Ta
+.No "word erase character"
+.It "xc bool false do
+.Tn NOT
+echo control chars as
+.Ql ^X
+.It "xf str" Ta So Li ^S Sc Ta Dv XOFF
+(stop output) character
+.It "xn str" Ta So Li ^Q Sc Ta Dv XON
+(start output) character
+.El
+.Pp
+If no line speed is specified, speed will not be altered
+from that which prevails when getty is entered.
+Specifying an input or output speed will override
+line speed for stated direction only.
+.Pp
+Terminal modes to be used for the output of the message,
+for input of the login name,
+and to leave the terminal set as upon completion,
+are derived from the boolean flags specified.
+If the derivation should prove inadequate,
+any (or all) of these three may be overridden
+with one of the
+.Em \&f0 ,
+.Em \&f1 ,
+or
+.Em \&f2
+numeric specifications, which can be used to specify
+(usually in octal, with a leading '0')
+the exact values of the flags.
+Local (new tty) flags are set in the top 16 bits
+of this (32 bit) value.
+.Pp
+Should
+.Xr getty
+receive a null character
+(presumed to indicate a line break)
+it will restart using the table indicated by the
+.Em nx
+entry. If there is none, it will re-use its original table.
+.Pp
+Delays are specified in milliseconds, the nearest possible
+delay available in the tty driver will be used.
+Should greater certainty be desired, delays
+with values 0, 1, 2, and 3 are interpreted as
+choosing that particular delay algorithm from the driver.
+.Pp
+The
+.Em \&cl
+screen clear string may be preceded by a (decimal) number
+of milliseconds of delay required (a la termcap).
+This delay is simulated by repeated use of the pad character
+.Em \&pc .
+.Pp
+The initial message, and login message,
+.Em \&im
+and
+.Em \&lm
+may include the character sequence
+.Em \&%h
+or
+.Em \&%t
+to obtain
+the hostname or tty name respectively.
+.Pf ( Em %%
+obtains a single '%' character.)
+The hostname is normally obtained from the system,
+but may be set by the
+.Em \&hn
+table entry.
+In either case it may be edited with
+.Em \&he .
+The
+.Em \&he
+string is a sequence of characters, each character that
+is neither '@' nor '#' is copied into the final hostname.
+A '@' in the
+.Em \&he
+string, causes one character from the real hostname to
+be copied to the final hostname.
+A '#' in the
+.Em \&he
+string, causes the next character of the real hostname
+to be skipped.
+Surplus '@' and '#' characters are ignored.
+.Pp
+When getty execs the login process, given
+in the
+.Em \&lo
+string (usually
+.Dq Pa /usr/bin/login ) ,
+it will have set
+the environment to include the terminal type, as indicated
+by the
+.Em \&tt
+string (if it exists).
+The
+.Em \&ev
+string, can be used to enter additional data into
+the environment.
+It is a list of comma separated strings, each of which
+will presumably be of the form
+.Em name=value .
+.Pp
+If a non-zero timeout is specified, with
+.Em \&to ,
+then getty will exit within the indicated
+number of seconds, either having
+received a login name and passed control
+to
+.Xr login ,
+or having received an alarm signal, and exited.
+This may be useful to hangup dial in lines.
+.Pp
+Output from
+.Xr getty
+is even parity unless
+.Em \&op
+is specified.
+The
+.Em \&op
+string
+may be specified with
+.Em \&ap
+to allow any parity on input, but generate odd parity output.
+Note: this only applies while getty is being run,
+terminal driver limitations prevent a more complete
+implementation.
+.Xr Getty
+does not check parity of input characters in
+.Dv RAW
+mode.
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr termcap 5 ,
+.Xr getty 8 .
+.Sh BUGS
+The special characters (erase, kill, etc.) are reset to system defaults
+by
+.Xr login 1 .
+In
+.Em all
+cases, '#' or '^H' typed in a login name will be treated as
+an erase character, and '@' will be treated as a kill character.
+.Pp
+The delay stuff is a real crock.
+Apart form its general lack of flexibility, some
+of the delay algorithms are not implemented.
+The terminal driver should support sane delay settings.
+.Pp
+The
+.Em \&he
+capability is stupid.
+.Pp
+The
+.Xr termcap
+format is horrid, something more rational should
+have been chosen.
+.Sh HISTORY
+The
+.Nm gettytab
+file format appeared in 4.2BSD.
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
new file mode 100644
index 0000000..6f393ce
--- /dev/null
+++ b/libexec/getty/gettytab.h
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ *
+ * @(#)gettytab.h 5.5 (Berkeley) 3/27/91
+ */
+
+/*
+ * Getty description definitions.
+ */
+struct gettystrs {
+ char *field; /* name to lookup in gettytab */
+ char *defalt; /* value we find by looking in defaults */
+ char *value; /* value that we find there */
+};
+
+struct gettynums {
+ char *field; /* name to lookup */
+ long defalt; /* number we find in defaults */
+ long value; /* number we find there */
+ int set; /* we actually got this one */
+};
+
+struct gettyflags {
+ char *field; /* name to lookup */
+ char invrt; /* name existing in gettytab --> false */
+ char defalt; /* true/false in defaults */
+ char value; /* true/false flag */
+ char set; /* we found it */
+};
+
+/*
+ * String values.
+ */
+#define NX gettystrs[0].value
+#define CL gettystrs[1].value
+#define IM gettystrs[2].value
+#define LM gettystrs[3].value
+#define ER gettystrs[4].value
+#define KL gettystrs[5].value
+#define ET gettystrs[6].value
+#define PC gettystrs[7].value
+#define TT gettystrs[8].value
+#define EV gettystrs[9].value
+#define LO gettystrs[10].value
+#define HN gettystrs[11].value
+#define HE gettystrs[12].value
+#define IN gettystrs[13].value
+#define QU gettystrs[14].value
+#define XN gettystrs[15].value
+#define XF gettystrs[16].value
+#define BK gettystrs[17].value
+#define SU gettystrs[18].value
+#define DS gettystrs[19].value
+#define RP gettystrs[20].value
+#define FL gettystrs[21].value
+#define WE gettystrs[22].value
+#define LN gettystrs[23].value
+
+/*
+ * Numeric definitions.
+ */
+#define IS gettynums[0].value
+#define OS gettynums[1].value
+#define SP gettynums[2].value
+#define ND gettynums[3].value
+#define CD gettynums[4].value
+#define TD gettynums[5].value
+#define FD gettynums[6].value
+#define BD gettynums[7].value
+#define TO gettynums[8].value
+#define F0 gettynums[9].value
+#define F0set gettynums[9].set
+#define F1 gettynums[10].value
+#define F1set gettynums[10].set
+#define F2 gettynums[11].value
+#define F2set gettynums[11].set
+#define PF gettynums[12].value
+
+/*
+ * Boolean values.
+ */
+#define HT gettyflags[0].value
+#define NL gettyflags[1].value
+#define EP gettyflags[2].value
+#define EPset gettyflags[2].set
+#define OP gettyflags[3].value
+#define OPset gettyflags[3].set
+#define AP gettyflags[4].value
+#define APset gettyflags[4].set
+#define EC gettyflags[5].value
+#define CO gettyflags[6].value
+#define CB gettyflags[7].value
+#define CK gettyflags[8].value
+#define CE gettyflags[9].value
+#define PE gettyflags[10].value
+#define RW gettyflags[11].value
+#define XC gettyflags[12].value
+#define LC gettyflags[13].value
+#define UC gettyflags[14].value
+#define IG gettyflags[15].value
+#define PS gettyflags[16].value
+#define HC gettyflags[17].value
+#define UB gettyflags[18].value
+#define AB gettyflags[19].value
+#define DX gettyflags[20].value
+#define NP gettyflags[21].value
+
+int getent();
+long getnum();
+int getflag();
+char *getstr();
+
+extern struct gettyflags gettyflags[];
+extern struct gettynums gettynums[];
+extern struct gettystrs gettystrs[];
+extern int hopcount;
diff --git a/libexec/getty/init.c b/libexec/getty/init.c
new file mode 100644
index 0000000..ac7112f
--- /dev/null
+++ b/libexec/getty/init.c
@@ -0,0 +1,121 @@
+/*
+ * 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[] = "@(#)init.c 5.6 (Berkeley) 3/27/91";
+#endif /* not lint */
+
+/*
+ * Getty table initializations.
+ *
+ * Melbourne getty.
+ */
+#include <sgtty.h>
+#include "gettytab.h"
+#include "pathnames.h"
+
+extern struct sgttyb tmode;
+extern struct tchars tc;
+extern struct ltchars ltc;
+extern char hostname[];
+
+struct gettystrs gettystrs[] = {
+ { "nx" }, /* next table */
+ { "cl" }, /* screen clear characters */
+ { "im" }, /* initial message */
+ { "lm", "login: " }, /* login message */
+ { "er", &tmode.sg_erase }, /* erase character */
+ { "kl", &tmode.sg_kill }, /* kill character */
+ { "et", &tc.t_eofc }, /* eof chatacter (eot) */
+ { "pc", "" }, /* pad character */
+ { "tt" }, /* terminal type */
+ { "ev" }, /* enviroment */
+ { "lo", _PATH_LOGIN }, /* login program */
+ { "hn", hostname }, /* host name */
+ { "he" }, /* host name edit */
+ { "in", &tc.t_intrc }, /* interrupt char */
+ { "qu", &tc.t_quitc }, /* quit char */
+ { "xn", &tc.t_startc }, /* XON (start) char */
+ { "xf", &tc.t_stopc }, /* XOFF (stop) char */
+ { "bk", &tc.t_brkc }, /* brk char (alt \n) */
+ { "su", &ltc.t_suspc }, /* suspend char */
+ { "ds", &ltc.t_dsuspc }, /* delayed suspend */
+ { "rp", &ltc.t_rprntc }, /* reprint char */
+ { "fl", &ltc.t_flushc }, /* flush output */
+ { "we", &ltc.t_werasc }, /* word erase */
+ { "ln", &ltc.t_lnextc }, /* literal next */
+ { 0 }
+};
+
+struct gettynums gettynums[] = {
+ { "is" }, /* input speed */
+ { "os" }, /* output speed */
+ { "sp" }, /* both speeds */
+ { "nd" }, /* newline delay */
+ { "cd" }, /* carriage-return delay */
+ { "td" }, /* tab delay */
+ { "fd" }, /* form-feed delay */
+ { "bd" }, /* backspace delay */
+ { "to" }, /* timeout */
+ { "f0" }, /* output flags */
+ { "f1" }, /* input flags */
+ { "f2" }, /* user mode flags */
+ { "pf" }, /* delay before flush at 1st prompt */
+ { 0 }
+};
+
+struct gettyflags gettyflags[] = {
+ { "ht", 0 }, /* has tabs */
+ { "nl", 1 }, /* has newline char */
+ { "ep", 0 }, /* even parity */
+ { "op", 0 }, /* odd parity */
+ { "ap", 0 }, /* any parity */
+ { "ec", 1 }, /* no echo */
+ { "co", 0 }, /* console special */
+ { "cb", 0 }, /* crt backspace */
+ { "ck", 0 }, /* crt kill */
+ { "ce", 0 }, /* crt erase */
+ { "pe", 0 }, /* printer erase */
+ { "rw", 1 }, /* don't use raw */
+ { "xc", 1 }, /* don't ^X ctl chars */
+ { "lc", 0 }, /* terminal las lower case */
+ { "uc", 0 }, /* terminal has no lower case */
+ { "ig", 0 }, /* ignore garbage */
+ { "ps", 0 }, /* do port selector speed select */
+ { "hc", 1 }, /* don't set hangup on close */
+ { "ub", 0 }, /* unbuffered output */
+ { "ab", 0 }, /* auto-baud detect with '\r' */
+ { "dx", 0 }, /* set decctlq */
+ { "np", 0 }, /* no parity at all (8bit chars) */
+ { 0 }
+};
diff --git a/libexec/getty/main.c b/libexec/getty/main.c
new file mode 100644
index 0000000..a161ed5
--- /dev/null
+++ b/libexec/getty/main.c
@@ -0,0 +1,502 @@
+/*-
+ * 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
+ * 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
+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 5.16 (Berkeley) 3/27/91";
+#endif /* not lint */
+
+#define USE_OLD_TTY
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sgtty.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"
+
+struct sgttyb tmode = {
+ 0, 0, CERASE, CKILL, 0
+};
+struct tchars tc = {
+ CINTR, CQUIT, CSTART,
+ CSTOP, CEOF, CBRK,
+};
+struct ltchars ltc = {
+ CSUSP, CDSUSP, CRPRNT,
+ CFLUSH, CWERASE, CLNEXT
+};
+
+int crmod, digit, lower, upper;
+
+char hostname[MAXHOSTNAMELEN];
+char name[16];
+char dev[] = _PATH_DEV;
+char ttyn[32];
+char *portselector();
+char *ttyname();
+
+#define OBUFSIZ 128
+#define TABBUFSIZ 512
+
+char defent[TABBUFSIZ];
+char defstrs[TABBUFSIZ];
+char tabent[TABBUFSIZ];
+char tabstrs[TABBUFSIZ];
+
+char *env[128];
+
+char partab[] = {
+ 0001,0201,0201,0001,0201,0001,0001,0201,
+ 0202,0004,0003,0205,0005,0206,0201,0001,
+ 0201,0001,0001,0201,0001,0201,0201,0001,
+ 0001,0201,0201,0001,0201,0001,0001,0201,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0000,0200,0200,0000,0200,0000,0000,0201
+};
+
+#define ERASE tmode.sg_erase
+#define KILL tmode.sg_kill
+#define EOT tc.t_eofc
+
+jmp_buf timeout;
+
+static void
+dingdong()
+{
+
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ longjmp(timeout, 1);
+}
+
+jmp_buf intrupt;
+
+static void
+interrupt()
+{
+
+ signal(SIGINT, interrupt);
+ longjmp(intrupt, 1);
+}
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char **environ;
+ char *tname;
+ int repcnt = 0;
+
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+
+ openlog("getty", LOG_ODELAY|LOG_CONS, LOG_AUTH);
+ gethostname(hostname, sizeof(hostname));
+ if (hostname[0] == '\0')
+ strcpy(hostname, "Amnesiac");
+ /*
+ * 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.
+ * J. Gettys - MIT Project Athena.
+ */
+ if (argc <= 2 || strcmp(argv[2], "-") == 0)
+ strcpy(ttyn, ttyname(0));
+ else {
+ int i;
+
+ strcpy(ttyn, dev);
+ strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
+ if (strcmp(argv[0], "+") != 0) {
+ chown(ttyn, 0, 0);
+ chmod(ttyn, 0600);
+ revoke(ttyn);
+ while ((i = open(ttyn, O_RDWR)) == -1) {
+ if (repcnt % 10 == 0) {
+ syslog(LOG_ERR, "%s: %m", ttyn);
+ closelog();
+ }
+ repcnt++;
+ sleep(60);
+ }
+ login_tty(i);
+ }
+ }
+
+ gettable("default", defent, defstrs);
+ gendefaults();
+ tname = "default";
+ if (argc > 1)
+ tname = argv[1];
+ for (;;) {
+ int off = 0;
+ int flushboth = 0;
+ struct sgttyb fake;
+
+ gettable(tname, tabent, tabstrs);
+ if (OPset || EPset || APset)
+ APset++, OPset++, EPset++;
+ setdefaults();
+ 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)
+ tmode.sg_ispeed = speed(SP);
+ if (OS)
+ tmode.sg_ospeed = speed(OS);
+ else if (SP)
+ tmode.sg_ospeed = speed(SP);
+ set_tmode(0);
+ setchars();
+ ioctl(0, TIOCSETC, &tc);
+ if (HC)
+ ioctl(0, TIOCHPCL, 0);
+ if (AB) {
+ extern char *autobaud();
+
+ tname = autobaud();
+ continue;
+ }
+ if (PS) {
+ tname = portselector();
+ continue;
+ }
+ if (CL && *CL)
+ putpad(CL);
+ edithost(HE);
+ if (IM && *IM)
+ putf(IM);
+ if (setjmp(timeout)) {
+ tmode.sg_ispeed = tmode.sg_ospeed = 0;
+ ioctl(0, TIOCSETP, &tmode);
+ exit(1);
+ }
+ if (TO) {
+ signal(SIGALRM, dingdong);
+ alarm(TO);
+ }
+ if (getname()) {
+ register int i;
+
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ oflush();
+ if (name[0] == '-') {
+ puts("user names may not start with '-'.");
+ continue;
+ }
+ if (!(upper || lower || digit))
+ continue;
+ set_tmode(2);
+ ioctl(0, TIOCSLTC, &ltc);
+ for (i = 0; environ[i] != (char *)0; i++)
+ env[i] = environ[i];
+ makeenv(&env[i]);
+
+ 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);
+ if (NX && *NX)
+ tname = NX;
+ }
+}
+
+getname()
+{
+ register int c;
+ register char *np;
+ char cs;
+ int flushin = 1 /*FREAD*/;
+
+ /*
+ * Interrupt may happen if we use CBREAK mode
+ */
+ if (setjmp(intrupt)) {
+ signal(SIGINT, SIG_IGN);
+ return (0);
+ }
+ signal(SIGINT, interrupt);
+ ioctl(0, TIOCFLUSH, &flushin); /* purge any input */
+ prompt();
+ oflush();
+ if (PF > 0) {
+ sleep(PF);
+ PF = 0;
+ }
+ set_tmode(1);
+ crmod = digit = lower = upper = 0;
+ np = name;
+ for (;;) {
+ oflush();
+ if (read(STDIN_FILENO, &cs, 1) <= 0)
+ exit(0);
+ if ((c = cs&0177) == 0)
+ return (0);
+ if (c == EOT || c == 4 /*^D*/)
+ exit(1);
+ if (c == '\r' || c == '\n' || np >= &name[sizeof name]) {
+ putf("\r\n");
+ break;
+ }
+ if (islower(c))
+ lower = 1;
+ else if (isupper(c))
+ upper = 1;
+ else if (c == ERASE || c == '\b' || c == 0177) {
+ if (np > name) {
+ np--;
+ if (tmode.sg_ospeed >= B1200)
+ puts("\b \b");
+ else
+ putchr(cs);
+ }
+ continue;
+ } else if (c == KILL || c == 025 /*^U*/) {
+ putchr('\r');
+ if (tmode.sg_ospeed < B1200)
+ putchr('\n');
+ /* this is the way they do it down under ... */
+ else if (np > name)
+ puts(" \r");
+ prompt();
+ np = name;
+ continue;
+ } else if (isdigit(c))
+ digit++;
+ if (IG && (c <= ' ' || c > 0176))
+ continue;
+ *np++ = c;
+ putchr(cs);
+ }
+ signal(SIGINT, SIG_IGN);
+ *np = 0;
+ if (c == '\r')
+ crmod = 1;
+ if (upper && !lower && !LC || UC)
+ for (np = name; *np; np++)
+ if (isupper(*np))
+ *np = tolower(*np);
+ return (1);
+}
+
+static
+short tmspc10[] = {
+ 0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5, 15
+};
+
+putpad(s)
+ register char *s;
+{
+ register pad = 0;
+ register mspc10;
+
+ if (isdigit(*s)) {
+ while (isdigit(*s)) {
+ pad *= 10;
+ pad += *s++ - '0';
+ }
+ pad *= 10;
+ if (*s == '.' && isdigit(s[1])) {
+ pad += s[1] - '0';
+ s += 2;
+ }
+ }
+
+ puts(s);
+ /*
+ * If no delay needed, or output speed is
+ * not comprehensible, then don't try to delay.
+ */
+ if (pad == 0)
+ return;
+ if (tmode.sg_ospeed <= 0 ||
+ tmode.sg_ospeed >= (sizeof tmspc10 / sizeof tmspc10[0]))
+ return;
+
+ /*
+ * Round up by a half a character frame, and then do the delay.
+ * Too bad there are no user program accessible programmed delays.
+ * Transmitting pad characters slows many terminals down and also
+ * loads the system.
+ */
+ mspc10 = tmspc10[tmode.sg_ospeed];
+ pad += mspc10 / 2;
+ for (pad /= mspc10; pad > 0; pad--)
+ putchr(*PC);
+}
+
+puts(s)
+ register char *s;
+{
+ while (*s)
+ putchr(*s++);
+}
+
+char outbuf[OBUFSIZ];
+int obufcnt = 0;
+
+putchr(cc)
+{
+ char c;
+
+ c = cc;
+ if (!NP) {
+ c |= partab[c&0177] & 0200;
+ if (OP)
+ c ^= 0200;
+ }
+ if (!UB) {
+ outbuf[obufcnt++] = c;
+ if (obufcnt >= OBUFSIZ)
+ oflush();
+ } else
+ write(STDOUT_FILENO, &c, 1);
+}
+
+oflush()
+{
+ if (obufcnt)
+ write(STDOUT_FILENO, outbuf, obufcnt);
+ obufcnt = 0;
+}
+
+prompt()
+{
+
+ putf(LM);
+ if (CO)
+ putchr('\n');
+}
+
+putf(cp)
+ register char *cp;
+{
+ extern char editedhost[];
+ time_t t;
+ char *slash, db[100];
+
+ while (*cp) {
+ if (*cp != '%') {
+ putchr(*cp++);
+ continue;
+ }
+ switch (*++cp) {
+
+ case 't':
+ slash = rindex(ttyn, '/');
+ if (slash == (char *) 0)
+ puts(ttyn);
+ else
+ puts(&slash[1]);
+ break;
+
+ case 'h':
+ puts(editedhost);
+ break;
+
+ case 'd': {
+ static char fmt[] = "%l:% %p on %A, %d %B %Y";
+
+ fmt[4] = 'M'; /* I *hate* SCCS... */
+ (void)time(&t);
+ (void)strftime(db, sizeof(db), fmt, localtime(&t));
+ puts(db);
+ break;
+ }
+
+ case '%':
+ putchr('%');
+ break;
+ }
+ 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
new file mode 100644
index 0000000..e02b8b9
--- /dev/null
+++ b/libexec/getty/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * 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
+ * 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 5.3 (Berkeley) 6/1/90
+ */
+
+#include <paths.h>
+
+#define _PATH_GETTYTAB "/etc/gettytab"
+#define _PATH_LOGIN "/usr/bin/login"
diff --git a/libexec/getty/subr.c b/libexec/getty/subr.c
new file mode 100644
index 0000000..6e4f5f0
--- /dev/null
+++ b/libexec/getty/subr.c
@@ -0,0 +1,518 @@
+/*
+ * 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.
+ *
+ * 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 5.10 (Berkeley) 2/26/91";
+#endif /* not lint */
+
+/*
+ * Melbourne getty.
+ */
+#include <sys/param.h>
+#include <sgtty.h>
+#include <unistd.h>
+#include <string.h>
+#include "gettytab.h"
+
+extern struct sgttyb tmode;
+extern struct tchars tc;
+extern struct ltchars ltc;
+
+/*
+ * Get a table entry.
+ */
+gettable(name, buf, area)
+ char *name, *buf, *area;
+{
+ register struct gettystrs *sp;
+ register struct gettynums *np;
+ register struct gettyflags *fp;
+ register n;
+
+ hopcount = 0; /* new lookup, start fresh */
+ if (getent(buf, name) != 1)
+ return;
+
+ for (sp = gettystrs; sp->field; sp++)
+ sp->value = getstr(sp->field, &area);
+ for (np = gettynums; np->field; np++) {
+ n = getnum(np->field);
+ if (n == -1)
+ np->set = 0;
+ else {
+ np->set = 1;
+ np->value = n;
+ }
+ }
+ for (fp = gettyflags; fp->field; fp++) {
+ n = getflag(fp->field);
+ if (n == -1)
+ fp->set = 0;
+ else {
+ fp->set = 1;
+ fp->value = n ^ fp->invrt;
+ }
+ }
+}
+
+gendefaults()
+{
+ register struct gettystrs *sp;
+ register struct gettynums *np;
+ register struct gettyflags *fp;
+
+ for (sp = gettystrs; sp->field; sp++)
+ if (sp->value)
+ sp->defalt = sp->value;
+ for (np = gettynums; np->field; np++)
+ if (np->set)
+ np->defalt = np->value;
+ for (fp = gettyflags; fp->field; fp++)
+ if (fp->set)
+ fp->defalt = fp->value;
+ else
+ fp->defalt = fp->invrt;
+}
+
+setdefaults()
+{
+ register struct gettystrs *sp;
+ register struct gettynums *np;
+ register struct gettyflags *fp;
+
+ for (sp = gettystrs; sp->field; sp++)
+ if (!sp->value)
+ sp->value = sp->defalt;
+ for (np = gettynums; np->field; np++)
+ if (!np->set)
+ np->value = np->defalt;
+ for (fp = gettyflags; fp->field; fp++)
+ if (!fp->set)
+ fp->value = fp->defalt;
+}
+
+static char **
+charnames[] = {
+ &ER, &KL, &IN, &QU, &XN, &XF, &ET, &BK,
+ &SU, &DS, &RP, &FL, &WE, &LN, 0
+};
+
+static char *
+charvars[] = {
+ &tmode.sg_erase, &tmode.sg_kill, &tc.t_intrc,
+ &tc.t_quitc, &tc.t_startc, &tc.t_stopc,
+ &tc.t_eofc, &tc.t_brkc, &ltc.t_suspc,
+ &ltc.t_dsuspc, &ltc.t_rprntc, &ltc.t_flushc,
+ &ltc.t_werasc, &ltc.t_lnextc, 0
+};
+
+setchars()
+{
+ register int i;
+ register char *p;
+
+ for (i = 0; charnames[i]; i++) {
+ p = *charnames[i];
+ if (p && *p)
+ *charvars[i] = *p;
+ else
+ *charvars[i] = '\377';
+ }
+}
+
+long
+setflags(n)
+{
+ register long f;
+
+ switch (n) {
+ case 0:
+ if (F0set)
+ return(F0);
+ break;
+ case 1:
+ if (F1set)
+ return(F1);
+ break;
+ default:
+ if (F2set)
+ return(F2);
+ break;
+ }
+
+ f = 0;
+
+ if (AP)
+ f |= ANYP;
+ else if (OP)
+ f |= ODDP;
+ else if (EP)
+ f |= EVENP;
+ if (NP)
+ f |= PASS8;
+
+ if (UC)
+ f |= LCASE;
+
+ if (NL)
+ f |= CRMOD;
+
+ f |= delaybits();
+
+ if (n == 1) { /* read mode flags */
+ if (RW)
+ f |= RAW;
+ else
+ f |= CBREAK;
+ return (f);
+ }
+
+ if (!HT)
+ f |= XTABS;
+
+ if (n == 0)
+ return (f);
+
+ if (CB)
+ f |= CRTBS;
+
+ if (CE)
+ f |= CRTERA;
+
+ if (CK)
+ f |= CRTKIL;
+
+ if (PE)
+ f |= PRTERA;
+
+ if (EC)
+ f |= ECHO;
+
+ if (XC)
+ f |= CTLECH;
+
+ if (DX)
+ f |= DECCTQ;
+
+ return (f);
+}
+
+struct delayval {
+ unsigned delay; /* delay in ms */
+ int bits;
+};
+
+/*
+ * below are random guesses, I can't be bothered checking
+ */
+
+struct delayval crdelay[] = {
+ 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,
+};
+
+struct delayval bsdelay[] = {
+ 1, BS1,
+ 0, 0,
+};
+
+struct delayval ffdelay[] = {
+ 1, FF1,
+ 1750, FF1,
+ 0, FF1,
+};
+
+struct delayval tbdelay[] = {
+ 1, TAB1,
+ 2, TAB2,
+ 3, XTABS, /* this is expand tabs */
+ 100, TAB1,
+ 0, TAB2,
+};
+
+delaybits()
+{
+ register f;
+
+ f = adelay(CD, crdelay);
+ f |= adelay(ND, nldelay);
+ f |= adelay(FD, ffdelay);
+ f |= adelay(TD, tbdelay);
+ f |= adelay(BD, bsdelay);
+ return (f);
+}
+
+adelay(ms, dp)
+ register ms;
+ register struct delayval *dp;
+{
+ if (ms == 0)
+ return (0);
+ while (dp->delay && ms > dp->delay)
+ dp++;
+ return (dp->bits);
+}
+
+char editedhost[MAXHOSTNAMELEN];
+
+edithost(pat)
+ register char *pat;
+{
+ register char *host = HN;
+ register char *res = editedhost;
+
+ 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)
+ strncpy(res, host, sizeof editedhost - (res - editedhost) - 1);
+ else
+ *res = '\0';
+ editedhost[sizeof editedhost - 1] = '\0';
+}
+
+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 */
+ 57600, B57600,
+ 115200, B115200,
+ 0
+};
+
+speed(val)
+{
+ register struct speedtab *sp;
+
+ if (val <= B115200)
+ return (val);
+
+ for (sp = speedtab; sp->speed; sp++)
+ if (sp->speed == val)
+ return (sp->uxname);
+
+ return (B300); /* default in impossible cases */
+}
+
+makeenv(env)
+ char *env[];
+{
+ static char termbuf[128] = "TERM=";
+ register char *p, *q;
+ register char **ep;
+ char *index();
+
+ ep = env;
+ if (TT && *TT) {
+ strcat(termbuf, TT);
+ *ep++ = termbuf;
+ }
+ if (p = EV) {
+ q = p;
+ while (q = index(q, ',')) {
+ *q++ = '\0';
+ *ep++ = p;
+ p = q;
+ }
+ if (*p)
+ *ep++ = p;
+ }
+ *ep = (char *)0;
+}
+
+/*
+ * This speed select mechanism is written for the Develcon DATASWITCH.
+ * The Develcon sends a string of the form "B{speed}\n" at a predefined
+ * baud rate. This string indicates the user's actual speed.
+ * The routine below returns the terminal type mapped from derived speed.
+ */
+struct portselect {
+ char *ps_baud;
+ char *ps_type;
+} portspeeds[] = {
+ { "B110", "std.110" },
+ { "B134", "std.134" },
+ { "B150", "std.150" },
+ { "B300", "std.300" },
+ { "B600", "std.600" },
+ { "B1200", "std.1200" },
+ { "B2400", "std.2400" },
+ { "B4800", "std.4800" },
+ { "B9600", "std.9600" },
+ { "B19200", "std.19200" },
+ { 0 }
+};
+
+char *
+portselector()
+{
+ char c, baud[20], *type = "default";
+ register struct portselect *ps;
+ int len;
+
+ alarm(5*60);
+ for (len = 0; len < sizeof (baud) - 1; len++) {
+ if (read(STDIN_FILENO, &c, 1) <= 0)
+ break;
+ c &= 0177;
+ if (c == '\n' || c == '\r')
+ break;
+ if (c == 'B')
+ len = 0; /* in case of leading garbage */
+ baud[len] = c;
+ }
+ baud[len] = '\0';
+ for (ps = portspeeds; ps->ps_baud; ps++)
+ if (strcmp(ps->ps_baud, baud) == 0) {
+ type = ps->ps_type;
+ break;
+ }
+ sleep(2); /* wait for connection to complete */
+ return (type);
+}
+
+/*
+ * This auto-baud speed select mechanism is written for the Micom 600
+ * portselector. Selection is done by looking at how the character '\r'
+ * is garbled at the different speeds.
+ */
+#include <sys/time.h>
+
+char *
+autobaud()
+{
+ int rfds;
+ struct timeval timeout;
+ char c, *type = "9600-baud";
+ int null = 0;
+
+ ioctl(0, TIOCFLUSH, &null);
+ rfds = 1 << 0;
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+ if (select(32, (fd_set *)&rfds, (fd_set *)NULL,
+ (fd_set *)NULL, &timeout) <= 0)
+ return (type);
+ if (read(STDIN_FILENO, &c, sizeof(char)) != sizeof(char))
+ return (type);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 20;
+ (void) select(32, (fd_set *)NULL, (fd_set *)NULL,
+ (fd_set *)NULL, &timeout);
+ ioctl(0, TIOCFLUSH, &null);
+ switch (c & 0377) {
+
+ case 0200: /* 300-baud */
+ type = "300-baud";
+ break;
+
+ case 0346: /* 1200-baud */
+ type = "1200-baud";
+ break;
+
+ case 015: /* 2400-baud */
+ case 0215:
+ type = "2400-baud";
+ break;
+
+ default: /* 4800-baud */
+ type = "4800-baud";
+ break;
+
+ case 0377: /* 9600-baud */
+ type = "9600-baud";
+ break;
+ }
+ return (type);
+}
diff --git a/libexec/getty/ttydefaults.c b/libexec/getty/ttydefaults.c
new file mode 100644
index 0000000..518f41b
--- /dev/null
+++ b/libexec/getty/ttydefaults.c
@@ -0,0 +1,54 @@
+/*-
+ * 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 sccsid[] = "@(#)ttydefaults.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/termios.h>
+
+#include "extern.h"
+
+void
+set_ttydefaults(fd)
+ int fd;
+{
+ struct termios term;
+
+ tcgetattr(fd, &term);
+ term.c_iflag = TTYDEF_IFLAG;
+ term.c_oflag = TTYDEF_OFLAG;
+ term.c_lflag = TTYDEF_LFLAG;
+ term.c_cflag = TTYDEF_CFLAG;
+ tcsetattr(fd, TCSAFLUSH, &term);
+}
diff --git a/libexec/getty/ttys.5 b/libexec/getty/ttys.5
new file mode 100644
index 0000000..8d31b23
--- /dev/null
+++ b/libexec/getty/ttys.5
@@ -0,0 +1,139 @@
+.\" 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.
+.\"
+.\" @(#)ttys.5 6.8 (Berkeley) 5/4/91
+.\"
+.Dd May 4, 1991
+.Dt TTYS 5
+.Os
+.Sh NAME
+.Nm ttys
+.Nd terminal initialization information
+.Sh DESCRIPTION
+The file
+.Nm ttys
+contains information that is used by various routines to initialize
+and control the use of terminal special files.
+This information is read with the
+.Xr getttyent 3
+library routines.
+There is one line in the
+.Nm ttys
+file per special device file.
+Fields are separated by tabs and/or spaces.
+Fields comprised of more than one word should be enclosed in double
+quotes (``"'').
+Blank lines and comments may appear anywhere in the file; comments
+are delimited by hash marks (``#'') and new lines.
+Any unspecified fields will default to null.
+.Pp
+The first field is the
+name of the terminal special file as it is found in
+.Pa /dev .
+.Pp
+The second field of the file is the command to execute for the line,
+usually
+.Xr getty 8 ,
+which initializes and opens the line, setting the speed, waiting for
+a user name and executing the
+.Xr login 1
+program.
+It can be, however, any desired command, for example
+the start up for a window system terminal emulator or some other
+daemon process, and can contain multiple words if quoted.
+.Pp
+The third field is the type of terminal usually connected to that
+tty line, normally the one found in the
+.Xr termcap 5
+data base file.
+The environment variable
+.Dv TERM
+is initialized with the value by
+either
+.Xr getty 8
+or
+.Xr login 1 .
+.Pp
+The remaining fields set flags in the
+.Fa ty_status
+entry (see
+.Xr getttyent 3 )
+or specify a window system process that
+.Xr init 8
+will maintain for the terminal line.
+.Pp
+As flag values, the strings ``on'' and ``off'' specify that
+.Xr init
+should (should not) execute the command given in the second field,
+while ``secure'' (if ``on'' is also specified) allows users with a
+uid of 0 to login on
+this line.
+These flag fields should not be quoted.
+.Pp
+The string ``window='' may be followed by a quoted command
+string which
+.Xr init
+will execute
+.Em before
+starting the command specified by the second field.
+.Sh EXAMPLES
+.Bd -literal
+# root login on console at 1200 baud
+console "/usr/libexec/getty std.1200" vt100 on secure
+# dialup at 1200 baud, no root logins
+ttyd0 "/usr/libexec/getty d1200" dialup on # 555-1234
+# Mike's terminal: hp2621
+ttyh0 "/usr/libexec/getty std.9600" hp2621-nl on # 457 Evans
+# John's terminal: vt100
+ttyh1 "/usr/libexec/getty std.9600" vt100 on # 459 Evans
+# terminal emulate/window system
+ttyv0 "/usr/new/xterm -L :0" vs100 on window="/usr/new/Xvs100 0"
+# Network pseudo ttys -- don't enable getty
+ttyp0 none network
+ttyp1 none network off
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/ttys -compact
+.It Pa /etc/ttys
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr getttyent 3 ,
+.Xr ttyslot 3 ,
+.Xr gettytab 5 ,
+.Xr termcap 5 ,
+.Xr getty 8 ,
+.Xr init 8
+.Sh HISTORY
+A
+.Nm
+file appeared in
+.At v6 .
diff --git a/libexec/kpasswdd/Makefile b/libexec/kpasswdd/Makefile
new file mode 100644
index 0000000..edb9482
--- /dev/null
+++ b/libexec/kpasswdd/Makefile
@@ -0,0 +1,20 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+#
+# We do still make this one conditional on kerberos, in case
+# the libraries do not exist
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \
+ || defined(MAKE_EBONES))
+
+PROG= kpasswdd
+SRCS= kpasswdd.c
+CFLAGS+=-DCRYPT -DKERBEROS -I${.CURDIR}/../../usr.bin/passwd
+DPADD= ${LIBKDB} ${LIBKRB} ${LIBDES}
+LDADD= -lkdb -lkrb -ldes
+.PATH: ${.CURDIR}/../../usr.bin/rlogin
+MAN8= kpasswdd.8
+DISTRIBUTION= krb
+
+.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/kpasswdd/kpasswdd.8 b/libexec/kpasswdd/kpasswdd.8
new file mode 100644
index 0000000..f6a401f
--- /dev/null
+++ b/libexec/kpasswdd/kpasswdd.8
@@ -0,0 +1,60 @@
+.\" 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.
+.\"
+.\" @(#)kpasswdd.8 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt KPASSWDD 8
+.Os
+.Sh NAME
+.Nm kpasswdd
+.Nd Kerberos password changing daemon
+.Sh SYNOPSIS
+.Nm kpasswdd
+.Sh DESCRIPTION
+.Nm Kpasswdd
+is the server for the
+.Xr passwd 1
+program.
+The server provides a remote password changing facility
+with Kerberos authentication.
+A user must provide the old Kerberos password, encrypted
+in a random session key, to the server.
+.Nm Kpasswdd
+runs only on the Kerberos server, as it directly updates the
+Kerberos database.
+.Sh SEE ALSO
+.Xr kerberos 1 ,
+.Xr passwd 1
+.Sh HISTORY
+The
+.Nm kpasswdd
+utility first appeared in 4.4BSD.
diff --git a/libexec/kpasswdd/kpasswdd.c b/libexec/kpasswdd/kpasswdd.c
new file mode 100644
index 0000000..23ff1f8
--- /dev/null
+++ b/libexec/kpasswdd/kpasswdd.c
@@ -0,0 +1,271 @@
+/*-
+ * 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[] = "@(#)kpasswdd.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+/*
+ * kpasswdd - update a principal's passwd field in the Kerberos
+ * database. Called from inetd.
+ * K. Fall
+ * 12-Dec-88
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/signal.h>
+#include <netinet/in.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+#include <kerberosIV/krb_db.h>
+#include <stdio.h>
+#include "kpasswd_proto.h"
+
+static struct kpasswd_data kpwd_data;
+static des_cblock master_key, key;
+static Key_schedule master_key_schedule,
+ key_schedule, random_sched;
+long mkeyversion;
+AUTH_DAT kdata;
+static Principal principal_data;
+static struct update_data ud_data;
+
+char inst[INST_SZ];
+char version[9];
+KTEXT_ST ticket;
+
+char *progname; /* for the library */
+
+main()
+{
+ struct sockaddr_in foreign;
+ int foreign_len = sizeof(foreign);
+ int rval, more;
+ static char name[] = "kpasswdd";
+
+ static struct rlimit rl = { 0, 0 };
+
+ progname = name;
+ openlog("kpasswdd", LOG_CONS | LOG_PID, LOG_AUTH);
+
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGTSTP, SIG_IGN);
+ if (setrlimit(RLIMIT_CORE, &rl) < 0) {
+ syslog(LOG_ERR, "setrlimit: %m");
+ exit(1);
+ }
+
+ if (getpeername(0, &foreign, &foreign_len) < 0) {
+ syslog(LOG_ERR,"getpeername: %m");
+ exit(1);
+ }
+
+ strcpy(inst, "*");
+ rval = krb_recvauth(
+ 0L, /* options--!MUTUAL */
+ 0, /* file desc */
+ &ticket, /* client's ticket */
+ SERVICE, /* expected service */
+ inst, /* expected instance */
+ &foreign, /* foreign addr */
+ (struct sockaddr_in *) 0, /* local addr */
+ &kdata, /* returned krb data */
+ "", /* service keys file */
+ (bit_64 *) NULL, /* returned key schedule */
+ version
+ );
+
+
+ if (rval != KSUCCESS) {
+ syslog(LOG_NOTICE, "krb_recvauth: %s", krb_err_txt[rval]);
+ cleanup();
+ exit(1);
+ }
+
+ if (*version == '\0') {
+ /* indicates error on client's side (no tickets, etc.) */
+ cleanup();
+ exit(0);
+ } else if (strcmp(version, "KPWDV0.1") != 0) {
+ syslog(LOG_NOTICE,
+ "kpasswdd version conflict (recv'd %s)",
+ version);
+ cleanup();
+ exit(1);
+ }
+
+
+ /* get master key */
+ if (kdb_get_master_key(0, master_key, master_key_schedule) != 0) {
+ syslog(LOG_ERR, "couldn't get master key");
+ cleanup();
+ exit(1);
+ }
+
+ mkeyversion = kdb_get_master_key(NULL, master_key, master_key_schedule);
+
+ if (mkeyversion < 0) {
+ syslog(LOG_NOTICE, "couldn't verify master key");
+ cleanup();
+ exit(1);
+ }
+
+ /* get principal info */
+ rval = kerb_get_principal(
+ kdata.pname,
+ kdata.pinst,
+ &principal_data,
+ 1,
+ &more
+ );
+
+ if (rval < 0) {
+ syslog(LOG_NOTICE,
+ "error retrieving principal record for %s.%s",
+ kdata.pname, kdata.pinst);
+ cleanup();
+ exit(1);
+ }
+
+ if (rval != 1 || (more != 0)) {
+ syslog(LOG_NOTICE, "more than 1 dbase entry for %s.%s",
+ kdata.pname, kdata.pinst);
+ cleanup();
+ exit(1);
+ }
+
+ /* get the user's key */
+
+ bcopy(&principal_data.key_low, key, 4);
+ bcopy(&principal_data.key_high, ((long *) key) + 1, 4);
+ kdb_encrypt_key(key, key, master_key, master_key_schedule,
+ DECRYPT);
+ key_sched(key, key_schedule);
+ des_set_key(key, key_schedule);
+
+
+ /* get random key and send it over {random} Kperson */
+
+ random_key(kpwd_data.random_key);
+ strcpy(kpwd_data.secure_msg, SECURE_STRING);
+ if (des_write(0, &kpwd_data, sizeof(kpwd_data)) != sizeof(kpwd_data)) {
+ syslog(LOG_NOTICE, "error writing initial data");
+ cleanup();
+ exit(1);
+ }
+
+ bzero(key, sizeof(key));
+ bzero(key_schedule, sizeof(key_schedule));
+
+ /* now read update info: { info }Krandom */
+
+ key_sched(kpwd_data.random_key, random_sched);
+ des_set_key(kpwd_data.random_key, random_sched);
+ if (des_read(0, &ud_data, sizeof(ud_data)) != sizeof(ud_data)) {
+ syslog(LOG_NOTICE, "update aborted");
+ cleanup();
+ exit(1);
+ }
+
+ /* validate info string by looking at the embedded string */
+
+ if (strcmp(ud_data.secure_msg, SECURE_STRING) != 0) {
+ syslog(LOG_NOTICE, "invalid update from %s",
+ inet_ntoa(foreign.sin_addr));
+ cleanup();
+ exit(1);
+ }
+
+ /* produce the new key entry in the database { key }Kmaster */
+ string_to_key(ud_data.pw, key);
+ kdb_encrypt_key(key, key,
+ master_key, master_key_schedule,
+ ENCRYPT);
+ bcopy(key, &principal_data.key_low, 4);
+ bcopy(((long *) key) + 1,
+ &principal_data.key_high, 4);
+ bzero(key, sizeof(key));
+ principal_data.key_version++;
+ if (kerb_put_principal(&principal_data, 1)) {
+ syslog(LOG_ERR, "couldn't write new record for %s.%s",
+ principal_data.name, principal_data.instance);
+ cleanup();
+ exit(1);
+ }
+
+ syslog(LOG_NOTICE,"wrote new password field for %s.%s from %s",
+ principal_data.name,
+ principal_data.instance,
+ inet_ntoa(foreign.sin_addr)
+ );
+
+ send_ack(0, "Update complete.\n");
+ cleanup();
+ exit(0);
+}
+
+cleanup()
+{
+ bzero(&kpwd_data, sizeof(kpwd_data));
+ bzero(master_key, sizeof(master_key));
+ bzero(master_key_schedule, sizeof(master_key_schedule));
+ bzero(key, sizeof(key));
+ bzero(key_schedule, sizeof(key_schedule));
+ bzero(random_sched, sizeof(random_sched));
+ bzero(&principal_data, sizeof(principal_data));
+ bzero(&ud_data, sizeof(ud_data));
+}
+
+send_ack(remote, msg)
+ int remote;
+ char *msg;
+{
+ int cc;
+ cc = des_write(remote, msg, strlen(msg) + 1);
+ if (cc <= 0) {
+ syslog(LOG_NOTICE, "error writing ack");
+ cleanup();
+ exit(1);
+ }
+}
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
new file mode 100644
index 0000000..369b1e7
--- /dev/null
+++ b/libexec/rbootd/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+
+PROG= rbootd
+SRCS= bpf.c conf.c parseconf.c rbootd.c rmpproto.c utils.c
+MAN8= rbootd.8
+
+# 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
new file mode 100644
index 0000000..f5e8bce
--- /dev/null
+++ b/libexec/rbootd/bpf.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * 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.
+ *
+ * @(#)bpf.c 8.1 (Berkeley) 6/4/93
+ *
+ * Utah $Hdr: bpf.c 3.1 92/07/06$
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)bpf.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/bpf.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "defs.h"
+#include "pathnames.h"
+
+static int BpfFd = -1;
+static unsigned BpfLen = 0;
+static u_char *BpfPkt = NULL;
+
+/*
+** BpfOpen -- Open and initialize a BPF device.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** File descriptor of opened BPF device (for select() etc).
+**
+** Side Effects:
+** If an error is encountered, the program terminates here.
+*/
+int
+BpfOpen()
+{
+ struct ifreq ifr;
+ char bpfdev[32];
+ int n = 0;
+
+ /*
+ * Open the first available BPF device.
+ */
+ do {
+ (void) sprintf(bpfdev, _PATH_BPF, n++);
+ BpfFd = open(bpfdev, O_RDWR);
+ } while (BpfFd < 0 && (errno == EBUSY || errno == EPERM));
+
+ if (BpfFd < 0) {
+ syslog(LOG_ERR, "bpf: no available devices: %m");
+ Exit(0);
+ }
+
+ /*
+ * Set interface name for bpf device, get data link layer
+ * type and make sure it's type Ethernet.
+ */
+ (void) strncpy(ifr.ifr_name, IntfName, sizeof(ifr.ifr_name));
+ if (ioctl(BpfFd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "bpf: ioctl(BIOCSETIF,%s): %m", IntfName);
+ Exit(0);
+ }
+
+ /*
+ * Make sure we are dealing with an Ethernet device.
+ */
+ if (ioctl(BpfFd, BIOCGDLT, (caddr_t)&n) < 0) {
+ syslog(LOG_ERR, "bpf: ioctl(BIOCGDLT): %m");
+ Exit(0);
+ }
+ if (n != DLT_EN10MB) {
+ syslog(LOG_ERR,"bpf: %s: data-link type %d unsupported",
+ IntfName, n);
+ Exit(0);
+ }
+
+ /*
+ * On read(), return packets immediately (do not buffer them).
+ */
+ n = 1;
+ if (ioctl(BpfFd, BIOCIMMEDIATE, (caddr_t)&n) < 0) {
+ syslog(LOG_ERR, "bpf: ioctl(BIOCIMMEDIATE): %m");
+ Exit(0);
+ }
+
+ /*
+ * Try to enable the chip/driver's multicast address filter to
+ * grab our RMP address. If this fails, try promiscuous mode.
+ * If this fails, there's no way we are going to get any RMP
+ * packets so just exit here.
+ */
+#ifdef MSG_EOR
+ ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2;
+#endif
+ ifr.ifr_addr.sa_family = AF_UNSPEC;
+ bcopy(&RmpMcastAddr[0], (char *)&ifr.ifr_addr.sa_data[0], RMP_ADDRLEN);
+ if (ioctl(BpfFd, SIOCADDMULTI, (caddr_t)&ifr) < 0) {
+ syslog(LOG_WARNING,
+ "bpf: can't add mcast addr (%m), setting promiscuous mode");
+
+ if (ioctl(BpfFd, BIOCPROMISC, (caddr_t)0) < 0) {
+ syslog(LOG_ERR, "bpf: can't set promiscuous mode: %m");
+ Exit(0);
+ }
+ }
+
+ /*
+ * Ask BPF how much buffer space it requires and allocate one.
+ */
+ if (ioctl(BpfFd, BIOCGBLEN, (caddr_t)&BpfLen) < 0) {
+ syslog(LOG_ERR, "bpf: ioctl(BIOCGBLEN): %m");
+ Exit(0);
+ }
+ if (BpfPkt == NULL)
+ BpfPkt = (u_char *)malloc(BpfLen);
+
+ if (BpfPkt == NULL) {
+ syslog(LOG_ERR, "bpf: out of memory (%u bytes for bpfpkt)",
+ BpfLen);
+ Exit(0);
+ }
+
+ /*
+ * Write a little program to snarf RMP Boot packets and stuff
+ * it down BPF's throat (i.e. set up the packet filter).
+ */
+ {
+#define RMP ((struct rmp_packet *)0)
+ static struct bpf_insn bpf_insn[] = {
+ { BPF_LD|BPF_B|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dsap },
+ { BPF_JMP|BPF_JEQ|BPF_K, 0, 5, IEEE_DSAP_HP },
+ { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.cntrl },
+ { BPF_JMP|BPF_JEQ|BPF_K, 0, 3, IEEE_CNTL_HP },
+ { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dxsap },
+ { BPF_JMP|BPF_JEQ|BPF_K, 0, 1, HPEXT_DXSAP },
+ { BPF_RET|BPF_K, 0, 0, RMP_MAX_PACKET },
+ { BPF_RET|BPF_K, 0, 0, 0x0 }
+ };
+#undef RMP
+ static struct bpf_program bpf_pgm = {
+ sizeof(bpf_insn)/sizeof(bpf_insn[0]), bpf_insn
+ };
+
+ if (ioctl(BpfFd, BIOCSETF, (caddr_t)&bpf_pgm) < 0) {
+ syslog(LOG_ERR, "bpf: ioctl(BIOCSETF): %m");
+ Exit(0);
+ }
+ }
+
+ return(BpfFd);
+}
+
+/*
+** BPF GetIntfName -- Return the name of a network interface attached to
+** the system, or 0 if none can be found. The interface
+** must be configured up; the lowest unit number is
+** preferred; loopback is ignored.
+**
+** Parameters:
+** errmsg - if no network interface found, *errmsg explains why.
+**
+** Returns:
+** A (static) pointer to interface name, or NULL on error.
+**
+** Side Effects:
+** None.
+*/
+char *
+BpfGetIntfName(errmsg)
+ char **errmsg;
+{
+ struct ifreq ibuf[8], *ifrp, *ifend, *mp;
+ struct ifconf ifc;
+ int fd;
+ int minunit, n;
+ char *cp;
+ static char device[sizeof(ifrp->ifr_name)];
+ static char errbuf[128] = "No Error!";
+
+ if (errmsg != NULL)
+ *errmsg = errbuf;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ (void) strcpy(errbuf, "bpf: socket: %m");
+ return(NULL);
+ }
+ ifc.ifc_len = sizeof ibuf;
+ ifc.ifc_buf = (caddr_t)ibuf;
+
+#ifdef OSIOCGIFCONF
+ if (ioctl(fd, OSIOCGIFCONF, (char *)&ifc) < 0 ||
+ ifc.ifc_len < sizeof(struct ifreq)) {
+ (void) strcpy(errbuf, "bpf: ioctl(OSIOCGIFCONF): %m");
+ return(NULL);
+ }
+#else
+ if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 ||
+ ifc.ifc_len < sizeof(struct ifreq)) {
+ (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFCONF): %m");
+ return(NULL);
+ }
+#endif
+ ifrp = ibuf;
+ ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
+
+ mp = 0;
+ minunit = 666;
+ for (; ifrp < ifend; ++ifrp) {
+ if (ioctl(fd, SIOCGIFFLAGS, (char *)ifrp) < 0) {
+ (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFFLAGS): %m");
+ return(NULL);
+ }
+
+ /*
+ * If interface is down or this is the loopback interface,
+ * ignore it.
+ */
+ if ((ifrp->ifr_flags & IFF_UP) == 0 ||
+#ifdef IFF_LOOPBACK
+ (ifrp->ifr_flags & IFF_LOOPBACK))
+#else
+ (strcmp(ifrp->ifr_name, "lo0") == 0))
+#endif
+ continue;
+
+ for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp)
+ ;
+ n = atoi(cp);
+ if (n < minunit) {
+ minunit = n;
+ mp = ifrp;
+ }
+ }
+
+ (void) close(fd);
+ if (mp == 0) {
+ (void) strcpy(errbuf, "bpf: no interfaces found");
+ return(NULL);
+ }
+
+ (void) strcpy(device, mp->ifr_name);
+ return(device);
+}
+
+/*
+** BpfRead -- Read packets from a BPF device and fill in `rconn'.
+**
+** Parameters:
+** rconn - filled in with next packet.
+** doread - is True if we can issue a read() syscall.
+**
+** Returns:
+** True if `rconn' contains a new packet, False otherwise.
+**
+** Side Effects:
+** None.
+*/
+int
+BpfRead(rconn, doread)
+ RMPCONN *rconn;
+ int doread;
+{
+ register int datlen, caplen, hdrlen;
+ static u_char *bp = NULL, *ep = NULL;
+ int cc;
+
+ /*
+ * The read() may block, or it may return one or more packets.
+ * We let the caller decide whether or not we can issue a read().
+ */
+ if (doread) {
+ if ((cc = read(BpfFd, (char *)BpfPkt, (int)BpfLen)) < 0) {
+ syslog(LOG_ERR, "bpf: read: %m");
+ return(0);
+ } else {
+ bp = BpfPkt;
+ ep = BpfPkt + cc;
+ }
+ }
+
+#define bhp ((struct bpf_hdr *)bp)
+ /*
+ * If there is a new packet in the buffer, stuff it into `rconn'
+ * and return a success indication.
+ */
+ if (bp < ep) {
+ datlen = bhp->bh_datalen;
+ caplen = bhp->bh_caplen;
+ hdrlen = bhp->bh_hdrlen;
+
+ if (caplen != datlen)
+ syslog(LOG_ERR,
+ "bpf: short packet dropped (%d of %d bytes)",
+ caplen, datlen);
+ else if (caplen > sizeof(struct rmp_packet))
+ syslog(LOG_ERR, "bpf: large packet dropped (%d bytes)",
+ caplen);
+ else {
+ rconn->rmplen = caplen;
+ bcopy((char *)&bhp->bh_tstamp, (char *)&rconn->tstamp,
+ sizeof(struct timeval));
+ bcopy((char *)bp + hdrlen, (char *)&rconn->rmp, caplen);
+ }
+ bp += BPF_WORDALIGN(caplen + hdrlen);
+ return(1);
+ }
+#undef bhp
+
+ return(0);
+}
+
+/*
+** BpfWrite -- Write packet to BPF device.
+**
+** Parameters:
+** rconn - packet to send.
+**
+** Returns:
+** True if write succeeded, False otherwise.
+**
+** Side Effects:
+** None.
+*/
+int
+BpfWrite(rconn)
+ RMPCONN *rconn;
+{
+ if (write(BpfFd, (char *)&rconn->rmp, rconn->rmplen) < 0) {
+ syslog(LOG_ERR, "write: %s: %m", EnetStr(rconn));
+ return(0);
+ }
+
+ return(1);
+}
+
+/*
+** BpfClose -- Close a BPF device.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** None.
+*/
+void
+BpfClose()
+{
+ struct ifreq ifr;
+
+ if (BpfPkt != NULL) {
+ free((char *)BpfPkt);
+ BpfPkt = NULL;
+ }
+
+ if (BpfFd == -1)
+ return;
+
+#ifdef MSG_EOR
+ ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2;
+#endif
+ ifr.ifr_addr.sa_family = AF_UNSPEC;
+ bcopy(&RmpMcastAddr[0], (char *)&ifr.ifr_addr.sa_data[0], RMP_ADDRLEN);
+ if (ioctl(BpfFd, SIOCDELMULTI, (caddr_t)&ifr) < 0)
+ (void) ioctl(BpfFd, BIOCPROMISC, (caddr_t)0);
+
+ (void) close(BpfFd);
+ BpfFd = -1;
+}
diff --git a/libexec/rbootd/conf.c b/libexec/rbootd/conf.c
new file mode 100644
index 0000000..b890eea
--- /dev/null
+++ b/libexec/rbootd/conf.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * 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.
+ *
+ * @(#)conf.c 8.1 (Berkeley) 6/4/93
+ *
+ * Utah $Hdr: conf.c 3.1 92/07/06$
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)conf.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include "defs.h"
+#include "pathnames.h"
+
+/*
+** Define (and possibly initialize) global variables here.
+**
+** Caveat:
+** The maximum number of bootable files (`char *BootFiles[]') is
+** limited to C_MAXFILE (i.e. the maximum number of files that
+** can be spec'd in the configuration file). This was done to
+** simplify the boot file search code.
+*/
+
+char *ProgName; /* path-stripped argv[0] */
+char MyHost[MAXHOSTNAMELEN+1]; /* host name */
+int MyPid; /* process id */
+int DebugFlg = 0; /* set true if debugging */
+int BootAny = 0; /* set true if we boot anyone */
+
+char *ConfigFile = NULL; /* configuration file */
+char *DfltConfig = _PATH_RBOOTDCONF; /* default configuration file */
+char *PidFile = _PATH_RBOOTDPID; /* file w/pid of server */
+char *BootDir = _PATH_RBOOTDLIB; /* directory w/boot files */
+char *DbgFile = _PATH_RBOOTDDBG; /* debug output file */
+
+FILE *DbgFp = NULL; /* debug file pointer */
+char *IntfName = NULL; /* intf we are attached to */
+
+u_short SessionID = 0; /* generated session ID */
+
+char *BootFiles[C_MAXFILE]; /* list of boot files */
+
+CLIENT *Clients = NULL; /* list of addrs we'll accept */
+RMPCONN *RmpConns = NULL; /* list of active connections */
+
+char RmpMcastAddr[RMP_ADDRLEN] = RMP_ADDR; /* RMP multicast address */
diff --git a/libexec/rbootd/defs.h b/libexec/rbootd/defs.h
new file mode 100644
index 0000000..a2e0cd0
--- /dev/null
+++ b/libexec/rbootd/defs.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * 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
+ *
+ * Utah $Hdr: defs.h 3.1 92/07/06$
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#include "rmp.h"
+#include "rmp_var.h"
+
+/*
+** Common #define's and external variables. All other files should
+** include this.
+*/
+
+/*
+ * This may be defined in <sys/param.h>, if not, it's defined here.
+ */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+/*
+ * SIGUSR1 and SIGUSR2 are defined in <signal.h> for 4.3BSD systems.
+ */
+#ifndef SIGUSR1
+#define SIGUSR1 SIGEMT
+#endif
+#ifndef SIGUSR2
+#define SIGUSR2 SIGFPE
+#endif
+
+/*
+ * These can be faster & more efficient than strcmp()/strncmp()...
+ */
+#define STREQN(s1,s2) ((*s1 == *s2) && (strcmp(s1,s2) == 0))
+#define STRNEQN(s1,s2,n) ((*s1 == *s2) && (strncmp(s1,s2,n) == 0))
+
+/*
+ * Configuration file limitations.
+ */
+#define C_MAXFILE 10 /* max number of boot-able files */
+#define C_LINELEN 1024 /* max length of line */
+
+/*
+ * Direction of packet (used as argument to DispPkt).
+ */
+#define DIR_RCVD 0
+#define DIR_SENT 1
+#define DIR_NONE 2
+
+/*
+ * These need not be functions, so...
+ */
+#define FreeStr(str) free(str)
+#define FreeClient(cli) free(cli)
+#define GenSessID() (++SessionID ? SessionID: ++SessionID)
+
+/*
+ * Converting an Ethernet address to a string is done in many routines.
+ * Using `rmp.hp_hdr.saddr' works because this field is *never* changed;
+ * it will *always* contain the source address of the packet.
+ */
+#define EnetStr(rptr) GetEtherAddr(&(rptr)->rmp.hp_hdr.saddr[0])
+
+/*
+ * Every machine we can boot will have one of these allocated for it
+ * (unless there are no restrictions on who we can boot).
+ */
+typedef struct client_s {
+ u_char addr[RMP_ADDRLEN]; /* addr of machine */
+ char *files[C_MAXFILE]; /* boot-able files */
+ struct client_s *next; /* ptr to next */
+} CLIENT;
+
+/*
+ * Every active connection has one of these allocated for it.
+ */
+typedef struct rmpconn_s {
+ struct rmp_packet rmp; /* RMP packet */
+ int rmplen; /* length of packet */
+ struct timeval tstamp; /* last time active */
+ int bootfd; /* open boot file */
+ struct rmpconn_s *next; /* ptr to next */
+} RMPCONN;
+
+/*
+ * All these variables are defined in "conf.c".
+ */
+extern char *ProgName; /* path-stripped argv[0] */
+extern char MyHost[]; /* this hosts' name */
+extern int MyPid; /* this processes' ID */
+extern int DebugFlg; /* set true if debugging */
+extern int BootAny; /* set true if we can boot anyone */
+
+extern char *ConfigFile; /* configuration file */
+extern char *DfltConfig; /* default configuration file */
+extern char *DbgFile; /* debug output file */
+extern char *PidFile; /* file containing pid of server */
+extern char *BootDir; /* directory w/boot files */
+
+extern FILE *DbgFp; /* debug file pointer */
+extern char *IntfName; /* interface we are attached to */
+
+extern u_short SessionID; /* generated session ID */
+
+extern char *BootFiles[]; /* list of boot files */
+
+extern CLIENT *Clients; /* list of addrs we'll accept */
+extern RMPCONN *RmpConns; /* list of active connections */
+
+extern char RmpMcastAddr[]; /* RMP multicast address */
+
+void AddConn __P((RMPCONN *));
+int BootDone __P((RMPCONN *));
+void BpfClose __P((void));
+char *BpfGetIntfName __P((char **));
+int BpfOpen __P((void));
+int BpfRead __P((RMPCONN *, int));
+int BpfWrite __P((RMPCONN *));
+void DebugOff __P((int));
+void DebugOn __P((int));
+void DispPkt __P((RMPCONN *, int));
+void DoTimeout __P((void));
+void DspFlnm __P((u_int, char *));
+void Exit __P((int));
+CLIENT *FindClient __P((RMPCONN *));
+RMPCONN *FindConn __P((RMPCONN *));
+void FreeClients __P((void));
+void FreeConn __P((RMPCONN *));
+void FreeConns __P((void));
+int GetBootFiles __P((void));
+char *GetEtherAddr __P((u_char *));
+CLIENT *NewClient __P((u_char *));
+RMPCONN *NewConn __P((RMPCONN *));
+char *NewStr __P((char *));
+u_char *ParseAddr __P((char *));
+int ParseConfig __P((void));
+void ProcessPacket __P((RMPCONN *, CLIENT *));
+void ReConfig __P((int));
+void RemoveConn __P((RMPCONN *));
+int SendBootRepl __P((struct rmp_packet *, RMPCONN *, char *[]));
+int SendFileNo __P((struct rmp_packet *, RMPCONN *, char *[]));
+int SendPacket __P((RMPCONN *));
+int SendReadRepl __P((RMPCONN *));
+int SendServerID __P((RMPCONN *));
diff --git a/libexec/rbootd/parseconf.c b/libexec/rbootd/parseconf.c
new file mode 100644
index 0000000..a01a4a5
--- /dev/null
+++ b/libexec/rbootd/parseconf.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * 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.
+ *
+ * @(#)parseconf.c 8.1 (Berkeley) 6/4/93
+ *
+ * Utah $Hdr: parseconf.c 3.1 92/07/06$
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parseconf.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include "defs.h"
+
+/*
+** ParseConfig -- parse the config file into linked list of clients.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** 1 on success, 0 otherwise.
+**
+** Side Effects:
+** - Linked list of clients will be (re)allocated.
+**
+** Warnings:
+** - GetBootFiles() must be called before this routine
+** to create a linked list of default boot files.
+*/
+int
+ParseConfig()
+{
+ FILE *fp;
+ CLIENT *client;
+ u_char *addr;
+ char line[C_LINELEN];
+ register char *cp, *bcp;
+ register int i, j;
+ int omask, linecnt = 0;
+
+ if (BootAny) /* ignore config file */
+ return(1);
+
+ FreeClients(); /* delete old list of clients */
+
+ if ((fp = fopen(ConfigFile, "r")) == NULL) {
+ syslog(LOG_ERR, "ParseConfig: can't open config file (%s)",
+ ConfigFile);
+ return(0);
+ }
+
+ /*
+ * We've got to block SIGHUP to prevent reconfiguration while
+ * dealing with the linked list of Clients. This can be done
+ * when actually linking the new client into the list, but
+ * this could have unexpected results if the server was HUP'd
+ * whilst reconfiguring. Hence, it is done here.
+ */
+ omask = sigblock(sigmask(SIGHUP));
+
+ /*
+ * GETSTR positions `bcp' at the start of the current token,
+ * and null terminates it. `cp' is positioned at the start
+ * of the next token. spaces & commas are separators.
+ */
+#define GETSTR while (isspace(*cp) || *cp == ',') cp++; \
+ bcp = cp; \
+ while (*cp && *cp!=',' && !isspace(*cp)) cp++; \
+ if (*cp) *cp++ = '\0'
+
+ /*
+ * For each line, parse it into a new CLIENT struct.
+ */
+ while (fgets(line, C_LINELEN, fp) != NULL) {
+ linecnt++; /* line counter */
+
+ if (*line == '\0' || *line == '#') /* ignore comment */
+ continue;
+
+ if ((cp = index(line,'#')) != NULL) /* trash comments */
+ *cp = '\0';
+
+ cp = line; /* init `cp' */
+ GETSTR; /* get RMP addr */
+ if (bcp == cp) /* all delimiters */
+ continue;
+
+ /*
+ * Get an RMP address from a string. Abort on failure.
+ */
+ if ((addr = ParseAddr(bcp)) == NULL) {
+ syslog(LOG_ERR,
+ "ParseConfig: line %d: cant parse <%s>",
+ linecnt, bcp);
+ continue;
+ }
+
+ if ((client = NewClient(addr)) == NULL) /* alloc new client */
+ continue;
+
+ GETSTR; /* get first file */
+
+ /*
+ * If no boot files are spec'd, use the default list.
+ * Otherwise, validate each file (`bcp') against the
+ * list of boot-able files.
+ */
+ i = 0;
+ if (bcp == cp) /* no files spec'd */
+ for (; i < C_MAXFILE && BootFiles[i] != NULL; i++)
+ client->files[i] = BootFiles[i];
+ else {
+ do {
+ /*
+ * For each boot file spec'd, make sure it's
+ * in our list. If so, include a pointer to
+ * it in the CLIENT's list of boot files.
+ */
+ for (j = 0; ; j++) {
+ if (j==C_MAXFILE||BootFiles[j]==NULL) {
+ syslog(LOG_ERR, "ParseConfig: line %d: no boot file (%s)",
+ linecnt, bcp);
+ break;
+ }
+ if (STREQN(BootFiles[j], bcp)) {
+ if (i < C_MAXFILE)
+ client->files[i++] =
+ BootFiles[j];
+ else
+ syslog(LOG_ERR, "ParseConfig: line %d: too many boot files (%s)",
+ linecnt, bcp);
+ break;
+ }
+ }
+ GETSTR; /* get next file */
+ } while (bcp != cp);
+
+ /*
+ * Restricted list of boot files were spec'd,
+ * however, none of them were found. Since we
+ * apparently cant let them boot "just anything",
+ * the entire record is invalidated.
+ */
+ if (i == 0) {
+ FreeClient(client);
+ continue;
+ }
+ }
+
+ /*
+ * Link this client into the linked list of clients.
+ * SIGHUP has already been blocked.
+ */
+ if (Clients)
+ client->next = Clients;
+ Clients = client;
+ }
+
+ (void) fclose(fp); /* close config file */
+
+ (void) sigsetmask(omask); /* reset signal mask */
+
+ return(1); /* return success */
+}
+
+/*
+** ParseAddr -- Parse a string containing an RMP address.
+**
+** This routine is fairly liberal at parsing an RMP address. The
+** address must contain 6 octets consisting of between 0 and 2 hex
+** chars (upper/lower case) separated by colons. If two colons are
+** together (e.g. "::", the octet between them is recorded as being
+** zero. Hence, the following addrs are all valid and parse to the
+** same thing:
+**
+** 08:00:09:00:66:ad 8::9:0:66:AD 8::9::66:aD
+**
+** For clarity, an RMP address is really an Ethernet address, but
+** since the HP boot code uses IEEE 802.3, it's really an IEEE
+** 802.3 address. Of course, all of these are identical.
+**
+** Parameters:
+** str - string representation of an RMP address.
+**
+** Returns:
+** pointer to a static array of RMP_ADDRLEN bytes.
+**
+** Side Effects:
+** None.
+**
+** Warnings:
+** - The return value points to a static buffer; it must
+** be copied if it's to be saved.
+** - For speed, we assume a u_char consists of 8 bits.
+*/
+u_char *
+ParseAddr(str)
+ char *str;
+{
+ static u_char addr[RMP_ADDRLEN];
+ register char *cp;
+ register unsigned i;
+ register int part, subpart;
+
+ bzero((char *)&addr[0], RMP_ADDRLEN); /* zero static buffer */
+
+ part = subpart = 0;
+ for (cp = str; *cp; cp++) {
+ /*
+ * A colon (`:') must be used to delimit each octet.
+ */
+ if (*cp == ':') {
+ if (++part == RMP_ADDRLEN) /* too many parts */
+ return(NULL);
+ subpart = 0;
+ continue;
+ }
+
+ /*
+ * Convert hex character to an integer.
+ */
+ if (isdigit(*cp))
+ i = *cp - '0';
+ else {
+ i = (isupper(*cp)? tolower(*cp): *cp) - 'a' + 10;
+ if (i < 10 || i > 15) /* not a hex char */
+ return(NULL);
+ }
+
+ if (subpart++) {
+ if (subpart > 2) /* too many hex chars */
+ return(NULL);
+ addr[part] <<= 4;
+ }
+ addr[part] |= i;
+ }
+
+ if (part != (RMP_ADDRLEN-1)) /* too few parts */
+ return(NULL);
+
+ return(&addr[0]);
+}
+
+/*
+** GetBootFiles -- record list of files in current (boot) directory.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Number of boot files on success, 0 on failure.
+**
+** Side Effects:
+** Strings in `BootFiles' are freed/allocated.
+**
+** Warnings:
+** - After this routine is called, ParseConfig() must be
+** called to re-order it's list of boot file pointers.
+*/
+int
+GetBootFiles()
+{
+ DIR *dfd;
+ struct stat statb;
+ register struct dirent *dp;
+ register int i;
+
+ /*
+ * Free the current list of boot files.
+ */
+ for (i = 0; i < C_MAXFILE && BootFiles[i] != NULL; i++) {
+ FreeStr(BootFiles[i]);
+ BootFiles[i] = NULL;
+ }
+
+ /*
+ * Open current directory to read boot file names.
+ */
+ if ((dfd = opendir(".")) == NULL) { /* open BootDir */
+ syslog(LOG_ERR, "GetBootFiles: can't open directory (%s)\n",
+ BootDir);
+ return(0);
+ }
+
+ /*
+ * Read each boot file name and allocate space for it in the
+ * list of boot files (BootFiles). All boot files read after
+ * C_MAXFILE will be ignored.
+ */
+ i = 0;
+ for (dp = readdir(dfd); dp != NULL; dp = readdir(dfd)) {
+ if (stat(dp->d_name, &statb) < 0 ||
+ (statb.st_mode & S_IFMT) != S_IFREG)
+ continue;
+ if (i == C_MAXFILE)
+ syslog(LOG_ERR,
+ "GetBootFiles: too many boot files (%s ignored)",
+ dp->d_name);
+ else if ((BootFiles[i] = NewStr(dp->d_name)) != NULL)
+ i++;
+ }
+
+ (void) closedir(dfd); /* close BootDir */
+
+ if (i == 0) /* cant find any boot files */
+ syslog(LOG_ERR, "GetBootFiles: no boot files (%s)\n", BootDir);
+
+ return(i);
+}
diff --git a/libexec/rbootd/pathnames.h b/libexec/rbootd/pathnames.h
new file mode 100644
index 0000000..d960dce
--- /dev/null
+++ b/libexec/rbootd/pathnames.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * 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
+ *
+ * Utah $Hdr: pathnames.h 3.1 92/07/06$
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#define _PATH_BPF "/dev/bpf%d"
+#define _PATH_RBOOTDCONF "/etc/rbootd.conf"
+#define _PATH_RBOOTDDBG "/tmp/rbootd.dbg"
+#define _PATH_RBOOTDLIB "/usr/mdec/rbootd"
+#define _PATH_RBOOTDPID "/var/run/rbootd.pid"
diff --git a/libexec/rbootd/rbootd.8 b/libexec/rbootd/rbootd.8
new file mode 100644
index 0000000..f4eb364
--- /dev/null
+++ b/libexec/rbootd/rbootd.8
@@ -0,0 +1,156 @@
+.\" Copyright (c) 1988, 1992 The University of Utah and the Center
+.\" for Software Science (CSS).
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Center for Software Science of the University of Utah Computer
+.\" Science Department. CSS requests users of this software to return
+.\" to css-dist@cs.utah.edu any improvements that they make and grant
+.\" CSS redistribution rights.
+.\"
+.\" 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.
+.\"
+.\" @(#)rbootd.8 8.2 (Berkeley) 12/11/93
+.\"
+.\" Utah $Hdr: rbootd.man 3.1 92/07/06$
+.\" Author: Jeff Forys, University of Utah CSS
+.\"
+.Dd "December 11, 1993"
+.Dt RBOOTD 8
+.Os
+.Sh NAME
+.Nm rbootd
+.Nd HP remote boot server
+.Sh SYNOPSIS
+.Nm rbootd
+.Op Fl ad
+.Op Fl i Ar interface
+.Op config_file
+.Sh DESCRIPTION
+The
+.Nm rbootd
+utility services boot requests from Hewlett-Packard workstations over a
+local area network.
+All boot files must reside in the boot file directory; further, if a
+client supplies path information in its boot request, it will be silently
+stripped away before processing.
+By default,
+.Nm rbootd
+only responds to requests from machines listed in its configuration file.
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl a
+Respond to boot requests from any machine.
+The configuration file is ignored if this option is specified.
+.It Fl d
+Run
+.Nm rbootd
+in debug mode.
+Packets sent and received are displayed to the terminal.
+.It Fl i Ar interface
+Service boot requests on specified interface.
+If unspecified,
+.Nm rbootd
+searches the system interface list for the lowest numbered, configured
+``up'' interface (excluding loopback).
+Ties are broken by choosing the earliest match.
+.El
+.Pp
+Specifying
+.Ar config_file
+on the command line causes
+.Nm rbootd
+to use a different configuration file from the default.
+.Pp
+The configuration file is a text file where each line describes a particular
+machine.
+A line must start with a machine's Ethernet address followed by an optional
+list of boot file names.
+An Ethernet address is specified in hexadecimal with each of its six octets
+separated by a colon.
+The boot file names come from the boot file directory.
+The ethernet address and boot file(s) must be separated by white-space
+and/or comma characters.
+A pound sign causes the remainder of a line to be ignored.
+.Pp
+Here is a sample configuration file:
+.Bl -column 08:00:09:0:66:ad SYSHPBSD,SYSHPUX "# vandy (anything)"
+.It #
+.It # ethernet addr boot file(s) comments
+.It #
+.It 08:00:09:0:66:ad SYSHPBSD # snake (4.3BSD)
+.It 08:00:09:0:59:5b # vandy (anything)
+.It 8::9:1:C6:75 SYSHPBSD,SYSHPUX # jaguar (either)
+.El
+.Pp
+.Nm Rbootd
+logs status and error messages via
+.Xr syslog 3 .
+A startup message is always logged, and in the case of fatal errors (or
+deadly signals) a message is logged announcing the server's termination.
+In general, a non-fatal error is handled by ignoring the event that caused
+it (e.g. an invalid Ethernet address in the config file causes that line
+to be invalidated).
+.Pp
+The following signals have the specified effect when sent to the server
+process using the
+.Xr kill 1
+command:
+.Bl -tag -width SIGUSR1 -offset -compact
+.It SIGHUP
+Drop all active connections and reconfigure.
+.It SIGUSR1
+Turn on debugging, do nothing if already on.
+.It SIGUSR2
+Turn off debugging, do nothing if already off.
+.El
+.Sh "FILES"
+.Bl -tag -width /usr/libexec/rbootd -compact
+.It /dev/bpf#
+packet-filter device
+.It /etc/rbootd.conf
+configuration file
+.It /tmp/rbootd.dbg
+debug output
+.It /usr/mdec/rbootd
+directory containing boot files
+.It /var/run/rbootd.pid
+process id
+.El
+.Sh SEE ALSO
+.Xr kill 1 ,
+.Xr socket 2 ,
+.Xr signal 3 ,
+.Xr syslog 3 ,
+.Xr rmp 4
+.Sh BUGS
+If multiple servers are started on the same interface, each will receive
+and respond to the same boot packets.
diff --git a/libexec/rbootd/rbootd.c b/libexec/rbootd/rbootd.c
new file mode 100644
index 0000000..0164345
--- /dev/null
+++ b/libexec/rbootd/rbootd.c
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * 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.
+ *
+ * @(#)rbootd.c 8.2 (Berkeley) 2/22/94
+ *
+ * Utah $Hdr: rbootd.c 3.1 92/07/06$
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#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[] = "@(#)rbootd.c 8.2 (Berkeley) 2/22/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "defs.h"
+
+
+/* fd mask macros (backward compatibility with 4.2BSD) */
+#ifndef FD_SET
+#ifdef notdef
+typedef struct fd_set { /* this should already be in 4.2 */
+ int fds_bits[1];
+} fd_set;
+#endif
+#define FD_ZERO(p) ((p)->fds_bits[0] = 0)
+#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)))
+#endif
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c, fd, omask, maxfds;
+ fd_set rset;
+
+ /*
+ * Find what name we are running under.
+ */
+ ProgName = (ProgName = rindex(argv[0],'/')) ? ++ProgName : *argv;
+
+ /*
+ * Close any open file descriptors.
+ * Temporarily leave stdin & stdout open for `-d',
+ * and stderr open for any pre-syslog error messages.
+ */
+ {
+ int i, nfds = getdtablesize();
+
+ for (i = 0; i < nfds; i++)
+ if (i != fileno(stdin) && i != fileno(stdout) &&
+ i != fileno(stderr))
+ (void) close(i);
+ }
+
+ /*
+ * Parse any arguments.
+ */
+ while ((c = getopt(argc, argv, "adi:")) != EOF)
+ switch(c) {
+ case 'a':
+ BootAny++;
+ break;
+ case 'd':
+ DebugFlg++;
+ break;
+ case 'i':
+ IntfName = optarg;
+ break;
+ }
+ for (; optind < argc; optind++) {
+ if (ConfigFile == NULL)
+ ConfigFile = argv[optind];
+ else {
+ fprintf(stderr,
+ "%s: too many config files (`%s' ignored)\n",
+ ProgName, argv[optind]);
+ }
+ }
+
+ if (ConfigFile == NULL) /* use default config file */
+ ConfigFile = DfltConfig;
+
+ if (DebugFlg) {
+ DbgFp = stdout; /* output to stdout */
+
+ (void) signal(SIGUSR1, SIG_IGN); /* dont muck w/DbgFp */
+ (void) signal(SIGUSR2, SIG_IGN);
+ } else {
+ (void) fclose(stdin); /* dont need these */
+ (void) fclose(stdout);
+
+ /*
+ * Fork off a child to do the work & exit.
+ */
+ switch(fork()) {
+ case -1: /* fork failed */
+ fprintf(stderr, "%s: ", ProgName);
+ perror("fork");
+ Exit(0);
+ case 0: /* this is the CHILD */
+ break;
+ default: /* this is the PARENT */
+ _exit(0);
+ }
+
+ /*
+ * Try to disassociate from the current tty.
+ */
+ {
+ char *devtty = "/dev/tty";
+ int i;
+
+ if ((i = open(devtty, O_RDWR)) < 0) {
+ /* probably already disassociated */
+ if (setpgrp(0, 0) < 0) {
+ fprintf(stderr, "%s: ", ProgName);
+ perror("setpgrp");
+ }
+ } else {
+ if (ioctl(i, (u_long)TIOCNOTTY, (char *)0) < 0){
+ fprintf(stderr, "%s: ", ProgName);
+ perror("ioctl");
+ }
+ (void) close(i);
+ }
+ }
+
+ (void) signal(SIGUSR1, DebugOn);
+ (void) signal(SIGUSR2, DebugOff);
+ }
+
+ (void) fclose(stderr); /* finished with it */
+
+#ifdef SYSLOG4_2
+ openlog(ProgName, LOG_PID);
+#else
+ openlog(ProgName, LOG_PID, LOG_DAEMON);
+#endif
+
+ /*
+ * If no interface was specified, get one now.
+ *
+ * This is convoluted because we want to get the default interface
+ * name for the syslog("restarted") message. If BpfGetIntfName()
+ * runs into an error, it will return a syslog-able error message
+ * (in `errmsg') which will be displayed here.
+ */
+ if (IntfName == NULL) {
+ char *errmsg;
+
+ if ((IntfName = BpfGetIntfName(&errmsg)) == NULL) {
+ syslog(LOG_NOTICE, "restarted (??)");
+ syslog(LOG_ERR, errmsg);
+ Exit(0);
+ }
+ }
+
+ syslog(LOG_NOTICE, "restarted (%s)", IntfName);
+
+ (void) signal(SIGHUP, ReConfig);
+ (void) signal(SIGINT, Exit);
+ (void) signal(SIGTERM, Exit);
+
+ /*
+ * Grab our host name and pid.
+ */
+ if (gethostname(MyHost, MAXHOSTNAMELEN) < 0) {
+ syslog(LOG_ERR, "gethostname: %m");
+ Exit(0);
+ }
+ MyHost[MAXHOSTNAMELEN] = '\0';
+
+ MyPid = getpid();
+
+ /*
+ * Write proc's pid to a file.
+ */
+ {
+ FILE *fp;
+
+ if ((fp = fopen(PidFile, "w")) != NULL) {
+ (void) fprintf(fp, "%d\n", MyPid);
+ (void) fclose(fp);
+ } else {
+ syslog(LOG_WARNING, "fopen: failed (%s)", PidFile);
+ }
+ }
+
+ /*
+ * All boot files are relative to the boot directory, we might
+ * as well chdir() there to make life easier.
+ */
+ if (chdir(BootDir) < 0) {
+ syslog(LOG_ERR, "chdir: %m (%s)", BootDir);
+ Exit(0);
+ }
+
+ /*
+ * Initial configuration.
+ */
+ omask = sigblock(sigmask(SIGHUP)); /* prevent reconfig's */
+ if (GetBootFiles() == 0) /* get list of boot files */
+ Exit(0);
+ if (ParseConfig() == 0) /* parse config file */
+ Exit(0);
+
+ /*
+ * Open and initialize a BPF device for the appropriate interface.
+ * If an error is encountered, a message is displayed and Exit()
+ * is called.
+ */
+ fd = BpfOpen();
+
+ (void) sigsetmask(omask); /* allow reconfig's */
+
+ /*
+ * Main loop: receive a packet, determine where it came from,
+ * and if we service this host, call routine to handle request.
+ */
+ maxfds = fd + 1;
+ FD_ZERO(&rset);
+ FD_SET(fd, &rset);
+ for (;;) {
+ struct timeval timeout;
+ fd_set r;
+ int nsel;
+
+ r = rset;
+
+ if (RmpConns == NULL) { /* timeout isnt necessary */
+ nsel = select(maxfds, &r, (fd_set *)0, (fd_set *)0,
+ (struct timeval *)0);
+ } else {
+ timeout.tv_sec = RMP_TIMEOUT;
+ timeout.tv_usec = 0;
+ nsel = select(maxfds, &r, (fd_set *)0, (fd_set *)0,
+ &timeout);
+ }
+
+ if (nsel < 0) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "select: %m");
+ Exit(0);
+ } else if (nsel == 0) { /* timeout */
+ DoTimeout(); /* clear stale conns */
+ continue;
+ }
+
+ if (FD_ISSET(fd, &r)) {
+ RMPCONN rconn;
+ CLIENT *client, *FindClient();
+ int doread = 1;
+
+ while (BpfRead(&rconn, doread)) {
+ doread = 0;
+
+ if (DbgFp != NULL) /* display packet */
+ DispPkt(&rconn,DIR_RCVD);
+
+ omask = sigblock(sigmask(SIGHUP));
+
+ /*
+ * If we do not restrict service, set the
+ * client to NULL (ProcessPacket() handles
+ * this). Otherwise, check that we can
+ * service this host; if not, log a message
+ * and ignore the packet.
+ */
+ if (BootAny) {
+ client = NULL;
+ } else if ((client=FindClient(&rconn))==NULL) {
+ syslog(LOG_INFO,
+ "%s: boot packet ignored",
+ EnetStr(&rconn));
+ (void) sigsetmask(omask);
+ continue;
+ }
+
+ ProcessPacket(&rconn,client);
+
+ (void) sigsetmask(omask);
+ }
+ }
+ }
+}
+
+/*
+** DoTimeout -- Free any connections that have timed out.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - Timed out connections in `RmpConns' will be freed.
+*/
+void
+DoTimeout()
+{
+ register RMPCONN *rtmp;
+ struct timeval now;
+
+ (void) gettimeofday(&now, (struct timezone *)0);
+
+ /*
+ * For each active connection, if RMP_TIMEOUT seconds have passed
+ * since the last packet was sent, delete the connection.
+ */
+ for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next)
+ if ((rtmp->tstamp.tv_sec + RMP_TIMEOUT) < now.tv_sec) {
+ syslog(LOG_WARNING, "%s: connection timed out (%u)",
+ EnetStr(rtmp), rtmp->rmp.r_type);
+ RemoveConn(rtmp);
+ }
+}
+
+/*
+** FindClient -- Find client associated with a packet.
+**
+** Parameters:
+** rconn - the new packet.
+**
+** Returns:
+** Pointer to client info if found, NULL otherwise.
+**
+** Side Effects:
+** None.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked since
+** a reconfigure can invalidate the information returned.
+*/
+
+CLIENT *
+FindClient(rconn)
+ register RMPCONN *rconn;
+{
+ register CLIENT *ctmp;
+
+ for (ctmp = Clients; ctmp != NULL; ctmp = ctmp->next)
+ if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0],
+ (char *)&ctmp->addr[0], RMP_ADDRLEN) == 0)
+ break;
+
+ return(ctmp);
+}
+
+/*
+** Exit -- Log an error message and exit.
+**
+** Parameters:
+** sig - caught signal (or zero if not dying on a signal).
+**
+** Returns:
+** Does not return.
+**
+** Side Effects:
+** - This process ceases to exist.
+*/
+void
+Exit(sig)
+ int sig;
+{
+ if (sig > 0)
+ syslog(LOG_ERR, "going down on signal %d", sig);
+ else
+ syslog(LOG_ERR, "going down with fatal error");
+ BpfClose();
+ exit(1);
+}
+
+/*
+** ReConfig -- Get new list of boot files and reread config files.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - All active connections are dropped.
+** - List of boot-able files is changed.
+** - List of clients is changed.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked.
+*/
+void
+ReConfig(signo)
+ int signo;
+{
+ syslog(LOG_NOTICE, "reconfiguring boot server");
+
+ FreeConns();
+
+ if (GetBootFiles() == 0)
+ Exit(0);
+
+ if (ParseConfig() == 0)
+ Exit(0);
+}
+
+/*
+** DebugOff -- Turn off debugging.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - Debug file is closed.
+*/
+void
+DebugOff(signo)
+ int signo;
+{
+ if (DbgFp != NULL)
+ (void) fclose(DbgFp);
+
+ DbgFp = NULL;
+}
+
+/*
+** DebugOn -- Turn on debugging.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - Debug file is opened/truncated if not already opened,
+** otherwise do nothing.
+*/
+void
+DebugOn(signo)
+ int signo;
+{
+ if (DbgFp == NULL) {
+ if ((DbgFp = fopen(DbgFile, "w")) == NULL)
+ syslog(LOG_ERR, "can't open debug file (%s)", DbgFile);
+ }
+}
diff --git a/libexec/rbootd/rmp.h b/libexec/rbootd/rmp.h
new file mode 100644
index 0000000..d3b0167
--- /dev/null
+++ b/libexec/rbootd/rmp.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * 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.
+ *
+ * @(#)rmp.h 8.1 (Berkeley) 6/4/93
+ *
+ * Utah $Hdr: rmp.h 3.1 92/07/06$
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+/*
+ * Define MIN/MAX sizes of RMP (ethernet) packet.
+ * For ease of computation, the 4 octet CRC field is not included.
+ *
+ * MCLBYTES is for bpfwrite(); it is adamant about using a cluster.
+ */
+
+#define RMP_MAX_PACKET MIN(1514,MCLBYTES)
+#define RMP_MIN_PACKET 60
+
+/*
+ * Define RMP/Ethernet Multicast address (9:0:9:0:0:4) and its length.
+ */
+#define RMP_ADDR { 0x9, 0x0, 0x9, 0x0, 0x0, 0x4 }
+#define RMP_ADDRLEN 6
+
+/*
+ * Define IEEE802.2 (Logical Link Control) information.
+ */
+#define IEEE_DSAP_HP 0xF8 /* Destination Service Access Point */
+#define IEEE_SSAP_HP 0xF8 /* Source Service Access Point */
+#define IEEE_CNTL_HP 0x0300 /* Type 1 / I format control information */
+
+#define HPEXT_DXSAP 0x608 /* HP Destination Service Access Point */
+#define HPEXT_SXSAP 0x609 /* HP Source Service Access Point */
+
+/*
+ * 802.3-style "Ethernet" header.
+ */
+
+struct hp_hdr {
+ u_char daddr[RMP_ADDRLEN];
+ u_char saddr[RMP_ADDRLEN];
+ u_short len;
+};
+
+/*
+ * HP uses 802.2 LLC with their own local extensions. This struct makes
+ * sence out of this data (encapsulated in the above 802.3 packet).
+ */
+
+struct hp_llc {
+ u_char dsap; /* 802.2 DSAP */
+ u_char ssap; /* 802.2 SSAP */
+ u_short cntrl; /* 802.2 control field */
+ u_short filler; /* HP filler (must be zero) */
+ u_short dxsap; /* HP extended DSAP */
+ u_short sxsap; /* HP extended SSAP */
+};
diff --git a/libexec/rbootd/rmp_var.h b/libexec/rbootd/rmp_var.h
new file mode 100644
index 0000000..7df1e87
--- /dev/null
+++ b/libexec/rbootd/rmp_var.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * 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.
+ *
+ * @(#)rmp_var.h 8.1 (Berkeley) 6/4/93
+ *
+ * Utah $Hdr: rmp_var.h 3.1 92/07/06$
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+/*
+ * Possible values for "rmp_type" fields.
+ */
+
+#define RMP_BOOT_REQ 1 /* boot request packet */
+#define RMP_BOOT_REPL 129 /* boot reply packet */
+#define RMP_READ_REQ 2 /* read request packet */
+#define RMP_READ_REPL 130 /* read reply packet */
+#define RMP_BOOT_DONE 3 /* boot complete packet */
+
+/*
+ * Useful constants.
+ */
+
+#define RMP_VERSION 2 /* protocol version */
+#define RMP_TIMEOUT 600 /* timeout connection after ten minutes */
+#define RMP_PROBESID 0xffff /* session ID for probes */
+#define RMP_HOSTLEN 13 /* max length of server's name */
+#define RMP_MACHLEN 20 /* length of machine type field */
+
+/*
+ * RMP error codes
+ */
+
+#define RMP_E_OKAY 0
+#define RMP_E_EOF 2 /* read reply: returned end of file */
+#define RMP_E_ABORT 3 /* abort operation */
+#define RMP_E_BUSY 4 /* boot reply: server busy */
+#define RMP_E_TIMEOUT 5 /* lengthen time out (not implemented) */
+#define RMP_E_NOFILE 16 /* boot reply: file does not exist */
+#define RMP_E_OPENFILE 17 /* boot reply: file open failed */
+#define RMP_E_NODFLT 18 /* boot reply: default file does not exist */
+#define RMP_E_OPENDFLT 19 /* boot reply: default file open failed */
+#define RMP_E_BADSID 25 /* read reply: bad session ID */
+#define RMP_E_BADPACKET 27 /* Bad packet detected */
+
+/*
+ * RMPDATALEN is the maximum number of data octets that can be stuffed
+ * into an RMP packet. This excludes the 802.2 LLC w/HP extensions.
+ */
+#define RMPDATALEN (RMP_MAX_PACKET - (sizeof(struct hp_hdr) + \
+ sizeof(struct hp_llc)))
+
+/*
+ * Define sizes of packets we send. Boot and Read replies are variable
+ * in length depending on the length of `s'.
+ *
+ * Also, define how much space `restofpkt' can take up for outgoing
+ * Boot and Read replies. Boot Request packets are effectively
+ * limited to 255 bytes due to the preceding 1-byte length field.
+ */
+
+#define RMPBOOTSIZE(s) (sizeof(struct hp_hdr) + sizeof(struct hp_llc) + \
+ sizeof(struct rmp_boot_repl) + s - sizeof(restofpkt))
+#define RMPREADSIZE(s) (sizeof(struct hp_hdr) + sizeof(struct hp_llc) + \
+ sizeof(struct rmp_read_repl) + s - sizeof(restofpkt) \
+ - sizeof(u_char))
+#define RMPDONESIZE (sizeof(struct hp_hdr) + sizeof(struct hp_llc) + \
+ sizeof(struct rmp_boot_done))
+#define RMPBOOTDATA 255
+#define RMPREADDATA (RMPDATALEN - \
+ (2*sizeof(u_char)+sizeof(u_short)+sizeof(u_word)))
+
+/*
+ * This protocol defines some field sizes as "rest of ethernet packet".
+ * There is no easy way to specify this in C, so we use a one character
+ * field to denote it, and index past it to the end of the packet.
+ */
+
+typedef char restofpkt;
+
+/*
+ * Due to the RMP packet layout, we'll run into alignment problems
+ * on machines that cant access words on half-word boundaries. If
+ * you know that your machine does not suffer from this problem,
+ * add it to the hp300 #define below.
+ *
+ * The following macros are used to deal with this problem:
+ * WORDZE(w) Return True if u_word `w' is zero, False otherwise.
+ * ZEROWORD(w) Set u_word `w' to zero.
+ * COPYWORD(w1,w2) Copy u_word `w1' to `w2'.
+ * GETWORD(w,i) Copy u_word `w' into int `i'.
+ * PUTWORD(i,w) Copy int `i' into u_word `w'.
+ *
+ * N.B. We do not support little endian alignment-challenged machines.
+ */
+#if defined(vax) || defined(tahoe) || defined(hp300)
+
+typedef u_int u_word;
+
+#define WORDZE(w) ((w) == 0)
+#define ZEROWORD(w) (w) = 0
+#define COPYWORD(w1,w2) (w2) = (w1)
+#define GETWORD(w, i) (i) = (w)
+#define PUTWORD(i, w) (w) = (i)
+
+#else
+
+#define _WORD_HIGHPART 0 /* XXX: assume Big Endian for now */
+#define _WORD_LOWPART 1
+
+typedef struct _uword { u_short val[2]; } u_word;
+
+#define WORDZE(w) \
+ ((w.val[_WORD_HIGHPART] == 0) && (w.val[_WORD_LOWPART] == 0))
+#define ZEROWORD(w) \
+ (w).val[_WORD_HIGHPART] = (w).val[_WORD_LOWPART] = 0
+#define COPYWORD(w1, w2) \
+ { (w2).val[_WORD_HIGHPART] = (w1).val[_WORD_HIGHPART]; \
+ (w2).val[_WORD_LOWPART] = (w1).val[_WORD_LOWPART]; \
+ }
+#define GETWORD(w, i) \
+ (i) = (((u_int)(w).val[_WORD_HIGHPART]) << 16) | (w).val[_WORD_LOWPART]
+#define PUTWORD(i, w) \
+ { (w).val[_WORD_HIGHPART] = (u_short) (((i) >> 16) & 0xffff); \
+ (w).val[_WORD_LOWPART] = (u_short) (i & 0xffff); \
+ }
+
+#endif
+
+/*
+ * Packet structures.
+ */
+
+struct rmp_raw { /* generic RMP packet */
+ u_char rmp_type; /* packet type */
+ u_char rmp_rawdata[RMPDATALEN-1];
+};
+
+struct rmp_boot_req { /* boot request */
+ u_char rmp_type; /* packet type (RMP_BOOT_REQ) */
+ u_char rmp_retcode; /* return code (0) */
+ u_word rmp_seqno; /* sequence number (real time clock) */
+ u_short rmp_session; /* session id (normally 0) */
+ u_short rmp_version; /* protocol version (RMP_VERSION) */
+ char rmp_machtype[RMP_MACHLEN]; /* machine type */
+ u_char rmp_flnmsize; /* length of rmp_flnm */
+ restofpkt rmp_flnm; /* name of file to be read */
+};
+
+struct rmp_boot_repl { /* boot reply */
+ u_char rmp_type; /* packet type (RMP_BOOT_REPL) */
+ u_char rmp_retcode; /* return code (normally 0) */
+ u_word rmp_seqno; /* sequence number (from boot req) */
+ u_short rmp_session; /* session id (generated) */
+ u_short rmp_version; /* protocol version (RMP_VERSION) */
+ u_char rmp_flnmsize; /* length of rmp_flnm */
+ restofpkt rmp_flnm; /* name of file (from boot req) */
+};
+
+struct rmp_read_req { /* read request */
+ u_char rmp_type; /* packet type (RMP_READ_REQ) */
+ u_char rmp_retcode; /* return code (0) */
+ u_word rmp_offset; /* file relative byte offset */
+ u_short rmp_session; /* session id (from boot repl) */
+ u_short rmp_size; /* max no of bytes to send */
+};
+
+struct rmp_read_repl { /* read reply */
+ u_char rmp_type; /* packet type (RMP_READ_REPL) */
+ u_char rmp_retcode; /* return code (normally 0) */
+ u_word rmp_offset; /* byte offset (from read req) */
+ u_short rmp_session; /* session id (from read req) */
+ restofpkt rmp_data; /* data (max size from read req) */
+ u_char rmp_unused; /* padding to 16-bit boundary */
+};
+
+struct rmp_boot_done { /* boot complete */
+ u_char rmp_type; /* packet type (RMP_BOOT_DONE) */
+ u_char rmp_retcode; /* return code (0) */
+ u_word rmp_unused; /* not used (0) */
+ u_short rmp_session; /* session id (from read repl) */
+};
+
+struct rmp_packet {
+ struct hp_hdr hp_hdr;
+ struct hp_llc hp_llc;
+ union {
+ struct rmp_boot_req rmp_brq; /* boot request */
+ struct rmp_boot_repl rmp_brpl; /* boot reply */
+ struct rmp_read_req rmp_rrq; /* read request */
+ struct rmp_read_repl rmp_rrpl; /* read reply */
+ struct rmp_boot_done rmp_done; /* boot complete */
+ struct rmp_raw rmp_raw; /* raw data */
+ } rmp_proto;
+};
+
+/*
+ * Make life easier...
+ */
+
+#define r_type rmp_proto.rmp_raw.rmp_type
+#define r_data rmp_proto.rmp_raw.rmp_data
+#define r_brq rmp_proto.rmp_brq
+#define r_brpl rmp_proto.rmp_brpl
+#define r_rrq rmp_proto.rmp_rrq
+#define r_rrpl rmp_proto.rmp_rrpl
+#define r_done rmp_proto.rmp_done
diff --git a/libexec/rbootd/rmpproto.c b/libexec/rbootd/rmpproto.c
new file mode 100644
index 0000000..d0a8d4a
--- /dev/null
+++ b/libexec/rbootd/rmpproto.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * 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.
+ *
+ * @(#)rmpproto.c 8.1 (Berkeley) 6/4/93
+ *
+ * Utah $Hdr: rmpproto.c 3.1 92/07/06$
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)rmpproto.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "defs.h"
+
+/*
+** ProcessPacket -- determine packet type and do what's required.
+**
+** An RMP BOOT packet has been received. Look at the type field
+** and process Boot Requests, Read Requests, and Boot Complete
+** packets. Any other type will be dropped with a warning msg.
+**
+** Parameters:
+** rconn - the new connection
+** client - list of files available to this host
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - If this is a valid boot request, it will be added to
+** the linked list of outstanding requests (RmpConns).
+** - If this is a valid boot complete, its associated
+** entry in RmpConns will be deleted.
+** - Also, unless we run out of memory, a reply will be
+** sent to the host that sent the packet.
+*/
+void
+ProcessPacket(rconn, client)
+ RMPCONN *rconn;
+ CLIENT *client;
+{
+ struct rmp_packet *rmp;
+ RMPCONN *rconnout;
+
+ rmp = &rconn->rmp; /* cache pointer to RMP packet */
+
+ switch(rmp->r_type) { /* do what we came here to do */
+ case RMP_BOOT_REQ: /* boot request */
+ if ((rconnout = NewConn(rconn)) == NULL)
+ return;
+
+ /*
+ * If the Session ID is 0xffff, this is a "probe"
+ * packet and we do not want to add the connection
+ * to the linked list of active connections. There
+ * are two types of probe packets, if the Sequence
+ * Number is 0 they want to know our host name, o/w
+ * they want the name of the file associated with
+ * the number spec'd by the Sequence Number.
+ *
+ * If this is an actual boot request, open the file
+ * and send a reply. If SendBootRepl() does not
+ * return 0, add the connection to the linked list
+ * of active connections, otherwise delete it since
+ * an error was encountered.
+ */
+ if (rmp->r_brq.rmp_session == RMP_PROBESID) {
+ if (WORDZE(rmp->r_brq.rmp_seqno))
+ (void) SendServerID(rconnout);
+ else
+ (void) SendFileNo(rmp, rconnout,
+ client? client->files:
+ BootFiles);
+ FreeConn(rconnout);
+ } else {
+ if (SendBootRepl(rmp, rconnout,
+ client? client->files: BootFiles))
+ AddConn(rconnout);
+ else
+ FreeConn(rconnout);
+ }
+ break;
+
+ case RMP_BOOT_REPL: /* boot reply (not valid) */
+ syslog(LOG_WARNING, "%s: sent a boot reply",
+ EnetStr(rconn));
+ break;
+
+ case RMP_READ_REQ: /* read request */
+ /*
+ * Send a portion of the boot file.
+ */
+ (void) SendReadRepl(rconn);
+ break;
+
+ case RMP_READ_REPL: /* read reply (not valid) */
+ syslog(LOG_WARNING, "%s: sent a read reply",
+ EnetStr(rconn));
+ break;
+
+ case RMP_BOOT_DONE: /* boot complete */
+ /*
+ * Remove the entry from the linked list of active
+ * connections.
+ */
+ (void) BootDone(rconn);
+ break;
+
+ default: /* unknown RMP packet type */
+ syslog(LOG_WARNING, "%s: unknown packet type (%u)",
+ EnetStr(rconn), rmp->r_type);
+ }
+}
+
+/*
+** SendServerID -- send our host name to who ever requested it.
+**
+** Parameters:
+** rconn - the reply packet to be formatted.
+**
+** Returns:
+** 1 on success, 0 on failure.
+**
+** Side Effects:
+** none.
+*/
+int
+SendServerID(rconn)
+ RMPCONN *rconn;
+{
+ register struct rmp_packet *rpl;
+ register char *src, *dst;
+ register u_char *size;
+
+ rpl = &rconn->rmp; /* cache ptr to RMP packet */
+
+ /*
+ * Set up assorted fields in reply packet.
+ */
+ rpl->r_brpl.rmp_type = RMP_BOOT_REPL;
+ rpl->r_brpl.rmp_retcode = RMP_E_OKAY;
+ ZEROWORD(rpl->r_brpl.rmp_seqno);
+ rpl->r_brpl.rmp_session = 0;
+ rpl->r_brpl.rmp_version = RMP_VERSION;
+
+ size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of host name */
+
+ /*
+ * Copy our host name into the reply packet incrementing the
+ * length as we go. Stop at RMP_HOSTLEN or the first dot.
+ */
+ src = MyHost;
+ dst = (char *) &rpl->r_brpl.rmp_flnm;
+ for (*size = 0; *size < RMP_HOSTLEN; (*size)++) {
+ if (*src == '.' || *src == '\0')
+ break;
+ *dst++ = *src++;
+ }
+
+ rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */
+
+ return(SendPacket(rconn)); /* send packet */
+}
+
+/*
+** SendFileNo -- send the name of a bootable file to the requester.
+**
+** Parameters:
+** req - RMP BOOT packet containing the request.
+** rconn - the reply packet to be formatted.
+** filelist - list of files available to the requester.
+**
+** Returns:
+** 1 on success, 0 on failure.
+**
+** Side Effects:
+** none.
+*/
+int
+SendFileNo(req, rconn, filelist)
+ struct rmp_packet *req;
+ RMPCONN *rconn;
+ char *filelist[];
+{
+ register struct rmp_packet *rpl;
+ register char *src, *dst;
+ register u_char *size, i;
+
+ GETWORD(req->r_brpl.rmp_seqno, i); /* SeqNo is really FileNo */
+ rpl = &rconn->rmp; /* cache ptr to RMP packet */
+
+ /*
+ * Set up assorted fields in reply packet.
+ */
+ rpl->r_brpl.rmp_type = RMP_BOOT_REPL;
+ PUTWORD(i, rpl->r_brpl.rmp_seqno);
+ i--;
+ rpl->r_brpl.rmp_session = 0;
+ rpl->r_brpl.rmp_version = RMP_VERSION;
+
+ size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of filename */
+ *size = 0; /* init length to zero */
+
+ /*
+ * Copy the file name into the reply packet incrementing the
+ * length as we go. Stop at end of string or when RMPBOOTDATA
+ * characters have been copied. Also, set return code to
+ * indicate success or "no more files".
+ */
+ if (i < C_MAXFILE && filelist[i] != NULL) {
+ src = filelist[i];
+ dst = (char *)&rpl->r_brpl.rmp_flnm;
+ for (; *src && *size < RMPBOOTDATA; (*size)++) {
+ if (*src == '\0')
+ break;
+ *dst++ = *src++;
+ }
+ rpl->r_brpl.rmp_retcode = RMP_E_OKAY;
+ } else
+ rpl->r_brpl.rmp_retcode = RMP_E_NODFLT;
+
+ rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */
+
+ return(SendPacket(rconn)); /* send packet */
+}
+
+/*
+** SendBootRepl -- open boot file and respond to boot request.
+**
+** Parameters:
+** req - RMP BOOT packet containing the request.
+** rconn - the reply packet to be formatted.
+** filelist - list of files available to the requester.
+**
+** Returns:
+** 1 on success, 0 on failure.
+**
+** Side Effects:
+** none.
+*/
+int
+SendBootRepl(req, rconn, filelist)
+ struct rmp_packet *req;
+ RMPCONN *rconn;
+ char *filelist[];
+{
+ int retval;
+ char *filename, filepath[RMPBOOTDATA+1];
+ RMPCONN *oldconn;
+ register struct rmp_packet *rpl;
+ register char *src, *dst1, *dst2;
+ register u_char i;
+
+ /*
+ * If another connection already exists, delete it since we
+ * are obviously starting again.
+ */
+ if ((oldconn = FindConn(rconn)) != NULL) {
+ syslog(LOG_WARNING, "%s: dropping existing connection",
+ EnetStr(oldconn));
+ RemoveConn(oldconn);
+ }
+
+ rpl = &rconn->rmp; /* cache ptr to RMP packet */
+
+ /*
+ * Set up assorted fields in reply packet.
+ */
+ rpl->r_brpl.rmp_type = RMP_BOOT_REPL;
+ COPYWORD(req->r_brq.rmp_seqno, rpl->r_brpl.rmp_seqno);
+ rpl->r_brpl.rmp_session = GenSessID();
+ rpl->r_brpl.rmp_version = RMP_VERSION;
+ rpl->r_brpl.rmp_flnmsize = req->r_brq.rmp_flnmsize;
+
+ /*
+ * Copy file name to `filepath' string, and into reply packet.
+ */
+ src = &req->r_brq.rmp_flnm;
+ dst1 = filepath;
+ dst2 = &rpl->r_brpl.rmp_flnm;
+ for (i = 0; i < req->r_brq.rmp_flnmsize; i++)
+ *dst1++ = *dst2++ = *src++;
+ *dst1 = '\0';
+
+ /*
+ * If we are booting HP-UX machines, their secondary loader will
+ * ask for files like "/hp-ux". As a security measure, we do not
+ * allow boot files to lay outside the boot directory (unless they
+ * are purposely link'd out. So, make `filename' become the path-
+ * stripped file name and spoof the client into thinking that it
+ * really got what it wanted.
+ */
+ filename = (filename = rindex(filepath,'/'))? ++filename: filepath;
+
+ /*
+ * Check that this is a valid boot file name.
+ */
+ for (i = 0; i < C_MAXFILE && filelist[i] != NULL; i++)
+ if (STREQN(filename, filelist[i]))
+ goto match;
+
+ /*
+ * Invalid boot file name, set error and send reply packet.
+ */
+ rpl->r_brpl.rmp_retcode = RMP_E_NOFILE;
+ retval = 0;
+ goto sendpkt;
+
+match:
+ /*
+ * This is a valid boot file. Open the file and save the file
+ * descriptor associated with this connection and set success
+ * indication. If the file couldnt be opened, set error:
+ * "no such file or dir" - RMP_E_NOFILE
+ * "file table overflow" - RMP_E_BUSY
+ * "too many open files" - RMP_E_BUSY
+ * anything else - RMP_E_OPENFILE
+ */
+ if ((rconn->bootfd = open(filename, O_RDONLY, 0600)) < 0) {
+ rpl->r_brpl.rmp_retcode = (errno == ENOENT)? RMP_E_NOFILE:
+ (errno == EMFILE || errno == ENFILE)? RMP_E_BUSY:
+ RMP_E_OPENFILE;
+ retval = 0;
+ } else {
+ rpl->r_brpl.rmp_retcode = RMP_E_OKAY;
+ retval = 1;
+ }
+
+sendpkt:
+ syslog(LOG_INFO, "%s: request to boot %s (%s)",
+ EnetStr(rconn), filename, retval? "granted": "denied");
+
+ rconn->rmplen = RMPBOOTSIZE(rpl->r_brpl.rmp_flnmsize);
+
+ return (retval & SendPacket(rconn));
+}
+
+/*
+** SendReadRepl -- send a portion of the boot file to the requester.
+**
+** Parameters:
+** rconn - the reply packet to be formatted.
+**
+** Returns:
+** 1 on success, 0 on failure.
+**
+** Side Effects:
+** none.
+*/
+int
+SendReadRepl(rconn)
+ RMPCONN *rconn;
+{
+ int retval;
+ RMPCONN *oldconn;
+ register struct rmp_packet *rpl, *req;
+ register int size = 0;
+ int madeconn = 0;
+
+ /*
+ * Find the old connection. If one doesnt exist, create one only
+ * to return the error code.
+ */
+ if ((oldconn = FindConn(rconn)) == NULL) {
+ if ((oldconn = NewConn(rconn)) == NULL)
+ return(0);
+ syslog(LOG_ERR, "SendReadRepl: no active connection (%s)",
+ EnetStr(rconn));
+ madeconn++;
+ }
+
+ req = &rconn->rmp; /* cache ptr to request packet */
+ rpl = &oldconn->rmp; /* cache ptr to reply packet */
+
+ if (madeconn) { /* no active connection above; abort */
+ rpl->r_rrpl.rmp_retcode = RMP_E_ABORT;
+ retval = 1;
+ goto sendpkt;
+ }
+
+ /*
+ * Make sure Session ID's match.
+ */
+ if (req->r_rrq.rmp_session !=
+ ((rpl->r_type == RMP_BOOT_REPL)? rpl->r_brpl.rmp_session:
+ rpl->r_rrpl.rmp_session)) {
+ syslog(LOG_ERR, "SendReadRepl: bad session id (%s)",
+ EnetStr(rconn));
+ rpl->r_rrpl.rmp_retcode = RMP_E_BADSID;
+ retval = 1;
+ goto sendpkt;
+ }
+
+ /*
+ * If the requester asks for more data than we can fit,
+ * silently clamp the request size down to RMPREADDATA.
+ *
+ * N.B. I do not know if this is "legal", however it seems
+ * to work. This is necessary for bpfwrite() on machines
+ * with MCLBYTES less than 1514.
+ */
+ if (req->r_rrq.rmp_size > RMPREADDATA)
+ req->r_rrq.rmp_size = RMPREADDATA;
+
+ /*
+ * Position read head on file according to info in request packet.
+ */
+ GETWORD(req->r_rrq.rmp_offset, size);
+ if (lseek(oldconn->bootfd, (off_t)size, L_SET) < 0) {
+ syslog(LOG_ERR, "SendReadRepl: lseek: %m (%s)",
+ EnetStr(rconn));
+ rpl->r_rrpl.rmp_retcode = RMP_E_ABORT;
+ retval = 1;
+ goto sendpkt;
+ }
+
+ /*
+ * Read data directly into reply packet.
+ */
+ if ((size = read(oldconn->bootfd, &rpl->r_rrpl.rmp_data,
+ (int) req->r_rrq.rmp_size)) <= 0) {
+ if (size < 0) {
+ syslog(LOG_ERR, "SendReadRepl: read: %m (%s)",
+ EnetStr(rconn));
+ rpl->r_rrpl.rmp_retcode = RMP_E_ABORT;
+ } else {
+ rpl->r_rrpl.rmp_retcode = RMP_E_EOF;
+ }
+ retval = 1;
+ goto sendpkt;
+ }
+
+ /*
+ * Set success indication.
+ */
+ rpl->r_rrpl.rmp_retcode = RMP_E_OKAY;
+
+sendpkt:
+ /*
+ * Set up assorted fields in reply packet.
+ */
+ rpl->r_rrpl.rmp_type = RMP_READ_REPL;
+ COPYWORD(req->r_rrq.rmp_offset, rpl->r_rrpl.rmp_offset);
+ rpl->r_rrpl.rmp_session = req->r_rrq.rmp_session;
+
+ oldconn->rmplen = RMPREADSIZE(size); /* set size of packet */
+
+ retval &= SendPacket(oldconn); /* send packet */
+
+ if (madeconn) /* clean up after ourself */
+ FreeConn(oldconn);
+
+ return (retval);
+}
+
+/*
+** BootDone -- free up memory allocated for a connection.
+**
+** Parameters:
+** rconn - incoming boot complete packet.
+**
+** Returns:
+** 1 on success, 0 on failure.
+**
+** Side Effects:
+** none.
+*/
+int
+BootDone(rconn)
+ RMPCONN *rconn;
+{
+ RMPCONN *oldconn;
+ struct rmp_packet *rpl;
+
+ /*
+ * If we cant find the connection, ignore the request.
+ */
+ if ((oldconn = FindConn(rconn)) == NULL) {
+ syslog(LOG_ERR, "BootDone: no existing connection (%s)",
+ EnetStr(rconn));
+ return(0);
+ }
+
+ rpl = &oldconn->rmp; /* cache ptr to RMP packet */
+
+ /*
+ * Make sure Session ID's match.
+ */
+ if (rconn->rmp.r_rrq.rmp_session !=
+ ((rpl->r_type == RMP_BOOT_REPL)? rpl->r_brpl.rmp_session:
+ rpl->r_rrpl.rmp_session)) {
+ syslog(LOG_ERR, "BootDone: bad session id (%s)",
+ EnetStr(rconn));
+ return(0);
+ }
+
+ RemoveConn(oldconn); /* remove connection */
+
+ syslog(LOG_INFO, "%s: boot complete", EnetStr(rconn));
+
+ return(1);
+}
+
+/*
+** SendPacket -- send an RMP packet to a remote host.
+**
+** Parameters:
+** rconn - packet to be sent.
+**
+** Returns:
+** 1 on success, 0 on failure.
+**
+** Side Effects:
+** none.
+*/
+int
+SendPacket(rconn)
+ register RMPCONN *rconn;
+{
+ /*
+ * Set Ethernet Destination address to Source (BPF and the enet
+ * driver will take care of getting our source address set).
+ */
+ bcopy((char *)&rconn->rmp.hp_hdr.saddr[0],
+ (char *)&rconn->rmp.hp_hdr.daddr[0], RMP_ADDRLEN);
+ rconn->rmp.hp_hdr.len = rconn->rmplen - sizeof(struct hp_hdr);
+
+ /*
+ * Reverse 802.2/HP Extended Source & Destination Access Pts.
+ */
+ rconn->rmp.hp_llc.dxsap = HPEXT_SXSAP;
+ rconn->rmp.hp_llc.sxsap = HPEXT_DXSAP;
+
+ /*
+ * Last time this connection was active.
+ */
+ (void) gettimeofday(&rconn->tstamp, (struct timezone *)0);
+
+ if (DbgFp != NULL) /* display packet */
+ DispPkt(rconn,DIR_SENT);
+
+ /*
+ * Send RMP packet to remote host.
+ */
+ return(BpfWrite(rconn));
+}
diff --git a/libexec/rbootd/utils.c b/libexec/rbootd/utils.c
new file mode 100644
index 0000000..58ead7c
--- /dev/null
+++ b/libexec/rbootd/utils.c
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * 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.
+ *
+ * @(#)utils.c 8.2 (Berkeley) 2/22/94
+ *
+ * Utah $Hdr: utils.c 3.1 92/07/06$
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)utils.c 8.2 (Berkeley) 2/22/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include "defs.h"
+
+/*
+** DispPkt -- Display the contents of an RMPCONN packet.
+**
+** Parameters:
+** rconn - packet to be displayed.
+** direct - direction packet is going (DIR_*).
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** None.
+*/
+void
+DispPkt(rconn, direct)
+ RMPCONN *rconn;
+ int direct;
+{
+ static char BootFmt[] = "\t\tRetCode:%u SeqNo:%lx SessID:%x Vers:%u";
+ static char ReadFmt[] = "\t\tRetCode:%u Offset:%lx SessID:%x\n";
+
+ struct tm *tmp;
+ register struct rmp_packet *rmp;
+ int i, omask;
+ u_int t;
+
+ /*
+ * Since we will be working with RmpConns as well as DbgFp, we
+ * must block signals that can affect either.
+ */
+ omask = sigblock(sigmask(SIGHUP)|sigmask(SIGUSR1)|sigmask(SIGUSR2));
+
+ if (DbgFp == NULL) { /* sanity */
+ (void) sigsetmask(omask);
+ return;
+ }
+
+ /* display direction packet is going using '>>>' or '<<<' */
+ fputs((direct==DIR_RCVD)?"<<< ":(direct==DIR_SENT)?">>> ":"", DbgFp);
+
+ /* display packet timestamp */
+ tmp = localtime((time_t *)&rconn->tstamp.tv_sec);
+ fprintf(DbgFp, "%02d:%02d:%02d.%06ld ", tmp->tm_hour, tmp->tm_min,
+ tmp->tm_sec, rconn->tstamp.tv_usec);
+
+ /* display src or dst addr and information about network interface */
+ fprintf(DbgFp, "Addr: %s Intf: %s\n", EnetStr(rconn), IntfName);
+
+ rmp = &rconn->rmp;
+
+ /* display IEEE 802.2 Logical Link Control header */
+ (void) fprintf(DbgFp, "\t802.2 LLC: DSAP:%x SSAP:%x CTRL:%x\n",
+ rmp->hp_llc.dsap, rmp->hp_llc.ssap, rmp->hp_llc.cntrl);
+
+ /* display HP extensions to 802.2 Logical Link Control header */
+ (void) fprintf(DbgFp, "\tHP Ext: DXSAP:%x SXSAP:%x\n",
+ rmp->hp_llc.dxsap, rmp->hp_llc.sxsap);
+
+ /*
+ * Display information about RMP packet using type field to
+ * determine what kind of packet this is.
+ */
+ switch(rmp->r_type) {
+ case RMP_BOOT_REQ: /* boot request */
+ (void) fprintf(DbgFp, "\tBoot Request:");
+ GETWORD(rmp->r_brq.rmp_seqno, t);
+ if (rmp->r_brq.rmp_session == RMP_PROBESID) {
+ if (WORDZE(rmp->r_brq.rmp_seqno))
+ fputs(" (Send Server ID)", DbgFp);
+ else
+ fprintf(DbgFp," (Send Filename #%u)",t);
+ }
+ (void) fputc('\n', DbgFp);
+ (void) fprintf(DbgFp, BootFmt, rmp->r_brq.rmp_retcode,
+ t, rmp->r_brq.rmp_session,
+ rmp->r_brq.rmp_version);
+ (void) fprintf(DbgFp, "\n\t\tMachine Type: ");
+ for (i = 0; i < RMP_MACHLEN; i++)
+ (void) fputc(rmp->r_brq.rmp_machtype[i], DbgFp);
+ DspFlnm(rmp->r_brq.rmp_flnmsize, &rmp->r_brq.rmp_flnm);
+ break;
+ case RMP_BOOT_REPL: /* boot reply */
+ fprintf(DbgFp, "\tBoot Reply:\n");
+ GETWORD(rmp->r_brpl.rmp_seqno, t);
+ (void) fprintf(DbgFp, BootFmt, rmp->r_brpl.rmp_retcode,
+ t, rmp->r_brpl.rmp_session,
+ rmp->r_brpl.rmp_version);
+ DspFlnm(rmp->r_brpl.rmp_flnmsize,&rmp->r_brpl.rmp_flnm);
+ break;
+ case RMP_READ_REQ: /* read request */
+ (void) fprintf(DbgFp, "\tRead Request:\n");
+ GETWORD(rmp->r_rrq.rmp_offset, t);
+ (void) fprintf(DbgFp, ReadFmt, rmp->r_rrq.rmp_retcode,
+ t, rmp->r_rrq.rmp_session);
+ (void) fprintf(DbgFp, "\t\tNoOfBytes: %u\n",
+ rmp->r_rrq.rmp_size);
+ break;
+ case RMP_READ_REPL: /* read reply */
+ (void) fprintf(DbgFp, "\tRead Reply:\n");
+ GETWORD(rmp->r_rrpl.rmp_offset, t);
+ (void) fprintf(DbgFp, ReadFmt, rmp->r_rrpl.rmp_retcode,
+ t, rmp->r_rrpl.rmp_session);
+ (void) fprintf(DbgFp, "\t\tNoOfBytesSent: %d\n",
+ rconn->rmplen - RMPREADSIZE(0));
+ break;
+ case RMP_BOOT_DONE: /* boot complete */
+ (void) fprintf(DbgFp, "\tBoot Complete:\n");
+ (void) fprintf(DbgFp, "\t\tRetCode:%u SessID:%x\n",
+ rmp->r_done.rmp_retcode,
+ rmp->r_done.rmp_session);
+ break;
+ default: /* ??? */
+ (void) fprintf(DbgFp, "\tUnknown Type:(%d)\n",
+ rmp->r_type);
+ }
+ (void) fputc('\n', DbgFp);
+ (void) fflush(DbgFp);
+
+ (void) sigsetmask(omask); /* reset old signal mask */
+}
+
+
+/*
+** GetEtherAddr -- convert an RMP (Ethernet) address into a string.
+**
+** An RMP BOOT packet has been received. Look at the type field
+** and process Boot Requests, Read Requests, and Boot Complete
+** packets. Any other type will be dropped with a warning msg.
+**
+** Parameters:
+** addr - array of RMP_ADDRLEN bytes.
+**
+** Returns:
+** Pointer to static string representation of `addr'.
+**
+** Side Effects:
+** None.
+**
+** Warnings:
+** - The return value points to a static buffer; it must
+** be copied if it's to be saved.
+** - For speed, we assume a u_char consists of 8 bits.
+*/
+char *
+GetEtherAddr(addr)
+ u_char *addr;
+{
+ static char Hex[] = "0123456789abcdef";
+ static char etherstr[RMP_ADDRLEN*3];
+ register int i;
+ register char *cp1, *cp2;
+
+ /*
+ * For each byte in `addr', convert it to "<hexchar><hexchar>:".
+ * The last byte does not get a trailing `:' appended.
+ */
+ i = 0;
+ cp1 = (char *)addr;
+ cp2 = etherstr;
+ for(;;) {
+ *cp2++ = Hex[*cp1 >> 4 & 0xf];
+ *cp2++ = Hex[*cp1++ & 0xf];
+ if (++i == RMP_ADDRLEN)
+ break;
+ *cp2++ = ':';
+ }
+ *cp2 = '\0';
+
+ return(etherstr);
+}
+
+
+/*
+** DispFlnm -- Print a string of bytes to DbgFp (often, a file name).
+**
+** Parameters:
+** size - number of bytes to print.
+** flnm - address of first byte.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - Characters are sent to `DbgFp'.
+*/
+void
+DspFlnm(size, flnm)
+ register u_int size;
+ register char *flnm;
+{
+ register int i;
+
+ (void) fprintf(DbgFp, "\n\t\tFile Name (%d): <", size);
+ for (i = 0; i < size; i++)
+ (void) fputc(*flnm++, DbgFp);
+ (void) fputs(">\n", DbgFp);
+}
+
+
+/*
+** NewClient -- allocate memory for a new CLIENT.
+**
+** Parameters:
+** addr - RMP (Ethernet) address of new client.
+**
+** Returns:
+** Ptr to new CLIENT or NULL if we ran out of memory.
+**
+** Side Effects:
+** - Memory will be malloc'd for the new CLIENT.
+** - If malloc() fails, a log message will be generated.
+*/
+CLIENT *
+NewClient(addr)
+ u_char *addr;
+{
+ CLIENT *ctmp;
+
+ if ((ctmp = (CLIENT *) malloc(sizeof(CLIENT))) == NULL) {
+ syslog(LOG_ERR, "NewClient: out of memory (%s)",
+ GetEtherAddr(addr));
+ return(NULL);
+ }
+
+ bzero(ctmp, sizeof(CLIENT));
+ bcopy(addr, &ctmp->addr[0], RMP_ADDRLEN);
+ return(ctmp);
+}
+
+/*
+** FreeClient -- free linked list of Clients.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - All malloc'd memory associated with the linked list of
+** CLIENTS will be free'd; `Clients' will be set to NULL.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked.
+*/
+void
+FreeClients()
+{
+ register CLIENT *ctmp;
+
+ while (Clients != NULL) {
+ ctmp = Clients;
+ Clients = Clients->next;
+ FreeClient(ctmp);
+ }
+}
+
+/*
+** NewStr -- allocate memory for a character array.
+**
+** Parameters:
+** str - null terminated character array.
+**
+** Returns:
+** Ptr to new character array or NULL if we ran out of memory.
+**
+** Side Effects:
+** - Memory will be malloc'd for the new character array.
+** - If malloc() fails, a log message will be generated.
+*/
+char *
+NewStr(str)
+ char *str;
+{
+ char *stmp;
+
+ if ((stmp = (char *)malloc((unsigned) (strlen(str)+1))) == NULL) {
+ syslog(LOG_ERR, "NewStr: out of memory (%s)", str);
+ return(NULL);
+ }
+
+ (void) strcpy(stmp, str);
+ return(stmp);
+}
+
+/*
+** To save time, NewConn and FreeConn maintain a cache of one RMPCONN
+** in `LastFree' (defined below).
+*/
+
+static RMPCONN *LastFree = NULL;
+
+/*
+** NewConn -- allocate memory for a new RMPCONN connection.
+**
+** Parameters:
+** rconn - initialization template for new connection.
+**
+** Returns:
+** Ptr to new RMPCONN or NULL if we ran out of memory.
+**
+** Side Effects:
+** - Memory may be malloc'd for the new RMPCONN (if not cached).
+** - If malloc() fails, a log message will be generated.
+*/
+RMPCONN *
+NewConn(rconn)
+ RMPCONN *rconn;
+{
+ RMPCONN *rtmp;
+
+ if (LastFree == NULL) { /* nothing cached; make a new one */
+ if ((rtmp = (RMPCONN *) malloc(sizeof(RMPCONN))) == NULL) {
+ syslog(LOG_ERR, "NewConn: out of memory (%s)",
+ EnetStr(rconn));
+ return(NULL);
+ }
+ } else { /* use the cached RMPCONN */
+ rtmp = LastFree;
+ LastFree = NULL;
+ }
+
+ /*
+ * Copy template into `rtmp', init file descriptor to `-1' and
+ * set ptr to next elem NULL.
+ */
+ bcopy((char *)rconn, (char *)rtmp, sizeof(RMPCONN));
+ rtmp->bootfd = -1;
+ rtmp->next = NULL;
+
+ return(rtmp);
+}
+
+/*
+** FreeConn -- Free memory associated with an RMPCONN connection.
+**
+** Parameters:
+** rtmp - ptr to RMPCONN to be free'd.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - Memory associated with `rtmp' may be free'd (or cached).
+** - File desc associated with `rtmp->bootfd' will be closed.
+*/
+void
+FreeConn(rtmp)
+ register RMPCONN *rtmp;
+{
+ /*
+ * If the file descriptor is in use, close the file.
+ */
+ if (rtmp->bootfd >= 0) {
+ (void) close(rtmp->bootfd);
+ rtmp->bootfd = -1;
+ }
+
+ if (LastFree == NULL) /* cache for next time */
+ rtmp = LastFree;
+ else /* already one cached; free this one */
+ free((char *)rtmp);
+}
+
+/*
+** FreeConns -- free linked list of RMPCONN connections.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - All malloc'd memory associated with the linked list of
+** connections will be free'd; `RmpConns' will be set to NULL.
+** - If LastFree is != NULL, it too will be free'd & NULL'd.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked.
+*/
+void
+FreeConns()
+{
+ register RMPCONN *rtmp;
+
+ while (RmpConns != NULL) {
+ rtmp = RmpConns;
+ RmpConns = RmpConns->next;
+ FreeConn(rtmp);
+ }
+
+ if (LastFree != NULL) {
+ free((char *)LastFree);
+ LastFree = NULL;
+ }
+}
+
+/*
+** AddConn -- Add a connection to the linked list of connections.
+**
+** Parameters:
+** rconn - connection to be added.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - RmpConn will point to new connection.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked.
+*/
+void
+AddConn(rconn)
+ register RMPCONN *rconn;
+{
+ if (RmpConns != NULL)
+ rconn->next = RmpConns;
+ RmpConns = rconn;
+}
+
+/*
+** FindConn -- Find a connection in the linked list of connections.
+**
+** We use the RMP (Ethernet) address as the basis for determining
+** if this is the same connection. According to the Remote Maint
+** Protocol, we can only have one connection with any machine.
+**
+** Parameters:
+** rconn - connection to be found.
+**
+** Returns:
+** Matching connection from linked list or NULL if not found.
+**
+** Side Effects:
+** None.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked.
+*/
+RMPCONN *
+FindConn(rconn)
+ register RMPCONN *rconn;
+{
+ register RMPCONN *rtmp;
+
+ for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next)
+ if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0],
+ (char *)&rtmp->rmp.hp_hdr.saddr[0], RMP_ADDRLEN) == 0)
+ break;
+
+ return(rtmp);
+}
+
+/*
+** RemoveConn -- Remove a connection from the linked list of connections.
+**
+** Parameters:
+** rconn - connection to be removed.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - If found, an RMPCONN will cease to exist and it will
+** be removed from the linked list.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked.
+*/
+void
+RemoveConn(rconn)
+ register RMPCONN *rconn;
+{
+ register RMPCONN *thisrconn, *lastrconn;
+
+ if (RmpConns == rconn) { /* easy case */
+ RmpConns = RmpConns->next;
+ FreeConn(rconn);
+ } else { /* must traverse linked list */
+ lastrconn = RmpConns; /* set back ptr */
+ thisrconn = lastrconn->next; /* set current ptr */
+ while (thisrconn != NULL) {
+ if (rconn == thisrconn) { /* found it */
+ lastrconn->next = thisrconn->next;
+ FreeConn(thisrconn);
+ break;
+ }
+ lastrconn = thisrconn;
+ thisrconn = thisrconn->next;
+ }
+ }
+}
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/authenc.c b/libexec/telnetd/authenc.c
new file mode 100644
index 0000000..fcd17fc
--- /dev/null
+++ b/libexec/telnetd/authenc.c
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)authenc.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#if defined(AUTHENTICATION) || defined(ENCRYPTION)
+#include "telnetd.h"
+#include <libtelnet/misc.h>
+
+ int
+net_write(str, len)
+ unsigned char *str;
+ int len;
+{
+ if (nfrontp + len < netobuf + BUFSIZ) {
+ bcopy((void *)str, (void *)nfrontp, len);
+ nfrontp += len;
+ return(len);
+ }
+ return(0);
+}
+
+ void
+net_encrypt()
+{
+#ifdef ENCRYPTION
+ char *s = (nclearto > nbackp) ? nclearto : nbackp;
+ if (s < nfrontp && encrypt_output) {
+ (*encrypt_output)((unsigned char *)s, nfrontp - s);
+ }
+ nclearto = nfrontp;
+#endif /* ENCRYPTION */
+}
+
+ int
+telnet_spin()
+{
+ ttloop();
+ return(0);
+}
+
+ char *
+telnet_getenv(val)
+ char *val;
+{
+ extern char *getenv();
+ return(getenv(val));
+}
+
+ char *
+telnet_gets(prompt, result, length, echo)
+ char *prompt;
+ char *result;
+ int length;
+ int echo;
+{
+ return((char *)0);
+}
+#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */
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